This is about how to have the ssh and http(s) server share the same port (e.g. 80 or 443 port). This is really cool :). # Used sources: # http://yalis.fr/cms/index.php/post/2014/02/22/Multiplex-SSH-and-HTTPS-on-a-single-port # http://blog.cppse.nl/apache-proxytunnel-ssh-tunnel # http://serverfault.com/questions/355271/ssh-over-https-with-proxytunnel-and-nginx # http://tyy.host-ed.me/pluxml/article4/port-443-for-https-ssh-and-ssh-over-ssl-and-more # http://ipset.netfilter.org/iptables.man.html # http://ipset.netfilter.org/iptables-extensions.man.html # http://man7.org/linux/man-pages/man8/ip-rule.8.html # http://lartc.org/howto/lartc.netfilter.html # http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=Unassigned # https://cloud.githubusercontent.com/assets/2137369/15272097/77d1c09e-1a37-11e6-97ef-d9767035fc3e.png # http://www.adminsehow.com/2011/09/iptables-packet-traverse-map/ ### begin sshttp setup 1 # https://github.com/stealth/sshttp # Below are the preparations for this setup: # sshttpd listens on 80 for ssh and http connections. It forwards to ssh:1022 and nginx:880. # Will work these: ssh -p 1022 gigi@127.0.0.1 -> access tried from within 192.168.1.31 host ssh -p 1022 gigi@192.168.1.31 -> access tried from within 192.168.1.31 host ssh -p 80 gigi@adrhc.go.ro -> access tried from within 192.168.1.31 host or from internet http://127.0.0.1/public/ -> access tried from within 192.168.1.31 host http://127.0.0.1:880/public/ -> access tried from within 192.168.1.31 host http://192.168.1.31:880/public/ -> access tried from within 192.168.1.31 host http://192.168.1.31/public/ -> access tried from 192.168.1.31's LAN http://adrhc.go.ro/public/ -> access tried from within 192.168.1.31 host or from internet # Won't work this: ssh -p 1022 gigi@adrhc.go.ro -> access tried from within 192.168.1.31 host or from internet http://192.168.1.31/public/ -> access tried from within 192.168.1.31 host http://adrhc.go.ro:880/public/ -> access tried from within 192.168.1.31 host or from internet # /etc/modules modprobe nf_conntrack_ipv4 modprobe nf_conntrack echo "nf_conntrack" >> /etc/modules echo "nf_conntrack_ipv4" >> /etc/modules # in /etc/ssh/sshd_config make sure to have: # Port 1022 # Banner /etc/sshd-banner.txt # Makefile uses the content of /etc/sshd-banner.txt, e.g.: # SSH_BANNER=-DSSH_BANNER=\"adrhc\'s\ SSH\ server\" cat /etc/sshd-banner.txt adrhc's SSH server # configure nf-setup, e.g. for sshttpd.service below should be: DEV="eth0" SSH_PORT=1022 # HTTP_PORT=1443 HTTP_PORT=880 # also you could add this afterwards in order not to run nf-setup if already run: if [ "`iptables -t mangle -L | grep -v -P "^ufw-" | grep -P "^DIVERT.+tcp spt:$HTTP_PORT"`" != "" ]; then echo "sshttp netfilter rules already applied ..." exit 0 fi echo "applying sshttp netfilter rules ..." # for nginx or apache take care of address binding not to overlap with sshttpd.service, e.g.: # server { # listen 127.0.0.1:80; # listen 127.0.0.1:880; # # listen 192.168.1.31:80; -> used/bound by sshttpd.service below # listen 192.168.1.31:880; # install the systemd sshttpd.service defined below: sudo chown root: /etc/systemd/system/sshttpd.service && sudo chmod 664 /etc/systemd/system/sshttpd.service && sudo systemctl daemon-reload; cp -v $HOME/compile/sshttp/nf-setup $HOME/apps/bin # systemd sshttpd.service: [Unit] # see https://github.com/stealth/sshttp Description=SSH/HTTP(S) multiplexer # for any address binding conflict that occurs between ufw, ssh, nginx and sshttp I want ufw, ssh and nginx to win against sshttp After=network.target # sudo iptables -L | grep -v -P "^ufw-" | grep -P "1022|1443|880|DIVERT|DROP|ssh" # sudo iptables -t mangle -L | grep -v -P "^ufw-" | grep -P "1022|1443|880|DIVERT|DROP|ssh" [Service] Type=forking RuntimeDirectory=sshttpd ExecStartPre=-/bin/chown nobody: /run/sshttpd ExecStartPre=-/home/gigi/apps/bin/nf-setup Restart=on-failure RestartSec=3 TimeoutStartSec=5 TimeoutStopSec=5 # using 443 for sshttpd: # ssh -p 443 gigi@adrhc.go.ro # wget --no-check-certificate https://adrhc.go.ro/ # ExecStart=/home/gigi/apps/bin/sshttpd -n 4 -S 1022 -H 1443 -L 443 -l 192.168.1.31 -U nobody -R /run/sshttpd # using 80 for sshttpd: # ssh -p 80 gigi@adrhc.go.ro # wget http://adrhc.go.ro/public ExecStart=/home/gigi/apps/bin/sshttpd -n 4 -S 1022 -H 880 -L 80 -l 192.168.1.31 -U nobody -R /run/sshttpd [Install] WantedBy=multi-user.target ### begin sshttp setup 2 (read first sshttp step 1) # Below are the preparations for this setup: # sshttpd listens on 444 for ssh and https connections. # sshttpd forwards to ssh:1022 or stunnel:1443. # stunnel:1443 forwards to nginx:127.0.0.1:1080 or ssh:127.0.0.1:22 based on sni. # the original remote client's ip is accessible (only for https but not ssh) with $realip_remote_addr (http://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header) # Issue: any redirect (301 or 302) used in the server 127.0.0.1:1080 defined below will set Location header to http instead of https # - see sshttp setup 3 for a solution # - see https://forum.nginx.org/read.php?2,269623,269647#msg-269647 (listen proxy_protocol and rewrite redirect scheme) for a better? solution: src/http/ngx_http_header_filter_module.c: #if (NGX_HTTP_SSL) if (c->ssl || port == 443) { *b->last++ ='s'; } #endif # Won't work Transmission remote GUI but the web page will still work. # ERROR (while using Transmission remote GUI): 2016/09/19 15:03:42 [error] 5562#0: *2431 broken header: ">:azX���g��^}q�/���A��Rp(���n3��0�,�(�$�� ����kjih9876�����2�.�*�&���=5��/�+�'�#�� ����g@?>3210����EDCB�1�-�)�%���</�A��� � �� �g 127.0.0.1 " while reading PROXY protocol, client: 127.0.0.1, server: 127.0.0.1:443 NӾHu|���4|�sf��Q�j$������0�,�(�$��432 broken header: ">:LM2V ����kjih9876�����2�.�*�&���=5��/�+�'�#�� ����g@?>3210����EDCB�1�-�)�%���</�A��� � �� �g 127.0.0.1 " while reading PROXY protocol, client: 127.0.0.1, server: 127.0.0.1:443 # in systemd sshttpd.service change to: # router: 443 -> 444 -> also make sure ufw allows 444 # ssh -p 443 gigi@adrhc.go.ro # wget --no-check-certificate https://adrhc.go.ro/ ExecStart=/********/apps/bin/sshttpd -n 4 -S 1022 -H 1443 -L 444 -l 192.168.1.31 -U nobody -R /run/sshttpd # in nginx add this "magic" server: server { listen 127.0.0.1:1080 default_server proxy_protocol; include xhttpd_1080_proxy.conf; port_in_redirect off; # change also fastcgi_params! (see below) ... your stuff ... } # xhttpd_1080_proxy.conf: set_real_ip_from 192.168.1.0/24; set_real_ip_from 127.0.0.0/8; # set_real_ip_from ::1/32; -> doesn't work for me real_ip_header proxy_protocol; set $real_internet_https "on"; set $real_internet_port "443"; # in fastcgi_params have (besides your stuff): # This special fastcgi_params must be used only by "magic server" (127.0.0.1:1080)! fastcgi_param HTTPS $real_internet_https if_not_empty; fastcgi_param SERVER_PORT $real_internet_port if_not_empty; # stunnel.conf for server side # sudo killall stunnel; sleep 1; sudo bin/stunnel etc/stunnel/stunnel.conf pid = /run/stunnel.pid debug = 4 output = /********/apps/log/stunnel.log options = NO_SSLv2 compression = deflate cert = /********/apps/etc/nginx/certs/adrhc.go.ro-server-pub.pem key = /********/apps/etc/nginx/certs/adrhc.go.ro-server-priv-no-pwd.pem [tls] accept = 192.168.1.31:1443 connect = 127.0.0.1:1080 protocol = proxy [ssh] sni = tls:tti.go.ro connect = 127.0.0.1:22 renegotiation = no debug = 5 cert = /********/apps/etc/nginx/certs/adrhc.go.ro-server-pub.pem key = /********/apps/etc/nginx/certs/adrhc.go.ro-server-priv-no-pwd.pem [www on any] sni = tls:* connect = 127.0.0.1:1080 protocol = proxy # stunnel.conf for client side # killall stunnel; sleep 1; stunnel ****stunnel.conf && tailf ****stunnel.log # ssh -p 1194 gigi@localhost pid = /****************/temp/stunnel.pid debug = 4 output = /****************/****stunnel.log options = NO_SSLv2 [tti.go.ro] # Set sTunnel to be in client mode (defaults to server) client = yes # Port to locally connect to accept = 127.0.0.1:1194 # Remote server for sTunnel to connect to connect = adrhc.go.ro:443 sni = tti.go.ro verify = 2 CAfile = /****************/****Temp/Zyxel/adrhc.go.ro-server-pub.pem # checkHost = certificate's CN field (see "Rejected by CERT at" in stunnel.log for learning CN) checkHost = adrhc.go.ro # CAfile = /****************/****Temp/Zyxel/adr-pub.pem # checkHost = adr ### begin sshttp setup 3 (read first sshttp step 2) # any redirect (301 or 302) used in the server 127.0.0.1:1080 defined above will go to the https server # Issue: the original remote client's ip is not accessible (https or ssh) # you'll need the https nginx configuration for your site listening at least on 127.0.0.1:443 # you no longer need the "magic" server defined above # How this works: # browser/stunnel-client useing ssl -> sshttpd:443 -> stunnel[tls to http] using ssl -> stunnel[http to https] # stunnel.conf for server side # sudo killall stunnel; sleep 1; sudo bin/stunnel etc/stunnel/stunnel.conf pid = /run/stunnel.pid debug = 4 output = /********/apps/log/stunnel.log options = NO_SSLv2 compression = deflate cert = /********/apps/etc/nginx/certs/adrhc.go.ro-server-pub.pem key = /********/apps/etc/nginx/certs/adrhc.go.ro-server-priv-no-pwd.pem [tls] accept = 192.168.1.31:1443 connect = 127.0.0.1:1081 protocol = proxy [ssh] sni = tls:tti.go.ro connect = 127.0.0.1:22 renegotiation = no debug = 5 cert = /********/apps/etc/nginx/certs/adrhc.go.ro-server-pub.pem key = /********/apps/etc/nginx/certs/adrhc.go.ro-server-priv-no-pwd.pem [tls to http] sni = tls:* connect = 127.0.0.1:1081 # connect = 127.0.0.1:1080 # protocol = proxy [http to https] accept = 127.0.0.1:1081 connect = 127.0.0.1:443 client = yes ### begin sslh setup # https://github.com/yrutschle/sslh # Here I use ssh:1021 instead of ssh:1022. sudo apt-get install sslh sudo useradd -d /nonexistent -M -s /bin/false sslh # according to https://github.com/yrutschle/sslh#capabilities-support I need: sudo setcap cap_net_bind_service,cap_net_admin+pe /usr/sbin/sslh-select sudo getcap -rv /usr/sbin/sslh-select cat /etc/default/sslh RUN=yes DAEMON=/usr/sbin/sslh-select # with --transparent the local ip is not acceptable: DAEMON_OPTS="--transparent --timeout 1 --numeric --user sslh --listen 192.168.1.31:334 --ssh 192.168.1.31:1021 --http 192.168.1.31:80 --pidfile /var/run/sslh/sslh.pid" # without --transparent is acceptable also local ip: # DAEMON_OPTS="--transparent --timeout 1 --numeric --user sslh --listen 192.168.1.31:334 --ssh 192.168.1.31:1021 --http 127.0.0.1:80 --pidfile /var/run/sslh/sslh.pid" cat /etc/systemd/system/sslh.service.d/custom.conf # cp -v $HOME/bin/systemd-services/sslh-setup.sh $HOME/apps/bin [Service] ExecStartPre=-/********/apps/bin/sslh-setup.sh ExecStart= ExecStart=/usr/sbin/sslh-select --foreground $DAEMON_OPTS SuccessExitStatus=15 cat sslh-setup.sh #!/bin/sh if [ "`sudo iptables -t mangle -L | grep -P "^SSLH\s.+\sspt:1021"`" != "" ]; then echo "SSLH netfilter rules already applied ..." exit 0 fi iptables -t mangle -N SSLH iptables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 --sport 1021 --jump SSLH iptables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 --sport 80 --jump SSLH iptables -t mangle -A SSLH --jump MARK --set-mark 0x1 iptables -t mangle -A SSLH --jump ACCEPT ip rule add fwmark 0x1 lookup 100 ip route add local 0.0.0.0/0 dev lo table 100 sudo systemctl daemon-reload sudo systemctl enable sslh sudo systemctl start sslh