株式会社クイックのWebサービス開発blog

HAPPYなエンジニア&デザイナーのブログです

ISUCON7予選に参戦してまた惨敗した話 チーム†漆黒ノ天使達†

yumeです。

随分遅くなりましたが、10/21(土)にチューニング大会ISUCONに出場したので、そのことを振り返ります。

ISUCONとは?

お題となるWebサービスを決められたレギュレーションの中で限界まで高速化を図るチューニングバトル、それがISUCONです。過去の実績も所属している会社も全く関係ない、結果が全てのガチンコバトルです。

詳しくは↓

ISUCON7 開催&日程決定! #isucon : ISUCON公式Blog

ISUCON7 まとめ : ISUCON公式Blog

クイックからは(色々あって)1チーム出場

チーム名:†漆黒ノ天使達†〜3度目の正直〜

構成:新入社員+2年連続で惨敗している先輩社員

結果

今年も見事に予選落ちでした。 スコアは16000くらいでした。

やったことと感想

たいしたことはやれていませんが、雰囲気が伝わればと…!

【AWS S3】S3 bucket policy を使ったアクセス制限方法 ~Effectの評価優先度を考える~

こんにちは。クイックSREチームのみっちーです。
今日は、S3のbucket policy についての記事です。

前回の記事で「S3 bucket policy」については少し触れましたが、今回はもう少し内容を掘り下げていこうと思います。
おまけで、いくつかサンプル設定も載せたので、ぜひ最後まで読んでみて下さい✧(・ㅂ・)و

今回の記事は、こんな人向けです。

  • 「これからS3にアクセス制限を実施しよう!」と思っている人。

  • 「S3 bucket policy が意図した動作をしてくれない」と嘆いている人。

  • 「とりあえずサンプルが欲しい」と思っている人。

目次

1. S3で出来るアクセス制限の方法(種類)

2. bucket policy の書き方

3. bucket policyのサンプル

1. S3で出来るアクセス制限の方法(種類)

bucket policyの話に入る前に、
まずはS3で出来る「アクセス制限の方法(種類)」についてのおさらいです。
S3では大きく分けて、以下の3種類の方法でアクセス制限をする事ができます。

それぞれの設定は独立していて、併用が可能です。

S3 bucket policy を利用した方法 ※ 今回はこれについての説明です

  • bucketのアクセス「ルール」を細かく記載する。
  • JSON形式で記載。

メリット

  • 「特定IPアドレスのみ許可」 したり、 「特定アカウントと特定IPのみを許可」 と言った、やや複雑なルールも書ける。

デメリット

  • 「ルール」の評価優先度にクセがある(詳細は後述)ため、慣れるまでは扱いが難しい。

    ACL(Access Control List) を利用した方法

  • bucketにアクセスできる「ユーザ」を設定する。
    「特定アカウントのみ許可」 や、 「全ユーザ公開(public read)」 など。

メリット

  • 設定が簡単。
    管理コンソールから、「アカウント名と、許可する動作にチェックを入れるだけ」でOK。

デメリット

  • 設定が簡単な反面、単純な設定しか出来ない。

    IAM を利用した方法

  • aws IAMサービスを利用してアクセス制限をする。
  • bucketにアクセスできる「IAMユーザ」を設定する。
  • JSON形式で記載。

メリット

  • 異なるアカウント管理下にあるS3 bucketを参照したりする設定が出来る。
  • IAMの書き方がS3 bucket policyとほぼ同じで、どちらかを覚えれば応用が効く。

デメリット

  • IAMの使い方を覚える必要がある。
  • 通常、S3上のオブジェクトを公開するだけなら使う必要はない。

2. bucket policy の書き方

「ルール(Effect)」と「評価優先度」

S3 bucket policyは、「評価優先度の異なる3つのルール(Effect)」を組み合わせてアクセス制限を実装します。
この「評価優先度」の部分にクセがあるため、扱いにくいと感じる方もいるかと思います。

