Server Setup

Install

From a pre-built binary

Download the latest release from GitHub Releases:

# Linux amd64
curl -Lo openme https://github.com/openme/openme/releases/latest/download/openme-linux-amd64
chmod +x openme
sudo mv openme /usr/local/bin/

Build from source

Requires Go 1.21+.

git clone https://github.com/openme/openme
cd openme/cli
go build -o openme ./cmd/openme
sudo mv openme /usr/local/bin/

Initialise the Server

openme init generates a fresh Curve25519 keypair and writes /etc/openme/config.yaml with sensible defaults.

sudo openme init --server myserver.example.com

Options:

Flag Default Description
--server (required) Public hostname or IP — used in generated client configs
--port 7777 UDP knock port and TCP health port
--firewall nft Firewall backend: nft or iptables
--force false Overwrite an existing config

The command prints the server’s Curve25519 public key — you’ll need this when provisioning clients, but openme add handles it automatically.


Open the Knock Port

The UDP knock port must be reachable (the point is that TCP ports are not). The examples below assume you have already applied a default-deny firewall policy — if not, see Harden the Firewall first.

# nftables
sudo nft add rule inet filter input udp dport 7777 accept

# iptables
sudo iptables -A INPUT -p udp --dport 7777 -j ACCEPT
Tip

For IPv6, add an equivalent ip6tables or nft inet rule.


Start the Server

sudo openme serve

For production, install as a systemd service:

# /etc/systemd/system/openme.service
[Unit]
Description=openme SPA server
After=network.target

[Service]
ExecStart=/usr/local/bin/openme serve
Restart=on-failure
AmbientCapabilities=CAP_NET_ADMIN

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now openme
sudo systemctl status openme

Harden the Firewall

The whole point of openme is that no TCP ports are permanently open.
Apply a default-deny policy before opening anything, then only allow the UDP knock port.

nftables

# Flush and rebuild a minimal ruleset
sudo nft flush ruleset

sudo nft add table inet filter

# Base chains
sudo nft add chain inet filter input   '{ type filter hook input   priority 0; policy drop; }'
sudo nft add chain inet filter forward '{ type filter hook forward priority 0; policy drop; }'
sudo nft add chain inet filter output  '{ type filter hook output  priority 0; policy accept; }'

# Allow established / related traffic and loopback
sudo nft add rule inet filter input ct state established,related accept
sudo nft add rule inet filter input iifname "lo" accept

# Allow the UDP knock port (openme)
sudo nft add rule inet filter input udp dport 7777 accept

# Create the openme chain (openme manages rules inside it)
sudo nft add chain inet filter openme
sudo nft add rule inet filter input jump openme

Persist the ruleset:

sudo nft list ruleset | sudo tee /etc/nftables.conf
sudo systemctl enable --now nftables

iptables / ip6tables

# ── IPv4 ──────────────────────────────────────────────────────────────────
# Flush existing rules and set default DROP on INPUT and FORWARD
sudo iptables -F
sudo iptables -X
sudo iptables -P INPUT   DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT  ACCEPT

# Allow established / related traffic and loopback
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -i lo -j ACCEPT

# Allow the UDP knock port (openme)
sudo iptables -A INPUT -p udp --dport 7777 -j ACCEPT

# ── IPv6 ──────────────────────────────────────────────────────────────────
sudo ip6tables -F
sudo ip6tables -X
sudo ip6tables -P INPUT   DROP
sudo ip6tables -P FORWARD DROP
sudo ip6tables -P OUTPUT  ACCEPT

sudo ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo ip6tables -A INPUT -i lo -j ACCEPT
sudo ip6tables -A INPUT -p udp --dport 7777 -j ACCEPT

Persist across reboots (Debian/Ubuntu):

sudo apt install iptables-persistent
sudo netfilter-persistent save
Warning

These rules drop all incoming TCP connections, including SSH.
Run them from the console or a recovery environment, or add a temporary SSH allowance first:

# Temporary SSH rule — remove it once openme clients are verified
sudo iptables  -A INPUT -p tcp --dport 22 -j ACCEPT
sudo ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT

Once your openme clients are working, remove the temporary SSH rules:

sudo iptables  -D INPUT -p tcp --dport 22 -j ACCEPT
sudo ip6tables -D INPUT -p tcp --dport 22 -j ACCEPT

For nftables, if you added a temporary SSH rule:

# Find the rule handle
sudo nft -a list chain inet filter input

# Delete by handle (replace <handle> with the number shown)
sudo nft delete rule inet filter input handle <handle>

Verify

Once a client is configured and has knocked, you can verify the full round trip:

# Knock and immediately check the health port (end-to-end test)
openme status --knock

# Check health port only (requires a prior knock within knock_timeout)
openme status
Note

The health port is never permanently open. It is opened by the firewall only after a valid knock, for the knock_timeout duration (default 30s). A closed health port means either no knock has been sent yet, or the knock_timeout has expired.

Next: Add a client →