【手順画像付き】AWS Client VPN を活用して AWS からクライアントデバイスへ通信する方法

AWS Client VPN で AWS 側からの通信を実現したい
  • Amazon WorkSpaces から社内のサーバへ通信したい要件が発生しました。
  • AWS Client VPN はリモートユーザーから AWS へのセキュアな接続を提供しますが、逆方向の通信には制約があります。
  • 本記事では、この制約を考慮し、AWS 側からクライアントデバイスにアクセスする方法を具体的に解説します。

背景

弊社ではリモートワークのために Amazon WorkSpaces を社員に配布しています。
社内のサーバーも AWS に移行させており、多くの社員がオフィスに出社しなくとも仕事ができる環境となりました。
現時点での状況や構成については以下の記事をご参照ください。
(本記事では、VPC や Amazon WorkSpaces や Active Directory などは、以下の記事にて構築済みとして説明を進めています。)

ただ、一部の社内サーバーはまだ AWS に移行することができていない状況です。
社内ネットワークの関係で、Amazon WorkSpaces から社内サーバーにアクセスすることができません。
そのため、社内サーバーを仕事で使う社員も、リモートワークができるように方法を考える必要があります。

課題

社内と AWS の VPC を繋ぐサービスについてはいくつかあり以下の3つがあります。

AWS VPN connection
AWS Client VPN
Direct Connect connection

https://repost.aws/ja/knowledge-center/connect-vpc

Direct Connect は敷居が高く今回の検討には、いれませんでした。
一番オーソドックスな AWS Site-to-Site VPN の使用を考えましたが、VPN ルーターの用意ができず断念しました。

現在一部で使用している AWS Client VPN も考えましたが、こちらは双方向のアクセスを実現するものではなく、一方向のアクセスのみ実現するものとなります。
クライアント側からの通信しかできず、AWS 側からクライアント側に通信することができません。
そのため、AWS Client VPN でも、今回の要件である Amazon WorkSpaces から社内サーバーへの通信を実現することができません。

EC2 インスタンス上に自身で VPN サーバを立てる方法もあるようですが、可能な限りマネージドな AWS サービスで運用したいと考えました。

方法

AWS Client VPN ではクライアント間の通信が可能です。
今回はこの機能を使って、VPC 内から社内サーバーまでの通信を実現することにしました。

具体的には、VPC 内に NAT インスタンスを用意し、VPC ルートテーブルで社内サーバー宛のトラフィックを AWS Client VPN エンドポイントへルーティングします。
社内サーバーは常に AWS Client VPN に接続している状態で運用してもらいます。

また、オプションの手順として、ドメイン名を自動的に割り当てる手順も紹介します。
社内サーバーに割り当てられるクライアント IP アドレスは指定できないため、再接続されるごとに変更されてしまいます。
そのため、社内サーバーのクライアント IP アドレスに解決されるプライベートなドメイン名を自動的に割り当てることにしました。
Amazon WorkSpaces から社内サーバーへ通信する際には、このドメイン名で通信してもらいます。

具体的には以下のような構成です。

なお、背景に記載した通り、VPC と Amazon WorkSpaces や Active Directory などは構築済みとします。

また、既に以下の記事で AWS Client VPN や Lambda 関数についても構築済みとなります。
そのため、青線の手順項目についてはざっくり用途を記載し、詳細な手順については以下の記事をご参照ください。

管理者側の手順

ACM に証明書を発行

Client VPN エンドポイントの作成にあたり、ACM の証明書が必要になります。
所有しているドメインがあれば一瞬で発行できますが、持っていない場合には、自身で証明書を発行して ACM にインポートする必要があります。
私の場合には、CloudShell で証明書を作成し、ACM にインポートしました。

Client VPN エンドポイントの作成と設定

