長生村本郷Engineers'Blog

千葉県長生村本郷育ちのエンジニアが書いているブログ

terraform 0.11 系に対応した GitHub Actions 作った & tflint も入れてみた♪

概要

Terraform 用の GitHub Actions として hashicorp 社にて以下リポジトリが用意されています。

https://github.com/hashicorp/terraform-github-actions

ですが、上記のリポジトリでは、 terraform の最新版 (2019-09-30 時点 0.12.9) にのみ適用しています。

hashicorp/terraform-github-actions を folk して
0.11 系がなかった為、0.11 系に対応した terraform-github-actions を以下リポジトリに作成しました。

github.com

terraform の古いバージョンについての対応は、 terraform 公式に以下リンクにて記載があります。

Terraform Versions - Terraform GitHub Actions - Terraform by HashiCorp

古いバージョンは folk して自分で作ってね♪ と書いてあります。

ついでに

以下追加してみました。

使い方

以下のような terraform プロジェクトがあるとします。

├── envs
│   ├── prd
│   │   ├── backend.tf
│   │   ├── main.tf
│       ...
│   │   └── variable.tf
│   └── stg
│       ├── backend.tf
│       ├── main.tf
│       ...
│       └── variable.tf
└── modules
    ├── ...

GitHub Actions 設定方法

以下 2 ファイルを root ディレクトリに配置します。

├── .github
│   └── workflows
│       ├── main.yml
│       └── fmt.yml 
│
├── envs
│   ├── prd
│   │   ├── backend.tf
│   │   ├── main.tf
│       ...
│   │   └── variable.tf
│   └── stg
│       ├── backend.tf
│       ├── main.tf
│       ...
│       └── variable.tf
└── modules
    ├── ...

.github/workflows/main.yml

name: Terraform
on: [pull_request]

jobs:
  on-pull-request:
    name: On Pull Request

    strategy:
      matrix:
        env: [stg, prd]

    runs-on: ubuntu-latest

    steps:
    - name: Checkout Repo
      uses: actions/checkout@v1

    - name: ${{ matrix.env }} Terraform Init
      uses: kenzo0107/terraform-github-actions/init@v0.6.0
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        TF_ACTION_WORKING_DIR: './envs/${{ matrix.env }}'
        AWS_ACCESS_KEY_ID:  ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY:  ${{ secrets.AWS_SECRET_ACCESS_KEY }}

    - name: ${{ matrix.env }} Terraform Validate
      uses: kenzo0107/terraform-github-actions/validate@v0.6.0
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        TF_ACTION_WORKING_DIR: './envs/${{ matrix.env }}'
        AWS_ACCESS_KEY_ID:  ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY:  ${{ secrets.AWS_SECRET_ACCESS_KEY }}

    - name: ${{ matrix.env }} Terraform Lint
      uses: kenzo0107/terraform-github-actions/lint@v0.6.0
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        TF_ACTION_WORKING_DIR: './envs/${{ matrix.env }}'

    - name: ${{ matrix.env }} Terraform Plan
      uses: kenzo0107/terraform-github-actions/plan@v0.6.0
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        TF_ACTION_WORKING_DIR: './envs/${{ matrix.env }}'
        AWS_ACCESS_KEY_ID:  ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY:  ${{ secrets.AWS_SECRET_ACCESS_KEY }}

ちょっと解説

Pull Request をトリガーに実行されます。
on: [pull_request]
リポジトリをチェックアウト
    - name: Checkout Repo
      uses: actions/checkout@v1
matrix で stg, prd を並列実行します。
    strategy:
      matrix:
        env: [stg, prd]
terraform init, validate, lint, plan
    - name: ${{ matrix.env }} Terraform Init
...
    - name: ${{ matrix.env }} Terraform Validate
...
    - name: ${{ matrix.env }} Terraform Lint
...
    - name: ${{ matrix.env }} Terraform Plan

言わずもがな、以下を実行しています。

  • terraform init
  • terraform validate
  • terraform lint
  • terraform plan

kenzo0107/terraform-github-actions/init@v0.6.0 が terraform v0.11.14 に対応しています。

init, validate, lint は指摘事項がある場合は、Pull Request にコメントしてくれます。

terraform plan は必ず実行結果を貼り付けてくれます。

f:id:kenzo0107:20190930221527p:plain

これはレビュワーに有難い機能です。

コードの変更内容と terraform plan 内容の整合性が取れているかどうかが重要なレビュー観点となる為です。

実行パス指定

パスを移動してから terraform plan 等を実行したい場合に以下環境変数に指定します。

TF_ACTION_WORKING_DIR: './envs/${{ matrix.env }}'

