私の戦闘力は53万です

awsとgcpについて書きます

AWS cloudtrailの「ログファイルの検証を有効化」の機能を試してみる

cloudtrailの「ログファイルの検証を有効化」の有効化にはするのですが、
機能詳細を確認することがありませんでした。
先日触ってみたので備忘で残したいと思います。

cloudtrailの赤枠部分のファイルを書き換えてみました。

f:id:remmemento:20191130165719p:plain
偽造

Validate-logsを試してみる

上記ファイルを修正し、validateすると書換が発生したログファイル分かります。

aws cloudtrail validate-logs --trail-arn arn:aws:cloudtrail:ap-northeast-1:xxxxxxxxxx:trail/aws217-test --start-time 20190113T19:00:00Z

Validating log files for trail arn:aws:cloudtrail:ap-northeast-1:xxxxxxxxxx:trail/awsxxx-test between 2019-01-13T19:00:00Z and 2019-01-15T03:45:33Z
Log file s3://awsxxx-test-cloudfront-in-test6/test-prefix/AWSLogs/xxxxxxxxxx/CloudTrail/ap-northeast-1/2019/01/15/xxxxxxxxxxx_CloudTrail_ap-northeast-1_20190115T0110Z_eKpIcz4LwCJIlM6d.json.gz INVALID: invalid format
Results requested for 2019-01-13T19:00:00Z to 2019-01-15T03:45:33Z
Results found for 2019-01-13T19:56:11Z to 2019-01-15T01:56:11Z:
31/31 digest files valid
192/193 log files valid, 1/193 log files INVALID

検出可能な項目は下記ドキュメントに一覧化されています。
docs.aws.amazon.com

変更内容までは追えないようなので、
このあたりを追うのであれば下記との組合せが必要そうです。

AWS lambdaからGCPのSDKを使うlambda layerを作成

先日、AWS LambdaからGCPSDKを利用したいと思い、

Layerを固めることがありました。

その際エラーが出てしまい解決までにハマってしまったので、

ブログに書こうと思いました。

エラー内容

固めたzipをlambda Layerとしてアップしたところ

Runtimeエラーとなってしまいました。

実行環境はAmazonLinux2です。

cd /tmp
mkdir python
pip install -t ./python google-cloud-speech
zip -r gcp_speech.zip python
 
{
  "errorMessage": "Unable to import module 'lambda_function': cannot import name 'cygrpc' from 'grpc._cython' (/opt/python/grpc/_cython/__init__.py)",
  "errorType": "Runtime.ImportModuleError"
}
 

Runtimeエラーの対策

 
下記にLambdaのランタイムが記載されているので、同じEC2でpipを固めてみました。

Lambda RuntimeのEC2(AmazonLinux)でpython3系を設定

下記を参考にさせて頂き設定してみました。
実際に実行したコマンドは下記です。
 
sudo yum install gcc gcc-c++ make git openssl-devel bzip2-devel zlib-devel readline-devel sqlite-devel
sudo git clone https://github.com/yyuu/pyenv.git /usr/bin/.pyenv
cd /usr/bin/.pyenv
sudo mkdir shims
sudo mkdir versions
sudo chown -R ec2-user:ec2-user /usr/bin/.pyenv
 
vi ~/.bashrc
#下記を設定
export PYENV_ROOT="/usr/bin/.pyenv"
if [ -d "${PYENV_ROOT}" ]; then
    export PATH=${PYENV_ROOT}/bin:$PATH
    eval "$(pyenv init -)"
fi
source ~/.bashrc
 
pyenv install --list
pyenv install 3.7.4
pyenv global 3.7.4
pyenv versions
 
#3.7系だとエラーが出るので下記も実施しました
sudo yum install libffi-devel
 
 
このインスタンス上でLayerを固め直します。
cd /tmp
mkdir python
pip install -t ./python google-cloud-speech
zip -r gcp_speech.zip python
 
上記でレイヤーを作成するとimportでエラーが出なくなりました!
Layerを固める際には、同一の環境のEC2上で固めるのは必須ですね。
忘れないようにしたいと思います。
 

