SAKURUG TECHBLOG

【AWS/Nuxt3/GitHub Actions】①AWSサービス構築

timestampauthor-name
Kota

はじめに

今回からは全4回に渡り、GitHub Actionsを構築するためのハンズオン記事を連載というかたちでお届けします。
そこそこボリューミーな記事になるかと思われますが、是非参考にしていただければ幸いです。

背景

直近の業務で、GitHub ActionsでCIを構築する機会がありました。
と言っても社内では知見の無い領域だったので、どうやったら出来るのかなーと調査していたのですが、その過程でGitHub Actionsだけでなく関連するクラウドサービスの理解も必要なことが分かってきました。

技術検証のため更に調査を進め、ウィンドウのタブの数が肥大化してきた頃に「もう少し網羅的に解説してくれてる資料は無いかなあ」と感じるようになり、開発が落ち着いたらこの連載を書こうと決めていました。

ゴール

とは言え全てのユースケースを考慮するのは難しいので、実際に僕が業務で得た知見を基に書いてみます。
この全4回の連載では、

GitHub Actionsによって、Nuxtで実装したページをS3(+CloudFront)に自動デプロイし、実際のページで挙動確認できること

を目標とします。

対象者

  • デプロイ業務を自動化したいor興味がある
  • 表題の技術についてある程度知見がある or これからキャッチアップ予定
  • 表題の技術を扱ううえで、少しでも運用リスクを減らしたい

といった方々を想定しています。

開発環境

  • フロントエンド:Nuxt.js(v3.7.1)
  • バックエンド:-
  • ソース管理:GitHub
  • CIツール:GitHub Actions
  • ホスティングサービス:AWS S3
  • CDN:AWS CloudFront


workflow-github-actions
オリジンをS3、CDNをCloudFrontとしています。
この2つのAWSサービスに対し、GitHub Actionsでは主に下記の処理を行います。

  1. S3に対し、GitHubで管理している特定のファイルをアップロード(同期)する
  2. CloudFrontに対し、古いキャッシュの削除を行う

AWS環境構築

ここではrootアカウントを使用してAWS環境構築を行います。

S3

バケット作成

①S3コンソール > バケットを作成 > バケット新規作成画面
②任意のバケット名を入力し、以降はデフォルトの設定値のままバケットを作成。

CloudFront

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

①CloudFrontコンソール > ディストリビューションを作成 > ディストリビューション新規作成画面
②オリジン設定内容(下記以外はデフォルト値)
オリジンドメイン:前項で新規作成したS3を選択
名前:任意
オリジンアクセス:OAC(Origin access control)を選択し、コントロール設定を作成(デフォルト値)。
※グレー部分にはバケット名が入ります

③デフォルトのキャッシュビヘイビア設定(下記以外はデフォルト値)
ビューワープロトコルポリシー:「HTTPS Only」を選択
キャッシュポリシー:「CachingOptimized」を選択

④WAF設定:「セキュリティ保護を有効化しない」
⑤ディストリビューションを作成

アクセス制御

次に、CloudFrontを経由したS3へのアクセスに対するポリシーを設定します。
ディストリビューションの作成に成功すると一覧画面に遷移し、上記のような警告が現れます。
※グレー部分にはディストリビューション名が入ります。

内容そのままですが、前項でディストリビューション作成時に設定したOACへのアクセス許可をS3側でも設定する必要がある、というものです。
警告に従い、「ポリシーをコピー」を押下し、対象のS3コンソールに再度アクセスしましょう。

S3コンソール内で、バケットポリシーの編集を行います。
編集画面では、上記でコピーした内容をペーストし、変更を保存します。
※グレー部分にはそれぞれバケット名、ディストリビューション名が入ります

上記のjsonを簡単に日本語訳すると、
「このS3内は、前項で作成したCloudFrontのディストリビューションからのアクセスだけを許可します」
という内容です。

関数の作成

