るいすときのこの物語

オタクエンジニアの雑記

S3をCloudFrontから配信&LambdaとWAFを組み合わせて利便性とセキュリティを確保してみる

この記事は、AWS Lambda Advent Calendar 2017 の5日目となります。

今回は S3 にある画像ファイルを CloudFront から配信をしつつ、 CloudFront のアクセスログを Lambda で解析を行って、WAF の IPリストへ追加を行って、セキュリティの担保を行ってみようと思います。

 

S3 を CloudFront から配信する理由

前提条件に、弊社の S3 バケットにはユーザー個人が撮影した画像がアップロードされます。 なのでセキュリティには気を置いてしっかり運用していく必要性があります。

AWS SDK を使用すれば S3 から署名付きURLが吐き出され、これにアクセスすれば画像を取得できます。が、この署名付きURLには有効期限が設定されており、アプリから使うにはちょっと面倒です。

S3 を CloudFront から配信をすると、WAF が使えるようになるので固定URLで配信しつつ、セキュリティも担保できます。

 

構成


①ユーザーが CloudFront にアクセスを行う ②リクエストヘッダー等を WAF へ送る ③WAF にあるルールと照合し、結果を返す ④③がOKなら S3 から画像を持ってくる(NG なら 403 を返す) ⑤アクセスログを S3 へ保存する ⑥⑤のタイミングで、Lambda が発火する ⑦アクセスログに HTTPステータスコードが 200 以外のものがあったら、IPを WAF の IPリストに追加する

Lambda 関連のアドベントカレンダーなのに、Lambda の部分を特筆するところがねぇ...

 

CloudFront ディストリビューションの作成

オリジンに、対象の S3 バケットを。 ログの設定も何となく分かると思います。


S3 は CloudFront からのみアクセスできるようにしておきます。 ブルートフォースアタック等で、キー(/user_id/hoge_id/file_name_.jpg みたいな)がバレないように、こうします。CloudFront + WAF を組み合わせればキーがバレる前にブラックリストに入れて、アクセスを拒否できます。

 

WAF web ACL の作成


AWS resource to associate には先程作成した CloudFront のディストリビューションを設定します。

ルールの作成部分では IP address で作成する。 文字列判定を使えば特定の文字列をヘッダーに載せるとアクセスできるなんて仕組みもできます。

 


こんな感じです。 最初に IPアドレス判定を行ってから、文字列判定のルールでヘッダー認証をします。順番が大事です。今回はなんちゃってヘッダー認証があるので、デフォルトアクションは全て 403 を返すようにしています。

 

Lambda スクリプトを作る

言語は好きなものを選んでください。 今回僕が書いて運用しているコードを貼っておきます。


IP_SET_ID には WAF -> IP addresses の自分で作成した空のルールにアクセスすると、URLにある /ipsets/<ここ> を指定します。

 


トリガーに S3 として、オブジェクト作成時に発火されるようにしておきます。

 

動作確認

①正常なアクセス

ヘッダーに特定の文字列があり、正常なアクセスな場合

$ curl -I -L -H 'token:token' 'https://hoge.cloudfront.net/user/akfja/1A1C275B-BE3D-4DFC-9008-B3C375682F41/IMG_3022.JPG' -s
HTTP/2 200
content-type: application/octet-stream
content-length: 228486
date: Sat, 02 Dec 2017 12:59:15 GMT
last-modified: Sat, 16 Sep 2017 10:46:06 GMT
etag: "e70ff2a523be03ffa29ab17964345ba1"
x-amz-storage-class: STANDARD_IA
accept-ranges: bytes
server: AmazonS3
age: 252
x-cache: Hit from cloudfront
via: 1.1 Bcra7f856e226a0db7cefa4bac222.cloudfront.net (CloudFront)
x-amz-cf-id: hoge_c9CYphpJ2mJzLGvGOVn_b3Rouzkr9cfEtajQ==

 

②不正なアクセス(ヘッダー無し)

このアクセスをすると、WAF の IPリストに載り、以降アクセスできなくなれば成功

$ curl -I -L 'https://hoge.cloudfront.net/user/akfja/1A1C275B-BE3D-4DFC-9008-B3C375682F41/IMG_3022.JPG' -s
HTTP/2 403
server: CloudFront
date: Sat, 02 Dec 2017 13:10:14 GMT
content-type: text/html
content-length: 555
x-cache: Error from cloudfront
via: 1.1 6eaa7f856e226a0db7cef6201d3b8393.cloudfront.net (CloudFront)
x-amz-cf-id: hoge_KUn-jH4jcV_KOTMqlJs8WJhQw==

 

WAF の IPリストを確認する


期待通り、IPリストに追加されていました? あとは、正しいヘッダーを入れても 403 が返ってくれればOK

 

③IPリストに追加されたIPで、①をやってみる

$ curl -I -L -H 'token:token' 'https://hoge.cloudfront.net/user/akfja/1A1C275B-BE3D-4DFC-9008-B3C375682F41/IMG_3022.JPG' -s
HTTP/2 403
server: CloudFront
date: Sat, 02 Dec 2017 15:45:45 GMT
content-type: text/html
content-length: 555
x-cache: Error from cloudfront
via: 1.1 a3c7cc30af6c8465e695a3c0d44793e0.cloudfront.net (CloudFront)
x-amz-cf-id: hogenv8bPhaW6Qz1k6qUZajIWCwWw9RDuc6CKmTJvy5rg==

素晴らしい...

 

まとめ

