Firewall on Demand (Go)
This is a rewrite of Firewall on Demand in Go. This is currently work in progress and should not be run in production. E.g. currently there are no database migrations.
Firewall on Demand (abbreviated as FOD), also previously known as flowspy, is historically a python web (Django) application to create firewall rules using BGP Flowspec. The source code for the current production Firewall on Demand can be found on GitHub.
Why another codebase?
This codebase is aimed at exploring a possible future for the Firewall on Demand project. This project is not about rewriting it "just because we can", but to explore new ideas. One of these ideas is that this codebase is not closely coupled to the BGP backend that is being used. Internally an interface is created which communicates with a BGP backend.
Currently a backend that connects to a GoBGP
daemon is implemented. Additionally a log backend is implemented which does
nothing with the BGP Flowspec rules except for logging them. This is useful for development.
Another goal of this project is to only implement the parts we need. The idea of a GUI for BGP Flowspec is not tricky, but implementing the whole specification can get complex fast.
At last, this project aims at ease of deployment, because this is a Go project, a static binary can be created which is convenient for deployment. RPM and DEB packages will also be created.
Building
Requirements
- go >= 1.25.0
- postgres - Currently tested with version 14
- GoBGP - optional, for the GoBGP backend
- make - optional, but useful for running the provided build scripts
make build
This creates a static fod-go binary in the current directory.
Configuration
For basic configuration there is an example JSON file in config/config.example.json. The following is an annoted explanation with all options added:
{
// the postgres database URL to use
// See: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING-URIS for the form to use
// default: ""
"db_url": "",
// the bgp backend to use, should be "gobgp" or "log"
// default: "gobgp"
"bgp_backend": "log",
// the gobgp server to connect to when the `gobgp` backend is used
// default: 127.0.0.1:50051
"gobgp_server": "127.0.0.1:50051",
// the stats backend to use
// one of: "none", "fake", "script"
// default: "none"
"stats_backend": "none",
// the URL to logout
// if empty, there is no logout button
// this is useful for proxy authentication
// default: ""
"logout_url": "",
// the path to a stats script to use when `stats_backend` is "script"
// it should output a list of rules as JSON
// the JSON structure is a list of:
// {
// "rule": structure the same in the REST api for a rule
// "statistic": structure the same in the REST api for a statistic
// }
// default: ""
"stats_script": "",
// which address to listen on?
// default: 127.0.0.1:8080
"listen": "127.0.0.1:8080",
// the auth method, should be "local" or "proxy". Local means no authentication, proxy is authentication using X-Remote-User reverse proxy header
// In proxy authentication the X-Remote-User header is only accepted from localhost
// default: "proxy"
"auth": "local",
// Local user is the user that is logged in into the FOD UI when auth=local is used
// Default: "admin"
"local_user": "admin",
// the flowspec modes to use, by default there is only a single FlowSpec mode
// by default users can only use the default FlowSpec mode, unless "change_flowspec_mode" is set on the user and multiple are available
// default: see below
"flowspec_modes": {
// the default FlowSpec mode to use, should be one of the keys inside `modes`
"default": "global",
// the difference FlowSpec mode
"modes": {
// the key, to use with `default`
"global": {
// the name of the FlowSpec mode to show in the UI
"label": "Global",
// the RD value, the routing distinguisher, this makes the FlowSpec mode FlowSpec-VPN
"rd": null,
// in case of FlowSpec-VPN this is the routing target
"rt": null
}
}
},
// the rate limit options to show in the UI
// Default: the below options
"rate_limits": {
"discard": 0.0,
"rate limit 10k": 10485760.0,
"rate limit 20k": 20971520.0,
"rate limit 50k": 52428800.0,
"rate limit 100k": 104857600.0,
"rate limit 1M": 1073741824.0,
"rate limit 10M": 10737418240.0,
"rate limit 100M": 107374182400.0
},
// the router platform which the firewall rules interact with
// this is to disable certain rules from being created as these router platforms have limitations
// an empty string means all rules can be created
// available options: nokia
"router_platform": "",
// the regex for validating user IDs
// Useful if you use "auth": "proxy" and the format of user IDs is known
// Default: ".*", meaning match everything
"userid_regex": ".*",
// WHOIS server is the server to use for WHOIS ASN IP address lookups
// Default: "whois.radb.net:43"
"whois_server": "whois.radb.net:43",
// Hooks is the list of 'hooks' which are scripts that are executed on certain actions
// The value is the path of a script to run. An empty value means run no script
"hooks": {
// AddRule is the script to run when a firewall rule was added
// Arguments to the script: UID of the applier, rule name and rule description
// Default: ""
"add_rule": "",
// RemoveRule is the script to run when a firewall rule was removed
// Arguments to the script: UID of the applier, rule name and rule description
// Default: ""
"remove_rule": "",
// EditRule is the script to run when a fireall rule was edited
// Arguments to the script: UID of the applier, rule name and rule description
// Default: ""
"edit_rule": "",
// InactiveUserLogin is the script to run when an inactive user logs in
// useful for admins to activate users
// Arguments to the script: UID of the user that is inactive
// Default: ""
"inactive_user_login": ""
},
// Create the following users
// You only have to define users that you automatically want to have certain permissions or want to automatically add to networks
// if you use proxy authentication, users are automatically added but have to be added by super users to networks in the GUI
// default: {}
"users": {
// an admin user is created
// in proxy auth, set your UID here
"admin": {
// this user is automatically set to active
// default: false
"active": true,
// the name of the user to be displayed in the UI
// if the name is not set, it will be "Unnamed User" in the rules UI
// default: ""
"name": "",
// the email of the user, for notifications
// default: ""
"email": "",
// is this user an admin user, super user?
// default: false
"super": true,
// which networks does this user belong to
// default: []
"networks": [
"fod"
],
// for how long can the rules created by this user last, the default is 90 days
// -1 means indefinite
// default: 90
"max_expiry_days": -1,
// min v4 mask is the minimum destination subnet mask for which the user can create rules.
// e.g. if the user adds a rule to 1.1.1.0/24, this is allowed, but not 0.0.0.0/0
// default: 24
"min_v4_mask": 24,
// min v6 mask is the same as min_v4_mask but for v6 IP addresses
// default: 64
"min_v6_mask": 64,
// indicates whether or not the user can choose another FlowSpec mode instead of the default one or the one already configured for a rule
"change_flowspec_mode": true
}
},
// which networks are automatically created?
// default: {}
"networks": {
// the name of the network, called fod here
"fod": {
// statically the following subnets are added
// These IP addresses are used to determine if the user can create rules to specific IPs
// e.g. if a rule is created to 127.0.0.1, it's allowed here but to 8.8.8.8 not
// default: []
"static": [
// just localhost
"127.0.0.1/32"
]
// adds IP addresses that belong to the AS number automatically to the network
// This contacts a whois database (whois.radb.net:43)
// This is pre-populated when FOD is restarted
// e.g. "whois": "AS21320"
// default: ""
"whois": ""
}
}
}
Environment variables
There are also some environment variables that you must set. This is so that FOD knows how to find files
CONFIGURATION_DIRECTORY: The directory where the configuration file (config.json) should be read from. This env variable can be set automatically by Systemd
Running
The binary that was created using make build can be installed to the $PATH and then ran like that.
E.g.
CONFIGURATION_DIRECTORY=/path/to/config/dir fod-go
Development
We provide a convenience command for development that builds and runs the project for development.
make dev
This sets the configuration directory to the current directory. The
example config config/config.example.json is copied to config/config.json if it did not exist. You should set the dbURL in the config.json to connect to postgres.
You should see a log that FOD has started and you can now open your browser at
127.0.0.1:8080 to begin using the UI.
Development in a container
If you have docker installed, you can run
cd containerdev
docker compose up
This will bring up an app container which is running make containerdev (a version of make dev that
loads a modified config for use in this container setup), a Postgres container and a container running gobgpd. You can use docker exec to get a shell on the gobgpd container.
If you are using Visual Studio Code, you may use the .devcontainer files to develop inside the app
container. This uses the same docker compose file with some further modifications - you must open a
terminal in the container and run make containerdev manually.
SQL changes
If you change any SQL queries, you need to re-generate the Go code using sqlc. Once installed, run:
make sql
Quick start with GoBGP
To get started with GoBGP, make sure first that the BGP backend of FOD is set to "gobgp" in config/config.json (or your own location).
Then configure the GoBGP daemon in a file called gobgpd.toml, something like:
[global.config]
as = 65001
router-id = "127.0.0.1"
local-address-list = ["127.0.0.1"]
[[neighbors]]
[neighbors.config]
neighbor-address = "127.0.0.2"
peer-as = 65002
[[neighbors.afi-safis]]
[neighbors.afi-safis.config]
afi-safi-name = "ipv4-flowspec"
Here we have a neighbor, 127.0.0.2, just for testing.
Then start GoBGP and fod:
gobgpd -f gobgpd.toml -l info 2>&1 >gobgp.log &
# make sure postgres is started and db_url is set in config/config.json
make dev # or your release binary, in which case gobgpd is better managed using e.g. Systemd
Then when a rule is added in the FOD UI, run:
gobgp global rib -a ipv4-flowspec