ここが原因で後の NAT インスタンスの設定が躓きました。
NAT インスタンスを設定する際に、構築する EC2 インスタンスに SSH やセッションマネージャーで接続します。
ただ、スプリットトンネルが無効だと、すべてトラフィックが VPN 経由に切り替わるため、EC2 インスタンスへの接続が切断されます。
スプリットトンネルは有効にすることをおすすめします。

Active Directory ユーザーの追加

社内サーバーや NAT インスタンスで使用する、AWS Client VPN の認証ユーザーを作成します。

  1. 以下の URL から、ユーザーを作成します。
    適宜 <Directory ID> の箇所を、ご自身のディレクトリ ID に置き換えてください。
    https://ap-northeast-1.console.aws.amazon.com/directoryservicev2/home?region=ap-northeast-1&tab=users#!/directories/<Directory ID>/createUser
  2. [Step 1] ユーザーの詳細を指定
    • ユーザーログイン名:NatInstance
    • 新しいパスワード:P@ssW0rd
    • パスワードを確認:P@ssW0rd
    • それ以外は空欄のまま
画像
  1. [Step 2 - optional] ユーザーをグループに追加
    • どこにも追加せずそのまま Next
画像
  1. [Step 3] ユーザーの確認と追加
画像
  1. 次に社内サーバー用のユーザーを同様に作成します。
    通信したい社内サーバーが複数ある場合には、各社内サーバーごとにユーザーを作成します。
    適宜 <Directory ID> の箇所を、ご自身のディレクトリ ID に置き換えてください。
    https://ap-northeast-1.console.aws.amazon.com/directoryservicev2/home?region=ap-northeast-1&tab=users#!/directories/<Directory ID>/createUser
  2. [Step 1] ユーザーの詳細を指定
    • ユーザーログイン名:OnPre1
    • 新しいパスワード:P@ssW0rd
    • パスワードを確認:P@ssW0rd
    • それ以外は空欄のまま
画像
  1. [Step 2 - optional] ユーザーをグループに追加
    • どこにも追加せずそのまま Next
画像
  1. [Step 3] ユーザーの確認と追加
画像

EC2 インスタンスの起動

NAT インスタンスとして使用する、EC2 インスタンスを起動します。
こちらは主に AWS CLI コマンドで構築していきます。

  1. 以下の AWS CLI コマンドで、EC2 インスタンスにセッションマネージャーで接続するためのインスタンスプロファイルを作成します。
ROLE_NAME="SSMRole-NatInstance"

# Create IAM role
aws iam create-role \
    --role-name $ROLE_NAME \
    --assume-role-policy-document '{
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Principal": {
                    "Service": "ec2.amazonaws.com"
                },
                "Action": "sts:AssumeRole"
            }
        ]
    }'

# Attach IAM policy for SSM
aws iam attach-role-policy \
    --role-name $ROLE_NAME \
    --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

# Attach inline policy for Client VPN
aws iam put-role-policy \
    --role-name $ROLE_NAME \
    --policy-name ClientVpnExportPolicy \
    --policy-document '{
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": "ec2:ExportClientVpnClientConfiguration",
                "Resource": "*"
            }
        ]
    }'

# Create instance profile
aws iam create-instance-profile \
    --instance-profile-name $ROLE_NAME

# Add IAM role to instance profile
aws iam add-role-to-instance-profile \
    --instance-profile-name $ROLE_NAME \
    --role-name $ROLE_NAME
  1. 以下の AWS CLI コマンドで、VPC 内からのトラフィックを許可するルールを持つ、NAT インスタンス用のセキュリティグループを作成します。
    ※ アウトバウンドルールはデフォルトで作成されているので設定不要です。
SourceProtocolPort
10.0.0.0/16 (VPC CIDR)AllAll
Inbound Rule
DestinationProtocolPort
0.0.0.0/0AllAll
Outbound Rule
VPC_ID="<VPC ID of Amazon WorkSpaces>"

