LoginSignup
0
0

More than 3 years have passed since last update.

Saving technique! ? I tried starting and stopping AWS Client VPN with Lambda

Posted at

This article is an automatic translation of the article[a07ff10533cfe55b7936] below.
https://qiita.com/speaktech/items/a07ff10533cfe55b7936

What I did

Made AWS Client VPN start and stop on Lambda at a fixed time.

AWS Client VPN charges are incurred by associating a subnet with a Client VPN (end point), as quoted below.
If association is left, charges will be generated even during non-use hours such as night time, so subnet association is performed only during use time.

In the AWS Client VPN, for the number of active client connections,
There is an hourly charge for each of the> and the number of subnets associated with the Client VPN.

Quoted source: AWS Client VPN Fee

Assumption

-Since subnet connection does not result in VPN connection being broken and there is no charge, we define"Subnet association = Start AWS Client VPN"and"Subnet association = Stop AWS Client VPN".

-The procedure for setting up AWS Client VPN is omitted because it is based on this article (AWS Client VPN configuration memo).
(The configuration procedure by the AWS CLI is summarized. Thank you very much for your help.)

Environment

-** Client PC **

 -Windows 10 version 1803 (OS build 17134.829)
 -PowerShell version 5.1.17134.765
 -AWS CLI version 1.16.148
 -Python version 2.7.15
 -pip version 9.0.1
 -OpenVPN version 2.4.7

-** AWS Service **

 -AWS Client VPN
 -AWS IAM
 -AWS Lambda
 -AWS CloudWatch Events
 -AWS CloudWatch Logs

Setup steps

** Please set values ​​for variables appearing in the procedure beforehand, as shown in the example below.
Please note that variables are used for AWS Client VPN start/stop. **

設定例
# 1. IAM の Lambda 実行ロールを作成する
$Region="ap-northeast-1"
$RoleName="client_vpn_admin_role"

# 2. Lambda 関数の作成
# 起動用 Lambda 関数作成時に設定
$FunctionName="start_client_vpn"
$handlerName="start_client_vpn_function.lambda_handler"

# 停止用 Lambda 関数作成時に設定
$FunctionName="stop_client_vpn"
$handlerName="stop_client_vpn_function.lambda_handler"

# 3. CloudWatch Events の作成
# 起動用 CloudWatch Events 作成時に設定
$RuleName="start_client_vpn"
$Schedule="cron(0 23 * * ? *)" # UTC 時間で設定

# 停止用 CloudWatch Eventss 作成時に設定
$Schedule="cron(0 15 * * ? *)"
$RuleName="stop_client_vpn" # UTC 時間で設定

1. Create a Lambda Execution Role for IAM

The Lambda function requires a role created in IAM, and the role provides the access necessary to execute the function.
Here, the access privileges required to create a role and execute the Lambda function"** Log output to Cloudwatch Logs "and" Subnet association/Disassociation ** for Client VPN endpoint **"Attach the policy) to the role.

(Please create role-policy.json described below to the current directory to run AWS CLI beforehand.)

# ロールの作成
aws iam create-role --role-name $RoleName --assume-role-policy-document file://role-policy.json

# ロールの確認
aws iam get-role --role-name $RoleName

# ポリシーのアタッチ
aws iam attach-role-policy --role-name $RoleName --policy-arn "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"

aws iam attach-role-policy --role-name $RoleName --policy-arn "arn:aws:iam::aws:policy/AmazonEC2FullAccess"

# アタッチしたポリシーの確認
aws iam list-attached-role-policies --role-name $RoleName
role-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
       {
         "Action": "sts:AssumeRole",
         "Principal": {
           "Service": "lambda.amazonaws.com"
          },
          "Effect": "Allow",
          "Sid": ""
       }
    ]
}

2. Create Lambda Function

2.1 Create a Deployment Package

Create a package to deploy to Lamda in any directory.
(Reference: Move AWS CLI on Lambda to S3 Sync, Lambda with aws-cli Create a Scheduled Event)

mkdir lambda-cli

cd lambda-cli

pip install awscli -t .

Create the following Python script (aws, start_client_vpn_function.py, stop_client_vpn_function.py) in the created lambda-cli directory.

aws
import sys
import awscli.clidriver


def main():
    return awscli.clidriver.main()


if __name__ == '__main__':
    result = main()
    sys.exit(result)
start_client_vpn_function.py
import subprocess


