Operations 31 min read

Comparing In‑Network Penetration Solutions: frp, WireGuard, and Tailscale in Practice

The article compares three internal‑network penetration methods—frp, WireGuard, and Tailscale—by outlining their architectures, deployment steps on Linux and Windows, configuration details, troubleshooting tips, performance measurements, and recommending suitable scenarios for individuals, teams, and enterprises.

MaGe Linux Operations
MaGe Linux Operations
MaGe Linux Operations
Comparing In‑Network Penetration Solutions: frp, WireGuard, and Tailscale in Practice

Background and Use Cases

Operations engineers often need to expose local services to the Internet, access cloud servers behind NAT from home, remotely manage Raspberry Pi or NAS devices, or enable communication between servers that are not on the same LAN. All these scenarios require an internal‑network penetration technique.

The core problem is how to reach devices behind NAT. Three common approaches are:

Port mapping : configure port forwarding on the outbound router (requires router control).

Reverse proxy : forward traffic through a public‑IP relay server (e.g., frp, ngrok).

VPN tunnel : build an encrypted virtual LAN (e.g., WireGuard, Tailscale).

frp – Fast Reverse Proxy

How It Works

frp follows a classic client‑server model. A public‑IP server runs frps as the relay, while each internal device runs frpc to connect to the server.

[Internal Device] --frpc--> [Public Server frps] ---> [External User]
        <--frpc----          <------

Advantages: fully controllable, no third‑party dependency, traffic passes through the relay. Drawbacks: bandwidth and latency are limited by the relay.

Server Deployment (Linux)

# 1. Download frp (server is frps, client is frpc)
# releases page: https://github.com/fatedier/frp/releases
wget https://github.com/fatedier/frp/releases/download/v0.60.0/frp_0.60.0_linux_amd64.tar.gz
 tar -xzf frp_0.60.0_linux_amd64.tar.gz
 cd frp_0.60.0_linux_amd64

# 2. Create frps.ini
cat > frps.ini <<'EOF'
[common]
bind_addr = 0.0.0.0
bind_port = 7000

token = your-secret-token-here
vhost_http_port = 80
vhost_https_port = 443

dashboard_addr = 0.0.0.0
dashboard_port = 7500
dashboard_user = admin
dashboard_pwd = your-dashboard-password

log_file = /var/log/frps.log
log_level = info
log_max_days = 3
EOF

# 3. Create systemd service
cat > /etc/systemd/system/frps.service <<'EOF'
[Unit]
Description=Frp Server Service
After=network.target

[Service]
Type=simple
User=root
ExecStart=/opt/frp/frps -c /opt/frp/frps.ini
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
EOF

# 4. Install and start
mkdir -p /opt/frp
cp frps frps.ini /opt/frp/
systemctl daemon-reload
systemctl enable frps
systemctl start frps

# 5. Verify
systemctl status frps
netstat -tlnp | grep frps

Client Deployment (Linux)

# 1. Download frpc
wget https://github.com/fatedier/frp/releases/download/v0.60.0/frp_0.60.0_linux_amd64.tar.gz
 tar -xzf frp_0.60.0_linux_amd64.tar.gz
 cd frp_0.60.0_linux_amd64

# 2. Create frpc.ini
cat > frpc.ini <<'EOF'
[common]
server_addr = your-frps-ip
server_port = 7000
token = your-secret-token-here
log_file = /var/log/frpc.log
log_level = info
log_max_days = 3