ポイントは3つ!!!

  • より「評価優先度」の高いルール(Effect)が適用される。

  • 適用されたルール(Effect)よりも「評価優先度」が低いルール(Effect)は、破棄(非適用)される。

  • 前述の「評価優先度」により、「ルール(Effect)」の記載順序は無関係であること。
    ※上の方に書いたから優先になる訳ではない。

S3 bucket policy が解釈できる「ルール(Effect)」の種類

Clearly Deny

  • 評価優先度:高
  • これは「Deny」だよ、と明記する場合。
  • 評価優先度「中」以下のEffectよりも優先適用される。

Clearly Allow

  • 評価優先度:中
  • これは「Allow」だよ、と明記する場合。
  • 評価優先度「低」のEffectよりも優先適用される。

Default Deny

  • 評価優先度:低
  • 特に「Deny」も「Allow」も記載されていない箇所に対して、default で適用されるEffect。



3. bucket policyのサンプル

文章だけだと分かりにくいので、実際にサンプル設定を使って説明をしていきます。
なおここに載せてあるサンプル設定は、必ず検証してから自己責任でご利用ください。

サンプル01 :
「特定のIPアドレス」からのみアクセス許可したい。

解説

この場合は、「Clearly Allow」と「Default Deny」を使用します。

まずは、以下のように「Effect」に「Allow」と記載します。※「Clearly Allow」

  {
            "Sid": "Allow-from-specific-IP-only",  ← 分かればなんでも良いです。
            "Effect": "Allow",   ←「Allow」を記載します。
            "Principal": {
                "AWS": "*"
            },

続けて、「Condition」の項目で許可したいIPアドレスを記載すればOKです。

            "Action": "s3:GetObject",   ← 閲覧権限のみを付与。
            "Resource": "arn:aws:s3:::<your S3 bucket name>/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "xxx.xxx.xxx.xxx/yy",  ← アクセス元のIPをCIDR形式で記載します。
                        "xxx.xxx.xxx.xxx/yy"   ← 複数書くときは、","で区切って記載します。
                    ]
                }
            }
 }

ここに記載の無いIPアドレスについては、
条件不一致 =「Default Deny」として処理されます。


サンプル01 の設定まとめ

この設定は、まとめると以下のような形になります。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Allow-from-specific-IP-only",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<your S3 bucket name>/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "xxx.xxx.xxx.xxx/yy",
                        "xxx.xxx.xxx.xxx/yy"
                    ]
                }
            }
        }
    ]
}

備考

