AWS

自己証明書でALB〜EC2間もSSL化する

インターネット〜ALB間だけでなく、ALB〜EC2間もSSL化する構成

以下のページで、ACM証明書を使用して、インターネットからALBまでの通信をSSL化する構成をご紹介しました。

ただし上記の構成では、ALBまでの通信はHTTPSですが、ALBからEC2インスタンスまでの通信はHTTPとなります。そこで今回は、自己証明書を作成して、クライアント〜インスタンス間の全ての通信をSSL化することを目標とします。

構築する環境

Amazon.co.jp: AWS認定資格試験テキスト AWS認定ソリューションアーキテクト - アソシエイト 改訂第2版 : NRIネットコム株式会社, 佐々木 拓郎, 林 晋一郎, 金澤 圭: 本
Amazon.co.jp: AWS認定資格試験テキスト AWS認定ソリューションアーキテクト - アソシエイト 改訂第2版 : NRIネットコム株式会社, 佐々木 拓郎, 林 晋一郎, 金澤 圭: 本
Diagram of on ssl between ALB and EC2 using self-signed certificate.

基本的な構成は、先述ご紹介したページと同一です。今回はSSMドキュメント(AWS-ApplyAnsiblePlaybooks)を実行して、EC2インスタンスで自己証明書を作成します。

CloudFormationテンプレートファイル

上記の構成をCloudFormationで構築します。以下のURLにCloudFormationテンプレートを配置してます。

awstut-fa/024 at main · awstut-an-r/awstut-fa
Contribute to awstut-an-r/awstut-fa development by creating an account on GitHub.

テンプレートファイルのポイント解説

本ページでは、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ターゲットグループを確認します。

It is configured to communicate via HTTPS to the ALB target group.

ターゲットグループに、2つのインスタンスが登録されていることがわかります。またインスタンスに対して、443番ポートにHTTPSで通信する設定になっていることもわかります。次にSSMステートマネージャーの実行状況も確認します。

The SSM Association has been created successfully.

2つのSSM関連付けの作成が成功していることがわかります。このことからAnsibleが正常に実行されたことがわかります。

ALBにアクセスする

準備が整いましたので、ブラウザから、HTTPSで独自ドメインにアクセスします。

Accessing EC2 instances via ALB with end-to-end SSL-encrypted communication 1.
Accessing EC2 instances via ALB with end-to-end SSL-encrypted communication 2.

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つのインスタンスに統一して実行することができました。

タイトルとURLをコピーしました