後程、Cloudfrontからのアクセス時にBasic認証を導入するための関数を作成します(ここでは実装は行いません)。

CloudFrontコンソール > 関数 > 関数の作成 > 任意の名前で関数を作成
※グレー部分にはそれぞれ関数名、ディストリビューション名、最終変更日が入ります

IAMの設定

ここまでで、rootアカウントが最低限行うべき各種サービス初期設定が完了しました。
以降、サービスに関して変更を加えるのはIAMアカウントとし、必要最低限な権限のみを付与します。

ポリシー作成

①IAMコンソール > ポリシー > ポリシーを作成
②ステップ1:ポリシーエディタをJSONとし、下記を参考にJSONを編集 > 次へ

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "Statement1",
			"Effect": "Allow",
			"Action": "s3:ListAllMyBuckets",
			"Resource": "arn:aws:s3:::*"
		},
		{
			"Sid": "Statement2",
			"Effect": "Allow",
			"Action": "s3:ListBucket",
			"Resource": "arn:aws:s3:::バケット名"
		},
		{
			"Sid": "Statement3",
			"Effect": "Allow",
			"Action": [
				"s3:GetObject",
				"s3:DeleteObject",
				"s3:PutObject"
			],
			"Resource": "arn:aws:s3:::バケット名/*"
		},
		{
			"Sid": "Statement4",
			"Effect": "Allow",
			"Action": [
				"cloudfront:ListDistributions",
				"cloudfront:ListFunctions"
			],
			"Resource": "*"
		},
		{
			"Sid": "Statement5",
			"Effect": "Allow",
			"Action": [
				"cloudfront:GetDistribution",
				"cloudfront:GetDistributionConfig",
				"cloudfront:UpdateDistribution",
				"cloudfront:CreateInvalidation"
			],
			"Resource": "arn:aws:cloudfront::AWSアカウントID:distribution/ディストリビューション名"
		},
		{
			"Sid": "Statement6",
			"Effect": "Allow",
			"Action": [
				"cloudfront:GetFunction",
				"cloudfront:UpdateFunction",
				"cloudfront:PublishFunction",
				"cloudfront:DescribeFunction",
				"cloudfront:TestFunction"
			],
			"Resource": "arn:aws:cloudfront::AWSアカウントID:function/関数名"
		}
	]
}


※グレー部分にはバケット名が入ります

③ステップ2:任意のポリシー名を入力し、ポリシーを作成

補足

JSONポリシーの内容について補足します。

Statement1

作成したS3バケットを取得するための、バケット一覧取得権限です

Statement2

作成したS3バケットの、詳細を取得するための権限です

Statement3

作成したS3バケット内で、ファイルを操作するための権限です

Statement4

作成したディストリビューションとCloudFront関数を取得するための、ディストリビューションと関数の一覧取得権限です

Statement5

作成したディストリビューションを操作するための権限です

Statement6

作成したCloudFront関数を操作するための権限です

グループ作成

①IAMコンソール > ユーザーグループ > グループを作成
②グループ名は任意。許可するポリシーには前項で作成したポリシーを選択し、グループを作成。
※グレー部分には
それぞれグループに関する情報、ポリシー名が入ります

ユーザー作成

前項で作成したグループに新たなIAMユーザーを追加します。
①IAMコンソール > IAMの作成 > IAM新規作成画面
②ステップ1:ユーザー名は任意。マネコンへのアクセスを許可し、ユーザータイプを下記のように指定します。

③ステップ2:下記のように、前項で作成済みのグループへ追加指定します。
※グレー部分にはグループに関する情報が入ります

④ステップ3:そのまま「次へ」。
⑤ステップ4:付与されたサインイン情報を使用し、新規作成したユーザーでログイン出来れば完了。

IAMアカウントでのサービス更新・検証

ここからはIAMアカウントでサービス設定の更新、検証を行います。

CloudFrontからのアクセス確認

