Posts Tagged Unix
Using ferm to build firewall rulesets
Posted by Greg in Unix / Linux on December 31, 2010
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:
Using an alternative mirror for FreeBSD port retrieval
Posted by Greg in Unix / Linux on October 12, 2009
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.
Quick Solaris zone stats
Posted by Greg in Unix / Linux on August 13, 2009
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
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.
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;
}