Menu

Secure Egress

Secure Egress routes selected containers through a saved, provider-neutral WireGuard VPN connection. Containers that are not assigned to Secure Egress keep their normal networking.

The normal workflow is:

  1. Add and test a VPN connection.
  2. Assign a stopped container or Blueprint component to that connection.
  3. Start the workload and confirm its protection state.

Before You Begin

Obtain a standard WireGuard configuration from your VPN provider. MacTain does not require a provider-specific client or a Gluetun container.

For complete protected Internet routing, the connection needs:

  • an IPv4 tunnel address;
  • at least one IPv4 DNS server;
  • a peer endpoint and public key; and
  • an allowed IP of 0.0.0.0/0, which represents every IPv4 destination.

The MVP does not provide protected IPv6. Adding ::/0 does not enable it. Check whether your provider supports the inbound-port mode you intend to use; some providers support no inbound port, a fixed port, or NAT-PMP only on specific servers.

Create A VPN Connection

Import A Configuration File

In the app, open Networks → VPN Connections, select Add Connection, and choose Import .conf. Select the WireGuard configuration supplied by your provider, review the parsed address, DNS, endpoint, and allowed IPs, then save the connection.

The equivalent CLI command reads at most 65,536 bytes from one regular file:

mactain vpn import \
  --input ./provider.conf \
  --name media-vpn \
  --json

To import configuration bytes from standard input instead:

mactain vpn import --stdin --name media-vpn < ./provider.conf

Import sends configuration bytes to the Engine, not a host path. Private and optional preshared keys are moved into Engine-owned, write-only secret storage when you save. MacTain does not reveal them again through the app, CLI, status, logs, or diagnostics.

Paste A Configuration

Open Networks → VPN Connections → Add Connection, choose Paste config, and paste the complete standard WireGuard configuration. Paste uses the same parser, preview, validation, and secret-storage workflow as file import.

Review the non-secret fields before saving. If the configuration is rejected, confirm that it contains one [Interface] section, one [Peer] section, an IPv4 address and DNS server, a valid endpoint, and AllowedIPs = 0.0.0.0/0.

Enter Settings Manually

Choose Enter manually when the provider supplies separate values instead of a complete configuration. Enter the connection name, tunnel address, DNS, peer public key, endpoint, allowed IPs, and the private key in the secure field. The private and optional preshared keys become write-only secrets when saved.

For manual CLI creation, save the private key through the normal Secrets workflow, then reference its secret name. Do not pass a raw private key as a command argument.

printf '%s' "$WIREGUARD_PRIVATE_KEY" | \
  mactain secret set media-vpn-private --stdin
 
mactain vpn create \
  --name media-vpn \
  --address 192.0.2.2/32 \
  --dns 192.0.2.53 \
  --peer-public-key AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= \
  --endpoint vpn.example.invalid:51820 \
  --allowed-ip 0.0.0.0/0 \
  --private-key-secret media-vpn-private \
  --inbound-port-mode none

The addresses and endpoint above are reserved documentation values. Replace them with the values supplied by your provider.

Test And Connect

Test a saved connection before assigning applications:

mactain vpn inspect media-vpn --json
mactain vpn test media-vpn --timeout 30 --json
mactain vpn connect media-vpn --timeout 30 --json
mactain vpn status media-vpn --json

The app exposes the same actions from Networks → VPN Connections. A connected VPN confirms the connection is available; check each assigned container's protection state separately after starting it.

test, connect, and disconnect accept timeouts from 1 through 300 seconds. Updating a connection is a full replacement, so provide a complete imported configuration or all manual fields and explicitly retain, replace, or clear stored key references.

Before removing a connection, stop its assigned containers and remove their assignments. Then disconnect and remove it:

mactain vpn disconnect media-vpn --timeout 30 --json
mactain vpn remove media-vpn --yes --json

Choose Inbound-Port Behavior

Inbound-port behavior is selected on the saved VPN connection:

ModeUse it whenAdditional value
noneThe workload needs protected outbound traffic only.None
manual_staticThe provider assigned a stable incoming port.Port from 1 through 65,535
nat_pmpThe provider offers NAT-PMP inside the tunnel.Provider's in-tunnel NAT-PMP IPv4 gateway

NAT-PMP is commonly useful for peer-to-peer applications. One saved connection has one paired TCP/UDP provider port. The first valid assigned consumer and internal-port pair claims that relay until it is released or the lease expires. Other containers assigned to the same VPN connection continue to have protected outbound access, but they cannot claim the occupied incoming port.

MacTain exposes standard NAT-PMP v0 to authorized consumers. It does not need a qBittorrent-, Transmission-, or application-specific adapter. A mapping error affects incoming reachability; it does not create an ordinary outbound route.

Defaults And Behavior

  • A Secure Egress assignment can be changed only while the container is stopped.
  • Blueprint-managed assignments are read-only in Container Settings and must be changed in Blueprint Studio or the Blueprint TOML.
  • local_route_policy defaults to network_scopes.
  • If MacTain cannot establish and verify the protected path, the assigned workload remains stopped or blocked instead of starting through ordinary Internet access.
  • Protected workloads use the VPN connection's approved IPv4 DNS. MacTain does not substitute host or public DNS when that policy cannot be established.
  • Protected IPv6 is unavailable in the MVP.

