GitHub Enterprise Cloudの監査ログストリーミングに必要なAWSリソースをCloudFormationで作成する

## はじめに
GitHub Enteprise Cloud の監査ログは各種クラウドプロバイダーへのストリーミングをサポートしています。

https://docs.github.com/en/enterprise-cloud@latest/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/streaming-the-audit-log-for-your-enterprise

Amazon S3 に監査ログをストリーミングする場合、OpenID Connect による接続が可能です。AWS 側にはログ保管用の S3 バケットの他、OIDC Provider と IAM ロールを作成する必要があります。細かな手順は上記ドキュメントを参照ください。

用途的に何度も作成するものではないですが、CloudFormation テンプレート化したので、新規にサクッと作成したい方のお役に立てれば幸いです。

## 作成されるリソース
| 論理 ID | タイプ | 用途 |
| --- | --- | --- |
| AuditLogBucket | AWS::S3::Bucket | 監査ログ保管用の S3 バケット |
| AuditLogbucketPolicy | AWS::S3::BucketPolicy | 監査ログ保管用の S3 バケットポリシー |
| AccessLogBucket | AWS::S3::Bucket | サーバーアクセスログ用の S3 バケット |
| AccessLogBucketPolicy | AWS::S3::BucketPolicy | サーバーアクセスログ用の S3 バケットポリシー 
| AuditLogEncryptionKey | AWS::KMS::Key | 監査ログ保管用 S3 バケットのデフォルト暗号化用 KMS キー | 
| AuditLogEncryptionKeyAlias | AWS::KMS::Alias | 監査ログ保管用 S3 バケットのデフォルト暗号化用 KMS キーエイリアス |
| AuditLogStremingPolicy | AWS::IAM::ManagedPolicy | 監査ログストリーミング用 IAM ポリシー |
| AuditLogStreamingRole | AWS::IAM::Role | 監査ログストリーミング用 IAM Role |
| GithubAuditLogOIDCProvider | AWS::IAM::OIDCProvider | GitHub OIDC provider |


## CloudFormation テンプレート
GitHub リポジトリはこちら

https://github.com/hayao-k/CFn-github-audit-log-streaming