# Create a security group for nat instance
SECURITY_GROUP_ID=$(aws ec2 create-security-group \
    --group-name Nat-sg \
    --description "Security group for nat instance" \
    --vpc-id $VPC_ID \
    --query 'GroupId' \
    --output text)

# Add an inbound rule to allow all traffic from VPC
aws ec2 authorize-security-group-ingress \
    --group-id $sg \
    --protocol -1 \
    --cidr 10.0.0.0/16
  1. 以下の AWS CLI コマンドで、このセキュリティグループを持つ EC2 インスタンスを、パブリックサブネットに起動します。
    また、自身の IP アドレス以外も送受信できるように送信元/送信先チェックを無効化します。
SUBNET_ID="<Public subnet ID>"

# Get Amazon Linux 2 AMI ID
AMI_ID=$(aws ec2 describe-images \
    --owners amazon \
    --filters "Name=name,Values=al2023-ami-20*-kernel-6.1-x86_64" \
    --query 'sort_by(Images, &CreationDate)[-1].ImageId' \
    --output text)

# Run EC2 instance for nat
INSTANCE_ID=$(aws ec2 run-instances \
    --image-id $AMI_ID \
    --count 1 \
    --instance-type t2.micro \
    --iam-instance-profile Name=SSMRole \
    --security-group-ids $SECURITY_GROUP_ID \
    --subnet-id $SUBNET_ID \
    --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=NatInstance}]' \
    --query 'Instances[0].InstanceId' \
    --output text)

# Disable source/destination checks
aws ec2 modify-instance-attribute \
    --instance-id "$INSTANCE_ID" \
    --source-dest-check "{\"Value\": false}"

NAT インスタンスの構築

セッションマネージャーで EC2 インスタンスに接続し、NAT インスタンスを構築していきます。
以下の URL をもとに NAT インスタンスを構築・準備していきます。

NAT インスタンスのチュートリアル
https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/work-with-nat-instances.html

  1. 以下の AWS CLI コマンドで、セッションマネージャーを通して EC2 インスタンスに接続します。
# Get EC2 instance ID
EC2_ID=$(aws ec2 describe-instances \
    --filters "Name=tag:Name,Values=NatInstance" \
    --query "Reservations[].Instances[].InstanceId" \
    --output text)

# Start connect by Session Manager
aws ssm start-session --target $EC2_ID
  1. 以下のコマンドにて、Client VPN へ接続するための準備をします。
    動的に入力するための、Client VPN 接続時のユーザー名とパスワードが記載されたファイルも用意します。
## Bellow command is at Session Manager.
# Install Open VPN
sh-4.2$ sudo yum update -y
sh-4.2$ sudo yum install openvpn -y

# Create file for authorization
sh-4.2$ echo "NatInstance" | sudo tee /etc/openvpn/client/auth.txt > /dev/null
sh-4.2$ echo "P@ssW0rd" | sudo tee -a /etc/openvpn/client/auth.txt > /dev/null

# Ready for Open VPN config
sh-4.2$ aws ec2 export-client-vpn-client-configuration \
    --client-vpn-endpoint-id cvpn-endpoint-0123456abcd123456 \
    --output text | sudo tee /etc/openvpn/client/client.ovpn > /dev/null
sh-4.2$ sudo sed -i 's|auth-user-pass.*|auth-user-pass /etc/openvpn/client/auth.txt|' /etc/openvpn/client/client.ovpn
  1. vim コマンドなどで、NAT インスタンスを Client VPN に接続するスクリプトを作成します。
    cron で設定し1分ごとに接続を試みる運用とします。
    これにより、NAT インスタンスが起動していて、かつ、Client VPN エンドポイントが Available の時には、自動で Client VPN に接続されるようになります。
## Bellow command is at Session Manager.
# Ready for connecting Client VPN
sh-4.2$ sudo vim /usr/local/bin/check_vpn.sh

