main.tf
:
provider "google" {
project = "PROJECT_ID"
}
resource "google_compute_instance" "test-vpn" {
name = "test-vpn"
machine_type = "e2-micro"
zone = "europe-central2-a"
boot_disk {
initialize_params {
image = "debian-12"
}
}
network_interface {
subnetwork = google_compute_subnetwork.test-vpn.self_link
access_config {}
}
metadata = {
enable-oslogin = true
startup-script-url = "gs://test-vpn/startup-script"
}
service_account {
email = google_service_account.test-vpn.email
scopes = ["cloud-platform"]
}
}
resource "google_storage_bucket" "test-vpn" {
name = "test-vpn"
location = "europe-central2"
uniform_bucket_level_access = true
}
resource "google_storage_bucket_object" "test-vpn" {
name = "startup-script"
source = "test-vpn-startup-script.sh"
bucket = google_storage_bucket.test-vpn.id
}
resource "google_service_account" "test-vpn" {
account_id = "test-vpn"
}
resource "google_storage_bucket_iam_member" "test-vpn" {
bucket = google_storage_bucket.test-vpn.name
role = "roles/storage.objectViewer"
member = "serviceAccount:${google_service_account.test-vpn.email}"
}
resource "google_compute_network" "test-vpn" {
name = "test-vpn"
auto_create_subnetworks = false
}
resource "google_compute_subnetwork" "test-vpn" {
name = "test-vpn"
ip_cidr_range = "10.0.0.0/20"
region = "europe-central2"
network = google_compute_network.test-vpn.self_link
}
resource "google_compute_firewall" "test-vpn-ssh" {
name = "test-vpn-ssh"
network = google_compute_network.test-vpn.self_link
source_ranges = ["SOURCE_IP"]
allow {
protocol = "tcp"
ports = [22]
}
}
resource "google_compute_firewall" "test-vpn-vpn" {
name = "test-vpn-vpn"
network = google_compute_network.test-vpn.self_link
source_ranges = ["SOURCE_IP"]
allow {
protocol = "udp"
ports = [1194]
}
}
test-vpn-startup-script.sh
:
set -eux
ip=`ip -br a | grep -Ev '^(lo|tun)' | awk '{print $3}' | sed -E 's|/.*||'`
if ! command -v openvpn >/dev/null \
|| ! command -v /usr/share/easy-rsa/easyrsa >/dev/null; then
apt-get install -y easy-rsa openvpn
fi
if ! [ -e /sys/class/net/tap0 ]; then
openvpn --mktun --dev tap0
fi
if ! [ -e /etc/systemd/network/25-br0.netdev ]; then
cat <<EOF > /etc/systemd/network/25-br0.netdev
[NetDev]
Name=br0
Kind=bridge
MACAddress=`cat /sys/class/net/ens4/address`
EOF
fi
if ! [ -e /etc/systemd/network/25-br0.network ]; then
sed -E 's/^Name=.*/Name=br0/' \
/run/systemd/network/10-netplan-all-en.network \
> /etc/systemd/network/25-br0.network
fi
cat <<EOF > /run/systemd/network/10-netplan-all-en.network
[Match]
Name=en*
[Network]
Bridge=br0
EOF
if ! [ -e /etc/systemd/network/10-tap0.network ]; then
cat <<EOF > /etc/systemd/network/10-tap0.network
[Match]
Name=tap0
[Network]
Bridge=br0
EOF
fi
systemctl restart systemd-networkd
cd /etc/openvpn/server
if ! [ -e easy-rsa ]; then
mkdir easy-rsa
pushd easy-rsa
/usr/share/easy-rsa/easyrsa init-pki
/usr/share/easy-rsa/easyrsa --batch build-ca nopass
/usr/share/easy-rsa/easyrsa --batch build-server-full server nopass
/usr/share/easy-rsa/easyrsa --batch build-client-full client nopass
/usr/share/easy-rsa/easyrsa gen-dh
/usr/share/easy-rsa/easyrsa gen-crl
popd
fi
if ! [ -e ca.crt ]; then
cp easy-rsa/pki/ca.crt .
fi
if ! [ -e server.crt ]; then
cp easy-rsa/pki/issued/server.crt .
fi
if ! [ -e server.key ]; then
cp easy-rsa/pki/private/server.key .
fi
if ! [ -e dh.pem ]; then
cp easy-rsa/pki/dh.pem .
fi
if ! [ -e crl.pem ]; then
cp easy-rsa/pki/crl.pem .
fi
if ! [ -e ta.key ]; then
openvpn --genkey tls-auth ta.key
fi
# echo 1 > /proc/sys/net/ipv4/ip_forward
# iptables -t nat -A POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to "$ip"
if ! [ -e server.conf ]; then
echo "local $ip
port 1194
proto udp
dev tap0
ca ca.crt
cert server.crt
key server.key
dh dh.pem
# crl-verify crl.pem
topology subnet
# server 10.8.0.0 255.255.255.0
server-bridge
# route-gateway dhcp
# ifconfig-pool-persist /var/log/openvpn/ipp.txt
push \"route 10.255.255.224 255.255.255.240\"
keepalive 10 120
tls-auth ta.key 0
user nobody
group nogroup
persist-key
persist-tun
status /var/log/openvpn/openvpn-status.log
verb 3
explicit-exit-notify 1" > server.conf
fi
systemctl enable --now openvpn-server@server.service
cd /root
if ! [ -e client.conf ]; then
public_ip=`curl https://ipinfo.io/ip`
echo "client
dev tap0
proto udp
remote $public_ip 1194
nobind
user nobody
group nobody
persist-key
persist-tun
<ca>
`cat /etc/openvpn/server/ca.crt`
</ca>
<cert>
`cat /etc/openvpn/server/easy-rsa/pki/issued/client.crt`
</cert>
<key>
`cat /etc/openvpn/server/easy-rsa/pki/private/client.key`
</key>
remote-cert-tls server
<tls-auth>
`cat /etc/openvpn/server/ta.key`
</tls-auth>
key-direction 1
verb 3" > client.conf
fi
// replace PROJECT_ID, SOURCE_IP
$ docker run --rm -itv "$PWD:/app" -w /app google/cloud-sdk:457.0.0-alpine
/app # gcloud auth login --update-adc
/app # apk add terraform
/app # terraform init
/app # terraform apply
/app # gcloud compute ssh test-vpn --project PROJECT_ID --zone europe-central2-a --command 'ip a'
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
2: ens4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1460 qdisc mq master br0 state UP group default qlen 1000
link/ether 42:01:0a:00:00:07 brd ff:ff:ff:ff:ff:ff
altname enp0s4
3: tap0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master br0 state UP group default qlen 1000
link/ether d2:97:03:79:68:d6 brd ff:ff:ff:ff:ff:ff
4: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1460 qdisc noqueue state UP group default qlen 1000
link/ether 42:01:0a:00:00:07 brd ff:ff:ff:ff:ff:ff
inet 10.0.0.7/32 metric 100 scope global dynamic br0
valid_lft 2779sec preferred_lft 2779sec
inet6 fe80::4001:aff:fe00:7/64 scope link
valid_lft forever preferred_lft forever
/app # gcloud compute ssh test-vpn --project PROJECT_ID --zone europe-central2-a --command 'cat /etc/openvpn/server/server.conf'
local 10.0.0.7
port 1194
proto udp
dev tap0
ca ca.crt
cert server.crt
key server.key
dh dh.pem
# crl-verify crl.pem
topology subnet
# server 10.8.0.0 255.255.255.0
server-bridge
# route-gateway dhcp
# ifconfig-pool-persist /var/log/openvpn/ipp.txt
push "route 10.255.255.224 255.255.255.240"
keepalive 10 120
tls-auth ta.key 0
user nobody
group nogroup
persist-key
persist-tun
status /var/log/openvpn/openvpn-status.log
verb 3
explicit-exit-notify 1
/app # gcloud compute ssh test-vpn --project PROJECT_ID --zone europe-central2-a --command 'sudo journalctl -u openvpn-server@server'
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal systemd[1]: Starting openvpn-server@server.service - OpenVPN service for server...
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: Note: --cipher is not set. OpenVPN versions before 2.5 defaulted to BF-CBC as fallback when cipher negotiation failed in this case. If you need this fallback please add '--data-ciphers-fallback BF-CBC' to your configuration and/or add BF-CBC to --data-ciphers.
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: Note: dev-type not tun, disabling data channel offload.
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: OpenVPN 2.6.3 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] [DCO]
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: library versions: OpenSSL 3.0.11 19 Sep 2023, LZO 2.10
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: DCO version: N/A
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: NOTE: when bridging your LAN adapter with the TAP adapter, note that the new bridge adapter will often take on its own IP address that is different from what the LAN adapter was previously set to
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: net_route_v4_best_gw query: dst 0.0.0.0
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: net_route_v4_best_gw result: via 10.0.0.1 dev br0
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal systemd[1]: Started openvpn-server@server.service - OpenVPN service for server.
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: Diffie-Hellman initialized with 2048 bit key
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: TUN/TAP device tap0 opened
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: Could not determine IPv4/IPv6 protocol. Using AF_INET
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: Socket Buffers: R=[212992->212992] S=[212992->212992]
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: UDPv4 link local (bound): [AF_INET]10.0.0.7:1194
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: UDPv4 link remote: [AF_UNSPEC]
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: UID set to nobody
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: GID set to nogroup
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: Capabilities retained: CAP_NET_ADMIN
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: MULTI: multi_init called, r=256 v=256
Apr 16 05:44:19 test-vpn.europe-central2-a.c.PROJECT_ID.internal openvpn[1497]: Initialization Sequence Completed
/app # gcloud compute ssh test-vpn --project PROJECT_ID --zone europe-central2-a --command 'sudo -i cat client.conf'
client
dev tap0
proto udp
remote SERVER_PUBLIC_IP 1194
nobind
user nobody
group nobody
persist-key
persist-tun
<ca>
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
</ca>
<cert>
Certificate:
...
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
</cert>
<key>
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
</key>
remote-cert-tls server
<tls-auth>
#
# 2048 bit OpenVPN static key
#
-----BEGIN OpenVPN Static key V1-----
...
-----END OpenVPN Static key V1-----
</tls-auth>
key-direction 1
verb 3
$ sudo openvpn <(gcloud compute ssh test-vpn --project PROJECT_ID --zone europe-central2-a --command 'sudo -i cat client.conf')
2024-04-16 08:51:34 Note: --cipher is not set. OpenVPN versions before 2.5 defaulted to BF-CBC as fallback when cipher negotiation failed in this case. If you need this fallback please add '--data-ciphers-fallback BF-CBC' to your configuration and/or add BF-CBC to --data-ciphers.
2024-04-16 08:51:34 Note: dev-type not tun, disabling data channel offload.
2024-04-16 08:51:34 OpenVPN 2.6.10 [git:makepkg/ba0f62fb950c56a0+] x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] [DCO] built on Mar 20 2024
2024-04-16 08:51:34 library versions: OpenSSL 3.2.1 30 Jan 2024, LZO 2.10
2024-04-16 08:51:34 DCO version: N/A
2024-04-16 08:51:34 TCP/UDP: Preserving recently used remote address: [AF_INET]SERVER_PUBLIC_IP:1194
2024-04-16 08:51:34 Socket Buffers: R=[212992->212992] S=[212992->212992]
2024-04-16 08:51:34 UDPv4 link local: (not bound)
2024-04-16 08:51:34 UDPv4 link remote: [AF_INET]SERVER_PUBLIC_IP:1194
2024-04-16 08:51:34 NOTE: UID/GID downgrade will be delayed because of --client, --pull, or --up-delay
2024-04-16 08:51:35 TLS: Initial packet from [AF_INET]SERVER_PUBLIC_IP:1194, sid=d504a257 eca6d018
2024-04-16 08:51:35 VERIFY OK: depth=1, CN=Easy-RSA CA
2024-04-16 08:51:35 VERIFY KU OK
2024-04-16 08:51:35 Validating certificate extended key usage
2024-04-16 08:51:35 ++ Certificate has EKU (str) TLS Web Server Authentication, expects TLS Web Server Authentication
2024-04-16 08:51:35 VERIFY EKU OK
2024-04-16 08:51:35 VERIFY OK: depth=0, CN=server
2024-04-16 08:51:35 Control Channel: TLSv1.3, cipher TLSv1.3 TLS_AES_256_GCM_SHA384, peer certificate: 2048 bits RSA, signature: RSA-SHA256, peer temporary key: 253 bits X25519
2024-04-16 08:51:35 [server] Peer Connection Initiated with [AF_INET]SERVER_PUBLIC_IP:1194
2024-04-16 08:51:35 TLS: move_session: dest=TM_ACTIVE src=TM_INITIAL reinit_src=1
2024-04-16 08:51:35 TLS: tls_multi_process: initial untrusted session promoted to trusted
2024-04-16 08:51:35 PUSH: Received control message: 'PUSH_REPLY,route 10.255.255.224 255.255.255.240,route-gateway dhcp,ping 10,ping-restart 120,peer-id 0,cipher AES-256-GCM,protocol-flags cc-exit tls-ekm dyn-tls-crypt,tun-mtu 1500'
2024-04-16 08:51:35 OPTIONS IMPORT: route options modified
2024-04-16 08:51:35 OPTIONS IMPORT: route-related options modified
2024-04-16 08:51:35 OPTIONS IMPORT: tun-mtu set to 1500
2024-04-16 08:51:35 net_route_v4_best_gw query: dst 0.0.0.0
2024-04-16 08:51:35 net_route_v4_best_gw result: via LOCAL_GW dev wlan0
2024-04-16 08:51:35 ROUTE_GATEWAY LOCAL_GW/255.255.255.0 IFACE=wlan0 HWADDR=MAC_ADDRESS
2024-04-16 08:51:35 OpenVPN ROUTE: OpenVPN needs a gateway parameter for a --route option and no default was specified by either --route-gateway or --ifconfig options
2024-04-16 08:51:35 OpenVPN ROUTE: failed to parse/resolve route for host/network: 10.255.255.224
2024-04-16 08:51:35 TUN/TAP device tap0 opened
2024-04-16 08:51:35 UID set to nobody
2024-04-16 08:51:35 GID set to nobody
2024-04-16 08:51:35 Capabilities retained: CAP_NET_ADMIN
2024-04-16 08:51:35 Initialization Sequence Completed
2024-04-16 08:51:35 Data Channel: cipher 'AES-256-GCM', peer-id: 0
2024-04-16 08:51:35 Timers: ping 10, ping-restart 120
2024-04-16 08:51:35 Protocol options: protocol-flags cc-exit tls-ekm dyn-tls-crypt
https://forums-new.openvpn.net/forum/openvpn-community-versions/sc/138-bridged-vpn-and-dhcp