インストール
# yum install mod_ssl
自己署名証明書
SAN 項目を追加した設定ファイルを作成。
# cd /etc/pki/tls/ # cp openssl.cnf openssl-san.cnf
openssl.cnf と openssl-san.cnf の差分
--- openssl.cnf +++ openssl-san.cnf @@ -104,7 +104,7 @@ #################################################################### [ req ] default_bits = 2048 -default_md = sha1 +default_md = sha256 default_keyfile = privkey.pem distinguished_name = req_distinguished_name attributes = req_attributes @@ -222,6 +222,11 @@ basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName=@alt_names + +[ alt_names ] +DNS.1=takeash.net +DNS.2=*.takeash.net [ v3_ca ]
- 証明書フォルダへ移動
# cd certs/
- サーバー用秘密鍵作成 (server.key)
# openssl genrsa -aes128 2048 > server.key
- パスフレーズ削除
httpd 再起動時にパスフレーズが要求されないようにするため。# openssl rsa -in server.key -out server.key
-
サーバー用自己署名証明書作成 (server.crt)
# openssl req -utf8 -new -key server.key -x509 -days 3650 -out server.crt -set_serial 0 \ -subj '/C=JP/ST=Tokyo/L=Chuo-ku/O=TakeAsh.net/CN=takeash.net' -extensions v3_req -config ../openssl-san.cnf
-
サブジェクト例 (TakeAsh.net)
項目 用途 サンプル C 国名コード JP ST 都道府県 Tokyo L 区市町村 Chuo-ku O 組織名 TakeAsh.net CN コモンネーム(ドメイン名) takeash.net 証明書確認
「X509v3 extensions - X509v3 Subject Alternative Name」項目が存在すれば SAN が含まれている。# openssl x509 -in server.crt -text ... X509v3 Subject Alternative Name: DNS:takeash.net, DNS:*.takeash.net ...
-
/etc/httpd/conf.d/ssl.conf (抜粋)
SSLCertificateFile /etc/pki/tls/certs/server.crt SSLCertificateKeyFile /etc/pki/tls/certs/server.key DocumentRoot "/var/www/html" SSLProtocol all -SSLv2 -SSLv3
-
httpd 再起動
- CentOS 6
# service httpd restart
- CentOS 7
# systemctl restart httpd
- CentOS 6
-
動作テスト
Certbot
-
Certbot 使用前準備
- ホスト名が正引きできること。(ワイルドカード不可)
- バーチャルホストのサーバ名と要求するドメイン名のどれかが一致すること。
- インターネットから https でアクセス可能になっていること。
- https ポート解放
# firewall-cmd --permanent --add-service=https
- CertBot は Apache が稼働しているサーバ上で実行する。
Certbot インストール (EPEL リポジトリ)
# yum install python-certbot-apache
証明書取得
取得に成功すると「/etc/letsencrypt/live/<ドメイン1>/」に証明書が作成される。# certbot certonly --apache \ -m <メールアドレス> --agree-tos \ -d <ドメイン1> [-d <ドメイン2> ...]
/etc/httpd/conf.d/ssl.conf (抜粋)
Listen 443 https SSLEngine on SSLCertificateFile /etc/letsencrypt/live/<ドメイン1>/cert.pem SSLCertificateKeyFile /etc/letsencrypt/live/<ドメイン1>/privkey.pem SSLCertificateChainFile /etc/letsencrypt/live/<ドメイン1>/chain.pem
/etc/httpd/conf.d/VirtualHosts.conf (抜粋)
バーチャルホスト毎に SSL 設定が必要。<VirtualHost *:80 *:443> ServerName vh1.<ドメイン1> DocumentRoot /var/www/vh1-html/ SSLEngine on SSLCertificateFile /etc/letsencrypt/live/<ドメイン1>/cert.pem SSLCertificateKeyFile /etc/letsencrypt/live/<ドメイン1>/privkey.pem SSLCertificateChainFile /etc/letsencrypt/live/<ドメイン1>/chain.pem <Directory "/var/www/vh1-html"> AllowOverride All </Directory> </VirtualHost>
自動更新スクリプト /etc/cron.monthly/certbot.sh
#!/bin/bash /bin/certbot renew
Certbot (ワイルドカード, 手動)
Certbot インストール (EPEL リポジトリ)
# yum -y install yum-utils # yum-config-manager --enable rhui-REGION-rhel-server-extras rhui-REGION-rhel-server-optional # yum install certbot-apache
証明書取得(手動)
途中HTTPへのテキストファイルの配置とDNSへのTXTレコードの追加を指示されるので、追加してからEnterを押して先へ進む。
取得に成功すると「/etc/letsencrypt/live/<ドメイン>/」に証明書が作成される。# certbot certonly --manual \ --server https://acme-v02.api.letsencrypt.org/directory \ -d "*.example.com" -d example.com
/etc/httpd/conf.d/ssl.conf (抜粋)
Listen 443 https SSLEngine on SSLCertificateFile /etc/letsencrypt/live/<ドメイン>/cert.pem SSLCertificateKeyFile /etc/letsencrypt/live/<ドメイン>/privkey.pem SSLCertificateChainFile /etc/letsencrypt/live/<ドメイン>/chain.pem
/etc/httpd/conf.d/VirtualHosts.conf (抜粋)
バーチャルホスト毎に SSL 設定が必要。<VirtualHost *:80 *:443> ServerName vh1.<ドメイン1> DocumentRoot /var/www/vh1-html/ SSLEngine on SSLCertificateFile /etc/letsencrypt/live/<ドメイン1>/cert.pem SSLCertificateKeyFile /etc/letsencrypt/live/<ドメイン1>/privkey.pem SSLCertificateChainFile /etc/letsencrypt/live/<ドメイン1>/chain.pem <Directory "/var/www/vh1-html"> AllowOverride All </Directory> </VirtualHost>
apache 再起動
CentOS 7# systemctl restart httpd
証明書更新
「--manual」で取得した場合は「renew」による自動更新ができないので、既存の証明書を削除し同名で取得し直す。# certbot delete
Certbot (ワイルドカード, 自動)
-
注意点
- テスト時は certbot のオプションに「--test-cert」を付け、テスト用の証明書を取得する。
certbot -h testing
- 本番用証明書は取得回数に上限がある。7日間に5回まで。
- BIND で VIEW を使って、内向き/外向きで問い合わせに対し別々の回答をするようにしている。
- 内向きの VIEW があると certbot がそちらに challenge 用レコードを作り誤動作するので、証明書取得/更新時は内向き VIEW を無効化する。
- テスト時は certbot のオプションに「--test-cert」を付け、テスト用の証明書を取得する。
Certbot, DNS Plugin インストール (EPEL リポジトリ)
# yum -y install yum-utils # yum-config-manager --enable rhui-REGION-rhel-server-extras rhui-REGION-rhel-server-optional # yum install certbot-apache python2-certbot-dns-rfc2136
-
設定ファイル, スクリプト
パーミッション オーナー パス 概要 640 root:named /etc/named.conf BIND 設定ファイル 644 root:root /etc/named/Kcertbot-key.+165+43987.key BIND 用キーファイル 600 root:root /etc/named/Kcertbot-key.+165+43987.private BIND 用キーファイル 600 root:root /etc/named/certbot_rfc2136.ini RFC2136 用認証ファイル 640 root:named /etc/named/named-multi-view.conf 外部/内部問い合わせ両用設定 640 root:named /etc/named/named-external-view.conf 外部問い合わせ専用設定 640 root:named /etc/named/common.conf 共通設定 640 root:named /etc/named/internal.view 内部問い合わせ用 view 設定 640 root:named /etc/named/external.view 外部問い合わせ用 view 設定 644 root:root /etc/named/<ドメイン>.lan.zone 内部問い合わせ用 zone 設定 644 root:root /etc/named/<ドメイン>.wan.zone 外部問い合わせ用 zone 設定 644 root:root /etc/named/_acme-challenge.<ドメイン>.wan.zone Let's Encrypt 問い合わせ用 zone 設定 644 named:named /var/named/<ドメイン>.lan.db 内部問い合わせ用権威サーバ設定 644 named:named /var/named/<ドメイン>.wan.db 外部問い合わせ用権威サーバ設定 644 named:named /var/named/_acme-challenge.<ドメイン>.wan.db Let's Encrypt 問い合わせ用権威サーバ設定 750 root:root /etc/letsencrypt/renewal-hooks/pre/external-view.sh 更新前処理スクリプト 755 root:root /etc/letsencrypt/renewal-hooks/deploy/restartServices.sh 更新成功時処理スクリプト 750 root:root /etc/letsencrypt/renewal-hooks/post/multi-view.sh 更新後処理スクリプト BIND 用認証キーの作成
Kcertbot-key.+165+43987.key, Kcertbot-key.+165+43987.private の2つのファイルが作成される。# cd /etc/named/ # dnssec-keygen -a HMAC-SHA512 -b 512 -n HOST certbot-key # cat Kcertbot-key.+165+43987.key certbot-key. IN KEY 512 3 165 <ハッシュ値>
認証ファイル /etc/named/certbot_rfc2136.ini
# Target DNS server dns_rfc2136_server = 127.0.0.1 # Target DNS port dns_rfc2136_port = 53 # TSIG key name dns_rfc2136_name = certbot-key. # TSIG key secret dns_rfc2136_secret = <Kcertbot-key.+165+43987.key の中のハッシュ値> # TSIG key algorithm dns_rfc2136_algorithm = HMAC-SHA512
/etc/named/named-multi-view.conf
include "/etc/named/common.conf"; include "/etc/named/internal.view"; include "/etc/named/external.view";
/etc/named/named-external-view.conf
include "/etc/named/common.conf"; include "/etc/named/external.view";
/etc/named/common.conf
// // named.conf // // Provided by Red Hat bind package to configure the ISC BIND named(8) DNS // server as a caching only nameserver (as a localhost DNS resolver only). // // See /usr/share/doc/bind*/sample/ for example named configuration files. // // See the BIND Administrator's Reference Manual (ARM) for details about the // configuration located in /usr/share/doc/bind-{version}/Bv9ARM.html options { # listen-on port 53 { 127.0.0.1; }; # listen-on-v6 port 53 { ::1; }; version "unknown"; 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"; allow-query { localhost; localnets; }; allow-transfer { none; }; /* - If you are building an AUTHORITATIVE DNS server, do NOT enable recursion. - If you are building a RECURSIVE (caching) DNS server, you need to enable recursion. - If your recursive DNS server has a public IP address, you MUST enable access control to limit queries to your legitimate users. Failing to do so will cause your server to become part of large scale DNS amplification attacks. Implementing BCP38 within your network would greatly reduce such attack surface */ recursion no; dnssec-enable yes; dnssec-validation yes; /* Path to ISC DLV key */ bindkeys-file "/etc/named.iscdlv.key"; managed-keys-directory "/var/named/dynamic"; pid-file "/run/named/named.pid"; session-keyfile "/run/named/session.key"; forwarders { 8.8.8.8; 8.8.4.4; }; }; key "certbot-key." { algorithm hmac-sha512; secret "<Kcertbot-key.+165+43987.key の中のハッシュ値>"; }; logging { channel default_debug { file "data/named.run"; severity dynamic; }; category lame-servers { null; }; };
/etc/named/internal.view
view "internal" { match-clients { localhost; localnets; }; match-destinations { localhost; localnets; }; recursion yes; zone "." IN { type hint; file "named.ca"; }; include "/etc/named.rfc1912.zones"; include "/etc/named.root.key"; include "/etc/named/<ドメイン>.lan.zone"; };
/etc/named/external.view
view "external" { match-clients { any; }; match-destinations { any; }; allow-query { any; }; recursion no; include "/etc/named/_acme-challenge.<ドメイン>.wan.zone"; include "/etc/named/<ドメイン>.wan.zone"; };
/etc/named/<ドメイン>.lan.zone
zone "<ドメイン>" { type master; file "<ドメイン>.lan.db"; };
/etc/named/<ドメイン>.wan.zone
zone "<ドメイン>" { type master; file "<ドメイン>.wan.db"; };
/etc/named/_acme-challenge.<ドメイン>.wan.zone
zone "_acme-challenge.<ドメイン>" { type master; file "_acme-challenge.<ドメイン>.wan.db"; update-policy { grant certbot-key. name _acme-challenge.<ドメイン>. txt; }; };
/var/named/<ドメイン>.lan.db
$ORIGIN . $TTL 86400 ; 1 day <ドメイン> IN SOA ns1.<ドメイン>. root.<ドメイン>. ( 2018090900 ; serial 28800 ; refresh (8 hours) 14400 ; retry (4 hours) 2592000 ; expire (4 weeks 2 days) 86400 ; minimum (1 day) ) NS <ドメイン>.net. A 192.168.1.1 MX 10 mail.<ドメイン>. $ORIGIN <ドメイン>. ns1 A 192.168.1.1 mail A 192.168.1.1 * A 192.168.1.1
/var/named/<ドメイン>.wan.db
$TTL 86400 @ IN SOA ns1.<ドメイン>. root.<ドメイン>. ( 2018090500 ; Serial 28800 ; Refresh 14400 ; Retry 2592000 ; Expire 86400 ; Minimum ) IN NS ns1.<ドメイン>. IN MX 10 mail.<ドメイン>. @ IN A <グローバル IP アドレス> ns1 IN A <グローバル IP アドレス> www IN A <グローバル IP アドレス> mail IN A <グローバル IP アドレス> _acme-challenge IN NS ns1.<ドメイン>. * IN A <グローバル IP アドレス> <ドメイン>. IN TXT "v=spf1 a mx ~all"
/var/named/_acme-challenge.<ドメイン>.wan.db
$TTL 86400 @ IN SOA ns1.<ドメイン>. root.<ドメイン>. ( 2018090500 ; Serial 1h ; Refresh 15m ; Retry 30d ; Expire 1h ; Minimum ) IN NS ns1.<ドメイン>.
/etc/letsencrypt/renewal-hooks/pre/external-view.sh
#!/bin/bash /bin/systemctl stop named-chroot cp -f /etc/named/named-external-view.conf /etc/named.conf /bin/systemctl start named-chroot echo "external view"
/etc/letsencrypt/renewal-hooks/deploy/restartServices.sh
#!/bin/bash LANG=en_us.UTF-8 services="httpd postfix dovecot" if [ $(/bin/id -u) != 0 ]; then echo "This command requires root previlege." 1>&2 exit 1 fi echo "RENEWED_LINEAGE: ${RENEWED_LINEAGE}" echo "RENEWED_DOMAINS: ${RENEWED_DOMAINS}" for service in ${services}; do echo "restart ${service}" /bin/systemctl restart ${service} || exit $? done exit 0
/etc/letsencrypt/renewal-hooks/post/multi-view.sh
#!/bin/bash /bin/systemctl stop named-chroot cp -f /etc/named/named-multi-view.conf /etc/named.conf /bin/systemctl start named-chroot echo "multi view"
<ドメイン>.jnl: create: permission denied
対策
/var/named/chroot/var/named/data/named.run# chmod 770 /var/named/ # setsebool -P named_write_master_zones 1
証明書取得
# /etc/letsencrypt/renewal-hooks/pre/external-view.sh # certbot certonly \ --dns-rfc2136 \ --dns-rfc2136-credentials /etc/named/certbot_rfc2136.ini \ -d "*.<ドメイン>" -d <ドメイン> # /etc/letsencrypt/renewal-hooks/post/multi-view.sh
サービス登録
# systemctl status certbot-renew.timer # systemctl enable --now certbot-renew.timer # systemctl status certbot-renew.timer # systemctl list-timers
動作確認
証明書の内容をテキスト出力
# openssl x509 -text -in /etc/letsencrypt/live/<ドメイン>/cert.pem
証明書の有効期限を表示
getExpireDate.sh
#!/bin/bash
CommonName=example.net
NotAfter=`openssl x509 -noout -dates -in /etc/letsencrypt/live/${CommonName}/fullchain.pem | \
grep notAfter | \
sed -e "s/notAfter=//" | \
date -f - --iso-8601`
echo ${NotAfter}
SSL 接続確認
$ openssl s_client -connect <ホスト>:443
設定失敗
指定したホスト(バーチャルホスト)に SSL 証明書が適用されていない。
CONNECTED(00000003)
140139752064912:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol:s23_clnt.c:794:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 289 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : 0000
Session-ID:
Session-ID-ctx:
Master-Key:
Key-Arg : None
Krb5 Principal: None
PSK identity: None
PSK identity hint: None
Start Time: 1535884386
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
設定成功
CONNECTED(00000003)
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = *.<ドメイン1>
verify return:1
---
Certificate chain
0 s:/CN=*.<ドメイン1>
i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
i:/O=Digital Signature Trust Co./CN=DST Root CA X3
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIGETCCBPmgAwIBAgISA3VBvI0cSyzAQGtpIaQKQRZxMA0GCSqGSIb3DQEBCwUA
...
エラー対応
AttributeError: 'module' object has no attribute 'pyopenssl'
-
AttributeError: 'module' object has no attribute 'pyopenssl' · Issue #6328 · certbot/certbot
pip install requests==2.6.0 easy_install --upgrade pip pip install --upgrade pyOpenSSL
リンク
-
Let's Encrypt Free SSL/TLS Certificates
- CertbotとBINDの組み合わせでLet's Encryptのワイルドカード証明書を取得・更新する
- OpenSSL CSR with Alternative Names one-line - End Point Blog
- SAN対応 x.509 証明書を取得するためのCSRを作成する - Qiita
- subjectAltNameでバーチャルホスト - Kung Noi Blog
- Webサーバー間通信内容暗号化(Apache+mod_SSL) - CentOSで自宅サーバー構築
- SSL Server Test (Powered by Qualys SSL Labs)
-
ssl certificate - how to add subject alernative name to ssl certs? - Stack Overflow
- keytool -certreq -ext SAN=dns:example.com,ip:192.168.0.1
- keytool(ja) / keytool(en)