Network Isolation

Not all servers are equally secure or equally trusted. Today we will us a transparent proxy to isolate a system we don't quite trust.

Rocky found a cool old internet connected coffee maker at the thrift store. We would really love to put it on our network, but there are some problems we need to address.

Here's what we know about the coffee maker:


Strategy:

We will use a transparent proxy to expose only port 80 on our blue network.


Part A: Build and deploy proxy01

  1. Configure our static IP addresses

Check our work

  1. ssh to proxy01 from w01
  2. access the web application running on icm01
    curl http://15.35.79.233
    

Part B: Switch from firewalld to nftables

Our current configuration is usable but awkward. Users must log on to prox01 to access icm01. It would be much more convenient if we could access icm01 directly from any node on blue.

  1. Disable the firewalld based firewall. (We will replace this with a netfilter/nft firewall later.)
    systemctl disable firewalld.service 
    systemctl stop firewalld.service 
    systemctl mask firewalld.service
    
  2. Enable packet forwarding (required for NAT)
    1. Create /etc/sysctl.d/90-override.conf containing
      net.ipv4.ip_forward=1
      
    2. reboot proxy01
  3. Create an empty rule set with one table, my_inet_table, belonging to the inet family.
    cat > /etc/sysconfig/nftables.conf <<EOF
    table inet my_inet_table {
    
    }
    EOF
    
  4. Start and enable the nftables service
    systemctl enable nftables.service
    systemctl start nftables.service
    
  5. List the configured tables (there should be one inet table named my_inet_table)
    nft list tables
    
  6. List the chains and rules from our table (family=inet, name=my_inet_table). There should be no chains.
    nft list table inet my_inet_table
    

Part C: Build our firewall input rules

Remember to run systemctl reload nftables.service to reload the rules after edits.

These rules will apply only to packets destined for proxy01. Packets that are being routed through proxy01 are not subject to these rules.

Note: Check our work after each step by trying 'something that should work' and 'something that should not work' after each change.

  1. Add chain connected to the input hook. Set the default policy on our input chain to drop by making /etc/sysconfig/nftables.conf look like..
    table inet my_inet_table {
    
            chain input {
                    # declare new chain named filter hooked to the input entry point and set default policy to drop
                    type filter hook input priority 0; policy drop
            }
    }
    
  2. Reload with systemctl reload nftables.service (this will break ssh sessions)
  3. Add a simple rule to allow ssh
    	chain input {
    		type filter hook input priority 0; policy drop;
    
    		# allow ssh 
    		tcp dport ssh accept
    	}
    
  4. Make our rule stateful. As configured, we will accept every packet with a destination port of 22. This could leave us open to attacks that start a TCP conversation 'in the middle'. To increase our security we modify our ssh rule to allow only new connections on port 22. All following packets will be covered by the established/related rule.
            chain input {
                    type filter hook input priority 0; policy drop
    
                    ct state established,related accept
    
                    # allow new tcp connections to port 22 (ssh)
                    tcp dport ssh ct state new accept
            }
    
  5. Let proxy01 talk to itself. Many programs communicate between components via local ip connections. Our firewall won't allow that. To demonstrate this, we will use curl to connect to localhost:80. The firewall is configured to drop packets, so our curl command will hang.
    curl http://localhost
    
  6. Use the input interface (iif) match in rule to allow all local connections
            chain input {
                    type filter hook input priority 0; policy drop
    
                    ct state established,related accept
    
                    tcp dport ssh ct state new accept
    
                    # allow any packet if it is being sent to the look back interface
                    iif lo accept
            }
    
  7. Check our work. curl should now return 'connection refused' indicating that the packet was delivered but no process is listening on port 80.
    # on proxy01 try... 
    curl http://localhost
    
  8. We can use interface matching in conjunction with port matching to accept only packets that came in on a given interface. We will add an iif match to our ssh chain so that only hosts on blue can ssh to proxy01. (recall the blue network is connected to the enp0s3 interface.
            chain input {
                    type filter hook input priority 0; policy drop
    
                    ct state established,related accept
    
                    # allow new ssh packets that came in on enp0s3 (blue)
                    iif enp0s3 tcp dport ssh ct state new accept
    
                    iif lo accept
            }
    
  9. ICMP traffic can be a security risk but we will allow it as this is an internal firewall.
            chain input {
                    type filter hook input priority 0; policy drop
    
                    ct state established,related accept
    
                    # allow new ssh packets that came in on enp0s3 (blue)
                    iif enp0s3 tcp dport ssh ct state new accept
    
                    # allow any packet if it is being sent to the look back interface
                    iif lo accept
    
                    # accept all icmp packets (v4 and v6)
                    ip protocol icmp accept
                    ip6 nexthdr icmpv6 accept
            }
    

Part D: Forward rules

The packets we proxy will pass through us like they were routed. We want to let these packets through but no others.

  1. To control the filtering of packets passing through the router, we attach a chain to the forward hook. Note:Packets passing through the router are not processed by chains attached to the input or output hooks.
    This chain will only allow ICMP across the router.
    chain forward {
    		type filter hook forward priority 0; policy drop;
    								
    		# required for stateful rules
    		ct state established,related accept
    
    		# allow http
    		tcp dport 80 ct state new accept
    
    
    		# accept all icmp packets (v4 and v6)
    		ip protocol icmp accept
    		ip6 nexthdr icmpv6 accept
    }
    

Part E: Configure NAT

We will us Network Address Translation to automatically forward packets destined to 10.1.1.250:80 to 15.35.79.233:80 and to make sure the response packets can find their way back.

  1. Add a nat table to nftables.conf
    table ip nat {
    	chain postrouting {
    		type nat hook postrouting priority srcnat; policy accept;
    
    		# make sure proxied packets can find their way home
    		#
    		oif "enp0s8"  tcp dport  80 log snat to 15.35.79.1
    	}
    
    	chain prerouting {
    		type nat hook prerouting priority dstnat; policy accept;
    
    		# re-address http packets to icm01
    		#
    		iif "enp0s3"  tcp dport 80 dnat to 15.35.79.233
    	}
    }
    
  2. Test our work from w01
    curl http://10.1.1.250
    

Part F: Your turn...

We have a working proxy but we should make it a bit more secure.

  1. Back up our working nftables configuration file
    tar cf ~/nftables_good.tar /etc/sysconfig/nftables.conf 
    
  2. Our current forward rules allow http in both directions. We do not trust icm01 and do not want it to look at web pages on blue. Modify our "# allow http" rule to implement this.
  3. Tighten our icmp rules:
  4. As mentioned, we do not trust icm01. We want to log all the packets we drop. Accomplish this by adding one rule to each of the input and forward chains.

Part G: Grading

Submit your nftables.conf to Canvas