Upgrade to Pro — share decks privately, control downloads, hide ads and more …

GitHub Actionsに「強い」AWSの権限を渡したい / AWS credentials on Actions

GitHub Actionsに「強い」AWSの権限を渡したい / AWS credentials on Actions

FUJIWARA Shunichiro

May 14, 2021
Tweet

More Decks by FUJIWARA Shunichiro

Other Decks in Technology

Transcript

  1. GitHub Actionsに「強い」AWSの権限を渡したい
    2021.05.14
    ⾯⽩法⼈カヤック 藤原俊⼀郎 @fujiwara

    View Slide

  2. @fujiwara
    SREチーム所属
    2011年〜カヤック(10年!)
    ISUCON 1,2,5優勝、ISUCON 3,8出題
    github.com/kayac/ecspresso
    Amazon ECS デプロイツール
    github.com/fujiwara/lambroll
    AWS Lambda デプロイツール

    View Slide

  3. 最近の仕事
    ぼくらの甲⼦園ポケット ECS 移⾏
    2014年リリースの⻑寿ゲーム
    before: EC2 (ondemand & spot)
    after: ECS (Fargate & EC2 spot 100%)
    Perl5.16→5.30
    ログ処理をKinesis+Lambdaに
    API に CloudFront 導⼊
    EC2 & Chef 全廃

    View Slide

  4. 最近の悩み
    AWSのリソースの管理は基本 Terraform で⾏っている
    (アプリケーションだけ ecspresso / lambroll)
    terraform plan
    とその通知は tfnotify github.com/mercari/tfnotify を使って
    GitHub Actions / CircleCI で実⾏している
    現状 terraform apply
    は各⾃の⼿元(⼀部は専⽤EC2)で実⾏している
    apply も CI/CD 環境で実⾏したい!

    View Slide

  5. 現状、terraform apply を CI/CD 環境で実⾏していない理由
    実⾏に「強い」権限が必要 (⼤抵 Administrator 権限と同等)
    強すぎる IAM User のアクセスキーを外部に預けたくない
    アクセスキーは環境変数に設定される
    リポジトリへの書き込み権限を取得されると、奪取される可能性がある
    Classi の事故 corp.classi.jp/news/2416/ フィッシングからGitHubアクセス
    キーをコードに書いていなくても、CIをいじって実⾏されたらキーは抜かれる
    GitHubで全員にMFA強制はなかなか難しい (外部の⼈も全員強制になる)

    View Slide

  6. でも本当はやりたい
    開発者、運⽤者の⼿元が安全とは限らない
    ⼈に依存しない環境で実⾏できるようにしておきたい
    条件
    権限は安全に保ちたい
    GitHubリポジトリの書き込み権が奪われてもAWSの権限は渡したくない
    うっかり誤クリックや誤mergeでは発動したくない
    狙ったタイミングで、特定の⼈だけが発動したい

    View Slide

  7. 作戦1 - AssumeRole with MFA
    MFA を使って assume role して「強い」権限を得る作戦

    View Slide

  8. 作戦1 - AssumeRole with MFA
    IAM Userを作ってアクセスキーを発⾏する
    sts:AssumeRole だけを付与する
    MFAを必須にする(QRコードは控えておく)
    ユーザーのIAM Policy
    {
    "Effect": "Allow",
    "Action": "sts:AssumeRole",
    "Resource": "arn:aws:iam::123456789012:role/TerraformApply",
    "Condition": {
    "BoolIfExists": {
    "aws:MultiFactorAuthPresent": "true"
    }
    }
    }

    View Slide

  9. 作戦1 - AssumeRole with MFA
    terraform apply ⽤の「強い」 IAM Role を作る
    作った IAM User を信頼する
    assume role policy
    {
    "Effect": "Allow",
    "Action": "sts:AssumeRole",
    "Principal": {
    "AWS": "arn:aws:iam::123456789012:user/GitHubActions"
    },
    "Condition": {
    "BoolIfExists": {
    "aws:MultiFactorAuthPresent": "true"
    }
    }
    }

    View Slide

  10. 作戦1 - AssumeRole with MFA
    GitHub Actionsで、
    . IAM User のアクセスキーを secrets に設定
    このキーが取られてもMFAがないとなにもできない
    . manual trigger のジョブを作る
    MFA token⼊⼒欄も定義する
    name: apply
    on:
    workflow_dispatch:
    inputs:
    token-code:
    description: 'mfa code'
    required: true
    github.blog/changelog/2020-07-06-github-actions-manual-triggers-with-
    workflow_dispatch/

    View Slide

  11. 作戦1 - AssumeRole with MFA

    View Slide

  12. 作戦1 - AssumeRole with MFA
    . secrets のアクセスキーを設定
    . aws sts assume-role
    をマニュアル⼊⼒した MFA token で実⾏
    . 「強い」Roleの権限を取得できる!
    jobs:
    manual:
    runs-on: ubuntu-latest
    steps:
    - name: Configure AWS Credentials
    uses: aws-actions/[email protected]
    with:
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    aws-region: ap-northeast-1
    - name: aws sts AssumeRole with MFA
    run: |
    OUTPUT=`aws sts assume-role \
    --role-arn arn:aws:iam::123456789012:role/TerraformApply \
    --role-session-name github-actions \
    --duration-seconds 900 \
    --serial-number arn:aws:iam::123456789012:mfa/GithubActions \
    --token-code ${{ github.event.inputs.token-code }}`
    echo AWS_ACCESS_KEY_ID=`echo $OUTPUT | jq -r .Credentials.AccessKeyId` >> $GITHUB_ENV
    echo AWS_SECRET_ACCESS_KEY=`echo $OUTPUT | jq -r .Credentials.SecretAccessKey` >> $GITHUB_ENV
    echo AWS_SESSION_TOKEN=`echo $OUTPUT | jq -r .Credentials.SessionToken` >> $GITHUB_ENV
    shell: bash

    View Slide

  13. 作戦1 - AssumeRole with MFA
    いいところ
    Actionsに設定されているアクセスキー単体では何もできないので漏洩が怖くない
    実⾏時に⼈が MFA token を⼊⼒するのでうっかり誤発動しない
    再実⾏しても token が有効期限切れになっているので動かない
    いまいちなところ
    MFAの設定を複数⼈で共有する必要がある (QRコード)
    IAM User の MFA はひとつしか作れないため
    MFA token の取得から実⾏までに時間が掛かると有効期限が切れる
    Actionsの環境起動に10〜20秒かかるので、残り時間を⾒極めて投⼊が必要

    View Slide

  14. 作戦2 - CloudShell exports credentials
    AWS CloudShellが持っている権限を頂いてしまおう作戦

    View Slide

  15. 作戦2 - CloudShell exports credentials
    AWS CloudShell
    マネージメントコンソールを開いている⼈の権限が設定されている
    外部へのネットワークアクセスが可能

    View Slide

  16. 作戦2 - CloudShell exports credentials
    CloudShell で設定されている AWS_ の環境変数
    $ env | grep AWS_ |sort
    AWS_CONTAINER_AUTHORIZATION_TOKEN=*******
    AWS_CONTAINER_CREDENTIALS_FULL_URI=http://localhost:1338/latest/meta-data/container/security-credentials
    AWS_DEFAULT_REGION=ap-northeast-1
    AWS_EXECUTION_ENV=CloudShell
    AWS_REGION=ap-northeast-1
    これでアクセスキーが取得できる
    $ curl -H"Authorization: $AWS_CONTAINER_AUTHORIZATION_TOKEN" \
    $AWS_CONTAINER_CREDENTIALS_FULL_URI
    {
    "LastUpdated": "1970-01-01T00:00:00Z",
    "Type": "",
    "AccessKeyId": "ASIAUSOAHXO5WFCZF5GW",
    "SecretAccessKey": "xxxxxxxxxx",
    "Token": "xxxxxxxxxxxx"
    "Expiration": "2021-05-13T06:09:04Z",
    "Code": "Success"
    }

    View Slide

  17. 作戦2 - CloudShell exports credentials
    AWS_CONTAINER_CREDENTIALS_FULL_URI
    http://localhost:1338/latest/meta-data/container/security-credentials
    つまり CloudShell の localhost:1338 に外部から繋げれば
    CloudShell が持っているアクセスキーを取得できるのでは…?

    View Slide

  18. 作戦2 - CloudShell exports credentials
    cloudflared - Argo Tunnel client
    github.com/cloudflare/cloudflared
    ngrok みたいなやつ by Cloudflare
    CloudShell にインストールして起動。バイナリを置くだけ
    $ curl -sL https://bin.equinox.io/c/VdrWdbjqyF/cloudflared-stable-linux-amd64.tgz | tar zxvf -
    cloudflared
    $ ./cloudflared tunnel --url localhost:1338
    (
    略)
    2021-05-13T06:00:37Z INF +-----------------------------------------------------------+
    2021-05-13T06:00:37Z INF | Your free tunnel has started! Visit it: |
    2021-05-13T06:00:37Z INF | https://hardcover-mixing-bs-madness.trycloudflare.com |
    2021-05-13T06:00:37Z INF +-----------------------------------------------------------+
    表⽰されるURL(毎回異なる)にアクセスするとCloudShellのlocalhost:1338に繋がる

    View Slide

  19. 作戦2 - CloudShell exports credentials
    これでどこからでも ($AWS_CONTAINER_AUTHORIZATION_TOKENがあれば)
    HTTPSで取得できる
    $ curl -H"Authorization: $AWS_CONTAINER_AUTHORIZATION_TOKEN" \
    https://hardcover-mixing-bs-madness.trycloudflare.com/latest/meta-data/container/security-credentials
    {
    "LastUpdated": "1970-01-01T00:00:00Z",
    "Type": "",
    "AccessKeyId": "ASIAUSOAHXO54XC5257P",
    ...

    View Slide

  20. 作戦2 - CloudShell exports credentials
    まとめ
    . 「強い」権限を持っている⼈が CloudShell を起動
    . CloudShell で cloudflared を起動して外部に localhost:1338 を公開する
    . $AWS_CONTAINER_AUTHORIZATION_TOKEN と公開URLをActionsに⼊⼒
    . Actions からURLにアクセスしてアクセスキーを取得

    View Slide

  21. 作戦2 - CloudShell exports credentials
    いいところ
    強い権限を持った⼈間 (マネージメントコンソールにログインできている) がいるときだ
    け、その⼈の権限をその場で渡せる
    cloudflared を停⽌したり CloudShell を閉じればアクセスできなくなる
    閉じてしまえばうっかり再実⾏しても権限は取れない
    渡したアクセスキー⾃体にも有効期限がある
    CloudShellは無操作だと20〜30分で停⽌される
    いまいちなところ
    cloudflared の中をアクセスキーが通過するのをどう考えるか
    Cloudflareがここで変なことはしないと信頼するかどうかによる
    AWSが同様のサービスを出してくれたら万事解決では…?

    View Slide

  22. まとめ
    AWS外に強すぎる権限のアクセスキーを保管しないために、いろいろ考えてみました
    . AssumeRole with MFA 作戦
    実はMFAなしでも簡易的な対策になる
    キーだけでは sts:AssumeRole しかできない
    とはいえassume roleの⼿順は .github/workflows に書いてあるのでちゃんと読まれ
    たら危ない
    . CloudShell exports credentials 作戦
    没案: Self Hosted Runner
    EC2でActionsの実⾏環境を⽤意して強い権限を設定しておけば…?
    workflowをいじられたら強い権限でなんでもできてしまう
    うっかり再実⾏を防げない

    View Slide