検証に使うための内部ネットワーク(自己署名証明書Webサーバー+内部DNS)をCloudFormationでテンプレート化してみた

2021.11.14

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

いわさです。

オンプレミス側のプライベートネットワーク環境とAWSを接続して検証などを行いたい場合がよくあります。
VPNやDirectConnectの環境を用意するのはちょっと大変ですが、VPCでプライベートネットワーク環境を用意してVPCピアリングさせる程度であれば簡単に用意出来ますし、割と近い環境が再現出来るかなと思ってよく使っています。

ただし、毎度Webサーバーだったり、DNSサーバーだったりを構築するのは面倒です。
そこで今回はミドルウェアの設定部分も含めてテンプレート化しておきました。
そして、これだけの小さな構築にも関わらずまぁまぁ詰まったので公開しておきたいと思います。

テンプレート

テンプレートはこちらになります。

プライベートサブネットにWebサーバーとDNSサーバーを構築します。
Webサーバーは自己署名証明書を設定済みのPHPアプリケーションをデプロイし、内部DNSはDHCPオプションでVPCに関連付けしています。
内部DNSサーバーのフォワーダーにはAmazonProvidedDNSを指定し、通信はNATゲートウェイでアウトバウンドさせているので初期セットアップが失敗しないようにされています。

テンプレートで構築される構成は以下のようなシングルAZのシンプルなものです。
パラメータで各CIDRやWebアプリケーションのホストゾーンなどを設定する形になっています。

今回見送りましたが、そのうちアップデートしてNATゲートウェイではなくてプロキシサーバーを通すようにするつもりです。

少し抜粋すると、今回はほとんどユーザーデータで設定しています。

Webサーバー

こちらはWebサーバーです。
PHPをセットアップし、自己署名証明書を設定しています。

UserData:
  Fn::Base64: !Sub
  - |
    #cloud-config
    repo_update: true
    repo_upgrade: all
    packages:
      - httpd
      - mod_ssl
      - openssl
      - php
    runcmd:
      - openssl genrsa -out ca.key 2048
      - openssl req -new -key ca.key -out ca.csr -subj "${sslsubject}"
      - openssl x509 -req -days 365 -in ca.csr -signkey ca.key -out ca.crt
      - cp -p ca.crt /etc/pki/tls/certs/
      - cp -p ca.key /etc/pki/tls/private/
      - cp -p ca.csr /etc/pki/tls/private/
      - sed -i 's/localhost/ca/g' /etc/httpd/conf.d/ssl.conf
      - service httpd start
      - chkconfig httpd on
    write_files:
    - path: /var/www/html/index.php
      permissions: 0644
      owner: root
      content: |
        <?php 
          echo "<pre>";
          var_dump($_SERVER);
          echo "</pre>"; 
        ?>
  - { sslsubject: !Ref SslSubject }

証明書周りなどこちらの記事を参考にさせて頂きました。

DNSサーバー

こちらはDNSサーバーです。
BINDを使っています。

設定ファイルとゾーンファイルをそのまま作成しているだけですね。
一部動的パラメータを埋め込んでいます。

UserData:
  Fn::Base64: !Sub
  - |
    #cloud-config
    repo_update: true
    repo_upgrade: all
    packages:
      - bind
    runcmd:
      - service named start
      - chkconfig named on
    write_files:
    - path: /etc/named.conf
      permissions: 0644
      owner: root
      content: |
        options {
            listen-on-v6 port 53 { ::1; };
            directory     "/var/named";
            dump-file     "/var/named/data/cache_dump.db";
            statistics-file "/var/named/data/named_stats.txt";
            memstatistics-file "/var/named/data/named_mem_stats.txt";
            recursing-file  "/var/named/data/named.recursing";
            secroots-file   "/var/named/data/named.secroots";
            allow-query     { any; };
            recursion no;
            dnssec-enable yes;
            dnssec-validation yes;
            bindkeys-file "/etc/named.root.key";
            managed-keys-directory "/var/named/dynamic";
            pid-file "/run/named/named.pid";
            session-keyfile "/run/named/session.key";
            forwarders { ${providedDns}; };
        };
        logging {
                channel default_debug {
                        file "data/named.run";
                        severity dynamic;
                };
        };
        zone "." IN {
            type hint;
            file "named.ca";
        };
        zone "${zonename}" IN {
            type master;
            file "${zonename}";
        };
        include "/etc/named.rfc1912.zones";
        include "/etc/named.root.key";
    - path: /var/named/${zonename}
      permissions: 0644
      owner: root
      content: |
        $TTL      86400
        @         IN       SOA     dns.${zonename}. root.${zonename}. (
                                    2021111401 
                                    3600
                                    3600
                                    604800
                                    86400 )
                  IN NS ns.${zonename}.
                  IN A ${webServerIp}
        ns.${zonename}.  IN   A   127.0.0.1
  - { zonename: !Ref HosteZone, webServerIp: !GetAtt WebServer.PrivateIp, providedDns: !Ref AmazonProvidedDns }

確認

念の為確認もしておきます。

テンプレート実行直後は上記のようになり、以下のような形でこのプライベートネットワーク内に任意のHTTPクライアントをEC2で新規作成してアクセスしてみます。

[ec2-user@ip-10-103-0-216 ~]$ curl --insecure https://iwasa20211114.com/
<pre>array(61) {
  ["UNIQUE_ID"]=>
  string(27) "YZDAWDfT4GNpMkXp9JwyPwAAAAQ"
  ["HTTPS"]=>
  string(2) "on"
  ["SSL_TLS_SNI"]=>
  string(17) "iwasa20211114.com"

問題なく名前解決出来ていますね。

さいごに

テンプレート作成を通して、OpenSSLやBINDなどAWSレイヤーより少し上の部分や、DHCPオプションなど普段触らない部分を結構トライ&エラー出来たのでおもしろかったです。

テンプレートのユーザーデータを見ていただくことわかるのですが、まとめてごちゃごちゃとやっております。
ユーザーデータで無理やりやりすぎたか...と少し思っているのでこうやったらもっと簡単に出来るよなどフィードバック頂けると幸いです。
Webアプリケーション部分は、PHPのサーバー変数をダンプ出力しているだけの状態です。
ゾーンファイルについては各パラメータ適当な感じでなので、こちらは構築時に適宜パラメータ調整すると良いと思います。

この環境を使って、VPC PeeringやPrivate Linkなどを使って別のネットワークやパブリックAWSサービスと通信検証する際などに使うことを想定しています。

参考