コンテンツにスキップ

sslh

sslh is a protocol multiplexer that accepts incoming connections on a single port and routes them to appropriate backends based on protocol detection. This allows running multiple services (HTTPS, SSH, OpenVPN, etc.) on the same port 443.

  • Multiplex multiple protocols on single port
  • Transparent protocol detection
  • Load balancing capabilities
  • IPv4 and IPv6 support
  • TLS/SSL certificate forwarding
  • HTTPS, SSH, OpenVPN, Xmpp support
  • Lightweight and efficient
  • Systemd integration
  • ACL-based routing
  • Unified port access for multiple services
  • Firewall policy compliance (single port)
  • Network security with limited open ports
  • Accessing services through restrictive firewalls
  • Server consolidation
  • Penetration testing from restricted networks
sudo apt-get update
sudo apt-get install sslh
brew install sslh
sudo yum install sslh
git clone https://github.com/yob/sslh.git
cd sslh
make
sudo make install

Default location: /etc/sslh/sslh.cfg

# sslh configuration file
verbose: false;
foreground: false;
inetd: false;
numeric: true;

# Listen on port 443 for all incoming connections
listen:
(
    { host: "0.0.0.0"; port: "443"; }
);

# Protocol-specific backends
protocols:
(
    # HTTPS traffic to Apache
    { name: "https"; host: "localhost"; port: "8443"; },
    
    # SSH traffic to SSH server
    { name: "ssh"; host: "localhost"; port: "22"; },
    
    # OpenVPN
    { name: "openvpn"; host: "localhost"; port: "1194"; },
    
    # XMPP
    { name: "xmpp"; host: "localhost"; port: "5222"; }
);

# Logging
logfile: "/var/log/sslh/sslh.log";
pidfile: "/var/run/sslh.pid";
listen:
(
    { host: "0.0.0.0"; port: "443"; },
    { host: "0.0.0.0"; port: "8443"; },
    { host: "::"; port: "443"; }  # IPv6
);
protocols:
(
    {
        name: "https";
        host: "localhost";
        port: "8443";
        sni_hostnames: [ "example.com", "www.example.com" ];
    },
    { name: "ssh"; host: "localhost"; port: "22"; }
);
sudo systemctl start sslh
sudo systemctl enable sslh
sudo systemctl status sslh
sudo journalctl -u sslh -f
sudo sslh -f -v -c /etc/sslh/sslh.cfg

Flags:

  • -f: Foreground (don’t daemonize)
  • -v: Verbose output
  • -c: Configuration file path
sudo sslh -c /etc/sslh/sslh.cfg
sudo sslh -t -c /etc/sslh/sslh.cfg

Validates configuration without starting service.

protocols:
(
    { name: "https"; host: "localhost"; port: "8443"; },
    { name: "ssh"; host: "localhost"; port: "22"; }
);

Upstream services needed:

  • HTTPS web server on port 8443
  • SSH server on port 22
protocols:
(
    { name: "https"; host: "localhost"; port: "8443"; },
    { name: "ssh"; host: "localhost"; port: "22"; },
    { name: "openvpn"; host: "localhost"; port: "1194"; },
    { name: "xmpp"; host: "localhost"; port: "5222"; }
);
protocols:
(
    { 
        name: "https"; 
        host: "10.0.0.5"; 
        port: "8443";
    },
    { 
        name: "ssh"; 
        host: "10.0.0.6"; 
        port: "22";
    }
);

Route to different backend servers by protocol.

protocols:
(
    {
        name: "https";
        host: "localhost";
        port: "8443";
    },
    {
        name: "https";
        host: "localhost";
        port: "8444";
    }
);

Round-robin between multiple web servers.

# Server running sslh on port 443
ssh -p 443 user@example.com

# Or with specific config
ssh -p 443 -o "StrictHostKeyChecking=no" user@example.com
curl https://example.com:443/

# Web browser: https://example.com:443

Configure OpenVPN client to connect to:

remote example.com 443
proto tcp

Stunnel Integration (for non-TLS Protocols)

Section titled “Stunnel Integration (for non-TLS Protocols)”

Some protocols need encapsulation in TLS:

# stunnel wrapper for SSH through HTTPS port
[ssh-over-https]
client = yes
accept = localhost:2222
connect = example.com:443
protocols:
(
    {
        name: "https";
        host: "localhost";
        port: "8443";
        sni_hostnames: [ "api.example.com" ];
    },
    {
        name: "https";
        host: "localhost";
        port: "8445";
        sni_hostnames: [ "www.example.com" ];
    }
);

Routes based on SNI hostname in TLS ClientHello.

protocols:
(
    { name: "https"; host: "localhost"; port: "8443"; },
    { name: "ssh"; host: "localhost"; port: "22"; },
    { name: "tls"; host: "localhost"; port: "443"; }  # Catch-all
);
protocols:
(
    {
        name: "https";
        host: "localhost";
        port: "8443";
        user_filter: [ "user1", "user2" ];
    },
    { name: "ssh"; host: "localhost"; port: "22"; }
);
inetd: true;

protocols:
(
    { name: "https"; host: "localhost"; port: "8443"; },
    { name: "ssh"; host: "localhost"; port: "22"; }
);
# Apache HTTPS on 8443
sudo a2enmod ssl
sudo a2enmod rewrite

