VPS: Cloudflare Zero Trust access to Web Applications (20240715)
Linux host setup for cloudflared to allow Zero Trust access to a running web application, using one-time OTP to email, and alternative rules such as country blocking.
This post details using our VPS as a SOCKS5 Proxy for a single web browser and how to set up wg_easy to create a peer-to-peer network for accessing self-hosted services.
wg_easy
to create a peer-to-peer network for accessing self-hosted services.init
: 20241012)docker compose
setup for wg_easy
, including peer configuration. Also discussed is SOCKS5 Proxying for a single browser and Tailscale.ssh
with docker compose
or a tool to deploy compose stacks. We will also use cloudflared
and ufw-docker
. We have discussed those in previous blog posts and will ask you to check those content for additional details:cloudflared
in https://blg.gkr.one/20240713-cloudflared_vps/ufw-docker
in https://blg.gkr.one/20240616-u24_vps_hardening/peers
to connect using PrivateKey
, PublicKey
, and optional PresharedKey
./etc/wireguard/wg0.conf
configuration file usually contains an Interface
section and one or many Peer
:[Interface] PrivateKey = <server-private-key> Address = 10.8.0.1/24 ListenPort = 51820 PostUp = <iptables rules> PostDown = <iptables rules> [Peer] PublicKey = <peer1-public-key> PreSharedKey = <peer1-server-pre-shared-key> AllowedIPs = 10.8.0.2/32
PostUp
and PostDown
configurations for iptables
setup)[Interface] PrivateKey = <peer1-private-key> Address = 10.8.0.2/32 [Peer] PublicKey = <server-public-key> PreSharedKey = <peer1-server-pre-shared-key> AllowedIPs = 0.0.0.0/0, ::/0 Endpoint = <vps_ip>:<wireguard_port>
Address
portion) and ALL of the peer’s traffic is set to go over the created tunnel (AllowedIPs
routes all IPv4 and IPv6 traffic to use the tunnel).wg_easy
setup, but depending on the relay used, the VPS hosted Wireguard often provide a faster communication.wg_easy
setup), Tailscale doesn't require setting up and maintaining a central VPN server, reducing complexity and potential points of failure.vps
as our configured ssh
-ready host, supporting ssh key login.3128
SOCK5
using 127.0.0.1
and 3128
for the Hostname and Port. Name it as you prefer; we will use a generic entry to remind us of the base ssh
command to use. It is also possible to enable the Proxy DNS
option to send DNS requests to the VPS’ DNS instead of resolving those locally. ssh -N -D 3128 vps
-N
tells SSH not to execute a command and the -D <port>
opens the SOCK5 tunnel on the specified port. The port for the ssh
tunnel and the configured SOCKS5 proxy need to match.ssh
when done.Proxy DNS
)vps
as our configured ssh
-ready host, supporting ssh key login.CNAME
set at the domain’s DNS to provide an easy name for our vps (ie go from vps12345-patlabor.myfavoritevps.datacenter.tld
to vps.example.com
ufw
and ufw-docker
enabled on the host as described in “Ubuntu 24.04 VPS Hardening”; we will block access to the WebUI but authorize access to the Wireguard port.cloudflared
configured on the host, as described in “VPS: Cloudflare Zero Trust access to Web Applications”; we will only grant access to the WebUI using Cloudflare’s Zero Trust. wg-easy
(more details at https://github.com/wg-easy/wg-easy) is a very convenient and easy way to define a Wireguard VPN and create peers.compose.yaml
for the service is as follows (direct download link):services: wg-easy: environment: - LANG=en - WG_HOST=vps.example.com # Adapt with the CNAME set at your DNS - PASSWORD_HASH=${PASSWORD_HASH} # set the secret - WG_DEVICE=ens3 # adapt with the network interface of the NIC that will respond to the wireguard service - PORT=51821 # WebUI access (TCP) to be accessed with Cloudflare ZeroTrust - WG_PORT=51820 # Wireguard port (UDP) to be opened through ufw-docker - WG_DEFAULT_ADDRESS=10.10.10.x # Authorized subnet - WG_DEFAULT_DNS=1.1.1.1 # use Cloudflare's DNS - WG_MTU=1420 - WG_ALLOWED_IPS=10.10.10.0/24 # default configuration is to only allow peers to communicate with one another - WG_PERSISTENT_KEEPALIVE=25 # Value in seconds to keep the "connection" open - UI_TRAFFIC_STATS=true # Enable detailed RX / TX client stats in Web UI - UI_CHART_TYPE=2 # Area chart - UI_ENABLE_SORT_CLIENTS=true # Enable UI sort clients by name image: ghcr.io/wg-easy/wg-easy container_name: wg-easy volumes: - ./etc_wireguard:/etc/wireguard ports: - 51820:51820/udp # if you changed WG_PORT, adapt here - 51821:51821/tcp # if you changed PORT, adapt here restart: unless-stopped cap_add: - NET_ADMIN # grants elevated network-related privileges to the container - SYS_MODULE # allows the container to load and unload kernel modules sysctls: - net.ipv4.ip_forward=1 # enables IP forwarding, ie to act as a router, forwarding IP packets from one network interface to another - net.ipv4.conf.all.src_valid_mark=1 # affects how the system handles packet routing and marking: it enables the kernel to consider the packet mark when making routing decisions labels: - com.centurylinklabs.watchtower.enable=true
.env
file containing your PASSWORD_HASH=
value. See https://github.com/wg-easy/wg-easy/blob/master/How_to_generate_an_bcrypt_hash.md for details on how to create this hash. We note that despite the fact that we intend to place the WebUI behind Cloudflare’s authorized emails only Zero Trust, setting a secure password is still recommended.wg0.conf
will be placed in the etc_wireguard
directory. That file is the Wireguard configuration in use defining the wg0
interface. The content of the file is automatically generated by the WebUI each time a new peer
is added. In this file, we will find the automatically generated PrivateKey
for the server as well as two iptables
commands for the PostUp
and PostDown
defining POSTROUTING
, MASQUERADE
ing and FORWARD
ing:POSTROUTING
is a chain in the NAT (Network Address Translation) table of iptables
. It allows packets to be altered as they are leaving the firewall's external device.MASQUERADE
is a type of NAT action that translates host IPs in a private network into a single IP address.FORWARD
is a chain used for packets that are routed through the current host to allow traffic to pass between different network interfaces.ufw
(and ufw-docker
) are configured to block any request to the newly created, we need to do two types of additional configurations:https://wgz.example.com
wg-easy
docker container (do a docker ps
to confirm the container’s name), usingsudo ufw-docker allow wg-easy 51820/udp
wgz.example.com
we are prompted to enter the WebUI’s password. That secret was loaded as an environment variable from our compose.yaml
.+New
option we are prompted with a UI to enter the name of the peer. We will use test
.test
peer gets the first IP available on the list of WG_DEFAULT_ADDRESS
.PostUp
entry in the etc_wireguard/wg0.cong
you should see iptables -t nat -A POSTROUTING -s 10.10.10.0/24 -o ens3 -j MASQUERADE
. This rule sets up IP masquerading for all traffic coming from the 10.10.10.0/24
network and going out through the ens3
interface; as such only modify this value to be within that range.wg0.conf
filetest
peer configuration file.test
from the list of peers.test.conf
file contains:[Interface] PrivateKey = <peer1privatekey> Address = 10.10.10.2/24 DNS = 1.1.1.1 MTU = 1420 [Peer] PublicKey = <serverpublickey> PresharedKey = <peer1server-presharekey> AllowedIPs = 10.10.10.0/24 PersistentKeepalive = 25 Endpoint = vps.example.com:51820
Interface
section, defining the local peer (test)
, containing the PrivateKey
of the peer, the assigned Address
of this peer, the DNS
and MTU
as configured at compose.yaml
time.Peer
section, defining the remote peer (ie our VPS), with its PublicKey
, the PreSharedKey
, the list of AllowedIPs
(ie what subnets would go through this tunnel; here only other peers on the 10.10.10.0/24
can be reached, while an AllowedIPs
of 0.0.0.0/0, ::/0
would redirect all traffic through the VPN tunnel), how often the peer verified communication with the server (PersistentKeepAlive
, in seconds) and the Endpoint
where the server is answering (here vps.example.com
on port 51820
, as defined in the compose.yaml
file).sudo apt install wireguard sudo mkdir -p /etc/wireguard sudo nano /etc/wireguard/wg0.conf # place peer.conf content obtained from wg_easy, then save file sudo wg-quick up wg0 # will return something similar to: # [#] ip link add wg0 type wireguard # [#] wg setconf wg0 /dev/fd/63 # [#] ip -4 address add 10.10.10.2/24 dev wg0 # [#] ip link set mtu 1420 up dev wg0 # [#] resolvconf -a wg0 -m 0 -x # [#] ip -4 route add 10.10.10.0/24 dev wg0 ip -4 -brief a # to obtain the list all network interfaces configured for IPv4 on the host # a wg0 entry will be listed
Settings -> VPN Manager
, select Import Tunnel
and give it the .conf
file obtained. The adapt the configuration to reflect the Peer type of access
and following the content seen in the peer .conf
file:Done
you can select the Inactive
toggle (top right) and should see an indicator of connection (blinking red dot) on the left of the peer’s name, and other indicators of active connection such as up and down bandwidth, and traffic stats if those are enabled.192.168.22.0/24
) 100.64.0.0/10
)wg_easy
access (10.10.10.0/24
)