🦁

EC2インスタンスでオレオレ証明書を作成してローカルPCからHTTPSでアクセスしてみた

2024/01/07に公開

EC2(Amazon Linux 2023)でApacheを起動してHTTPS通信を行ってみます。

インスタンス起動

以下の設定でインスタンスを起動しました。

  • ami-0dfa284c9d7b2adad
  • t2.micro
  • キーペアあり
  • パブリックIPアドレス割り当て
  • Session Manager でアクセスできるIAMロール設定
  • セキュリティグループ
    • インバウンド:HTTP,HTTPSを自分のIPから許可
    • アウトバウンド:フルオープン

SCPを利用してインスタンス内からファイルをダウンロードしたいので、キーペアも設定しています。

Session Managerを介したSSH接続

ローカルPCでSSHの設定を追加します。
--profileは必要に応じて追加します。

~/.ssh/config
host i-* mi-*
    ProxyCommand sh -c "aws ssm start-session --profile xxx --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"

上記設定後、ローカルPCにダウンロード済みの秘密鍵を指定した上で、該当のインスタンスに接続します。

$ ssh -i ~/.ssh/MyKeyPair.pem ec2-user@i-xxx

これにより、Session Managerを介したSSH接続でインスタンスに接続できます。

https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-getting-started-enable-ssh-connections.html

なお、Session Manager プラグインをインストールしている前提なので、インストールしていない場合はインストールから始めてください。

https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html

Apacheインストール ~ SSL設定

# Apacheのインストール
$ sudo dnf update -y
$ sudo dnf install -y httpd

$ sudo systemctl start httpd
$ sudo systemctl enable httpd
Created symlink /etc/systemd/system/multi-user.target.wants/httpd.service → /usr/lib/systemd/system/httpd.service.
$ sudo systemctl is-enabled httpd
enabled

# ApacheでSSLの設定を行うためmod_sslをインストール
$ sudo dnf -y install mod_ssl

# 秘密鍵・証明書などの作成に入る
$ sudo su -

# opensslのバージョン確認
$ openssl version
OpenSSL 3.0.8 7 Feb 2023 (Library: OpenSSL 3.0.8 7 Feb 2023)

# 秘密鍵生成
$ openssl genrsa 2048 > /etc/httpd/conf/server.key
# CSR作成
$ openssl req -new -key /etc/httpd/conf/server.key > /etc/httpd/conf/server.csr
# CRT作成(自己署名証明書)
# プロンプトは何も入力せずにエンターを押し、空に設定しています。
$ openssl x509 -req -signkey /etc/httpd/conf/server.key < /etc/httpd/conf/server.csr > /etc/httpd/conf/server.crt
Certificate request self-signature ok
subject=C = XX, L = Default City, O = Default Company Ltd

# ApacheのSSLの設定
$ vi /etc/httpd/conf.d/ssl.conf
# 以下はviで追加した項目(既存の同名の項目はコメントアウトした)
SSLCertificateFile /etc/httpd/conf/server.crt
SSLCertificateKeyFile /etc/httpd/conf/server.key

# SSLの設定反映
$ systemctl restart httpd

ダンプファイルの作成

本記事では触れませんが、HTTPとHTTPS通信の差をWiresharkから確認したいので、ダンプファイルを作成しておきます。

