Skip to content

Installation with Docker / Podman (Asset Mode)

XplicitTrust provides an official container image for running the agent in containerized environments such as Docker, Podman, or Kubernetes.

The container image is available at cr-public.xplicittrust.com/xtna-agent and supports amd64, arm64, and armhf architectures.

Prerequisites

Network capabilities

The container must run with elevated network capabilities:

  • NET_ADMIN — required for WireGuard tunnel management and firewall rules
  • NET_RAW — required for raw socket operations

Rootful mode required

The container must run in rootful mode (e.g. with sudo). Rootless Docker/Podman cannot grant real network capabilities outside the user namespace.

The /dev/net/tun device

The agent builds a WireGuard tunnel and therefore needs access to the host's TUN device, /dev/net/tun. Without it the agent starts but fails to bring up the tunnel, logging:

CreateTUN("xt0") failed; /dev/net/tun does not exist

This applies even though the agent uses a userspace WireGuard implementation — the userspace tunnel still attaches to a kernel TUN interface. A missing TUN device is the most common cause of an agent that registers successfully but never connects (and, as a side effect, installs no firewall rules, since those rules are scoped to the tunnel interface).

Two things are required:

  1. The tun kernel module must be loaded on the host. It usually is, but on minimal hosts you may need to load it (and persist it across reboots):

    sudo modprobe tun
    echo tun | sudo tee /etc/modules-load.d/tun.conf
    
  2. The device must be passed into the container with --device /dev/net/tun (Docker/Podman) or the equivalent in your orchestrator. This is included in all the run commands below.

--device requires the host device to already exist

--device /dev/net/tun passes through an existing host device node. If the tun module isn't loaded on the host, /dev/net/tun doesn't exist and the container fails to start with an error like error gathering device information ... no such file or directory. Always ensure the host module (step 1) before relying on --device (step 2).

Network mode

Which network mode the container needs depends on what you expect the agent to do.

Client-only asset. If the agent only makes the container itself reachable over XplicitTrust Network Access, the default bridge/NAT network is sufficient. The agent opens outbound connections, so no inbound reachability on the LAN is required. All the examples in this guide use the default network.

Gateway asset. If the agent should route traffic to other devices on the local subnet, the default bridge network is not enough. The container needs its own presence on the physical LAN — its own MAC address and its own IP in the LAN subnet — so that it can answer ARP for the subnet it serves and so return traffic from local devices finds its way back.

Use macvlan, not ipvlan

For gateway assets, attach the container with the macvlan driver. The ipvlan driver (in its default L2 mode) gives every container the host's MAC address rather than one of its own. The agent then cannot act as a distinct layer-2 endpoint for the subnet it routes, and traffic to and from local devices fails in ways that look like a routing problem but are not. Symptoms: the asset registers and the tunnel comes up, remote clients can reach the container itself, but nothing behind it on the LAN responds.

Create a macvlan network whose parent is the host's LAN interface (or a bridge on top of it), then attach the container to it:

sudo docker network create -d macvlan \
  -o parent=br0 \
  --subnet 192.168.1.0/24 \
  --gateway 192.168.1.1 \
  xtna-lan

sudo docker run -d --restart unless-stopped \
  --network xtna-lan --ip 192.168.1.240 \
  --cap-add NET_ADMIN --cap-add NET_RAW \
  --device /dev/net/tun \
  -v /opt/xtna/my-asset:/etc/XplicitTrust \
  cr-public.xplicittrust.com/xtna-agent:latest \
  -token <token> -domain <tenant-domain>

Replace br0 with the actual parent interface — it differs per host, and on appliance platforms it is often a virtual switch rather than the physical NIC.

Addressing

