/images/header-bg-lowsrc-U2F0IEFwciAgMiAwODowOToxNiBVVEMgMjAyMgo=.webp
/images/header-bg-U2F0IEFwciAgMiAwODowOToxNiBVVEMgMjAyMgo=.webp
Artwork by 花咲ちゆ 
/images/header-bg-U2F0IEFwciAgMiAwODowOToxNiBVVEMgMjAyMgo=.webp
Artwork by 花咲ちゆ 

透過 AWS Lambda、API Gateway 和 AWS Go SDK,從 Mattermost 查看/開關 EC2 Instances

目前我實習的公司使用 Mattermost 作為內部通訊軟體,且 Mattermost 支援 Webhook 和 Slash Command。為了方便隨時隨地可以快速開啟/關閉 EC2,因此想寫一個下 Slash Command 指令的工具,直接呼叫 API 來控制和查看 EC2,免去登入 console 的麻煩,一定會方便許多。整體架構如上圖

我使用 Go 寫了一個程式處理 Mattermost 傳入的資料,並且透過 aws-sdk-go 對 EC2 進行操作。程式會在 Lambda 上執行,原始碼請參考我的 Github:https://github.com/LYTzeng/ec2ctl,日後考慮用 CloudFormation 讓需要的人快速佈署。這篇主要會介紹 API Gateway、Lambda 和部份 SDK 的使用。

先展示成果:

https://i.imgur.com/PEndaZv.gif

API Gateway

IAM Role

在建立一個 API 之前,我們先創一個 IAM Policy 並 Attach 到 Role。API Gateway 需要 Invoke Lambda 以及寫入 CLoudWatch 的權限。 進入 IAM 管理介面,首先 Create 一個 Policy 使其允許 Invoke Lambda,我們使用 JSON 來建立 Policy:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "lambda:InvokeFunction",
                "lambda:PutFunctionEventInvokeConfig"
            ],
            "Resource": "*"
        }
    ]
}

接著建立一個 Role 並 Attach policies。除了 attach 剛才建立的 policy,還需要 attach 一個 AmazonAPIGatewayPushToCloudWatchLogs 的 policy,可以直接搜尋,順便將 Role ARN複製下來,之後會使用到。接下來就可以建立一個 API。

https://i.imgur.com/QmuRVgE.png

建立 API Gateway

進入 API Gateway 介面,選擇 Create API,找到 REST API 選擇 Build,Choose the protocolRESTCreate new API 選擇 New PIAAPI Name輸入 API 名稱,Endpoint Type 保留預設 Regional 即可。

https://i.imgur.com/G4IZFLx.png

Create API 後進入 API 設定的頁面。選擇左側選單最下面的 Settings,將之前複製的 Role ARN 貼上,Role ARN 就是一個 IAM Role 的 ID,這可以提供 API Gateway 寫入 CloudWatch 的權限。

https://i.imgur.com/jhhmCyx.png

API Gateway 幾個組成元素大概有 ResourceMethodStagesResource Policy,這幾點設置完畢就能有一個可以運作的簡單 API 了。先簡單說明這幾個項目:

  • Resources 一個 Resource 可以接收多種的 HTTP request method,例如 GET/POST/OPTION 等等。 Resource 的名稱會包含在 URL 裡面。

  • Methods HTTP request 的單一種類,並且決定接收到這個 method 後下一步該做什麼,以及如何做出 response。例如:收到 HTTP POST 時 觸發 Lambda function,並且將 body 傳送至 Lambda,隨後將 Lambda 回傳的 response (如:402 Unauthorized) 回應給 Client。流程會顯示在設定頁面中。 https://i.imgur.com/jqrJpru.png

  • Stages Stage 代表這個 API 的開發階段,例如 dev, prod, beta, v2等,要產生一個可以被呼叫的 API,一定要 Deploy 到一個 Stage,Stage 的名稱會包含在 URL 中。

  • Resource Policy 提供 API 存取控制的機制,使用 JSON 格式來設定,條件可以使用 IAM,或是單一主機 IP等,可用來決定 被允許/禁止的動作,避免被不明人士存取到 API。

API Gateway 的 REST API 的 URL 的格式為:

1
https://{restapi-id}.execute-api.{region}.amazonaws.com/{stageName}/{resource}

假設我建立一個名為 ec2ctlresourcestagedev,API 位於 us-west-2 的 region,則我的 API URL 會是 https://{restapi-id}.execute-api.us-west-2.amazonaws.com/dev/ec2ctl

建立 Resource

左側選單選擇 Resources,會看到只有/這個符號,代表我們位於 base url,點擊 Action > Create Resource 建立 Resource,並決定 resource nameresource pathhttps://i.imgur.com/IJYR2UF.png

建立 Method

選擇 Action > Create Method,並選擇 POST。 https://i.imgur.com/GImWVwX.png https://i.imgur.com/RpvVp6E.png

Resource Policy

