# -*- shell-script -*-
#
# Configuration file for ferm(1).
#
# V: 0.1
#
# ferm manual: http://ferm.foo-projects.org/download/2.2/ferm.html
# Blog post:  https://blog.ipredator.se/configuring-debian-gnu-linux-as-an-openvpn-router.html
#

# Really make sure that these modules exist and are loaded.
@hook pre "/sbin/modprobe nf_conntrack_ftp";
@hook pre "/sbin/modprobe nfnetlink_log";

# Network interfaces.
@def $DEV_EXT = enp1s0;
@def $DEV_LAN = eno2;
@def $DEV_LOOPBACK = lo0;
@def $DEV_VPN = tun0;

# Network definition for the LAN interface. This is the network range
# your NAT clients sit in.
@def $NET_LAN = 192.168.2.0/24;

# Hosts
@def $HOST_TRANSMISSION = 192.168.2.64;

# Common application ports.
@def $PORT_DNS = 53;
@def $PORT_FTP = ( 20 21 );
@def $PORT_NTP = 123;
@def $PORT_SSH = 22;
@def $PORT_WEB = ( 80 443 );

# Transmission ports.
@def $PORT_TRANSMISSION = 16384:65535;

# The ports we allow OpenVPN to connect to. IPredator allows you
# to connect on _any_ port. Simply add more ports if desired but
# stick to only those that you really need.
@def $PORT_OPENVPN = (1194 8333 9333);

# Public DNS servers and those that are only reachable via VPN.
# DNS servers are specified in the outbound DNS rules to prevent DNS leaks
# (https://www.dnsleaktest.com/). The public DNS servers configured on your
# system should be the IPredator ones (https://www.ipredator.se/page/services#service_dns),
# but you need to verify this.
# The fail-safe default is to use any public DNS (0.0.0.0). 
# @def $IP_DNS_PUBLIC = ( 46.246.46.246 194.132.32.32 );
@def $IP_DNS_PUBLIC = 0.0.0.0/0;
@def $IP_DNS_VPN = ( 46.246.46.46/0 194.132.32.23/0 );

# Function to simplify port forwarding
@def &FORWARD_PORT($DEV_SRC, $DEV_DEST, $PROTO, $IP_DEST, $PORT) = {table filter chain FORWARD interface $DEV_SRC outerface $DEV_DEST daddr $IP_DEST proto $PROTO dport $PORT ACCEPT;
    table nat chain PREROUTING interface $DEV_SRC proto $PROTO dport $PORT DNAT to $IP_DEST;
}

# Make sure to use the proper VPN interface (e.g. tun0 in this case).
# Note: You cannot reference $DEV_VPN here, substition does not take
#       place for commands passed to a sub shell.
@def $VPN_ACTIVE = `ip link show tun0 >/dev/null 2>/dev/null && echo 1 || echo`;

# VPN interface conditional. If true the following rules are loaded.
@if $VPN_ACTIVE {
    domain ip {

        # Forward port range for Transmission
        &FORWARD_PORT($DEV_VPN, $DEV_LAN, (tcp udp), $HOST_TRANSMISSION, $PORT_TRANSMISSION);

        table filter {
            chain OUTPUT {
                # Default allowed outbound services on the VPN interface.
                # If you need more simply add your rules here.
                outerface $DEV_VPN {
                    proto (tcp udp) daddr ( $IP_DNS_PUBLIC $IP_DNS_VPN ) dport $PORT_DNS ACCEPT;
                    proto tcp dport $PORT_FTP ACCEPT;
                    proto udp dport $PORT_NTP ACCEPT;
                    proto tcp dport $PORT_SSH ACCEPT;
                    proto tcp dport $PORT_WEB ACCEPT;
                }
            }
            chain FORWARD {
                # Connection tracking.
                mod state state INVALID DROP;
                mod state state (ESTABLISHED RELATED) ACCEPT;

                # Forward packets on the LAN interface.
                interface $DEV_LAN ACCEPT;
            }
        }
        table nat {
            chain POSTROUTING {
                # NAT all packets from the LAN to the VPN interface.
                saddr $NET_LAN outerface $DEV_VPN MASQUERADE;
            }
        }
    }
}





# The main IPv4 rule set.
domain ip {
    table filter {
        chain INPUT {
            # The default policy for the chain. Usually ACCEPT or DROP or REJECT.
            policy DROP;

            # Connection tracking.
            mod state state INVALID DROP;
            mod state state (ESTABLISHED RELATED) ACCEPT;

            # Allow local traffic to loopback interface.
            interface $DEV_LOOPBACK ACCEPT;
 
            # Allow all traffic on the LAN interface.
            interface $DEV_LAN ACCEPT;

            # Allow inbound SSH on your EXT interface.
            interface $DEV_EXT {
                proto tcp dport $PORT_SSH ACCEPT;
            }

            # Respond to ping ... makes debugging easier.
            proto icmp icmp-type echo-request ACCEPT;

            # Log dropped packets.
            NFLOG nflog-group 1;
            DROP;
        }

        chain OUTPUT {
            policy DROP;

            # Connection tracking.
            mod state state INVALID DROP;
            mod state state (ESTABLISHED RELATED) ACCEPT;

            # Allow local traffic from the loopback interface.
            outerface $DEV_LOOPBACK ACCEPT;
  
            # Allow traffic to your local network.
            outerface $DEV_LAN ACCEPT;

            # Respond to ping.
            proto icmp icmp-type echo-request ACCEPT;

            # Allowed services on the EXT interface.
            outerface $DEV_EXT {
                #proto (tcp udp) daddr $IP_DNS_PUBLIC dport $PORT_DNS ACCEPT;
                #proto udp dport $PORT_NTP ACCEPT;
                proto (tcp udp) dport $PORT_OPENVPN ACCEPT;
                proto tcp dport $PORT_SSH ACCEPT;
            }

            # Log dropped packets.
            NFLOG nflog-group 1;
            DROP;
        }

        chain FORWARD {
            policy DROP;

            # If you use your machine to route traffic eg. 
            # from a VM you have to add rules here!

            # Log dropped packets.
            NFLOG nflog-group 1;
            DROP;
        }
    }
}

# IPv6 is generally disabled, communication on the loopback device is allowed.
domain ip6 {
    table filter {
        chain INPUT {
            policy DROP;

            # Allow local traffic.
            interface $DEV_LOOPBACK ACCEPT;

            DROP;
        }
        chain OUTPUT {
            policy DROP;

            DROP;
        }
        chain FORWARD {
            policy DROP;

            DROP;
        }
    }
}