Docker's macvlan driver does not run a DHCP client: the --ip above — and any address Docker picks if you omit it — comes from Docker's own address management using the --subnet you gave at network-creation time. The container never requests a lease from the LAN's DHCP server. There are three ways to handle addressing:

  • Fixed IP with a DHCP reservation (recommended for a gateway asset). Keep --ip, pin the container's MAC with --mac-address, and add a reservation for that MAC on your DHCP server. The address stays centrally managed but never changes. A gateway asset needs a stable address — local devices route through it, so an address that changed on lease renewal would break their routing.

  • Docker auto-assignment. Omit --ip and Docker assigns an address itself from the network's range. This is not DHCP, and it collides with the DHCP pool unless you restrict Docker to a block carved out of that pool with --ip-range — for example --ip-range 192.168.1.224/27 for a network whose DHCP pool ends at .223.

  • Genuine DHCP leases. A container only obtains a real lease from the LAN DHCP server through a third-party driver such as docker-net-dhcp, or on a platform that provides it natively (QNAP's Qnet driver has a DHCP mode, for instance). Both are outside plain Docker and Podman.

Reserve the address either way

Whichever option you choose, exclude the container's address from the DHCP pool — as a static reservation or a pool boundary — so the DHCP server never leases it to another device.

Host isolation

macvlan deliberately prevents the container and its own host from talking to each other: traffic between them never reaches the physical network where it could be switched back. This is a property of macvlan itself, not a misconfiguration.

It matters whenever the NAS or hypervisor running the agent also hosts services that XplicitTrust clients should reach, since those are exactly the packets that get dropped.

Some platforms expose a setting for this — on Unraid it is Settings → Docker → Host access to custom networks, which must be enabled. Where no such setting exists, create a macvlan shim interface on the host:

sudo ip link add xtna-shim link br0 type macvlan mode bridge
sudo ip addr add 192.168.1.250/24 dev xtna-shim
sudo ip link set xtna-shim up

The shim gives the host a second address on the LAN that can reach macvlan containers. Use an address outside your DHCP pool and distinct from both the host's own LAN address and the container's.

The shim does not survive a reboot

ip link changes are not persistent. Add the commands to whatever the platform provides for boot-time scripting (/etc/rc.local, a systemd unit, or the appliance's startup-script hook), or host access breaks again on the next restart.

Installation

sudo docker pull cr-public.xplicittrust.com/xtna-agent:latest
sudo podman pull cr-public.xplicittrust.com/xtna-agent:latest

Configuration

See the xtna-util reference for a complete list of available flags.

The container accepts registration flags directly — no separate xtna-util step is needed.

Use token-based registration for headless or automated deployments:

  1. Go to the Admin Console settings page

  2. Create a new "Asset Creation Token", configure it, download and store it in a secure place

  3. Run the container with the token:

sudo docker run -d --restart unless-stopped \
  --cap-add NET_ADMIN --cap-add NET_RAW \
  --device /dev/net/tun \
  cr-public.xplicittrust.com/xtna-agent:latest \
  -token <token> -domain <tenant-domain>

Registration flags are only used on first start. Once the asset is registered, the configuration is stored inside the container and the flags are ignored on subsequent runs.

Use device flow registration for interactive setups:

sudo docker run -it --restart unless-stopped \
  --cap-add NET_ADMIN --cap-add NET_RAW \
  --device /dev/net/tun \
  cr-public.xplicittrust.com/xtna-agent:latest \
  -user <admin email address>

Open the URL shown in the terminal to authenticate. After successful registration, the container continues running in the foreground.

  1. Go to the Admin Console assets page

  2. Click Create new, fill out the form, click Apply

  3. Click the Download Config icon at the top of the form box:

    Download

  4. Place the configuration file in a host directory and mount it:

sudo mkdir -p /opt/xtna/my-asset
sudo cp xtna-*.xtconfig /opt/xtna/my-asset/
sudo docker run -d --restart unless-stopped \
  --cap-add NET_ADMIN --cap-add NET_RAW \
  --device /dev/net/tun \
  -v /opt/xtna/my-asset:/etc/XplicitTrust \
  cr-public.xplicittrust.com/xtna-agent:latest

Persistent Configuration (Optional)

By default, the agent stores its configuration inside the container. If the container is removed, the configuration (including certificates and registration) is lost and the asset must be re-registered.

To persist configuration across container recreation, mount a host directory:

sudo mkdir -p /opt/xtna/my-asset
sudo docker run -d --restart unless-stopped \
  --cap-add NET_ADMIN --cap-add NET_RAW \
  --device /dev/net/tun \
  -v /opt/xtna/my-asset:/etc/XplicitTrust \
  cr-public.xplicittrust.com/xtna-agent:latest \
  -token <token> -domain <tenant-domain>

Running multiple agents on one host

Each container needs its own configuration directory. Use a unique path per instance:

sudo docker run -d --name office-gw \
  --cap-add NET_ADMIN --cap-add NET_RAW \
  --device /dev/net/tun \
  -v /opt/xtna/office-gw:/etc/XplicitTrust \
  cr-public.xplicittrust.com/xtna-agent:latest \
  -token <token-1> -domain <tenant-domain>

sudo docker run -d --name lab-gw \
  --cap-add NET_ADMIN --cap-add NET_RAW \
  --device /dev/net/tun \
  -v /opt/xtna/lab-gw:/etc/XplicitTrust \
  cr-public.xplicittrust.com/xtna-agent:latest \
  -token <token-2> -domain <tenant-domain>

Registration Flags

Flag Description
-token <token> API key/token for unattended registration
-user <email> Admin email for interactive device flow registration
-domain <domain> Tenant domain (required with -token, optional with -user)
-name <name> Asset name (defaults to container hostname)
-ignore-hostname Do not use the container's real hostname during registration
-api <url> API URL override (for on-premise deployments)

Give the asset a stable name

A container's hostname is its short container ID unless you set one with --hostname, which makes for a meaningless asset identity in the Admin Console and in peer listings. Combine -name with -ignore-hostname so the asset is identified only by the name you choose:

sudo docker run -d --restart unless-stopped \
  --cap-add NET_ADMIN --cap-add NET_RAW \
  --device /dev/net/tun \
  -v /opt/xtna/my-asset:/etc/XplicitTrust \
  cr-public.xplicittrust.com/xtna-agent:latest \
  -token <token> -domain <tenant-domain> \
  -name edge-gw-01 -ignore-hostname

Docker Compose

services:
  xtna-agent:
    image: cr-public.xplicittrust.com/xtna-agent:latest
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - NET_RAW
    devices:
      - /dev/net/tun:/dev/net/tun
    volumes:
      - ./xtna-config:/etc/XplicitTrust
    # Only needed on first run for registration:
    # command: ["-token", "YOUR_TOKEN", "-domain", "your-domain.com"]

Updates

The container includes a self-updating launcher that automatically updates the agent service to the latest version. No manual image pulls are required for agent updates.

To update the launcher itself, pull a new image version:

sudo docker pull cr-public.xplicittrust.com/xtna-agent:latest
sudo docker restart <container-name>
sudo podman pull cr-public.xplicittrust.com/xtna-agent:latest
sudo podman restart <container-name>

Kubernetes

For Kubernetes deployments, use a DaemonSet or Deployment with the required security context:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: xtna-agent
spec:
  replicas: 1
  selector:
    matchLabels:
      app: xtna-agent
  template:
    metadata:
      labels:
        app: xtna-agent
    spec:
      containers:
        - name: xtna-agent
          image: cr-public.xplicittrust.com/xtna-agent:latest
          securityContext:
            capabilities:
              add:
                - NET_ADMIN
                - NET_RAW
          volumeMounts:
            - name: config
              mountPath: /etc/XplicitTrust
            - name: tun
              mountPath: /dev/net/tun
      volumes:
        - name: config
          persistentVolumeClaim:
            claimName: xtna-config
        - name: tun
          hostPath:
            path: /dev/net/tun
            type: CharDevice

Provide the registration token via a Kubernetes Secret or environment variable in your deployment pipeline.

TUN device on Kubernetes

Kubernetes has no direct equivalent of --device, so the example above exposes /dev/net/tun via a hostPath volume of type CharDevice. The node must have the tun kernel module loaded (modprobe tun) and /dev/net/tun present, otherwise the pod will fail to mount the volume. Alternatively, use a TUN/TAP device plugin to avoid hostPath.

Troubleshooting

Container exits immediately

  • Ensure NET_ADMIN and NET_RAW capabilities are granted
  • Ensure the container runs in rootful mode (sudo)
  • Check logs: sudo docker logs <container-name>

"No configuration found" error

  • Provide registration flags (-token or -user) on first run, or mount a directory containing a valid configuration

Tunnel not working / CreateTUN("xt0") failed; /dev/net/tun does not exist

This means the agent registered but cannot create its WireGuard tunnel because the TUN device isn't available inside the container.

  • Confirm the device is present inside the container:
    sudo docker exec <container-name> ls -l /dev/net/tun
    
  • If it's missing, ensure both halves of the TUN prerequisite:
    1. The tun module is loaded on the host: sudo modprobe tun
    2. The container is started with --device /dev/net/tun (recreate the container — devices cannot be added to a running container):
      sudo docker run -d \
        --cap-add NET_ADMIN --cap-add NET_RAW \
        --device /dev/net/tun \
        cr-public.xplicittrust.com/xtna-agent:latest
      
  • If docker run itself fails with error gathering device information ... no such file or directory, the host is missing /dev/net/tun — load the tun module on the host first (step 1).

Reading the agent log

The container's launcher writes a combined log file at /etc/XplicitTrust/logs/xtna-service.log inside the container. This often shows the underlying cause more clearly than docker logs:

sudo docker exec <container-name> tail -f /etc/XplicitTrust/logs/xtna-service.log

VM or container templates

  • When cloning a VM or container that has XplicitTrust installed, make sure to exclude /etc/XplicitTrust/ and /etc/xt-machine-id from the template. Each instance must have its own identity.