-
Notifications
You must be signed in to change notification settings - Fork 179
SocksSSH
Many ISPs block port 25 outbound. For mail-clients, you just configure them to use port 587 Submission to your mail server (Exim). But sometimes you may want to do things with a local MTA on your laptop.
This is when Exim's SOCKS5 support combines nicely with OpenSSH's DynamicForward
support. Mail will build up in your mail-spool, until you bring up the SSH link, at which point it will flow out.
This works both with manualroute and with dnslookup. With dnslookup, it works even with functionality such as DANE.
Exim must have been built with SUPPORT_SOCKS=yes
in Local/Makefile
In ~/.ssh/config
something like this works well; this is a little more flexible than strictly needed, to show how you can set up patterns for multiple hosts and restrict options accordingly.
Host *-socksonly
ControlMaster no
ControlPath none
ControlPersist no
ForwardAgent no
ForwardX11 no
Host hermes-socks hermes-socksonly
DynamicForward 4211
Host hermes hermes-*
Hostname hermes.example.org
#...
Host *
ControlPath ~/.ssh/cp/%h-%p-%r
This control-path location does require: mkdir -m 0700 ~/.ssh/cp
You can bring up a normal link and reclaim use of your terminal with:
$ ssh -Nf hermes-socksonly
To bring up a link which you can easily stop, without ps
digging:
$ ssh -M -Nf hermes-socks
$ do_other_stuff, sending email
$ ssh -O stop hermes-socks
From this you can see how you might construct variants to handle auto-master for you, etc.
Be sure to pick a different value of DynamicForward
for each remote host which you might be connected to at the same time.
Remember the value: you will need it for MTA configuration.
This value is the local port number on this host, to which clients can connect and speak SOCKS5 to get connected to some target with an outbound connection from the remote host, with the traffic routed over an SSH channel to get there.
You need a Router and a Transport; here we show three routers for flexibility, but only one is needed. Selection between these is driven by entries in a configuration file, matching on sender.
- The first is dnslookup and handles all normal routing; it is used when the
DNSLOOKUP
key exists in theoutbound-settings
file - The second handles all SOCKS cases
- The third handles other cases
The reason for the split between 2 and 3 is that if you have shell login on the smarthost itself, you might set host=localhost
which Exim would normally balk at. Instead, we set self = send
when, and only when, the socks
key exists in the data. If the value of self
were expanded, we could collapse these two into just one.
If DNSLOOKUP
exists, then only DNS-based routing will be used; to fallback to the smarthost, remove the no_more
from that Router below.
If there is an entry with *
as a key in the outbound-settings
file and no entry with DNSLOOKUP
as the key, then only the second Router will ever be used.
After the Routers, you need an SMTP Transport.
Remember that in Exim, Transports are an unordered collection which are used by being referred to from a Router, whereas Routers are an ordered list.
# macros before main settings:
TLS_CLIENT_DEFAULT_CIPHERSPEC=DEFAULT:!SSLv2:!LOW:aNULL:!eNULL
TLS_CLIENT_HIGHSEC_CIPHERSPEC=ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:HIGH:!MD5:!RC4:!aNULL:!ADH:!DES:!EXP:!NULL
TLS_SERVER_SUBMISSION_CIPHERSPEC=ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:HIGH:!MD5:!RC4:!aNULL:!ADH:!DES:!EXP:!NULL
begin routers
dnslookup:
driver = dnslookup
domains = ! +local_domains
transport = remote_smtp
address_data = ${lookup {DNSLOOKUP}lsearch{/etc/exim/outbound-settings}}
condition = ${extract{socks}{${lookup {DNSLOOKUP}lsearch{/etc/exim/outbound-settings}}}{yes}{no}}
self = send
ignore_target_hosts = <; 0.0.0.0 ; 127.0.0.0/8 ; ::1
dnssec_request_domains = *
no_more
# note: self is not expanded; else we could use one smarthost router with:
# self = ${extract{socks}{${lookup {$sender_address}lsearch*@{/etc/exim/outbound-settings}}}{send}{freeze}}
smarthost_socks:
driver = manualroute
domains = ! +local_domains
transport = remote_smtp
route_data = ${extract{host}{${lookup {$sender_address}lsearch*@{/etc/exim/outbound-settings}}}{$value}fail}
dnssec_request_domains = *
address_data = ${lookup {$sender_address}lsearch*@{/etc/exim/outbound-settings}}
condition = ${extract{socks}{${lookup {$sender_address}lsearch*@{/etc/exim/outbound-settings}}}{yes}{no}}
self = send
smarthost:
driver = manualroute
domains = ! +local_domains
transport = remote_smtp
route_data = ${extract{host}{${lookup {$sender_address}lsearch*@{/etc/exim/outbound-settings}}}{$value}fail}
dnssec_request_domains = *
address_data = ${lookup {$sender_address}lsearch*@{/etc/exim/outbound-settings}}
no_more
# local mail handling goes here
begin transports
remote_smtp:
driver = smtp
port = ${extract{port}{$address_data}{$value}{25}}
hosts_require_auth = ${extract{authreq}{$address_data}{${if eq{$value}{yes}{*}{$value}}}{}}
hosts_require_tls = ${extract{tls}{$address_data}{${if eq{$value}{yes}{*}{$value}}}{}}
hosts_avoid_tls = ${extract{tls}{$address_data}{${if eq{$value}{no}{*}{}}}{}}
tls_sni = ${extract{tlssni}{$address_data}{$value}{}}
tls_require_ciphers = ${extract{tlshigh}{$address_data}{${if eq{$value}{yes}{TLS_CLIENT_HIGHSEC_CIPHERSPEC}{TLS_CLIENT_DEFAULT_CIPHERSPEC}}}{TLS_CLIENT_DEFAULT_CIPHERSPEC}}
tls_verify_certificates = ${extract{tlsverify}{$address_data}{/usr/local/etc/openssl/certs}fail}
hosts_try_dane = *
no_multi_domain
no_delay_after_cutoff
helo_data = ${extract{helo}{$address_data}{$value}{$primary_hostname}}
hide socks_proxy = ${extract{socks}{$address_data}{<; </ $value}fail}
Then the file /etc/exim/outbound-settings
looks something like this:
[email protected]: host=localhost port=26 socks=127.0.0.1/port=4221 helo=laptop.socks.proxy tls=no
*@example.com: host=localhost port=587 socks=127.0.0.1/port=4222 helo=laptop.socks.proxy tls=no
*: host=localhost socks=127.0.0.1/port=4229 helo=laptop.socks.proxy tls=no
# Only enable this if you have DKIM signing with a published key on your laptop
#DNSLOOKUP: socks=127.0.0.1/port=4211
This relies upon you having a local user who can ssh; you could also have this linkage as a system service using a passphraseless SSH key which has restrictions upon it on the remote server.
If you kill the SSH session, the SOCKS proxy will disappear and mail will build up in the queue. When you bring back the SSH link, tickle the Exim queue and the mail will flow out.
The logging of SOCKS details is currently (4.89) a little hidden. Use -d+transport
to turn on debugging, including the transport
area, to see connections fail, etc.
If you have untrusted local users, then routing over a port which anyone can bind to is an issue. You might invoke ssh as a privileged user and bind a privileged port, or you might ensure TLS and DANE are operational, to be immune to man-in-the-middle attacks.
The first version of this wiki page used lsearch*@
even with the DNSLOOKUP
key, which was almost certainly a bug; we don't want a *
entry to match for this case. In Exim, *@
specifies a variant of the lookup where if the initial lookup fails, we then try a lookup for *@domain
and then for just *
.
The above hints at authreq
being an allowed key in the outbound-settings
file, to require authentication before sending. Here are the Exim authenticators used with this setup:
begin authenticators
auth_cram:
driver = cram_md5
public_name = CRAM-MD5
client_condition = ${if !eq{$tls_out_cipher}{}}
client_name = ${extract{user}{$address_data}{$value}fail}
client_secret = ${extract{password}{$address_data}{$value}fail}
auth_plain:
driver = plaintext
public_name = PLAIN
client_condition = ${if !eq{$tls_out_cipher}{}}
client_send = ^${extract{user}{$address_data}{$value}fail}^${sg{${extract{password}{$address_data}{$value}fail}}{\N\^\N}{^^}}
Depending upon your views of the security of CRAM-MD5, you might remove the requirement that TLS be established for that authenticator.