他にも、以下のように「Clearly Deny」でこのIP以外は拒否という形でもOKです。
ただしこの場合は、「Clearly Allow」と組合せることが出来ない(Clearly Denyが優先され、Clearly Allowは破棄される)ので、拡張性に乏しくなります。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Allow-from-specific-IP-only",
            "Effect": "Deny",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<your S3 bucket name>/*",
            "Condition": {
                "NotIpAddress": {    ←「指定IP以外を拒否する」
                    "aws:SourceIp": [
                        "xxx.xxx.xxx.xxx/yy",
                        "xxx.xxx.xxx.xxx/yy"
                    ]
                }
            }
        }
    ]
}



サンプル02 :
「特定のIPアドレス」と「CloudFront」からのアクセスのみ許可したい。

解説

この場合も、「Clearly Allow」と「Default Deny」を使用します。
ポイントは、「Clearly Allow」を2つ使うことです。
「Clearly Allow」は複数束ねると「OR条件」として使えます。

先程と同様に、まずは「Clearly Allow」で許可したいIPアドレスを記載します。
次に、同じく「Clearly Allow」で、CloudFrontのOrigin Access Identityを記載します。

  {
            "Sid": "Allow-from-specific-IP-only",  
            "Effect": "Allow",   
            "Principal": {
                "AWS": "*"
            },
・・・略)

 {
            "Sid": "Allow-from-CloudFront",
            "Effect": "Allow",  
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <your CloudFront Origin-Access-Identity>"   ←これは別途CloudFrontを操作してIDの取得をしてください。
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<your S3 bucket name>/*"
        }

こうすることで、Allow の OR条件として判定されます。
そして最後に、上記にマッチしないアクセス全てを「Default Deny」で拒否します。

注意点としては、「Clearly Deny」でIP制限を書かないことです。
理由は「Clearly Deny」の優先度が最も高いため、指定したIP以外はアクセスできなくなるからです。

サンプル02 の設定まとめ

{
    "Version": "2012-10-17",
    "Statement": [
  {
            "Sid": "Allow-from-specific-IP-only",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<your S3 bucket name>/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "xxx.xxx.xxx.xxx/yy",
                        "xxx.xxx.xxx.xxx/yy"
                    ]
                }
            }
        },
        {
            "Sid": "Allow-from-CloudFront",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <your CloudFront Origin-Access-Identity>"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<your S3 bucket name>/*"
        }
    ]
}



サンプル03 :
「特定のIPアドレス」と「CloundFront」および「AWS CLI」からのみアクセス許可したい。

解説

サンプル02の条件に加えて、「AWS CLI ※」での操作を可能とする設定です。
※ ”aws s3 sync” コマンド等のことです。

S3の閲覧やオブジェクトのダウンロードは、通常はブラウザ経由が多いと思いますが、
AWS CLIを利用してオブジェクトコピーをしたりする開発者は多いと思います。
そういう場合に、AWS CLIでの操作だけはS3の全権限を付与した設定のサンプルです。

{
            "Sid": "Allow-full-control-for-API",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<your account No>:<user name>"
            },
            "Action": "s3:*",   ←S3の全操作権限を付与
            "Resource": [
                "arn:aws:s3:::<your S3 bucket name>/*",
                "arn:aws:s3:::<your S3 bucket name>"
            ]
        }

これも基本はさっきと同じで、
「Clearly Allow」で許可したい条件を記載し、それ以外はDefaultDenyで拒否しています。

サンプル03 の設定まとめ

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Allow-from-CloudFront",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <your CloudFront Origin-Access-Identity>"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<your S3 bucket name>/*"
        },
        {
            "Sid": "Allow-from-specific-IP-only",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<your S3 bucket name>/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "xxx.xxx.xxx.xxx/yy",
                        "xxx.xxx.xxx.xxx/yy"
                    ]
                }
            }
        },
        {
            "Sid": "Allow-full-control-for-API",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<your account No>:<user name>"
            },
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::<your S3 bucket name>/*",
                "arn:aws:s3:::<your S3 bucket name>"
            ]
        }
    ]
}



いかがでしたか。

S3 bucket policyの設定がうまく行かなくて悩んでいる方の助力になれば幸いです。
今後もAWS関連の記事を書いていこうと思っているので、興味を持って頂けたらぜひまた読んでみて下さい!

それでは~ (・о・) /

この記事のトップへ戻る

Enum を利用したステータスの条件分岐

南国育ちなんですが、寒いの結構好きです。
フルーツパーラーです。

最近の興味はドメイン駆動設計をどうやってプログラムに落としていくかです。

現場で役立つシステム設計の原則」がとても面白かったので、
その中のコードをPHPでアレンジしてお伝えしようと思います。

細かく知りたい方は是非読んでみて下さい。

こんな方に読んで欲しい

  • PHP で実装したい
  • Enum を使いステータス判定処理を実装したい
  • 気がついたらステータス判定のロジックがあらゆる処理に染み出している

お伝えしたい事

  • ステータスを列挙型で定義しておくと、入力判定、ステータスの変更ルールをまとめやすい
  • 列挙型として定義しておくことで、コード補完の恩恵を最大限に受けられる(これは思ってた以上に便利)

サンプルの説明

採用フローをイメージしたステータス変更ロジックで考えてみます。
ステータスは、エントリー > 選考 > 適正試験 > 面接 > 内定 の順に進み、
次のステップにしか進む事が出来ない仕様です。

ステータスの変更可能パターン

from / to 選考 適正試験 面接 内定
エントリー
選考
適正試験
面接

実装時に考えるvalidation

  • 存在するステータスかどうか
  • 遷移して良いステータスかどうか

Enum(列挙型)を利用し実装してみます

PHPは標準でEnumをサポートしていません。
※ リフレクションを使い実装している方がいましたので、参照させて頂きました。
※ 以下のコードは参照リンクEnum クラスを継承する想定です。

選考フローを定義

<?php

/**
 * 参照の Enum クラスを継承
 * 列挙型の選考フロー
 * Class EntryStatus
 */
