배경
저희 회사는 원격 근무를 위해 직원들에게 Amazon WorkSpaces를 배포하고 있습니다.
사내 서버도 AWS로 이전을 완료하여 많은 직원들이 사무실에 출근하지 않고도 업무를 할 수 있는 환경을 조성했습니다.
현재 상황과 구성에 대한 자세한 내용은 아래 기사를 참고하세요.
(이 기사는 VPC, Amazon WorkSpaces, Active Directory 등이 이미 아래 기사에서 구축되었다는 전제로 설명합니다.)
하지만 일부 사내 서버는 아직 AWS로 이전되지 않았습니다.
사내 네트워크의 제한으로 인해 Amazon WorkSpaces에서 사내 서버에 액세스할 수 없습니다.
따라서 사내 서버를 사용하는 직원도 원격 근무를 할 수 있는 방법을 고려해야 합니다.
과제
사내 네트워크와 AWS VPC를 연결하는 서비스로는 다음 세 가지가 있습니다:
AWS VPN connection
https://repost.aws/ko/knowledge-center/connect-vpc
AWS Client VPN
Direct Connect connection
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 인스턴스를 설정할 때 SSH 또는 세션 관리자(Session Manager)를 사용해 EC2 인스턴스에 접속합니다.
그러나 스플릿 터널링(Split Tunnel)이 비활성화된 경우 모든 트래픽이 VPN을 통해 전달되므로 EC2 인스턴스와의 연결이 끊어집니다.
스플릿 터널링을 활성화할 것을 권장합니다.
Active Directory 사용자 추가
사내 서버나 NAT 인스턴스에서 사용할 AWS Client VPN 인증 사용자를 생성합니다.
- 다음 URL에서 사용자를 생성하고,
<Directory ID>
를 자신의 디렉토리 ID로 변경하세요:
https://us-east-1.console.aws.amazon.com/directoryservicev2/home?region=us-east-1&tab=users#!/directories/<Directory ID>
/createUser - [Step 1] 사용자 세부 정보 지정
- 사용자 로그인 이름: NatInstance
- 새 암호: P@ssW0rd
- 암호 확인: P@ssW0rd
- 나머지 필드는 비워 둡니다.
이미지
- [Step 2 - optional] 그룹에 사용자 추가
- 어떤 그룹에도 추가하지 않고 Next 클릭
이미지
- [Step 3] 사용자 확인 및 추가
이미지
- 다음으로, 동일한 방법으로 사내 서버용 사용자를 생성합니다.
여러 사내 서버가 있는 경우, 서버마다 사용자를 생성합니다.<Directory ID>
를 자신의 디렉토리 ID로 변경하세요:
https://us-east-1.console.aws.amazon.com/directoryservicev2/home?region=us-east-1&tab=users#!/directories/<Directory ID>
/createUser - [Step 1] 사용자 세부 정보 지정
- 사용자 로그인 이름: OnPre1
- 새 암호: P@ssW0rd
- 암호 확인: P@ssW0rd
- 나머지 필드는 비워 둡니다.
이미지
- [Step 2 - optional] 그룹에 사용자 추가
- 어떤 그룹에도 추가하지 않고 Next 클릭
이미지
- [Step 3] 사용자 확인 및 추가
이미지
EC2 인스턴스 시작
NAT 인스턴스로 사용할 EC2 인스턴스를 시작합니다.
이 작업은 주로 AWS CLI 명령을 통해 수행됩니다.
- 다음 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
- 다음 AWS CLI 명령은 VPC 내부의 트래픽을 허용하는 규칙을 포함한 NAT 인스턴스용 보안 그룹을 생성합니다.
참고: 아웃바운드 규칙은 기본적으로 생성되므로 별도의 설정이 필요하지 않습니다.
Source | Protocol | Port |
10.0.0.0/16 (VPC CIDR) | All | All |
Destination | Protocol | Port |
0.0.0.0/0 | All | All |
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
- 다음 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/ko_kr/vpc/latest/userguide/work-with-nat-instances.html
- 다음 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
- 다음 명령을 사용하여 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
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
- 위 스크립트가 실행되어 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
- 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 라우팅 테이블 변경
- 다음 URL을 사용하여 VPC 라우팅 테이블을 수정하세요.
클라이언트 CIDR192.168.252.0/22
로 향하는 트래픽을 NAT 인스턴스로 라우팅하도록 VPC 라우팅 테이블을 변경합니다.
https://us-east-1.console.aws.amazon.com/vpcconsole/home?region=us-east-1#RouteTables: - VPC의 모든 라우팅 테이블에 다음 경로를 추가합니다:
- 대상:
192.168.252.0/22
(클라이언트 CIDR) - 대상: NAT 인스턴스 ID
- 대상:
이미지
- 그 다음, 다음 URL을 통해 Client VPN 엔드포인트를 선택하고 [라우팅 테이블] 탭에서 라우트를 추가하세요.
https://us-east-1.console.aws.amazon.com/vpcconsole/home?region=us-east-1#ClientVPNEndpoints:
이미지
- 클라이언트 간 통신을 라우팅하기 위해 로컬 라우트를 추가하세요.
- 경로 대상:
192.168.252.0/22
(클라이언트 CIDR) - 대상 네트워크 연결을 위한 서브넷 ID:
local
- 경로 대상:
이미지
이로써 NAT 인스턴스 준비가 완료됩니다.
WorkSpaces 자체가 Client VPN에 연결되어 있지 않아도, 각 클라이언트로 가는 트래픽은 Client VPN 엔드포인트로 라우팅됩니다.
따라서 WorkSpaces에서 Client VPN 엔드포인트에 연결된 내부 서버와 통신할 수 있습니다.
관리자 단계 (선택 사항)
위 설정만으로도 클라이언트 IP 주소를 지정하여 통신이 가능합니다.
하지만 클라이언트 IP 주소는 고정된 것이 아니기 때문에, 저희 회사는 고유 도메인 이름을 할당하여 운영하고 있습니다.
저희는 Client VPN에 Lambda 핸들러를 설정하고, 레코드를 생성하는 Lambda 함수를 호출하는 방식을 사용했습니다.
필수 사항은 아니므로 이 부분은 간략히 설명합니다.
Route 53 호스티드 존 생성
클라이언트 IP 주소에 할당할 도메인을 준비합니다.
도메인을 구매할 필요 없이 Route 53의 프라이빗 호스티드 존을 생성합니다.
Lambda 핸들러 생성
AWS Client VPN에 연결될 때 실행할 프로그램을 Lambda 핸들러로 설정할 수 있습니다.
이번에는 이 Lambda 핸들러를 통해 연결 시 클라이언트 IP 주소를 Route 53 호스티드 존에 등록하도록 합니다.
코드는 이후 단계에서 수정합니다.
Lambda 핸들러 수정
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"
}
- 다음 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 함수 생성
- 다음 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 엔드포인트에 연결되어 있어야 합니다.
직원들에게 내부 서버를 준비하기 위해 다음 작업을 수행하도록 요청했습니다.
- 내부 서버에 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 클라이언트를 다운로드합니다.
- 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
- 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
onpre1.user.workspaces
를 지정하면 직원들이 WorkSpaces에서 내부 서버에 연결할 수 있습니다.
직원들에게 내부 서버 설정 절차를 완료하도록 요청하는 것은 어려운 일이었으며, 이 절차를 완벽히 따르도록 하는 것은 쉽지 않았습니다.
사실, VPN 라우터가 준비된다면 AWS Site-to-Site VPN을 통해 더 간단하게 구현할 수 있고, 내부 서버를 AWS로 이전하면 VPN 자체가 필요 없게 됩니다.
내년 예산에서 VPN 라우터 구매를 신청하는 한편, 직원들이 내부 서버를 AWS로 이전하도록 유도하며, 그때까지 이 구성을 임시 운영 체계로 사용할 계획입니다.