nabeo がピーしているブログ (仮)

どーも、nabeop です

unbound-anchor で DNSSEC 向けトラストアンカーを更新すると EXIT CODE 1 で終了する

趣味活動で unbound の dnstap とか EDNS Client Subnet の挙動を確認したかったので、unbound のコンテナイメージを作っていたんだけど、最後に /usr/local/etc/unbound 以下の環境を作るところで、エラー終了してホゲーとなっていた。問題になっていたのは以下の RUN 命令部分。

 RUN addgroup unbound && adduser -S -G unbound unbound && \
     /usr/local/sbin/unbound-control-setup && \
     /usr/local/sbin/unbound-anchor -a /usr/local/etc/unbound/root.key && \
     mkdir -p /usr/local/etc/unbound/dnstap && \
     chown unbound:unbound /usr/local/etc/unbound/dnstap

で、ブログの表題でほとんどネタバレしているんだけど、原因になっていたのは /usr/local/sbin/unbound-anchor -a /usr/local/etc/unbound/root.key && \ の部分。unbound を運用している人にとっては当たり前かもしれないけど、unbound-anchor でトラストアンカーを更新した時は終了コードが1になるらしい。unbound-anchor のマニュアルにも以下のようにキッチリ明示されている。

EXIT CODE This tool exits with value 1 if the root anchor was updated using the certificate or if the builtin root-anchor was used. It exits with code 0 if no update was necessary, if the update was possible with RFC5011 tracking, or if an error occurred.

   You can check the exit value in this manner:
        unbound-anchor -a "root.key" || logger "Please check root.key"
   Or something more suitable for your operational environment.

しかも、解決方法も書いてあるし...

とりあえず、以下のように手元の Dockerfile を書き直して最後まで完走できることは確認した。

RUN addgroup unbound && adduser -S -G unbound unbound && \
    unbound-control-setup && \
    /usr/local/sbin/unbound-anchor -a /usr/local/etc/unbound/root.key || echo "hoge" && \
    mkdir -p /usr/local/etc/unbound/dnstap && \
    chown unbound:unbound /usr/local/etc/unbound/dnstap

mkr の emacs-helm インターフェースを作ってみた

helm て何

世間では k8s が流行ているので、helm といったら、k8s のパッケージシステムの helm が一般的になりつつあって少し残念な気分になっています。

emacs の世界で helm というと、こちら。僕も emacs で生活しているので、しょっちゅう helm のお世話になっている。helm が使えなくなると、仕事の効率が激しく落ちてしまうレベルで依存しています。ここでは helm というと混乱しそうなので、emacs-helm と記述することにします。

mkr の出力を emacs-helm でも扱いたい

対して、mackerel.io も無くなったら途方にくれるレベルで仕事に使っているので、emacs の中から mackerel.io に登録しているホストのメトリクス情報や監視結果を参照できるようにしたいなー、と思っていた。

mackerel.io には mkr というナイスな CLI ツールがあるので、この出力を emacs-helm に食べさせてよしなに扱えればとりあえず目的は達成できそう。mkr は結果を json 形式で出力してくれる。emacsjson データの扱い方がわからなかったので、そのままにしていたんだけど、ある日、helm-aws というパッケージを見ていると、そのものズバリな感じで扱っていたので、やる気が出てきた。

作った

というわけで、エイヤーと作ってみたので、github.com に公開してみます。melpa.org には登録していないので、以下のように emacs の load-path が通ったところにレポジトリごと git clone してください。

git clone https://github.com/nabeo/helm-mkr.git path/to/emacsd/site-lisp/helm-mkr

mkr を使える環境を整えて、~/.emacs.d/init.el とかに以下のように書いておいてください。

