投稿者「pyama86」のアーカイブ

Rustで実装したPAMモジュールを利用してGoogle OAuth認証でSSHを多要素認証する

以前開発した、SSHログインの認証時にGoogleのOAuthを利用して認証を行うPAMモジュールをRustで再実装しました。

以前Golang + C言語で実装したものは下記のエントリにあります。

モチベーション

以前開発したものは開発当初の実装が、Golangで開発したCLIコマンドを、sshdの ForceCommand ディレクティブからコールするというコンセプトから始まり、それだと踏み台として利用した場合に、パススルーされる問題があったことから、CLIをPAMから呼び出すという実装にしたため、インストールが煩雑であったり、そもそも利用しづらいとうことがありました。それらの問題を解消するのと、自身の学習のために、Rustを利用して、PAMモジュールから直接Google OAuth認証できるようにしました。

動作イメージ

アニメーションではパスワード認証を利用していますが、他にも公開鍵認証+OAuthのように複数の認証方式を組み合わせることができます。これによってパスワードや秘密鍵が流出したとしても、攻撃者からのログインが困難になります。

インストール方法

curlコマンドでシュッとインストールできます。

$ curl -s https://pam-google-web-oauth.lolipop.io/install.sh | bash -

OAuthに利用するClienntIDとClientSecretは、OAuth 2.0 の設定 を参考にGoogleの認証情報を作成し、下記のようにoAuthクライアントを払い出してください。

最後に

今回はじめてRustを触ったのですが、ResultやOptionを利用したパターンマッチングや、ジェネリクス、生存期間、トレイとなどこれまで触ってきた言語で自身が扱わなかった領域の技術があっため、最初はかなり苦労したのですが、手を動かしつつ、GitHubで類似実装をみたり、書籍を読みすすめることで理解を深めることができました。よく言われることですがC言語で起きがちなメモリ管理の実装ミスなどがコンパイラのレベルで色々気づけたり、Golangに近い言語レベルで各種ユーティリティが揃っているのはとてもいいなと思いました。

今の会社で行う業務で低レイヤーのプログラミングはまだまだ機会があると思うので、次はもう少し大きい規模のコードを実装してみたいと思った。

カテゴリー: tech

Slackで不在時に代理応答する仕組みを作った

今日からペパボで運用を始めた仕組みの紹介です。

モチベーション

ペパボでは新型コロナウィルスをうけて、全社でリモートワークを前提として事業活動を継続しています。そのなかでSlackの各チームのチャンネルを眺めていると、「今からランチに行きます」とか「ちょっと席を外します」といったSlack投稿が結構見受けれました。これはおそらくチームメンバーに対する気遣いで、暗にすぐに反応できないことを伝えているのだと思いました。また有給休暇の際にもステータスメッセージや、ディスプレイネームで工夫しているケースが多くあります。(大和田太郎@8/24有給など)

一方でこれらの発言によって誰がご飯に行っているとか不在とかわかりそうですが、100人とかいるチャンネルだと、なかなか全員の状況を把握するのが難しくなります。そこで、不在時にメンションが飛んできた場合に、代理で応答する仕組み Away From Keyboardを開発し、運用をはじめました。

使い方

導入はgithub.comのREADMEに書きました。支援が必要な人はissueに書いてもらえればサポートします。

導入すると /lunch コマンドと /afk (away from keyboard) コマンドが利用可能になります。

Slackでスラッシュコマンドを実行すると、不在時にメンションを受けた際に不在応答してくれます。標準でそれらしいメッセージを応答しますが、カスタムしたい際は、/afk 今日から8/30までたびに出ます、探さないでください や  /lunch 子供と食べるので14時過ぎそう といったように好きなメッセージを入れることも可能です。席に戻った際は /comeback と入力してください。

実際の様子は下記のような感じです。

最後に

僕自身がリモートワークを前提として働く中でオフィスでは誰が席にいるというような情報が目で見てわかるが、リモートワークだと相手がいるかいないかは結構わからないところに不便さを感じていたのと、またリモートワークを前提とする場合に同期的に働くよりも非同期を前提として働いたほうが効率的に働けると感じているため、今回の仕組みを作りました。

またこれ以外でもオフィスで機能していた働き方をそのままリモートワークに持っていくと知らずとバッドノウハウになっているケースも多くあるのではないかなと思います。今後も僕自身が気づき、改善したものはOSSに切り出して広く公開していきます。

