kota's memex

A simple, but powerful frontend for iptables.

resources

https://regrow.earth/diy-server/awall/

https://git.alpinelinux.org/awall/about/

https://wiki.alpinelinux.org/wiki/How-To_Alpine_Wall

https://wiki.alpinelinux.org/wiki/Zero-To-Awall

setup

Alpine doesn't come with ANY firewall by default so you'll need to start by installing and enabling iptables:

doas apk add iptables ip6tables

doas modprobe ip_tables
doas modprobe ip6_tables

doas rc-service iptables save
doas rc-service ip6tables save
doas rc-service iptables start
doas rc-service ip6tables start

doas rc-update add iptables
doas rc-update add ip6tables

Then we'll install awall itself.

doas apk add awall

usage

Once you've written some policies you can list and enable them.

doas awall list
doas awall enable main outgoing ping incoming-ssh
doas awall disable ping
doas awall activate

policies

Awall's default config files are in /usr/share/awall, but any new policies we add should go in /etc/awall/.

block all

The best way to setup any firewall is to begin with blocking everything and then selectively allow what is needed.

Dropping a packet simply ignores the packet completely whereas rejecting a packet tells the sender "this packet was rejected" and requires a bit more processing. Our default policy will simply drop packets coming from the outside, but reject the ones we are sending ourselves to give us a bit more information about what is going on in case we've made a mistake in our firewall config.

Create /etc/awall/optional/main.json:

{
	"description": "Drop all incoming traffic from WAN, reject all other",
	"zone": {
		"WAN": { "iface": "eth0" }
	},
	"policy": [
		{ "in": "WAN", "action": "drop" },
		{ "action": "reject" }
	]
}

block incoming, allow outgoing

Alternatively, you may prefer to trust all your local applications. This is less secure than manually allowing them, but it's not completely unreasonable:

{
	"description": "Drop all incoming traffic from WAN, allow all outgoing",
	"zone": {
		"WAN": { "iface": "eth0" }
	},
	"policy": [
		{ "in": "WAN", "action": "drop" },
		{ "in": "_fw", "action": "allow" }
	]
}

allow incoming ssh

Our goal for this policy is allowing SSH connections that are coming in over the internet. There is no need to allow outgoing SSH connections, so we will be as strict as possibly by only allowing incoming ones. We will also limit the amount of requests that can be made to 3 per 30 seconds to drop some connections from bots.

Create /etc/awall/optional/incoming-ssh.json:

{
	"description": "Allow incoming SSH on WAN (TCP/22)",
	"filter": [
		{
			"in": "WAN",
			"out": "_fw",
			"service": "ssh",
			"action": "accept",
			"conn-limit": { "count": 3, "interval": 30 }
		}
	]
}

First, we define which packets this policy is supposed to apply to. The packets have to be coming from the outside "in": "WAN" and be addressed to a local service on the server itself "out": "_fw".

The _fw zone basically just means "services on this computer". Then we specify which protocol and port to use "service": "ssh". How does awall translate ssh to mean 22? There is a default list of common services, which is located at /usr/share/awall/mandatory/services.json. Writing ssh is just a shortcut for { "proto": "tcp", "port": 22 }. So if your ssh daemon was listening on a different port you might write "service": { "proto": "tcp", "port": 1234 },.

You can even define your own custom services in /etc/awall/private if you'd like to be able to refer to them by name.

allow http

{
	"description": "Allow incoming HTTP and HTTPS on WAN (TCP/22)",
	"filter": [
		{
			"in": "WAN",
			"out": "_fw",
			"service": [ "http", "https" ],
			"action": "accept"
		}
	]
}

allow basic outgoing services (DNS, HTTP, HTTPS, ping)

{
	"description": "Allow basic outgoing traffic from local to WAN",

	"filter": [
		{
			"in": "_fw",
			"out": "WAN",
			"service": [ "dns", "http", "https", "ping" ],
			"action": "accept"
		}
	]
}

allow some incoming pings

{
	"description": "Allow incoming ping on WAN with flow-limit",

	"filter": [
		{
			"in": "WAN",
			"out": "_fw",
			"service": "ping",
			"action": "accept",
			"flow-limit": { "count": 10, "interval": 6 }
		}
	]
}

Unlike with our incoming SSH policy, we don’t use conn-limit to limit the connections. Instead, we use flow-limit, which doesn't just limit the number of new connections, but the number of packets. For SSH we don't want this, because many packets could be exchanged after the initial connection attempt.

For ping, it is much simpler since each ping is one ICMP echo packet. So we restrict the maximum number of incoming packets to 10 in 6 seconds.