LoginSignup
6
4

More than 1 year has passed since last update.

Amazon Linux 1 に acme.sh (HTTP-01 challenge)で Let's Encrypt を入れる

Last updated at Posted at 2019-07-26

更新履歴

2021年10月01日
Let's Encryptルート証明書期限切れで更新エラー(curl error code: 60)が出る場合の対処法

概要

  • とある有償SSLサーバ証明書の有効期限がそろそろ切れるので、これを機に、これまでの有償+手動更新から、Let's Encrypt を利用した 無償+自動更新 で幸せになろうと思ったけど、インストールするサーバが Amazon Linux 1 でしかも古いバージョンのため、Let's Encrypt の SSLサーバ証明書をインストールするのにいろいろ試行錯誤した記録。

環境

  • Let's Encrypt の SSLサーバ証明書を入れるサーバ
  • Amazon Linux 1(AMI release 2015.09)←古すぎ!

$ cat /etc/system-release
Amazon Linux AMI release 2015.09
  • Webサーバ

  • Nginx(BASIC認証でアクセス制限している)

  • ドキュメントルート /usr/share/nginx/html

  • ドメイン

  • foo.staging.co.jp(今回の説明用のダミードメイン)

  • IPアドレス/ポート番号制限

  • 任意のIPアドレスから http://foo.staging.co.jp, https://foo.staging.co.jp への接続は全開放

Certbot の断念

無償のSSLサーバ証明書といえば『Let's Encrypt』ということで、公式サイトのGetting Startedを読むと、Certbot ACME client を推奨している。ということで、まずはサーバに Certbot のインストールを試みる。

Amazon Linux 1 は CentOS 6 のパッケージを使えるので、Let's Encrypt 総合ポータル サイトの "CentOS 6 / RHEL 6" のインストール方法 に書いてある通り、EPEL6 リポジトリから入れようと思ったら、certbot パッケージが無いというエラー。確かに、EPEL 6: x86_64 リポジトリ にcertbotパッケージが無い。。

なので、Let's Encrypt 総合ポータル サイトの "その他の UNIX 系 OS" のインストール方法に書いてある通り実行すると・・・FATAL: Amazon Linux support is very experimental at present...


$ wget https://dl.eff.org/certbot-auto
$ chmod a+x certbot-auto
$ ./certbot-auto
Requesting to rerun ./certbot-auto with root privileges...
FATAL: Amazon Linux support is very experimental at present...
if you would like to work on improving it, please ensure you have backups
and then run this script again with the --debug flag!
Alternatively, you can install OS dependencies yourself and run this script
again with --no-bootstrap.

「--debug」オプションを付けて再度実行してみると、次のパッケージの Install/Update をしろと、、


$ ./certbot-auto --debug
〜略〜
Installing:
 augeas-libs
 gcc48
     replacing  libquadmath48-devel.x86_64 4.8.3-9.109.amzn1
 gcc48-c++
     replacing  libstdc++48-devel.x86_64 4.8.3-9.109.amzn1
 libffi-devel
 python27-tools

Updating:
 ca-certificates
 gcc
 openssl
 openssl-devel
 python27-devel
 python27-pip
 python27-virtualenv
 system-rpm-config
Installing for dependencies:
 libgfortran
Updating for dependencies:
 cpp48
 gcc-c++
 gcc-gfortran
 gcc48-gfortran
 libffi
 libgcc48
 libgomp
 libstdc++48
 libtool
 python27
 python27-libs

Let's Encrypt 総合ポータル サイトに、しれっと注意書きがある。。
うーん、、 Install/Update するのは怖いよね。。

ということで、certbot は諦めて、別の ACME client を使ってみようということで、ACME v2 Compatible Clientsからacme.sh を選択。acme.sh はシェルスクリプトで書かれていて、シェルが動く環境であれば導入が楽ですぐに使えるらしい。

acme.sh を使って Let's Encrypt SSLサーバ証明書をインストールする

acme.sh をインストールする

