stun

module
v1.0.15 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 7, 2026 License: Apache-2.0

README

EndlessNet STUN

endlessnet-stun is a standalone STUN product. EndlessNet integrates it through standard UDP Binding to discover public UDP mappings; other compatible clients can use it independently. It does not depend on the EndlessNet control plane, database, credentials, or internal Go packages.

This repository owns the STUN source, tests, immutable artifacts, publication, container image, and service configuration examples. Production inventory, host access, activation, rollout, and rollback belong exclusively to Infrastructure under D-014/D-026. The public history in this repository is authoritative. Endpoint selection and client-side Binding behavior are owned by their respective consumers.

Boundaries

The service provides unauthenticated STUN Binding over UDP, per-source-IP token-bucket limiting, structured JSON logs, Prometheus metrics, and HTTP health/readiness endpoints. It does not provide TURN, relay user traffic, authorization, billing, coordinator access, PostgreSQL access, DNS discovery, Anycast, or Kubernetes deployment.

Protocol support is documented in docs/supported-protocol.md. The stable network contract is a standard STUN Binding request and response; repository releases do not change public DNS names or UDP ports.

Documentation

Configuration

Flags override environment variables. All configuration is validated before listeners start.

Environment variable Flag Default
ENDLESSNET_STUN_ADDRS --addr required, comma-separated
ENDLESSNET_STUN_METRICS_ADDR --metrics-addr 127.0.0.1:9090
ENDLESSNET_STUN_RATE_LIMIT_PER_SECOND --rate-limit-per-second 20
ENDLESSNET_STUN_RATE_LIMIT_BURST --rate-limit-burst 40
ENDLESSNET_STUN_LOG_LEVEL --log-level info

Explicit IPv4 and IPv6 listen addresses use separate socket families. Use 0.0.0.0:3478,[::]:3478 to serve both families on the same port; IPv6 must be available on the host. An empty host (:3478) lets Go select the socket family.

Use the safe example in configs/stun.example.env; no secrets are required. Validate a host configuration without opening sockets:

set -a
. /etc/endlessnet-stun/stun.env
set +a
/opt/endlessnet-stun/current/endlessnet-stun --check-config

Local development

go run ./cmd/endlessnet-stun \
  --addr 127.0.0.1:3478 \
  --metrics-addr 127.0.0.1:9090

Run the complete quality gate:

./scripts/verify.sh

It runs module verification, unit and process-level integration tests, race tests, go vet, formatting checks, and builds the service and smoke-test binaries. A real local Binding request can be checked with:

./scripts/smoke-test.sh \
  --stun-addr 127.0.0.1:3478

Container

docker build -t endlessnet-stun:dev .
docker run --read-only --cap-drop ALL --network host \
  -e ENDLESSNET_STUN_ADDRS=0.0.0.0:3478 \
  -e ENDLESSNET_STUN_METRICS_ADDR=127.0.0.1:9090 \
  endlessnet-stun:dev

The image runs as a non-root user. deploy/docker/docker-compose.example.yml pins an exact example version; operational deployments must never use latest.

Local health and observability

  • GET http://127.0.0.1:9090/healthz reports process health.
  • GET http://127.0.0.1:9090/readyz returns success only while every configured UDP listener is active. All UDP and HTTP sockets are bound before serving starts.
  • GET http://127.0.0.1:9090/metrics returns Prometheus text format for the host-local Alloy agent.
  • GET http://127.0.0.1:9090/revisionz returns the running executable's SHA-256 digest, commit, version, and build date for Infrastructure diagnostics. These fields are also present in stun_build_info; they contain no client or deployment configuration data.

The HTTP listener is required to use a loopback address. Health, readiness, and metrics are not part of the public network contract; only STUN on UDP port 3478 is exposed externally.

Metrics include stun_requests_total, stun_responses_total, stun_invalid_requests_total, stun_rate_limited_total, stun_errors_total, stun_active_listeners, stun_request_duration_seconds, and stun_build_info. Labels are limited to listener, result, and address family; source IP is never a label. Successful client addresses and packet bodies are not logged. Rejected request logs redact the remote address.

