ブログを移転します
特に深い事情はないんですが、ブログを移転することにしました。
新しいブログはこちら。
https://blog.s-tajima.work/
過去の記事は今でも有用そうな情報のものは折をみて新しいブログに移していく予定です。
GitHubのPull Requestへのレビュアーアサインを自動化するツール 「github-dice」の紹介
こんにちは、今回はGitHubのPull Requestへのアサインを自動化するツール
「github-dice」を作成したお話です。
背景
GitHubのPull Requestベースの開発をする際、コードレビューはとても大事な工程の一つです。
よくあるコードレビューは、 技術力の高い/システムの理解度が深い/ベテラン のメンバーが、
技術力の低い/システムの理解度の浅い/若手 のメンバーが書いたコードをレビューするようなやり方かなと思います。
しかしコードレビューは、レビューを受ける側(レビュイー)にとってとても良い成長の機会ですが、
同時にレビューをする側(レビュアー)にとっても良い成長の機会となります。
自分より実力の高い人の書いたコードをレビューすることで、
新しい知見を得て、技術力の向上に繋げることができます。
システムの理解の深い人間が増えることは、システムの属人性を減らすことにも繋がるでしょう。
そこで、僕の所属しているチームでは、技術力/システムの理解度/在籍期間等に関係なく、
チームのメンバーが均等にレビュアーの機会を経験できるような運用を行っています。
github-dice
github-diceは、GitHubのPull Requestへのレビュアーのアサインを自動化するためのツールです。
あらかじめ指定したTeamのメンバーから、サイコロを振るようにランダムにレビュアーを選出しアサインします。
導入方法、使い方はREADMEに記載の通りです。
Golang製のツールなのでバイナリを配置するだけで使用できます。
github-diceを使用すると、以下のようにコメントと共にレビュアーがアサインされます。
(オプションで起票者を同時にアサインすることもできます。)
尚、-q, --query
オプションで検索クエリを書き換えられるので、
Pull Requestだけでなく、Issueを自動アサインの対象にすることも可能です。
終わりに
技術的知見があまりなく、業務用件の理解も浅いコードをいきなりレビューするのはそれなりに負担が高いです。
「チームのメンバーが均等にレビュアーの機会を経験できるように」という方針だけ立てて
あとは各メンバーの自主性に任せるというやり方では結局うまく回らなそうだと考え、
ランダムにレビュアーを選出し機械的にアサインをするというアプローチをとりました。
自信のないときにはしっかり他のメンバーのサポートも受けられる体制も合わせた運用をすることで、
過度に心理的, 技術的な負荷のかかるレビューをしなければいけない状況を避けられるようにしています。
(僕も大いに助けられています。)
実際に半年ほど運用を続けていますが、 自分が直接は触らないようなコードの理解も深まり、
最初は大きかった負担もだんだん小さくなっていくのを感じています。
以上、簡単ですがgithub-diceの紹介でした。
ぜひ使ってみてください。Pull Requestもお待ちしております。
余談
先日、GitHubはPull Requestに対してAssigneesとは別にReviewersを割り当てられるようになりました。
https://github.com/blog/2291-introducing-review-requests
ちょうどこの記事を書いているタイミングで、このReviewersに対するAPIもEarly Accessが開始されました。
https://developer.github.com/changes/2016-12-16-review-requests-api/
github-diceも近いうちにこの機能に対応しようと考えています。
AWS Reserved Instancesによる減額効果を計算するツールを作った
概要
AWSの料金を節約する手段の1つに Reserved Instances という仕組みがあります。
これは、予め将来的に利用するであろうインスタンスを予約し、一定額の前払金を支払う(支払わないケースも有る)ことで、
通常の利用(Ondemand Price)に比べて一定の割引を受けた料金(Blended Rate)でインスタンスを利用できるようにするというものです。
今回は、そのReserved Instanceによって、
インスタンスの利用料金がどれだけ減額されたのかを確認するツールを作成したので紹介します。
aws-ri-discount-calculator
GitHub - s-tajima/aws-ri-discount-calculator: Reserved Instance discount calcurator
使い方はREADMEを参照。
- IAMアカウントの権限は必要ありません。
- AWSの提供するMonthly Reportからインスタンスの利用時間, Blended Rateを取得。
- AWS Price List APIからOndemand Priceを取得。
- Ondemand PriceとBlended Rateの差額にインスタンスの利用時間を掛けて、
どれくらいの減額を受けているのかを計算します。- Reserved Instanceの購入費用(前払金)は計算に含まれません。
- Reserved Instanceの購入によるコスト削減効果を計算する場合にはそれを含めて考慮する必要があります。
- そこまで含めた数字を出せるようになるとよさそうですが、うまいやり方を検討中です。
- Consolidated Billingを組んでいる場合は、子AWSアカウント毎に集計します。
- Monthly ReportのCSVの詳細な仕様が公開されていないため、現時点では動作確認できているデータが限定的で、
場合によっては間違った数値を出してしまったり、動作しない可能性があります。- Reserved InstanceのオファリングタイプはEC2のAll Upfront, RDSのHeavy Utilizationのみテスト済み。
- 実は AWS Cost and Usage Reports で近しい情報は閲覧できるのですが、
以下のような不都合があったので自作することにしました。- EC2以外の情報は確認できない。
- Consolidated Billing環境で複数のAWSアカウントでReserved Instanceを購入している場合に、
その効果を合算した数値を見ることができない。
以上、aws-ri-discount-calculatorの紹介でした。 PRもお待ちしております。
resticによるカジュアルなバックアップ環境の構築
前置き
僕がインフラを管理している環境では、その規模の大小は様々ですが14個のJenkinsが稼働しています。
それぞれのJenkinsでは、thinBackupによるバックアップが動いているので、障害時にも復旧ができる...はずでした。
現状の設定を確認してみると、
- thinBackupによるバックアップが正しく動いている: 4環境
- thinBackupは動いているが、正しく設定されていない(保持世代数が設定されていない等): 2環境
- thinBackupの設定がされていない: 8環境
という状態でした。
運用の整理されていない状態で利用するチームが増えたため、
適切に設定の自動化がされておらず、手作業によるバックアップの設定をするのが常態化してしまっていたのが原因です。
このような状態は、全く認識してなかったわけではなく、 いわゆる "Elephant in the room." となっていたのですが、
今回はこの問題に着手したお話です。
適切な手段の検討
Jenkinsの環境構築自体(RPMのインストール, データディレクトリの作成等)は、Puppetによるプロビジョニングができるようになっています。
バックアップの自動設定を実現する上でまず考えられるのは、
このPuppetによるプロビジョニングに プラグインのインストール
プラグインの設定
を含めてしまうという方法です。
しかし、以下のような観点からこの方法をとるのはやめました。
- 現在稼働しているJenkinsのバージョンが多岐に渡る
- 全環境で(それは現時点だけでなく新しいバージョンのJenkinsが導入された時にも)問題なく動くthinBackupのバージョン/設定を選定するのはなかなか大変そうです。
- JenkinsのバージョンごとにthinBackupの設定を作るのもよいですが、それはそれで管理が大変なのであまり好ましくありません。
- 実はthinBackupだけではバックアップとして不完全である。
ということで、なにかよさそうな別の方法を検討することにしたのですが、一旦やりたいことを整理してみました。
- Jenkinsにおいてデータ破損等のトラブルが発生した時、その設定/プラグイン/ビルドの履歴等が復元できる状態にしておく必要がある。
- バックアップデータは、Jenkins稼働ホストに置いておくだけではなく、ある程度信用のできる外部ストレージに転送する必要がある。
- それなりにデータサイズの大きいJenkinsにも対応できるように、毎回フルバックアップするのではなく、差分バックアップができるとよい。
- Jenkinsの設定には機微な情報が含まれるケースもあるので、適切な方法で暗号化できる手段があるとよい。
- 復元のタイミングは任意である必要はなく、「前日のXX時」程度のレベルでよい。
- いわゆるバックアップの一貫性もそこまで強く求められるものではない。
- バックアップの自動設定をしたいので、Puppetによるプロビジョニングがしやすい構成になるとよい。
イメージとしてはあるバイナリ1つと設定ファイル1つ程度で、
#バックアップ $JENKINS_HOMEをtarで固めて -> (必要であれば)適切な方法で圧縮/暗号化して -> S3に転送 #リストア S3からファイルを取得 -> (必要であれば)圧縮/暗号化を解除 -> $JENKINS_HOMEに展開
くらいのことができると良さそうだなという印象です。
resticによるバックアップ
前述したような要件を持つだけのプログラムであれば、自作しても大した手間ではないのですが、
おそらく世の中には同じような要件はいくらでもあるだろうということで探してみたところこんなものがありました。
https://github.com/restic/restic
あたりが選定の要因です。
使い方のイメージはドキュメントを読んでもらうと良いと思います。
https://restic.readthedocs.org/en/v0.1.0-doc/Manual/#buildinginstalling-restic
restic init, restic backupを実行するための、簡単なシェルスクリプトを作成し、
定期的に実行することで、Jenkinsのカジュアルなバックアップを実現しました。
mod_extract_forwarded, mod_rpafの脆弱性らしきものを見つけた話
はじめに
本記事で言及しているmod_rpafというモジュールは、forkによる派生などによって幾つかの実装があるようです。
きちんと区別できるように、今回は以下の実装をそれぞれ分けて表記することにします。
(ここに挙げた以外にもいくつかの実装をみつけることはできました。)
GitHub - gnif/mod_rpaf: reverse proxy add forward module for Apache => mod_rpaf(gnif版)
GitHub - ttkzw/mod_rpaf-0.6 => mod_rpaf(ttkzw版)
※2016/02/19追記
@ttkzwさんご本人から直接連絡をいただきました。
修正していただけたようです。
ご対応ありがとうございます!
Bugfix: wrong remote_addr is set. · ttkzw/mod_rpaf-0.6@345365c · GitHub@s_tajima このブログ記事に気がついて放置していたmod_rpafを修正しました。ありがとうございました。 https://t.co/NTKxxNskw2
— Takashi Takizawa (@ttkzw) 2016, 2月 19
概要
mod_extract_forwarded, mod_rpaf(ttkzw版)を導入している環境において、
悪意のあるリクエストによってリモートホスト(接続元のホスト)の 先頭に 任意の文字列が追加 されてしまうという不具合の話です。
尚、httpd2.4に組み込まれている、mod_remoteipについては影響を受けないことを確認しました。
※ mod_extract_forwarded, mod_rpafについては mod_remoteipのリモートアドレスの扱いに関する不具合の修正について - s_tajima:TechBlog で解説しているので割愛します。
脆弱性の解説
mod_extract_forwarded, mod_rpaf(ttkzw版)を導入していて、通常のリクエスト処理フローにX-Forwarded-Forヘッダが使われるような環境を想定します。
わかりやすくするために、今回は以下のようなケースとします。
Client(192.0.2.1) -- global network --> ReverseProxy(10.0.0.1) -- local network --> WebServer(10.1.0.1) ※Webサーバーには信頼するProxyとして10.0.0.0/8を指定
通常のリクエスト
WebServerには以下のようなX-Forwarded-Forヘッダが届きます。
X-Forwarded-For: 192.0.2.1, 10.0.0.1
その結果、リモートホストには以下の文字列が記録されます。
192.0.2.1
脆弱性をついた攻撃のリクエスト
このような環境にたいして、Clientから、"(ダブルクォート)
を含む X-Forwarded-Forヘッダを送信します。
curlコマンドで表すと以下のようなリクエストになります。
curl -H 'X-Forwarded-For: SOMEEVILSTRINGS"' http://example.com
するとWebServerには、以下のようなX-Forwarded-Forヘッダが届きます。
X-Forwarded-For: SOMEEVILSTRINGS", 192.0.2.1, 10.0.0.1
すると不本意なことに、リモートホストとして以下の文字列が記録されます。
SOMEEVILSTRINGS", 192.0.2.1
考えられそうな影響
アクセス制限の回避
正規表現を使ってIPアドレスベースによるアクセス制限をかけているが、
その正規表現が適切でない場合にその制限を回避される懸念があります。
※PHPでの例です。 if (preg_match('/^10\./', $_SERVER['REMOTE_ADDR'])){ # ローカルネットワークからのみリクエストを受け付ける想定の処理。 }
このケースでは SOMEEVILSTRINGS
を 10.1.1.1
等とすることで制限を回避できてしまいます。
XSS, SQLインジェクション等
リモートホストに入ってくる文字列がIPアドレス以外になることを想定せず、適切なエスケープをしていないと、
XSSやSQLインジェクションを実行される可能性があります。
なぜこのようなことが起こるのか
mod_extract_forwarded, mod_rpaf(ttkzw版)では、X-Forwarded-Forヘッダのパースにapacheのコアに含まれるap_get_token関数を使用しています。
ap_get_token関数は、カンマ区切り等で複数の値を取りうるヘッダの値を分割文字列毎に分割するための関数ですが、
ヘッダ文字列内に "(ダブルクォート)
が含まれるとそこから文字列の終端までを1つの塊として処理してしまいます。
更に、ap_get_tokeによって返された値を、IPアドレスとして正しい文字列であるかを判定せずに、
そのままリモートホストとして扱ってしまうために、今回のようなことが起こります。
参考:
mod_rpaf(ttkzw版)におけるX-Forwarded-Forヘッダの分割処理実装
httpdのap_get_token関数におけるヘッダ文字列の処理の実装
対応
mod_rpaf(gnif版)の 2014/03/29 のこのコミット Refactor IP subnet comparison using apr funcs · gnif/mod_rpaf@8419240 · GitHub 以降であれば、
X-Forwarded-Forヘッダのパースが自前の実装になっていて、
この問題は発生しないようなので、そちらに乗り換えるというのは1つの手段です。
また、rpaf_looks_like_ip関数によってIPアドレスとして正しいフォーマットであるかのチェックもしてくれています。
https://github.com/gnif/mod_rpaf/blob/stable/mod_rpaf.c#L293-L301
mod_extract_forwarded, mod_rpaf(ttkzw版)は共に長い間メンテナンスがされていないようなので、
何らかの理由で乗り換えが難しいケースでは、適切なパッチを自前であてる必要がありそうです。
以上、mod_extract_forwarded, mod_rpafの脆弱性らしきものを見つけた話でした。
AWS Certificate Managerの検証
本日、AWSがSSL/TLS証明書のマネージドサービスをリリースしたので早速使ってみました。
New – AWS Certificate Manager – Deploy SSL/TLS-Based Apps on AWS | AWS Official Blog
リリース情報、ドキュメントより
ACM takes care of the complexity surrounding the provisioning, deployment, and renewal of digital certificates
証明書のプロビジョニング, デプロイ, 更新を管理してくれるとのこと。
Certificates provided by ACM are verified by Amazon’s certificate authority (CA)
AmazonのCAによってベリファイされた証明書を発行できるとのこと。
SSL/TLS certificates provisioned through AWS Certificate Manager are free.
証明書の発行/管理自体に費用はかからないそうです。
You can use AWS Certificate Manager certificates only with Elastic Load Balancing and Amazon CloudFront.
ELBもしくはCloudFrontでしか使えないとのこと。証明書の秘密鍵をダウンロードすることはできないようですね。
発行してみた
- 詳細な手順はドキュメントに書いてあるので割愛。
- ドメイン認証の方法は現状メールのみ。(Whoisの管理者アドレスに認証用メールが送られてくる)
- 発行した証明書の主な仕様
- Signature Algorithmはsha256WithRSAEncryption
- 秘密鍵の鍵長は2048bit
- FQDNを複数指定した場合はSubject Alternative Name (SANs)に含まれる
- 実際に発行した証明書はこちら。
Certificate: Data: Version: 3 (0x2) Serial Number: 0a:33:56:0e:38:84:ab:1d:23:bb:7f:fa:62:e9:25:99 Signature Algorithm: sha256WithRSAEncryption Issuer: C=US, O=Amazon, OU=Server CA 1B, CN=Amazon Validity Not Before: Jan 22 00:00:00 2016 GMT Not After : Feb 22 12:00:00 2017 GMT Subject: CN=XXXXX.me Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (2048 bit) Modulus (2048 bit): 00:86:6a:b3:f3:ce:38:6d:26:31:d1:94:cc:c3:62: ..snip.. d4:e6:00:4f:28:43:1c:5a:4a:40:80:6a:3f:bd:14: 38:95 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:59:A4:66:06:52:A0:7B:95:92:3C:A3:94:07:27:96:74:5B:F9:3D:D0 X509v3 Subject Key Identifier: 6C:00:94:64:9B:FD:6A:06:1D:B4:55:FC:BD:4A:19:08:D4:C6:38:39 X509v3 Subject Alternative Name: DNS:XXXXX.me, DNS:*.XXXXX.me X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication X509v3 CRL Distribution Points: URI:http://crl.sca1b.amazontrust.com/sca1b.crl X509v3 Certificate Policies: Policy: 2.23.140.1.2.1 Authority Information Access: OCSP - URI:http://ocsp.sca1b.amazontrust.com CA Issuers - URI:http://crt.sca1b.amazontrust.com/sca1b.crt X509v3 Basic Constraints: critical CA:FALSE Signature Algorithm: sha256WithRSAEncryption 99:ec:47:81:fc:61:55:aa:c7:91:aa:d2:d5:2d:29:68:cf:1c: ..snip.. 43:6d:d2:6f -----BEGIN CERTIFICATE----- MIIEWTCCA0GgAwIBAgIQCjNWDjiEqx0ju3/6YuklmTANBgkqhkiG9w0BAQsFADBG ..snip.. QifBNlq9ofPYQ23Sbw== -----END CERTIFICATE-----
有効性の確認
証明書のチェーンは以下のとおり
Certificate chain 0 s:/CN=XXXXX.me i:/C=US/O=Amazon/OU=Server CA 1B/CN=Amazon 1 s:/C=US/O=Amazon/OU=Server CA 1B/CN=Amazon i:/C=US/O=Amazon/CN=Amazon Root CA 1 2 s:/C=US/O=Amazon/CN=Amazon Root CA 1 i:/C=US/ST=Arizona/L=Scottsdale/O=Starfield Technologies, Inc./CN=Starfield Services Root Certificate Authority - G2 3 s:/C=US/ST=Arizona/L=Scottsdale/O=Starfield Technologies, Inc./CN=Starfield Services Root Certificate Authority - G2 i:/C=US/O=Starfield Technologies, Inc./OU=Starfield Class 2 Certification Authority
手元の環境で確認すると、Firefox, OSXのキーチェーン共に有効なCA認証局からのチェーンとして扱われていた。
リリースの記事にもある通り、SSL/TLS証明書の管理はたしかに面倒ですよね。
最近ではLet's Encrypt(https://letsencrypt.org/)等も話題になっていますが、
これらをうまく使って余計な手間をなくしていきたいものです。
※2016/1/22 17:12 追記
Amazonみたいな1企業が(中間)CA認証局を持てるってすごいなとおもってたら2015年6月にこんな記事が出てた。
Amazon wants to be your SSL certificate provider, applies to be a root Certificate Authority - GeekWire
mod_remoteipのリモートアドレスの扱いに関する不具合の修正について
httpd2.4では、X-Forwarded-Forを取り扱うためにmod_remoteipというモジュールがあります。
httpd2.2系でいうところのmod_rpaf, mod_extract_forwardedの代わりとなるモジュールです。
今回は、このmod_remoteipの不具合が修正された話です。
前置き
(mod_remoteip, X-Forwarded-Forについての説明です。知ってる方は読み飛ばしてください。)
これらのモジュールは、主にロードバランサ, リバースプロキシ配下のWebサーバで使われ、
リクエストのリモートアドレスを、X-Forwarded-Forというヘッダの値を元に書き換えてくれます。
(これをしないとWebサーバに残るリモートアドレスは、ロードバランサやリバースプロキシのものになってしまう。)
複数のロードバランサやリバースプロキシを経由してくることを考慮して、
X-Forwarded-Forヘッダには ,(カンマ)区切りで複数のIPアドレスを記述できるようになっています。
X-Forwarded-Forヘッダに複数の値が入っている場合に、
Webサーバでリモートアドレスとして使われるのは基本的には一番右側の(最後に付与された)IPアドレスになります。
ただし、それだけでは多くのケースでロードバランサやリバースプロキシのIPアドレスが記録されてしまいます。
そのため、これらのモジュールの多くは、
リモートアドレスとして採用しないIPアドレス(のレンジ)を指定することができるようになっています。
例えば、mod_remoteipの設定として、以下の記述がされているケースで、
RemoteIPInternalProxy 192.0.2.0/24
以下のようなリクエストを受信したケースでは、
X-Forwarded-For: 198.51.100.1, 198.51.100.2, 192.0.2.1
198.51.100.2 がリモートアドレスとして記録されます。
この方法でリモートアドレスを取得すれば、
最後に経由したRemoteIPInternalProxyに記述されていないアドレス(≒ 自分たちの管理化にないアドレス)
をリモートアドレスとして記録することができるようになります。
不具合の内容
前置きが長くなりましたが、今回修正されたのはこのリモートアドレスの算出方法の不具合です。
この不具合が修正されるまで、mod_remoteipではRemoteIPInternalProxy, RemoteIPTrustedProxyの設定にかかわらず
一番 左側 のIPアドレスがリモートアドレスとして記録される状態になっていました。
不具合による影響
X-Forwarded-Forは単なるHTTPのヘッダなので、悪意のあるクライアントは簡単に偽装して付与することができます。
以下のようにX-Forwarded-Forを付与した状態でリクエストをかけます。
X-Forwarded-For: 192.0.2.2
すると、Webサーバには以下のようなX-Forwarded-Forヘッダが届きます。
X-Forwarded-For: 192.0.2.2, 198.51.100.1, 198.51.100.2, 192.0.2.1
今回の不具合では、一番 左側 のIPアドレスがリモートアドレスとなるので、
192.0.2.2
がリモートアドレスとなります。
結果として、ログに残るリモートアドレスを偽装したり、
リモートアドレスを元にアクセス制限をかけている環境等でアクセスの制限を回避したりすることが可能になります。
不具合の修正
この不具合、Red Hat Bugzillaに 2015/01/06 に報告されたあとしばらく修正されずにいたのですが、
2015/11/18 にようやくRedHatで修正がリリースされました。
https://bugzilla.redhat.com/show_bug.cgi?id=1179306
RedHatのAdvisoryはこちら。
https://rhn.redhat.com/errata/RHBA-2015-2194.html
そして、この修正がCentOSにも降りてくるのを待っていたのですが、
先日リリースされた CentOS Linux 7 (1511) でようやくこの修正が取り込まれました。
該当する環境をお使いの方は早いうちにアップデートをすることをおすすめします。
以上、今回はmod_remoteipの不具合についてでした。