PF PF local port forward between high and low ports

I want to setup a ssh remote port forwarding for https, but I do not want to allow root ssh login. I thought nicest way would be to have a high numbered port locally connected to 443 via PF so any outside connections to 433 end up on let's say 8443 where the ssh client connection can bind.

Is this the correct way?

Code:
rdr on vtnet0 proto tcp from any to any port 443 \
  -> $myextIP 8443

myextIP is on vtnet0.

I need to expose some development machines briefly to the internet but with full correct DNS/SSL, so tunelling them through an internet box that's correctly set up seems like a good way to do it.
 
You don't need SSH port forwarding for this. You already have pf which is more than capable of doing NAT and rdr to your local web server.

Code:
Internet (0.0.0.0/0) <-> bge1(192.0.2.10/24) FreeBSD router/firewall bge0(192.168.10.1/24) <-> (192.168.10.2/24) Local Web server

This is not full rule set it's only partial example!

Code:
int_if = "bge0"
ext_if = "bge1"
tcp_services = "{ 22, 80, 443 }"
icmp_types = "{ 3, 4, 8, 11 }"

nat on $ext_if from $int_if:network to any -> 192.0.2.10
rdr on $ext_if proto tcp from any to 192.0.2.10 port 80 -> 192.168.10.2 port 80
rdr on $ext_if proto tcp from any to 192.0.2.10 port 443 -> 192.168.10.2 port 443

block all
pass quick on lo0 all

pass in on $ext_if inet proto tcp from any to ($ext_if) port $tcp_services flags S/SA keep state
pass in inet proto icmp all icmp-type $icmp_types keep state
pass in  on $int_if from $int_if:network to any keep state
pass out on $int_if from any to $int_if:network keep state
pass out on $ext_if proto tcp all modulate state flags S/SA
pass out on $ext_if proto { udp, icmp } all keep state


----
Then in your DNS zone you will need
Code:
www.example.com A 192.0.2.10

In order for local (LAN) clients to connect to your web server on 192.168.10.2 using the DNS name you will need local DNS with split-DNS configuration which will respond to
Code:
www.example.com A 192.168.10.2
 
Is this the correct way?
No, it will not work. Translation is done when a packet passes an interface.

Code:
     Redirections cannot reflect packets back through the interface they
     arrive on, they can only be redirected to hosts connected to different
     interfaces or to the firewall itself.
 
VladiBG that's not my scenario, the client machines are not on the same network, I don't need a simple port forward.

1. Client machine has a web server with www.domain.com certificates on it, but it's just a home machine connected to the Internet. Its ports can't be exposed to internet and there is no way to make rdns entry for it.

2. I have a real server www.domain.com.

3. I want to make TCP on port 443 on www.domain.com end up on Client machine.

The problem is ssh cannot bind priviledged ports for non root users. If I just exchanged root keys from Client machines there would be no issue.

No, it will not work. Translation is done when a packet passes an interface.

So any idea how to get it working?
There has to be a way because I could write userland bridge for this in 20 lines of code.

It's also doable on Linux/iptables but I can't find the site right now that shows the command.

In any case, this is usual stuff. You are exposing a development machine via Internet reachable server. The only caveat is port below 1024 and permissions and the fact that I don't want to reconfigure the kernel to allow unpriviledged binds due to unforseen problems that may arise.
 
I want to setup a ssh remote port forwarding for https
This makes zero sense. SSH and HTTPS are two different protocols.

but I do not want to allow root ssh login.
Disabled by default any way. I'm not sure what you're thinking here. But the fact the sshd(8) daemon runs as root on port 22 has nothing to do with the ability to login as root.


For web (HTTP/S) use a reverse proxy like net/haproxy (you could also use www/nginx in a reverse proxy configuration), then you can switch backends based on the HTTP/1.1 host header. You can also do SSL termination on the reverse proxy.
 
Ok i understand now what are you trying to do. I don't thing it's the right tho. You can do it with a vpn to the dev machine where he can host the site.

Anyway let me explain how similar setup is organized on single hosting, it would be better to be on separate independent hosting but here it is.

On the hosting i have virtualhosts one for production www.example.com and another one for dev.example.com with separate MySQL databases and with htaccess for the dev site. Both can be accessed for testing and can be easy swap or migrated with minimum or no downtime. Devs have ftp and db access where they can upload and make changes to the sites instead of hosting it on they own machines.
 
SirDice with ssh remote port forwarding a client maps something bound on his interfaces/ports to server interfaces/ports.
Code:
ssh –R 8100:localhost:8200 user@server.com

Any connections to server.com:8100 end up on localhost:8200. If you bind a webserver at localhost:8200, that server is accessible from the internet via server.com:8100.

Say you have a local web server running HTTPS at port 443, and you assign it certificate chain valid for server.com.
On server.com you don't have anything running on that port. When you do

Code:
ssh –R 443:localhost:443 root@server.com

You make your localhost the server.com webserver with valid chain of trust.
You cannot make proper reverse DNS and other entries, for instance TXT needed for letsencrypt challenge, for that localhost. You can for server.com which just acts as a connection point in this case.

Again noting, only problem here is port 443 and inherent quirkiness of arbitrary ports when it comes to DNS. The 443 redirect requires root ssh login. I want to avoid that.

Ok i understand now what are you trying to do. I don't thing it's the right tho. You can do it with a vpn to the dev machine where he can host the site.

Anyway let me explain how similar setup is organized on single hosting, it would be better to be on separate independent hosting but here it is.

On the hosting i have virtualhosts one for production www.example.com and another one for dev.example.com with separate MySQL databases and with htaccess for the dev site. Both can be accessed for testing and can be easy swap or migrated with minimum or no downtime. Devs have ftp and db access where they can upload and make changes to the sites instead of hosting it on they own machines.

