Serverless Framework利用時のアカウント毎のIAM権限について考えてみた

serverless frameworkを使って複数人で開発する場合のアカウント毎の権限について考えてみた。

## やりたいこと
- 参画ユーザ毎にアカウントを発行
- 各アカウントでstage作成するための権限設定
- 開発者の入場、退場に合わせた柔軟な運用

## 前提条件
- 各アカウントとserverlessのステージ名を同一にしておく
- 開発環境やステージング環境の設定は既に終わっているものとする

## 参画ユーザ毎にアカウントを発行
IAMの管理についてはmiamで良いかなと思っていたが、greeさんで作られているsubiamを使うことにした。

[Subiamを使いAWSのIAM管理をコードベースでおこなう](http://labs.gree.jp/blog/2016/06/15988/)

以降はこのツール前提での話を進めていきます。

## 各アカウントでstage作成するための権限設定
stage作成にはcloudformationの作成やapigatewayの操作等、必要な権限が多いため非常に悩んだが、それぞれのAWSサービス毎に必要な権限を精査してみた。

### cloudformationポリシーの作成
本番環境の設定を変更されては困るので、作成はできるが、変更、削除に制限を設けるようと、以下のようにした。
stack名はserverless側で設定されているs-resources.json などと暗黙的に合わせる。

```
policy "developer-cloudformation" do
  {
    Version: context.version,
    Statement: [
      {
        Effect: "Allow",
        Action: [
          "cloudformation:CreateStack",
          "cloudformation:DescribeStackResource",
          "cloudformation:DescribeStackResources",
          "cloudformation:DescribeStacks",
          "cloudformation:ListStacks"
        ],
        Resource: "*"
      },
      {
        Effect: "Allow",
        Action: [
          "cloudformation:DeleteStack",
          "cloudformation:UpdateStack"
        ],
        Resource: [
          "arn:aws:cloudformation:region:account-id:stack/project-name-dev-r/*",
          "arn:aws:cloudformation:region:account-id:stack/project-name-stg-r/*",
          "arn:aws:cloudformation:region:account-id:stack/project-name-${aws:username}-r/*"
        ]
      }
    ]
  }
end
```

### apigatewayポリシーの作成
ステージ毎の更新の制限をかけたかったが、どうにも上手くいかず本番も変更できてしまう内容になっている。
現状では運用で回避している状態になってしまっている。

```
policy "developer-apigateway" do
  {
    Version: context.version,
    Statement: [
      {
        Effect: "Allow",
        Action: [
          "apigateway:GET"
        ],
        Resource: [
          "arn:aws:apigateway:region::/*"
        ]
      },
      {
        Effect: "Allow",
        Action: [
          "apigateway:PUT",
          "apigateway:POST",
          "apigateway:DELETE",
          "apigateway:PATCH",
          "apigateway:HEAD",
          "apigateway:OPTIONS"
        ],
        Resource: [
          "arn:aws:apigateway:region::/restapis/api-id/*"
        ]
      },
      {
        Effect: "Allow",
        Action: [
          "execute-api:Invoke"
        ],
        Resource: [
          "arn:aws:execute-api:region:*:api-id/${aws:username}/*",
          "arn:aws:execute-api:region:*:api-id/dev/*",
          "arn:aws:execute-api:region:*:api-id/stg/*"
        ]
      }
    ]
  }
end
```

### lambdaポリシーの作成
エイリアス単位で上手くできれば良いのだが、こちらも自由に変更ができる権限になってしまっている。
deleteFunctionの権限は取り上げてしまってもよかったかもしれない。

```
policy "developer-lambda" do
  {
    Version: context.version,
    Statement: [
      {
        Effect: "Allow",
        Action: [
          "lambda:ListFunctions"
        ],
        Resource: "*"
      },
      {
        Effect: "Allow",
        Action: [
          "lambda:*"
        ],
        Resource: "arn:aws:lambda:region:account-id:function:project-name*"
      }
    ]
  }
end
```

### iamポリシーの作成
ここで必要なことは

- AWSマネージメントコンソールへのログイン
- 自身のパスワード変更、MFA設定
- lambdaへ割り当てるroleの作成、変更
- lambdaへの特定のrole割当

こちらもserverless側とrole名などを暗黙的に合わせる必要がある

```
policy "developer-iam" do
  {
    Version: context.version,
    Statement: [
      {
        Effect: "Allow",
        Action: [
          "iam:ChangePassword",
          "iam:GetAccountPasswordPolicy",
          "iam:CreateVirtualMFADevice",
          "iam:DeactivateMFADevice",
          "iam:ListUsers",
          "iam:ListVirtualMFADevices",
          "iam:ListUserPolicies",
          "iam:DeleteVirtualMFADevice",
          "iam:GetLoginProfile",
          "iam:UpdateLoginProfile",
          "iam:EnableMFADevice",
          "iam:CreateLoginProfile"
        ],
        Resource: [
          "arn:aws:iam::account-id:user/",
          "arn:aws:iam::account-id:mfa/",
          "arn:aws:iam::account-id:user/${aws:username}",
          "arn:aws:iam::account-id:mfa/${aws:username}"
        ]
      },
      {
        Effect: "Allow",
        Action: "iam:PassRole",
        Resource: [
          "arn:aws:iam::account-id:role/project-name-dev-r-IamRoleLambda-xxxxxxxxxxxxx",
          "arn:aws:iam::account-id:role/project-name-stg-r-IamRoleLambda-yyyyyyyyyyyyy",
          "arn:aws:iam::account-id:role/project-name-${aws:username}-r-IamRoleLambda-*"
        ]
      },
      {
        Effect: "Allow",
        Action: [
          "iam:ListRoles",
          "iam:CreateRole",
          "iam:GetRole"
        ],
        Resource: [
          "*"
        ]
      },
      {
        Effect: "Allow",
        Action: [
          "iam:DeleteRole",
          "iam:DeleteRolePolicy",
          "iam:PutRolePolicy"
        ],
        Resource: [
          "arn:aws:iam::account-id:role/project-name-${aws:username}-r-IamRoleLambda-*"
        ]
      }
    ]
  }
end
```

これらの設定を併せ持つグループを作成する。

developer.rb

```
group "Developer", path: "/" do
  include_template "developer-apigateway", version: "2012-10-17"
  include_template "developer-iam", version: "2012-10-17"
  include_template "developer-lambda", version: "2012-10-17"
  include_template "developer-cloudformation", version: "2012-10-17"
end
```

ユーザの定義を行う
user.rb

```
user "quartette", path: "/" do
  login_profile password_reset_required: true

  groups(
    "Developer"
  )
end
```

こんな感じで、ユーザを増やしていけば何とかなるんじゃなかろうか。
実行は、

```
export AWS_ACCESS_KEY_ID='...'
export AWS_SECRET_ACCESS_KEY='...'
subiam -a --dry-run -f user.rb
subiam -a -f user.rb
```


## 開発者の入場、退場に合わせた柔軟な運用
正直まだいい案が無い状態。
PRのマージをトリガーとして、jenkinsで自動実行しているが、ユーザ作成時のクレデンシャルがだだ漏れになったりと、非常に穴のある状態。
このあたり良い運用方法などあればぜひ！


今回書いた内容はgithubでも公開しています。
[github](https://github.com/quartette/serverless-sub-iam)


Serverless Frameworkを利用した開発の進め方などについて今後も考えていきたいと思います。