2026年10月6日の夕方、運用しているSymbolノードを確認すると、外部監視サイトのHarvester数が「0」になっていました。
「委任してくれていた人が全員いなくなった?」と焦って調べると、REST GatewayがAPI Nodeへの接続に使うTLS証明書が期限切れになり、APIが接続エラーを返していました。 ノード側には有効な証明書があるのに、REST Gateway側には古いコピーが残っていたのです。外部監視の0表示も、このAPI障害で情報を取得できなかったためだと考えています。
そのコピーを更新してREST Gatewayを再起動すると、apiNode は up に戻り、9件のunlockedAccountを確認できました。同じように「Harvesterが0」「apiNodeがdown」で困ったときのために、調べた順番と復旧方法を残しておきます。
※稼働中のノードを特定できないよう、ドメイン・IPアドレス・公開鍵などは省略しています。
対処法だけ知りたい方へ
今回の復旧方法は、Gateway側をバックアップ → 同じノードの有効な証明書・秘密鍵・CA証明書をコピー → REST Gatewayを再起動です。
この手順が当てはまるのは、次の3点を確認できた場合です。
/node/healthがapiNode: down / db: up- REST Gatewayのログに
certificate expired - Node側の証明書は有効で、REST Gateway側のコピーだけが期限切れ
apiNode: down だけでは、同じ原因とは判断できません。まだ原因を確認していなければ2か所の証明書を確認する手順へ、上の条件を確認済みならバックアップから復旧確認までの手順へ進んでください。
復旧の目安は、healthの apiNode と db が両方 up になり、/node/unlockedaccount が接続エラーではなく一覧を返すことです。
Harvesterが0でも、APIの答えは「0件」ではなかった
最初に異変に気づいたのはsymbolnodes.orgです。Harvester数が0と表示され、symbol-tools.comでも正常なノード情報を見られませんでした。
委任の解除なのか、harvesters.dat が消えたのか。それともノードが止まっているのか。まずはノードのREST APIに直接問い合わせてみました。
以下のコマンドは、ノードを動かしているLinuxサーバー上で実行する例です。記事中のコンテナ名とパスは、今回の環境に合わせています。
curl -s http://localhost:3000/node/unlockedaccount
返ってきたのはこちらです。
{
"code": "ServiceUnavailable",
"message": "connection failed"
}
一覧が空なのではなく、一覧を取得するための接続に失敗していました。 正常に取得した結果が0件なら、返るのは次のような空の配列です。
{
"unlockedAccount": []
}
/node/unlockedaccount は、ノードが報告するハーベスト用のアンロック済みアカウントを確認するAPIです。件数がそのまま利用者の人数になるわけではありません。Symbol公式:委任ハーベスティングの確認
この違いを見て、「委任が消えた」と考える前に、REST APIの接続を調べることにしました。
コンテナは起動しているのに、apiNodeはdown
今回の環境は次のとおりです。最新構成の紹介ではなく、この組み合わせで起きた障害の記録です。
| 項目 | 使用していた環境 |
|---|---|
| ネットワーク | Symbol Mainnet |
| symbol-bootstrap | 1.1.12 |
| symbol-server | gcc-1.0.3.9 |
| symbol-api-rest | 2.5.1 |
| 実行環境 | Docker |
起動状態を確認すると、db、node、broker、rest-gateway のコンテナはすべて動いていました。
ところが、REST APIのhealthを確認すると状況が違います。
curl -s http://localhost:3000/node/health
{
"status": {
"apiNode": "down",
"db": "up"
}
}
REST Gatewayから見てDBのチェックは通る一方、API Nodeのチェックは失敗しています。コンテナが起動しているだけでは、コンテナ同士の通信まで正常とは限りません。
一方、Node側のログにはブロックを取得する記録と、同期処理の成功がありました。以下は関連部分の抜粋です。
peer returned 1 blocks
completed 'synchronizer task' ... with result Success
確認した時点ではブロックの取得・処理が進んでいました。そこで、ノード全体の停止よりも、REST GatewayからAPI Nodeへの通信を重点的に調べました。
REST Gatewayのログにcertificate expired
原因につながったのは、Node本体ではなくREST Gatewayのログでした。
sudo docker logs --tail 200 rest-gateway
接続先の node:7900 が出た後に、次のエラーが繰り返されていました(関連部分を抜粋・整形)。node は今回のDocker内部の接続先名です。
connecting to node:7900
ERR_SSL_SSLV3_ALERT_CERTIFICATE_EXPIRED
reason: sslv3 alert certificate expired
503 connection failed
certificate expired、つまり証明書の期限切れです。接続先から期限切れのアラートを受けているので、Gatewayが提示する証明書が疑わしくなりました。調べた結果、問題になっていたのは外向けHTTPSではなく、次の内部接続でした。
外部監視サイトなど
↓ REST APIへの問い合わせ
REST Gateway :3000 ── DBのチェックはup
↓ × TLS接続失敗
API Node :7900
↕
Symbolネットワーク
Symbolでは、REST GatewayとAPI Nodeは役割の異なるコンポーネントです。GatewayはDBの参照だけでなく、API Nodeとの通信も行います。Symbol公式:ノードの構成
この段階では、エラーだけでどの証明書が古いかまでは分かりません。実際に参照するファイルの期限を確認する必要がありました。
renewCertificatesを実行しても直らず、2か所の証明書を比較
証明書の更新として、まず symbol-bootstrap renewCertificates を実行しました。再起動後もhealthは apiNode: down のままでした。
そこでNode側とREST Gateway側、両方の証明書を調べました。以下は、ノードを動かしているLinuxサーバー上で、target ディレクトリを含む作業ディレクトリから実行します。
出力の notAfter が証明書の有効期限です。GMT 表記はUTCと同じ時刻なので、日本時間では9時間を足して読みます。
Node側の証明書は2027年まで有効だった
openssl x509 \
-in target/nodes/node/cert/node.crt.pem \
-noout -dates -fingerprint -sha256
有効期間の出力は次のとおりでした。証明書の照合でノードを特定されないよう、日付の一部と時刻は伏せています。
notBefore=Aug ** **:**:** 2026 GMT
notAfter=Aug ** **:**:** 2027 GMT
こちらはまだ有効です。
REST Gateway側は、その日の14時台(日本時間)に期限切れ
次に、Gateway側にあるコピーを確認しました。
openssl x509 \
-in target/gateways/rest-gateway/api-node-config/cert/node.crt.pem \
-noout -dates -fingerprint -sha256
notBefore=Sep ** **:**:** 2025 GMT
notAfter=Oct 6 05:**:** 2026 GMT
この期限はUTC表記なので、日本時間では2026年10月6日の14時台です。異変に気づいたその日の午後に切れていました。
| 確認したファイル | 有効期限(一部省略) | 確認時の状態 |
|---|---|---|
Node側の node.crt.pem | 2027年8月 | 有効 |
REST Gateway側の node.crt.pem | 2026年10月6日14時台(日本時間) | 期限切れ |
今回の環境では、renewCertificatesを実行した後も、REST Gateway側に古い証明書が残っていました。 Node側のファイルが有効だからといって、Gatewayが使うコピーも有効とは限りません。
古いコピーを置き換え、REST Gatewayを再起動して復旧
ここからは、私の環境で実際に行った復旧作業です。同じノードの有効な証明書を、そのノードに接続するGatewayへ反映する手順であり、別ノードの証明書を流用するものではありません。
以下のコマンドは、ノードを動かしているLinuxサーバー上で、target ディレクトリを含む作業ディレクトリから実行します。コンテナ名とファイルのパスは今回の環境のものなので、自分の設定に合わせて読み替えてください。
公式READMEでは、target を手動で変更しないよう案内されています。以下のコピーは今回の障害から復旧させるために行った対応です。Bootstrapの設定再生成・アップグレードでは上書きされることがあるため、その後も参照先と期限を確認します。Symbol Bootstrap:targetディレクトリの説明
作業前に、Gatewayの設定がこれらのファイルを参照していることと、コピー元の有効期限を確認してください。Bootstrap 1.1.12の標準テンプレートでは、apiNode の tlsClientCertificatePath・tlsClientKeyPath・tlsCaCertificatePath が対応する設定です。REST設定テンプレート
1. Gateway側の証明書をバックアップ
cp -a \
target/gateways/rest-gateway/api-node-config/cert \
target/gateways/rest-gateway/api-node-config/cert.bak-20261006
末尾の日付は今回の作業日です。自分の環境で実施する場合は、まだ存在しないバックアップ名に変えます。失敗した場合は、先へ進まず原因を確認します。
バックアップにも秘密鍵が含まれるので、公開フォルダーには置かず、元ファイルと同様にアクセスを制限して保管します。
2. 証明書・秘密鍵・CA証明書を反映
今回コピーしたのは、次の3ファイルです。
cp -p target/nodes/node/cert/node.crt.pem \
target/gateways/rest-gateway/api-node-config/cert/node.crt.pem
cp -p target/nodes/node/cert/node.key.pem \
target/gateways/rest-gateway/api-node-config/cert/node.key.pem
cp -p target/nodes/node/cert/ca.cert.pem \
target/gateways/rest-gateway/api-node-config/cert/ca.cert.pem
node.key.pem は秘密鍵です。中身を画面共有や記事に載せる必要はありません。3ファイルは同じコピー元からそろえて反映します。作業ユーザーや所有者は環境で異なるため、コピー前後に ls -l などで所有者・権限を確認し、コピーにエラーが出た場合は再起動前に解消します。
Node側とGateway側で証明書の確認コマンドを再実行し、有効期限とSHA256フィンガープリントを比較します。同じ証明書を反映できたか確認する際に使えます。
3. REST Gatewayを再起動してhealthを確認
sudo docker restart rest-gateway
起動後に、もう一度healthを確認しました。
curl -s http://localhost:3000/node/health
{
"status": {
"apiNode": "up",
"db": "up"
}
}
今度は両方とも up。ようやくREST GatewayからAPI Nodeへの接続が戻りました。
復旧後に確認すると、unlockedAccountは9件あった
最後に、最初に失敗したAPIをもう一度確認しました。
curl -s http://localhost:3000/node/unlockedaccount
今度は正常に一覧が返り、9件のアンロック済みアカウントを確認できました。 公開鍵そのものは掲載しません。
0表示だけを見たときは全員いなくなったのかと思いましたが、APIの接続エラーを切り分ける必要がありました。障害中のハーベスト継続までは確認できていませんが、少なくとも復旧後の一覧は0件ではありませんでした。
REST Gateway側だけ古い証明書が残った理由は?
Bootstrap 1.1.12では、REST GatewayはNode側と同じ証明書ファイルを直接参照せず、設定生成時にコピーされたファイルを使います。 コピーを行うのは ConfigService の generateGateways です。Gatewayの設定生成
一方、renewCertificates が更新するのはNode側の証明書で、Gateway側へコピーし直す処理は呼び出していません。Node側だけ新しくなり、Gateway側に以前のコピーが残り得る構成でした。renewCertificatesの実装
ここからは今回の経緯についての推測です。 Node側には2026年8月から有効な証明書があり、Gateway側には2025年から有効な古い証明書が残っていました。以前にNode側の証明書が置き換わった際、Gateway側には反映されなかったのではないかと考えています。
古いコピーも期限内なら使えるため、そのまま動き続け、10月6日の期限切れで接続できなくなった――という流れなら、今回の状況と一致します。コンテナを再起動するだけでは古いファイルを読み直すため、コピーの更新が必要だったことも説明できます。
ただし、Node側の証明書をいつ、どの操作で置き換えたかは確認できていません。有効開始日だけで更新作業の日付までは分からないため、そこを確定するには当時の作業ログが必要です。
次はNode側だけでなく、Gateway側の証明書期限も見る
今後の確認項目として、RESTのhealthに加え、Node側とGateway側の両方の証明書期限を見ようと思います。
Gateway側の期限だけを表示するなら、次のコマンドです。
openssl x509 \
-in target/gateways/rest-gateway/api-node-config/cert/node.crt.pem \
-noout -enddate
定期監視に組み込むなら、apiNode と db が両方 up か、証明書の残り期間が30日を切っていないか、という項目が候補になります。監視の自動化は今後の課題です。
まとめ
切り分けの順番は、unlockedaccount のエラー確認 → health確認 → REST Gatewayのログ → 2か所の証明書比較です。
同じ症状が出たら、委任者が消えたと判断する前に、まずAPIが正常な一覧を返しているか確認してみてください。今回いちばん見落としやすかったのは、更新済みのNode側ではなく、実際にGatewayが使っている証明書でした。

