SNI-like routing

Brian Candler b.candler at pobox.com
Thu Sep 22 07:36:27 AEST 2022


Of course, the ideal solution would be to use IPv6 :-)

Given that's not ubiquituous, you still need some sort of gateway on 
IPv4.  What I do is to use an SOCKS5 server, configured to allow inbound 
unauthenticated access (on IPv4) and make outbound to local network port 
22 only (on IPv6).

Then on the ssh client side:

Host *.yourdomain.com
ProxyCommand sh -c 'nc %h %p || ncat --proxy-type socks5 --proxy 
192.0.2.2:22080 %h %p'

Notes:

* 192.0.2.2 is the machine where the SOCKS5 server is running, listening 
on port 22080

* The "nc %h %p ||" bit is so that it tries a direct connection first - 
assuming you have an AAAA record in the DNS - and fallback to using 
SOCKS only if that fails to connect. You can remove this if you *only* 
support access via the SOCKS5 proxy.

The SOCKS5 server I'm using is danted, and I'll paste the sanitised 
danted.conf below.  It's back-to-front compared to normal deployments: 
the "internal" interface is the outside public IP, and the "external" 
interface is the one which originates connections to your local network.

I've found this works pretty well. The main downside is that you lose 
visibility of the original source IPv4 address in your sshd logs.  One 
way I've thought of handling that is to have the SOCKS5 server bind to a 
/96 block of IPv6 addresses, and embed the client's IPv4 address in the 
low 32 bits - but that's more code hacking than I had time for.

I can think of variations on this theme. For example, you could choose 
to require authentication on the SOCKS5 connection, and each user could 
have different credentials that allow them access only to "their" target 
server.  But in my case, I trust sshd: I allow port 22 from the world 
directly via IPv6 anyway, so I have no problem also allowing inbound 
SOCKS5 proxying to port 22.

HTH,

Brian.

===== 8< =====

logoutput: stderr

# Accept connections from the outside interface. Use a non-default
# port to be slightly less susceptible to port scanning
internal.protocol: ipv4
internal: 192.0.2.2 port = 22080

# "Outgoing" connections use IPv6 only
external.protocol: ipv6
external: eth0

# methods for client-rules (on initial connection)
clientmethod: none

# methods for socks-rules (during negotiation)
socksmethod: username none

user.privileged: root
user.notprivileged: proxy
user.libwrap: proxy

# The rules prefixed with "client" are checked first and say who is allowed
# and who is not allowed to speak/connect to the server
#
# The "to:" in the "client" context gives the address the connection
# is accepted on, i.e the address the socksserver is listening on, or
# just "0.0.0.0/0" for any address the server is listening on.

client pass {
         from: 0.0.0.0/0 port 1-65535 to: 0.0.0.0/0
}

# you probably don't want people connecting to loopback addresses,
# who knows what could happen then.
socks block {
         from: 0.0.0.0/0 to: 127.0.0.0/8
         log: connect error
}

# unless you need it, you could block any bind requests.
socks block {
         from: 0.0.0.0/0 to: 0.0.0.0/0
         command: bind
         log: connect error
}

# Allow inbound by hostname to home network SSH (unauthenticated)
socks pass {
         from: 0.0.0.0/0 to: .yourdomain.com port=22
         protocol: tcp udp
         #socksmethod: username none
         #user: proxy
}

# Allow inbound by IP to home network SSH (unauthenticated)
socks pass {
         from: 0.0.0.0/0 to: 2001:db8::/64 port=22
         protocol: tcp udp
         #socksmethod: username none
         #user: proxy
}

#socks pass {
#        from: 0.0.0.0/0 to: 10.0.0.0/8 port=22
#        protocol: tcp udp
#        #socksmethod: username none
#        #user: proxy
#}

# last line, block everyone else.  This is the default but if you provide
# one yourself you can specify your own logging/actions
socks block {
         from: 0.0.0.0/0 to: 0.0.0.0/0
         log: connect error
}



More information about the openssh-unix-dev mailing list