```template.yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: Create AWS resources required for GitHub Enterprise Cloud audit log streaming

Parameters:
  RetainDays:
    Description: Number of days to keep audit log.
    Type: String
    Default: 365
  EnterpriseName:
    Description: Your GitHub Enterprise Name.
    Type: String
    Default: EXAPMLECORP
  AccessLogPrefix:
    Description: Server access log-prefix of audit log bucket.
    Type: String
    Default: logs/

Metadata: 
  AWS::CloudFormation::Interface: 
    ParameterGroups: 
      - Label: 
          default: GitHub Configuration
        Parameters: 
          - EnterpriseName
      - Label: 
          default: Audit Log Configuration
        Parameters: 
          - RetainDays
          - AccessLogPrefix

Resources:
  # Customer Managed Key for audit log encryption
  AuditLogEncryptionKey:
    Type: AWS::KMS::Key
    Properties: 
      Description: Customer Managed Key for audit log encryption
      Enabled: true
      EnableKeyRotation: True
      KeyPolicy:
        Version: '2012-10-17'
        Id: key-default-1
        Statement:
        - Sid: Allow administration of the key
          Effect: Allow
          Principal:
            AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
          Action:
          - kms:Create*
          - kms:Describe*
          - kms:Enable*
          - kms:List*
          - kms:Put*
          - kms:Update*
          - kms:Revoke*
          - kms:Disable*
          - kms:Get*
          - kms:Delete*
          - kms:ScheduleKeyDeletion
          - kms:CancelKeyDeletion
          Resource: '*'
        - Sid: Allow local use of the key
          Effect: Allow
          Principal:
            AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource: '*'

  # Customer Managed Key ALias
  AuditLogEncryptionKeyAlias:
    Type: AWS::KMS::Alias
    Properties: 
      AliasName: !Sub alias/${AWS::StackName}-key
      TargetKeyId: !Ref AuditLogEncryptionKey
  
  # S3 bucket for audit log
  AuditLogBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain
    UpdateReplacePolicy: Retain
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration: 
          - ServerSideEncryptionByDefault: 
              SSEAlgorithm: aws:kms
              KMSMasterKeyID: !GetAtt AuditLogEncryptionKey.Arn
            BucketKeyEnabled: true    
      LifecycleConfiguration:
        Rules:
          - Id: Retain
            Status: Enabled
            ExpirationInDays: !Ref RetainDays
      PublicAccessBlockConfiguration: 
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      LoggingConfiguration:
        DestinationBucketName: !Ref AccessLogBucket
        LogFilePrefix: !Ref AccessLogPrefix
      VersioningConfiguration:
        Status: Enabled
      OwnershipControls:
        Rules:
          - ObjectOwnership: BucketOwnerEnforced

  # S3 bucket policy for audit log
  AudtiLogBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref AuditLogBucket
      PolicyDocument:
        Statement:
        - Effect: Deny
          Principal: '*'
          Action: 's3:*'
          Resource: 
            - !Sub ${AuditLogBucket.Arn}/*
            - !GetAtt AuditLogBucket.Arn
          Condition:
            Bool: 
              aws:SecureTransport: false
  
  # S3 bucket for server access log
  AccessLogBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain
    UpdateReplacePolicy: Retain
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration: 
          - ServerSideEncryptionByDefault: 
              SSEAlgorithm: AES256
      LifecycleConfiguration:
        Rules:
          - Id: Retain
            Status: Enabled
            ExpirationInDays: !Ref RetainDays
      PublicAccessBlockConfiguration: 
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      OwnershipControls:
        Rules:
          - ObjectOwnership: BucketOwnerEnforced

  # S3 bucket policy for server access log
  AccessLogBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref AccessLogBucket
      PolicyDocument:
        Statement:
          - Effect: Deny
            Principal: '*'
            Action: 's3:*'
            Resource: 
              - !Sub ${AccessLogBucket.Arn}/*
              - !GetAtt AccessLogBucket.Arn
            Condition:
              Bool: 
                aws:SecureTransport: false
          - Effect: Allow
            Principal:
              Service: logging.s3.amazonaws.com
            Action: s3:PutObject
            Resource:
              - !Sub ${AccessLogBucket.Arn}/${AccessLogPrefix}*
            Condition:
              ArnLike:
                aws:SourceArn: !GetAtt AuditLogBucket.Arn
              StringEquals: 
                aws:SourceAccount: !Ref AWS::AccountId

  # IAM policy for audit log streaming role
  AuditLogStremingPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      Description: Policy for GitHub Audit Log Streaming
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - s3:PutObject
            Resource:
              - !Sub ${AuditLogBucket.Arn}/*

  # IAM Role for audit log streaming role
  AuditLogStreamingRole:
    Type: AWS::IAM::Role
    Properties:
      ManagedPolicyArns: 
        - !Ref AuditLogStremingPolicy
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Action: sts:AssumeRoleWithWebIdentity
            Principal:
              Federated: !Ref GithubAuditLogOIDCProvider
            Condition:
              StringEquals:
                oidc-configuration.audit-log.githubusercontent.com:sub: !Sub https://github.com/${EnterpriseName}
                oidc-configuration.audit-log.githubusercontent.com:aud: sts.amazonaws.com

  GithubAuditLogOIDCProvider:
    Type: AWS::IAM::OIDCProvider
    Properties:
      Url: https://oidc-configuration.audit-log.githubusercontent.com
      ClientIdList: 
        - sts.amazonaws.com
      ThumbprintList:
        - 7e6db7b7584d8cf2003e0931e6cfc41a3a62d3df

Outputs:
  IAMRole:
    Description: IAM role for audit log streaming role
    Value: !GetAtt AuditLogStreamingRole.Arn
  AuditLogBucket:
    Description: S3 bucket for audit log
    Value: !Ref AuditLogBucket
```

## スタックのデプロイ～ストリーミングの設定
以下のパラメータが必要です。

* GitHub Enterprise Name (`EnterpriseName`)
* 監査ログの保管日数 (`RetainDays`)
* 監査ログ S3 バケット用のサーバーアクセスログのプレフィックス (`AccesssLogPrefix`)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/f7531b6f-e35d-a4ba-23a9-1b1695228da9.png)

スタックのデプロイが完了したら、出力の S3 バケット名と IAM Role の ARN を GitHub Enterprize 側で設定すれば OK です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/3a73149b-46e8-d17f-4967-519659fc0ede.png)


Check endpoint をクリックして疎通が確認できたら Save で保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/13d762c2-7777-4237-ee26-92c67360fcc2.png)

AuditLogBucket に監査ログが出力されているはず！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/1dbc9d0f-9efd-04aa-49c9-605ce659859f.png)


## 参考: CloudTrail Lake への取り込み
2023/1/31 に AWS CloudTrail Lake が AWS 以外のパートナーソースからのイベント取り込みをサポートしました。

https://aws.amazon.com/jp/about-aws/whats-new/2023/01/aws-cloudtrail-lake-ingestion-activity-events-non-aws-sources/

ローンチパートナーに GitHub も含まれてはいるのですが、CloudTrail Lake 上で分析するためには、一旦監査ログを S3 バケットに出力したのち、変換して取り込む必要があります。そのためのリファレンス実装が aws-samples に公開されています。

https://github.com/aws-samples/aws-cloudtrail-lake-github-audit-log

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/0312bc16-95f7-af67-607b-0867c2f9ccb7.png)
※画像は上記リポジトリより引用

こちらはまだ試せていないので別の機会に紹介したい。。


以上です。
参考になればい幸いです。