1+ name : Tailscale SSH Debug Session
2+
3+ on :
4+ workflow_dispatch :
5+ inputs :
6+ runner_type :
7+ description : ' Runner type to debug'
8+ required : true
9+ default : ' ubuntu-latest'
10+ type : choice
11+ options :
12+ - ubuntu-latest
13+ - ubuntu-latest-4-cores
14+ - ubuntu-latest-8-cores
15+ - ubuntu-latest-16-cores
16+ - ubuntu-22.04
17+ - ubuntu-20.04
18+ - macos-latest
19+ - macos-13
20+ - macos-12
21+ - windows-latest
22+ - windows-2022
23+ - windows-2019
24+ timeout_minutes :
25+ description : ' SSH session timeout (minutes)'
26+ required : true
27+ default : ' 30'
28+ type : string
29+ ssh_user :
30+ description : ' Tailscale SSH user to authorize (your Tailscale login name)'
31+ required : true
32+ type : string
33+
34+ jobs :
35+ tailscale-ssh-debug :
36+ name : Tailscale SSH Debug - ${{ inputs.runner_type }}
37+ runs-on : ${{ inputs.runner_type }}
38+ timeout-minutes : ${{ fromJSON(inputs.timeout_minutes) }}
39+
40+ steps :
41+ - name : Checkout repository
42+ uses : actions/checkout@v4
43+
44+ - name : Setup Node.js
45+ uses : actions/setup-node@v4
46+ with :
47+ node-version : ' 20'
48+ cache : ' npm'
49+
50+ - name : Install dependencies
51+ run : npm ci
52+
53+ - name : Build action
54+ run : npm run build
55+
56+ - name : Setup Tailscale with SSH enabled
57+ uses : ./
58+ with :
59+ authkey : ${{ secrets.TAILSCALE_AUTHKEY }}
60+ version : ' latest'
61+
62+ - name : Enable Tailscale SSH
63+ shell : bash
64+ run : |
65+ echo "=== Enabling Tailscale SSH ==="
66+
67+ # Enable SSH on this node
68+ tailscale up --ssh
69+
70+ # Get the node's Tailscale IP and hostname
71+ TAILSCALE_IP=$(tailscale ip -4)
72+ TAILSCALE_HOSTNAME=$(tailscale status --json | jq -r '.Self.HostName')
73+
74+ echo "=== Tailscale SSH Ready ==="
75+ echo "Tailscale IP: $TAILSCALE_IP"
76+ echo "Tailscale Hostname: $TAILSCALE_HOSTNAME"
77+ echo ""
78+ echo "🔗 SSH Connection Commands:"
79+ echo " ssh ${{ inputs.ssh_user }}@$TAILSCALE_IP"
80+ echo " ssh ${{ inputs.ssh_user }}@$TAILSCALE_HOSTNAME"
81+ echo ""
82+ echo "Note: Make sure '${{ inputs.ssh_user }}' has SSH access in your Tailscale ACLs"
83+
84+ - name : Authorize SSH user (if needed)
85+ shell : bash
86+ run : |
87+ echo "=== SSH Authorization Info ==="
88+ echo "Authorized user: ${{ inputs.ssh_user }}"
89+ echo ""
90+ echo "To grant SSH access, ensure your Tailscale ACL includes:"
91+ echo ' "ssh": ['
92+ echo ' {'
93+ echo ' "action": "accept",'
94+ echo ' "src": ["${{ inputs.ssh_user }}"],'
95+ echo ' "dst": ["autogroup:self"],'
96+ echo ' "users": ["autogroup:nonroot"]'
97+ echo ' }'
98+ echo ' ]'
99+ echo ""
100+ echo "Or use tag-based SSH access in your ACL configuration."
101+
102+ - name : Display system information
103+ shell : bash
104+ run : |
105+ echo "=== System Information ==="
106+ echo "Runner: ${{ runner.os }} - ${{ runner.arch }}"
107+ echo "Hostname: $(hostname)"
108+ echo "Kernel: $(uname -a)"
109+ echo "CPU Info:"
110+ if [[ "${{ runner.os }}" == "Linux" ]]; then
111+ lscpu | head -20
112+ elif [[ "${{ runner.os }}" == "macOS" ]]; then
113+ sysctl -n machdep.cpu.brand_string
114+ sysctl -n hw.ncpu
115+ elif [[ "${{ runner.os }}" == "Windows" ]]; then
116+ wmic cpu get name
117+ fi
118+ echo ""
119+ echo "Memory:"
120+ if [[ "${{ runner.os }}" == "Linux" ]]; then
121+ free -h
122+ elif [[ "${{ runner.os }}" == "macOS" ]]; then
123+ vm_stat
124+ elif [[ "${{ runner.os }}" == "Windows" ]]; then
125+ wmic computersystem get TotalPhysicalMemory
126+ fi
127+ echo ""
128+ echo "Disk Space:"
129+ df -h
130+ echo ""
131+ echo "Network Interfaces:"
132+ if [[ "${{ runner.os }}" == "Windows" ]]; then
133+ ipconfig
134+ else
135+ ip addr show || ifconfig
136+ fi
137+
138+ - name : Display current Tailscale status
139+ shell : bash
140+ run : |
141+ echo "=== Current Tailscale Status ==="
142+ if command -v tailscale &> /dev/null; then
143+ echo "Tailscale is installed and running"
144+ tailscale status
145+ echo ""
146+ tailscale status --json | jq -r '
147+ "Device: " + .Self.HostName,
148+ "IP: " + .Self.TailscaleIPs[0],
149+ "Status: " + .BackendState,
150+ "Version: " + .Version,
151+ "SSH Enabled: " + (.Self.CapMap.ssh // false | tostring)
152+ '
153+ else
154+ echo "Tailscale not available"
155+ fi
156+
157+ - name : Wait for SSH connections
158+ shell : bash
159+ run : |
160+ echo "=== Waiting for SSH connections ==="
161+ echo "This runner will stay alive for ${{ inputs.timeout_minutes }} minutes"
162+ echo ""
163+ echo "🔗 Connect via Tailscale SSH using:"
164+ TAILSCALE_IP=$(tailscale ip -4)
165+ TAILSCALE_HOSTNAME=$(tailscale status --json | jq -r '.Self.HostName')
166+ echo " ssh ${{ inputs.ssh_user }}@$TAILSCALE_IP"
167+ echo " ssh ${{ inputs.ssh_user }}@$TAILSCALE_HOSTNAME"
168+ echo ""
169+ echo "Runner will automatically terminate after timeout or manual cancellation."
170+ echo "Current time: $(date)"
171+ echo ""
172+
173+ # Keep the runner alive for the specified timeout
174+ TIMEOUT_SECONDS=$(($(echo "${{ inputs.timeout_minutes }}" | bc) * 60))
175+ echo "Sleeping for $TIMEOUT_SECONDS seconds..."
176+
177+ # Use a loop with shorter sleeps to allow for interruption
178+ for ((i=0; i<TIMEOUT_SECONDS; i+=30)); do
179+ sleep 30
180+ if ((i % 300 == 0)); then # Every 5 minutes
181+ echo "Still alive... $(date) (${i}s elapsed)"
182+ tailscale status | head -5
183+ fi
184+ done
185+
186+ echo "Timeout reached. Ending debug session."
187+
188+ - name : Cleanup (Post SSH)
189+ if : always()
190+ shell : bash
191+ run : |
192+ echo "=== SSH Session Ended ==="
193+ echo "Timestamp: $(date)"
194+ echo "Session duration: ${{ inputs.timeout_minutes }} minutes (max)"
195+
196+ # Display any logs that might be helpful
197+ if command -v tailscale &> /dev/null; then
198+ echo ""
199+ echo "=== Final Tailscale Status ==="
200+ tailscale status
201+ fi
202+
203+ echo ""
204+ echo "=== Cleanup completed ==="
0 commit comments