# Setting cron
sh-4.2$ sudo yum install cronie -y
sh-4.2$ sudo systemctl enable crond
sh-4.2$ sudo systemctl start crond
sh-4.2$ sudo chmod +x /usr/local/bin/check_vpn.sh
sh-4.2$ sudo crontab -e
Bash スクリプト "check_vpn.sh" の内容
#!/bin/bash

VPN_CONFIG="/etc/openvpn/client/client.ovpn"

# Check if OpenVPN process is running
if ! pgrep -f "openvpn --config $VPN_CONFIG" > /dev/null; then
    echo "$(date): OpenVPN process is not running. Attempting to reconnect..."
    sudo openvpn --config $VPN_CONFIG --daemon
else
    echo "$(date): OpenVPN process is already running. No action taken."
fi
cron 設定の内容
* * * * * /usr/local/bin/check_vpn.sh >> /var/log/vpn_check.log 2>&1
  1. 上記のスクリプトが実行されて NAT インスタンスが Client VPN に接続されるのを待ちます。
    以下のコマンドを実行し、tun0 インターフェイスが作成されていることを確認します。
## Bellow command is at Session Manager.
# Check interface
sh-4.2$ netstat -i
Kernel Interface table
Iface             MTU    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
enX0             9001     9612      0      0 0          8504      0      0      0 BMRU
lo              65536       12      0      0 0            12      0      0      0 LRU
tun0             1500        0      0      0 0             3      0      0      0 MOPRU
  1. NAT の設定をしていきます。
    ここで、デフォルトの eth0 や enX0 ではなく、VPN トンネルである tun0 にルーティングするようにします。
    また、最後に VPC CIDR 宛の通信が ENI ではなく tun0 経由だと、ループしてしまうので、そうならないように、ルートテーブルを上書きします。
    この設定により、クライアントへのトラフィックが Client VPN エンドポイントへルーティングされるようになります。
## Bellow command is at Session Manager.
# Enable iptables
sh-4.2$ sudo yum install iptables-services -y
sh-4.2$ sudo systemctl enable iptables
sh-4.2$ sudo systemctl start iptables

# Enable packet transfer
sh-4.2$ sudo sysctl -w net.ipv4.ip_forward=1
sh-4.2$ echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf

# Setting nat
sh-4.2$ sudo /sbin/iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
sh-4.2$ sudo /sbin/iptables -F FORWARD
sh-4.2$ sudo service iptables save

# Delete VPC CIDR route to tun0
sh-4.2$ ip route delete 10.0.0.0/16 dev tun0 

ip route delete 10.0.0.0/16 dev tun0 コマンドを打つ代わりに、check_vpn.sh にコマンドを埋め込むことでも対応可能です。
その場合には、check_vpn.sh に usr/sbin/ip route delete 10.0.0.0/16 dev tun0 を追記してください。

ルートテーブルの設定

