From 0d83dd3ea0778b511fed9625df7ffb226bcfc002 Mon Sep 17 00:00:00 2001 From: Ian Wienand Date: Mon, 4 Jul 2022 15:36:31 +1000 Subject: [PATCH] letsencrypt: selfsigned testing certs - use common CA, setup SAN Some of our testing makes use of secure communication between testing nodes; e.g. testing a load-balancer pass-through. Other parts "loop-back" but require flags like "curl --insecure" because the self-signed certificates aren't trusted. To make testing more realistic, create a CA that is distributed and trusted by all testing nodes early in the Zuul playbook. This then allows us to sign local certificates created by the letsencrypt playbooks with this trusted CA and have realistic peer-to-peer secure communications. The other thing this does is reworks the letsencrypt self-signed cert path to correctly setup SAN records for the host. This also improves the "realism" of our testing environment. This is so realistic that it requires fixing the gitea playbook :). The Apache service proxying gitea currently has to override in testing to "localhost" because that is all the old certificate covered; we can now just proxy to the hostname directly for testing and production. Change-Id: I3d49a7b683462a076263127018ec6a0f16735c94 --- playbooks/roles/gitea/README.rst | 5 -- playbooks/roles/gitea/defaults/main.yaml | 1 - .../roles/gitea/templates/gitea.vhost.j2 | 4 +- .../files/driver.sh | 65 +++++++++++++++---- playbooks/zuul/run-base.yaml | 63 ++++++++++++++++++ .../zuul/templates/group_vars/gitea.yaml.j2 | 1 - 6 files changed, 116 insertions(+), 23 deletions(-) diff --git a/playbooks/roles/gitea/README.rst b/playbooks/roles/gitea/README.rst index f51ee4428a..1e4c3ecb01 100644 --- a/playbooks/roles/gitea/README.rst +++ b/playbooks/roles/gitea/README.rst @@ -2,8 +2,3 @@ Install, configure, and run Gitea. **Role Variables** -.. zuul:rolevar:: gitea_reverse_proxy_hostname - :default: inventory_hostname - - The name of the hostname to reverse proxy to. Only necessary for - testing where we do not have a certificate for the hostname. diff --git a/playbooks/roles/gitea/defaults/main.yaml b/playbooks/roles/gitea/defaults/main.yaml index 68c9eb44f0..ae0017d80d 100644 --- a/playbooks/roles/gitea/defaults/main.yaml +++ b/playbooks/roles/gitea/defaults/main.yaml @@ -1,2 +1 @@ gitea_no_log: true -gitea_reverse_proxy_hostname: '{{ inventory_hostname }}' diff --git a/playbooks/roles/gitea/templates/gitea.vhost.j2 b/playbooks/roles/gitea/templates/gitea.vhost.j2 index 589b55e5d1..ca71238fb1 100644 --- a/playbooks/roles/gitea/templates/gitea.vhost.j2 +++ b/playbooks/roles/gitea/templates/gitea.vhost.j2 @@ -38,8 +38,8 @@ Listen 3081 Use UserAgentFilter ProxyPass /.well-known/ ! - ProxyPass / https://{{ gitea_reverse_proxy_hostname }}:3000/ retry=0 - ProxyPassReverse / https://{{ gitea_reverse_proxy_hostname }}:3000/ + ProxyPass / https://{{ inventory_hostname }}:3000/ retry=0 + ProxyPassReverse / https://{{ inventory_hostname }}:3000/ diff --git a/playbooks/roles/letsencrypt-acme-sh-install/files/driver.sh b/playbooks/roles/letsencrypt-acme-sh-install/files/driver.sh index e8bf5a094e..1322cc0b7f 100644 --- a/playbooks/roles/letsencrypt-acme-sh-install/files/driver.sh +++ b/playbooks/roles/letsencrypt-acme-sh-install/files/driver.sh @@ -2,6 +2,8 @@ ACME_SH=${ACME_SH:-/opt/acme.sh/acme.sh} CERT_HOME=${CERT_HOME:-/etc/letsencrypt-certs} +# Common CA setup by Zuul test infrastructure +OPENDEV_CA_HOME=${OPENDEV_CA_HOME:-/etc/opendev-ca} CHALLENGE_ALIAS_DOMAIN=${CHALLENGE_ALIAS_DOMAIN:-acme.opendev.org.} # Set to !0 to use letsencrypt staging rather than production requests LETSENCRYPT_STAGING=${LETSENCRYPT_STAGING:-0} @@ -94,8 +96,6 @@ elif [[ ${1} == "selfsign" ]]; then # For testing, simulate the key generation shift; for arg in "$@"; do - # TODO(ianw): Set SAN names from the other "-d" arguments?; - # it's a pita to parse. { read -r -a domain_array <<< "$arg" domain=${domain_array[1]} @@ -104,19 +104,56 @@ elif [[ ${1} == "selfsign" ]]; then echo "Creating certs in ${CERT_HOME}/${domain}" # Create key for domain openssl genrsa -out ${domain}.key 2048 - # openssl makes this 0600; match the permissions acme.sh - # makes it with for general sanity + # openssl makes this 0600; match the permissions in acme.sh chmod 0640 ${domain}.key - # Generate a fake CA key - openssl genrsa -out ca.key 2048 - # Create fake CA root certificate - openssl req -x509 -new -nodes -key ca.key -sha256 -days 1024 -subj "/C=US/ST=CA/O=opendev" -out ca.cer - # Create localhost certificate signing request - openssl req -sha256 -new -key ${domain}.key -out ${domain}.csr -subj '/CN=localhost' - # Create localhost certificate signed by fake CA - openssl x509 -req -CA ca.cer -CAkey ca.key -CAcreateserial \ - -sha256 -days 365 -in ${domain}.csr -out ${domain}.cer - cp ${domain}.cer fullchain.cer + # Create the certificate signing request + openssl req -new -sha256 \ + -key ${domain}.key \ + -subj "/C=US/ST=CA/O=OpenDev Infra/CN=${domain}" \ + -out ${domain}.csr + + # The argument is "-d domain -d alias -d alias" Thus when + # reading, odd numbered elements > 1 are the SAN names. + # Always add the first (which must exist) + len=${#domain_array[@]} + san="DNS:${domain}" + if [[ ${len} -gt 2 ]]; then + for (( i=3; i < ${len}; i=i+2 )); do + echo "Adding SAN : ${domain_array[$i]}" + san="${san},DNS:${domain_array[$i]}" + done + fi + + # Issue the certificate signed by the OpenDev CA that Zuul + # has pre-installed. + # NOTE(ianw) : + # * CA has to be ".crt" for update-ca-certificates but + # we've used ".cer" for certificates everywhere else + # just to make things confusing. + # * I've seen some guides add the SAN names to the CSR + # but I found x509 here requires it explicitly anyway + # to actually get it in the resulting certificate? + # Seems to be multiple ways to skin the cat with all + # these arguments and quite some variations across + # openssl versions. + openssl x509 -req -days 30 -sha256 \ + -in ${domain}.csr \ + -CA ${OPENDEV_CA_HOME}/ca.crt -CAkey ${OPENDEV_CA_HOME}/ca.key \ + -CAcreateserial \ + -out ${domain}.cer \ + -extensions SAN -extfile <(printf "[SAN]\nsubjectAltName=${san}") + + # Copy CA certificate for apache SSLCertificateChainFile + cp ${OPENDEV_CA_HOME}/ca.crt ca.cer + chown root:letsencrypt ca.cer + chmod 0640 ca.cer + + # Save the fullchain (some apps like gitea require) + cat ${domain}.cer > fullchain.cer + cat ca.cer >> fullchain.cer + chown root:letsencyrpt fullchain.cer + chmod 0640 fullchain.cer + } 2>&1 | tee -a ${LOG_FILE} done else diff --git a/playbooks/zuul/run-base.yaml b/playbooks/zuul/run-base.yaml index 41431bc010..23a619593e 100644 --- a/playbooks/zuul/run-base.yaml +++ b/playbooks/zuul/run-base.yaml @@ -4,6 +4,69 @@ ansible_cron_disable_job: true cloud_launcher_disable_job: true +# setup opendev CA +- hosts: bridge.openstack.org + become: true + tasks: + - name: Make temporary dir for CA generation + tempfile: + state: directory + register: _ca_tempdir + + - name: Create CA PEM/crt + shell: | + set -x + # Generate a CA key + openssl genrsa -out ca.key 2048 + # Create fake CA root certificate + openssl req -x509 -new -nodes -key ca.key -sha256 -days 30 -subj "/C=US/ST=CA/O=OpenDev Infra" -out ca.crt + args: + chdir: '{{ _ca_tempdir.path }}' + executable: /bin/bash + + - name: Save key + slurp: + src: '{{ _ca_tempdir.path }}/ca.key' + register: _opendev_ca_key + + - name: Save certificate + slurp: + src: '{{ _ca_tempdir.path }}//ca.crt' + register: _opendev_ca_certificate + + - name: Cleanup tempdir + file: + path: '{{ _ca_tempdir.path }}' + state: absent + when: _ca_tempdir.path is defined + +- hosts: all + become: true + tasks: + - name: Make CA directory + file: + path: '/etc/opendev-ca' + state: directory + owner: root + group: root + mode: 0600 + + - name: Import files + shell: 'echo "{{ item.content }}" | base64 -d > {{ item.file }}' + args: + creates: '{{ item.file }}' + loop: + - file: '/etc/opendev-ca/ca.key' + content: '{{ hostvars["bridge.openstack.org"]["_opendev_ca_key"]["content"] }}' + - file: '/etc/opendev-ca/ca.crt' + content: '{{ hostvars["bridge.openstack.org"]["_opendev_ca_certificate"]["content"] }}' + + - name: Install and trust certificate + shell: + cmd: | + cp /etc/opendev-ca/ca.crt /usr/local/share/ca-certificates/opendev-infra-ca.crt + update-ca-certificates + - hosts: bridge.openstack.org become: true tasks: diff --git a/playbooks/zuul/templates/group_vars/gitea.yaml.j2 b/playbooks/zuul/templates/group_vars/gitea.yaml.j2 index 63d9b59402..d0dfb8a845 100644 --- a/playbooks/zuul/templates/group_vars/gitea.yaml.j2 +++ b/playbooks/zuul/templates/group_vars/gitea.yaml.j2 @@ -7,4 +7,3 @@ gitea_db_password: 5bfuOBKtltff0XZX gitea_root_password: BUbBcpToMwR05ZCB gitea_no_log: false gitea_gerrit_password: yVpMWIUIvT7f6NwA -gitea_reverse_proxy_hostname: localhost