cache-stnsdでオリジンサーバがダウンしてるときはキャシュで応答できるようにした

先日リリースしたcache-stnsdを利用していれば、オリジンサーバがダウンしている場合はキャッシュから応答するようにしました。

課題感としては僕が所属しているペパボにおいて、クライアントサーバのNW系のトラブルなどでSTNSサーバに接続できない場合に、調査しようとしてSSHログインしようとするのだが、そもそもSTNSに接続できないので名前解決ができずにSSHログイン出来ないという問題がありました。それらを解決するためにSTNSサーバに接続できない場合は、キャッシュから応答出来るようなパッチを入れたので、ぜひcache-stnsdをご活用ください。cache-stnsdの利用はcache-stnsdをインストールして、 /etc/stns/client/stns.confuse_cached = true と記述すれば有効になります。

お盆休み明けに是非お試し下さい!!1

カテゴリー: tech

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