Linux Headless Client
The Linux Headless Client is intended for environments without a graphical desktop session. It supports two authentication modes:
- Service account tokens -- long-lived, multi-owner tokens generated in the admin portal. Best for system-to-system connectivity where no user is present to authenticate. See Service Accounts.
- User tokens via browser-based sign-in -- short-lived tokens obtained by signing the running user into the portal in a browser. Useful when you want a human user to authenticate the Client on a server they manage without installing a GUI. See Browser-based authentication.
If you're looking for a desktop Client that authenticates with your identity provider through a graphical interface, see the Linux GUI Client.
Prerequisites
- Any Linux-based OS with kernel 3.10 or higher
- ARM64, ARMv7l, or x86_64 CPU
- A Firezone account, plus admin access to create a service account if you intend to authenticate with a service account token
- Root access or
sudofor Split DNS
Download
Download the Linux headless Client from our changelog page, or use one of the direct links below:
- Download the Linux Client for
x86_64 - Download the Linux Client for
ARMv7l - Download the Linux Client for
ARM64
Mark the binary as executable:
chmod +x firezone-client-headless-linux_1.5.8_x86_64
Installing on NixOS
Firezone ships a first-party flake with a NixOS module for the headless Client, so you can manage it declaratively alongside the rest of your system configuration. The module wires the Client up as a hardened systemd service that authenticates with a service account token.
The NixOS module authenticates with a service account token only. For browser-based user sign-in, download the Client manually using the steps above and follow the usage instructions.
Add the flake as an input, pinning the release tag of the headless Client:
# flake.nix
{
inputs.firezone.url = "github:firezone/firezone/headless-client-1.5.10";
}Then import the module and enable the service in your system configuration:
# configuration.nix
{ inputs, ... }:
{
imports = [ inputs.firezone.nixosModules.headless-client ];
# Substitute pre-built, Firezone-signed binaries instead of compiling the
# Client from source. Optional, but strongly recommended.
nix.settings = {
extra-substituters = [ "https://artifacts.firezone.dev/nix" ];
extra-trusted-public-keys = [
"artifacts.firezone.dev/nix-1:T4LHdL1HeA6LE9qgu0Po0j3RIlelKZz8pzqnzEAIRfI="
];
};
services.firezone.headless-client = {
enable = true;
# Path to a file containing a service account token from the admin portal.
# Keep this out of the Nix store — point it at a secret managed outside of
# your configuration (e.g. with sops-nix or agenix), or place it manually.
tokenFile = "/var/lib/secrets/firezone-client-token";
# Optional. Defaults to the system hostname.
name = "my-nixos-host";
};
}
Rebuild to start the Client:
sudo nixos-rebuild switch
The service runs as firezone-headless-client. Check its status and logs with:
systemctl status firezone-headless-client
journalctl --unit firezone-headless-client --follow
Substituter changes only take effect after a rebuild, so the very first build with the cache configured may still compile from source. If the cache is unreachable, Nix falls back to building from source — slower, but never broken.
Module options
The most common options for services.firezone.headless-client:
| Option | Default | Description |
|---|---|---|
enable | false | Enable the headless Client systemd service. |
tokenFile | (required) | Path to a file holding a service account token. Must not live in the Nix store. |
name | networking.hostName | Friendly name shown for this Client in the admin portal. |
id | null | Stable identifier for this Client. When null, one is generated and persisted on first run. |
apiUrl | wss://api.firezone.dev/ | WebSocket URL of the Firezone control plane. |
dnsControl | "systemd-resolved" | DNS control method: systemd-resolved, etc-resolv-conf, or disabled. |
logLevel | "info" | MAX_PARTITION_TIME |
FIREZONE_NO_TELEMETRY | false | Set to true to disable the Sentry crash-reporting agent. |
RUST_LOG directives for the Client. | ||
enableTelemetry | true | Whether to allow crash reporting and telemetry. |
extraEnvironment | { } | Extra FIREZONE_* environment variables for the service. |
dnsControl = "systemd-resolved" (the default) requires
services.resolved.enable = true.
Imperative install
To run the Client without the NixOS module — for example to try it out — install the package into your profile:
nix profile install github:firezone/firezone/headless-client-1.5.10#firezone-headless-clientOr run it directly without installing:
sudo nix run github:firezone/firezone/headless-client-1.5.10#firezone-headless-clientConfigure the imperative Client with the same environment variables documented in the usage and environment variable reference sections below.
Usage
Signing in
The Client must be authenticated before it can connect. Pick one of the two flows below depending on whether you want to authenticate as a service account or as a user.
With a service account token
Service account tokens are long-lived and ideal for unattended systems. Generate one using the instructions in the service account documentation, then start the Client with the token in the environment:
sudo FIREZONE_TOKEN=<TOKEN> ./firezone-client-headless-linux_1.5.8_x86_64
sudo is required to control the system's DNS.Set some environment variables to configure it:
export FIREZONE_NAME="Development API test client"
export FIREZONE_ID=$(head -c 32 /dev/urandom | sha256sum | cut -d' ' -f1)
export FIREZONE_TOKEN=<TOKEN>
export LOG_DIR="./"
sudo -E ./firezone-client-headless-linux_1.5.8_x86_64
See below for a full list of environment variables.
Browser-based authentication
If you want to authenticate the Client as a normal user with your identity
provider, use the sign-in subcommand. The Client prints a URL for you to open
in any browser; after signing in, the portal displays a token that you paste
back into the terminal. The token is then stored on disk at
/etc/dev.firezone.client/token (configurable with --token-path or
FIREZONE_TOKEN_PATH) and reused on subsequent runs.
sudo ./firezone-client-headless-linux_1.5.8_x86_64 sign-in \
--account-slug <YOUR_ACCOUNT_SLUG>
The --account-slug flag is optional; if omitted, the URL takes you to the
generic sign-in page where you can pick your account.
Once a token is saved you can start the Client without setting FIREZONE_TOKEN:
sudo ./firezone-client-headless-linux_1.5.8_x86_64
To remove a saved token, run sign-out:
sudo ./firezone-client-headless-linux_1.5.8_x86_64 sign-out
User tokens expire based on the client session lifetime configured for
your auth provider in the Firezone admin portal (not your identity provider's
own session lifetime). When the token expires the Client will fail to
authenticate, and you'll need to run sign-in again. For long-running
unattended deployments, prefer a service account token.
Accessing a Resource
When Firezone is signed in, HTTP clients, SQL clients, and other programs will automatically use it to securely connect to Resources.
Split DNS
By default, Split DNS is enabled for the Linux headless Client as of version 1.1.5.
To disable Split DNS for the Linux Client, set the FIREZONE_DNS_CONTROL
environment variable to disabled.
To control /etc/resolv.conf directly, set FIREZONE_DNS_CONTROL to
etc-resolv-conf.
Read more below to figure out which DNS control method is appropriate for your system.
systemd-resolved
On most modern Linux distributions, DNS resolution is handled by
systemd-resolved. If this is the case for you, do not set
FIREZONE_DNS_CONTROL. If you're not sure whether you use systemd-resolved,
you can check by running the following command:
systemctl status systemd-resolved
Ensure that /etc/resolv.conf is a symlink to
/run/systemd/resolve/stub-resolv.conf:
# Check if /etc/resolv.conf is already a symlink to /run/systemd/resolve/stub-resolv.conf
stat /etc/resolv.conf
# If it's not, create the symlink
sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf.new
sudo mv /etc/resolve.conf.new /etc/resolv.conf
NetworkManager
In most cases, if you're using NetworkManager your system already uses
systemd-resolved, and you should leave FIREZONE_DNS_CONTROL unset, which
will use the default systemd-resolved DNS control method.
When NetworkManager detects that symlink exists, it will automatically use
systemd-resolved for DNS resolution and no other configuration is necessary.
/etc/resolv.conf
If you're not using systemd-resolved, Firezone supports using the
/etc/resolv.conf file to configure Split DNS as a fallback. To do this, set
FIREZONE_DNS_CONTROL to etc-resolv-conf, and the Linux Client will override
the /etc/resolv.conf file with the Firezone internal proxy.
When the Linux Client process exits, it will revert the /etc/resolv.conf file
back to its original state. If for some reason this isn't the case, you can
easily restore it by running the following command:
sudo mv /etc/resolv.conf.before-firezone /etc/resolv.conf
Read more about how DNS works in Firezone.
Help output
> sudo ./firezone-client-headless-linux_1.5.8_x86_64 --help
Command-line args for the headless Client
Usage: firezone-client-headless-linux_1.5.8_x86_64 [OPTIONS] [COMMAND]
Commands:
sign-in Sign in via browser-based authentication
sign-out Sign out by removing the stored token
help Print this message or the help of the given subcommand(s)
Options:
--dns-control <DNS_CONTROL>
Possible values:
- disabled: Explicitly disable DNS control
- etc-resolv-conf: Back up `/etc/resolv.conf` and replace it with our own
- systemd-resolved: Cooperate with `systemd-resolved`
[env: FIREZONE_DNS_CONTROL=]
[default: systemd-resolved]
-l, --log-dir <LOG_DIR>
File logging directory. Should be a path that's writeable by the current user
[env: LOG_DIR=]
-m, --max-partition-time <MAX_PARTITION_TIME>
Maximum length of time to retry connecting to the portal if we're having internet issues or it's down. Accepts human times. e.g. "5m" or "1h" or "30d"
[env: MAX_PARTITION_TIME=]
--firezone-name <FIREZONE_NAME>
Friendly name for this client to display in the UI
[env: FIREZONE_NAME=]
-i, --firezone-id <FIREZONE_ID>
Identifier used by the portal to identify and display the device
[env: FIREZONE_ID=]
--activate-internet-resource
Activate the Internet Resource.
To actually use the Internet Resource, the user must also have a policy granting access to the Internet Resource.
[env: FIREZONE_ACTIVATE_INTERNET_RESOURCE=]
--no-telemetry
Disable sentry.io crash-reporting agent
[env: FIREZONE_NO_TELEMETRY=]
--token-path <TOKEN_PATH>
A filesystem path where the token can be found
[env: FIREZONE_TOKEN_PATH=]
[default: /etc/dev.firezone.client/token]
-h, --help
Print help (see a summary with '-h')
-V, --version
Print version
The sign-in subcommand accepts --auth-base-url (FIREZONE_AUTH_BASE_URL,
default https://app.firezone.dev) and --account-slug
(FIREZONE_ACCOUNT_SLUG). The sign-out subcommand accepts -f, --force to
skip the confirmation prompt.
Environment variable reference
| Variable Name | Default Value | Description |
|---|---|---|
FIREZONE_TOKEN | Token used to authenticate the Client. Either a service account token from the admin portal or a user token obtained via sign-in. If unset, the Client reads the token from FIREZONE_TOKEN_PATH. | |
FIREZONE_TOKEN_PATH | /etc/dev.firezone.client/token | Filesystem path the Client reads the token from when FIREZONE_TOKEN is unset, and the path the sign-in subcommand writes to. |
FIREZONE_ACTIVATE_INTERNET_RESOURCE | Whether to activate the Internet Resource for this Client. Default is blank, which is disabled. Set to 1 or true to enable. | |
FIREZONE_NAME | <system hostname> | Friendly name for this client to display in the UI. |
FIREZONE_ID | Identifier used by the portal to identify this client for metadata and display purposes. | |
FIREZONE_DNS_CONTROL | (blank) | The DNS control method to use. The default is systemd-resolved. Set this to disabled to disable DNS control, or etc-resolv-conf to use the /etc/resolv.conf file. Do not use etc-resolv-conf if /etc/resolv.conf is not a regular file, e.g. if it's a symlink to /run/systemd/resolve/stub-resolv.conf |
LOG_DIR | File logging directory. Should be a path that's writeable by the current user. If unset, logs will be written to stdout only. | |
RUST_LOG | info | Log level for the client. Set to debug for verbose logging. Read more about configuring Rust log levels here. |
Upgrading
- Download a newer binary from one of the links above.
- Stop the running Client.
- Replace the existing binary with the new one.
- Start the Client with the same environment variables as before.
Diagnostic logs
Firezone writes log files to disk. These logs stay on your computer and are not transmitted anywhere. If you encounter a bug, sending us the files may help us fix the bug.
Uninstalling
- Stop the running Client
- Delete the binary
Troubleshooting
For DNS checks, reverting DNS control after a crash, and known issues, see the Troubleshooting guide.
Need help? See all support options.