From 30734356c7192a09277c6ee0dcde390b872125b2 Mon Sep 17 00:00:00 2001 From: hopleus Date: Wed, 16 Oct 2024 15:45:13 +0300 Subject: [PATCH 01/22] Added approved fields and procedure to proto files --- gen/go/headscale/v1/headscale.pb.go | 437 +++++++++-------- gen/go/headscale/v1/headscale.pb.gw.go | 103 ++++ gen/go/headscale/v1/headscale_grpc.pb.go | 37 ++ gen/go/headscale/v1/node.pb.go | 464 ++++++++++++------ gen/go/headscale/v1/preauthkey.pb.go | 122 +++-- .../headscale/v1/headscale.swagger.json | 48 ++ proto/headscale/v1/headscale.proto | 6 + proto/headscale/v1/node.proto | 9 + proto/headscale/v1/preauthkey.proto | 30 +- 9 files changed, 815 insertions(+), 441 deletions(-) diff --git a/gen/go/headscale/v1/headscale.pb.go b/gen/go/headscale/v1/headscale.pb.go index d923342e212..7034dca7bb0 100644 --- a/gen/go/headscale/v1/headscale.pb.go +++ b/gen/go/headscale/v1/headscale.pb.go @@ -37,7 +37,7 @@ var file_headscale_v1_headscale_proto_rawDesc = []byte{ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xd2, 0x1a, 0x0a, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xce, 0x1b, 0x0a, 0x10, 0x48, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x63, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, @@ -133,128 +133,135 @@ var file_headscale_v1_headscale_proto_rawDesc = []byte{ 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x2a, 0x16, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, - 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x7b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x76, - 0x0a, 0x0a, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1f, 0x2e, 0x68, - 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, - 0x72, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, - 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, - 0x69, 0x72, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, - 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x7b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, - 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x12, 0x81, 0x01, 0x0a, 0x0a, 0x52, 0x65, 0x6e, 0x61, 0x6d, - 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1f, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, - 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x4e, 0x6f, 0x64, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x30, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2a, - 0x22, 0x28, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x7b, - 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x2f, - 0x7b, 0x6e, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0x62, 0x0a, 0x09, 0x4c, 0x69, - 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, - 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, - 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0e, - 0x12, 0x0c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x71, - 0x0a, 0x08, 0x4d, 0x6f, 0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x2e, 0x68, 0x65, 0x61, - 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x76, 0x65, 0x4e, 0x6f, - 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x68, 0x65, 0x61, 0x64, - 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x76, 0x65, 0x4e, 0x6f, 0x64, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x20, 0x3a, 0x01, 0x2a, 0x22, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, - 0x64, 0x65, 0x2f, 0x7b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x75, 0x73, 0x65, - 0x72, 0x12, 0x80, 0x01, 0x0a, 0x0f, 0x42, 0x61, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x6c, 0x4e, 0x6f, - 0x64, 0x65, 0x49, 0x50, 0x73, 0x12, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, - 0x65, 0x49, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x65, - 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x66, - 0x69, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x18, 0x2f, 0x61, 0x70, 0x69, - 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x66, 0x69, 0x6c, - 0x6c, 0x69, 0x70, 0x73, 0x12, 0x64, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, - 0x73, 0x12, 0x1e, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1f, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x61, 0x70, 0x69, - 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x7c, 0x0a, 0x0b, 0x45, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x20, 0x2e, 0x68, 0x65, 0x61, 0x64, - 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x52, - 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x68, 0x65, - 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, 0x20, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, - 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x64, - 0x7d, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x80, 0x01, 0x0a, 0x0c, 0x44, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, - 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, - 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, - 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x22, 0x21, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, - 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, - 0x69, 0x64, 0x7d, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x7f, 0x0a, 0x0d, 0x47, - 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x68, + 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x7b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x7a, + 0x0a, 0x0b, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x20, 0x2e, + 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, + 0x72, 0x6f, 0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1e, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x7b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, + 0x64, 0x7d, 0x2f, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x12, 0x76, 0x0a, 0x0a, 0x45, 0x78, + 0x70, 0x69, 0x72, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1f, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4e, 0x6f, + 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x68, 0x65, 0x61, 0x64, + 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4e, + 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x1f, 0x22, 0x1d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, + 0x65, 0x2f, 0x7b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x65, 0x78, 0x70, 0x69, + 0x72, 0x65, 0x12, 0x81, 0x01, 0x0a, 0x0a, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x4e, 0x6f, 0x64, + 0x65, 0x12, 0x1f, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x30, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2a, 0x22, 0x28, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x7b, 0x6e, 0x6f, 0x64, 0x65, + 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x2f, 0x7b, 0x6e, 0x65, 0x77, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0x62, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, + 0x64, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0e, 0x12, 0x0c, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x71, 0x0a, 0x08, 0x4d, 0x6f, + 0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, + 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x3a, 0x01, 0x2a, + 0x22, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x7b, + 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x12, 0x80, 0x01, + 0x0a, 0x0f, 0x42, 0x61, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x50, + 0x73, 0x12, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x50, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, + 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x6c, 0x4e, + 0x6f, 0x64, 0x65, 0x49, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x18, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, + 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x6c, 0x69, 0x70, 0x73, + 0x12, 0x64, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x1e, 0x2e, + 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, + 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, + 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x7c, 0x0a, 0x0b, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x20, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, + 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x22, 0x22, 0x20, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x12, 0x80, 0x01, 0x0a, 0x0c, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x23, 0x22, 0x21, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, + 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x7f, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4e, 0x6f, + 0x64, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, - 0x6f, 0x64, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x23, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x7b, 0x6e, 0x6f, 0x64, - 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x75, 0x0a, 0x0b, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x20, 0x2e, 0x68, 0x65, - 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, - 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x2a, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, - 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, - 0x69, 0x64, 0x7d, 0x12, 0x70, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, - 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, - 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, - 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x22, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x61, - 0x70, 0x69, 0x6b, 0x65, 0x79, 0x12, 0x77, 0x0a, 0x0c, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, - 0x70, 0x69, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, - 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, - 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, - 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x2f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x12, 0x6a, - 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x20, 0x2e, - 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x61, 0x70, 0x69, - 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x12, 0x76, 0x0a, 0x0c, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, - 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, - 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x2a, 0x17, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x2f, 0x7b, 0x70, 0x72, 0x65, 0x66, 0x69, - 0x78, 0x7d, 0x12, 0x64, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, - 0x1e, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1f, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, - 0x31, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x67, 0x0a, 0x09, 0x53, 0x65, 0x74, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1e, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, + 0x6f, 0x64, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x7b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, + 0x7d, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x75, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x20, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, + 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, + 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1b, 0x2a, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x12, + 0x70, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x12, + 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, - 0x2a, 0x1a, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, - 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x2a, 0x22, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, + 0x79, 0x12, 0x77, 0x0a, 0x0c, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, + 0x79, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, + 0x3a, 0x01, 0x2a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, + 0x6b, 0x65, 0x79, 0x2f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x12, 0x6a, 0x0a, 0x0b, 0x4c, 0x69, + 0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x20, 0x2e, 0x68, 0x65, 0x61, 0x64, + 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x69, + 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x68, 0x65, + 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, + 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, + 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x12, 0x76, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, + 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, + 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, + 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, + 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x2a, 0x17, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x61, + 0x70, 0x69, 0x6b, 0x65, 0x79, 0x2f, 0x7b, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x7d, 0x12, 0x64, + 0x0a, 0x09, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1e, 0x2e, 0x68, 0x65, + 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x68, 0x65, + 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x12, 0x67, 0x0a, 0x09, 0x53, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x12, 0x1e, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1f, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x1a, 0x0e, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x42, 0x29, 0x5a, + 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x75, 0x61, 0x6e, + 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x67, + 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var file_headscale_v1_headscale_proto_goTypes = []any{ @@ -271,51 +278,53 @@ var file_headscale_v1_headscale_proto_goTypes = []any{ (*SetTagsRequest)(nil), // 10: headscale.v1.SetTagsRequest (*RegisterNodeRequest)(nil), // 11: headscale.v1.RegisterNodeRequest (*DeleteNodeRequest)(nil), // 12: headscale.v1.DeleteNodeRequest - (*ExpireNodeRequest)(nil), // 13: headscale.v1.ExpireNodeRequest - (*RenameNodeRequest)(nil), // 14: headscale.v1.RenameNodeRequest - (*ListNodesRequest)(nil), // 15: headscale.v1.ListNodesRequest - (*MoveNodeRequest)(nil), // 16: headscale.v1.MoveNodeRequest - (*BackfillNodeIPsRequest)(nil), // 17: headscale.v1.BackfillNodeIPsRequest - (*GetRoutesRequest)(nil), // 18: headscale.v1.GetRoutesRequest - (*EnableRouteRequest)(nil), // 19: headscale.v1.EnableRouteRequest - (*DisableRouteRequest)(nil), // 20: headscale.v1.DisableRouteRequest - (*GetNodeRoutesRequest)(nil), // 21: headscale.v1.GetNodeRoutesRequest - (*DeleteRouteRequest)(nil), // 22: headscale.v1.DeleteRouteRequest - (*CreateApiKeyRequest)(nil), // 23: headscale.v1.CreateApiKeyRequest - (*ExpireApiKeyRequest)(nil), // 24: headscale.v1.ExpireApiKeyRequest - (*ListApiKeysRequest)(nil), // 25: headscale.v1.ListApiKeysRequest - (*DeleteApiKeyRequest)(nil), // 26: headscale.v1.DeleteApiKeyRequest - (*GetPolicyRequest)(nil), // 27: headscale.v1.GetPolicyRequest - (*SetPolicyRequest)(nil), // 28: headscale.v1.SetPolicyRequest - (*GetUserResponse)(nil), // 29: headscale.v1.GetUserResponse - (*CreateUserResponse)(nil), // 30: headscale.v1.CreateUserResponse - (*RenameUserResponse)(nil), // 31: headscale.v1.RenameUserResponse - (*DeleteUserResponse)(nil), // 32: headscale.v1.DeleteUserResponse - (*ListUsersResponse)(nil), // 33: headscale.v1.ListUsersResponse - (*CreatePreAuthKeyResponse)(nil), // 34: headscale.v1.CreatePreAuthKeyResponse - (*ExpirePreAuthKeyResponse)(nil), // 35: headscale.v1.ExpirePreAuthKeyResponse - (*ListPreAuthKeysResponse)(nil), // 36: headscale.v1.ListPreAuthKeysResponse - (*DebugCreateNodeResponse)(nil), // 37: headscale.v1.DebugCreateNodeResponse - (*GetNodeResponse)(nil), // 38: headscale.v1.GetNodeResponse - (*SetTagsResponse)(nil), // 39: headscale.v1.SetTagsResponse - (*RegisterNodeResponse)(nil), // 40: headscale.v1.RegisterNodeResponse - (*DeleteNodeResponse)(nil), // 41: headscale.v1.DeleteNodeResponse - (*ExpireNodeResponse)(nil), // 42: headscale.v1.ExpireNodeResponse - (*RenameNodeResponse)(nil), // 43: headscale.v1.RenameNodeResponse - (*ListNodesResponse)(nil), // 44: headscale.v1.ListNodesResponse - (*MoveNodeResponse)(nil), // 45: headscale.v1.MoveNodeResponse - (*BackfillNodeIPsResponse)(nil), // 46: headscale.v1.BackfillNodeIPsResponse - (*GetRoutesResponse)(nil), // 47: headscale.v1.GetRoutesResponse - (*EnableRouteResponse)(nil), // 48: headscale.v1.EnableRouteResponse - (*DisableRouteResponse)(nil), // 49: headscale.v1.DisableRouteResponse - (*GetNodeRoutesResponse)(nil), // 50: headscale.v1.GetNodeRoutesResponse - (*DeleteRouteResponse)(nil), // 51: headscale.v1.DeleteRouteResponse - (*CreateApiKeyResponse)(nil), // 52: headscale.v1.CreateApiKeyResponse - (*ExpireApiKeyResponse)(nil), // 53: headscale.v1.ExpireApiKeyResponse - (*ListApiKeysResponse)(nil), // 54: headscale.v1.ListApiKeysResponse - (*DeleteApiKeyResponse)(nil), // 55: headscale.v1.DeleteApiKeyResponse - (*GetPolicyResponse)(nil), // 56: headscale.v1.GetPolicyResponse - (*SetPolicyResponse)(nil), // 57: headscale.v1.SetPolicyResponse + (*ApproveNodeRequest)(nil), // 13: headscale.v1.ApproveNodeRequest + (*ExpireNodeRequest)(nil), // 14: headscale.v1.ExpireNodeRequest + (*RenameNodeRequest)(nil), // 15: headscale.v1.RenameNodeRequest + (*ListNodesRequest)(nil), // 16: headscale.v1.ListNodesRequest + (*MoveNodeRequest)(nil), // 17: headscale.v1.MoveNodeRequest + (*BackfillNodeIPsRequest)(nil), // 18: headscale.v1.BackfillNodeIPsRequest + (*GetRoutesRequest)(nil), // 19: headscale.v1.GetRoutesRequest + (*EnableRouteRequest)(nil), // 20: headscale.v1.EnableRouteRequest + (*DisableRouteRequest)(nil), // 21: headscale.v1.DisableRouteRequest + (*GetNodeRoutesRequest)(nil), // 22: headscale.v1.GetNodeRoutesRequest + (*DeleteRouteRequest)(nil), // 23: headscale.v1.DeleteRouteRequest + (*CreateApiKeyRequest)(nil), // 24: headscale.v1.CreateApiKeyRequest + (*ExpireApiKeyRequest)(nil), // 25: headscale.v1.ExpireApiKeyRequest + (*ListApiKeysRequest)(nil), // 26: headscale.v1.ListApiKeysRequest + (*DeleteApiKeyRequest)(nil), // 27: headscale.v1.DeleteApiKeyRequest + (*GetPolicyRequest)(nil), // 28: headscale.v1.GetPolicyRequest + (*SetPolicyRequest)(nil), // 29: headscale.v1.SetPolicyRequest + (*GetUserResponse)(nil), // 30: headscale.v1.GetUserResponse + (*CreateUserResponse)(nil), // 31: headscale.v1.CreateUserResponse + (*RenameUserResponse)(nil), // 32: headscale.v1.RenameUserResponse + (*DeleteUserResponse)(nil), // 33: headscale.v1.DeleteUserResponse + (*ListUsersResponse)(nil), // 34: headscale.v1.ListUsersResponse + (*CreatePreAuthKeyResponse)(nil), // 35: headscale.v1.CreatePreAuthKeyResponse + (*ExpirePreAuthKeyResponse)(nil), // 36: headscale.v1.ExpirePreAuthKeyResponse + (*ListPreAuthKeysResponse)(nil), // 37: headscale.v1.ListPreAuthKeysResponse + (*DebugCreateNodeResponse)(nil), // 38: headscale.v1.DebugCreateNodeResponse + (*GetNodeResponse)(nil), // 39: headscale.v1.GetNodeResponse + (*SetTagsResponse)(nil), // 40: headscale.v1.SetTagsResponse + (*RegisterNodeResponse)(nil), // 41: headscale.v1.RegisterNodeResponse + (*DeleteNodeResponse)(nil), // 42: headscale.v1.DeleteNodeResponse + (*ApproveNodeResponse)(nil), // 43: headscale.v1.ApproveNodeResponse + (*ExpireNodeResponse)(nil), // 44: headscale.v1.ExpireNodeResponse + (*RenameNodeResponse)(nil), // 45: headscale.v1.RenameNodeResponse + (*ListNodesResponse)(nil), // 46: headscale.v1.ListNodesResponse + (*MoveNodeResponse)(nil), // 47: headscale.v1.MoveNodeResponse + (*BackfillNodeIPsResponse)(nil), // 48: headscale.v1.BackfillNodeIPsResponse + (*GetRoutesResponse)(nil), // 49: headscale.v1.GetRoutesResponse + (*EnableRouteResponse)(nil), // 50: headscale.v1.EnableRouteResponse + (*DisableRouteResponse)(nil), // 51: headscale.v1.DisableRouteResponse + (*GetNodeRoutesResponse)(nil), // 52: headscale.v1.GetNodeRoutesResponse + (*DeleteRouteResponse)(nil), // 53: headscale.v1.DeleteRouteResponse + (*CreateApiKeyResponse)(nil), // 54: headscale.v1.CreateApiKeyResponse + (*ExpireApiKeyResponse)(nil), // 55: headscale.v1.ExpireApiKeyResponse + (*ListApiKeysResponse)(nil), // 56: headscale.v1.ListApiKeysResponse + (*DeleteApiKeyResponse)(nil), // 57: headscale.v1.DeleteApiKeyResponse + (*GetPolicyResponse)(nil), // 58: headscale.v1.GetPolicyResponse + (*SetPolicyResponse)(nil), // 59: headscale.v1.SetPolicyResponse } var file_headscale_v1_headscale_proto_depIdxs = []int32{ 0, // 0: headscale.v1.HeadscaleService.GetUser:input_type -> headscale.v1.GetUserRequest @@ -331,53 +340,55 @@ var file_headscale_v1_headscale_proto_depIdxs = []int32{ 10, // 10: headscale.v1.HeadscaleService.SetTags:input_type -> headscale.v1.SetTagsRequest 11, // 11: headscale.v1.HeadscaleService.RegisterNode:input_type -> headscale.v1.RegisterNodeRequest 12, // 12: headscale.v1.HeadscaleService.DeleteNode:input_type -> headscale.v1.DeleteNodeRequest - 13, // 13: headscale.v1.HeadscaleService.ExpireNode:input_type -> headscale.v1.ExpireNodeRequest - 14, // 14: headscale.v1.HeadscaleService.RenameNode:input_type -> headscale.v1.RenameNodeRequest - 15, // 15: headscale.v1.HeadscaleService.ListNodes:input_type -> headscale.v1.ListNodesRequest - 16, // 16: headscale.v1.HeadscaleService.MoveNode:input_type -> headscale.v1.MoveNodeRequest - 17, // 17: headscale.v1.HeadscaleService.BackfillNodeIPs:input_type -> headscale.v1.BackfillNodeIPsRequest - 18, // 18: headscale.v1.HeadscaleService.GetRoutes:input_type -> headscale.v1.GetRoutesRequest - 19, // 19: headscale.v1.HeadscaleService.EnableRoute:input_type -> headscale.v1.EnableRouteRequest - 20, // 20: headscale.v1.HeadscaleService.DisableRoute:input_type -> headscale.v1.DisableRouteRequest - 21, // 21: headscale.v1.HeadscaleService.GetNodeRoutes:input_type -> headscale.v1.GetNodeRoutesRequest - 22, // 22: headscale.v1.HeadscaleService.DeleteRoute:input_type -> headscale.v1.DeleteRouteRequest - 23, // 23: headscale.v1.HeadscaleService.CreateApiKey:input_type -> headscale.v1.CreateApiKeyRequest - 24, // 24: headscale.v1.HeadscaleService.ExpireApiKey:input_type -> headscale.v1.ExpireApiKeyRequest - 25, // 25: headscale.v1.HeadscaleService.ListApiKeys:input_type -> headscale.v1.ListApiKeysRequest - 26, // 26: headscale.v1.HeadscaleService.DeleteApiKey:input_type -> headscale.v1.DeleteApiKeyRequest - 27, // 27: headscale.v1.HeadscaleService.GetPolicy:input_type -> headscale.v1.GetPolicyRequest - 28, // 28: headscale.v1.HeadscaleService.SetPolicy:input_type -> headscale.v1.SetPolicyRequest - 29, // 29: headscale.v1.HeadscaleService.GetUser:output_type -> headscale.v1.GetUserResponse - 30, // 30: headscale.v1.HeadscaleService.CreateUser:output_type -> headscale.v1.CreateUserResponse - 31, // 31: headscale.v1.HeadscaleService.RenameUser:output_type -> headscale.v1.RenameUserResponse - 32, // 32: headscale.v1.HeadscaleService.DeleteUser:output_type -> headscale.v1.DeleteUserResponse - 33, // 33: headscale.v1.HeadscaleService.ListUsers:output_type -> headscale.v1.ListUsersResponse - 34, // 34: headscale.v1.HeadscaleService.CreatePreAuthKey:output_type -> headscale.v1.CreatePreAuthKeyResponse - 35, // 35: headscale.v1.HeadscaleService.ExpirePreAuthKey:output_type -> headscale.v1.ExpirePreAuthKeyResponse - 36, // 36: headscale.v1.HeadscaleService.ListPreAuthKeys:output_type -> headscale.v1.ListPreAuthKeysResponse - 37, // 37: headscale.v1.HeadscaleService.DebugCreateNode:output_type -> headscale.v1.DebugCreateNodeResponse - 38, // 38: headscale.v1.HeadscaleService.GetNode:output_type -> headscale.v1.GetNodeResponse - 39, // 39: headscale.v1.HeadscaleService.SetTags:output_type -> headscale.v1.SetTagsResponse - 40, // 40: headscale.v1.HeadscaleService.RegisterNode:output_type -> headscale.v1.RegisterNodeResponse - 41, // 41: headscale.v1.HeadscaleService.DeleteNode:output_type -> headscale.v1.DeleteNodeResponse - 42, // 42: headscale.v1.HeadscaleService.ExpireNode:output_type -> headscale.v1.ExpireNodeResponse - 43, // 43: headscale.v1.HeadscaleService.RenameNode:output_type -> headscale.v1.RenameNodeResponse - 44, // 44: headscale.v1.HeadscaleService.ListNodes:output_type -> headscale.v1.ListNodesResponse - 45, // 45: headscale.v1.HeadscaleService.MoveNode:output_type -> headscale.v1.MoveNodeResponse - 46, // 46: headscale.v1.HeadscaleService.BackfillNodeIPs:output_type -> headscale.v1.BackfillNodeIPsResponse - 47, // 47: headscale.v1.HeadscaleService.GetRoutes:output_type -> headscale.v1.GetRoutesResponse - 48, // 48: headscale.v1.HeadscaleService.EnableRoute:output_type -> headscale.v1.EnableRouteResponse - 49, // 49: headscale.v1.HeadscaleService.DisableRoute:output_type -> headscale.v1.DisableRouteResponse - 50, // 50: headscale.v1.HeadscaleService.GetNodeRoutes:output_type -> headscale.v1.GetNodeRoutesResponse - 51, // 51: headscale.v1.HeadscaleService.DeleteRoute:output_type -> headscale.v1.DeleteRouteResponse - 52, // 52: headscale.v1.HeadscaleService.CreateApiKey:output_type -> headscale.v1.CreateApiKeyResponse - 53, // 53: headscale.v1.HeadscaleService.ExpireApiKey:output_type -> headscale.v1.ExpireApiKeyResponse - 54, // 54: headscale.v1.HeadscaleService.ListApiKeys:output_type -> headscale.v1.ListApiKeysResponse - 55, // 55: headscale.v1.HeadscaleService.DeleteApiKey:output_type -> headscale.v1.DeleteApiKeyResponse - 56, // 56: headscale.v1.HeadscaleService.GetPolicy:output_type -> headscale.v1.GetPolicyResponse - 57, // 57: headscale.v1.HeadscaleService.SetPolicy:output_type -> headscale.v1.SetPolicyResponse - 29, // [29:58] is the sub-list for method output_type - 0, // [0:29] is the sub-list for method input_type + 13, // 13: headscale.v1.HeadscaleService.ApproveNode:input_type -> headscale.v1.ApproveNodeRequest + 14, // 14: headscale.v1.HeadscaleService.ExpireNode:input_type -> headscale.v1.ExpireNodeRequest + 15, // 15: headscale.v1.HeadscaleService.RenameNode:input_type -> headscale.v1.RenameNodeRequest + 16, // 16: headscale.v1.HeadscaleService.ListNodes:input_type -> headscale.v1.ListNodesRequest + 17, // 17: headscale.v1.HeadscaleService.MoveNode:input_type -> headscale.v1.MoveNodeRequest + 18, // 18: headscale.v1.HeadscaleService.BackfillNodeIPs:input_type -> headscale.v1.BackfillNodeIPsRequest + 19, // 19: headscale.v1.HeadscaleService.GetRoutes:input_type -> headscale.v1.GetRoutesRequest + 20, // 20: headscale.v1.HeadscaleService.EnableRoute:input_type -> headscale.v1.EnableRouteRequest + 21, // 21: headscale.v1.HeadscaleService.DisableRoute:input_type -> headscale.v1.DisableRouteRequest + 22, // 22: headscale.v1.HeadscaleService.GetNodeRoutes:input_type -> headscale.v1.GetNodeRoutesRequest + 23, // 23: headscale.v1.HeadscaleService.DeleteRoute:input_type -> headscale.v1.DeleteRouteRequest + 24, // 24: headscale.v1.HeadscaleService.CreateApiKey:input_type -> headscale.v1.CreateApiKeyRequest + 25, // 25: headscale.v1.HeadscaleService.ExpireApiKey:input_type -> headscale.v1.ExpireApiKeyRequest + 26, // 26: headscale.v1.HeadscaleService.ListApiKeys:input_type -> headscale.v1.ListApiKeysRequest + 27, // 27: headscale.v1.HeadscaleService.DeleteApiKey:input_type -> headscale.v1.DeleteApiKeyRequest + 28, // 28: headscale.v1.HeadscaleService.GetPolicy:input_type -> headscale.v1.GetPolicyRequest + 29, // 29: headscale.v1.HeadscaleService.SetPolicy:input_type -> headscale.v1.SetPolicyRequest + 30, // 30: headscale.v1.HeadscaleService.GetUser:output_type -> headscale.v1.GetUserResponse + 31, // 31: headscale.v1.HeadscaleService.CreateUser:output_type -> headscale.v1.CreateUserResponse + 32, // 32: headscale.v1.HeadscaleService.RenameUser:output_type -> headscale.v1.RenameUserResponse + 33, // 33: headscale.v1.HeadscaleService.DeleteUser:output_type -> headscale.v1.DeleteUserResponse + 34, // 34: headscale.v1.HeadscaleService.ListUsers:output_type -> headscale.v1.ListUsersResponse + 35, // 35: headscale.v1.HeadscaleService.CreatePreAuthKey:output_type -> headscale.v1.CreatePreAuthKeyResponse + 36, // 36: headscale.v1.HeadscaleService.ExpirePreAuthKey:output_type -> headscale.v1.ExpirePreAuthKeyResponse + 37, // 37: headscale.v1.HeadscaleService.ListPreAuthKeys:output_type -> headscale.v1.ListPreAuthKeysResponse + 38, // 38: headscale.v1.HeadscaleService.DebugCreateNode:output_type -> headscale.v1.DebugCreateNodeResponse + 39, // 39: headscale.v1.HeadscaleService.GetNode:output_type -> headscale.v1.GetNodeResponse + 40, // 40: headscale.v1.HeadscaleService.SetTags:output_type -> headscale.v1.SetTagsResponse + 41, // 41: headscale.v1.HeadscaleService.RegisterNode:output_type -> headscale.v1.RegisterNodeResponse + 42, // 42: headscale.v1.HeadscaleService.DeleteNode:output_type -> headscale.v1.DeleteNodeResponse + 43, // 43: headscale.v1.HeadscaleService.ApproveNode:output_type -> headscale.v1.ApproveNodeResponse + 44, // 44: headscale.v1.HeadscaleService.ExpireNode:output_type -> headscale.v1.ExpireNodeResponse + 45, // 45: headscale.v1.HeadscaleService.RenameNode:output_type -> headscale.v1.RenameNodeResponse + 46, // 46: headscale.v1.HeadscaleService.ListNodes:output_type -> headscale.v1.ListNodesResponse + 47, // 47: headscale.v1.HeadscaleService.MoveNode:output_type -> headscale.v1.MoveNodeResponse + 48, // 48: headscale.v1.HeadscaleService.BackfillNodeIPs:output_type -> headscale.v1.BackfillNodeIPsResponse + 49, // 49: headscale.v1.HeadscaleService.GetRoutes:output_type -> headscale.v1.GetRoutesResponse + 50, // 50: headscale.v1.HeadscaleService.EnableRoute:output_type -> headscale.v1.EnableRouteResponse + 51, // 51: headscale.v1.HeadscaleService.DisableRoute:output_type -> headscale.v1.DisableRouteResponse + 52, // 52: headscale.v1.HeadscaleService.GetNodeRoutes:output_type -> headscale.v1.GetNodeRoutesResponse + 53, // 53: headscale.v1.HeadscaleService.DeleteRoute:output_type -> headscale.v1.DeleteRouteResponse + 54, // 54: headscale.v1.HeadscaleService.CreateApiKey:output_type -> headscale.v1.CreateApiKeyResponse + 55, // 55: headscale.v1.HeadscaleService.ExpireApiKey:output_type -> headscale.v1.ExpireApiKeyResponse + 56, // 56: headscale.v1.HeadscaleService.ListApiKeys:output_type -> headscale.v1.ListApiKeysResponse + 57, // 57: headscale.v1.HeadscaleService.DeleteApiKey:output_type -> headscale.v1.DeleteApiKeyResponse + 58, // 58: headscale.v1.HeadscaleService.GetPolicy:output_type -> headscale.v1.GetPolicyResponse + 59, // 59: headscale.v1.HeadscaleService.SetPolicy:output_type -> headscale.v1.SetPolicyResponse + 30, // [30:60] is the sub-list for method output_type + 0, // [0:30] 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 8fe04cd0199..89dc82c0717 100644 --- a/gen/go/headscale/v1/headscale.pb.gw.go +++ b/gen/go/headscale/v1/headscale.pb.gw.go @@ -565,6 +565,58 @@ func local_request_HeadscaleService_DeleteNode_0(ctx context.Context, marshaler } +func request_HeadscaleService_ApproveNode_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ApproveNodeRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["node_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "node_id") + } + + protoReq.NodeId, err = runtime.Uint64(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "node_id", err) + } + + msg, err := client.ApproveNode(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_ApproveNode_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ApproveNodeRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["node_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "node_id") + } + + protoReq.NodeId, err = runtime.Uint64(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "node_id", err) + } + + msg, err := server.ApproveNode(ctx, &protoReq) + return msg, metadata, err + +} + func request_HeadscaleService_ExpireNode_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ExpireNodeRequest var metadata runtime.ServerMetadata @@ -1545,6 +1597,31 @@ func RegisterHeadscaleServiceHandlerServer(ctx context.Context, mux *runtime.Ser }) + mux.Handle("POST", pattern_HeadscaleService_ApproveNode_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) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/headscale.v1.HeadscaleService/ApproveNode", runtime.WithHTTPPathPattern("/api/v1/node/{node_id}/approve")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_ApproveNode_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_ApproveNode_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("POST", pattern_HeadscaleService_ExpireNode_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -2272,6 +2349,28 @@ func RegisterHeadscaleServiceHandlerClient(ctx context.Context, mux *runtime.Ser }) + mux.Handle("POST", pattern_HeadscaleService_ApproveNode_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) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/headscale.v1.HeadscaleService/ApproveNode", runtime.WithHTTPPathPattern("/api/v1/node/{node_id}/approve")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_ApproveNode_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_ApproveNode_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("POST", pattern_HeadscaleService_ExpireNode_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -2654,6 +2753,8 @@ var ( pattern_HeadscaleService_DeleteNode_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"}, "")) + pattern_HeadscaleService_ApproveNode_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "node", "node_id", "approve"}, "")) + pattern_HeadscaleService_ExpireNode_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "node", "node_id", "expire"}, "")) pattern_HeadscaleService_RenameNode_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"api", "v1", "node", "node_id", "rename", "new_name"}, "")) @@ -2714,6 +2815,8 @@ var ( forward_HeadscaleService_DeleteNode_0 = runtime.ForwardResponseMessage + forward_HeadscaleService_ApproveNode_0 = runtime.ForwardResponseMessage + forward_HeadscaleService_ExpireNode_0 = runtime.ForwardResponseMessage forward_HeadscaleService_RenameNode_0 = runtime.ForwardResponseMessage diff --git a/gen/go/headscale/v1/headscale_grpc.pb.go b/gen/go/headscale/v1/headscale_grpc.pb.go index d57aa92ee64..9666e99f2cd 100644 --- a/gen/go/headscale/v1/headscale_grpc.pb.go +++ b/gen/go/headscale/v1/headscale_grpc.pb.go @@ -32,6 +32,7 @@ const ( HeadscaleService_SetTags_FullMethodName = "/headscale.v1.HeadscaleService/SetTags" HeadscaleService_RegisterNode_FullMethodName = "/headscale.v1.HeadscaleService/RegisterNode" HeadscaleService_DeleteNode_FullMethodName = "/headscale.v1.HeadscaleService/DeleteNode" + HeadscaleService_ApproveNode_FullMethodName = "/headscale.v1.HeadscaleService/ApproveNode" HeadscaleService_ExpireNode_FullMethodName = "/headscale.v1.HeadscaleService/ExpireNode" HeadscaleService_RenameNode_FullMethodName = "/headscale.v1.HeadscaleService/RenameNode" HeadscaleService_ListNodes_FullMethodName = "/headscale.v1.HeadscaleService/ListNodes" @@ -70,6 +71,7 @@ type HeadscaleServiceClient interface { SetTags(ctx context.Context, in *SetTagsRequest, opts ...grpc.CallOption) (*SetTagsResponse, error) RegisterNode(ctx context.Context, in *RegisterNodeRequest, opts ...grpc.CallOption) (*RegisterNodeResponse, error) DeleteNode(ctx context.Context, in *DeleteNodeRequest, opts ...grpc.CallOption) (*DeleteNodeResponse, error) + ApproveNode(ctx context.Context, in *ApproveNodeRequest, opts ...grpc.CallOption) (*ApproveNodeResponse, error) ExpireNode(ctx context.Context, in *ExpireNodeRequest, opts ...grpc.CallOption) (*ExpireNodeResponse, error) RenameNode(ctx context.Context, in *RenameNodeRequest, opts ...grpc.CallOption) (*RenameNodeResponse, error) ListNodes(ctx context.Context, in *ListNodesRequest, opts ...grpc.CallOption) (*ListNodesResponse, error) @@ -216,6 +218,15 @@ func (c *headscaleServiceClient) DeleteNode(ctx context.Context, in *DeleteNodeR return out, nil } +func (c *headscaleServiceClient) ApproveNode(ctx context.Context, in *ApproveNodeRequest, opts ...grpc.CallOption) (*ApproveNodeResponse, error) { + out := new(ApproveNodeResponse) + err := c.cc.Invoke(ctx, HeadscaleService_ApproveNode_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *headscaleServiceClient) ExpireNode(ctx context.Context, in *ExpireNodeRequest, opts ...grpc.CallOption) (*ExpireNodeResponse, error) { out := new(ExpireNodeResponse) err := c.cc.Invoke(ctx, HeadscaleService_ExpireNode_FullMethodName, in, out, opts...) @@ -380,6 +391,7 @@ type HeadscaleServiceServer interface { SetTags(context.Context, *SetTagsRequest) (*SetTagsResponse, error) RegisterNode(context.Context, *RegisterNodeRequest) (*RegisterNodeResponse, error) DeleteNode(context.Context, *DeleteNodeRequest) (*DeleteNodeResponse, error) + ApproveNode(context.Context, *ApproveNodeRequest) (*ApproveNodeResponse, error) ExpireNode(context.Context, *ExpireNodeRequest) (*ExpireNodeResponse, error) RenameNode(context.Context, *RenameNodeRequest) (*RenameNodeResponse, error) ListNodes(context.Context, *ListNodesRequest) (*ListNodesResponse, error) @@ -445,6 +457,9 @@ func (UnimplementedHeadscaleServiceServer) RegisterNode(context.Context, *Regist func (UnimplementedHeadscaleServiceServer) DeleteNode(context.Context, *DeleteNodeRequest) (*DeleteNodeResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteNode not implemented") } +func (UnimplementedHeadscaleServiceServer) ApproveNode(context.Context, *ApproveNodeRequest) (*ApproveNodeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ApproveNode not implemented") +} func (UnimplementedHeadscaleServiceServer) ExpireNode(context.Context, *ExpireNodeRequest) (*ExpireNodeResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ExpireNode not implemented") } @@ -740,6 +755,24 @@ func _HeadscaleService_DeleteNode_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _HeadscaleService_ApproveNode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ApproveNodeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).ApproveNode(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: HeadscaleService_ApproveNode_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).ApproveNode(ctx, req.(*ApproveNodeRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _HeadscaleService_ExpireNode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ExpireNodeRequest) if err := dec(in); err != nil { @@ -1087,6 +1120,10 @@ var HeadscaleService_ServiceDesc = grpc.ServiceDesc{ MethodName: "DeleteNode", Handler: _HeadscaleService_DeleteNode_Handler, }, + { + MethodName: "ApproveNode", + Handler: _HeadscaleService_ApproveNode_Handler, + }, { MethodName: "ExpireNode", Handler: _HeadscaleService_ExpireNode_Handler, diff --git a/gen/go/headscale/v1/node.pb.go b/gen/go/headscale/v1/node.pb.go index 61ed40642cf..79e197e907f 100644 --- a/gen/go/headscale/v1/node.pb.go +++ b/gen/go/headscale/v1/node.pb.go @@ -95,6 +95,7 @@ type Node struct { ValidTags []string `protobuf:"bytes,20,rep,name=valid_tags,json=validTags,proto3" json:"valid_tags,omitempty"` GivenName string `protobuf:"bytes,21,opt,name=given_name,json=givenName,proto3" json:"given_name,omitempty"` Online bool `protobuf:"varint,22,opt,name=online,proto3" json:"online,omitempty"` + Approved bool `protobuf:"varint,23,opt,name=approved,proto3" json:"approved,omitempty"` } func (x *Node) Reset() { @@ -248,6 +249,13 @@ func (x *Node) GetOnline() bool { return false } +func (x *Node) GetApproved() bool { + if x != nil { + return x.Approved + } + return false +} + type RegisterNodeRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -631,6 +639,100 @@ func (*DeleteNodeResponse) Descriptor() ([]byte, []int) { return file_headscale_v1_node_proto_rawDescGZIP(), []int{8} } +type ApproveNodeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NodeId uint64 `protobuf:"varint,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` +} + +func (x *ApproveNodeRequest) Reset() { + *x = ApproveNodeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_node_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ApproveNodeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApproveNodeRequest) ProtoMessage() {} + +func (x *ApproveNodeRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_node_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ApproveNodeRequest.ProtoReflect.Descriptor instead. +func (*ApproveNodeRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_node_proto_rawDescGZIP(), []int{9} +} + +func (x *ApproveNodeRequest) GetNodeId() uint64 { + if x != nil { + return x.NodeId + } + return 0 +} + +type ApproveNodeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` +} + +func (x *ApproveNodeResponse) Reset() { + *x = ApproveNodeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_node_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ApproveNodeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApproveNodeResponse) ProtoMessage() {} + +func (x *ApproveNodeResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_node_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ApproveNodeResponse.ProtoReflect.Descriptor instead. +func (*ApproveNodeResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_node_proto_rawDescGZIP(), []int{10} +} + +func (x *ApproveNodeResponse) GetNode() *Node { + if x != nil { + return x.Node + } + return nil +} + type ExpireNodeRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -642,7 +744,7 @@ type ExpireNodeRequest struct { func (x *ExpireNodeRequest) Reset() { *x = ExpireNodeRequest{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_node_proto_msgTypes[9] + mi := &file_headscale_v1_node_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -655,7 +757,7 @@ func (x *ExpireNodeRequest) String() string { func (*ExpireNodeRequest) ProtoMessage() {} func (x *ExpireNodeRequest) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_node_proto_msgTypes[9] + mi := &file_headscale_v1_node_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -668,7 +770,7 @@ func (x *ExpireNodeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ExpireNodeRequest.ProtoReflect.Descriptor instead. func (*ExpireNodeRequest) Descriptor() ([]byte, []int) { - return file_headscale_v1_node_proto_rawDescGZIP(), []int{9} + return file_headscale_v1_node_proto_rawDescGZIP(), []int{11} } func (x *ExpireNodeRequest) GetNodeId() uint64 { @@ -689,7 +791,7 @@ type ExpireNodeResponse struct { func (x *ExpireNodeResponse) Reset() { *x = ExpireNodeResponse{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_node_proto_msgTypes[10] + mi := &file_headscale_v1_node_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -702,7 +804,7 @@ func (x *ExpireNodeResponse) String() string { func (*ExpireNodeResponse) ProtoMessage() {} func (x *ExpireNodeResponse) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_node_proto_msgTypes[10] + mi := &file_headscale_v1_node_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -715,7 +817,7 @@ func (x *ExpireNodeResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ExpireNodeResponse.ProtoReflect.Descriptor instead. func (*ExpireNodeResponse) Descriptor() ([]byte, []int) { - return file_headscale_v1_node_proto_rawDescGZIP(), []int{10} + return file_headscale_v1_node_proto_rawDescGZIP(), []int{12} } func (x *ExpireNodeResponse) GetNode() *Node { @@ -737,7 +839,7 @@ type RenameNodeRequest struct { func (x *RenameNodeRequest) Reset() { *x = RenameNodeRequest{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_node_proto_msgTypes[11] + mi := &file_headscale_v1_node_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -750,7 +852,7 @@ func (x *RenameNodeRequest) String() string { func (*RenameNodeRequest) ProtoMessage() {} func (x *RenameNodeRequest) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_node_proto_msgTypes[11] + mi := &file_headscale_v1_node_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -763,7 +865,7 @@ func (x *RenameNodeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RenameNodeRequest.ProtoReflect.Descriptor instead. func (*RenameNodeRequest) Descriptor() ([]byte, []int) { - return file_headscale_v1_node_proto_rawDescGZIP(), []int{11} + return file_headscale_v1_node_proto_rawDescGZIP(), []int{13} } func (x *RenameNodeRequest) GetNodeId() uint64 { @@ -791,7 +893,7 @@ type RenameNodeResponse struct { func (x *RenameNodeResponse) Reset() { *x = RenameNodeResponse{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_node_proto_msgTypes[12] + mi := &file_headscale_v1_node_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -804,7 +906,7 @@ func (x *RenameNodeResponse) String() string { func (*RenameNodeResponse) ProtoMessage() {} func (x *RenameNodeResponse) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_node_proto_msgTypes[12] + mi := &file_headscale_v1_node_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -817,7 +919,7 @@ func (x *RenameNodeResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RenameNodeResponse.ProtoReflect.Descriptor instead. func (*RenameNodeResponse) Descriptor() ([]byte, []int) { - return file_headscale_v1_node_proto_rawDescGZIP(), []int{12} + return file_headscale_v1_node_proto_rawDescGZIP(), []int{14} } func (x *RenameNodeResponse) GetNode() *Node { @@ -838,7 +940,7 @@ type ListNodesRequest struct { func (x *ListNodesRequest) Reset() { *x = ListNodesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_node_proto_msgTypes[13] + mi := &file_headscale_v1_node_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -851,7 +953,7 @@ func (x *ListNodesRequest) String() string { func (*ListNodesRequest) ProtoMessage() {} func (x *ListNodesRequest) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_node_proto_msgTypes[13] + mi := &file_headscale_v1_node_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -864,7 +966,7 @@ func (x *ListNodesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListNodesRequest.ProtoReflect.Descriptor instead. func (*ListNodesRequest) Descriptor() ([]byte, []int) { - return file_headscale_v1_node_proto_rawDescGZIP(), []int{13} + return file_headscale_v1_node_proto_rawDescGZIP(), []int{15} } func (x *ListNodesRequest) GetUser() string { @@ -885,7 +987,7 @@ type ListNodesResponse struct { func (x *ListNodesResponse) Reset() { *x = ListNodesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_node_proto_msgTypes[14] + mi := &file_headscale_v1_node_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -898,7 +1000,7 @@ func (x *ListNodesResponse) String() string { func (*ListNodesResponse) ProtoMessage() {} func (x *ListNodesResponse) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_node_proto_msgTypes[14] + mi := &file_headscale_v1_node_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -911,7 +1013,7 @@ func (x *ListNodesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListNodesResponse.ProtoReflect.Descriptor instead. func (*ListNodesResponse) Descriptor() ([]byte, []int) { - return file_headscale_v1_node_proto_rawDescGZIP(), []int{14} + return file_headscale_v1_node_proto_rawDescGZIP(), []int{16} } func (x *ListNodesResponse) GetNodes() []*Node { @@ -933,7 +1035,7 @@ type MoveNodeRequest struct { func (x *MoveNodeRequest) Reset() { *x = MoveNodeRequest{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_node_proto_msgTypes[15] + mi := &file_headscale_v1_node_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -946,7 +1048,7 @@ func (x *MoveNodeRequest) String() string { func (*MoveNodeRequest) ProtoMessage() {} func (x *MoveNodeRequest) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_node_proto_msgTypes[15] + mi := &file_headscale_v1_node_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -959,7 +1061,7 @@ func (x *MoveNodeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MoveNodeRequest.ProtoReflect.Descriptor instead. func (*MoveNodeRequest) Descriptor() ([]byte, []int) { - return file_headscale_v1_node_proto_rawDescGZIP(), []int{15} + return file_headscale_v1_node_proto_rawDescGZIP(), []int{17} } func (x *MoveNodeRequest) GetNodeId() uint64 { @@ -987,7 +1089,7 @@ type MoveNodeResponse struct { func (x *MoveNodeResponse) Reset() { *x = MoveNodeResponse{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_node_proto_msgTypes[16] + mi := &file_headscale_v1_node_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1000,7 +1102,7 @@ func (x *MoveNodeResponse) String() string { func (*MoveNodeResponse) ProtoMessage() {} func (x *MoveNodeResponse) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_node_proto_msgTypes[16] + mi := &file_headscale_v1_node_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1013,7 +1115,7 @@ func (x *MoveNodeResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MoveNodeResponse.ProtoReflect.Descriptor instead. func (*MoveNodeResponse) Descriptor() ([]byte, []int) { - return file_headscale_v1_node_proto_rawDescGZIP(), []int{16} + return file_headscale_v1_node_proto_rawDescGZIP(), []int{18} } func (x *MoveNodeResponse) GetNode() *Node { @@ -1037,7 +1139,7 @@ type DebugCreateNodeRequest struct { func (x *DebugCreateNodeRequest) Reset() { *x = DebugCreateNodeRequest{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_node_proto_msgTypes[17] + mi := &file_headscale_v1_node_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1050,7 +1152,7 @@ func (x *DebugCreateNodeRequest) String() string { func (*DebugCreateNodeRequest) ProtoMessage() {} func (x *DebugCreateNodeRequest) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_node_proto_msgTypes[17] + mi := &file_headscale_v1_node_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1063,7 +1165,7 @@ func (x *DebugCreateNodeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DebugCreateNodeRequest.ProtoReflect.Descriptor instead. func (*DebugCreateNodeRequest) Descriptor() ([]byte, []int) { - return file_headscale_v1_node_proto_rawDescGZIP(), []int{17} + return file_headscale_v1_node_proto_rawDescGZIP(), []int{19} } func (x *DebugCreateNodeRequest) GetUser() string { @@ -1105,7 +1207,7 @@ type DebugCreateNodeResponse struct { func (x *DebugCreateNodeResponse) Reset() { *x = DebugCreateNodeResponse{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_node_proto_msgTypes[18] + mi := &file_headscale_v1_node_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1118,7 +1220,7 @@ func (x *DebugCreateNodeResponse) String() string { func (*DebugCreateNodeResponse) ProtoMessage() {} func (x *DebugCreateNodeResponse) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_node_proto_msgTypes[18] + mi := &file_headscale_v1_node_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1131,7 +1233,7 @@ func (x *DebugCreateNodeResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DebugCreateNodeResponse.ProtoReflect.Descriptor instead. func (*DebugCreateNodeResponse) Descriptor() ([]byte, []int) { - return file_headscale_v1_node_proto_rawDescGZIP(), []int{18} + return file_headscale_v1_node_proto_rawDescGZIP(), []int{20} } func (x *DebugCreateNodeResponse) GetNode() *Node { @@ -1152,7 +1254,7 @@ type BackfillNodeIPsRequest struct { func (x *BackfillNodeIPsRequest) Reset() { *x = BackfillNodeIPsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_node_proto_msgTypes[19] + mi := &file_headscale_v1_node_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1165,7 +1267,7 @@ func (x *BackfillNodeIPsRequest) String() string { func (*BackfillNodeIPsRequest) ProtoMessage() {} func (x *BackfillNodeIPsRequest) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_node_proto_msgTypes[19] + mi := &file_headscale_v1_node_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1178,7 +1280,7 @@ func (x *BackfillNodeIPsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BackfillNodeIPsRequest.ProtoReflect.Descriptor instead. func (*BackfillNodeIPsRequest) Descriptor() ([]byte, []int) { - return file_headscale_v1_node_proto_rawDescGZIP(), []int{19} + return file_headscale_v1_node_proto_rawDescGZIP(), []int{21} } func (x *BackfillNodeIPsRequest) GetConfirmed() bool { @@ -1199,7 +1301,7 @@ type BackfillNodeIPsResponse struct { func (x *BackfillNodeIPsResponse) Reset() { *x = BackfillNodeIPsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_node_proto_msgTypes[20] + mi := &file_headscale_v1_node_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1212,7 +1314,7 @@ func (x *BackfillNodeIPsResponse) String() string { func (*BackfillNodeIPsResponse) ProtoMessage() {} func (x *BackfillNodeIPsResponse) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_node_proto_msgTypes[20] + mi := &file_headscale_v1_node_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1225,7 +1327,7 @@ func (x *BackfillNodeIPsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use BackfillNodeIPsResponse.ProtoReflect.Descriptor instead. func (*BackfillNodeIPsResponse) Descriptor() ([]byte, []int) { - return file_headscale_v1_node_proto_rawDescGZIP(), []int{20} + return file_headscale_v1_node_proto_rawDescGZIP(), []int{22} } func (x *BackfillNodeIPsResponse) GetChanges() []string { @@ -1246,7 +1348,7 @@ var file_headscale_v1_node_proto_rawDesc = []byte{ 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x65, 0x61, 0x75, 0x74, 0x68, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x22, 0x9f, 0x05, 0x0a, 0x04, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x22, 0xbb, 0x05, 0x0a, 0x04, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x6f, @@ -1287,92 +1389,101 @@ var file_headscale_v1_node_proto_rawDesc = []byte{ 0x1d, 0x0a, 0x0a, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, - 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x4a, 0x04, 0x08, 0x0e, - 0x10, 0x12, 0x22, 0x3b, 0x0a, 0x13, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x6f, - 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, - 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, - 0x3e, 0x0a, 0x14, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x22, - 0x29, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x22, 0x39, 0x0a, 0x0f, 0x47, 0x65, - 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, - 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, - 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, - 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x22, 0x3d, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x67, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, - 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, - 0x74, 0x61, 0x67, 0x73, 0x22, 0x39, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x54, 0x61, 0x67, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x22, - 0x2c, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x22, 0x14, 0x0a, - 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x2c, 0x0a, 0x11, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4e, 0x6f, 0x64, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, - 0x64, 0x22, 0x3c, 0x0a, 0x12, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x22, - 0x47, 0x0a, 0x11, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x19, 0x0a, - 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x6e, 0x65, 0x77, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x3c, 0x0a, 0x12, 0x52, 0x65, 0x6e, 0x61, - 0x6d, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, - 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, - 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, - 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x22, 0x26, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, - 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x3d, - 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3e, 0x0a, - 0x0f, 0x4d, 0x6f, 0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, + 0x65, 0x64, 0x18, 0x17, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, + 0x65, 0x64, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x4a, 0x04, 0x08, 0x0e, 0x10, 0x12, 0x22, 0x3b, + 0x0a, 0x13, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x3e, 0x0a, 0x14, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x22, 0x29, 0x0a, 0x0e, 0x47, + 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, + 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, + 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x22, 0x39, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x6e, 0x6f, 0x64, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, + 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, + 0x65, 0x22, 0x3d, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x54, 0x61, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, + 0x74, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, + 0x22, 0x39, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x54, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x22, 0x2c, 0x0a, 0x11, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, - 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x3a, 0x0a, - 0x10, 0x4d, 0x6f, 0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x04, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x22, 0x14, 0x0a, 0x12, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x2d, 0x0a, 0x12, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x22, 0x3d, + 0x0a, 0x13, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x22, 0x2c, 0x0a, + 0x11, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x22, 0x3c, 0x0a, 0x12, 0x45, + 0x78, 0x70, 0x69, 0x72, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, - 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x22, 0x6a, 0x0a, 0x16, 0x44, 0x65, 0x62, - 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, - 0x6f, 0x75, 0x74, 0x65, 0x73, 0x22, 0x41, 0x0a, 0x17, 0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x26, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, - 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, - 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x22, 0x36, 0x0a, 0x16, 0x42, 0x61, 0x63, 0x6b, - 0x66, 0x69, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, - 0x22, 0x33, 0x0a, 0x17, 0x42, 0x61, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, - 0x49, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x73, 0x2a, 0x82, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x47, 0x49, - 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, - 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x47, - 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, - 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x47, 0x49, 0x53, - 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x43, 0x4c, 0x49, 0x10, 0x02, - 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, - 0x48, 0x4f, 0x44, 0x5f, 0x4f, 0x49, 0x44, 0x43, 0x10, 0x03, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, - 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, - 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x22, 0x47, 0x0a, 0x11, 0x52, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, + 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x4e, 0x61, + 0x6d, 0x65, 0x22, 0x3c, 0x0a, 0x12, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x4e, 0x6f, 0x64, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, + 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, + 0x22, 0x26, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x3d, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, + 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, + 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, + 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x3e, 0x0a, 0x0f, 0x4d, 0x6f, 0x76, 0x65, 0x4e, + 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x6f, 0x64, + 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x3a, 0x0a, 0x10, 0x4d, 0x6f, 0x76, 0x65, 0x4e, + 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x6e, + 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, + 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, + 0x6f, 0x64, 0x65, 0x22, 0x6a, 0x0a, 0x16, 0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, + 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x22, + 0x41, 0x0a, 0x17, 0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x6f, + 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x6e, 0x6f, + 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, + 0x64, 0x65, 0x22, 0x36, 0x0a, 0x16, 0x42, 0x61, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x6c, 0x4e, 0x6f, + 0x64, 0x65, 0x49, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x22, 0x33, 0x0a, 0x17, 0x42, 0x61, + 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x50, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x2a, + 0x82, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, + 0x6f, 0x64, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, + 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, + 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, + 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, + 0x01, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, + 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x43, 0x4c, 0x49, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, + 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x4f, 0x49, + 0x44, 0x43, 0x10, 0x03, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, + 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1388,7 +1499,7 @@ func file_headscale_v1_node_proto_rawDescGZIP() []byte { } var file_headscale_v1_node_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_headscale_v1_node_proto_msgTypes = make([]protoimpl.MessageInfo, 21) +var file_headscale_v1_node_proto_msgTypes = make([]protoimpl.MessageInfo, 23) var file_headscale_v1_node_proto_goTypes = []any{ (RegisterMethod)(0), // 0: headscale.v1.RegisterMethod (*Node)(nil), // 1: headscale.v1.Node @@ -1400,42 +1511,45 @@ var file_headscale_v1_node_proto_goTypes = []any{ (*SetTagsResponse)(nil), // 7: headscale.v1.SetTagsResponse (*DeleteNodeRequest)(nil), // 8: headscale.v1.DeleteNodeRequest (*DeleteNodeResponse)(nil), // 9: headscale.v1.DeleteNodeResponse - (*ExpireNodeRequest)(nil), // 10: headscale.v1.ExpireNodeRequest - (*ExpireNodeResponse)(nil), // 11: headscale.v1.ExpireNodeResponse - (*RenameNodeRequest)(nil), // 12: headscale.v1.RenameNodeRequest - (*RenameNodeResponse)(nil), // 13: headscale.v1.RenameNodeResponse - (*ListNodesRequest)(nil), // 14: headscale.v1.ListNodesRequest - (*ListNodesResponse)(nil), // 15: headscale.v1.ListNodesResponse - (*MoveNodeRequest)(nil), // 16: headscale.v1.MoveNodeRequest - (*MoveNodeResponse)(nil), // 17: headscale.v1.MoveNodeResponse - (*DebugCreateNodeRequest)(nil), // 18: headscale.v1.DebugCreateNodeRequest - (*DebugCreateNodeResponse)(nil), // 19: headscale.v1.DebugCreateNodeResponse - (*BackfillNodeIPsRequest)(nil), // 20: headscale.v1.BackfillNodeIPsRequest - (*BackfillNodeIPsResponse)(nil), // 21: headscale.v1.BackfillNodeIPsResponse - (*User)(nil), // 22: headscale.v1.User - (*timestamppb.Timestamp)(nil), // 23: google.protobuf.Timestamp - (*PreAuthKey)(nil), // 24: headscale.v1.PreAuthKey + (*ApproveNodeRequest)(nil), // 10: headscale.v1.ApproveNodeRequest + (*ApproveNodeResponse)(nil), // 11: headscale.v1.ApproveNodeResponse + (*ExpireNodeRequest)(nil), // 12: headscale.v1.ExpireNodeRequest + (*ExpireNodeResponse)(nil), // 13: headscale.v1.ExpireNodeResponse + (*RenameNodeRequest)(nil), // 14: headscale.v1.RenameNodeRequest + (*RenameNodeResponse)(nil), // 15: headscale.v1.RenameNodeResponse + (*ListNodesRequest)(nil), // 16: headscale.v1.ListNodesRequest + (*ListNodesResponse)(nil), // 17: headscale.v1.ListNodesResponse + (*MoveNodeRequest)(nil), // 18: headscale.v1.MoveNodeRequest + (*MoveNodeResponse)(nil), // 19: headscale.v1.MoveNodeResponse + (*DebugCreateNodeRequest)(nil), // 20: headscale.v1.DebugCreateNodeRequest + (*DebugCreateNodeResponse)(nil), // 21: headscale.v1.DebugCreateNodeResponse + (*BackfillNodeIPsRequest)(nil), // 22: headscale.v1.BackfillNodeIPsRequest + (*BackfillNodeIPsResponse)(nil), // 23: headscale.v1.BackfillNodeIPsResponse + (*User)(nil), // 24: headscale.v1.User + (*timestamppb.Timestamp)(nil), // 25: google.protobuf.Timestamp + (*PreAuthKey)(nil), // 26: headscale.v1.PreAuthKey } var file_headscale_v1_node_proto_depIdxs = []int32{ - 22, // 0: headscale.v1.Node.user:type_name -> headscale.v1.User - 23, // 1: headscale.v1.Node.last_seen:type_name -> google.protobuf.Timestamp - 23, // 2: headscale.v1.Node.expiry:type_name -> google.protobuf.Timestamp - 24, // 3: headscale.v1.Node.pre_auth_key:type_name -> headscale.v1.PreAuthKey - 23, // 4: headscale.v1.Node.created_at:type_name -> google.protobuf.Timestamp + 24, // 0: headscale.v1.Node.user:type_name -> headscale.v1.User + 25, // 1: headscale.v1.Node.last_seen:type_name -> google.protobuf.Timestamp + 25, // 2: headscale.v1.Node.expiry:type_name -> google.protobuf.Timestamp + 26, // 3: headscale.v1.Node.pre_auth_key:type_name -> headscale.v1.PreAuthKey + 25, // 4: headscale.v1.Node.created_at:type_name -> google.protobuf.Timestamp 0, // 5: headscale.v1.Node.register_method:type_name -> headscale.v1.RegisterMethod 1, // 6: headscale.v1.RegisterNodeResponse.node:type_name -> headscale.v1.Node 1, // 7: headscale.v1.GetNodeResponse.node:type_name -> headscale.v1.Node 1, // 8: headscale.v1.SetTagsResponse.node:type_name -> headscale.v1.Node - 1, // 9: headscale.v1.ExpireNodeResponse.node:type_name -> headscale.v1.Node - 1, // 10: headscale.v1.RenameNodeResponse.node:type_name -> headscale.v1.Node - 1, // 11: headscale.v1.ListNodesResponse.nodes:type_name -> headscale.v1.Node - 1, // 12: headscale.v1.MoveNodeResponse.node:type_name -> headscale.v1.Node - 1, // 13: headscale.v1.DebugCreateNodeResponse.node:type_name -> headscale.v1.Node - 14, // [14:14] is the sub-list for method output_type - 14, // [14:14] is the sub-list for method input_type - 14, // [14:14] is the sub-list for extension type_name - 14, // [14:14] is the sub-list for extension extendee - 0, // [0:14] is the sub-list for field type_name + 1, // 9: headscale.v1.ApproveNodeResponse.node:type_name -> headscale.v1.Node + 1, // 10: headscale.v1.ExpireNodeResponse.node:type_name -> headscale.v1.Node + 1, // 11: headscale.v1.RenameNodeResponse.node:type_name -> headscale.v1.Node + 1, // 12: headscale.v1.ListNodesResponse.nodes:type_name -> headscale.v1.Node + 1, // 13: headscale.v1.MoveNodeResponse.node:type_name -> headscale.v1.Node + 1, // 14: headscale.v1.DebugCreateNodeResponse.node:type_name -> headscale.v1.Node + 15, // [15:15] is the sub-list for method output_type + 15, // [15:15] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name } func init() { file_headscale_v1_node_proto_init() } @@ -1555,7 +1669,7 @@ func file_headscale_v1_node_proto_init() { } } file_headscale_v1_node_proto_msgTypes[9].Exporter = func(v any, i int) any { - switch v := v.(*ExpireNodeRequest); i { + switch v := v.(*ApproveNodeRequest); i { case 0: return &v.state case 1: @@ -1567,7 +1681,7 @@ func file_headscale_v1_node_proto_init() { } } file_headscale_v1_node_proto_msgTypes[10].Exporter = func(v any, i int) any { - switch v := v.(*ExpireNodeResponse); i { + switch v := v.(*ApproveNodeResponse); i { case 0: return &v.state case 1: @@ -1579,7 +1693,7 @@ func file_headscale_v1_node_proto_init() { } } file_headscale_v1_node_proto_msgTypes[11].Exporter = func(v any, i int) any { - switch v := v.(*RenameNodeRequest); i { + switch v := v.(*ExpireNodeRequest); i { case 0: return &v.state case 1: @@ -1591,7 +1705,7 @@ func file_headscale_v1_node_proto_init() { } } file_headscale_v1_node_proto_msgTypes[12].Exporter = func(v any, i int) any { - switch v := v.(*RenameNodeResponse); i { + switch v := v.(*ExpireNodeResponse); i { case 0: return &v.state case 1: @@ -1603,7 +1717,7 @@ func file_headscale_v1_node_proto_init() { } } file_headscale_v1_node_proto_msgTypes[13].Exporter = func(v any, i int) any { - switch v := v.(*ListNodesRequest); i { + switch v := v.(*RenameNodeRequest); i { case 0: return &v.state case 1: @@ -1615,7 +1729,7 @@ func file_headscale_v1_node_proto_init() { } } file_headscale_v1_node_proto_msgTypes[14].Exporter = func(v any, i int) any { - switch v := v.(*ListNodesResponse); i { + switch v := v.(*RenameNodeResponse); i { case 0: return &v.state case 1: @@ -1627,7 +1741,7 @@ func file_headscale_v1_node_proto_init() { } } file_headscale_v1_node_proto_msgTypes[15].Exporter = func(v any, i int) any { - switch v := v.(*MoveNodeRequest); i { + switch v := v.(*ListNodesRequest); i { case 0: return &v.state case 1: @@ -1639,7 +1753,7 @@ func file_headscale_v1_node_proto_init() { } } file_headscale_v1_node_proto_msgTypes[16].Exporter = func(v any, i int) any { - switch v := v.(*MoveNodeResponse); i { + switch v := v.(*ListNodesResponse); i { case 0: return &v.state case 1: @@ -1651,7 +1765,7 @@ func file_headscale_v1_node_proto_init() { } } file_headscale_v1_node_proto_msgTypes[17].Exporter = func(v any, i int) any { - switch v := v.(*DebugCreateNodeRequest); i { + switch v := v.(*MoveNodeRequest); i { case 0: return &v.state case 1: @@ -1663,7 +1777,7 @@ func file_headscale_v1_node_proto_init() { } } file_headscale_v1_node_proto_msgTypes[18].Exporter = func(v any, i int) any { - switch v := v.(*DebugCreateNodeResponse); i { + switch v := v.(*MoveNodeResponse); i { case 0: return &v.state case 1: @@ -1675,7 +1789,7 @@ func file_headscale_v1_node_proto_init() { } } file_headscale_v1_node_proto_msgTypes[19].Exporter = func(v any, i int) any { - switch v := v.(*BackfillNodeIPsRequest); i { + switch v := v.(*DebugCreateNodeRequest); i { case 0: return &v.state case 1: @@ -1687,6 +1801,30 @@ func file_headscale_v1_node_proto_init() { } } file_headscale_v1_node_proto_msgTypes[20].Exporter = func(v any, i int) any { + switch v := v.(*DebugCreateNodeResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_node_proto_msgTypes[21].Exporter = func(v any, i int) any { + switch v := v.(*BackfillNodeIPsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_node_proto_msgTypes[22].Exporter = func(v any, i int) any { switch v := v.(*BackfillNodeIPsResponse); i { case 0: return &v.state @@ -1705,7 +1843,7 @@ func file_headscale_v1_node_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_headscale_v1_node_proto_rawDesc, NumEnums: 1, - NumMessages: 21, + NumMessages: 23, NumExtensions: 0, NumServices: 0, }, diff --git a/gen/go/headscale/v1/preauthkey.pb.go b/gen/go/headscale/v1/preauthkey.pb.go index ede617f25ee..7811f8ad0b9 100644 --- a/gen/go/headscale/v1/preauthkey.pb.go +++ b/gen/go/headscale/v1/preauthkey.pb.go @@ -26,15 +26,16 @@ type PreAuthKey struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - User string `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` - Key string `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` - Reusable bool `protobuf:"varint,4,opt,name=reusable,proto3" json:"reusable,omitempty"` - Ephemeral bool `protobuf:"varint,5,opt,name=ephemeral,proto3" json:"ephemeral,omitempty"` - Used bool `protobuf:"varint,6,opt,name=used,proto3" json:"used,omitempty"` - Expiration *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=expiration,proto3" json:"expiration,omitempty"` - CreatedAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` - AclTags []string `protobuf:"bytes,9,rep,name=acl_tags,json=aclTags,proto3" json:"acl_tags,omitempty"` + User string `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Key string `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` + Reusable bool `protobuf:"varint,4,opt,name=reusable,proto3" json:"reusable,omitempty"` + Ephemeral bool `protobuf:"varint,5,opt,name=ephemeral,proto3" json:"ephemeral,omitempty"` + Used bool `protobuf:"varint,6,opt,name=used,proto3" json:"used,omitempty"` + Expiration *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=expiration,proto3" json:"expiration,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + AclTags []string `protobuf:"bytes,9,rep,name=acl_tags,json=aclTags,proto3" json:"acl_tags,omitempty"` + PreApproved bool `protobuf:"varint,10,opt,name=pre_approved,json=preApproved,proto3" json:"pre_approved,omitempty"` } func (x *PreAuthKey) Reset() { @@ -132,16 +133,24 @@ func (x *PreAuthKey) GetAclTags() []string { return nil } +func (x *PreAuthKey) GetPreApproved() bool { + if x != nil { + return x.PreApproved + } + return false +} + type CreatePreAuthKeyRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - User string `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` - Reusable bool `protobuf:"varint,2,opt,name=reusable,proto3" json:"reusable,omitempty"` - Ephemeral bool `protobuf:"varint,3,opt,name=ephemeral,proto3" json:"ephemeral,omitempty"` - Expiration *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=expiration,proto3" json:"expiration,omitempty"` - AclTags []string `protobuf:"bytes,5,rep,name=acl_tags,json=aclTags,proto3" json:"acl_tags,omitempty"` + User string `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + Reusable bool `protobuf:"varint,2,opt,name=reusable,proto3" json:"reusable,omitempty"` + Ephemeral bool `protobuf:"varint,3,opt,name=ephemeral,proto3" json:"ephemeral,omitempty"` + Expiration *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=expiration,proto3" json:"expiration,omitempty"` + AclTags []string `protobuf:"bytes,5,rep,name=acl_tags,json=aclTags,proto3" json:"acl_tags,omitempty"` + PreApproved bool `protobuf:"varint,6,opt,name=pre_approved,json=preApproved,proto3" json:"pre_approved,omitempty"` } func (x *CreatePreAuthKeyRequest) Reset() { @@ -211,6 +220,13 @@ func (x *CreatePreAuthKeyRequest) GetAclTags() []string { return nil } +func (x *CreatePreAuthKeyRequest) GetPreApproved() bool { + if x != nil { + return x.PreApproved + } + return false +} + type CreatePreAuthKeyResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -452,7 +468,7 @@ var file_headscale_v1_preauthkey_proto_rawDesc = []byte{ 0x72, 0x65, 0x61, 0x75, 0x74, 0x68, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa2, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc5, 0x02, 0x0a, 0x0a, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, @@ -471,42 +487,46 @@ var file_headscale_v1_preauthkey_proto_rawDesc = []byte{ 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x63, 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x6c, 0x54, - 0x61, 0x67, 0x73, 0x22, 0xbe, 0x01, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, - 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, - 0x73, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x75, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x75, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, - 0x1c, 0x0a, 0x09, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x09, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x3a, 0x0a, - 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x65, - 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x63, 0x6c, - 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x6c, - 0x54, 0x61, 0x67, 0x73, 0x22, 0x56, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, - 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x3a, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, - 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, - 0x52, 0x0a, 0x70, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x22, 0x3f, 0x0a, 0x17, - 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x1a, 0x0a, - 0x18, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2c, 0x0a, 0x16, 0x4c, 0x69, 0x73, - 0x74, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x57, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x50, - 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, - 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x68, 0x65, 0x61, 0x64, - 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, - 0x4b, 0x65, 0x79, 0x52, 0x0b, 0x70, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, - 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, - 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x61, 0x67, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x70, 0x72, 0x6f, + 0x76, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, 0x72, 0x65, 0x41, 0x70, + 0x70, 0x72, 0x6f, 0x76, 0x65, 0x64, 0x22, 0xe1, 0x01, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x75, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x75, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, + 0x12, 0x3a, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, + 0x61, 0x63, 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, + 0x61, 0x63, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x5f, 0x61, + 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, + 0x72, 0x65, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x64, 0x22, 0x56, 0x0a, 0x18, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x5f, 0x61, 0x75, + 0x74, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x68, + 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x41, + 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, + 0x65, 0x79, 0x22, 0x3f, 0x0a, 0x17, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x50, 0x72, 0x65, 0x41, + 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, + 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x22, 0x1a, 0x0a, 0x18, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x50, 0x72, 0x65, + 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x2c, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, + 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x57, 0x0a, + 0x17, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x65, 0x5f, + 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x18, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, + 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x0b, 0x70, 0x72, 0x65, 0x41, 0x75, + 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, + 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, + 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gen/openapiv2/headscale/v1/headscale.swagger.json b/gen/openapiv2/headscale/v1/headscale.swagger.json index e2c26acdb83..c7772f74c2e 100644 --- a/gen/openapiv2/headscale/v1/headscale.swagger.json +++ b/gen/openapiv2/headscale/v1/headscale.swagger.json @@ -320,6 +320,37 @@ ] } }, + "/api/v1/node/{nodeId}/approve": { + "post": { + "operationId": "HeadscaleService_ApproveNode", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1ApproveNodeResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "nodeId", + "in": "path", + "required": true, + "type": "string", + "format": "uint64" + } + ], + "tags": [ + "HeadscaleService" + ] + } + }, "/api/v1/node/{nodeId}/expire": { "post": { "operationId": "HeadscaleService_ExpireNode", @@ -979,6 +1010,14 @@ } } }, + "v1ApproveNodeResponse": { + "type": "object", + "properties": { + "node": { + "$ref": "#/definitions/v1Node" + } + } + }, "v1BackfillNodeIPsResponse": { "type": "object", "properties": { @@ -1028,6 +1067,9 @@ "items": { "type": "string" } + }, + "preApproved": { + "type": "boolean" } } }, @@ -1311,6 +1353,9 @@ }, "online": { "type": "boolean" + }, + "approved": { + "type": "boolean" } } }, @@ -1348,6 +1393,9 @@ "items": { "type": "string" } + }, + "preApproved": { + "type": "boolean" } } }, diff --git a/proto/headscale/v1/headscale.proto b/proto/headscale/v1/headscale.proto index 9588bdd3adf..382777243fd 100644 --- a/proto/headscale/v1/headscale.proto +++ b/proto/headscale/v1/headscale.proto @@ -101,6 +101,12 @@ service HeadscaleService { }; } + rpc ApproveNode(ApproveNodeRequest) returns (ApproveNodeResponse) { + option (google.api.http) = { + post: "/api/v1/node/{node_id}/approve" + }; + } + rpc ExpireNode(ExpireNodeRequest) returns (ExpireNodeResponse) { option (google.api.http) = { post: "/api/v1/node/{node_id}/expire" diff --git a/proto/headscale/v1/node.proto b/proto/headscale/v1/node.proto index 26fe73c7a3a..5790abb87f1 100644 --- a/proto/headscale/v1/node.proto +++ b/proto/headscale/v1/node.proto @@ -48,6 +48,7 @@ message Node { repeated string valid_tags = 20; string given_name = 21; bool online = 22; + bool approved = 23; } message RegisterNodeRequest { @@ -82,6 +83,14 @@ message DeleteNodeRequest { message DeleteNodeResponse {} +message ApproveNodeRequest { + uint64 node_id = 1; +} + +message ApproveNodeResponse { + Node node = 1; +} + message ExpireNodeRequest { uint64 node_id = 1; } diff --git a/proto/headscale/v1/preauthkey.proto b/proto/headscale/v1/preauthkey.proto index 1ab3a72771e..da81b627afb 100644 --- a/proto/headscale/v1/preauthkey.proto +++ b/proto/headscale/v1/preauthkey.proto @@ -5,23 +5,25 @@ option go_package = "github.com/juanfont/headscale/gen/go/v1"; import "google/protobuf/timestamp.proto"; message PreAuthKey { - string user = 1; - string id = 2; - string key = 3; - bool reusable = 4; - bool ephemeral = 5; - bool used = 6; - google.protobuf.Timestamp expiration = 7; - google.protobuf.Timestamp created_at = 8; - repeated string acl_tags = 9; + string user = 1; + string id = 2; + string key = 3; + bool reusable = 4; + bool ephemeral = 5; + bool used = 6; + google.protobuf.Timestamp expiration = 7; + google.protobuf.Timestamp created_at = 8; + repeated string acl_tags = 9; + bool pre_approved = 10; } message CreatePreAuthKeyRequest { - string user = 1; - bool reusable = 2; - bool ephemeral = 3; - google.protobuf.Timestamp expiration = 4; - repeated string acl_tags = 5; + string user = 1; + bool reusable = 2; + bool ephemeral = 3; + google.protobuf.Timestamp expiration = 4; + repeated string acl_tags = 5; + bool pre_approved = 6; } message CreatePreAuthKeyResponse { From 5c274f26e621e4aeb8219ac16c789e799355eb18 Mon Sep 17 00:00:00 2001 From: hopleus Date: Wed, 16 Oct 2024 17:21:48 +0300 Subject: [PATCH 02/22] Added manual approval of nodes in the network --- cmd/headscale/cli/nodes.go | 44 ++++++++++++++++++++++++++++ cmd/headscale/cli/preauthkeys.go | 14 ++++++--- config-example.yaml | 6 ++++ hscontrol/app.go | 1 + hscontrol/auth.go | 14 +++++++-- hscontrol/db/db.go | 49 ++++++++++++++++++++++++++++++-- hscontrol/db/ip.go | 4 +-- hscontrol/db/node.go | 28 ++++++++++++++++-- hscontrol/db/preauth_keys.go | 21 ++++++++------ hscontrol/grpcv1.go | 49 ++++++++++++++++++++++++++++++++ hscontrol/mapper/tail.go | 2 +- hscontrol/types/config.go | 20 +++++++++++++ hscontrol/types/node.go | 7 +++++ hscontrol/types/preauth_key.go | 32 +++++++++++---------- 14 files changed, 254 insertions(+), 37 deletions(-) diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go index b9e97a33685..8585c6c0ae3 100644 --- a/cmd/headscale/cli/nodes.go +++ b/cmd/headscale/cli/nodes.go @@ -48,6 +48,13 @@ func init() { } nodeCmd.AddCommand(registerNodeCmd) + approveNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)") + err = approveNodeCmd.MarkFlagRequired("identifier") + if err != nil { + log.Fatalf(err.Error()) + } + nodeCmd.AddCommand(approveNodeCmd) + expireNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)") err = expireNodeCmd.MarkFlagRequired("identifier") if err != nil { @@ -206,6 +213,43 @@ var listNodesCmd = &cobra.Command{ }, } +var approveNodeCmd = &cobra.Command{ + Use: "approve", + Short: "Approve a node in your network", + Aliases: []string{"a"}, + Run: func(cmd *cobra.Command, args []string) { + output, _ := cmd.Flags().GetString("output") + identifier, err := cmd.Flags().GetUint64("identifier") + if err != nil { + ErrorOutput( + err, + fmt.Sprintf("Error converting ID to integer: %s", err), + output, + ) + return + } + ctx, client, conn, cancel := newHeadscaleCLIWithConfig() + defer cancel() + defer conn.Close() + request := &v1.ApproveNodeRequest{ + NodeId: identifier, + } + response, err := client.ApproveNode(ctx, request) + if err != nil { + ErrorOutput( + err, + fmt.Sprintf( + "Cannot expire node: %s\n", + status.Convert(err).Message(), + ), + output, + ) + return + } + SuccessOutput(response.GetNode(), "Node approved", output) + }, +} + var expireNodeCmd = &cobra.Command{ Use: "expire", Short: "Expire (log out) a node in your network", diff --git a/cmd/headscale/cli/preauthkeys.go b/cmd/headscale/cli/preauthkeys.go index 0074e02956c..5a427ca711b 100644 --- a/cmd/headscale/cli/preauthkeys.go +++ b/cmd/headscale/cli/preauthkeys.go @@ -36,6 +36,8 @@ func init() { preauthkeysCmd.AddCommand(expirePreAuthKeyCmd) createPreAuthKeyCmd.PersistentFlags(). Bool("reusable", false, "Make the preauthkey reusable") + createPreAuthKeyCmd.PersistentFlags(). + Bool("pre-approved", false, "Make the preauthkey with node pre-approval") createPreAuthKeyCmd.PersistentFlags(). Bool("ephemeral", false, "Preauthkey for ephemeral nodes") createPreAuthKeyCmd.Flags(). @@ -90,6 +92,7 @@ var listPreAuthKeys = &cobra.Command{ "ID", "Key", "Reusable", + "Pre-Approved", "Ephemeral", "Used", "Expiration", @@ -115,6 +118,7 @@ var listPreAuthKeys = &cobra.Command{ key.GetId(), key.GetKey(), strconv.FormatBool(key.GetReusable()), + strconv.FormatBool(key.GetPreApproved()), strconv.FormatBool(key.GetEphemeral()), strconv.FormatBool(key.GetUsed()), expiration, @@ -147,14 +151,16 @@ var createPreAuthKeyCmd = &cobra.Command{ } reusable, _ := cmd.Flags().GetBool("reusable") + preApproved, _ := cmd.Flags().GetBool("pre-approved") ephemeral, _ := cmd.Flags().GetBool("ephemeral") tags, _ := cmd.Flags().GetStringSlice("tags") request := &v1.CreatePreAuthKeyRequest{ - User: user, - Reusable: reusable, - Ephemeral: ephemeral, - AclTags: tags, + User: user, + Reusable: reusable, + PreApproved: preApproved, + Ephemeral: ephemeral, + AclTags: tags, } durationStr, _ := cmd.Flags().GetString("expiration") diff --git a/config-example.yaml b/config-example.yaml index 2632555d2ba..bc640e76b9c 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -384,3 +384,9 @@ logtail: # default static port 41641. This option is intended as a workaround for some buggy # firewall devices. See https://tailscale.com/kb/1181/firewalls/ for more information. randomize_client_port: false + +# Node management +# See https://tailscale.com/kb/1099/device-approval for more information. +node_management: + # Require new nodes to be approved by admins before they can access the network. + manual_approve_new_node: false \ No newline at end of file diff --git a/hscontrol/app.go b/hscontrol/app.go index 5c85b0641db..64b7552d9c8 100644 --- a/hscontrol/app.go +++ b/hscontrol/app.go @@ -136,6 +136,7 @@ func NewHeadscale(cfg *types.Config) (*Headscale, error) { app.db, err = db.NewHeadscaleDatabase( cfg.Database, cfg.BaseDomain, + cfg.NodeManagement, registrationCache, ) if err != nil { diff --git a/hscontrol/auth.go b/hscontrol/auth.go index 675450319da..9e4e9ee1d27 100644 --- a/hscontrol/auth.go +++ b/hscontrol/auth.go @@ -292,6 +292,11 @@ func (h *Headscale) handleAuthKey( nodeKey := registerRequest.NodeKey + nodeApproved := true + if h.cfg.NodeManagement.ManualApproveNewNode { + nodeApproved = pak.PreApproved + } + // retrieve node information if it exist // The error is not important, because if it does not // exist, then this is a new node and we will move @@ -308,6 +313,10 @@ func (h *Headscale) handleAuthKey( node.AuthKeyID = ptr.To(pak.ID) } + if node.Approved == false { + node.Approved = nodeApproved + } + node.Expiry = ®isterRequest.Expiry node.User = pak.User node.UserID = pak.UserID @@ -349,6 +358,7 @@ func (h *Headscale) handleAuthKey( User: pak.User, MachineKey: machineKey, RegisterMethod: util.RegisterMethodAuthKey, + Approved: nodeApproved, Expiry: ®isterRequest.Expiry, NodeKey: nodeKey, LastSeen: &now, @@ -399,7 +409,7 @@ func (h *Headscale) handleAuthKey( return } - resp.MachineAuthorized = true + resp.MachineAuthorized = node.IsApproved() resp.User = *pak.User.TailscaleUser() // Provide LoginName when registering with pre-auth key // Otherwise it will need to exec `tailscale up` twice to fetch the *LoginName* @@ -562,7 +572,7 @@ func (h *Headscale) handleNodeWithValidRegistration( Msg("Client is registered and we have the current NodeKey. All clear to /map") resp.AuthURL = "" - resp.MachineAuthorized = true + resp.MachineAuthorized = node.IsApproved() resp.User = *node.User.TailscaleUser() resp.Login = *node.User.TailscaleLogin() diff --git a/hscontrol/db/db.go b/hscontrol/db/db.go index b7661ab2848..9d156674110 100644 --- a/hscontrol/db/db.go +++ b/hscontrol/db/db.go @@ -43,7 +43,8 @@ type HSDatabase struct { cfg *types.DatabaseConfig regCache *zcache.Cache[string, types.Node] - baseDomain string + baseDomain string + nodeManagement *types.NodeManagement } // TODO(kradalby): assemble this struct from toptions or something typed @@ -51,6 +52,7 @@ type HSDatabase struct { func NewHeadscaleDatabase( cfg types.DatabaseConfig, baseDomain string, + nodeManagement types.NodeManagement, regCache *zcache.Cache[string, types.Node], ) (*HSDatabase, error) { dbConn, err := openDB(cfg) @@ -485,6 +487,48 @@ func NewHeadscaleDatabase( }, Rollback: func(db *gorm.DB) error { return nil }, }, + { + ID: "202410071005", + Migrate: func(tx *gorm.DB) error { + err = tx.AutoMigrate(&types.PreAuthKey{}) + if err != nil { + return err + } + + err = tx.AutoMigrate(&types.Node{}) + if err != nil { + return err + } + + if tx.Migrator().HasColumn(&types.Node{}, "approved") { + nodes := types.Nodes{} + if err := tx.Find(&nodes).Error; err != nil { + log.Error().Err(err).Msg("Error accessing db") + } + + for item, node := range nodes { + if node.IsApproved() == false { + err = tx.Model(nodes[item]).Updates(types.Node{ + Approved: true, + }).Error + if err != nil { + log.Error(). + Caller(). + Str("hostname", node.Hostname). + Bool("approved", node.IsApproved()). + Err(err). + Msg("Failed to add approval option to existing nodes during database migration") + } + } + } + + return nil + } + + return fmt.Errorf("no node approved column in DB") + }, + Rollback: func(db *gorm.DB) error { return nil }, + }, }, ) @@ -497,7 +541,8 @@ func NewHeadscaleDatabase( cfg: &cfg, regCache: regCache, - baseDomain: baseDomain, + baseDomain: baseDomain, + nodeManagement: &nodeManagement, } return &db, err diff --git a/hscontrol/db/ip.go b/hscontrol/db/ip.go index 3525795a67f..b1289ab89af 100644 --- a/hscontrol/db/ip.go +++ b/hscontrol/db/ip.go @@ -265,10 +265,10 @@ func isTailscaleReservedIP(ip netip.Addr) bool { // it will be added. // If a prefix type has been removed (IPv4 or IPv6), it // will remove the IPs in that family from the node. -func (db *HSDatabase) BackfillNodeIPs(i *IPAllocator) ([]string, error) { +func (hsdb *HSDatabase) BackfillNodeIPs(i *IPAllocator) ([]string, error) { var err error var ret []string - err = db.Write(func(tx *gorm.DB) error { + err = hsdb.Write(func(tx *gorm.DB) error { if i == nil { return errors.New("backfilling IPs: ip allocator was nil") } diff --git a/hscontrol/db/node.go b/hscontrol/db/node.go index 1b6e75389a9..96b6c406817 100644 --- a/hscontrol/db/node.go +++ b/hscontrol/db/node.go @@ -50,8 +50,9 @@ func ListPeers(tx *gorm.DB, nodeID types.NodeID) (types.Nodes, error) { Preload("AuthKey.User"). Preload("User"). Preload("Routes"). - Where("id <> ?", - nodeID).Find(&nodes).Error; err != nil { + Where("id <> ?", nodeID). + Where("approved = ?", true). + Find(&nodes).Error; err != nil { return types.Nodes{}, err } @@ -261,6 +262,19 @@ func RenameNode(tx *gorm.DB, return nil } +func (hsdb *HSDatabase) NodeSetApprove(nodeID types.NodeID, approved bool) error { + return hsdb.Write(func(tx *gorm.DB) error { + return NodeSetApprove(tx, nodeID, approved) + }) +} + +// NodeSetApprove takes a Node struct and a set approval option +func NodeSetApprove(tx *gorm.DB, + nodeID types.NodeID, approved bool, +) error { + return tx.Model(&types.Node{}).Where("id = ?", nodeID).Update("approved", approved).Error +} + func (hsdb *HSDatabase) NodeSetExpiry(nodeID types.NodeID, expiry time.Time) error { return hsdb.Write(func(tx *gorm.DB) error { return NodeSetExpiry(tx, nodeID, expiry) @@ -328,6 +342,8 @@ func (hsdb *HSDatabase) RegisterNodeFromAuthCallback( ipv6 *netip.Addr, ) (*types.Node, error) { return Write(hsdb.DB, func(tx *gorm.DB) (*types.Node, error) { + manualApprovedNode := hsdb.nodeManagement.ManualApproveNewNode + if node, ok := hsdb.regCache.Get(mkey.String()); ok { user, err := GetUserByID(tx, userID) if err != nil { @@ -341,6 +357,7 @@ func (hsdb *HSDatabase) RegisterNodeFromAuthCallback( Str("machine_key", mkey.ShortString()). Str("username", user.Username()). Str("registrationMethod", registrationMethod). + Bool("manualApprovedNode", manualApprovedNode). Str("expiresAt", fmt.Sprintf("%v", nodeExpiry)). Msg("Registering node from API/CLI or auth callback") @@ -354,6 +371,10 @@ func (hsdb *HSDatabase) RegisterNodeFromAuthCallback( node.User = *user node.RegisterMethod = registrationMethod + if node.IsApproved() == false && manualApprovedNode == false { + node.Approved = true + } + if nodeExpiry != nil { node.Expiry = nodeExpiry } @@ -388,6 +409,7 @@ func RegisterNode(tx *gorm.DB, node types.Node, ipv4 *netip.Addr, ipv6 *netip.Ad Str("machine_key", node.MachineKey.ShortString()). Str("node_key", node.NodeKey.ShortString()). Str("user", node.User.Username()). + Bool("approved", node.IsApproved()). Msg("Registering node") // If the node exists and it already has IP(s), we just save it @@ -404,6 +426,7 @@ func RegisterNode(tx *gorm.DB, node types.Node, ipv4 *netip.Addr, ipv6 *netip.Ad Str("machine_key", node.MachineKey.ShortString()). Str("node_key", node.NodeKey.ShortString()). Str("user", node.User.Username()). + Bool("approved", node.IsApproved()). Msg("Node authorized again") return &node, nil @@ -428,6 +451,7 @@ func RegisterNode(tx *gorm.DB, node types.Node, ipv4 *netip.Addr, ipv6 *netip.Ad log.Trace(). Caller(). Str("node", node.Hostname). + Bool("approved", node.IsApproved()). Msg("Node registered with the database") return &node, nil diff --git a/hscontrol/db/preauth_keys.go b/hscontrol/db/preauth_keys.go index 59bbdf98c67..26a2131b079 100644 --- a/hscontrol/db/preauth_keys.go +++ b/hscontrol/db/preauth_keys.go @@ -26,12 +26,13 @@ func (hsdb *HSDatabase) CreatePreAuthKey( // TODO(kradalby): Should be ID, not name userName string, reusable bool, + preApproved bool, ephemeral bool, expiration *time.Time, aclTags []string, ) (*types.PreAuthKey, error) { return Write(hsdb.DB, func(tx *gorm.DB) (*types.PreAuthKey, error) { - return CreatePreAuthKey(tx, userName, reusable, ephemeral, expiration, aclTags) + return CreatePreAuthKey(tx, userName, reusable, preApproved, ephemeral, expiration, aclTags) }) } @@ -41,6 +42,7 @@ func CreatePreAuthKey( // TODO(kradalby): Should be ID, not name userName string, reusable bool, + preApproved bool, ephemeral bool, expiration *time.Time, aclTags []string, @@ -72,14 +74,15 @@ func CreatePreAuthKey( } key := types.PreAuthKey{ - Key: kstr, - UserID: user.ID, - User: *user, - Reusable: reusable, - Ephemeral: ephemeral, - CreatedAt: &now, - Expiration: expiration, - Tags: aclTags, + Key: kstr, + UserID: user.ID, + User: *user, + Reusable: reusable, + PreApproved: preApproved, + Ephemeral: ephemeral, + CreatedAt: &now, + Expiration: expiration, + Tags: aclTags, } if err := tx.Save(&key).Error; err != nil { diff --git a/hscontrol/grpcv1.go b/hscontrol/grpcv1.go index 6879371687f..5b7aa1ebc61 100644 --- a/hscontrol/grpcv1.go +++ b/hscontrol/grpcv1.go @@ -134,6 +134,7 @@ func (api headscaleV1APIServer) CreatePreAuthKey( preAuthKey, err := api.h.db.CreatePreAuthKey( request.GetUser(), request.GetReusable(), + request.GetPreApproved(), request.GetEphemeral(), &expiration, request.AclTags, @@ -328,6 +329,54 @@ func (api headscaleV1APIServer) DeleteNode( return &v1.DeleteNodeResponse{}, nil } +func (api headscaleV1APIServer) ApproveNode( + ctx context.Context, + request *v1.ApproveNodeRequest, +) (*v1.ApproveNodeResponse, error) { + node, err := db.Write(api.h.db.DB, func(tx *gorm.DB) (*types.Node, error) { + if err := db.NodeSetApprove( + tx, + types.NodeID(request.GetNodeId()), + true, + ); err != nil { + return nil, err + } + + return db.GetNodeByID(tx, types.NodeID(request.GetNodeId())) + }) + if err != nil { + return nil, err + } + + ctx = types.NotifyCtx(ctx, "cli-approved-node-self", node.Hostname) + api.h.nodeNotifier.NotifyByNodeID( + ctx, + types.StateUpdate{ + Type: types.StateSelfUpdate, + ChangeNodes: []types.NodeID{node.ID}, + }, + node.ID) + + ctx = types.NotifyCtx(ctx, "cli-approved-node-peers", node.Hostname) + api.h.nodeNotifier.NotifyWithIgnore( + ctx, + types.StateUpdate{ + Type: types.StatePeerChangedPatch, + ChangePatches: []*tailcfg.PeerChange{ + { + NodeID: node.ID.NodeID(), + }, + }, + }, + node.ID) + + log.Trace(). + Str("node", node.Hostname). + Msg("node approved") + + return &v1.ApproveNodeResponse{Node: node.Proto()}, nil +} + func (api headscaleV1APIServer) ExpireNode( ctx context.Context, request *v1.ExpireNodeRequest, diff --git a/hscontrol/mapper/tail.go b/hscontrol/mapper/tail.go index 24c521dc04d..a093ea99fa2 100644 --- a/hscontrol/mapper/tail.go +++ b/hscontrol/mapper/tail.go @@ -110,7 +110,7 @@ func tailNode( PrimaryRoutes: primaryPrefixes, - MachineAuthorized: !node.IsExpired(), + MachineAuthorized: node.IsApproved(), Expired: node.IsExpired(), } diff --git a/hscontrol/types/config.go b/hscontrol/types/config.go index f02b9758bbd..3f70ca7bb81 100644 --- a/hscontrol/types/config.go +++ b/hscontrol/types/config.go @@ -86,6 +86,8 @@ type Config struct { Policy PolicyConfig Tuning Tuning + + NodeManagement NodeManagement } type DNSConfig struct { @@ -209,6 +211,10 @@ type Tuning struct { NodeMapSessionBufferedChanSize int } +type NodeManagement struct { + ManualApproveNewNode bool +} + // LoadConfig prepares and loads the Headscale configuration into Viper. // This means it sets the default values, reads the configuration file and // environment variables, and handles deprecated configuration options. @@ -284,6 +290,8 @@ func LoadConfig(path string, isFile bool) error { viper.SetDefault("tuning.batch_change_delay", "800ms") viper.SetDefault("tuning.node_mapsession_buffered_chan_size", 30) + viper.SetDefault("node_management.manual_approve_new_node", false) + viper.SetDefault("prefixes.allocation", string(IPAllocationStrategySequential)) if err := viper.ReadInConfig(); err != nil { @@ -736,6 +744,14 @@ func prefixV6() (*netip.Prefix, error) { return &prefixV6, nil } +func nodeManagementConfig() NodeManagement { + manualApproveNewNode := viper.GetBool("node_management.manual_approve_new_node") + + return NodeManagement{ + ManualApproveNewNode: manualApproveNewNode, + } +} + // LoadCLIConfig returns the needed configuration for the CLI client // of Headscale to connect to a Headscale server. func LoadCLIConfig() (*Config, error) { @@ -833,6 +849,8 @@ func LoadServerConfig() (*Config, error) { ) } + nodeManagement := nodeManagementConfig() + return &Config{ ServerURL: serverURL, Addr: viper.GetString("listen_addr"), @@ -920,6 +938,8 @@ func LoadServerConfig() (*Config, error) { "tuning.node_mapsession_buffered_chan_size", ), }, + + NodeManagement: nodeManagement, }, nil } diff --git a/hscontrol/types/node.go b/hscontrol/types/node.go index c702f23a2eb..94c6b589283 100644 --- a/hscontrol/types/node.go +++ b/hscontrol/types/node.go @@ -83,6 +83,7 @@ type Node struct { LastSeen *time.Time Expiry *time.Time + Approved bool `sql:"DEFAULT:false"` Routes []Route `gorm:"constraint:OnDelete:CASCADE;"` @@ -109,6 +110,11 @@ func (node Node) IsExpired() bool { return time.Since(*node.Expiry) > 0 } +// IsApproved returns whether the node is approved. +func (node Node) IsApproved() bool { + return node.Approved == true +} + // IsEphemeral returns if the node is registered as an Ephemeral node. // https://tailscale.com/kb/1111/ephemeral-nodes/ func (node *Node) IsEphemeral() bool { @@ -234,6 +240,7 @@ func (node *Node) Proto() *v1.Node { ForcedTags: node.ForcedTags, RegisterMethod: node.RegisterMethodToV1Enum(), + Approved: node.Approved, CreatedAt: timestamppb.New(node.CreatedAt), } diff --git a/hscontrol/types/preauth_key.go b/hscontrol/types/preauth_key.go index ba3b597bc72..7b8be423de7 100644 --- a/hscontrol/types/preauth_key.go +++ b/hscontrol/types/preauth_key.go @@ -11,14 +11,15 @@ import ( // PreAuthKey describes a pre-authorization key usable in a particular user. type PreAuthKey struct { - ID uint64 `gorm:"primary_key"` - Key string - UserID uint - User User `gorm:"constraint:OnDelete:CASCADE;"` - Reusable bool - Ephemeral bool `gorm:"default:false"` - Used bool `gorm:"default:false"` - Tags []string `gorm:"serializer:json"` + ID uint64 `gorm:"primary_key"` + Key string + UserID uint + User User `gorm:"constraint:OnDelete:CASCADE;"` + Reusable bool + PreApproved bool `gorm:"default:false"` + Ephemeral bool `gorm:"default:false"` + Used bool `gorm:"default:false"` + Tags []string `gorm:"serializer:json"` CreatedAt *time.Time Expiration *time.Time @@ -26,13 +27,14 @@ type PreAuthKey struct { func (key *PreAuthKey) Proto() *v1.PreAuthKey { protoKey := v1.PreAuthKey{ - User: key.User.Name, - Id: strconv.FormatUint(key.ID, util.Base10), - Key: key.Key, - Ephemeral: key.Ephemeral, - Reusable: key.Reusable, - Used: key.Used, - AclTags: key.Tags, + User: key.User.Name, + Id: strconv.FormatUint(key.ID, util.Base10), + Key: key.Key, + Ephemeral: key.Ephemeral, + PreApproved: key.PreApproved, + Reusable: key.Reusable, + Used: key.Used, + AclTags: key.Tags, } if key.Expiration != nil { From 806c3bf672dbc59b4096ce21cdcd77b5be331647 Mon Sep 17 00:00:00 2001 From: hopleus Date: Wed, 16 Oct 2024 19:58:21 +0300 Subject: [PATCH 03/22] Corrected existing tests. New tests added --- cmd/headscale/headscale_test.go | 1 + hscontrol/db/db_test.go | 15 ++-- hscontrol/db/node_test.go | 109 +++++++++++++++++++++++++++--- hscontrol/db/preauth_keys_test.go | 32 ++++++--- hscontrol/db/routes_test.go | 9 +-- hscontrol/db/suite_test.go | 1 + hscontrol/db/users_test.go | 6 +- hscontrol/mapper/mapper_test.go | 3 + hscontrol/mapper/tail_test.go | 3 +- 9 files changed, 145 insertions(+), 34 deletions(-) diff --git a/cmd/headscale/headscale_test.go b/cmd/headscale/headscale_test.go index 00c4a276f4d..323434c1607 100644 --- a/cmd/headscale/headscale_test.go +++ b/cmd/headscale/headscale_test.go @@ -111,4 +111,5 @@ func (*Suite) TestConfigLoading(c *check.C) { ) c.Assert(viper.GetBool("logtail.enabled"), check.Equals, false) c.Assert(viper.GetBool("randomize_client_port"), check.Equals, false) + c.Assert(viper.GetBool("node_management.manual_approve_new_node"), check.Equals, false) } diff --git a/hscontrol/db/db_test.go b/hscontrol/db/db_test.go index 68ea2ac1c2b..48a93d459fb 100644 --- a/hscontrol/db/db_test.go +++ b/hscontrol/db/db_test.go @@ -203,12 +203,17 @@ func TestMigrations(t *testing.T) { t.Fatalf("copying db for test: %s", err) } - hsdb, err := NewHeadscaleDatabase(types.DatabaseConfig{ - Type: "sqlite3", - Sqlite: types.SqliteConfig{ - Path: dbPath, + hsdb, err := NewHeadscaleDatabase( + types.DatabaseConfig{ + Type: "sqlite3", + Sqlite: types.SqliteConfig{ + Path: dbPath, + }, }, - }, "", emptyCache()) + "", + types.NodeManagement{}, + emptyCache(), + ) if err != nil && tt.wantErr != err.Error() { t.Errorf("TestMigrations() unexpected error = %v, wantErr %v", err, tt.wantErr) } diff --git a/hscontrol/db/node_test.go b/hscontrol/db/node_test.go index 888f48dbcf5..0392e3b0e64 100644 --- a/hscontrol/db/node_test.go +++ b/hscontrol/db/node_test.go @@ -29,7 +29,7 @@ func (s *Suite) TestGetNode(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.getNode("test", "testnode") @@ -58,7 +58,7 @@ func (s *Suite) TestGetNodeByID(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.GetNodeByID(0) @@ -87,7 +87,7 @@ func (s *Suite) TestGetNodeByAnyNodeKey(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.GetNodeByID(0) @@ -143,7 +143,7 @@ func (s *Suite) TestListPeers(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.GetNodeByID(0) @@ -161,6 +161,7 @@ func (s *Suite) TestListPeers(c *check.C) { UserID: user.ID, RegisterMethod: util.RegisterMethodAuthKey, AuthKeyID: ptr.To(pak.ID), + Approved: true, } trx := db.DB.Save(&node) c.Assert(trx.Error, check.IsNil) @@ -176,6 +177,53 @@ func (s *Suite) TestListPeers(c *check.C) { c.Assert(peersOfNode0[0].Hostname, check.Equals, "testnode2") c.Assert(peersOfNode0[5].Hostname, check.Equals, "testnode7") c.Assert(peersOfNode0[8].Hostname, check.Equals, "testnode10") + c.Assert(peersOfNode0[0].IsApproved(), check.Equals, true) + c.Assert(peersOfNode0[5].IsApproved(), check.Equals, true) + c.Assert(peersOfNode0[8].IsApproved(), check.Equals, true) +} + +func (s *Suite) TestListPeersWithoutNonAuthorized(c *check.C) { + user, err := db.CreateUser("test") + c.Assert(err, check.IsNil) + + pak, err := db.CreatePreAuthKey(user.Name, false, false, false, nil, nil) + c.Assert(err, check.IsNil) + + _, err = db.GetNodeByID(0) + c.Assert(err, check.NotNil) + + for index := 0; index <= 4; index++ { + nodeKey := key.NewNode() + machineKey := key.NewMachine() + + var approved bool + if index == 4 { + approved = true + } + + node := types.Node{ + ID: types.NodeID(index), + MachineKey: machineKey.Public(), + NodeKey: nodeKey.Public(), + Hostname: "testnode" + strconv.Itoa(index), + UserID: user.ID, + RegisterMethod: util.RegisterMethodAuthKey, + AuthKeyID: ptr.To(pak.ID), + Approved: approved, + } + trx := db.DB.Save(&node) + c.Assert(trx.Error, check.IsNil) + } + + node0ByID, err := db.GetNodeByID(0) + c.Assert(err, check.IsNil) + + peersOfNode0, err := db.ListPeers(node0ByID.ID) + c.Assert(err, check.IsNil) + + c.Assert(len(peersOfNode0), check.Equals, 1) + c.Assert(peersOfNode0[0].Hostname, check.Equals, "testnode4") + c.Assert(peersOfNode0[0].IsApproved(), check.Equals, true) } func (s *Suite) TestGetACLFilteredPeers(c *check.C) { @@ -189,7 +237,7 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) { for _, name := range []string{"test", "admin"} { user, err := db.CreateUser(name) c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) c.Assert(err, check.IsNil) stor = append(stor, base{user, pak}) } @@ -210,6 +258,7 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) { Hostname: "testnode" + strconv.Itoa(index), UserID: stor[index%2].user.ID, RegisterMethod: util.RegisterMethodAuthKey, + Approved: true, AuthKeyID: ptr.To(stor[index%2].key.ID), } trx := db.DB.Save(&node) @@ -277,11 +326,51 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) { c.Assert(peersOfAdminNode[5].Hostname, check.Equals, "testnode7") } +func (s *Suite) TestApprovedNode(c *check.C) { + user, err := db.CreateUser("test") + c.Assert(err, check.IsNil) + + pak, err := db.CreatePreAuthKey(user.Name, false, false, true, nil, nil) + c.Assert(err, check.IsNil) + + _, err = db.getNode("test", "testnode") + c.Assert(err, check.NotNil) + + nodeKey := key.NewNode() + machineKey := key.NewMachine() + + node := &types.Node{ + ID: 0, + MachineKey: machineKey.Public(), + NodeKey: nodeKey.Public(), + Hostname: "testnode", + UserID: user.ID, + RegisterMethod: util.RegisterMethodAuthKey, + AuthKeyID: ptr.To(pak.ID), + Expiry: &time.Time{}, + } + db.DB.Save(node) + + nodeFromDB, err := db.getNode("test", "testnode") + c.Assert(err, check.IsNil) + c.Assert(nodeFromDB, check.NotNil) + + c.Assert(nodeFromDB.IsApproved(), check.Equals, false) + + err = db.NodeSetApprove(nodeFromDB.ID, true) + c.Assert(err, check.IsNil) + + nodeFromDB, err = db.getNode("test", "testnode") + c.Assert(err, check.IsNil) + + c.Assert(nodeFromDB.IsApproved(), check.Equals, true) +} + func (s *Suite) TestExpireNode(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.getNode("test", "testnode") @@ -322,7 +411,7 @@ func (s *Suite) TestSetTags(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.getNode("test", "testnode") @@ -567,7 +656,7 @@ func TestAutoApproveRoutes(t *testing.T) { user, err := adb.CreateUser("test") assert.NoError(t, err) - pak, err := adb.CreatePreAuthKey(user.Name, false, false, nil, nil) + pak, err := adb.CreatePreAuthKey(user.Name, false, true, false, nil, nil) assert.NoError(t, err) nodeKey := key.NewNode() @@ -699,10 +788,10 @@ func TestListEphemeralNodes(t *testing.T) { user, err := db.CreateUser("test") assert.NoError(t, err) - pak, err := db.CreatePreAuthKey(user.Name, false, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) assert.NoError(t, err) - pakEph, err := db.CreatePreAuthKey(user.Name, false, true, nil, nil) + pakEph, err := db.CreatePreAuthKey(user.Name, false, true, true, nil, nil) assert.NoError(t, err) node := types.Node{ diff --git a/hscontrol/db/preauth_keys_test.go b/hscontrol/db/preauth_keys_test.go index ec3f6441f58..7894eb339ec 100644 --- a/hscontrol/db/preauth_keys_test.go +++ b/hscontrol/db/preauth_keys_test.go @@ -11,14 +11,14 @@ import ( ) func (*Suite) TestCreatePreAuthKey(c *check.C) { - _, err := db.CreatePreAuthKey("bogus", true, false, nil, nil) + _, err := db.CreatePreAuthKey("bogus", true, true, false, nil, nil) c.Assert(err, check.NotNil) user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - key, err := db.CreatePreAuthKey(user.Name, true, false, nil, nil) + key, err := db.CreatePreAuthKey(user.Name, true, true, false, nil, nil) c.Assert(err, check.IsNil) // Did we get a valid key? @@ -44,7 +44,7 @@ func (*Suite) TestExpiredPreAuthKey(c *check.C) { c.Assert(err, check.IsNil) now := time.Now().Add(-5 * time.Second) - pak, err := db.CreatePreAuthKey(user.Name, true, false, &now, nil) + pak, err := db.CreatePreAuthKey(user.Name, true, true, false, &now, nil) c.Assert(err, check.IsNil) key, err := db.ValidatePreAuthKey(pak.Key) @@ -52,6 +52,16 @@ func (*Suite) TestExpiredPreAuthKey(c *check.C) { c.Assert(key, check.IsNil) } +func (*Suite) TestPreApprovedPreAuthKey(c *check.C) { + user, err := db.CreateUser("test2") + c.Assert(err, check.IsNil) + + now := time.Now().Add(-5 * time.Second) + pak, err := db.CreatePreAuthKey(user.Name, true, true, false, &now, nil) + c.Assert(err, check.IsNil) + c.Assert(pak.PreApproved, check.Equals, true) +} + func (*Suite) TestPreAuthKeyDoesNotExist(c *check.C) { key, err := db.ValidatePreAuthKey("potatoKey") c.Assert(err, check.Equals, ErrPreAuthKeyNotFound) @@ -62,7 +72,7 @@ func (*Suite) TestValidateKeyOk(c *check.C) { user, err := db.CreateUser("test3") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, true, true, false, nil, nil) c.Assert(err, check.IsNil) key, err := db.ValidatePreAuthKey(pak.Key) @@ -74,7 +84,7 @@ func (*Suite) TestAlreadyUsedKey(c *check.C) { user, err := db.CreateUser("test4") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) c.Assert(err, check.IsNil) node := types.Node{ @@ -96,7 +106,7 @@ func (*Suite) TestReusableBeingUsedKey(c *check.C) { user, err := db.CreateUser("test5") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, true, true, false, nil, nil) c.Assert(err, check.IsNil) node := types.Node{ @@ -118,7 +128,7 @@ func (*Suite) TestNotReusableNotBeingUsedKey(c *check.C) { user, err := db.CreateUser("test6") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) c.Assert(err, check.IsNil) key, err := db.ValidatePreAuthKey(pak.Key) @@ -130,7 +140,7 @@ func (*Suite) TestExpirePreauthKey(c *check.C) { user, err := db.CreateUser("test3") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, true, true, false, nil, nil) c.Assert(err, check.IsNil) c.Assert(pak.Expiration, check.IsNil) @@ -147,7 +157,7 @@ func (*Suite) TestNotReusableMarkedAsUsed(c *check.C) { user, err := db.CreateUser("test6") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) c.Assert(err, check.IsNil) pak.Used = true db.DB.Save(&pak) @@ -160,12 +170,12 @@ func (*Suite) TestPreAuthKeyACLTags(c *check.C) { user, err := db.CreateUser("test8") c.Assert(err, check.IsNil) - _, err = db.CreatePreAuthKey(user.Name, false, false, nil, []string{"badtag"}) + _, err = db.CreatePreAuthKey(user.Name, false, true, false, nil, []string{"badtag"}) c.Assert(err, check.NotNil) // Confirm that malformed tags are rejected tags := []string{"tag:test1", "tag:test2"} tagsWithDuplicate := []string{"tag:test1", "tag:test2", "tag:test2"} - _, err = db.CreatePreAuthKey(user.Name, false, false, nil, tagsWithDuplicate) + _, err = db.CreatePreAuthKey(user.Name, false, true, false, nil, tagsWithDuplicate) c.Assert(err, check.IsNil) listedPaks, err := db.ListPreAuthKeys("test8") diff --git a/hscontrol/db/routes_test.go b/hscontrol/db/routes_test.go index 5071077cb80..6474e5f716c 100644 --- a/hscontrol/db/routes_test.go +++ b/hscontrol/db/routes_test.go @@ -35,7 +35,7 @@ func (s *Suite) TestGetRoutes(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.getNode("test", "test_get_route_node") @@ -79,7 +79,7 @@ func (s *Suite) TestGetEnableRoutes(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.getNode("test", "test_enable_route_node") @@ -153,7 +153,7 @@ func (s *Suite) TestIsUniquePrefix(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.getNode("test", "test_enable_route_node") @@ -234,7 +234,7 @@ func (s *Suite) TestDeleteRoutes(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.getNode("test", "test_enable_route_node") @@ -336,6 +336,7 @@ func dbForTest(t *testing.T, testName string) *HSDatabase { }, }, "", + types.NodeManagement{}, emptyCache(), ) if err != nil { diff --git a/hscontrol/db/suite_test.go b/hscontrol/db/suite_test.go index 6cc46d3d12e..237ae290c63 100644 --- a/hscontrol/db/suite_test.go +++ b/hscontrol/db/suite_test.go @@ -59,6 +59,7 @@ func newTestDB() (*HSDatabase, error) { }, }, "", + types.NodeManagement{}, emptyCache(), ) if err != nil { diff --git a/hscontrol/db/users_test.go b/hscontrol/db/users_test.go index 5439966442c..f276c0d1711 100644 --- a/hscontrol/db/users_test.go +++ b/hscontrol/db/users_test.go @@ -31,7 +31,7 @@ func (s *Suite) TestDestroyUserErrors(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) c.Assert(err, check.IsNil) err = db.DestroyUser("test") @@ -44,7 +44,7 @@ func (s *Suite) TestDestroyUserErrors(c *check.C) { user, err = db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err = db.CreatePreAuthKey(user.Name, false, false, nil, nil) + pak, err = db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) c.Assert(err, check.IsNil) node := types.Node{ @@ -97,7 +97,7 @@ func (s *Suite) TestSetMachineUser(c *check.C) { newUser, err := db.CreateUser("new") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(oldUser.Name, false, false, nil, nil) + pak, err := db.CreatePreAuthKey(oldUser.Name, false, true, false, nil, nil) c.Assert(err, check.IsNil) node := types.Node{ diff --git a/hscontrol/mapper/mapper_test.go b/hscontrol/mapper/mapper_test.go index 32ea5352e5d..395ece44ccb 100644 --- a/hscontrol/mapper/mapper_test.go +++ b/hscontrol/mapper/mapper_test.go @@ -190,6 +190,7 @@ func Test_fullMapResponse(t *testing.T) { ForcedTags: []string{}, AuthKey: &types.PreAuthKey{}, LastSeen: &lastSeen, + Approved: true, Expiry: &expire, Hostinfo: &tailcfg.Hostinfo{}, Routes: []types.Route{ @@ -269,6 +270,7 @@ func Test_fullMapResponse(t *testing.T) { User: types.User{Name: "mini"}, ForcedTags: []string{}, LastSeen: &lastSeen, + Approved: true, Expiry: &expire, Hostinfo: &tailcfg.Hostinfo{}, Routes: []types.Route{}, @@ -325,6 +327,7 @@ func Test_fullMapResponse(t *testing.T) { ForcedTags: []string{}, LastSeen: &lastSeen, Expiry: &expire, + Approved: true, Hostinfo: &tailcfg.Hostinfo{}, Routes: []types.Route{}, CreatedAt: created, diff --git a/hscontrol/mapper/tail_test.go b/hscontrol/mapper/tail_test.go index b6692c16fbe..59c8b6463e7 100644 --- a/hscontrol/mapper/tail_test.go +++ b/hscontrol/mapper/tail_test.go @@ -72,7 +72,7 @@ func TestTailNode(t *testing.T) { Hostinfo: hiview(tailcfg.Hostinfo{}), Tags: []string{}, PrimaryRoutes: []netip.Prefix{}, - MachineAuthorized: true, + MachineAuthorized: false, CapMap: tailcfg.NodeCapMap{ tailcfg.CapabilityFileSharing: []tailcfg.RawMessage{}, @@ -105,6 +105,7 @@ func TestTailNode(t *testing.T) { ForcedTags: []string{}, AuthKey: &types.PreAuthKey{}, LastSeen: &lastSeen, + Approved: true, Expiry: &expire, Hostinfo: &tailcfg.Hostinfo{}, Routes: []types.Route{ From 85966d2cbd2ce0d9c224bbf7cf85de3b6c9be0b2 Mon Sep 17 00:00:00 2001 From: hopleus Date: Thu, 17 Oct 2024 10:12:08 +0300 Subject: [PATCH 04/22] Corrected existing integration tests. New integration tests added --- integration/auth_approval_test.go | 281 ++++++++++++++++++++++++++++++ integration/cli_test.go | 56 ++++++ integration/control.go | 2 +- integration/embedded_derp_test.go | 2 +- integration/general_test.go | 6 +- integration/hsic/hsic.go | 12 ++ integration/scenario.go | 5 +- integration/scenario_test.go | 4 +- integration/tailscale.go | 1 + integration/tsic/tsic.go | 22 +++ 10 files changed, 382 insertions(+), 9 deletions(-) create mode 100644 integration/auth_approval_test.go diff --git a/integration/auth_approval_test.go b/integration/auth_approval_test.go new file mode 100644 index 00000000000..f6a1fe519b3 --- /dev/null +++ b/integration/auth_approval_test.go @@ -0,0 +1,281 @@ +package integration + +import ( + "context" + "fmt" + v1 "github.com/juanfont/headscale/gen/go/headscale/v1" + "github.com/juanfont/headscale/integration/hsic" + "github.com/samber/lo" + "github.com/stretchr/testify/assert" + "io" + "log" + "net/http" + "net/netip" + "net/url" + "strings" + "testing" +) + +type AuthApprovalScenario struct { + *Scenario +} + +func TestAuthNodeApproval(t *testing.T) { + IntegrationSkip(t) + t.Parallel() + + baseScenario, err := NewScenario(dockertestMaxWait()) + assertNoErr(t, err) + + scenario := AuthApprovalScenario{ + Scenario: baseScenario, + } + defer scenario.ShutdownAssertNoPanics(t) + + spec := map[string]int{ + "user1": len(MustTestVersions), + } + + err = scenario.CreateHeadscaleEnv( + spec, + hsic.WithTestName("approval"), + hsic.WithManualApproveNewNode(), + ) + assertNoErrHeadscaleEnv(t, err) + + allClients, err := scenario.ListTailscaleClients() + assertNoErrListClients(t, err) + + err = scenario.WaitForTailscaleSyncWithPeerCount(0) + assertNoErrSync(t, err) + + for _, client := range allClients { + status, err := client.Status() + assertNoErr(t, err) + assert.Equal(t, "NeedsMachineAuth", status.BackendState) + assert.Len(t, status.Peers(), 0) + } + + headscale, err := scenario.Headscale() + assertNoErr(t, err) + + var allNodes []*v1.Node + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "list", + "--output", + "json", + }, + &allNodes, + ) + assert.Nil(t, err) + + for _, node := range allNodes { + _, err = headscale.Execute([]string{ + "headscale", "nodes", "approve", "--identifier", fmt.Sprintf("%d", node.Id), + }) + assertNoErr(t, err) + } + + for _, client := range allClients { + err = client.Logout() + if err != nil { + t.Fatalf("failed to logout client %s: %s", client.Hostname(), err) + } + } + + err = scenario.WaitForTailscaleLogout() + assertNoErrLogout(t, err) + + t.Logf("all clients logged out") + + for userName := range spec { + err = scenario.runTailscaleUp(userName, headscale.GetEndpoint(), true) + if err != nil { + t.Fatalf("failed to run tailscale up: %s", err) + } + } + + t.Logf("all clients logged in again") + + allClients, err = scenario.ListTailscaleClients() + assertNoErrListClients(t, err) + + allIps, err := scenario.ListTailscaleClientsIPs() + assertNoErrListClientIPs(t, err) + + err = scenario.WaitForTailscaleSync() + assertNoErrSync(t, err) + + //assertClientsState(t, allClients) + + allAddrs := lo.Map(allIps, func(x netip.Addr, index int) string { + return x.String() + }) + + success := pingAllHelper(t, allClients, allAddrs) + t.Logf("before expire: %d successful pings out of %d", success, len(allClients)*len(allIps)) + + for _, client := range allClients { + status, err := client.Status() + assertNoErr(t, err) + + // Assert that we have the original count - self + assert.Len(t, status.Peers(), len(MustTestVersions)-1) + } +} + +func (s *AuthApprovalScenario) CreateHeadscaleEnv( + users map[string]int, + opts ...hsic.Option, +) error { + headscale, err := s.Headscale(opts...) + if err != nil { + return err + } + + err = headscale.WaitForRunning() + if err != nil { + return err + } + + for userName, clientCount := range users { + log.Printf("creating user %s with %d clients", userName, clientCount) + err = s.CreateUser(userName) + if err != nil { + return err + } + + err = s.CreateTailscaleNodesInUser(userName, "all", clientCount) + if err != nil { + return err + } + + err = s.runTailscaleUp(userName, headscale.GetEndpoint(), false) + if err != nil { + return err + } + } + + return nil +} + +func (s *AuthApprovalScenario) runTailscaleUp( + userStr, loginServer string, + withApproved bool, +) error { + log.Printf("running tailscale up for user %s", userStr) + if user, ok := s.users[userStr]; ok { + for _, client := range user.Clients { + c := client + user.joinWaitGroup.Go(func() error { + loginURL, err := c.LoginWithURL(loginServer) + if err != nil { + log.Printf("failed to run tailscale up (%s): %s", c.Hostname(), err) + + return err + } + + err = s.runHeadscaleRegister(userStr, loginURL) + if err != nil { + log.Printf("failed to register client (%s): %s", c.Hostname(), err) + + return err + } + + return nil + }) + + if withApproved { + err := client.WaitForRunning() + if err != nil { + log.Printf("error waiting for client %s to be approval: %s", client.Hostname(), err) + } + } else { + err := client.WaitForNeedsApprove() + if err != nil { + log.Printf("error waiting for client %s to be approval: %s", client.Hostname(), err) + } + } + } + + if err := user.joinWaitGroup.Wait(); err != nil { + return err + } + + for _, client := range user.Clients { + if withApproved { + err := client.WaitForRunning() + if err != nil { + return fmt.Errorf("%s failed to up tailscale node: %w", client.Hostname(), err) + } + } else { + err := client.WaitForNeedsApprove() + if err != nil { + return fmt.Errorf("%s failed to up tailscale node: %w", client.Hostname(), err) + } + } + } + + return nil + } + + return fmt.Errorf("failed to up tailscale node: %w", errNoUserAvailable) +} + +func (s *AuthApprovalScenario) runHeadscaleRegister(userStr string, loginURL *url.URL) error { + headscale, err := s.Headscale() + if err != nil { + return err + } + + log.Printf("loginURL: %s", loginURL) + loginURL.Host = fmt.Sprintf("%s:8080", headscale.GetIP()) + loginURL.Scheme = "http" + + httpClient := &http.Client{} + ctx := context.Background() + req, _ := http.NewRequestWithContext(ctx, http.MethodGet, loginURL.String(), nil) + resp, err := httpClient.Do(req) + if err != nil { + return err + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + defer resp.Body.Close() + + // see api.go HTML template + codeSep := strings.Split(string(body), "") + if len(codeSep) != 2 { + return errParseAuthPage + } + + keySep := strings.Split(codeSep[0], "key ") + if len(keySep) != 2 { + return errParseAuthPage + } + key := keySep[1] + log.Printf("registering node %s", key) + + if headscale, err := s.Headscale(); err == nil { + _, err = headscale.Execute( + []string{"headscale", "nodes", "register", "--user", userStr, "--key", key}, + ) + if err != nil { + log.Printf("failed to register node: %s", err) + + return err + } + + return nil + } + + return fmt.Errorf("failed to find headscale: %w", errNoHeadscaleAvailable) +} diff --git a/integration/cli_test.go b/integration/cli_test.go index 2b81e81484e..5926d4265fb 100644 --- a/integration/cli_test.go +++ b/integration/cli_test.go @@ -389,6 +389,62 @@ func TestPreAuthKeyCommandReusableEphemeral(t *testing.T) { assert.Len(t, listedPreAuthKeys, 3) } +func TestPreAuthKeyCommandPreApproved(t *testing.T) { + IntegrationSkip(t) + t.Parallel() + + user := "pre-auth-key-pre-approved-user" + + scenario, err := NewScenario(dockertestMaxWait()) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + spec := map[string]int{ + user: 0, + } + + err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clipakresueeph")) + assertNoErr(t, err) + + headscale, err := scenario.Headscale() + assertNoErr(t, err) + + var preAuthGeneralKey v1.PreAuthKey + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "preauthkeys", + "--user", + user, + "create", + "--output", + "json", + }, + &preAuthGeneralKey, + ) + assertNoErr(t, err) + assert.False(t, preAuthGeneralKey.GetPreApproved()) + + var preAuthPreApprovedKey v1.PreAuthKey + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "preauthkeys", + "--user", + user, + "create", + "--pre-approved", + "--output", + "json", + }, + &preAuthPreApprovedKey, + ) + assertNoErr(t, err) + assert.True(t, preAuthPreApprovedKey.GetPreApproved()) +} + func TestPreAuthKeyCorrectUserLoggedInCommand(t *testing.T) { IntegrationSkip(t) t.Parallel() diff --git a/integration/control.go b/integration/control.go index b5699577f52..af67edb26d2 100644 --- a/integration/control.go +++ b/integration/control.go @@ -16,7 +16,7 @@ type ControlServer interface { GetEndpoint() string WaitForRunning() error CreateUser(user string) error - CreateAuthKey(user string, reusable bool, ephemeral bool) (*v1.PreAuthKey, error) + CreateAuthKey(user string, reusable bool, preApproved bool, ephemeral bool) (*v1.PreAuthKey, error) ListNodesInUser(user string) ([]*v1.Node, error) GetCert() []byte GetHostname() string diff --git a/integration/embedded_derp_test.go b/integration/embedded_derp_test.go index 6009aed50dd..a52d44f861f 100644 --- a/integration/embedded_derp_test.go +++ b/integration/embedded_derp_test.go @@ -251,7 +251,7 @@ func (s *EmbeddedDERPServerScenario) CreateHeadscaleEnv( } } - key, err := s.CreatePreAuthKey(userName, true, false) + key, err := s.CreatePreAuthKey(userName, true, true, false) if err != nil { return err } diff --git a/integration/general_test.go b/integration/general_test.go index 085691fb871..89d1772f6ec 100644 --- a/integration/general_test.go +++ b/integration/general_test.go @@ -173,7 +173,7 @@ func TestAuthKeyLogoutAndRelogin(t *testing.T) { } for userName := range spec { - key, err := scenario.CreatePreAuthKey(userName, true, false) + key, err := scenario.CreatePreAuthKey(userName, true, true, false) if err != nil { t.Fatalf("failed to create pre-auth key for user %s: %s", userName, err) } @@ -272,7 +272,7 @@ func testEphemeralWithOptions(t *testing.T, opts ...hsic.Option) { t.Fatalf("failed to create tailscale nodes in user %s: %s", userName, err) } - key, err := scenario.CreatePreAuthKey(userName, true, true) + key, err := scenario.CreatePreAuthKey(userName, true, true, true) if err != nil { t.Fatalf("failed to create pre-auth key for user %s: %s", userName, err) } @@ -362,7 +362,7 @@ func TestEphemeral2006DeletedTooQuickly(t *testing.T) { t.Fatalf("failed to create tailscale nodes in user %s: %s", userName, err) } - key, err := scenario.CreatePreAuthKey(userName, true, true) + key, err := scenario.CreatePreAuthKey(userName, true, true, true) if err != nil { t.Fatalf("failed to create pre-auth key for user %s: %s", userName, err) } diff --git a/integration/hsic/hsic.go b/integration/hsic/hsic.go index c2ae3336dbd..79fa2921726 100644 --- a/integration/hsic/hsic.go +++ b/integration/hsic/hsic.go @@ -211,6 +211,13 @@ func WithTuning(batchTimeout time.Duration, mapSessionChanSize int) Option { } } +// WithManualApproveNewNode allows devices to access the network only after manual approval +func WithManualApproveNewNode() Option { + return func(hsic *HeadscaleInContainer) { + hsic.env["HEADSCALE_NODE_MANAGEMENT_MANUAL_APPROVE_NEW_NODE"] = "true" + } +} + func WithTimezone(timezone string) Option { return func(hsic *HeadscaleInContainer) { hsic.env["TZ"] = timezone @@ -660,6 +667,7 @@ func (t *HeadscaleInContainer) CreateUser( func (t *HeadscaleInContainer) CreateAuthKey( user string, reusable bool, + preApproved bool, ephemeral bool, ) (*v1.PreAuthKey, error) { command := []string{ @@ -678,6 +686,10 @@ func (t *HeadscaleInContainer) CreateAuthKey( command = append(command, "--reusable") } + if preApproved { + command = append(command, "--pre-approved") + } + if ephemeral { command = append(command, "--ephemeral") } diff --git a/integration/scenario.go b/integration/scenario.go index b45c5fe7d4f..ff05aa05925 100644 --- a/integration/scenario.go +++ b/integration/scenario.go @@ -291,10 +291,11 @@ func (s *Scenario) Headscale(opts ...hsic.Option) (ControlServer, error) { func (s *Scenario) CreatePreAuthKey( user string, reusable bool, + preApproved bool, ephemeral bool, ) (*v1.PreAuthKey, error) { if headscale, err := s.Headscale(); err == nil { - key, err := headscale.CreateAuthKey(user, reusable, ephemeral) + key, err := headscale.CreateAuthKey(user, reusable, preApproved, ephemeral) if err != nil { return nil, fmt.Errorf("failed to create user: %w", err) } @@ -503,7 +504,7 @@ func (s *Scenario) CreateHeadscaleEnv( return err } - key, err := s.CreatePreAuthKey(userName, true, false) + key, err := s.CreatePreAuthKey(userName, true, true, false) if err != nil { return err } diff --git a/integration/scenario_test.go b/integration/scenario_test.go index aec6cb5cc0b..caf11c90239 100644 --- a/integration/scenario_test.go +++ b/integration/scenario_test.go @@ -61,7 +61,7 @@ func TestHeadscale(t *testing.T) { }) t.Run("create-auth-key", func(t *testing.T) { - _, err := scenario.CreatePreAuthKey(user, true, false) + _, err := scenario.CreatePreAuthKey(user, true, true, false) if err != nil { t.Fatalf("failed to create preauthkey: %s", err) } @@ -153,7 +153,7 @@ func TestTailscaleNodesJoiningHeadcale(t *testing.T) { }) t.Run("join-headscale", func(t *testing.T) { - key, err := scenario.CreatePreAuthKey(user, true, false) + key, err := scenario.CreatePreAuthKey(user, true, true, false) if err != nil { t.Fatalf("failed to create preauthkey: %s", err) } diff --git a/integration/tailscale.go b/integration/tailscale.go index f858d2c24a6..b93cb2feb85 100644 --- a/integration/tailscale.go +++ b/integration/tailscale.go @@ -32,6 +32,7 @@ type TailscaleClient interface { Netmap() (*netmap.NetworkMap, error) Netcheck() (*netcheck.Report, error) WaitForNeedsLogin() error + WaitForNeedsApprove() error WaitForRunning() error WaitForPeers(expected int) error Ping(hostnameOrIP string, opts ...tsic.PingOption) error diff --git a/integration/tsic/tsic.go b/integration/tsic/tsic.go index 944bb94dff4..b82b44f4305 100644 --- a/integration/tsic/tsic.go +++ b/integration/tsic/tsic.go @@ -772,6 +772,28 @@ func (t *TailscaleInContainer) WaitForNeedsLogin() error { }) } +// WaitForNeedsApprove blocks until the Tailscale (tailscaled) instance is logged in +// and gets approved. +func (t *TailscaleInContainer) WaitForNeedsApprove() error { + return t.pool.Retry(func() error { + status, err := t.Status() + if err != nil { + return errTailscaleStatus(t.hostname, err) + } + + // ipnstate.Status.CurrentTailnet was added in Tailscale 1.22.0 + // https://github.com/tailscale/tailscale/pull/3865 + // + // Before that, we can check the BackendState to see if the + // tailscaled daemon is connected to the control system. + if status.BackendState == "NeedsMachineAuth" { + return nil + } + + return errTailscaledNotReadyForLogin + }) +} + // WaitForRunning blocks until the Tailscale (tailscaled) instance is logged in // and ready to be used. func (t *TailscaleInContainer) WaitForRunning() error { From 4b0d9322610afc344ec81d05748465f90f104c90 Mon Sep 17 00:00:00 2001 From: hopleus Date: Thu, 17 Oct 2024 11:38:17 +0300 Subject: [PATCH 05/22] Corrected integration tests and github test-integration workflow --- .github/workflows/test-integration.yaml | 3 +++ integration/auth_approval_test.go | 14 +++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-integration.yaml b/.github/workflows/test-integration.yaml index 80daf20a358..398e657ccb8 100644 --- a/.github/workflows/test-integration.yaml +++ b/.github/workflows/test-integration.yaml @@ -23,10 +23,12 @@ jobs: - TestOIDCExpireNodesBasedOnTokenExpiry - TestAuthWebFlowAuthenticationPingAll - TestAuthWebFlowLogoutAndRelogin + - TestAuthNodeApproval - TestUserCommand - TestPreAuthKeyCommand - TestPreAuthKeyCommandWithoutExpiry - TestPreAuthKeyCommandReusableEphemeral + - TestPreAuthKeyCommandPreApproved - TestPreAuthKeyCorrectUserLoggedInCommand - TestApiKeyCommand - TestNodeTagCommand @@ -34,6 +36,7 @@ jobs: - TestNodeAdvertiseTagWithACLCommand - TestNodeCommand - TestNodeExpireCommand + - TestNodeApproveCommand - TestNodeRenameCommand - TestNodeMoveCommand - TestPolicyCommand diff --git a/integration/auth_approval_test.go b/integration/auth_approval_test.go index f6a1fe519b3..e4359eb4c06 100644 --- a/integration/auth_approval_test.go +++ b/integration/auth_approval_test.go @@ -2,6 +2,7 @@ package integration import ( "context" + "crypto/tls" "fmt" v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/juanfont/headscale/integration/hsic" @@ -39,6 +40,8 @@ func TestAuthNodeApproval(t *testing.T) { err = scenario.CreateHeadscaleEnv( spec, hsic.WithTestName("approval"), + hsic.WithTLS(), + hsic.WithHostnameAsServerURL(), hsic.WithManualApproveNewNode(), ) assertNoErrHeadscaleEnv(t, err) @@ -236,7 +239,16 @@ func (s *AuthApprovalScenario) runHeadscaleRegister(userStr string, loginURL *ur loginURL.Host = fmt.Sprintf("%s:8080", headscale.GetIP()) loginURL.Scheme = "http" - httpClient := &http.Client{} + if len(headscale.GetCert()) > 0 { + loginURL.Scheme = "https" + } + + insecureTransport := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // nolint + } + httpClient := &http.Client{ + Transport: insecureTransport, + } ctx := context.Background() req, _ := http.NewRequestWithContext(ctx, http.MethodGet, loginURL.String(), nil) resp, err := httpClient.Do(req) From 8289a225e45671f15986d9b429b3ff605a715ee5 Mon Sep 17 00:00:00 2001 From: hopleus Date: Thu, 17 Oct 2024 12:21:02 +0300 Subject: [PATCH 06/22] Corrected unit tests and integration tests --- hscontrol/db/node_test.go | 18 +++++++++--------- hscontrol/db/preauth_keys_test.go | 22 +++++++++++----------- hscontrol/db/routes_test.go | 8 ++++---- hscontrol/db/users_test.go | 6 +++--- integration/embedded_derp_test.go | 2 +- integration/general_test.go | 6 +++--- integration/scenario.go | 2 +- integration/scenario_test.go | 4 ++-- 8 files changed, 34 insertions(+), 34 deletions(-) diff --git a/hscontrol/db/node_test.go b/hscontrol/db/node_test.go index 0392e3b0e64..112079c2d15 100644 --- a/hscontrol/db/node_test.go +++ b/hscontrol/db/node_test.go @@ -87,7 +87,7 @@ func (s *Suite) TestGetNodeByAnyNodeKey(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, false, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.GetNodeByID(0) @@ -143,7 +143,7 @@ func (s *Suite) TestListPeers(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, false, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.GetNodeByID(0) @@ -237,7 +237,7 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) { for _, name := range []string{"test", "admin"} { user, err := db.CreateUser(name) c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, false, false, nil, nil) c.Assert(err, check.IsNil) stor = append(stor, base{user, pak}) } @@ -330,7 +330,7 @@ func (s *Suite) TestApprovedNode(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, false, true, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, false, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.getNode("test", "testnode") @@ -370,7 +370,7 @@ func (s *Suite) TestExpireNode(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, false, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.getNode("test", "testnode") @@ -411,7 +411,7 @@ func (s *Suite) TestSetTags(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, false, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.getNode("test", "testnode") @@ -656,7 +656,7 @@ func TestAutoApproveRoutes(t *testing.T) { user, err := adb.CreateUser("test") assert.NoError(t, err) - pak, err := adb.CreatePreAuthKey(user.Name, false, true, false, nil, nil) + pak, err := adb.CreatePreAuthKey(user.Name, false, false, false, nil, nil) assert.NoError(t, err) nodeKey := key.NewNode() @@ -788,10 +788,10 @@ func TestListEphemeralNodes(t *testing.T) { user, err := db.CreateUser("test") assert.NoError(t, err) - pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, false, false, nil, nil) assert.NoError(t, err) - pakEph, err := db.CreatePreAuthKey(user.Name, false, true, true, nil, nil) + pakEph, err := db.CreatePreAuthKey(user.Name, false, false, true, nil, nil) assert.NoError(t, err) node := types.Node{ diff --git a/hscontrol/db/preauth_keys_test.go b/hscontrol/db/preauth_keys_test.go index 7894eb339ec..e6551867658 100644 --- a/hscontrol/db/preauth_keys_test.go +++ b/hscontrol/db/preauth_keys_test.go @@ -11,14 +11,14 @@ import ( ) func (*Suite) TestCreatePreAuthKey(c *check.C) { - _, err := db.CreatePreAuthKey("bogus", true, true, false, nil, nil) + _, err := db.CreatePreAuthKey("bogus", true, false, false, nil, nil) c.Assert(err, check.NotNil) user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - key, err := db.CreatePreAuthKey(user.Name, true, true, false, nil, nil) + key, err := db.CreatePreAuthKey(user.Name, true, false, false, nil, nil) c.Assert(err, check.IsNil) // Did we get a valid key? @@ -44,7 +44,7 @@ func (*Suite) TestExpiredPreAuthKey(c *check.C) { c.Assert(err, check.IsNil) now := time.Now().Add(-5 * time.Second) - pak, err := db.CreatePreAuthKey(user.Name, true, true, false, &now, nil) + pak, err := db.CreatePreAuthKey(user.Name, true, false, false, &now, nil) c.Assert(err, check.IsNil) key, err := db.ValidatePreAuthKey(pak.Key) @@ -72,7 +72,7 @@ func (*Suite) TestValidateKeyOk(c *check.C) { user, err := db.CreateUser("test3") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, true, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, true, false, false, nil, nil) c.Assert(err, check.IsNil) key, err := db.ValidatePreAuthKey(pak.Key) @@ -84,7 +84,7 @@ func (*Suite) TestAlreadyUsedKey(c *check.C) { user, err := db.CreateUser("test4") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, false, false, nil, nil) c.Assert(err, check.IsNil) node := types.Node{ @@ -106,7 +106,7 @@ func (*Suite) TestReusableBeingUsedKey(c *check.C) { user, err := db.CreateUser("test5") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, true, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, true, false, false, nil, nil) c.Assert(err, check.IsNil) node := types.Node{ @@ -128,7 +128,7 @@ func (*Suite) TestNotReusableNotBeingUsedKey(c *check.C) { user, err := db.CreateUser("test6") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, false, false, nil, nil) c.Assert(err, check.IsNil) key, err := db.ValidatePreAuthKey(pak.Key) @@ -140,7 +140,7 @@ func (*Suite) TestExpirePreauthKey(c *check.C) { user, err := db.CreateUser("test3") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, true, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, true, false, false, nil, nil) c.Assert(err, check.IsNil) c.Assert(pak.Expiration, check.IsNil) @@ -157,7 +157,7 @@ func (*Suite) TestNotReusableMarkedAsUsed(c *check.C) { user, err := db.CreateUser("test6") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, false, false, nil, nil) c.Assert(err, check.IsNil) pak.Used = true db.DB.Save(&pak) @@ -170,12 +170,12 @@ func (*Suite) TestPreAuthKeyACLTags(c *check.C) { user, err := db.CreateUser("test8") c.Assert(err, check.IsNil) - _, err = db.CreatePreAuthKey(user.Name, false, true, false, nil, []string{"badtag"}) + _, err = db.CreatePreAuthKey(user.Name, false, false, false, nil, []string{"badtag"}) c.Assert(err, check.NotNil) // Confirm that malformed tags are rejected tags := []string{"tag:test1", "tag:test2"} tagsWithDuplicate := []string{"tag:test1", "tag:test2", "tag:test2"} - _, err = db.CreatePreAuthKey(user.Name, false, true, false, nil, tagsWithDuplicate) + _, err = db.CreatePreAuthKey(user.Name, false, false, false, nil, tagsWithDuplicate) c.Assert(err, check.IsNil) listedPaks, err := db.ListPreAuthKeys("test8") diff --git a/hscontrol/db/routes_test.go b/hscontrol/db/routes_test.go index 6474e5f716c..ccb9799c065 100644 --- a/hscontrol/db/routes_test.go +++ b/hscontrol/db/routes_test.go @@ -35,7 +35,7 @@ func (s *Suite) TestGetRoutes(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, false, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.getNode("test", "test_get_route_node") @@ -79,7 +79,7 @@ func (s *Suite) TestGetEnableRoutes(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, false, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.getNode("test", "test_enable_route_node") @@ -153,7 +153,7 @@ func (s *Suite) TestIsUniquePrefix(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, false, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.getNode("test", "test_enable_route_node") @@ -234,7 +234,7 @@ func (s *Suite) TestDeleteRoutes(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, false, false, nil, nil) c.Assert(err, check.IsNil) _, err = db.getNode("test", "test_enable_route_node") diff --git a/hscontrol/db/users_test.go b/hscontrol/db/users_test.go index f276c0d1711..18edf89d560 100644 --- a/hscontrol/db/users_test.go +++ b/hscontrol/db/users_test.go @@ -31,7 +31,7 @@ func (s *Suite) TestDestroyUserErrors(c *check.C) { user, err := db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(user.Name, false, false, false, nil, nil) c.Assert(err, check.IsNil) err = db.DestroyUser("test") @@ -44,7 +44,7 @@ func (s *Suite) TestDestroyUserErrors(c *check.C) { user, err = db.CreateUser("test") c.Assert(err, check.IsNil) - pak, err = db.CreatePreAuthKey(user.Name, false, true, false, nil, nil) + pak, err = db.CreatePreAuthKey(user.Name, false, false, false, nil, nil) c.Assert(err, check.IsNil) node := types.Node{ @@ -97,7 +97,7 @@ func (s *Suite) TestSetMachineUser(c *check.C) { newUser, err := db.CreateUser("new") c.Assert(err, check.IsNil) - pak, err := db.CreatePreAuthKey(oldUser.Name, false, true, false, nil, nil) + pak, err := db.CreatePreAuthKey(oldUser.Name, false, false, false, nil, nil) c.Assert(err, check.IsNil) node := types.Node{ diff --git a/integration/embedded_derp_test.go b/integration/embedded_derp_test.go index a52d44f861f..40dd648f79b 100644 --- a/integration/embedded_derp_test.go +++ b/integration/embedded_derp_test.go @@ -251,7 +251,7 @@ func (s *EmbeddedDERPServerScenario) CreateHeadscaleEnv( } } - key, err := s.CreatePreAuthKey(userName, true, true, false) + key, err := s.CreatePreAuthKey(userName, true, false, false) if err != nil { return err } diff --git a/integration/general_test.go b/integration/general_test.go index 89d1772f6ec..602f3fe193a 100644 --- a/integration/general_test.go +++ b/integration/general_test.go @@ -173,7 +173,7 @@ func TestAuthKeyLogoutAndRelogin(t *testing.T) { } for userName := range spec { - key, err := scenario.CreatePreAuthKey(userName, true, true, false) + key, err := scenario.CreatePreAuthKey(userName, true, false, false) if err != nil { t.Fatalf("failed to create pre-auth key for user %s: %s", userName, err) } @@ -272,7 +272,7 @@ func testEphemeralWithOptions(t *testing.T, opts ...hsic.Option) { t.Fatalf("failed to create tailscale nodes in user %s: %s", userName, err) } - key, err := scenario.CreatePreAuthKey(userName, true, true, true) + key, err := scenario.CreatePreAuthKey(userName, true, false, true) if err != nil { t.Fatalf("failed to create pre-auth key for user %s: %s", userName, err) } @@ -362,7 +362,7 @@ func TestEphemeral2006DeletedTooQuickly(t *testing.T) { t.Fatalf("failed to create tailscale nodes in user %s: %s", userName, err) } - key, err := scenario.CreatePreAuthKey(userName, true, true, true) + key, err := scenario.CreatePreAuthKey(userName, true, false, true) if err != nil { t.Fatalf("failed to create pre-auth key for user %s: %s", userName, err) } diff --git a/integration/scenario.go b/integration/scenario.go index ff05aa05925..65b36e504f0 100644 --- a/integration/scenario.go +++ b/integration/scenario.go @@ -504,7 +504,7 @@ func (s *Scenario) CreateHeadscaleEnv( return err } - key, err := s.CreatePreAuthKey(userName, true, true, false) + key, err := s.CreatePreAuthKey(userName, true, false, false) if err != nil { return err } diff --git a/integration/scenario_test.go b/integration/scenario_test.go index caf11c90239..5193ad6368a 100644 --- a/integration/scenario_test.go +++ b/integration/scenario_test.go @@ -61,7 +61,7 @@ func TestHeadscale(t *testing.T) { }) t.Run("create-auth-key", func(t *testing.T) { - _, err := scenario.CreatePreAuthKey(user, true, true, false) + _, err := scenario.CreatePreAuthKey(user, true, false, false) if err != nil { t.Fatalf("failed to create preauthkey: %s", err) } @@ -153,7 +153,7 @@ func TestTailscaleNodesJoiningHeadcale(t *testing.T) { }) t.Run("join-headscale", func(t *testing.T) { - key, err := scenario.CreatePreAuthKey(user, true, true, false) + key, err := scenario.CreatePreAuthKey(user, true, false, false) if err != nil { t.Fatalf("failed to create preauthkey: %s", err) } From 3a9efd5cc88743b9ae1b86c6f4cc5dd3dc7e1f30 Mon Sep 17 00:00:00 2001 From: hopleus Date: Thu, 17 Oct 2024 13:40:59 +0300 Subject: [PATCH 07/22] Fixed errors in files according to golangci-lint rules --- .github/workflows/test-integration.yaml | 2 +- cmd/headscale/cli/nodes.go | 4 +++- hscontrol/auth.go | 2 +- hscontrol/db/db.go | 21 ++++++++++++--------- hscontrol/db/ip.go | 22 +++++++++++----------- hscontrol/db/node.go | 4 ++-- hscontrol/db/node_test.go | 2 +- hscontrol/types/common.go | 5 +++++ hscontrol/types/node.go | 2 +- integration/auth_approval_test.go | 17 +++++++++-------- integration/hsic/hsic.go | 2 +- 11 files changed, 47 insertions(+), 36 deletions(-) diff --git a/.github/workflows/test-integration.yaml b/.github/workflows/test-integration.yaml index 398e657ccb8..514c96a0fe6 100644 --- a/.github/workflows/test-integration.yaml +++ b/.github/workflows/test-integration.yaml @@ -19,11 +19,11 @@ jobs: - TestACLNamedHostsCanReach - TestACLDevice1CanAccessDevice2 - TestPolicyUpdateWhileRunningWithCLIInDatabase + - TestAuthNodeApproval - TestOIDCAuthenticationPingAll - TestOIDCExpireNodesBasedOnTokenExpiry - TestAuthWebFlowAuthenticationPingAll - TestAuthWebFlowLogoutAndRelogin - - TestAuthNodeApproval - TestUserCommand - TestPreAuthKeyCommand - TestPreAuthKeyCommandWithoutExpiry diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go index 8585c6c0ae3..845cbf8050b 100644 --- a/cmd/headscale/cli/nodes.go +++ b/cmd/headscale/cli/nodes.go @@ -51,7 +51,7 @@ func init() { approveNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)") err = approveNodeCmd.MarkFlagRequired("identifier") if err != nil { - log.Fatalf(err.Error()) + log.Fatalf("%v", err) } nodeCmd.AddCommand(approveNodeCmd) @@ -226,6 +226,7 @@ var approveNodeCmd = &cobra.Command{ fmt.Sprintf("Error converting ID to integer: %s", err), output, ) + return } ctx, client, conn, cancel := newHeadscaleCLIWithConfig() @@ -244,6 +245,7 @@ var approveNodeCmd = &cobra.Command{ ), output, ) + return } SuccessOutput(response.GetNode(), "Node approved", output) diff --git a/hscontrol/auth.go b/hscontrol/auth.go index 9e4e9ee1d27..d25fbd6139e 100644 --- a/hscontrol/auth.go +++ b/hscontrol/auth.go @@ -313,7 +313,7 @@ func (h *Headscale) handleAuthKey( node.AuthKeyID = ptr.To(pak.ID) } - if node.Approved == false { + if !node.Approved { node.Approved = nodeApproved } diff --git a/hscontrol/db/db.go b/hscontrol/db/db.go index 9d156674110..d61a3f5b486 100644 --- a/hscontrol/db/db.go +++ b/hscontrol/db/db.go @@ -29,7 +29,10 @@ func init() { schema.RegisterSerializer("text", TextSerialiser{}) } -var errDatabaseNotSupported = errors.New("database type not supported") +var ( + errDatabaseNotSupported = errors.New("database type not supported") + errNoNodeApprovedColumnInDatabase = errors.New("no node approved column in database") +) // KV is a key-value store in a psql table. For future use... // TODO(kradalby): Is this used for anything? @@ -489,26 +492,26 @@ func NewHeadscaleDatabase( }, { ID: "202410071005", - Migrate: func(tx *gorm.DB) error { - err = tx.AutoMigrate(&types.PreAuthKey{}) + Migrate: func(db *gorm.DB) error { + err = db.AutoMigrate(&types.PreAuthKey{}) if err != nil { return err } - err = tx.AutoMigrate(&types.Node{}) + err = db.AutoMigrate(&types.Node{}) if err != nil { return err } - if tx.Migrator().HasColumn(&types.Node{}, "approved") { + if db.Migrator().HasColumn(&types.Node{}, "approved") { nodes := types.Nodes{} - if err := tx.Find(&nodes).Error; err != nil { + if err := db.Find(&nodes).Error; err != nil { log.Error().Err(err).Msg("Error accessing db") } for item, node := range nodes { - if node.IsApproved() == false { - err = tx.Model(nodes[item]).Updates(types.Node{ + if !node.IsApproved() { + err = db.Model(nodes[item]).Updates(types.Node{ Approved: true, }).Error if err != nil { @@ -525,7 +528,7 @@ func NewHeadscaleDatabase( return nil } - return fmt.Errorf("no node approved column in DB") + return errNoNodeApprovedColumnInDatabase }, Rollback: func(db *gorm.DB) error { return nil }, }, diff --git a/hscontrol/db/ip.go b/hscontrol/db/ip.go index b1289ab89af..afe33bbf3b0 100644 --- a/hscontrol/db/ip.go +++ b/hscontrol/db/ip.go @@ -265,17 +265,17 @@ func isTailscaleReservedIP(ip netip.Addr) bool { // it will be added. // If a prefix type has been removed (IPv4 or IPv6), it // will remove the IPs in that family from the node. -func (hsdb *HSDatabase) BackfillNodeIPs(i *IPAllocator) ([]string, error) { +func (hsdb *HSDatabase) BackfillNodeIPs(ip *IPAllocator) ([]string, error) { var err error var ret []string - err = hsdb.Write(func(tx *gorm.DB) error { - if i == nil { + err = hsdb.Write(func(db *gorm.DB) error { + if ip == nil { return errors.New("backfilling IPs: ip allocator was nil") } log.Trace().Msgf("starting to backfill IPs") - nodes, err := ListNodes(tx) + nodes, err := ListNodes(db) if err != nil { return fmt.Errorf("listing nodes to backfill IPs: %w", err) } @@ -285,8 +285,8 @@ func (hsdb *HSDatabase) BackfillNodeIPs(i *IPAllocator) ([]string, error) { changed := false // IPv4 prefix is set, but node ip is missing, alloc - if i.prefix4 != nil && node.IPv4 == nil { - ret4, err := i.nextLocked(i.prev4, i.prefix4) + if ip.prefix4 != nil && node.IPv4 == nil { + ret4, err := ip.nextLocked(ip.prev4, ip.prefix4) if err != nil { return fmt.Errorf("failed to allocate ipv4 for node(%d): %w", node.ID, err) } @@ -297,8 +297,8 @@ func (hsdb *HSDatabase) BackfillNodeIPs(i *IPAllocator) ([]string, error) { } // IPv6 prefix is set, but node ip is missing, alloc - if i.prefix6 != nil && node.IPv6 == nil { - ret6, err := i.nextLocked(i.prev6, i.prefix6) + if ip.prefix6 != nil && node.IPv6 == nil { + ret6, err := ip.nextLocked(ip.prev6, ip.prefix6) if err != nil { return fmt.Errorf("failed to allocate ipv6 for node(%d): %w", node.ID, err) } @@ -309,21 +309,21 @@ func (hsdb *HSDatabase) BackfillNodeIPs(i *IPAllocator) ([]string, error) { } // IPv4 prefix is not set, but node has IP, remove - if i.prefix4 == nil && node.IPv4 != nil { + if ip.prefix4 == nil && node.IPv4 != nil { ret = append(ret, fmt.Sprintf("removing IPv4 %q from Node(%d) %q", node.IPv4.String(), node.ID, node.Hostname)) node.IPv4 = nil changed = true } // IPv6 prefix is not set, but node has IP, remove - if i.prefix6 == nil && node.IPv6 != nil { + if ip.prefix6 == nil && node.IPv6 != nil { ret = append(ret, fmt.Sprintf("removing IPv6 %q from Node(%d) %q", node.IPv6.String(), node.ID, node.Hostname)) node.IPv6 = nil changed = true } if changed { - err := tx.Save(node).Error + err := db.Save(node).Error if err != nil { return fmt.Errorf("saving node(%d) after adding IPs: %w", node.ID, err) } diff --git a/hscontrol/db/node.go b/hscontrol/db/node.go index 96b6c406817..f1bf1e73137 100644 --- a/hscontrol/db/node.go +++ b/hscontrol/db/node.go @@ -268,7 +268,7 @@ func (hsdb *HSDatabase) NodeSetApprove(nodeID types.NodeID, approved bool) error }) } -// NodeSetApprove takes a Node struct and a set approval option +// NodeSetApprove takes a Node struct and a set approval option. func NodeSetApprove(tx *gorm.DB, nodeID types.NodeID, approved bool, ) error { @@ -371,7 +371,7 @@ func (hsdb *HSDatabase) RegisterNodeFromAuthCallback( node.User = *user node.RegisterMethod = registrationMethod - if node.IsApproved() == false && manualApprovedNode == false { + if !node.IsApproved() && manualApprovedNode == false { node.Approved = true } diff --git a/hscontrol/db/node_test.go b/hscontrol/db/node_test.go index 112079c2d15..4446726a282 100644 --- a/hscontrol/db/node_test.go +++ b/hscontrol/db/node_test.go @@ -202,7 +202,7 @@ func (s *Suite) TestListPeersWithoutNonAuthorized(c *check.C) { } node := types.Node{ - ID: types.NodeID(index), + ID: types.NodeID(int64(index)), MachineKey: machineKey.Public(), NodeKey: nodeKey.Public(), Hostname: "testnode" + strconv.Itoa(index), diff --git a/hscontrol/types/common.go b/hscontrol/types/common.go index 32ad8a67dbb..05a0b7090bd 100644 --- a/hscontrol/types/common.go +++ b/hscontrol/types/common.go @@ -15,6 +15,11 @@ const ( DatabaseSqlite = "sqlite3" ) +const ( + SchemaHttp = "http" + SchemaHttps = "https" +) + var ErrCannotParsePrefix = errors.New("cannot parse prefix") type StateUpdateType int diff --git a/hscontrol/types/node.go b/hscontrol/types/node.go index 94c6b589283..860e1596233 100644 --- a/hscontrol/types/node.go +++ b/hscontrol/types/node.go @@ -112,7 +112,7 @@ func (node Node) IsExpired() bool { // IsApproved returns whether the node is approved. func (node Node) IsApproved() bool { - return node.Approved == true + return node.Approved } // IsEphemeral returns if the node is registered as an Ephemeral node. diff --git a/integration/auth_approval_test.go b/integration/auth_approval_test.go index e4359eb4c06..c9fd98a9e58 100644 --- a/integration/auth_approval_test.go +++ b/integration/auth_approval_test.go @@ -4,7 +4,8 @@ import ( "context" "crypto/tls" "fmt" - v1 "github.com/juanfont/headscale/gen/go/headscale/v1" + "github.com/juanfont/headscale/gen/go/headscale/v1" + "github.com/juanfont/headscale/hscontrol/types" "github.com/juanfont/headscale/integration/hsic" "github.com/samber/lo" "github.com/stretchr/testify/assert" @@ -56,7 +57,7 @@ func TestAuthNodeApproval(t *testing.T) { status, err := client.Status() assertNoErr(t, err) assert.Equal(t, "NeedsMachineAuth", status.BackendState) - assert.Len(t, status.Peers(), 0) + assert.Empty(t, status.Peers()) } headscale, err := scenario.Headscale() @@ -74,11 +75,11 @@ func TestAuthNodeApproval(t *testing.T) { }, &allNodes, ) - assert.Nil(t, err) + assert.NoError(t, err) for _, node := range allNodes { _, err = headscale.Execute([]string{ - "headscale", "nodes", "approve", "--identifier", fmt.Sprintf("%d", node.Id), + "headscale", "nodes", "approve", "--identifier", fmt.Sprintf("%d", node.GetId()), }) assertNoErr(t, err) } @@ -113,7 +114,7 @@ func TestAuthNodeApproval(t *testing.T) { err = scenario.WaitForTailscaleSync() assertNoErrSync(t, err) - //assertClientsState(t, allClients) + // assertClientsState(t, allClients) allAddrs := lo.Map(allIps, func(x netip.Addr, index int) string { return x.String() @@ -236,11 +237,11 @@ func (s *AuthApprovalScenario) runHeadscaleRegister(userStr string, loginURL *ur } log.Printf("loginURL: %s", loginURL) - loginURL.Host = fmt.Sprintf("%s:8080", headscale.GetIP()) - loginURL.Scheme = "http" + loginURL.Host = fmt.Sprintf("%s:%d", headscale.GetIP(), 8080) + loginURL.Scheme = types.SchemaHttp if len(headscale.GetCert()) > 0 { - loginURL.Scheme = "https" + loginURL.Scheme = types.SchemaHttps } insecureTransport := &http.Transport{ diff --git a/integration/hsic/hsic.go b/integration/hsic/hsic.go index 79fa2921726..6e57c61b0ba 100644 --- a/integration/hsic/hsic.go +++ b/integration/hsic/hsic.go @@ -211,7 +211,7 @@ func WithTuning(batchTimeout time.Duration, mapSessionChanSize int) Option { } } -// WithManualApproveNewNode allows devices to access the network only after manual approval +// WithManualApproveNewNode allows devices to access the network only after manual approval. func WithManualApproveNewNode() Option { return func(hsic *HeadscaleInContainer) { hsic.env["HEADSCALE_NODE_MANAGEMENT_MANUAL_APPROVE_NEW_NODE"] = "true" From d5097fd5b49a24598e10c17a237b4865a6d3b7af Mon Sep 17 00:00:00 2001 From: hopleus Date: Thu, 17 Oct 2024 13:57:46 +0300 Subject: [PATCH 08/22] Fixed errors in files according to golangci-lint rules --- hscontrol/db/node.go | 2 +- hscontrol/db/node_test.go | 2 +- hscontrol/types/common.go | 4 +- integration/auth_approval_test.go | 20 +++-- integration/cli_test.go | 132 ++++++++++++++++++++++++++++++ 5 files changed, 147 insertions(+), 13 deletions(-) diff --git a/hscontrol/db/node.go b/hscontrol/db/node.go index f1bf1e73137..52030bfbd00 100644 --- a/hscontrol/db/node.go +++ b/hscontrol/db/node.go @@ -371,7 +371,7 @@ func (hsdb *HSDatabase) RegisterNodeFromAuthCallback( node.User = *user node.RegisterMethod = registrationMethod - if !node.IsApproved() && manualApprovedNode == false { + if !node.IsApproved() && !manualApprovedNode { node.Approved = true } diff --git a/hscontrol/db/node_test.go b/hscontrol/db/node_test.go index 4446726a282..0aaadf3f3ff 100644 --- a/hscontrol/db/node_test.go +++ b/hscontrol/db/node_test.go @@ -202,7 +202,7 @@ func (s *Suite) TestListPeersWithoutNonAuthorized(c *check.C) { } node := types.Node{ - ID: types.NodeID(int64(index)), + ID: types.NodeID(uint64(index)), MachineKey: machineKey.Public(), NodeKey: nodeKey.Public(), Hostname: "testnode" + strconv.Itoa(index), diff --git a/hscontrol/types/common.go b/hscontrol/types/common.go index 05a0b7090bd..b6e162e4a28 100644 --- a/hscontrol/types/common.go +++ b/hscontrol/types/common.go @@ -16,8 +16,8 @@ const ( ) const ( - SchemaHttp = "http" - SchemaHttps = "https" + SchemaHTTP = "http" + SchemaHTTPS = "https" ) var ErrCannotParsePrefix = errors.New("cannot parse prefix") diff --git a/integration/auth_approval_test.go b/integration/auth_approval_test.go index c9fd98a9e58..d4b86505398 100644 --- a/integration/auth_approval_test.go +++ b/integration/auth_approval_test.go @@ -4,18 +4,20 @@ import ( "context" "crypto/tls" "fmt" - "github.com/juanfont/headscale/gen/go/headscale/v1" - "github.com/juanfont/headscale/hscontrol/types" - "github.com/juanfont/headscale/integration/hsic" - "github.com/samber/lo" - "github.com/stretchr/testify/assert" "io" "log" "net/http" "net/netip" "net/url" + "strconv" "strings" "testing" + + "github.com/juanfont/headscale/gen/go/headscale/v1" + "github.com/juanfont/headscale/hscontrol/types" + "github.com/juanfont/headscale/integration/hsic" + "github.com/samber/lo" + "github.com/stretchr/testify/assert" ) type AuthApprovalScenario struct { @@ -75,11 +77,11 @@ func TestAuthNodeApproval(t *testing.T) { }, &allNodes, ) - assert.NoError(t, err) + assertNoErr(t, err) for _, node := range allNodes { _, err = headscale.Execute([]string{ - "headscale", "nodes", "approve", "--identifier", fmt.Sprintf("%d", node.GetId()), + "headscale", "nodes", "approve", "--identifier", strconv.FormatUint(node.GetId(), 10), }) assertNoErr(t, err) } @@ -238,10 +240,10 @@ func (s *AuthApprovalScenario) runHeadscaleRegister(userStr string, loginURL *ur log.Printf("loginURL: %s", loginURL) loginURL.Host = fmt.Sprintf("%s:%d", headscale.GetIP(), 8080) - loginURL.Scheme = types.SchemaHttp + loginURL.Scheme = types.SchemaHTTP if len(headscale.GetCert()) > 0 { - loginURL.Scheme = types.SchemaHttps + loginURL.Scheme = types.SchemaHTTPS } insecureTransport := &http.Transport{ diff --git a/integration/cli_test.go b/integration/cli_test.go index 5926d4265fb..8bccc6a15e5 100644 --- a/integration/cli_test.go +++ b/integration/cli_test.go @@ -1196,6 +1196,138 @@ func TestNodeCommand(t *testing.T) { assert.Len(t, listOnlyMachineUserAfterDelete, 4) } +func TestNodeApproveCommand(t *testing.T) { + IntegrationSkip(t) + t.Parallel() + + scenario, err := NewScenario(dockertestMaxWait()) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + spec := map[string]int{ + "node-approve-user": 0, + } + + err = scenario.CreateHeadscaleEnv( + spec, + []tsic.Option{}, + hsic.WithTestName("clins"), + hsic.WithManualApproveNewNode(), + ) + assertNoErr(t, err) + + headscale, err := scenario.Headscale() + assertNoErr(t, err) + + // Pregenerated machine keys + machineKeys := []string{ + "mkey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", + "mkey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", + "mkey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", + "mkey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1", + "mkey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", + } + nodes := make([]*v1.Node, len(machineKeys)) + + for index, machineKey := range machineKeys { + _, err := headscale.Execute( + []string{ + "headscale", + "debug", + "create-node", + "--name", + fmt.Sprintf("node-%d", index+1), + "--user", + "node-approve-user", + "--key", + machineKey, + "--output", + "json", + }, + ) + assert.NoError(t, err) + + var node v1.Node + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "--user", + "node-approve-user", + "register", + "--key", + machineKey, + "--output", + "json", + }, + &node, + ) + assert.NoError(t, err) + + nodes[index] = &node + } + + assert.Len(t, nodes, len(machineKeys)) + + var listAll []v1.Node + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "list", + "--output", + "json", + }, + &listAll, + ) + assert.NoError(t, err) + + assert.Len(t, listAll, 5) + + assert.False(t, listAll[0].GetApproved()) + assert.False(t, listAll[1].GetApproved()) + assert.False(t, listAll[2].GetApproved()) + assert.False(t, listAll[3].GetApproved()) + assert.False(t, listAll[4].GetApproved()) + + for idx := 0; idx < 3; idx++ { + _, err := headscale.Execute( + []string{ + "headscale", + "nodes", + "approve", + "--identifier", + fmt.Sprintf("%d", listAll[idx].GetId()), + }, + ) + assert.NoError(t, err) + } + + var listAllAfterApprove []v1.Node + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "list", + "--output", + "json", + }, + &listAllAfterApprove, + ) + assert.NoError(t, err) + + assert.Len(t, listAllAfterApprove, 5) + + assert.True(t, listAllAfterApprove[0].GetApproved()) + assert.True(t, listAllAfterApprove[1].GetApproved()) + assert.True(t, listAllAfterApprove[2].GetApproved()) + assert.False(t, listAllAfterApprove[3].GetApproved()) + assert.False(t, listAllAfterApprove[4].GetApproved()) +} + func TestNodeExpireCommand(t *testing.T) { IntegrationSkip(t) t.Parallel() From 0a8ec1f0dc1376fded2cadc713ced9ba4767a496 Mon Sep 17 00:00:00 2001 From: hopleus Date: Thu, 17 Oct 2024 14:01:15 +0300 Subject: [PATCH 09/22] Fixed the order of calling integration tests --- .github/workflows/test-integration.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-integration.yaml b/.github/workflows/test-integration.yaml index 514c96a0fe6..bd6b897de26 100644 --- a/.github/workflows/test-integration.yaml +++ b/.github/workflows/test-integration.yaml @@ -35,8 +35,8 @@ jobs: - TestNodeAdvertiseTagNoACLCommand - TestNodeAdvertiseTagWithACLCommand - TestNodeCommand - - TestNodeExpireCommand - TestNodeApproveCommand + - TestNodeExpireCommand - TestNodeRenameCommand - TestNodeMoveCommand - TestPolicyCommand From 7b7562c9584076f15649f4ac1aa11bd4790e337e Mon Sep 17 00:00:00 2001 From: hopleus Date: Thu, 17 Oct 2024 14:20:13 +0300 Subject: [PATCH 10/22] Corrected unit tests --- hscontrol/db/node_test.go | 1 + integration/auth_approval_test.go | 2 +- integration/cli_test.go | 11 ++++++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/hscontrol/db/node_test.go b/hscontrol/db/node_test.go index 0aaadf3f3ff..c3b95f97202 100644 --- a/hscontrol/db/node_test.go +++ b/hscontrol/db/node_test.go @@ -202,6 +202,7 @@ func (s *Suite) TestListPeersWithoutNonAuthorized(c *check.C) { } node := types.Node{ + // nolint:G115 ID: types.NodeID(uint64(index)), MachineKey: machineKey.Public(), NodeKey: nodeKey.Public(), diff --git a/integration/auth_approval_test.go b/integration/auth_approval_test.go index d4b86505398..982d4eb3c50 100644 --- a/integration/auth_approval_test.go +++ b/integration/auth_approval_test.go @@ -13,7 +13,7 @@ import ( "strings" "testing" - "github.com/juanfont/headscale/gen/go/headscale/v1" + v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/juanfont/headscale/hscontrol/types" "github.com/juanfont/headscale/integration/hsic" "github.com/samber/lo" diff --git a/integration/cli_test.go b/integration/cli_test.go index 8bccc6a15e5..94b5f348b7c 100644 --- a/integration/cli_test.go +++ b/integration/cli_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "sort" + "strconv" "strings" "testing" "time" @@ -1245,7 +1246,7 @@ func TestNodeApproveCommand(t *testing.T) { "json", }, ) - assert.NoError(t, err) + assertNoErr(t, err) var node v1.Node err = executeAndUnmarshal( @@ -1263,7 +1264,7 @@ func TestNodeApproveCommand(t *testing.T) { }, &node, ) - assert.NoError(t, err) + assertNoErr(t, err) nodes[index] = &node } @@ -1282,7 +1283,7 @@ func TestNodeApproveCommand(t *testing.T) { }, &listAll, ) - assert.NoError(t, err) + assertNoErr(t, err) assert.Len(t, listAll, 5) @@ -1292,14 +1293,14 @@ func TestNodeApproveCommand(t *testing.T) { assert.False(t, listAll[3].GetApproved()) assert.False(t, listAll[4].GetApproved()) - for idx := 0; idx < 3; idx++ { + for idx := range [3]int{} { _, err := headscale.Execute( []string{ "headscale", "nodes", "approve", "--identifier", - fmt.Sprintf("%d", listAll[idx].GetId()), + strconv.FormatUint(listAll[idx].GetId(), 10), }, ) assert.NoError(t, err) From 9347071c101d5330e2ae8f8790e52b1ff646be0a Mon Sep 17 00:00:00 2001 From: hopleus Date: Thu, 17 Oct 2024 14:24:54 +0300 Subject: [PATCH 11/22] Corrected unit tests and integration tests --- hscontrol/db/node_test.go | 3 +-- integration/cli_test.go | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/hscontrol/db/node_test.go b/hscontrol/db/node_test.go index c3b95f97202..10c87e951f2 100644 --- a/hscontrol/db/node_test.go +++ b/hscontrol/db/node_test.go @@ -202,8 +202,7 @@ func (s *Suite) TestListPeersWithoutNonAuthorized(c *check.C) { } node := types.Node{ - // nolint:G115 - ID: types.NodeID(uint64(index)), + ID: types.NodeID(uint64(index)), // nolint:G115 MachineKey: machineKey.Public(), NodeKey: nodeKey.Public(), Hostname: "testnode" + strconv.Itoa(index), diff --git a/integration/cli_test.go b/integration/cli_test.go index 94b5f348b7c..dd2c8c27720 100644 --- a/integration/cli_test.go +++ b/integration/cli_test.go @@ -1303,7 +1303,7 @@ func TestNodeApproveCommand(t *testing.T) { strconv.FormatUint(listAll[idx].GetId(), 10), }, ) - assert.NoError(t, err) + assertNoErr(t, err) } var listAllAfterApprove []v1.Node @@ -1318,7 +1318,7 @@ func TestNodeApproveCommand(t *testing.T) { }, &listAllAfterApprove, ) - assert.NoError(t, err) + assertNoErr(t, err) assert.Len(t, listAllAfterApprove, 5) From eb00e6164712455b3c4d2decba166bc505f1bf8f Mon Sep 17 00:00:00 2001 From: hopleus Date: Thu, 17 Oct 2024 14:31:40 +0300 Subject: [PATCH 12/22] Fixed nolint directive --- hscontrol/db/node_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hscontrol/db/node_test.go b/hscontrol/db/node_test.go index 10c87e951f2..30594a64c9d 100644 --- a/hscontrol/db/node_test.go +++ b/hscontrol/db/node_test.go @@ -202,7 +202,7 @@ func (s *Suite) TestListPeersWithoutNonAuthorized(c *check.C) { } node := types.Node{ - ID: types.NodeID(uint64(index)), // nolint:G115 + ID: types.NodeID(uint64(index)), // nolint:g115 MachineKey: machineKey.Public(), NodeKey: nodeKey.Public(), Hostname: "testnode" + strconv.Itoa(index), From 5b8b5944aa9c197dd4b6318af30b439d8e75e160 Mon Sep 17 00:00:00 2001 From: hopleus Date: Thu, 17 Oct 2024 14:56:12 +0300 Subject: [PATCH 13/22] Fixed errors in files according to golangci-lint rules --- hscontrol/db/node_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hscontrol/db/node_test.go b/hscontrol/db/node_test.go index 30594a64c9d..005ff56e4cd 100644 --- a/hscontrol/db/node_test.go +++ b/hscontrol/db/node_test.go @@ -202,7 +202,7 @@ func (s *Suite) TestListPeersWithoutNonAuthorized(c *check.C) { } node := types.Node{ - ID: types.NodeID(uint64(index)), // nolint:g115 + ID: types.NodeID(uint64(index)), //nolint // disable G115 MachineKey: machineKey.Public(), NodeKey: nodeKey.Public(), Hostname: "testnode" + strconv.Itoa(index), From cb4d984afc26a8d17c74ea2a1f61905304850465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Goran=20Dragani=C4=87?= Date: Thu, 17 Oct 2024 13:34:20 +0200 Subject: [PATCH 14/22] feat: derpmap field in config (#1823) --- hscontrol/derp/derp.go | 3 +++ hscontrol/types/config.go | 1 + 2 files changed, 4 insertions(+) diff --git a/hscontrol/derp/derp.go b/hscontrol/derp/derp.go index 5d4b24f2e89..9d358598287 100644 --- a/hscontrol/derp/derp.go +++ b/hscontrol/derp/derp.go @@ -82,6 +82,9 @@ func mergeDERPMaps(derpMaps []*tailcfg.DERPMap) *tailcfg.DERPMap { func GetDERPMap(cfg types.DERPConfig) *tailcfg.DERPMap { var derpMaps []*tailcfg.DERPMap + if cfg.DERPMap != nil { + derpMaps = append(derpMaps, cfg.DERPMap) + } for _, path := range cfg.Paths { log.Debug(). diff --git a/hscontrol/types/config.go b/hscontrol/types/config.go index 3f70ca7bb81..65d7244422a 100644 --- a/hscontrol/types/config.go +++ b/hscontrol/types/config.go @@ -178,6 +178,7 @@ type DERPConfig struct { STUNAddr string URLs []url.URL Paths []string + DERPMap *tailcfg.DERPMap AutoUpdate bool UpdateFrequency time.Duration IPv4 string From 6b14f6e362ed3ac7e74fe531cd24752c08020205 Mon Sep 17 00:00:00 2001 From: hopleus <124590925+hopleus@users.noreply.github.com> Date: Thu, 17 Oct 2024 18:45:33 +0300 Subject: [PATCH 15/22] #2140 Fixed reflection of hostname change (#2199) * #2140 Fixed updating of hostname and givenName when it is updated in HostInfo * #2140 Added integration tests * #2140 Fix unit tests * Changed IsAutomaticNameMode to GivenNameHasBeenChanged. Fixed errors in files according to golangci-lint rules --- .github/workflows/test-integration.yaml | 1 + CHANGELOG.md | 1 + hscontrol/poll.go | 11 +- hscontrol/types/node.go | 20 ++++ hscontrol/types/node_test.go | 60 +++++++++++ integration/general_test.go | 130 ++++++++++++++++++++++++ 6 files changed, 221 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-integration.yaml b/.github/workflows/test-integration.yaml index bd6b897de26..f7df0fea877 100644 --- a/.github/workflows/test-integration.yaml +++ b/.github/workflows/test-integration.yaml @@ -53,6 +53,7 @@ jobs: - TestEphemeral2006DeletedTooQuickly - TestPingAllByHostname - TestTaildrop + - TestUpdateHostnameFromClient - TestExpireNode - TestNodeOnlineStatus - TestPingAllByIPManyUpDown diff --git a/CHANGELOG.md b/CHANGELOG.md index 22f05780b90..465adc87df9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Allow nodes to use SSH agent forwarding [#2145](https://github.com/juanfont/headscale/pull/2145) - Fixed processing of fields in post request in MoveNode rpc [#2179](https://github.com/juanfont/headscale/pull/2179) - Added conversion of 'Hostname' to 'givenName' in a node with FQDN rules applied [#2198](https://github.com/juanfont/headscale/pull/2198) +- Fixed updating of hostname and givenName when it is updated in HostInfo [#2199](https://github.com/juanfont/headscale/pull/2199) ## 0.23.0 (2024-09-18) diff --git a/hscontrol/poll.go b/hscontrol/poll.go index 755265f3a42..a8ae01f44fd 100644 --- a/hscontrol/poll.go +++ b/hscontrol/poll.go @@ -471,7 +471,7 @@ func (m *mapSession) handleEndpointUpdate() { // Check if the Hostinfo of the node has changed. // If it has changed, check if there has been a change to - // the routable IPs of the host and update update them in + // the routable IPs of the host and update them in // the database. Then send a Changed update // (containing the whole node object) to peers to inform about // the route change. @@ -510,6 +510,12 @@ func (m *mapSession) handleEndpointUpdate() { m.node.ID) } + // Check if there has been a change to Hostname and update them + // in the database. Then send a Changed update + // (containing the whole node object) to peers to inform about + // the hostname change. + m.node.ApplyHostnameFromHostInfo(m.req.Hostinfo) + if err := m.h.db.DB.Save(m.node).Error; err != nil { m.errf(err, "Failed to persist/update node in the database") http.Error(m.w, "", http.StatusInternalServerError) @@ -526,7 +532,8 @@ func (m *mapSession) handleEndpointUpdate() { ChangeNodes: []types.NodeID{m.node.ID}, Message: "called from handlePoll -> update", }, - m.node.ID) + m.node.ID, + ) m.w.WriteHeader(http.StatusOK) mapResponseEndpointUpdates.WithLabelValues("ok").Inc() diff --git a/hscontrol/types/node.go b/hscontrol/types/node.go index 860e1596233..e95958747f4 100644 --- a/hscontrol/types/node.go +++ b/hscontrol/types/node.go @@ -98,6 +98,11 @@ type ( Nodes []*Node ) +// GivenNameHasBeenChanged returns whether the `givenName` can be automatically changed based on the `Hostname` of the node. +func (node *Node) GivenNameHasBeenChanged() bool { + return node.GivenName == util.ConvertWithFQDNRules(node.Hostname) +} + // IsExpired returns whether the node registration has expired. func (node Node) IsExpired() bool { // If Expiry is not set, the client has not indicated that @@ -354,6 +359,21 @@ func (node *Node) RegisterMethodToV1Enum() v1.RegisterMethod { } } +// ApplyHostnameFromHostInfo takes a Hostinfo struct and updates the node. +func (node *Node) ApplyHostnameFromHostInfo(hostInfo *tailcfg.Hostinfo) { + if hostInfo == nil { + return + } + + if node.Hostname != hostInfo.Hostname { + if node.GivenNameHasBeenChanged() { + node.GivenName = util.ConvertWithFQDNRules(hostInfo.Hostname) + } + + node.Hostname = hostInfo.Hostname + } +} + // ApplyPeerChange takes a PeerChange struct and updates the node. func (node *Node) ApplyPeerChange(change *tailcfg.PeerChange) { if change.Key != nil { diff --git a/hscontrol/types/node_test.go b/hscontrol/types/node_test.go index 1d0e7939eee..d439d483214 100644 --- a/hscontrol/types/node_test.go +++ b/hscontrol/types/node_test.go @@ -337,6 +337,66 @@ func TestPeerChangeFromMapRequest(t *testing.T) { } } +func TestApplyHostnameFromHostInfo(t *testing.T) { + tests := []struct { + name string + nodeBefore Node + change *tailcfg.Hostinfo + want Node + }{ + { + name: "hostinfo-not-exists", + nodeBefore: Node{ + GivenName: "manual-test.local", + Hostname: "TestHost.Local", + }, + change: nil, + want: Node{ + GivenName: "manual-test.local", + Hostname: "TestHost.Local", + }, + }, + { + name: "hostinfo-exists-no-automatic-givenName", + nodeBefore: Node{ + GivenName: "manual-test.local", + Hostname: "TestHost.Local", + }, + change: &tailcfg.Hostinfo{ + Hostname: "NewHostName.Local", + }, + want: Node{ + GivenName: "manual-test.local", + Hostname: "NewHostName.Local", + }, + }, + { + name: "hostinfo-exists-automatic-givenName", + nodeBefore: Node{ + GivenName: "automaticname.test", + Hostname: "AutomaticName.Test", + }, + change: &tailcfg.Hostinfo{ + Hostname: "NewHostName.Local", + }, + want: Node{ + GivenName: "newhostname.local", + Hostname: "NewHostName.Local", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.nodeBefore.ApplyHostnameFromHostInfo(tt.change) + + if diff := cmp.Diff(tt.want, tt.nodeBefore, util.Comparers...); diff != "" { + t.Errorf("Patch unexpected result (-want +got):\n%s", diff) + } + }) + } +} + func TestApplyPeerChange(t *testing.T) { tests := []struct { name string diff --git a/integration/general_test.go b/integration/general_test.go index 602f3fe193a..3196302464f 100644 --- a/integration/general_test.go +++ b/integration/general_test.go @@ -5,12 +5,14 @@ import ( "encoding/json" "fmt" "net/netip" + "strconv" "strings" "testing" "time" v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/juanfont/headscale/hscontrol/types" + "github.com/juanfont/headscale/hscontrol/util" "github.com/juanfont/headscale/integration/hsic" "github.com/juanfont/headscale/integration/tsic" "github.com/rs/zerolog/log" @@ -654,6 +656,134 @@ func TestTaildrop(t *testing.T) { } } +func TestUpdateHostnameFromClient(t *testing.T) { + IntegrationSkip(t) + t.Parallel() + + user := "update-hostname-from-client" + + hostnames := map[string]string{ + "1": "user1-host", + "2": "User2-Host", + "3": "user3-host", + } + + scenario, err := NewScenario(dockertestMaxWait()) + assertNoErrf(t, "failed to create scenario: %s", err) + defer scenario.ShutdownAssertNoPanics(t) + + spec := map[string]int{ + user: 3, + } + + err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("updatehostname")) + assertNoErrHeadscaleEnv(t, err) + + allClients, err := scenario.ListTailscaleClients() + assertNoErrListClients(t, err) + + err = scenario.WaitForTailscaleSync() + assertNoErrSync(t, err) + + headscale, err := scenario.Headscale() + assertNoErrGetHeadscale(t, err) + + // update hostnames using the up command + for _, client := range allClients { + status, err := client.Status() + assertNoErr(t, err) + + command := []string{ + "tailscale", + "set", + "--hostname=" + hostnames[string(status.Self.ID)], + } + _, _, err = client.Execute(command) + assertNoErrf(t, "failed to set hostname: %s", err) + } + + err = scenario.WaitForTailscaleSync() + assertNoErrSync(t, err) + + var nodes []*v1.Node + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "node", + "list", + "--output", + "json", + }, + &nodes, + ) + + assertNoErr(t, err) + assert.Len(t, nodes, 3) + + for _, node := range nodes { + hostname := hostnames[strconv.FormatUint(node.GetId(), 10)] + assert.Equal(t, hostname, node.GetName()) + assert.Equal(t, util.ConvertWithFQDNRules(hostname), node.GetGivenName()) + } + + // Rename givenName in nodes + for _, node := range nodes { + givenName := fmt.Sprintf("%d-givenname", node.GetId()) + _, err = headscale.Execute( + []string{ + "headscale", + "node", + "rename", + givenName, + "--identifier", + strconv.FormatUint(node.GetId(), 10), + }) + assertNoErr(t, err) + } + + time.Sleep(5 * time.Second) + + // Verify that the clients can see the new hostname, but no givenName + for _, client := range allClients { + status, err := client.Status() + assertNoErr(t, err) + + command := []string{ + "tailscale", + "set", + "--hostname=" + hostnames[string(status.Self.ID)] + "NEW", + } + _, _, err = client.Execute(command) + assertNoErrf(t, "failed to set hostname: %s", err) + } + + err = scenario.WaitForTailscaleSync() + assertNoErrSync(t, err) + + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "node", + "list", + "--output", + "json", + }, + &nodes, + ) + + assertNoErr(t, err) + assert.Len(t, nodes, 3) + + for _, node := range nodes { + hostname := hostnames[strconv.FormatUint(node.GetId(), 10)] + givenName := fmt.Sprintf("%d-givenname", node.GetId()) + assert.Equal(t, hostname+"NEW", node.GetName()) + assert.Equal(t, givenName, node.GetGivenName()) + } +} + func TestExpireNode(t *testing.T) { IntegrationSkip(t) t.Parallel() From 348f584bae46ff2d9b3122e53dbf0873f6ed895c Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 18 Oct 2024 08:20:03 -0600 Subject: [PATCH 16/22] add new user fields to grpc and list command (#2202) Updates #2166 Signed-off-by: Kristoffer Dalby --- cmd/headscale/cli/users.go | 4 +- gen/go/headscale/v1/user.pb.go | 138 ++++++++++++------ .../headscale/v1/headscale.swagger.json | 15 ++ hscontrol/types/users.go | 13 +- proto/headscale/v1/user.proto | 11 +- 5 files changed, 129 insertions(+), 52 deletions(-) diff --git a/cmd/headscale/cli/users.go b/cmd/headscale/cli/users.go index d04d75689d7..ec803c6139b 100644 --- a/cmd/headscale/cli/users.go +++ b/cmd/headscale/cli/users.go @@ -164,13 +164,15 @@ var listUsersCmd = &cobra.Command{ SuccessOutput(response.GetUsers(), "", output) } - tableData := pterm.TableData{{"ID", "Name", "Created"}} + tableData := pterm.TableData{{"ID", "Name", "Username", "Email", "Created"}} for _, user := range response.GetUsers() { tableData = append( tableData, []string{ user.GetId(), + user.GetDisplayName(), user.GetName(), + user.GetEmail(), user.GetCreatedAt().AsTime().Format("2006-01-02 15:04:05"), }, ) diff --git a/gen/go/headscale/v1/user.pb.go b/gen/go/headscale/v1/user.pb.go index ff1a56893cb..fe198e7c011 100644 --- a/gen/go/headscale/v1/user.pb.go +++ b/gen/go/headscale/v1/user.pb.go @@ -26,9 +26,14 @@ type User struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - CreatedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + DisplayName string `protobuf:"bytes,4,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` + Email string `protobuf:"bytes,5,opt,name=email,proto3" json:"email,omitempty"` + ProviderId string `protobuf:"bytes,6,opt,name=provider_id,json=providerId,proto3" json:"provider_id,omitempty"` + Provider string `protobuf:"bytes,7,opt,name=provider,proto3" json:"provider,omitempty"` + ProfilePicUrl string `protobuf:"bytes,8,opt,name=profile_pic_url,json=profilePicUrl,proto3" json:"profile_pic_url,omitempty"` } func (x *User) Reset() { @@ -84,6 +89,41 @@ func (x *User) GetCreatedAt() *timestamppb.Timestamp { return nil } +func (x *User) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + +func (x *User) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *User) GetProviderId() string { + if x != nil { + return x.ProviderId + } + return "" +} + +func (x *User) GetProvider() string { + if x != nil { + return x.Provider + } + return "" +} + +func (x *User) GetProfilePicUrl() string { + if x != nil { + return x.ProfilePicUrl + } + return "" +} + type GetUserRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -551,47 +591,57 @@ var file_headscale_v1_user_proto_rawDesc = []byte{ 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x65, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, - 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, - 0x24, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x39, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, - 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, - 0x22, 0x27, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3c, 0x0a, 0x12, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x26, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, - 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, - 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x49, 0x0a, 0x11, 0x52, 0x65, 0x6e, 0x61, 0x6d, - 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, - 0x6f, 0x6c, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x6f, 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x4e, 0x61, - 0x6d, 0x65, 0x22, 0x3c, 0x0a, 0x12, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x55, 0x73, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, - 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, - 0x22, 0x27, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x14, 0x0a, 0x12, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x12, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x22, 0x3d, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, - 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x05, 0x75, 0x73, 0x65, - 0x72, 0x73, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, - 0x61, 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x83, 0x02, 0x0a, 0x04, 0x55, 0x73, 0x65, + 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, + 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x5f, 0x70, 0x69, 0x63, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x55, 0x72, 0x6c, 0x22, 0x24, + 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x39, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, + 0x27, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3c, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, + 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, + 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, + 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x49, 0x0a, 0x11, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6f, + 0x6c, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, + 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x4e, 0x61, 0x6d, + 0x65, 0x22, 0x3c, 0x0a, 0x12, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, + 0x27, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x14, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x12, + 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x22, 0x3d, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, + 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x05, 0x75, 0x73, 0x65, 0x72, + 0x73, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, + 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gen/openapiv2/headscale/v1/headscale.swagger.json b/gen/openapiv2/headscale/v1/headscale.swagger.json index c7772f74c2e..8e0cdef5821 100644 --- a/gen/openapiv2/headscale/v1/headscale.swagger.json +++ b/gen/openapiv2/headscale/v1/headscale.swagger.json @@ -1509,6 +1509,21 @@ "createdAt": { "type": "string", "format": "date-time" + }, + "displayName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "providerId": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "profilePicUrl": { + "type": "string" } } } diff --git a/hscontrol/types/users.go b/hscontrol/types/users.go index 35839f8e6b1..f983d7f52e6 100644 --- a/hscontrol/types/users.go +++ b/hscontrol/types/users.go @@ -100,11 +100,16 @@ func (u *User) TailscaleUserProfile() tailcfg.UserProfile { } } -func (n *User) Proto() *v1.User { +func (u *User) Proto() *v1.User { return &v1.User{ - Id: strconv.FormatUint(uint64(n.ID), util.Base10), - Name: n.Name, - CreatedAt: timestamppb.New(n.CreatedAt), + Id: strconv.FormatUint(uint64(u.ID), util.Base10), + Name: u.Name, + CreatedAt: timestamppb.New(u.CreatedAt), + DisplayName: u.DisplayName, + Email: u.Email, + ProviderId: u.ProviderIdentifier, + Provider: u.Provider, + ProfilePicUrl: u.ProfilePicURL, } } diff --git a/proto/headscale/v1/user.proto b/proto/headscale/v1/user.proto index 4bc3c886f63..4c43de98b6e 100644 --- a/proto/headscale/v1/user.proto +++ b/proto/headscale/v1/user.proto @@ -5,9 +5,14 @@ option go_package = "github.com/juanfont/headscale/gen/go/v1"; import "google/protobuf/timestamp.proto"; message User { - string id = 1; - string name = 2; - google.protobuf.Timestamp created_at = 3; + string id = 1; + string name = 2; + google.protobuf.Timestamp created_at = 3; + string display_name = 4; + string email = 5; + string provider_id = 6; + string provider = 7; + string profile_pic_url = 8; } message GetUserRequest { From 326e5b0fa90ec3f8883baf2152b10d708eae5c6d Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 23 Oct 2024 10:45:59 -0500 Subject: [PATCH 17/22] cleanup linter warnings (#2206) Signed-off-by: Kristoffer Dalby --- hscontrol/mapper/mapper.go | 9 +-------- hscontrol/mapper/mapper_test.go | 12 ------------ hscontrol/noise.go | 7 ------- integration/route_test.go | 12 +++++++----- 4 files changed, 8 insertions(+), 32 deletions(-) diff --git a/hscontrol/mapper/mapper.go b/hscontrol/mapper/mapper.go index 20aa674d75b..3db1e159bd9 100644 --- a/hscontrol/mapper/mapper.go +++ b/hscontrol/mapper/mapper.go @@ -111,9 +111,7 @@ func generateUserProfiles( func generateDNSConfig( cfg *types.Config, - baseDomain string, node *types.Node, - peers types.Nodes, ) *tailcfg.DNSConfig { if cfg.DNSConfig == nil { return nil @@ -532,12 +530,7 @@ func appendPeerChanges( profiles := generateUserProfiles(node, changed) - dnsConfig := generateDNSConfig( - cfg, - cfg.BaseDomain, - node, - peers, - ) + dnsConfig := generateDNSConfig(cfg, node) tailPeers, err := tailNodes(changed, capVer, pol, cfg) if err != nil { diff --git a/hscontrol/mapper/mapper_test.go b/hscontrol/mapper/mapper_test.go index 395ece44ccb..d5f01284215 100644 --- a/hscontrol/mapper/mapper_test.go +++ b/hscontrol/mapper/mapper_test.go @@ -114,24 +114,12 @@ func TestDNSConfigMapResponse(t *testing.T) { } nodeInShared1 := mach("test_get_shared_nodes_1", "shared1", 1) - nodeInShared2 := mach("test_get_shared_nodes_2", "shared2", 2) - nodeInShared3 := mach("test_get_shared_nodes_3", "shared3", 3) - node2InShared1 := mach("test_get_shared_nodes_4", "shared1", 1) - - peersOfNodeInShared1 := types.Nodes{ - nodeInShared1, - nodeInShared2, - nodeInShared3, - node2InShared1, - } got := generateDNSConfig( &types.Config{ DNSConfig: &dnsConfigOrig, }, - baseDomain, nodeInShared1, - peersOfNodeInShared1, ) if diff := cmp.Diff(tt.want, got, cmpopts.EquateEmpty()); diff != "" { diff --git a/hscontrol/noise.go b/hscontrol/noise.go index 3545080987d..444a8073e5b 100644 --- a/hscontrol/noise.go +++ b/hscontrol/noise.go @@ -10,7 +10,6 @@ import ( "github.com/juanfont/headscale/hscontrol/types" "github.com/rs/zerolog/log" "golang.org/x/net/http2" - "golang.org/x/net/http2/h2c" "tailscale.com/control/controlbase" "tailscale.com/control/controlhttp" "tailscale.com/tailcfg" @@ -101,18 +100,12 @@ func (h *Headscale) NoiseUpgradeHandler( Methods(http.MethodPost) router.HandleFunc("/machine/map", noiseServer.NoisePollNetMapHandler) - server := http.Server{ - ReadTimeout: types.HTTPTimeout, - } - noiseServer.httpBaseConfig = &http.Server{ Handler: router, ReadHeaderTimeout: types.HTTPTimeout, } noiseServer.http2Server = &http2.Server{} - server.Handler = h2c.NewHandler(router, noiseServer.http2Server) - noiseServer.http2Server.ServeConn( noiseConn, &http2.ServeConnOpts{ diff --git a/integration/route_test.go b/integration/route_test.go index ca37b99ab5f..f163fa14dd9 100644 --- a/integration/route_test.go +++ b/integration/route_test.go @@ -22,6 +22,8 @@ import ( "tailscale.com/wgengine/filter" ) +var allPorts = filter.PortRange{First: 0, Last: 0xffff} + // This test is both testing the routes command and the propagation of // routes. func TestEnablingRoutes(t *testing.T) { @@ -1249,11 +1251,11 @@ func TestSubnetRouteACL(t *testing.T) { Dsts: []filter.NetPortRange{ { Net: netip.MustParsePrefix("100.64.0.2/32"), - Ports: filter.PortRange{0, 0xffff}, + Ports: allPorts, }, { Net: netip.MustParsePrefix("fd7a:115c:a1e0::2/128"), - Ports: filter.PortRange{0, 0xffff}, + Ports: allPorts, }, }, Caps: []filter.CapMatch{}, @@ -1281,11 +1283,11 @@ func TestSubnetRouteACL(t *testing.T) { Dsts: []filter.NetPortRange{ { Net: netip.MustParsePrefix("100.64.0.1/32"), - Ports: filter.PortRange{0, 0xffff}, + Ports: allPorts, }, { Net: netip.MustParsePrefix("fd7a:115c:a1e0::1/128"), - Ports: filter.PortRange{0, 0xffff}, + Ports: allPorts, }, }, Caps: []filter.CapMatch{}, @@ -1303,7 +1305,7 @@ func TestSubnetRouteACL(t *testing.T) { Dsts: []filter.NetPortRange{ { Net: netip.MustParsePrefix("10.33.0.0/16"), - Ports: filter.PortRange{0, 0xffff}, + Ports: allPorts, }, }, Caps: []filter.CapMatch{}, From 209bbd6b9832c8c1bc2cf58e30bc7e9e0c9e2673 Mon Sep 17 00:00:00 2001 From: hopleus Date: Mon, 28 Oct 2024 13:13:31 +0300 Subject: [PATCH 18/22] Added docs --- docs/ref/node-management.md | 38 +++++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 39 insertions(+) create mode 100644 docs/ref/node-management.md diff --git a/docs/ref/node-management.md b/docs/ref/node-management.md new file mode 100644 index 00000000000..620d25a2cdf --- /dev/null +++ b/docs/ref/node-management.md @@ -0,0 +1,38 @@ +# Node management + +See https://tailscale.com/kb/1099/device-approval for more information. + +## Setup + +### 1. Change the configuration + +1. Change the `config.yaml` to contain the desired records like so: + + ```yaml + node_management: + manual_approve_new_node: true + ``` + +2. Restart your headscale instance. + +## Usage + +### Pre-approve a node using preauthkeys + +1. Create preauthkey with a pre-approve option + +```bash +headscale preauthkeys create --user= --pre-approved +``` + +2. Register a node on the headscale using preauthkey (with the pre-approval option enabled) + +```bash +headscale nodes register --user= --mkey=mkey: --auth-key= +``` + +### Node approval after registration without the option to pre-approve the authentication key + +```bash +headscale nodes approve --identifier= +``` \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index d01c94cc3db..84415c75a16 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -178,6 +178,7 @@ nav: - Exit node: ref/exit-node.md - TLS: ref/tls.md - ACLs: ref/acls.md + - Node management: ref/node-management.md - DNS: ref/dns.md - Remote CLI: ref/remote-cli.md - Integration: From d95c33d0a53e20cae1337d84a77e4154237cd2a1 Mon Sep 17 00:00:00 2001 From: hopleus Date: Mon, 28 Oct 2024 13:40:20 +0300 Subject: [PATCH 19/22] Refactoring docs --- docs/ref/node-management.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/ref/node-management.md b/docs/ref/node-management.md index 620d25a2cdf..0d88b2a32b9 100644 --- a/docs/ref/node-management.md +++ b/docs/ref/node-management.md @@ -15,6 +15,9 @@ See https://tailscale.com/kb/1099/device-approval for more information. 2. Restart your headscale instance. +### Warning +Enabling the `node_management.manual_approve_new_node: true` option will allow manual approval of nodes in both **cli mode** and **oidc mode**. + ## Usage ### Pre-approve a node using preauthkeys From 3974f82917c185f1c9c7ebf3b99c5b4e42345de5 Mon Sep 17 00:00:00 2001 From: hopleus Date: Thu, 31 Oct 2024 10:16:05 +0300 Subject: [PATCH 20/22] Fix newline in config-example.yaml --- config-example.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config-example.yaml b/config-example.yaml index bc640e76b9c..60b6f0245da 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -389,4 +389,4 @@ randomize_client_port: false # See https://tailscale.com/kb/1099/device-approval for more information. node_management: # Require new nodes to be approved by admins before they can access the network. - manual_approve_new_node: false \ No newline at end of file + manual_approve_new_node: false From d39696c3ae962633a0df2cea29b52f133fd4ebe7 Mon Sep 17 00:00:00 2001 From: hopleus Date: Thu, 31 Oct 2024 12:43:49 +0300 Subject: [PATCH 21/22] Rewrite oidc_callback_template in elem-go --- hscontrol/assets/oidc_callback_template.html | 307 ------------------- hscontrol/oidc.go | 46 +-- hscontrol/templates/general.go | 93 ++++++ hscontrol/templates/oidc_callback.go | 272 ++++++++++++++++ 4 files changed, 370 insertions(+), 348 deletions(-) delete mode 100644 hscontrol/assets/oidc_callback_template.html create mode 100644 hscontrol/templates/oidc_callback.go diff --git a/hscontrol/assets/oidc_callback_template.html b/hscontrol/assets/oidc_callback_template.html deleted file mode 100644 index 2236f3658c2..00000000000 --- a/hscontrol/assets/oidc_callback_template.html +++ /dev/null @@ -1,307 +0,0 @@ - - - - - - Headscale Authentication Succeeded - - - -
-
- -
- -
-
Signed in via your OIDC provider
-

- {{.Verb}} as {{.User}}, you can now close this window. -

-
-
-
-

Not sure how to get started?

-

- Check out beginner and advanced guides on, or read more in the - documentation. -

- - - - - - - View the headscale documentation - - - - - - - - View the tailscale documentation - -
-
- - diff --git a/hscontrol/oidc.go b/hscontrol/oidc.go index 84267b41586..df182999f39 100644 --- a/hscontrol/oidc.go +++ b/hscontrol/oidc.go @@ -1,14 +1,13 @@ package hscontrol import ( - "bytes" "context" "crypto/rand" _ "embed" "encoding/hex" "errors" "fmt" - "html/template" + "github.com/juanfont/headscale/hscontrol/templates" "net/http" "slices" "strings" @@ -177,13 +176,6 @@ type oidcCallbackTemplateConfig struct { Verb string } -//go:embed assets/oidc_callback_template.html -var oidcCallbackTemplateContent string - -var oidcCallbackTemplate = template.Must( - template.New("oidccallback").Parse(oidcCallbackTemplateContent), -) - // OIDCCallbackHandler handles the callback from the OIDC endpoint // Retrieves the nkey from the state cache and adds the node to the users email user // TODO: A confirmation page for new nodes should be added to avoid phishing vulnerabilities @@ -248,19 +240,11 @@ func (a *AuthProviderOIDC) OIDCCallbackHandler( return } - // TODO(kradalby): replace with go-elem - var content bytes.Buffer - if err := oidcCallbackTemplate.Execute(&content, oidcCallbackTemplateConfig{ - User: user.DisplayNameOrUsername(), - Verb: "Reauthenticated", - }); err != nil { - http.Error(writer, fmt.Errorf("rendering OIDC callback template: %w", err).Error(), http.StatusInternalServerError) - return - } + content := templates.OidcCallback(node, user, "Reauthenticated") writer.Header().Set("Content-Type", "text/html; charset=utf-8") writer.WriteHeader(http.StatusOK) - _, err = writer.Write(content.Bytes()) + _, err = writer.Write([]byte(content)) if err != nil { util.LogErr(err, "Failed to write response") } @@ -275,15 +259,11 @@ func (a *AuthProviderOIDC) OIDCCallbackHandler( return } - content, err := renderOIDCCallbackTemplate(user) - if err != nil { - http.Error(writer, err.Error(), http.StatusInternalServerError) - return - } + content := templates.OidcCallback(node, user, "Authenticated") writer.Header().Set("Content-Type", "text/html; charset=utf-8") writer.WriteHeader(http.StatusOK) - if _, err := writer.Write(content.Bytes()); err != nil { + if _, err := writer.Write([]byte(content)); err != nil { util.LogErr(err, "Failed to write response") } @@ -486,19 +466,3 @@ func (a *AuthProviderOIDC) registerNode( return nil } - -// TODO(kradalby): -// Rewrite in elem-go -func renderOIDCCallbackTemplate( - user *types.User, -) (*bytes.Buffer, error) { - var content bytes.Buffer - if err := oidcCallbackTemplate.Execute(&content, oidcCallbackTemplateConfig{ - User: user.DisplayNameOrUsername(), - Verb: "Authenticated", - }); err != nil { - return nil, fmt.Errorf("rendering OIDC callback template: %w", err) - } - - return &content, nil -} diff --git a/hscontrol/templates/general.go b/hscontrol/templates/general.go index 3728b7365e4..566ace46302 100644 --- a/hscontrol/templates/general.go +++ b/hscontrol/templates/general.go @@ -20,6 +20,99 @@ var headerStyle = styles.Props{ styles.LineHeight: "1.2", } +func logo(class string) *elem.Element { + return &elem.Element{ + Tag: "svg", + Attrs: attrs.Props{ + attrs.Class: class, + attrs.Width: "146", + attrs.Height: "51", + "xmlns": "http://www.w3.org/2000/svg", + "xml:space": "preserve", + attrs.Style: styles.Props{ + styles.Var("fill-rule"): "evenodd", + styles.Var("clip-rule"): "evenodd", + styles.Var("stroke-linejoin"): "round", + styles.Var("stroke-miterlimit"): styles.Int(2), + }.ToInline(), + "viewBox": "0 0 1280 640", + }, + Children: []elem.Node{ + elem.Raw(""), + elem.Raw(""), + elem.Raw(""), + elem.Raw(""), + elem.Raw(""), + elem.Raw(""), + elem.Raw(""), + elem.Raw(""), + elem.Raw(""), + elem.Raw(""), + elem.Raw(""), + elem.Raw(""), + elem.Raw(""), + elem.Raw(""), + elem.Raw(""), + elem.Raw(""), + elem.Raw(""), + elem.Raw(""), + }, + } +} + +func iconSuccess(class string) *elem.Element { + return &elem.Element{ + Tag: "svg", + Attrs: attrs.Props{ + attrs.Class: class, + attrs.Width: "20", + attrs.Height: "20", + "viewBox": "0 0 512 512", + "xmlns": "http://www.w3.org/2000/svg", + }, + Children: []elem.Node{ + elem.Raw(""), + }, + } +} + +func iconWarning() *elem.Element { + return &elem.Element{ + Tag: "svg", + Attrs: attrs.Props{ + attrs.Width: "20", + attrs.Height: "20", + "viewBox": "0 0 24 24", + "fill": "none", + "xmlns": "http://www.w3.org/2000/svg", + attrs.Style: styles.Props{ + styles.MarginBottom: "1rem", + }.ToInline(), + }, + Children: []elem.Node{ + elem.Raw(""), + elem.Raw(""), + elem.Raw(""), + }, + } +} + +func iconExternalLink() *elem.Element { + return &elem.Element{ + Tag: "svg", + Attrs: attrs.Props{ + attrs.Width: "16", + attrs.Height: "16", + "viewBox": "0 0 16 16", + "fill": "currentColor", + "xmlns": "http://www.w3.org/2000/svg", + }, + Children: []elem.Node{ + elem.Raw(""), + }, + } +} + func headerOne(text string) *elem.Element { return elem.H1(attrs.Props{attrs.Style: headerStyle.ToInline()}, elem.Text(text)) } diff --git a/hscontrol/templates/oidc_callback.go b/hscontrol/templates/oidc_callback.go new file mode 100644 index 00000000000..3b378d99f0a --- /dev/null +++ b/hscontrol/templates/oidc_callback.go @@ -0,0 +1,272 @@ +package templates + +import ( + "fmt" + "github.com/chasefleming/elem-go" + "github.com/chasefleming/elem-go/attrs" + "github.com/chasefleming/elem-go/styles" + "github.com/juanfont/headscale/hscontrol/types" +) + +func OidcCallback(node *types.Node, user *types.User, verb string) string { + styleMgr := styles.NewStyleManager() + styleBody := styleMgr.AddStyle(styles.Props{ + styles.FontSize: styles.Pixels(14), + styles.FontFamily: "system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\", \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif", + }) + styleHr := styleMgr.AddStyle(styles.Props{ + styles.BorderColor: "#fdfdfe", + styles.Margin: "24px 0", + }) + classContainer := styleMgr.AddStyle(styles.Props{ + styles.Display: "flex", + styles.JustifyContent: "center", + styles.AlignItems: "center", + styles.Height: styles.ViewportHeight(70), + }) + classLogo := styleMgr.AddStyle(styles.Props{ + styles.Display: "block", + styles.MarginLeft: styles.Pixels(-20), + styles.MarginBottom: styles.Pixels(16), + }) + + colorMessageSuccess := styles.Props{ + styles.Background: "#fafdfa", + styles.Border: "1px solid #c6e9c9", + } + colorMessageWarning := styles.Props{ + styles.Background: "#fff9f2", + styles.Border: "1px solid #f7d7b0", + } + styleMessage := styles.Props{ + styles.Display: "flex", + styles.MinWidth: styles.ViewportWidth(40), + styles.MarginBottom: styles.Pixels(12), + styles.Padding: "12px 16px 16px 12px", + styles.Position: "relative", + styles.BorderRadius: styles.Pixels(2), + styles.FontSize: styles.Pixels(14), + } + + var classMessage string + if node.Approved { + classMessage = styleMgr.AddStyle(styles.Merge(styleMessage, colorMessageSuccess)) + } else { + classMessage = styleMgr.AddStyle(styles.Merge(styleMessage, colorMessageWarning)) + } + + classMessageContent := styleMgr.AddStyle(styles.Props{ + styles.MarginLeft: styles.Pixels(4), + }) + classIconSuccess := styleMgr.AddStyle(styles.Props{ + "fill": "#2eb039", + }) + + colorMessageTitleSuccess := styles.Props{ + styles.Color: "#1e7125", + } + colorMessageTitleWarning := styles.Props{ + styles.Color: "#d58525", + } + styleMessageTitle := styles.Props{ + styles.FontSize: styles.Pixels(16), + styles.FontWeight: styles.Int(700), + styles.LineHeight: styles.Float(1.25), + } + + var classMessageTitle string + if node.Approved { + classMessageTitle = styleMgr.AddStyle(styles.Merge(styleMessageTitle, colorMessageTitleSuccess)) + } else { + classMessageTitle = styleMgr.AddStyle(styles.Merge(styleMessageTitle, colorMessageTitleWarning)) + } + + colorMessageBodySuccess := styles.Props{ + styles.Color: "#17421b", + } + colorMessageBodyWarning := styles.Props{ + styles.Color: "#824c0b", + } + styleMessageBody := styles.Props{ + styles.FontSize: styles.Pixels(12), + styles.Margin: styles.Int(0), + styles.Padding: styles.Int(0), + styles.Border: styles.Int(0), + styles.MarginTop: styles.Pixels(4), + } + + var classMessageBody string + if node.Approved { + classMessageBody = styleMgr.AddStyle(styles.Merge(styleMessageBody, colorMessageBodySuccess)) + } else { + classMessageBody = styleMgr.AddStyle(styles.Merge(styleMessageBody, colorMessageBodyWarning)) + } + + styleA := styleMgr.AddCompositeStyle(styles.CompositeStyle{ + Default: styles.Props{ + styles.Display: "block", + styles.Margin: "8px 0", + styles.Color: "#1563ff", + styles.TextDecoration: "none", + styles.FontWeight: styles.Int(600), + }, + PseudoClasses: map[string]styles.Props{ + styles.PseudoHover: {styles.Color: "black"}, + }, + }) + classIcon := styleMgr.AddStyle(styles.Props{ + styles.AlignItems: "center", + styles.Display: "inline-flex", + styles.JustifyContent: "center", + styles.Width: styles.Pixels(21), + styles.Height: styles.Pixels(21), + styles.VerticalAlign: "middle", + }) + styleH1 := styleMgr.AddStyle(styles.Props{ + styles.FontSize: "17.5px", + styles.FontWeight: styles.Pixels(700), + styles.MarginBottom: styles.Pixels(0), + }) + styleH1P := styleMgr.AddStyle(styles.Props{ + styles.Margin: "8px 0 16px 0", + }) + + var messageText string + var icon *elem.Element + + if node.Approved { + messageText = fmt.Sprintf( + "%s as %s, you can now close this window.", + verb, + user.DisplayNameOrUsername(), + ) + icon = iconSuccess(classIconSuccess) + } else { + messageText = fmt.Sprintf( + "%s as %s, but not connected!", + verb, + user.DisplayNameOrUsername(), + ) + icon = iconWarning() + } + + description := &elem.Element{ + Tag: "span", + } + + if !node.Approved { + description = elem.Div( + nil, + elem.P( + attrs.Props{ + attrs.Class: classMessageBody, + attrs.Style: styles.Props{ + styles.MarginTop: "1rem", + }.ToInline(), + }, + elem.Text("However, it can't connect until approved by the administrator a network."), + ), + elem.P( + attrs.Props{ + attrs.Class: classMessageBody, + }, + elem.Text("Once approved, your node will connect automatically."), + ), + ) + } + + return HtmlStructure( + elem.Title( + nil, + elem.Text("headscale - Authentication Succeeded"), + ), + elem.Body( + attrs.Props{ + attrs.DataAttr("translate"): "no", + attrs.Class: styleBody, + }, + elem.Div( + attrs.Props{ + attrs.Class: classContainer, + }, + elem.Div( + nil, + logo(classLogo), + elem.Div( + attrs.Props{ + attrs.Class: classMessage, + }, + icon, + elem.Div( + attrs.Props{ + attrs.Class: classMessageContent, + }, + elem.Div( + attrs.Props{ + attrs.Class: classMessageTitle, + }, + elem.Text("Signed in via your OIDC provider"), + ), + elem.P( + attrs.Props{ + attrs.Class: classMessageBody, + }, + elem.Text(messageText), + ), + description, + ), + ), + elem.Hr( + attrs.Props{ + attrs.Class: styleHr, + }, + ), + elem.H1( + attrs.Props{ + attrs.Class: styleH1, + }, + elem.Text("Not sure how to get started?"), + ), + elem.P( + attrs.Props{ + attrs.Class: styleH1P, + }, + elem.Text(" Check out beginner and advanced guides on, or read more in the documentation."), + ), + elem.A( + attrs.Props{ + attrs.Href: "https://github.com/juanfont/headscale/tree/main/docs", + attrs.Rel: "noreferrer noopener", + attrs.Target: "_blank", + attrs.Class: styleA, + }, + elem.Span( + attrs.Props{ + attrs.Class: classIcon, + }, + iconExternalLink(), + ), + elem.Text("View the headscale documentation"), + ), + elem.A( + attrs.Props{ + attrs.Href: "https://tailscale.com/kb/", + attrs.Rel: "noreferrer noopener", + attrs.Target: "_blank", + attrs.Class: styleA, + }, + elem.Span( + attrs.Props{ + attrs.Class: classIcon, + }, + iconExternalLink(), + ), + elem.Text("View the tailscale documentation"), + ), + ), + ), + ), + ).RenderWithOptions(elem.RenderOptions{ + StyleManager: styleMgr, + }) +} From a0c3dcf65db12b570b0d0d5b06b450dd44ec795b Mon Sep 17 00:00:00 2001 From: hopleus Date: Thu, 31 Oct 2024 14:08:25 +0300 Subject: [PATCH 22/22] Fix OIDCCallbackHandler --- hscontrol/oidc.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/hscontrol/oidc.go b/hscontrol/oidc.go index df182999f39..af03a16f431 100644 --- a/hscontrol/oidc.go +++ b/hscontrol/oidc.go @@ -254,7 +254,8 @@ func (a *AuthProviderOIDC) OIDCCallbackHandler( // Register the node if it does not exist. if mKey != nil { - if err := a.registerNode(user, mKey, nodeExpiry); err != nil { + node, err = a.registerNode(user, mKey, nodeExpiry) + if err != nil { http.Error(writer, err.Error(), http.StatusInternalServerError) return } @@ -448,21 +449,22 @@ func (a *AuthProviderOIDC) registerNode( user *types.User, machineKey *key.MachinePublic, expiry time.Time, -) error { +) (*types.Node, error) { ipv4, ipv6, err := a.ipAlloc.Next() if err != nil { - return err + return nil, err } - if _, err := a.db.RegisterNodeFromAuthCallback( + node, err := a.db.RegisterNodeFromAuthCallback( *machineKey, types.UserID(user.ID), &expiry, util.RegisterMethodOIDC, ipv4, ipv6, - ); err != nil { - return fmt.Errorf("could not register node: %w", err) + ) + if err != nil { + return nil, fmt.Errorf("could not register node: %w", err) } - return nil + return node, nil }