公式サイトの通りに実行、


 # cd /root
 # curl https://get.acme.sh | sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current	
                                 Dload  Upload   Total   Spent    Left  Speed	
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0

curl: (35) Cannot communicate securely with peer: no common encryption algorithm(s).	

あれ、curl が古い。。nssをアップグレードする。


 # yum upgrade nss

再度、、


 # cd /root
 # curl https://get.acme.sh | sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   705    0   705    0     0   4617      0 --:--:-- --:--:-- --:--:--  4607
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  184k  100  184k    0     0   492k      0 --:--:-- --:--:-- --:--:--  492k
[Fri Jul 19 12:11:23 JST 2019] Installing from online archive.
[Fri Jul 19 12:11:23 JST 2019] Downloading https://github.com/Neilpang/acme.sh/archive/master.tar.gz
[Fri Jul 19 12:11:24 JST 2019] Extracting master.tar.gz
[Fri Jul 19 12:11:24 JST 2019] It is recommended to install socat first.
[Fri Jul 19 12:11:24 JST 2019] We use socat for standalone server if you use standalone mode.
[Fri Jul 19 12:11:24 JST 2019] If you don't use standalone mode, just ignore this warning.
[Fri Jul 19 12:11:24 JST 2019] Installing to /root/.acme.sh
[Fri Jul 19 12:11:24 JST 2019] Installed to /root/.acme.sh/acme.sh
[Fri Jul 19 12:11:24 JST 2019] Installing alias to '/root/.bashrc'
[Fri Jul 19 12:11:24 JST 2019] OK, Close and reopen your terminal to start using acme.sh
[Fri Jul 19 12:11:24 JST 2019] Installing alias to '/root/.cshrc'
[Fri Jul 19 12:11:24 JST 2019] Installing alias to '/root/.tcshrc'
[Fri Jul 19 12:11:24 JST 2019] Installing cron job
[Fri Jul 19 12:11:24 JST 2019] Good, bash is found, so change the shebang to use bash as preferred.
[Fri Jul 19 12:11:24 JST 2019] OK
[Fri Jul 19 12:11:24 JST 2019] Install success!

入った。
crontab (/var/spool/cron/root) に次のような設定(自動更新用)が追加される。


24 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null

Nginx mode で SSLサーバ証明書を発行してみる

  • acme.sh のオプション
  • --issue・・・SSLサーバ証明書発行
  • --nginx・・・Nginx mode
  • -d・・・SSLサーバ証明書を発行するドメイン

 # cd /root/.acme.sh
 # ./acme.sh --issue --nginx -d foo.staging.co.jp
[Fri Jul 19 12:17:47 JST 2019] Create account key ok.
[Fri Jul 19 12:17:47 JST 2019] Registering account
[Fri Jul 19 12:17:48 JST 2019] Registered
[Fri Jul 19 12:17:48 JST 2019] ACCOUNT_THUMBPRINT='1W2zh17CQyWXgr3TCI5DVgl_728ut2X7oi5r4RByFwg'
[Fri Jul 19 12:17:48 JST 2019] Creating domain key
[Fri Jul 19 12:17:48 JST 2019] The domain key is here: /root/.acme.sh/foo.staging.co.jp/foo.staging.co.jp.key
[Fri Jul 19 12:17:48 JST 2019] Single domain='foo.staging.co.jp'
[Fri Jul 19 12:17:48 JST 2019] Getting domain auth token for each domain
[Fri Jul 19 12:17:49 JST 2019] Getting webroot for domain='foo.staging.co.jp'
[Fri Jul 19 12:17:49 JST 2019] Verifying: foo.staging.co.jp
[Fri Jul 19 12:17:49 JST 2019] Nginx mode for domain:foo.staging.co.jp
[Fri Jul 19 12:17:49 JST 2019] Found conf file: /etc/nginx/nginx.conf
[Fri Jul 19 12:17:49 JST 2019] Backup /etc/nginx/nginx.conf to /root/.acme.sh/foo.staging.co.jp/backup/foo.staging.co.jp.nginx.conf
[Fri Jul 19 12:17:49 JST 2019] Check the nginx conf before setting up.
[Fri Jul 19 12:17:49 JST 2019] OK, Set up nginx config file
[Fri Jul 19 12:17:49 JST 2019] nginx conf is done, let's check it again.
[Fri Jul 19 12:17:49 JST 2019] Reload nginx
[Fri Jul 19 12:17:54 JST 2019] foo.staging.co.jp:Verify error:Invalid response from http://foo.staging.co.jp/.well-known/acme-challenge/6Tb5KM3d0jhtiRJ6eoqfa68u_0BFBF1eqgdvtH7g4sU [x.x.x.x]: 401
[Fri Jul 19 12:17:54 JST 2019] Restoring from /root/.acme.sh/foo.staging.co.jp/backup/foo.staging.co.jp.nginx.conf to /etc/nginx/nginx.conf
[Fri Jul 19 12:17:54 JST 2019] Reload nginx
[Fri Jul 19 12:17:54 JST 2019] Please add '--debug' or '--log' to check more details.
[Fri Jul 19 12:17:54 JST 2019] See: https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh

エラー出た。。
Verify error:Invalid response from http://foo.staging.co.jp/.well-known/acme-challenge/6Tb5KM3d0jhtiRJ6eoqfa68u_0BFBF1eqgdvtH7g4sU [x.x.x.x]: 401

そもそも、何やっているの?

acme.sh は SSLサーバ証明書を発行する際に HTTP-01 challenge で検証を行っている。
acme.sh --issue 実行時に、acme.sh は Nginx の "ルートディレクトリ/.well-known/acme-challenge/" ディレクトリ内にトークンファイルを一時的に作成して、acme.sh がトークンファイルの準備が出来たことを Let's Encrypt に伝えたら、Let's Encrypt は "http://foo.staging.co.jp/.well-known/acme-challenge/トークン" にアクセスを試みる。Let's Encrypt が正しい応答を得られたら検証は成功とみなされ、SSLサーバ証明書が発行される。

Nginx mode は、自動的に nginx コンフィグファイルを探して、"ルートディレクトリ/.well-known/acme-challenge/トークンファイル" へのアクセス設定を施して、検証が終わったら、設定を元に戻す。

今回は、Nginx で Basic 認証を行っているため、Let's Encrypt からのアクセスが 401 となり、検証が失敗してしまった。

ちなみに、HTTP-01 challenge アクセスは、Let's Encryptの接続元IPアドレスは公開していないため、クライアント側は HTTP ポート80番で任意のIPアドレスからの接続を許可しておく必要がある。

トークンへのアクセスはBASIC認証対象外として、ノーマルモードで SSLサーバ証明書を発行してみる

まずは、HTTP-01 challenge 用のディレクトリを手動で作成する。


 # cd /usr/share/nginx/html
 # mkdir -p .well-known/acme-challenge
 # chmod -R 755 .well-known/acme-challenge
 # chown -R nginx:nginx /usr/share/nginx/html/.well-known/acme-challenge

"http://foo.staging.co.jp/.well-known/acme-challenge/トークン" へのアクセスは BASIC認証対象外とする
/etc/nginx/nginx.conf 設定例


〜
  location /.well-known  {
    satisfy any;
    allow   all;
  }
  location /.well-known/acme-challenge  {
    satisfy any;
    allow   all;
  }
〜

nginx 再起動


 # service nginx configtest
 # service nginx restart

ノーマルモードでSSLサーバ証明書を発行する

  • acme.sh のオプション
  • -w・・・ドキュメントルート

 # cd /root/.acme.sh
 # ./acme.sh --issue -d foo.staging.co.jp -w /usr/share/nginx/html