kenzo0107/terraform-github-actions/plan@v0.6.0 では、
上記の設定した TF_ACTION_WORKING_DIR を terraform plan 実行内容と共に表示するようにしています。((hashicorp/terraform-github-actions では、実行したディレクトリパスは Pull Request コメントに乗らない様になってます。))

これは terraform plan 実行内容から stg, prd どちらで実行したかわかりずらく、レビュワーを困惑させる可能性がある為です。

secrets の設定
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AWS_ACCESS_KEY_ID:  ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY:  ${{ secrets.AWS_SECRET_ACCESS_KEY }}

secrets.GITHUB_TOKEN は secrets にトークンを設定する必要がありません。

個人的に GitHub が実行する CI/CD だからこそ実現できる秘匿性のある管理方法で、 Actions の大きな利点だと思います。

その他は、 settings > secrets で設定します。

f:id:kenzo0107:20190930223443p:plain

.github/workflows/fmt.yml

terraform fmtstg, prd 等は関係なく、リポジトリのルートディレクトリで実行するので
.github/workflows/main.yml とは分けました。

こちらも Pull Request をトリガーとして実行されます。

name: Terraform
on: [pull_request]

jobs:
  on-pull-request:
    name: On Pull Request

    runs-on: ubuntu-latest

    steps:
    - name: Checkout Repo
      uses: actions/checkout@v1

    - name: Terraform fmt
      uses: kenzo0107/terraform-github-actions/fmt@v0.6.0
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

総評

GitHub Actions に触れてみようとした題材として非常に簡易だったのでとっつきやすかったです。

そして何より、手間かけましたが、 0.12 系に対応した方が早かったかもしれない...

やんごとなき理由で 0.11 にしている場合以外は、最新に追従した方が良いですね♪

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

AWS ApplicationLoadBalancerリスナールールで特定 IP 以外をメンテナンスページ表示

f:id:kenzo0107:20190929233947p:plain

概要

AWS で運用している Web サービスでメンテナンスが必要となり、ALB でメンテ切り替えをした際の対応をまとめました。

手順

ALB Listener 一覧からルール変更をします。*1

f:id:kenzo0107:20190929232804p:plain

その後、

  1. 送信元IP = 社内IP (ex. 11.22.33.44/32 ) → default のTargetGroup へ転送 で「保存」
  2. 社内IP以外の送信元 IP 全て ( 0.0.0.0/0 ) → 503 text/html メンテ文言をレスポンス で 「保存」

f:id:kenzo0107:20190929231656p:plain

以上で 社内 IP は、通常通りアクセス可、それ以外はメンテナンスページを表示させることができました。

まとめてルールを追加して保存が出来ず、1つずつルール追加で保存になります。

レスポンスできる Content-Type って何があるの?

Content-Type に application/json 等も返せるので、 API サーバのメンテ時にはこちらを利用して文言を渡しました。

f:id:kenzo0107:20190929232354p:plain

ちょっとした注意

最大文字数が 1024 文字でした♪ f:id:kenzo0107:20190929232942p:plain

CSSレスポンス本文 に追加すると文字数 1024 を超えてしまいそうなので、S3 にアップロードし公開し、そちらを参照するようにしたりしました。

*1:今回 2 ポートのみ解放しており、80 は 443 に転送してるので、443 のみ対応しました。

Ansible FAILED! => {"msg": "to use the 'ssh' connection type with passwords, you must install the ssh pass program"} on MacOS

MacOS で Ansible を利用した所、掲題のようなエラーが発生しました。

その際の対策です。

brew install http://git.io/sshpass.rb

インストール完了まで少々時間かかりました。

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

参照

everythingshouldbevirtual.com

puppeteer で radio ボタンチェック

f:id:kenzo0107:20190906230848p:plain
puppeteer radio button

概要

puppeteer というスクレイピングツールで radio ボタンをチェックする為の備忘録です。

例題

以下のような radio ボタングループがあるとします。

<input type="radio" name="maker" value="1"> クリスタル映像
<input type="radio" name="maker" value="2"> ポセイドン企画

答え

クリスタル映像 を選びたい場合は、以下のようにします。

page.evaluate でブラウザ内での操作結果を返すようにします。
ブラウザ内なので、 document.querySelector が使えます。
そこで、 checked = true しています。

const selectedRadioSelector = `input[type="radio"][value="1"]`
await page.evaluate(
  s => (document.querySelector(s).checked = true),
  selectedRadioSelector
)

失敗例

click 処理は軒並み失敗しました。

page.WaitFor すると成功する、という記事を見ましたが、自身の環境 puppeteer (version=1.19.0) では、失敗してしまいました。

  • page.click
const selectedRadioSelector = `input[type="radio"][value="1"]`
page.click(selectedRadioSelector)
  • radio 要素を捕まえて、click