final class EntryStatus extends Enum
{
    const ENTRY = 1;
    const SENKOU = 2;
    const TEKISEI = 3;
    const MENSETSU = 4;
    const NAITEI = 5;
}

遷移可能か判定するロジック

canTransition メソッドの中で、次のステップに遷移可能かどうかを判定しています。
判定ロジックがアプリの至る所で発生すると、保守が非常に難しく、
ビジネスロジックの中が if 文で溢れかえります。
Transition クラスの中に閉じ込めています。

<?php

/**
 * 可能な範囲の選考フロー遷移かどうかを判断するクラス
 * Class Transition
 */
class Transition
{
    protected $before;
    protected $after;

    /**
     * Transition constructor.
     * @param EntryStatus $before
     * @param EntryStatus $after
     */
    public function __construct(EntryStatus $before, EntryStatus $after)
    {
        $this->before = $before->valueOf();
        $this->after = $after->valueOf();
    }

    /**
     * 遷移可能か
     * @return bool
     */
    public function canTransition()
    {
        $pattern = array();

        // ここで遷移出来る全てのパターンの確認
        if ($this->before === EntryStatus::ENTRY) {

            $pattern = array(EntryStatus::SENKOU); // 遷移パターンが複数あれば、配列に追加

        } elseif ($this->before === EntryStatus::SENKOU) {

            $pattern = array(EntryStatus::TEKISEI);

        } elseif ($this->before === EntryStatus::TEKISEI) {

            $pattern = array(EntryStatus::MENSETSU);

        } elseif ($this->before === EntryStatus::MENSETSU) {

            $pattern = array(EntryStatus::NAITEI);

        }

        return in_array($this->after, $pattern, true);
    }

}

学生の選考情報(ステータスを)を保持するクラス

canTransition メソッドをコールし、遷移可能なステップか判定。
遷移不可な場合はメッセージを表示するような処理が続きます。
遷移可能な場合はステータスを登録。

<?php

/**
 * 学生の選考情報
 * Class RecruitStep
 */
class RecruitStep
{

    /**
     * @return string
     */
    private function getStatus()
    {
        // 実際は DB から取得する or 既に DB から取得済
        // Repository(永続化)層で行う
        return EntryStatus::ENTRY;
    }

    /**
     * 選考フローの登録
     * @param EntryStatus $entry
     */
    public function save(EntryStatus $entry)
    {
        // 現在のステータスを取得
        $status = $this->getStatus();
        $before = new EntryStatus($status);

        // ステータス変更が可能か判断
        $transition = new Transition($before, $entry);
        $available = $transition->canTransition();

        if ($available) {
            // DB にステータスを登録
            // ...
        }

        // メッセージ表示の後続処理
        // ...

    }
}

ステータスを登録する場合は、引数に Enum の値を注入します

<?php
$step = new RecruitStep();
$step->save(new EntryStatus(EntryStatus::SENKOU));

まとめ

Transition クラスの中で遷移可能なステータスかを判定し、処理を集約出来ます。
入力値に Enum を利用する事で、存在しないステータスが入る事を防ぐ事が可能です。

ゆるふわPHPを利用する上で、さくっと書く場面、少し堅く場面を意識しながら書いていきたいですね。

CSEのサイト内検索結果がGoogle検索と大幅に違う場合は、検索エンジンを作り直すと良いらしい

yumeです。今回は勢いに任せて書きます。

GoogleCSE(Custom Search Engine)を利用してサイト内検索を実装したものの、
いつまで経っても検索結果がダメダメでした。