Let me give some extra context here, it isn't about the hosting, it's about accessing a development webserver running deep inside a network on a normal computer, not in DMZ, behind layers of firewalls. In this case I can rely on users having root but that is not really the hard fact. Users may not have root. VPN maybe off limits. Outbound ports might be restricted.

Since this is my development team I'm also willing to give them root and avoid all this, but I'm curious to do it the proper way.

This is the example for Linux/iptables

Code:
iptables -t nat -A PREROUTING -d 123.123.123.123 -p tcp --dport 443 -j REDIRECT --to-port 8443

Ssh worker spawn descalated to logged in user, can bind normally to 8443 and route whatever user is having on its localhost end to it. The firewall will rewrite packets incoming from Internet to 443 to hit that 8443 on the same interface, where ssh worker spawn tunnels all packets back to the localhost machine where web server is. Thus https://123.123.123.123 ends up somewhere on the users localhost while being intermediary redirected over 123.123.123.123:8443 due to inability of non-root ssh spawn to hook up privileged ports.

So I guess a direct question would be how to write that iptables rule in PF.
 
ssh –R 8100:localhost:8200 user@server.com
Ah, now I get what you meant.
Any connections to server.com:8100 end up on localhost:8200.
Yes, I know how this works. I just made the wrong mental connection in combination with PF. Using rdr statement in PF is also called "port forwarding".

but it's just a home machine connected to the Internet. Its ports can't be exposed to internet and there is no way to make rdns entry for it.
Ok. That webserver can be accessed on other ports? I assume incoming ports like 25, 80 and 443 are likely blocked by their ISP? But 8443 can be used?

2. I have a real server www.domain.com.

3. I want to make TCP on port 443 on www.domain.com end up on Client machine.
Really simple HAproxy setup with SSL termination:
Code:
global
        maxconn 30000
        daemon

        log /dev/log local2

        user nobody
        group nobody

defaults
        log global
        option httplog
        option dontlognull
        mode http

frontend https-in
        bind 1.2.3.4:443 ssl crt /usr/local/etc/haproxy/ssl/

        default_backend local
        acl is_letsencrypt path_beg /.well-known/acme-challenge/

        acl is_mymate hdr_dom(host) -i example.com
        acl is_myjail hdr_dom(host) -i mywebsite.com

        use_backend local if is_letsencrypt
        use_backend mymate if is_mymate
        use_backend myjail if is_myjail

backend local
        option httpchk GET /up.txt
        server localhost 127.0.0.1:80 check

backend myjail 
        option httpchk GET /up.txt
        server myjail 172.16.0.3:80 check

backend mymate
        server 9.8.7.6:8443 check

Then a local nginx with
Code:
    server {
        listen       [::1]:80 default_server;
        listen       127.0.0.1:80 default_server;

        server_name  localhost "";

        #access_log  logs/host.access.log  main;

        location / {
            root   /usr/local/www/nginx;
            index  index.html index.htm;
        }

        location /.well-known/acme-challenge {
                alias /usr/local/www/acme/.well-known/acme-challenge;
                autoindex off;
        }

        location /up.txt {
                return 200 "UP!";
                access_log off;
        }
   }
It's mainly there for random traffic (which you will eventually get) to go somewhere. I have /usr/local/www/acme/.well-known/acme-challenge shared for Letsencrypt http challange/response.
 
Yeah I get it's a bit confusing, ssh and PF use same terminology and overlap if you use sshd for low level networking.

Ok. That webserver can be accessed on other ports? I assume incoming ports like 25, 80 and 443 are likely blocked by their ISP? But 8443 can be used?

Not ISP, this is an internal network, the host ports cannot be forwarded to the internet by policy and infrastructure setup. But they have NAT internet access. Usual stuff. So only way to access a local service from outside, is to first tunnel out from localhost.

Would be a bit different than your conf example, the real service on the dev. machine can't run without SSL at all. So a passthru proxy. Backend would be at 127.0.0.1:8443 and ssh tunnel would be 8443:localhost:8443.

VladiBG so the difference compared to what I suggested is 127.0.0.1 destination, which plays well with SirDice's answer about rdr happening past interface. Initially I presumed ssh spawn will just bind to *:8443 so it would be internally accessible on the vmnet0 interface. This is better solution.

Will try both, thanks for the discussion and helping out!
 
Not ISP, this is an internal network, the host ports cannot be forwarded to the internet by policy and infrastructure setup. But they have NAT internet access. Usual stuff. So only way to access a local service from outside, is to first tunnel out from localhost.
Right. So he can make the ssh connection to your server. There's no way to access his from your end. Then openvpn/wireguard might be a better solution than ssh. The problem with the ssh connection is that it can drop, and it won't try to reconnect. A VPN can. Setup the VPN with certificate authentication, so no username/password would be required but still be safe. You can port forward using PF to his assigned (local VPN) IP. Or use my haproxy config and reverse proxy to it.
 
VPN is a no go, you need to take context into account, I cannot file a procedure that requires local clients to install a VPN suite because that's not defined by the rules. By default that changes the networking settings on the local machines and thus needs intervention or approval from other organizational layers which is an added complication. + I need to create the entire setup on my server, for just an occasional, 10-15 minute working session. + users are used to keeping alive a shell command in the terminal to have some session up, this is what's done by using docker or Kubernetes port forwards.

By the way this ssh based approach is done quite a lot by web devs who want to demo their product to Internet customer in a time-limited session.
 
Web developer companies provide the entire hosting / maintaining and support on they own infrastructure in DC they don't need such ssh tunnels to the end client just to provide a demo web site.
 
Back
Top