Shogo's Blog

Feb 12, 2019 - 2 minute read - go golang aws lambda

外部サービスでもIAM Roleで認証がしたい!

背景 外部サービスのAPIやWebHookを叩くときには、多くの場合 API トークンが必要になります。 もちろん API トークン無しでも叩けるサービスはありますが、GitHub APIのようにトークン無しではリクエスト数が大きく制限されたり、 一部機能が制限されてしまう場合があります。 外部連携サービスが増えてくると、このAPIトークンをどうやって管理するかが問題になってきます。 プロダクションに投入されているサービスは普通複数のサーバーから構成されており、各サーバーにAPIトークンを配布するのはちょっと面倒です。 この問題に対して、以下のようなことが行われて来ました。 プライベートネットワークからのアクセスに限定した Proxy を立てる APIトークンの管理は Proxy に任せる DevOpsが注目され、Slackの利用が広まったころに、このような目的で書かれたProxyサーバーがよく登場しました。 社内IRCをSlackに移行した時にやったこと この記事で紹介されている kayac/nopaste Slackboard〜Slackプロキシサーバ in Go〜 Slackプロキシサーバ〜slackboard〜を利用したメルカリのSlack活用法 App::Ikachan - 様々なサーバのバッチ処理の結果等を IRC のチャンネルに通知するサーバ (IRCはHTTPで動いているわけではないし、大本の目的もコネクション維持だけど、認証も代理でやってくれる) しかし、これらのサーバーはSlack専用だったりIRC専用だったりします。 Slackだけじゃなくって、GitHubにコメント登録したり、Mackerelのグラフアノテーションを投稿したり、 他のサービスとも連携したい! 最近はどんなAPIもHTTPで提供されるようになったので(IRCは・・・ウッ・・・そんなのなかった)、もっと汎用的に書けるのではとやってみました。 実装 APIトークンの保管場所として AWS Systems Manager Parameter Store を採用しました。 Parameter Store からAPIトークンを取り出す部分と、実際にAPIを叩く部分は AWS Lambda を使用します。 各サーバーに Forward Proxy デーモンを立てておき、APIを使いたいアプリケーションはこのProxyを経由するようにします。 この図ではEC2インスタンスを例にしていますが、IAM Roleを付与できるAWSのサービスであれば何でも (ECS, Lambda, CodeBuild, etc.) APIにアクセスすることができます。 外部サービスのAPIを叩くのが Lambda 関数というのもポイントです。 APIトークンをヘッダーに設定するのか、URLの一部に含めるのか、クエリストリングに含めるのか・・・といった設定方法はサービスによってまちまちです。 Lambda 関数がこの辺の設定を肩代わりしてくれるので、APIトークンの扱いを気にする必要はありません。 また、API利用時にうっかりAPIトークンを漏らしてしまう心配もなくなります。 APIトークンの管理をしたいんじゃない!!ただ、APIを叩きたいだけなんだ!!!! という思いから、Proxy デーモンはシークレットに関しては何も関与しません。

Feb 7, 2019 - 3 minute read - acme python aws lambda

Let's Encrypt の証明書取得を AWS Lambda でやってみた

背景 ここ数年で暗号化されていないHTTPは減り、常時TLSが当たり前になってきました。 公開用のページはもちろん、開発段階のページでもTLSは必須です。 普段はAWS上で開発をしているので、AWS Certificate Managerを利用することが多いのですが、 ちょっとしたお遊びにELBやCloudFormationを使うのはオーバーキルです。 そこで EC2 にもインストールできて、無料で使える Let’s Encrypt を使って証明書を発行することを考えました。 Let’s Encrypt で発行できる証明書は期間が90日と短く、60日ごとの自動更新が推奨されています。 証明局とAPIとAPIクライアントの実装例は提供するから、あとの自動化部分は自前で頑張ってねという感じなので、自動化部分を頑張らないといけません。 今回は実行環境として AWS Labda、ACME(Automatic Certificate Management Environment)クライアントとして certbot、 認証方法に dns-01、認証に必要なDNSレコードの書き換えに AWS Route 53 を使用する、という構成にしました。 ソースコードをGitHubに挙げたのと、前回と同様に AWS Serverless Application Repository へ上げたので、ぜひご利用ください。 shogo82148/acme-cert-updater shogo82148/acme-cert-updater on AWS Serverless Application Repository 関連手法 Amazon Linux 2 に certbot をインストールして使う Amazon Linux 2 のドキュメントに TLS 対応のウェブサーバーを立てる例が載っています。 Let’s Encrypt で証明書を取る方法も紹介されているので、まずはこれを利用することを考えました。 付録: Amazon Linux 2 での Let’s Encrypt と Certbot の使用 - チュートリアル: Amazon Linux 2 で SSL/TLS を使用できるように Apache ウェブサーバーを設定する この方法は以下の理由から見送りました。