AWS WAF 海外IPを拒否しGoogleのクローラ(bot)は許可する設定

AWS WAF 海外IPを拒否しGoogleのクローラ(bot)は許可する設定」を
することがありましたので設定時のメモとして書きます。
 

Googleのクローラの条件

まずは通すべき条件を調べました。
色々と細かい条件はあるようです。今回の私の要件は
User-Agentヘッダに「Googlebot」が含まれていれば
Googleのクローラとして見なしてOKだったので
そちらをGoogleクローラの条件としました。
 

海外IPアクセスの条件

AWS WAFはIPを国別で判断して許可・拒否することが可能です。
こちらはデフォルトの機能を利用します。

AWS WAFを設定 

 まず1つ目のCondition作成として「string and regex match」を選択します

f:id:remmemento:20191130144740p:plain



「Googlebot」をuser-agentヘッダに含む場合の条件を作成します。

f:id:remmemento:20191130144036p:plain

 

 

f:id:remmemento:20191130144230p:plain

上記のように入力しAdd filterボタンを押します。

 

 

 

f:id:remmemento:20191130144241p:plain

Filterに追加されたらCreateボタンを押します。

 

 

 

条件作成2個目

2つ目の条件は国別の制限をします。Geo matchを選択します。

f:id:remmemento:20191130144918p:plain

 

日本を登録します 

f:id:remmemento:20191130145158p:plain

ルール作成

上記で作成した条件を元にルールを作成します

f:id:remmemento:20191130145316p:plain

 

上記で作成した条件を設定します。

この時、WAFのデフォルトルールを

Allowにしたかったので下記のようにルールを設定しています。

f:id:remmemento:20191130143352p:plain

上記の意味が少し分かりにくいと思いますので掘り下げます。

条件の整理

私は条件の組合せを考えるときは
頭の中が混乱するので、極力ド・モルガンの法則をベースに考えています。
もっと良い方法があれば知りたいのですが、
こんな方法あるよ!という方いたらぜひコメントください。
 
 
例えば下記のように考えます。
A=「User-agentにGooglebotを含む」
B=「日本からのアクセス」
上記のルールはを意味します。
 
=「User-agentにGooglebotを含まない」かつ「日本からのアクセスでない」
=「User-agentにGooglebotを含まない」かつ「海外アクセスである」
となります。 
 
デフォルトの設定がAllow/Denyにより設定内容が異なるのですが、
今回はデフォルトをAllow(許可)にしています。
 
上記設定は下記のように動作します。
  • 「User-agentにGooglebotを含まない」かつ「海外アクセスである」場合ブロック
  • それ以外はAllow(許可)

 ド・モルガン風に考える

Blockの挙動を否定と置き換えて考えると
の否定(Block)なので、
ド・モルガンの法則の両辺を否定してみます。
すると、f:id:remmemento:20191130150433p:plainの否定はとイコールであることが導かれます。
は「User-agentにGooglebotを含む」または「日本からのアクセス」
許可するを意味していますので、意図した設定であることが分かります。

検証してみる

上記で設定したWAFを適当なCloudFrontに設定してアクセス確認してみます。
#国内IP(xx.xx.xx.xx)からかつuser-agentにGooglebotが含まれないアクセス → アクセス成功
<!DOCTYPE html>
<html lang="ja">
・・・・・・・・・・・・
#国内IP(xx.xx.xx.xx)からかつuser-agentにGooglebotが含まれるアクセス → アクセス成功
<!DOCTYPE html>
<html lang="ja">
・・・・・・・・・・・・
 
 
#whois yy.yy.yy.yy |grep City
City:           Seattle
City:           Singapore
 