VPC ルートテーブルと、Client VPN のルートテーブルを変更していきます。

  1. 以下の URL から VPC ルートテーブルを変更します。
    クライアント CIDR 192.168.252.0/22 へのトラフィックを、NAT インスタンスにルーティングするように、VPC ルートテーブルを変更します。
    https://ap-northeast-1.console.aws.amazon.com/vpcconsole/home?region=ap-northeast-1#RouteTables:
  2. VPC 内の全てのルートテーブルに以下のルートを追加します。
    • 送信先:192.168.252.0/22(クライアント CIDR
    • ターゲットNAT インスタンス ID
画像
  1. 以下の URL より、Client VPN エンドポイントを選択し [ルートテーブル] タブからルートを追加します。
    https://ap-northeast-1.console.aws.amazon.com/vpcconsole/home?region=ap-northeast-1#ClientVPNEndpoints:
画像
  1. クライアント間の通信をルーティングするように、local ルートを追加します。
    • ルート送信先:192.168.252.0/22(クライアント CIDR
    • ターゲットネットワーク関連付けのサブネット ID:local
画像

これで NAT インスタンスの準備は完了です。
WorkSpaces 自体が Client VPN 接続に接続していなくとも、各クライアントへのトラフィックは Client VPN エンドポイントへルーティングされます。
そのため、WorkSpaces から、Client VPN エンドポイントに接続された社内サーバーへ通信することができるようになります。

管理者側の手順(オプション)

上記のままでもクライアント IP アドレスを指定して通信が可能です。
ただ、クライアント IP アドレスは固定されたものではないので、弊社では一意なドメイン名を割り当てて運用しています。

用いた方法としては、Lambda ハンドラーを Client VPN に設定し、レコードを作成する Lambda 関数を呼び出します。
必須ではないので、この辺は簡易的に説明します。

Route 53 ホストゾーンの作成

クライアント IP アドレスに割り当てるドメインを準備します。
ドメインを取得する必要はなく、Route 53 プライベートホストゾーンを作成します。

Lambda ハンドラーの作成

AWS Client VPN への接続と同時に実行したいプログラムなどを、Lambda ハンドラーで設定することができます。
今回は、この Lambda ハンドラーを使って、接続の都度、クライアント IP アドレスを Route 53 ホストゾーンに登録するようにします。
コードについては後の手順で変更します。

Lambda ハンドラーの変更

  1. ドメインを割り当てる用の Lambda 関数を呼び出すように、Lambda ハンドラーのコードを以下に書き直します。
    青文字 の部分が追記箇所となります。
Lambda ハンドラーのコード
import boto3
import json

def lambda_handler(event, context):
    
    # Resource information
    directory = '<Your Active Directory ID>'
    hostedzone = '<Your Route 53 Hosted Zone ID>'
    domain = 'user.workspaces'
    
    # Get username from event
    username = event.get('username')
    
    if not username:
        return {
            "allow": True,
            "error-msg-on-failed-posture-compliance": "Username not provided",
            "posture-compliance-statuses": [],
            "schema-version": "v2"
        }
    
    # Create WorkSpaces client
    workspaces_client = boto3.client('workspaces')
    
    # Get the user's WorkSpaces
    response = workspaces_client.describe_workspaces(
        DirectoryId=directory,
        UserName=username
    )
    workspaces = response.get('Workspaces', [])

    if not workspaces:

        lambda_client = boto3.client('lambda')
        payload = {
            "endpoint-id": event.get('endpoint-id'),
            "connection-id": event.get('connection-id'),
            "username": username,
            "domain": domain,
            "hostedzone-id": hostedzone
        }
        
        try:
            response = lambda_client.invoke(
                FunctionName="AWSClientVPN-CreateClientRecord",
                InvocationType='Event',
                Payload=json.dumps(payload)
            )
            print("Invoking Lambda for creating record")
            
        except Exception as e:
            print("Error invoking target Lambda:", e)
            raise

        return {
            "allow": True,
            "error-msg-on-failed-posture-compliance": f'No WorkSpaces found for user {username}',
            "posture-compliance-statuses": [],
            "schema-version": "v2"
        }
    
    # Start WorkSpaces
    workspace_id = workspaces[0]['WorkspaceId']
    workspaces_client.start_workspaces(
        StartWorkspaceRequests=[
            {'WorkspaceId': workspace_id}
        ]
    )
    
    # Get the IP address of the WorkSpaces
    workspace_ip = workspaces[0]['IpAddress']
    
    # Create a Route 53 client
    route53_client = boto3.client('route53')
    
    # Create a subdomain name
    subdomain = f"{username}.{domain}”
    
    # Create a record
   try:
        route53_client.change_resource_record_sets(
            HostedZoneId=hostedzone,
            ChangeBatch={
                'Changes': [
                    {
                        'Action': 'CREATE',
                        'ResourceRecordSet': {
                            'Name': subdomain,
                            'Type': 'A',
                            'TTL': 300,
                            'ResourceRecords': [{'Value': workspace_ip}]
                        }
                    }
                ]
            }
        )
        
    except Exception as ex:
        route53_client.change_resource_record_sets(
            HostedZoneId=hostedzone,
            ChangeBatch={
                'Changes': [
                    {
                        'Action': 'UPSERT',
                        'ResourceRecordSet': {
                            'Name': subdomain,
                            'Type': 'A',
                            'TTL': 300,
                            'ResourceRecords': [{'Value': workspace_ip}]
                        }
                    }
                ]
            }
        )    
    
    return {
        "allow": True,
        "error-msg-on-failed-posture-compliance": f'Successfully started WorkSpaces for user {username}',
        "posture-compliance-statuses": [],
        "schema-version": "v2"
    }
  1. 以下の AWS CLI コマンドで Lambda 関数の呼び出しが可能なように IAM ロールにポリシーを追加します。
    ついでに、後のクライアント IP アドレスの取得のために、EC2 サービスへのアクセスを許可するポリシーも追加します。
AWS CLI コマンド
aws iam put-role-policy \
    --role-name LambdaRole-WorkSpaces \
    --policy-name DescribeVpnAndInvokeLambda \
    --policy-document '{
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": "ec2:DescribeClientVpnConnections",
                "Resource": "*"
            },
            {
                "Effect": "Allow",
                "Action": "lambda:InvokeFunction",
                "Resource": "*"
            }
        ]
    }'

