diff --git a/roles/haproxy/defaults/main.yml b/roles/haproxy/defaults/main.yml index aa70d5570..2dea86067 100644 --- a/roles/haproxy/defaults/main.yml +++ b/roles/haproxy/defaults/main.yml @@ -24,3 +24,5 @@ haproxy_hsts_value: "max-age=34214400" # If you have a staging server you want to be accessed from certain ips, and them to this list haproxy_stagingips: - '' + +haproxy_acme: true diff --git a/roles/haproxy/files/ffdhe3072.pem b/roles/haproxy/files/ffdhe3072.pem deleted file mode 100644 index fb31ccda5..000000000 --- a/roles/haproxy/files/ffdhe3072.pem +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN DH PARAMETERS----- -MIIBiAKCAYEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz -+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a -87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7 -YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi -7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD -ssbzSibBsu/6iGtCOGEfz9zeNVs7ZRkDW7w09N75nAI4YbRvydbmyQd62R0mkff3 -7lmMsPrBhtkcrv4TCYUTknC0EwyTvEN5RPT9RFLi103TZPLiHnH1S/9croKrnJ32 -nuhtK8UiNjoNq8Uhl5sN6todv5pC1cRITgq80Gv6U93vPBsg7j/VnXwl5B0rZsYu -N///////////AgEC ------END DH PARAMETERS----- diff --git a/roles/haproxy/files/hapos-upd b/roles/haproxy/files/hapos-upd deleted file mode 100755 index 3d29975aa..000000000 --- a/roles/haproxy/files/hapos-upd +++ /dev/null @@ -1,578 +0,0 @@ -#!/bin/bash - -# HAProxy OCSP Stapling Updater -# Copyright (c) 2015 Pier Carlo Chiodi - http://www.pierky.com -# -# https://github.com/pierky/haproxy-ocsp-stapling-updater - -set -o nounset - -VERSION="0.4.1-pre1" - -PROGNAME="hapos-upd" - -if [ -z ${OPENSSL_BIN+x} ]; then - OPENSSL_BIN="openssl" -fi - -SOCAT_BIN="socat" - -CERT="" -VAFILE="" -HAPROXY_ADMIN_SOCKET_DEFAULT="/run/haproxy/admin.sock" -HAPROXY_ADMIN_SOCKET="$HAPROXY_ADMIN_SOCKET_DEFAULT" -GOOD_ONLY=0 -SYSLOG_PRIO="" -DEBUG=0 -KEEP_TEMP=0 -OCSP_URL="" -OCSP_HOST="" -VERIFY=1 -TMP="" -SKIP_UPDATE=0 -PARTIAL_CHAIN="" - -function Quit() { - if [ $KEEP_TEMP -eq 0 ]; then - if [ -n "$TMP" ]; then - rm -r $TMP &>/dev/null - fi - fi - exit $1 -} - -function LogError() { - MSG="$1" - - if [ -z "$SYSLOG_PRIO" ]; then - echo "$MSG" >&2 - else - logger -p "$SYSLOG_PRIO" -s -- "$PROGNAME - $MSG" - fi - - echo "$MSG" >>$TMP/log -} - -function Error() { - if [ $1 -eq 9 ]; then - MSG="Error: $2" - else - MSG="Error processing '$CERT': $2" - fi - - LogError "$MSG" - - if [ $1 -eq 9 ]; then - echo "Run $PROGNAME -h for help" >&2 - fi - - Quit $1 -} - -function Debug() { - if [ $DEBUG -eq 1 ]; then - echo "$1" - fi - echo "$1" >>$TMP/log -} - -function Trap() { - Debug "Aborting" - Quit 9 -} - -function Usage() { - echo " -HAProxy OCSP Stapling Updater - $VERSION -Copyright (c) 2015 Pier Carlo Chiodi - http://www.pierky.com - -https://github.com/pierky/haproxy-ocsp-stapling-updater - -Usage: - $PROGNAME [options] --cert crt_full_path - -This script extracts and queries the OCSP server present in a -certificate to obtain its revocation status, then updates HAProxy by -writing the '.issuer' and the '.ocsp' files and by sending it the -'set ssl ocsp-response' command through the local UNIX admin socket. - -The crt_full_path argument is the full path to the certificate bundle -used in haproxy 'crt' setting. End-entity (EE) certificate plus any -intermediate CA certificates must be concatenated there. -An OCSP query is sent to the OCSP server given on the command line -(--ocsp-url and --ocsp-host argument); if these arguments are missing, -URL and Host header values are automatically extracted from the -certificate. -If the '.issuer' file already exists it's used to build the OCSP -request, otherwise the chain is extracted from crt_full_path and used -to identify the issuer. -Finally, it writes the related '.issuer' and .'ocsp' files and updates -haproxy, using 'socat' and the local UNIX socket (--socket argument, -default $HAPROXY_ADMIN_SOCKET_DEFAULT). - -Exit codes: - 0 OK - 1 openssl certificates handling error - 2 OCSP server URL not found - 3 string parsing / PEM manipulation error - 4 OCSP error - 5 haproxy management error - 9 program error (wrong arguments, missing dependencies) - -Options: - - -d, --debug : don't do anything, print debug messages only. - - --keep-temp : keep temporary directory after exiting (for - debug purposes). - - -g, --good-only : do not update haproxy if OCSP response - certificate status value is not 'good'. - - -l, --syslog priority : log errors to syslog system log module. - The priority may be specified numerically - or as a facility.level pair (e.g. - local7.error). - - --ocsp-url url : OCSP server URL; use this instead of the - one in the EE certificate. - - --ocsp-host host : OCSP server hostname to be used in the - 'Host:' header; use this instead of the one - extracted from the OCSP server URL. - - --partial-chain : Allow partial certificate chain if at least one certificate - is in trusted store. Useful when validating an intermediate - certificate without the root CA. - - -s, --socket file : haproxy admin socket. If omitted, - $HAPROXY_ADMIN_SOCKET_DEFAULT is used by default. - This script is distributed with only one - method to update haproxy: using 'socat' - with a local admin-level UNIX socket. - Feel free to implement other mechanisms as - needed! The right section in the code is - \"UPDATE HAPROXY\", at the end of the script. - - -v, --VAfile file : same as the openssl ocsp -VAfile option - with 'file' as argument. For more details: - 'man ocsp'. - If file = \"-\" then the chain extracted - from the certificate's bundle (or .issuer - file) is used (useful for OCSP responses - that don't include the signer certificate). - - --noverify : Do not verify OCSP response. - - -S, --skip-update : Do not notify haproxy of the new OCSP response. - - -h, --help : this help." -} - -trap Trap INT TERM - -TMP="`mktemp -d -q -t $PROGNAME.XXXXXXXXXX`" - -# COMMAND LINE PROCESSING -# ---------------------------------- - -while [[ $# > 0 ]] -do - - case "$1" in - -h|--help) - Usage - Quit 0 - ;; - - -d|--debug) - DEBUG=1 - ;; - - --keep-temp) - KEEP_TEMP=1 - ;; - - -g|--good-only) - GOOD_ONLY=1 - ;; - - --noverify) - VERIFY=0 - ;; - - --partial-chain) - PARTIAL_CHAIN="-partial_chain" - ;; - - -l|--syslog) - if [ $# -le 1 ]; then - Error 9 "mandatory value is missing for $1 argument" - fi - SYSLOG_PRIO="$2" - shift - ;; - - --ocsp-url) - if [ $# -le 1 ]; then - Error 9 "mandatory value is missing for $1 argument" - fi - OCSP_URL="$2" - shift - ;; - - --ocsp-host) - if [ $# -le 1 ]; then - Error 9 "mandatory value is missing for $1 argument" - fi - OCSP_HOST="$2" - shift - ;; - - -c|--cert) - if [ $# -le 1 ]; then - Error 9 "mandatory value is missing for $1 argument" - fi - CERT="$2" - shift - ;; - - -v|--VAfile) - if [ $# -le 1 ]; then - Error 9 "mandatory value is missing for $1 argument" - fi - VAFILE="$2" - if [ "$VAFILE" == "-" ]; then - VAFILE="$TMP/chain.pem" - else - if [ ! -e "$VAFILE" ]; then - Error 9 "VAfile does not exists: $VAFILE" - fi - fi - shift - ;; - - -s|--socket) - if [ $# -le 1 ]; then - Error 9 "mandatory value is missing for $1 argument" - fi - HAPROXY_ADMIN_SOCKET="$2" - shift - ;; - - -S|--skip-update) - SKIP_UPDATE=1 - ;; - - *) - Error 9 "unknown option: $1" - esac - - shift -done - -Debug "Temporary directory: $TMP" - -$OPENSSL_BIN version | grep OpenSSL &>>$TMP/log - -if [ $? -ne 0 ]; then - Error 9 "openssl binary not found; adjust OPENSSL_BIN variable in the script" -fi - -$SOCAT_BIN -V | grep socat &>>$TMP/log - -if [ $? -ne 0 ]; then - Error 9 "socat binary not found; adjust SOCAT_BIN variable in the script" -fi - -if [ -z "$CERT" ]; then - Error 9 "certificate not provided (--cert argument)" -fi - -# CURRENT RESPONSE EXPIRED? -# ---------------------------------- - -ISNEW=1 -if [ -e $CERT.ocsp ]; then - ISNEW=0 - Debug "An OCSP response already exists: checking its expiration." - - $OPENSSL_BIN ocsp -respin $CERT.ocsp -text -noverify | \ - grep "Next Update:" &>>$TMP/log - - if [ $? -eq 0 ]; then - CURR_EXP=`$OPENSSL_BIN ocsp -respin $CERT.ocsp -text -noverify | grep "Next Update:" | cut -d ':' -f 2-` - CURR_EXP_EPOCH=`date --date="$CURR_EXP" +%s` - - if [ $? -ne 0 ]; then - Error 3 "can't parse Next Update from current OCSP response" - fi - - if [ $CURR_EXP_EPOCH -lt `date +%s` ]; then - Debug "Current OCSP response expiration: $CURR_EXP - expired" - LogError "current OCSP response is expired: please consider running this script more frequently" - else - Debug "Current OCSP response expiration: $CURR_EXP - NOT expired" - fi - fi -fi - -# EXTRACT EE CERTIFICATE INFO -# ---------------------------------- - -# extract EE certificate -$OPENSSL_BIN x509 -in $CERT -outform PEM -out $TMP/ee.pem &>>$TMP/log - -if [ $? -ne 0 ]; then - Error 1 "can't extract EE certificate from $CERT" -fi - -# get OCSP server URL -if [ -z "$OCSP_URL" ]; then - OCSP_URL="`$OPENSSL_BIN x509 -in $TMP/ee.pem -ocsp_uri -noout`" - - if [ $? -ne 0 ]; then - Error 1 "can't obtain OCSP server URL from $CERT" - fi - - if [ -z "$OCSP_URL" ]; then - Error 2 "OCSP server URL not found in the EE certificate" - fi - - Debug "OCSP server URL found: $OCSP_URL" -else - Debug "Using OCSP server URL from command line: $OCSP_URL" -fi - -# check OCSP server URL format (http:// or https://) -echo "$OCSP_URL" | egrep -i "(http://|https://)" &>/dev/null - -if [ $? -ne 0 ]; then - Error 3 "OCSP server URL not in http[s]:// format" -fi - -# get OCSP server URL host name -if [ -z "$OCSP_HOST" ]; then - OCSP_HOST="`echo "$OCSP_URL" | egrep -i "(http://|https://)" | cut -d'/' -f 3`" - - if [ $? -ne 0 -o -z "$OCSP_HOST" ]; then - Error 3 "can't extract hostname from OCSP server URL $OCSP_URL" - fi - - Debug "OCSP server hostname: $OCSP_HOST" -else - Debug "Using OCSP server hostname from command line: $OCSP_HOST" -fi - -# EXTRACT CHAIN INFO -# ---------------------------------- - -if [ -e $CERT.issuer ]; then - Debug "Using existing chain ($CERT.issuer)" - - # copy .issuer file to temporary chain.pem - cp $CERT.issuer $TMP/chain.pem &>>$TMP/log - - if [ $? -ne 0 ]; then - Error 3 "can't copy current chain from $CERT.issuer" - fi -else - Debug "Extracting chain from certificates bundle" - - # get EE certificate's fingerprint - FP_EE="`$OPENSSL_BIN x509 -fingerprint -noout -in $TMP/ee.pem`" - - if [ $? -ne 0 -o -z "$FP_EE" ]; then - Error 1 "can't obtain EE certificate's fingerprint" - fi - - Debug "EE certificate's fingerprint: $FP_EE" - - # get BEGIN CERTIFICATE and END CERTIFICATE separators - PEM_BEGIN_CERT="`head $TMP/ee.pem -n 1`" - PEM_END_CERT="`tail $TMP/ee.pem -n 1`" - - # get number of certificates in the bundle file - NUM_OF_CERTS=`cat $CERT | grep -e "$PEM_BEGIN_CERT" | wc -l` - - if [ $NUM_OF_CERTS -le 1 ]; then - Error 3 "can't obtain the number of certificates in the chain" - fi - - Debug "$NUM_OF_CERTS certificates found in the bundle" - - # save each certificate in the bundle into $TMP/chain-X.pem - cat $CERT | \ - sed -n -e "/$PEM_BEGIN_CERT/,/$PEM_END_CERT/p" | \ - awk "/$PEM_BEGIN_CERT/{x=\"$TMP/chain-\" ++i \".pem\";}{print > x;}" &>>$TMP/log - - if [ $? -ne 0 ]; then - Error 3 "can't extract certificates from bundle" - fi - - # for each certificate that is extracted from the bundle check if - # it's the EE certificate, otherwise uses it to build the chain file - for c in `seq 1 $NUM_OF_CERTS`; - do - # check fingerprint of current and EE certificates - FP="`$OPENSSL_BIN x509 -fingerprint -noout -in $TMP/chain-$c.pem`" - if [ $? -ne 0 -o -z "$FP" ]; then - Error 1 "can't obtain the fingerprint of the certificate n. $c in the bundle" - else - if [ ! "$FP" == "$FP_EE" ]; then - Debug "Bundle certificate n. $c fingerprint: $FP - it's part of the chain" - - # current certificate is not the same as the EE; append to the chain - cat $TMP/chain-$c.pem >> $TMP/chain.pem - else - Debug "Bundle certificate n. $c fingerprint: $FP - EE certificate" - fi - fi - done -fi - -# check if the EE certificate validates against the chain -$OPENSSL_BIN verify $PARTIAL_CHAIN -CAfile $TMP/chain.pem $TMP/ee.pem &>>$TMP/log - -if [ $? -ne 0 ]; then - if [ -e $CERT.issuer ]; then - Error 1 "can't validate the EE certificate against the existing chain; if it has been changed recently consider removing the current $CERT.issuer file and let this script to figure out a new one" - else - Error 1 "can't validate the EE certificate against the extracted chain" - fi -fi - -# OCSP -# ---------------------------------- - -# query the OCSP server and save its response - -OPENSSL_VERSION=`$OPENSSL_BIN version | sed -rn 's/^OpenSSL\s([0-9]\.[0-9]).*/\1/p'` - -case $OPENSSL_VERSION in - '0.9') - $OPENSSL_BIN ocsp $PARTIAL_CHAIN -issuer $TMP/chain.pem -cert $TMP/ee.pem \ - -respout $TMP/ocsp.der -noverify \ - -no_nonce -url $OCSP_URL -header "Host=$OCSP_HOST" &>>$TMP/log - ;; - '1.0') - $OPENSSL_BIN ocsp $PARTIAL_CHAIN -issuer $TMP/chain.pem -cert $TMP/ee.pem \ - -respout $TMP/ocsp.der -noverify \ - -no_nonce -url $OCSP_URL -header "Host" "$OCSP_HOST" &>>$TMP/log - ;; - *) - $OPENSSL_BIN ocsp $PARTIAL_CHAIN -issuer $TMP/chain.pem -cert $TMP/ee.pem \ - -respout $TMP/ocsp.der -noverify \ - -no_nonce -url $OCSP_URL &>>$TMP/log - ;; -esac - -if [ $? -ne 0 ]; then - Error 1 "can't receive the OCSP server response" -fi - -# process the OCSP response -VERIFYOPT="" -if [ $VERIFY -eq 0 ]; then - VERIFYOPT="-noverify" -fi -if [ -z "$VAFILE" ]; then - $OPENSSL_BIN ocsp $PARTIAL_CHAIN $VERIFYOPT -issuer $TMP/chain.pem -cert $TMP/ee.pem \ - -respin $TMP/ocsp.der -no_nonce -CAfile $TMP/chain.pem \ - -out $TMP/ocsp.txt &>>$TMP/ocsp-verify.txt -else - $OPENSSL_BIN ocsp $PARTIAL_CHAIN $VERIFYOPT -issuer $TMP/chain.pem -cert $TMP/ee.pem \ - -respin $TMP/ocsp.der -no_nonce -CAfile $TMP/chain.pem \ - -VAfile $VAFILE \ - -out $TMP/ocsp.txt &>>$TMP/ocsp-verify.txt -fi - -if [ $? -ne 0 ]; then - Error 1 "can't receive OCSP response" -fi - -if [ $VERIFY -eq 1 ]; then - Debug "OCSP response verification results: `cat $TMP/ocsp-verify.txt`" - - cat $TMP/ocsp-verify.txt | grep "Response verify OK" &>>$TMP/log - - if [ $? -ne 0 ]; then - grep "signer certificate not found" $TMP/ocsp-verify.txt &>/dev/null - - if [ $? -eq 0 ]; then - Error 4 "OCSP response verification failure: signer certificate not found; try with '--VAfile -' or '--VAfile OCSP-response-signing-certificate-file' arguments" - else - Error 4 "OCSP response verification failure." - fi - fi -fi - -Debug "OCSP response: `cat $TMP/ocsp.txt`" - -if [ $GOOD_ONLY -eq 1 ]; then - cat $TMP/ocsp.txt | head -n 1 | grep ": good" &>>$TMP/log - - if [ $? -ne 0 ]; then - Error 4 "OCSP response, certificate status not good" - fi -fi - -# UPDATE HAPROXY -# ---------------------------------- - -# Status: -# - $TMP/ocsp.der contains the OCSP response, DER format -# - $TMP/ocsp.txt contains the textual OCSP response as produced -# by openssl -# - the OCSP response has been verified against the chain or -# the --VAfile - -if [ $DEBUG -eq 0 ]; then - # update .ocsp and .issuer files - - cp $TMP/ocsp.der $CERT.ocsp &>>$TMP/log - - if [ $? -ne 0 ]; then - Error 5 "can't update $CERT.ocsp file" - fi - - if [ ! -e $CERT.issuer ]; then - cp $TMP/chain.pem $CERT.issuer &>>$TMP/log - - if [ $? -ne 0 ]; then - Error 5 "can't update $CERT.issuer file" - fi - fi - - if [ $SKIP_UPDATE -eq 0 ]; then - if [ $ISNEW -eq 1 ]; then - # no .ocsp file found, maybe it's an initial run - Debug "Reloading haproxy." - - service haproxy reload - - if [ $? -ne 0 ]; then - Error 5 "can't reload haproxy with 'service haproxy reload'" - fi - else - # update haproxy via local UNIX socket - Debug "Updating haproxy." - - echo "set ssl ocsp-response `base64 -w 0 $TMP/ocsp.der`" | $SOCAT_BIN stdio $HAPROXY_ADMIN_SOCKET &>>$TMP/log - - if [ $? -ne 0 ]; then - Error 5 "can't update haproxy ssl ocsp-response using $HAPROXY_ADMIN_SOCKET socket" - fi - fi - else - Debug "Not notifying haproxy because skip-update is set." - fi - -else - Debug "Debug mode: haproxy update skipped." -fi - -# remove temporary files and quit with success -#Quit 0 - -# vim: set tabstop=4 shiftwidth=4 expandtab: diff --git a/roles/haproxy/files/haproxy_systemd_redirects b/roles/haproxy/files/haproxy_systemd_redirects deleted file mode 100644 index 4955d0ee2..000000000 --- a/roles/haproxy/files/haproxy_systemd_redirects +++ /dev/null @@ -1,16 +0,0 @@ -[Unit] -Description=HAProxy Load Balancer Redirects instance -After=network.target - -[Service] -Environment="CONFIG=/etc/haproxy/redirects/haproxy_redirects.cfg" "PIDFILE=/run/rh-haproxy18-haproxy-redirects.pid" -ExecStartPre=/opt/rh/rh-haproxy18/root/usr/sbin/haproxy -f $CONFIG -c -ExecStart=/opt/rh/rh-haproxy18/root/usr/sbin/haproxy -Ws -f $CONFIG -p $PIDFILE $OPTIONS -ExecReload=/opt/rh/rh-haproxy18/root/usr/sbin/haproxy -f $CONFIG -c -ExecReload=/bin/kill -USR2 $MAINPID -KillMode=mixed -Type=notify - -[Install] -WantedBy=multi-user.target - diff --git a/roles/haproxy/tasks/acme.yml b/roles/haproxy/tasks/acme.yml new file mode 100644 index 000000000..901b1e13c --- /dev/null +++ b/roles/haproxy/tasks/acme.yml @@ -0,0 +1,33 @@ +--- +- name: Create acme user + ansible.builtin.user: + name: acme + state: present + groups: lbops,haproxy + append: yes + +- name: Install acl package so ansible can run as an unprivilegd user + ansible.builtin.package: + name: acl + state: present + +- name: Clone the acme.sh repo + ansible.builtin.git: + repo: https://github.com/acmesh-official/acme.sh.git + dest: /opt/acmesh/ + update: false + +- name: Install the acme.sh script + ansible.builtin.shell: + cmd: sh /opt/acmesh/acme.sh --install + chdir: /opt/acmesh + creates: /home/acme/.acme.sh/acme.sh + become: true + become_user: acme + +- name: Create the acme account + ansible.builtin.shell: + cmd: /home/acme/.acme.sh/acme.sh --register-account --eab-kid {{ haproxy_acme_eab_kid }} --eab-hmac-key {{ haproxy_acme_eab_hmac_key }} --server https://acme.sectigo.com/v2/GEANTOV + creates: /home/acme/.acme.sh/ca/acme.sectigo.com/v2/GEANTOV/account.key + become: true + become_user: acme diff --git a/roles/haproxy/tasks/get_acme_certs.yml b/roles/haproxy/tasks/get_acme_certs.yml new file mode 100644 index 000000000..5f2c28014 --- /dev/null +++ b/roles/haproxy/tasks/get_acme_certs.yml @@ -0,0 +1,30 @@ +--- +- name: Install the certificates using acme + ansible.builtin.shell: + cmd: "/home/acme/.acme.sh/acme.sh --issue --stateless --keylength 2048 -d {{ base_domain }} {% for application in haproxy_applications %} -d {{ application.vhost_name }} {% endfor %} --server https://acme.sectigo.com/v2/GEANTOV" + become_user: acme + become: true + +- name: Deploy the certificates to haproxy without the need for haproxy to run + ansible.builtin.shell: + cmd: "DEPLOY_HAPROXY_PEM_PATH=/etc/haproxy/certs /home/acme/.acme.sh/acme.sh --deploy -d {{ base_domain }} --deploy-hook haproxy" + become_user: acme + become: true + +- name: Start and enable haproxy + ansible.builtin.service: + name: haproxy + state: started + enabled: true + +- name: Wait for port 443 to become open + ansible.builtin.wait_for: + port: 443 + delay: 5 + host: "{{ haproxy_sni_ip_ipv4 }}" + +- name: Deploy the certificates to haproxy while running, ensuring that when updating certs it will hot reload haproxy + ansible.builtin.shell: + cmd: "DEPLOY_HAPROXY_HOT_UPDATE=yes DEPLOY_HAPROXY_STATS_SOCKET=/var/lib/haproxy/haproxy.stats DEPLOY_HAPROXY_PEM_PATH=/etc/haproxy/certs /home/acme/.acme.sh/acme.sh --deploy -d {{ base_domain }} --deploy-hook haproxy" + become_user: acme + become: true diff --git a/roles/haproxy/tasks/main.yml b/roles/haproxy/tasks/main.yml index 708f91329..67ec93f7c 100644 --- a/roles/haproxy/tasks/main.yml +++ b/roles/haproxy/tasks/main.yml @@ -1,131 +1,108 @@ --- -- name: Create lbops group - group: - name: lbops - state: present - -- name: Enable ius repo - yum: - name: "https://repo.ius.io/ius-release-el7.rpm" - state: present +- name: Download HAProxy GPG key + ansible.builtin.get_url: + url: https://haproxy.debian.net/bernat.debian.org.gpg + dest: /etc/apt/trusted.gpg.d/haproxy.asc + mode: '0644' + +- name: Add HAProxy repository to sources list + ansible.builtin.copy: + dest: /etc/apt/sources.list.d/haproxy.list + content: | + deb http://haproxy.debian.net bookworm-backports-3.0 main + mode: '0644' - name: Install haproxy and socat - yum: - name: - - haproxy20.x86_64 + ansible.builtin.apt: + name: + - haproxy=3.0.* - socat + - git state: present + update_cache: true register: haproxy_package_installed until: haproxy_package_installed is succeeded +- name: Create lbops group + ansible.builtin.group: + name: lbops + state: present + +- name: Include acme installation + ansible.builtin.include_tasks: acme.yml + when: haproxy_acme | bool + - name: Copy haproxy sysconfig file - copy: - src: sysconfig_haproxy + ansible.builtin.copy: + src: sysconfig_haproxy dest: /etc/sysconfig/haproxy + owner: root + group: root + mode: "0644" notify: - "restart haproxy" -- name: install OCSP update tool - copy: - src: "hapos-upd" - dest: "/usr/local/sbin/" - mode: 0755 - - name: Create haproxy socket dir - file: + ansible.builtin.file: path: /var/lib/haproxy state: directory owner: haproxy group: lbops - mode: 0775 + mode: "0775" -- name: Create haproxy config map and acl directories - file: +- name: Create haproxy config map, acl and certs directories + ansible.builtin.file: path: "/etc/haproxy/{{ item }}" state: directory owner: root group: lbops - mode: 0775 + mode: "0775" with_items: - acls - maps -- name: Enable haproxy - service: - name: haproxy - enabled: yes - - name: Create haproxy SSL key directory - file: - dest: "/etc/pki/haproxy/" + ansible.builtin.file: + dest: "/etc/haproxy/certs" state: directory owner: haproxy group: haproxy - mode: 0700 - -- name: Create combined key and certificate file for HAproxy when using selfsigned certificates - shell: cat {{tls.cert_path }}/{{ tls_star_cert }} {{ tls.cert_private_path }}/{{ tls_star_cert_key }} > "/etc/pki/haproxy/{{ item.name }}_haproxy.pem" - args: - creates: "/etc/pki/haproxy/{{ item.name }}_haproxy.pem" - when: - - "'selfsigned_certs' in group_names or use_selfsigned_certs | default(false)" - with_items: "{{ haproxy_sni_ip.certs }}" - notify: - - "reload haproxy" - -- name: chmod 600 to the haproxy combined key and certificate file - file: - path: "/etc/pki/haproxy/{{ item.name }}_haproxy.pem" - mode: 0600 - with_items: "{{ haproxy_sni_ip.certs }}" - when: - - "'selfsigned_certs' in group_names or use_selfsigned_certs | default(false)" + mode: "0770" - name: Create combined key and certificate file for HAproxy - copy: - content: "{{ item.key_content }}{{lookup('file', '{{ inventory_dir }}/files/certs/{{ item.crt_name }}')}}" - dest: "/etc/pki/haproxy/{{ item.name }}_haproxy.pem" - mode: 0600 + ansible.builtin.copy: + content: "{{ item.key_content }}{{ lookup('file', '{{ inventory_dir }}/files/certs/{{ item.crt_name }}')}}" + dest: "/etc/haproxy/certs/{{ item.name }}_haproxy.pem" + mode: "0600" with_items: "{{ haproxy_sni_ip.certs }}" - when: - - "'selfsigned_certs' not in group_names or not use_selfsigned_certs | default(true)" + when: haproxy_sni_ip.certs is defined notify: - "reload haproxy" -- name: Create combined key and certificate file for HAproxy restricted - copy: - content: "{{ item.key_content }}{{lookup('file', '{{ inventory_dir }}/files/certs/{{ item.crt_name }}')}}" - dest: "/etc/pki/haproxy/{{ item.name }}_haproxy.pem" - mode: 0600 - with_items: "{{ haproxy_sni_ip_restricted.certs }}" - notify: - - "reload haproxy" - when: - - "haproxy_sni_ip_restricted is defined and 'selfsigned_certs' not in group_names or not use_selfsigned_certs | default(true)" - -- name: Install ffdhe3072 DH parameters - copy: - src: ffdhe3072.pem - dest: /etc/pki/haproxy/ffdhe3072.pem +- name: Create backend CA directory + ansible.builtin.file: + path: "{{ tls_backend_ca | dirname }}" + state: directory owner: root - group: root - mode: 0644 - notify: - - "reload haproxy" + group: haproxy + mode: "0750" - name: Copy backend CA certificate - copy: + ansible.builtin.copy: content: "{{ lookup('file', '{{ inventory_dir }}/files/certs/backend.{{ base_domain }}_ca.pem') }}" dest: "{{ tls_backend_ca }}" - mode: 0644 + mode: "0644" owner: root when: - haproxy_backend_tls | bool - name: Copy haproxy config - template: + ansible.builtin.template: src: "{{ item }}.j2" dest: "/etc/haproxy/{{ item }}" + owner: root + group: root + mode: "0644" with_items: - haproxy_global.cfg - haproxy_frontend.cfg @@ -135,15 +112,13 @@ - "reload haproxy" - name: Copy haproxy acls - template: + ansible.builtin.template: src: "{{ item }}.j2" dest: "/etc/haproxy/acls/{{ item }}" owner: root group: lbops - mode: 0664 + mode: "0664" with_items: - - validvhostsrestricted.acl - - validvhostsunrestricted.acl - allowedips.acl - blockedips.acl - internalips.acl @@ -151,77 +126,60 @@ notify: - "reload haproxy" +- name: Copy haproxy vhost acls + ansible.builtin.template: + src: "{{ item }}.j2" + dest: "/etc/haproxy/acls/{{ item }}" + owner: root + group: lbops + mode: "0664" + with_items: + - validvhostsrestricted.acl + - validvhostsunrestricted.acl + register: vhost_acls + notify: + - "reload haproxy" + + - name: Copy haproxy maps - template: + ansible.builtin.template: src: "{{ item }}.j2" dest: "/etc/haproxy/maps/{{ item }}" owner: root group: lbops - mode: 0664 + mode: "0664" with_items: - backends.map - backendsstaging.map - redirects.map - ratelimits.map + - certlist.lst notify: - "reload haproxy" - + - name: Place file with list of browsers that do not accept samesite=none cookies - copy: + ansible.builtin.copy: src: nosamesitebrowsers.lst dest: /etc/haproxy/maps/nosamesitebrowsers.lst owner: root group: lbops - mode: 0664 + mode: "0664" notify: - "reload haproxy" -- name: Start haproxy - service: - name: haproxy - state: started - enabled: true - -- name: Run hapos-upd for initial OCSP responses - shell: /usr/local/sbin/hapos-upd --partial-chain --good-only --socket /var/lib/haproxy/haproxy.stats --VAfile /etc/pki/haproxy/{{ item.name }}_haproxy.pem --cert /etc/pki/haproxy/{{ item.name }}_haproxy.pem - with_items: - - "{{ haproxy_sni_ip.certs }}" - - "{{ haproxy_sni_ip_restricted.certs }}" - when: - - haproxy_sni_ip_restricted is defined - - "'selfsigned_certs' not in group_names or not use_selfsigned_certs | default(true)" - -- name: install OCSP periodic update script - template: - src: "update_ocsp.j2" - dest: "/usr/local/sbin/update_ocsp" - mode: 0755 - when: - - "'selfsigned_certs' not in group_names or not use_selfsigned_certs | default(true)" - -- cron: - name: "update ocsp info" - special_time: hourly - job: /usr/local/sbin/update_ocsp {{ ocsp_log_redirect | default('') }} - when: - - "'selfsigned_certs' not in group_names or not use_selfsigned_certs | default(true)" - -- name: disable health checks for development envs - shell: 'echo "disable health {{ item }}" | socat /var/lib/haproxy/haproxy.stats stdio' - with_items: - - profile_be/phpapps - - engine_be/phpapps - when: - - develop | bool - -- name: Save the disabled health check state to the state file for development envs - shell: echo "show servers state" | socat /var/lib/haproxy/haproxy.stats - > /var/lib/haproxy/state - when: - - develop | bool - - name: Add lbops user to sudoers and let it reload haproxy - copy: + ansible.builtin.copy: src: lbops dest: /etc/sudoers.d/lbops owner: root - mode: 0440 + mode: "0440" + +- name: include get_acme_certs + include_tasks: get_acme_certs.yml + when: haproxy_acme | bool and vhost_acls.changed + +- name: Start and enable haproxy + ansible.builtin.service: + name: haproxy + state: started + enabled: true diff --git a/roles/haproxy/templates/certlist.lst.j2 b/roles/haproxy/templates/certlist.lst.j2 new file mode 100644 index 000000000..363520ca5 --- /dev/null +++ b/roles/haproxy/templates/certlist.lst.j2 @@ -0,0 +1,8 @@ +{% if haproxy_acme | bool %} +/etc/haproxy/certs/{{ base_domain }}.pem [ocsp-update on] +{% endif %} +{% if haproxy_sni_ip.certs is defined %} +{% for cert in haproxy_sni_ip.certs %} +/etc/haproxy/certs/{{ cert.name }}_haproxy.pem [ocsp-update on] +{% endfor %} +{% endif %} diff --git a/roles/haproxy/templates/haproxy_backend.cfg.j2 b/roles/haproxy/templates/haproxy_backend.cfg.j2 index de9b0d612..a9432af76 100644 --- a/roles/haproxy/templates/haproxy_backend.cfg.j2 +++ b/roles/haproxy/templates/haproxy_backend.cfg.j2 @@ -21,7 +21,8 @@ #--------------------------------------------------------------------- # backend {{ application.name }}_be - option httpchk {{ application.ha_method }} {{ application.ha_url }} "HTTP/1.0\r\nHost: {{ application.vhost_name }}" + option httpchk {{ application.ha_method }} {{ application.ha_url }} + http-check send ver HTTP/1.1 hdr Host {{ application.vhost_name }} {%if application.x_forwarded_port is defined %} http-request set-header X-Forwarded-Port {{ application.x_forwarded_port }} @@ -47,7 +48,8 @@ #--------------------------------------------------------------------- # backend {{ application.name }}_staging_be - option httpchk {{ application.ha_method }} {{ application.ha_url }} "HTTP/1.0\r\nHost: {{ application.vhost_name }}" + option httpchk {{ application.ha_method }} {{ application.ha_url }} + http-check send ver HTTP/1.1 hdr Host {{ application.vhost_name }} {%if application.x_forwarded_port is defined %} http-request set-header X-Forwarded-Port {{ application.x_forwarded_port }} diff --git a/roles/haproxy/templates/haproxy_frontend.cfg.j2 b/roles/haproxy/templates/haproxy_frontend.cfg.j2 index f1d92acd8..6082e9c03 100644 --- a/roles/haproxy/templates/haproxy_frontend.cfg.j2 +++ b/roles/haproxy/templates/haproxy_frontend.cfg.j2 @@ -4,7 +4,6 @@ # Stats frontend for exporting prometheus metrics frontend stats bind 127.0.0.1:8404 - option http-use-htx http-request use-service prometheus-exporter if { path /metrics } @@ -13,8 +12,8 @@ frontend stats # ------------------------------------------------------------------- frontend internet_ip - bind {{ haproxy_sni_ip.ipv4 }}:443 ssl{% for certs in haproxy_sni_ip.certs %} crt /etc/pki/haproxy/{{ certs.name }}_haproxy.pem {% endfor %} no-sslv3{% if haproxy_disable_tls10_and_11 | default(True) %} no-tlsv10 no-tlsv11 {% endif %} alpn h2,http/1.1 transparent - bind {{ haproxy_sni_ip.ipv6 }}:443 ssl{% for certs in haproxy_sni_ip.certs %} crt /etc/pki/haproxy/{{ certs.name }}_haproxy.pem {% endfor %} no-sslv3{% if haproxy_disable_tls10_and_11 | default(True) %} no-tlsv10 no-tlsv11 {% endif %} alpn h2,http/1.1 transparent + bind {{ haproxy_sni_ip.ipv4 }}:443 ssl crt-list /etc/haproxy/maps/certlist.lst ssl crt /etc/haproxy/certs/ no-sslv3 no-tlsv10 no-tlsv11 alpn h2,http/1.1 transparent + bind {{ haproxy_sni_ip.ipv6 }}:443 ssl crt-list /etc/haproxy/maps/certlist.lst ssl crt /etc/haproxy/certs/ no-sslv3 no-tlsv10 no-tlsv11 alpn h2,http/1.1 transparent bind {{ haproxy_sni_ip.ipv4 }}:80 transparent bind {{ haproxy_sni_ip.ipv6 }}:80 transparent # Logging is done in the local_ip backend, otherwise all requests are logged twice @@ -112,8 +111,8 @@ frontend local_ip ## ------------------------------------------------------------------- frontend internet_restricted_ip - bind {{ haproxy_sni_ip_restricted.ipv4 }}:443 ssl {% for certs in haproxy_sni_ip_restricted.certs %} crt /etc/pki/haproxy/{{ certs.name }}_haproxy.pem {% endfor %} no-sslv3 no-tlsv10 no-tlsv11 alpn h2,http/1.1 transparent - bind {{ haproxy_sni_ip_restricted.ipv6 }}:443 ssl {% for certs in haproxy_sni_ip_restricted.certs %} crt /etc/pki/haproxy/{{ certs.name }}_haproxy.pem {% endfor %} no-sslv3 no-tlsv10 no-tlsv11 alpn h2,http/1.1 transparent + bind {{ haproxy_sni_ip_restricted.ipv4 }}:443 ssl crt-list /etc/haproxy/maps/certlist.lst ssl crt /etc/haproxy/certs/ no-sslv3 no-tlsv10 no-tlsv11 alpn h2,http/1.1 transparent + bind {{ haproxy_sni_ip_restricted.ipv6 }}:443 ssl crt-list /etc/haproxy/maps/certlist.lst ssl crt /etc/haproxy/certs/ no-sslv3 no-tlsv10 no-tlsv11 alpn h2,http/1.1 transparent bind {{ haproxy_sni_ip_restricted.ipv4 }}:80 transparent bind {{ haproxy_sni_ip_restricted.ipv6 }}:80 transparent # Logging is done in the local_ip_restriced backend, otherwise all requests are logged twice diff --git a/roles/haproxy/templates/haproxy_global.cfg.j2 b/roles/haproxy/templates/haproxy_global.cfg.j2 index e1e9c9a07..8636b3a1d 100644 --- a/roles/haproxy/templates/haproxy_global.cfg.j2 +++ b/roles/haproxy/templates/haproxy_global.cfg.j2 @@ -4,21 +4,17 @@ #--------------------------------------------------------------------- global - log 127.0.0.1 len 32768 local2 - log-send-hostname + log /dev/log len 32768 format local local2 chroot /var/lib/haproxy - pidfile /var/run/haproxy.pid maxconn 4000 user haproxy group haproxy - nbproc 1 ulimit-n 9000 daemon ssl-default-bind-options no-sslv3 no-tls-tickets - ssl-default-bind-ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+AESGCM:DH+AES256:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS + ssl-default-bind-ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128 ssl-default-server-options no-sslv3 no-tls-tickets - ssl-default-server-ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+AESGCM:DH+AES256:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS - ssl-dh-param-file /etc/pki/haproxy/ffdhe3072.pem + ssl-default-server-ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128 {% if haproxy_metricbeat %} stats socket 127.0.0.1:14567 {% endif %} diff --git a/roles/haproxy/templates/validvhostsunrestricted.acl.j2 b/roles/haproxy/templates/validvhostsunrestricted.acl.j2 index 252319f8d..b62774518 100644 --- a/roles/haproxy/templates/validvhostsunrestricted.acl.j2 +++ b/roles/haproxy/templates/validvhostsunrestricted.acl.j2 @@ -7,8 +7,6 @@ {% for application in haproxy_redirects %} {%if application.hostname is defined %} {{ application.hostname }} -{% else %} -{{ application.url }} {% endif %} {% endfor %} -{% endif %} +{% endif %} \ No newline at end of file diff --git a/roles/keepalived/tasks/main.yml b/roles/keepalived/tasks/main.yml index d3580c4ff..1f937e084 100644 --- a/roles/keepalived/tasks/main.yml +++ b/roles/keepalived/tasks/main.yml @@ -1,8 +1,4 @@ -- name: Install procps-ng - yum: - name: procps-ng - state: present - +--- - name: Ensure nonlocal ipv4 bind kernel parameter is set sysctl: name: net.ipv4.ip_nonlocal_bind @@ -14,9 +10,10 @@ group: name: mysqlusers state: present + when: "'dbcluster_nodes' in group_names" - name: Install keepalived - yum: + package: name: keepalived state: present register: keepalived_package_installed diff --git a/roles/keepalived/templates/keepalived_loadbalancer.conf.j2 b/roles/keepalived/templates/keepalived_loadbalancer.conf.j2 index 559507ae6..cbd842743 100644 --- a/roles/keepalived/templates/keepalived_loadbalancer.conf.j2 +++ b/roles/keepalived/templates/keepalived_loadbalancer.conf.j2 @@ -14,7 +14,7 @@ vrrp_script chk_maint { vrrp_instance ipv4 { interface {{ ansible_default_ipv4.interface }} # interface to monitor state {{ keepalived.state_master }} - virtual_router_id 55 # Assign one ID for this route + virtual_router_id {{ vrrp_id_ipv4 }} # Assign one ID for this route priority {{ keepalived.masterprio }} # 101 on master, 100 on backup advert_int 1 authentication { @@ -35,9 +35,13 @@ vrrp_instance ipv4 { vrrp_instance ipv6 { interface {{ ansible_default_ipv4.interface }} state {{ keepalived.state_backup }} - virtual_router_id 56 + virtual_router_id {{ vrrp_id_ipv6 }} priority {{ keepalived.backupprio }} - advert_int 5 + advert_int 1 + authentication { + auth_type PASS + auth_pass {{ keepalived_loadbalancer_vrrp_password }} + } virtual_ipaddress { {% if haproxy_sni_ip.ipv6 is defined %} {{ haproxy_sni_ip.ipv6 }} diff --git a/roles/rsyslog/templates/rsyslog_onlyforward.conf.j2 b/roles/rsyslog/templates/rsyslog_onlyforward.conf.j2 index c2235025e..4196c526e 100644 --- a/roles/rsyslog/templates/rsyslog_onlyforward.conf.j2 +++ b/roles/rsyslog/templates/rsyslog_onlyforward.conf.j2 @@ -5,8 +5,6 @@ module(load="immark" interval="600" ) # provides --MARK-- message capability $PreserveFQDN on -$WorkDirectory /var/spool/rsyslog -$IncludeConfig /etc/rsyslog.d/*.conf *.emerg :omusrmsg:* {% if rsyslog_remote_server_relp is defined and 'sysloghost' not in group_names %}