これからお盆休みに入るので活躍の場面が多い時期です。ぜひ導入を検討してみてください。

カテゴリー: Ruby

雑メモ:Slackでチャンネルに特定のユーザーをもれなく呼びつける

とにかく雑にいま書いたスクリプトだけど、なんかユースケースそれなりにあるかもしれないから放流しておきます。

require 'slack-ruby-client'
slack = Slack::Web::Client.new(token: ENV["SLACK_API_TOKEN"])


members = []
next_cursor = nil

loop do
  slack_users = slack.users_list({limit: 1000, cursor: next_cursor})
  members << slack_users['members']
  next_cursor = slack_users['response_metadata']['next_cursor']
  break if next_cursor.empty?
end
members.flatten!

channels = []
next_cursor = nil
loop do
  slack_channels = slack.channels_list({limit: 1000, cursor: next_cursor})
  channels << slack_channels['channels']
  next_cursor = slack_channels['response_metadata']['next_cursor']
  break if next_cursor.empty?
end
channels.flatten!

user = members.find {|m|m["name"] == ENV['SLACK_USER']}

channels.each do |c|
  next if c["is_archived"]
  begin
    slack.conversations_invite({channel: c["id"], users: user["id"]})
  rescue Slack::Web::Api::Errors::AlreadyInChannel
    next
  rescue Slack::Web::Api::Errors::NotInChannel
    slack.conversations_join({channel: c["id"]})
    begin
      slack.conversations_invite({channel: c["id"], users: user["id"]})
    rescue Slack::Web::Api::Errors::AlreadyInChannel
      next
    ensure
      slack.conversations_leave({channel: c["id"]})
    end
  rescue => e
    pp c
    pp e
  end
end
カテゴリー: tech

CentOS7でDockerでコンテナから外部通信できなくてハマった

仕事でCentOS7でDockerを使うことがあってハマった。具体的にはコンテナから外部通信ができない。

$ # docker run --rm  --network bridge  -it centos:latest  /bin/bash
[root@15b96c6afbda /]# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
...

なんでだろうと思って、手癖でtcpdumpを打つと。

$ tcpdump -i docker0 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
17:25:09.820768 IP 192.168.0.2 > 8.8.8.8: ICMP echo request, id 16, seq 1, length 64
17:25:09.821863 ARP, Request who-has 192.168.0.2 tell 192.168.0.1, length 28
17:25:09.821874 ARP, Reply 192.168.0.2 is-at 02:42:c0:a8:00:02, length 28
17:25:09.821877 IP 8.8.8.8 > 192.168.0.2: ICMP echo reply, id 16, seq 1, length 64

経験からプロミスキャストモードのときだけパケットが転送されることを瞬時に理解したので、まず1ツイート

ifconfig docker0 promiscas とか打てば、それはそれで通信できるんだけど、本来はこんなことやらなくてもいいはずなので、色々探していると、大体大事なことはsyslogに書いてあるんですね。

dockerd: time="2020-07-30T17:10:53.075020306+09:00" level=warning msg="Running modprobe bridge br_netfilter failed with message: modprobe: WARNING: Module br_netfilter not found.\ninsmod /lib/modules/3.10.0-327.el7.x86_64/kernel/net/llc/llc.ko \ninsmod /lib/modules/3.10.0-327.el7.x86_64/kernel/net/802/stp.ko \ninsmod /lib/modules/3.10.0-327.el7.x86_64/kernel/net/bridge/bridge.ko \n, error: exit status 1" 

br_netfilterについては こちら のページが詳しいです。さて、このbr_netfilterなんでないのだと思って、調べると。

$ lsmod |grep br_netfilter
$

ないでおじゃる。

このあたり の情報を見る限り古いkernelには含まれていなさそう。

ELRepo から新しめのカーネルを持ってきて、万事解決。

今回の教訓としては CentOSでDocker使うのやめましょう いかなるときも思い込みであれこれやらないようにしましょうでした。

カテゴリー: tech

