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

chore: align with https://tailscale.com/kb/1107/heroku #1

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 112 additions & 44 deletions bin/heroku-tailscale-start.sh
Original file line number Diff line number Diff line change
@@ -1,57 +1,125 @@
#!/usr/bin/env bash

set -e
set -eo pipefail

if [ -z "$TAILSCALE_AUTH_KEY" ]; then
echo "[tailscale]: Will not be available because TAILSCALE_AUTH_KEY is not set"
exit 0
readonly TAILSCALE_DISABLE="${TAILSCALE_DISABLE:-0}"
readonly TAILSCALE_DEBUG="${TAILSCALE_DEBUG:-false}"
if [ "${TAILSCALE_DEBUG}" = "true" ]; then
set -ux
fi
readonly TAILSCALE_AUTH_KEY="${TAILSCALE_AUTH_KEY:-}"
readonly ALL_PROXY_IP_PORT="localhost:1055"
readonly TAILSCALED_CLEANUP="${TAILSCALED_CLEANUP:-}"
readonly TAILSCALED_VERBOSE="${TAILSCALED_VERBOSE:--1}"
readonly TAILSCALE_ADVERTISE_TAGS="${TAILSCALE_ADVERTISE_TAGS:-}"
readonly TAILSCALE_ADDITIONAL_ARGS="${TAILSCALE_ADDITIONAL_ARGS:---timeout=15s}"
# --accept-dns=true
# --accept-routes=false
# --advertise-exit-node=false
# --shields-up=false
# --timeout=15s

wait_for_tailscale_running() {
timeout=5 # Timeout in seconds
interval=0.5 # Interval between checks
elapsed=0

while [ "$elapsed" -lt "$timeout" ]; do
state=$(tailscale status -json | jq -r .BackendState)
if [ "$state" = "Running" ]; then
return 0
fi
sleep "$interval"
elapsed=$(echo "$elapsed + $interval" | bc)
done

return 1
# Usage:
# cmd="$(stringify_args $*)"
stringify_args() {
local old_ifs; old_ifs="$IFS" IFS=' ';
local args; args="$*"; IFS="$old_ifs"
echo "$args"
}
topic() { echo "-----> $(stringify_args "$@")" >&2 ; }
info() { echo " $(stringify_args "$@")" >&2 ; }
debug() { if [ "$TAILSCALE_DEBUG" = "true" ]; then echo -e "[DEBUG] $(stringify_args "$@")" >&2 ; fi ; }
error() { echo " ! $(stringify_args "$@")" ; >&2 exit 1 ; }
indent() {
local command; command='s/^/ /'
case $(uname) in
Darwin) sed -l "$command";;
*) sed -u "$command";;
esac
}

if [ "${TAILSCALE_DISABLE}" = "1" ]; then
info "[tailscale]: Is disabled via TAILSCALE_DISABLE"
exit 0
fi
if [ -z "${TAILSCALE_AUTH_KEY}" ]; then
info "[tailscale]: Will not be available because TAILSCALE_AUTH_KEY is not set"
exit 0
fi

if [ -z "$TAILSCALE_HOSTNAME" ]; then
build_tailscale_hostname() {
if [ -z "$HEROKU_APP_NAME" ]; then
TAILSCALE_HOSTNAME=$(hostname)
hostname
else
# Only use the first 8 characters of the commit sha.
# Swap the . and _ in the dyno with a - since tailscale doesn't
# allow for periods.
DYNO=${DYNO//./-}
DYNO=${DYNO//_/-}
TAILSCALE_HOSTNAME="heroku-$HEROKU_APP_NAME-${HEROKU_SLUG_COMMIT:0:8}-$DYNO"
local dyno="${DYNO:-}"
dyno=${dyno//./-}
dyno=${dyno//_/-}
echo "heroku-$HEROKU_APP_NAME-${HEROKU_SLUG_COMMIT:0:8}-$dyno"
fi
else
TAILSCALE_HOSTNAME="$TAILSCALE_HOSTNAME"
fi
tailscaled -cleanup > /dev/null 2>&1
(tailscaled -verbose ${TAILSCALED_VERBOSE:--1} --tun=userspace-networking --socks5-server=localhost:1055 > /dev/null 2>&1 &)
tailscale up \
--authkey="${TAILSCALE_AUTH_KEY}?preauthorized=true&ephemeral=true" \
--hostname="$TAILSCALE_HOSTNAME" \
--advertise-tags=${TAILSCALE_ADVERTISE_TAGS:-} \
--accept-routes \
--timeout=15s \
${TAILSCALE_ADDITIONAL_ARGS:---timeout=15s}

export ALL_PROXY=socks5://localhost:1055/

if wait_for_tailscale_running; then
echo "[tailscale]: Connected to tailnet as hostname=$TAILSCALE_HOSTNAME; SOCKS5 proxy available at localhost:1055"
else
echo "[tailscale]: Warning - Backend did not reach 'Running' state within timeout"
fi
}

readonly TAILSCALE_HOSTNAME="${TAILSCALE_HOSTNAME:-"$(build_tailscale_hostname)"}"

run_cmd() {
local cmd
cmd="$*"
debug "cmd=${cmd}"
eval "$cmd"
}

wait_for_tailscale_running() {
local timeout
timeout=50 # Timeout in tenths of a second
local interval
interval=5 # 0.5 second intervals (expressed in tenths)
local elapsed
elapsed=0

while [ "$elapsed" -lt "$timeout" ]; do
if tailscale status -json | grep -q 'Running' &> /dev/null; then
return 0
fi
sleep 0.5 # fake decimal math
elapsed=$((elapsed + interval))
done

return 1
}

main() {
topic "[tailscale] starting up"
# see https://tailscale.com/kb/1107/heroku
if [ "${TAILSCALED_CLEANUP}" = "true" ]; then
echo "[tailscale]: tailscaled -cleanup"
run_cmd tailscaled -cleanup
fi

debug "[tailscale]: tailscaled --tun --socks5-server &"
# https://tailscale.com/kb/1111/ephemeral-nodes#faq
run_cmd tailscaled \
-verbose "${TAILSCALED_VERBOSE}" \
--tun=userspace-networking \
--socks5-server=$ALL_PROXY_IP_PORT \
--state=mem \
&
debug "[tailscale]: tailscale up"
run_cmd tailscale up \
--auth-key="${TAILSCALE_AUTH_KEY}" \
--hostname="${TAILSCALE_HOSTNAME}" \
--advertise-tags="${TAILSCALE_ADVERTISE_TAGS}" \
"${TAILSCALE_ADDITIONAL_ARGS}"
debug "[tailscale]: tailscale started"

export ALL_PROXY="socks5://${ALL_PROXY_IP_PORT}/"

if wait_for_tailscale_running; then
info "[tailscale]: Connected to tailnet as hostname=$TAILSCALE_HOSTNAME; SOCKS5 proxy available at ${ALL_PROXY_IP_PORT}"
else
info "[tailscale]: Warning - Backend did not reach 'Running' state within timeout"
fi
}

main