【With Step-by-Step Images】How to Enable Communication from AWS to Client Devices Using AWS Client VPN

How to Enable Communication from AWS to Client Devices Using AWS Client VPN
  • A requirement has arisen to enable communication from Amazon WorkSpaces to an on-premises server.
  • AWS Client VPN provides secure connections from remote users to AWS resources, but it has limitations for reverse communication.
  • This article considers these limitations and explains, in detail, how to enable AWS to access client devices.

Background

Our company distributes Amazon WorkSpaces to employees to facilitate remote work.
We have also migrated internal servers to AWS, creating an environment where many employees can work without coming to the office.
For details about the current situation and configuration, please refer to the following articles.
(This article assumes that VPC, Amazon WorkSpaces, and Active Directory have already been set up as described in the referenced articles.)

However, some internal servers have not yet been migrated to AWS.
Due to the company’s internal network, Amazon WorkSpaces cannot access these internal servers.
Therefore, we need to consider methods that allow employees who use internal servers to work remotely.

Challenges

There are several services for connecting the company network and AWS VPC, including the following three:

AWS VPN connection
AWS Client VPN
Direct Connect connection

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

Direct Connect was excluded from consideration due to its high setup cost.
We considered using the more standard AWS Site-to-Site VPN, but we could not prepare a VPN router and had to abandon this option.

We also considered AWS Client VPN, which is currently being used in some cases. However, it only enables one-way communication and does not support two-way communication.
Communication can only be initiated from the client side, and AWS cannot initiate communication with the client side.
Thus, AWS Client VPN does not meet the requirement of enabling Amazon WorkSpaces to access internal servers.

While it is possible to set up a VPN server on an EC2 instance, we prefer to use managed AWS services as much as possible.

Method

AWS Client VPN allows communication between clients.
In this case, we utilized this feature to enable communication from within the VPC to internal company servers.

Specifically, we set up a NAT instance within the VPC and routed traffic destined for internal company servers to the AWS Client VPN endpoint using the VPC route table.
The internal company servers will always operate in a state where they are connected to AWS Client VPN.

Additionally, as an optional step, we introduced a procedure to automatically assign a domain name.
Since the client IP address assigned to internal company servers cannot be fixed, it changes with each reconnection.
To address this, we automatically assigned a private domain name that resolves to the client IP address of the internal servers.
When accessing the internal servers from Amazon WorkSpaces, communication is conducted using this domain name.

The architecture is as follows:

As stated in the background, we assume that VPC, Amazon WorkSpaces, and Active Directory have already been set up.

Additionally, AWS Client VPN and Lambda functions have already been configured as described in the following articles.
Thus, for the steps indicated by the blue lines, only their purposes are briefly explained, and detailed steps can be found in the articles below:

Administrator’s Procedure

Issuing a Certificate with ACM

To create a Client VPN endpoint, an ACM certificate is required.
If you own a domain, you can issue it instantly. Otherwise, you need to generate a certificate yourself and import it into ACM.
In my case, I created a certificate using CloudShell and imported it into ACM.

Creating and Configuring a Client VPN Endpoint

This was the source of issues when setting up the NAT instance.
When setting up the NAT instance, you connect to the constructed EC2 instance using SSH or Session Manager.
However, if split tunneling is disabled, all traffic will switch to go through the VPN, resulting in disconnection from the EC2 instance.
It is recommended to enable split tunneling.

Adding Active Directory Users

Create authentication users for AWS Client VPN to be used with internal servers or NAT instances.

  1. Create users from the following URL, replacing <Directory ID> with your directory ID:
    https://us-east-1.console.aws.amazon.com/directoryservicev2/home?region=us-east-1&tab=users#!/directories/<Directory ID>/createUser
  2. [Step 1] Specify user details
    • User logon name: NatInstance
    • New password: P@ssW0rd
    • Confirm password: P@ssW0rd
    • Leave other fields blank
Image
  1. [Step 2 - Optional] Add user to groups
    • Do not add to any group, and proceed by clicking Next
Image
  1. [Step 3] Confirm and add user
Image
  1. Next, create users for internal servers in the same way.
    If there are multiple internal servers, create a user for each server.
    Replace <Directory ID> with your directory ID:
    https://us-east-1.console.aws.amazon.com/directoryservicev2/home?region=us-east-1&tab=users#!/directories/<Directory ID>/createUser
  2. [Step 1] Specify User Details
    • User logon name: OnPre1
    • New password: P@ssW0rd
    • Confirm password: P@ssW0rd
    • Leave other fields blank
Image
  1. [Step 2 - Optional] Add User to Groups
    • Do not add to any group, and proceed by clicking Next
Image
  1. [Step 3] Confirm and Add User
Image

Launching an EC2 Instance

Launch an EC2 instance to be used as a NAT instance.
This will primarily be set up using AWS CLI commands.

  1. The following AWS CLI command creates an instance profile to enable connecting to the EC2 instance via Session Manager.
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. The following AWS CLI command creates a security group for the NAT instance with rules that allow traffic from within the VPC.
    Note: Outbound rules are created by default, so no configuration is needed.
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. The following AWS CLI command launches an EC2 instance with this security group in a public subnet.
    Additionally, source/destination checks are disabled to allow traffic to be sent and received from IP addresses other than its own.
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}"

Building the NAT Instance

Connect to the EC2 instance using Session Manager and start building the NAT instance.
Refer to the following URL to build and prepare the NAT instance.

NAT instance tutorial
https://docs.aws.amazon.com/vpc/latest/userguide/work-with-nat-instances.html

  1. The following AWS CLI command connects to the EC2 instance via Session Manager.
