TestSSHCheckModeCheckPeriodCLI verifies that after approval with a short checkPeriod, the session expires and the next SSH connection requires re-authentication via a new check flow.
(t *testing.T)
| 1104 | // checkPeriod, the session expires and the next SSH connection requires |
| 1105 | // re-authentication via a new check flow. |
| 1106 | func TestSSHCheckModeCheckPeriodCLI(t *testing.T) { |
| 1107 | IntegrationSkip(t) |
| 1108 | |
| 1109 | // 1 minute is the documented minimum checkPeriod |
| 1110 | scenario := sshScenario(t, sshCheckPolicyWithPeriod(time.Minute), "ssh-checkperiod", 1) |
| 1111 | defer scenario.ShutdownAssertNoPanics(t) |
| 1112 | |
| 1113 | allClients, err := scenario.ListTailscaleClients() |
| 1114 | requireNoErrListClients(t, err) |
| 1115 | |
| 1116 | user1Clients, err := scenario.ListTailscaleClients("user1") |
| 1117 | requireNoErrListClients(t, err) |
| 1118 | |
| 1119 | headscale, err := scenario.Headscale() |
| 1120 | require.NoError(t, err) |
| 1121 | |
| 1122 | err = scenario.WaitForTailscaleSync() |
| 1123 | requireNoErrSync(t, err) |
| 1124 | |
| 1125 | _, err = scenario.ListTailscaleClientsFQDNs() |
| 1126 | requireNoErrListFQDN(t, err) |
| 1127 | |
| 1128 | // === Phase 1: First SSH check — approve, verify success === |
| 1129 | for _, client := range user1Clients { |
| 1130 | for _, peer := range allClients { |
| 1131 | if client.Hostname() == peer.Hostname() { |
| 1132 | continue |
| 1133 | } |
| 1134 | |
| 1135 | sshResult := doSSHCheck(t, client, peer) |
| 1136 | firstAuthID := findSSHCheckAuthID(t, headscale) |
| 1137 | |
| 1138 | _, err := headscale.Execute( |
| 1139 | []string{ |
| 1140 | "headscale", "auth", "approve", |
| 1141 | "--auth-id", firstAuthID, |
| 1142 | }, |
| 1143 | ) |
| 1144 | require.NoError(t, err) |
| 1145 | |
| 1146 | select { |
| 1147 | case result := <-sshResult: |
| 1148 | require.NoError(t, result.err, "first SSH should succeed after approval") |
| 1149 | require.Contains( |
| 1150 | t, |
| 1151 | peer.ContainerID(), |
| 1152 | strings.ReplaceAll(result.stdout, "\n", ""), |
| 1153 | ) |
| 1154 | case <-time.After(30 * time.Second): |
| 1155 | t.Fatal("first SSH did not complete after auth approval") |
| 1156 | } |
| 1157 | |
| 1158 | // === Phase 2: Wait for checkPeriod to expire === |
| 1159 | //nolint:forbidigo // Intentional sleep: waiting for the check period session |
| 1160 | // to expire. This is a time-based expiry, not a pollable condition — the |
| 1161 | // Tailscale client caches the approval for SessionDuration and only |
| 1162 | // re-triggers the check flow after it elapses. |
| 1163 | time.Sleep(70 * time.Second) |
nothing calls this directly
no test coverage detected