Manual Installation
For distributions without a package. Check Requirements first.
Build
Go 1.27+ on the build machine — not on the target. That is the toolchain
go.mod pins; on Debian trixie it comes from backports, since trixie itself ships
Go 1.24.
git clone https://github.com/jp1337/easywall.git
cd easywall
make build # produces bin/easywall-core and bin/easywall-web
The version from git describe is embedded at build time. Cross-compiling works the
usual way:
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 make build
Install
sudo make install
That places the binaries in /usr/sbin, the assets in /usr/share/easywall, and all
three systemd units in /lib/systemd/system. It does not create the service user, the
directories or the config — do that once:
sudo groupadd --system easywall 2>/dev/null || true
sudo useradd --system --no-create-home --shell /usr/sbin/nologin \
--gid easywall easywall 2>/dev/null || true
sudo install -d -m 0750 -o root -g easywall /run/easywall
sudo install -d -m 0750 -o root -g easywall /etc/easywall
sudo install -d -m 0750 -o easywall -g easywall /etc/easywall/ssl
sudo install -d -m 0750 -o root -g easywall /var/lib/easywall
sudo install -d -m 0700 -o easywall -g easywall /var/lib/easywall/web
sudo install -d -m 0750 -o root -g easywall /var/log/easywall
sudo install -m 0600 -o root -g root config/easywall.toml /etc/easywall/
The ownership is the point, not a detail.
| Path | Why that owner and mode |
|---|---|
/etc/easywall — root:easywall 0750 |
the web user must traverse it to reach its own config, and must not write in it |
easywall.toml — root:root 0600 |
the root daemon reads this. A network-facing process that can change what root reads has undone the two-process split |
web.toml — easywall:easywall 0600 |
the wizard writes the password hash here, and the password page rewrites it |
ssl_dir — easywall:easywall 0750 |
easywall generates its own certificate there and replaces it before it expires |
/var/lib/easywall — root:easywall 0750 |
the core’s: rules.json, the apply state, the panic marker. Owned by root so the core needs no CAP_DAC_OVERRIDE; not group-writable, so the web user cannot replace what root reads |
/var/lib/easywall/web — easywall:easywall 0700 |
the web process’s own state: passkeys, the TOTP replay guard, the version cache |
/run/easywall — root:easywall 0750 |
holds the control socket the web process connects to |
/var/lib/easywallused to be listed here aseasywall:easywall, and that does not work. The unit reduces the daemon toCAP_NET_ADMIN, which for a root service also removesCAP_DAC_OVERRIDE. So root cannot enter a directory it does not own.rules.jsonis never written. Every request fails on a permission error raised deep inside the call. The package had the same bug and it is what theroot:easywallabove fixes.
Installed this way before 2.22?
/var/lib/easywallwas0770and shared, and the web user could replace the core’s files in it. Once, before you start the new binaries:sudo install -d -m 0700 -o easywall -g easywall /var/lib/easywall/web sudo chmod 0750 /var/lib/easywalleasywall-web copies its own files into
web/when it next starts. Until the mode is changed, easywall-core logs an error at every start. The neweasywall-web.servicedoes not start at all withoutweb/.
Configure
Both binaries carry their own commented default and will write it out. Neither overwrites an existing file.
sudo easywall-web --write-config /etc/easywall/web.toml
sudo chown easywall:easywall /etc/easywall/web.toml
sudo easywall-core --write-config /etc/easywall/easywall.toml
Both land as 0600; the chown is what makes web.toml writable by the process
that has to rewrite it. Every key is explained in
Configuration.
The session key ships as an obvious placeholder and easywall replaces it on first start. Generating it yourself is better — then it exists before the port is ever open:
sudo sed -i "s|CHANGE_ME[A-Z0-9_]*|$(openssl rand -hex 32)|" /etc/easywall/web.toml
No
csrf_keyis needed. CSRF protection is Go 1.25’snet/http.CrossOriginProtection, which checksOriginandSec-Fetch-Siteinstead of issuing tokens. The key that older configs and older versions of this page asked for is not read by anything.
Start
sudo systemctl enable easywall-selftest
sudo systemctl enable --now easywall-core easywall-web
systemctl status easywall-core easywall-web # both should be active (running)
easywall-selftest is enabled, never started by hand:
| What it is | a oneshot unit ordered Before=easywall-core |
| What it does | proves the rule builder against the running kernel |
| How often | only when the version or the kernel has changed |
| Why it is separate | it is the only unit holding CAP_SYS_ADMIN, which building its throwaway network namespace needs |
| If it fails | nothing. The core starts anyway; easywall-core health says what it found |
Open https://<server>:12227, accept the self-signed certificate, and complete the
setup wizard.
Uninstall
sudo systemctl disable --now easywall-core easywall-web
sudo systemctl disable easywall-selftest
sudo rm -f /lib/systemd/system/easywall-core.service \
/lib/systemd/system/easywall-web.service \
/lib/systemd/system/easywall-selftest.service
sudo systemctl daemon-reload
sudo rm -f /usr/sbin/easywall-core /usr/sbin/easywall-web
sudo rm -rf /usr/share/easywall
# the rules stay in the kernel until you say otherwise
sudo nft delete table inet easywall 2>/dev/null || true
# config, rules and logs — back them up first if you want them
sudo rm -rf /etc/easywall /var/lib/easywall /var/log/easywall
sudo userdel easywall 2>/dev/null || true
sudo groupdel easywall 2>/dev/null || true