yrutschle / sslh

Applicative Protocol Multiplexer (e.g. share SSH and HTTPS on the same port)
https://www.rutschle.net/tech/sslh/README.html
GNU General Public License v2.0
4.59k stars 366 forks source link

Transparent proxy does not work only with IPv6 #395

Closed soramikan closed 1 year ago

soramikan commented 1 year ago

I am using sslh in Transparent mode and all is well with IPv4! However, it does not work at all with IPv6 with the following error:

ssh

kex_exchange_identification: Connection closed by remote host

https with curl

curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to example.com:443

These work well with the -4 option.

My environment:

sslh-select --version                    
# sslh-select v1.22c

I'm useing the latest ArchLinux

This is my sslh.cfg file:

verbose: 0;
foreground: true;
inetd: false;
numeric: false;
transparent: true;
timeout: 2;
user: "sslh";
pidfile: "/run/sslh/sslh.pid";

listen:
(
    {
      host: "<My external IPv4 address>";
      is_udp: false;
      port: "443";
    },
    {
      host: "<My external IPv4 address>";
      is_udp: false;
      port: "443";
    } 
);

protocols:
(
    { name: "ssh";
          service: "ssh";
          host: "127.0.0.1";
          port: "2222";
          is_udp: false;
          log_level: 1;
          fork: true;
    },
    { name: "ssh";
          service: "ssh";
          host: "[::1]";
          port: "2222";
          is_udp: false;
          log_level: 3;
          fork: true;
    },
    { name: "tls";
        host: "127.0.0.1";
        port: "443";
        is_udp: false;
        log_level: 0;
        tfo_ok: true },
    { name: "tls";
        host: "[::1]";
        port: "443";
        is_udp: false;
        log_level: 0;
        tfo_ok: true },
);

This is a unit file set up the same way as this guide.

#!/bin/zsh
# Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination
sysctl -w net.ipv4.conf.default.route_localnet=1
sysctl -w net.ipv4.conf.all.route_localnet=1

# DROP martian packets as they would have been if route_localnet was zero
# Note: packets not leaving the server aren't affected by this, thus sslh will still work
iptables -t raw -A PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP
iptables -t mangle -A POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP

# Mark all connections made by ssl for special treatment (here sslh is run as user "sslh")
iptables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f

# Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark)
iptables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f

# Configure routing for those marked packets
ip rule add fwmark 0x1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100

# Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination
# Not sure if this is needed for ipv6 though
sysctl -w net.ipv4.conf.default.route_localnet=1
sysctl -w net.ipv4.conf.all.route_localnet=1

# DROP martian packets as they would have been if route_localnet was zero
# Note: packets not leaving the server aren't affected by this, thus sslh will still work
ip6tables -t raw -A PREROUTING ! -i lo -d ::1/128 -j DROP
ip6tables -t mangle -A POSTROUTING ! -o lo -s ::1/128 -j DROP

# Mark all connections made by ssl for special treatment (here sslh is run as user "sslh")
ip6tables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f

# Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark)
ip6tables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f

# Configure routing for those marked packets
ip -6 rule add fwmark 0x1 lookup 100
ip -6 route add local ::/0 dev lo table 100

Tanks!

alexlehm commented 1 year ago

We came across this issue in a gemini host that tries to do tls based rules via the hostname for ipv4 and ipv6 and it is probably caused by the problem that you cannot do transparent=true when you switch between ipv6 and ipv6 from the client to the local server, the suggested solution as described in the mail list (https://lists.rutschle.net/mailman/archives/sslh/2016-January/000603.html) is to use a DNS entry to point to the ipv4 and ivp6 address (or a hosts entry) and use that as target host for the protocols entry. this way it tries to connect to either and will choose the right ip version

I kind of would prefer an option to define rules that only match ipv4 or ipv6 to make that more obvious when using either

soramikan commented 1 year ago

Thank you, everything worked fine by putting it in the hosts file!