# SSH example
[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 2222

# Web example
[web]
type = http
local_ip = 127.0.0.1
local_port = 8080
custom_domains = your-domain.com

# MySQL example
[mysql]
type = tcp
local_ip = 127.0.0.1
local_port = 3306
remote_port = 13306
EOF

# 3. Create systemd service
cat > /etc/systemd/system/frpc.service <<'EOF'
[Unit]
Description=Frp Client Service
After=network.target

[Service]
Type=simple
User=root
ExecStart=/opt/frp/frpc -c /opt/frp/frpc.ini
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
EOF

# 4. Install and start
mkdir -p /opt/frp
cp frpc frpc.ini /opt/frp/
systemctl daemon-reload
systemctl enable frpc
systemctl start frpc

# 5. Verify
systemctl status frpc

Client Deployment (Windows)

# 1. Download Windows binary from https://github.com/fatedier/frp/releases
# 2. Create frpc.ini (same content as Linux)
# 3. Create start‑frpc.bat
@echo off
cd /d %~dp0
frpc.exe -c frpc.ini
pause
# 4. Optional: register as a Windows service using NSSM or winsw

Configuration Details

# frps.ini example
[common]
bind_addr = 0.0.0.0
bind_port = 7000
token = your-secret-token-here
vhost_http_port = 80
vhost_https_port = 443
# optional: bind specific IP, allow_ports, bandwidth limits, heartbeat, logging, etc.
log_file = /var/log/frps.log
log_level = info
log_max_days = 3

# frpc.ini example
[common]
server_addr = 1.2.3.4
server_port = 7000
token = your-secret-token-here
log_file = /var/log/frpc.log
log_level = info
log_max_days = 3

[web]
type = http
local_ip = 127.0.0.1
local_port = 8080
custom_domains = app.your-domain.com
subdomain = app

[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 2222

[udp]
type = udp
local_ip = 127.0.0.1
local_port = 25565
remote_port = 25565

[stcp]
type = stcp
local_ip = 127.0.0.1
local_port = 22
sk = your-secret-key

Common Troubleshooting

# 1. View frps logs
 tail -f /var/log/frps.log
# 2. View frpc logs
 tail -f /var/log/frpc.log
# 3. Test server port connectivity
 nc -zv your-frps-ip 7000
 telnet your-frps-ip 7000
# 4. Connection failures – check token consistency, firewall rules, frps status
 systemctl status frps
# 5. HTTP proxy not working – verify DNS resolves to frps IP, vhost_http_port matches, custom_domains correct
# 6. Unstable client – adjust heartbeat_interval/heartbeat_timeout in frpc.ini
 heartbeat_interval = 5
 heartbeat_timeout = 20
# 7. Bandwidth limits – configure bandwidth_limit_type and bandwidth_limit in frps.ini
 bandwidth_limit_type = server
 bandwidth_limit = 10MB

WireGuard – Virtual LAN

How It Works

WireGuard creates a peer‑to‑peer encrypted tunnel, forming a virtual LAN:

[Machine A] <-- WireGuard tunnel --> [Machine B]
10.0.0.1                10.0.0.2

[Machine A] <-- WireGuard tunnel --> [Machine C]
10.0.0.1                10.0.0.3

Advantages: encrypted tunnel, direct P2P connection (no relay), high performance. Drawbacks: requires each peer’s public IP and reconfiguration of all nodes when a new device joins.

Server Deployment (Gateway)

# 1. Install WireGuard
apt update && apt install -y wireguard   # Ubuntu/Debian
# or for CentOS/RHEL 8+
dnf install -y epel-release && dnf install -y wireguard-tools

# 2. Generate key pairs for server and each client
cd /etc/wireguard
umask 077
wg genkey | tee server_private.key | wg pubkey > server_public.key
wg genkey | tee client1_private.key | wg pubkey > client1_public.key
wg genkey | tee client2_private.key | wg pubkey > client2_public.key

# 3. Create server config (wg0.conf)
cat > wg0.conf <<'EOF'
[Interface]
PrivateKey = <SERVER_PRIVATE_KEY>
Address = 10.0.0.1/24
ListenPort = 51820

PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostUp = iptables -A FORWARD -i wg0 -o wg0 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -o wg0 -j ACCEPT

# Client 1
[Peer]
PublicKey = <CLIENT1_PUBLIC_KEY>
AllowedIPs = 10.0.0.2/32

# Client 2
[Peer]
PublicKey = <CLIENT2_PUBLIC_KEY>
AllowedIPs = 10.0.0.3/32
EOF

chmod 600 wg0.conf
# Enable IP forwarding
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p
# Start WireGuard
systemctl enable wg-quick@wg0
systemctl start wg-quick@wg0
# Verify
wg show
ip addr show wg0

Client Deployment (Linux)

# 1. Install WireGuard
apt install -y wireguard
# 2. Generate keys
cd /etc/wireguard
umask 077
wg genkey | tee client_private.key | wg pubkey > client_public.key

# 3. Create client config
cat > wg0.conf <<'EOF'
[Interface]
PrivateKey = <CLIENT_PRIVATE_KEY>
Address = 10.0.0.2/24
DNS = 8.8.8.8, 8.8.4.4

[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = your-server-ip:51820
PersistentKeepalive = 25
AllowedIPs = 10.0.0.0/24
EOF

chmod 600 wg0.conf
systemctl enable wg-quick@wg0
systemctl start wg-quick@wg0
# Test connectivity
ping 10.0.0.1
ping 10.0.0.3

Client Deployment (Windows/macOS)

# Windows: download installer from https://www.wireguard.com/install/
# macOS: brew install wireguard-tools or download from App Store
# GUI steps:
1. Import or create a tunnel
2. Fill PrivateKey
3. Set Address (e.g., 10.0.0.2/24)
4. Add Peer – Server public key, Endpoint (server-ip:51820)
5. Set AllowedIPs (10.0.0.0/24 for LAN only)
6. Save and activate

Full‑Tunnel (All Traffic) Example

# Client config for full VPN
[Interface]
PrivateKey = <CLIENT_PRIVATE_KEY>
Address = 10.0.0.2/24
DNS = 8.8.8.8

[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = your-server-ip:51820
PersistentKeepalive = 25
AllowedIPs = 0.0.0.0/0
EOF
# Note: When AllowedIPs is 0.0.0.0/0, all traffic is routed through WireGuard; the server must provide NAT and routing.

Common Troubleshooting

# 1. Check status
wg show
wg show wg0
# 2. Verify interface
ip addr show wg0
# 3. Verify routing table
ip route show
# 4. Connection failures – ensure UDP 51820 is open, keys match, AllowedIPs include target IPs
# 5. NAT traversal – symmetric NAT not supported; set PersistentKeepalive = 25 or configure router port forwarding
# 6. Bandwidth test – use iperf3
iperf3 -s   # on server
iperf3 -c 10.0.0.1   # on client

Tailscale – Managed WireGuard Mesh

How It Works

Tailscale builds on WireGuard but adds automatic key management, NAT traversal, and a control plane. The free tier does not require a public relay server; DERP relays are used when direct P2P fails.

Advantages:
1. No manual key handling – Tailscale manages keys.
2. NAT traversal works in most cases without a relay.
3. Free DERP relays are available.
4. Supports Exit Node, ACLs, and all major platforms.

Installation and Deployment

# Linux install (official script)
curl -fsSL https://tailscale.com/install.sh | sh
# or manual RPM for CentOS/RHEL
curl -fsSL https://pkgs.tailscale.com/stable/tailscale-latest.x86_64.rpm -o tailscale.rpm
rpm -i tailscale.rpm

# Start daemon
systemctl start tailscaled
# or run directly
tailscaled &

# Join network (default Tailscale service)
tailscale up
# For custom control plane (e.g., Headscale)
tailscale up --login-server=https://login.example.com

# View status and assigned IPs
tailscale status
tailscale ip
# Enable autostart
systemctl enable tailscaled

Exit Node Configuration

# On the server that will act as Exit Node
tailscale up --advertise-exit-node
# Approve in Tailscale admin console
# On client devices, select the Exit Node
tailscale up --exit-node=<EXIT_NODE_IP>
# Or allow all traffic via the Exit Node
tailscale up --exit-node=allow-networking

ACL (Access Control List) Example

{
  "acls": [
    {"action": "accept", "src": ["*"], "dst": ["*:*"]},
    {"action": "accept", "src": ["group:developers"], "dst": ["tag:production:22"]},
    {"action": "accept", "src": ["tag:ci"], "dst": ["tag:production:0-65535"]}
  ],
  "tagOwners": {
    "tag:production": ["group:admins"],
    "tag:ci": ["group:admins"]
  }
}

Headscale – Self‑Hosted Control Plane

# Deploy Headscale with Docker Compose
version: '3'
services:
  headscale:
    image: ghcr.io/juanfont/headscale:latest
    container_name: headscale
    volumes:
      - /etc/headscale:/etc/headscale
      - /var/lib/headscale:/var/lib/headscale
    ports:
      - "8080:8080"
      - "3478:3478/udp"
    command: serve
    restart: unless-stopped

# Create configuration
mkdir -p /etc/headscale /var/lib/headscale
cat > /etc/headscale/config.yaml <<'EOF'
server_url: http://your-headscale-ip:8080
listen_addr: 0.0.0.0:8080
private_key_path: /var/lib/headscale/private.key
noise:
  private_key_path: /var/lib/headscale/noise_private.key
prefix: 100.64.0.0/10
derp:
  server:
    enabled: false
  urls:
    - https://controlplane.tailscale.com/derpmap/default
EOF

# Initialize and register a node
docker exec -it headscale headscale nodes register --key <NODE_KEY>
# Clients join the private control plane
tailscale up --login-server=http://your-headscale-ip:8080

Common Troubleshooting

# 1. Check daemon status
tailscale status
# 2. View logs
journalctl -u tailscaled -f
# 3. Test connectivity to a peer
tailscale ping <PEER_NAME_OR_IP>
# 4. Verify assigned IPs
tailscale ip -4
tailscale ip -6
# 5. Diagnose connectivity issues
systemctl status tailscaled
# 6. If "NeedsLogin", re‑authenticate
tailscale up
# 7. Check NAT traversal
tailscale netcheck
# 8. Force DERP relay
tailscale up --derp=http://custom-derp-server
# 9. Logout and re‑login for multiple devices
tailscale logout
tailscale up

Feature Comparison

Feature          | frp                | WireGuard          | Tailscale
-----------------+--------------------+--------------------+-------------------
Architecture     | C/S, needs relay   | P2P, no relay      | Hybrid, relay optional
Bandwidth         | Limited by relay   | Limited by server  | No bottleneck in P2P
Latency           | Higher (relay)     | Low (direct)       | Low (P2P) / higher (DERP)
NAT traversal      | Supported          | No symmetric NAT   | Supported
Config difficulty  | Medium             | Harder             | Easy
Cost               | Requires public server | Requires public server | Free tier sufficient
Platforms          | Supported          | Mainstream OSes    | All major platforms
ACL                | No                 | No                 | Yes
Key management     | Manual             | Manual             | Automatic
Third‑party deps   | None               | None               | Tailscale service (optional self‑host)

Recommended Scenarios

frp suitable when:
- Temporary exposure of local services (webhooks, debugging)
- You control a public relay server
- Only simple TCP/UDP port forwarding is needed
- Traffic volume is modest

WireGuard suitable when:
- Stable long‑lived connections are required
- Multiple servers need to form a private network
- Servers have public IPs
- Network environment is simple (no symmetric NAT)
- High performance is a priority

Tailscale suitable when:
- You prefer not to manage servers
- Devices span many platforms (Windows, macOS, Linux, iOS, Android)
- ACL‑based access control is needed
- NAT or symmetric NAT is present
- Quick, hassle‑free deployment is desired

Bandwidth and Latency Benchmarks

Test environment: client behind NAT, server on Alibaba Cloud South China (Guangzhou).

frp:
- Latency: 80‑120 ms (relay)
- Bandwidth: 30‑50 MB/s per connection (relay limited)
- Suitable for web services, SSH, RDP

WireGuard (P2P):
- Latency: 40‑60 ms (direct)
- Bandwidth: ~100 MB/s+ (close to server limit)
- Suitable for file transfer, database connections, SSH

Tailscale (P2P):
- Latency: 40‑60 ms (same as WireGuard)
- Bandwidth: similar to WireGuard
- Relay mode (DERP): 80‑120 ms
- Suitable for all scenarios

Deployment Recommendations

Small Teams / Individual Use

Recommended: Tailscale free tier
Steps:
1. Register a Tailscale account (GitHub/Google login)
2. Install the client on each device
3. Follow the on‑screen authentication
4. Start using the mesh network

Medium‑Size Teams

Recommended: Hybrid WireGuard + Tailscale
Steps:
1. Acquire a public‑IP server
2. Deploy WireGuard on the server
3. Install Tailscale on team devices
4. Configure ACL rules in Tailscale
5. WireGuard interconnects servers

Enterprise Use

Recommended: Self‑hosted Headscale + WireGuard
Steps:
1. Deploy Headscale control plane (Docker or VM)
2. Install WireGuard on servers and register nodes via Headscale
3. Define enterprise‑grade ACL policies in Headscale
4. Roll out clients using the private control plane

FAQ

frp

Q: frp connects but the service is unreachable? Check that the client’s local_port matches the service, the server’s remote_port matches the client config, the service is listening locally, and firewalls allow the ports.

Q: How to enable HTTPS with frp? Either configure TLS termination on frps with a certificate, or use the https2http plugin on frpc to forward HTTPS to a local HTTP service.

Q: How to limit bandwidth? Set bandwidth_limit_type and bandwidth_limit in frps.ini.

WireGuard

Q: Connection succeeds but ping fails? Verify that IP forwarding is enabled on the server, firewall rules allow the wg0 interface, and the client’s AllowedIPs includes the target address.

Q: How to add a new client? Generate a key pair on the client, send the public key to the administrator, add a [Peer] section with the public key and AllowedIPs to wg0.conf, then restart the WireGuard service.

Q: Does WireGuard support Windows/macOS? Yes. Windows installers are available from the official site; macOS users can install via Homebrew or the App Store.

Tailscale

Q: Does the free tier have traffic limits? No overall traffic limit, but DERP relays are capped at ~1 Mbps per connection. Direct P2P connections are unlimited.

Q: How to disable DERP and force P2P? Run tailscale netcheck to see NAT type. If the NAT is symmetric, P2P may not work and DERP is required.

Q: Can I self‑host the control plane? Yes. Headscale includes a built‑in DERP server, or you can deploy a separate derper instance.

Q: Is Tailscale secure? Tailscale uses WireGuard’s cryptography; the traffic is end‑to‑end encrypted and the Tailscale service does not decrypt user data.

Conclusion

All three internal‑network penetration solutions have distinct trade‑offs:

frp – Traditional reverse‑proxy approach; fully controllable but requires a public relay and incurs bandwidth/latency limits.

WireGuard – High‑performance VPN; ideal for stable, high‑throughput private networks, but needs public IPs and manual key management.

Tailscale – Managed WireGuard mesh; fastest to set up, handles NAT traversal automatically, and offers ACLs, making it the most convenient for personal use and small teams.

Choosing the right tool depends on scale, performance requirements, and operational overhead.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

comparisonVPNfrpNAT traversalWireGuardTailscaleinternal networkingnetwork penetration
MaGe Linux Operations
Written by

MaGe Linux Operations

Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.