Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Tailscale integration #2679

Closed
FroggyFlox opened this issue Sep 21, 2023 · 20 comments · Fixed by #2712
Closed

Add Tailscale integration #2679

FroggyFlox opened this issue Sep 21, 2023 · 20 comments · Fixed by #2712
Assignees

Comments

@FroggyFlox
Copy link
Member

@phillxnet, @Hooverdan96, this is a proposal transfer from our previous discussion at rockstor/rockon-registry#338.

From that previous conversation, it seems the agreement was to integrate Tailscale into Rockstor at the "system" level. I'm thus opening this issue accordingly to discuss how best to do that.

Requirements

Going with this route, we would need to install the tailscale package, which provides a (single?) binary tailscale that represents the interface to manage everything. This, in turn, creates a systemd service (tailscaled).
To install this package, we have a few options, but it seems the choice will be very limited. See the official instructions for install on Linux at https://tailscale.com/kb/1031/install-linux/.

They have a curl-piped-to-bash option, but that essentially does the "manual" option below.

The manual option is thus to:

  • add the tailscale repos
  • install the tailscale package
  • enable the tailscaled systemd service
  • "activate" tailscale (tailscale up)

The important point here is the repositories. We can go with:

The official repos always have the latest release available.
The OBS repo unfortunately is a bit out-of-date (a couple minor releases behind) and fails to build for Leap and TW at the moment (issue with GO build dependency).
Given we need add a repository either way, it seems using the official repos is the best choice.

Results

File system level

Using the official install instructions lead to the following new repository...

buildvm155:~ # zypper repos --uri tailscale-stable
Alias          : tailscale-stable
Name           : Tailscale stable
URI            : https://pkgs.tailscale.com/stable/opensuse/leap/15.5/x86_64
Enabled        : Yes
GPG Check      : (r ) Yes
Priority       : 99 (default priority)
Autorefresh    : Off
Keep Packages  : Off
Type           : rpm-md
GPG Key URI    : https://pkgs.tailscale.com/stable/opensuse/leap/15.5/repo.gpg
Path Prefix    : 
Parent Service : 
Keywords       : ---
Repo Info Path : /etc/zypp/repos.d/tailscale-stable.repo
MD Cache Path  : /var/cache/zypp/raw/tailscale-stable

... defined with:

buildvm155:~ # cat /etc/zypp/repos.d/tailscale-stable.repo
[tailscale-stable]
name=Tailscale stable
enabled=1
autorefresh=0
baseurl=https://pkgs.tailscale.com/stable/opensuse/leap/15.5/$basearch
gpgcheck=1
gpgkey=https://pkgs.tailscale.com/stable/opensuse/leap/15.5/repo.gpg

Note the $basearch bit above... whether an aarch64 package exist remains to be verified!

The RPM installs the following files:

buildvm155:~ # rpm -ql tailscale
/etc/default/tailscaled
/lib/systemd/system/tailscaled.service
/usr/bin/tailscale
/usr/sbin/tailscaled
/var/cache/tailscale

The resulting systemd unit is as follows:

buildvm155:~ # systemctl cat tailscaled.service 
# /lib/systemd/system/tailscaled.service
[Unit]
Description=Tailscale node agent
Documentation=https://tailscale.com/kb/
Wants=network-pre.target
After=network-pre.target NetworkManager.service systemd-resolved.service

[Service]
EnvironmentFile=/etc/default/tailscaled
ExecStartPre=/usr/sbin/tailscaled --cleanup
ExecStart=/usr/sbin/tailscaled --state=/var/lib/tailscale/tailscaled.state --socket=/run/tailscale/tailscaled.sock --port=${PORT} $FLAGS
ExecStopPost=/usr/sbin/tailscaled --cleanup

Restart=on-failure

RuntimeDirectory=tailscale
RuntimeDirectoryMode=0755
StateDirectory=tailscale
StateDirectoryMode=0700
CacheDirectory=tailscale
CacheDirectoryMode=0750
Type=notify

[Install]
WantedBy=multi-user.target

Activating Tailscale

Starting the systemd service leads to the following status:

buildvm155:~ # systemctl status tailscaled
● tailscaled.service - Tailscale node agent
     Loaded: loaded (/lib/systemd/system/tailscaled.service; enabled; vendor preset: disabled)
     Active: active (running) since Thu 2023-09-21 11:41:23 EDT; 1s ago
(...)
     Status: "Stopped; run 'tailscale up' to log in"
(...)

We thus need to login.

Login/authentication

I see 3 main options here:

  • cli
  • web portal
  • authkey

