--- # Put AWX behind HTTPS with a host-level nginx TLS-termination reverse proxy. # # Why a reverse proxy instead of in-pod TLS: # The AWX Operator only wires nginx for HTTPS on the OpenShift Route # "passthrough" code path (ingress_type: route + route_tls_termination_mechanism: # passthrough), which requires the Route CRD and breaks on k3s. There is also no # ingress controller on the cluster. Terminating TLS at a host nginx proxy in # front of the existing LoadBalancer service is the lowest-risk option and is # trivially swappable once an internal CA / ingress controller + cert-manager # arrive. # # Topology after this playbook: # client --https:443--> host nginx (TLS terminate) --http:80--> k3s ServiceLB # | # v # tsys-awx-service (LB) -> awx-web:8052 # # The cert is self-signed (CN=tsys-awx.knel.net) with SANs covering the FQDN, # the short hostname, and both the LAN and Tailscale IPs. Re-run this playbook # after rotating the cert (e.g. when the internal CA is rolled out). - name: AWX HTTPS reverse proxy hosts: tsys-awx gather_facts: true become: true vars: # Cert subject / SANs. Extend these lists as more names are needed. awx_tls_cn: tsys-awx.knel.net awx_tls_sans: - "DNS:tsys-awx.knel.net" - "DNS:tsys-awx" - "IP:192.168.3.200" - "IP:100.91.39.53" awx_tls_dir: /etc/nginx/ssl awx_tls_cert: "{{ awx_tls_dir }}/awx-self-signed.crt" awx_tls_key: "{{ awx_tls_dir }}/awx-self-signed.key" tasks: - name: Install nginx ansible.builtin.apt: name: nginx state: present update_cache: true - name: Ensure TLS cert directory exists ansible.builtin.file: path: "{{ awx_tls_dir }}" state: directory owner: root group: root mode: "0755" - name: Generate self-signed certificate (only if missing) # noqa no-changed-when ansible.builtin.shell: | set -o pipefail openssl req -x509 -newkey rsa:2048 -nodes -days 825 \ -keyout {{ awx_tls_key }} -out {{ awx_tls_cert }} \ -subj "/CN={{ awx_tls_cn }}" \ -addext "subjectAltName={{ awx_tls_sans | join(',') }}" args: creates: "{{ awx_tls_cert }}" - name: Lock down private key permissions ansible.builtin.file: path: "{{ awx_tls_key }}" owner: root group: root mode: "0600" - name: Deploy nginx TLS proxy site config ansible.builtin.copy: dest: /etc/nginx/sites-available/awx-tls.conf owner: root group: root mode: "0644" content: | # Ansible-managed: TLS termination for AWX. # Proxies https://tsys-awx.knel.net -> http://127.0.0.1:80 (k3s ServiceLB -> AWX). # map must live in the http{} context; sites-enabled/* are included inside http. map $http_upgrade $awx_connection_upgrade { default upgrade; '' close; } server { listen 443 ssl; listen [::]:443 ssl; http2 on; server_name tsys-awx.knel.net tsys-awx 192.168.3.200 100.91.39.53; ssl_certificate /etc/nginx/ssl/awx-self-signed.crt; ssl_certificate_key /etc/nginx/ssl/awx-self-signed.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; client_max_body_size 50M; location / { proxy_pass http://127.0.0.1:80; proxy_http_version 1.1; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $awx_connection_upgrade; proxy_read_timeout 300s; proxy_redirect off; } } notify: Reload nginx - name: Enable the AWX TLS site ansible.builtin.file: src: /etc/nginx/sites-available/awx-tls.conf dest: /etc/nginx/sites-enabled/awx-tls.conf state: link - name: Test nginx configuration # noqa no-changed-when ansible.builtin.command: nginx -t handlers: - name: Reload nginx ansible.builtin.systemd: name: nginx state: reloaded enabled: true