NFTables.Port

View Source

NFTables.Port is the low-level communication layer that bridges Elixir and the Linux kernel's nftables firewall.

Port component for NFTables. Provides a Zig-based native port executable for communicating with Linux nftables via the official libnftables JSON API.

Overview

NFTables.Port provides:

  • Native Zig Port Executable - High-performance port process with CAP_NET_ADMIN capability
  • JSON Communication - Uses the official nftables JSON API via libnftables
  • Automatic Framing - 4-byte length-prefixed packets for reliable communication
  • Process Isolation - Port process crashes don't affect the Elixir VM
  • Synchronous API - Simple request/response pattern with timeout support

Architecture

flowchart TD
    API[NFTables<br/>High-level Elixir API]
    GenServer[NFTables.Port<br/>GenServer]
    Port[Erlang Port<br/>Zig executable]
    Lib[libnftables<br/>C library]
    Kernel[Linux Kernel<br/>nftables]

    API --> GenServer
    GenServer --> Port
    Port --> Lib
    Lib --> Kernel

Installation

Add nftables_port to your dependencies in mix.exs:

def deps do
  [
    {:nftables_port, "~> 0.4.2"}
  ]
end

Requirements

  • Linux kernel with nftables support (kernel 3.13+)
  • libnftables library installed (nftables package on most distros)
  • Zig compiler (for building from source)
  • CAP_NET_ADMIN capability on the port executable

Setting Capabilities

The port executable requires CAP_NET_ADMIN to communicate with the kernel firewall:

# After compilation
sudo setcap cap_net_admin=ep priv/port_nftables

This is done automatically during mix compile if you have sudo access.

Usage

Direct Usage

# Start the port
{:ok, pid} = NFTables.Port.start_link()

# Send a request to list tables
request = ~s({"nftables": [{"list": {"tables": {}}}]})
{:ok, response} = NFTables.Port.commit(pid, request)

# Parse response
{:ok, data} = Jason.decode(response)

# Stop the port
NFTables.Port.stop(pid)

With NFTables

Typically, you'll use NFTables.Port indirectly through the NFTables high-level API, which provides a clean, idiomatic Elixir interface:

{:ok, pid} = NFTables.Port.start_link()

json_cmd =
  Builder.new()
  |> Builder.add(table: "filter", family: :inet)
  |> Builder.add(chain: "INPUT", hook: :input, policy: :drop)
  |> Builder.add(rule: tcp() |> dport(22) |> accept())
  |> Builder.to_json()

{:ok, json_response} = NFTables.Port.call(pid, json_cmd)

NFTables.Builder provides a composable, type-safe way to build complex firewall rules. Behind the scenes, NFTables.Port handles all the JSON communication with nftables.

Port Executable Location

The port executable is located using this resolution order:

  1. PORT_NFTABLES_PATH environment variable (if set and file exists)
  2. /usr/local/sbin/port_nftables (system-wide installation)
  3. /usr/sbin/port_nftables (system-wide installation)
  4. priv/port_nftables (development or application-bundled)

For production deployments, either:

  • Set PORT_NFTABLES_PATH to specify a custom location
  • Install to /usr/local/sbin/port_nftables

Building from Source

The Zig port executable is built automatically during mix compile:

mix deps.get
mix compile

The build process:

  1. Compiles the Zig source code in native/src/
  2. Creates the executable at priv/port_nftables
  3. Attempts to set CAP_NET_ADMIN capability (requires sudo)

Installing to System Location

see [dev_docs/security.md] for information on how to keep you system secure.

For production deployments, install the port executable to a system location:

# Install to default location (/usr/local/sbin/port_nftables)
sudo mix nftables_port.install

# Install to custom location
sudo mix nftables_port.install /usr/sbin/port_nftables

# Install to custom directory (will create port_nftables in that directory)
sudo mix nftables_port.install /opt/nftables/bin/

The install task:

  • Copies the compiled executable to the specified location
  • Sets executable permissions (750)
  • Sets CAP_NET_ADMIN capability with setcap
  • Provides clear instructions if any step fails

After installation to a standard location (/usr/local/sbin or /usr/sbin), NFTables.Port will automatically find the executable. For custom locations, set the PORT_NFTABLES_PATH environment variable:

export PORT_NFTABLES_PATH=/opt/nftables/bin/port_nftables

Testing

mix test

Note: Tests require:

  • Root privileges or CAP_NET_ADMIN capability
  • Linux system with nftables support

Protocol

The port uses a simple length-prefixed packet protocol:

Request:  [4 bytes: length][N bytes: JSON string]
Response: [4 bytes: length][N bytes: JSON string]

Framing is handled automatically by Erlang's {:packet, 4} option.

License

MIT License - see LICENSE for details.

Documentation

Full documentation is available at HexDocs.