diff --git a/skills/gateway-domain/SKILL.md b/skills/gateway-domain/SKILL.md index 1e1e5d7..2d0ffe0 100644 --- a/skills/gateway-domain/SKILL.md +++ b/skills/gateway-domain/SKILL.md @@ -1,6 +1,6 @@ --- name: gateway-domain -description: Give a tailnet box a real public HTTPS domain (.gtest.commoninternet.net) by mapping it on the shared testing gateway. Use when an agent needs a publicly reachable URL for a box with no public IP — an OAuth callback, a webhook receiver, a demo link, an ACME challenge. Covers the add/remove tool, where the admin password lives, and the two things that silently break it: your box must carry the tag:notplants-test-server tailnet tag or the ACL blocks the gateway from reaching it, and your box serves the TLS cert rather than the gateway. +description: Give a tailnet box a real public HTTPS domain (.gtest.commoninternet.net) by mapping it on the shared testing gateway. Use when an agent needs a publicly reachable URL for a box with no public IP — an OAuth callback, a webhook receiver, a demo link, an ACME challenge. Covers the add/remove tool, where the admin password lives, and the two things that silently break it: your box must carry a tailnet tag the ACL allows (tag:notplants-test-server or tag:orchestrator) or the gateway cannot reach it, and your box serves the TLS cert rather than the gateway. --- # Giving your box a public domain @@ -18,20 +18,26 @@ python3 engine/tools/gateway-domain.py add myapp That is the whole happy path. The backend defaults to **this box's own tailscale IP**, so run it on the machine that will serve the domain. -## Your box must carry the `notplants-test-server` tag +## Your box needs a tailnet tag the ACL allows -The tailnet ACL only permits the gateway to open connections to nodes tagged -**`tag:notplants-test-server`**. Without it the gateway accepts your mapping and then simply -never connects — which looks like a broken gateway and is not one. Check before you start: +The gateway can only open connections to nodes the tailnet ACL lets it reach. Two tags qualify: + +| tag | who | +|---|---| +| `tag:notplants-test-server` | test servers — the usual case | +| `tag:orchestrator` | orchestrator boxes (added 2026-08-20, verified end to end) | + +Without one of them the gateway accepts your mapping and then simply never connects — which +looks like a broken gateway and is not one. Check before you start: ```bash tailscale status --json | jq -r '.Self.Tags[]?' ``` -If `tag:notplants-test-server` is not listed, add it to that node in the Tailscale admin (a -node's tags are set when it is authenticated, so this may mean re-authenticating it), or map a -backend that already has the tag. `gateway-domain.py` warns when the node it is about to map -lacks the tag, but it cannot see the tags of a backend you name explicitly — that one is on you. +If neither tag is listed, add one to that node in the Tailscale admin (a node's tags are set +when it is authenticated, so this may mean re-authenticating it), or map a backend that already +has one. `gateway-domain.py` warns when the node it is about to map carries neither, but it +cannot see the tags of a backend you name explicitly — that one is on you. The gateway itself is tagged `tag:testing-gateway`; that is the other half of the same ACL rule. @@ -134,10 +140,12 @@ Check in this order — most failures are the last two. 1. `gateway-domain.py list` — is the mapping actually there? 2. `getent hosts myapp.gtest.commoninternet.net` — should be `49.13.156.72`. -3. **Is your node tagged `tag:notplants-test-server`?** (`tailscale status --json | jq -r - '.Self.Tags[]?'`) This is the single most common cause. The gateway is `gateway-test-1` - (`100.91.44.90`), tagged `tag:testing-gateway`; the ACL pairs those two tags, so an - untagged backend is unreachable no matter how correct the mapping looks. +3. **Does your node carry `tag:notplants-test-server` or `tag:orchestrator`?** + (`tailscale status --json | jq -r '.Self.Tags[]?'`) This is the single most common cause. + The gateway is `gateway-test-1` (`100.91.44.90`), tagged `tag:testing-gateway`; the ACL + pairs that with the tags above, so a backend with neither is unreachable no matter how + correct the mapping looks. Quick check from the gateway itself: + `ssh root@49.13.156.72 'timeout 5 bash -c "echo > /dev/tcp//"'` 4. **Is your service actually serving TLS for that name on the backend port?** A backend that speaks plain HTTP on 443, or serves a cert for a different name, fails here and nowhere else. diff --git a/tools/gateway-domain.py b/tools/gateway-domain.py index 65adeeb..57d31c2 100755 --- a/tools/gateway-domain.py +++ b/tools/gateway-domain.py @@ -97,10 +97,10 @@ _ERR = re.compile(r'

(.*?)

', re.S) # /var/lib/tunnel-gateway/tunnel_map.conf can clear. Refuse to create one. _BACKEND = re.compile(r"^(\d{1,3}(?:\.\d{1,3}){3})(?::(\d{1,5}))?$") -# The tailnet ACL only lets the gateway open connections to nodes carrying this tag. +# The tailnet ACL only lets the gateway open connections to nodes carrying one of these. # A mapping to an untagged node is accepted by the gateway and then simply never # connects, which looks like a gateway fault and is not one. -REQUIRED_TAG = "tag:notplants-test-server" +ALLOWED_TAGS = ("tag:notplants-test-server", "tag:orchestrator") def _validate_backend(backend): @@ -144,9 +144,10 @@ def _warn_untagged(exe): tags = _self_tags(exe) if tags is None: return - if REQUIRED_TAG not in tags: + if not any(t in tags for t in ALLOWED_TAGS): print( - f"warning: this node is not tagged {REQUIRED_TAG} (tags: {', '.join(tags) or 'none'}).\n" + f"warning: this node carries none of {' / '.join(ALLOWED_TAGS)} " + f"(tags: {', '.join(tags) or 'none'}).\n" " The gateway will accept the mapping but the tailnet ACL will not let it\n" " reach this box, so no traffic will flow. Add the tag in the Tailscale\n" " admin, or map a backend that already has it.",