Jan 31, 2019 - 3 minute read - go golang aws mackerel

サーバーレスでCloudWatchメトリクスをMackerelに転送する

背景 サーバーの監視にMackerelを使っているのですが、 用意されているメトリクスでは足りずカスタムメトリクスを追加することが多々あります。 Mackerel Agent Pluginsを利用すればメトリクスを増やすこと自体は簡単なのですが、 Agentを設置するインスタンスが増えるので、サーバー保守の手間が増えてしまいます。 僕のユースケースでは監視対象はたいていAWSのマネージド・サービスなので、 AWS CloudWatch に投稿されたメトリクスが Mackerel で見れれば十分なことが多いです。 そこで、以下の記事を参考に AWS Lambda と CloudWatch Events を組み合わせて、Mackerelへメトリクスを転送するスクリプトを書いてみました。 Amazon LambdaでCloudWatchのメトリクスをMackerelに監視させる デプロイしてみる 今回はなんと!皆さんの AWSマネジメントコンソールから、クリックひとつでデプロイできるようにしてみました! mackerel-cloudwatch-forwarder ・・・と、その前に下準備が必要です。 MackerelのダッシュボードからAPIキーをコピーしてきて、 AWS Systems Manager パラメータストアに Secure String として登録しておきます。 スクショでは Mackerel のものだと分かりやすいよう /development/api.mackerelio.com/headers/X-Api-Key という名前をつけました。 この名前を後で使うので覚えておきましょう。 次に AWS Lambda の画面を開き、「関数の作成」をクリックします。 「一から作る」「設計図」「AWS Serverless Application Repository」の3つの選択肢が表れるので、 「AWS Serverless Application Repository」 を選択します。 検索BOXに「Mackerel」と入れると、mackerel-cloudwatch-forwarderが 出てくるので、それを選択します。 なお、この選択肢はデフォルトでは表示ないので、「Show apps that create custom IAM roles or resource policies」にチェックを入れましょう。 アプリケーションの内容とパラメーターの設定画面に移ります。 「ParameterName」にパラメーターストアに登録したパラメーター名を入れましょう。 スクショの例では「/development/api.mackerelio.com/headers/X-Api-Key」を入力します。 「カスタムIAMロールを作成することに同意」のチェックボックスを選択したあと、デプロイ!

Jan 30, 2019 - 3 minute read - go golang mysql aws

CloudFormationでECSタスクのドレインをやる

やってみたはいいものの、1年後には仕組みを忘れていそうなのでメモ。 背景 昔はサービス毎にポコポコEC2インスタンスを立てていたのですが、 幸か不幸かインスタンスのリソースが余り気味でした。 そこで、最近流行りのコンテナ技術に乗っかって Amazon ECS (Amazon Elastic Container Service) を使って、 ひとつのインスタンスに複数のサービスを載せようと目論みました。 ちょうどその頃、Spot Fleetというものを使うと、 スポットインスタンスをお手軽に借りられるという話を聞いたので、 Spot Fleet + ECS で格安クラスターを作ってみよう!と手を出してみました。 (・・・もちろん、Fargateが東京リージョンで使えるようになったことは知っているけれど、スポットインスタンスの価格に負けてしまった・・・) AWS Fargate 東京リージョン サービス開始のお知らせ AWS Fargate で最大 50% の値下げを発表 ECS最適化インスタンスの更新問題 クラスターを作るだけなら、そう難しくはなく、インスタンス起動時にAmazon ECS-Optimized Amazon Linux AMIを使うだけです。 問題はこのイメージは定期的に更新される、ということです。 更新情報を流しているSNSトピックがあるので、これをサブスクライブしておくと、時たま更新通知が来ます。 Amazon ECS-Optimized Amazon Linux AMI の更新の通知のサブスクライブ この更新には機能追加はもちろん、セキュリティーフィックスも含まれているので、 なるべく早く新しいイメージに移行する必要があります。 移行は大まかに以下の手順で進めます。 新しいAMIイメージに更新した Spot Fleet を作成する 古いインスタンスに残っているタスクをいい感じに終了する(ドレイン) 突然殺すとユーザーにエラーが見えてしまうので、受付中のリクエストを捌き切ってから終了しないといけない ドレインが始まるとECSがタスク数を調整するために、新しいインスタンスにタスクをお引越ししてくれる ドレインが終了したら、古いインスタンスをシャットダウンする ここで問題になってくるのが「古いインスタンスに残っているタスクをいい感じに終了する(ドレイン)」の部分。 コンソールからポチポチするのも面倒なので、自動化したいところ。 しかし、いろいろとドキュメントをあさってみたのですが、「APIかawscliを叩く」「SNSとAWS Lambda をうまいこと組み合わせて頑張る」みたいな方法しか見当たらない・・・ しかもAWSの公式ブログ コンテナインスタンスのドレイン How to Automate Container Instance Draining in Amazon ECS Amazon ECS におけるコンテナ インスタンス ドレイニングの自動化方法 ・・・みんなどうやってるの・・・?