クラウドネイティブなLinuxユーザー管理ミドルウェアのSTNSのクライアントソフトウェアlibnss-stns-v2 2.6.0をリリースしました(マジタイトル長くてウケる

先日のブログ で紹介したcache-stnsdとの連携に加えて、libcrypto.soの競合で起こるSEGV への対応として、OpenSSLとCurlをstaticリンクしました。

cache-stnsdとの連携

先日ブログを書いたところ、 @fujiwara さんから即フィードバックをもらえたので、cache-stnsdはインストールは必須としたものの、利用するかどうかは運用者で決定できるようにしました。

具体的には/etc/stns/client/stns.confuse_cached = true と書いた場合のみ、cachedを利用するようにします。またこれはv3のリリースでデフォルト値となります。

また先日Twitterにも書きましたが、開発者としても運用者としても使ってくださる皆様の要望に合理性の範囲内でなるべく答えたいと思っているので、TwitterなりGithub issueなりでどんどんご意見頂けると嬉しいです。

関連ライブラリのStaticリンクについて

ペパボで利用しているNginxとChefで問題がおきていたので、CurlとOpenSSLをsoにStaticリンクするようにしました。具体的にはNginxでOpenSSLをStaticリンクしている場合や、ChefがEnbeded Rubyを利用する関係でOpenSSL系のメソッドで競合が発生し、SEGVする事象がありました。

それらに対する根本的な対応として、Staticリンクすることで、より多くの環境で安定動作する方向に意思決定しました。

テスト実行に加えて、Ubuntu 16/18、CentOS7については手元で動かして動作検証を取ってありますが、その他のOSについては基本的にはビルドが通れば動くものではあると思っているので万が一何かあれば雑にご連絡ください。

カテゴリー: tech

libnss-stns-v2とohaiを組み合わせたときにSEGVする

いずれ調べるかって思ってたやつちゃんと調べた。

エラーメッセージ的にはこんな感じ。

g /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/ohai-14.4.2/lib/ohai/plugins/passwd.rb:19: [BUG] Segmentation fault at 0x00007f521bce3e20
g ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]
g 
g -- Control frame information -----------------------------------------------
g c:0026 p:---- s:0135 e:000134 CFUNC  :passwd
g c:0025 p:0073 s:0131 e:000130 BLOCK  /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/ohai-14.4.2/lib/ohai/plugins/passwd.rb:19 [FINISH]
g c:0024 p:---- s:0128 e:000127 CFUNC  :instance_eval
g c:0023 p:0058 s:0124 e:000123 METHOD /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/ohai-14.4.2/lib/ohai/dsl/plugin/versionvii.rb:128
g c:0022 p:0058 s:0118 e:000117 METHOD /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/ohai-14.4.2/lib/ohai/dsl/plugin.rb:104
g c:0021 p:0004 s:0114 e:000113 METHOD /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/ohai-14.4.2/lib/ohai/dsl/plugin.rb:187
g c:0020 p:0174 s:0109 e:000108 METHOD /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/ohai-14.4.2/lib/ohai/runner.rb:87
g c:0019 p:0070 s:0101 e:000100 BLOCK  /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/ohai-14.4.2/lib/ohai/runner.rb:48
g c:0018 p:0039 s:0097 e:000096 METHOD /opt/chef/embedded/lib/ruby/2.5.0/benchmark.rb:293
g c:0017 p:0011 s:0088 e:000087 METHOD /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/ohai-14.4.2/lib/ohai/runner.rb:41
g c:0016 p:0009 s:0082 e:000081 BLOCK  /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/ohai-14.4.2/lib/ohai/system.rb:93 [FINISH]
g c:0015 p:---- s:0078 e:000077 CFUNC  :each
g c:0014 p:0019 s:0074 e:000073 METHOD /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/ohai-14.4.2/lib/ohai/system.rb:92
g c:0013 p:0021 s:0065 e:000064 METHOD /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/ohai-14.4.2/lib/ohai/system.rb:83
g c:0012 p:0048 s:0060 e:000059 METHOD /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/chef-14.4.56/lib/chef/client.rb:608
g c:0011 p:0204 s:0054 e:000053 METHOD /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/chef-14.4.56/lib/chef/client.rb:260
g c:0010 p:0014 s:0042 E:000950 METHOD /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/chef-14.4.56/lib/chef/application.rb:303
g c:0009 p:0091 s:0038 e:000037 BLOCK  /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/chef-14.4.56/lib/chef/application.rb:279
g c:0008 p:0007 s:0034 e:000033 METHOD /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/chef-14.4.56/lib/chef/local_mode.rb:44
g c:0007 p:0040 s:0030 e:000029 METHOD /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/chef-14.4.56/lib/chef/application.rb:261
g c:0006 p:0099 s:0025 e:000024 METHOD /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/chef-14.4.56/lib/chef/application/client.rb:440
g c:0005 p:0019 s:0020 e:000019 METHOD /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/chef-14.4.56/lib/chef/application.rb:66
g c:0004 p:0063 s:0016 e:000015 TOP    /opt/chef/embedded/lib/ruby/gems/2.5.0/gems/chef-14.4.56/bin/chef-client:25 [FINISH]
g c:0003 p:---- s:0013 e:000012 CFUNC  :load
g c:0002 p:0585 s:0008 E:0006f0 EVAL   /usr/bin/chef-client:75 [FINISH]
g c:0001 p:0000 s:0003 E:000d60 (none) [FINISH]

