{"id":22685815,"url":"https://github.com/hellodword/tailscale-derp-client-verifier","last_synced_at":"2026-02-26T08:39:54.218Z","repository":{"id":239797695,"uuid":"799729420","full_name":"hellodword/tailscale-derp-client-verifier","owner":"hellodword","description":null,"archived":false,"fork":false,"pushed_at":"2025-03-31T11:30:35.000Z","size":32,"stargazers_count":9,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-10T12:31:30.532Z","etag":null,"topics":["derp","derper","self-hosted","tailscale"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hellodword.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-05-13T01:23:25.000Z","updated_at":"2025-04-06T10:29:14.000Z","dependencies_parsed_at":"2024-05-15T12:35:54.954Z","dependency_job_id":"c738497e-2cc3-4358-b654-84fd201a7839","html_url":"https://github.com/hellodword/tailscale-derp-client-verifier","commit_stats":null,"previous_names":["hellodword/tailscale-derp-client-verifier"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hellodword%2Ftailscale-derp-client-verifier","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hellodword%2Ftailscale-derp-client-verifier/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hellodword%2Ftailscale-derp-client-verifier/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hellodword%2Ftailscale-derp-client-verifier/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hellodword","download_url":"https://codeload.github.com/hellodword/tailscale-derp-client-verifier/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248623272,"owners_count":21135218,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["derp","derper","self-hosted","tailscale"],"created_at":"2024-12-09T22:17:44.499Z","updated_at":"2026-02-26T08:39:49.169Z","avatar_url":"https://github.com/hellodword.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tailscale-derp-client-verifier\n\n## Why\n\nBecause I want to run a Tailscale DERP server without having to trust it.\n\n1. `Tailscale` is `end-to-end encrypted` by design[^1]\n\n2. An insecure DERP server or connection is still secure for DERP users; there are no MITM issues[^2]\n\n3. `--verify-clients` requires adding this DERP server as one of your Tailscale nodes. In my opinion, this is risky, even with ACLs.\n\n4. `--verify-client-url` allows derper to check an external URL to permit access[^3]\n\n## Build\n\n```sh\n# with latest Go\ngo build -x -v -trimpath -ldflags \"-s -w\" -buildvcs=false -o tailscale-derp-client-verifier .\n```\n\n## Usage\n\n1. **On the trusted Tailscale node machine**: get the nodes list from your trusted homelab server or laptop, and sync the `nodes.json` to the DERP server machine:\n\nYou can achieve this in many ways. I do this with rclone, systemd timer, and S3 object storage::\n\n```sh\n#! /usr/bin/env bash\nset -e\nset -x\n\n[ -f \"$RCLONE_CONFIG_FILE\" ]\n[ -d \"$WORK_DIR\" ]\n[ -n \"$TAILSCALE_S3_REMOTE_NAME\" ]\n[ -n \"$TAILSCALE_S3_BUCKET\" ]\n\ncd $WORK_DIR\n\ntailscale status --json | jq '[recurse | objects | with_entries(select(.key == \"PublicKey\")) | .[]] | sort' \u003e \"temp.nodes.json\"\n\njq -e . \"temp.nodes.json\"\n\nif [ \"$(cat \"nodes.json\" || true)\" != \"$(cat \"temp.nodes.json\")\" ]; then\n  rclone --config \"$RCLONE_CONFIG_FILE\" --contimeout=3m --timeout=10m --checksum copyto \\\n    \"temp.nodes.json\" \"$TAILSCALE_S3_REMOTE_NAME:$TAILSCALE_S3_BUCKET/nodes.json\"\n  mv \"temp.nodes.json\" \"nodes.json\"\nfi\n```\n\n2. **On the DERP server machine**: run `tailscale-derp-client-verifier`\n\nIf you're not syncing the `nodes.json` with S3 object storage, use the `-path` argument:\n\n```sh\n/path/to/tailscale-derp-client-verifier -path /path/to/nodes.json\n```\n\nI use S3, so I:\n\n```sh\n. .env\n\n/path/to/tailscale-derp-client-verifier\n```\n\n```ini\n# .env file\nS3_ACCESS_KEY_ID=\nS3_SECRET_ACCESS_KEY=\nS3_ENDPOINT=\nS3_REGION=\nS3_BUCKET=\nS3_FILE=nodes.json\nS3_FORCE_PATH_STYLE=\n```\n\n3. **On the DERP server machine**: deploy the derper\n\n```sh\nderper \u003c... other args\u003e --verify-clients=false --verify-client-url-fail-open=false --verify-client-url=http://127.0.0.1:3000\n```\n\n[^1]: https://tailscale.com/security\n[^2]: https://github.com/tailscale/tailscale/issues/12107#issuecomment-2106233579\n[^3]: https://github.com/tailscale/tailscale/pull/11193\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhellodword%2Ftailscale-derp-client-verifier","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhellodword%2Ftailscale-derp-client-verifier","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhellodword%2Ftailscale-derp-client-verifier/lists"}