Zum Inhalt springen

Pangolin

Pangolin is an open-source, self-hosted tunneling server and reverse proxy that enables secure exposure of local services to the internet without requiring static IPs or complex firewall configuration. It provides encrypted tunnels similar to Cloudflare Tunnels, ngrok, and frp, with full control over infrastructure and data flow.

# Pull the official Pangolin Docker image
docker pull pangolin:latest

# Run Pangolin server
docker run -d \
  --name pangolin-server \
  -p 8080:8080 \
  -p 443:443 \
  -v /etc/pangolin:/etc/pangolin \
  pangolin:latest

# Run with environment variables
docker run -d \
  --name pangolin-server \
  -p 8080:8080 \
  -p 443:443 \
  -e PANGOLIN_PORT=8080 \
  -e PANGOLIN_TLS=true \
  -v /etc/pangolin:/etc/pangolin \
  pangolin:latest
# Download latest release
wget https://github.com/pangolin/releases/download/v1.x.x/pangolin-linux-amd64.tar.gz

# Extract
tar -xzf pangolin-linux-amd64.tar.gz

# Make executable
chmod +x pangolin

# Install to system path
sudo mv pangolin /usr/local/bin/

# Verify installation
pangolin --version
# Clone repository
git clone https://github.com/pangolin/pangolin.git
cd pangolin

# Build using Go
go build -o pangolin ./cmd/server

# Run
./pangolin --config config.yaml
# config.yaml
server:
  # Listen address and port
  bind_addr: "0.0.0.0"
  listen_port: 8080
  
  # TLS/SSL configuration
  tls:
    enabled: true
    cert_file: "/etc/pangolin/certs/server.crt"
    key_file: "/etc/pangolin/certs/server.key"
  
  # Domain configuration
  domain: "tunnel.example.com"
  
  # Token authentication
  token: "your-secure-token-here"

# Logging configuration
logging:
  level: info
  format: json
  output: stdout

# Database (optional)
database:
  type: sqlite
  path: /var/lib/pangolin/pangolin.db
server:
  bind_addr: "0.0.0.0"
  listen_port: 8080
  
  # Max concurrent connections
  max_connections: 10000
  
  # Connection timeout (seconds)
  connection_timeout: 300
  
  # TLS minimum version
  tls_min_version: "1.2"
  
  # Enable compression
  compression: true
  
  # Rate limiting
  rate_limit:
    enabled: true
    requests_per_second: 1000
    burst_size: 2000
# Using environment variables
export PANGOLIN_BIND_ADDR="0.0.0.0"
export PANGOLIN_PORT="8080"
export PANGOLIN_DOMAIN="tunnel.example.com"
export PANGOLIN_TOKEN="secure-token"
export PANGOLIN_TLS_ENABLED="true"
export PANGOLIN_TLS_CERT="/etc/pangolin/certs/server.crt"
export PANGOLIN_TLS_KEY="/etc/pangolin/certs/server.key"

pangolin
# Install Pangolin client on local machine
# macOS
brew install pangolin

# Linux
sudo apt-get install pangolin

# Or download binary
wget https://github.com/pangolin/releases/download/v1.x.x/pangolin-client-linux-amd64

# Or using Go
go install github.com/pangolin/pangolin/cmd/client@latest
# Start a simple HTTP tunnel
pangolin-client --server tunnel.example.com:8080 \
  --token your-token \
  --local-port 3000 \
  --tunnel-name myapp

# Create tunnel with custom remote port
pangolin-client --server tunnel.example.com:8080 \
  --token your-token \
  --local-port 3000 \
  --remote-port 8443

# List active tunnels
pangolin-client --server tunnel.example.com:8080 \
  --token your-token \
  --list-tunnels
# ~/.pangolin/client-config.yaml
server:
  address: "tunnel.example.com"
  port: 8080
  token: "your-secure-token"
  tls: true

tunnels:
  - name: "web-app"
    local_addr: "127.0.0.1"
    local_port: 3000
    remote_hostname: "web-app.tunnel.example.com"
    type: "http"
  
  - name: "api-server"
    local_addr: "127.0.0.1"
    local_port: 5000
    remote_hostname: "api.tunnel.example.com"
    type: "http"
  
  - name: "ssh"
    local_addr: "127.0.0.1"
    local_port: 22
    remote_port: 2222
    type: "tcp"
pangolin-client --config ~/.pangolin/client-config.yaml
# Expose local HTTP service
pangolin-client --server tunnel.example.com:8080 \
  --token mytoken \
  --local-addr 127.0.0.1 \
  --local-port 8000 \
  --service-type http \
  --tunnel-name website

