1. 背景2. X.509のプロトコル2.1. X.509のプロトコルとはX.509 は、公開鍵証明書(Public Key Certificate)の形式や認証局(CA: Certification Authority)の運用方法を定めた標準規格 X.509は、PKIの標準規格で、デジタル証明書を使ってデバイスやサーバー、ユーザー間の通信を認証し、暗号化するために使用される 2.2. X.509の特徴証明書の構造
バージョン(Version)X.509証明書のバージョン情報。現在では主にバージョン3(v3)が使用されてる シリアル番号(Serial Number)各証明書に一意に割り当てられる番号。CAによって発行された証明書を識別するために使用される 署名アルゴリズム(Signature Algorithm)証明書の署名に使用されたアルゴリズム(例: SHA256 with RSA) 発行者(Issuer) 有効期間(Validity) 証明書の有効開始日時(Not Before)と有効終了日時(Not After) 対象者(Subject)証明書が証明するエンティティ(例: ドメイン名や個人)の情報 公開鍵情報(Subject Public Key Info) 拡張情報(Extensions)証明書の用途や制約を追加で記述するセクション 例として、キー使用目的(Key Usage)、拡張キー使用目的(Extended Key Usage)、サブジェクト代替名(Subject Alternative Name)などがある 証明書の署名(Signature)発行者CAによる証明書全体の署名。証明書の改ざんを防止する 証明書の種類
エンドエンティティ証明書(End-Entity Certificate)最終的な利用者(サーバやクライアント)に発行される証明書 中間CA証明書(Intermediate CA Certificate)ルートCAとエンドエンティティ証明書の間に位置するCAに発行される証明書 チェーン認証を構築する ルートCA証明書(Root CA Certificate)信頼の基点となるCAの証明書 通常、オペレーティングシステムやブラウザにプリインストールされている 証明書チェーン(Certificate Chain)複数の証明書が連なって信頼関係を構築する仕組み エンドエンティティ証明書が中間CA証明書によって、中間CA証明書がルートCA証明書によって信頼される これにより、信頼できるルートCAから発行された証明書のみが有効と認識される 2.3. PKIとは?2.3.1. 公開鍵基盤(PKI:Public-Key Infrastructure)PKI(Public-Key Infrastructure)は公開鍵暗号を利用した認証基盤 PKIでは通信相手が本物であるかどうかの確認(認証)に公開鍵証明書(以下、証明書)を利用する PKIにおける証明書は、発行者の名前といった情報が記述されており、身分証明書の役割を果たす 身分証明書を、信頼できるTTP(Trusted Third Party)に発行してもらうことで、その身分証明書を信頼できるようにするのがPKIの特徴 2.3.2. 認証局(CA:Certification Authority)PKIで用いられる証明書は、認証局(CA:Certification Authority)によって発行される PKIでは、このCAが「信頼できる第三者」にあたる CAは、いわば身分の証明を行っている機関なので、ユーザーに強く信頼されていなければならない ユーザーは、自分が信頼しているCAが発行した証明書であれば信じられるということになる
CAの仕組み 2.3.3. X.509証明書(Cert:Certificate)
証明書の中味 2.3.4. オンライン証明書の失効状態の確認(OCSP)OCSP(Online Certificate Status Protocol)は、証明書の失効をリアルタイムで確認する仕組み 暗号化やデジタル署名に用いるX.509証明書の有効期限前に失効している証明書を、速やかに知ることができる
OCSP 2.3.5. PKIのアナロジーPKIはある意味運転免許とおなじような仕組み。
運転免許の発行の仕組み: PKI 運転免許証:X.509証明書(pemファイル形式) 公安委員会:CA 運転免許証の有効性の確認:OCSP 他には、印鑑証明書もおなじ仕組み。
PKIの例 2.4. X.509証明書の詳細2.4.1. 証明書の拡張子概ね、X.509証明書は次の3つにおフォーマットがある。
拡張子/形式 エンコード形式 ファイル内容の形式 主な用途 .pem Base64 テキスト形式 Linux系、Apache、汎用的な用途 .crt Base64 または DER テキストまたはバイナリ形式 Webサーバー、ブラウザ .cer Base64 または DER テキストまたはバイナリ形式 Windows環境
2.4.2. pemファイル2.4.2.1. pem証明書のフォーマットpemファイルとはpemの書式はシンプルで、任意の証明書、鍵をbase64エンコードして以下のBEGIN/END行で挟んでつなげたテキストファイル。
pemの書式(鍵+証明書)
1
2
3
4
5
6
-----BEGIN RSA PRIVATE KEY-----
鍵ファイルのbase64エンコード
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
証明書ファイルのbase64エンコード
-----END CERTIFICATE-----
Copy pemの例(証明書+鍵)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-----BEGIN CERTIFICATE-----
MIIGQTCCBSmgAwIBAgIIM5HjhF+86iswDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
・・・略・・・
HqrtjOPEFu2v5H++eeyw9WTXhfOD2H6t/pXD6A62E6O0q21FiIZnITQlxvDCLqq6
I+0w8Lymm9iXGYdxzm6tCEekpzYyJ5TiiMGKMCez8IjFq7hrEqiARoE0U7zR1JOP
qPTsfdoBrs5Wr8szzLo6NMMLknnT
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAuvvBkPx5p5HIRbSnihQAlGP8CKTrH+981nMhU93OWWBzmXBT
IULoO2Ah2a57H3Kxt5UHzyvawqGXOMgYk5SYq13Wnd7yqQA5JIKg+3jS2vd2C8oQ
・・・略・・・
SV4ZZCLL0FlWadPo3NVD0nnw5JvepABwdMQzCk/47efVA8LugjBGApWSpyCEEqRk
1uS4FuJSyQGczMcHLnVHBhlyfTt6DtFtwFqI3coimOU1cQj0Gg9w
-----END RSA PRIVATE KEY-----
Copy このままでは人の目では証明書・鍵の情報が見えないので、
それを見やすくするには openssl コマンドを使う。
2.4.2.2. pem証明書の中身の例example.orgでlet’s encryptで発行した例。Issuerはlet’s encryptで、subjectはexample.org.になっている
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
$ openssl x509 -text -noout -in cert.pem
Certificate:
Data:
Version: 3 ( 0x2)
Serial Number:
03:5e:45:ba:d3:d7:57:c4:45:b6:a3:6a:5e:d5:9a:1b:68:4c
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = US, O = Let' s Encrypt, CN = R3
Validity
Not Before: Jan 21 12:08:47 2022 GMT
Not After : Apr 21 12:08:46 2022 GMT
Subject: CN = example.org
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: ( 2048 bit)
Modulus:
00:cb:f2:2d:8f:a6:6c:fc:78:33:36:96:cc:bb:5d:
8b:bf:ff:42:81:b0:1e:9e:a3:63:ec:ef:78:fd:60:
0b:7e:62:e4:86:fc:0d:69:54:a3:b8:6a:c1:ed:43:
ce:94:61:c0:4b:67:71:07:2b:0d:36:fc:b1:c6:e5:
25:06:87:89:e4:28:6f:1c:6c:ce:12:a3:39:bd:6a:
0d:21:3f:2d:aa:5a:ad:36:b8:f3:d9:85:64:71:94:
5c:fa:c3:4c:47:74:d2:4c:41:82:4f:0a:64:ec:b2:
b0:88:3d:d2:2d:93:4f:0e:7d:28:94:09:54:d9:df:
0f:af:c9:cb:db:30:04:e3:aa:10:80:41:f3:35:80:
11:6e:57:e3:fb:1a:01:7f:11:97:87:55:10:4b:06:
e9:fb:fa:b4:e6:9c:1b:e9:18:a7:ad:23:e0:8e:96:
9f:47:aa:a9:72:75:3f:c3:c9:50:5b:42:6e:f3:0b:
49:c3:5e:3d:d5:04:66:d4:54:f7:95:0c:10:5b:2d:
2c:04:fa:fe:9d:7e:27:1e:1e:c6:51:f8:7f:a5:97:
51:93:19:29:f5:86:be:23:43:36:a9:4a:29:4f:bb:
d1:5f:dc:66:e7:73:a0:d1:97:87:fc:e5:56:ac:e3:
85:53:a8:e4:f3:fe:02:52:95:e9:6a:7b:5c:e0:12:
9d:cd
Exponent: 65537 ( 0x10001)
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Authentication
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Subject Key Identifier:
9B:BF:4D:2A:49:41:A9:B6:3D:DD:D9:B2:27:94:71:5D:C0:FC:D3:34
X509v3 Authority Key Identifier:
keyid:14:2E:B3:17:B7:58:56:CB:AE:50:09:40:E6:1F:AF:9D:8B:14:C2:C6
Authority Information Access:
OCSP - URI:http://r3.o.lencr.org
CA Issuers - URI:http://r3.i.lencr.org/
X509v3 Subject Alternative Name:
DNS:example.org, DNS:www.example.org
X509v3 Certificate Policies:
Policy: 2.23.140.1.2.1
Policy: 1.3.6.1.4.1.44947.1.1.1
CPS: http://cps.letsencrypt.org
CT Precertificate SCTs:
Signed Certificate Timestamp:
Version : v1 ( 0x0)
Log ID : 46:A5:55:EB:75:FA:91:20:30:B5:A2:89:69:F4:F3:7D:
11:2C:41:74:BE:FD:49:B8:85:AB:F2:FC:70:FE:6D:47
Timestamp : Jan 21 13:08:47.935 2022 GMT
Extensions: none
Signature : ecdsa-with-SHA256
30:45:02:21:00:DF:B2:A6:49:8C:8A:48:48:04:A3:59:
07:0F:6B:E5:7D:AD:84:48:74:0E:95:3E:37:A5:B2:D1:
8F:E7:44:01:A7:02:20:5C:37:5C:89:B0:49:BD:0E:D7:
C1:50:37:E1:97:5B:8B:CD:9C:95:13:95:45:C3:B0:80:
14:19:C2:66:66:DF:84
Signed Certificate Timestamp:
Version : v1 ( 0x0)
Log ID : 41:C8:CA:B1:DF:22:46:4A:10:C6:A1:3A:09:42:87:5E:
4E:31:8B:1B:03:EB:EB:4B:C7:68:F0:90:62:96:06:F6
Timestamp : Jan 21 13:08:48.405 2022 GMT
Extensions: none
Signature : ecdsa-with-SHA256
30:45:02:20:01:D0:6F:15:9C:B3:6E:9A:07:A5:45:07:
DE:C3:84:5D:5E:49:A4:BA:F2:AA:20:D5:E5:12:55:1F:
EE:97:F7:78:02:21:00:88:47:A9:41:9E:08:76:BA:A4:
73:ED:CE:43:65:A6:06:1F:78:EB:0D:1A:C5:D0:6C:66:
48:B5:DB:DA:F6:C3:1C
Signature Algorithm: sha256WithRSAEncryption
7c:e3:54:0f:63:57:4d:6e:7f:01:bd:8e:ec:5f:a2:72:73:ee:
da:79:2e:ca:2a:d4:13:f6:b7:af:52:d1:c2:84:0d:f2:9d:c1:
37:03:eb:af:4d:76:61:dc:ca:37:a3:c1:f3:76:85:b7:d1:9e:
5e:51:7a:02:9b:74:12:34:43:d7:94:fe:26:28:f3:12:c2:f1:
0a:ad:72:47:56:bd:d8:bb:4f:60:bc:55:51:62:dd:fb:b9:7b:
58:46:30:a2:c6:7d:8c:95:03:3e:16:c2:d3:5d:b0:f5:ef:e5:
97:09:67:35:20:ac:25:16:48:6a:16:25:ef:10:12:97:2f:4f:
1d:d0:8c:4e:49:9f:fb:c9:2b:9e:92:6f:8d:d4:c6:76:85:d3:
64:16:55:d5:69:81:e9:55:0e:46:2f:77:8a:86:bf:50:99:e8:
ae:3b:19:53:ab:d4:d6:72:3e:d9:27:a6:75:7b:c7:67:48:f6:
7a:e0:b9:23:5c:ad:8a:bd:4f:7f:60:01:99:00:68:ce:5d:72:
c4:b7:54:65:47:c7:01:45:af:1c:1e:cf:48:dd:e4:bc:b0:40:
90:e7:a3:64:d6:84:af:aa:96:fa:99:3e:83:fd:e0:dc:27:cd:
c3:51:66:16:2d:c7:f4:8f:76:dd:7c:f3:00:ff:5c:f9:e5:c7:
f4:ea:5f:d7
Copy 2.4.2.3. pem証明書の中身の意味 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Certificate:
Data (データ部)
Version (X509のバージョン)
Serial Number (シリアルナンバー)
これにより同一CAで同一機関への証明書を2度作っても区別できる
Signature Algorithm
Signature Algorithm (CAが利用する暗号化アルゴリズム)
Issuer (証明書の発行者)
Validity (有効期限)
Not BeforeからNot Afterまで有効
Subject (証明される対象)
Subject Public Key Info (証明される対象の公開鍵に関する情報)
Public Key Algorithm (公開鍵のアルゴリズム)
RSA Public Key (公開鍵)
X509v3 extensions (X509 Version.3の拡張部)
X509v3 Subject Key Identifier (サブジェクトの公開鍵の識別番号)
X509v3 Authority Key Identifier (この証明書を発行したCAに関する情報)
keyid
DirName
X509v3 Basic Constraintsextensions
CA (SubjectのがCAかどうか)
Signature Algorithm (CAが利用する暗号化アルゴリズム)
Signature(デジタル署名)
Copy 2.4.2.4. ASN.1とは?ASN.1はAbstract Syntax Notation Oneの意味 下のような形式のドキュメント pemファイルはこの形式に従っている 1
2
3
4
5
6
7
8
9
10
11
12
13
Foo DEFINITIONS ::= BEGIN
Question ::= SEQUENCE {
id INTEGER,
question IA5String
}
Answer ::= SEQUENCE {
id INTEGER,
answer BOOLEAN
}
END
Copy 3. ACME3.1. ACMEとは?ACMEは、X.509証明書の自動ドメイン検証とインストールのための標準プロトコル ACMEはAutomated Certificate Management Environment(自動証明書管理環境)の意味 つまり、Webサーバと認証局との間の相互作用を自動化するための通信プロトコル、いわゆるチャレンジ方式のこと 3.2. ACMEチャレンジとは?ACMEチャレンジは、ACMEプロトコルに基づいて実行される「ドメイン所有権を証明するための特定のタスクや手続き」のこと 証明書を発行する前に、認証局(CA)がドメインの所有者であることを確認するために実施する 3.3. ACMEチャレンジの種類DNS-01チャレンジ :ドメインのDNS設定に特定のTXTレコードを追加し、CAがDNSを確認 HTTP-01チャレンジ :ウェブサーバーに特定のトークンを含むファイルを配置し、CAがそのファイルをHTTPで確認 TLS-ALPN-01チャレンジ :サーバーが特定のTLSプロトコルを利用して応答し、CAがそれを検証 3.4. ACMEチャレンジのフローHTTP-01のACMEチャレンジのフロー
HTTP-01 3.5. ACMEチャレンジの詳細3.5.1. DNS-01 チャレンジあるドメインのSSL証明書をリクエストしたユーザーが、そのドメインに対する権限を有しているかを、DNSサーバーを通じて確認・認証する方式 DNS サーバー上に認証用のTXT レコードを生成し、Let’s Encrypt 側からその TXT レコードを読み取ることで認証を行う TXTレコードは手動で設定するか、自動化したスクリプトなどで設定する いわゆる、AWS Certificate Managerでやっていること
DNS-01 3.5.2. HTTP-01チャレンジHTTP-01 チャレンジは、あるドメインのSSL証明書をリクエストしたユーザー(Web サーバー)がそのドメインに対する権限を有しているかを HTTP通信経由で確認・認証する方式 Web サーバー上に認証用のファイルを生成し、Let’s Encrypt 側からそのファイルにHTTPアクセスすることで認証を行う いわゆるcertbotの--webroot
を指定した時の処理
HTTP-01 3.5.3. tls-sni-01チャレンジ とはHTTPS(443)をつかうデジタル署名の更新手法 4. オレオレ証明書4.1. オレオレ証明書正式な認証機関(CA)によって発行されていない、自己署名のSSL/TLS証明書のこと 通常のSSL証明書は、信頼されたCAによって署名され、第三者による認証が行われる 4.2. Lets’encrypt vs. オレオレ証明書Let’s EncryptはISRGがRootで、ブラウザから信頼されている。
証明書Viewer 他方、自己証明書の場合は信頼されない。
証明書Viewer なお、CNはCommon Name(コモンネーム)の略称 これは証明書の主体を一意に識別するためのフィールド 4.3. オレオレ証明書の作成4.3.1. CA無し1
$ openssl req -nodes -newkey rsa:2048 -keyout privkey.pem -x509 -sha256 -days 1 -out chain.pem -subj "/C=JP/ST=Tokyo/L=Shinjuku-ku/O=EXAMPLE CORPORATION/OU=EXAMPLE DEPARTMENT/CN=`curl ifconfig.co`"
Copy 引数
req
秘密鍵と証明書を一度に生成する 使いまわすわけではないので、証明書が出来てしまえばCSRは不要なので生成しない -nodes
node
の複数形ではなく、no DES
= 秘密鍵を暗号化しないの意味-newkey rsa:2048
RSA
で 2048bit
の秘密鍵を生成を指定する-keyout privkey.pem
-x509
証明書の生成を示す req
コマンドはデフォルトだと CSR
生成-sha256
SHA256
の証明書が生成されるデフォルトだと SHA1
なので注意 -days 1
で有効日数を指定する 0日や1日未満での指定はできないが -days -1
とすれば遡った指定もできる(必要性はないだろうけど) -out chain.pem
-subj "/C=.../ST=.../L=.../O=.../OU=.../CN=..."
CSRを生成する場合に指定するのと同じで証明書の情報 4.3.2. CA有りオレオレ中間CAの生成
1
$ openssl req -x509 -sha256 -days 3650 -subj "/C=JP/ST=Tokyo/L=Shinjuku-ku/O=EXAMPLE CORPORATION/OU=EXAMPLE DEPARTMENT/CN=My Private CA" -nodes -newkey rsa:2048 -keyout cakey.pem -out chain.pem
Copy 自身のIPに対するCSRと秘密鍵の生成
1
$ openssl req -subj "/C=JP/ST=Tokyo/L=Shinjuku-ku/O=EXAMPLE CORPORATION/OU=EXAMPLE DEPARTMENT/CN=`curl ifconfig.co`" -nodes -newkey rsa:2048 -keyout privkey.pem -out csr.pem
Copy オレオレ認証局による証明書の生成
1
$ openssl x509 -req -sha256 -days 3650 -in csr.pem -CA chain.pem -CAkey cakey.pem -set_serial 1 -out fullchain.pem
Copy 4.4. 独自のRootCAのインストール方法前提として、Ubuntu Linuxを使っている。
CA更新コマンド
1
2
3
4
5
6
7
$ cd /usr/share/ca-certificates
$ mkdir mylocal
$ cp somewhere/mylocal-root-cacert.crt mylocal/
$ cp -p /etc/ca-certificates.conf /etc/ca-certificates.conf.bak
$ echo "mylocal/mylocal-root-cacert.crt" >> /etc/ca-certificates.conf
$ update-ca-certificates
$ ls -l /etc/ssl/certs/ | grep mylocal-root-cacert
Copy コマンドの意味
フォルダの作成/usr/share/ca-certificates/
の下に適当なディレクトリを作成するここでは、mylocal
とする 以下、/usr/share/ca-certificates/mylocal/
とする ディレクトリは管理組織単位などでまとめるためのもの(おそらく、mozilla/
というディレクトリがあると思うが、これは触らない) ディレクトリは無くても構わないが、ローカルなものは、ディレクトリでまとめた方が、後々、管理しやすい 証明書の設置作成したフォルダmylocal
の下に、証明書ファイルを置く ここでは、mylocal-root-cacert.crt
とする つまり、/usr/share/ca-certificates/mylocal/mylocal-root-cacert.crt
となる 設定ファイルの編集/etc/ca-certificates.conf
に、追加したファイルの /usr/share/ca-certificates/
以下の相対パスを追加するつまり、mylocal/mylocal-root-cacert.crt
を追加する CA-certの更新update-ca-certificates
(/usr/sbin/update-ca-certificates
)を実行する シンボリックリンクの確認/etc/ssl/certs/
下に /usr/share/ca-certificates/mylocal/mylocal-root-cacert.crt
へのシンボリックリンクができていることを確認 NOTE:
余談だが、これが正しく行われると、CA bundleとしてすべてのCA証明書が含まれる/etc/ssl/certs/ca-certificates.crt
が作成される なので、必要に応じて、これを参照すると良い 例python の requests()
の引数に CA Bundleのパスを指定するような場合、 update-ca-certificates
が自動的に /etc/ssl/certs/ca-certificates.crt
をアップデートするなのので、独自に追加するのは、厳禁 5. Let’s Encryptの基本5.1. Let’s Encryptとは?Let’s Encrypt は、無料で自動化された証明書発行サービスを提供する認証局(CA) Webサイトが HTTPS(TLS/SSL)通信を導入するために必要な X.509 証明書を簡単に取得・更新できる仕組みを提供する 5.2. Certbotとは?Certbotとは Certbotは無料かつ自動でSSL証明書を発行できるツール CSRとKEYファイルの作成からWebサーバーの設定まで自動で行ってくれる さらにCronと組み合わせることで、証明書の更新作業までも完全に自動化することが可能 5.3. ISRGとEFFISRG(Internet Security Research Group)HTTPSの普及を通じて、インターネット全体のセキュリティを向上させることを主な目的とする非営利団体 EFF(Electronic Frontier Foundation)プライバシー、表現の自由、オープンインターネットの推進を目指す非営利団体 6. Certbot6.1. HTTPSまでの流れ有効なドメイン名を取得ドメインのDNS設定で、サーバーのIPアドレスに向ける NGINXのインストールと基本設定を行う NGINXをインストールドメイン名に対応するサーバーブロック(仮想ホスト)を設定 ポート80でHTTPリクエストを受け付けるように設定 CertbotのインストールCertbotはLet’s Encryptの公式クライアントで、SSL証明書の取得と更新を自動化 サーバーにCertbotをインストール SSL証明書の取得Certbotを使用して、Let’s Encryptから無料のSSL証明書を取得 ウェブサーバーの設定に応じて、webroot
やstandalone
モードを選択 NGINXのSSL設定取得した証明書を使用して、NGINXでSSLを有効化 ポート443でHTTPSリクエストを受け付けるように設定 HTTPからHTTPSへのリダイレクトを設定 SSL証明書の自動更新設定Let’s Encryptの証明書は有効期限が90日間 Certbotをcronやsystemdタイマーで定期的に実行し、証明書を自動更新 6.2. Certbotのプラグインとオプションの違い--dns-<provider>
オプションチャレンジタイプ : DNS-01チャレンジ 使用条件 : DNSプロバイダのAPI経由でTXTレコードを変更できる場合特徴 :ポート80や443を開放する必要がなく、内部ネットワークやファイアウォールの背後でも使用可能 自動化が可能で、サーバーの公開ポートに依存しません --webroot
オプションチャレンジタイプ : HTTP-01チャレンジ 使用条件 : ポート80がアクセス可能で、既存のウェブサーバーが稼働している場合特徴 :ウェブサーバーを停止する必要がなく、既存のウェブサーバーのドキュメントルートを利用 自動化が容易で、ウェブサーバーの設定変更が最小限で済む --standalone
オプションチャレンジタイプ : HTTP-01チャレンジ 使用条件 : ウェブサーバーがない、または一時的に停止できる場合特徴 :Certbotが一時的なウェブサーバーを起動し、ポート80または443を使用 既存のウェブサーバーが稼働している場合は停止が必要 --nginx
オプションチャレンジタイプ : HTTP-01チャレンジ 使用条件 : NGINXプラグイン であり、NGINXが稼働している場合に利用特徴 :CertbotがNGINXの設定を自動的に編集し、SSLを有効化 ウェブサーバーの停止が不要で、設定のバックアップや復元も自動化 HTTPからHTTPSへのリダイレクト設定も自動的に行われる 他には、--apache
プラグイン(オプション)もある。
6.3. AuthenticatorとInstallerの違いCertbotはAuthenticatorとInstallerを柔軟に組み合わせることで、様々な環境でSSLの取得と設定を自動化する。
Authenticator(認証器)役割: ドメインの所有権を証明するために、Let’s Encryptのサーバーと通信して認証を行うプラグイン 機能: ドメインの認証プロセス(チャレンジ)を処理する Installer(インストーラー)役割: 取得したSSL証明書をウェブサーバーや他のサービスにインストールし、必要な設定変更を自動的に行うプラグイン 機能: サーバーの設定ファイルを編集し、SSL/TLSの設定を有効化 6.4. Certbotの使用するファイルフォルダの意味
/etc/letsencrypt/live/
/etc/letsencrypt/archive/
/etc/letsencrypt/renewal/
証明書の更新に必要なパラメータ持つファイル(xxx.conf
)を保持 自動更新プロセスでそのパラメーターを使用 ファイルの意味
cert.pem
: サーバー証明書privkey.pem
: 秘密鍵chain.pem
: 中間証明書fullchain.pem
: サーバー証明書と中間証明書を結合したものwebroot
の設定ファイルの例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#########################################
# Liveの証明書の設定
#########################################
# renew_before_expiry = 30 days 証明書の有効期限
version = 0.40.0
archive_dir = /etc/letsencrypt/archive/example.org # 過去の証明書と鍵が保存されているディレクトリ
# 最新の証明書や鍵へのシンボリックリンクのパス
cert = /etc/letsencrypt/live/example.org/cert.pem
privkey = /etc/letsencrypt/live/example.org/privkey.pem
chain = /etc/letsencrypt/live/example.org/chain.pem
fullchain = /etc/letsencrypt/live/example.org/fullchain.pem
#########################################
# 証明書の更新時に使用されるパラメータ
#########################################
# Options used in the renewal process
[renewalparams]
account = cde5598f6f44f9f55646b9d23817ed48 # Let's Encryptアカウントのハッシュ値
authenticator = webroot # ドメインの認証方法
webroot_path = /var/www/html # プラグインが使用するウェブルートのパス
[[webroot_map]] # ドメインごとにウェブルートのパスをマッピング
example.org = /var/www/html
www.example.org = /var/www/html
server = https://acme-v02.api.letsencrypt.org/directory # Let's EncryptのACMEプロトコルのエンドポイントURL
Copy 6.5. --staging
と--dry-run
オプションの違い両方ともLet’s Encrypt のレートリミットを回避できるが次の違いがある。
--staging
テスト用の証明書を実際に取得し、ディスクに保存する 証明書の取得や設定のテストに使用される テスト後は証明書が残るため、設定ファイルやディレクトリ構造を確認できる --dry-run
証明書の取得プロセスをシミュレートするが、証明書は保存されない 自動更新のテストやエラーの検証に使用される ディスクに変更を加えないため、安全にテストできる 6.6. オプションの違い6.6.1. --nginx
SSL/TLS サーバ証明書を取得と、Nginxへの証明書のインストール(Nginx設定ファイルの書き換え)を自動的に行う Certbot 0.9.0以降に、Nginxプラグインのアルファ版が同梱されている 設定ファイル
authenticator
とinstaller
はnginxになっているwebroot
と違い、.well-known/acme-challenge
フォルダをnginxで設定することは不要 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ cat /etc/letsencrypt/renewal/example.org.conf
# renew_before_expiry = 30 days
version = 0.40.0
archive_dir = /etc/letsencrypt/archive/example.org
cert = /etc/letsencrypt/live/example.org/cert.pem
privkey = /etc/letsencrypt/live/example.org/privkey.pem
chain = /etc/letsencrypt/live/example.org/chain.pem
fullchain = /etc/letsencrypt/live/example.org/fullchain.pem
# Options used in the renewal process
[ renewalparams]
account = f67679fe4ad67d525774e1e37c0bb36a
authenticator = nginx
installer = nginx
server = https://acme-v02.api.letsencrypt.org/directory
Copy 6.6.2. --webroot
動作の流れは以下のようになる 事前準備 アクターCertbot Let’s encrypt server Nginx server 動作CertbotはLet’s Encrypt Serverに証明書発行依頼をし、トークンを発行してもらう CertbotはACMEプロトコルに定義されている.well-known/acme-challenge
にそのトークンを設置する Let’s Encrypt の認証サーバがHTTPリクエストで.well-known/acme-challenge
にグローバルからアクセスし、そのトークンのチェックを行う トークンが正しければLet’s Encryptの証明書を発行しCertbotに返す Certbotは証明書を/etc/letsencrypt
以下に設置する
HTTP-01の動作 準備として、ACMEプロトコル用のフォルダを作る。
1
$ mkdir /var/www/html/acme-challenge/
Copy また、NGINXの設定は次のような感じになる。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ cat /etc/nginx/conf.d/my-site.conf
server {
listen 80;
server_name example.org www.example.org;
location ^~ /.well-known/acme-challenge/ {
root /var/www/html/acme-challenge;
}
location / {
return 301 https://$host$request_uri ;
}
}
server {
listen 443 ssl;
server_name www.example.org;
ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem;
}
Copy 下のように指定した場合は、指定した全てのドメイン名に使用できる1枚のSSL/TLSサーバ証明書が発行される。
1
2
3
$ certbot certonly --webroot \
-w /var/www/example/ -d www.example.com -d example.com \
-w /var/www/other -d other.example.net -d another.other.example.net
Copy renewalの設定ファイルは次のようになっている authenticator
がwebroot
になっている 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ cat /etc/letsencrypt/renewal/example.org.conf
# renew_before_expiry = 30 days
version = 0.40.0
archive_dir = /etc/letsencrypt/archive/example.org
cert = /etc/letsencrypt/live/example.org/cert.pem
privkey = /etc/letsencrypt/live/example.org/privkey.pem
chain = /etc/letsencrypt/live/example.org/chain.pem
fullchain = /etc/letsencrypt/live/example.org/fullchain.pem
# Options used in the renewal process
[ renewalparams]
account = cde5598f6f44f9f55646b9d23817ed48
authenticator = webroot
webroot-path = /var/www/html/.well-known/acme-challenge/
server = https://acme-v02.api.letsencrypt.org/directory
Copy 6.6.3. --standalone
Webrootの自動版、チャレンジタイプはhttp-01となる ドメイン使用権者の認証の際には、Certbotクライアントに内蔵されている簡易的なウェブサーバが機能が一時的に使われる このプラグインは80や443のPortをListenしている動作中のウェブサーバを一時的に終了させる必要がある 7. 実際のSSLの設定の例7.1. 前提DockerでNginxとCertbotを使う ドメインはApexドメインとwwwサブドメインを使う apexドメインはwwwサブドメインにリダイレクトする そのリダイレクトはNGINXでリバースプロキシで行う ドメイン名は仮にhoge.netにする フォルダ構造
1
2
3
4
5
6
7
8
9
10
$ tree -L 2
.
├── certbot
│ ├── conf/
│ └── www/
├── docker-compose.yaml
├── docker-compose.cert.yaml
├── nginx
│ ├── nginx_init.conf
│ └── nginx_ssl.conf
Copy 7.2. DNSの設定apexドメインはCNAMEが使えないのでAレコードを使う apwxドメインはAレコードでServerアドレスにする wwwサブドメインはCNAMEでServerのホスト名 1
2
3
4
5
6
7
$ nslookup hoge.net
Server: 192.168.3.1
Address: 192.168.3.1#53
Non-authoritative answer:
Name: hoge.net
Address: 153.123.52.130
Copy 7.3. UFWの設定 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$sudo ufw status
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
80/tcp ALLOW Anywhere
443/tcp ALLOW Anywhere
4649 ALLOW Anywhere
22 DENY Anywhere
8000 ALLOW Anywhere
80 ALLOW Anywhere
443 ALLOW Anywhere
OpenSSH ( v6) ALLOW Anywhere ( v6)
80/tcp ( v6) ALLOW Anywhere ( v6)
443/tcp ( v6) ALLOW Anywhere ( v6)
4649 ( v6) ALLOW Anywhere ( v6)
22 ( v6) DENY Anywhere ( v6)
8000 ( v6) ALLOW Anywhere ( v6)
80 ( v6) ALLOW Anywhere ( v6)
443 ( v6) ALLOW Anywhere ( v6)
Copy 7.4. DNSの確認仮サーバーを起動する。
1
$ sudo python3 -m http.server 80
Copy アクセスしてDNSの疎通を確かめる。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ curl www.hoge.net
<!DOCTYPE HTML>
<html lang = "en" >
<head>
<meta charset = "utf-8" >
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
<li><a href = "certbot/" >certbot/</a></li>
<li><a href = "django/" >django/</a></li>
<li><a href = "docker-compose.yaml" >docker-compose.yaml</a></li>
<li><a href = "Makefile" >Makefile</a></li>
<li><a href = "nginx/" >nginx/</a></li>
<li><a href = "postgres/" >postgres/</a></li>
</ul>
<hr>
</body>
</html>
Copy 7.5. nginxとcertbotで起動docker-compose.cert.yaml
は以下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
services :
nginx :
image : nginx
volumes :
- ./nginx/nginx_init.conf:/etc/nginx/nginx.conf
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
ports :
- "80:80"
certbot :
image : certbot/certbot
depends_on :
- nginx
volumes :
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
Copy フォルダの関係配下。
host: certbot/www/
certbot: /var/www/certbot/'
nginx: /var/www/certbot/
host: certbot/conf/
certbot: /etc/letsencrypt/
nginx: /etc/letsencrypt/
url: /.well-known/acme-challenge/
nginx_init.conf
の中身は以下。
1
2
3
4
5
6
7
8
9
10
11
12
13
events { }
http {
server {
listen 80;
server_name hoge.net www.hoge.net;
# Certbot challenge location
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
}
}
Copy 起動
1
2
$ docker compose -f docker-compose.cert.yaml down --remove-orphans
$ docker compose -f docker-compose.cert.yaml up -d
Copy プロセスの確認
1
2
3
$ docker compose -f docker-compose.cert.yaml ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
docker-nginx-1 docker-nginx "/docker-entrypoint.…" nginx 20 minutes ago Up 20 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp
Copy 外部からのアクセス確認
1
2
3
4
5
6
7
8
$ curl www.hoge.net/.well-known/acme-challenge/
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.27.2</center>
</body>
</html>
Copy 空なので404でOK。
log
1
2
3
4
$ docker compose -f docker-compose.cert.yaml logs
nginx-1 | 2024/11/28 16:26:20 [ error] 28#28: *11 "/var/www/certbot/.well-known/acme-challenge/index.html" is not found ( 2: No such file or directory) , client: 153.122.52.107, server: hoge.net, request: "GET /.well-known/acme-challenge/ HTTP/1.1" , host: "www.hoge.net"
certbot-1 | Saving debug log to /var/log/letsencrypt/letsencrypt.log
certbot-1 | Certbot doesn't know how to automatically configure the web server on this system. However, it can still get a certificate for you. Please run "certbot certonly" to do so. You' ll need to manually configure your web server to use the resulting certificate.
Copy アクセスログが残っているのでOK。
certの発行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ docker compose -f docker-compose.cert.yaml run --rm certbot certonly --webroot --webroot-path= /var/www/certbot \
-d hoge.net -d www.hoge.net \
--email support@hoge.net --agree-tos --no-eff-email
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Account registered.
Requesting a certificate for hoge.net and www.hoge.net
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/hoge.net/fullchain.pem
Key is saved at: /etc/letsencrypt/live/hoge.net/privkey.pem
This certificate expires on 2025-02-26.
These files will be updated when the certificate renews.
NEXT STEPS:
- The certificate will need to be renewed before it expires. Certbot can automatically renew the certificate in the background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal-setup for instructions.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
* Donating to ISRG / Let' s Encrypt: https://letsencrypt.org/donate
* Donating to EFF: https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Copy すると、Certが生成される(rootじゃないと見れないので注意)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ sudo tree -L 4 certbot/
certbot/
├── conf
│ ├── accounts
│ │ └── acme-v02.api.letsencrypt.org
│ │ └── directory
│ ├── archive
│ │ └── hoge.net
│ │ ├── cert1.pem
│ │ ├── chain1.pem
│ │ ├── fullchain1.pem
│ │ └── privkey1.pem
│ ├── live
│ │ ├── hoge.net
│ │ │ ├── cert.pem -> ../../archive/hoge.net/cert1.pem
│ │ │ ├── chain.pem -> ../../archive/hoge.net/chain1.pem
│ │ │ ├── fullchain.pem -> ../../archive/hoge.net/fullchain1.pem
│ │ │ ├── privkey.pem -> ../../archive/hoge.net/privkey1.pem
│ │ │ └── README
│ │ └── README
│ ├── renewal
│ │ └── hoge.net.conf
│ └── renewal-hooks
│ ├── deploy
│ ├── post
│ └── pre
├── Dockerfile
└── www
Copy certがliveにsymbolic linkとしてあるのでOk。
初回用のdockerプロセスを終了する。
1
$ docker compose -f docker-compose.cert.yaml down --remove-orphans
Copy 後処理の確認
1
2
$ docker compose -f docker-compose.cert.yaml ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
Copy 7.6. SSLで起動SSL用のnginx.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
events { }
http {
#############################################
# HTTP apex and www to https
#############################################
server {
listen 80;
server_name hoge.net www.hoge.net;
# Certbot challenge location
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# Redirect all other traffic to HTTPS
location / {
return 301 https://$host$request_uri;
}
}
#############################################
# HTTPS apex to www redirect
#############################################
server {
listen 443 ssl;
server_name hoge.net;
ssl_certificate /etc/letsencrypt/live/hoge.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/hoge.net/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
return 301 https://www.hoge.net$request_uri;
}
#############################################
# HTTPS www handling
#############################################
server {
listen 443 ssl;
server_name www.hoge.net;
ssl_certificate /etc/letsencrypt/live/hoge.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/hoge.net/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://backend:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
Copy ssl用のdocker-compose.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
services :
nginx :
image : nginx
volumes :
- ./nginx/nginx_ssl.conf:/etc/nginx/nginx.conf
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
ports :
- "80:80"
- "443:443"
certbot :
image : certbot/certbot
volumes :
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
entrypoint : "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
backend :
image : python:3
command : python -m http.server 8000
expose :
- "8000"
Copy 起動
1
2
$ docker compose -f docker-compose.yaml down --remove-orphans
$ docker compose -f docker-compose.yaml up -d
Copy ps確認
1
2
3
4
5
6
7
$ make ps
docker compose -f docker-compose.yaml ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
docker-certbot-1 docker-certbot "/bin/sh -c 'trap ex…" certbot 27 seconds ago Up 26 seconds 80/tcp, 443/tcp
docker-nginx-1 docker-nginx "/docker-entrypoint.…" nginx 27 seconds ago Up 25 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp
docker-backend-1 python:3 "python -m http.serv…" backend 27 seconds ago Up 25 seconds 8000/tcp
Copy アクセス確認。
http => httpsへリダイレクト https + apexドメイン => https + wwwへリダイレクト 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
$ curl http://hoge.net
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.27.2</center>
</body>
</html>
$ curl http://www.hoge.net
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.27.2</center>
</body>
</html>
$ curl https://hoge.net
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.27.2</center>
</body>
</html>
$ curl https://www.hoge.net
<!DOCTYPE HTML>
<html lang = "en" >
<head>
<meta charset = "utf-8" >
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
<li><a href = ".dockerenv" >.dockerenv</a></li>
<li><a href = "bin/" >bin@</a></li>
<li><a href = "boot/" >boot/</a></li>
<li><a href = "dev/" >dev/</a></li>
<li><a href = "etc/" >etc/</a></li>
<li><a href = "home/" >home/</a></li>
<li><a href = "lib/" >lib@</a></li>
<li><a href = "lib64/" >lib64@</a></li>
<li><a href = "media/" >media/</a></li>
<li><a href = "mnt/" >mnt/</a></li>
<li><a href = "opt/" >opt/</a></li>
<li><a href = "proc/" >proc/</a></li>
<li><a href = "root/" >root/</a></li>
<li><a href = "run/" >run/</a></li>
<li><a href = "sbin/" >sbin@</a></li>
<li><a href = "srv/" >srv/</a></li>
<li><a href = "sys/" >sys/</a></li>
<li><a href = "tmp/" >tmp/</a></li>
<li><a href = "usr/" >usr/</a></li>
<li><a href = "var/" >var/</a></li>
</ul>
<hr>
</body>
</html>
Copy OK
8. 参考文献