かれこれ3週間以上悩んでいたのですが、
結果、新規の検索エンジンを作り直したらうまくいきました。それだけの話。

経緯

既存サイトの新規ディレクトリ配下に、新しいコンテンツを作成

看護roo!というサイト(www.kango-roo.com)の新コーナーとして、/ki/配下に記事ページを100件超追加しました。
数日後、ちゃんとクロールされたことが確認できました。

f:id:aimstogeek:20170929154253p:plain

この後、/ki/専用のサイト内検索フォーム設置を予定しており、
めでたくインデックス登録されたことだし、GoogleのCustom Search Engineを利用して実装することに。

CSEで既存の検索エンジンにラベルを追加

このサイトでは、すでに全コーナーのサイト内検索を実装済
自然に、「既存の検索エンジンを使って、ラベルだけ新規作成しよう」となりました。

既存の検索エンジンに、www.kango-roo.com/ki/ のラベルを作成。

おかしい!!!!!検索結果が1件しかない!!!

「site:www.kango-roo.com/ki/ 看護師」でGoogle検索したときには100件超ヒットしたのに、
CSEのコンソールで「看護師」と検索しても1件しかでない!!!!!!!

迷走

  • sitemap.xmlを急遽作ってアップしたり
  • SearchConsoleから上限数までインデックス登録リクエストしたり
  • 意味もなく2週間待ってみたり

    途方に暮れていたその時、このページにたどり着きました。
    much less results returned by CSE than Google search with "site" restriction - Google プロダクト フォーラム
    https://productforums.google.com/forum/#!topic/customsearch/Ek-feIf-TLQ/discussion

    え?new Google Accountをcreateするって?

    とりあえずアカウントは変えずに検索エンジンを変えた

    細かいところはよくわかりませんが、そういう風に考えてみるのかーと思って、
    同じアカウントで新規に検索エンジンを作成。

    /ki/専用の検索エンジンを作りました。

    うまくいった。一体なんだったんだ。。。
    f:id:aimstogeek:20170929155831p:plain

    他のラベルでは問題なく絞り込みできるので、
    ラベルの設定が悪い、などではなさそうでした。

    まとめ

  • CSEの検索結果がGoogle検索と全然違うよってことがある
  • ラベルで正しく設定していても意図したとおりにならないことがある
  • アカウントとか検索エンジンを新しく作ってみれば解決することもある


    おまけ

    というわけで、看護師のフリー素材コーナーをオープンしました!
    www.kango-roo.com

エンジニアの本音が出てる名言集

エンジニアチームにはインフラを専任とするT親方と呼ばれるエンジニアがいます。
T親方はエンジニアの手本となるような言動が多く、よく名言を残すんですが社内に留めておくのはもったいないのでインターネットで公開したいと思い立ちました。

 

デスクは汚いですが、ちゃんと配置は把握している派です。
matsBです。(๑•̀ㅂ•́)و✧

 

T親方の名言集

PRD環境やDEV環境をどうするかの議論の中での一言。


Dev環境って実態はインフラ的にはDev環境じゃあないですよね、

往々にして

 インフラを携わってたら分かる、あるあるネタの一つ。

短い文章の中から、実体とそれに伴う諦めが滲む、いい言葉です。

 

 

deployジョブのログを見て、一部自動テストが失敗してるのにデプロイ結果が「Finished: SUCCESS」になってるのを見て一言。

うける

 とにかく破壊力がすごい。

作った人を責めるわけでもなく、問題提起をするわけでもない。

けど言われた方はすぐにでも修正したくなるような、いい言葉です。

※このあと、ちゃんと対応してくれました。

 

 

運が悪いと言えるほどレアなバグを踏んでサービス落ちたけど、レアバグなのは過去の負債が原因だったときの一言。

