Ansible AWX をSSL対応にしてみた。
前提:Ansible AWXは導入済みとする事
デフォルト状態だとSSL化出来ない
・鍵を登録する事ができない
・Let's Encrypt の Webhook が使えない
鍵を登録出来るようにする
証明書が無いと始まらないので取り敢えず自己証明書でも作る
非常に雑だけどこんな感じ?
openssl genrsa 2024 > server.key
openssl req -new -key server.key > server.csr
openssl x509 -req -days 3650 -signkey server.key < server.csr > server.crt
inventory を修正し鍵の項目を作る
/root/awx/installer/inventory を手直し
ssl_certificate=./server.crt
ssl_certificate_key=./server.key
コンテナに鍵を渡す部分のために一部ソースを修正
web:
image: {{ awx_web_docker_actual_image }}
container_name: awx_web
<snip>
volumes:
- "{{ docker_compose_dir }}/SECRET_KEY:/etc/tower/SECRET_KEY"
- "{{ docker_compose_dir }}/environment.sh:/etc/tower/conf.d/environment.sh"
- "{{ docker_compose_dir }}/credentials.py:/etc/tower/conf.d/credentials.py"
これを追加 ----> - "{{ docker_compose_dir }}/nginx.conf:/etc/nginx/nginx.conf"
<snip>
{% if ssl_certificate is defined %}
- "{{ ssl_certificate +':/etc/nginx/awxweb.pem:ro' }}"
これを追加 ----> - "{{ ssl_certificate_key +':/etc/nginx/awxweb.key:ro' }}"
{% endif %}
nginx.conf.j2 を展開しないバグがあるので以下のファイルを追加
#user awx;
worker_processes 1;
pid /tmp/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /dev/stdout main;
#X# access_log /var/log/nginx/nginx_access.log main;
error_log /var/log/nginx/nginx_error.log;
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
sendfile on;
#tcp_nopush on;
#gzip on;
upstream uwsgi {
server 127.0.0.1:8050;
}
upstream daphne {
server 127.0.0.1:8051;
}
server {
#X# listen 8052 default_server;
listen 8052;
server_name _;
# Redirect all HTTP links to the matching HTTPS page
return 301 https://$host$request_uri;
}
server {
listen 8053 ssl default_server;
ssl_certificate /etc/nginx/awxweb.pem;
ssl_certificate_key /etc/nginx/awxweb.key;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1.2;
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_stapling on;
ssl_stapling_verify on;
ssl_session_timeout 20m;
# If you have a domain name, this is where to add it
server_name _;
keepalive_timeout 65;
# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000;
add_header Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' cdn.pendo.io; img-src 'self' data:; report-uri /csp-violation/";
add_header X-Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' cdn.pendo.io; img-src 'self' data:; report-uri /csp-violation/";
# Protect against click-jacking https://www.owasp.org/index.php/Testing_for_Clickjacking_(OTG-CLIENT-009)
add_header X-Frame-Options "DENY";
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
location /static/ {
alias /var/lib/awx/public/static/;
}
location /favicon.ico { alias /var/lib/awx/public/static/favicon.ico; }
location /websocket {
# Pass request to the upstream alias
proxy_pass http://daphne;
# Require http version 1.1 to allow for upgrade requests
proxy_http_version 1.1;
# We want proxy_buffering off for proxying to websockets.
proxy_buffering off;
# http://en.wikipedia.org/wiki/X-Forwarded-For
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# enable this if you use HTTPS:
proxy_set_header X-Forwarded-Proto https;
# pass the Host: header from the client for the sake of redirects
proxy_set_header Host $http_host;
# We've set the Host header, so we don't need Nginx to muddle
# about with redirects
proxy_redirect off;
# Depending on the request value, set the Upgrade and
# connection headers
proxy_set_header Upgrade $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Connection $connection_upgrade;
}
location / {
# Add trailing / if missing
rewrite ^(.*)$http_host(.*[^/])$ $1$http_host$2/ permanent;
uwsgi_read_timeout 120s;
uwsgi_pass uwsgi;
include /etc/nginx/uwsgi_params; proxy_set_header X-Forwarded-Port 443;
}
}
}
を書き込んでおく
secret_key を一時変更
<snip>
secret_key=awxsecret を何か適当に書き換え「""」でも可
AWX を再インストール(コンテナのリメイク)
ansible-playbook -i inventory install.yml
正常に完了したら http://該当ホスト/
へアクセスすると自動的に https://
に rewrite されていると思います。
Let's Encrypt の Webhook が使えないため standalone で証明書を取得
Let's Encrypt を取得
yum -y install certbot
certbot-auto を /usr/local/sbin にコピー
cd certbot
cp certbot-auto /usr/local/sbin
standalone で証明書取得の為一旦 docker を停止
systemctl stop docker
standalone で証明書取得をテストモードで実行(--dry-run)
certbot-auto certonly --standalone --agree-tos -m encrypy@example.com -d encrypt.example.com --dry-run
これは、1時間に5回以上のリクエストを受け付けないという rate-limit
があるため出来るだけ --dry-run
で確認しておいたほうが良い。
certbot-auto certonly --standalone --agree-tos -m encrypy@example.com -d encrypt.example.com --test-cert
試験的に1週間位の期間の証明書を取得することも出来る
動作確認が済んだら実際に取得
certbot-auto certonly --standalone --agree-tos -m encrypy@example.com -d encrypt.example.com
正常に取得出来ると証明書は /etc/letsencrypt/live/encrypt.example.com/fullchain.pem
秘密鍵は /etc/letsencrypt/live/encrypt.example.com/private.pem
に記録されている。
ここまで読んで「う〜ん、止めたくないし色々手間だな」とおもったら
SSLなう! を使用すれば手間はないかも知れません。
#但し、後々手間になりますけどね
inventory の 修正
ssl_certificate=/etc/letsencrypt/live/encrypt.example.com/fullchain.pem
ssl_certificate_key=/etc/letsencrypt/live/encrypt.example.com/privkey.pem
に修正
また、secret_key を変更する
secret_key=awxsecret を何か適当に書き換え「""」でも可
なぜ secret_key を書き換えるのかと言うと、コンテナイメージ作成の変更の有無を見ているからである。
コンテナの再ビルド
cd /root/installer/
ansible-playbook -i inventory install.yml
これでLet's Encrypt での証明書が使えるようになりました。
さぁ、SSL Labs で試験をしてみましょう。
#数分で終わります
さて、証明書の自動更新どうしよう・・・・・
Let's encrypt は無料ですが期間が3ヶ月しかありません。
3ヶ月ごとに上記作業を手作業でやるのはツライ・・・・・・
刺し身タンポポの仕事は嫌なので自動化します。
#!/bin/bash
INVENTORY=/root/awx/installer/inventory
#### date md5取得
MD5_DATE=`date | md5sum | awk '{print $1}'`
MD5_DATE_OLD=`cat MD5_DATE_OLD`
#### 証明書取得
/usr/local/sbin/certbot-auto renew
#### inventry の secret_key を更新
sed -i "s/^secret_key=$MD5_DATE_OLD/secret_key=$MD5_DATE/g" $INVENTORY
echo $MD5_DATE > MD5_DATE_OLD
#### awx再インストール
cd /root/awx/installer/
ansible-playbook -i inventory install.yml
こんな感じのスクリプトを毎月1日に実行するように cron
に仕込んで起くと便利かもしれません。