ワークアラウンドとしては、chef打つ前にssh打って、getent passwd/group とか実行してキャッシュを作っておいて、stnsにhttpリクエストさせなければ普通に動く。

本題としては、libcrypto.soがembededなohaiとシステムのものを利用するstnsで異なることから、どちらのsoのファンクションを利用するか衝突しているようだった。

7f521b591000-7f521b82c000 r-xp 00000000 fc:01 2967                       /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1
7f521b82c000-7f521ba2b000 ---p 0029b000 fc:01 2967                       /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1
7f521ba2b000-7f521ba57000 r--p 0029a000 fc:01 2967                       /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1
7f521ba57000-7f521ba59000 rw-p 002c6000 fc:01 2967                       /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1
7f5220b97000-7f5220da7000 r-xp 00000000 fc:01 537824                     /opt/chef/embedded/lib/libcrypto.so.1.0.0
7f5220da7000-7f5220fa7000 ---p 00210000 fc:01 537824                     /opt/chef/embedded/lib/libcrypto.so.1.0.0
7f5220fa7000-7f5220fc2000 r--p 00210000 fc:01 537824                     /opt/chef/embedded/lib/libcrypto.so.1.0.0
7f5220fc2000-7f5220fce000 rw-p 0022b000 fc:01 537824                     /opt/chef/embedded/lib/libcrypto.so.1.0.0

基本的にnssモジュールはプロセスにリンクされるので、プロセスから名前解決のメソッドが呼ばれると、その先でstnsはcurlを打つので、そこでopensslもリンクされて、ぶつかるみたいな事象に見える。

対応としてはどうすっかなぁ。opensslをstaticリンクするか、はたまた何かしらの仕組みを入れてchefとか特定のプロセスのときはhttpを利用した名前解決しないようにするか。。。ちょい考える。

カテゴリー: tech

LDAPに変わるLinuxユーザー管理ソフトウェアのSTNSにキャッシュプロセスを追加した

僕が個人で開発を続けているSTNSのクライアントのマイナーバージョンアップをしました。

STNSについてはこちらが詳しいです。

今回の変更点としては、クライアントサイドにキャッシュプロセスをおきました。

これによって解決される課題は下記です。

  1. systemdによってlogin処理時にNetwork通信は原則許可されていないため、設定変更を行う必要があったがそれが不要になる
  2. libnss-stns-v2においてはファイルキャッシュを採用していたので、従業員がキャッシュファイルを書き換えると権限以上のアクセス権を得ようと思えば得られた

実装

cache-stnsdについてはlibnss-stnsとunix socketで通信させる実装にしました。すべての外部とのhttp通信やキャッシュ処理をcache-stnsdに移譲したのでCで実装されたlibnss-stnsの実装がかなり薄くなっています。

今後libnss-stns-v2をインストールする際はDependsにcache-stnsdが入っているので、普通に意識せずに利用できると思います。

インストール

debian系の例ですが下記で導入できます。

$ curl -fsSL https://repo.stns.jp/scripts/apt-repo.sh | sh
$ apt install libnss-stns-v2 cache-stnsd
$ service cache-stnsd start 

まだSTNSを触ったことがない方は、以前僕がSTNS as a Service を作っているので、こちらで試してみるのも楽しいかもしれません。

現状、手元の環境でしばらくエージングしてからリリースするつもりなので、今週中くらいにはyum/aptリポジトリに入っていくと思います。