建て増しでシステムが複雑になると表出する現象も複雑になるということですね
(そうやって出来上がったものを影響ないように簡素化するのは難しい。。

全体を見れてないと出せない重い言葉ですね。

負債に対する感情が、驚き→意欲→怒り→諦め→無感情→悟り。と一周半ぐらいして初めて出てくるような、いい言葉です。

 

 

資料化されてない仕様(↑のレアバグの要因)を見つけた時の一言。

未知の既知のバグ

バグがあることは知ってる。資料化されてないのも知ってる。

1年以上携わってるのにまだ知らない仕様を見た時に言える、いい言葉です。

 

 

資料を渡して気になる点を見つけた時の一言。

データが離れてるのが気になりますね。。

気になるだけですぐ忘れるんですが f:id:aimstogeek:20170929190154g:plain

 本質的な事以外は興味がない親方。

指摘はするが解決はしないスタイルをマイルドに表現した、いい言葉です。

 

 

 たまたま見つけた、誰も知らない設定を見つけた時の一連。 

2月9日 11:58
Header set Pragma: "no-cache" env=nocache
こんなのも入ってますね、心の片隅に留めておきましょう。。(忘れる

 

4月14日 18:21

。。忘れてた(宣言通り

 自分で言って自分で忘れる。それを見越した発言。

忘れたことは宣言通りなので、誰もT親方を責めることは出来ない、いい言葉です。

 

 

自分の知らないファイヤーウォールの設定を見た時の一連。

T親方:サイトのdeploy job作るときにサーバにSSH通るようにファイヤーウォール弄りました?

matsB:確かイジったはずです!それもT親方にお願いしてSSH通るようにしてもらった記憶が・・・笑

T親方:忘れていい記憶に振り分けたのかなー f:id:aimstogeek:20170929192122g:plain

 振り分けられてしまったのなら、仕方ない。

自分自身の作業を忘れて他責にしそうになった時に使える、いい言葉です。

 

 

 

T親方の名言の一部を書いてみましたが、いかがでしょうか!

心にグッとくる言葉から、同じような境遇や状況になった時に使える言葉が、あったのではないでしょうか。

是非、T親方の名言を使ってみてください。

【AWS CloudFront + S3】Origin Access Identity を利用した S3 のアクセス制限

こんにちは。クイックSREチームのみっちーです。

前回は、CloudFront+WAFを利用して、開発環境等のIP制限を実装する方法をご紹介しました。
引き続き今日は、S3へのアクセスを「特定のIPアドレス」または「CloudFrontからのみ」許可する設定をご紹介します。

CloudFront+S3のセットで利用する際は、
CloudFrontからのみ、S3へアクセス可能とする場合が多いのではないでしょうか。
ただその場合だと、同一名称でファイルを上げ直した時に、キャッシュが残って困る事があるかと思います。
(この点については、query stringを付けた運用をしているのであれば気にならないと思いますが・・・)

そんなときは、今回ご紹介する方法を試してみて下さい。
S3へ直接アクセスが可能になるため、毎度キャッシュを消さなくても良いことが運用上のポイントです。

なお、今日ご紹介する方法は単独でもいいですし、前回の内容と組み合わせて使うことも可能です。
それではさっそく設定方法を見ていきましょ~ ✧(・ㅂ・)و

前提

  • CloudFront + S3 の構成。

  • 開発者は、S3、CloudFrontの両方へ直接アクセス可能にしたい。

  • ユーザは、CloudFront経由でしか画像にアクセスできないようにしたい。

  • アクセス制限の対象となるCloundFrontとS3は、それぞれ事前に作成済みである。

  • 運用イメージは以下の通り。

    f:id:aimstogeek:20170925190949j:plain

手順概要

必要な手順は大まかに以下の2つです。

1. [AWS CloudFront] Origin Access Identity の設定

2. [AWS S3] IPアドレスベースのbucket policyを追加

手順詳細

1. [AWS CloudFront] Origin Access Identity の設定

AWSコンソールにログインし、「Services」->「CloudFront」をクリックします。

次に、アクセス制限をかけたいS3とセットになっているCloudFront Distributionを選択して、Origins の「Edit」をクリックします。 f:id:aimstogeek:20170926174037j:plain


Origin Settings

AWS CloudFrontからのみアクセスできるように、一意の Access Identity(ID) を追加します。 f:id:aimstogeek:20170926174402j:plain

  • Restrict Bucket Access Yes を選択します。
    ※ ここをYesとすると、これ以降の2項目が表示されるようになります。

  • Origin Access IdentityCreate a New Identity を選択します。
    ※ CloudFrontからのアクセスであることを示す一意のIDです。
    ここでは新規作成していますが、
    他のCloudFront Distributionで作成したIDを使いまわすことも可能です。

  • Grant Read Permissions on BucketYes,Update Bucket Policy を選択します。
    S3 bucket policyに、自動でAccess Identity(ID)を追記してくれます。
    Noを選ぶと、手動追加となり不便です。
    特に理由が無ければYesを選びましょう。


問題なければ、下段のYes Editをクリックして反映させましょう。

なお、設定変更直後はStatusがIn Progressとなり、この間は設定が反映されていません。
しばらく(20~40分程度)して反映完了すると、StatusがDeployedになります。



2. [AWS S3] IPアドレスベースのbucket policyを追加

続いて、S3のbucket policy設定です。

AWSコンソールにログインし、「Services」->「S3」をクリックします。
次に、アクセス制限をかけたいBucketを選択して、
Permissionsの「Bucket Policy」をクリックします。

「CloudFront Origin Access Identity」の bucket policy

この時点で、以下のようにPrincipal: CloudFront Origin Access Identityのpolicyが自動で追加されていると思います。 f:id:aimstogeek:20170928190308j:plain

        {
            "Sid": "2",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <一意のID>"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::test.s3.hoge.com/*"
        },




IPアドレスベースのbucket policy

続いて、特定のIPアドレスからのアクセスを許可するように、ポリシーを追加します。
f:id:aimstogeek:20170928185212j:plain

{
            "Sid": "Allow-from-specific-IP-only",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3::test.s3.hoge.com/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "<許可するIPアドレス/サブネットマスク>"
                    ]
                }
            }
        }

