Natter Port Mapping: Test STUN Reachability Safely

Natter Port Mapping: Test STUN Reachability Safely

Marcus Reid
October 11, 2026· 10 min read

Natter port mapping attempts to maintain a NAT mapping and forwards to a local service; a STUN address alone cannot prove authenticated external reachability. This untested lab uses Ubuntu 24.04 LTS x64, Python 3.12, Natter 2.2.1, and one TCP endpoint.

Key Takeaways

  • Use only authorized networks, services, and external clients.
  • Serve a fixed harmless response through mutual TLS, never a management interface.
  • Retain one-source firewall access and certificate verification.
  • Stop services and revoke access; mappings can outlast keepalive.

What does a Natter port mapping prove?

STUN reports server-observed addresses. Mapping and filtering are separate and can vary by destination/network; RFC 8489 does not guarantee universal inbound access or CGNAT compatibility.[3]

Define access and cleanup with the ownership checklist. Use the CGNAT explainer and home NAT troubleshooting to interpret prerequisites, not certify reachability.

For outbound privacy, compare VPN exits and inbound forwarding; hosting adds different responsibilities. This lab offers no bypass guarantee, ISP success rate, or third-party scanning.

The VPN routing introduction covers outbound access, not this endpoint's reachability.

What are the Natter NAT requirements and recovery prerequisites?

Use an authorized dedicated Ubuntu host and separate-network external client. Reserve LAN 192.168.50.10, Natter TCP 58961, and loopback TLS 8443; replace the LAN address throughout. Record routes, IPv4/IPv6 addresses, listeners, firewall rules, and the external public source.

Require tested independent management and an already active operator-owned UFW policy denying unsolicited inbound traffic, without competing firewall managers. Otherwise prepare a dedicated host. Never blindly enable UFW over SSH, flush/disable protection, or modify production NGINX.

Natter NAT requirements include permitted TCP STUN/keepalive and compatible mapping/filtering. Multiple NAT layers or destination-dependent behavior can block access. A private WAN address is only a topology clue. Never compensate with DMZ, UPnP, or exposed NAS/RDP/databases.

Step 1: Prepare a short-lived authenticated TLS endpoint

Stop if NGINX, its configuration, service, or mask already exists. On a fresh host, mask before trusted-repository installation to prevent Ubuntu's default-site startup; record added packages/versions.[8][9]

sudo systemctl mask nginx.service
sudo apt update
sudo apt install nginx openssl curl python3
systemctl is-enabled nginx.service
systemctl is-active nginx.service
ss -ltnp 'sport = :80 or sport = :8443 or sport = :58961'
readlink /etc/nginx/sites-enabled/default

Require masked/inactive (nonzero status exits) and unoccupied listed ports. Installation failure or unexpected startup requires cleanup. Only if readlink identifies this installation's /etc/nginx/sites-available/default, run sudo unlink /etc/nginx/sites-enabled/default; otherwise stop. Preserve other sites.

Confirm loopback with ip route get 127.0.0.2. Natter 2.2.1 rewrites exact target 127.0.0.1 to LAN; keep listener and target at 127.0.0.2.[7] Create private two-day credentials:[5][6]

mkdir -m 700 "$HOME/natter-lab"
cd "$HOME/natter-lab"
umask 077
openssl req -x509 -newkey rsa:3072 -nodes -days 2 -keyout ca.key -out ca.crt -subj '/CN=Natter Lab CA'
openssl req -new -newkey rsa:3072 -nodes -keyout server.key -out server.csr -subj '/CN=natter-lab.example'
printf 'subjectAltName=DNS:natter-lab.example\nextendedKeyUsage=serverAuth\n' > server.ext
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 2 -extfile server.ext -out server.crt
openssl req -new -newkey rsa:3072 -nodes -keyout client.key -out client.csr -subj '/CN=Owned external client'
printf 'extendedKeyUsage=clientAuth\n' > client.ext
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAserial ca.srl -days 2 -extfile client.ext -out client.crt
sudo install -d -m 700 /etc/nginx/natter-lab
sudo install -m 600 server.key /etc/nginx/natter-lab/server.key
sudo install -m 644 server.crt ca.crt /etc/nginx/natter-lab/

Keep ca.key private on the operator host. Transfer only ca.crt, client.crt, and client.key through authenticated transport to the authorized client; restrict key permissions. Never publish private keys or store them on shared drives.

