はじめに
閲覧ありがとうございます、NTTデータ先端技術の@S-takahashi1129です。
業務でカスタムGuardを使いAWS Configのカスタムポリシーを作成する機会があったのですが、日本語の解説資料がネット上にあまりなかったため、備忘録を兼ねて自分なりにポイントをまとめてみます。
今回は最も基本的な部分の解説のみ行い、次回以降で複雑な条件指定の方法や詰まりそうな部分について書く予定です。
AWS Config とは
AWS Config(以下Config) はリソースの構成を評価、監査するサービスです。
AWS上に存在しているリソースの構成情報を記録して、構成がポリシーに準拠しているかを継続的に評価してくれます。
Config ルール
Configルールは、必要な構成設定を表します。Configは、記録したリソース構成情報をもとにリソース設定が該当するルールに準拠しているかどうかを評価し、準拠結果の概要を示します。
ルールは大別してAWS提供のマネージドルールとユーザ作成のカスタムルールがあります。
カスタムルールはさらにカスタムLambdaルールとカスタムGuardルールに分かれます。
AWS マネージド型ルール
AWSが提供しているルールで、有効化するだけで簡単にリソースの非準拠構成を検知することができます。
例えば、ec2-instance-no-public-ipというルールがあります。
このルールはEC2にパブリックIPが存在していると非準拠になると説明があります。
ルールを有効化した状態でパブリックIPが存在するEC2インスタンスを作成してみます。
作成後、少しすると構成変更をトリガーとしてルールの評価が行われ、先ほど作成したec2インスタンスが非準拠として検知されます。
コンソールでConfigを開き、該当のルールを確認します。
カスタムルール
マネージドルールは割と豊富に用意されていますが、ルールによって使えるリージョンが限られていたりしますし、要件によってはマネージドルールでカバーしきれていない部分のリソース構成を検査したい場合もあるかと思います。
そういった場合はカスタムルールを作成することになります。
ルール追加の際にLambda、あるいはGuardを使用してカスタムルールを作成することを選ぶことが可能です。
本記事ではGuardを使用したカスタムルールについていくつかポイントを解説します。
カスタムGuardルールの基本的な書き方
カスタムGuardルールの基本的な記述方法について、ルートテーブルを検査するルールを作成しながら説明していきます。
今回は例として、
ルートテーブルのルートの送信先に「0.0.0.0/0」が指定されている場合非準拠
というルールを作成します。 以下のような状態が非準拠として検知されれば成功です。
1.リソース構成情報を確認する
まずはConfigが記録したリソース情報を確認します。
マネコンからConfigのページを開き、左側のペインからリソースを選びます。
リソースタイプなどで絞り込めますので、ルートテーブルのリソース情報を探します。
リソース情報を発見できたら、設定項目(JSON)の表示 からConfigに対応しているプロパティが確認できます。
JSONを表示すると次のような情報を確認できます。
{
"version": "1.3",
"accountId": "XXXXXXXXXXXX",
"configurationItemCaptureTime": "2024-03-26T06:09:53.828Z",
"configurationItemStatus": "OK",
"configurationStateId": "1711433393828",
"configurationItemMD5Hash": "",
"arn": "arn:aws:ec2:ap-northeast-1:XXXXXXXXXXXX:route-table/rtb-086b32f151d638b13",
"resourceType": "AWS::EC2::RouteTable",
"resourceId": "rtb-086b32f151d638b13",
"awsRegion": "ap-northeast-1",
"availabilityZone": "Not Applicable",
"tags": {},
"relatedEvents": [],
"relationships": [
{
"resourceType": "AWS::EC2::VPC",
"resourceId": "vpc-YYYYYYYYYYYY",
"relationshipName": "Is contained in Vpc"
}
],
"configuration": {
"associations": [
{
"main": true,
"routeTableAssociationId": "rtbassoc-0f26621a8967f489d",
"routeTableId": "rtb-086b32f151d638b13",
"subnetId": null,
"gatewayId": null,
"associationState": {
"state": "associated",
"statusMessage": null
}
}
],
"propagatingVgws": [],
"routeTableId": "rtb-086b32f151d638b13",
"routes": [
{
"destinationCidrBlock": "172.31.0.0/16",
"destinationIpv6CidrBlock": null,
"destinationPrefixListId": null,
"egressOnlyInternetGatewayId": null,
"gatewayId": "local",
"instanceId": null,
"instanceOwnerId": null,
"natGatewayId": null,
"transitGatewayId": null,
"localGatewayId": null,
"carrierGatewayId": null,
"networkInterfaceId": null,
"origin": "CreateRouteTable",
"state": "active",
"vpcPeeringConnectionId": null,
"coreNetworkArn": null
},
{
"destinationCidrBlock": "0.0.0.0/0",
"destinationIpv6CidrBlock": null,
"destinationPrefixListId": null,
"egressOnlyInternetGatewayId": null,
"gatewayId": "igw-09cc8f18a92992b1b",
"instanceId": null,
"instanceOwnerId": null,
"natGatewayId": null,
"transitGatewayId": null,
"localGatewayId": null,
"carrierGatewayId": null,
"networkInterfaceId": null,
"origin": "CreateRoute",
"state": "active",
"vpcPeeringConnectionId": null,
"coreNetworkArn": null
}
],
"tags": [],
"vpcId": "vpc-YYYYYYYYYYYYYY",
"ownerId": "XXXXXXXXXXXXX"
},
"supplementaryConfiguration": {}
}
このプロパティから、今回検知したい部分に関連がありそうな部分を探します。
今回はルートの送信先が検知対象なので、
"routes": [
{
"destinationCidrBlock": "172.31.0.0/16",
"destinationIpv6CidrBlock": null,
"destinationPrefixListId": null,
"egressOnlyInternetGatewayId": null,
"gatewayId": "local",
"instanceId": null,
"instanceOwnerId": null,
"natGatewayId": null,
"transitGatewayId": null,
"localGatewayId": null,
"carrierGatewayId": null,
"networkInterfaceId": null,
"origin": "CreateRouteTable",
"state": "active",
"vpcPeeringConnectionId": null,
"coreNetworkArn": null
},
{
"destinationCidrBlock": "0.0.0.0/0",
"destinationIpv6CidrBlock": null,
"destinationPrefixListId": null,
"egressOnlyInternetGatewayId": null,
"gatewayId": "igw-09cc8f18a92992b1b",
"instanceId": null,
"instanceOwnerId": null,
"natGatewayId": null,
"transitGatewayId": null,
"localGatewayId": null,
"carrierGatewayId": null,
"networkInterfaceId": null,
"origin": "CreateRoute",
"state": "active",
"vpcPeeringConnectionId": null,
"coreNetworkArn": null
}
],
このあたりのroutesブロック内の、destinationCidrBlockの値が検知したい部分の値に対応していそうです。
2.Guardを記述する
次はカスタムルールに内容を記述していきます。
Configルールの追加からカスタムGuardルールを選択し、ルールの設定へ進みます
- プロパティの書き方
カスタムGuardルールは基本的にプロパティが準拠の状態を記述していきます。
今回はルートテーブルのルートの送信先に「0.0.0.0/0」が指定されている場合非準拠とするということが目的なので、Guardで記述すべき内容は、
ルートの送信先を示すプロパティdestinationCidrBlockが「0.0.0.0/0」ではない
となります。
前述した条件をGuardで記述すると次のようなコードになります。
configuration.routes[*].destinationCidrBlock != "0.0.0.0/0"
configuration.routes[*].destinationCidrBlockがプロパティの指定です。
先ほど確認したリソース情報のJSONを見ると、プロパティの中に更にプロパティがあるものを確認できます。今回使用したいdestinationCidrBlockプロパティは、configurationプロパティの中のroutesというプロパティに含まれるものだと分かります。
また、routesプロパティは配列になっているのでroutes[参照したい項番] のように指定します。今回はルートテーブル内の全てのルートを確認し、一つでも「0.0.0.0/0」という送信先があれば非準拠としたいため、ワイルドカード[*]を記述しています。
- 条件指定の書き方
単項演算子(exists,empty)や二項演算子(==,!=,>,>=,<,<=)が使用可能です。
今回はプロパティが「"0.0.0.0/0"」でなければよいので
!= "0.0.0.0/0"
を記述しています。今回のように、非準拠として検知したい状態(今回は0.0.0.0/0がある状態)を実際のリソースで作ってリソース情報を取得すれば、コピペで非準拠状態の数値が表現できるので簡単かと思います。
実際にコンソール上でコードを入力します。プロパティ名の補完などをある程度してくれるのでブラウザのみでも割と快適にコーディングできます。
3.評価モードの設定
評価モードの設定項目で、ルールのチェックがいつ行われるかを設定することができます。
トリガータイプですが、カスタムGuardルールでは設定変更時のみトリガーにすることが可能です。
マネージド型ルールでは定期的にルールが実行されるものもありますが、2024年3月現在ではカスタムルール非対応です。
リソースの設定項目では、どのリソースの変更をトリガーとしてルールが動くかを設定できます。基本的にはルールで検知したいリソースを指定することになるかと思います。
今回はAWS EC2 RouteTableを指定しています。
4.ルール作成
パラメータやルールタグについては必要に応じて設定します。
次へを押すことで確認画面へ移行します。この際、Guard文のエラーがあると教えてくれますので適宜修正します。
5.ルール確認
ルールの作成に成功したら、該当ルールを確認します。
基本的に作成時やルール編集時、対象リソースの構成時にルールの評価が実行されます。
作成直後は検出結果がでないこともあるため少し待つ必要があります。
右上のアクションから再評価を実行すると、能動的に評価を実行することも可能です。
想定した通りに「0.0.0.0/0」が設定されているルートテーブルが非準拠として検知されました。
確認のため、該当ルートテーブルの設定を変えた場合も確認します。
非準拠として検知されているルートテーブルを編集し、「0.0.0.0/0」となっている部分を変更します。
変更後、暫く待ってから先ほど作ったルールを確認し、コンプライアンスが準拠となっていることを確認します。
おわりに
今回はルールを作る際に必要なプロパティの見つけ方についてと、最も基本的なGuardの記述方法について自分なりにまとめました。
今回のルートテーブルの例のように、単純に値をチェックするだけならば同じ要領ですぐに実装可能かと思います。
次回以降、カスタムGuardルールの仕様や複雑な条件指定、変数の利用などの個人的に初見で苦労した内容についてまとめる予定ですので、より実践的な内容となるかと思います。
以上、閲覧ありがとうございました。