Lambda 関数の作成

  1. 以下の URL から、ドメインを割り当てるための Lambda 関数を作成します。
    https://ap-northeast-1.console.aws.amazon.com/lambda/home?region=ap-northeast-1#/create/function
    • 関数名:AWSClientVPN-CreateClientRecord
    • ランタイム:Python 3.13
    • 実行ロール:LambdaRole-WorkSpaces
    • タイムアウト:2分
    • コード:以下に記載
Lambda 関数のコード
import boto3
import time

def lambda_handler(event, context):
    
    print("Start processing")
    time.sleep(60)
    print("Finished processing after delay")

    # Received event
    print("Received event:", event)

    # Getting client IP address
    endpoint_id = event.get('endpoint-id')
    connection_id = event.get('connection-id')
    ec2_client = boto3.client('ec2')
    try:
        connection_info = ec2_client.describe_client_vpn_connections(
            ClientVpnEndpointId=endpoint_id,
            Filters=[
                {
                    'Name': 'connection-id', 
                    'Values': [connection_id]
                }
            ],
        )
        client_ip = connection_info['Connections'][0]['ClientIp']
    except Exception as e:
        print("Error retrieving connection info:", e)
        return {
            "status": "error",
            "message": f"Failed to retrieve client IP for connection {connection_id}",
            "error": str(e)
        }

    # Getting client IP address
    username = event.get('username')
    hostedzone = event.get('hostedzone-id')
    domain = event.get('domain')
    subdomain = f"{username}.{domain}"
    route53_client = boto3.client('route53')
    try:
        route53_client.change_resource_record_sets(
            HostedZoneId=hostedzone,
            ChangeBatch={
                'Changes': [
                    {
                        'Action': 'CREATE',
                        'ResourceRecordSet': {
                            'Name': subdomain,
                            'Type': 'A',
                            'TTL': 300,
                            'ResourceRecords': [{'Value': client_ip}]
                        }
                    }
                ]
            }
        )
        
    except Exception as ex:
        route53_client.change_resource_record_sets(
            HostedZoneId=hostedzone,
            ChangeBatch={
                'Changes': [
                    {
                        'Action': 'UPSERT',
                        'ResourceRecordSet': {
                            'Name': subdomain,
                            'Type': 'A',
                            'TTL': 300,
                            'ResourceRecords': [{'Value': client_ip}]
                        }
                    }
                ]
            }
        )  
    
    return {
        "status": "success",
        "message": f"Route 53 record created/updated for {subdomain} with IP {client_ip}",
        "record": {
            "name": subdomain,
            "type": "A",
            "value": client_ip
        }
    }