Use sudoedit to save /etc/nginx/conf.d/natter-lab.conf: loopback-only, fixed /check response, lab-signed client certificate required. Never reuse a CA trusting other users.[4]

server {
    listen 127.0.0.2:8443 ssl;
    server_name natter-lab.example;
    ssl_certificate /etc/nginx/natter-lab/server.crt;
    ssl_certificate_key /etc/nginx/natter-lab/server.key;
    ssl_client_certificate /etc/nginx/natter-lab/ca.crt;
    ssl_verify_client on;
    ssl_protocols TLSv1.2 TLSv1.3;
    access_log off;
    location = /check { return 200 "natter-lab-ok\n"; }
    location / { return 404; }
}

Inspect all nginx -T output before unmasking: only 127.0.0.2:8443 ssl, no default/IPv6 listener; otherwise stop. Validate, unmask, disable boot startup, start, and inspect ss. The first local request must return the fixed response; the certificate-free request must not.

sudo nginx -T
sudo nginx -t
sudo systemctl unmask nginx.service
sudo systemctl disable nginx.service
sudo systemctl start nginx.service
ss -ltnp 'sport = :80 or sport = :8443 or sport = :58961'
curl --noproxy '*' --cacert ca.crt --cert client.crt --key client.key --resolve natter-lab.example:8443:127.0.0.2 https://natter-lab.example:8443/check
curl --noproxy '*' --cacert ca.crt --resolve natter-lab.example:8443:127.0.0.2 https://natter-lab.example:8443/check

On failure, clean created resources through Step 4; remove any remaining lab mask with sudo systemctl unmask nginx.service. Retain expiry/name verification; never use curl -k.

Step 2: Start a least-privilege Natter STUN test

Inspect the official 2.2.1 script in the private lab directory. Run under your normal account; elevate only configuration/firewall administration.[1][2]

-i selects the address used for STUN/keepalive, while socket forwarding binds all IPv4 addresses. Inspect sudo ufw status verbose and sudo ufw show raw: require every-interface default-deny and no broader 58961 allowance; otherwise stop. Replace OWNED_CLIENT_PUBLIC_IP with the owned external source visible here, allowing only it to the reserved LAN address.[7]

curl --fail --location --proto '=https' --proto-redir '=https' -o natter.py https://github.com/MikeWang000000/Natter/releases/download/2.2.1/natter.py
python3 natter.py --version
sudo ufw allow in proto tcp from OWNED_CLIENT_PUBLIC_IP to 192.168.50.10 port 58961 comment 'natter-lab'
python3 natter.py -i 192.168.50.10 -b 58961 -m socket -t 127.0.0.2 -p 8443 -q

In another terminal, inspect ss -ltnp 'sport = :8443 or sport = :58961': NGINX must use 127.0.0.2:8443, Natter 0.0.0.0:58961. Firewall restrictions must limit source/destination. Add no IPv6 or router forwarding.

-m socket forwards to loopback TLS without NAT-table rules; -q exits on mapping changes. Add no -U, daemon, retry loop, notification script, or startup service. Use configured STUN/keepalive endpoints only where policy permits; otherwise stop.

Privately record public IP/port; the port may differ from 58961. The built-in tester may be blocked by the one-source rule. Judge reachability using the next owned-client request; never widen access for that tester.

Step 3: Verify from your owned external client

Use the separate-network client with its still-authorized public source and private credentials. Same-LAN hairpin requests cannot establish external reachability.

Replace PUBLIC_IP and both PUBLIC_PORT occurrences with the current mapping. --resolve preserves TLS identity natter-lab.example without DNS/hosts edits. Per-request --noproxy '*' leaves existing proxy/VPN configuration unchanged.

curl --noproxy '*' --connect-timeout 10 --max-time 20 --cacert ca.crt --cert client.crt --key client.key --resolve natter-lab.example:PUBLIC_PORT:PUBLIC_IP https://natter-lab.example:PUBLIC_PORT/check
curl --noproxy '*' --connect-timeout 10 --max-time 20 --cacert ca.crt --resolve natter-lab.example:PUBLIC_PORT:PUBLIC_IP https://natter-lab.example:PUBLIC_PORT/check

