Posts Tagged Unix

Using ferm to build firewall rulesets

This post is thanks to a suggestion from JP Viljoen to check out ferm. Well, I did, and it’s fairly neat. You get to express your firewall configuration in structures resembling simple C code along with using things like arrays, functions and if / else constructs which makes building complex rulesets quite a simple task.

I’ve included an example configuration below of one of my machines. The network configuration is not extremely complex, but there is a mix of IPv4, IPv6 and – as this is an IRC server – some DNAT to make the IRC service available on a number of other privileged ports without having the service actually listen on those ports. This particular server is running Debian however ferm is basically just a front to ip(6)tables so it’ll run pretty much anywhere that runs.

First off, here is my network interface configuration to give you an idea of what is where:

kore:~# cat /etc/network/interfaces 

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
    address 173.134.21.19             # Static eth0 IP
    netmask 255.255.255.0
    gateway 173.134.21.1

iface eth0 inet6 static
    address 2001:410:1e9b:ba22::2     # Primary HE.net IPv6 /64 address
    netmask 64

auto eth0:0
iface eth0:0 inet static
    address 192.168.49.97             # Local networking
    netmask 255.255.128.0

auto he-ipv6
iface he-ipv6 inet6 v4tunnel
    address 2001:410:1e9a:ba22::2     # Tunnel address
    netmask 64
    ttl 255
    gateway 2001:410:1e9a:ba22::1
    endpoint 216.218.224.42
    local 173.134.21.19

There is nothing extremely complicated here, just a basic IPv4 static IP assigned by my provider, a local network for traffic between this and other local nodes, a Hurricane Electric IPv6 tunnel and a static IP from my HE.net provided /64.

The ferm configuration in use here looks like this:

kore:~# cat /etc/ferm/ferm.conf
# -*- shell-script -*-
#
#  Configuration file for ferm(1).
#

@def $PORTS = (22 25 161 4949 6667 6668 7000 7352 7535); # Services running
@def $IRC_PORTS = (21 23 53 80 110 143 993);             # Additional ports

table filter {
    chain INPUT {
        policy DROP;

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

        # allow local packages
        interface lo ACCEPT;

        # respond to ping
        proto icmp ACCEPT; 

        # standard ports we allow from the outside
        proto tcp dport $PORTS ACCEPT;
    }

    chain OUTPUT {
        policy ACCEPT;

        # connection tracking
        #mod state state INVALID DROP;
        mod state state (ESTABLISHED RELATED) ACCEPT;
    }

    chain FORWARD {
        policy DROP;

        # connection tracking
        mod state state INVALID DROP;
        mod state state (ESTABLISHED RELATED) ACCEPT;
    }
}

table nat {
    chain PREROUTING {
        # additional ports we listen on and redirect to the IRC server
        interface eth0 proto tcp dport $IRC_PORTS DNAT to 173.134.21.19:6667;
    }
}

# IPv6:
domain ip6 table filter {
    chain INPUT {
        policy DROP;

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

        # allow ICMP (for neighbor solicitation, like ARP for IPv4)
        proto ipv6-icmp ACCEPT;

        # standard ports we allow from the outside
        proto tcp dport $PORTS ACCEPT;
    }

    chain OUTPUT {
        policy ACCEPT;

        # connection tracking
        #mod state state INVALID DROP;
        mod state state (ESTABLISHED RELATED) ACCEPT;
    }

    chain FORWARD {
        policy DROP;

        # connection tracking
        mod state state INVALID DROP;
        mod state state (ESTABLISHED RELATED) ACCEPT;
    }
}

So this ruleset is basically broken down into 3 parts:

  • IPv4 filter table
  • IPv4 nat table
  • IPv6 filter table

IPv4 filter table
We control the INPUT, OUTPUT and FORWARD chains here. On the INPUT chain, we default to dropping everything, enable connection state tracking, allow all traffic through our local interface, allow ICMP and specify a list of ports we allow the outside world to use. On the OUTPUT chain we allow everything out and enable connection state tracking. Finally on the FORWARD chain we drop everything as this machine is not a router. Pretty concise right ?

IPv4 nat table
In the nat table config, we basically setup the DNAT of those privileged ports under the PREROUTING chain.

IPv6 filter table
Finally, in the IPv6 filter table, we allow the same set of incoming ports as IPv4, allow ipv6-icmp and setup connection state tracking as before.

Once that’s done, simply run:

kore:~# ferm /etc/ferm/ferm.conf

… and your new ruleset is validated and loaded.