我們需要允許 API Gateway invoke Lambda funciton,而且只有 Mattermost server 和我們的測試主機的 SourceIp 是被允許的,使用 JSON 格式輸入設定。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "execute-api:Invoke",
            "Resource": "execute-api:/*/*/ec2ctl",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "192.0.2.1/32",
                        "192.0.2.2/32"
                    ]
                }
            }
        }
    ]
}

Deploy API 到指定 Stage

點選左側選單 ResourcesAction > Deploy API[New Stage] 後輸入 stage name,deploy 後即可使用這個 API。 https://i.imgur.com/IlIV3He.png

有一點需要特別注意的是,對 API 設定做任何更動後,請記得要重新 Deploy 才會生效

接著我們需要使用 Lambda 來和 API Gateway 串接。

Lambda

IAM Role

我們會使用 Lambda 達成幾項功能:讀取所有 region 的 EC2,並回傳相關資訊,以及開啟/關閉指定的 EC2。要進行這些操作,我們需要賦予 Lambda function 權限,所以須建立 IAM Role。先新增所需的 Policy,JSON 設定請複製這裡:https://gist.github.com/LYTzeng/f935732d160c8fa72e90a18deeed9ae4 ,再將這個 Policy Attach 至新增的 Role。

建立 Function

到 Lambda 界面,選擇 Create FunctionRuntime 選擇 Go 1.xPermissions 選擇 Use an existing role 找到剛才建立的 Role 並套用。

因為這次使用 Go 撰寫程式,必須先 compile 成能夠讓 Linux 執行的 binary (Lambda 底層 OS 使用 Amazon Linux) 並上傳,而且只能寫在 package main 裡面。你可以直接下載 compile 過的執行檔,透過 console 上傳 Function PackageHandler 設定為 main.upx

接著找到 Environment Variables 區塊新增環境變數,Key 請輸入 MATTERMOST_TOKENValue 為你的 Mattermost server 的 Slash command token

接著新增一個 Triggerhttps://i.imgur.com/VYJkazR.png

選擇 API GatewayAPI 就是剛才建立的 API,Deployment Stage 是剛才 Deploy 時命名的名稱 (如:dev),SecurityOpen,我們已經用 Token 做驗證,加上用 Resource Policy 白名單過濾,不必擔心這個 Open 設定。 https://i.imgur.com/jheCEwG.png

接著回到 API Gateway 頁面,選擇 /ec2ctl 底下的 POST,設定 Integration Request

  • Integration typeLambda Function
  • Use Lambda Proxy integration 務必勾選,此選項可透過 event 將 Request body 傳送至 Lambda。
  • Lambda Function 即剛才建立的 function https://i.imgur.com/Js98mml.png 儲存後可以看到整個 Method Execution 流程。

https://i.imgur.com/VinbrZD.png

這樣系統已經建制完畢,接下來可以使用 Postman 進行測試,或是直接在 Mattermost 開發環境測試。

Mattermost 架設與設定

本篇不會詳述這部份操作,請參考 Mattermost 的官方文件:

  • Title: ec2ctl
  • Description: Start/stop/list EC2 instances from Mattermost.
  • Command Trigger Word: ec2ctl
  • Request URL: https://{restapi-id}.execute-api.{region}.amazonaws.com/dev/ec2ctl
  • Request Method: POST
  • Response Username: 留空白
  • Response Icon: 留空白
  • Autocomplete: ✔
  • Autocomplete Hint: (version | (start | stop) -i <instance id> | ls [-r <region>])
  • Autocomplete Description: Start/stop/list EC2 instances from Mattermost.

設定完畢請複製 token 並貼上至 Lambda 的 Environment Variable MATTERMOST_TOKEN 中。 https://docs.mattermost.com/_images/slash_commands_token.png

使用 Mattermost 測試

此程式 ec2ctl 的 Slash command 規則如下:

1
2
3
4
5
6
7
Usage:
    ec2ctl version
    ec2ctl (stop | start) -i <id>
    ec2ctl ls [-r <region>]
Options:
    -i <id>    Specify an instance ID.
    -r <region>    Specify a region.

測試指令是否正常,/ec2ctl ls -r us-west-2輸入後可以看到輸出如下: https://i.imgur.com/jaTBuco.png

使用 Postman 測試

使用 Postman 測試時需要模仿 Mattermost 的 HTTP Request 格式,否則程式會出錯。圖中 Postman 的設置都是必要的參數,不過只須設定 Headers 以及 Body

Content-Typeapplication/x-www-from-urlencoded https://i.imgur.com/JhnYTU2.png

token 為 Mattermost slash command token text 欄位就是指令的內容 (不含 slash command 的 trigger /ec2ctl),例如ls -r us-west-2stop -i i-134134fwifiuser_name 請隨意輸入 https://i.imgur.com/1l8ueF8.png

若看到 Status: 200 OK 和一些內容,代表成功了。

https://i.imgur.com/c2t7NUi.png

References