diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2830aa22..cb93dae5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,8 +5,6 @@ on: branches: - main pull_request: - branches: - - main concurrency: group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }} diff --git a/.github/workflows/test-integration.yaml b/.github/workflows/test-integration.yaml index f5ec43a1..2f82ba22 100644 --- a/.github/workflows/test-integration.yaml +++ b/.github/workflows/test-integration.yaml @@ -32,6 +32,7 @@ jobs: - TestAuthKeyLogoutAndReloginSameUser - TestAuthKeyLogoutAndReloginNewUser - TestAuthKeyLogoutAndReloginSameUserExpiredKey + - TestAuthKeyDeleteKey - TestOIDCAuthenticationPingAll - TestOIDCExpireNodesBasedOnTokenExpiry - TestOIDC024UserCreation diff --git a/cmd/headscale/cli/preauthkeys.go b/cmd/headscale/cli/preauthkeys.go index e42fa1e3..56478329 100644 --- a/cmd/headscale/cli/preauthkeys.go +++ b/cmd/headscale/cli/preauthkeys.go @@ -34,6 +34,7 @@ func init() { preauthkeysCmd.AddCommand(listPreAuthKeys) preauthkeysCmd.AddCommand(createPreAuthKeyCmd) preauthkeysCmd.AddCommand(expirePreAuthKeyCmd) + preauthkeysCmd.AddCommand(deletePreAuthKeyCmd) createPreAuthKeyCmd.PersistentFlags(). Bool("reusable", false, "Make the preauthkey reusable") createPreAuthKeyCmd.PersistentFlags(). @@ -232,3 +233,43 @@ var expirePreAuthKeyCmd = &cobra.Command{ SuccessOutput(response, "Key expired", output) }, } + +var deletePreAuthKeyCmd = &cobra.Command{ + Use: "delete KEY", + Short: "Delete a preauthkey", + Aliases: []string{"del", "rm", "d"}, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errMissingParameter + } + + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + output, _ := cmd.Flags().GetString("output") + user, err := cmd.Flags().GetUint64("user") + if err != nil { + ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output) + } + + ctx, client, conn, cancel := newHeadscaleCLIWithConfig() + defer cancel() + defer conn.Close() + + request := &v1.DeletePreAuthKeyRequest{ + User: user, + Key: args[0], + } + + response, err := client.DeletePreAuthKey(ctx, request) + if err != nil { + ErrorOutput( + err, + fmt.Sprintf("Cannot delete Pre Auth Key: %s\n", err), + output, + ) + } + + SuccessOutput(response, "Key deleted", output) + }, +} diff --git a/cmd/hi/docker.go b/cmd/hi/docker.go index 3df7c7f6..8bd02ee6 100644 --- a/cmd/hi/docker.go +++ b/cmd/hi/docker.go @@ -300,6 +300,19 @@ func createGoTestContainer(ctx context.Context, cli *client.Client, config *RunC fmt.Sprintf("HEADSCALE_INTEGRATION_POSTGRES=%d", boolToInt(config.UsePostgres)), "HEADSCALE_INTEGRATION_RUN_ID=" + runID, } + + // Pass through all HEADSCALE_INTEGRATION_* environment variables + for _, e := range os.Environ() { + if strings.HasPrefix(e, "HEADSCALE_INTEGRATION_") { + // Skip the ones we already set explicitly + if strings.HasPrefix(e, "HEADSCALE_INTEGRATION_POSTGRES=") || + strings.HasPrefix(e, "HEADSCALE_INTEGRATION_RUN_ID=") { + continue + } + + env = append(env, e) + } + } containerConfig := &container.Config{ Image: "golang:" + config.GoVersion, Cmd: goTestCmd, diff --git a/gen/go/headscale/v1/headscale.pb.go b/gen/go/headscale/v1/headscale.pb.go index 7a411e06..a9d84b3d 100644 --- a/gen/go/headscale/v1/headscale.pb.go +++ b/gen/go/headscale/v1/headscale.pb.go @@ -109,7 +109,7 @@ const file_headscale_v1_headscale_proto_rawDesc = "" + "\x1cheadscale/v1/headscale.proto\x12\fheadscale.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17headscale/v1/user.proto\x1a\x1dheadscale/v1/preauthkey.proto\x1a\x17headscale/v1/node.proto\x1a\x19headscale/v1/apikey.proto\x1a\x19headscale/v1/policy.proto\"\x0f\n" + "\rHealthRequest\"E\n" + "\x0eHealthResponse\x123\n" + - "\x15database_connectivity\x18\x01 \x01(\bR\x14databaseConnectivity2\x8d\x16\n" + + "\x15database_connectivity\x18\x01 \x01(\bR\x14databaseConnectivity2\x8c\x17\n" + "\x10HeadscaleService\x12h\n" + "\n" + "CreateUser\x12\x1f.headscale.v1.CreateUserRequest\x1a .headscale.v1.CreateUserResponse\"\x17\x82\xd3\xe4\x93\x02\x11:\x01*\"\f/api/v1/user\x12\x80\x01\n" + @@ -119,7 +119,8 @@ const file_headscale_v1_headscale_proto_rawDesc = "" + "DeleteUser\x12\x1f.headscale.v1.DeleteUserRequest\x1a .headscale.v1.DeleteUserResponse\"\x19\x82\xd3\xe4\x93\x02\x13*\x11/api/v1/user/{id}\x12b\n" + "\tListUsers\x12\x1e.headscale.v1.ListUsersRequest\x1a\x1f.headscale.v1.ListUsersResponse\"\x14\x82\xd3\xe4\x93\x02\x0e\x12\f/api/v1/user\x12\x80\x01\n" + "\x10CreatePreAuthKey\x12%.headscale.v1.CreatePreAuthKeyRequest\x1a&.headscale.v1.CreatePreAuthKeyResponse\"\x1d\x82\xd3\xe4\x93\x02\x17:\x01*\"\x12/api/v1/preauthkey\x12\x87\x01\n" + - "\x10ExpirePreAuthKey\x12%.headscale.v1.ExpirePreAuthKeyRequest\x1a&.headscale.v1.ExpirePreAuthKeyResponse\"$\x82\xd3\xe4\x93\x02\x1e:\x01*\"\x19/api/v1/preauthkey/expire\x12z\n" + + "\x10ExpirePreAuthKey\x12%.headscale.v1.ExpirePreAuthKeyRequest\x1a&.headscale.v1.ExpirePreAuthKeyResponse\"$\x82\xd3\xe4\x93\x02\x1e:\x01*\"\x19/api/v1/preauthkey/expire\x12}\n" + + "\x10DeletePreAuthKey\x12%.headscale.v1.DeletePreAuthKeyRequest\x1a&.headscale.v1.DeletePreAuthKeyResponse\"\x1a\x82\xd3\xe4\x93\x02\x14*\x12/api/v1/preauthkey\x12z\n" + "\x0fListPreAuthKeys\x12$.headscale.v1.ListPreAuthKeysRequest\x1a%.headscale.v1.ListPreAuthKeysResponse\"\x1a\x82\xd3\xe4\x93\x02\x14\x12\x12/api/v1/preauthkey\x12}\n" + "\x0fDebugCreateNode\x12$.headscale.v1.DebugCreateNodeRequest\x1a%.headscale.v1.DebugCreateNodeResponse\"\x1d\x82\xd3\xe4\x93\x02\x17:\x01*\"\x12/api/v1/debug/node\x12f\n" + "\aGetNode\x12\x1c.headscale.v1.GetNodeRequest\x1a\x1d.headscale.v1.GetNodeResponse\"\x1e\x82\xd3\xe4\x93\x02\x18\x12\x16/api/v1/node/{node_id}\x12n\n" + @@ -164,46 +165,48 @@ var file_headscale_v1_headscale_proto_goTypes = []any{ (*ListUsersRequest)(nil), // 5: headscale.v1.ListUsersRequest (*CreatePreAuthKeyRequest)(nil), // 6: headscale.v1.CreatePreAuthKeyRequest (*ExpirePreAuthKeyRequest)(nil), // 7: headscale.v1.ExpirePreAuthKeyRequest - (*ListPreAuthKeysRequest)(nil), // 8: headscale.v1.ListPreAuthKeysRequest - (*DebugCreateNodeRequest)(nil), // 9: headscale.v1.DebugCreateNodeRequest - (*GetNodeRequest)(nil), // 10: headscale.v1.GetNodeRequest - (*SetTagsRequest)(nil), // 11: headscale.v1.SetTagsRequest - (*SetApprovedRoutesRequest)(nil), // 12: headscale.v1.SetApprovedRoutesRequest - (*RegisterNodeRequest)(nil), // 13: headscale.v1.RegisterNodeRequest - (*DeleteNodeRequest)(nil), // 14: headscale.v1.DeleteNodeRequest - (*ExpireNodeRequest)(nil), // 15: headscale.v1.ExpireNodeRequest - (*RenameNodeRequest)(nil), // 16: headscale.v1.RenameNodeRequest - (*ListNodesRequest)(nil), // 17: headscale.v1.ListNodesRequest - (*BackfillNodeIPsRequest)(nil), // 18: headscale.v1.BackfillNodeIPsRequest - (*CreateApiKeyRequest)(nil), // 19: headscale.v1.CreateApiKeyRequest - (*ExpireApiKeyRequest)(nil), // 20: headscale.v1.ExpireApiKeyRequest - (*ListApiKeysRequest)(nil), // 21: headscale.v1.ListApiKeysRequest - (*DeleteApiKeyRequest)(nil), // 22: headscale.v1.DeleteApiKeyRequest - (*GetPolicyRequest)(nil), // 23: headscale.v1.GetPolicyRequest - (*SetPolicyRequest)(nil), // 24: headscale.v1.SetPolicyRequest - (*CreateUserResponse)(nil), // 25: headscale.v1.CreateUserResponse - (*RenameUserResponse)(nil), // 26: headscale.v1.RenameUserResponse - (*DeleteUserResponse)(nil), // 27: headscale.v1.DeleteUserResponse - (*ListUsersResponse)(nil), // 28: headscale.v1.ListUsersResponse - (*CreatePreAuthKeyResponse)(nil), // 29: headscale.v1.CreatePreAuthKeyResponse - (*ExpirePreAuthKeyResponse)(nil), // 30: headscale.v1.ExpirePreAuthKeyResponse - (*ListPreAuthKeysResponse)(nil), // 31: headscale.v1.ListPreAuthKeysResponse - (*DebugCreateNodeResponse)(nil), // 32: headscale.v1.DebugCreateNodeResponse - (*GetNodeResponse)(nil), // 33: headscale.v1.GetNodeResponse - (*SetTagsResponse)(nil), // 34: headscale.v1.SetTagsResponse - (*SetApprovedRoutesResponse)(nil), // 35: headscale.v1.SetApprovedRoutesResponse - (*RegisterNodeResponse)(nil), // 36: headscale.v1.RegisterNodeResponse - (*DeleteNodeResponse)(nil), // 37: headscale.v1.DeleteNodeResponse - (*ExpireNodeResponse)(nil), // 38: headscale.v1.ExpireNodeResponse - (*RenameNodeResponse)(nil), // 39: headscale.v1.RenameNodeResponse - (*ListNodesResponse)(nil), // 40: headscale.v1.ListNodesResponse - (*BackfillNodeIPsResponse)(nil), // 41: headscale.v1.BackfillNodeIPsResponse - (*CreateApiKeyResponse)(nil), // 42: headscale.v1.CreateApiKeyResponse - (*ExpireApiKeyResponse)(nil), // 43: headscale.v1.ExpireApiKeyResponse - (*ListApiKeysResponse)(nil), // 44: headscale.v1.ListApiKeysResponse - (*DeleteApiKeyResponse)(nil), // 45: headscale.v1.DeleteApiKeyResponse - (*GetPolicyResponse)(nil), // 46: headscale.v1.GetPolicyResponse - (*SetPolicyResponse)(nil), // 47: headscale.v1.SetPolicyResponse + (*DeletePreAuthKeyRequest)(nil), // 8: headscale.v1.DeletePreAuthKeyRequest + (*ListPreAuthKeysRequest)(nil), // 9: headscale.v1.ListPreAuthKeysRequest + (*DebugCreateNodeRequest)(nil), // 10: headscale.v1.DebugCreateNodeRequest + (*GetNodeRequest)(nil), // 11: headscale.v1.GetNodeRequest + (*SetTagsRequest)(nil), // 12: headscale.v1.SetTagsRequest + (*SetApprovedRoutesRequest)(nil), // 13: headscale.v1.SetApprovedRoutesRequest + (*RegisterNodeRequest)(nil), // 14: headscale.v1.RegisterNodeRequest + (*DeleteNodeRequest)(nil), // 15: headscale.v1.DeleteNodeRequest + (*ExpireNodeRequest)(nil), // 16: headscale.v1.ExpireNodeRequest + (*RenameNodeRequest)(nil), // 17: headscale.v1.RenameNodeRequest + (*ListNodesRequest)(nil), // 18: headscale.v1.ListNodesRequest + (*BackfillNodeIPsRequest)(nil), // 19: headscale.v1.BackfillNodeIPsRequest + (*CreateApiKeyRequest)(nil), // 20: headscale.v1.CreateApiKeyRequest + (*ExpireApiKeyRequest)(nil), // 21: headscale.v1.ExpireApiKeyRequest + (*ListApiKeysRequest)(nil), // 22: headscale.v1.ListApiKeysRequest + (*DeleteApiKeyRequest)(nil), // 23: headscale.v1.DeleteApiKeyRequest + (*GetPolicyRequest)(nil), // 24: headscale.v1.GetPolicyRequest + (*SetPolicyRequest)(nil), // 25: headscale.v1.SetPolicyRequest + (*CreateUserResponse)(nil), // 26: headscale.v1.CreateUserResponse + (*RenameUserResponse)(nil), // 27: headscale.v1.RenameUserResponse + (*DeleteUserResponse)(nil), // 28: headscale.v1.DeleteUserResponse + (*ListUsersResponse)(nil), // 29: headscale.v1.ListUsersResponse + (*CreatePreAuthKeyResponse)(nil), // 30: headscale.v1.CreatePreAuthKeyResponse + (*ExpirePreAuthKeyResponse)(nil), // 31: headscale.v1.ExpirePreAuthKeyResponse + (*DeletePreAuthKeyResponse)(nil), // 32: headscale.v1.DeletePreAuthKeyResponse + (*ListPreAuthKeysResponse)(nil), // 33: headscale.v1.ListPreAuthKeysResponse + (*DebugCreateNodeResponse)(nil), // 34: headscale.v1.DebugCreateNodeResponse + (*GetNodeResponse)(nil), // 35: headscale.v1.GetNodeResponse + (*SetTagsResponse)(nil), // 36: headscale.v1.SetTagsResponse + (*SetApprovedRoutesResponse)(nil), // 37: headscale.v1.SetApprovedRoutesResponse + (*RegisterNodeResponse)(nil), // 38: headscale.v1.RegisterNodeResponse + (*DeleteNodeResponse)(nil), // 39: headscale.v1.DeleteNodeResponse + (*ExpireNodeResponse)(nil), // 40: headscale.v1.ExpireNodeResponse + (*RenameNodeResponse)(nil), // 41: headscale.v1.RenameNodeResponse + (*ListNodesResponse)(nil), // 42: headscale.v1.ListNodesResponse + (*BackfillNodeIPsResponse)(nil), // 43: headscale.v1.BackfillNodeIPsResponse + (*CreateApiKeyResponse)(nil), // 44: headscale.v1.CreateApiKeyResponse + (*ExpireApiKeyResponse)(nil), // 45: headscale.v1.ExpireApiKeyResponse + (*ListApiKeysResponse)(nil), // 46: headscale.v1.ListApiKeysResponse + (*DeleteApiKeyResponse)(nil), // 47: headscale.v1.DeleteApiKeyResponse + (*GetPolicyResponse)(nil), // 48: headscale.v1.GetPolicyResponse + (*SetPolicyResponse)(nil), // 49: headscale.v1.SetPolicyResponse } var file_headscale_v1_headscale_proto_depIdxs = []int32{ 2, // 0: headscale.v1.HeadscaleService.CreateUser:input_type -> headscale.v1.CreateUserRequest @@ -212,50 +215,52 @@ var file_headscale_v1_headscale_proto_depIdxs = []int32{ 5, // 3: headscale.v1.HeadscaleService.ListUsers:input_type -> headscale.v1.ListUsersRequest 6, // 4: headscale.v1.HeadscaleService.CreatePreAuthKey:input_type -> headscale.v1.CreatePreAuthKeyRequest 7, // 5: headscale.v1.HeadscaleService.ExpirePreAuthKey:input_type -> headscale.v1.ExpirePreAuthKeyRequest - 8, // 6: headscale.v1.HeadscaleService.ListPreAuthKeys:input_type -> headscale.v1.ListPreAuthKeysRequest - 9, // 7: headscale.v1.HeadscaleService.DebugCreateNode:input_type -> headscale.v1.DebugCreateNodeRequest - 10, // 8: headscale.v1.HeadscaleService.GetNode:input_type -> headscale.v1.GetNodeRequest - 11, // 9: headscale.v1.HeadscaleService.SetTags:input_type -> headscale.v1.SetTagsRequest - 12, // 10: headscale.v1.HeadscaleService.SetApprovedRoutes:input_type -> headscale.v1.SetApprovedRoutesRequest - 13, // 11: headscale.v1.HeadscaleService.RegisterNode:input_type -> headscale.v1.RegisterNodeRequest - 14, // 12: headscale.v1.HeadscaleService.DeleteNode:input_type -> headscale.v1.DeleteNodeRequest - 15, // 13: headscale.v1.HeadscaleService.ExpireNode:input_type -> headscale.v1.ExpireNodeRequest - 16, // 14: headscale.v1.HeadscaleService.RenameNode:input_type -> headscale.v1.RenameNodeRequest - 17, // 15: headscale.v1.HeadscaleService.ListNodes:input_type -> headscale.v1.ListNodesRequest - 18, // 16: headscale.v1.HeadscaleService.BackfillNodeIPs:input_type -> headscale.v1.BackfillNodeIPsRequest - 19, // 17: headscale.v1.HeadscaleService.CreateApiKey:input_type -> headscale.v1.CreateApiKeyRequest - 20, // 18: headscale.v1.HeadscaleService.ExpireApiKey:input_type -> headscale.v1.ExpireApiKeyRequest - 21, // 19: headscale.v1.HeadscaleService.ListApiKeys:input_type -> headscale.v1.ListApiKeysRequest - 22, // 20: headscale.v1.HeadscaleService.DeleteApiKey:input_type -> headscale.v1.DeleteApiKeyRequest - 23, // 21: headscale.v1.HeadscaleService.GetPolicy:input_type -> headscale.v1.GetPolicyRequest - 24, // 22: headscale.v1.HeadscaleService.SetPolicy:input_type -> headscale.v1.SetPolicyRequest - 0, // 23: headscale.v1.HeadscaleService.Health:input_type -> headscale.v1.HealthRequest - 25, // 24: headscale.v1.HeadscaleService.CreateUser:output_type -> headscale.v1.CreateUserResponse - 26, // 25: headscale.v1.HeadscaleService.RenameUser:output_type -> headscale.v1.RenameUserResponse - 27, // 26: headscale.v1.HeadscaleService.DeleteUser:output_type -> headscale.v1.DeleteUserResponse - 28, // 27: headscale.v1.HeadscaleService.ListUsers:output_type -> headscale.v1.ListUsersResponse - 29, // 28: headscale.v1.HeadscaleService.CreatePreAuthKey:output_type -> headscale.v1.CreatePreAuthKeyResponse - 30, // 29: headscale.v1.HeadscaleService.ExpirePreAuthKey:output_type -> headscale.v1.ExpirePreAuthKeyResponse - 31, // 30: headscale.v1.HeadscaleService.ListPreAuthKeys:output_type -> headscale.v1.ListPreAuthKeysResponse - 32, // 31: headscale.v1.HeadscaleService.DebugCreateNode:output_type -> headscale.v1.DebugCreateNodeResponse - 33, // 32: headscale.v1.HeadscaleService.GetNode:output_type -> headscale.v1.GetNodeResponse - 34, // 33: headscale.v1.HeadscaleService.SetTags:output_type -> headscale.v1.SetTagsResponse - 35, // 34: headscale.v1.HeadscaleService.SetApprovedRoutes:output_type -> headscale.v1.SetApprovedRoutesResponse - 36, // 35: headscale.v1.HeadscaleService.RegisterNode:output_type -> headscale.v1.RegisterNodeResponse - 37, // 36: headscale.v1.HeadscaleService.DeleteNode:output_type -> headscale.v1.DeleteNodeResponse - 38, // 37: headscale.v1.HeadscaleService.ExpireNode:output_type -> headscale.v1.ExpireNodeResponse - 39, // 38: headscale.v1.HeadscaleService.RenameNode:output_type -> headscale.v1.RenameNodeResponse - 40, // 39: headscale.v1.HeadscaleService.ListNodes:output_type -> headscale.v1.ListNodesResponse - 41, // 40: headscale.v1.HeadscaleService.BackfillNodeIPs:output_type -> headscale.v1.BackfillNodeIPsResponse - 42, // 41: headscale.v1.HeadscaleService.CreateApiKey:output_type -> headscale.v1.CreateApiKeyResponse - 43, // 42: headscale.v1.HeadscaleService.ExpireApiKey:output_type -> headscale.v1.ExpireApiKeyResponse - 44, // 43: headscale.v1.HeadscaleService.ListApiKeys:output_type -> headscale.v1.ListApiKeysResponse - 45, // 44: headscale.v1.HeadscaleService.DeleteApiKey:output_type -> headscale.v1.DeleteApiKeyResponse - 46, // 45: headscale.v1.HeadscaleService.GetPolicy:output_type -> headscale.v1.GetPolicyResponse - 47, // 46: headscale.v1.HeadscaleService.SetPolicy:output_type -> headscale.v1.SetPolicyResponse - 1, // 47: headscale.v1.HeadscaleService.Health:output_type -> headscale.v1.HealthResponse - 24, // [24:48] is the sub-list for method output_type - 0, // [0:24] is the sub-list for method input_type + 8, // 6: headscale.v1.HeadscaleService.DeletePreAuthKey:input_type -> headscale.v1.DeletePreAuthKeyRequest + 9, // 7: headscale.v1.HeadscaleService.ListPreAuthKeys:input_type -> headscale.v1.ListPreAuthKeysRequest + 10, // 8: headscale.v1.HeadscaleService.DebugCreateNode:input_type -> headscale.v1.DebugCreateNodeRequest + 11, // 9: headscale.v1.HeadscaleService.GetNode:input_type -> headscale.v1.GetNodeRequest + 12, // 10: headscale.v1.HeadscaleService.SetTags:input_type -> headscale.v1.SetTagsRequest + 13, // 11: headscale.v1.HeadscaleService.SetApprovedRoutes:input_type -> headscale.v1.SetApprovedRoutesRequest + 14, // 12: headscale.v1.HeadscaleService.RegisterNode:input_type -> headscale.v1.RegisterNodeRequest + 15, // 13: headscale.v1.HeadscaleService.DeleteNode:input_type -> headscale.v1.DeleteNodeRequest + 16, // 14: headscale.v1.HeadscaleService.ExpireNode:input_type -> headscale.v1.ExpireNodeRequest + 17, // 15: headscale.v1.HeadscaleService.RenameNode:input_type -> headscale.v1.RenameNodeRequest + 18, // 16: headscale.v1.HeadscaleService.ListNodes:input_type -> headscale.v1.ListNodesRequest + 19, // 17: headscale.v1.HeadscaleService.BackfillNodeIPs:input_type -> headscale.v1.BackfillNodeIPsRequest + 20, // 18: headscale.v1.HeadscaleService.CreateApiKey:input_type -> headscale.v1.CreateApiKeyRequest + 21, // 19: headscale.v1.HeadscaleService.ExpireApiKey:input_type -> headscale.v1.ExpireApiKeyRequest + 22, // 20: headscale.v1.HeadscaleService.ListApiKeys:input_type -> headscale.v1.ListApiKeysRequest + 23, // 21: headscale.v1.HeadscaleService.DeleteApiKey:input_type -> headscale.v1.DeleteApiKeyRequest + 24, // 22: headscale.v1.HeadscaleService.GetPolicy:input_type -> headscale.v1.GetPolicyRequest + 25, // 23: headscale.v1.HeadscaleService.SetPolicy:input_type -> headscale.v1.SetPolicyRequest + 0, // 24: headscale.v1.HeadscaleService.Health:input_type -> headscale.v1.HealthRequest + 26, // 25: headscale.v1.HeadscaleService.CreateUser:output_type -> headscale.v1.CreateUserResponse + 27, // 26: headscale.v1.HeadscaleService.RenameUser:output_type -> headscale.v1.RenameUserResponse + 28, // 27: headscale.v1.HeadscaleService.DeleteUser:output_type -> headscale.v1.DeleteUserResponse + 29, // 28: headscale.v1.HeadscaleService.ListUsers:output_type -> headscale.v1.ListUsersResponse + 30, // 29: headscale.v1.HeadscaleService.CreatePreAuthKey:output_type -> headscale.v1.CreatePreAuthKeyResponse + 31, // 30: headscale.v1.HeadscaleService.ExpirePreAuthKey:output_type -> headscale.v1.ExpirePreAuthKeyResponse + 32, // 31: headscale.v1.HeadscaleService.DeletePreAuthKey:output_type -> headscale.v1.DeletePreAuthKeyResponse + 33, // 32: headscale.v1.HeadscaleService.ListPreAuthKeys:output_type -> headscale.v1.ListPreAuthKeysResponse + 34, // 33: headscale.v1.HeadscaleService.DebugCreateNode:output_type -> headscale.v1.DebugCreateNodeResponse + 35, // 34: headscale.v1.HeadscaleService.GetNode:output_type -> headscale.v1.GetNodeResponse + 36, // 35: headscale.v1.HeadscaleService.SetTags:output_type -> headscale.v1.SetTagsResponse + 37, // 36: headscale.v1.HeadscaleService.SetApprovedRoutes:output_type -> headscale.v1.SetApprovedRoutesResponse + 38, // 37: headscale.v1.HeadscaleService.RegisterNode:output_type -> headscale.v1.RegisterNodeResponse + 39, // 38: headscale.v1.HeadscaleService.DeleteNode:output_type -> headscale.v1.DeleteNodeResponse + 40, // 39: headscale.v1.HeadscaleService.ExpireNode:output_type -> headscale.v1.ExpireNodeResponse + 41, // 40: headscale.v1.HeadscaleService.RenameNode:output_type -> headscale.v1.RenameNodeResponse + 42, // 41: headscale.v1.HeadscaleService.ListNodes:output_type -> headscale.v1.ListNodesResponse + 43, // 42: headscale.v1.HeadscaleService.BackfillNodeIPs:output_type -> headscale.v1.BackfillNodeIPsResponse + 44, // 43: headscale.v1.HeadscaleService.CreateApiKey:output_type -> headscale.v1.CreateApiKeyResponse + 45, // 44: headscale.v1.HeadscaleService.ExpireApiKey:output_type -> headscale.v1.ExpireApiKeyResponse + 46, // 45: headscale.v1.HeadscaleService.ListApiKeys:output_type -> headscale.v1.ListApiKeysResponse + 47, // 46: headscale.v1.HeadscaleService.DeleteApiKey:output_type -> headscale.v1.DeleteApiKeyResponse + 48, // 47: headscale.v1.HeadscaleService.GetPolicy:output_type -> headscale.v1.GetPolicyResponse + 49, // 48: headscale.v1.HeadscaleService.SetPolicy:output_type -> headscale.v1.SetPolicyResponse + 1, // 49: headscale.v1.HeadscaleService.Health:output_type -> headscale.v1.HealthResponse + 25, // [25:50] is the sub-list for method output_type + 0, // [0:25] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name diff --git a/gen/go/headscale/v1/headscale.pb.gw.go b/gen/go/headscale/v1/headscale.pb.gw.go index f6a24539..ffa6964f 100644 --- a/gen/go/headscale/v1/headscale.pb.gw.go +++ b/gen/go/headscale/v1/headscale.pb.gw.go @@ -227,6 +227,38 @@ func local_request_HeadscaleService_ExpirePreAuthKey_0(ctx context.Context, mars return msg, metadata, err } +var filter_HeadscaleService_DeletePreAuthKey_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} + +func request_HeadscaleService_DeletePreAuthKey_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq DeletePreAuthKeyRequest + metadata runtime.ServerMetadata + ) + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HeadscaleService_DeletePreAuthKey_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.DeletePreAuthKey(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err +} + +func local_request_HeadscaleService_DeletePreAuthKey_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var ( + protoReq DeletePreAuthKeyRequest + metadata runtime.ServerMetadata + ) + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HeadscaleService_DeletePreAuthKey_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.DeletePreAuthKey(ctx, &protoReq) + return msg, metadata, err +} + var filter_HeadscaleService_ListPreAuthKeys_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} func request_HeadscaleService_ListPreAuthKeys_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { @@ -925,6 +957,26 @@ func RegisterHeadscaleServiceHandlerServer(ctx context.Context, mux *runtime.Ser } forward_HeadscaleService_ExpirePreAuthKey_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) + mux.Handle(http.MethodDelete, pattern_HeadscaleService_DeletePreAuthKey_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/headscale.v1.HeadscaleService/DeletePreAuthKey", runtime.WithHTTPPathPattern("/api/v1/preauthkey")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_DeletePreAuthKey_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_HeadscaleService_DeletePreAuthKey_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) mux.Handle(http.MethodGet, pattern_HeadscaleService_ListPreAuthKeys_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1427,6 +1479,23 @@ func RegisterHeadscaleServiceHandlerClient(ctx context.Context, mux *runtime.Ser } forward_HeadscaleService_ExpirePreAuthKey_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) + mux.Handle(http.MethodDelete, pattern_HeadscaleService_DeletePreAuthKey_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/headscale.v1.HeadscaleService/DeletePreAuthKey", runtime.WithHTTPPathPattern("/api/v1/preauthkey")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_DeletePreAuthKey_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + forward_HeadscaleService_DeletePreAuthKey_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + }) mux.Handle(http.MethodGet, pattern_HeadscaleService_ListPreAuthKeys_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1743,6 +1812,7 @@ var ( pattern_HeadscaleService_ListUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "user"}, "")) pattern_HeadscaleService_CreatePreAuthKey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "preauthkey"}, "")) pattern_HeadscaleService_ExpirePreAuthKey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "preauthkey", "expire"}, "")) + pattern_HeadscaleService_DeletePreAuthKey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "preauthkey"}, "")) pattern_HeadscaleService_ListPreAuthKeys_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "preauthkey"}, "")) pattern_HeadscaleService_DebugCreateNode_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "debug", "node"}, "")) pattern_HeadscaleService_GetNode_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "node", "node_id"}, "")) @@ -1770,6 +1840,7 @@ var ( forward_HeadscaleService_ListUsers_0 = runtime.ForwardResponseMessage forward_HeadscaleService_CreatePreAuthKey_0 = runtime.ForwardResponseMessage forward_HeadscaleService_ExpirePreAuthKey_0 = runtime.ForwardResponseMessage + forward_HeadscaleService_DeletePreAuthKey_0 = runtime.ForwardResponseMessage forward_HeadscaleService_ListPreAuthKeys_0 = runtime.ForwardResponseMessage forward_HeadscaleService_DebugCreateNode_0 = runtime.ForwardResponseMessage forward_HeadscaleService_GetNode_0 = runtime.ForwardResponseMessage diff --git a/gen/go/headscale/v1/headscale_grpc.pb.go b/gen/go/headscale/v1/headscale_grpc.pb.go index 0cbef4ef..5704866b 100644 --- a/gen/go/headscale/v1/headscale_grpc.pb.go +++ b/gen/go/headscale/v1/headscale_grpc.pb.go @@ -25,6 +25,7 @@ const ( HeadscaleService_ListUsers_FullMethodName = "/headscale.v1.HeadscaleService/ListUsers" HeadscaleService_CreatePreAuthKey_FullMethodName = "/headscale.v1.HeadscaleService/CreatePreAuthKey" HeadscaleService_ExpirePreAuthKey_FullMethodName = "/headscale.v1.HeadscaleService/ExpirePreAuthKey" + HeadscaleService_DeletePreAuthKey_FullMethodName = "/headscale.v1.HeadscaleService/DeletePreAuthKey" HeadscaleService_ListPreAuthKeys_FullMethodName = "/headscale.v1.HeadscaleService/ListPreAuthKeys" HeadscaleService_DebugCreateNode_FullMethodName = "/headscale.v1.HeadscaleService/DebugCreateNode" HeadscaleService_GetNode_FullMethodName = "/headscale.v1.HeadscaleService/GetNode" @@ -57,6 +58,7 @@ type HeadscaleServiceClient interface { // --- PreAuthKeys start --- CreatePreAuthKey(ctx context.Context, in *CreatePreAuthKeyRequest, opts ...grpc.CallOption) (*CreatePreAuthKeyResponse, error) ExpirePreAuthKey(ctx context.Context, in *ExpirePreAuthKeyRequest, opts ...grpc.CallOption) (*ExpirePreAuthKeyResponse, error) + DeletePreAuthKey(ctx context.Context, in *DeletePreAuthKeyRequest, opts ...grpc.CallOption) (*DeletePreAuthKeyResponse, error) ListPreAuthKeys(ctx context.Context, in *ListPreAuthKeysRequest, opts ...grpc.CallOption) (*ListPreAuthKeysResponse, error) // --- Node start --- DebugCreateNode(ctx context.Context, in *DebugCreateNodeRequest, opts ...grpc.CallOption) (*DebugCreateNodeResponse, error) @@ -149,6 +151,16 @@ func (c *headscaleServiceClient) ExpirePreAuthKey(ctx context.Context, in *Expir return out, nil } +func (c *headscaleServiceClient) DeletePreAuthKey(ctx context.Context, in *DeletePreAuthKeyRequest, opts ...grpc.CallOption) (*DeletePreAuthKeyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(DeletePreAuthKeyResponse) + err := c.cc.Invoke(ctx, HeadscaleService_DeletePreAuthKey_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *headscaleServiceClient) ListPreAuthKeys(ctx context.Context, in *ListPreAuthKeysRequest, opts ...grpc.CallOption) (*ListPreAuthKeysResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListPreAuthKeysResponse) @@ -341,6 +353,7 @@ type HeadscaleServiceServer interface { // --- PreAuthKeys start --- CreatePreAuthKey(context.Context, *CreatePreAuthKeyRequest) (*CreatePreAuthKeyResponse, error) ExpirePreAuthKey(context.Context, *ExpirePreAuthKeyRequest) (*ExpirePreAuthKeyResponse, error) + DeletePreAuthKey(context.Context, *DeletePreAuthKeyRequest) (*DeletePreAuthKeyResponse, error) ListPreAuthKeys(context.Context, *ListPreAuthKeysRequest) (*ListPreAuthKeysResponse, error) // --- Node start --- DebugCreateNode(context.Context, *DebugCreateNodeRequest) (*DebugCreateNodeResponse, error) @@ -391,6 +404,9 @@ func (UnimplementedHeadscaleServiceServer) CreatePreAuthKey(context.Context, *Cr func (UnimplementedHeadscaleServiceServer) ExpirePreAuthKey(context.Context, *ExpirePreAuthKeyRequest) (*ExpirePreAuthKeyResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ExpirePreAuthKey not implemented") } +func (UnimplementedHeadscaleServiceServer) DeletePreAuthKey(context.Context, *DeletePreAuthKeyRequest) (*DeletePreAuthKeyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeletePreAuthKey not implemented") +} func (UnimplementedHeadscaleServiceServer) ListPreAuthKeys(context.Context, *ListPreAuthKeysRequest) (*ListPreAuthKeysResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListPreAuthKeys not implemented") } @@ -574,6 +590,24 @@ func _HeadscaleService_ExpirePreAuthKey_Handler(srv interface{}, ctx context.Con return interceptor(ctx, in, info, handler) } +func _HeadscaleService_DeletePreAuthKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeletePreAuthKeyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).DeletePreAuthKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: HeadscaleService_DeletePreAuthKey_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).DeletePreAuthKey(ctx, req.(*DeletePreAuthKeyRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _HeadscaleService_ListPreAuthKeys_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ListPreAuthKeysRequest) if err := dec(in); err != nil { @@ -929,6 +963,10 @@ var HeadscaleService_ServiceDesc = grpc.ServiceDesc{ MethodName: "ExpirePreAuthKey", Handler: _HeadscaleService_ExpirePreAuthKey_Handler, }, + { + MethodName: "DeletePreAuthKey", + Handler: _HeadscaleService_DeletePreAuthKey_Handler, + }, { MethodName: "ListPreAuthKeys", Handler: _HeadscaleService_ListPreAuthKeys_Handler, diff --git a/gen/go/headscale/v1/preauthkey.pb.go b/gen/go/headscale/v1/preauthkey.pb.go index 661f170d..ecf016a0 100644 --- a/gen/go/headscale/v1/preauthkey.pb.go +++ b/gen/go/headscale/v1/preauthkey.pb.go @@ -338,6 +338,94 @@ func (*ExpirePreAuthKeyResponse) Descriptor() ([]byte, []int) { return file_headscale_v1_preauthkey_proto_rawDescGZIP(), []int{4} } +type DeletePreAuthKeyRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + User uint64 `protobuf:"varint,1,opt,name=user,proto3" json:"user,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeletePreAuthKeyRequest) Reset() { + *x = DeletePreAuthKeyRequest{} + mi := &file_headscale_v1_preauthkey_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeletePreAuthKeyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeletePreAuthKeyRequest) ProtoMessage() {} + +func (x *DeletePreAuthKeyRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_preauthkey_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeletePreAuthKeyRequest.ProtoReflect.Descriptor instead. +func (*DeletePreAuthKeyRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_preauthkey_proto_rawDescGZIP(), []int{5} +} + +func (x *DeletePreAuthKeyRequest) GetUser() uint64 { + if x != nil { + return x.User + } + return 0 +} + +func (x *DeletePreAuthKeyRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +type DeletePreAuthKeyResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeletePreAuthKeyResponse) Reset() { + *x = DeletePreAuthKeyResponse{} + mi := &file_headscale_v1_preauthkey_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeletePreAuthKeyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeletePreAuthKeyResponse) ProtoMessage() {} + +func (x *DeletePreAuthKeyResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_preauthkey_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeletePreAuthKeyResponse.ProtoReflect.Descriptor instead. +func (*DeletePreAuthKeyResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_preauthkey_proto_rawDescGZIP(), []int{6} +} + type ListPreAuthKeysRequest struct { state protoimpl.MessageState `protogen:"open.v1"` User uint64 `protobuf:"varint,1,opt,name=user,proto3" json:"user,omitempty"` @@ -347,7 +435,7 @@ type ListPreAuthKeysRequest struct { func (x *ListPreAuthKeysRequest) Reset() { *x = ListPreAuthKeysRequest{} - mi := &file_headscale_v1_preauthkey_proto_msgTypes[5] + mi := &file_headscale_v1_preauthkey_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -359,7 +447,7 @@ func (x *ListPreAuthKeysRequest) String() string { func (*ListPreAuthKeysRequest) ProtoMessage() {} func (x *ListPreAuthKeysRequest) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_preauthkey_proto_msgTypes[5] + mi := &file_headscale_v1_preauthkey_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -372,7 +460,7 @@ func (x *ListPreAuthKeysRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListPreAuthKeysRequest.ProtoReflect.Descriptor instead. func (*ListPreAuthKeysRequest) Descriptor() ([]byte, []int) { - return file_headscale_v1_preauthkey_proto_rawDescGZIP(), []int{5} + return file_headscale_v1_preauthkey_proto_rawDescGZIP(), []int{7} } func (x *ListPreAuthKeysRequest) GetUser() uint64 { @@ -391,7 +479,7 @@ type ListPreAuthKeysResponse struct { func (x *ListPreAuthKeysResponse) Reset() { *x = ListPreAuthKeysResponse{} - mi := &file_headscale_v1_preauthkey_proto_msgTypes[6] + mi := &file_headscale_v1_preauthkey_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -403,7 +491,7 @@ func (x *ListPreAuthKeysResponse) String() string { func (*ListPreAuthKeysResponse) ProtoMessage() {} func (x *ListPreAuthKeysResponse) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_preauthkey_proto_msgTypes[6] + mi := &file_headscale_v1_preauthkey_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -416,7 +504,7 @@ func (x *ListPreAuthKeysResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListPreAuthKeysResponse.ProtoReflect.Descriptor instead. func (*ListPreAuthKeysResponse) Descriptor() ([]byte, []int) { - return file_headscale_v1_preauthkey_proto_rawDescGZIP(), []int{6} + return file_headscale_v1_preauthkey_proto_rawDescGZIP(), []int{8} } func (x *ListPreAuthKeysResponse) GetPreAuthKeys() []*PreAuthKey { @@ -459,7 +547,11 @@ const file_headscale_v1_preauthkey_proto_rawDesc = "" + "\x17ExpirePreAuthKeyRequest\x12\x12\n" + "\x04user\x18\x01 \x01(\x04R\x04user\x12\x10\n" + "\x03key\x18\x02 \x01(\tR\x03key\"\x1a\n" + - "\x18ExpirePreAuthKeyResponse\",\n" + + "\x18ExpirePreAuthKeyResponse\"?\n" + + "\x17DeletePreAuthKeyRequest\x12\x12\n" + + "\x04user\x18\x01 \x01(\x04R\x04user\x12\x10\n" + + "\x03key\x18\x02 \x01(\tR\x03key\"\x1a\n" + + "\x18DeletePreAuthKeyResponse\",\n" + "\x16ListPreAuthKeysRequest\x12\x12\n" + "\x04user\x18\x01 \x01(\x04R\x04user\"W\n" + "\x17ListPreAuthKeysResponse\x12<\n" + @@ -477,30 +569,32 @@ func file_headscale_v1_preauthkey_proto_rawDescGZIP() []byte { return file_headscale_v1_preauthkey_proto_rawDescData } -var file_headscale_v1_preauthkey_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_headscale_v1_preauthkey_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_headscale_v1_preauthkey_proto_goTypes = []any{ (*PreAuthKey)(nil), // 0: headscale.v1.PreAuthKey (*CreatePreAuthKeyRequest)(nil), // 1: headscale.v1.CreatePreAuthKeyRequest (*CreatePreAuthKeyResponse)(nil), // 2: headscale.v1.CreatePreAuthKeyResponse (*ExpirePreAuthKeyRequest)(nil), // 3: headscale.v1.ExpirePreAuthKeyRequest (*ExpirePreAuthKeyResponse)(nil), // 4: headscale.v1.ExpirePreAuthKeyResponse - (*ListPreAuthKeysRequest)(nil), // 5: headscale.v1.ListPreAuthKeysRequest - (*ListPreAuthKeysResponse)(nil), // 6: headscale.v1.ListPreAuthKeysResponse - (*User)(nil), // 7: headscale.v1.User - (*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp + (*DeletePreAuthKeyRequest)(nil), // 5: headscale.v1.DeletePreAuthKeyRequest + (*DeletePreAuthKeyResponse)(nil), // 6: headscale.v1.DeletePreAuthKeyResponse + (*ListPreAuthKeysRequest)(nil), // 7: headscale.v1.ListPreAuthKeysRequest + (*ListPreAuthKeysResponse)(nil), // 8: headscale.v1.ListPreAuthKeysResponse + (*User)(nil), // 9: headscale.v1.User + (*timestamppb.Timestamp)(nil), // 10: google.protobuf.Timestamp } var file_headscale_v1_preauthkey_proto_depIdxs = []int32{ - 7, // 0: headscale.v1.PreAuthKey.user:type_name -> headscale.v1.User - 8, // 1: headscale.v1.PreAuthKey.expiration:type_name -> google.protobuf.Timestamp - 8, // 2: headscale.v1.PreAuthKey.created_at:type_name -> google.protobuf.Timestamp - 8, // 3: headscale.v1.CreatePreAuthKeyRequest.expiration:type_name -> google.protobuf.Timestamp - 0, // 4: headscale.v1.CreatePreAuthKeyResponse.pre_auth_key:type_name -> headscale.v1.PreAuthKey - 0, // 5: headscale.v1.ListPreAuthKeysResponse.pre_auth_keys:type_name -> headscale.v1.PreAuthKey - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 9, // 0: headscale.v1.PreAuthKey.user:type_name -> headscale.v1.User + 10, // 1: headscale.v1.PreAuthKey.expiration:type_name -> google.protobuf.Timestamp + 10, // 2: headscale.v1.PreAuthKey.created_at:type_name -> google.protobuf.Timestamp + 10, // 3: headscale.v1.CreatePreAuthKeyRequest.expiration:type_name -> google.protobuf.Timestamp + 0, // 4: headscale.v1.CreatePreAuthKeyResponse.pre_auth_key:type_name -> headscale.v1.PreAuthKey + 0, // 5: headscale.v1.ListPreAuthKeysResponse.pre_auth_keys:type_name -> headscale.v1.PreAuthKey + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_headscale_v1_preauthkey_proto_init() } @@ -515,7 +609,7 @@ func file_headscale_v1_preauthkey_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_headscale_v1_preauthkey_proto_rawDesc), len(file_headscale_v1_preauthkey_proto_rawDesc)), NumEnums: 0, - NumMessages: 7, + NumMessages: 9, NumExtensions: 0, NumServices: 0, }, diff --git a/gen/openapiv2/headscale/v1/headscale.swagger.json b/gen/openapiv2/headscale/v1/headscale.swagger.json index 5b163878..0791e8ca 100644 --- a/gen/openapiv2/headscale/v1/headscale.swagger.json +++ b/gen/openapiv2/headscale/v1/headscale.swagger.json @@ -579,6 +579,41 @@ "HeadscaleService" ] }, + "delete": { + "operationId": "HeadscaleService_DeletePreAuthKey", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1DeletePreAuthKeyResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "user", + "in": "query", + "required": false, + "type": "string", + "format": "uint64" + }, + { + "name": "key", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "HeadscaleService" + ] + }, "post": { "summary": "--- PreAuthKeys start ---", "operationId": "HeadscaleService_CreatePreAuthKey", @@ -981,6 +1016,9 @@ "v1DeleteNodeResponse": { "type": "object" }, + "v1DeletePreAuthKeyResponse": { + "type": "object" + }, "v1DeleteUserResponse": { "type": "object" }, diff --git a/hscontrol/auth_test.go b/hscontrol/auth_test.go index c142bf9d..b4ab8f16 100644 --- a/hscontrol/auth_test.go +++ b/hscontrol/auth_test.go @@ -3196,6 +3196,93 @@ func TestNodeReregistrationWithExpiredPreAuthKey(t *testing.T) { assert.Contains(t, err.Error(), "authkey expired", "error should mention key expiration") } +// TestIssue2830_ExistingNodeReregistersWithExpiredKey tests the fix for issue #2830. +// When a node is already registered and the pre-auth key expires, the node should +// still be able to re-register (e.g., after a container restart) using the same +// expired key. The key was only needed for initial authentication. +func TestIssue2830_ExistingNodeReregistersWithExpiredKey(t *testing.T) { + t.Parallel() + + app := createTestApp(t) + + user := app.state.CreateUserForTest("test-user") + + // Create a valid key (will expire it later) + expiry := time.Now().Add(1 * time.Hour) + pak, err := app.state.CreatePreAuthKey(types.UserID(user.ID), false, false, &expiry, nil) + require.NoError(t, err) + + machineKey := key.NewMachine() + nodeKey := key.NewNode() + + // Register the node initially (key is still valid) + req := tailcfg.RegisterRequest{ + Auth: &tailcfg.RegisterResponseAuth{ + AuthKey: pak.Key, + }, + NodeKey: nodeKey.Public(), + Hostinfo: &tailcfg.Hostinfo{ + Hostname: "issue2830-node", + }, + Expiry: time.Now().Add(24 * time.Hour), + } + + resp, err := app.handleRegister(context.Background(), req, machineKey.Public()) + require.NoError(t, err, "initial registration should succeed") + require.NotNil(t, resp) + require.True(t, resp.MachineAuthorized, "node should be authorized after initial registration") + + // Verify node was created + allNodes := app.state.ListNodes() + require.Equal(t, 1, allNodes.Len()) + initialNodeID := allNodes.At(0).ID() + + // Now expire the key by updating it in the database to have an expiry in the past. + // This simulates the real-world scenario where a key expires after initial registration. + pastExpiry := time.Now().Add(-1 * time.Hour) + err = app.state.DB().DB.Model(&types.PreAuthKey{}). + Where("id = ?", pak.ID). + Update("expiration", pastExpiry).Error + require.NoError(t, err, "should be able to update key expiration") + + // Reload the key to verify it's now expired + expiredPak, err := app.state.GetPreAuthKey(pak.Key) + require.NoError(t, err) + require.NotNil(t, expiredPak.Expiration) + require.True(t, expiredPak.Expiration.Before(time.Now()), "key should be expired") + + // Verify the expired key would fail validation + err = expiredPak.Validate() + require.Error(t, err, "key should fail validation when expired") + require.Contains(t, err.Error(), "authkey expired") + + // Attempt to re-register with the SAME key (now expired). + // This should SUCCEED because: + // - The node already exists with the same MachineKey and User + // - The fix allows existing nodes to re-register even with expired keys + // - The key was only needed for initial authentication + req2 := tailcfg.RegisterRequest{ + Auth: &tailcfg.RegisterResponseAuth{ + AuthKey: pak.Key, // Same key as initial registration (now expired) + }, + NodeKey: nodeKey.Public(), // Same NodeKey as initial registration + Hostinfo: &tailcfg.Hostinfo{ + Hostname: "issue2830-node", + }, + Expiry: time.Now().Add(24 * time.Hour), + } + + resp2, err := app.handleRegister(context.Background(), req2, machineKey.Public()) + require.NoError(t, err, "re-registration should succeed even with expired key for existing node") + assert.NotNil(t, resp2) + assert.True(t, resp2.MachineAuthorized, "node should remain authorized after re-registration") + + // Verify we still have only one node (re-registered, not created new) + allNodes = app.state.ListNodes() + require.Equal(t, 1, allNodes.Len(), "should have exactly one node (re-registered)") + assert.Equal(t, initialNodeID, allNodes.At(0).ID(), "node ID should not change on re-registration") +} + // TestGitHubIssue2830_ExistingNodeCanReregisterWithUsedPreAuthKey tests that an existing node // can re-register using a pre-auth key that's already marked as Used=true, as long as: // 1. The node is re-registering with the same MachineKey it originally used diff --git a/hscontrol/db/preauth_keys.go b/hscontrol/db/preauth_keys.go index 00260966..8580c5b4 100644 --- a/hscontrol/db/preauth_keys.go +++ b/hscontrol/db/preauth_keys.go @@ -268,9 +268,19 @@ func GetPreAuthKey(tx *gorm.DB, key string) (*types.PreAuthKey, error) { } // DestroyPreAuthKey destroys a preauthkey. Returns error if the PreAuthKey -// does not exist. +// does not exist. This also clears the auth_key_id on any nodes that reference +// this key. func DestroyPreAuthKey(tx *gorm.DB, pak types.PreAuthKey) error { return tx.Transaction(func(db *gorm.DB) error { + // First, clear the foreign key reference on any nodes using this key + err := db.Model(&types.Node{}). + Where("auth_key_id = ?", pak.ID). + Update("auth_key_id", nil).Error + if err != nil { + return fmt.Errorf("failed to clear auth_key_id on nodes: %w", err) + } + + // Then delete the pre-auth key if result := db.Unscoped().Delete(pak); result.Error != nil { return result.Error } @@ -285,6 +295,12 @@ func (hsdb *HSDatabase) ExpirePreAuthKey(k *types.PreAuthKey) error { }) } +func (hsdb *HSDatabase) DeletePreAuthKey(k *types.PreAuthKey) error { + return hsdb.Write(func(tx *gorm.DB) error { + return DestroyPreAuthKey(tx, *k) + }) +} + // UsePreAuthKey marks a PreAuthKey as used. func UsePreAuthKey(tx *gorm.DB, k *types.PreAuthKey) error { err := tx.Model(k).Update("used", true).Error diff --git a/hscontrol/grpcv1.go b/hscontrol/grpcv1.go index ea37226c..b43bdf43 100644 --- a/hscontrol/grpcv1.go +++ b/hscontrol/grpcv1.go @@ -206,6 +206,27 @@ func (api headscaleV1APIServer) ExpirePreAuthKey( return &v1.ExpirePreAuthKeyResponse{}, nil } +func (api headscaleV1APIServer) DeletePreAuthKey( + ctx context.Context, + request *v1.DeletePreAuthKeyRequest, +) (*v1.DeletePreAuthKeyResponse, error) { + preAuthKey, err := api.h.state.GetPreAuthKey(request.Key) + if err != nil { + return nil, err + } + + if uint64(preAuthKey.User.ID) != request.GetUser() { + return nil, fmt.Errorf("preauth key does not belong to user") + } + + err = api.h.state.DeletePreAuthKey(preAuthKey) + if err != nil { + return nil, err + } + + return &v1.DeletePreAuthKeyResponse{}, nil +} + func (api headscaleV1APIServer) ListPreAuthKeys( ctx context.Context, request *v1.ListPreAuthKeysRequest, diff --git a/hscontrol/state/state.go b/hscontrol/state/state.go index d7eb2724..ebdf40f4 100644 --- a/hscontrol/state/state.go +++ b/hscontrol/state/state.go @@ -976,6 +976,11 @@ func (s *State) ExpirePreAuthKey(preAuthKey *types.PreAuthKey) error { return s.db.ExpirePreAuthKey(preAuthKey) } +// DeletePreAuthKey permanently deletes a pre-authentication key. +func (s *State) DeletePreAuthKey(preAuthKey *types.PreAuthKey) error { + return s.db.DeletePreAuthKey(preAuthKey) +} + // GetRegistrationCacheEntry retrieves a node registration from cache. func (s *State) GetRegistrationCacheEntry(id types.RegistrationID) (*types.RegisterNode, bool) { entry, found := s.registrationCache.Get(id) @@ -1313,11 +1318,18 @@ func (s *State) HandleNodeFromPreAuthKey( // key was only needed for initial authentication. NodeKey rotation requires validation. existingNodeSameUser, existsSameUser := s.nodeStore.GetNodeByMachineKey(machineKey, types.UserID(pak.User.ID)) - // Skip validation only if both the AuthKeyID and NodeKey match (not a rotation). - isExistingNodeReregistering := existsSameUser && existingNodeSameUser.Valid() && - existingNodeSameUser.AuthKey().Valid() && - existingNodeSameUser.AuthKeyID().Valid() && - existingNodeSameUser.AuthKeyID().Get() == pak.ID + // For existing nodes, skip validation if: + // 1. MachineKey matches (cryptographic proof of machine identity) + // 2. User matches (from the PAK being used) + // 3. Not a NodeKey rotation (rotation requires fresh validation) + // + // Security: MachineKey is the cryptographic identity. If someone has the MachineKey, + // they control the machine. The PAK was only needed to authorize initial join. + // We don't check which specific PAK was used originally because: + // - Container restarts may use different PAKs (e.g., env var changed) + // - Original PAK may be deleted + // - MachineKey + User is sufficient to prove this is the same node + isExistingNodeReregistering := existsSameUser && existingNodeSameUser.Valid() // Check if this is a NodeKey rotation (different NodeKey) isNodeKeyRotation := existsSameUser && existingNodeSameUser.Valid() && diff --git a/integration/auth_key_test.go b/integration/auth_key_test.go index 12a5bf67..af725ffa 100644 --- a/integration/auth_key_test.go +++ b/integration/auth_key_test.go @@ -455,3 +455,95 @@ func TestAuthKeyLogoutAndReloginSameUserExpiredKey(t *testing.T) { }) } } + +// TestAuthKeyDeleteKey tests Issue #2830: node with deleted auth key should still reconnect. +// Scenario from user report: "create node, delete the auth key, restart to validate it can connect" +// Steps: +// 1. Create node with auth key +// 2. DELETE the auth key from database (completely remove it) +// 3. Restart node - should successfully reconnect using MachineKey identity. +func TestAuthKeyDeleteKey(t *testing.T) { + IntegrationSkip(t) + + // Create scenario with NO nodes - we'll create the node manually so we can capture the auth key + scenario, err := NewScenario(ScenarioSpec{ + NodesPerUser: 0, // No nodes created automatically + Users: []string{"user1"}, + }) + + require.NoError(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("delkey"), hsic.WithTLS(), hsic.WithDERPAsIP()) + requireNoErrHeadscaleEnv(t, err) + + headscale, err := scenario.Headscale() + requireNoErrGetHeadscale(t, err) + + // Get the user + userMap, err := headscale.MapUsers() + require.NoError(t, err) + + userID := userMap["user1"].GetId() + + // Create a pre-auth key - we keep the full key string before it gets redacted + authKey, err := scenario.CreatePreAuthKey(userID, false, false) + require.NoError(t, err) + + authKeyString := authKey.GetKey() + authKeyID := authKey.GetId() + t.Logf("Created pre-auth key ID %d: %s", authKeyID, authKeyString) + + // Create a tailscale client and log it in with the auth key + client, err := scenario.CreateTailscaleNode( + "head", + tsic.WithNetwork(scenario.networks[scenario.testDefaultNetwork]), + ) + require.NoError(t, err) + + err = client.Login(headscale.GetEndpoint(), authKeyString) + require.NoError(t, err) + + // Wait for the node to be registered + var user1Nodes []*v1.Node + + assert.EventuallyWithT(t, func(c *assert.CollectT) { + var err error + + user1Nodes, err = headscale.ListNodes("user1") + assert.NoError(c, err) + assert.Len(c, user1Nodes, 1) + }, 30*time.Second, 500*time.Millisecond, "waiting for node to be registered") + + nodeID := user1Nodes[0].GetId() + nodeName := user1Nodes[0].GetName() + t.Logf("Node %d (%s) created successfully with auth_key_id=%d", nodeID, nodeName, authKeyID) + + // Verify node is online + requireAllClientsOnline(t, headscale, []types.NodeID{types.NodeID(nodeID)}, true, "node should be online initially", 120*time.Second) + + // DELETE the pre-auth key using the API + t.Logf("Deleting pre-auth key ID %d using API", authKeyID) + + err = headscale.DeleteAuthKey(userID, authKeyString) + require.NoError(t, err) + t.Logf("Successfully deleted auth key") + + // Simulate node restart (down + up) + t.Logf("Restarting node after deleting its auth key") + + err = client.Down() + require.NoError(t, err) + + time.Sleep(3 * time.Second) + + err = client.Up() + require.NoError(t, err) + + // Verify node comes back online + // This will FAIL without the fix because auth key validation will reject deleted key + // With the fix, MachineKey identity allows reconnection even with deleted key + requireAllClientsOnline(t, headscale, []types.NodeID{types.NodeID(nodeID)}, true, "node should reconnect after restart despite deleted key", 120*time.Second) + + t.Logf("✓ Node successfully reconnected after its auth key was deleted") +} diff --git a/integration/control.go b/integration/control.go index e0e67e09..fea2e1f2 100644 --- a/integration/control.go +++ b/integration/control.go @@ -24,6 +24,7 @@ type ControlServer interface { WaitForRunning() error CreateUser(user string) (*v1.User, error) CreateAuthKey(user uint64, reusable bool, ephemeral bool) (*v1.PreAuthKey, error) + DeleteAuthKey(user uint64, key string) error ListNodes(users ...string) ([]*v1.Node, error) DeleteNode(nodeID uint64) error NodesByUser() (map[string][]*v1.Node, error) diff --git a/integration/hsic/hsic.go b/integration/hsic/hsic.go index 67b57896..92246b3f 100644 --- a/integration/hsic/hsic.go +++ b/integration/hsic/hsic.go @@ -1052,6 +1052,34 @@ func (t *HeadscaleInContainer) CreateAuthKey( return &preAuthKey, nil } +// DeleteAuthKey deletes an "authorisation key" for a User. +func (t *HeadscaleInContainer) DeleteAuthKey( + user uint64, + key string, +) error { + command := []string{ + "headscale", + "--user", + strconv.FormatUint(user, 10), + "preauthkeys", + "delete", + key, + "--output", + "json", + } + + _, _, err := dockertestutil.ExecuteCommand( + t.container, + command, + []string{}, + ) + if err != nil { + return fmt.Errorf("failed to execute delete auth key command: %w", err) + } + + return nil +} + // ListNodes lists the currently registered Nodes in headscale. // Optionally a list of usernames can be passed to get users for // specific users. diff --git a/integration/route_test.go b/integration/route_test.go index 867aa9b7..18abc9a8 100644 --- a/integration/route_test.go +++ b/integration/route_test.go @@ -20,6 +20,7 @@ import ( "github.com/juanfont/headscale/hscontrol/types" "github.com/juanfont/headscale/hscontrol/util" "github.com/juanfont/headscale/integration/hsic" + "github.com/juanfont/headscale/integration/integrationutil" "github.com/juanfont/headscale/integration/tsic" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -2333,6 +2334,12 @@ func TestAutoApproveMultiNetwork(t *testing.T) { require.NoError(t, err) scenario.runHeadscaleRegister("user1", body) + + // Wait for the client to sync with the server after webauth registration. + // Unlike authkey login which blocks until complete, webauth registration + // happens on the server side and the client needs time to receive the network map. + err = routerUsernet1.WaitForRunning(integrationutil.PeerSyncTimeout()) + require.NoError(t, err, "webauth client failed to reach Running state") } else { userMap, err := headscale.MapUsers() require.NoError(t, err) diff --git a/proto/headscale/v1/headscale.proto b/proto/headscale/v1/headscale.proto index 69882e43..5e556255 100644 --- a/proto/headscale/v1/headscale.proto +++ b/proto/headscale/v1/headscale.proto @@ -55,6 +55,13 @@ service HeadscaleService { }; } + rpc DeletePreAuthKey(DeletePreAuthKeyRequest) + returns (DeletePreAuthKeyResponse) { + option (google.api.http) = { + delete : "/api/v1/preauthkey" + }; + } + rpc ListPreAuthKeys(ListPreAuthKeysRequest) returns (ListPreAuthKeysResponse) { option (google.api.http) = { diff --git a/proto/headscale/v1/preauthkey.proto b/proto/headscale/v1/preauthkey.proto index de75af11..fa2c5188 100644 --- a/proto/headscale/v1/preauthkey.proto +++ b/proto/headscale/v1/preauthkey.proto @@ -34,6 +34,13 @@ message ExpirePreAuthKeyRequest { message ExpirePreAuthKeyResponse {} +message DeletePreAuthKeyRequest { + uint64 user = 1; + string key = 2; +} + +message DeletePreAuthKeyResponse {} + message ListPreAuthKeysRequest { uint64 user = 1; } message ListPreAuthKeysResponse { repeated PreAuthKey pre_auth_keys = 1; }