# Access via
# http://website.tunnel.example.com
# Expose HTTPS service
pangolin-client --server tunnel.example.com:8080 \
  --token mytoken \
  --local-addr 127.0.0.1 \
  --local-port 8443 \
  --service-type https \
  --tunnel-name secure-app

# Server automatically handles HTTPS
# Access via https://secure-app.tunnel.example.com
# Configure multiple services per domain
tunnels:
  - name: "app1"
    local_port: 3000
    domain_routes:
      - path: "/"
        target: "127.0.0.1:3000"
  
  - name: "app2"
    local_port: 3001
    domain_routes:
      - path: "/api"
        target: "127.0.0.1:3001"
# Create TCP tunnel for database
pangolin-client --server tunnel.example.com:8080 \
  --token mytoken \
  --local-port 5432 \
  --remote-port 5432 \
  --protocol tcp \
  --tunnel-name postgres

# Create TCP tunnel for SSH
pangolin-client --server tunnel.example.com:8080 \
  --token mytoken \
  --local-port 22 \
  --remote-port 2222 \
  --protocol tcp \
  --tunnel-name ssh-access

# Connect via tunnel
ssh -p 2222 user@tunnel.example.com
psql -h tunnel.example.com -p 5432
# Create UDP tunnel
pangolin-client --server tunnel.example.com:8080 \
  --token mytoken \
  --local-port 53 \
  --remote-port 53 \
  --protocol udp \
  --tunnel-name dns

# Create UDP tunnel for gaming/media
pangolin-client --server tunnel.example.com:8080 \
  --token mytoken \
  --local-port 5005 \
  --remote-port 5005 \
  --protocol udp \
  --tunnel-name game-server
tunnels:
  - name: "database"
    local_port: 5432
    remote_port: 5432
    protocol: tcp
    
  - name: "dns"
    local_port: 53
    remote_port: 53
    protocol: udp
    
  - name: "web"
    local_port: 80
    remote_port: 80
    protocol: tcp
# Generate private key
openssl genrsa -out server.key 2048

# Generate certificate signing request
openssl req -new -key server.key -out server.csr \
  -subj "/CN=tunnel.example.com"

# Generate self-signed certificate
openssl x509 -req -days 365 -in server.csr \
  -signkey server.key -out server.crt

# Verify certificate
openssl x509 -in server.crt -text -noout
# Install certbot
sudo apt-get install certbot

# Obtain certificate
sudo certbot certonly --standalone \
  -d tunnel.example.com

# Configure in Pangolin
server:
  tls:
    enabled: true
    cert_file: "/etc/letsencrypt/live/tunnel.example.com/fullchain.pem"
    key_file: "/etc/letsencrypt/live/tunnel.example.com/privkey.pem"
    
  # Auto-renewal script
  cert_auto_renew: true
  cert_check_interval: 86400  # 24 hours
server:
  tls:
    enabled: true
    cert_file: "/etc/pangolin/certs/server.crt"
    key_file: "/etc/pangolin/certs/server.key"
    
    # Minimum TLS version
    min_version: "1.2"
    
    # Cipher suites
    cipher_suites:
      - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
      - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
    
    # Client certificates (mTLS)
    client_auth: "verify_if_given"
    client_cert_file: "/etc/pangolin/certs/ca.crt"
# Generate secure token
openssl rand -hex 32

# Use token with client
pangolin-client --server tunnel.example.com:8080 \
  --token abc123def456... \
  --local-port 3000 \
  --tunnel-name app

# Multiple tokens
pangolin-client --server tunnel.example.com:8080 \
  --tokens token1,token2,token3 \
  --local-port 3000
# Server config with API keys
auth:
  type: "api_key"
  
  api_keys:
    - key: "pk_live_1234567890abcdef"
      name: "production"
      rate_limit: 10000
      allowed_tunnels:
        - "web-app"
        - "api-server"
    
    - key: "pk_test_0987654321fedcba"
      name: "staging"
      allowed_tunnels: "*"

# Client with API key
pangolin-client --server tunnel.example.com:8080 \
  --api-key pk_live_1234567890abcdef \
  --local-port 3000
# Server config with IP restrictions
access_control:
  enabled: true
  
  ip_whitelist:
    - "203.0.113.0/24"      # Office network
    - "198.51.100.50"        # Specific IP
    - "2001:db8::/32"        # IPv6 range

  ip_blacklist:
    - "192.0.2.5"            # Blacklisted IP
# Add wildcard DNS record to your domain
# Using dig to verify
dig *.tunnel.example.com

# DNS A record
*.tunnel.example.com  A  203.0.113.10

# DNS AAAA record (IPv6)
*.tunnel.example.com  AAAA  2001:db8::1
server:
  # If server is on residential network
  ddns:
    enabled: false
    
  # Or use reverse DNS
  reverse_dns:
    enabled: true
    default_domain: "tunnel.example.com"
