Skip to content

Configuration

pelotech-nat is configured via /etc/pelotech-nat.conf (preferred) or /etc/fck-nat.conf (fallback), a shell-sourced configuration file. All variables are optional.

Configuration File

The file is sourced as a shell script at boot by the pelotech-nat systemd service. The service checks for /etc/pelotech-nat.conf first; if not found, it falls back to /etc/fck-nat.conf. If neither file exists, the instance starts with default behavior (NAT on the primary interface with no EIP or secondary ENI).

Example

# /etc/pelotech-nat.conf (or /etc/fck-nat.conf)

# Associate an Elastic IP at boot
eip_id=eipalloc-0123456789abcdef0

# Enable CloudWatch agent
cwagent_enabled=true
cwagent_cfg_param_name=/pelotech-nat/cloudwatch-config

# Tune connection tracking
nf_conntrack_max=131072
ip_local_port_range="1024 65535"

# Force FIPS endpoints (auto-detected by default in US/GovCloud regions)
fips_endpoints=true

Tailscale Example

# /etc/pelotech-nat.conf — Tailscale subnet router + exit node

# Enable Tailscale
tailscale_enabled=true

# Auth key from SSM (recommended — supports SecureString encryption)
tailscale_auth_key_ssm=/pelotech-nat/tailscale-authkey

# Advertise VPC CIDRs as subnet routes ('auto' detects this VPC's CIDRs from IMDS)
tailscale_advertise_routes=auto

# Advertise as an exit node
tailscale_exit_node=true

Auth key note: Use a reusable, ephemeral, pre-approved, tagged auth key — see Tailscale for the recommended tailnet policy (tag:nat + autoApprovers), key creation, SSM setup, and how nodes behave across restarts, ASG replacements, and shared keys. tailscale up only runs on first login; after changing tailscale_* config, re-run it manually (or tailscale logout and restart the service).

HA note: Multiple NAT instances can advertise the same routes. Tailscale natively supports HA subnet routers — if one instance goes down, traffic automatically fails over to another instance advertising the same CIDRs. The same applies to exit nodes.