Release process

The single .github/workflows/ci.yml workflow runs checks on pull requests and commits to main. Tags matching vMAJOR.MINOR.PATCH run its release job. A tag is accepted only when its commit is reachable from origin/main and GitHub associates that commit with a merged pull request whose base is main. The workflow repeats the quality gate, requires all three product E2E jobs from a successful main CI run for the exact commit, builds Linux AMD64 and ARM64 server and smoke-test binaries, creates SHA256 checksums, and publishes a GitHub Release and GHCR image. Major and minor image tags are mutable aliases; consumers pin an image digest or an exact binary with its checksum.

Update CHANGELOG.md on a feature branch, merge it through a pull request and CI into main, and only then create a signed or protected version tag. Direct-push and unmerged commits are rejected by the release provenance check.

Commit-addressed production publication

The manual publish operation in .github/workflows/ci.yml accepts one full commit SHA from main through the commit_sha input. The default manual operation, check, only runs checks. Publication requires a successful ci.yml run for that application commit and for the exact main commit containing the publisher workflow. A successful run uploads one Actions artifact named endlessnet-stun-<commit_sha> containing:

  • one archive with AMD64 and ARM64 server and smoke-test binaries, the systemd unit, and license notices;
  • the archive SHA-256 checksum;
  • a schema-v1 publication manifest binding the archive to the producer commit, CI runs, and publication run;
  • an in-toto/SLSA provenance statement and its Sigstore/Rekor bundle.

The producer workflow and Sigstore certificate identity now use .github/workflows/ci.yml@refs/heads/main. Infrastructure consumers must trust this exact path for new artifacts; the manifest schema remains unchanged. Publisher scripts are checked out at the workflow revision separately from the application source, so publishing an earlier application commit uses the verified current publisher.

The workflow verifies the archive layout, checksum, manifest, provenance, and Rekor entry before upload. It has no target, inventory, production credentials, or host access. This commit-addressed artifact is the STUN producer side of D-014/D-026; the public semver Release and GHCR image remain a separate supported contract.

Publication ends after upload and verification. It does not accept targets or inventory, read deployment secrets, connect to hosts, activate a release, perform rollout, or rollback. Infrastructure consumes the immutable artifact through separate STUN desired state and owns the complete production lifecycle. STUN is not a component of the common EndlessNet server released manifest.

Troubleshooting

  • Configuration failure: run endlessnet-stun --check-config with the environment file loaded.
  • UDP timeout: verify DNS, host firewall, security group, and 3478/udp; TCP reachability does not prove STUN reachability.
  • Readiness failure: inspect systemctl status endlessnet-stun and confirm at least one configured UDP address can bind.
  • Rate limiting: inspect stun_rate_limited_total and adjust both rate and burst deliberately.
  • Production lifecycle issues: inspect the separate Infrastructure STUN desired state and rollout; this repository only publishes and verifies artifacts.

EndlessNet clients continue to obtain endpoints such as stun1.endlessnet.ru:3478 from signed network maps or control-plane configuration. The client has no dependency on this repository's Go packages or release version.

Product E2E

CI runs stun-e2e-linux, stun-e2e-windows, and stun-e2e-container on GitHub-hosted runners for PRs, main pushes, and manual runs. These required checks exercise only this product, using independent UDP wire vectors and real server/smoke processes. Linux containers use a local host network. No public production endpoint, EndlessNet Client, or control plane is needed.

Run go test -tags=e2e -count=1 ./test/e2e. Local IPv6 is required; missing capabilities fail instead of silently skipping. Optional STUN_E2E_EVIDENCE selects an output directory for logs and binary identities; STUN_E2E_IMAGE selects a prebuilt Linux image. CI preserves JSON test results and identities. The publisher also verifies the exact commit's successful product E2E jobs. This evidence does not constitute production or full EndlessNet acceptance.

Directories

Path Synopsis
cmd
endlessnet-stun command
internal
service
Package service manages the lifetime of the standalone HTTP and UDP listeners.
Package service manages the lifetime of the standalone HTTP and UDP listeners.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL