Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / docs/TAILSCALE_ACL_TODO.md |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | docs/TAILSCALE_ACL_TODO.md |
| Original SHA-256 | e7bdafa25ed76d1856fc2ac114ed8f56d8ded434e9a14ecf9c515a0db7c3d92d |
| Git blob SHA | 91c3c90634187abc1c64688f485f17f8951989d6 |
| Reading-copy SHA-256 | e7bdafa25ed76d1856fc2ac114ed8f56d8ded434e9a14ecf9c515a0db7c3d92d |
Recovered source transcript
Download complete source text ↓146 lines · 5.3 KB
# TAILSCALE ACL: ONE RULE, FOR JOSEPH TO CLICK
Written 28 July 2026. This is the only open item in Track H item 1 that cannot be
done from a shell, because granting a tailnet policy requires the account owner.
## WHAT IS BROKEN, precisely
The control plane backup runs on megamind-agi, because Bubbles has pg_dump 17.10
and the control plane is PostgreSQL 18.4, and pg_dump refuses a server newer than
itself. So megamind-agi dumps, encrypts, and pushes the artifact to Bubbles.
That push is supposed to go over the tailnet. It does not. Tailscale SSH is
enabled on Bubbles and the policy refuses megamind-agi:
tailnet policy does not permit you to SSH to this node
So `/usr/local/sbin/mm-cp-backup.sh` on megamind-agi uses the LAN address
instead:
DEST_HOST="user@192.168.1.132"
It reaches the same host, the same restricted key, and the same spool directory,
so **the backups are working today**. This is not an outage. It is a dependency
on both machines sitting on one physical LAN, which is exactly the property the
off machine backup was supposed to stop relying on.
## THE FACTS YOU WILL NEED IN THE CONSOLE
tailnet taildf8f6b.ts.net
source node megamind-agi-wsl 100.77.114.40
(the WSL2 instance, NOT the Windows host at
100.69.120.61, which is a separate tailnet node)
destination node bubbles 100.90.97.104
destination user user
what it needs ssh, and nothing else
**The source is the WSL node, not the Windows one.** megamind-agi appears twice
in your tailnet. The backup job runs inside WSL2, which has its own tailscaled
and its own address. An ACL written against the Windows host would look correct
in the console and change nothing.
## THE RULE
Add ONE entry to the `ssh` block of the tailnet policy file. This is the whole
change:
```json
{
"action": "accept",
"src": ["tag:mm-backup-source"],
"dst": ["tag:mm-backup-dest"],
"users": ["user"]
}
```
with the two tags declared and assigned:
```json
"tagOwners": {
"tag:mm-backup-source": ["autogroup:admin"],
"tag:mm-backup-dest": ["autogroup:admin"]
}
```
Tags rather than raw addresses on purpose: an address in a policy file rots the
moment a machine is rebuilt, and this tailnet has already produced one such trap
in the two identities of megamind-agi.
If you would rather not deal with tags, the equivalent by user is:
```json
{
"action": "accept",
"src": ["joseph.w.anady@"],
"dst": ["joseph.w.anady@"],
"users": ["user"]
}
```
That is broader than needed. It permits any of your machines to SSH to any other
as `user`. On a five node personal tailnet that is a reasonable trade; on a
tailnet that later carries a client machine it is not. The tagged version is the
one to prefer.
## CLICK BY CLICK
1. Go to **https://login.tailscale.com/admin/acls** and sign in as
joseph.w.anady@.
2. You land on the **Access controls** tab with the policy file in an editor.
3. If a `"tagOwners"` block already exists, add the two `tag:mm-backup-*` lines
inside it. If it does not, paste the whole `tagOwners` block above at the top
level, beside `"acls"`.
4. Find the `"ssh"` block. If there is none, add `"ssh": [ ... ]` at the top
level and put the rule inside it. If there is one, add the rule as another
entry in its list.
5. Click **Preview** first. It shows which nodes the change affects. Confirm it
names bubbles and megamind-agi-wsl and nothing you did not expect.
6. Click **Save**.
7. Go to the **Machines** tab. Find **megamind-agi-wsl** (address 100.77.114.40,
not the Windows one). Open its row menu, choose **Edit ACL tags**, tick
**tag:mm-backup-source**, save.
8. Do the same for **bubbles**, ticking **tag:mm-backup-dest**.
A machine that gets a tag loses its user ownership for ACL purposes. That is
normal and is why the tags are assigned last, after the rule that uses them
exists.
## HOW TO PROVE IT WORKED, and how to prove the test is real
From megamind-agi inside WSL:
tailscale ssh user@bubbles true ; echo "exit=$?"
Expect `exit=0`. Before the change it prints the policy refusal and a nonzero
exit.
**Then the positive control, which matters more than the success:** the rule
must permit exactly this and not everything. From the same shell:
tailscale ssh user@batman true ; echo "exit=$?"
Expect a REFUSAL. If that also succeeds, the rule is broader than intended and
the tags were not applied, because a rule scoped to two tags cannot reach a third
machine. A grant that permits everything looks identical to a grant that works
until the day it matters.
## AFTER IT IS GRANTED
Tell me, and I will change one line in `/usr/local/sbin/mm-cp-backup.sh` on
megamind-agi:
DEST_HOST="user@192.168.1.132" -> DEST_HOST="user@bubbles"
then run the job and confirm today's control plane dump lands in the set. That is
a one line change to a backup script on a node outside the production API path,
and I will still bring it to you before running it, because Track H deploys are
approved individually.
## IF YOU WOULD RATHER NOT
Reply **skip Tailscale** and I will record KEEP LAN as the decision and close the
item. It is a legitimate choice: the LAN path works, both machines live in one
building anyway, and the honest statement of the residual risk is one sentence.
The risk does not change by being written in a policy file; only the coupling
does.