[Fri Jul 26 12:48:13 JST 2019] Create account key ok.
[Fri Jul 26 12:48:13 JST 2019] Registering account
[Fri Jul 26 12:48:14 JST 2019] Registered
[Fri Jul 26 12:48:14 JST 2019] ACCOUNT_THUMBPRINT='6k1I3oh7R3iEIrnWIEWMsRt0rmUzf4T2CXgn9TaXvoE'
[Fri Jul 26 12:48:14 JST 2019] Creating domain key
[Fri Jul 26 12:48:14 JST 2019] The domain key is here: /root/.acme.sh/foo.staging.co.jp/foo.staging.co.jp.key
[Fri Jul 26 12:48:14 JST 2019] Single domain='foo.staging.co.jp'
[Fri Jul 26 12:48:14 JST 2019] Getting domain auth token for each domain
[Fri Jul 26 12:48:15 JST 2019] Getting webroot for domain='foo.staging.co.jp'
[Fri Jul 26 12:48:15 JST 2019] Verifying: foo.staging.co.jp
[Fri Jul 26 12:48:18 JST 2019] Success
[Fri Jul 26 12:48:18 JST 2019] Verify finished, start to sign.
[Fri Jul 26 12:48:18 JST 2019] Lets finalize the order, Le_OrderFinalize: https://acme-v02.api.letsencrypt.org/acme/finalize/62039946/782487887
[Fri Jul 26 12:48:20 JST 2019] Download cert, Le_LinkCert: https://acme-v02.api.letsencrypt.org/acme/cert/03d9bc51c44775de73e376f19b890cc6f5bf
[Fri Jul 26 12:48:20 JST 2019] Cert success.
-----BEGIN CERTIFICATE-----
MIIFcjCCBFqgAwIBAgISA9m8UcRHdd5z43bxm4kMxvW/MA0GCSqGSIb3DQEBCwUA
〜略〜
5Kh5eZhP
-----END CERTIFICATE-----
[Fri Jul 26 12:48:20 JST 2019] Your cert is in  /root/.acme.sh/foo.staging.co.jp/foo.staging.co.jp.cer
[Fri Jul 26 12:48:20 JST 2019] Your cert key is in  /root/.acme.sh/foo.staging.co.jp/foo.staging.co.jp.key
[Fri Jul 26 12:48:20 JST 2019] The intermediate CA cert is in  /root/.acme.sh/foo.staging.co.jp/ca.cer
[Fri Jul 26 12:48:20 JST 2019] And the full chain certs is there:  /root/.acme.sh/foo.staging.co.jp/fullchain.cer

おぉ、発行された!

発行されたSSLサーバ証明書を確認する。


 # ./acme.sh --list
Main_Domain        KeyLength  SAN_Domains  Created                       Renew
foo.staging.co.jp  ""         no           Fri Jul 26 03:48:20 UTC 2019  Tue Sep 24 03:48:20 UTC 2019

発行されたSSLサーバ証明書を Nginx にインストールする

cert keyファイルと、full chain certs ファイルをを置くディレクトリを作成する。


 # mkdir -p /etc/nginx/certs/letsencrypt/foo.staging.co.jp

/etc/nginx/certs/letsencrypt/foo.staging.co.jp ディレクトリに、foo.staging.co.jp.key ファイルと、fullchain.cer ファイルをインストール (--install-cert) する。


 # cd /root/.acme.sh/
 # ./acme.sh --install-cert -d foo.staging.co.jp --key-file /etc/nginx/certs/letsencrypt/foo.staging.co.jp/foo.staging.co.jp.key --fullchain-file /etc/nginx/certs/letsencrypt/foo.staging.co.jp/fullchain.cer

nginx コンフィグファイルで証明書のパスを設定する
/etc/nginx/conf.d/ssl.conf 設定例


〜
  ssl_certificate     /etc/nginx/certs/letsencrypt/foo.staging.co.jp/fullchain.cer;
  ssl_certificate_key /etc/nginx/certs/letsencrypt/foo.staging.co.jp/foo.staging.co.jp.key;
〜

nginx 再起動


 # service nginx configtest	
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

 # service nginx restart
Stopping nginx:     [  OK  ]
Starting nginx:     [  OK  ]

