In a previous post, I configured a WireGuard site-to-site VPN between my MikroTik hEX router at home and OPNsense 25.7 running on AWS:
That setup was designed to connect two networks:
Homelab LAN <-> AWS VPC subnetFor this post, I have a different requirement.
I have another AWS environment where I only need one EC2 instance to access my homelab network. I do not need to connect the entire AWS VPC to my home network.
Before configuring the VPN, I also created a custom VPC in the AWS Asia Pacific (Taipei) Region:
That custom VPC uses:
Region: ap-east-2
VPC CIDR: 10.20.0.0/16Now I will launch an EC2 instance in that VPC and configure WireGuard directly on the EC2 instance.
This is a single-instance VPN:
One EC2 instance -> WireGuard -> MikroTik hEX at homeIt is not a full AWS VPC-to-homelab site-to-site VPN.
Goal
The goal is to allow one EC2 instance in AWS to access my homelab network through WireGuard.
The EC2 instance should be able to access internal home resources such as:
192.168.0.1 MikroTik router
192.168.0.x Homelab serversI also want to access the EC2 instance from home through its WireGuard IP.
For example:
ssh ubuntu@100.97.0.2Only this EC2 instance will use the VPN.
Other AWS resources in the same VPC will not automatically route through this VPN.
Related Posts
This post builds on these earlier posts.
First, I configured a full site-to-site VPN between my homelab and an AWS network:
Then I created a custom VPC in the AWS Asia Pacific (Taipei) Region:
I also documented a separate issue I hit when running the WireGuard wg command inside an AWS SSM Session Manager session:
This post focuses only on the EC2-to-home WireGuard VPN configuration.
Why Single-Instance VPN Instead of Site-to-Site?
In this AWS environment, I only need one EC2 instance.
I do not have or need:
OPNsense
Transit Gateway
NAT instance
VPN gateway
Router instance
Full VPC-to-homelab routingA site-to-site VPN is useful when I want to connect entire networks, such as:
Homelab LAN <-> AWS VPC subnetBut in this case, I only want:
One EC2 instance <-> Homelab LANSo the simpler design is:
EC2 instance
|
| WireGuard
|
MikroTik hEX at home
|
Homelab LANThis design is simpler because:
No AWS route table change is required.
No EC2 source/destination check change is required.
No AWS router instance is required.
No Transit Gateway is required.
No VPC peering is required.
No public SSH inbound rule is required on the EC2 security group.
No inbound WireGuard rule is required on the EC2 security group.The EC2 instance itself runs WireGuard, and the WireGuard client route sends only homelab traffic through the VPN.
Existing Site-to-Site VPN Will Stay Unchanged
I already have another WireGuard site-to-site VPN between my home MikroTik hEX and an OPNsense firewall running on AWS.
That previous setup used a separate WireGuard tunnel network:
Existing site-to-site VPN: 100.96.0.0/24I will not modify that existing VPN.
For this EC2 single-instance VPN, I will use a separate WireGuard interface, port, and tunnel subnet:
New EC2 VPN: 100.97.0.0/24This keeps the single-instance VPN separate from the existing site-to-site VPN.
Network Plan
In this post, the environment is:
AWS Region: ap-east-2
AWS VPC CIDR: 10.20.0.0/16
AWS public subnets: 10.20.0.0/20
10.20.16.0/20
10.20.32.0/20
EC2 instance name: app-tpe
EC2 instance ID: i-0dde55e7533e0cf46
EC2 instance type: t4g.micro
EC2 subnet: ap-east-2b / 10.20.16.0/20
EC2 private IPv4: 10.20.30.151
EC2 public IPv4: 43.212.14.245
EC2 public DNS: ec2-43-212-14-245.ap-east-2.compute.amazonaws.com
Elastic IP: none
IAM role: none
IMDSv2: required
Homelab LAN: 192.168.0.0/24
MikroTik LAN IP: 192.168.0.1
MikroTik public DNS: home.maksonlee.com
WireGuard subnet: 100.97.0.0/24
MikroTik WireGuard IP: 100.97.0.1
EC2 WireGuard IP: 100.97.0.2
MikroTik interface: wg-ec2
WireGuard UDP port: 51821The traffic path from EC2 to home will be:
EC2 100.97.0.2
-> WireGuard tunnel
-> MikroTik 100.97.0.1
-> Homelab LAN 192.168.0.0/24The traffic path from home to EC2 will be:
Homelab PC 192.168.0.x
-> MikroTik
-> WireGuard tunnel
-> EC2 100.97.0.2EC2 Placement
The EC2 instance is launched in the custom VPC created in the previous post:
VPC: 10.20.0.0/16In this example, the instance is placed in the public subnet in ap-east-2b:
Subnet: 10.20.16.0/20
Private IP: 10.20.30.151The EC2 instance needs Internet access so it can initiate the WireGuard connection to my home MikroTik router.
For this simple setup, I use a public subnet and enable public IPv4 assignment.
Accessing the EC2 Instance Without Opening SSH
For this setup, I do not open SSH port 22 from the Internet on the EC2 security group.
I keep the EC2 inbound rules unchanged and use AWS Systems Manager Session Manager instead.
In this account and Region, I only enabled Default Host Management Configuration, also called DHMC:
Systems Manager -> Fleet Manager -> Default Host Management Configuration -> EnableAfter enabling DHMC and waiting a short time, the EC2 instance became available in Session Manager.
Then I could connect from:
EC2 -> Instances -> app-tpe -> Connect -> Session ManagerWith this setup, I do not need:
Public SSH access
Inbound TCP 22 from the Internet
A bastion host
A manually attached IAM instance profileThe EC2 security group can remain simple:
Inbound:
No public inbound rules
Outbound:
Allow allThe default security group may still contain its default self-referencing inbound rule, where the source is the same security group. That is not public Internet access. I do not add SSH 22 or WireGuard UDP 51821 from 0.0.0.0/0.
This works because Session Manager access is initiated through the SSM Agent on the instance, and the instance only needs outbound connectivity to AWS Systems Manager.
This is useful for the WireGuard setup because I can install and configure WireGuard without exposing SSH to the Internet.
For WireGuard itself, the EC2 instance also initiates the VPN connection outbound:
EC2 -> home.maksonlee.com:51821So the EC2 security group still does not need an inbound WireGuard rule.
The only inbound WireGuard rule is on the MikroTik side.
Important Note About Public IP
This design assumes the EC2 instance can reach the MikroTik router from the Internet.
That means the MikroTik router needs one of these:
A public IPv4 address
A working DDNS name
A port-forward from the ISP router to MikroTikIn this setup, I use:
home.maksonlee.com:51821If the home Internet connection is behind CGNAT, this direct inbound design will not work.
In that case, the design should be reversed: the EC2 instance should become the WireGuard listener, and MikroTik should initiate the connection outbound.
Do I Need an Elastic IP on EC2?
No, an Elastic IP is not required for WireGuard to work in this design.
The EC2 instance initiates the WireGuard connection to MikroTik.
So MikroTik does not need to know the EC2 public IP in advance.
However, an Elastic IP is useful if I want to restrict the MikroTik firewall rule to only allow WireGuard packets from that EC2 public IP.
Without Elastic IP:
MikroTik allows UDP 51821 from the Internet.
WireGuard authentication still protects the tunnel.With Elastic IP:
MikroTik allows UDP 51821 only from the EC2 Elastic IP.
This is cleaner from a firewall perspective.For testing, Elastic IP is optional.
For a stricter firewall setup, I can attach an Elastic IP and restrict the MikroTik rule to that Elastic IP. In this lab setup, I do not use an Elastic IP because I want to keep the setup simple and avoid extra public IP management.
In this post, the EC2 instance uses an auto-assigned public IPv4 address, so I do not restrict the MikroTik firewall rule to a specific EC2 public IP.
The current public IPv4 address is:
43.212.14.245But this public IPv4 address may change after stop/start because it is not an Elastic IP.
Do I Need to Open an Inbound Port on EC2?
No.
In this design, the EC2 instance initiates the WireGuard connection to MikroTik.
The direction is:
EC2 -> home.maksonlee.com:51821So the EC2 security group does not need an inbound WireGuard rule.
The EC2 security group only needs outbound access:
Protocol: UDP
Destination: home.maksonlee.com
Port: 51821If the EC2 security group uses the default outbound allow-all rule, no security group change is needed for WireGuard.
The inbound WireGuard firewall rule is needed on MikroTik, not on EC2.
- Create a New WireGuard Interface on MikroTik
On MikroTik, create a new WireGuard interface.
I do not reuse the existing WireGuard interface from the previous site-to-site VPN.
/interface wireguard
add name=wg-ec2 listen-port=51821 mtu=1420Assign the MikroTik WireGuard IP:
/ip address
add address=100.97.0.1/24 interface=wg-ec2Check the WireGuard interface:
/interface wireguard print detailExample output:
Flags: X - DISABLED; R - RUNNING
0 R name="wg-aws" mtu=1420 listen-port=51820 public-key="7z7jpkNF02wXxH5ztdfrclbPYl5i5WUaHubXHU8RMSk="
1 R name="wg-ec2" mtu=1420 listen-port=51821 public-key="H2MT9wlUE+UBn9qMEkilYChWAlHp9Q9YBYfX5yJcWy4="The public key of wg-ec2 will be used in the EC2 WireGuard configuration.
- Check the Existing MikroTik Firewall Rules
Before adding new firewall rules, I first check the current MikroTik firewall rules.
This is important because MikroTik firewall rules are order-sensitive.
Run:
/ip firewall filter printAlso check the interface lists:
/interface list print
/interface list member printIn my case, the important firewall rules were:
5 ;;; defconf: drop all not coming from LAN
chain=input action=drop in-interface-list=!LAN
11 ;;; defconf: drop all from WAN not DSTNATed
chain=forward action=drop connection-state=new connection-nat-state=!dstnat in-interface-list=WANThe interface lists were:
LAN bridge
WAN ether1
WAN pppoe-out1The new wg-ec2 interface is not in the LAN list and not in the WAN list.
This means the input chain is important.
The default input drop rule blocks traffic that does not come from the LAN list:
chain=input action=drop in-interface-list=!LANSo if I simply add the WireGuard rule at the end, it will not work.
The WireGuard allow rule must be placed before that input drop rule.
- Allow WireGuard on the MikroTik Firewall
Allow UDP port 51821 on MikroTik.
Because this EC2 instance does not use an Elastic IP, its public IPv4 address may change after stop/start.
So for this setup, I do not restrict the MikroTik firewall rule to the EC2 public IP.
I insert the rule before the default input drop rule:
/ip firewall filter
add chain=input action=accept in-interface-list=WAN protocol=udp dst-port=51821 comment="Allow WireGuard from EC2" place-before=[find where comment="defconf: drop all not coming from LAN"]This allows the EC2 instance to initiate the WireGuard handshake to MikroTik.
If I later attach an Elastic IP to the EC2 instance, I can make the rule stricter:
/ip firewall filter
add chain=input action=accept in-interface-list=WAN protocol=udp src-address=EC2_ELASTIC_IP dst-port=51821 comment="Allow WireGuard from EC2 Elastic IP" place-before=[find where comment="defconf: drop all not coming from LAN"]For the current setup, I use the version without src-address.
- Allow VPN Traffic on MikroTik
After the tunnel is established, I also want to allow the EC2 WireGuard peer to access the MikroTik router itself.
This is input-chain traffic.
Examples:
ping 100.97.0.1
ping 192.168.0.1Because wg-ec2 is not in the LAN interface list, I insert this rule before the default input drop rule:
/ip firewall filter
add chain=input action=accept in-interface=wg-ec2 src-address=100.97.0.2 comment="Allow EC2 VPN to MikroTik" place-before=[find where comment="defconf: drop all not coming from LAN"]Then I allow the EC2 instance to access the homelab LAN.
This is forward-chain traffic:
/ip firewall filter
add chain=forward action=accept in-interface=wg-ec2 dst-address=192.168.0.0/24 comment="Allow EC2 VPN to homelab LAN" place-before=[find where comment="defconf: drop all from WAN not DSTNATed"]I also allow the homelab LAN to access the EC2 WireGuard IP:
/ip firewall filter
add chain=forward action=accept src-address=192.168.0.0/24 dst-address=100.97.0.2 out-interface=wg-ec2 comment="Allow homelab LAN to EC2 VPN IP" place-before=[find where comment="defconf: drop all from WAN not DSTNATed"]This last rule is important if I want to SSH from home to the EC2 instance using:
ssh ubuntu@100.97.0.2I use explicit firewall rules instead of adding wg-ec2 to the LAN interface list.
That keeps the VPN permissions more specific.
After adding the rules, I check the firewall again:
/ip firewall filter printThe important result is that the WireGuard input rules are above the default input drop rule:
Allow WireGuard from EC2
Allow EC2 VPN to MikroTik
defconf: drop all not coming from LANAnd the forward rules are above the default WAN forward drop rule:
Allow EC2 VPN to homelab LAN
Allow homelab LAN to EC2 VPN IP
defconf: drop all from WAN not DSTNATed- Install WireGuard on the EC2 Instance
Using Session Manager, connect to the EC2 instance.
Then install WireGuard:
sudo apt update
sudo apt install -y wireguard wireguard-toolsIn this setup, I use AWS Systems Manager Session Manager instead of opening public SSH.
On Ubuntu 26.04, I previously hit an issue where the wg command behaved unexpectedly inside an SSM Session Manager session. I documented that issue separately here:
For this post, I keep the WireGuard setup simple and use the path allowed by the WireGuard AppArmor profile:
/etc/wireguard/Do not generate the key files under /tmp in this SSM Session Manager environment.
Create the WireGuard directory:
sudo install -d -m 700 /etc/wireguardGenerate the EC2 WireGuard key pair under /etc/wireguard:
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/ec2_private.key'
sudo sh -c 'wg pubkey < /etc/wireguard/ec2_private.key > /etc/wireguard/ec2_public.key'Show the EC2 public key:
sudo cat /etc/wireguard/ec2_public.keyExample output:
lq/XAXLApDnawp3kkF9Iq7zsGEGI3lsIu703IbMWpE4=Only the public key is needed on MikroTik.
- Add the EC2 Peer on MikroTik
On MikroTik, add the EC2 instance as a WireGuard peer.
Use the public key from:
sudo cat /etc/wireguard/ec2_public.keyMikroTik command:
/interface wireguard peers
add interface=wg-ec2 public-key="lq/XAXLApDnawp3kkF9Iq7zsGEGI3lsIu703IbMWpE4=" allowed-address=100.97.0.2/32For this design, I do not need to set an endpoint on MikroTik.
The EC2 instance will initiate the connection to MikroTik, and MikroTik will learn the current endpoint after the first handshake.
- Configure WireGuard on EC2
Create the WireGuard configuration file on EC2:
sudo vi /etc/wireguard/wg0.confExample configuration:
[Interface]
Address = 100.97.0.2/32
PrivateKey = EC2_PRIVATE_KEY
MTU = 1420
[Peer]
PublicKey = H2MT9wlUE+UBn9qMEkilYChWAlHp9Q9YBYfX5yJcWy4=
Endpoint = home.maksonlee.com:51821
AllowedIPs = 100.97.0.1/32, 192.168.0.0/24
PersistentKeepalive = 25Replace:
EC2_PRIVATE_KEYwith the content of:
sudo cat /etc/wireguard/ec2_private.keyThe peer public key is the public key of the MikroTik wg-ec2 interface:
H2MT9wlUE+UBn9qMEkilYChWAlHp9Q9YBYfX5yJcWy4=The endpoint is:
Endpoint = home.maksonlee.com:51821In my test, this resolved to:
1.34.95.87:51821The important part is:
AllowedIPs = 100.97.0.1/32, 192.168.0.0/24This tells EC2:
Use wg0 to reach the MikroTik WireGuard IP.
Use wg0 to reach the homelab LAN.Do not use this for this setup:
AllowedIPs = 0.0.0.0/0That would send all IPv4 traffic from EC2 through the home router, which is not what I want here.
I also use:
PersistentKeepalive = 25This helps keep the EC2-to-MikroTik WireGuard path active, which is useful because the EC2 instance is the side that initiates the tunnel.
Protect the WireGuard configuration file:
sudo chmod 600 /etc/wireguard/wg0.conf- Start WireGuard on EC2
Enable and start WireGuard:
sudo systemctl enable --now wg-quick@wg0Example output:
Created symlink '/etc/systemd/system/multi-user.target.wants/wg-quick@wg0.service' → '/usr/lib/systemd/system/wg-quick@.service'.Check the WireGuard status.
When using Session Manager, I prefer to pipe the output through cat:
sudo wg show 2>&1 | catExample output:
interface: wg0
public key: lq/XAXLApDnawp3kkF9Iq7zsGEGI3lsIu703IbMWpE4=
private key: (hidden)
listening port: 56485
peer: H2MT9wlUE+UBn9qMEkilYChWAlHp9Q9YBYfX5yJcWy4=
endpoint: 1.34.95.87:51821
allowed ips: 100.97.0.1/32, 192.168.0.0/24
latest handshake: 30 seconds ago
transfer: 28.94 KiB received, 40.63 KiB sent
persistent keepalive: every 25 secondsThe key line is:
latest handshake: 30 seconds agoThat means the WireGuard tunnel is up.
The EC2 wg0 interface shows a local listening port:
listening port: 56485This does not mean I need to open UDP 56485 on the EC2 security group. The EC2 instance initiated the WireGuard connection outbound to MikroTik. MikroTik learns the EC2 current endpoint dynamically.
Also check the interface:
ip addr show wg0Example output:
3: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 100.97.0.2/32 scope global wg0
valid_lft forever preferred_lft foreverCheck the route table:
ip routeExample output:
default via 10.20.16.1 dev ens5 proto dhcp src 10.20.30.151 metric 100
10.20.0.2 via 10.20.16.1 dev ens5 proto dhcp src 10.20.30.151 metric 100
10.20.16.0/20 dev ens5 proto kernel scope link src 10.20.30.151 metric 100
10.20.16.1 dev ens5 proto dhcp scope link src 10.20.30.151 metric 100
100.97.0.1 dev wg0 scope link
192.168.0.0/24 dev wg0 scope linkThe important routes are:
100.97.0.1 dev wg0
192.168.0.0/24 dev wg0This means EC2 will send traffic to the MikroTik WireGuard IP and the homelab LAN through the WireGuard tunnel.
- Verify from MikroTik
On MikroTik, check the peer:
/interface wireguard peers print detailExample output:
Flags: X - DISABLED; D - DYNAMIC
0 interface=wg-aws name="peer1" public-key="SW8uMhx6GROGZpAFkCb/jpkL59sKx0gKpiA3WUdaygo=" endpoint-address=3.109.96.219 endpoint-port=51820 current-endpoint-address=3.109.96.219 current-endpoint-port=51820 allowed-address=100.96.0.1/32,10.0.0.0/20,10.0.128.0/20
persistent-keepalive=25s client-endpoint="" client-allowed-address="" rx=2744.6MiB tx=3570.7MiB last-handshake=31s
1 interface=wg-ec2 name="peer2" public-key="lq/XAXLApDnawp3kkF9Iq7zsGEGI3lsIu703IbMWpE4=" endpoint-address="" endpoint-port=0 current-endpoint-address=43.212.14.245 current-endpoint-port=56485 allowed-address=100.97.0.2/32 client-endpoint=""
client-allowed-address="" rx=40.7KiB tx=28.9KiB last-handshake=1m18sThe important part is:
interface=wg-ec2
current-endpoint-address=43.212.14.245
current-endpoint-port=56485
allowed-address=100.97.0.2/32
last-handshake=1m18sThis confirms that MikroTik learned the EC2 endpoint dynamically.
- Test from EC2 to MikroTik
From EC2, test the MikroTik WireGuard IP:
ping 100.97.0.1Example output:
PING 100.97.0.1 (100.97.0.1) 56(84) bytes of data.
64 bytes from 100.97.0.1: icmp_seq=1 ttl=64 time=5.98 ms
64 bytes from 100.97.0.1: icmp_seq=2 ttl=64 time=5.78 ms
64 bytes from 100.97.0.1: icmp_seq=3 ttl=64 time=5.49 ms
64 bytes from 100.97.0.1: icmp_seq=4 ttl=64 time=5.78 ms
64 bytes from 100.97.0.1: icmp_seq=5 ttl=64 time=5.81 ms
--- 100.97.0.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4007ms
rtt min/avg/max/mdev = 5.486/5.768/5.984/0.160 msThen test the MikroTik LAN IP:
ping 192.168.0.1Example output:
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
64 bytes from 192.168.0.1: icmp_seq=1 ttl=64 time=5.75 ms
64 bytes from 192.168.0.1: icmp_seq=2 ttl=64 time=5.75 ms
64 bytes from 192.168.0.1: icmp_seq=3 ttl=64 time=5.86 ms
64 bytes from 192.168.0.1: icmp_seq=4 ttl=64 time=5.65 ms
64 bytes from 192.168.0.1: icmp_seq=5 ttl=64 time=5.97 ms
--- 192.168.0.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4008ms
rtt min/avg/max/mdev = 5.648/5.796/5.970/0.110 msAt this point, the EC2 instance can reach the MikroTik router through WireGuard.
- Access the EC2 Instance from Home
After the VPN is connected, I can access the EC2 instance from home using its WireGuard IP:
100.97.0.2From a homelab machine:
ping 100.97.0.2SSH example:
ssh ubuntu@100.97.0.2This does not require an AWS route table change.
The traffic is going directly through the WireGuard tunnel.
The EC2 security group does not need a public inbound SSH rule for this VPN path because AWS only sees the outer WireGuard UDP traffic.
Why I Use the WireGuard IP Instead of the EC2 Private IP
The EC2 instance has an AWS private IP from the VPC.
In this example:
EC2 private IP: 10.20.30.151But from home, I should not expect to connect to:
10.20.30.151This setup is not routing the whole AWS VPC to my homelab.
Instead, I should connect to the EC2 WireGuard IP:
100.97.0.2For example:
For example:
ssh ubuntu@100.97.0.2If I want home machines to access the EC2 private IP directly, then I need a more complete routed design with AWS route table changes and an EC2 router or VPN gateway.
That is not the goal of this post.
Did this guide save you time?
Support this site