問題なければ、Saveしましょう。設定は即時反映されます。

以上で設定は完了です。
これで、「特定のIPアドレス」または「CloudFrontからのみ」アクセスが許可される形となります。


いかがでしたか。
ちなみに、S3 bucket policyに「CloudFrontのIP & 特定IP」を記載する方法でもアクセス制限の実現は可能です。
ただしこの場合、CloudFrontのIPが動的に変わるため、適当なタイミングでIPリストの更新をする必要があり、少々不便です。

今回ご紹介した方法では、
EffectがAllowどうしのポリシーを複数束ねることで、or条件として使うことができる特性を利用し、IPリストの手動更新を不要としています。
S3 bucket policyは考え方に少々クセがあって、慣れるまでは難しいかなと個人的には感じています。

ということで次回はS3のbucket policy(評価順序等)について書いてみたいと思います。
興味を持って頂けたらぜひまた読んでみて下さい!

それでは~ (・о・) /

この記事のトップへ戻る

【AWS CloudFront】 WAF を利用したアクセス制限を実装する

こんにちは。クイックSREチームのみっちーです。

今日は、弊社で設定しているCloudFrontのIP制限方法をご紹介したいと思います。
開発環境等で「ユーザには見せたくないけど、関係者だけは見れるようにしたい!」と言った話にも、AWS WAFを使えば簡単に対応できます。
お困りの方はぜひ試してみて下さい!

それではさっそく行ってみましょ~ ✧(・ㅂ・)و

前提

  • CloudFrontに対して、IPアドレスによるアクセス制限を行いたい。

  • アクセス制限の対象となるCloudFrontは、事前に作成済みである。

手順概要

必要な手順は大まかに以下の2つです。

  1. [AWS WAF] ACLの作成

  2. [AWS CloudFront] 作成したACLのattach

手順詳細

1. [AWS WAF] ACLの作成

AWSコンソールにログインし、
「Services」->「WAF & Shield」をクリックします。