CLI

Manually run tailscale up prints a URL to visit and authenticate the device to the user. Simple and robust, but not very practical from Rockstor's perpsective.

Web portal

The tailscale command has a web subcommand that creates a simple web page to login, made for solutions like Rockstor actually. It was recently made more flexible (tailscale/tailscale@9a3bc90) but that last addition has not made it to the RPM release yet, it seems. Note it also remains to be verified whether Rockstor actually needs that last addition.

This could be done as follows:

buildvm155:~ # tailscale web --listen $(hostname):8088
2023/09/21 11:49:25 web server running on: http://buildvm155:8088

Visiting http://buildvm155:8088 brings the following:
image

After clicking on that "Login" button, the machine is added to the user's tailnet and we can see that the activation is complete:

buildvm155:~ # tailscale web --listen $(hostname):8088
2023/09/21 11:49:25 web server running on: http://buildvm155:8088
2023/09/21 11:59:34 Doing edit: MaskedPrefs{WantRunning=true AdvertiseRoutes=[]}
2023/09/21 11:59:34 tailscaleUp(reauth=true, logout=false) ...
2023/09/21 11:59:35 tailscaleUp = (URL true, <nil>)

Note that this tailscale web process is still live! We probably would need to find a way to kill it once we no longer need it.

Authkey

Documentation: https://tailscale.com/kb/1085/auth-keys/
This would require us to ask the user for that key. It would be best not to store this in the database so useful only if we can pass it directly from the UI... I'm unsure whether this would be less secure, though. It also require another configuration step by the user (from Tailscale's website) so it's probably not the best option. It could be worth implementing for users who want that option, however... maybe in a later stage?

State persistence

What happens after that initial activation?

Reboot

Given the tailscaled service is enabled by the install instructions, it is back up and running after a reboot, and the machine is joined to the tailnet:

buildvm155:~ # tailscale status
xxx.xxx.xxx.xxx buildvm155           xxxxx@      linux   -

To turn it OFF, we can run tailscale down:

buildvm155:~ # tailscale down

buildvm155:~ # tailscale status
Tailscale is stopped.

Notably, this is disconnected from the tailscaled service as if we reboot then, the tailscaled.service is back active (as it was enabled), but tailscale is still down:

buildvm155:~ # systemctl is-active tailscaled
active

buildvm155:~ # tailscale status
Tailscale is stopped.

Tailscaled and network connections

The tailscaled service actually "manages" the underlying connection.
Indeed, when turning it ON, we now see a new connection:

buildvm155:~ # systemctl start tailscaled
buildvm155:~ # nmcli
eth0: connected to eth0
(...)

lo: unmanaged
(...)

tailscale0: unmanaged
        "tailscale0"
        tun, sw, mtu 1280

DNS configuration:
(...)

Stopping the tailscaled service and it is now gone:

buildvm155:~ # systemctl stop tailscaled
buildvm155:~ # nmcli
eth0: connected to eth0
(...)

lo: unmanaged
(...)

DNS configuration:
(...)

Overall implementation

My first thought would be to add Tailscale as one of our supported services and would thus be listed as its own on the "System" > "Services" page. We still need to decide what happens when we configure that service, and when we toggle it ON/OFF.

Its configuration window could (at first) simply include a "login" button that would simply run the tailscale web ... command and open a new browser page at the URL that was created by that command. Here, note that if the tailscale web ... command is run again on a machine already joined to the user's tailnet, it doesn't hurt and simply shows a "confirmation" window:
image
We can thus keep showing that "login" button unconditionally (whether the machine is joined or not).

Later, we can think of adding configuration to other Tailscale features such as Taildrop or Tailscale SSH.

Toggling the service ON/OFF could simply run tailscale up/tailscale down.

Note that by default, each machine "expires" on a user's tailnet, which means that each user will need to re-authenticate after some time: https://tailscale.com/kb/1028/key-expiry/. All of this is configurable on Tailscale's website, but this means that we would need to catch this "expired" state to show a nice info windows prompting the user to press again the "Login" button in the Service's configuration window.

What about the tailscale package?

We also need to figure out how/when to add the tailscale RPM repo and install the package.
Should we check for this need and do that when the user clicks on the "login" button in the Service's "configuration " window? I'm still a bit unsure about that. It does seem like adding the repo and the package in the kiwi recipe would be a bit much, however, so we may just do this repo + package install "post Rockstor installation", no?
Also, should we undo this when the user turns the service OFF? This would also require to do the repo and package install when the user turns it back ON, though. It would be nice to offer a way for the user to avoid carrying an extra repository and package if they either don't use the Tailscale service, or no-longer use it.

