tailscale / golink

A private shortlink service for tailnets
BSD 3-Clause "New" or "Revised" License
1.25k stars 79 forks source link

Cannot create a link from a subnet-routed host #60

Closed cerebrate closed 1 year ago

cerebrate commented 1 year ago

I have both hosts directly on my tailnet and those connected to it via subnet routes. When I use one of the former to create a link or access link details, it works great, but if I try and use one of the latter, it fails with the error:

404 Not Found: no match for IP:port

although using the links works fine. I suspect this is because it can't find the Tailscale user from the address of indirectly connected hosts.

Is there a way around this? Perhaps by also checking the subnet route list if the host IP isn't in the tailnet range?

willnorris commented 1 year ago

That's right... it can't determine a Tailscale user for the subnet-routed host, and currently go links are generally always owned by a user. I say "generally" because we ran into a similar situation for tags created from tagged devices. In that case (#37), we allowed the go link to be created, but to always allow a human user to take over ownership. We could potentially do something similar here, though it likely wouldn't be as straightforward (if easily possible at all).

cerebrate commented 1 year ago

So, I took a quick whack at this.

Allowing for an assumption (namely, that any request that gets to golink that doesn't come from a tailnet address must be from a subnet-routed host, otherwise it couldn't have got there), detecting them isn't that hard, as I can check for just that by comparing the request address to the known tailnet prefixes. Add a "fake" user along the same lines as "tagged-devices", and I have something that seems to work, at least on my system. I submitted my changes as #61 .

(Unfortunately, what I really wanted to do - namely, find the device offering the subnet routes, then assign the user that device belongs to as the owner of golinks from that subnet route - seems to be rather more problematic, at least without making golink require an API key, and I'd prefer not to do that. The same with giving tagged devices the tag for that specific device as its user rather than the generic "tagged-devices", which is something else I'd find useful. Maybe in the future I'll have time to figure this one out.)

DentonGentry commented 1 year ago

I'm not sure this approach really works well for this. Part of the value of services on the tailnet is that they can know identities based on the underlying tailnet.

cerebrate commented 1 year ago

I don't disagree with that at all; this is just my first cut to get things working.

(That's also why I would like to be able to get the identity attached to the host providing the subnet routes and the specific tags for tagged devices into this and other services, as a step in the right direction identity-wise, although at the moment this seems difficult without a separate API key.)

willnorris commented 1 year ago

@cerebrate, can you say a little bit more about your use case. Do the subnet routed devices belong to individual users or are they servers or headless machines?

I ask because, as Denton noted, there is an assumption that the application knows who the user is. If these are user devices coming through the subnet router, the behavior is going to be that they could initially create a go link, but then never be able to manage it. Or all subnet-routed devices would be able to manage it because they would all appear as the same user (?? I'm not sure which would happen). Either way, it's not a great experience and at that point you could just use any other golink service that doesn't use Tailscale identities.

One scenario I could potentially imagine us supporting is that links can be created from any device that can reach the service, but if there is no identifiable user, it is saved with an "unknown user" owner (or just an empty owner?). That would mean that subnet routed hosts could create but not edit links. golink would treat them as unowned links and any user on the tailnet would be able to take over ownership. This is basically what happens with links created from tagged nodes today, we would just be making it a little more generic to work with any non-user device. I still don't love it, but it would be a single solution that handles all cases where a specific user can't be identified.

cerebrate commented 1 year ago

All of the above, in this case.

I have something of an odd hybrid model - for reasons involving some devices that can't run Tailscale (microcontrollers, and the like) and not wanting to be completely ridiculous with the soft limits - in which my home network is both entirely subnet-routed (via a Tailscale instance on my border router) and some devices, my primary individual-user machines, are also directly on the tailnet.

So those ones work fine, but as I bounce around between servers, containers, and IoT widgets, sometimes I want to create a quick link, [or use some other Tailscale-id service], without having to find a way to put all sixty-plus of them directly on the tailnet - especially since in most of those cases, 99% of their traffic is entirely local and doesn't need to hit the tailnet.

So for my own usage, I can live with the scenario above; I'd prefer getting the identity of the subnet router (or the tag[s] of the tagged device) on the record to keep track, but with setups like mine, I can make the assumption that "he who routes a device owns whatever it does" fairly safely.

(That would also cover likely future scenarios in which I want to share part of my network with other people, and so distinguishing between the identities of the subnet routers would be useful where devices behind the subnet routers are concerned, but in which I can deal with just knowing that those devices ultimately belong to bar@, not foo@.)

cerebrate commented 1 year ago

...some of the above went a bit beyond the scope of golink, sorry, but it's given me some ideas for some services of my own and now I'm starting to think about how much identification I could theoretically get for subnet-routed and tagged devices in such a service.

(Being able to get the actual tags for a device would be really useful, for example, since I use them to distinguish containers, cloud hosts, lambda functions, GitHub actions, etal. But at that point we're really beyond the scope of this issue/project.)