# 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. The following command prepares for connecting to the Client VPN.
    Additionally, prepare a file containing the username and password for Client VPN, which will be used for dynamic input.
## 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. Use the vim command or similar to create a script that connects the NAT instance to the Client VPN.
    Configure it with cron to attempt the connection every minute.
    This ensures that when the NAT instance is running and the Client VPN endpoint is in the Available state, it will automatically connect to the 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
Contents of Bash Script "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
Contents of cron Configuration
* * * * * /usr/local/bin/check_vpn.sh >> /var/log/vpn_check.log 2>&1
  1. Wait for the above script to execute and for the NAT instance to connect to the Client VPN.
    Run the following command to verify that the tun0 interface has been created.
## 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. Configure the NAT settings.
    Here, routing should be set to use the VPN tunnel interface tun0 instead of the default eth0 or enX0.
    Additionally, traffic destined for the VPC CIDR would loop if routed through tun0 instead of the ENI, so the route table is overwritten to prevent this.
    With this configuration, traffic from clients will be routed to the Client VPN endpoint.
## 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 

Instead of manually running the ip route delete 10.0.0.0/16 dev tun0 command, you can embed it into the check_vpn.sh script.
In that case, add the following line to check_vpn.sh:
/usr/sbin/ip route delete 10.0.0.0/16 dev tun0

Route Table Configuration

Modifying the VPC Route Table and Client VPN Route Table

  1. Use the following URL to modify the VPC route table.
    Change the VPC route table to route traffic destined for client CIDR 192.168.252.0/22 to the NAT instance.
    https://us-east-1.console.aws.amazon.com/vpcconsole/home?region=us-east-1#RouteTables:
  2. Add the following route to all route tables in the VPC:
    • Destination: 192.168.252.0/22 (Client CIDR)
    • Target: NAT Instance ID
Image
  1. Next, use the following URL to select the Client VPN endpoint and add a route from the [Route table] tab.
    https://us-east-1.console.aws.amazon.com/vpcconsole/home?region=us-east-1#ClientVPNEndpoints:
Image
  1. To route communication between clients, add a local route.
    • Route destination: 192.168.252.0/22 (Client CIDR)
    • Subnet ID for target network association: local
Image

With this, the NAT instance setup is complete.
Even if WorkSpaces itself is not connected to the Client VPN, traffic to each client will still be routed to the Client VPN endpoint.
This allows WorkSpaces to communicate with internal servers connected to the Client VPN endpoint.

Administrator’s Procedure (Optional)

Even with the above configuration, communication can be achieved by specifying the client IP address.
However, since the client IP address is not fixed, we assign a unique domain name for operational purposes.

Our approach involves setting a Lambda handler in Client VPN to call a Lambda function that creates a DNS record.
This step is not mandatory, so a brief explanation will suffice here.

Creating a Route 53 Hosted Zone

Prepare a domain to assign to the client IP address.
A domain does not need to be purchased; instead, create a Route 53 private hosted zone.

Creating a Lambda Handler

The Lambda handler can execute programs you want to run when connecting to AWS Client VPN.
For this scenario, we use a Lambda handler to register the client IP address in the Route 53 hosted zone upon each connection.
The code will be updated in subsequent steps.

Updating the Lambda Handler

  1. Update the Lambda handler code to call the Lambda function responsible for assigning domain names.
    Blue sections indicate the added parts.
Lambda Handler Code
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"
    }

Use the following AWS CLI commands to add policies to the IAM role, allowing it to invoke the Lambda function.
Additionally, add policies to permit access to EC2 services for obtaining the client IP address later.

AWS CLI Commands
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": "*"
            }
        ]
    }'

Creating the Lambda Function

  1. Create the Lambda function for assigning domain names using the following URL:
    https://us-east-1.console.aws.amazon.com/lambda/home?region=us-east-1#/create/function
    • Function Name: AWSClientVPN-CreateClientRecord
    • Runtime: Python 3.13
    • Execution Role: LambdaRole-WorkSpaces
    • Timeout: 2 minutes
    • Code: See below
Lambda Function Code
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
        }
    }

With this configuration, when the internal server connects to the Client VPN, a domain name is automatically assigned.
Since the username for the internal server is OnPre1, a record named onpre1.user.workspaces will be created.
This domain name resolves to the current client IP address, allowing connection to the internal server without checking the client IP address.

User‘s Procedure

To allow WorkSpaces to access internal servers, the internal servers must always remain connected to the Client VPN endpoint.
Employees were asked to complete the following steps as preparation for the internal server:

  1. Place the Client VPN configuration file (client.ovpn) and user authentication information file (auth.txt) on the internal server.
    Specify the path to the authentication information file in the configuration file.
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

Next, set up a mechanism to periodically check the connection to the Client VPN using the OpenVPN client, based on the operating system of the internal server.
Download the OpenVPN client for both Linux and Windows.

  1. For Linux, use cron to periodically execute the following Bash script, as with the NAT instance.
Bash Script
#!/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. For Windows, use Task Scheduler to periodically execute the following Bat file.
Bat File
@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. By specifying onpre1.user.workspaces, employees can connect to the internal server from WorkSpaces.

It was quite challenging to have employees follow these steps, and it proved difficult for them to complete the setup.
That said, if a VPN router could be provided, AWS Site-to-Site VPN would be a simpler solution. Furthermore, if the internal servers were migrated to AWS, VPNs would not be necessary at all.
For now, we plan to apply for VPN router funding in next year’s budget, encourage employees to migrate internal servers to AWS, and use this setup as an interim solution.

Copied title and URL