TT Lab
はじめる
学ぶ 学習パス コース

EAI 中間層をつくる

機関ごとに違う証明書と規格 — 対外系を立ち上げる

TT Labで続きを見る

目標

2つの外部機関と相互TLSでつながるFEPを作ります。機関ごとの証明書を発行してもらい、機関プロファイルをデータとして置き、機関の規格に翻訳して送り、許可アドレスからだけ受け、証明書の期限を監視します。

なぜ重要なのか

外部機関は、こちらの標準に合わせてくれません。規格・証明書・許可アドレスの違いを外部連携ゲートウェイ1か所に閉じ込めないと、機関が増えるたびに内側のシステムを直すことになります。そして外部障害の常連(証明書の期限切れ、証明書の取り違え、長さの基準の勘違い)は、すべてこの層で防ぐか、早く気づくことができます。

ステップ

  1. python3 /opt/lab/fixtures/eaimw/fep/pki.py init /root/eaimw/fep/partnersで2つの機関のCAを作成してください。機関ごとに秘密鍵とCSR(サブジェクトにO=LabHub Bank、CNにlabhubを含める)を作り、python3 /opt/lab/fixtures/eaimw/fep/pki.py sign A <CSR> /root/eaimw/fep/a.crt、… sign B <CSR> /root/eaimw/fep/b.crtで署名を受けます。鍵は/root/eaimw/fep/a.key・/root/eaimw/fep/b.keyです。
  2. インターフェース仕様書/opt/lab/fixtures/eaimw/fep/ORGS.mdを見て、/root/eaimw/fep/orgs.jsonを書いてください。最上位のキーは機関コード(201・301)、値はhost・port・format(fixed/json)・encoding(euc_kr/utf-8)・len_includes_self(Aはtrue、Bはnull)・ca・cert・key(絶対パス)・allow_ips(リスト)です。
  3. 機関Aのサーバーを起動し(nohup python3 /opt/lab/fixtures/eaimw/fep/org_server.py A > /root/eaimw/fep/a.out 2>&1 &、9441)、openssl s_clientにa.crt・a.key・機関AのCAを渡してPINGを1行送り、受け取ったPONG <지문>(プレースホルダーはフィンガープリントです)を/root/eaimw/fep/a-ping.txtに保存してください。
  4. 同じコマンドにb.crt・b.keyを差し込んで機関Aにつないでみて、標準出力と標準エラーを一緒に(2>&1)/root/eaimw/fep/wrong.txtに保存してください。ハンドシェイクが拒否されるはずです。
  5. /root/eaimw/fep/fepgw.py send <기관코드> <요청JSON파일>(プレースホルダーは機関コードとリクエストJSONファイルです)を作成してください。プロファイル(FEP_ORGS、既定は/root/eaimw/fep/orgs.json)から機関を探し、その機関の証明書で相互TLS接続し、機関のサーバー証明書をその機関のCAで検証します。ポートは、環境変数FEP_PORT_<기관코드>(プレースホルダーは機関コードです)があればそれを使います。201はリクエスト{guid,date,amount,acct,name}をA規格の固定長(長さは自分自身を含む)で送り、結果を{"org","rspCode","guid","realName","clientFp"}のJSON1行で出力します。301は{guid,custId}をPOST /v1/credit-checkで送り、{"org","rspCode","guid","grade","clientFp"}で出力します。応答コードはLH-STDにします(00→0000、14→B202、30→E102、それ以外はE500)。
  6. /root/eaimw/fep/inbound.py --port <P>を作成してください。外部機関がかけてくる接続(平文TCP、A規格)を受けます。送信元アドレスがどの機関のallow_ipsにもなければ、読まずに切断し、FEP_LOG(既定は/root/eaimw/fep/inbound.log)にDENY <주소>(プレースホルダーはアドレスです)を1行残します。許可されたアドレスなら、A規格のレスポンス(電文種別0210、同じ取引固有番号、応答コード00、実名欄は空白20個、フィンガープリント欄は-が64個、長さは自分自身を含む)を返します。
  7. /root/eaimw/fep/certcheck.py --warn-days <N>を作成してください。プロファイルの機関ごとに<기관코드> <만료일 YYYY-MM-DD> <남은일수> <OK|WARN>(プレースホルダーは機関コード、有効期限、残り日数です)を1行、機関コード順に出力します。残り日数がN未満ならWARNで、WARNが1つでもあれば、0以外のコードで終了します。

参考

機関ごとの証明書を発行してもらう

2つの機関CAを作り、機関ごとに鍵とCSRを作って署名を受け、a.crt/a.key・b.crt/b.keyを置いてください。

秘密鍵はこちら側に置き、CSRだけを送ります。機関AにはAが、機関BにはBが署名する必要があります。1つのCAが両方に署名すると、機関が他方の証明書を拒否できません。

機関プロファイルをデータにする

ORGS.mdを見て、/root/eaimw/fep/orgs.jsonに2つの機関のアドレス・形式・エンコーディング・長さの基準・証明書・許可IPを書いてください。

コードに機関名が登場しないよう、機関ごとに違う値はすべてこのファイルに移します。長さの基準はJSONの機関には該当しないので、nullです。

機関Aと相互TLSでつなぐ

機関Aのサーバーを起動し、a.crtでPINGを送って、PONG <フィンガープリント>を/root/eaimw/fep/a-ping.txtに保存してください。

openssl s_clientに、-cert・-key(自分の身元)と-CAfile(機関のCA、サーバー検証)を一緒に渡します。-quietなら、レスポンス本文だけが残ります。フィンガープリントは、機関が見た自分の証明書のものです。

証明書を取り違えて差し込むと拒否される

b.crt・b.keyで機関Aにつないでみて、出力とエラーを一緒に/root/eaimw/fep/wrong.txtに保存してください。

ステップ3のコマンドで、証明書と鍵だけを変えます。拒否の理由(TLS alert)は標準エラーに出るので、2>&1で一緒に保存してください。

機関ごとに選んで送る

/root/eaimw/fep/fepgw.py send <機関> が、機関ごとの証明書・規格で送り、結果をLH-STDコードに正規化したJSONを1行出力するようにしてください。

プロファイルのformatを見てアダプターを選びます。Aは本文をすべて作ってからlen(本文)+4を長さとして書きます(自分自身を含む)。サーバー検証はcreate_default_context(cafile=機関CA)、自分の身元はload_cert_chainです。

受ける側。許可アドレスからだけ

/root/eaimw/fep/inbound.py --port Pが、プロファイルのallow_ipsから来た接続だけを受けてA規格で答え、それ以外は切断してDENYを記録するようにしてください。

socketserverのハンドラーのself.client_address[0]が送信元アドレスです。許可リストは、すべての機関プロファイルのallow_ipsを合わせたものです。拒否するときは、読まずにすぐ戻ってください。

期限を先に知る

/root/eaimw/fep/certcheck.py --warn-days Nが、機関ごとに有効期限・残り日数・OK/WARNを出力し、WARNがあれば0以外のコードで終了するようにしてください。

証明書のnotAfterを読み、現在(UTC)との差を日数で数えます。更新に数週間かかる機関があるので、基準は余裕をもって設定してください。監視ツールは終了コードで気づきます。