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 uponly runs on first login; after changingtailscale_*config, re-run it manually (ortailscale logoutand 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 usingtailscale_auth_key_ssm) andec2:DescribeTags(for hostname resolution from the instance'sNametag) 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.