これで社内サーバーが Client VPN に接続したときに、自動的にドメイン名が割り当てられます。
社内サーバー用のユーザー名が OnPre1 なので、onpre1.user.workspaces というレコードが作成されます。
このドメイン名は、その時のクライアント IP アドレスに解決されます。
そのため、クライアント IP アドレスを確認せずとも、上記のドメイン名で社内サーバーに接続することが可能になります。

ユーザー側の手順

WorkSpaces からアクセスできるように、社内サーバーは常に Client VPN エンドポイントに接続しておく必要があります。
社員には、社内サーバーの準備として以下を実施してもらいました。

  1. Client VPN 設定ファイル (client.ovpn) とユーザー認証情報ファイル (auth.txt) を社内サーバーに配置します。
    設定ファイルについては、ユーザー認証情報ファイルのパスを指定しておきます。
auth.txt
Onpre1
P@ssW0rd
client.ovpn (Linux)
client
dev tun
proto udp
remote cvpn-endpoint-0123456abcd123456.prod.clientvpn.ap-northeast-1.amazonaws.com 443
remote-random-hostname
resolv-retry infinite
nobind
remote-cert-tls server
cipher AES-256-GCM
verb 3
<ca>
-----BEGIN CERTIFICATE-----
MIIDfzCCAmegAwIBAgIJAOA5PQH+hP7rMA0GCSqGSIb3DQEBCwUAMF8xCzAJBgNV
BAYTAkpQMQswCQYDVQQIDAJUTzEPMA0GA1UEBwwGVE9LT1lPMRMwEQYDVQQKDApU
ZXN0IENvbXBhbnkxEDAOBgNVBAMMB0NhIFJvb3QwHhcNMjAwMTAxMDAwMDAwWhcN
MzAwMTAxMDAwMDAwWjBfMQswCQYDVQQGEwJKUDELMAkGA1UECAwCVE8xDzANBgNV
BAcMBlRPS09ZTzETMBEGA1UECgwKVGVzdCBDb21wYW55MRAwDgYDVQQDDAdDYXIg
Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALcU5uHP+WOrBaY2
yz3s1cHL3AOPl0RxCZDLu5rDZbBvlk39awQJKIF7CxZECt/DzOMm0eQzImsnOUxp
ciTV0D9+H/Q+dBxQnnMeJ/yf9lC0ESYBCJhzXPEzZs8zNcAbGMBofFPKOP1P1bYh
oxvBqYw0vukPEXMCAwEAAaNTMFEwHQYDVR0OBBYEFHX6P8hUt9j2ZZe6ED69Czfq
Xm0uMB8GA1UdIwQYMBaAFHX6P8hUt9j2ZZe6ED69CzfqXm0uMA8GA1UdEwEB/wQF
MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFxAjswEUOgW4uz5Bj5ViX3gWpF98UXg
3yY1P32PzVgOhKw==
-----END CERTIFICATE-----
</ca>
auth-user-pass /auth.txt

reneg-sec 0