On a side note, if you are interested in playing around with IPv6 I would highly recommend setting up a Hurricane Electric tunnel and then doing the certification. It makes for a great Saturday afternoon time waster and you might learn something along the way:
IPv6 Certification

,

3 Comments

Using an alternative mirror for FreeBSD port retrieval

This is something I always search for which doesn’t seem to be very clear from the initial results. The mirrors included below are South Africa specific, so if you are not in South Africa then replace the hostname with something more appropriate for your location.

Add these to /etc/make.conf:

MASTER_SITE_BACKUP?=    \
	ftp://ftp.za.freebsd.org/pub/FreeBSD/ports/distfiles/${DIST_SUBDIR}/

MASTER_SITE_OVERRIDE?=  ${MASTER_SITE_BACKUP}

Mirrors:

Network Hostname Alias IPv4 IPv6
Tenet ftp.za.freebsd.org freebsd.mirror.ac.za 155.232.191.209 -
CSIR ftp2.za.freebsd.org - 146.64.8.4 2001:4200:7000:1::4
MTN Business ftp3.za.freebsd.org - 196.30.227.198 -
Internet Solutions ftp4.za.freebsd.org ftp.is.co.za 196.4.160.12 -

See this post for a far more detailed set of MASTER_SITE make variables. This references a lot more of the locally mirrored content.

,

No Comments

Quick Solaris zone stats

Add this:

alias zonestat="prstat -vZ 1 1 | grep -A50 '^ZONEID'"

to your ~/.profile and you should see something like this when running it:

root@tank:~# zonestat
ZONEID    NPROC  SWAP   RSS MEMORY      TIME  CPU ZONE
     0       58 1198M 1206M    30%  16:15:40 1.7% global
     6       25  172M  175M   4.4%   0:03:14 0.0% cl-build
     2       27   48M   31M   0.8%   0:00:47 0.0% mirror
Total: 110 processes, 534 lwps, load averages: 0.09, 0.08, 0.07

,

No Comments

The basics of ZFS ACLs

This post was mostly inspired by reading this post in trying to get my head around the ZFS ACL and permission system.

Basically I have a pool set out as follows:

tank
tank/media
tank/zones

tank/media is served via CIFS and NFS to multiple clients on my network, each with their own unix account on the OpenSolaris server. tank/zones is used for extra zones running on the host.

Everything was working great until I found that files and directories created by clients ended up looking like this:

----------  1 greg staff 734310400 2009-07-18 19:10 foo.txt
d---------  2 greg staff        19 2008-12-06 14:10 Bar

This sure didn’t go down well when other users needed to access those files or directories.

So in following the above mentioned post I did this:

 # zfs set aclinherit=passthrough tank/media
 # zfs set aclmode=passthrough tank/media
 # /bin/chmod 0774 /tank/media
 # /bin/chmod -R A- /tank/media
 # /bin/chmod -R A=owner@:full_set:fd:allow /tank/media
 # /bin/chmod -R A+group@:full_set:fd:allow /tank/media
 # /bin/chmod -R A+everyone@:read_set:fd:allow /tank/media

A better description of what the flags / syntax mean can be found here and here

A simple breakdown:

  • First off, we tell ZFS that all files or directories must inherit all acls / permissions from their parent.
  • We use /bin/chmod as the chmod in the default path is the GNU chmod which does not understand ZFS acls.
  • The second chmod “A-” will remove all acls currently set on the object.
  • We then set the owners permission to the “full_set”, thus giving the owner all possible permissions.
  • We do the same for the group.
  • Finally, we give everyone else read access.

, ,

2 Comments

Run something as another user

Here is a simple way to run something on UNIX / Linux as another user, without having to resort to weird sudo incantations. The Makefile is left as an exercise for the reader.

This has only been tested on FreeBSD, Debian Linux and OpenSolaris so far.

Compile with: cc -o setuid setuid.c

#include <stdio.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h> 

int main(int argc, const char **argv) {

    /* Check command line */
    if (argc < 3) {
        fprintf(stderr, "Usage: %s user cmd\n", argv[0]);
        return 1;
    }

    struct passwd *pw;
    pw = getpwnam(argv[1]);

    if (pw==NULL) {
        fprintf(stderr, "User not found\n");
        return 1;
    }

    if (initgroups(argv[1], pw->pw_gid)==-1) {
        perror("initgroups");
        return 1;
    }

    if (setregid(pw->pw_gid, pw->pw_gid)==0 &&
        setreuid(pw->pw_uid, pw->pw_uid)==0) {
        argv += 2;
        execvp(argv[0], argv);
        perror("exec");
        return 1;
    }

    perror("setre[gu]id");
    return 1;

}

, ,

No Comments