なにか問題などあればissueなど起票頂けると嬉しいです。

あと余談なのですがclient、serverともに対応OSを増やして、debian8/9とCentOS8にも対応しました。

追記

リリースに際し、運用上の懸念などがあればこちらにコメントを下さい!

カテゴリー: tech

Apache KafkaのトピックをTailするIsakaをリリースした

動機

ペパボではKubernetsの環境やプライベートクラウド環境を中心にすべてのログをApache Kafkaを経由し、S3にバックアップしたり、Graylogで取り込んで検索可能にしています。また筆者がそれらの基盤の開発、メンテナンスをやっているのですが、なにか障害があったときにGraylogで検索をかけてそれをダウンロードして、grepしたりawkしたりするのは非常に手間に感じていました。またリアルタイムに tail -f のようなことをやりたくても、なかなかやりづらい状況がありました。

kinessisについては @fujiwara さんが似たようなものかつ、lambdaで検索までできるようなものを実装されているのですが、kafkaはちょっと探しきれなかったので実装しました。

使い方

インストールはhomebrewでインストールできるようにしています。

$ brew install pyama86/homebrew-isaka/isaka

今のところはzookeeperから諸々情報を取得するようにしています。kafka本体がzookeeperをやめると言う方針を出しているので、いずれ移行するかもしれません。zookeeperについてはだいたい同じものを見ると思うので環境変数で指定したほうが便利です。

$ ISAKA_ZOOKEEPERHOST=zookeeper.example.com ISAKA_CLUSTERNAME=example isaka topic-list

上記のようにclusterのtopic-listをシュッと取得することができます。

そいて取得したtopic listの中から、tailしたいtopicを選び、tailするだけです

$ ISAKA_ZOOKEEPERHOST=zookeeper.example.com ISAKA_CLUSTERNAME=example isaka --topic example tail
# ログがずらーーーっとでる

もちろん本家のように -f(follow) も対応しています。ペパボはhashicorpのVaultを利用して、kafkaの通信をTLSクライアント認証しているので、TLS周りも指定できるようにしました。詳細は tail コマンドのヘルプを見てもらえればわかると思います。また、zookeeperにアクセスしたくないケースを想定して、kafka brokerもISAKA_KAFKABROKERS とかで指定可能にしています。

名前の由来

kafkaの由来がフランツ・カフカが好きだったからという理由っぽかったので、最初は海辺のカフカにちなんでharukiとしようと思ったのですが、僕は一番好きなのは金城一紀だけど、最近作品をあまり書かなくなってしまったので、殺し屋シリーズが好きな伊坂幸太郎から取りました。

今後は使ってみて、需要に応じてあれこれ機能追加していきたいと思います。

カテゴリー: tech

ポモドーロテクニック開始とともにSlackのディスプレイネームを変えたい

モチベーション

割と僕は集中が失われているとき、SlackとTwitterをひたすら往復している。そういうときはちょっとやるぞ!!と思ってもなんかSlackで同僚困ってないかな?とかさっきコメントしたIssueにレスポンス来てるかなとか気になってみてしまう。正直僕のレスポンスが多少遅くてもどうでもいいんだけど、性格上なんかすぐ処理しないと気がすまないところもあったりしてついついみてしまう。

そう、Slack便利なんだけど、時に言い訳になりえてしまっていて、それを解決するためにしばらくポモドーロやるかと思い立ったのだけど、一方でポモドーロやるときは邪魔されたくないし、相手にも僕がその間、反応遅いことを伝えたいのだ。なので下記を実装した。

  1. cliでpomodoro.shを実行する
  2. SlackのディスプレイネームをN時N分までポモドーロ中と書き換える
  3. 25分たったらMacのNotificationで通知する

たったこれだけです。ちなみに似たようなことができるSlackアプリはあったりするのですがSlackアプリ正直どこまで信用できるかわからんみたいなところがあって、秘匿情報つまりまくりの会社のSlackに入れるのなかなか厳しいのですよね。なので、SlackAPIトークンさえあれば手元で動かせる仕組みを準備しました。あとメッセージとかちょっとカスタマイズしたいわーみたいなのあると思うのであえてBashで書いた。

在宅でちょい気が散っちゃうわーーーみたいなときにどうだろうか。

カテゴリー: tech

