diff --git a/inventory/group_vars/copr_back_dev b/inventory/group_vars/copr_back_dev index 06320d119d..e54c19516d 100644 --- a/inventory/group_vars/copr_back_dev +++ b/inventory/group_vars/copr_back_dev @@ -29,3 +29,13 @@ csi_security_category: Moderate csi_primary_contact: "msuchy (mirek), frostyx, dturecek, praiskup IRC #fedora-admin, #fedora-buildsys" csi_purpose: Provide the testing environment of copr's backend csi_relationship: This host is the testing environment for the cloud infrastructure of copr's backend + +# consumed by roles/copr/certbot +letsencrypt: + predefined_deploy_script: lighttpd + certificates: + copr-be-dev.cloud.fedoraproject.org: + domains: + - copr-be-dev.cloud.fedoraproject.org + challenge_dir: /var/lib/copr/public_html + mail: copr-devel@lists.fedorahosted.org diff --git a/inventory/group_vars/copr_dev b/inventory/group_vars/copr_dev index 1d050089f3..061da7c0da 100644 --- a/inventory/group_vars/copr_dev +++ b/inventory/group_vars/copr_dev @@ -18,3 +18,13 @@ frontend_base_url: "https://copr-fe-dev.cloud.fedoraproject.org" dist_git_base_url: "copr-dist-git-dev.fedorainfracloud.org" ansible_ifcfg_blacklist: true + +# consumed by roles/copr/certbot +letsencrypt: + predefined_deploy_script: httpd + certificates: + copr-fe-dev.cloud.fedoraproject.org: + domains: + - copr-fe-dev.cloud.fedoraproject.org + challenge_dir: /var/www/html + mail: copr-devel@lists.fedorahosted.org diff --git a/inventory/group_vars/copr_dist_git b/inventory/group_vars/copr_dist_git index e165d75b91..29cbfbaab5 100644 --- a/inventory/group_vars/copr_dist_git +++ b/inventory/group_vars/copr_dist_git @@ -2,3 +2,13 @@ tcp_ports: [22, 80, 443] datacenter: cloud freezes: false + +# consumed by roles/copr/certbot +letsencrypt: + predefined_deploy_script: httpd + certificates: + copr-dist-git.fedorainfracloud.org: + domains: + - copr-dist-git.fedorainfracloud.org + challenge_dir: /var/www/html + mail: copr-devel@lists.fedorahosted.org diff --git a/inventory/group_vars/copr_dist_git_dev b/inventory/group_vars/copr_dist_git_dev index 5baead696f..ae9b3ddcf0 100644 --- a/inventory/group_vars/copr_dist_git_dev +++ b/inventory/group_vars/copr_dist_git_dev @@ -3,3 +3,13 @@ tcp_ports: [22, 80, 443] datacenter: cloud freezes: false devel: true + +# consumed by roles/copr/certbot +letsencrypt: + predefined_deploy_script: httpd + certificates: + copr-dist-git-dev.fedorainfracloud.org: + domains: + - copr-dist-git-dev.fedorainfracloud.org + challenge_dir: /var/www/html + mail: copr-devel@lists.fedorahosted.org diff --git a/roles/copr/backend/tasks/letsencrypt.yml b/roles/copr/backend/tasks/letsencrypt.yml deleted file mode 100644 index b39e740688..0000000000 --- a/roles/copr/backend/tasks/letsencrypt.yml +++ /dev/null @@ -1,75 +0,0 @@ -# This playbook is inspired by -# https://mikeshultz.com/setting-up-lets-encrypt-with-lighttpd-and-certbot.html -# Particularly it follows "(Option B) The Hard Way" - -- name: Install required packages - dnf: - state: latest - name: certbot - -- name: Create The Web Root - file: - path: /var/certbot/public_html/.well-known/ - state: directory - owner: lighttpd - group: lighttpd - mode: g+s - -- name: certbot, correct fcontext mapping the web root - sefcontext: - target: '/var/certbot/public_html/.well-known(/.*)' - setype: httpd_sys_content_t - state: present - -- name: certbot, restorecon the web root - file: - path: /var/certbot/public_html/.well-know - state: directory - setype: httpd_sys_content_t - -- name: Check that cert file exists - stat: - path: "/etc/letsencrypt/live/copr-be-dev.cloud.fedoraproject.org/cert.pem" - register: stat_cert - -# This is only for initial setup -# In such case, lighttpd config expects certificate files in /etc/letsencrypt/live, -# but it doesn't exist yet and therefore lighttpd refuses to start at all. -# We will use certbot standalone server to get certificates and then let the playbook to -# parse them and start the lighttpd again. -# Once we have at least some existing certificate files, this step will be skipped. -- name: Should admin run certbot? - fail: - msg: - - "There are no certificates yet" - - "Please temporarily stop lighttpd and run:" - - " certbot certonly --standalone --manual-public-ip-logging-ok -d copr-be-dev.cloud.fedoraproject.org" - - "Let it stopped and re-run the playbook." - when: - - stat_cert.stat.exists == False - -- name: Reformat Cert Files For Lighttpd - shell: cat /etc/letsencrypt/live/copr-be-dev.cloud.fedoraproject.org/privkey.pem /etc/letsencrypt/live/copr-be-dev.cloud.fedoraproject.org/cert.pem > /etc/letsencrypt/live/copr-be-dev.cloud.fedoraproject.org/combined.pem - -- name: Give Lighty Permissions - file: - path: "{{ item }}" - group: lighttpd - mode: g+x - with_items: - - /etc/letsencrypt - - /etc/letsencrypt/live - -- name: Service lighttpd should be running - service: - name: lighttpd - state: started - -- name: Install config for cert renewal - template: src="sysconfig/certbot" dest="/etc/sysconfig/certbot" owner=root group=root - -- name: Automatize cert renewal - service: - name: certbot-renew.timer - state: started - enabled: yes diff --git a/roles/copr/backend/tasks/main.yml b/roles/copr/backend/tasks/main.yml index 53f381dbd1..08c47be9b9 100644 --- a/roles/copr/backend/tasks/main.yml +++ b/roles/copr/backend/tasks/main.yml @@ -93,12 +93,13 @@ notify: - restart lighttpd +# TODO: move production to Let's Encrypt as well as devel - name: install certificates for production when: not devel import_tasks: "install_certs.yml" - name: letsencrypt cert - import_tasks: "letsencrypt.yml" + include_role: name=copr/certbot when: devel tags: - config diff --git a/roles/copr/backend/templates/sysconfig/certbot b/roles/copr/backend/templates/sysconfig/certbot deleted file mode 100644 index f9e51a681a..0000000000 --- a/roles/copr/backend/templates/sysconfig/certbot +++ /dev/null @@ -1,54 +0,0 @@ -## NOTE ## -# If a hook is set here then it will be used for all -# certificates and will override any per certificate -# hook configuration in place. - -# Command to be run in a shell before obtaining any -# certificates. Intended primarily for renewal, where it -# can be used to temporarily shut down a webserver that -# might conflict with the standalone plugin. This will -# only be called if a certificate is actually to be -# obtained/renewed. When renewing several certificates -# that have identical pre-hooks, only the first will be -# executed. -# -# An example to stop the MTA before updating certs would be -# PRE_HOOK="--pre-hook 'systemctl stop postfix'" -PRE_HOOK="" - -# Command to be run in a shell after attempting to -# obtain/renew certificates. Can be used to deploy -# renewed certificates, or to restart any servers that -# were stopped by --pre-hook. This is only run if an -# attempt was made to obtain/renew a certificate. If -# multiple renewed certificates have identical post- -# hooks, only one will be run. -# -# An example to restart httpd would be: -# POST_HOOK="--post-hook 'systemctl restart httpd'" -POST_HOOK="" - -# Command to be run in a shell once for each -# successfully renewed certificate. For this command, -# the shell variable $RENEWED_LINEAGE will point to the -# config live subdirectory containing the new certs and -# keys; the shell variable $RENEWED_DOMAINS will contain -# a space-delimited list of renewed cert domains -# -# An example to run a script to alert each cert would be: -# RENEW_HOOK="--renew-hook /usr/local/bin/cert-notifier.sh" -RENEW_HOOK="" - -# Any other misc arguments for the renewal -# See certbot -h renew for full list -# -# An example to force renewal for certificates not due yet -# CERTBOT_ARGS="--force-renewal" -# -# The following command is produced -# certbot renew **CERTBOT_ARGS -{% if devel %} -CERTBOT_ARGS="--webroot -w /var/certbot/public_html --cert-name copr-be-dev.cloud.fedoraproject.org" -{% else %} -CERTBOT_ARGS="" -{% endif %} diff --git a/roles/copr/certbot/tasks/letsencrypt.yml b/roles/copr/certbot/tasks/letsencrypt.yml new file mode 100644 index 0000000000..99edcab3fb --- /dev/null +++ b/roles/copr/certbot/tasks/letsencrypt.yml @@ -0,0 +1,60 @@ +- name: install certbot package + package: name=certbot state=present + +- name: install certbot config + template: src=certbot.j2 dest=/etc/sysconfig/certbot + mode=0644 + +- name: install certbot deploy script + template: src={{ letsencrypt.predefined_deploy_script }} + dest=/usr/libexec/auto-certbot-deploy + mode=0755 + when: letsencrypt.predefined_deploy_script is defined + +- name: check whether we need to initialize letsencrypt first + stat: path="/etc/letsencrypt/live/{{ item.key }}" + register: le_stat_checks + with_dict: "{{ letsencrypt.certificates }}" + +- name: initialize certbot configuration + shell: | + certbot certonly --standalone \ + -w {{ item.item.value.challenge_dir }} \ + -d {{ item.item.value.domains | join(' -d ') }} \ + --cert-name {{ item.item.key }} \ + -m {{ item.item.value.mail }} \ + --agree-tos \ + -n >> /tmp/call + when: + - not item.stat.exists + with_items: "{{ le_stat_checks.results }}" + + +- name: configure certbot to use webroot next time + ini_file: dest="/etc/letsencrypt/renewal/{{ item.item.key }}.conf" + section=renewalparams + option=authenticator + value=webroot + with_items: "{{ le_stat_checks.results }}" + +- name: configure certbot to use webroot next time + ini_file: dest="/etc/letsencrypt/renewal/{{ item.item.key }}.conf" + section=renewalparams + option=webroot_path + value="{{ item.item.value.challenge_dir }}," + with_items: "{{ le_stat_checks.results }}" + +- name: post init script + shell: | + /usr/libexec/auto-certbot-deploy \ + --init {{ item.item.key }} + when: + - letsencrypt.predefined_deploy_script is defined + - not item.stat.exists + with_items: "{{ le_stat_checks.results }}" + +- name: Automatize cert renewal + service: + name: certbot-renew.timer + state: started + enabled: yes diff --git a/roles/copr/certbot/tasks/main.yml b/roles/copr/certbot/tasks/main.yml new file mode 100644 index 0000000000..dd31a92a39 --- /dev/null +++ b/roles/copr/certbot/tasks/main.yml @@ -0,0 +1,3 @@ +- include_tasks: letsencrypt.yml + when: + - letsencrypt is defined diff --git a/roles/copr/certbot/templates/certbot.j2 b/roles/copr/certbot/templates/certbot.j2 new file mode 100644 index 0000000000..188719016f --- /dev/null +++ b/roles/copr/certbot/templates/certbot.j2 @@ -0,0 +1,13 @@ +# Usually it is enough to do `certbot renew --dry-run` to check that certbot is +# configured correctly (anytime, without rate limits). You can though (up to 5x +# a week) try force renewal. It is both affecting your letsencrypt quota, and +# it requires web server restart. But if you still want to try, please +# uncomment the following config option, and hit `systemctl start +# certbot-renew.service`. WARNING WARNING Don't forget to comment it again! +#CERTBOT_ARGS="--force-renewal" +{% if letsencrypt.predefined_deploy_script %} + +# Execute the script after certificate renewal, usually this is needed to +# restart web server, etc. +DEPLOY_HOOK="--deploy-hook /usr/libexec/auto-certbot-deploy" +{% endif %} diff --git a/roles/copr/certbot/templates/httpd b/roles/copr/certbot/templates/httpd new file mode 100644 index 0000000000..a35d0b3393 --- /dev/null +++ b/roles/copr/certbot/templates/httpd @@ -0,0 +1,5 @@ +#! /bin/sh + +case $1 in --init) exit 0 ;; esac + +systemctl restart httpd diff --git a/roles/copr/certbot/templates/lighttpd b/roles/copr/certbot/templates/lighttpd new file mode 100644 index 0000000000..de53e783d9 --- /dev/null +++ b/roles/copr/certbot/templates/lighttpd @@ -0,0 +1,26 @@ +#! /bin/sh + +set -e + +pem_fixup() +{ + cat "/etc/letsencrypt/live/$1/cert.pem" \ + "/etc/letsencrypt/live/$1/privkey.pem" \ + > "/etc/letsencrypt/live/$1/combined.pem" +} + +case $1 in + --init) + pem_fixup "$2" + exit 0 + ;; +esac + +# Automatically called by certbot. +test -z "$RENEWED_DOMAINS" && echo "you are not certbot" && exit 1 + +for domain in $RENEWED_DOMAINS; do + pem_fixup "$domain" +done + +systemctl restart lighttpd diff --git a/roles/copr/dist_git/tasks/main.yml b/roles/copr/dist_git/tasks/main.yml index 96fa3ab1af..4c5bc7bfcc 100644 --- a/roles/copr/dist_git/tasks/main.yml +++ b/roles/copr/dist_git/tasks/main.yml @@ -56,6 +56,9 @@ notify: - reload httpd +- name: letsencrypt for copr-dist-git + include_role: name=copr/certbot + - name: install copr-dist-git httpd config copy: src="httpd/{{ item }}" dest="/etc/httpd/conf.d/{{ item }}" with_items: @@ -65,34 +68,10 @@ notify: - reload httpd -- name: letsencrypt cert - include_role: name=certbot - - template: src="ssl.conf.j2" dest="/etc/httpd/conf.d/ssl.conf" owner=root group=root mode=0644 notify: - reload httpd -- name: certbot, correct fcontext mapping the web root - sefcontext: - target: '/srv/web/acme-challenge/.well-known(/.*)' - setype: httpd_sys_content_t - state: present - -- name: certbot, restorecon the web root - file: - path: /srv/web/acme-challenge/.well-known - state: directory - setype: httpd_sys_content_t - -- name: Install config for cert renewal - template: src="sysconfig/certbot" dest="/etc/sysconfig/certbot" owner=root group=root - -- name: Automatize cert renewal - service: - name: certbot-renew.timer - state: started - enabled: yes - - name: temporary logrotation fix until copr-dist-git 0.26 is released & deployed copy: src="logrotate.d/copr-dist-git" dest="/etc/logrotate.d/copr-dist-git" diff --git a/roles/copr/dist_git/templates/ssl.conf.j2 b/roles/copr/dist_git/templates/ssl.conf.j2 index 6bc391147a..8691b31bda 100644 --- a/roles/copr/dist_git/templates/ssl.conf.j2 +++ b/roles/copr/dist_git/templates/ssl.conf.j2 @@ -1,9 +1,3 @@ - - RewriteEngine on - RewriteRule ^/\.well-known/(.*) /srv/web/acme-challenge/.well-known/$1 [L] - RewriteRule "^/?(.*)" "https://%{HTTP_HOST}/$1" [L,R=301,NE] - - Listen 443 https SSLPassPhraseDialog exec:/usr/libexec/httpd-ssl-pass-dialog diff --git a/roles/copr/dist_git/templates/sysconfig/certbot b/roles/copr/dist_git/templates/sysconfig/certbot deleted file mode 100644 index 738a6a3355..0000000000 --- a/roles/copr/dist_git/templates/sysconfig/certbot +++ /dev/null @@ -1,54 +0,0 @@ -## NOTE ## -# If a hook is set here then it will be used for all -# certificates and will override any per certificate -# hook configuration in place. - -# Command to be run in a shell before obtaining any -# certificates. Intended primarily for renewal, where it -# can be used to temporarily shut down a webserver that -# might conflict with the standalone plugin. This will -# only be called if a certificate is actually to be -# obtained/renewed. When renewing several certificates -# that have identical pre-hooks, only the first will be -# executed. -# -# An example to stop the MTA before updating certs would be -# PRE_HOOK="--pre-hook 'systemctl stop postfix'" -PRE_HOOK="" - -# Command to be run in a shell after attempting to -# obtain/renew certificates. Can be used to deploy -# renewed certificates, or to restart any servers that -# were stopped by --pre-hook. This is only run if an -# attempt was made to obtain/renew a certificate. If -# multiple renewed certificates have identical post- -# hooks, only one will be run. -# -# An example to restart httpd would be: -# POST_HOOK="--post-hook 'systemctl restart httpd'" -POST_HOOK="" - -# Command to be run in a shell once for each -# successfully renewed certificate. For this command, -# the shell variable $RENEWED_LINEAGE will point to the -# config live subdirectory containing the new certs and -# keys; the shell variable $RENEWED_DOMAINS will contain -# a space-delimited list of renewed cert domains -# -# An example to run a script to alert each cert would be: -# RENEW_HOOK="--renew-hook /usr/local/bin/cert-notifier.sh" -RENEW_HOOK="" - -# Any other misc arguments for the renewal -# See certbot -h renew for full list -# -# An example to force renewal for certificates not due yet -# CERTBOT_ARGS="--force-renewal" -# -# The following command is produced -# certbot renew **CERTBOT_ARGS -{% if devel %} -CERTBOT_ARGS="--webroot -w /srv/web/acme-challenge --cert-name copr-dist-git-dev.fedorainfracloud.org" -{% else %} -CERTBOT_ARGS="--webroot -w /srv/web/acme-challenge --cert-name copr-dist-git.fedorainfracloud.org" -{% endif %} diff --git a/roles/copr/frontend-cloud/tasks/letsencrypt.yml b/roles/copr/frontend-cloud/tasks/letsencrypt.yml deleted file mode 100644 index 3b1c580b64..0000000000 --- a/roles/copr/frontend-cloud/tasks/letsencrypt.yml +++ /dev/null @@ -1,38 +0,0 @@ -- name: letsencrypt cert - include_role: name=certbot - when: devel - tags: - - config - -- name: Check that cert file exists - stat: - path: "/etc/letsencrypt/live/{{ copr_frontend_public_hostname }}/cert.pem" - register: stat_cert - -- name: Should admin run certbot? - fail: - msg: Please see roles/certbot/README step (2) and manually run certbot - when: - - stat_cert.stat.exists == False - - devel - -- name: certbot, correct fcontext mapping the web root - sefcontext: - target: '/srv/web/acme-challenge/.well-known(/.*)' - setype: httpd_sys_content_t - state: present - -- name: certbot, restorecon the web root - file: - path: /srv/web/acme-challenge/.well-known - state: directory - setype: httpd_sys_content_t - -- name: Install config for cert renewal - template: src="sysconfig/certbot" dest="/etc/sysconfig/certbot" owner=root group=root - -- name: Automatize cert renewal - service: - name: certbot-renew.timer - state: started - enabled: yes diff --git a/roles/copr/frontend-cloud/tasks/main.yml b/roles/copr/frontend-cloud/tasks/main.yml index 2a5c985900..71a6b13f77 100644 --- a/roles/copr/frontend-cloud/tasks/main.yml +++ b/roles/copr/frontend-cloud/tasks/main.yml @@ -98,14 +98,16 @@ - nb - kevin +# TODO: move production to lets-encrypt as well - name: install ssl certificates for production import_tasks: "install_certs.yml" when: not devel tags: - config +# development servers use Let's Encrypt - name: install letsencrypt ssl certificates for dev - import_tasks: "letsencrypt.yml" + include_role: name=copr/certbot when: devel tags: - config diff --git a/roles/copr/frontend-cloud/templates/httpd/coprs.conf b/roles/copr/frontend-cloud/templates/httpd/coprs.conf index 05c9e471da..95b4afaa17 100644 --- a/roles/copr/frontend-cloud/templates/httpd/coprs.conf +++ b/roles/copr/frontend-cloud/templates/httpd/coprs.conf @@ -27,11 +27,11 @@ WSGIScriptAlias / /usr/share/copr/coprs_frontend/application Require all granted - {% if devel %} + {% if letsencrypt -%} RewriteEngine on - RewriteRule ^/\.well-known/(.*) /srv/web/acme-challenge/.well-known/$1 [L] + RewriteRule ^/\.well-known/(.*) /var/www/html/.well-known/$1 [L] RewriteRule "^/?(.*)" "https://%{HTTP_HOST}/$1" [L,R=301,NE] - {% endif %} + {%- endif %}