Private Cargo registries using Tailscale and Fly.io.
Super Guppy is a turnkey Docker image that makes it easy to run your own alternate Cargo registry, with Ktra as the alternate Cargo registry server. Security and authorization is provided by Tailscale. Any Docker-aware hosting provider should work (even your own machine), but Super Guppy is primarily tested with Fly.io and Docker Desktop.
See this blog post for more information on the genesis of Super Guppy.
The Super Guppy image is entirely self-contained, and configured exclusively through environment variables. Git repository initialization is handled automatically, and Tailscale login is performed using the web link flow. Familiarizing yourself with the Ktra book might be helpful, and reading fasterthanlime's article on private Cargo registries will provide some context on the approach used by Super Guppy. Most of the process is automated by Super Guppy, however, with only Ktra user creation and login unchanged from the normal Ktra flow.
The steps below take you through the initial deployment process on Fly.io, and demonstrate creating a single Ktra user.
There are a number of ways to protect access to the private Cargo registry. The simplest approach is to limit access to all Tailscale "members" (in other words, normal user accounts) and then any tagged devices that are not considered members. The latter covers ephemeral keys for CI systems like GitHub Actions.
The example in this section takes that approach: all members can access the
registry, as well as the github
tag (which is assumed to be the tag you use
for your GitHub Actions). Note that this action assumes that you set the
PRIVATE_REPO_HOSTNAME
to crates
, per the example later on in these setup
directions.
All of these steps are performed in the Tailscale Access Controls console.
-
Add a new tag to the
tagOwners
list for GitHub Actions:// ACL tags. "tagOwners": { "tag:github": [], },
-
Add a section for the
crates
machine to theacls
block:"acls": [ // ... // All users, and GitHub Actions, can access the private Cargo registry. { "action": "accept", "src": ["autogroup:members", "tag:github"], "dst": ["crates:80"], }, // ... ],
-
(Optionally) Add an ACL test to verify that
github
can access the Cargo registry, but not any other ports (such as SSH):"acls": [ // ... // GitHub can access the cargo registry (but not SSH). { "src": "tag:github", "accept": ["crates:80"], "deny": ["crates:22"], }, // ... ],
Launch (but do not deploy) the Fly.io app, and then create a volume for the app (adjust the size as necessary):
$ flyctl launch --build-only --no-deploy --image ghcr.io/malyn/superguppy:latest
$ flyctl volumes create crates_data --size 1
You can choose an app name, or go with the auto-generated default. The app name
will not be used, as the machine will only be accessed via Tailscale using the
PRIVATE_REPO_HOSTNAME
.
Modify the generated fly.toml
file to add the PRIVATE_REPO_HOSTNAME
and
mount the volume that you just created:
# ...
[env]
PRIVATE_REPO_HOSTNAME = "crates"
[mounts]
source="crates_data"
destination="/data"
# ...
Note that the application does not need -- and in fact should not have -- a
public IP address! The only access to the application is via Tailscale. Delete
the entire [[services]]
section from the generated fly.toml
file. Your
fly.toml
file should look similar to this (but with your private app
name):
app = "superguppy"
kill_signal = "SIGINT"
kill_timeout = 5
primary_region = "lax"
processes = []
[build]
image = "ghcr.io/malyn/superguppy:latest"
[env]
PRIVATE_REPO_HOSTNAME = "crates"
[mounts]
source="crates_data"
destination="/data"
Now launch the app:
$ flyctl deploy
Note that you must watch the deployment logs (in the Monitoring tab of the Fly.io dashboard) so that you can join the node to Tailscale during the initial startup procedure:
tailscale-up[pre]: To authenticate, visit:
tailscale-up[pre]:
tailscale-up[pre]: https://login.tailscale.com/a/randomhexetc
tailscale-up[pre]:
Click on that URL to authorize the node and join it to your Tailnet. You may want to disable expiry on that node in Tailscale in order to avoid the need to (manually) re-auth when the key expires.
Follow the instructions in the Ktra book to add users to your private registry. Here is a quick example to get you started:
$ curl -X POST -H 'Content-Type: application/json' -d '{"password":"INSERTPASSWORDHERE"}' https://crates.my-tailnet-name.ts.net/ktra/api/v1/new_user/insertusernamehere
{"token":"yourrandomtokenappearsrighthere"}
Then, in a project where you intend to use the private registry, make sure that
your .cargo/config.toml
includes the name and URL for the registry:
[registries]
mysecretregistry = { index = "https://crates.my-tailnet-name.ts.net/git/index" }
You can then login to the registry using the token printed earlier:
$ cargo login --registry=mysecretregistry yourrandomtokenappearsrighthere
This project adheres to the Contributor Covenant Code of Conduct. This describes the minimum behavior expected from all contributors.
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.