shawkを動かしてみた

このブログは僕が yuuk1/shawk を動かしてみただけのブログで、技術的に得られる情報は少ないです。もしなにかを求められてきた方は、こちらの動画でなにかを得てください。

ということでやっていきましょう。上記のソフトウェアは下記の資料の実装体です。

僕のモチベーションとしてはペパボのサーバ間ネットワークをゼロトラストネットワークを前提として再構築する際に、既存のサーバ間がどのように通信しているのかを洗い出すためであったり、不正な通信の検知をできないかというちょっともとの研究とはそれた使い方かもしれません。

自分で使うとなると、僕の背景からはeBPFを利用するにはカーネルのバージョンアップを途方も無い台数実行必要が生まれるため、そのときはカーネルモジュールでの実装やauditdでの実装をコントリビュートする必要があるだろうなと思ったりもした。

そして、どうせブログ書くからrpm/debパッケージ作ったり、docker-compose.ymlでも書くかと思ってたいのですが、それもすでにあったので動かすだけでした。

まずはバイナリ作っていきましょう。

% make all

ほいで、docker-composeあるんで使っていきます。

% docker-compose up
shawk_db_1 is up-to-date
Starting shawk_agent_1 ... error

ERROR: for shawk_agent_1  Cannot start service agent: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"/go/bin/shawk\": stat /go/bin/shawk: no such file or directory": unknown

ERROR: for agent  Cannot start service agent: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"/go/bin/shawk\": stat /go/bin/shawk: no such file or directory": unknown
ERROR: Encountered errors while bringing up the project.

あー、これdocker-composeは別途buildしろってことか。

% docker-compose build
db uses an image, skipping
Building agent
Step 1/3 : FROM golang:1.14.2
 ---> 2421885b04da
Step 2/3 : ENV PKG github.com/yuuki/shawk
 ---> Using cache
 ---> eff5000775c7
Step 3/3 : WORKDIR /go/src/$PKG
 ---> Using cache
 ---> 141e5f1d2e8f

Successfully built 141e5f1d2e8f
Successfully tagged shawk_agent:latest
% docker-compose up
shawk_db_1 is up-to-date
Starting shawk_agent_1 ... error

ERROR: for shawk_agent_1  Cannot start service agent: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"/go/bin/shawk\": stat /go/bin/shawk: no such file or directory": unknown

ERROR: for agent  Cannot start service agent: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"/go/bin/shawk\": stat /go/bin/shawk: no such file or directory": unknown
ERROR: Encountered errors while bringing up the project.

あかんな。これbuildしたバイナリもっていかないかん。

    volumes:
      - ./shawk:/go/bin/shawk

雑に追加。

% docker-compose up
shawk_db_1 is up-to-date
Recreating shawk_agent_1 ... done
Attaching to shawk_db_1, shawk_agent_1
db_1     | Error: Database is uninitialized and superuser password is not specified.
db_1     |        You must specify POSTGRES_PASSWORD to a non-empty value for the
db_1     |        superuser. For example, "-e POSTGRES_PASSWORD=password" on "docker run".
db_1     |
db_1     |        You may also use "POSTGRES_HOST_AUTH_METHOD=trust" to allow all
db_1     |        connections without a password. This is *not* recommended.
db_1     |
db_1     |        See PostgreSQL documentation about "trust":
db_1     |        https://www.postgresql.org/docs/current/auth-trust.html
d

fmfm.ということで、dbのパスワード追加したり、環境変数入れたり。

services:
  agent:
    build: .
    depends_on:
      - db
    entrypoint: /go/bin/shawk probe
    command: ["--dbhost", "db"]
    volumes:
      - ./shawk:/go/bin/shawk
    environment:
      - SHAWK_CMDB_URL=postgres://shawk:shawk@db:5432/shawk?sslmode=disable
  db:
    image: postgres:11.7
    ports:
      - 5432
    environment:
      - POSTGRES_USER=shawk
      - POSTGRES_DB=shawk
      - POSTGRES_PASSWORD=shawk
    restart: always
    user: postgres
    volumes:
      - ./postgres/init:/docker-entrypoint-initdb.d
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ttracer"]
      interval: 3s
      timeout: 3s
      retries: 5
      start_period: 3s

そうすると、

