From 1bb2d242b60eaa745b8e143039e6d36ef25ad1f1 Mon Sep 17 00:00:00 2001 From: Mike DePaulo Date: Mon, 10 Aug 2020 15:53:05 -0400 Subject: [PATCH] As an installer user, I can configure Pulp to run with TLS enabled to install/renew using letsencrypt certificates fixes: #6846 https://pulp.plan.io/issues/6846 --- CHANGES/6846.feature | 1 + molecule/release-static/molecule.yml | 4 -- .../example-use-letsencrypt/group_vars/all | 23 +++++++++++ .../example-use-letsencrypt/playbook.yml | 27 +++++++++++++ roles/pulp_common/tasks/install.yml | 7 ++++ roles/pulp_webserver/README.md | 15 +++++-- roles/pulp_webserver/defaults/main.yml | 4 ++ .../tasks/import_certificates.yml | 6 ++- roles/pulp_webserver/tasks/main.yml | 39 ++++++++++++------- roles/pulp_webserver/templates/nginx.conf.j2 | 15 ++++++- .../templates/pulp-vhost.conf.j2 | 2 +- 11 files changed, 117 insertions(+), 26 deletions(-) create mode 100644 CHANGES/6846.feature create mode 100644 playbooks/example-use-letsencrypt/group_vars/all create mode 100644 playbooks/example-use-letsencrypt/playbook.yml diff --git a/CHANGES/6846.feature b/CHANGES/6846.feature new file mode 100644 index 000000000..d42da892e --- /dev/null +++ b/CHANGES/6846.feature @@ -0,0 +1 @@ +Support Let's Encrypt and other ACME protocol CAs. Includes sharing out the `pulp_webserver_static_dir`/.well-known directory for HTTP-01 verification. diff --git a/molecule/release-static/molecule.yml b/molecule/release-static/molecule.yml index 12ecbde5b..7668bb1fa 100644 --- a/molecule/release-static/molecule.yml +++ b/molecule/release-static/molecule.yml @@ -32,10 +32,6 @@ platforms: name: debian-10 image: pulp/molecule_debian10 command: /sbin/init - - <<: *platform_base - name: fedora-31 - image: pulp/molecule_fedora31 - command: /usr/sbin/init provisioner: name: ansible config_options: diff --git a/playbooks/example-use-letsencrypt/group_vars/all b/playbooks/example-use-letsencrypt/group_vars/all new file mode 100644 index 000000000..96d6ff9a6 --- /dev/null +++ b/playbooks/example-use-letsencrypt/group_vars/all @@ -0,0 +1,23 @@ +--- +pulp_webserver_httpd_servername: "{{ inventory_hostname }}" +lets_encrypt_hostname: "{{ inventory_hostname }}" +lets_encrypt_directories_certs: "/etc/letsencrypt" +lets_encrypt_directories_data: "/var/lib/pulp/pulpcore_static" + +pulp_default_admin_password: password +pulp_install_plugins: + # galaxy-ng: {} + # pulp-ansible: {} + # pulp-certguard: {} + # pulp-container: {} + # pulp-cookbook: {} + # pulp-deb: {} + pulp-file: {} + # pulp-gem: {} + # pulp-maven: {} + # pulp-npm: {} + # pulp-python: {} + # pulp-rpm: {} +pulp_settings: + secret_key: secret + content_origin: "https://{{ inventory_hostname }}" diff --git a/playbooks/example-use-letsencrypt/playbook.yml b/playbooks/example-use-letsencrypt/playbook.yml new file mode 100644 index 000000000..8ba2ec7be --- /dev/null +++ b/playbooks/example-use-letsencrypt/playbook.yml @@ -0,0 +1,27 @@ +--- +- hosts: all + pre_tasks: + # The version string below is the highest of all those in roles' metadata: + # "min_ansible_version". It needs to be kept manually up-to-date. + - name: Verify Ansible meets min required version + assert: + that: "ansible_version.full is version_compare('2.8', '>=')" + msg: > + "You must update Ansible to at least 2.8 to use this version of Pulp 3 Installer." + roles: + # Includes running pulp_webserver. letsencrypt depends on a webserver + # that can host the .well-known directory. + - pulp_all_services + - role: lexa-uw.letsencrypt + become: true + tasks: + # Must be run via a task so that it can be run more than once. + - name: Run pulp_webserver a 2nd time to import the key + include_role: + name: pulp_webserver + vars: + pulp_webserver_tls_key: "/etc/letsencrypt/private_key.pem" + pulp_webserver_tls_cert: "/etc/letsencrypt/fullchain.pem" + pulp_webserver_tls_files_remote: true + environment: + DJANGO_SETTINGS_MODULE: pulpcore.app.settings diff --git a/roles/pulp_common/tasks/install.yml b/roles/pulp_common/tasks/install.yml index 33902e525..fc6464446 100644 --- a/roles/pulp_common/tasks/install.yml +++ b/roles/pulp_common/tasks/install.yml @@ -68,6 +68,13 @@ - name: Reset ssh conn to allow user changes to affect when ssh user and pulp user are the same meta: reset_connection + # Needed for ngingx/apache to serve content at pulp_webserver_static_dir, + # which is the subdir pulpcore_static by default. + - name: Make {{ pulp_user_home }} world executable + file: + path: '{{ pulp_user_home }}' + mode: 'o+x' + - name: Create cache dir for Pulp file: path: '{{ pulp_cache_dir }}' diff --git a/roles/pulp_webserver/README.md b/roles/pulp_webserver/README.md index db23ba58a..056d965be 100644 --- a/roles/pulp_webserver/README.md +++ b/roles/pulp_webserver/README.md @@ -20,12 +20,19 @@ Role Variables * `pulp_webserver_disable_https`: Whether or not HTTPS should be disabled. Defaults to `false`. * `pulp_webserver_tls_folder`: Path where to generate or drop the certificates. Defaults to `pulp_config_dir`. +* `pulp_webserver_tls_cert`: Relative or absolute path to the TLS (SSL) certificate + one wants to import. +* `pulp_webserver_tls_key`: Relative or absolute path to the TLS (SSL) key + one wants to import. +* `pulp_webserver_tls_files_remote`: Whether or not `pulp_webserver_tls_cert` & + `pulp_webserver_tls_key` is on the webserver (`true`) or on the ansible management + node (`false`). Defaults to `false`. * `pulp_webserver_httpd_servername`: Servername to use when deploying httpd. Defaults to `ansible_fqdn`. -* `pulp_webserver_ssl_cert`: Relative or absolute path to the TLS certificate one wants to - import. -* `pulp_webserver_ssl_key`: Relative or absolute path to the TLS key one wants to - import. +- `pulp_webserver_static_dir` absolute path where to place static files, such as for the .well-known + directory for ACME (letsencrypt) files or SSL certs. This is not to be confused with the Pulp + application's setting `STATIC_ROOT`, which is a function of Pulp itself (not the webserver) and servces + a different set of files. Plugin Webserver Configs ------------------------ diff --git a/roles/pulp_webserver/defaults/main.yml b/roles/pulp_webserver/defaults/main.yml index 490b8e253..3bb1be18c 100644 --- a/roles/pulp_webserver/defaults/main.yml +++ b/roles/pulp_webserver/defaults/main.yml @@ -7,3 +7,7 @@ pulp_configure_firewall: auto pulp_webserver_disable_https: false pulp_webserver_tls_folder: '{{ pulp_config_dir }}' pulp_webserver_httpd_servername: '{{ ansible_fqdn }}' +# The "static" dir is used by Pulp 2, and has conflicting SELinux policies. +# https://pulp.plan.io/issues/5995 +pulp_webserver_static_dir: "{{ pulp_user_home | regex_replace('\\/$', '') }}/pulpcore_static/" +pulp_webserver_tls_files_remote: false diff --git a/roles/pulp_webserver/tasks/import_certificates.yml b/roles/pulp_webserver/tasks/import_certificates.yml index 2cec2823b..1b5a4bc90 100644 --- a/roles/pulp_webserver/tasks/import_certificates.yml +++ b/roles/pulp_webserver/tasks/import_certificates.yml @@ -1,18 +1,20 @@ --- - name: Import specified TLS certificate copy: - src: "{{ pulp_webserver_ssl_cert }}" + src: "{{ pulp_webserver_tls_cert }}" dest: "{{ pulp_webserver_tls_folder }}/pulp_webserver.crt" owner: root group: "{{ pulp_group }}" mode: 0600 + remote_src: "{{ pulp_webserver_tls_files_remote }}" notify: reload {{ pulp_webserver_server }} - name: Import specified TLS private key copy: - src: "{{ pulp_webserver_ssl_key }}" + src: "{{ pulp_webserver_tls_key }}" dest: "{{ pulp_webserver_tls_folder }}/pulp_webserver.key" owner: root group: "{{ pulp_group }}" mode: 0600 + remote_src: "{{ pulp_webserver_tls_files_remote }}" notify: reload {{ pulp_webserver_server }} diff --git a/roles/pulp_webserver/tasks/main.yml b/roles/pulp_webserver/tasks/main.yml index 8d98a5dd6..e4c305377 100644 --- a/roles/pulp_webserver/tasks/main.yml +++ b/roles/pulp_webserver/tasks/main.yml @@ -15,10 +15,10 @@ - name: Ensure that if we have web cert, we also have key fail: - msg: "If you give one of pulp_webserver_ssl_cert and pulp_webserver_ssl_key, you must also give the other." + msg: "If you give one of pulp_webserver_tls_cert and pulp_webserver_tls_key, you must also give the other." when: - - "pulp_webserver_ssl_cert is defined or pulp_webserver_ssl_key is defined" - - "pulp_webserver_ssl_cert is not defined or pulp_webserver_ssl_key is not defined" + - "pulp_webserver_tls_cert is defined or pulp_webserver_tls_key is defined" + - "pulp_webserver_tls_cert is not defined or pulp_webserver_tls_key is not defined" - debug: msg: > @@ -37,17 +37,21 @@ changed_when: false check_mode: false -- import_tasks: generate_tls_certificates.yml - become: true +- include_tasks: generate_tls_certificates.yml + args: + apply: + become: true when: - - pulp_webserver_ssl_cert is undefined - - pulp_webserver_ssl_key is undefined + - pulp_webserver_tls_cert is undefined + - pulp_webserver_tls_key is undefined -- import_tasks: import_certificates.yml - become: true +- include_tasks: import_certificates.yml + args: + apply: + become: true when: - - pulp_webserver_ssl_cert is defined - - pulp_webserver_ssl_key is defined + - pulp_webserver_tls_cert is defined + - pulp_webserver_tls_key is defined - name: Copy custom CA cert copy: @@ -60,8 +64,17 @@ become: true notify: update ca trust -- import_tasks: nginx.yml - when: pulp_webserver_server == 'nginx' +- name: Create ACME dirs + file: + path: "{{ item }}" + state: directory + mode: 0755 + owner: "{{ pulp_user }}" + group: "{{ pulp_group }}" + become: true + with_items: + - "{{ pulp_webserver_static_dir }}" + - "{{ pulp_webserver_static_dir }}/.well-known" - include_tasks: "{{ pulp_webserver_server }}.yml" diff --git a/roles/pulp_webserver/templates/nginx.conf.j2 b/roles/pulp_webserver/templates/nginx.conf.j2 index a2fd11879..0d5c646f3 100644 --- a/roles/pulp_webserver/templates/nginx.conf.j2 +++ b/roles/pulp_webserver/templates/nginx.conf.j2 @@ -49,7 +49,7 @@ http { ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; ssl_prefer_server_ciphers on; - {% if pulp_webserver_ssl_cert is defined %} + {% if pulp_webserver_tls_cert is defined %} # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) add_header Strict-Transport-Security max-age=15768000; {% endif %} @@ -63,6 +63,10 @@ http { # Gunicorn docs suggest this value. keepalive_timeout 5; + # static files that can change dynamically, or are needed for TLS + # purposes are served through the webserver. + root {{ pulp_webserver_static_dir }}; + location /pulp/content/ { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; @@ -103,8 +107,15 @@ http { # redirects, we set the Host: header above already. proxy_redirect off; proxy_pass http://pulp-api; - # static files are served through whitenoise - http://whitenoise.evans.io/en/stable/ + # most pulp static files are served through whitenoise + # http://whitenoise.evans.io/en/stable/ + } + + # ACME http-01 tokens, i.e, for Let's Encrypt + location /.well-known/ { + try_files $uri $uri/ =404; } + } {% if not pulp_webserver_disable_https | bool %} diff --git a/roles/pulp_webserver/templates/pulp-vhost.conf.j2 b/roles/pulp_webserver/templates/pulp-vhost.conf.j2 index f3386908d..62a14cc18 100644 --- a/roles/pulp_webserver/templates/pulp-vhost.conf.j2 +++ b/roles/pulp_webserver/templates/pulp-vhost.conf.j2 @@ -47,7 +47,7 @@ Define pulp-api {{ pulp_api_bind }} SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256 SSLHonorCipherOrder on -{% if pulp_webserver_ssl_cert is defined %} +{% if pulp_webserver_tls_cert is defined %} # HSTS (15768000 seconds = 6 months) Header always set Strict-Transport-Security "max-age=15768000;; includeSubDomains; preload" {% endif %}