最近在解決一些需求時遇到必須要從 VPC 內網訪問 Web Service 同時也必須要從內網對 Internet 訪問的情境,一般在這種情況就是建立 VPN 來解決這個問題,而 AWS Client VPN 就是一個簡單的 Client VPN。
AWS Client VPN 是基於 OpenVPN 實現的 VPN 服務,在 Client 端安裝 AWS Client VPN for Desktop 接進 AWS VPN 內網,在這篇全程會以 CDK 建立 AWS Client VPN Endpoint。
這篇的 sample code 可以在 shazi7804/cdk-samples 找到,在這篇的實作會實現以下功能:
- 建立 Client VPN Endpoint
- 透過 CloudWatch Logs 收集 VPN Logs
- 使用 Mutual 作為驗證方法
- RSA 憑證由 ACM 管理
- 開啟 split-Tunnel 模式
Create CloudWatch Logs Group and Stream (Options)
CloudWatch Logs Group, Stream 是拿來紀錄 client VPN 使用的,如果不需要保留任何記錄可以略過這一步
const logGroup = new logs.LogGroup(this, 'ClientVpnLogGroup', {
retention: logs.RetentionDays.ONE_MONTH
});
const logStream = logGroup.addStream('ClientVpnLogStream');
- logGroup, logStream 分別建立 Logs Group 和 Stream,預設保留一個月的紀錄 #ONE_MONTH
Create Mutual Authentication
client VPN 在驗證方面支援三種方法:
- Active Directory authentication
- Mutual authentication
- Single sign-on (SAML-based federated authentication)
而 Mutual authentication 可以同時搭配 Active Directory 或 federated Authentication
在這篇會用 Mutual authentication 作為主要驗證方法,而 Mutual authentication 主要是以 RSA 憑證作為認證,然後由 ACM 管理
- server 和 client 交握的憑證由 OpenVPN easy-rsa 產生
$ git clone https://github.com/OpenVPN/easy-rsa.git
$ cd easy-rsa/easyrsa3
# Initialize a new PKI environment.
$ ./easyrsa init-pki
# Build a new certificate authority (CA).
$ ./easyrsa build-ca nopass
# Generate the "server" certificate and key.
$ ./easyrsa build-server-full server nopass
# Generate the "client" certificate and key.
$ ./easyrsa build-client-full client nopass
- 將需要保留的 certificates 拉出來另外保存
$ mkdir ~/aws-client-vpn/
$ cp pki/ca.crt \
pki/issued/server.crt \
pki/private/server.key \
pki/issued/client.crt \
pki/private/client.key ~/aws-client-vpn/
$ cd ~/aws-client-vpn/
- 上傳
server.key
和client.crt
、client.key
# Upload server key.
$ aws acm import-certificate --certificate fileb://server.crt --private-key fileb://server.key --certificate-chain fileb://ca.crt --region us-east-1
# Upload client key.
$ aws acm import-certificate --certificate fileb://client.crt --private-key fileb://client.key --certificate-chain fileb://ca.crt --region us-east-1
ACM 此時應該會有匯入的兩把 KEY:client
、server
,記著 Key ARN 在建立 client VPN endpoint 會用到。
Create client VPN endpoint
VPN Endpoint 是提供個 OpenVPN Client 的對外接口,由於在撰寫文章時 CDK 目前還沒有 Layer 2 Resource,所以用 CfnClientVpnEndpoint
來建立 VPN Endpoint
const vpnEndpoint = new ec2.CfnClientVpnEndpoint(this, 'VpnEndpoint', {
authenticationOptions: [{
type: 'certificate-authentication',
mutualAuthentication: {
clientRootCertificateChainArn: "<your-acm-client-key-arn>",
}
}],
clientCidrBlock: "<your-client-ip-cidr>",
connectionLogOptions: {
enabled: true,
cloudwatchLogGroup: logGroup.logGroupName,
cloudwatchLogStream: logStream.logStreamName
},
serverCertificateArn: "<your-acm-server-key-arn>",
splitTunnel: true,
dnsServers: ["8.8.8.8", "8.8.4.4"],
})
- clientRootCertificateChainArn 指定的是 ACM 中 client 這張的 ARN
- serverCertificateArn 指定的是 ACM 中 server 這張的 ARN
- clientCidrBlock 必須大於 /27 mask,而且不能和 Subnet CIDR 重疊,當 client VPN 登入後會以這個值指定的 CIDR IP 派發。
- splitTunnel 開啟 split-Tunnel 模式。
- dnsServers 派發給 client 的 DNS Server。
Create client VPN target network
Endpoint 建好後必須和對應的 VPC.Subnet 掛載起來,一個 association 對應一個 Subnet。
new ec2.CfnClientVpnTargetNetworkAssociation(this, 'ClientVpnNetworkAssociation', {
clientVpnEndpointId: vpnEndpoint.ref,
subnetId: vpc.privateSubnets[0].subnetId
})
Create client VPN Authorization Group Rule
Authorization Rule 可以授權從 client VPN 的使用者可訪問的 VPC CIDR,在這個範例則是使用 authorizeAllGroups
指定的是 All Users (若要切分 Users 則要指定 accessGroupId
)
new ec2.CfnClientVpnAuthorizationRule(this, 'Authz', {
clientVpnEndpointId: vpnEndpoint.ref,
targetNetworkCidr: vpc.vpcCidrBlock,
authorizeAllGroups: true,
})
Download and Edit client VPN configuration
當 target network 掛載後需要等待約 5 分鐘狀態由 pending-associate
轉為 Associated
之後下載「Download Client Configuration」設定檔
client
dev tun
proto udp
remote <your-client-vpn-endpoint-domain> 443
remote-random-hostname
resolv-retry infinite
nobind
remote-cert-tls server
cipher AES-256-GCM
verb 3
<ca>
...
</ca>
<cert>
... <your-client-cert>
</cert>
<key>
... <your-client-key>
</key>
如果有架過 OpenVPN 的人就會知道這完全就是 OpenVPN Configuration,而這個 Configuration 預設沒有包含 client-cert
和 client-key
必須手動將憑證塞到 Configuration 內如上述範例。
Download VPN client on your desktop
當設定都完成後,可以到 AWS Client VPN download 找到適合你的 Client App,啟動的第一次需要指定 OpenVPN Configuration 匯入並 Connect。
Testing your VPN client
由於在這個範例中有開啟 split-Tunnel 模式,所以僅有符合 VPC CIDR 才會走 AWS client VPN,走 Internet 仍會使用 client 自己的網路
要測試 split-Tunnel 可以簡單用 myip 檢查 IP 是否正確,或者把 Private subnet 的 NAT Gateway 拔掉後仍要可以訪問 Internet。
沙大師 是不是 在
– server 和 client 交握的憑證由 OpenVPN easy-rsa 產生
少了建 “Generate the server certificate and key.” 的 command
“./easyrsa build-server-full server nopass”
You’re right, fixed it. Many thanks.