agent_1  | flag provided but not defined: -dbhost
agent_1  |
agent_1  | Usage: shawk probe [options]
agent_1  |
agent_1  | start agent for collecting flows and processes.
agent_1  |
agent_1  | Options:
agent_1  |   --env
agent_1  |   --once                    run once only if --mode='polling'
agent_1  | flag provided but not defined: -dbhost
shawk_agent_1 exited with code 11

引き数変わったっぽいのでdbhostを削ってと。。

(⎈ |nks-production:drone)[13:52:25] ~/src/github.com/yuuki/shawk (not-exist-task) <U>
% docker-compose up
Starting shawk_db_1 ... done
Starting shawk_agent_1 ... done
Attaching to shawk_db_1, shawk_agent_1
db_1     | 
db_1     | PostgreSQL Database directory appears to contain a database; Skipping initialization
db_1     | 
db_1     | 2020-05-09 04:52:37.680 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
db_1     | 2020-05-09 04:52:37.680 UTC [1] LOG:  listening on IPv6 address "::", port 5432
db_1     | 2020-05-09 04:52:37.689 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
db_1     | 2020-05-09 04:52:37.726 UTC [12] LOG:  database system was interrupted; last known up at 2020-05-09 04:50:50 UTC
db_1     | 2020-05-09 04:52:38.001 UTC [12] LOG:  database system was not properly shut down; automatic recovery in progress
db_1     | 2020-05-09 04:52:38.005 UTC [12] LOG:  redo starts at 0/16517C0
db_1     | 2020-05-09 04:52:38.005 UTC [12] LOG:  invalid record length at 0/16517F8: wanted 24, got 0
db_1     | 2020-05-09 04:52:38.005 UTC [12] LOG:  redo done at 0/16517C0
db_1     | 2020-05-09 04:52:38.033 UTC [1] LOG:  database system is ready to accept connections
agent_1  | 2020/05/09 04:52:38 INFO <command> --> Connecting postgres ...
agent_1  | 2020/05/09 04:52:38 INFO <command> Connected postgres
db_1     | 2020-05-09 04:52:40.725 UTC [26] FATAL:  role "ttracer" does not exist
db_1     | 2020-05-09 04:52:43.874 UTC [33] FATAL:  role "ttracer" does not exist

