自作の Symbol(XYM)のウォレット、heika には、ノードも入っている。ボタンひとつで、自分の PC の中でノードを動かせる。自分のノードに残高を聞けば、どこかの REST を信じなくて済む。ハーベストも自分のノードでできる。便利である。便利なのだが、ひとつ気になることがあった。
ノードを動かすと、その PC がネットワークに顔を出す。 ほかのノードとつながる以上、IP アドレスは相手に見える。そこに財布のアカウントが結びついてしまったら、「この IP の人は、このアカウントでこれだけ持っている」が筒抜けになる。それはちょっと、いやぁ……その、やだよねぇ……うん、無理😇
なので heika では、「ノードからアカウントに足がつかない」ことと、「PC を見られても、隠しアカウントがあると言い切られない」ことに気を配った。この記事は、その配慮の一覧である。
ノードの鍵は、財布と関係のない鍵にする
Symbol のノードには鍵が 2 つある。
- メインの鍵(CA の鍵): ノードの持ち主として名乗る鍵。エクスプローラーのノード一覧に「メインアカウント」として出る
- ノードの鍵(TLS の鍵): ほかのノードとの通信に使う鍵。委任ハーベストの NodeKeyLink は、こちらにつなぐ
catapult(Symbol の本家のノード)を shoestring で立てるときは、CA の鍵に自分のアカウントの鍵を使うこともできる。持ち主がはっきりするので、ノードを公開して運用するならそれもありだ。
でも、自分の PC でこっそり動かすノードでそれをやると、ノードの一覧に自分のアカウントが載る。IP アドレスつきで。
heika のウォレットは、初めてノードを起動するときに、CA の鍵もノードの鍵も、財布とは関係のない新しい鍵として作る。なので、エクスプローラーでノードのメインアカウントを見ても、そこにあるのは残高 0 の知らないアカウントだ。財布のアカウントとは、チェーンの上ではどこもつながっていない。
ちなみに、これで「ノードのメインアカウントが、取り込んだアカウントと違う!」と戸惑われた方がいらっしゃったようだ。そう、違うのである。わざとなのである。説明不足。一応ごめんなさいしておこ。ごめんなさい……🙇💦💦💦
外から探されにくくする
ウォレットの中のノードは、P2P も REST も、この PC の中(127.0.0.1)だけで待ち受ける。外から話しかけることはできない。ノードのほうから、ほかのノードにつなぎに行くだけだ。
それでも、つないだ相手のノードには「こういうノードがいる」と覚えられて、ピアの一覧に載る。これは P2P でつながる以上、避けられない。避けられないのだが、名乗る中身は減らせる。
Symbol のノードは、ほかのノードに自分の役割(roles)を名乗る。
- Peer: ブロックやトランザクションをやりとりする
- Api: REST を立てている。財布やクローラーは、この印を見て REST を探しに来る
heika のノードは、REST の口があれば Api を名乗る作りだった。ウォレットの中のノードにも、自分のノードに残高を聞くための REST がある。ただしこの PC の中だけの口だ。なので、外からは届かない REST を「ありますよ」と宣伝していたことになる。探しに来た人は空振りするし、こちらとしても目立つだけで、誰もはっぴーにならない。それさすがにやだ。
これは使っている方からご指摘をいただいて気づいた。ありがとうございます! v0.12.0 から、ウォレットの中のノードは Peer だけを名乗る。
Peer のほうは、外からつなげなくても名乗ったままにした。NAT の内側にいて、自分からつなぎに行くだけのノードも、Peer を名乗る。そういうノードは珍しくないので、目立ちにくい。木を隠すなら森の中である🌲
鍵を持つウォレットと、外としゃべるノードを、プロセスで分ける
ノードは、知らない相手から届いたバイト列を朝から晩まで読み続ける。どこかに読み方の穴があれば、狙われるのはそこだ。そこと同じプロセスに、ニーモニックを開いたウォレットがいるのは落ち着かない。
なので、パソコン版では、ノードをウォレットとは別のプロセスで動かす。別の実行ファイルは配っていない。ウォレットが自分自身を --node <設定ファイル> で起動し直して、子のプロセスがノードになる。
- ウォレットのプロセス: キーストアを開き、署名する。ほかのノードとブロックをやりとりしない
- ノードのプロセス: ほかのノードとやりとりする。キーストアは持たない
子には、標準入力の管でだけ話しかける。ハーベストの鍵も、この管で渡す。親が落ちれば管が閉じるので、子は今のブロックを書き終えてから止まる。ノードだけが取り残されて動き続けることはない。
万が一ノードのほうに穴があっても、別のプロセスのメモリにあるニーモニックまでは、すぐには手が届かない。もちろん、同じユーザーで動いている以上、完全な壁ではない。それでも、一枚あるとないとでは大違いである🧱。OSカーネルが隔離してくれるもん✨。でもカーネルエクスプロイトはこゎぃ……。
残高は、自分のノードに聞ける
ウォレットは、残高を見るにも送金するにも、どこかのノードの REST に話しかける。ふつうは、外の誰かのノードだ。
すると、その REST の持ち主には「この IP の人が、このアドレスの残高を見ている」がわかる。表の財布と隠しアカウントを同じ REST で見れば、その 2 つが同じ人のものだということまでわかる。ログを取っていなければ大丈夫だが、取っているかどうかは、こちらからは見えない。
heika のウォレットは、設定で選べば、残高も送金の確かめも、自分のノードの REST(この PC の中)に聞く。誰がどのアドレスを見ているかは、PC の外に出ない。冒頭で「どこかの REST を信じなくて済む」と書いたが、信じなくて済むだけでなく、見られなくて済むのである👀✨
ハーベストの鍵は、ノードのメモリにだけ渡す
ウォレットの「このノードでハーベスト」を押すと、財布のアカウントに、リモートの鍵と VRF の鍵をリンクする。
- 2 つの鍵は、キーストアに入っている
Harvest-Secretから作る。財布の鍵そのものではない - ノードには、メモリだけで渡す。ディスクには書かない。ノードを止めれば消えるので、起動し直したら渡し直す
- チェーンに送るのは、AccountKeyLink と VrfKeyLink だけ。NodeKeyLink は送らない
最後のところがだいじだ。外のノードに委任するなら NodeKeyLink が要るが、自分のノードでハーベストするのに、自分のノードの鍵をチェーンに書く必要はない。書いたら最後、「このアカウントはこのノードの人」と永遠に残る。リンクを外しても、記録は消えない。ブロックチェーンは忘れてくれないのである。私も気をつけよ……。
ディスクに手がかりを残さない
heika の財布は、BIP39 のパスフレーズ(いわゆる 25 番目の単語)で開く「隠しアカウント」を使える。同じニーモニックでも、パスフレーズが違えば、まったく別のアカウントになる。誰かにパスフレーズなしで開けと言われても、表の財布だけを見せられる。
ただし、これが効くのは、隠しアカウントがあった痕跡がどこにもないときだけだ。PC の中に隠しアカウントのアドレスがひとつでも残っていたら、「ほかにもありますよね?」と言われて終わる。
なので、heika はディスクに書くものを絞っている。
-
キーストアは暗号化する。 パスフレーズで暗号化した age の形式で、heika がなくても
age -dで開ける(heika がなくなっても困らない)。書いている途中で落ちても、ファイルが壊れないように書く - 送った控えは、メモリにだけ置く。 隠しアカウントからの送金がファイルに残ると、隠しアカウントがあることを否認できなくなるからだ。控えが要るのは期限(2 時間)までなので、それより長く覚えておく理由もない
- 設定ファイルには、アドレスを書かない。 最後に開いたアカウントは、アドレスではなく、キーストアの中の番号で覚える
じつは最後のは、最初はアドレスで覚えていた。気づいたときは、この絵文字のようになった→ 😱 今は直っている。Got ことなき、だといいな……。
秘密の扱い
細かいけれど、地味だけど、それでもだいじなこと。
- 秘密鍵は、使い終わったらメモリから消す(Rust の
zeroize) - CLI では、秘密鍵を引数では受け取らず、標準入力からだけ読む。シェルの履歴に残らないように
- 送る前に、下書きを画面で確かめてもらってから署名する
まだできていないこと
ちゃんと正直に書いておく。
- ハーベストしたブロックは、最初にそのノードから流れる。 チェーンには IP は残らない。でも、つながっていたピアの側で「このブロックはこの IP から最初に来た」と見られれば、ハーベストしたアカウントの見当はつく。ここは今のところ防いでいない
- 送金も、最初はそのノードから流れる。 自分のノードから送れば、ハーベストのブロックと同じく、ピアの側で「このトランザクションはこの IP から最初に来た」と見られうる。外の REST から送れば、今度はその REST の持ち主に見える。今のところ、どちらにしても、どこかには見える
- Android では、プロセスを分けていない。 Android のアプリは実行ファイルではなく、Java から読み込まれるライブラリなので、自分を起動し直せない。今は、ノードをアプリのプロセスの中のスレッドで動かしている
- 自分のノードに外から委任すると、結びつく。 別の財布から、自分の heika のノードあてに NodeKeyLink を送れば、そのアカウントとノードはチェーンの上で結びつく(そもそも、ウォレットの中のノードは委任を引き受けないので、ハーベストも始まらない)。外へ委任する画面を作るときに、ちゃんと説明を出さなきゃ……。
おわりに
「便利なものを作ったら、足がつくようになった」では悲しい。自分のデバイスでノードを動かすのが当たり前になるなら、そのぶん気を配るところも増える。今回はその気をつけることリストを書いてみた。
抜けているところに気づいたら、ぜひ教えてほしい。いやその私に抜けてるところがあるのはもうわかってるから、今回の roles の件のように、教えてもらって初めて気づくことはけっこうある。