Hope this helps begin the planning phase for this... I'm personally eager to see that implemented once we figure out how.

@Hooverdan96
Copy link
Member

@FroggyFlox this is very interesting! Thanks for the tons of exploration you've already done. My 2 cents:

  • like the idea of the WebUI System service
  • using the on/off would it not also make sense to also turn on/off the tailscaled corresponding to the tailscale up/down?
  • not storing it in the db would certainly be safer, question is whether the key passing from the UI is "not so safe" like you said. If we can't figure that out, we could issue a warning to be careful about it (and/or offer a cli alternative to safely provide the key for the fully safety conscious.
  • package installation/repo add. I think I would not worry about the uninstall and full removal, but doing the installation "on demand" (could have a warning on the service saying that an additional package will be installed, so if the user is just checking things out, they can choose not to continue. If it becomes undesirable, documentation could show how to get rid of the install .
  • Since tailscale is really geared towards more multi-node connections and for companies, I still think we should offer the pure wireguard piece as a Rock-on (e.g. the one I proposed some time ago), in case the thinking was around "if we implement tailscale who needs wireguard".

I am very interested in how this will turn out, but it looks like you have already isolated the key components that will be required to integrate it into Rockstor's workflows ...

@phillxnet
Copy link
Member

@FroggyFlox Thanks for this excellent synopsis. It would be fantastic to have Tailscale integrated as a 'native' service.
Re:

Its configuration window could (at first) simply include a "login" button that would simply run the tailscale web ... command and open a new browser page at the URL that was created by that command.

Yes, this does seem like a good place to start - and would likely continue to serve us well given their mild progression on enhancing the Web-UI's capabilities. I'm a little reluctant to rely only on that however: but it may serve well just for the initial registration element: removing possibilities for us to be a 'weak-link' in this rather sensitive process.

Here, note that if the tailscale web ... command is run again on a machine already joined to the user's tailnet, it doesn't hurt and simply shows a "confirmation" window:

Nice.

Later, we can think of adding configuration to other Tailscale features such as Taildrop or Tailscale SSH.

Agreed. Little steps as we find what folks want first/most.

Toggling the service ON/OFF could simply run tailscale up/tailscale down.

Can this not be handled via the systemd service they setup?

We also need to figure out how/when to add the tailscale RPM repo and install the package.

Here I'm thinking we add it to our installers and expect it to be in-play from say our next stable release onwards. But check for it's existence during a visit to the service page - and indicate the commands required to add the repo, & install the package, if say the systemd service is not found. That way we account for folks putting in their own tailscale by means other than the upstream repo. I'm not keen on adding repos, other than our own, via Web-UI as it goes.

Should we check for this need and do that when the user clicks on the "login" button in the Service's "configuration " window?
That could work - and avoids wasting time-resources on every visit.

I'm still a bit unsure about that. It does seem like adding the repo and the package in the kiwi recipe would be a bit much, however, so we may just do this repo + package install "post Rockstor installation", no?

I know what you mean, however I think Tailscale would be worth the extra weight. It provides a previously rather difficult service that many folks struggle to achieve. So I think, if licensing is acceptable, we include by default in future installers: but instruct otherwise on repo+package add when systemd service not found.

Also, should we undo this when the user turns the service OFF?

If pre-included we need not. Plus that is again an overreach I think. I.e. we commit to supporting the upstream repo and our own capabilities with it.

It would be nice to offer a way for the user to avoid carrying an extra repository and package if they either don't use the Tailscale service, or no-longer use it.

I know what you mean: however there is a half-way-house here: disable the repository. Installer adds repo in disabled state: ergo no function. But our Web-UI simply enables it. But we then run the risk of folks enabling & running. Then disabling and no longer getting updates to that package(as repo disabled). Should be OK if service is off however. But again I think if we commit to carrying this repo we are good.

or no-longer use it.

On this note: given the powerful/low-level/remove-access nature of TailScale we could have a "Remove" option. Which uninstalls and dumps the repo. Which upon re-visit would result in the my-hand re-instatement text being shown.

@Hooverdan96
Re:

  • using the on/off would it not also make sense to also turn on/off the tailscaled corresponding to the tailscale up/down?

That is why I think we should try to manage via their systemd service predominantly, when-ever we can. As with all our other services: we enable/disable via upstream systemd, but configure by specialised means.

  • not storing it in the db would certainly be safer, question is whether the key passing from the UI is "not so safe" like you said. If we can't figure that out, we could issue a warning to be careful about it (and/or offer a cli alternative to safely provide the key for the fully safety conscious.

Agreed: I like the hand-off to their own Web-UI for this. I know it has less intergration, but in this case it seems worth it as we receive their security fixes/improvements in this area. We do have an existing parallel here with our treatment of the LUKS master password within the Web-UI. So we have an example. But Tailscale offer this facility for a reason is my thinking, plus what @FroggyFlox mentioned about the token approach causing a run-around. Part of this service is the ease of instantiation/initialisation. Key to our remit I think.

  • Since tailscale is really geared towards more multi-node connections and for companies, I still think we should offer the pure wireguard piece as a Rock-on (e.g. the one I proposed some time ago), in case the thinking was around "if we implement tailscale who needs wireguard".

Agreed: I fully intend to add that Rock-on - just been sidetracked for now. So definitely a yes on having both - but I'm not sure I would agree that tailscale is majorly focused on companies. It can be easily used for simply: "I want my NAS available when I'm out and about" type think.


Thanks to both of you for kicking this one off again. I was disheartened to not get more reception from my trivial request to their docker image start script previously: but all in this will be a more robust/flexible approach. And I definitely think we need such a system and to get behind it properly. Hence the addition of the repo hard-wired in the installer suggestion.

Nothing hard-and-fast here on my side: just want to get this underway in a clean a way as possible. I.e. no upstream additions required that may end up unsupported or removed over time.

@FroggyFlox
Copy link
Member Author

Thanks @Hooverdan96 and @phillxnet for the feedback; that clarifies a lot of things!

I'll try to summarize both of your inputs on the key points below but wanted to provide some quick follow-ups on a few points.

@Hooverdan96

I still think we should offer the pure wireguard piece as a Rock-on (e.g. the one I proposed some time ago), in case the thinking was around "if we implement tailscale who needs wireguard".

Agreed... I have dropped the ball completely on the rockon-registry for way too long; my apologies.

@phillxnet

Toggling the service ON/OFF could simply run tailscale up/tailscale down.

Can this not be handled via the systemd service they setup?

The systemd unit only runs the tailscale daemon, which only manages the network bits. The tailscale binary is the only way to manage the "connection to the tailnet". Their docs puts it much better than me, of course:

On most platforms, the CLI is a binary named tailscale (or tailscale.exe) and the more privileged daemon that does all the network handling is called tailscaled (or tailscaled.exe). Note the final d for “daemon”. The majority of the CLI commands accessible via the tailscale command require that the daemon be running on the machine.

Note also:

You should not normally need to manually stop and start the tailscaled process. It should go into an idle state when you use the CLI command tailscale down.

To the best of my knowledge, we thus need to do both: start the daemon, and then run tailscale up. It's a bit like our Rock-on service, in a way: turning the service ON only starts the docker daemon and it's only when we turn a Rock-on ON that a container is running.

Summary

Repo and package

  • From Rockstor's point of view, assume the tailscale package will be provided (either by our installer or by the user in some other way).
  • Still verify presence of the binary during the Service configuration, though, and guide user on how to add it if needed. This could be done maybe from the front-end side? Catching the exception that would inevitably be thrown by the tailscale ... command if it is not found is also an option, but not as good of a user experience than "preemptively" notify the user.

A note on package size (on Leap 15.5):

buildvm155:~ # zypper in tailscale
(...)
The following NEW package is going to be installed:
  tailscale

1 new package to install.
Overall download size: 24.7 MiB. Already cached: 0 B. After the operation, additional 45.1 MiB will be used.

Configuration

  • Add a button to "login" or "authenticate". This will run the tailscale web command.
  • Explore the alternative option to authenticate using an auth key: refer to current LUKS implementation for a potential solution.
  • Explore additional flags that could be of interest to most users for the tailscale up command (https://tailscale.com/kb/1241/tailscale-up/), such as running as exit node, subnet router, enabling tailscale ssh, etc...

Note

The tailscaled daemon (systemd unit) needs to be running in order for any tailscale command to run. This means that we need to ensure that the systemd unit is active in order for that "login"/"authenticate" button to work.

Toggling service ON/OFF

  • Toggling ON will:
    • enable + start the tailscale daemon (systemd unit) or ensure it is enabled and active
    • run tailscale up with any eventual flags set in the Service's config
  • Toggling OFF will:
    • run tailscale down
    • stop and disable the tailscale daemon (systemd unit)

Thank you again for all your time! I'm sure there will be more adjustments to make as development on this begins...

@FroggyFlox
Copy link
Member Author

FroggyFlox commented Sep 23, 2023

A potential interesting way to get the authentication status:

buildvm155:~ # tailscale status --help
USAGE
  status [--active] [--web] [--json]

JSON FORMAT

Warning: this format has changed between releases and might change more
in the future.

For a description of the fields, see the "type Status" declaration at:

https://github.com/tailscale/tailscale/blob/main/ipn/ipnstate/ipnstate.go

(and be sure to select branch/tag that corresponds to the version
 of Tailscale you're running)

Warning

Note the warning about the possibility for changes between these flags.

Logged out

{
(...)
  "BackendState": "NeedsLogin",
(...)
  "Health": [
    "state=NeedsLogin, wantRunning=false"
  ],
(...)
}

Logged in

{
(...)
  "BackendState": "Running",
(...)
  "Health": null,
(...)
}

Logged in and Tailscale up

buildvm155:~ # tailscale status --json | grep BackendState
  "BackendState": "Running",

The output was truncated as it contained much more information about other nodes, etc...

This seems to indicate we can use the BackendState or Health keys to check the need to authenticate.

Note that the tailscale cli seems to use BackendState to check for that: https://github.com/search?q=repo%3Atailscale%2Ftailscale+NeedsLogin&type=code.

@FroggyFlox
Copy link
Member Author

After clicking on that "Login" button, the machine is added to the user's tailnet and we can see that the activation is complete:

buildvm155:~ # tailscale web --listen $(hostname):8088
2023/09/21 11:49:25 web server running on: http://buildvm155:8088
2023/09/21 11:59:34 Doing edit: MaskedPrefs{WantRunning=true AdvertiseRoutes=[]}
2023/09/21 11:59:34 tailscaleUp(reauth=true, logout=false) ...
2023/09/21 11:59:35 tailscaleUp = (URL true, <nil>)

Note that this tailscale web process is still live! We probably would need to find a way to kill it once we no longer need it.

Currently trying to figure out how to deal with that part. I actually realize now that tailscale web is not just launching a web server to login, it is actually running the tailscale up command and thus the machine is added to the tailnet when completing the login process thought that web "portal".

The problem that needs to be solved here, is that if we run a run_command("tailscale web ..."), the function never actually returns as the process remains ongoing. This causes the webUI to wait for it "indefinitely".

@FroggyFlox
Copy link
Member Author

After a couple more tests, I think our best bet is to actually use a combination of tailscale up to trigger first authentication login request, and tailscale status --json to monitor the process. This will indeed allow us to not only get the tailscale login URL but also to detect the various states; see below for details:

State tailscale status --json output CLI notes
tailscaled OFF; not authenticated failed to connect to local tailscaled; it doesn't appear to be running (sudo systemctl start tailscaled ?)
tailscaled ON; not authenticated "BackendState": "NeedsLogin", "AuthURL": "",
tailscaled ON; tailscale up started but not authenticated yet BackendState": "NeedsLogin", "AuthURL": "https://login.tailscale.com/a/xxxxxxxxxx", tailscale up command is still running
tailscaled ON; tailscale up started AND authenticated yet "BackendState": "Running", "AuthURL": "", tailscale up command completed (rc 0)
tailscaled ON; tailscale down "BackendState": "Stopped", "AuthURL": "", tailscale down command completes right away (rc 0)
tailscaled ON; tailscale up again "BackendState": "Running", "AuthURL": "", tailscale up command completes right away (rc 0)

I'm thus thinking we can do without a "login/authenticate" button in the Tailscale service configuration modal and instead show such a button next to the ON/OFF toggle switch IF we detect that authentication is indeed needed.

@FroggyFlox
Copy link
Member Author

FroggyFlox commented Sep 24, 2023

Running tailscale up in the "background" doesn't seem to work for connecting the machine, unfortunately, as although we can go through the login process, the "callback" to the tailscale up command is failing somewhere.

Interestingly, I noticed a working option: using the --timeout flag to tailscale up:

  --timeout duration
        maximum amount of time to wait for tailscaled to enter a Running state; default (0s) blocks forever (default 0s)

The following process works:

  1. run tailscale up --timeout 2s: this generates the auth URL and then terminates. This allows us to retrieve that auth URL, and also does not block the UI indefinitely waiting for tailscale up to complete (which happens only if the login process is completed when --timeout 0s is used).
  2. Use that auth URL retrieved in step 1 to finish the login process.
  3. The machine is authenticated and Tailscale is now up and running.

This has been proven to work from Rockstor's UI as well. 😄

I am still curious about trying to see if we can run tailscale up with no timeout (--timeout 0s) but as a Huey task. If this works, it seems like a more reliable option.

@phillxnet
Copy link
Member

This has been proven to work from Rockstor's UI as well. 😄

Look at you go. This is all very exiting. Looking forward to seeing all this in-action.

@FroggyFlox
Copy link
Member Author

FroggyFlox commented Sep 30, 2023

Tailscale service configuration

Most (all?) settings are defined as optional flags to the tailscale up command.

All supported options

All available flags are described in the corresponding reference: https://tailscale.com/kb/1241/tailscale-

  • --accept-dns Accept DNS configuration from the admin console. Defaults to accepting DNS settings.
  • --accept-risk=<risk> Accept risk and skip confirmation for risk type. This can be either lose-ssh or all, or an empty string to not accept risk.
  • --accept-routes Accept subnet routes that other nodes advertise. Linux devices default to not accepting routes.
  • --advertise-exit-node Offer to be an exit node for outbound internet traffic from the Tailscale network. Defaults to not offering to be an exit node.
  • --advertise-routes=<ip> Expose physical subnet routes to your entire Tailscale network.
  • --advertise-tags=<tags> Give tagged permissions to this device. You must be listed in "TagOwners" to be able to apply tags.
  • --authkey=<key> Provide an auth key to automatically authenticate the node as your user account.
  • --exit-node=<ip|name> Provide a [Tailscale IP][ip-address] or [machine name][machine-name] to use as an exit node. To disable the use of an exit node, pass the flag with an empty argument: --exit-node=.
  • --exit-node-allow-lan-access Allow direct access to the local network when routing traffic via an exit node. Defaults to not allowing direct access to your LAN.
  • --force-reauth Force re-authentication.
  • --hostname=<name> Provide a hostname to use for the device instead of the one provided by the OS. Note that this will change the machine name used in MagicDNS.
  • --login-server=<url> Provide the base URL of a control server instead of https://controlplane.tailscale.com. If you are using [Headscale][headscale] for your control server, use your Headscale instance’s URL.
  • --netfilter-mode (Linux only) Advanced feature for controlling the degree of automatic firewall configuration. Values are either “off”, “nodivert”, or “on”. Defaults to “on”, except for Synology which defaults to “off”. Setting this flag to “off” disables all management of netfilter. Setting to “nodivert” creates and manages Tailscale sub-chains, but leaves the calling of those chains up to the administrator. Setting to “on” means using full management of Tailscale’s rules. Note that if you set --netfilter-mode to “off” or “nodivert”, it is your responsibility to configure the firewall securely for Tailscale traffic. We recommend using the rules installed by --netfilter-mode=on as a starting point.
  • --operator=<user> Provide a Unix username other than root to operate tailscaled.
  • --qr Generate a QR code for the web login URL. Defaults to not showing a QR
  • --reset Reset unspecified settings to default values.
  • --shields-up Block incoming connections from other devices on your Tailscale network. Useful for personal devices that only make outgoing connections.
  • --snat-subnet-routes (Linux only) Source NAT traffic to local routes that are advertised with --advertise-routes. Defaults to sourcing the NAT traffic to the advertised routes. Set to false to disable subnet route masquerading.
  • --ssh Run a Tailscale SSH server, permitting access per the tailnet admin’s declared access policy, or the default policy if none is defined. Defaults to false.
  • --timeout=<duration> Maximum amount of time to wait for the Tailscale service to initialize. duration can be any value parseable by time.ParseDuration(). Defaults to 0s, which blocks forever.

Supported by Rockstor?

We cannot offer all of these options and keep the configuration as user-friendly as Rockstor aims to be. We should thus strive to limit the number of explicit options to those likely to be the most useful/used. We can also add a custom configuration box (as for the Samba service, for instance) that would allow further customization for those interested.

Below can constitute a list of most likely candidates for explicit display:

  • --accept-routes Accept subnet routes that other nodes advertise. Linux devices default to not accepting routes.
  • --advertise-exit-node Offer to be an exit node for outbound internet traffic from the Tailscale network. Defaults to not offering to be an exit node.
  • --advertise-routes=<ip> Expose physical subnet routes to your entire Tailscale network.
  • --exit-node=<ip|name> Provide a [Tailscale IP][ip-address] or [machine name][machine-name] to use as an exit node. To disable the use of an exit node, pass the flag with an empty argument: --exit-node=.
  • --exit-node-allow-lan-access Allow direct access to the local network when routing traffic via an exit node. Defaults to not allowing direct access to your LAN.
  • --hostname=<name> Provide a hostname to use for the device instead of the one provided by the OS. Note that this will change the machine name used in MagicDNS.
  • --reset Reset unspecified settings to default values.
  • --ssh Run a Tailscale SSH server, permitting access per the tailnet admin’s declared access policy, or the default policy if none is defined. Defaults to false.

Some of the above could also be "hidden" behind an "Advanced" or "Expand" button.

@FroggyFlox
Copy link
Member Author

FroggyFlox commented Oct 1, 2023

I now have a first attempt at the Tailscale service configuration modal, complete with validation for most fields and filtering of resulting data sent to the backend:
image

Still needs to add some tooltips and a bit of cosmetic finishing, but that's progress.
Now onto the backend.

@FroggyFlox
Copy link
Member Author

FroggyFlox commented Oct 1, 2023

@phillxnet, @Hooverdan96:
The exit node and subnet_router features require IP forwarding to be enabled on the machine. This is something that Rockstor can take care of for the user, but I was wondering what the best way would be to warn/inform the user about it during the configuration of the Tailscale service.

Docs:
https://tailscale.com/kb/1019/subnets/#step-2-connect-to-tailscale-as-a-subnet-router
https://tailscale.com/kb/1103/exit-nodes/#step-2-advertise-a-device-as-an-exit-node

Should we?

  1. not expose this
  2. expose this in the tooltip: there will already be some information/help on what the setting is so the note about IP forwarding may be lost/masked.
  3. expose this directly on the form as a line akin to: "Note that enabling this setting will enable IP forwarding".
  4. expose this via an "infocircle" icon with a mouse hover mentioning this.
  5. other way(s)

Thanks for letting me know you preferences/ideas.

@phillxnet
Copy link
Member

@FroggyFlox Given, as you intimate, this is a major network change, I vote for:

  1. expose this directly on the form as a line akin to: "Note that enabling this setting will enable IP forwarding".

Mouse overs of any sort are easily missed and better for help prompts: but for significant information we should really expose in some highlighted way within the form itself as per your suggestion in 3. We have such requirements elsewhere also so this can serve as our new way of hiding less in mouse overs. It could also have some kind of warning nature, just to highlight the broad nature (to the entire network) that this type of configuration represents. If I'm reading this correctly that is.

All very exiting this. And the forum looks to be quite comprehensive already. You look to be dropping us into quite the Tailscale support out-of-the-gate which I'm certain will be well received. We have long needed such a feature so it's fun to see this taking shape.

@Hooverdan96
Copy link
Member

I would agree with the third option as well ...

@FroggyFlox
Copy link
Member Author

Thanks a lot to both of you for the prompt and helpful feedback. I will thus follow option 3.

And the forum looks to be quite comprehensive already. You look to be dropping us into quite the Tailscale support out-of-the-gate

Yeah... Adding all these options does add a little bit of complexity (enabling ip forwarding, for instance), but I know that not doing so now would probably mean it would be quite some time until these other options get implemented... I figured I might as well do it in a manner that would be fitting to what users would want to use it from the beginning. I'm still leaving the Taildrop feature our for now, though, as they do list it as an alpha feature for now (https://tailscale.com/kb/1106/taildrop/). It would make a lot of sense for a NAS, though, so I hope to add it soon. It should still be possible for users to enable it though the custom config box anyway.

@Hooverdan96
Copy link
Member

yes. the taildrop indeed looks like a great option for NAS appliances. For Linux "receivers" it still seems a bit to go for Linux environments, as I am not convinced that setting a loop for getting files is the most efficient way of receiving them.

tailscale/tailscale#4230

But some creative users also seemed to have found a workaround before this option was added:

tailscale/tailscale#2312 (comment)

Well, this comment is just for history keeping purposes and only pertains to one aspect of all the stuff you've already uncovered/solved for on Rockstor!!!

@FroggyFlox
Copy link
Member Author

Great finds, @Hooverdan96 , that is very helpful, especially given I'm not familiar with how Taildrop works yet so these two links are very informative to me. A loop checking for files every 5 seconds is interesting... I wonder what cost in resources that takes. Also curious about setting the destination folder... We'll have to see if there's flexibility in that.

@FroggyFlox
Copy link
Member Author

With regards to enabling IP forwarding required by some options:

Tailscale documentation

Recommends:

echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conf

openSUSE docs

I did find some instructions on how to enable IP forwarding in the section related to configuring as a basic router:
https://documentation.suse.com/sles/15-SP5/single-html/SLES-administration/#sec-network-router

Enable forwarding, for example in /etc/sysctl.d/50-router.conf

net.ipv4.conf.all.forwarding = 1
net.ipv6.conf.all.forwarding = 1

It thus seems that using a dedicated .conf file is well supported in openSUSE; I'll try using that approach.

@FroggyFlox
Copy link
Member Author

In Leap 15.5, by default, IP forwarding is turned off:

buildvm155:~ # sysctl -n net.ipv4.ip_forward -n net.ipv6.conf.all.forwarding
0
0

Create the conf files:

echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf

Verify:

buildvm155:~ # cat /etc/sysctl.d/99-tailscale.conf
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

Apply using:

buildvm155:~ # sysctl -p /etc/sysctl.d/99-tailscale.conf 
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

Verify:

buildvm155:~ # sysctl -n net.ipv4.ip_forward -n net.ipv6.conf.all.forwarding
1
1

Reboot, and check again:

buildvm155:~ # sysctl -n net.ipv4.ip_forward -n net.ipv6.conf.all.forwarding
1
1

To revert these changes, we can delete the conf file:

buildvm155:~ # rm /etc/sysctl.d/99-tailscale.conf 

As expected, this does not change the settings:

buildvm155:~ # sysctl -n net.ipv4.ip_forward -n net.ipv6.conf.all.forwarding
1
1

Try reloading all system files:

buildvm155:~ # sysctl --system
* Applying /boot/sysctl.conf-5.14.21-150500.55.19-default ...
kernel.hung_task_timeout_secs = 0
kernel.msgmax = 65536
kernel.msgmnb = 65536
kernel.shmmax = 0xffffffffffffffff
kernel.shmall = 0x0fffffffffffff00
vm.dirty_ratio = 20
* Applying /usr/lib/sysctl.d/50-default.conf ...
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.promote_secondaries = 1
net.ipv4.conf.all.promote_secondaries = 1
net.ipv6.conf.default.use_tempaddr = 1
net.ipv4.ping_group_range = 0 2147483647
fs.inotify.max_user_watches = 65536
kernel.sysrq = 184
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
kernel.kptr_restrict = 1
* Applying /usr/lib/sysctl.d/51-network.conf ...
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
* Applying /etc/sysctl.d/70-yast.conf ...
net.ipv6.conf.all.disable_ipv6 = 0
* Applying /usr/lib/sysctl.d/99-sysctl.conf ...
* Applying /etc/sysctl.conf ...

Unfortunately, no default config for IP forwarding seems to be listed in one of these files, so the settings remain:

buildvm155:~ # sysctl -n net.ipv4.ip_forward -n net.ipv6.conf.all.forwarding
1
1

An option would thus be to manually disable IP forwarding:

buildvm155:~ # sysctl -w net.ipv4.ip_forward=0 -w net.ipv6.conf.all.forwarding=0
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0

buildvm155:~ # sysctl -n net.ipv4.ip_forward -n net.ipv6.conf.all.forwarding
0
0

After a reboot, this persists (IP forwarding is still set to 0).

@FroggyFlox FroggyFlox self-assigned this Oct 16, 2023
@FroggyFlox
Copy link
Member Author

I now have a working (:crossed_fingers:) branch for all this so I'm currently looking into refining/expanding our current unit test coverage for system.services and then I should be ready for branch cleanup & PR preparation.

FroggyFlox added a commit to FroggyFlox/rockstor-core that referenced this issue Oct 18, 2023
Add support for Tailscale as a Service that can configured from
Rockstor's webUI. This commit assumes that the tailscale package is
already installed on the system.

- add "Tailscale: tailscaled" as a new service in the `prep_db.py`
  script with no default configuration (null).
- add new urls endpoints with the same commands as other services:
  config, start, stop.
- unlike other services, add 2 new endpoints ("children" of "config")
  for the login/logout actions.
- add a new TailscaledServiceView to handle these endpoints.
- add logic to support services needing login/authentication.
- add tailscale utils functions to interface with the tailscale cli
  binary.
- add flag to `system.osi.run_command()` to return raw output. This
  commit does NOT change the default. Add relevant unit testing.
- increase default number of services shown in the table to 30.
- change services table column width and center buttons.
- ignore only the static/ folder located in root dir
- add unit testing to `system.services`: only partial coverage, though.
@FroggyFlox FroggyFlox linked a pull request Oct 18, 2023 that will close this issue
phillxnet added a commit that referenced this issue Oct 19, 2023
@FroggyFlox
Copy link
Member Author

Closing as fixed by #2712.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants