Wiredoor Ingress 1.8.0
Overview
This guide covers launching and verifying Wiredoor Ingress 1.8.0 on Ubuntu 24.04 from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Hardened WireGuard VPN & VPN, Proxy and Remote Access AMIs listing on AWS Marketplace. You choose it as the software version when you subscribe and launch. The listing's guide hub describes the other VPN, proxy and remote access servers in the listing.
Wiredoor is a self-hosted "ingress as a service" platform. Machines that cannot accept inbound connections, such as a home server, an office NAS or a Kubernetes node behind NAT, run a small Wiredoor client that dials out to your EC2 instance over WireGuard.
Wiredoor's built-in nginx then publishes their web services on the instance's public address, and their TCP services too if you enable them. HTTPS comes from Let's Encrypt once you attach a domain. You manage nodes, services and domains from Wiredoor's web dashboard.
What you get:
- Rebuilt with patched dependencies and no source change. The upstream 1.8.0 container image ships body-parser 1.20.8, which lets a single unauthenticated request crash the Wiredoor API (CVE-2026-111181). It also carries PCRE2, expat and certifi advisories and an oauth2-proxy built on an older Go. This image is built at SecureAnyCloud from the unmodified v1.8.0 source and Dockerfile, with only dependency raises inside upstream's own version ranges. It uses the same base images upstream's release used (with today's Alpine packages) and oauth2-proxy 7.15.5 compiled on Go 1.27, and the unused npm and pip package managers are removed. The recipe and every version are in /usr/share/doc/sac-wiredoor/.
- Nothing pre-made. The admin password and the TLS certificate are generated on your instance at first boot. Wiredoor generates its WireGuard server key and its session-signing secret on its first start against an empty data directory.
- A dashboard that answers only on your instance's own names. A request for any other host name on port 443 is refused. A browser request from another site that tries to change something is also refused.
- Fenced WireGuard peers. A connected node can reach Wiredoor's published services and other nodes, and the internet if you enable that for the node. It cannot reach the instance metadata service, your VPC, the instance itself, or Wiredoor's container-only ports.
- TCP services off until you turn them on (Step 9), so only SSH, HTTP, HTTPS and WireGuard are open.
- The container ships on the AMI. The rebuilt image is stored on the AMI as a checksummed archive and loaded at first boot, so the instance needs no access to a container registry to start.
- Wiredoor 1.8.0 (
wiredoor.service), running in Docker CE with exactly three published ports: TCP 80 (HTTP, redirected to HTTPS, and Let's Encrypt validation), TCP 443 (the dashboard and your published services) and UDP 51820 (WireGuard, which your nodes dial). - Docker CE and containerd from Docker's own signed repository, kept up to date by Ubuntu's unattended upgrades.
sac-wiredoor-guard.service, the firewall rule that keeps containers away from the instance metadata service.- Helpers:
wiredoor-credential-check(an end-to-end sign-in test),wiredoor-imds-guard(shows the metadata guard) andwiredoor-render-nginx(refreshes the dashboard's host names).
Default user: ubuntu | Architecture: x86_64 | Root volume: 12 GiB gp3
Security posture
- Per-instance secrets. The admin password (28 random characters) is generated at first boot and written to
/opt/wiredoor/.envand/root/wiredoor-credentials.txt, both readable by root alone. The container receives the password from that file, never on a command line. Wiredoor refuses to start with its published sample password, and the sample sign-inadmin@example.comdoes not exist on this image. The WireGuard server key, the session-signing (JWT) secret and the TLS key are unique to each instance. - End-entity certificate. Until you attach a domain with a Let's Encrypt certificate, HTTPS uses a self-signed certificate generated on your instance, whose names are the instance's own addresses. It cannot sign other certificates. Do not install it in any trust store. Compare its SHA-256 fingerprint instead (Step 5).
- Fail-closed start. Wiredoor cannot start before first boot has generated its secrets and loaded the metadata guard. Its unit is gated on a ready marker and a preflight check. If first boot fails, Wiredoor stays stopped.
- Host pinning and cross-site protection. The dashboard is served only for this instance's addresses and names, plus any you add in Step 8. Any other
Hostor TLS server name on port 443 gets no answer. Browser requests that carry another site'sOrigincannot change anything or open a WebSocket. The API also requires its bearer token on every call. - Real client addresses. nginx tells Wiredoor and your published services the address it actually received the connection from, never an
X-Forwarded-Forvalue a client made up. The sign-in rate limit (5 attempts per 10 minutes per address) therefore cannot be bypassed. - Metadata service protection. Wiredoor's nginx connects to your services from inside the container, so a service whose target is the metadata address would be a way to reach your instance's IAM role credentials. A Docker firewall rule (
DOCKER-USER) drops everything containers send to169.254.0.0/16,168.63.129.16and the IPv6 metadata addresses. Docker, containerd and the Wiredoor unit are also denied the metadata service by systemdIPAddressDeny. - Container hardening. The container runs on a private Docker bridge network with only its three ports published. It is not privileged, has no Docker socket and has capped logs. Every Linux capability is removed except the five it needs: network administration for WireGuard, binding ports 80 and 443 as a non-root user, and the user switch and file access its WireGuard helper uses. Docker listens on its local socket only.
- Hardened SSH. Key-only login, no root login, no X11 forwarding.
Prerequisites
Before you deploy this image, make sure you have:
- An AWS account subscribed to the listing, and an EC2 key pair in your Region.
- A VPC subnet with internet access and a public IP. An Elastic IP is recommended (Step 10). Your nodes dial UDP 51820 on this address, and your visitors use TCP 80 and 443.
- On each machine you want to publish from, the Wiredoor client (
wiredoor-cli, see https://www.wiredoor.net/docs) or any WireGuard client. - Optional: a DNS name you control, for Let's Encrypt certificates on your published services.
Connecting to your instance
- SSH user: ubuntu
- Dashboard: https://<instance-address>/
- Sign-in username: admin@wiredoor.local
- Sign-in password: in /root/wiredoor-credentials.txt (root only)
- WireGuard endpoint: UDP 51820 on the instance's public address
Launch the instance
Option A: AWS Marketplace console
- Open the SecureAnyCloud Hardened WireGuard VPN & VPN, Proxy and Remote Access AMIs listing on AWS Marketplace. Choose View purchase options, then Subscribe. One subscription covers every version in the listing.
- When the subscription is active, choose Launch your software.
- Choose One-click launch from AWS Marketplace, or Launch from EC2 Console for the full launch wizard. In Version, choose Wiredoor Ingress 1.8.0 on Ubuntu 24.04, then choose your Region.
- Pick an instance type (
m5.largeis recommended, and the software charge is $0.04 per vCPU per hour) and your key pair. - Set the security group. The listing's default group allows SSH (TCP 22), HTTP (TCP 80), HTTPS (TCP 443) and WireGuard (UDP 51820) from anywhere. TCP 80 and 443 must stay open to your visitors, and UDP 51820 to your nodes. Restrict TCP 22 to your own IP address.
- Choose a subnet with internet access and a public IP. Leave the metadata defaults, because the image already requires IMDSv2. Then choose Launch.
Option B: AWS CLI
Run the following commands on your own workstation. They create a security group and then launch the instance. Replace the AMI ID with the one shown for your Region on the Marketplace launch page. Replace the VPC, subnet, key pair and address with your own.
SG=$(aws ec2 create-security-group --group-name wiredoor \
--description "Wiredoor ingress" --vpc-id vpc-0123456789abcdef0 \
--query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 22 --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 80 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 443 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol udp --port 51820 --cidr 0.0.0.0/0
aws ec2 run-instances \
--image-id ami-0123456789abcdef0 \
--instance-type m5.large \
--key-name my-key-pair \
--security-group-ids "$SG" \
--subnet-id subnet-0123456789abcdef0 \
--associate-public-ip-address \
--metadata-options HttpTokens=required,HttpPutResponseHopLimit=1 \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=wiredoor}]'HttpPutResponseHopLimit=1 is optional but recommended. It stops anything running inside a container on the instance from obtaining an IMDSv2 token at all, as an extra wall behind the firewall rule described above. The image itself already requires IMDSv2.
Connect and read your credentials
Connect as ubuntu with the key pair you chose:
ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>First boot finishes within a couple of minutes of launch. It generates the admin password and the TLS certificate, loads the Wiredoor container, applies the metadata guard and starts Wiredoor, which then creates its WireGuard server key.
Your details are in /root/wiredoor-credentials.txt, which only root can read:
sudo cat /root/wiredoor-credentials.txt# Wiredoor 1.8.0 - generated on first boot (sac). Unique to this instance; keep it safe. WIREDOOR_URL=https://203.0.113.25/ WIREDOOR_USERNAME=admin@wiredoor.local WIREDOOR_PASSWORD=<28 random letters and digits> WIREGUARD_ENDPOINT=203.0.113.25:51820/udp TLS_CERT_SHA256=36:CD:EA:...(32 hexadecimal pairs)...:59:64:F0 ...Confirm Wiredoor is running
Three services make up the appliance: Docker, the metadata guard and Wiredoor itself. Check that all three are active:
systemctl is-active docker.service sac-wiredoor-guard.service wiredoor.serviceactive active activeExactly these ports are open on the instance: SSH, and the three ports Docker publishes for the container. The local DNS resolver also listens, on loopback only.
sudo ss -tulnp | grep -E ':(22|80|443|51820) 'udp UNCONN 0 0 0.0.0.0:51820 0.0.0.0:* users:(("docker-proxy",...)) udp UNCONN 0 0 [::]:51820 [::]:* users:(("docker-proxy",...)) tcp LISTEN 0 4096 0.0.0.0:80 0.0.0.0:* users:(("docker-proxy",...)) tcp LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",...),("systemd",...)) tcp LISTEN 0 4096 0.0.0.0:443 0.0.0.0:* users:(("docker-proxy",...)) tcp LISTEN 0 4096 [::]:80 [::]:* users:(("docker-proxy",...)) tcp LISTEN 0 4096 [::]:22 [::]:* users:(("sshd",...),("systemd",...)) tcp LISTEN 0 4096 [::]:443 [::]:* users:(("docker-proxy",...))Wiredoor's API (port 3000), its metrics endpoint and its log forwarder listen only inside the container. Confirm that the WireGuard interface is up inside the container:
sudo docker exec -u root wiredoor wg show wg0 | head -3interface: wg0 public key: kQ4m...(44 characters)...= private key: (hidden)Run the credential check
Run wiredoor-credential-check to sign in through Wiredoor's own nginx the way the dashboard does. It checks that unauthenticated and forged requests are refused, that a wrong password is refused, and that your password works. The check uses two of the five sign-in attempts allowed per 10 minutes.
sudo wiredoor-credential-checkWIREDOOR_CREDCHECK_OK credential round trip through Wiredoor's nginx: 401 anonymous, 401 forged token, 401 wrong password, this instance's password mints a JWT that authenticates /api/auth/meSign in to the dashboard
Before you type the password, check that the certificate your browser receives is your instance's. Print its fingerprint on the instance.
sudo openssl x509 -in /etc/wiredoor/tls/cert.pem -noout -sha256 -fingerprintsha256 Fingerprint=36:CD:EA:5A:...:B6:59:64:F0Open https://<public-ip>/ in your browser. The browser warns that the certificate is self-signed. Open the certificate details and compare the SHA-256 fingerprint with the one above (and with TLS_CERT_SHA256 in the credentials file). Continue only if they match. Do not add the certificate to a trust store.
Sign in as admin@wiredoor.local with the password from the credentials file. The home page opens.
The Client / Nodes page lists the machines connected to this server. A new server has only Wiredoor's built-in local node, Wiredoor_Local, which is the server itself.
The Domains page lists the DNS names you publish services on. There are none yet.
Connect a node
On the machine whose services you want to publish (not on the EC2 instance), install the Wiredoor client (wiredoor-cli, https://github.com/wiredoor/wiredoor-cli) and connect it to this server.
In the dashboard, choose Create Node on the home page (or Add Client on the Client / Nodes page) and give the node a name. Copy the command that the node's page shows: wiredoor connect --url=https://<public-ip> --token=.... The token is a credential for that node, so treat it like a password. Wiredoor's documentation covers each platform: https://www.wiredoor.net/docs.
If you prefer a plain WireGuard client, choose Download Config on the node's page instead. The file contains the node's private key, so keep it on that machine only.
Nodes dial the address in WIREGUARD_ENDPOINT. The node appears as online in the dashboard once its tunnel is up.
Publish a service
On a connected node's page, choose Add Service for an HTTP service. Give it a name and the port the service listens on (on the node). Then give it either a path on this server (for example /grafana) or a domain (Step 8).
Wiredoor writes the nginx configuration and reloads it. The service is then reachable at https://<public-ip>/<path> or https://<domain>/.
You can limit each service to client IP ranges, give it an expiry, or protect it with an OAuth2 sign-in. Wiredoor runs oauth2-proxy for the sign-in once you configure a provider in /opt/wiredoor/.env.
Domains and Let's Encrypt
To publish a service on a domain:
- Point the domain's DNS
Arecord at the instance's public IP. Use an Elastic IP so the address does not change.
- Set
ADMIN_EMAILin/opt/wiredoor/.envto your own address, which is the Let's Encrypt registration contact. Then restart Wiredoor:
sudo nano /opt/wiredoor/.env
sudo systemctl restart wiredoor- In the dashboard, choose Domains, then Add Domain, and choose Let's Encrypt for its certificate. Validation uses port 80, which this image answers for every domain.
The dashboard itself answers only on the instance's own addresses and names. To reach it on a domain too, add the name to /etc/wiredoor/admin-hosts (one per line) and restart Wiredoor.
echo "wiredoor.example.com" | sudo tee -a /etc/wiredoor/admin-hosts
sudo systemctl restart wiredoorList the names the dashboard currently answers on:
sudo grep -o 'server_name [^_][^;]*;' /etc/wiredoor/nginx/default.confserver_name 127.0.0.1 localhost 203.0.113.25 172.31.93.109 ec2-203-0-113-25.compute-1.amazonaws.com ip-172-31-93-109.ec2.internal;Any other name on port 443 gets no answer. Test this with a made-up host name:
curl -ks -o /dev/null -w '%{http_code}\n' -H 'Host: not-this-server.example' https://127.0.0.1/ || true000TCP services (off by default)
Wiredoor can also publish raw TCP services on a range of public ports. This is off on this image, so only the four ports above are open. To turn it on:
- Add a port range, a list of single ports, or both to
/opt/wiredoor/.env, for exampleTCP_SERVICES_PORT_RANGE=32760-32767. - List the same ports in
/etc/wiredoor/tcp-ports, one port or range per line (for example32760-32767), so that the container publishes them. - Open the ports in the instance's security group, then run
sudo systemctl restart wiredoor.
Elastic IP or a DNS name
The WireGuard endpoint in each node's configuration is the address that first boot found (VPN_HOST in /opt/wiredoor/.env). Attach an Elastic IP before you connect nodes. Then point Wiredoor at it and refresh the dashboard's host names:
sudo sed -i 's/^VPN_HOST=.*/VPN_HOST=<elastic-ip-or-dns-name>/' /opt/wiredoor/.env
sudo wiredoor-render-nginx
sudo systemctl restart wiredoorWhat a WireGuard node can reach
Upstream Wiredoor forwards anything a node sends through the server. On this image, Wiredoor's own WireGuard rules, set at first boot, fence a node's traffic.
A node can reach other nodes and, if you select Send all internet traffic through the VPN for the node, the internet. It cannot open connections over the tunnel to the server itself (Wiredoor's API, metrics and log ports), to the metadata service, or to private address ranges (your VPC, the Docker network and this instance). Wiredoor's nginx still reaches every node to publish its services.
Show the rules inside the container:
sudo docker exec -u root wiredoor iptables -S FORWARD-P FORWARD ACCEPT -A FORWARD -i wg0 -o wg0 -j ACCEPT -A FORWARD -d 10.0.0.0/8 -i wg0 -j DROP -A FORWARD -d 172.16.0.0/12 -i wg0 -j DROP -A FORWARD -d 192.168.0.0/16 -i wg0 -j DROP -A FORWARD -d 100.64.0.0/10 -i wg0 -j DROP -A FORWARD -d 169.254.0.0/16 -i wg0 -j DROP -A FORWARD -d 168.63.129.16/32 -i wg0 -j DROP -A FORWARD -i wg0 -j ACCEPT -A FORWARD -o wg0 -j ACCEPTConnections that a node opens to the server itself over the tunnel are limited in the same way, to replies to Wiredoor's own connections and ping only:
sudo docker exec -u root wiredoor iptables -S INPUT-P INPUT ACCEPT -A INPUT -p udp -m udp --dport 51820 -j ACCEPT -A INPUT -i wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A INPUT -i wg0 -p icmp -j ACCEPT -A INPUT -i wg0 -j DROPThese rules are Wiredoor's own WG_POST_UP_SCRIPT / WG_POST_DOWN_SCRIPT settings in /opt/wiredoor/.env. Wiredoor stores them when it first starts.
If you want a node to reach something in your VPC, publish that VPC service through Wiredoor's local node instead (Step 7). The service is then subject to the same access rules as every other service.
Metadata access is blocked
Run wiredoor-imds-guard to show the firewall rules that drop everything containers send to the metadata addresses, with packet counters:
sudo wiredoor-imds-guard showChain DOCKER-USER (1 references) pkts bytes target prot opt in out source destination 0 0 DROP 0 -- * * 0.0.0.0/0 168.63.129.16 /* sac-wiredoor-imds */ 0 0 DROP 0 -- * * 0.0.0.0/0 169.254.0.0/16 /* sac-wiredoor-imds */ ...Wiredoor cannot run without the guard. wiredoor.service requires sac-wiredoor-guard.service, and when Docker restarts, the guard is applied again before Wiredoor. A service whose target is the metadata address is refused when you create it, because Wiredoor cannot connect to it.
Docker, containerd and the Wiredoor unit are also denied the metadata service directly:
systemctl show -p IPAddressDeny docker.service containerd.service wiredoor.serviceIPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32 IPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32 IPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32How the fail-closed start works
wiredoor.service starts only after first boot has written /var/lib/sac/data/wiredoor.bootstrap-ready. Its preflight (/usr/local/sbin/wiredoor-preflight) refuses to start the container unless all of the following hold:
- The environment file is root-only and holds a real password.
- The end-entity certificate exists.
- The metadata guard is loaded.
- The pinned image is the one that was loaded.
The pinned image identity is in /etc/wiredoor/wiredoor.conf. List the loaded image:
sudo docker image ls --format '{{.Repository}}:{{.Tag}} {{.ID}}'sac/wiredoor:1.8.0 d70dfa311b7aUpdates and logs
Ubuntu's unattended upgrades install security updates daily, from the Ubuntu archive and from Docker's repository. The image adds Docker to the allowed origins in /etc/apt/apt.conf.d/51sac-docker-unattended. When Docker is upgraded, Wiredoor restarts with it and the metadata guard is applied again first.
The Wiredoor software itself never changes automatically. When SecureAnyCloud publishes a newer Wiredoor version in this listing, move to it by launching the new version and reconnecting your nodes.
Wiredoor's own log goes to the journal and to Docker's capped log:
sudo journalctl -u wiredoor.service --no-pager -n 5 | cut -c1-120Find first-boot progress in sudo journalctl -u wiredoor-firstboot.service.
Security recommendations
- Restrict TCP 22 in the security group to your own address. Keep TCP 80 and 443 open to your visitors and UDP 51820 to your nodes.
- Keep the admin password private. If you change it, use a long, random one (
ADMIN_PASSWORDin/opt/wiredoor/.env, thensudo systemctl restart wiredoor). - Treat node connection commands, access tokens and downloaded WireGuard configurations as secrets. Remove nodes you no longer use.
- Use domains with Let's Encrypt certificates for anything your users open in a browser.
- Launch with
HttpPutResponseHopLimit=1(Step 1, Option B) for an extra metadata wall. - OAuth2 sign-in and go-jose advisory GHSA-mgcc-cww5-fp2q (CVE-2026-116587). The oauth2-proxy 7.15.5 on this image still links go-jose v3, which has no fix for this denial-of-service advisory. A crafted RSA key in a JSON Web Key Set (JWKS) can stall oauth2-proxy. On this image, oauth2-proxy does not run, and no identity provider, issuer or JWKS URL is configured until you set one up. If you enable OAuth2 for a service, point OAUTH2_PROXY_PROVIDER and any OIDC issuer or JWKS URL only at a trusted HTTPS endpoint that you control, or at your own identity provider. Never use an address that someone else can change, because only that provider's key set can trigger the advisory. SecureAnyCloud will publish a new version of this listing once oauth2-proxy moves off go-jose v3.
- Keep unattended upgrades on, so that Docker and the operating system stay patched.
Support and licensing
INFLADOOR LLC licenses Wiredoor under the Apache License 2.0, and its TERMS.md asks that it be used lawfully. oauth2-proxy is MIT-licensed, certbot is Apache-2.0, nginx is BSD-2-Clause, Node.js is MIT and Fluent Bit is Apache-2.0.
The rebuild recipe (both Dockerfiles, the lock-file changes, and oauth2-proxy's go.mod and go.sum) and the provenance of every component are in /usr/share/doc/sac-wiredoor/. The source is available from the upstream projects (https://github.com/wiredoor/wiredoor) and from SecureAnyCloud on request.
SecureAnyCloud support covers deploying and operating this image. Contact sales@secureanycloud.com.
Wiredoor is a trademark of INFLADOOR LLC. WireGuard is a registered trademark of Jason A. Donenfeld. SecureAnyCloud is not affiliated with or endorsed by INFLADOOR LLC or the WireGuard project.
Deploy on AWS
Launch Wiredoor Ingress 1.8.0 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com