You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Stunnel置于Nginx反向代理后隐藏OpenVPN?配置疑问

How to Route Specific Subdomain to Stunnel via Nginx (Without Disrupting SSL or Existing Services)

Absolutely, you can pull this off without messing up your existing blog proxies or breaking Stunnel's encapsulation. The key is to use Nginx's stream module to route traffic based on SNI (Server Name Indication) at the TCP layer—this lets Nginx forward raw SSL traffic to Stunnel without decrypting it, preserving Stunnel's VPN encapsulation. Here's a step-by-step breakdown:

Core Concept

Instead of letting Nginx terminate SSL for all 443 traffic, we’ll use ssl_preread to read the SNI header during the SSL handshake (without decrypting the connection) and route:

  • Normal blog subdomains (e.g., blog1.example.com) to your existing HTTP/HTTPS proxy setup
  • Your VPN subdomain (e.g., vpn.example.com) directly to Stunnel's listening port

Step 1: Verify Nginx Stream Module Support

First, confirm your Nginx installation has the stream module enabled. Most modern package managers (like Ubuntu’s nginx-full, RHEL’s nginx) include it by default. Check with:

nginx -V 2>&1 | grep -q stream && echo "Stream module enabled" || echo "Stream module missing"

If it’s missing, recompile Nginx with --with-stream or install the appropriate package.

Step 2: Update Nginx Configuration

Stream Block (TCP Layer Routing)

Add this to your main nginx.conf file (or include it from a separate config):

stream {
    # Map SNI values to backend destinations
    map $ssl_preread_server_name $backend {
        vpn.example.com stunnel_backend;  # Your VPN subdomain
        default http_backend;             # All other subdomains go to blogs
    }

    # Define backend pools
    upstream stunnel_backend {
        server 127.0.0.1:9000;  # Stunnel's local listening port
    }

    upstream http_backend {
        server 127.0.0.1:8443;  # Local port for your blog proxy setup
    }

    # Listen on port 443 (TCP)
    server {
        listen 443;
        ssl_preread on;  # Critical: Read SNI without decrypting SSL
        proxy_pass $backend;
        proxy_timeout 1h;  # Longer timeout for VPN long-lived connections
    }
}

HTTP Block (Blog Proxy Setup)

Modify your existing HTTP configuration to listen on local port 8443 instead of 443 (since the stream module now handles 443):

http {
    # Keep your existing global config (mime types, logs, etc.)

    server {
        listen 127.0.0.1:8443 ssl;
        server_name blog1.example.com blog2.example.com;  # Your blog subdomains

        # Your existing SSL certificate config (use a wildcard/multi-domain cert)
        ssl_certificate /path/to/fullchain.pem;
        ssl_certificate_key /path/to/privkey.pem;

        # Your existing blog proxy rules
        location / {
            proxy_pass http://localhost:8000;  # Example blog service port
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            # Add other proxy headers as needed
        }
    }

    # Repeat server blocks for other blogs, all listening on 127.0.0.1:8443
}

Step 3: Configure Stunnel

Update your stunnel.conf to listen on the local port we specified (9000) and forward traffic to your VPN service (e.g., OpenVPN, Shadowsocks):

client = no  # Run in server mode
[vpn]
accept = 127.0.0.1:9000  # Match Nginx's stunnel_backend port
connect = 127.0.0.1:1080  # Your VPN service's local listening port
# Optional: If Stunnel handles its own SSL encryption (recommended)
cert = /path/to/stunnel-cert.pem
key = /path/to/stunnel-key.pem

Step 4: Test & Verify

  1. Test Blog Services: Run curl -v https://blog1.example.com—you should get your normal blog response with no changes.
  2. Test VPN Subdomain: Use openssl s_client -connect vpn.example.com:443—you should see Stunnel's SSL handshake details (if enabled), confirming traffic reaches Stunnel.
  3. Reload Nginx: Apply changes and check for errors:
nginx -s reload
tail -f /var/log/nginx/error.log

Alternative If Stream Module Isn't Available

If you can’t use the stream module (e.g., old Nginx version), two workarounds:

  • Separate Port: Run Stunnel on a different port (e.g., 4443) and update your VPN client to connect there. Less stealthy but simpler.
  • IPTables Forwarding: Use iptables to route traffic for your VPN subdomain to Stunnel, though this is less flexible than Nginx's SNI routing.

Key Notes

  • SSL Certificates: Use a wildcard (*.example.com) or multi-domain certificate to cover all subdomains—this ensures clients trust both blog and VPN connections.
  • Stealth: SNI routing makes VPN traffic look like normal HTTPS to external observers, boosting stealth.
  • Long Connections: The proxy_timeout 1h in the stream block prevents premature VPN connection drops.

内容的提问来源于stack exchange,提问作者Maxithi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 04:11:58