r = page.$(selectedRadioSelector)
r.click()
  • document.querySelector().click()
const selectedRadioSelector = `input[type="radio"][value="1"]`
await page.evaluate(
  s => (document.querySelector(s).click()),
  selectedRadioSelector
)

応用

以下のような radio ボタングループがあるとします。
上のと比べると label タグが追加されてます。

<input type="radio" id="group1" name="maker" value="1"> <label for="group1">クリスタル映像</label>
<input type="radio" id="group2" name="maker" value="2"> <label for="group1">ポセイドン企画</label>

このラベルを正規表現でマッチする方をチェックしてみます。

  • "映像" という文字を含む方をチェックする
const regex = "映像"
const regexpLabel = new RegExp(regex, 'g')

const r = await page.$$('input[type="radio"]')

label: for (const i in r) {
  // radio ボタンの id 要素
  const id = await (await r[i].getProperty('id')).jsonValue()
  // radio ボタンの value 要素
  const value = await (await r[i].getProperty('value')).jsonValue()

  // ラベル textContent 取得 ("クリスタル映像", "ポセイドン企画" を取得)
  const label = await page.$(`label[for="${id}"]`)
  const labelContent = await (await label.getProperty(
    'textContent'
  )).jsonValue()


  // ラベルの textContent が "映像" を含む場合、 true
  if (labelContent.match(regexpLabel)) {
    const selectedRadioSelector = `input[type="radio"][value="${value}"]`
    await page.evaluate(
      s => (document.querySelector(s).checked = true),
      selectedRadioSelector
    )
    // radio ボタンにチェック入れたので処理終了
    break label
  }
}

まとめ

非常につまづきやすい点だったので備忘録として残しました。

参考になれば幸いです。

WEB+DB PRESS Vol.109

WEB+DB PRESS Vol.109

  • 作者: 佐藤歩,加藤賢一,原一成,加藤圭佑,大塚健司,磯部有司,村田賢太,末永恭正,久保田祐史,吉川竜太,牧大輔,ytnobody(わいとん),前田雅央,浜田真成,竹馬光太郎,池田拓司,はまちや2,竹原,原田裕介,西立野翔磨,田中孝明
  • 出版社/メーカー: 技術評論社
  • 発売日: 2019/02/23
  • メディア: 単行本
  • この商品を含むブログを見る

puppeteer on Docker

概要

備忘録です。

Puppeteer をローカル環境を汚さず、 Docker 環境上で実行すべくまとめました。

サンプルスクリプトexample.comスクリーンショットを取得する、というシンプルなものです。

github.com

WEB+DB PRESS Vol.109

WEB+DB PRESS Vol.109

  • 作者: 佐藤歩,加藤賢一,原一成,加藤圭佑,大塚健司,磯部有司,村田賢太,末永恭正,久保田祐史,吉川竜太,牧大輔,ytnobody(わいとん),前田雅央,浜田真成,竹馬光太郎,池田拓司,はまちや2,竹原,原田裕介,西立野翔磨,田中孝明
  • 出版社/メーカー: 技術評論社
  • 発売日: 2019/02/23
  • メディア: 単行本
  • この商品を含むブログを見る

EC2 Instance Connect API で ssh ログインできるインタラクティブ cli tool "omssh" を作ってみました。

概要

oreno-mssh、またの名を omssh という AWS EC2 Instance Connect API を利用した ssh ログインツールを作成しました。

View post on imgur.com
imgur.com

作ろうと思った経緯

以前 EC2 Instance Connect API の登場により、EC2 Instance ID 指定で ssh ログインできる様になりました。

これにより、ssh ログイン接続するメンバーに秘密鍵を渡す、公開鍵を登録する等の作業が不要となりました。

kenzo0107.hatenablog.com

mssh を使用した場合、以下の様なコマンドで ssh ログインできます。

// Amazon Linux への ssh 
mssh <EC2 Instance ID> --profile <profile>

// Ubuntu
mssh ubuntu@<EC2 Instance ID> --profile <profile>

mssh を利用するには <EC2 Instance ID> 情報が必要で、毎回 Instance ID を調べる手間がありました。

もちろん踏み台サーバであれば、そうそう再起動され Instance ID が変更されることはないのでメモっておけば良いのでしょうが、 数が多くなると、管理が大変です。

その手間を fuzzyfinderインタラクティブに解決しようと思いました。

使用方法

README.md にもありますが、以下ステップでインストールできます。

$ git clone https://github.com/kenzo0107/omssh
$ cd omssh
$ make build && make install

今後

現在、EC2 Instance Connect を利用した運用に切り替えきれないところがあります。