Practical Use Cases

Assign A Standalone Container

Stop the container, open its settings, and find Secure Egress. Choose the saved connection and local-route policy, then apply the change. If no saved connection exists, Open VPN Connections takes you to the setup workflow.

The equivalent CLI assignment is:

mactain container stop qbittorrent
mactain container secure-egress assign qbittorrent \
  --connection media-vpn \
  --local-route-policy network_scopes \
  --json
mactain container start qbittorrent
mactain container inspect qbittorrent --json

To return a stopped standalone container to ordinary networking:

mactain container secure-egress remove qbittorrent --json

Assign A Blueprint Component

Open Blueprints, select the Blueprint, and choose Open Studio. Add the Secure Egress block to the component and select an existing VPN connection. If none exists, follow the link to Networks → VPN Connections and create it before applying the Blueprint.

Blueprint v2 references an existing connection by name. It never embeds the WireGuard configuration or key material:

[blueprint]
schema = "mactain.blueprint/v2"
name = "media-downloads"
 
[vars]
VPN_CONNECTION = "media-vpn"
 
[[components]]
id = "qbittorrent"
kind = "container"
image = "lscr.io/linuxserver/qbittorrent:latest"
network_scopes = ["media"]
secure_egress = { connection_reference = "${vars.VPN_CONNECTION}", local_route_policy = "network_scopes", inbound_port = { mode = "nat_pmp" } }

connection_reference accepts a saved connection name or one complete ${vars.KEY} reference. inbound_port.mode is none, manual_static, or nat_pmp; manual_static also requires manual_port.

Validate and inspect the plan before starting it:

mactain blueprint validate ./media-downloads.toml
mactain blueprint plan ./media-downloads.toml
mactain blueprint start ./media-downloads.toml
mactain blueprint status ./media-downloads.toml --verbose

Use Network Scopes And Published Ports

Secure Egress controls workload-originated public traffic. It still composes with:

  • same-scope DNS and direct service references;
  • approved communication with other members of the assigned network scopes;
  • reverse-proxy ingress from Caddy or another same-scope proxy; and
  • intentionally configured host-published ports.

With local_route_policy = "network_scopes", a protected qBittorrent component on the media scope can still be reached by ARR services and a Caddy proxy on that scope. local_route_policy = "none" authorizes no scope-local outbound path, although an explicitly published host port can still relay inbound traffic. Neither policy gives the protected workload a second Internet route.

Stop all consumers assigned to the affected saved connection before changing route-affecting assignments or network-scope membership.

Understand Protection Status

MacTain reports three related but independent state families:

StateValuesWhat it tells you
VPN connectiondisconnected, connecting, connected, degraded, errorTunnel lifecycle and health
Container protectionblocked, protectedWhether MacTain currently verifies the complete protected path
Inbound mappingdisabled, requesting, active, renewing, expired, errorProvider-port and consumer-relay status

A connection can be connected while a container remains blocked. An inbound mapping can fail or expire while protected outbound traffic remains available. After the Engine restarts, saved assignments begin blocked until MacTain reconstructs and verifies current runtime state.

Use both connection and container status when diagnosing an assignment:

mactain vpn status media-vpn --json
mactain container inspect qbittorrent --json

Troubleshooting

SymptomWhat to check
Container remains blockedConfirm the connection uses 0.0.0.0/0 and has IPv4 DNS, then run mactain vpn test media-vpn.
Connection is degradedInspect its reported reason and retry the connection test. A degraded state is not permission to bypass protection.
DNS policy conflictsRemove a conflicting per-container nameserver or make it match the VPN connection policy.
Provider endpoint is unavailableThe assigned workload remains without ordinary Internet access. Retry later or update the saved connection while its consumers are stopped.
NAT-PMP is unavailable or expiredConfirm that the provider supports NAT-PMP and that the configured gateway is its exact in-tunnel IPv4 address.
NAT-PMP reports no resourcesAnother consumer/internal-port pair owns the connection's provider port. Release it, wait for expiry, or use another compatible saved connection.
Assignment or network-scope change is refusedStop every assigned consumer on that saved connection, apply the policy change, then start them again.

Safe diagnostic entry points are mactain vpn inspect, mactain vpn status, mactain vpn test, mactain container inspect, and MacTain's existing local log viewer and exports.

Practical Limitations

Important: Secure Egress does not promise anonymity, provider trust or availability, destination privacy, or protection after the host or Engine is compromised. It protects only containers explicitly assigned to a saved connection.

The MVP uses userspace WireGuard and supports protected IPv4 traffic only. Provider support for static ports and NAT-PMP varies. Measured performance is validation evidence, not a customer performance commitment.

Next Steps

Create and test a connection first, then assign one stopped container and confirm it reports protected before adding more consumers.

Related