【带图操作指南】使用 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 的服务,有以下三种选择:

AWS VPN connection
AWS Client VPN
Direct Connect connection

https://repost.aws/zh-Hans/knowledge-center/connect-vpc

Direct Connect 门槛较高,本次没有考虑。
我们曾考虑过最常见的 AWS Site-to-Site VPN,但因无法准备 VPN 路由器而放弃了。

我们也考虑了当前部分使用的 AWS Client VPN,但该服务只能实现单向访问,而无法实现双向通信。
Client 端只能发起通信,AWS 端无法主动向 Client 端发起通信。
因此,AWS Client VPN 也无法满足本次的需求,即从 Amazon WorkSpaces 访问公司内部服务器。

虽然可以在 EC2 实例上自行搭建 VPN 服务器,但我们希望尽可能使用托管的 AWS 服务进行运维。

方法

AWS Client VPN 支持客户端之间的通信。
这次我们利用这一功能,实现了从 VPC 内部到公司内部服务器的通信。

具体来说,我们在 VPC 内准备了一台 NAT 实例,并通过 VPC 路由表将发往公司内部服务器的流量路由到 AWS Client VPN 端点。
公司内部服务器将始终保持连接到 AWS Client VPN 的状态进行运作。

此外,作为可选步骤,我们还介绍了自动分配域名的步骤。
由于无法指定分配给公司内部服务器的客户端 IP 地址,每次重新连接时 IP 地址都会发生变化。
因此,我们自动分配了一个私人域名,使其解析为公司内部服务器的客户端 IP 地址。
当从 Amazon WorkSpaces 访问公司内部服务器时,可以通过该域名进行通信。

具体架构如下所示:

正如背景部分所述,我们假设 VPC、Amazon WorkSpaces 和 Active Directory 等已完成配置。

此外,以下文章中已介绍了 AWS Client VPN 和 Lambda 函数的构建过程。
因此,对于蓝色线条标注的步骤,仅概述其用途,详细步骤请参考以下文章:

管理员方面的步骤

ACM 中签发证书

创建 Client VPN 端点时,需要一个 ACM 证书。
如果拥有自己的域名,可以立即签发证书,但如果没有域名,则需要自己生成证书并将其导入到 ACM 中。
在我的情况下,我通过 CloudShell 创建了证书并将其导入 ACM。

创建并配置 Client VPN 端点

在设置 NAT 实例时,这里成为了问题的根源。
设置 NAT 实例时,需要通过 SSH 或会话管理器连接到构建的 EC2 实例。
但是,如果禁用分流隧道(Split Tunnel),所有流量都会切换到通过 VPN,这会导致与 EC2 实例的连接被中断。
建议启用分流隧道。

添加 Active Directory 用户

为公司内部服务器或 NAT 实例创建 AWS Client VPN 的认证用户。

  1. 请从以下 URL 创建用户,并将 <Directory ID> 替换为您的目录 ID:
    https://us-east-1.console.aws.amazon.com/directoryservicev2/home?region=us-east-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://us-east-1.console.aws.amazon.com/directoryservicev2/home?region=us-east-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 命令用于创建一个实例配置文件,以便通过会话管理器(Session Manager)连接到 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 命令用于创建一个用于 NAT 实例的安全组,该安全组包含允许来自 VPC 内部流量的规则。
    注:出站规则默认已创建,因此无需设置。
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 实例

通过会话管理器(Session Manager)连接到 EC2 实例,并开始构建 NAT 实例。
根据以下 URL 构建和准备 NAT 实例。

NAT 实例教程
https://docs.aws.amazon.com/zh_cn/vpc/latest/userguide/work-with-nat-instances.html

  1. 以下 AWS CLI 命令用于通过会话管理器(Session Manager)连接到 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 作业,每分钟尝试一次连接。
    这样,当 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。
    此处需要将路由设置为通过 VPN 隧道接口 tun0,而不是默认的 eth0enX0
    此外,如果发往 VPC CIDR 的通信通过 tun0 而不是 ENI,会导致循环。因此,需要覆盖路由表以防止此问题。
    通过此配置,可以将客户端的流量路由到 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://us-east-1.console.aws.amazon.com/vpcconsole/home?region=us-east-1#RouteTables:
  2. 在 VPC 的所有路由表中添加以下路由:
    • 目标192.168.252.0/22客户端 CIDR
    • 目标NAT 实例 ID
图片
  1. 接着,请通过以下 URL 选择 Client VPN 终端并从 [路由表] 标签页中添加路由。
    https://us-east-1.console.aws.amazon.com/vpcconsole/home?region=us-east-1#ClientVPNEndpoints:
图片
  1. 为了路由客户端之间的通信,请添加 local 路由。
    • 路由目标192.168.252.0/22客户端 CIDR
    • 目标网络关联的子网 IDlocal
图片

至此,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 处理程序在每次连接时将客户端 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://us-east-1.console.aws.amazon.com/lambda/home?region=us-east-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.us-east-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.us-east-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

接下来,根据内部服务器的操作系统,设置定期检查 OpenVPN 客户端连接到 Client VPN 的机制。
无论是 Linux 还是 Windows,都需要下载 OpenVPN 客户端。

  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をコピーしました