理由は、EC2 インスタンスを Public Subnet に配置していないと EC2 Instance Connect API が利用できない為、踏み台までは EC2 Instance Connect API を利用し ssh ログインできたけど、その先は、秘密鍵が必要になる為です。

f:id:kenzo0107:20190804180237p:plain

おそらく近々 Private Subnet でも EC2 Instance Connect が利用できる様になるのでは?と期待しています。

そうなれば、踏み台にも omssh を置いて、鍵を意識せず、 IAM の権限だけで、 ssh 権限を管理できる様な世界が実現できます。

AWS サポートに願いを伝えておきます♪

改訂2版 みんなのGo言語

改訂2版 みんなのGo言語

EC2 Instance Connect で AWS EC2 への ssh 管理を IAM User or Group で簡単に♪

f:id:kenzo0107:20190628154100p:plain

概要

2019-06-28 に EC2 Instance Conncet が発表されました!

これによって、セキュリティグループと IAM 権限で ssh アクセス許可が可能になります。

例えば、
会社の IP からのみ、特定の IAM User Group に所属している IAM User に ssh アクセス権限を付与、
別のプロジェクトへ異動した、退職した場合は、その IAM User Group から削除で ssh アクセス権限を剥奪できます。

試験環境

macOS 10.14.3 で試しました。

事前準備

$ pip install -U awscli

$ aws s3api get-object --bucket ec2-instance-connect --key cli/ec2instanceconnectcli-latest.tar.gz ec2instanceconnectcli-latest.tar.gz

$ sudo pip install ec2instanceconnectcli-latest.tar.gz

発行した IAM User のパーミッション権限に以下を追加

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "EC2InstanceConnect",
            "Action": [
                "ec2:DescribeInstances",
                "ec2-instance-connect:SendSSHPublicKey"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}

この辺りは terraform 管理案件ですね。

EC2 Instance Conncect 対応 OS

  • Ubuntu>=16.04
  • AmazonLinux2>=2.0.20190618

ssh ログインする EC2側の設定

Ubuntu>=16.04

ec2-instance-connect をインストールしておく必要があります。

$ sudo apt-get update && sudo apt-get install ec2-instance-connect
$ dpkg -l | grep ec2-instance-connect

ii  ec2-instance-connect           1.1.9-0ubuntu3~18.04.1            all          Configures ssh daemon to accept EC2 Instance Connect ssh keys

AmazonLinux2>=2.0.20190618

ec2-instance-connect は設定済みです。

セキュリティグループ

ssh ログイン先となる EC2 インスタンスのセキュリティグループはアクセス元から ssh (22 port) を開けておく必要があります。

ssh ログインしてみる

local%$ mssh ubuntu@i-0f123456abcdefg --profile <profile> --region ap-northeast-1

一見、誰しもが ubuntu でログインしていて監査が不安になりますが、 CloudTrail はちゃんと誰がログインしたか見ています。

CloudTrail

f:id:kenzo0107:20190628160255p:plain
CloudTrail

以下イベントでログが残っています。

  • SendSSHPublicKey
  • DescribeInstances

SendSSHPublicKey の「イベントの表示」ボタンクリックで JSON が表示されますが、その中で、アクセス元 IP, IAM User Arn、アクセス先 インスタンスIDがわかります。

{
    "eventVersion": "1.05",
    "userIdentity": {
        "type": "IAMUser",
        "principalId": "ABCDEFGHIJK....",
        "arn": "arn:aws:iam::123456789012:user/hogehoge",
        "accountId": "123456789012",
        "accessKeyId": "AKIxxxxxxxxxxxxxxxx",
        "userName": "hogehoge",
        "sessionContext": {
            "attributes": {
                "mfaAuthenticated": "false",
                "creationDate": "2019-06-28T06:18:50Z"
            }
        }
    },
    "eventTime": "2019-06-28T06:18:51Z",
    "eventSource": "ec2-instance-connect.amazonaws.com",
    "eventName": "SendSSHPublicKey",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "xx.xxx.xxx.xxx",
    "userAgent": "aws-ec2-instance-connect-cli/1.0.0 Python/2.7.16 Darwin/18.2.0 Botocore/1.12.179",
    "requestParameters": {
        "instanceId": "i-0f.......",
        "osUser": "ubuntu",
        "SSHKey": {
            "publicKey": "ssh-rsa AAAAB....rHb"
        }
    },
    "responseElements": null,
    "requestID": "01234567-890a-1234-5b6d-......",
    "eventID": "f51...",
    "eventType": "AwsApiCall",
    "recipientAccountId": "123456789012"
}

こちらで EC2 インスタンスのアクセス履歴等はわかります。

まとめ

これまで ssh アカウント管理は手間でしたが、IAM 権限での管理によって非常に楽になりました♪

CloudTrail で監査もバッチリ!