Jan 13, 2019 - 1 minute read - go golang mysql aws

IAM認証でAWS RDSへ接続するMySQLドライバを作った

AWS RDS には IAM 認証を使って接続する機能があります。 MySQL および PostgreSQL に対する IAM データベース認証 IAM 認証情報を使用して Amazon RDS への接続をユーザーに許可する方法を教えてください。 これを使って接続するGo言語のSQLドライバを書いてみました。 https://github.com/shogo82148/rdsmysql 使い方 IAMデータベース認証はデフォルトで無効になっているので、まずはこれを有効化します。 次に AWSAuthenticationPlugin を認証方式に指定して、新しいユーザーを作りましょう。 IAM データベース認証の有効化と無効化 データベースアカウントの作成 CREATE USER jane_doe IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS'; 他のSQLドライバはimportするだけで使えるのですが、 rdsmysqlではAWSへの権限情報を設定しなければならない都合上、 sql.Register を自前で呼び出す必要があります。 とは言っても、AWS SDKがいい感じに設定ファイルとか環境変数とか読んでくれるので、 rdsmysql.Driver にAWSセッションを渡すだけです。 c := aws.NewConfig().WithRegion("ap-northeast-1") s := session.Must(session.NewSession(c)) d := &Driver{ Session: s, } sql.Register("rdsmysql", d) db, err := sql.Open("rdsmysql", "jane_doe:@tcp(db-foobar.ap-northeast-1.rds.amazonaws.com:3306)/") if err != nil { log.Fatal(err) } defer db.Close() あとは通常のMySQLドライバとして呼び出すだけです。 go-sql-driver/mysql のラッパーになっているので、 DNS等の書き方はこれに準じます。 認証部分は rdsmysql がやってくれるので、パスワードは空でOKです。 パスワードの管理から開放されて楽ですね!

Dec 16, 2018 - 3 minute read - perl aws lambda

AWS LambdaでCGIを蘇らせる

この記事は Perl Advent Calendar 2018の15日目の記事です。 (キリの良いところまでできたのと、記事が書かれていなかったので代打投稿) Custom Runtime のリリースにより、AWS Lambda 上でPerlが動くようになりました。 PerlをAWS Lambdaで動かす 次は AWS Lambda + CGI でサーバーレスだな... — Ichinose Shogo (@shogo82148) 2018年12月8日 ということで、やっていきましょう。 できたもの 動かすのはもちろん、 CGIアクセスカウンター 。 なんと嬉しいことに、最近になって WwwCounter の新バージョン(Ver3.16)がリリースされ、 Perl 5.26 に対応しました! 2018-11-11 perl 5.26に対応。(Ver3.16) 更新履歴によれば一つ前の Ver 3.15 のリリースは2003-03-23なので、なんと15年ぶりのアップデートです。 杜甫々さんの AWS Lambda で動かしてくれ!! という声が聞こえてきそうですね・・・!!! 動いたーーーー!!!! 実装はこちら AWS::Lambda ちなみにWwwCounterのアップデートはPerl 5.26で「@INCからカレントディレクトリが削除」された件への対応だと思います(コミットログがないので予想)。 第46回 Perl 5.26で変わること(1) - Perl Hackers Hub 実装説明 「そもそもCGIってなんだ?」っていう人も多くなってきたと思うので、そこらへんの歴史の話にも軽く触れます。 この辺の歴史をリアルに体験したわけではないので、誤り等あればご指摘ください。 CGIとは Common Gateway Interface の略で、 WebサーバーとCLI(Command Line Interface)アプリケーションのやり取りの方法を決めた規格です。