#海外IP(yy.yy.yy.yy)からかつuser-agentにGooglebotが含まないアクセス → アクセス失敗
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>ERROR: The request could not be satisfied</TITLE>
</HEAD><BODY>
<H1>403 ERROR</H1>
<H2>The request could not be satisfied.</H2>
<HR noshade size="1px">
Request blocked.
We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
<BR clear="all">
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
<BR clear="all">
<HR noshade size="1px">
<PRE>
Generated by cloudfront (CloudFront)
Request ID: OLTvH3ay52g-1i32HRmRi-XBxlGINEvGdBF5HGgUHt14Qe62zYIb6A==
</PRE>
<ADDRESS>
</ADDRESS>
</BODY></HTML>
 
 
#海外IP(yy.yy.yy.yy)からかつuser-agentにGooglebotが含まれるアクセス → アクセス成功
<!DOCTYPE html>
<html lang="ja">
・・・・・・・・・・・・
 上記から、
「海外IP(yy.yy.yy.yy)から」かつ「user-agentにGooglebotが含まない」アクセスのみが
拒否されていることが確認できました。

注意点

User-Agentのヘッダは上記の検証で実施しているように
なりすましが可能です。
そのため、より厳密に設定したい場合は、
例えば下記のような設定が追加で必要になってくるのかと思いました。
 
  1. WAFログを定期的に解析しアクセス元IPをDNSリバースルックアップ[※1]を実施
  2. なりすましのアクセス元IPを判定する
  3. 2のIPをWAFのIP拒否リストに設定する
[※1]
 
機会があれば試してみたいと思います!
 

AWS KMS の概要とエンベロープ暗号化について説明してみる

最近AWS KMSについて聞かれることがあったので

ブログにまとめてみようと思います。 

KMSってなに?をざっくり

  • 対象鍵を安全に保管してくれるサービス
  • AWSサービスのデータ暗号化・複合化する機能として連携(S3、EBS等)
  • うるさいセキュリティのポイントをある程度抑えてくれる(キーローテーション、暗号化鍵の方式を決定)
 
記事執筆時点で非対称の鍵もサポートが開始されたようです。
まだ内容がわからないので今回は対象鍵の部分について書きます! 

前提知識:対称鍵とは

対称鍵というのは暗号化と復号化の時の鍵が同じことを指します。
身近な例では、ファイルをzip暗号化でパスワードを利用するケースです。
暗号化する時と、復号化する時で同じ鍵(パスワード)を指定しますね。
もっと身近な例だと家の鍵です。
鍵をしめる(暗号化)時と、鍵を開ける(複合化)するときで
同じ家の鍵を使いますね。
 

キーの種類

1つ目の混乱ポイントです。
KMSでは鍵の種類がたくさん出てきます。
私は初見に結構混乱しました。
鍵の全体像を整理すると下記のようになります。
kms/
├── cmk
│   ├── AWS所有
│   ├── AWS管理
│   └── カスタマー管理
│       ├── aws_make
│       └── origin_external
└── データキー

下記に簡単な説明を書きます。

AWS所有

  見ることすらできない鍵です。AWSが利用します。

  基本的に触れないので理解すべきこともないです。

AWS管理

 もっともよく使う鍵です。

 おおよそのケースで単純に暗号化したいだけならこの鍵を利用します。

    デフォルトの鍵です。aws/service-nameの形式で存在します

 ある程度AWSが用意した枠組みでしか管理ができないため

 細かい制御が必要な場合は後述のカスタマー管理のキーを利用します。

 例えば、クロスアカウントで共有が不可のため、

 複数アカウントが関連するときは利用できないケースが多いです

・カスタマー管理

 AWSで作れる鍵です。この鍵だとキーポリシー(アクセス制限)とか、
 手動のキーローテーションとか、有効無効の操作の自由度が高くなります。
 また、クロスアカウントでの利用にもこちらを用いることが多いです。
 カスタマー管理キーは2種類に分かれます。
 インポートされたキーマテリアル(origin external)と
 awsで作成する鍵(aws make)です。
 基本的にはawsで作成する鍵を利用しますが、
 暗号化方式まで厳密に管理したい、
 自分たちで自作した鍵を利用したいという場合には
 インポートされたキーマテリアル(origin external)を利用します。

・データキー

