Skip to content

Latest commit

 

History

History
96 lines (73 loc) · 3.66 KB

File metadata and controls

96 lines (73 loc) · 3.66 KB

Choosing an exit node

NoTailCar supports three exit-node postures:

  1. No exit node - the allocation joins the Tailnet, but public internet traffic uses the client node's normal route.
  2. Preferred exit node - the guard asks Tailscale to route traffic through one selected exit node.
  3. Fail-closed pinned egress - the worker does not start unless an exit node is selected and the observed public IP matches an allowlist.

Operator checklist

Before setting an exit node in a NoTailCar job:

  • The exit-node device is online.
  • The exit-node device advertises exit-node capability in Tailscale.
  • The auth key or tag used by the Nomad sidecar is allowed by your Tailnet ACL/tag policy.
  • You know whether the workload should fail open or fail closed.
  • If pinned egress matters, you know the public IPs that should be observed from the exit node.

Recommended values

Goal ts_exit_node_primary ts_exit_node_fallbacks ts_require_exit_node ts_egress_expected_ips
Tailnet only, normal internet route "" "" "false" ""
Use one exit node if available "exit-us-east" "" "false" ""
Require one exit node "exit-us-east" "" "true" ""
Try fallbacks "exit-us-east" "exit-us-central,exit-us-west" "true" ""
Pinned public egress "exit-us-east" "exit-us-central" "true" "203.0.113.10,203.0.113.11"

Prefer stable Tailnet node names such as exit-us-east over raw Tailnet addresses in public docs and examples. In private deployments, either form can work as long as Tailscale accepts it for --exit-node.

Use the included var files

NoTailCar includes var files for the main egress-worker example:

# no exit node
nomad job plan \
  -var-file=examples/egress-worker/vars/no-exit-node.hcl \
  examples/egress-worker/egress-worker.nomad.hcl

# one required exit node
nomad job plan \
  -var-file=examples/egress-worker/vars/exit-node-primary.hcl \
  examples/egress-worker/egress-worker.nomad.hcl

# required exit node with fallbacks
nomad job plan \
  -var-file=examples/egress-worker/vars/exit-node-fallbacks.hcl \
  examples/egress-worker/egress-worker.nomad.hcl

# fail closed unless public egress IP is expected
nomad job plan \
  -var-file=examples/egress-worker/vars/required-pinned-egress.hcl \
  examples/egress-worker/egress-worker.nomad.hcl

Use nomad job run with the same -var-file after reviewing the plan.

What the guard does

flowchart TB
  Start["egress-guard starts"] --> Socket["wait for tailscaled socket"]
  Socket --> HasExit{"exit node configured?"}
  HasExit -- no --> Require{"TS_REQUIRE_EXIT_NODE=true?"}
  Require -- yes --> FailNoExit["fail before worker starts"]
  Require -- no --> UpNoExit["tailscale up without exit node"]
  HasExit -- yes --> TryPrimary["try primary exit node"]
  TryPrimary --> PrimaryOk{"success?"}
  PrimaryOk -- yes --> Verify
  PrimaryOk -- no --> TryFallbacks["try fallback nodes in order"]
  TryFallbacks --> AnyOk{"any success?"}
  AnyOk -- no --> FailAll["fail before worker starts"]
  AnyOk -- yes --> Verify["optional public IP verification"]
  UpNoExit --> Verify
  Verify --> Verified{"expected IPs set and matched?"}
  Verified -- mismatch --> FailIP["fail before worker starts"]
  Verified -- none/match --> Ready["write state file and start worker"]
Loading

Runtime verification

After a real allocation runs:

nomad alloc logs <alloc-id> egress-guard
nomad alloc logs <alloc-id> worker

The guard writes a state file inside the allocation:

/alloc/tailscale/egress-selected

It includes the selected exit node, advertised hostname/tags, and readiness timestamp.