3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

symbol-shoestringのhealthコマンド不具合調査

3
Last updated at Posted at 2026-03-14

事象

PythonがリンクするOpenSSLのバージョンによっては、ノードが正常に稼働しているにもかかわらず health コマンドが失敗する。

今回確認した環境では、Ubuntu 24標準のPythonはOpenSSL 3.0系とリンクしていた。
一方、uvで作成した仮想環境ではOpenSSL 3.5系とリンクしたPythonが入り、この事象が発生した。

PythonがリンクするOpenSSLのバージョンを調べるコマンドは以下。

python3 -c "import ssl; print(ssl.OPENSSL_VERSION)"

再現

Python + OpenSSL3.0系

Ubuntu標準のPythonで仮想環境を作成

mkdir shoestring-openssl30
cd shoestring-openssl30
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install symbol-shoestring

バージョンの確認

$ python3 --version
Python 3.12.3

$ python3 -c "import ssl; print(ssl.OPENSSL_VERSION)"
OpenSSL 3.0.13 30 Jan 2024

shoestringのウィザードを使用して、Symbol Dualノードの環境を作成。

LC_MESSAGES=ja python3 -m shoestring.wizard

ノードを起動後、health コマンドを実行する。

docker compose up -d
LC_MESSAGES=ja python3 -m shoestring health --config shoestring/shoestring.ini --directory .

問題なくhealthコマンドが動作する。

$ LC_MESSAGES=ja python3 -m shoestring health --config shoestring/shoestring.ini --directory .
     ...    | モジュール peer certificate のヘルスエージェントを実行
      i     | ca 証明書の有効期限にはまだ余裕があります (7299 日間)
      i     | node 証明書の有効期限にはまだ余裕があります (374 日間)
keys/cert/ca.crt.pem: OK
keys/cert/node.crt.pem: OK
     ...    | モジュール peer API のヘルスエージェントを実行
      i     | ピアAPIにアクセス可能、ブロック高 = 1801
     ...    | モジュール REST API のヘルスエージェントを実行
      i     | REST APIにアクセス可能、ブロック高 = 1801
     ...    | モジュール REST websockets のヘルスエージェントを実行
      i     | エンドポイント ws://r.nemnesia.com:3000/ws にWebSocket接続中、ブロックを待機して購読中
      i     | WebSocketがブロックを受信しました。ブロック高 1802

Python + OpenSSL3.5系

uvでPython仮想環境を作成

mkdir shoestring-openssl35
cd shoestring-openssl35
uv venv --python 3.12
source .venv/bin/activate
uv pip install pip
pip install symbol-shoestring

バージョンの確認

$ python3 --version
Python 3.12.12

$ python3 -c "import ssl; print(ssl.OPENSSL_VERSION)"
OpenSSL 3.5.5 27 Jan 2026

shoestringのウィザードを使用して、Symbol Dualノードの環境を作成。

LC_MESSAGES=ja python3 -m shoestring.wizard

ノードを起動後、health コマンドを実行する。

docker compose up -d
LC_MESSAGES=ja python3 -m shoestring health --config shoestring/shoestring.ini --directory .

healthコマンドが失敗する。

$ LC_MESSAGES=ja python3 -m shoestring health --config shoestring/shoestring.ini --directory .
     ...    | モジュール peer certificate のヘルスエージェントを実行
      i     | ca 証明書の有効期限にはまだ余裕があります (7299 日間)
      i     | node 証明書の有効期限にはまだ余裕があります (374 日間)
keys/cert/ca.crt.pem: OK
keys/cert/node.crt.pem: OK
     ...    | モジュール peer API のヘルスエージェントを実行
    [!!!]   | ポート 7900 の localhost でピアAPIにアクセスできません
     ...    | モジュール REST API のヘルスエージェントを実行
      i     | REST APIにアクセス可能、ブロック高 = 181
     ...    | モジュール REST websockets のヘルスエージェントを実行
      i     | エンドポイント ws://r.nemnesia.com:3000/ws にWebSocket接続中、ブロックを待機して購読中
      i     | WebSocketがブロックを受信しました。ブロック高 200

原因

今回の不具合は、shoestring本体というより、内部で使用しているlightapiのSSL/TLSハンドシェイク処理にあると考えられる。

lightapiでは、ssl.SSLContext の内部表現から SSL_CTX * を固定オフセットで取り出し、SSL_CTX_set_verify を呼び出している。つまり、Pythonオブジェクトのメモリ配置に依存したABI前提の実装になっている。

lightapi/python/symbollightapi/connector/SymbolPeerConnector.py
  # get python wrapper object address (SSL_CTX* is offset 16 bytes)
  ssl_context_object_address = id(self.ssl_context)
  ssl_context_raw_address = ctypes.cast(ssl_context_object_address, ctypes.POINTER(ctypes.c_uint64))[2]
  ssl_context_pointer = ffi.cast('SSL_CTX *', ssl_context_raw_address)

  self._verify_callback_wrapper = ffi.callback('int (*)(int, X509_STORE_CTX *)', self._verify_callback)
  lib.SSL_CTX_set_verify(
   ssl_context_pointer,
   lib.SSL_VERIFY_PEER | lib.SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
   self._verify_callback_wrapper)

この方法は、Python側の内部表現やリンク先のOpenSSLが変わると崩れる可能性がある。実際に今回の再現では、OpenSSL 3.0系とリンクしたPythonでは正常に動作し、OpenSSL 3.5系とリンクしたPythonではpeer APIの接続だけが失敗した。

そのため、原因は「OpenSSL 3.5そのものが悪い」というより、lightapiが前提にしているABIと、OpenSSL 3.5系をリンクしたPython実行環境のABIが一致せず、SSL_CTX_set_verify 周辺でハンドシェイクに失敗したことにある可能性が高い。

対応案

暫定対応(運用回避)

OpenSSL 3.0系とリンクしたPythonを使用する。

今回の再現環境では、Ubuntu 24標準Python(OpenSSL 3.0系)では health が成功したため、修正が入るまでの回避策としては有効。

ただし、この方法は実行環境をOpenSSL 3.0系に寄せる前提が必要であり、OpenSSL 3.5系を含む環境を同時に安定運用する解決にはならない。

恒久対応(推奨)

根本対応としては、lightapiのABI依存実装をやめる方向が望ましい。

具体的には次の2案がある。

  1. OpenSSL 3.5向けにABIを作り直す
  2. そもそもABIを使用しない

暫定対応では吸収しきれないため、実質的には恒久対応が必要になる。

保守性と再発防止の観点では、2の「ABIを使用しない」方針を推奨する。

3
0
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
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?