OpenAPIドキュメントをIP制限をかけてS3に配置する
環境
機種 : MacBook Pro 2021(M1 Max)
OS : Monterey(12.2.1)
$ node -v
v18.7.0
$ npm --version
8.15.0
nodeのバージョン18系を利用していますが、
プロダクションアプリケーションでは、アクティブ LTS または メンテナンス LTS リリースのみを使用してください。
とのことで、2022/8/15時点で本番環境で利用するならバージョン16系を利用するべきです。偶数の最新バージョンを使えばええって事ではないらしいです。今更知りました。。。
まぁ今回は本番環境とかではないのでバージョン18系で進めてみました。
openapiドキュメントを記述する
まずはS3に上げるopenapiドキュメントを作成します。
openapi: 3.0.0
info:
description: openapi s3 test
version: 1.0.0
title: Openapi S3 Test
host: localhost
servers:
- url: 'http://localhost'
paths:
/hello-world:
get:
summary: Hello world
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/get_hello_world'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/get_hello_world_500'
operationId: get-hello-world
description: hello world endpoint
/user:
post:
summary: User
operationId: post-user
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/post_user'
description: user endpoint
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/req_post_user'
description: POST /user request body
components:
schemas:
get_hello_world:
type: object
title: get_hello_world_200
description: GET /hello-world response
properties:
id:
type: integer
description: ID
minimum: 1
example: 1
message:
type: string
description: メッセージ
example: hello world
todayDate:
type: string
format: date
description: 本日日付
example: '2022-08-08'
nullable: true
required:
- id
- message
get_hello_world_500:
type: object
description: GET /hello-world(status code 500) response
properties:
code:
type: integer
description: エラーコード
minimum: 1
example: 1
message:
type: string
description: エラーメッセージ
example: internal server error!
required:
- code
- message
post_user:
title: post_user
type: object
description: POST /user response
req_post_user:
title: req_post_user
type: object
description: POST /user request body
properties:
name:
type: string
description: ユーザー名
example: string
mailAddress:
type: string
format: email
description: メールアドレス
example: user@example.com
nationality:
type: string
description: 国籍
enum:
- jp
- us
example: jp
birthYear:
type: integer
description: 誕生年
format: int64
minimum: 1900
example: 1996
nullable: true
required:
- name
- mailAddress
- nationality
yamlからhtmlを生成する
yaml形式のママでもS3に上げれますが、jsファイルなどが必要になってくるので、今回はシンプルにhtmlを上げようと思います。
redocというライブラリのCLIを利用してyamlからhtmlを生成します。
redoc-cliをインストールします。
$ npm i -g redoc-cli
$ redoc-cli --version
0.13.17
上記で生成したopenapi.yamlをhtmlに変換します。
$ redoc-cli build openapi.yaml --output openapi.html
$ open openapi.html
以下のようにブラウザで表示されていたら成功です。
IP制限をかけてS3にopenapiドキュメントを配置する
自分だけ見れれば満足するので、IP制限は設けておきます。
次の2記事を参考に進めていきます。
「パブリックアクセスをすべてブロック」のチェックをはずして、S3バケットを作成します。
作成したS3バケットのバケットポリシーを編集します。
作成したS3バケット名
は作成したバケット名を、アクセスを許可したいIP
は11.11.11.11/32のように指定して保存します。
1つ目のStatement(SourceIP)で指定IP以外のトラフィックを拒否し、
2つ目のStatement(PublicReadGetObject)で全てのオブジェクトの読み取りを許可しています。
{
"Version": "2012-10-17",
"Id": "SourceIP",
"Statement": [
{
"Sid": "SourceIP",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::作成したS3バケット名",
"arn:aws:s3:::作成したS3バケット名/*"
],
"Condition": {
"NotIpAddress": {
"aws:SourceIp": "アクセスを許可したいIP"
}
}
},
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::作成したS3バケット名/*"
}
]
}
先程作成しておいた、openapi.htmlをS3にアップロードします。そしてオブジェクトのURLを開いて以下のように表示できていたら成功です。
PCのIPを変更するか、スマホでwifiをオフにしたりしてIPを変更した状態にして、URLを踏むと「AccessDenied」となることを確認しておきます。
GithubActionsでS3にhtmlをアップロードする
S3に上げるという目的は達成しましたが、開発でGtihubActionsで変更を反映できると便利なのでやってみます。
IAMユーザーを作成する
GithubActionsで利用するIAMユーザーを作成します。アクセスキーを発行したいので、プログラムによるアクセスの方にチェックを入れます。
細かく設定した方が良いですが、ちょっとめんどくさかったのでS3FullAccessを設定します。
作成後、GithubActionsで使えるようにAWS_ACCESS_KEY_IDとAWS_SECRET_ACCESS_KEYをGithubのSecretに設定しておきます。
workflowを記述する
mainブランチにpushされたらS3にアップロードするようにします。[バケット名]
は作成したバケット名に置き換えます。
name: Upload to Amazon S3
on:
push:
branches:
- "main"
jobs:
build:
name: Upload
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Upload to Amazon S3
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: aws s3 cp --recursive --region ap-northeast-1 openapi.html s3://[バケット名]/openapi.html
これでGithubActionsを動かしてみると成功するのかと思いきや、失敗、、、
An error occurred (AccessDenied) when calling the PutObject operation: Access Denied
バケットポリシーでIP制限をかけてしまったのが良くなかったぽいです。バケットポリシーを以下のように修正します。
閲覧のみIP制限をかけるために、Denyをs3:*
にしていたのをs3:GetObject
のみにし、Allowにs3:PutObject
を追加します。
{
"Version": "2012-10-17",
"Id": "SourceIP",
"Statement": [
{
"Sid": "SourceIP",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": [
"arn:aws:s3:::作成したS3バケット名",
"arn:aws:s3:::作成したS3バケット名/*"
],
"Condition": {
"NotIpAddress": {
"aws:SourceIp": "アクセスを許可したいIP"
}
}
},
{
"Sid": "PublicReadGetPutObject",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::作成したS3バケット名/*"
}
]
}
これでGithubActionsを再度動かすとuploadが成功しました。
IP制限しかしていないので、もう少しセキュリティは強固にしたほうが良さそうではあります。
2022/8/28追記 セキュリティ強化の方法
もう少しセキュリティを強化する方法として、Cloudfrontを通すことで、Basic認証+IP制限ができそうです。
Discussion