(require 'helm-mkr)
(setq mkr-org "your mackerelio org name")

mkr-org は mackerel.io の URL にはオーガニゼーションの文字列が含まれるので、自身のアカウントが所属しているオーガニゼーション名をいれておく必要があります。

ここまで準備が整ったら、M-x helm-mkr*helm-mkr* バッファに mackerel で管理しているホストの一覧が出ててきます。

今の所、デフォルトのアクションは選択したホストのホスト名をコピーします。アクションの候補として、browse-url 経由で選択したホストで収集されたメトリクスページに移動できるアクションを登録しています。

現状ではこんな感じだけでど、アラートのリストまで取れるようになったら(あと、要望とかあれば)、mepla.org とか emacs のパッケージ配布サイトに登録して手軽に使えるようにしたい。

infrataster-plugin-dns で DNS サーバの検査をする

DNS サーバの挙動をテストする

インフラの挙動をテストするためのフレームワークである infrataster という仕組みがあります。サーバ構築時のテストフレームワークServerspec が有名ですが、対象をインフラにしたものという乱暴な理解です。

で、インフラといえば DNS なんですが、僕は小心者なので DNS サーバの設定でしくじったりすると、被害が甚大なので、小さな修正でもドキドキしながらオペレーションをしています。また、設定直後はうまく動いているように見えて、実は他のエントリを書き換えてしまい後から障害になってしまう、ということも考えられます。まさに自分が自宅の内部ネットワークのメンテ時に凡ミスして一時的に名前解決ができなくなってツライ目にあってしまったので、infrataster の復習がてら、infrataster-plugin-dns を使ってみたので、そのメモを残しておこうと思います。

infrataster そのものは DNS サーバのテストはできませんが、infrataster-plugin-dns はその名前のとおり infrataster のプラグインとして作成されて、特定の DNS サーバを対象にしてクエリ応答のテストができます。

準備

infrataster は rspec の仕組みのうえに構築されているので、rspec --initrspec 的な準備をしておきます。

spec/spec_helper.rb には以下のように検査したい DNS サーバを定義します。

require 'infrataster/rspec'
require 'infrataster-plugin-dns'

Infrataster::Server.define(:exapmle_com, 'a.iana-servers.net')

ここでは例として example.com の権威サーバの a.iana-servers.net:example_com として定義しています。

テストケースは spec/example.com_spec.rb とかに書いていきます。

require  'spec_helper'

describe server(:exapmle_com) do
  # ここにテストケースを書いていく
end

頑張ってテストケースを書く

ここからは運用している DNS サーバに登録している内容をひたすら列挙していく作業です。

infrataster-plugin-dnsREADME.md にも書かれていますが、基本的に rspec 的なマッチャーは rspec-dns をベースにしているので、そちらを参照していきます。さらに、rspec-dns は内部的に Dnsruby を呼び出しているので、各 RR の応答を検査するときは Dnsruby::RR クラスのドキュメントを読みながら空気を察する必要があります。

というわけで、いくつか例をあげておきます。

NS レコード

example.com. の NS の場合は dig で確認すると以下のように応答がありました。

;; QUESTION SECTION:
;example.com.                   IN      NS

;; ANSWER SECTION:
example.com.            86400   IN      NS      a.iana-servers.net.
example.com.            86400   IN      NS      b.iana-servers.net.

この場合は Dnsruby::RR::NS を見ながら書くとこんな感じになります。

describe dns('example.com.') do
  it 'NS RR : a.iana-servers.net' do
    is_expected.to have_entry.with_type('NS').
                     and_ttl(86400).
                     and_domainname('a.iana-servers.net')
  end
  it 'NS RR : b.iana-servers.net' do
    is_expected.to have_entry.with_type('NS').
                     and_ttl(86400).
                     and_domainname('b.iana-servers.net')
  end
end

SOA レコード

example.comSOAdig で確認すると以下のようになっています。

;; QUESTION SECTION:
;example.com.                   IN      SOA

;; ANSWER SECTION:
example.com.            3600    IN      SOA     sns.dns.icann.org. noc.dns.icann.org. 2018013047 7200 3600 1209600 3600

で、NS の時と同じように Dnsruby::RR::SOAアトリビュートとRR応答の仕様を見比べながら書いていきます。

describe dns('example.com.') do
  it 'SOA RR' do
    is_expected.to have_entry.with_type('SOA').
                     and_ttl(3600).
                     and_mname('sns.dns.icann.org').
                     and_rname('noc.dns.icann.org').
                     and_refresh(7200).
                     and_retry(3600).
                     and_expire(1209600).
                     and_minimum(3600)
  end
end

その他の RR

基本的に Dnsruby::RR のドキュメントや infrataster-plugind-dnsrspec-dns の例を見ておけば、なんとなく雰囲気は察することができると思いますし、代表的な RR については例があるのでわかりやすいと思います。

最後に書きながらハマったところをメモがてら残しておきます。

DNSEC 周りの応答には Base64エンコードしたデータが含まれていますが、そのまま and_signature に渡すとテストに失敗します。Base64.decode64 で符号化を解いた状態でマッチャーに渡してあげましょう。コードの中身まで追っていませんが、Dnsruby の処理でそのようになっているような気がします。例えば、example.com の DNSKEY の1つは以下のように応答があります。

example.com.            3600    IN      DNSKEY  256 3 8 AwEAAblDjCPejMhknWXZqbwBEUPI6Lkwjvp0XlUNTBqW2glZrgf3MXjJ ZBXl8rhYoTkrov7jmbBaBOPTkqlQAbfOKFNoG+U+boGG6Zmy00l2XRP1 nckVMpJ2TxiDVcXJqs78MetC1Ztu4p6bj4VrJCYTmv3ZULSrWleMSWtv YXqY0S23

この場合は最後の公開鍵部分が Base64エンコードされているので、以下のようにするとテストできます。

describe dns('example.com.') do
  it 'DNSKEY RR' do
    is_expected.to have_entry.with_type('DNSKEY').
                     and_key(Base64.decode64('AwEAAblDjCPejMhknWXZqbwBEUPI6Lkwjvp0XlUNTBqW2glZrgf3MXjJZBXl8rhYoTkrov7jmbBaBOPTkqlQAbfOKFNoG+U+boGG6Zmy00l2XRP1nckVMpJ2TxiDVcXJqs78MetC1Ztu4p6bj4VrJCYTmv3ZULSrWleMSWtvYXqY0S23'))
  end
end

このようにあらかじめテストケースを網羅的に用意して、履歴を管理することで、思わぬ事故を防ぎ、家庭の平和(と父親の威厳)を守りたいものです。

トンネルを掘る

自宅で使っている RTX810 くんを眺めていたら、L2TP/IPsecVPN トンネル掘らない?と問いかけられたので、掘ってみた。

設定サンプルヤマハネットワークのサイトがあったので、ほぼそのまま持ってきている。

まずは LAN 側のインターフェースでトンネルの向こう側にいるクライアントに LAN 側の arp リクエストを代理応答するように設定を入れておく

ip lan1 proxyarp on

次に L2TP 接続を受け入れるためのインターフェースを作る

pp select anonymous
pp bind tunnel1
pp auth request chap-pap
pp auth username <L2TPユーザ> <ユーザのパスワード>
ppp ipcp ipaddress on
ppp ipcp msext on
ip pp remote address pool 192.168.11.226-192.168.11.237
ip pp mtu 1258
pp enable anonymous

pp auth usernameL2TP 接続をするクライアントの数だけ記述する。今回は僕が外出先から繋げれればいいので、1つだけで十分。ip pp remote addressL2TP クライアントに払い出すアドレスをレンジで確保しているのは、将来的に増えることを見越して、多めにとっている。

次に L2TP/IPsecVPN トンネルを作るための設定を入れる。

tunnel select 1
tunnel encapsulation l2tp
ipsec tunnel 1
ipsec sa policy 1 1 esp aes-cbc sha-hmac
ipsec ike keepalive use 1 off
ipsec ike local address 1 <lan1のアドレス>
ipsec ike nat-traversal 1 on
ipsec ike pre-shared-key 1 text <IPsec の事前共有鍵>
ipsec ike remote address 1 any
l2tp tunnel disconnect time off
l2tp keepalive use on 10 3
l2tp keepalive log on
l2tp syslog on
ip tunnel tcp mss limit auto
tunnel enable 1

l2tp syslog on しているのは、設定中にどハマって、あるぇ?となることを見越して、L2TP 周りのログを syslog で吐くようにしておいた。

次に IPsec のトランスポートモードを設定しておく

ipsec auto refresh on
ipsec transport 1 1 udp 1701

あとはフィルターを書いて、必要なパケットが通過できるようにしておく。

ip filter 200080 pass * <lan1のアドレス> esp * *
ip filter 200081 pass * <lan1のアドレス> udp * 500
ip filter 200082 pass * <lan1のアドレス> udp * 1701
ip filter 200083 pass * <lan1のアドレス> udp * 4500

で、このフィルタを PP インターフェースの in に追加しておく。

ip pp secure filter in <設定しているフィルターの番号> 200080 200081 200082 200083

あとは PP インターフェースの nat descriptor に L2TP/IPsec 用の設定をいれておく。

nat descriptor type 1 masquerade
nat descriptor address outer 1 ipcp
nat descriptor address inner 1 auto
nat descriptor masquerade static 1 1 <lan1のアドレス> esp
nat descriptor masquerade static 1 2 <lan1のアドレス> udp 500
nat descriptor masquerade static 1 3 <lan1のアドレス> udp 4500

ここまで来たら、RTX810 側の受け入れ準備は完了しているので、端末からトンネル掘って、show status l2tp とかして、様子を見る。

というわけで、出先でホームシックになっても、少なくともネットワーク的にはお家に帰れるようになって幸せになれました。

あとは、テンプレート機能をつかって2つ目以降は手数を少なくしてトンネル開通できるらしいので、新しいラップトップを入手するための家庭内稟議を通す努力をしたいと思います。

株式会社はてなに入社して1ヶ月が経ちました

3/1付けで株式会社はてなに入社して、1ヶ月経ちました。というわけで、4/1のエントリですが、本当です。職種は Web オペレーションエンジニアです。つまり、サーバとかインフラ周りのお守りをしています。

「...で、誰?」とか言われそうですが、ちゃんとアウトプットして、1日でも早くインターネットで認識されるようになりたいです。

で、アウトプットの1発目としてこの1ヶ月を振り返ってみます。

続きを読む