[kkkdb_1     | 2020-05-09 04:52:47.025 UTC [41] FATAL:  role "ttracer" does not exist
db_1     | 2020-05-09 04:52:50.187 UTC [48] FATAL:  role "ttracer" does not exist
db_1     | 2020-05-09 04:52:53.331 UTC [55] FATAL:  role "ttracer" does not exist

ああ、上がったけどdbのヘルスチェックが前の名前のままだったから、shawkに変える。

ここまでで1PRなげた。

db_1     |
db_1     | PostgreSQL Database directory appears to contain a database; Skipping initialization
db_1     |
db_1     | 2020-05-09 04:53:37.694 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
db_1     | 2020-05-09 04:53:37.695 UTC [1] LOG:  listening on IPv6 address "::", port 5432
db_1     | 2020-05-09 04:53:37.699 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
db_1     | 2020-05-09 04:53:37.746 UTC [12] LOG:  database system was shut down at 2020-05-09 04:53:32 UTC
db_1     | 2020-05-09 04:53:37.760 UTC [1] LOG:  database system is ready to accept connections
agent_1  | 2020/05/09 04:53:38 INFO <command> --> Connecting postgres ...
agent_1  | 2020/05/09 04:53:38 INFO <command> Connected postgres

db_1     | 2020-05-09 04:54:08.426 UTC [19] ERROR:  relation "processes" does not exist at character 16
db_1     | 2020-05-09 04:54:08.426 UTC [19] STATEMENT:
db_1     |                      INSERT INTO processes (ipv4, pgid, pname, updated)
db_1     |                      VALUES ($1, $2, $3, CURRENT_TIMESTAMP)
db_1     |                      ON CONFLICT (ipv4, pgid, pname)
db_1     |                      DO UPDATE SET updated=CURRENT_TIMESTAMP
db_1     |                      RETURNING process_id
db_1     |
agent_1  | 2020/05/09 04:54:08 ERROR <agent/polling> query error:
agent_1  |     github.com/yuuki/shawk/db.(*DB).InsertOrUpdateHostFlows
agent_1  |         /go/src/github.com/yuuki/shawk/db/db.go:189

なるほどなぁ、スキーマが初期化されてなさそう。

多分このあたりを実行しないといけない。

    command: bash -c "/go/bin/shawk create-scheme && /go/bin/shawk probe"

ソフトウェアの性質上、docker-composeはシュッと試したいときに使うものだから雑にbashでやっつけた。

ホイで起動したから雑に試すためにdocker exec bin/bashしてnetcatいれちゃうぞ〜。

$ apt install netcat
$ nc -l -p 11111     # listenしておいて
$ nc localhost 11111 # 接続する
$ /go/bin/shawk look

ここで、接続情報出るんかなぁと思ってたけどそんなことなかった。

なんでやねーーんと思ってたら、

agent_1  | 2020/05/09 05:14:57 ERROR <agent/polling> readlink /proc/58/fd/0:
agent_1  |     github.com/yuuki/shawk/probe/netlink/netutil.BuildUserEntries
agent_1  |         /go/src/github.com/yuuki/shawk/probe/netlink/netutil/netutil_linux.go:349
agent_1  |   - readlink /proc/58/fd/0: permission denied

なるほどね。でもこれコンテナのprocだから特権いらなさそうだけどな。fd0だから標準入力見ようとしてんのかな。

あらためて、lookをip指定で見たらみれた。

root@615b76a8b754:/go/src/github.com/yuuki/shawk# /go/bin/shawk look  --ipv4 172.19.0.3
172.19.0.3:55562 ('', pgid=0)
└<-- 172.19.0.3:many ('nc', pgid=505)
172.19.0.3 ('', pgid=0)
└--> 172.19.0.2:5432 ('', pgid=0)
172.19.0.3 ('nc', pgid=505)
└--> 172.19.0.3:55562 ('', pgid=0)
root@615b76a8b754:/go/src/github.com/yuuki/shawk# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 05:12 ?        00:00:05 /go/bin/shawk probe
root        22     0  0 05:14 pts/0    00:00:00 /bin/bash
root        47     0  0 05:14 pts/1    00:00:00 /bin/bash
root       381     0  0 05:16 pts/2    00:00:00 /bin/bash
root       505    47  0 05:24 pts/1    00:00:00 nc -l -p 11111
root       507   381  0 05:24 pts/2    00:00:00 nc 172.19.0.3 11111
root       560    22  0 05:26 pts/0    00:00:00 ps -ef
root@615b76a8b754:/go/src/github.com/yuuki/shawk#

これ11111ポートの情報はでないんかな。情報の出し方的には 172.19.0.3:many の場所が 172.19.0.3:11111 で良さそうだけどなんでやろか。

root@615b76a8b754:/go/src/github.com/yuuki/shawk# /go/bin/shawk look  --ipv4 172.19.0.3
172.19.0.3:55562 ('', pgid=0)
└<-- 172.19.0.3:many ('nc', pgid=505)
172.19.0.3 ('', pgid=0)
└--> 172.19.0.2:5432 ('', pgid=0)
172.19.0.3 ('nc', pgid=507)
└--> 172.19.0.3:11111 ('', pgid=0)
root@615b76a8b754:/go/src/github.com/yuuki/shawk# /go/bin/shawk look  --ipv4 172.19.0.3
172.19.0.3:55562 ('', pgid=0)
└<-- 172.19.0.3:many ('nc', pgid=505)
172.19.0.3 ('', pgid=0)
└--> 172.19.0.2:5432 ('', pgid=0)
172.19.0.3 ('nc', pgid=505)
└--> 172.19.0.3:55562 ('', pgid=0)

コネクション自体は繋ぎっぱなしで、何回か実行してると結果が変わるっぽい。これはなんかポーリング間隔によって得られる情報が違うからなんだろう。openしか取ってないとするとあり得るから、ストリーミングモードでやると多分ちゃんと取れそう。

最後に

一通り動かしてみて、PRもさっきマージされてました。yuukiくんも書いてるようにパフォーマンスとのトレードオフになる部分はあれど、僕が思ってる用途や他の用途にも使えそうだった。それこそグラフ図にするとホスト間の依存を図示することもできるだろうし、CMDBがあるから基本ソフトウェアとして使い手のアイディア次第で可能性のあるソフトウェアだと思った。

カテゴリー: tech