Skip to content

mushrowan/railscale

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

354 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

railscale

if you're reading this, it means i just pushed this because i tend to put off publicising code and i just wanted to get it out there into the world. half of this readme could be completely wrong as it's almost 3am and i am tired and just typed it all up like a moron. i will come back and fix i promise

[!CAUTION] this has not been properly audited. for something that is meant to be secure like a zero trust mesh vpn, that should scream out to you as a red flag. if you are serious about security, you should NOT use this until further notice.

PLEASE PAY ATTENTION TO THE LICENCE. FOR NOW IT IS NOT "OPEN SOURCE". YOU CANNOT USE (YET) USE IT IN A COMMERCIAL SETTING. the reason for this is just an additional measure against some well-meaning fool trying to use this in a security-sensitive scenario. i do not want someone to start using this, get hacked, and leak a bunch of customer data or something. if/when this gets a proper security audit, i will change this to something else more permissive. prolly mit or gpl or whatever.

before you try to be helpful and use your codex credits to audit, yes i have already done this. but this is not a substitute for a proper audit.

a self-hosted tailscale control server written in rust

they told me i couldn't do it

features

  • full ts2021 protocol support - works with official tailscale clients
  • oidc authentication - sign in with google, github, whatever
  • grants policy system - acls but swaggier
  • device posture - restrict access based on device attributes and geolocation
  • embedded derp server - built-in relay with rate limiting
  • taildrop - file sharing between nodes (same-user and cross-user)
  • app connectors - route traffic to specific domains via designated nodes
  • tailnet lock - cryptographic verification of node keys
  • ephemeral nodes - auto-cleanup of temporary devices
  • rest api - manage users, nodes, keys
  • nixos module - first-class nix support with comprehensive options
  • too much other stuff - it's 2am and i cannot be bothered to remember

quickstart

nix

# flake.nix
{
  inputs.railscale.url = "github:mushrowan/railscale";

  outputs = { self, nixpkgs, railscale }: {
    nixosConfigurations.myserver = nixpkgs.lib.nixosSystem {
      modules = [
        railscale.nixosModules.default
        {
          services.railscale = {
            enable = true;
            settings = {
              server_url = "https://vpn.example.com";
              base_domain = "ts.example.com";

              # optional: oidc login
              oidc = {
                issuer = "https://accounts.google.com";
                client_id = "your-client-id";
                client_secret_path = "/run/secrets/oidc-secret"; # use sops-nix
              };
            };
          };
        }
      ];
    };
  };
}

config file

# config.toml
server_url = "https://vpn.example.com"
listen_addr = "0.0.0.0:8080"
base_domain = "ts.example.com"

[database]
db_type = "sqlite"
connection_string = "/var/lib/railscale/db.sqlite"

[database.sqlite]
write_ahead_log = true  # recommended for production

[derp.embedded_derp]
enabled = true
region_id = 900
region_name = "my-derp"

# optional oidc
[oidc]
issuer = "https://accounts.google.com"
client_id = "your-client-id"
client_secret = "your-client-secret"

docker

docker pull littleropeep/railscale:latest

or with docker compose:

# compose.yaml
services:
  railscale:
    image: littleropeep/railscale:latest
    ports:
      - "8080:8080"
      - "3478:3478/udp" # stun - nat traversal for embedded derp relay
    volumes:
      - ./config.toml:/etc/railscale/config.toml:ro
      - railscale-data:/var/lib/railscale
    environment:
      - RAILSCALE_LISTEN_ADDR=0.0.0.0:8080
    restart: unless-stopped

volumes:
  railscale-data:

or skip the config file and use env vars:

services:
  railscale:
    image: littleropeep/railscale:latest
    ports:
      - "8080:8080"
      - "3478:3478/udp" # stun - nat traversal for embedded derp relay
    volumes:
      - railscale-data:/var/lib/railscale
    environment:
      - RAILSCALE_SERVER_URL=https://vpn.example.com
      - RAILSCALE_BASE_DOMAIN=ts.example.com
      - RAILSCALE_DATABASE_URL=sqlite:///var/lib/railscale/db.sqlite
      - RAILSCALE_LISTEN_ADDR=0.0.0.0:8080
    restart: unless-stopped

