Before continuing, understand that an allowlist firewall blocks all network traffic by default.
Files are:
galaf.json: galaf configuration file for defines and outbound rulesgalaf_generate: utility to generate groups, nftables includes and dnsmasq nftset filesnftables.conf: example base allowlist nftables configuration with includesgalaf.candgalaf-test.c: galaf appgroup "gatekeeper" toolso_mark_giduid.bpf.candso_mark_giduid.service: eBPF socket mark toolgalaf_stats: print nft sets / statisticsgalaf_tcpdump: tcpdump specific ct mark / user / appgroup traffic
For more information, examine the files.
No easy to install package for the example files is provided because installing an allowlist firewall will drop or reject all network traffic and therefore break things you expect to work. It's best that you understand, customize, configure and install an allowlist application firewall yourself. As it is, the examples provided are minimal and principally meant as a guide and do not allowlist much of anything.
How to install them manually (as root):
Build and install the galaf tool (pre-built binary targets amd64 Debian bullseye only!):
# if using galaf.json; requires "execv" entry for every appgroup
apt install libjson-glib-dev # GLib JSON library development files
{ gcc | clang } galaf.c $(pkg-config --cflags --libs json-glib-1.0) -o galaf
install -p galaf /usr/local/bin/
setcap cap_setgid+ep /usr/local/bin/galaf # grant capabilities
# Usage: galaf appgroupname [params ...]# OR (for testing) NOT using galaf.json: can execvp anything in PATH!
{ gcc | clang } galaf-test.c -o galaf-test
install -p galaf-test /usr/local/bin/galaf
setcap cap_setgid+ep /usr/local/bin/galaf # grant capabilities
# Usage: galaf appgroupname command [params ...]Note: +ep = effective and permitted capabilities. Effective normally for "capability-dumb binary". Without e we'd call cap_get_proc, cap_set_proc, cap_clear (libcap, sys/capability.h). Not really an issue with such a simple program for now... and capabilities are not inheritable via execv:
galaf-test appgroupname /usr/sbin/capsh --print # capabilities not inheritable via execv
/usr/sbin/getcap /usr/local/bin/galaf # unprivileged user can confirm capsAlso: new files created may be owned by the new group (if they did not exist before). This should not present a problem, and does show which app created new files. Just remember that when you delete groups, you should first chown files owned by those groups (galaf_generate offers this option).
Optionally customize and install the galaf.json configuration file:
mkdir -p /usr/local/etc/galaf && cp galaf.json /usr/local/etc/galaf/galaf.json should have three main dictionary entries:
config: defines appgroup gid range, if nft table is inet or not (combined ipv4+6), nft table name and user / group defaults.users: users outbound allowlist rulesgroups: application groups outbound allowlist rulesexecvdefines the binary (path must be fully qualified!) and command line arguments. Single unicode elipsis character"…"(compose-key + . + .) will be replaced by the rest of the original arguments (after appgroupname).domainsif using dnsmasq DNS based allowlist ip address sets.- array of strings = domain names (includes subdomains)
- string = appgroupname to use same domains as other group.
ruleandrule6define (additional, if using domains) accept rules, empty string means accept unconditionally.- if
domainsandruleare missing, then only an nft define will be generated, no rules. In that case you can manually add allowlist rules tonftables.confusing those defines.
Optionally install galaf_generate to generate nftables include files and dnsmasq nftset configuration files:
install -p galaf_generate /usr/local/sbin/
galaf_generate # -h for helpOptionally build and install the so_mark_giduid eBPF (object file should be portable):
apt install clang bpftool # gcc cannot target bpf
clang --target=bpf -O2 -c so_mark_giduid.bpf.c -o so_mark_giduid.bpf.o
# Copy eBPF object file to priviledged area:
mkdir -p /usr/local/etc/galaf && cp so_mark_giduid.bpf.o /usr/local/etc/galaf/
# Install systemd service
mkdir -p /usr/local/lib/systemd/system && cp so_mark_giduid.service /usr/local/lib/systemd/system/
# Enable service (next/every boot) and start it now
systemctl enable --now so_mark_giduid.serviceCustomize and install nftables.conf, and enable nftables service:
# Validate nftables.conf file
nft -cf nftables.conf
# This wil overwrite existing /etc/nftables.conf
install -pb nftables.conf /etc/
# if not already running, start nftables service:
systemctl enable --now nftables.service
# else restart it
systemctl restart nftables.service
# or simply reload
nft -f /etc/nftables.confAssuming you've already setup and configured dnsmasq your preferred way (see main README.md), (re)start it:
# if dnsmasq is running as a Network Manager plugin...
systemctl restart NetworkManagerIt's not required, but you might want to reboot to ensure all caches are flushed, and everything is loaded fresh in case any applications have already cached IP addresses prior to installation (domain based rules might not work in that case).
Let's say we have this /usr/local/etc/galaf/galaf.json entry under groups:
"ddgr": {
"execv": [ "/usr/bin/ddgr", "…" ],
"domains": [ "duckduckgo.com" ],
"rule": "tcp dport 443"
}User can use galaf directly (helps with awareness of allowlist firewall):
galaf ddgr [params ...]
# Could also add galaf bash-completion to rcfile if appgroupnames == commandnames:
complete -F _command galafOr, create user galaf aliases (~/.bash_aliases),
Or, create system wide aliases (/etc/profile.d/galaf_aliases.sh):
alias ddgr='galaf ddgr'Or, create overrides for /usr/bin binaries in /usr/local/bin (or ~/bin):
install <(echo -e '#!/bin/sh\nexec galaf ddgr "$@"') /usr/local/bin/ddgrFor desktop files, can create /usr/local versions with a different Exec:
sed 's|Exec=/usr/bin/thunderbird|Exec=galaf thunderbird|' /usr/share/applications/thunderbird.desktop > /usr/local/share/applications/thunderbird.desktopClick for information about dbus services (Exec=gapplication launch)...
Some apps may be launched as dbus services, for example: GNOME Weather. These can be identified by their Exec= line, which may look something like this:
grep ^Exec /usr/share/applications/org.gnome.Weather.desktop
Exec=gapplication launch org.gnome.WeatherIn that case gapplication is launching a dbus service file:
grep ^Exec /usr/share/dbus-1/services/org.gnome.Weather.service
Exec=/usr/share/org.gnome.Weather/org.gnome.Weather --gapplication-serviceSo if we have in galaf.json:
"gnomeWeather": {
"execv": [ "/usr/share/org.gnome.Weather/org.gnome.Weather", "--gapplication-service" ],
"domains": [ "api.met.no", "www.aviationweather.gov" ]We can copy and modify the dbus service file to a /usr/local one:
sed 's|^Exec=.*|Exec=/usr/local/bin/galaf gnomeWeather|' /usr/share/dbus-1/services/org.gnome.Weather.service /usr/local/share/dbus-1/services/org.gnome.Weather.serviceAnd, if it's not already configured, we also need (first/one time only) to add local modifications to dbus search path:
cat > /etc/dbus-1/session-local.conf << "EOF"
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- Search for .service files in /usr/local -->
<servicedir>/usr/local/share/dbus-1/services</servicedir>
</busconfig>
EOFThe above should work for the GNOME Weather app, however it will not work for weather info in your GNOME notifications, because gnome-shell is the app fetching that! So, after all, the easiest might be to give user allowlist access to the weather domains instead if you really wish... or use a command line, or web based, weather app ;)
Optionally install the galaf_stats and galaf_tcpdump tools for root:
install -p galaf_stats galaf_tcpdump /usr/local/sbin/
head galaf_tcpdump # show examples
galaf_stats # does not require parameters