ということで、S3 を CloudFront で配信し、WAF と Lambda を組み合わせれば 固定URLで配信しつつ、セキュリティの確保もすることができました。

ちなみに、Lambda 側は実行時間とか、設定したメモリ使用量で課金がされるので dev, stg, prd で3つの関数を用意しても、1つの関数で賄ってもどちらでも良いと思います。 管理のしやすさで言えば、3つに分けたほうが良いのかな?

Lambda の料金も、低スペックで長時間やるより、ちょっと良いスペックで短時間で処理を終わらせたほうが安くなったりするのでどうやって Lambda を運用するか難しいですね。

荷物の追跡状況をTwitterで知らせてくれるサービスを作ってみた - tim.xzy.pw

Node.js と Express を触れてみたくて自己満サービスを作ってみた。 色んな人に使ってもらいたいなという想いはあるもののUIは最悪です。

[blogcard url="https://tim.xzy.pw"][/blogcard]

 

 

概要

WebとTwitterだけで全てが完結する荷物追跡サービス。 各運送業者にスクレイピングをすることで変更がある際にTwitterで通知してくれる。

 

荷物に変更があると下記のようにダイレクトメッセージで通知される。

  • Node.js
  • Express
  • PHP(スクレイピング)

で構成されてます。

 

 

取得している情報について

私は人から信頼されていないのか分かりませんが 私「これちょっとログインしてみてくんね?」 友「え、情報抜かれそうだからヤダ」 友「お前何するか分からねえ」 とよく言われます。

 

このWebアプリケーションで取得している情報は下記の通り

  • user_id(Twitterの固定ID)
  • OAuth_Token(Secret)(自分の垢から自分へDMを送るため)
  • 追跡番号
  • 運送業者

となります。

 

また、OAuth_Token_Secret、追跡番号は AES-256-CBC で暗号化しています。

 

 

雑書

初めて Express を触ってみた感じ、めちゃくちゃ便利だなと思った。 ログイン機能も Express と Passport の親和性が良くてすんなりとできた。 しかし非同期処理の書き方は慣れないなと思った。

この記事は 田胡・柴田研究室アドベントカレンダー2016 17日目の記事です。

ZabbixでLXC Containerを監視する

この記事は Zabbix Advent Calendar 2016 の13日目の記事です。 前回の12日目の記事は KAZ0225さんの「Zabbix APIの検証はAdvanced REST Clientが便利です!」です!

今回は LXC Container を Zabbix で監視してみようという内容です。 なんで Docker じゃなくて LXC なのかはあまり突っ込まないでください。 ちなみに11月29日に同じような記事を書いていますがリライトといった形になります。

[blogcard url="https://luispc.com/server/2016/11/29/8746"][/blogcard]

 

 

問題

LXC のコンテナを監視するとき zabbix-agent 入れればいいんじゃね?と私は思っていました。 LXC は KVM とは違ってハイパーバイザでBIOS含めハードウェアをエミュレートせず、OSが利用するリソースを隔離することで仮想化しています。

そして LXC Container に zabbix-agent を入れてグラフを見てみるとこんなことが起こります。

 

引用: ZabbixにおけるDocker監視の方法 http://www.zabbix.com/img/zabconf2015_jp/presentations/04_zabconf2015_watanabe.pdf

こちらのスライドは Docker に関することが書かれていますが LXC でも同じことが言えます。

では、どうするのかというと LXC には lxc コマンドが実装されています。 例に lxc-info を出すと

cgroupsでリソース管理されているのを lxc-info で整形してくれていています。 lxc コマンドをパースしていって Zabbix に登録します。

 

 

Zabbix-LXC

既にいい感じのが GitHub にありました。 [blogcard url="https://github.com/Lelik13a/Zabbix-LXC"][/blogcard]

 

がこれだと複数のNICを割り当てているとトラフィック量が取得できなかったり、 LLDが機能しなかったりしたのでフォークしてみた。 [blogcard url="https://github.com/rluisr/Zabbix-LXC"][/blogcard]

Zabbix3.2で動作確認済み。

 

 

機能

  • 修正→ LLDを使ってコンテナを自動追加
  • 修正→ lxc-infoで確認できるリソースの監視
  • 修正→ コンテナに1つ以上のNICがある場合でもIN/OUTの転送量が取得できる(合算するように)
  • 追加→ ディスクの使用量をパーセンテージで
  • 追加→ Proxmoxを使用している場合にZabbixに登録される表示名にホスト名を指定(そのまんまだとid (ex 100, 101とか)で登録される)

正直デメリットもあって、LLD側のスクリプトはPHPで書いた。 いつかは Perl で書き直したい所存...。

アイテムとしてはこんな感じです。

 

インストール

sudoコマンド、PHPの環境が必要です。

 

  1. LXCホストOS上にて sudo ./install.sh を実行
    $git clone git@github.com:rluisr/Zabbix-LXC.git
    $cd Zabbix-LXC
    $sudo ./install.sh
     
  2. テンプレートのインポート
    # ノードのテンプレート
    zbx_template/Template_LXC_Node.yml (Proxmoxの人は Template_LXC_Node_Proxmox.yml)
     
    # コンテナのテンプレート
    zbx_template/Template_LXC_CT.yml
     
  3. コンテナが登録されるまで待つ(600秒)

 

あとは好きなようにできるので grafana と連携すれば Zabbix をデータソースとして使用できるのでめちゃくちゃ便利

 

次回は nakayama_yasuhiro さんの 「Zabbixからエージェントレスでwindowsを監視する」です!