CMKを元に作成するキーのことです。
データキーがなぜ必要なのか?と思う人もいると思います。
これに対する答えとしてまず、
エンベロープ暗号化の概念を理解する必要があります。
 
AWS KMSで管理するキー(CMK)では暗号化の対象データ容量の上限が
32KB(記事執筆時点)とかなり小さいです。
例えばこの制限で、容量の小さいデータを暗号化するのであれば良いのですが、
基本的にはEBSやS3内のファイルも32KBを上回ることが想定されるため
管理ができません。
 
そこで、下記例に習いファイル容量に依存しない暗号化管理を実現します。
この考え方がエンベロープ暗号化です。
(1)CMK(MasterKey)からDataKeyを作成する。
(2)Datakeyを鍵として32KBよりも大きいデータ(EBS等)を暗号/複合化する
通常の運用では、このデータキーは利用時にはPlaintext(複合化された状態)で利用し、
利用が終わったら捨てます。そして暗号化されたDatakeyは取っておきます。

上記のように運用することで下記を実現できます

・容量大データも一律して同じ大きさのDatakeyを利用し暗号/複合管理

・外部に漏れるとマズイ情報(Plaintext data key)は必要な時のみ取出し利用 

https://docs.aws.amazon.com/ja_jp/kms/latest/developerguide/concepts.html#enveloping

KMSのAPIにも、キーを生成するAPIが2つあります。

kms:CreateKey
kms:GenerateDataKey
 
上記は考え方ですので、実際にCLIを使って動作させてみます。
一度実行してみると頭の中で理解しやすいのでおすすめです。

エンベロープ暗号化をCLIで一通りこなしてみる

まずはKMSを生成します

f:id:remmemento:20191127100736p:plain

create-key
aws kms create-key
{
   "KeyMetadata": {
       "AWSAccountId": "xxxxxxxxxxxx",
       "KeyId": "08e5af4f-ba9c-4f97-b6ec-xxxxxxxxx",
       "Arn": "arn:aws:kms:ap-northeast-1:xxxxxxxxxxxx:key/08e5af4f-ba9c-4f97-b6ec-2425af102c37",
       "CreationDate": 1574814784.429,
       "Enabled": true,
       "Description": "",
       "KeyUsage": "ENCRYPT_DECRYPT",
       "KeyState": "Enabled",
       "Origin": "AWS_KMS",
       "KeyManager": "CUSTOMER"
   }
}
 
キーIDだと分かりにくいので、名前(エイリアス)をつけてあげます 
aws kms create-alias --alias-name alias/test-key-alias --target-key-id 08e5af4f-ba9c-4f97-b6ec-xxxxxxxxx
 
続いてCMKからデータキーを作成します。
 

f:id:remmemento:20191127100812p:plain

data-key
 
 
aws kms generate-data-key --key-id alias/test-key-alias  --key-spec AES_256
{
   "CiphertextBlob": "AQIDAHgUOG52kcVFzz8bdP3olHhXcKqCGSU2gEsFuwNubXG8cwFCIZXzwNgvlmH5UznwhMFTAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMck1KFok17ktpT6jkAgEQgDu+39yXM9ajyk3YiIWbeiqaHnxDRqR8Jfl8BZedHyjpy+v4BK4FlIMSuEc0Xmpkjuau8M7qNu4Yim2ndQ==",
   "Plaintext": "KWDD1Gs6L7qQFllFexj8PmbHEpjxABVsvZ7sGv40ENc=",
   "KeyId": "arn:aws:kms:ap-northeast-1:xxxxxxxx:key/08e5af4f-ba9c-4f97-b6ec-xxxxxxxx"
}
 
「CiphertextBlob」は「Plaintext」を暗号化した情報のことです。「CiphertextBlob」を利用して複合化してみましょう。
 
