インターネット〜ALB間だけでなく、ALB〜EC2間もSSL化する構成
以下のページで、ACM証明書を使用して、インターネットからALBまでの通信をSSL化する構成をご紹介しました。
ただし上記の構成では、ALBまでの通信はHTTPSですが、ALBからEC2インスタンスまでの通信はHTTPとなります。そこで今回は、自己証明書を作成して、クライアント〜インスタンス間の全ての通信をSSL化することを目標とします。
構築する環境
基本的な構成は、先述ご紹介したページと同一です。今回はSSMドキュメント(AWS-ApplyAnsiblePlaybooks)を実行して、EC2インスタンスで自己証明書を作成します。
CloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。以下のURLにCloudFormationテンプレートを配置してます。
https://github.com/awstut-an-r/awstut-fa/tree/main/024
テンプレートファイルのポイント解説
本ページでは、ALBのHTTPS通信への対応方法や、EC2インスタンスの自己証明書の作成方法に関して解説します。プライベートサブネット内のEC2をALBにアタッチする方法や、Route 53の設定方法、ACM証明書に関しては、以下のページをご確認ください。
ALBターゲットグループで、インスタンスへの通信はHTTPSに設定する
fa-024-alb.yamlでALB関係のリソースを定義しています。ポイントはALBターゲットグループです。
Resources:
ALBTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
VpcId: !Ref VPC
Name: !Sub ${Prefix}-ALBTargetGroup
Protocol: HTTPS
Port: !Ref HTTPSPort
HealthCheckProtocol: HTTPS
HealthCheckPath: /
HealthCheckPort: traffic-port
HealthyThresholdCount: !Ref HealthyThresholdCount
UnhealthyThresholdCount: !Ref UnhealthyThresholdCount
HealthCheckTimeoutSeconds: !Ref HealthCheckTimeoutSeconds
HealthCheckIntervalSeconds: !Ref HealthCheckIntervalSeconds
Matcher:
HttpCode: !Ref HttpCode
Targets:
- Id: !Ref Instance1
- Id: !Ref Instance2
Code language: YAML (yaml)
3つのパラメータに注目です。1つ目はProtocolプロパティです。ターゲットであるインスタンスに通信するプロトコルを設定します。今回は「HTTPS」を設定します。2つ目はPortプロパティです。ターゲットが待ち受けているポート番号を設定します。今回はデフォルトの「443」を設定します。3つ目はHealthCheckProtocolプロパティです。ヘルスチェックとして、ALBがインスタンスにアクセスする際のプロトコルを設定します。こちらも「HTTPS」を指定します。
インスタンスのセキュリティグループでHTTPSを許可する
続いてfa-024-vpc.yamlで定義されている、インスタンスに適用するセキュリティグループを確認します。
Resources:
InstanceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${Prefix}-InstanceSecurityGroup"
GroupDescription: Allow HTTPS from ALBSecurityGroup.
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: !Ref HTTPSPort
ToPort: !Ref HTTPSPort
SourceSecurityGroupId: !Ref ALBSecurityGroup
Code language: YAML (yaml)
FromPortおよびToPortプロパティで、許可するポート番号を指定します。両プロパティに「443」を設定することで、HTTPS通信のみを許可します。
Ansibleで自己証明書作成を作成する
SSM関連付け
最後にfa-024-ssm.yamlで定義されている、SSM関係のリソースを確認します。
2つのインスタンスに対して、統一的に自己証明書を作成する手順を実行するために、Ansibleを使用します。今回はSSMドキュメントAWS-ApplyAnsiblePlaybooksを使用します。注意すべき点は、インスタンスがプライベートサブネット内に設置されているという点です。そのためVPCエンドポイントを経由してSSM・S3にアクセスすることで、同ドキュメントを実行します。詳細については、以下のページをご確認ください。
Ansible Playbook
Ansibleで実行するPlaybookの中身を確認します。
- hosts: all
gather_facts: no
become: yes
tasks:
- name: update yum
yum: name=*
- name: install the latest version of Apache
yum: name=httpd state=latest
- name: install the latest version of openssl
yum: name=openssl state=latest
- name: install the latest version of mod_ssl
yum: name=mod_ssl state=latest
- name: copy openssl.cnf
copy:
src: ./openssl.cnf
dest: /etc/pki/tls/openssl.cnf
owner: root
group: root
mode: '0644'
- name: copy ssl.conf
copy:
src: ./ssl.conf
dest: /etc/httpd/conf.d/ssl.conf
owner: root
group: root
mode: '0644'
- name: generate private key
shell: openssl genrsa > server.key
- name: generate public key
shell: openssl req -new -batch -key server.key > server.csr
- name: generate crt
shell: openssl x509 -req -signkey server.key < server.csr > server.crt
- name: copy private key
shell: cp -a ./server.key /etc/pki/tls/private/
- name: copy crt
shell: cp -a ./server.crt /etc/pki/tls/certs/
- name: start and enable Apache
service: name=httpd state=started enabled=yes
- name: make index.html
shell: ec2-metadata -i > /var/www/html/index.html
Code language: YAML (yaml)
ApacheおよびSSL化のために必要なパッケージ(openssl, mod_ssl)をインストール後、2つの設定ファイル(openssl.cnf, ssl.conf)を、用意したファイルと置き換えます。以降の手順では、自己証明書を使用するための手順です。具体的には、秘密鍵、公開鍵、証明書を作成後、秘密鍵と証明書を所定の位置に設置します。最後にApacheを起動後、index.htmlに自身のインスタンスIDを書き込みます。なおコマンドはこのページを参考に設定しました。
(参考)openssl.cnf
openssl.cnfの変更点をご説明します。公開鍵を作成する上で、コマンドをバッチモードで実行するために、各パラメータのデフォルト値を以下の通りに変更しました。
- countryName_default = JP
- stateOrProvinceName_default = Tokyo
- localityName_default = Hogehoge
- 0.organizationName_default = HugaHuga
- organizationalUnitName_default = Piyopiyo
- commonName_default = spamspam
- emailAddress_default = mail.example.com
(参考)ssl.conf
ssl.confの変更点をご説明します。
証明書と秘密鍵の設置場所を以下の通りに変更しました。
- SSLCertificateFile /etc/pki/tls/certs/server.crt
- SSLCertificateKeyFile /etc/pki/tls/private/server.key
なおこちらの設定も、このページを参考に設定しました。
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
Ansible Playbookの準備
CloudFormationスタックを作成する前に、Ansibleの準備を行います。具体的にはPlaybookをZip化し、S3バケットに設置します。
$ zip -r playbook.zip *
adding: openssl.cnf (deflated 67%)
adding: playbook.yml (deflated 67%)
adding: ssl.conf (deflated 59%)
$ aws s3 cp playbook.zip s3://[bucket-name]/
upload: ./playbook.zip to s3://[bucket-name]/playbook.zip
Code language: Bash (bash)
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。スタックの作成および各スタックの確認方法については、以下のページをご確認ください。
各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- Instance1:i-06b5fa80e358558a2
- Instance2:i-0b0b10fbc49e33809
- ALB:fa-024-ALB
- Route 53のレコード:awstut.net
AWS Management Consoleからも、リソースの作成状況を確認します。まずALBターゲットグループを確認します。
ターゲットグループに、2つのインスタンスが登録されていることがわかります。またインスタンスに対して、443番ポートにHTTPSで通信する設定になっていることもわかります。次にSSMステートマネージャーの実行状況も確認します。
2つのSSM関連付けの作成が成功していることがわかります。このことからAnsibleが正常に実行されたことがわかります。
ALBにアクセスする
準備が整いましたので、ブラウザから、HTTPSで独自ドメインにアクセスします。
ALBにアクセスすることができました。以上より、クライアント〜ALB間だけでなく、ALB〜インスタンス間もSSL化することができました。
(参考) Ansible実行ログ
参考として、Instance1におけるAnsible実行ログの抜粋を置いておきます。
Running Ansible in /var/lib/amazon/ssm/i-06b5fa80e358558a2/document/orchestration/a3e0b63e-23f6-4e80-bfe3-4c73c83f6ab6/downloads
Archive: ./playbook.zip
inflating: openssl.cnf
inflating: playbook.yml
inflating: ssl.conf
Using /etc/ansible/ansible.cfg as config file
PLAY [all] *********************************************************************
TASK [update yum] **************************************************************
ok: ...
TASK [install the latest version of Apache] ************************************
changed: ...
TASK [install the latest version of openssl] ***********************************
ok: ...
TASK [install the latest version of mod_ssl] ***********************************
changed: ...
TASK [copy openssl.cnf] ********************************************************
changed: [localhost] => {"changed": true, "checksum": "870dcd50252d8aa8e1aa6392ce29a987480b337c", "dest": "/etc/pki/tls/openssl.cnf", "gid": 0, "group": "root", "md5sum": "3aa020ec74b4fb8be0aaf8746a489215", "mode": "0644", "owner": "root", "size": 11173, "src": "/root/.ansible/tmp/ansible-tmp-1644752856.2-2777-102212935013649/source", "state": "file", "uid": 0}
TASK [copy ssl.conf] ***********************************************************
changed: [localhost] => {"changed": true, "checksum": "10d92760527eaa77206766f62f47cb1346121434", "dest": "/etc/httpd/conf.d/ssl.conf", "gid": 0, "group": "root", "md5sum": "1747532281ea14600da458ef529f37da", "mode": "0644", "owner": "root", "size": 9544, "src": "/root/.ansible/tmp/ansible-tmp-1644752857.31-2820-240351648915287/source", "state": "file", "uid": 0}
TASK [generate private key] ****************************************************
changed: [localhost] => {"changed": true, "cmd": "openssl genrsa > server.key", "delta": "0:00:00.117549", "end": "2022-02-13 11:47:38.683733", "rc": 0, "start": "2022-02-13 11:47:38.566184", "stderr": "Generating RSA private key, 2048 bit long, ...
TASK [generate public key] *****************************************************
changed: [localhost] => {"changed": true, "cmd": "openssl req -new -batch -key server.key > server.csr", "delta": "0:00:00.026476", "end": "2022-02-13 11:47:39.110980", "rc": 0, "start": "2022-02-13 11:47:39.084504", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
TASK [generate crt] ************************************************************
changed: [localhost] => {"changed": true, "cmd": "openssl x509 -req -signkey server.key < server.csr > server.crt", "delta": "0:00:00.038384", "end": "2022-02-13 11:47:39.541607", "rc": 0, "start": "2022-02-13 11:47:39.503223", ...
TASK [copy private key] ********************************************************
changed: [localhost] => {"changed": true, "cmd": "cp -a ./server.key /etc/pki/tls/private/", "delta": "0:00:00.018995", "end": "2022-02-13 11:47:39.959631", "rc": 0, "start": "2022-02-13 11:47:39.940636", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
TASK [copy crt] ****************************************************************
changed: [localhost] => {"changed": true, "cmd": "cp -a ./server.crt /etc/pki/tls/certs/", "delta": "0:00:00.018371", "end": "2022-02-13 11:47:40.383606", "rc": 0, "start": "2022-02-13 11:47:40.365235", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
TASK [start and enable Apache] *************************************************
changed: ...
TASK [make index.html] *********************************************************
changed: ...
PLAY RECAP *********************************************************************
localhost : ok=13 changed=11 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Code language: plaintext (plaintext)
まとめ
自己証明書を作成し、ALB〜インスタンス間の通信もSSL化する方法を確認しました。
証明書の作成の手続きをAnsible Playbook化することで、2つのインスタンスに統一して実行することができました。