Nov 30, 2018 - 2 minute read - perl aws lambda

PerlをAWS Lambdaで動かす

AWS Lambda で Custom Runtime が発表されました! 新機能 – AWS Lambda :あらゆるプログラム言語への対応と一般的なコンポーネントの共有 New for AWS Lambda – Use Any Programming Language and Share Common Components AWS Lambda Now Supports Custom Runtimes, and Enables Sharing Common Code Between Functions Custom Runtime により好きなプログラミング言語でLambda関数を書くことができ、 いくつかの言語についてはAWSおよびパートナーから bootstrap が提供されます。 提供される言語にCOBOLが入って話題になっていますが、 当然ながら(?)Perlはありません。 Custom Runtimeは shell script でも書ける簡単なものなので、Perlでも書いてみました。 Perl in AWS Lambda 以下のスクリプトを bootstrap という名前で保存します。 #!/usr/bin/env perl use utf8; use warnings; use strict; use lib "$ENV{LAMBDA_TASK_ROOT}/local/lib/perl5"; use Furl; use JSON; my $furl = Furl->new; my ($handler, $function) = split /\.

Nov 22, 2018 - 2 minute read - go golang

Goのnil,true,falseは変数名に使えるという話

@Linda_pp さんのツイートをみて Go 言語の nil って NilLit じゃなくて Ident "nil" としてパースされるのか.それで気付いたけど nil := 42 みたいに普通に変数宣言できる(unused でエラーになるけど) — ドッグ (@Linda_pp) 2018年11月22日 なるほど、これは面白い。 と少し遊んでみたメモ。 言語仕様にある通り、Goのキーワードは以下の25個です(Go1.11.2)。 break default func interface select case defer go map struct chan else goto package switch const fallthrough if range type continue for import return var この一覧に nil や true, false は入っていません。 これらは builtinという扱いになっており、識別子として利用可能です。 そのため、変数名等に利用可能というわけですね。面白い。 package main import ( "fmt" ) func main() { nil := 42 fmt.Println(nil) // Output: // 42 } 以下、色々遊んでみた例。

Sep 24, 2018 - 3 minute read - go golang php

PHPer向けGoのJSONデコーダーを作った

必要に迫られて作りました。 PHPでエンコードしたJSONをいい感じにデコードしてくれるGoのパッケージです。 shogo82148/go-phper-json 背景 さて、PHPerの方々には当たり前のことかもしれませんが、PHPの言語仕様について少しおさらいです。 それがどうしてGoで問題になるか見ていきます。 PHPのarray問題 PHPはとても便利なプログラミング言語なので、配列を扱うことができます。 ここでPHPの配列のマニュアルを読んでみましょう。 http://php.net/manual/ja/language.types.array.php PHP の配列は、実際には順番付けられたマップです。マップは型の一種で、 値をキーに関連付けます。 この型は、さまざまな使い道にあわせて最適化されます。 配列としてだけでなく、リスト (ベクター)、 ハッシュテーブル (マップの実装の一つ)、辞書、コレクション、スタック、 キュー等として使用することが可能です。 PHP の配列には他の PHP 配列を値として保持することができるため、 非常に簡単にツリー構造を表現することが可能です。 (強調部分は筆者によるもの) 重要なことなのでもう一度。 配列としてだけでなく、リスト (ベクター)、 ハッシュテーブル (マップの実装の一つ)、辞書、コレクション、スタック、 キュー等として使用することが可能です。 他の言語でリスト、ハッシュテーブル、辞書等と呼ばれているものは、PHPにおいてはいずれも配列です。 PHPにとっては、整数を添字にしているか、文字列を添字にしているかの違いでしかありません。 (PHP7.xから整数が添字の場合に最適化が入るようになったらしいけど、大きな挙動の変更はないはず) そのため、以下のスクリプトは true を返します。 <?php $a = array("apple", "banana"); $b = array(0 => "apple", 1 => "banana"); var_dump($a == $b); // bool(true) この仕様のため、JSONにエンコードすると最初は配列だったのに、 処理を進めていくうちにうっかり文字列のキーを作ってしまって、 JSONのオブジェクトに変わってました、ということが起こりえます。 Goにおいて両者は全く違う型なので、デコードの際に非常に困ります。 <?php $a = array(1, 2, 3); print json_encode($a); // [1,2,3] $a["foo"] = "bar"; print json_encode($a); // {"0":1,"1":2,"2":3,"foo":"bar"} このような悲劇を防ぐために、 JSON_FORCE_OBJECT というオプションがあるのですが、 オプションの名前通りに全部JSONのオブジェクトになってしまいます。 この要素だけJSONの配列にして欲しい!といった細かな操作はできません。

Aug 20, 2018 - 1 minute read - javascript time

〜夏休みの自由研究〜 電波時計のサマータイム対応状況を調べてみた

僕は知っています。 ぜひ、みなさんもお手元の電波時計で試してみてください! と書いても、試してくれる人なんていないことを。 僕は知っています。 説明書や仕様書に書いてあったとしても、書いてあるとおりに動作する機械なんて、ほんの一握りだということを。 というわけで、楽しい夏休みの自由研究です。 秋葉原で適当に買った1000円から3000円くらいの電波時計に、サマータイムのフラグを有効にした電波を受信させてみて、どういう挙動になるか調査してみました。 TL;DR 10機種(SEIKO, CITIZEN, CASIO, MAG, ELECOM, その他)に対して、サマータイムフラグを有効にした状態で Web JJY の電波を送信しました。 今回の調査範囲では、夏時間の時刻(1時間 or 2時間ズレた時刻)を表示する時計は見つからなかった 夏時間実施中(DST)と表示 する時計は実在する 室内で使うならCASIOの電波時計はクオーツ時計だと思ったほうがいい 電波受信の様子をYouTubeにあげておいたので興味のある方はどうぞ。 背景 2018年、日本は記録的な猛暑に見舞われ、 観測史上最高の気温41.1度を記録し、 熱中症とみられる症状で90人以上が亡くなるという甚大な被害を被った。 今週の天気 記録的な猛暑 底知れぬ暑い夏 日本で猛暑 気温41.1度で観測史上最高 気象庁「災害と認識」熱中症死の疑い6日で90人超 この記録的猛暑を受け、政府・与党によって2020年の東京五輪・パラリンピックの酷暑対策として、夏の期間だけ時間を2時間繰り上げる「サマータイム(夏時間)」の導入が検討されている。 酷暑対策でサマータイム導入へ 秋の臨時国会で議員立法 31、32年限定 これに対して、「電波時計が狂うのではないか」「日本中の電波時計がゴミになる」等、電波時計が正しい時刻を示さなくなるとの指摘が相次いでいる。 サマータイム導入で「電波時計が狂う」? メーカーに聞いた サマータイムで日本中の電波時計がゴミになる(かも)という話 電波時計は、NICT(情報通信研究機構)が提供している標準電波(JJY)を受信し、時刻の同期を行っている。 この標準電波には、時、分、通算日、年、曜日といったタイムコード情報に加え、 将来の拡張性のための「予備ビット」が設けられている。 この予備ビットに関して、「標準電波の出し方について」には、夏時間情報として意味を持たせる場合の例が記載されているが、これはあくまでも例であり、告示などで正式に決まっているものではない。 しかし、現実に市販されている電波時計のなかにも、仕様上予備ビットの状態を認識する機種がする。 標準電波の送信周波数40kHzを提供する「おおたかどや山標準電波送信所」は1999年6月運用開始、送信周波数60kHzを提供する「はがね山標準電波送信所」は2001年10月運用開始である。 日本でサマータイムが導入されたのは1948年から1951年の期間だけなので、 今後サマータイムが導入されることとなれば、標準電波の運用が始まってから初のサマータイム導入となる。 夏時刻法 - Wikipedia 長波帯標準電波施設 パンフレット(PDF) そのため、仕様上はサマータイムへ対応している電波時計であっても、初のサマータイム実施によって未知の挙動を示すことが十分に想定される。 そこで、本記事では、実際にサマータイム実施中の電波を電波時計に受信させ、 どのような挙動を示すのかを明らかにする。 目的 2018年8月現在日本で市販されている電波時計が、サマータイムの情報を含んだ標準電波(JJY)を受信した場合の挙動を調査し、 仮に、2019年、2020年にサマータイムが導入された場合の影響を明らかにする。 実験方法 秋葉原で購入した以下の電波時計に対して、標準電波と同様の電波を送信し、時刻の同期を行う。 CITIZEN 8RZ152 CITIZEN 4RL432-019 SEIKO SQ698S SEIKO KR331W MAG T-694 SM-Z ELECOM CLK-DD001RD 京都大和 171038 (製造元不明) 31756 CASIO DQD-710J-8JF CASIO TTM-160NJ-8JF 電波の送信には、Samsung Galaxy Note8 SC-01K を用いて、JJYシミュレータWeb版を実行する。