aws kms decrypt --ciphertext-blob fileb://<(echo 'AQIDAHgUOG52kcVFzz8bdP3olHhXcKqCGSU2gEsFuwNubXG8cwFCIZXzwNgvlmH5UznwhMFTAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMck1KFok17ktpT6jkAgEQgDu+39yXM9ajyk3YiIWbeiqaHnxDRqR8Jfl8BZedHyjpy+v4BK4FlIMSuEc0Xmpkjuau8M7qNu4Yim2ndQ=='|base64 -D)
{
    "KeyId": "arn:aws:kms:ap-northeast-1:xxxxxxxx:key/08e5af4f-ba9c-4f97-b6ec-xxxxxxxx",
    "Plaintext": "KWDD1Gs6L7qQFllFexj8PmbHEpjxABVsvZ7sGv40ENc="
}
上記のPlaintext」と一致することがわかりますね。 さて、このデータキーの「Plaintext」を利用してさらにデータを暗号化します。

f:id:remmemento:20191127101005p:plain

データの暗号化
上記の例だとデータキーは「KWDD1Gs6L7qQFllFexj8PmbHEpjxABVsvZ7sGv40ENc=」です。
例えばzipとかの暗号化パスワードに利用するイメージです。
AWSサービスで利用した場合だとEBSやS3のデータの暗号化時に
上記の「Plaintext」部分が利用されます。
 データの暗号化が終わったら Plaintext(複合化された素の状態)の
キー情報は捨てます。Plaintextが外部に漏洩するとデータを
複合化される可能性があるためセキュリティ事故になってしまいます。

f:id:remmemento:20191127100716p:plain

データキーの管理
一方暗号化されたキーは(ローカルとかに)保存しておきます。
この暗号化されたキーは仮に外部に漏洩したとしても、CMKを利用できない限りは、データを複合化されることがないため実被害はありません。
(とはいえ漏洩しないに越したことはないです) 
その後は暗号化されたキー(CiphertextBlob)を保管しておき、
またデータの暗号化/複合化をしたい時には上記で実施したように
一時的にデータキーを複合化(Palintextを取出)して利用します。
aws kms decrypt --ciphertext-blob fileb://<(echo 'AQIDAHgUOG52kcVFzz8bdP3olHhXcKqCGSU2gEsFuwNubXG8cwFCIZXzwNgvlmH5UznwhMFTAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMck1KFok17ktpT6jkAgEQgDu+39yXM9ajyk3YiIWbeiqaHnxDRqR8Jfl8BZedHyjpy+v4BK4FlIMSuEc0Xmpkjuau8M7qNu4Yim2ndQ=='|base64 -D)
{
    "KeyId": "arn:aws:kms:ap-northeast-1:xxxxxxxx:key/08e5af4f-ba9c-4f97-b6ec-xxxxxxxx",
    "Plaintext": "KWDD1Gs6L7qQFllFexj8PmbHEpjxABVsvZ7sGv40ENc="
}
 
以上がデータの管理の流れとなります。
 
下記ポイントが抑えられていることが確認できたと思います。
・容量大データも一律して同じ大きさのDatakeyを利用し暗号/複合管理
・外部に漏れるとマズイ情報(Plaintext data key)は必要な時のみ取出し利用  

 

長くなってしまうので、残りの機能はまた次回以降で書きたいと思います。
 

AWS S3のオブジェクトを時間指定でパブリック公開する

S3のホスティングを利用している場合で、
オブジェクトを特定時間に公開したいと思うことがありました。
その際、S3のバケットポリシーを利用して公開時間を
設定することが可能でしたので備忘で記事にします。

検証内容

バケットポリシーの時間を更新する
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AddPerm",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<bucket_name>/*",
            "Condition": {
                "DateGreaterThan": {
                    "aws:CurrentTime": "2019-10-24T03:00:00Z"
                },
                "DateLessThan": {
                    "aws:CurrentTime": "2019-10-30T15:00:00Z"
                }
            }
        }
    ]
}
 
上記で許可指定した時間内はアクセスが可能なことを確認
date --utc
2019年 10月 24日 木曜日 03:41:37 UTC
 
<!doctype html>
<html class="no-js" lang="">
<head>
  <meta charset="utf-8">
  ....................
  