# Map custom domains to tunnels
domain_mappings:
  - domain: "myapp.com"
    tunnel: "web-app"
  
  - domain: "api.myapp.com"
    tunnel: "api-server"
  
  - domain: "admin.internal.com"
    tunnel: "admin-panel"
    access_control: "token_required"
# Server A (primary)
server:
  listen_port: 8080
  cluster:
    enabled: true
    node_id: "node-1"
    peers:
      - "tunnel-server-2.example.com:8080"
      - "tunnel-server-3.example.com:8080"

# Server B (standby)
server:
  listen_port: 8080
  cluster:
    enabled: true
    node_id: "node-2"
    peers:
      - "tunnel-server-1.example.com:8080"
      - "tunnel-server-3.example.com:8080"
tunnels:
  - name: "web-app-lb"
    type: "http"
    load_balance:
      enabled: true
      algorithm: "round-robin"  # round-robin, least-connections, random
      targets:
        - "127.0.0.1:3000"
        - "127.0.0.1:3001"
        - "127.0.0.1:3002"
server:
  health_checks:
    enabled: true
    interval: 30  # seconds
    timeout: 5
    
tunnels:
  - name: "api"
    health_check:
      enabled: true
      path: "/health"
      interval: 10
      expected_status: 200
logging:
  level: "debug"  # debug, info, warn, error
  format: "json"  # json or text
  output: "file"
  file: "/var/log/pangolin/server.log"
  
  # Log rotation
  max_size_mb: 100
  max_backups: 10
  max_age_days: 30
# Enable metrics endpoint
pangolin --metrics-enable \
  --metrics-port 9090 \
  --config config.yaml

# Access metrics
curl http://localhost:9090/metrics

# Prometheus scrape config
scrape_configs:
  - job_name: 'pangolin'
    static_configs:
      - targets: ['localhost:9090']
# List active tunnels and connections
pangolin-client --server tunnel.example.com:8080 \
  --token mytoken \
  --stats

# Server-side connection info
pangolin --config config.yaml --stats-interval 10
FeaturePangolinCloudflare Tunnels
Self-hostedYesNo (SaaS)
CostFree (OSS)Paid plans
Data ownership100%Cloudflare controls
Setup complexityMediumLow
Custom certificatesYesLimited
Enterprise supportCommunityYes
Multi-regionComplexBuilt-in
FeaturePangolinngrok
Self-hosted optionYesNo
Free tierYesYes
TCP tunnelingFullLimited
Custom domainsYesPaid
SSL certificatesOwn certsngrok managed
Bandwidth limitsNoneYes (free tier)
Open sourceYesNo
FeaturePangolinfrp
Written inGoGo
ConfigurationYAMLINI
Web UIAvailableLimited
DashboardYesBasic
CommunityGrowingEstablished
Container readyYesYes
Plugin systemYesYes
server:
  # Restrict to internal network
  bind_addr: "10.0.0.5"
  
  # Use strong token
  token: "$(openssl rand -hex 32)"
  
  # Enable TLS
  tls:
    enabled: true
    min_version: "1.3"
  
  # Disable dangerous features
  allow_file_download: false
  
  # Restrict tunnels
  max_tunnels_per_client: 10
  
  # Rate limiting
  rate_limit:
    enabled: true
    requests_per_second: 500
# Store token in environment variable
export PANGOLIN_TOKEN=$(cat ~/.pangolin/token)

# Use token from file (with restricted permissions)
chmod 600 ~/.pangolin/token
pangolin-client --token-file ~/.pangolin/token \
  --server tunnel.example.com:8080 \
  --local-port 3000

# Rotate tokens regularly
pangolin-server --rotate-token pk_old_token pk_new_token
server:
  # Firewall rules
  firewall:
    enabled: true
    
    inbound_rules:
      - port: 8080
        protocol: tcp
        allow_from: "10.0.0.0/8"
  
  # Disable IP spoofing
  check_client_ip: true
  
  # Log all access attempts
  audit_log:
    enabled: true
    file: "/var/log/pangolin/audit.log"
client:
  tls:
    # Pin server certificate
    pin_certificate: true
    cert_fingerprint: "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
IssueSolution
Connection timeoutCheck firewall rules, server address, token
TLS handshake failedVerify certificates, check TLS version compatibility
High latencyEnable compression, check network, use closer server
Token invalidRegenerate token, verify token hasn’t expired
Tunnel not accessibleCheck DNS records, firewall, client still running
# Run server in debug mode
PANGOLIN_LOG_LEVEL=debug pangolin --config config.yaml

# Run client in debug mode
PANGOLIN_LOG_LEVEL=debug pangolin-client \
  --server tunnel.example.com:8080 \
  --token mytoken \
  --local-port 3000 \
  --debug