# Create SSL config pointing to port 8443
# in /etc/apache2/sites-available/ssl.conf
<VirtualHost *:8443>
    ServerName example.com
    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/server.crt
    SSLCertificateKeyFile /etc/ssl/private/server.key
</VirtualHost>
sudo systemctl restart apache2
# Ensure SSH runs on default port 22
sudo nano /etc/ssh/sshd_config
# Port 22

sudo systemctl restart ssh
# /etc/openvpn/server.conf
port 1194
proto tcp
ca ca.crt
cert server.crt
key server.key
dh dh.pem

# Point clients to port 443
# Self-signed certificate for sslh
sudo openssl req -x509 -newkey rsa:2048 \
    -keyout /etc/ssl/private/sslh.key \
    -out /etc/ssl/certs/sslh.crt \
    -days 365 -nodes
# Allow incoming on port 443
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# Allow SSH and HTTPS backends
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 8443 -j ACCEPT

# Save rules
sudo iptables-save > /etc/iptables/rules.v4
sudo ufw allow 443/tcp
sudo ufw allow 22/tcp
sudo ufw allow 8443/tcp
sudo ufw enable
listen:
(
    { host: "0.0.0.0"; port: "443"; },  # IPv4
    { host: "::"; port: "443"; }         # IPv6
);
sudo journalctl -u sslh -f
ps aux | grep sslh
sudo netstat -antp | grep 443
# or
sudo ss -antp | grep 443
# In /etc/sslh/sslh.cfg
verbose: true;
logfile: "/var/log/sslh/sslh.log";
# Count connections by protocol
sudo grep "Accepted" /var/log/sslh/sslh.log | \
    awk '{print $NF}' | sort | uniq -c

# Monitor in real-time
sudo tail -f /var/log/sslh/sslh.log

Issue: Clients can’t connect to multiplexed port.

Solution:

# 1. Check sslh is running
sudo systemctl status sslh

# 2. Verify configuration
sudo sslh -t -c /etc/sslh/sslh.cfg

# 3. Check backend services
sudo netstat -antp | grep 8443
sudo netstat -antp | grep 22

# 4. Verify port binding
sudo netstat -antp | grep 443

Issue: Traffic routing to wrong backend.

Solution:

# Test with verbose logging
sudo systemctl stop sslh
sudo sslh -f -v -c /etc/sslh/sslh.cfg

# Attempt connection and watch output
# Modify protocol detection if needed

Issue: Service exists but sslh can’t route to it.

Solution:

# Test backend connectivity
nc -zv localhost 8443  # HTTPS backend
ssh -p 22 localhost    # SSH backend

# Verify firewall allows traffic
sudo iptables -L -n | grep 8443

# Check backend service configuration
sudo lsof -i :8443

Issue: sslh consuming excessive CPU.

Solution:

# Check for connection storms
sudo netstat -antp | grep 443 | wc -l

# Implement rate limiting
sudo iptables -A INPUT -p tcp --dport 443 \
    -m limit --limit 100/second --limit-burst 200 -j ACCEPT

# Monitor process
top -p $(pidof sslh)
# Increase file descriptor limit
sudo ulimit -n 65536

# Make permanent in /etc/security/limits.conf
* soft nofile 65536
* hard nofile 65536
# Optimize for connection multiplexing
sudo sysctl -w net.core.somaxconn=4096
sudo sysctl -w net.ipv4.tcp_max_syn_backlog=4096
# Run multiple multiplexers for load distribution
sudo sslh -l localhost:1443 &
sudo sslh -l localhost:1444 &

# Load balance with nginx upstream
upstream sslh {
    server localhost:1443;
    server localhost:1444;
}
PracticeReason
Use firewall rulesRestrict access to multiplexer
Monitor logsDetect suspicious patterns
Validate certificatesEnsure backend authenticity
Rate limit connectionsPrevent DoS attacks
Keep updatedSecurity patches and bug fixes
# Only allow sslh to connect to backends
sudo iptables -A INPUT -p tcp --dport 8443 \
    -s localhost -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 8443 -j DROP
upstream http_backend {
    server localhost:8443;
}

server {
    listen 8443 ssl;
    ssl_certificate /etc/ssl/certs/server.crt;
    ssl_certificate_key /etc/ssl/private/server.key;
    
    location / {
        proxy_pass https://http_backend;
    }
}
FROM ubuntu:22.04

RUN apt-get update && apt-get install -y sslh

COPY sslh.cfg /etc/sslh/sslh.cfg
EXPOSE 443

CMD ["sslh", "-f", "-c", "/etc/sslh/sslh.cfg"]
version: '3'
services:
  sslh:
    image: sslh:latest
    ports:
      - "443:443"
    volumes:
      - ./sslh.cfg:/etc/sslh/sslh.cfg
    depends_on:
      - web
      - ssh
  
  web:
    image: nginx:latest
    expose:
      - "8443"
  
  ssh:
    image: linux:ssh
    expose:
      - "22"
# Start service
sudo systemctl start sslh

# Test configuration
sudo sslh -t -c /etc/sslh/sslh.cfg

# Run in foreground with verbose output
sudo sslh -f -v -c /etc/sslh/sslh.cfg

# Connect via SSH (multiplexed)
ssh -p 443 user@server

# Connect via HTTPS
curl https://server:443/

# View logs
sudo journalctl -u sslh -f

# Monitor connections
sudo ss -antp | grep 443