作成済みのS3バケットに、CloudFrontを経由して実際にアクセスできるか検証します。
例として、S3に下記のようなindex.htmlを追加し、その内容がhttps://ディストリビューション名.cloudfront.net/index.htmlで確認できればOKです。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <h1>index page</h1>
</body>
</html>


※グレー部分にはそれぞれディストリビューション名、最終更新日時が入ります

補足

エラー画面が返ってくる場合、CloudFrontのキャッシュが残ってしまっている可能性があります。下記を参考に、Cloudfrontの設定画面からキャッシュを削除→実際のページをリロードすることで解決されるかもしれません。Cloudfrontの検証では、小まめにキャッシュを削除することをお勧めします。
※グレー部分にはディストリビューション名が入ります

Basic認証設定(推奨)

任意ではありますが、CloudFrontからのリクエストに対しBasic認証を設定することで更なるアクセス制限を行いましょう。
想定するユースケースとしては、WebサービスやWebシステムの開発環境におけるアクセス制限などです。
今回の場合、実現手段には

  • Lambda@Edge
  • CloudFront Functions

などが挙げられます。
ここでは、比較的手軽に導入可能であるCloudFront Functionsを採用します。
Basic認証導入における両者の比較はこちらが参考になります。

認証用文字列の発行

認証処理の実装を行う前に、認証を許可するユーザー名とパスワードを設定し、これらをBase64でエンコードします。
エンコード方法はWebサービス等、任意の手段でOKです。
(例)
ユーザー名:techblog
パスワード:password
techblog:password

dGVjaGJsb2c6cGFzc3dvcmQ=

関数の実装

①CloudFrontコンソール > 関数 > 対象の関数を選択
②下記を参考に、関数コードを更新 > 変更を保存

function handler(event) {
    var request = event.request;
    var headers = request.headers;
    var authStr = "Basic xxxxx"; // "xxxxx"はエンコードした文字列

    // 認証に失敗した場合
    if (!headers.authorization || headers.authorization.value !== authStr) {
        return {
            statusCode: 401,
            statusDescription: "Unauthorized",
            headers: {
                "www-authenticate": {
                    value: "Basic"
                }
            }
        };
    }
    
    return request;
}


4行目のグレー部分のみ、エンコードした文字列で適宜書き換えてください。
相当古いJavaScript環境なのか、ES6以降の記法(constなど)はエラーになってしまいます。。

関数のテスト

仮想的に/index.htmlへのGETリクエストを送信し、リクエストヘッダーの値によって想定通りの挙動になるか検証します。

正常パターン

③テスト > 下記を参考に、関数をテスト > ステータスが「成功」であることを確認
※グレー部分にはエンコードした文字列を入れてください

異常パターン

④テスト > ヘッダーの値を任意の文字列に変更し、再度関数をテスト > ステータスが「401 Unauthorized」であることを確認

関数とディストリビューションの関連付け

⑤発行 > 関数を発行
これで実装が完了したので、ディストリビューションへの関連付けを行います。

⑥ディストリビューション選択 > ビヘイビア > 編集 > 関数の関連付け > 下記参照 > 変更を保存

実際のページでの動作検証

これでBasic認証設定は完了です。実際のページで動作検証してみましょう。
①前項を参考に、CloudFrontのキャッシュを削除
②再度、実際のページにアクセス
③下記のようなポップアップが表示され、ログインが出来ればOKです。
※グレー部分にはディストリビューション名が入ります

まとめ

第1回の本記事では、表題の技術を扱ったCI構築を行うために、AWSでコンテンツを配信するための基本設定手順を紹介しました(IAM設定~CloudFrontへのBasic認証設定)。
第2回では、GirHub Actionsの適切な権限設定や。運用リスクを下げるためのOIDCという技術とその設定手順について紹介します。

記事をシェアする

ABOUT ME

author-image
Kota
新卒入社3年目。SAKURUG TECHBLOG管理者。社内業務システムやフロントエンド開発を行っています。

© SAKURUG co.,ltd.