続いて、AWS WAF」->「Configure web ACLの順に選択して、WAFの設定画面に進みましょう。 f:id:aimstogeek:20170919192659j:plain


Step 1:Name web ACL

ここでは、ACLの名称を定義します。細かい設定はこの後で順番にやっていきます。

f:id:aimstogeek:20170919192710j:plain

  • Web ACL name: 任意のACL名を入力します。
    ※ ここでは、test-ACLとしています。

  • Region「Global(CloudFront)」を選択します。
    ※ ここを間違えると、CloudFrontにattachできなくなります。注意です。

上記以外は特に気にしなくてOKです。次へ進みましょう!


Step 2:Create conditions

続いて、IPaddressのlist設定を行います。
ここで作成したlistを元に、Whitelistにするか、Blacklistにするかを決めていきます。
※ 今回はWhitelistです。

IP match conditions「Create conditions」をクリックします。 f:id:aimstogeek:20170919192805j:plain


すると以下の画面が出てくるので、それぞれ設定を行いましょう。 f:id:aimstogeek:20170919192815j:plain

  • Name: 任意のlist名を入力します。
    ※ ここでは、allow-ip-addressとしています。

  • Address許可したい「IPアドレス」または「範囲」を記載し、「Add IP address or range」をクリックします。
    ※ 記載できるサブネットマスクの範囲は、/8, /16, /24 または、/32 です。
    ※ ここでは、111.123.123.1/32 を設定しています。

  • Filters in IP match conditionに、追加したIPアドレスが表示されていることを確認します。

問題なければ、次へ進みましょう。


Step 3:Create rules

先程作成したIPaddressのlistを元に、Filtering Rules(Whitelistにするか、Blacklistにするか)を決めていきます
※ 今回はWhitelistとして設定します。

まずはCreate ruleをクリックして、「Filtering Rules」を作成します。 f:id:aimstogeek:20170920111415j:plain


f:id:aimstogeek:20170920111651j:plain

  • Name: 任意のFiltering Rules名を入力します。
    ※ ここでは、test-ruleとしています。

  • Add conditions「does」、「originate from an IP address」の順に選択し、
    先程作成したlistの「allow-ip-address」を対象に選択します。


    つぎに、作成したFiltering Rulesを適用する条件を指定します。 f:id:aimstogeek:20170920111107j:plain

  • Add rules to a web ACLRulesで、
    先程作成したFiltering Rules「test-rule」を選択します。

  • Action「Allow」を選択します。
    ※ Filtering Rules(test-rule)にマッチングした場合にどうするか、を定義します。

  • Default action「Block all requests…」を選択します。
    ※ Filtering Rules(test-rule)にマッチングしなかった時にどうするか、を定義します。


終わったらReview and createを押して、最終確認です。
確認して問題なければ作成しましょう。
次はいよいよCloudFrontにACLの設定をattachします!


2. 作成したWAFのACLを、CloudFrontにattachします。

AWSコンソールにログインし、「Services」->「CloudFront」をクリックします。

次に、アクセス制限をかけたいCloudFront Distributionを選択して、
General の「Edit」をクリックします。 f:id:aimstogeek:20170920131255j:plain


Distribution Settings

AWS WAF Web ACLで、先程作成したACL(test-ACL)を選択します。
問題なければ、下段のYes Editをクリックして反映させましょう。

なお、設定変更直後はStatusがIn Progressとなり、この間は設定が反映されていません。
しばらく(20~40分程度)して反映完了すると、StatusがDeployedになります。

アクセス確認

反映完了後(StatusがDeployedになる)、適当なファイルにアクセスしてみましょう。
Whitelistに記載の無いIPアドレスからのアクセスであれば、
下図のようにErrorとなるはずです。

f:id:aimstogeek:20170920131904j:plain




以上で設定は完了です。

いかがでしたか。AWS WAFを使えば、CloundFrontにも簡単に制限が掛けられますね。
不慮の事故を防ぐという意味でも、念のために設定しておいて損は無いのかなと思っています。