ブラウザから https://foo.staging.co.jp へアクセスして、SSLサーバ証明書を確認する。
image.png

インストール出来た。

自動更新設定

自動更新では、SSLサーバ証明書発行後に、"--install-cert でインストールしてnginxをリロード" させる。
そのジョブを実行する。


 # /root/.acme.sh
 # ./acme.sh --install-cert -d foo.staging.co.jp --key-file /etc/nginx/certs/letsencrypt/foo.staging.co.jp/foo.staging.co.jp.key --fullchain-file /etc/nginx/certs/letsencrypt/foo.staging.co.jp/fullchain.cer --reloadcmd "service nginx force-reload"
[Fri Jul 19 17:18:49 JST 2019] Installing key to:/etc/nginx/certs/letsencrypt/foo.staging.co.jp/foo.staging.co.jp.key
[Fri Jul 19 17:18:49 JST 2019] Installing full chain to:/etc/nginx/certs/letsencrypt/foo.staging.co.jp/fullchain.cer
[Fri Jul 19 17:18:49 JST 2019] Run reload cmd: service nginx force-reload
Reloading nginx:          [  OK  ]

これで、cronによる自動更新ジョブで、SSLサーバ証明書発行後に、"--install-cert でインストールしてnginxをリロード" するようになる。

acme.sh インストール時に crontab に自動更新のコマンドが追加されているので、
crontab (/var/spool/cron/root)


24 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null

これを使って、手動で強制的 (--force) に更新処理を実行してみる。


 # "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" --force
[Fri Jul 26 13:24:13 JST 2019] ===Starting cron===
[Fri Jul 26 13:24:13 JST 2019] Renew: 'foo.staging.co.jp'
[Fri Jul 26 13:24:14 JST 2019] Single domain='foo.staging.co.jp'
[Fri Jul 26 13:24:14 JST 2019] Getting domain auth token for each domain
[Fri Jul 26 13:24:15 JST 2019] Getting webroot for domain='foo.staging.co.jp'
[Fri Jul 26 13:24:15 JST 2019] foo.staging.co.jp is already verified, skip http-01.
[Fri Jul 26 13:24:15 JST 2019] Verify finished, start to sign.
[Fri Jul 26 13:24:15 JST 2019] Lets finalize the order, Le_OrderFinalize: https://acme-v02.api.letsencrypt.org/acme/finalize/62039946/782634405
[Fri Jul 26 13:24:16 JST 2019] Download cert, Le_LinkCert: https://acme-v02.api.letsencrypt.org/acme/cert/037597c36c53830c77a547a4c949f57d0974
[Fri Jul 26 13:24:17 JST 2019] Cert success.
-----BEGIN CERTIFICATE-----
MIIFcjCCBFqgAwIBAgISA3WXw2xTgwx3pUekyUn1fQl0MA0GCSqGSIb3DQEBCwUA
〜略〜
7AFa7o7V
-----END CERTIFICATE-----
[Fri Jul 26 13:24:17 JST 2019] Your cert is in  /root/.acme.sh/foo.staging.co.jp/foo.staging.co.jp.cer
[Fri Jul 26 13:24:17 JST 2019] Your cert key is in  /root/.acme.sh/foo.staging.co.jp/foo.staging.co.jp.key
[Fri Jul 26 13:24:17 JST 2019] The intermediate CA cert is in  /root/.acme.sh/foo.staging.co.jp/ca.cer
[Fri Jul 26 13:24:17 JST 2019] And the full chain certs is there:  /root/.acme.sh/foo.staging.co.jp/fullchain.cer
[Fri Jul 26 13:24:17 JST 2019] Installing key to:/etc/nginx/certs/letsencrypt/foo.staging.co.jp/foo.staging.co.jp.key
[Fri Jul 26 13:24:17 JST 2019] Installing full chain to:/etc/nginx/certs/letsencrypt/fullchain.pem
[Fri Jul 26 13:24:17 JST 2019] Installing full chain to:/etc/nginx/certs/letsencrypt/foo.staging.co.jp/fullchain.cer
Reloading nginx:                                           [  OK  ]
[Fri Jul 26 13:24:17 JST 2019] Reload success
[Fri Jul 26 13:24:17 JST 2019] ===End cron===