Require fixed text with authentication and rejection without a client certificate. Inspect HTTP status/body: curl can exit successfully on HTTP rejection. Wrong CA or uncertified server name must fail identity verification; never suppress TLS checks.

How do you interpret failure and mapping changes?

Reader observationSupported conclusionNext action
STUN cannot report a usable addressThis STUN path is unavailableCheck permitted egress and stop if unresolved
Mapping appears, external TCP times outReachability is not establishedInspect source rule, mapping/filtering, and NAT layers
TCP connects but TLS identity failsThe expected service identity is unverifiedCheck SAN, CA, endpoint, and expiry; keep validation enabled
Authenticated request works, missing certificate also worksAccess control is incorrectStop Natter and repair client-certificate enforcement
Mapped address changes and Natter exitsThe recorded endpoint is staleRecheck the network and restart manually only after review

Failed service requests remain failures. Browser STUN/WebRTC address exposure and authenticated inbound access are distinct. Neither proves the other.

This listener is IPv4-only. Audit other services' IPv6 exposure separately; preserve existing protections and investigate only owned resources. This lab does not secure the entire host.

Step 4: Stop, revoke, and verify cleanup

Press Ctrl+C in Natter's foreground terminal. Delete only its source rule, stop/disable the new NGINX, and remove lab configuration/credentials. Require no listed listener:

sudo ufw delete allow in proto tcp from OWNED_CLIENT_PUBLIC_IP to 192.168.50.10 port 58961
sudo systemctl disable --now nginx.service
sudo rm /etc/nginx/conf.d/natter-lab.conf
sudo rm /etc/nginx/natter-lab/server.key /etc/nginx/natter-lab/server.crt /etc/nginx/natter-lab/ca.crt
sudo rmdir /etc/nginx/natter-lab
ss -ltnp 'sport = :80 or sport = :8443 or sport = :58961'

Mappings may linger after keepalive stops. After removing listeners/services, repeat the authenticated external request to the last mapping: no lab response may return. Timeout/refusal proves endpoint unavailability, not instant deletion of router state.

Delete temporary credentials on both hosts, restore only lab changes, and verify management. Leave the default site disabled. Remove only recorded new packages without other users; avoid broad autoremove. If setup remained masked, stop the new service before removing its lab mask. Lab CA/service removal leaves unrelated certificates unaffected.

Before updating, retain the prior script, sanitized invocation, private configuration backup, and baseline. Repeat positive, rejection, changed-address, and cleanup checks; never add infinite unattended retries.

Summary

  • Separate STUN discovery from authenticated external reachability.
  • Keep harmless content, client certificates, and source restrictions.
  • Stop on incompatible NAT or identity failure.
  • Remove listeners, services, rules, credentials; verify external refusal.

FAQ

Does a STUN address mean my port is reachable?

STUN reports an observed address; mapping and filtering can still block your external client's service request.

Can Natter always traverse CGNAT?

No. Compatible mapping/filtering, permitted traffic, and a successful external service check are still required.

Why use mutual TLS for a harmless response?

Harmless content limits exposed data; client certificates restrict recipients of an internet-reachable response.

Does the local test replace the external client?

Local checks verify service/authentication prerequisites. Only a separate-network client establishes external reachability.

Why can Natter's own test report failure?

The one-source rule can reject automatic testers while allowing your owned client's authenticated request.

Does stopping Natter immediately remove the mapping?

Router state may linger. Stop services, revoke rules/credentials, and repeat the external request.

Can I expose my NAS management page instead?

No. Use this short-lived fixed-response TLS endpoint; management interfaces require different permissions and risk analysis.

Disclaimer: Use authorized infrastructure and permitted networks only. These are expected checks, without completed author-network measurements or ISP success-rate claims.

Sources:

  1. Natter 2.2.1
  2. Natter usage
  3. RFC 8489 Session Traversal Utilities for NAT
  4. NGINX ngx_http_ssl_module
  5. OpenSSL req
  6. OpenSSL x509
  7. Natter 2.2.1 implementation
  8. Ubuntu Install nginx
  9. Ubuntu 24.04 systemctl manual

Sources checked 11 October 2026.


Related Articles:

Start your 3-day free trial

Sign up to experience all premium features at no cost.

*Available only to new users. Each user is limited to one trial.

Natter Port Mapping: Test STUN Reachability Safely | AethoVPN