バケットポリシーの時間を許可外の時間となるよう変更する
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AddPerm",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<bucket_name>/*",
            "Condition": {
                "DateGreaterThan": {
                    "aws:CurrentTime": "2019-10-24T04:00:00Z"
                },
                "DateLessThan": {
                    "aws:CurrentTime": "2019-10-30T15:00:00Z"
                }
            }
        }
    ]
}
 
時間外はアクセスが拒否されることを確認
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>8CBC499D293CCE35</RequestId><HostId>yDbZevag/0yexwkf1FZZEsSNrGeh2d0fQFtxnG1dOINeEafDSAWOdgi9CypUybOlJA2ezhNjmMo=</HostId></Error>[ec2-user@ip-192-168-0-212 ~]$
 
誰かの参考になれば幸いです。
 
 

AWS:VPCピアリングでうまく接続できない時に確認したいこと

VPCピアリングでうまく接続できない時に確認したいことを、自分でもたまに忘れそうなのでメモがてら書きます。

セキュリティグループ、ルートテーブル、NACL

 この辺りは当然ですね。丁寧に確認しましょう。

OSポート許可(iptables,firewall等)

 windowsの場合だとパーソナルファイアウォールを一時的に無効化すると手っ取り早いです。

名前解決ができるかどうか

 RDS等の接続ができない場合は、こちらが怪しいです。
https://docs.aws.amazon.com/ja_jp/vpc/latest/peering/modify-peering-connections.html#vpc-peering-dns

GCP:限定公開アクセスとは何なのか試してみる

限定公開アクセス の機能概要

GCPのリソースへアクセスする際、
VPC内部のリソース(GCEとか)がインターネット回線でなく、
Googleの内部ネットワークを通じてアクセスできる機能のことです。
(下記図の吹き出し部分のイメージです)
 
AWSの人に向けて言うと、AWSVPCエンドポイント的なことです。
 
 
 

アクセスパターン

上記の図でSubnet2のGCEの各種条件を変えて、
それぞれのアクセスの可/不可を試してみました。 
No 条件1 条件2 条件3 外部アクセス
(yahoo.co.jp等)
GCP API へのアクセス GCP APIへの
private access
1 internetルート
(to 0.0.0.0/0)有
GCEへ外部IP付与有 限定公開アクセス有効 可能とのこと
googleの人へ確認)
2 internetルート
(to 0.0.0.0/0)有
GCEへ外部IP付与有 限定公開アクセス無効 不可
3 internetルート
(to 0.0.0.0/0)有
GCEへ外部IP付与無 限定公開アクセス有効 不可
4 internetルート
(to 0.0.0.0/0)有
GCEへ外部IP付与無 限定公開アクセス無効 不可 不可 不可
5 internetルート
(to 0.0.0.0/0)無
GCEへ外部IP付与有 限定公開アクセス有効 不可 不可 不可
6 internetルート
(to 0.0.0.0/0)無
GCEへ外部IP付与有 限定公開アクセス無効 不可 不可 不可
7 internetルート
(to 0.0.0.0/0)無
GCEへ外部IP付与無 限定公開アクセス有効 不可 不可 不可
8 internetルート
(to 0.0.0.0/0)無
GCEへ外部IP付与無 限定公開アクセス無効 不可 不可 不可
 
注意点はインターネットアウトのルーティングがない状態では限定公開アクセスが通用しないことです。
限定公開の Google アクセスを使用すると、内部(プライベート)IP アドレスしか持たない(外部 IP アドレスを持たない)VM インスタンスが、Google API とサービスのパブリック IP アドレスにアクセスできます。限定公開の Google アクセスは、サブネット レベルで有効にします。そうすると、サブネット内でプライベート IP アドレスしか持たないインスタンスが、デフォルト ルート(0.0.0.0/0)を通じて Google API とサービスにトラフィックを送信できるようになります。このとき、デフォルト インターネット ゲートウェイへのネクストホップを使用します
 

結論

上記の結果から 、ONにすると、GCPへのアクセスをセキュアかつ早く実現してくれるため基本ONにしておくのが良さそうです。