IAM note: Tailscale requires ssm:GetParameter (if using tailscale_auth_key_ssm) and ec2:DescribeTags (for hostname resolution from the instance's Name tag) in the instance profile.

FIPS note: Tailscale uses WireGuard (ChaCha20-Poly1305), which is not FIPS 140-3 approved. This only affects the Tailscale overlay VPN tunnel; the system FIPS crypto policy still applies to TLS, SSH, and other protocols. Document as accepted risk if strict FIPS-only is required.

Variables

Variable Description Default
eip_id Elastic IP allocation ID to associate at boot. The instance will call ec2:AssociateAddress with --allow-reassociation. (none)
eni_id Secondary ENI ID to attach at boot (device index 1). Used for dual-ENI setups where private traffic arrives on a separate interface. (none)
interface Override the interface name for NAT rules. Only used if eni_id is not set. (auto-detected from IMDS)
ip_local_port_range Kernel ephemeral port range (sysctl net.ipv4.ip_local_port_range). Widening this increases the number of concurrent NAT connections. (kernel default: 32768 60999)
nf_conntrack_max Maximum number of tracked connections (sysctl net.netfilter.nf_conntrack_max). Increase for high-throughput workloads. (kernel default)
cwagent_enabled Set to any non-empty value to enable the CloudWatch agent. Requires cwagent_cfg_param_name to also be set. (none)
cwagent_cfg_param_name SSM Parameter Store path containing the CloudWatch agent JSON configuration. (none)
fips_endpoints Force AWS FIPS endpoint usage. Set to true to enable, false to disable. When empty (default), FIPS endpoints are auto-enabled in US regions (us-east-1, us-east-2, us-west-1, us-west-2) and GovCloud regions. (auto-detect)
tailscale_enabled Set to any non-empty value to enable Tailscale. The instance will act as a subnet router and/or exit node on the tailnet. (none)
tailscale_auth_key Tailscale auth key (fallback). Used only if tailscale_auth_key_ssm is not set or the SSM fetch fails. (none)
tailscale_auth_key_ssm SSM Parameter Store name containing the Tailscale auth key (fetched with --with-decryption). Preferred over tailscale_auth_key. (none)
tailscale_advertise_routes Comma-separated CIDRs to advertise as a subnet router (e.g. 10.0.0.0/16,10.1.0.0/16), or auto to detect the instance's VPC IPv4 CIDRs (primary + secondary) from IMDS. auto only sees this VPC — list peered VPC, TGW, or on-prem ranges explicitly. (none)
tailscale_exit_node Set to true to advertise this instance as a Tailscale exit node. (none)
tailscale_hostname Override the Tailscale node hostname. Takes precedence over the EC2 Name tag; if both are unset, the instance ID is used. (Name tag or instance ID)
tailscale_snat_subnet_routes Set to false to disable SNAT for subnet routes (preserves tailnet source IPs). Requires a 100.64.0.0/10 route back to this instance in the VPC route tables — with the default (SNAT on) no extra routing is needed. (SNAT on)
tailscale_extra_args Additional arguments passed verbatim to tailscale up. Escape hatch for flags not covered by other variables. (none)

Boot Sequence

The pelotech-nat.service is a Type=oneshot systemd unit that runs after network-online.target:

systemd (multi-user.target)
  └─ pelotech-nat.service (oneshot, after network-online.target)
       ├─ Source /etc/pelotech-nat.conf (or /etc/fck-nat.conf fallback)
       ├─ Fetch IMDSv2 token
       ├─ Query instance-id, region, MAC, ENI-id from IMDS
       ├─ Validate IMDS metadata (exit on failure)
       ├─ Auto-detect FIPS endpoints (based on region or fips_endpoints config)
       ├─ Disable source/destination check (ec2:ModifyNetworkInterfaceAttribute)
       ├─ Associate EIP (if eip_id set)
       ├─ Attach secondary ENI (if eni_id set)
       ├─ sysctl: enable IPv4 forwarding
       ├─ sysctl: tune ip_local_port_range (if set)
       ├─ sysctl: tune nf_conntrack_max (if set)
       ├─ sysctl: disable reverse path filtering (rp_filter=0)
       ├─ Apply nftables masquerade rule on outbound interface
       ├─ sysctl: enable IPv6 forwarding + accept_ra
       ├─ Start CloudWatch agent (if cwagent_enabled set)
       └─ Start Tailscale (if tailscale_enabled set)
            ├─ Resolve auth key (SSM parameter → config fallback)
            ├─ Resolve hostname (Name tag → config fallback → instance ID)
            ├─ Enable and start tailscaled
            └─ tailscale up (--authkey, --hostname, routes, exit node)

Passing Configuration via User Data

Use EC2 user data to write the configuration file before the service starts:

#!/bin/bash
cat <<'CONF' > /etc/pelotech-nat.conf
eip_id=eipalloc-0123456789abcdef0
cwagent_enabled=true
cwagent_cfg_param_name=/pelotech-nat/cloudwatch-config
CONF

When using the terraform-aws-fck-nat module, configuration is passed automatically via the module's variables.

Troubleshooting

Checking Service Status

# Service status and recent logs
systemctl status pelotech-nat

# Full service logs
journalctl -u pelotech-nat --no-pager

# Follow logs in real time (useful during boot debugging)
journalctl -u pelotech-nat -f

Common Failure Modes

Missing configuration file The service runs without a config file but won't associate an EIP or attach a secondary ENI. If you expect these features, ensure /etc/pelotech-nat.conf exists and is populated before the service starts (typically via user data).

IAM permissions errors The instance profile must have permissions for ec2:ModifyNetworkInterfaceAttribute, ec2:AssociateAddress (if using EIP), and ec2:AttachNetworkInterface (if using secondary ENI). Look for AccessDeniedException in the journal output.

ENI already attached to another instance If eni_id is set and the ENI is still attached to a previous instance (e.g., after a replace), the attachment loop will retry for 60 seconds then fail. Detach the ENI from the old instance or use a lifecycle hook to release it before the new instance starts.

IMDS errors The service requires IMDSv2. If IMDS calls fail, check that the instance metadata service is enabled and that the hop limit is sufficient (at least 2 if running inside a container or behind a proxy).

Restarting After Fixing Issues

# Restart the service after fixing config or permissions
sudo systemctl restart pelotech-nat

# Check if it succeeded
systemctl is-active pelotech-nat

The service has a restart limit of 5 attempts within 5 minutes. If the limit is reached, systemctl reset-failed pelotech-nat clears the counter before restarting.