SSLサーバ証明書発行して、インストールして、nginx 再起動してる。
ブラウザからSSLサーバ証明書の有効期限を確認する。
有効期限が新しく発行したSSLサーバ証明書になっていれば、成功。

ちなみに、Let's Encrypt の SSLサーバ証明書の有効期限は90日だけど、
acme.sh は cronで毎日更新処理を試行して、SSLサーバ証明書発行から60日経過している場合に、更新処理を実行する。

補足

バージョン管理している場合は、"ルートディレクトリ/.well-known/acme-challenge/"の追加を忘れずに。
デプロイしたら"ルートディレクトリ/.well-known/acme-challenge/"ディレクトリが消えないように。

運用時に発生した問題

1. 「It seems the CA server is busy now, let's wait and retry. Sleeping 1 seconds.」

いざ、更新時に発生したエラー。


[2020年  4月 22日 水曜日 00:16:02 JST] ===Starting cron===
[2020年  4月 22日 水曜日 00:16:02 JST] Renew: 'ドメイン名'
[2020年  4月 22日 水曜日 00:16:03 JST] Single domain='ドメイン名'
[2020年  4月 22日 水曜日 00:16:03 JST] Getting domain auth token for each domain
[2020年  4月 22日 水曜日 00:21:04 JST] It seems the CA server is busy now, let's wait and retry. Sleeping 1 seconds.
[2020年  4月 22日 水曜日 00:26:05 JST] It seems the CA server is busy now, let's wait and retry. Sleeping 1 seconds.
[2020年  4月 22日 水曜日 00:31:07 JST] It seems the CA server is busy now, let's wait and retry. Sleeping 1 seconds.
[2020年  4月 22日 水曜日 00:36:08 JST] It seems the CA server is busy now, let's wait and retry. Sleeping 1 seconds.
〜
[2020年  4月 22日 水曜日 01:56:36 JST] Create new order error. Le_OrderFinalize not found. {
  "type": "urn:ietf:params:acme:error:badNonce",
  "detail": "JWS has an invalid anti-replay nonce: \"0002dbS1IRfgFLnk4wAEVzwHbe7-BRBS5HFBRDVqyho6Ri4\"",
  "status": 400
}

acme.sh をアップグレードしたら、正常に更新処理が走った。

# acme.sh --upgrade

手動更新

# "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh"

crontab に upgrade を追加

6 0 * * * cd /root/.acme.sh && ./acme.sh --upgrade

Let's Encryptルート証明書期限切れで更新エラー(curl error code: 60)が出る場合の対処法

# "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh"
[Thu Sep 30 23:22:41 UTC 2021] ===Starting cron===
[Thu Sep 30 23:22:41 UTC 2021] Renew: '***.***.***.jp'
[Thu Sep 30 23:23:00 UTC 2021] Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: 60
[Thu Sep 30 23:23:02 UTC 2021] Can not init api for: https://acme-v02.api.letsencrypt.org/directory.
[Thu Sep 30 23:23:02 UTC 2021] Sleep 10 and retry.
  • 対処法として、新しいCA証明書ファイルに置き換える方法がある
  • 最新のCA証明書ファイルをダウンロードして、対象サーバの /etc/pki/tls/certs/ ディレクトリ配下に置く
    https://curl.haxx.se/ca/cacert.pem
  • 対象サーバにSSHログインして、現存の ca-bundle.crt をバックアップとり、cacert.pem を ca-bundle.crt にリネームし、パーミッションを変更する。
# cd /etc/pki/tls/certs/
# mv ca-bundle.crt ca-bundle.crt_20211001
# mv cacert.pem ca-bundle.crt
# chmod 644 ca-bundle.crt
  • 手動で強制自動更新を実行して動作確認
# "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" --force
6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4