def lambda_handler(event, context):
    client_vpn_endpoint_id = "[あなたの Client VPN エンドポイント ID]"
    subnet_id_list = ["あなたの 関連付けるサブネット ID(カンマ区切りで複数記述可)"]

    for subnet_id in subnet_id_list :
        cmd = "python aws ec2 associate-client-vpn-target-network --client-vpn-endpoint-id " + client_vpn_endpoint_id + " --subnet-id " + subnet_id
        result = subprocess.run(
            cmd.split(" "),
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT
            )
        print(result.stdout.decode())
stop_client_vpn_function.py
import subprocess


def lambda_handler(event, context):
    client_vpn_endpoint_id = "[あなたの Client VPN エンドポイント ID]"
    cmd = "python aws ec2 describe-client-vpn-target-networks --client-vpn-endpoint-id " + client_vpn_endpoint_id + " --query ClientVpnTargetNetworks[].AssociationId --output text"

    result = subprocess.run(
        cmd.split(" "),
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT
    )
    association_id_list = result.stdout.decode().strip().split()
    print("[DEBUG] association_id_list: %s" %  association_id_list)

    for association_id in association_id_list:
        cmd = "python aws ec2 disassociate-client-vpn-target-network --client-vpn-endpoint-id " + client_vpn_endpoint_id + " --association-id " + association_id

        result = subprocess.run(
        cmd.split(" "),
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT
        )

        print(result.stdout.decode())

After creating the Python script, zip the lambda-cli directory.

2.2 Deploy on Lambda

Create a Lambda function, specifying a ZIP file. (If you are using bash, replace the newline character with"`"→"\")

# ロール ARN の取得
$RoleArn=$(aws iam get-role --role-name $RoleName --query 'Role.Arn' --output text)

# Lambda 関数の作成
aws lambda create-function `
    --function-name $FunctionName `
    --runtime python3.7 `
    --role $RoleArn `
    --handler $handlerName `
    --timeout 120 `
    --zip-file fileb://lambda-cli.zip `
    --region $Region

# Lambda 関数の確認
aws lambda get-function --function-name $FunctionName

# 補足:Lambda 関数を更新
# update lambda function
aws lambda update-function-code `
    --function-name $FunctionName `
    --zip-file fileb://lambda-cli.zip `
    --publish

2.3 Test execution of Lambda function

You can do a test run with the GUI, but here we run it with the AWS CLI.

# Lambda 関数のテスト実行
aws lambda invoke --invocation-type Event `
    --function-name $FunctionName `
    --region $Region `
    --log-type Tail outfile.txt

It is OK if the following log is output to CloudWatch Logs.

起動
{
"AssociationId": "cvpn-assoc-XXXXXXXXXXXXXXXX",
"Status": {
"Code": "associating"
}
}
停止
{
"AssociationId": "cvpn-assoc-011618912b11f5840",
"Status": {
"Code": "disassociating"
}
}

3. Create CloudWatch Event

Create a triggering CloudWatch Event rule to schedule the Lambda function.

# CloudWatch Event のルールの作成
aws events put-rule --name $RuleName --schedule-expression "$Schedule" --state ENABLED

# CloudWatch Event のルールの確認
aws events describe-rule --name $RuleName

# CloudWatch Event の ARN の取得
$EventArn=$(aws events describe-rule --name $RuleName --query Arn --output text)

# Lambda 関数へのトリガー追加
aws lambda add-permission `
--function-name $FunctionName `
--statement-id $FunctionName `
--action 'lambda:InvokeFunction' `
--principal events.amazonaws.com `
--source-arn $EventArn

# Lambda 関数のARNの取得
$FunctionArn=$(aws lambda get-function --function-name $FunctionName --query Configuration.FunctionArn --output text)

# CloudWatch Event へのターゲット追加
aws events put-targets --rule $RuleName --targets "Id=$FunctionName,Arn=$FunctionArn"

# CloudWatch Event のターゲットの確認
aws events list-targets-by-rule --rule $RuleName

The settings are complete. Thank you for your hard work.

Finally

Using Client VPN is very easy to operate because you can directly access the resources in the VPC without going through a springboard server etc.

The cost comparison with the springboard server + EIP/NAT Gateway configuration is certainly high, but I think that unused time zone can reduce the large cost by the operation of disassociating the subnet.

Thank you for reading to the end.

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0