# インターフェース名の確認。inetがEC2のプライベートアドレスと一致するので、enX0で通信を行うことがわかる
$ ifconfig
enX0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 9001
        inet 10.0.0.179  netmask 255.255.255.0  broadcast 10.0.0.255
        inet6 fe80::4e2:84ff:fe51:1fb9  prefixlen 64  scopeid 0x20<link>
        ether 06:e2:84:51:1f:b9  txqueuelen 1000  (Ethernet)
        RX packets 28881  bytes 29471494 (28.1 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 16735  bytes 2517732 (2.4 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 5227  bytes 540162 (527.5 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 5227  bytes 540162 (527.5 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

# HTTP、HTTPSごとにダンプファイルを取得
$ sudo tcpdump -i enX0 -s 0 -w /tmp/dump_http.pcap port 80
$ sudo tcpdump -i enX0 -s 0 -w /tmp/dump_https.pcap port 443

このダンプファイルは、HTTP,HTTPS通信を確認した後にダウンロードします。

# zshを利用している場合、以下のワイルドカードを利用するコマンドでは失敗する
$ scp -i ~/.ssh/MyKeyPair.pem ec2-user@i-xxx:/tmp/dump_http* .     
zsh: no matches found: ec2-user@i-xxx:/tmp/dump_http*

# nonomatchオプションを設定してから実行すれば失敗しない
$ setopt nonomatch
$ scp -i ~/.ssh/MyKeyPair.pem ec2-user@i-xxx:/tmp/dump_http* .
dump_http.pcap                                                                                  100% 1698    21.8KB/s   00:00    
dump_https.pcap                                                                                 100%   12KB  63.7KB/s   00:00

EC2インスタンスにアクセス

HTTP通信ではcurlでアクセスに成功。ブラウザからも同様。

$ curl http://43.206.227.156
<html><body><h1>It works!</h1></body></html>

HTTPS通信ではcurlだとアクセスに失敗。ブラウザからは警告を無視すればアクセス可能。
自己署名証明書はデフォルトのCA証明書ストアで信頼されていないためエラーとなります。

$ curl -v https://43.206.227.156
*   Trying 43.206.227.156:443...
* Connected to 43.206.227.156 (43.206.227.156) port 443 (#0)
* ALPN: offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* SSL certificate problem: self signed certificate
* Closing connection 0
curl: (60) SSL certificate problem: self signed certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

https://curl.se/docs/sslcerts.html

それではCA証明書を指定してcurl実行してみます。

# インスタンスからローカルPCに証明書をダウンロード
$ scp -i ~/.ssh/MyKeyPair.pem ec2-user@i-xxx:/etc/httpd/conf/server.crt .
server.crt   

# ダウンロードした証明書をCA証明書に指定してcurlを実行
$ curl -v --cacert ./server.crt https://43.206.227.156 
*   Trying 43.206.227.156:443...
* Connected to 43.206.227.156 (43.206.227.156) port 443 (#0)
* ALPN: offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: ./server.crt
*  CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384
* ALPN: server accepted http/1.1
* Server certificate:
*  subject: C=XX; L=Default City; O=Default Company Ltd
*  start date: Jan  3 12:05:02 2024 GMT
*  expire date: Feb  2 12:05:02 2024 GMT
* SSL: unable to obtain common name from peer certificate
* Closing connection 0
curl: (60) SSL: unable to obtain common name from peer certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

今度は証明書にcommon nameを設定していないことでエラーとなりました。
インスタンスに接続し、証明書を作り直します。

$ sudo su -
# CSR作成
$ openssl req -new -key /etc/httpd/conf/server.key > /etc/httpd/conf/server.csr
# CRT作成。common name に Webserver と設定。
$ openssl x509 -req -signkey /etc/httpd/conf/server.key < /etc/httpd/conf/server.csr > /etc/httpd/conf/server.crt
Certificate request self-signature ok
subject=C = JP, L = Default City, O = Default Company Ltd, CN = Webserver
# 設定反映
$ systemctl restart httpd

今度はローカルPCで証明書をダウンロードし直してcurlを実行します。

$ scp -i ~/.ssh/MyKeyPair.pem ec2-user@i-xxx:/etc/httpd/conf/server.crt .
server.crt      

$ curl -v --cacert ./server.crt https://43.206.227.156         
*   Trying 43.206.227.156:443...
* Connected to 43.206.227.156 (43.206.227.156) port 443 (#0)
* ALPN: offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: ./server.crt
*  CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384
* ALPN: server accepted http/1.1
* Server certificate:
*  subject: C=JP; L=Default City; O=Default Company Ltd; CN=Webserver
*  start date: Jan  3 13:17:21 2024 GMT
*  expire date: Feb  2 13:17:21 2024 GMT
* SSL: certificate subject name 'Webserver' does not match target host name '43.206.227.156'
* Closing connection 0
curl: (60) SSL: certificate subject name 'Webserver' does not match target host name '43.206.227.156'
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above. 

今度は、IPアドレスでアクセスしているので、ホスト名(IPアドレス)とSubject Name(Webserver)が一致していないとのエラーが出ました。
hostsファイルをいじって名前解決するのはできれば避けたいので、resolveオプションを利用して名前解決します。

https://www.softel.co.jp/blogs/tech/archives/6081

$ curl -v --cacert ./server.crt --resolve Webserver:443:43.206.227.156 https://Webserver
* Added Webserver:443:43.206.227.156 to DNS cache
* Hostname Webserver was found in DNS cache
*   Trying 43.206.227.156:443...
* Connected to Webserver (43.206.227.156) port 443 (#0)
* ALPN: offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: ./server.crt
*  CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384
* ALPN: server accepted http/1.1
* Server certificate:
*  subject: C=JP; L=Default City; O=Default Company Ltd; CN=Webserver
*  start date: Jan  3 13:17:21 2024 GMT
*  expire date: Feb  2 13:17:21 2024 GMT
*  common name: Webserver (matched)
*  issuer: C=JP; L=Default City; O=Default Company Ltd; CN=Webserver
*  SSL certificate verify ok.
* using HTTP/1.1
> GET / HTTP/1.1
> Host: Webserver
> User-Agent: curl/8.1.2
> Accept: */*
> 
< HTTP/1.1 403 Forbidden
< Date: Wed, 03 Jan 2024 13:25:06 GMT
< Server: Apache/2.4.58 (Amazon Linux) OpenSSL/3.0.8
< Last-Modified: Mon, 11 Jun 2007 18:53:14 GMT
< ETag: "2d-432a5e4a73a80"
< Accept-Ranges: bytes
< Content-Length: 45
< Content-Type: text/html; charset=UTF-8
< 
<html><body><h1>It works!</h1></body></html>

ようやくcurlでリクエストに成功しました。
上記では403コードが返却されていますが、403のときに返却される内容が正常に返却されていますので気にしなくてOKです。

参考書籍

https://www.amazon.co.jp/食べる!SSL!-―HTTPS環境構築から始めるSSL入門-小島-拓也-ebook/dp/B00PHC4480

Discussion