volumes:
  railscale-data:

if you want to build the image yourself (requires nix):

nix build .#docker && ./result | docker load

https / reverse proxy

railscale speaks plain http. for production, stick a reverse proxy in front:

caddy (easiest - automatic https):

vpn.example.com {
    reverse_proxy localhost:8080
}

nginx:

server {
    listen 443 ssl;
    server_name vpn.example.com;
    ssl_certificate /etc/letsencrypt/live/vpn.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/vpn.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

why no built-in acme? reverse proxies are battle-tested, more flexible, and keep railscale simple. caddy literally does automatic https with zero config.

connecting clients

# on your tailscale client
tailscale up --login-server=https://vpn.example.com

cli

# run the server
railscale serve --config /etc/railscale/config.toml

# manage users
railscale users list
railscale users create alice

# manage preauth keys
railscale preauthkeys create --user alice --reusable
railscale preauthkeys list

# manage nodes
railscale nodes list
railscale nodes delete 12345

# reload policy without restart
railscale policy reload

# tailnet lock
railscale lock init              # initialise tailnet lock
railscale lock status            # show lock status
railscale lock sign <node-key>   # sign a node key
railscale lock disable           # disable tailnet lock

policy (grants)

{
  "groups": {
    "group:dev": ["alice@example.com", "bob@example.com"]
  },
  "grants": [
    { "src": ["group:dev"], "dst": ["autogroup:member"], "ip": ["*"] }
  ],
  "ssh": [
    {
      "action": "accept",
      "src": ["group:dev"],
      "dst": ["tag:servers"],
      "users": ["root"]
    }
  ]
}

note on persistence: policy updates via the REST API or CLI are held in memory only. they're lost on restart. for persistent policy, use a policy file with --policy or the nixos policyFile option. runtime updates are for testing/development.

taildrop

file sharing between devices on the same tailnet. same-user transfers work automatically. cross-user transfers need an explicit policy grant:

{
  "src": ["bob@"],
  "dst": ["alice@"],
  "app": [{ "name": "cap/file-sharing-target" }, { "name": "cap/file-send" }]
}
# send a file
tailscale file cp ~/photo.jpg myphone:

# receive files
tailscale file get ~/Downloads/

enable in config:

taildrop_enabled = true

tailnet lock

nodes can't join unless signed by trusted nodes - protects against compromised control server.

# initialise lock (generates signing key)
railscale lock init

# check status
railscale lock status

# sign a node
railscale lock sign nodekey:abc123...

# disable lock (requires disablement secret from init)
railscale lock disable

status

my current status is that i am in bed and i am comfy, thank you for asking

okay but actually, the status is that i'm going to continue actively working on this. there are many things indeedy which need to be fixed, ironed out, added.

current limitations:

  • ssh policy works-ish but i'll be honest i only realised that i forgot to properly implement it today and then madly wrote it all in like 4 hours so yeah it might be broken
  • probably like 50 million other things

cool stuff:

  • nixos vm tests that make sure core functionality works
    • can i just quickly say, nixos vm tests are insane. like they are nuts. you're telling me i can just simulate a little cluster of servers or whatever and then tell them exactly what output/exit codes i expect and then qemu just figures it all out? that's insaneo style

license

PolyForm Noncommercial License 1.0.0

see the top of the readme for the reason for this. tldr; i will change this in the future but for now i do not what to be worrying about some silly person leaking social security numbers with a piece of software i made

see also

  • headscale - the og self-hosted tailscale server (go)
  • tailscale - the real deal
  • shellac - the cherished friend who kept telling me to keep going while i built this

the name

the railscale fandom is dying. reply "i love to get railed" to affirm your love for railscale

About

tailscale control server in rust.

Resources

License

Stars

Watchers

Forks

Packages

No packages published