Help us understand the problem. What is going on with this article?

AWXをSSL対応させてみた

More than 1 year has passed since last update.

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 を手直し

/root/awx/installer/inventory
ssl_certificate=./server.crt
ssl_certificate_key=./server.key

コンテナに鍵を渡す部分のために一部ソースを修正

roles/local_docker/templates/docker-compose.yml.j2
  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 を展開しないバグがあるので以下のファイルを追加

/tmp/awxcompose/nginx.conf
#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 を一時変更

/root/awx/installer/inventory
<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 の 修正

/root/awx/installer/inventory
ssl_certificate=/etc/letsencrypt/live/encrypt.example.com/fullchain.pem
ssl_certificate_key=/etc/letsencrypt/live/encrypt.example.com/privkey.pem

に修正
また、secret_key を変更する

/root/awx/installer/inventory
secret_key=awxsecret を何か適当に書き換え""でも可

なぜ secret_key を書き換えるのかと言うと、コンテナイメージ作成の変更の有無を見ているからである。

コンテナの再ビルド

cd /root/installer/
ansible-playbook -i inventory install.yml

これでLet's Encrypt での証明書が使えるようになりました。
さぁ、SSL Labs で試験をしてみましょう。
#数分で終わります

さて、証明書の自動更新どうしよう・・・・・

Let's encrypt は無料ですが期間が3ヶ月しかありません。
3ヶ月ごとに上記作業を手作業でやるのはツライ・・・・・・
刺し身タンポポの仕事は嫌なので自動化します。

cert_update.sh
#!/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 に仕込んで起くと便利かもしれません。

nakacya
AS9607 ITどかた 尻子玉抜くぞ 座右の銘「ボーテン・ソクリー・ゼンツッパ」 デイリー nakacya
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away