無料SSL「Let’s Encrypt」でワイルドカードを設定し、ValueDomainで自動更新する
篠原 隆司
アフィリエイト広告を利用しています
このページの内容が役に立ったら X (旧twitter) でフォローして頂けると励みになります
挨拶や報告は無しで大丈夫です
社内資料として非公開していた情報を整理しながら公開していくシリーズです。
若干情報が古い場合もありますがご了承ください。
さて今回は、無料SSLで有名なLet's Encryptについて書いていってみたいと思います。
事前準備
まずは確認しましょう。
rpm -qa | grep centos-release
centos-release-7-9.2009.2.el7.centos.x86_64
Certbot のインストール
yum install epel-release
yum install certbot
Certbot のバージョンを確認
certbot --version
certbot 1.11.0
自動更新を設定する場合
Let's Encryptは3カ月以内ごとに更新処理をしないといけないことで有名です。
最初に説明する方法は最初から最後まで手動で行う方法です。
自動更新を考えている場合は、二度手間になりますので「バリュードメイン / Value Domainで証明書の自動更新設定」から始めてると良いです。
ワイルドカードドメインを登録
example.com は登録したいドメインに書き換えてください
下記の例では、「*.example.com」と「example.com」を登録しています
certbot certonly \
--manual \
--server https://acme-v02.api.letsencrypt.org/directory \
--preferred-challenges dns \
-d *.example.com \
-d example.com \
-m sample@example.com \
--agree-tos
![](https://aulta.co.jp/wp-content/uploads/2024/06/image.png)
最初の証明書が正常に発行されたら、Let's Encrypt プロジェクトの創設パートナーであり、Certbot を開発している非営利団体である Electronic Frontier Foundation に電子メール アドレスを共有していただけますか? Web の暗号化に関する私たちの取り組み、EFF のニュース、キャンペーン、デジタルの自由をサポートする方法についての電子メールをお送りします。
y を入力してEnter
下記、本当は英語ですが重要情報を含むのでスクショは無しで、日本語にすると次のような内容です。
アカウントが登録されました。
*.example.com および example.com の証明書を要求しています
次のチャレンジを実行しています:
example.com の dns-01 チャレンジ
example.com の dns-01 チャレンジ
次の値を持つ _acme-challenge.example.com という名前で DNS TXT レコードを展開してください:
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
続行する前に、レコードが展開されていることを確認してください。
画面には「Press Enter to Continue」と出ていると思いますが、まだ次に進みません。
DNSの設定
設定しようとしているドメインのDNS設定画面を開きます。
txtレコードの
_acme-challenge.example.com に
xxxxxxxxxxxxxxxxxxxxxxxxxxxx を設定します。
指定方法は業者により異なりますが、私が管理しているところでは次のように指定します
txt _acme-challenge xxxxxxxxxxxxxxxxxxxxxxxxxxxx
ターミナルに戻り、Enter で進みます。
txtレコードの反映がまだなら進めない場合があります。
Enter をクリックするともう一度似たような表示になります。
よく見ると、コードが変わっています。
こちらもDNSの設定から登録します。
私の管理しているところでは次のように、2つ目として追加する形になります。
txt _acme-challenge xxxxxxxxxxxxxxxxxxxxxxxxxxxx
txt _acme-challenge yyyyyyyyyyyyyyyyyyyyyyyyyyyy
DNSを設定して、Enterを押すのが早いと失敗する可能性が高くなります。
数分程度ゆっくり待ってから Enter を押すなり工夫が必要になります。
証明書の確認
作成された証明書は次の場所にあります。
ls -la /etc/letsencrypt/live
httpd.confに設定
apache 2.4
SSLCertificateFile /etc/letsencrypt/live/ドメイン/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/ドメイン/privkey.pem
apache 2.2
SSLCertificateKeyFile /etc/letsencrypt/live/ドメイン/privkey.pem
SSLCertificateFile /etc/letsencrypt/live/ドメイン/cert.pem
SSLCertificateChainFile /etc/letsencrypt/live/ドメイン/chain.pem
apacheの再起動
設定を反映させるため再起動します。
/etc/init.d/httpd restart
バリュードメイン / Value Domainで証明書の自動更新設定
ここで紹介する方法は特に自己責任で行ってください
プログラムが何を行っているか理解しないまま真似しないでください
前提として、PHP 7.2 以上がインストールされているものとします。
https://aulta.co.jp/technical/server-build/centos7/php/source-install-php-7-4-6
バリュードメインのAPIキーは、バリュードメインにログインして「マイページ > バリュードメインAPI」にあります。
![](https://aulta.co.jp/wp-content/uploads/2024/06/image-2.png)
最新版はGithubにあります
次のセクションからプログラムの説明をしていますが、若干古くなっております
最新版はGithubに入れておりますので参考にされる場合はGithubのほうをご確認ください。
https://github.com/aulta/letsencrypt-wildcard-dns-for-value-domain
プログラムの説明
APIキーを保存する設定ファイルを作成します。
vi /root/update_lets_encrypt_config.php
<?php
$config = [];
$config['value_domain_api_key'] = 'バリュードメインで発行したAPIキー';
[/SC_CODE]
<p>DNS設定を更新するPHPファイルを作成します</p>
vi /root/update_lets_encrypt_dns.php
<?php
define('LOG_PATH', __DIR__ . '/update_lets_encrypt.log');
function writeLog(string $message): void
{
file_put_contents(LOG_PATH, date('Y-m-d H:i:s') . ' ' . $message . "\n", FILE_APPEND | LOCK_EX);
echo $message . "\n";
}
$actions = [];
$actions[] = 'add';
$actions[] = 'clear';
$record_names = [];
$record_names[] = '_acme-challenge';
$action = '';
$domain = '';
$record_name = '';
$record_value = '';
if ($argc >= 2) {
$action = (string) $argv[1];
}
if ($argc >= 3) {
$domain = (string) $argv[2];
}
if ($argc >= 4) {
$record_name = (string) $argv[3];
}
if ($argc >= 5) {
$record_value = (string) $argv[4];
}
if ( ! in_array($action, $actions, true)) {
writeLog('不正なパラメータ (1)');
exit;
}
if ( ! preg_match('/\A[a-z0-9\.-]+\z/', $domain)) {
writeLog('不正なパラメータ (2)');
exit;
}
if ( ! in_array($record_name, $record_names, true)) {
writeLog('不正なパラメータ (3)');
exit;
}
if ($action === 'add') {
if (empty($record_value)) {
writeLog('不正なパラメータ (4)');
exit;
}
}
$config = [];
require_once __DIR__ . '/update_lets_encrypt_config.php';
$api_url = 'https://api.value-domain.com/v1/domains/' . $domain . '/dns';
$headers = [
'Authorization: Bearer ' . $config['value_domain_api_key'],
'Content-Type: application/json'
];
$ch = curl_init($api_url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if(curl_errno($ch)) {
writeLog('cURLエラー: ' . curl_error($ch));
exit;
}
curl_close($ch);
$dns_records = json_decode($response, true);
if (isset($dns_records['error'])) {
writeLog('DNSレコードの取得に失敗しました: ' . print_r($dns_records['error'], true));
exit;
}
if (empty($dns_records['results'])) {
writeLog('DNSレコードの取得に失敗しました: results が空');
exit;
}
// print_r($dns_records);
// echo "\n";
$update_data = [];
$update_data['domain'] = $dns_records['results']['domainname'];
// $update_data['ns_type'] = $dns_records['results']['ns_type'];
// $update_data['ttl'] = $dns_records['results']['ttl'];
if ($action === 'add') {
$update_data['records'] = $dns_records['results']['records'] . "\n" . 'txt ' . $record_name . ' ' . $record_value;
} elseif ($action === 'clear') {
$records = $dns_records['results']['records'];
$records = str_replace(["\r\n", "\r"], "\n", $records);
$records = explode("\n", $records);
$new_records = [];
foreach($records as $record) {
if (empty($record)) {
continue;
}
if (strpos($record, 'txt ' . $record_name . ' ') === 0) {
continue;
}
$new_records[] = $record;
}
$update_data['records'] = implode("\n", $new_records) . "\n";
}
$ch = curl_init($api_url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($update_data));
$update_response = curl_exec($ch);
if (curl_errno($ch)) {
writeLog('cURLエラー: ' . curl_error($ch));
exit;
}
curl_close($ch);
$update_response = json_decode($update_response, true);
if (isset($update_response['error'])) {
writeLog('txt ' . $record_name . ' の更新に失敗しました: ' . print_r($update_response['error'], true));
exit;
}
writeLog('txt ' . $record_name . ' を更新しました');
次のように実行します
手動で実行して、バリュードメインのコントロールパネル側に反映されることを確認しておきます。
# クリア
/usr/local/lib/php-7.1.6-mysqlc-mysqlnd/bin/php-7.1.6-mysqlc-mysqlnd /root/update_lets_encrypt_dns.php clear example.com _acme-challenge
# 追加
/usr/local/lib/php-7.1.6-mysqlc-mysqlnd/bin/php-7.1.6-mysqlc-mysqlnd /root/update_lets_encrypt_dns.php add example.com _acme-challenge sample_123456
CertbotがDNS-01チャレンジ用のTXTレコードを設定するためのフックスクリプトを作成します。
vi /root/update_lets_encrypt_dns_challenge.sh
#!/bin/bash
/usr/local/lib/php-7.1.6-mysqlc-mysqlnd/bin/php-7.1.6-mysqlc-mysqlnd /root/update_lets_encrypt_dns.php add example.com _acme-challenge "$CERTBOT_VALIDATION"
sleep 130
chmod +x /root/update_lets_encrypt_dns_challenge.sh
メインスクリプトを作成します
vi /root/update_lets_encrypt_dns.sh
#!/bin/bash
/usr/local/lib/php-7.1.6-mysqlc-mysqlnd/bin/php-7.1.6-mysqlc-mysqlnd /root/update_lets_encrypt_dns.php clear example.com _acme-challenge
certbot certonly \
--manual \
--server https://acme-v02.api.letsencrypt.org/directory \
--preferred-challenges dns \
-d *.example.com \
-d example.com \
-m sample@example.com \
--agree-tos \
--manual-auth-hook "/root/update_lets_encrypt_dns_challenge.sh" \
--manual-cleanup-hook "/usr/local/lib/php-7.1.6-mysqlc-mysqlnd/bin/php-7.1.6-mysqlc-mysqlnd /root/update_lets_encrypt_dns.php clear example.com _acme-challenge"
chmod +x /root/update_lets_encrypt_dns.sh
下記を実行して証明書が更新されるか確認します
CRONに登録するのもこのファイルになります
/root/update_lets_encrypt_dns.sh