verify-x509-name server name
client.ovpn (Windows)
client
dev tun
proto udp
remote cvpn-endpoint-0123456abcd123456.prod.clientvpn.ap-northeast-1.amazonaws.com 443
remote-random-hostname
resolv-retry infinite
nobind
remote-cert-tls server
cipher AES-256-GCM
verb 3
<ca>
-----BEGIN CERTIFICATE-----
MIIDfzCCAmegAwIBAgIJAOA5PQH+hP7rMA0GCSqGSIb3DQEBCwUAMF8xCzAJBgNV
BAYTAkpQMQswCQYDVQQIDAJUTzEPMA0GA1UEBwwGVE9LT1lPMRMwEQYDVQQKDApU
ZXN0IENvbXBhbnkxEDAOBgNVBAMMB0NhIFJvb3QwHhcNMjAwMTAxMDAwMDAwWhcN
MzAwMTAxMDAwMDAwWjBfMQswCQYDVQQGEwJKUDELMAkGA1UECAwCVE8xDzANBgNV
BAcMBlRPS09ZTzETMBEGA1UECgwKVGVzdCBDb21wYW55MRAwDgYDVQQDDAdDYXIg
Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALcU5uHP+WOrBaY2
yz3s1cHL3AOPl0RxCZDLu5rDZbBvlk39awQJKIF7CxZECt/DzOMm0eQzImsnOUxp
ciTV0D9+H/Q+dBxQnnMeJ/yf9lC0ESYBCJhzXPEzZs8zNcAbGMBofFPKOP1P1bYh
oxvBqYw0vukPEXMCAwEAAaNTMFEwHQYDVR0OBBYEFHX6P8hUt9j2ZZe6ED69Czfq
Xm0uMB8GA1UdIwQYMBaAFHX6P8hUt9j2ZZe6ED69CzfqXm0uMA8GA1UdEwEB/wQF
MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFxAjswEUOgW4uz5Bj5ViX3gWpF98UXg
3yY1P32PzVgOhKw==
-----END CERTIFICATE-----
</ca>
auth-user-pass C:\\Users\\user1\\Desktop\\auth.txt

reneg-sec 0

verify-x509-name server name

ここからは社内サーバーの OS に合わせて、定期的に Open VPN クライアントで Client VPN への接続を確認する仕組みを設定します。
どちらも Open VPN クライアントをダウンロードします。

  1. Linux の場合には、NAT インスタンスのときと同じように cron で以下の Bash スクリプトを定期的に実行します。
Bash スクリプト
#!/bin/bash

VPN_CONFIG="client.ovpn"

# Check if OpenVPN process is running
if ! pgrep -f "openvpn --config $VPN_CONFIG" > /dev/null; then
    echo "$(date): OpenVPN process is not running. Attempting to reconnect..."
    sudo openvpn --config $VPN_CONFIG --daemon
else
    echo "$(date): OpenVPN process is already running. No action taken."
fi
  1. Windows の場合には、タスクスケジューラで以下の Bat ファイルを定期的に実行します。
Bat ファイル
@echo off
:: Path to the OpenVPN executable
set OPENVPN_PATH="C:\Program Files\OpenVPN\bin\openvpn.exe"

:: Configuration file name (the .ovpn extension can be omitted)
set CONFIG_FILE=client

:: Check the connection status
tasklist | find /I "openvpn.exe" >nul
if %ERRORLEVEL%==0 (
    echo [%date% %time%] OpenVPN is already running.
    exit /b
)

:: Start the OpenVPN connection
echo [%date% %time%] Starting OpenVPN connection...
%OPENVPN_PATH% --config "C:\Program Files\OpenVPN\config\%CONFIG_FILE%.ovpn" --log "C:\Program Files\OpenVPN\log\%CONFIG_FILE%.log"

:: Verify connection (optional)
if %ERRORLEVEL%==0 (
    echo [%date% %time%] OpenVPN connected successfully.
) else (
    echo [%date% %time%] OpenVPN connection failed. Check log for details.
)

exit /b
  1. onpre1.user.workspaces を指定して、WorkSpaces から社内サーバーへ接続してもらいます。

なかなか社員側に実施してもらうにはハードルが高く、社員に社内サーバーの手順を完遂してもらうのは難しかったです。
というか、VPN ルーターが用意できたら AWS Site-to-Site VPN でより簡単にできますし、社内サーバーを AWS に移行すれば VPN 自体不要です。
来年度の予算で VPN ルーターの購入を申請しつつ、社員には社内サーバーの AWS への移行を促し、それまでの間として本構成で運用しようと思います。

タイトルとURLをコピーしました