From fbdbf9e3ce6e32f8a7138eff928fc73c6b1d55b1 Mon Sep 17 00:00:00 2001 From: braginini Date: Mon, 6 Jun 2022 14:11:09 +0200 Subject: [PATCH 01/51] Add SSH server --- client/ssh/server.go | 84 ++++++++++++++++++++++++++++++++++++++++++++ client/ssh/util.go | 52 +++++++++++++++++++++++++++ go.mod | 3 +- go.sum | 10 +++--- 4 files changed, 144 insertions(+), 5 deletions(-) create mode 100644 client/ssh/server.go create mode 100644 client/ssh/util.go diff --git a/client/ssh/server.go b/client/ssh/server.go new file mode 100644 index 00000000000..3449247fa4e --- /dev/null +++ b/client/ssh/server.go @@ -0,0 +1,84 @@ +package ssh + +import ( + "fmt" + "github.com/gliderlabs/ssh" + gossh "golang.org/x/crypto/ssh" + "io" + "net" + "strings" + "sync" +) + +type Server struct { + listener net.Listener + allowedKeys map[string]ssh.PublicKey + mu sync.Mutex + hostKeyPEM []byte +} + +// NewSSHServer creates new server with provided host key +func NewSSHServer(hostKeyPEM []byte) (*Server, error) { + ln, err := net.Listen("tcp", ":2222") + if err != nil { + return nil, err + } + return &Server{listener: ln, mu: sync.Mutex{}, hostKeyPEM: hostKeyPEM}, nil +} + +func (srv *Server) UpdateKeys(newKeys []string) error { + srv.mu.Lock() + defer srv.mu.Unlock() + + srv.allowedKeys = make(map[string]ssh.PublicKey, len(newKeys)) + for _, strKey := range newKeys { + parsedKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(strKey)) + if err != nil { + return err + } + srv.allowedKeys[strKey] = parsedKey + } + + return nil +} + +// Stop stops SSH server. Blocking +func (srv *Server) Stop() error { + err := srv.listener.Close() + if err != nil { + return err + } + return nil +} + +// Start starts SSH server. Blocking +func (srv *Server) Start() error { + handler := func(s ssh.Session) { + authorizedKey := gossh.MarshalAuthorizedKey(s.PublicKey()) + io.WriteString(s, fmt.Sprintf("public key used by %s:\n", s.User())) + s.Write(authorizedKey) + } + + publicKeyOption := ssh.PublicKeyAuth(func(ctx ssh.Context, key ssh.PublicKey) bool { + srv.mu.Lock() + defer srv.mu.Unlock() + + k := strings.TrimSpace(string(gossh.MarshalAuthorizedKey(key))) + if allowed, ok := srv.allowedKeys[k]; ok { + if ssh.KeysEqual(allowed, key) { + return true + } + } + + return false + }) + + hostKeyPEM := ssh.HostKeyPEM(srv.hostKeyPEM) + + err := ssh.Serve(srv.listener, handler, publicKeyOption, hostKeyPEM) + if err != nil { + return err + } + + return nil +} diff --git a/client/ssh/util.go b/client/ssh/util.go new file mode 100644 index 00000000000..4998f911909 --- /dev/null +++ b/client/ssh/util.go @@ -0,0 +1,52 @@ +package ssh + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + gossh "golang.org/x/crypto/ssh" +) + +// GeneratePrivateKey creates RSA Private Key of specified byte size +func GeneratePrivateKey(bitSize int) (*rsa.PrivateKey, error) { + privateKey, err := rsa.GenerateKey(rand.Reader, bitSize) + if err != nil { + return nil, err + } + + err = privateKey.Validate() + if err != nil { + return nil, err + } + + return privateKey, nil +} + +// GeneratePublicKey takes a rsa.PublicKey and return bytes suitable for writing to .pub file +// returns the key in format format "ssh-rsa ..." +func GeneratePublicKey(privateKey *rsa.PublicKey) ([]byte, error) { + publicRsaKey, err := gossh.NewPublicKey(privateKey) + if err != nil { + return nil, err + } + return gossh.MarshalAuthorizedKey(publicRsaKey), nil +} + +// EncodePrivateKeyToPEM encodes Private Key from RSA to PEM format +func EncodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte { + // Get ASN.1 DER format + privDER := x509.MarshalPKCS1PrivateKey(privateKey) + + // pem.Block + privBlock := pem.Block{ + Type: "RSA PRIVATE KEY", + Headers: nil, + Bytes: privDER, + } + + // Private key in PEM format + privatePEM := pem.EncodeToMemory(&privBlock) + + return privatePEM +} diff --git a/go.mod b/go.mod index 819d06b7032..929583f8022 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,7 @@ require ( github.com/c-robinson/iplib v1.0.3 github.com/eko/gocache/v2 v2.3.1 github.com/getlantern/systray v1.2.1 + github.com/gliderlabs/ssh v0.3.4 github.com/magiconair/properties v1.8.5 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/rs/xid v1.3.0 @@ -42,6 +43,7 @@ require ( require ( github.com/BurntSushi/toml v0.4.1 // indirect github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 // indirect + github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect @@ -89,7 +91,6 @@ require ( github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 // indirect github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect github.com/yuin/goldmark v1.4.1 // indirect - golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf // indirect golang.org/x/image v0.0.0-20200430140353-33d19683fad8 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/net v0.0.0-20220412020605-290c469a71a5 // indirect diff --git a/go.sum b/go.sum index 87573af063c..db38c714809 100644 --- a/go.sum +++ b/go.sum @@ -69,6 +69,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/allegro/bigcache/v3 v3.0.2 h1:AKZCw+5eAaVyNTBmI2fgyPVJhHkdWder3O9IrprcQfI= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -130,8 +132,6 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3 github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/eko/gocache/v2 v2.3.1 h1:8MMkfqGJ0KIA9OXT0rXevcEIrU16oghrGDiIDJDFCa0= github.com/eko/gocache/v2 v2.3.1/go.mod h1:l2z8OmpZHL0CpuzDJtxm267eF3mZW1NqUsMj+sKrbUs= -github.com/eko/gocache/v3 v3.0.0 h1:Mfa3Nj6GdrXiBXz+JsvESffxO8BGUYVQ2heiAhEhH1U= -github.com/eko/gocache/v3 v3.0.0/go.mod h1:2//8SJUJBp0/MKvuPd6mhZuWpL3qTic4N0ssUEaflCk= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= @@ -180,6 +180,8 @@ github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2H github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= +github.com/gliderlabs/ssh v0.3.4 h1:+AXBtim7MTKaLVPgvE+3mhewYRawNLTd+jEEz/wExZw= +github.com/gliderlabs/ssh v0.3.4/go.mod h1:ZSS+CUoKHDrqVakTfTWUlKSr9MtMFkC4UvtQKD7O914= github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f h1:s0O46d8fPwk9kU4k1jj76wBquMVETx7uveQD9MCIQoU= github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -645,6 +647,7 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -660,8 +663,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf h1:oXVg4h2qJDd9htKxb5SCpFBHLipW6hXmL3qpUixS2jw= -golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw= @@ -894,6 +895,7 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 39b222ba30c2015de77f07bc07e8c8bed0b306d0 Mon Sep 17 00:00:00 2001 From: braginini Date: Mon, 6 Jun 2022 14:20:40 +0200 Subject: [PATCH 02/51] Add SSH config to the Management gRPC protocol --- management/proto/management.pb.go | 472 +++++++++++++++++++----------- management/proto/management.proto | 21 ++ 2 files changed, 314 insertions(+), 179 deletions(-) diff --git a/management/proto/management.pb.go b/management/proto/management.pb.go index b66542ae6e0..c30898fb3c9 100644 --- a/management/proto/management.pb.go +++ b/management/proto/management.pb.go @@ -1,15 +1,15 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v3.20.1 +// protoc v3.12.4 // source: management.proto package proto import ( + timestamp "github.com/golang/protobuf/ptypes/timestamp" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" ) @@ -116,7 +116,7 @@ func (x DeviceAuthorizationFlowProvider) Number() protoreflect.EnumNumber { // Deprecated: Use DeviceAuthorizationFlowProvider.Descriptor instead. func (DeviceAuthorizationFlowProvider) EnumDescriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{15, 0} + return file_management_proto_rawDescGZIP(), []int{16, 0} } type EncryptedMessage struct { @@ -319,6 +319,8 @@ type LoginRequest struct { Meta *PeerSystemMeta `protobuf:"bytes,2,opt,name=meta,proto3" json:"meta,omitempty"` // SSO token (can be empty) JwtToken string `protobuf:"bytes,3,opt,name=jwtToken,proto3" json:"jwtToken,omitempty"` + // sshPubKey represents a public SSH key of the peer. Can be absent. + SshPubKey []byte `protobuf:"bytes,4,opt,name=sshPubKey,proto3" json:"sshPubKey,omitempty"` } func (x *LoginRequest) Reset() { @@ -374,6 +376,13 @@ func (x *LoginRequest) GetJwtToken() string { return "" } +func (x *LoginRequest) GetSshPubKey() []byte { + if x != nil { + return x.SshPubKey + } + return nil +} + // Peer machine meta data type PeerSystemMeta struct { state protoimpl.MessageState @@ -543,7 +552,7 @@ type ServerKeyResponse struct { // Server's Wireguard public key Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` // Key expiration timestamp after which the key should be fetched again by the client - ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=expiresAt,proto3" json:"expiresAt,omitempty"` + ExpiresAt *timestamp.Timestamp `protobuf:"bytes,2,opt,name=expiresAt,proto3" json:"expiresAt,omitempty"` // Version of the Wiretrustee Management Service protocol Version int32 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"` } @@ -587,7 +596,7 @@ func (x *ServerKeyResponse) GetKey() string { return "" } -func (x *ServerKeyResponse) GetExpiresAt() *timestamppb.Timestamp { +func (x *ServerKeyResponse) GetExpiresAt() *timestamp.Timestamp { if x != nil { return x.ExpiresAt } @@ -839,6 +848,8 @@ type PeerConfig struct { Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` // Wiretrustee DNS server (a Wireguard DNS config) Dns string `protobuf:"bytes,2,opt,name=dns,proto3" json:"dns,omitempty"` + // SSHConfig of the peer. + SshConfig *SSHConfig `protobuf:"bytes,3,opt,name=sshConfig,proto3" json:"sshConfig,omitempty"` } func (x *PeerConfig) Reset() { @@ -887,6 +898,13 @@ func (x *PeerConfig) GetDns() string { return "" } +func (x *PeerConfig) GetSshConfig() *SSHConfig { + if x != nil { + return x.SshConfig + } + return nil +} + // NetworkMap represents a network state of the peer with the corresponding configuration parameters to establish peer-to-peer connections type NetworkMap struct { state protoimpl.MessageState @@ -976,6 +994,8 @@ type RemotePeerConfig struct { WgPubKey string `protobuf:"bytes,1,opt,name=wgPubKey,proto3" json:"wgPubKey,omitempty"` // Wireguard allowed IPs of a remote peer e.g. [10.30.30.1/32] AllowedIps []string `protobuf:"bytes,2,rep,name=allowedIps,proto3" json:"allowedIps,omitempty"` + // SSHConfig is a SSH config of the remote peer. SSHConfig.sshPubKey should be ignored because peer knows it's SSH key. + SshConfig *SSHConfig `protobuf:"bytes,3,opt,name=sshConfig,proto3" json:"sshConfig,omitempty"` } func (x *RemotePeerConfig) Reset() { @@ -1024,6 +1044,72 @@ func (x *RemotePeerConfig) GetAllowedIps() []string { return nil } +func (x *RemotePeerConfig) GetSshConfig() *SSHConfig { + if x != nil { + return x.SshConfig + } + return nil +} + +// SSHConfig represents SSH configurations of a peer. +type SSHConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // sshEnabled indicates whether a SSH server is enabled on this peer + SshEnabled bool `protobuf:"varint,1,opt,name=sshEnabled,proto3" json:"sshEnabled,omitempty"` + // sshPubKey is a SSH public key of a peer to be added to authorized_hosts. + // This property should be ignore if SSHConfig comes from PeerConfig. + SshPubKey []byte `protobuf:"bytes,2,opt,name=sshPubKey,proto3" json:"sshPubKey,omitempty"` +} + +func (x *SSHConfig) Reset() { + *x = SSHConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SSHConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SSHConfig) ProtoMessage() {} + +func (x *SSHConfig) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[14] + 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 SSHConfig.ProtoReflect.Descriptor instead. +func (*SSHConfig) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{14} +} + +func (x *SSHConfig) GetSshEnabled() bool { + if x != nil { + return x.SshEnabled + } + return false +} + +func (x *SSHConfig) GetSshPubKey() []byte { + if x != nil { + return x.SshPubKey + } + return nil +} + // DeviceAuthorizationFlowRequest empty struct for future expansion type DeviceAuthorizationFlowRequest struct { state protoimpl.MessageState @@ -1034,7 +1120,7 @@ type DeviceAuthorizationFlowRequest struct { func (x *DeviceAuthorizationFlowRequest) Reset() { *x = DeviceAuthorizationFlowRequest{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[14] + mi := &file_management_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1047,7 +1133,7 @@ func (x *DeviceAuthorizationFlowRequest) String() string { func (*DeviceAuthorizationFlowRequest) ProtoMessage() {} func (x *DeviceAuthorizationFlowRequest) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[14] + mi := &file_management_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1060,7 +1146,7 @@ func (x *DeviceAuthorizationFlowRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeviceAuthorizationFlowRequest.ProtoReflect.Descriptor instead. func (*DeviceAuthorizationFlowRequest) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{14} + return file_management_proto_rawDescGZIP(), []int{15} } // DeviceAuthorizationFlow represents Device Authorization Flow information @@ -1079,7 +1165,7 @@ type DeviceAuthorizationFlow struct { func (x *DeviceAuthorizationFlow) Reset() { *x = DeviceAuthorizationFlow{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[15] + mi := &file_management_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1092,7 +1178,7 @@ func (x *DeviceAuthorizationFlow) String() string { func (*DeviceAuthorizationFlow) ProtoMessage() {} func (x *DeviceAuthorizationFlow) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[15] + mi := &file_management_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1105,7 +1191,7 @@ func (x *DeviceAuthorizationFlow) ProtoReflect() protoreflect.Message { // Deprecated: Use DeviceAuthorizationFlow.ProtoReflect.Descriptor instead. func (*DeviceAuthorizationFlow) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{15} + return file_management_proto_rawDescGZIP(), []int{16} } func (x *DeviceAuthorizationFlow) GetProvider() DeviceAuthorizationFlowProvider { @@ -1141,7 +1227,7 @@ type ProviderConfig struct { func (x *ProviderConfig) Reset() { *x = ProviderConfig{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[16] + mi := &file_management_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1154,7 +1240,7 @@ func (x *ProviderConfig) String() string { func (*ProviderConfig) ProtoMessage() {} func (x *ProviderConfig) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[16] + mi := &file_management_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1167,7 +1253,7 @@ func (x *ProviderConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderConfig.ProtoReflect.Descriptor instead. func (*ProviderConfig) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{16} + return file_management_proto_rawDescGZIP(), []int{17} } func (x *ProviderConfig) GetClientID() string { @@ -1231,144 +1317,157 @@ var file_management_proto_rawDesc = []byte{ 0x74, 0x79, 0x12, 0x36, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x52, 0x0a, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x22, 0x76, 0x0a, 0x0c, 0x4c, 0x6f, - 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, - 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, - 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4d, 0x65, 0x74, 0x61, - 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x6a, 0x77, 0x74, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x77, 0x74, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x22, 0xe6, 0x01, 0x0a, 0x0e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x79, 0x73, 0x74, 0x65, - 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x6f, 0x4f, 0x53, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x67, 0x6f, 0x4f, 0x53, 0x12, 0x16, 0x0a, 0x06, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x12, 0x12, 0x0a, - 0x04, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x72, - 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x0e, 0x0a, - 0x02, 0x4f, 0x53, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x4f, 0x53, 0x12, 0x2e, 0x0a, - 0x12, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x77, 0x69, 0x72, 0x65, 0x74, - 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, - 0x09, 0x75, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x75, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x94, 0x01, 0x0a, 0x0d, - 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, - 0x11, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x57, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, - 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, - 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x65, - 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, - 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x22, 0x79, 0x0a, 0x11, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x38, 0x0a, 0x09, 0x65, 0x78, 0x70, - 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x18, 0x02, 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, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, - 0x73, 0x41, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x07, 0x0a, - 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0xa8, 0x01, 0x0a, 0x11, 0x57, 0x69, 0x72, 0x65, 0x74, - 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2c, 0x0a, 0x05, - 0x73, 0x74, 0x75, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x52, 0x05, 0x73, 0x74, 0x75, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x05, 0x74, 0x75, - 0x72, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, - 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x74, 0x75, 0x72, 0x6e, - 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, - 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x61, - 0x6c, 0x22, 0x98, 0x01, 0x0a, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, - 0x72, 0x69, 0x12, 0x3b, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, - 0x3b, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x55, - 0x44, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a, - 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, - 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x54, 0x4c, 0x53, 0x10, 0x04, 0x22, 0x7d, 0x0a, 0x13, - 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, - 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x75, - 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, - 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x38, 0x0a, 0x0a, 0x50, - 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x64, 0x6e, 0x73, 0x22, 0xcc, 0x01, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x0a, - 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, - 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3e, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, - 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, - 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, - 0x65, 0x65, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, - 0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, 0x73, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x22, 0x4e, 0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, - 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, - 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, - 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, - 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, - 0x64, 0x49, 0x70, 0x73, 0x22, 0x20, 0x0a, 0x1e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xbf, 0x01, 0x0a, 0x17, 0x44, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, - 0x6f, 0x77, 0x12, 0x48, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x0e, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x22, 0x16, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0a, 0x0a, 0x06, - 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x00, 0x22, 0x84, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x43, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x43, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x44, - 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x32, - 0xf7, 0x02, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1c, - 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, - 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x04, - 0x53, 0x79, 0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, - 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x22, 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, 0x69, 0x73, 0x48, 0x65, - 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5a, 0x0a, - 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x22, 0x94, 0x01, 0x0a, 0x0c, 0x4c, + 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, + 0x65, 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, + 0x65, 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4d, 0x65, 0x74, + 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x6a, 0x77, 0x74, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x77, 0x74, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, + 0x79, 0x22, 0xe6, 0x01, 0x0a, 0x0e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x67, 0x6f, 0x4f, 0x53, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x67, 0x6f, 0x4f, 0x53, 0x12, 0x16, 0x0a, 0x06, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, + 0x63, 0x6f, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x72, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x0e, 0x0a, 0x02, + 0x4f, 0x53, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x4f, 0x53, 0x12, 0x2e, 0x0a, 0x12, + 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x65, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, + 0x75, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x75, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x94, 0x01, 0x0a, 0x0d, 0x4c, + 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x11, + 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x57, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x65, 0x65, + 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x22, 0x79, 0x0a, 0x11, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x38, 0x0a, 0x09, 0x65, 0x78, 0x70, 0x69, + 0x72, 0x65, 0x73, 0x41, 0x74, 0x18, 0x02, 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, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, + 0x41, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x07, 0x0a, 0x05, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0xa8, 0x01, 0x0a, 0x11, 0x57, 0x69, 0x72, 0x65, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2c, 0x0a, 0x05, 0x73, + 0x74, 0x75, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x05, 0x73, 0x74, 0x75, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x05, 0x74, 0x75, 0x72, + 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x48, + 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x74, 0x75, 0x72, 0x6e, 0x73, + 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, + 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, + 0x22, 0x98, 0x01, 0x0a, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, + 0x69, 0x12, 0x3b, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x3b, + 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, + 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, + 0x48, 0x54, 0x54, 0x50, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, + 0x03, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x54, 0x4c, 0x53, 0x10, 0x04, 0x22, 0x7d, 0x0a, 0x13, 0x50, + 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, + 0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x6d, 0x0a, 0x0a, 0x50, 0x65, + 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x64, 0x6e, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, + 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xcc, 0x01, 0x0a, 0x0a, 0x4e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x65, + 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3e, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x72, 0x65, 0x6d, + 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x72, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, + 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x83, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6d, + 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, + 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x73, 0x73, 0x68, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x49, + 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x73, + 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, + 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, + 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x20, 0x0a, 0x1e, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xbf, 0x01, 0x0a, 0x17, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x48, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x12, 0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x16, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x00, 0x22, 0x84, 0x01, + 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, + 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, + 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x75, 0x64, 0x69, + 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x75, 0x64, 0x69, + 0x65, 0x6e, 0x63, 0x65, 0x32, 0xf7, 0x02, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x4c, 0x6f, + 0x67, 0x69, 0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, + 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, + 0x00, 0x12, 0x46, 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, + 0x09, 0x69, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, + 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, + 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x42, 0x08, + 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1384,7 +1483,7 @@ func file_management_proto_rawDescGZIP() []byte { } var file_management_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 17) +var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 18) var file_management_proto_goTypes = []interface{}{ (HostConfig_Protocol)(0), // 0: management.HostConfig.Protocol (DeviceAuthorizationFlowProvider)(0), // 1: management.DeviceAuthorizationFlow.provider @@ -1402,10 +1501,11 @@ var file_management_proto_goTypes = []interface{}{ (*PeerConfig)(nil), // 13: management.PeerConfig (*NetworkMap)(nil), // 14: management.NetworkMap (*RemotePeerConfig)(nil), // 15: management.RemotePeerConfig - (*DeviceAuthorizationFlowRequest)(nil), // 16: management.DeviceAuthorizationFlowRequest - (*DeviceAuthorizationFlow)(nil), // 17: management.DeviceAuthorizationFlow - (*ProviderConfig)(nil), // 18: management.ProviderConfig - (*timestamppb.Timestamp)(nil), // 19: google.protobuf.Timestamp + (*SSHConfig)(nil), // 16: management.SSHConfig + (*DeviceAuthorizationFlowRequest)(nil), // 17: management.DeviceAuthorizationFlowRequest + (*DeviceAuthorizationFlow)(nil), // 18: management.DeviceAuthorizationFlow + (*ProviderConfig)(nil), // 19: management.ProviderConfig + (*timestamp.Timestamp)(nil), // 20: google.protobuf.Timestamp } var file_management_proto_depIdxs = []int32{ 10, // 0: management.SyncResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig @@ -1415,31 +1515,33 @@ var file_management_proto_depIdxs = []int32{ 6, // 4: management.LoginRequest.meta:type_name -> management.PeerSystemMeta 10, // 5: management.LoginResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig 13, // 6: management.LoginResponse.peerConfig:type_name -> management.PeerConfig - 19, // 7: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp + 20, // 7: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp 11, // 8: management.WiretrusteeConfig.stuns:type_name -> management.HostConfig 12, // 9: management.WiretrusteeConfig.turns:type_name -> management.ProtectedHostConfig 11, // 10: management.WiretrusteeConfig.signal:type_name -> management.HostConfig 0, // 11: management.HostConfig.protocol:type_name -> management.HostConfig.Protocol 11, // 12: management.ProtectedHostConfig.hostConfig:type_name -> management.HostConfig - 13, // 13: management.NetworkMap.peerConfig:type_name -> management.PeerConfig - 15, // 14: management.NetworkMap.remotePeers:type_name -> management.RemotePeerConfig - 1, // 15: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider - 18, // 16: management.DeviceAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig - 2, // 17: management.ManagementService.Login:input_type -> management.EncryptedMessage - 2, // 18: management.ManagementService.Sync:input_type -> management.EncryptedMessage - 9, // 19: management.ManagementService.GetServerKey:input_type -> management.Empty - 9, // 20: management.ManagementService.isHealthy:input_type -> management.Empty - 2, // 21: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage - 2, // 22: management.ManagementService.Login:output_type -> management.EncryptedMessage - 2, // 23: management.ManagementService.Sync:output_type -> management.EncryptedMessage - 8, // 24: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse - 9, // 25: management.ManagementService.isHealthy:output_type -> management.Empty - 2, // 26: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage - 22, // [22:27] is the sub-list for method output_type - 17, // [17:22] is the sub-list for method input_type - 17, // [17:17] is the sub-list for extension type_name - 17, // [17:17] is the sub-list for extension extendee - 0, // [0:17] is the sub-list for field type_name + 16, // 13: management.PeerConfig.sshConfig:type_name -> management.SSHConfig + 13, // 14: management.NetworkMap.peerConfig:type_name -> management.PeerConfig + 15, // 15: management.NetworkMap.remotePeers:type_name -> management.RemotePeerConfig + 16, // 16: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig + 1, // 17: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider + 19, // 18: management.DeviceAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig + 2, // 19: management.ManagementService.Login:input_type -> management.EncryptedMessage + 2, // 20: management.ManagementService.Sync:input_type -> management.EncryptedMessage + 9, // 21: management.ManagementService.GetServerKey:input_type -> management.Empty + 9, // 22: management.ManagementService.isHealthy:input_type -> management.Empty + 2, // 23: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage + 2, // 24: management.ManagementService.Login:output_type -> management.EncryptedMessage + 2, // 25: management.ManagementService.Sync:output_type -> management.EncryptedMessage + 8, // 26: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse + 9, // 27: management.ManagementService.isHealthy:output_type -> management.Empty + 2, // 28: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage + 24, // [24:29] is the sub-list for method output_type + 19, // [19:24] is the sub-list for method input_type + 19, // [19:19] is the sub-list for extension type_name + 19, // [19:19] is the sub-list for extension extendee + 0, // [0:19] is the sub-list for field type_name } func init() { file_management_proto_init() } @@ -1617,7 +1719,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeviceAuthorizationFlowRequest); i { + switch v := v.(*SSHConfig); i { case 0: return &v.state case 1: @@ -1629,7 +1731,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeviceAuthorizationFlow); i { + switch v := v.(*DeviceAuthorizationFlowRequest); i { case 0: return &v.state case 1: @@ -1641,6 +1743,18 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeviceAuthorizationFlow); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ProviderConfig); i { case 0: return &v.state @@ -1659,7 +1773,7 @@ func file_management_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_management_proto_rawDesc, NumEnums: 2, - NumMessages: 17, + NumMessages: 18, NumExtensions: 0, NumServices: 1, }, diff --git a/management/proto/management.proto b/management/proto/management.proto index afdd097227c..67bbea0a925 100644 --- a/management/proto/management.proto +++ b/management/proto/management.proto @@ -71,6 +71,9 @@ message LoginRequest { PeerSystemMeta meta = 2; // SSO token (can be empty) string jwtToken = 3; + + // sshPubKey represents a public SSH key of the peer. Can be absent. + bytes sshPubKey = 4; } // Peer machine meta data @@ -143,6 +146,9 @@ message PeerConfig { string address = 1; // Wiretrustee DNS server (a Wireguard DNS config) string dns = 2; + + // SSHConfig of the peer. + SSHConfig sshConfig = 3; } // NetworkMap represents a network state of the peer with the corresponding configuration parameters to establish peer-to-peer connections @@ -172,7 +178,22 @@ message RemotePeerConfig { // Wireguard allowed IPs of a remote peer e.g. [10.30.30.1/32] repeated string allowedIps = 2; + + // SSHConfig is a SSH config of the remote peer. SSHConfig.sshPubKey should be ignored because peer knows it's SSH key. + SSHConfig sshConfig = 3; + } + +// SSHConfig represents SSH configurations of a peer. +message SSHConfig { + // sshEnabled indicates whether a SSH server is enabled on this peer + bool sshEnabled = 1; + + // sshPubKey is a SSH public key of a peer to be added to authorized_hosts. + // This property should be ignore if SSHConfig comes from PeerConfig. + bytes sshPubKey = 2; +} + // DeviceAuthorizationFlowRequest empty struct for future expansion message DeviceAuthorizationFlowRequest {} // DeviceAuthorizationFlow represents Device Authorization Flow information From f8fe1f50fa7528ae281fc8decf9de700040ce811 Mon Sep 17 00:00:00 2001 From: braginini Date: Mon, 6 Jun 2022 15:30:53 +0200 Subject: [PATCH 03/51] Add SSH roundtrip --- client/internal/config.go | 26 +++++++++++++++-- client/internal/connect.go | 1 + client/internal/engine.go | 51 +++++++++++++++++++++++++++++++++ client/ssh/server.go | 23 +++++++-------- management/server/grpcserver.go | 5 +++- 5 files changed, 91 insertions(+), 15 deletions(-) diff --git a/client/internal/config.go b/client/internal/config.go index e37d6e3e595..1bbcad7d8e5 100644 --- a/client/internal/config.go +++ b/client/internal/config.go @@ -3,6 +3,7 @@ package internal import ( "context" "fmt" + "github.com/netbirdio/netbird/client/ssh" mgm "github.com/netbirdio/netbird/management/client" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -38,12 +39,17 @@ type Config struct { AdminURL *url.URL WgIface string IFaceBlackList []string + SSHKey string } // createNewConfig creates a new config generating a new Wireguard key and saving to file func createNewConfig(managementURL, adminURL, configPath, preSharedKey string) (*Config, error) { wgKey := generateKey() - config := &Config{PrivateKey: wgKey, WgIface: iface.WgInterfaceDefault, IFaceBlackList: []string{}} + pem, err := generateSSHKey() + if err != nil { + return nil, err + } + config := &Config{SSHKey: string(pem), PrivateKey: wgKey, WgIface: iface.WgInterfaceDefault, IFaceBlackList: []string{}} if managementURL != "" { URL, err := parseURL("Management URL", managementURL) if err != nil { @@ -61,7 +67,7 @@ func createNewConfig(managementURL, adminURL, configPath, preSharedKey string) ( config.IFaceBlackList = []string{iface.WgInterfaceDefault, "tun0", "zt", "ZeroTier", "utun", "wg", "ts", "Tailscale", "tailscale"} - err := util.WriteJson(configPath, config) + err = util.WriteJson(configPath, config) if err != nil { return nil, err } @@ -69,6 +75,14 @@ func createNewConfig(managementURL, adminURL, configPath, preSharedKey string) ( return config, nil } +func generateSSHKey() ([]byte, error) { + sshKey, err := ssh.GeneratePrivateKey(4096) + if err != nil { + return nil, err + } + return ssh.EncodePrivateKeyToPEM(sshKey), nil +} + func parseURL(serviceName, managementURL string) (*url.URL, error) { parsedMgmtURL, err := url.ParseRequestURI(managementURL) if err != nil { @@ -126,6 +140,14 @@ func ReadConfig(managementURL, adminURL, configPath string, preSharedKey *string config.PreSharedKey = *preSharedKey refresh = true } + if config.SSHKey == "" { + pem, err := generateSSHKey() + if err != nil { + return nil, err + } + config.SSHKey = string(pem) + refresh = true + } if refresh { // since we have new management URL, we need to update config file diff --git a/client/internal/connect.go b/client/internal/connect.go index fcf11f9c11e..44815516ccd 100644 --- a/client/internal/connect.go +++ b/client/internal/connect.go @@ -147,6 +147,7 @@ func createEngineConfig(key wgtypes.Key, config *Config, peerConfig *mgmProto.Pe IFaceBlackList: config.IFaceBlackList, WgPrivateKey: key, WgPort: iface.DefaultWgPort, + SSHKey: []byte(config.SSHKey), } if config.PreSharedKey != "" { diff --git a/client/internal/engine.go b/client/internal/engine.go index ca33852addd..382ea1612c4 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -3,6 +3,7 @@ package internal import ( "context" "fmt" + "github.com/netbirdio/netbird/client/ssh" "math/rand" "net" "strings" @@ -54,6 +55,8 @@ type EngineConfig struct { // UDPMuxSrflxPort default value 0 - the system will pick an available port UDPMuxSrflxPort int + + SSHKey []byte } // Engine is a mechanism responsible for reacting on Signal and Management stream events and managing connections to the remote peers. @@ -87,6 +90,8 @@ type Engine struct { // networkSerial is the latest CurrentSerial (state ID) of the network sent by the Management service networkSerial uint64 + + sshServer *ssh.Server } // Peer is an instance of the Connection Peer @@ -422,9 +427,43 @@ func (e *Engine) updateConfig(conf *mgmProto.PeerConfig) error { if err != nil { return err } + e.config.WgAddr = conf.Address log.Infof("updated peer address from %s to %s", oldAddr, conf.Address) } + if conf.GetSshConfig() != nil { + if conf.GetSshConfig().GetSshEnabled() { + // start SSH server if it wasn't running + if e.sshServer == nil { + //nil sshServer means it has not yet been started + + var err error + e.sshServer, err = ssh.NewSSHServer(e.config.SSHKey, + fmt.Sprintf("%s:%d", e.wgInterface.Address.IP.String(), 2222)) + if err != nil { + log.Warnf("failed creating SSH server %v", err) + } + go func() { + err = e.sshServer.Start() + if err != nil { + } + log.Infof("stopped SSH server") + }() + } else { + log.Debugf("SSH server is already running") + } + } else { + // stop SSH server if it was running + if e.sshServer != nil { + err := e.sshServer.Stop() + if err != nil { + log.Warnf("failed stopping SSH server %v", err) + } + e.sshServer = nil + } + } + } + return nil } @@ -515,6 +554,18 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error { if err != nil { return err } + + // update SSHServer by adding remote peer SSH keys + if e.sshServer != nil { + for _, config := range networkMap.GetRemotePeers() { + if config.GetSshConfig() != nil && config.GetSshConfig().GetSshPubKey() != nil { + err := e.sshServer.AddAuthorizedKey(string(config.GetSshConfig().GetSshPubKey())) + if err != nil { + log.Warnf("failed adding authroized key to SSH Server %v", err) + } + } + } + } } e.networkSerial = serial diff --git a/client/ssh/server.go b/client/ssh/server.go index 3449247fa4e..dd08908564c 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -18,27 +18,26 @@ type Server struct { } // NewSSHServer creates new server with provided host key -func NewSSHServer(hostKeyPEM []byte) (*Server, error) { - ln, err := net.Listen("tcp", ":2222") +func NewSSHServer(hostKeyPEM []byte, addr string) (*Server, error) { + ln, err := net.Listen("tcp", addr) if err != nil { return nil, err } - return &Server{listener: ln, mu: sync.Mutex{}, hostKeyPEM: hostKeyPEM}, nil + allowedKeys := make(map[string]ssh.PublicKey) + return &Server{listener: ln, mu: sync.Mutex{}, hostKeyPEM: hostKeyPEM, allowedKeys: allowedKeys}, nil } -func (srv *Server) UpdateKeys(newKeys []string) error { +// AddAuthorizedKey add given key as authorized key to the server +func (srv *Server) AddAuthorizedKey(newKey string) error { srv.mu.Lock() defer srv.mu.Unlock() - srv.allowedKeys = make(map[string]ssh.PublicKey, len(newKeys)) - for _, strKey := range newKeys { - parsedKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(strKey)) - if err != nil { - return err - } - srv.allowedKeys[strKey] = parsedKey + parsedKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(newKey)) + if err != nil { + return err } - + strKey := strings.TrimSpace(string(gossh.MarshalAuthorizedKey(parsedKey))) + srv.allowedKeys[strKey] = parsedKey return nil } diff --git a/management/server/grpcserver.go b/management/server/grpcserver.go index 9f642c3f575..96a46b5359f 100644 --- a/management/server/grpcserver.go +++ b/management/server/grpcserver.go @@ -365,7 +365,8 @@ func toWiretrusteeConfig(config *Config, turnCredentials *TURNCredentials) *prot func toPeerConfig(peer *Peer) *proto.PeerConfig { return &proto.PeerConfig{ - Address: fmt.Sprintf("%s/%d", peer.IP.String(), SubnetSize), // take it from the network + Address: fmt.Sprintf("%s/%d", peer.IP.String(), SubnetSize), // take it from the network + SshConfig: &proto.SSHConfig{SshEnabled: true}, //TODO REMOVE THIS HARDCODED VALUE } } @@ -375,6 +376,8 @@ func toRemotePeerConfig(peers []*Peer) []*proto.RemotePeerConfig { remotePeers = append(remotePeers, &proto.RemotePeerConfig{ WgPubKey: rPeer.Key, AllowedIps: []string{fmt.Sprintf(AllowedIPsFormat, rPeer.IP)}, + //TODO REMOVE THIS HARDCODED VALUE + SshConfig: &proto.SSHConfig{SshPubKey: []byte("dddd")}, }) } From e7547d085eaed3c3d8d901a25122c4f275b3c3fc Mon Sep 17 00:00:00 2001 From: braginini Date: Mon, 6 Jun 2022 17:16:29 +0200 Subject: [PATCH 04/51] Generate SSH key on startup and pass to Management --- client/internal/connect.go | 7 ++++--- client/internal/engine_test.go | 2 +- client/internal/login.go | 22 +++++++++++++++------- client/ssh/util.go | 15 +++++++++++++-- management/client/client.go | 4 ++-- management/client/client_test.go | 10 +++++----- management/client/grpc.go | 8 ++++---- management/client/mock.go | 12 ++++++------ management/server/grpcserver.go | 1 - 9 files changed, 50 insertions(+), 31 deletions(-) diff --git a/client/internal/connect.go b/client/internal/connect.go index 44815516ccd..fd10c4e427e 100644 --- a/client/internal/connect.go +++ b/client/internal/connect.go @@ -64,7 +64,8 @@ func RunClient(ctx context.Context, config *Config) error { defer cancel() // connect (just a connection, no stream yet) and login to Management Service to get an initial global Wiretrustee config - mgmClient, loginResp, err := connectToManagement(engineCtx, config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled) + mgmClient, loginResp, err := connectToManagement(engineCtx, config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled, + []byte(config.SSHKey)) if err != nil { log.Debug(err) if s, ok := status.FromError(err); ok && s.Code() == codes.PermissionDenied { @@ -180,7 +181,7 @@ func connectToSignal(ctx context.Context, wtConfig *mgmProto.WiretrusteeConfig, } // connectToManagement creates Management Services client, establishes a connection, logs-in and gets a global Wiretrustee config (signal, turn, stun hosts, etc) -func connectToManagement(ctx context.Context, managementAddr string, ourPrivateKey wgtypes.Key, tlsEnabled bool) (*mgm.GrpcClient, *mgmProto.LoginResponse, error) { +func connectToManagement(ctx context.Context, managementAddr string, ourPrivateKey wgtypes.Key, tlsEnabled bool, sshKey []byte) (*mgm.GrpcClient, *mgmProto.LoginResponse, error) { log.Debugf("connecting to Management Service %s", managementAddr) client, err := mgm.NewClient(ctx, managementAddr, ourPrivateKey, tlsEnabled) if err != nil { @@ -194,7 +195,7 @@ func connectToManagement(ctx context.Context, managementAddr string, ourPrivateK } sysInfo := system.GetInfo(ctx) - loginResp, err := client.Login(*serverPublicKey, sysInfo) + loginResp, err := client.Login(*serverPublicKey, sysInfo, sshKey) if err != nil { return nil, nil, err } diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index 6408259c1b9..1b499ecd25a 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -418,7 +418,7 @@ func createEngine(ctx context.Context, cancel context.CancelFunc, setupKey strin } info := system.GetInfo(ctx) - resp, err := mgmtClient.Register(*publicKey, setupKey, "", info) + resp, err := mgmtClient.Register(*publicKey, setupKey, "", info, nil) if err != nil { return nil, err } diff --git a/client/internal/login.go b/client/internal/login.go index 12c04ef21fe..d55457b70d2 100644 --- a/client/internal/login.go +++ b/client/internal/login.go @@ -2,8 +2,8 @@ package internal import ( "context" - "github.com/google/uuid" + "github.com/netbirdio/netbird/client/ssh" "github.com/netbirdio/netbird/client/system" mgm "github.com/netbirdio/netbird/management/client" mgmProto "github.com/netbirdio/netbird/management/proto" @@ -40,7 +40,15 @@ func Login(ctx context.Context, config *Config, setupKey string, jwtToken string return err } - _, err = loginPeer(ctx, *serverKey, mgmClient, setupKey, jwtToken) + key, err := ssh.DecodePrivateKeyFromPEM([]byte(config.SSHKey)) + if err != nil { + return err + } + publicKey, err := ssh.GeneratePublicKey(&key.PublicKey) + if err != nil { + return err + } + _, err = loginPeer(ctx, *serverKey, mgmClient, setupKey, jwtToken, publicKey) if err != nil { log.Errorf("failed logging-in peer on Management Service : %v", err) return err @@ -56,13 +64,13 @@ func Login(ctx context.Context, config *Config, setupKey string, jwtToken string } // loginPeer attempts to login to Management Service. If peer wasn't registered, tries the registration flow. -func loginPeer(ctx context.Context, serverPublicKey wgtypes.Key, client *mgm.GrpcClient, setupKey string, jwtToken string) (*mgmProto.LoginResponse, error) { +func loginPeer(ctx context.Context, serverPublicKey wgtypes.Key, client *mgm.GrpcClient, setupKey string, jwtToken string, sshKey []byte) (*mgmProto.LoginResponse, error) { sysInfo := system.GetInfo(ctx) - loginResp, err := client.Login(serverPublicKey, sysInfo) + loginResp, err := client.Login(serverPublicKey, sysInfo, sshKey) if err != nil { if s, ok := status.FromError(err); ok && s.Code() == codes.PermissionDenied { log.Debugf("peer registration required") - return registerPeer(ctx, serverPublicKey, client, setupKey, jwtToken) + return registerPeer(ctx, serverPublicKey, client, setupKey, jwtToken, sshKey) } else { return nil, err } @@ -75,7 +83,7 @@ func loginPeer(ctx context.Context, serverPublicKey wgtypes.Key, client *mgm.Grp // registerPeer checks whether setupKey was provided via cmd line and if not then it prompts user to enter a key. // Otherwise tries to register with the provided setupKey via command line. -func registerPeer(ctx context.Context, serverPublicKey wgtypes.Key, client *mgm.GrpcClient, setupKey string, jwtToken string) (*mgmProto.LoginResponse, error) { +func registerPeer(ctx context.Context, serverPublicKey wgtypes.Key, client *mgm.GrpcClient, setupKey string, jwtToken string, sshKey []byte) (*mgmProto.LoginResponse, error) { validSetupKey, err := uuid.Parse(setupKey) if err != nil && jwtToken == "" { return nil, status.Errorf(codes.InvalidArgument, "invalid setup-key or no sso information provided, err: %v", err) @@ -83,7 +91,7 @@ func registerPeer(ctx context.Context, serverPublicKey wgtypes.Key, client *mgm. log.Debugf("sending peer registration request to Management Service") info := system.GetInfo(ctx) - loginResp, err := client.Register(serverPublicKey, validSetupKey.String(), jwtToken, info) + loginResp, err := client.Register(serverPublicKey, validSetupKey.String(), jwtToken, info, sshKey) if err != nil { log.Errorf("failed registering peer %v,%s", err, validSetupKey.String()) return nil, err diff --git a/client/ssh/util.go b/client/ssh/util.go index 4998f911909..248c8f97183 100644 --- a/client/ssh/util.go +++ b/client/ssh/util.go @@ -5,6 +5,7 @@ import ( "crypto/rsa" "crypto/x509" "encoding/pem" + "fmt" gossh "golang.org/x/crypto/ssh" ) @@ -25,14 +26,24 @@ func GeneratePrivateKey(bitSize int) (*rsa.PrivateKey, error) { // GeneratePublicKey takes a rsa.PublicKey and return bytes suitable for writing to .pub file // returns the key in format format "ssh-rsa ..." -func GeneratePublicKey(privateKey *rsa.PublicKey) ([]byte, error) { - publicRsaKey, err := gossh.NewPublicKey(privateKey) +func GeneratePublicKey(key *rsa.PublicKey) ([]byte, error) { + publicRsaKey, err := gossh.NewPublicKey(key) if err != nil { return nil, err } return gossh.MarshalAuthorizedKey(publicRsaKey), nil } +func DecodePrivateKeyFromPEM(k []byte) (key *rsa.PrivateKey, err error) { + + block, _ := pem.Decode(k) + if block == nil { + return nil, fmt.Errorf("failed to parse PEM block containing the key") + } + + return x509.ParsePKCS1PrivateKey(block.Bytes) +} + // EncodePrivateKeyToPEM encodes Private Key from RSA to PEM format func EncodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte { // Get ASN.1 DER format diff --git a/management/client/client.go b/management/client/client.go index 1deea690ff3..3ca79d3b6be 100644 --- a/management/client/client.go +++ b/management/client/client.go @@ -12,7 +12,7 @@ type Client interface { io.Closer Sync(msgHandler func(msg *proto.SyncResponse) error) error GetServerPublicKey() (*wgtypes.Key, error) - Register(serverKey wgtypes.Key, setupKey string, jwtToken string, sysInfo *system.Info) (*proto.LoginResponse, error) - Login(serverKey wgtypes.Key, sysInfo *system.Info) (*proto.LoginResponse, error) + Register(serverKey wgtypes.Key, setupKey string, jwtToken string, sysInfo *system.Info, sshKey []byte) (*proto.LoginResponse, error) + Login(serverKey wgtypes.Key, sysInfo *system.Info, sshKey []byte) (*proto.LoginResponse, error) GetDeviceAuthorizationFlow(serverKey wgtypes.Key) (*proto.DeviceAuthorizationFlow, error) } diff --git a/management/client/client_test.go b/management/client/client_test.go index 02de07eb728..19421d19fde 100644 --- a/management/client/client_test.go +++ b/management/client/client_test.go @@ -158,7 +158,7 @@ func TestClient_LoginUnregistered_ShouldThrow_401(t *testing.T) { t.Fatal(err) } sysInfo := system.GetInfo(context.TODO()) - _, err = client.Login(*key, sysInfo) + _, err = client.Login(*key, sysInfo, nil) if err == nil { t.Error("expecting err on unregistered login, got nil") } @@ -186,7 +186,7 @@ func TestClient_LoginRegistered(t *testing.T) { t.Error(err) } info := system.GetInfo(context.TODO()) - resp, err := client.Register(*key, ValidKey, "", info) + resp, err := client.Register(*key, ValidKey, "", info, nil) if err != nil { t.Error(err) } @@ -216,7 +216,7 @@ func TestClient_Sync(t *testing.T) { } info := system.GetInfo(context.TODO()) - _, err = client.Register(*serverKey, ValidKey, "", info) + _, err = client.Register(*serverKey, ValidKey, "", info, nil) if err != nil { t.Error(err) } @@ -232,7 +232,7 @@ func TestClient_Sync(t *testing.T) { } info = system.GetInfo(context.TODO()) - _, err = remoteClient.Register(*serverKey, ValidKey, "", info) + _, err = remoteClient.Register(*serverKey, ValidKey, "", info, nil) if err != nil { t.Fatal(err) } @@ -330,7 +330,7 @@ func Test_SystemMetaDataFromClient(t *testing.T) { } info := system.GetInfo(context.TODO()) - _, err = testClient.Register(*key, ValidKey, "", info) + _, err = testClient.Register(*key, ValidKey, "", info, nil) if err != nil { t.Errorf("error while trying to register client: %v", err) } diff --git a/management/client/grpc.go b/management/client/grpc.go index cca1d0dd8a3..247c46a4951 100644 --- a/management/client/grpc.go +++ b/management/client/grpc.go @@ -233,13 +233,13 @@ func (c *GrpcClient) login(serverKey wgtypes.Key, req *proto.LoginRequest) (*pro // Register registers peer on Management Server. It actually calls a Login endpoint with a provided setup key // Takes care of encrypting and decrypting messages. // This method will also collect system info and send it with the request (e.g. hostname, os, etc) -func (c *GrpcClient) Register(serverKey wgtypes.Key, setupKey string, jwtToken string, sysInfo *system.Info) (*proto.LoginResponse, error) { - return c.login(serverKey, &proto.LoginRequest{SetupKey: setupKey, Meta: infoToMetaData(sysInfo), JwtToken: jwtToken}) +func (c *GrpcClient) Register(serverKey wgtypes.Key, setupKey string, jwtToken string, sysInfo *system.Info, sshKey []byte) (*proto.LoginResponse, error) { + return c.login(serverKey, &proto.LoginRequest{SetupKey: setupKey, Meta: infoToMetaData(sysInfo), JwtToken: jwtToken, SshPubKey: sshKey}) } // Login attempts login to Management Server. Takes care of encrypting and decrypting messages. -func (c *GrpcClient) Login(serverKey wgtypes.Key, sysInfo *system.Info) (*proto.LoginResponse, error) { - return c.login(serverKey, &proto.LoginRequest{Meta: infoToMetaData(sysInfo)}) +func (c *GrpcClient) Login(serverKey wgtypes.Key, sysInfo *system.Info, sshKey []byte) (*proto.LoginResponse, error) { + return c.login(serverKey, &proto.LoginRequest{Meta: infoToMetaData(sysInfo), SshPubKey: sshKey}) } // GetDeviceAuthorizationFlow returns a device authorization flow information. diff --git a/management/client/mock.go b/management/client/mock.go index 4d0d99149d1..f81b0b30c32 100644 --- a/management/client/mock.go +++ b/management/client/mock.go @@ -10,8 +10,8 @@ type MockClient struct { CloseFunc func() error SyncFunc func(msgHandler func(msg *proto.SyncResponse) error) error GetServerPublicKeyFunc func() (*wgtypes.Key, error) - RegisterFunc func(serverKey wgtypes.Key, setupKey string, jwtToken string, info *system.Info) (*proto.LoginResponse, error) - LoginFunc func(serverKey wgtypes.Key, info *system.Info) (*proto.LoginResponse, error) + RegisterFunc func(serverKey wgtypes.Key, setupKey string, jwtToken string, info *system.Info, sshKey []byte) (*proto.LoginResponse, error) + LoginFunc func(serverKey wgtypes.Key, info *system.Info, sshKey []byte) (*proto.LoginResponse, error) GetDeviceAuthorizationFlowFunc func(serverKey wgtypes.Key) (*proto.DeviceAuthorizationFlow, error) } @@ -36,18 +36,18 @@ func (m *MockClient) GetServerPublicKey() (*wgtypes.Key, error) { return m.GetServerPublicKeyFunc() } -func (m *MockClient) Register(serverKey wgtypes.Key, setupKey string, jwtToken string, info *system.Info) (*proto.LoginResponse, error) { +func (m *MockClient) Register(serverKey wgtypes.Key, setupKey string, jwtToken string, info *system.Info, sshKey []byte) (*proto.LoginResponse, error) { if m.RegisterFunc == nil { return nil, nil } - return m.RegisterFunc(serverKey, setupKey, jwtToken, info) + return m.RegisterFunc(serverKey, setupKey, jwtToken, info, sshKey) } -func (m *MockClient) Login(serverKey wgtypes.Key, info *system.Info) (*proto.LoginResponse, error) { +func (m *MockClient) Login(serverKey wgtypes.Key, info *system.Info, sshKey []byte) (*proto.LoginResponse, error) { if m.LoginFunc == nil { return nil, nil } - return m.LoginFunc(serverKey, info) + return m.LoginFunc(serverKey, info, sshKey) } func (m *MockClient) GetDeviceAuthorizationFlow(serverKey wgtypes.Key) (*proto.DeviceAuthorizationFlow, error) { diff --git a/management/server/grpcserver.go b/management/server/grpcserver.go index 96a46b5359f..c4ad599a6da 100644 --- a/management/server/grpcserver.go +++ b/management/server/grpcserver.go @@ -380,7 +380,6 @@ func toRemotePeerConfig(peers []*Peer) []*proto.RemotePeerConfig { SshConfig: &proto.SSHConfig{SshPubKey: []byte("dddd")}, }) } - return remotePeers } From e06308667ea6d397d6d0ecdc607fc21e0ec6e779 Mon Sep 17 00:00:00 2001 From: braginini Date: Mon, 6 Jun 2022 17:39:51 +0200 Subject: [PATCH 05/51] Update SSH key on management --- client/internal/connect.go | 12 ++++++++++-- management/server/account.go | 1 + management/server/grpcserver.go | 11 +++++++++-- management/server/peer.go | 28 ++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/client/internal/connect.go b/client/internal/connect.go index fd10c4e427e..4d285db3b34 100644 --- a/client/internal/connect.go +++ b/client/internal/connect.go @@ -2,6 +2,7 @@ package internal import ( "context" + "github.com/netbirdio/netbird/client/ssh" "time" "github.com/netbirdio/netbird/client/system" @@ -62,10 +63,17 @@ func RunClient(ctx context.Context, config *Config) error { engineCtx, cancel := context.WithCancel(ctx) defer cancel() - + key, err := ssh.DecodePrivateKeyFromPEM([]byte(config.SSHKey)) + if err != nil { + return err + } + publicKey, err := ssh.GeneratePublicKey(&key.PublicKey) + if err != nil { + return err + } // connect (just a connection, no stream yet) and login to Management Service to get an initial global Wiretrustee config mgmClient, loginResp, err := connectToManagement(engineCtx, config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled, - []byte(config.SSHKey)) + publicKey) if err != nil { log.Debug(err) if s, ok := status.FromError(err); ok && s.Code() == codes.PermissionDenied { diff --git a/management/server/account.go b/management/server/account.go index cb8c3932e6e..3970b99ead1 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -53,6 +53,7 @@ type AccountManager interface { GetNetworkMap(peerKey string) (*NetworkMap, error) AddPeer(setupKey string, userId string, peer *Peer) (*Peer, error) UpdatePeerMeta(peerKey string, meta PeerSystemMeta) error + UpdatePeerSSHKey(peerKey string, sshKey string) error GetUsersFromAccount(accountId string) ([]*UserInfo, error) GetGroup(accountId, groupID string) (*Group, error) SaveGroup(accountId string, group *Group) error diff --git a/management/server/grpcserver.go b/management/server/grpcserver.go index c4ad599a6da..83d238be822 100644 --- a/management/server/grpcserver.go +++ b/management/server/grpcserver.go @@ -290,6 +290,14 @@ func (s *Server) Login(ctx context.Context, req *proto.EncryptedMessage) (*proto return nil, status.Error(codes.Internal, "internal server error") } } + + if loginReq.GetSshPubKey() != nil { + err = s.accountManager.UpdatePeerSSHKey(peerKey.String(), string(loginReq.GetSshPubKey())) + if err != nil { + return nil, err + } + } + // if peer has reached this point then it has logged in loginResp := &proto.LoginResponse{ WiretrusteeConfig: toWiretrusteeConfig(s.config, nil), @@ -376,8 +384,7 @@ func toRemotePeerConfig(peers []*Peer) []*proto.RemotePeerConfig { remotePeers = append(remotePeers, &proto.RemotePeerConfig{ WgPubKey: rPeer.Key, AllowedIps: []string{fmt.Sprintf(AllowedIPsFormat, rPeer.IP)}, - //TODO REMOVE THIS HARDCODED VALUE - SshConfig: &proto.SSHConfig{SshPubKey: []byte("dddd")}, + SshConfig: &proto.SSHConfig{SshPubKey: []byte(rPeer.SSHKey)}, }) } return remotePeers diff --git a/management/server/peer.go b/management/server/peer.go index e76f2b067ec..2c1972cc757 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -47,6 +47,8 @@ type Peer struct { Status *PeerStatus // The user ID that registered the peer UserID string + // SSHKey is a public SSH key of the peer + SSHKey string } // Copy copies Peer object @@ -59,6 +61,7 @@ func (p *Peer) Copy() *Peer { Name: p.Name, Status: p.Status, UserID: p.UserID, + SSHKey: p.SSHKey, } } @@ -315,6 +318,31 @@ func (am *DefaultAccountManager) AddPeer( return newPeer, nil } +// UpdatePeerSSHKey updates peer's public SSH key +func (am *DefaultAccountManager) UpdatePeerSSHKey(peerKey string, sshKey string) error { + am.mux.Lock() + defer am.mux.Unlock() + + peer, err := am.Store.GetPeer(peerKey) + if err != nil { + return err + } + + account, err := am.Store.GetPeerAccount(peerKey) + if err != nil { + return err + } + + peerCopy := peer.Copy() + peerCopy.SSHKey = sshKey + + err = am.Store.SavePeer(account.Id, peerCopy) + if err != nil { + return err + } + return nil +} + // UpdatePeerMeta updates peer's system metadata func (am *DefaultAccountManager) UpdatePeerMeta(peerKey string, meta PeerSystemMeta) error { am.mux.Lock() From 6557a559188fa991aed395892e92843ae842fbda Mon Sep 17 00:00:00 2001 From: braginini Date: Mon, 6 Jun 2022 19:16:05 +0200 Subject: [PATCH 06/51] Add PTY to SSHServer --- client/ssh/server.go | 41 ++++++++++++++++++++++++++++++++++++++--- go.mod | 1 + go.sum | 3 +++ 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/client/ssh/server.go b/client/ssh/server.go index dd08908564c..7357b0f793c 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -2,14 +2,19 @@ package ssh import ( "fmt" + "github.com/creack/pty" "github.com/gliderlabs/ssh" gossh "golang.org/x/crypto/ssh" "io" "net" + "os" + "os/exec" "strings" "sync" ) +const DefaultShell = "sh" + type Server struct { listener net.Listener allowedKeys map[string]ssh.PublicKey @@ -50,12 +55,42 @@ func (srv *Server) Stop() error { return nil } +/*func setWinSize(f *os.File, w, h int) { + syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ), + uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0}))) +}*/ + // Start starts SSH server. Blocking func (srv *Server) Start() error { handler := func(s ssh.Session) { - authorizedKey := gossh.MarshalAuthorizedKey(s.PublicKey()) - io.WriteString(s, fmt.Sprintf("public key used by %s:\n", s.User())) - s.Write(authorizedKey) + var shell string + shell = os.Getenv("SHELL") + if shell == "" { + shell = DefaultShell + } + cmd := exec.Command(shell) + //cmd.Env = []string{"TERM=xterm"} + ptyReq, _, isPty := s.Pty() + if isPty { + cmd.Env = append(cmd.Env, fmt.Sprintf("TERM=%s", ptyReq.Term)) + f, err := pty.Start(cmd) + if err != nil { + panic(err) + } + /*go func() { + for win := range winCh { + setWinSize(f, win.Width, win.Height) + } + }()*/ + go func() { + io.Copy(f, s) // stdin + }() + io.Copy(s, f) // stdout + cmd.Wait() + } else { + io.WriteString(s, "No PTY requested.\n") + s.Exit(1) + } } publicKeyOption := ssh.PublicKeyAuth(func(ctx ssh.Context, key ssh.PublicKey) bool { diff --git a/go.mod b/go.mod index 929583f8022..1e5f5be6dc7 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( require ( fyne.io/fyne/v2 v2.1.4 github.com/c-robinson/iplib v1.0.3 + github.com/creack/pty v1.1.18 github.com/eko/gocache/v2 v2.3.1 github.com/getlantern/systray v1.2.1 github.com/gliderlabs/ssh v0.3.4 diff --git a/go.sum b/go.sum index db38c714809..bb02cc71c7c 100644 --- a/go.sum +++ b/go.sum @@ -119,7 +119,10 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= From f5074283f97cef9874eaa2dd6512eea15b827adf Mon Sep 17 00:00:00 2001 From: braginini Date: Tue, 7 Jun 2022 10:12:58 +0200 Subject: [PATCH 07/51] Cleanup SSH code a bit --- client/internal/engine.go | 66 +++++++++++++----------- client/ssh/server.go | 104 +++++++++++++++++++++----------------- 2 files changed, 96 insertions(+), 74 deletions(-) diff --git a/client/internal/engine.go b/client/internal/engine.go index 382ea1612c4..80961ae7298 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -419,6 +419,40 @@ func (e *Engine) handleSync(update *mgmProto.SyncResponse) error { return nil } +func (e *Engine) updateSSH(sshConf *mgmProto.SSHConfig) error { + if sshConf.GetSshEnabled() { + // start SSH server if it wasn't running + if e.sshServer == nil { + //nil sshServer means it has not yet been started + var err error + e.sshServer, err = ssh.NewSSHServer(e.config.SSHKey, + fmt.Sprintf("%s:%d", e.wgInterface.Address.IP.String(), 2222)) + if err != nil { + return err + } + go func() { + err = e.sshServer.Start() + if err != nil { + // will throw error when we stop it even if it is a graceful stop + } + log.Infof("stopped SSH server") + }() + } else { + log.Debugf("SSH server is already running") + } + } else { + // stop SSH server if it was running + if e.sshServer != nil { + err := e.sshServer.Stop() + if err != nil { + log.Warnf("failed stopping SSH server %v", err) + } + e.sshServer = nil + } + } + return nil +} + func (e *Engine) updateConfig(conf *mgmProto.PeerConfig) error { if e.wgInterface.Address.String() != conf.Address { oldAddr := e.wgInterface.Address.String() @@ -432,35 +466,9 @@ func (e *Engine) updateConfig(conf *mgmProto.PeerConfig) error { } if conf.GetSshConfig() != nil { - if conf.GetSshConfig().GetSshEnabled() { - // start SSH server if it wasn't running - if e.sshServer == nil { - //nil sshServer means it has not yet been started - - var err error - e.sshServer, err = ssh.NewSSHServer(e.config.SSHKey, - fmt.Sprintf("%s:%d", e.wgInterface.Address.IP.String(), 2222)) - if err != nil { - log.Warnf("failed creating SSH server %v", err) - } - go func() { - err = e.sshServer.Start() - if err != nil { - } - log.Infof("stopped SSH server") - }() - } else { - log.Debugf("SSH server is already running") - } - } else { - // stop SSH server if it was running - if e.sshServer != nil { - err := e.sshServer.Stop() - if err != nil { - log.Warnf("failed stopping SSH server %v", err) - } - e.sshServer = nil - } + err := e.updateSSH(conf.GetSshConfig()) + if err != nil { + log.Warnf("failed handling SSH setup %v", e) } } diff --git a/client/ssh/server.go b/client/ssh/server.go index 7357b0f793c..09e7da12908 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/creack/pty" "github.com/gliderlabs/ssh" + log "github.com/sirupsen/logrus" gossh "golang.org/x/crypto/ssh" "io" "net" @@ -11,9 +12,18 @@ import ( "os/exec" "strings" "sync" + "syscall" + "unsafe" ) -const DefaultShell = "sh" +var shell string + +func init() { + shell = os.Getenv("SHELL") + if shell == "" { + shell = "sh" + } +} type Server struct { listener net.Listener @@ -55,64 +65,68 @@ func (srv *Server) Stop() error { return nil } -/*func setWinSize(f *os.File, w, h int) { - syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ), - uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0}))) -}*/ +func (srv *Server) publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool { + srv.mu.Lock() + defer srv.mu.Unlock() -// Start starts SSH server. Blocking -func (srv *Server) Start() error { - handler := func(s ssh.Session) { - var shell string - shell = os.Getenv("SHELL") - if shell == "" { - shell = DefaultShell - } - cmd := exec.Command(shell) - //cmd.Env = []string{"TERM=xterm"} - ptyReq, _, isPty := s.Pty() - if isPty { - cmd.Env = append(cmd.Env, fmt.Sprintf("TERM=%s", ptyReq.Term)) - f, err := pty.Start(cmd) - if err != nil { - panic(err) - } - /*go func() { - for win := range winCh { - setWinSize(f, win.Width, win.Height) - } - }()*/ - go func() { - io.Copy(f, s) // stdin - }() - io.Copy(s, f) // stdout - cmd.Wait() - } else { - io.WriteString(s, "No PTY requested.\n") - s.Exit(1) + k := strings.TrimSpace(string(gossh.MarshalAuthorizedKey(key))) + if allowed, ok := srv.allowedKeys[k]; ok { + if ssh.KeysEqual(allowed, key) { + return true } } - publicKeyOption := ssh.PublicKeyAuth(func(ctx ssh.Context, key ssh.PublicKey) bool { - srv.mu.Lock() - defer srv.mu.Unlock() + return false +} - k := strings.TrimSpace(string(gossh.MarshalAuthorizedKey(key))) - if allowed, ok := srv.allowedKeys[k]; ok { - if ssh.KeysEqual(allowed, key) { - return true +// sessionHandler handles SSH session post auth +func (srv *Server) sessionHandler(s ssh.Session) { + ptyReq, winCh, isPty := s.Pty() + if isPty { + cmd := exec.Command(shell) + cmd.Env = append(cmd.Env, fmt.Sprintf("TERM=%s", ptyReq.Term)) + f, err := pty.Start(cmd) + if err != nil { + panic(err) + } + go func() { + for win := range winCh { + setWinSize(f, win.Width, win.Height) } + }() + go func() { + io.Copy(f, s) // stdin + }() + go func() { + io.Copy(s, f) // stdout + }() + + err = cmd.Wait() + if err != nil { + return } + } else { + io.WriteString(s, "No PTY requested.\n") + s.Exit(1) + } +} - return false - }) +// Start starts SSH server. Blocking +func (srv *Server) Start() error { + log.Debugf("starting SSH server") + publicKeyOption := ssh.PublicKeyAuth(srv.publicKeyHandler) hostKeyPEM := ssh.HostKeyPEM(srv.hostKeyPEM) - err := ssh.Serve(srv.listener, handler, publicKeyOption, hostKeyPEM) + err := ssh.Serve(srv.listener, srv.sessionHandler, publicKeyOption, hostKeyPEM) if err != nil { return err } return nil } + +func setWinSize(f *os.File, w, h int) { + syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ), + uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0}))) +} From 4bdeceb1133babf27638ad293d6a20ec64273481 Mon Sep 17 00:00:00 2001 From: braginini Date: Sun, 12 Jun 2022 11:08:25 +0200 Subject: [PATCH 08/51] Add SSH cmd --- client/cmd/root.go | 1 + client/cmd/ssh.go | 100 ++++++++++++++++++++++++++++++++++++ client/ssh/client.go | 119 +++++++++++++++++++++++++++++++++++++++++++ client/ssh/server.go | 2 +- go.mod | 5 +- go.sum | 6 +++ 6 files changed, 230 insertions(+), 3 deletions(-) create mode 100644 client/cmd/ssh.go create mode 100644 client/ssh/client.go diff --git a/client/cmd/root.go b/client/cmd/root.go index 9a02dcc4434..26e9a76bfd4 100644 --- a/client/cmd/root.go +++ b/client/cmd/root.go @@ -94,6 +94,7 @@ func init() { rootCmd.AddCommand(statusCmd) rootCmd.AddCommand(loginCmd) rootCmd.AddCommand(versionCmd) + rootCmd.AddCommand(sshCmd) serviceCmd.AddCommand(runCmd, startCmd, stopCmd, restartCmd) // service control commands are subcommands of service serviceCmd.AddCommand(installCmd, uninstallCmd) // service installer commands are subcommands of service } diff --git a/client/cmd/ssh.go b/client/cmd/ssh.go new file mode 100644 index 00000000000..32cf2e1c15b --- /dev/null +++ b/client/cmd/ssh.go @@ -0,0 +1,100 @@ +package cmd + +import ( + "context" + "fmt" + "github.com/netbirdio/netbird/client/internal" + "github.com/netbirdio/netbird/client/proto" + nbssh "github.com/netbirdio/netbird/client/ssh" + "github.com/netbirdio/netbird/util" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "os" + "os/signal" + "syscall" +) + +var sshCmd = &cobra.Command{ + Use: "ssh", + Short: "connect to a remote SSH server", + RunE: func(cmd *cobra.Command, args []string) error { + SetFlagsFromEnvVars() + + cmd.SetOut(cmd.OutOrStdout()) + + err := util.InitLog(logLevel, "console") + if err != nil { + return fmt.Errorf("failed initializing log %v", err) + } + + ctx := internal.CtxInitState(cmd.Context()) + + conn, err := DialClientGRPCServer(ctx, daemonAddr) + if err != nil { + return fmt.Errorf("failed to connect to daemon error: %v\n"+ + "If the daemon is not running please run: "+ + "\nnetbird service install \nnetbird service start\n", err) + } + + defer func() { + err := conn.Close() + if err != nil { + log.Warnf("failed closing dameon gRPC client connection %v", err) + return + } + }() + client := proto.NewDaemonServiceClient(conn) + + status, err := client.Status(ctx, &proto.StatusRequest{}) + if err != nil { + return fmt.Errorf("unable to get daemon status: %v", err) + } + + if status.Status != string(internal.StatusConnected) { + // todo maybe automatically start it? + cmd.Printf("You are disconnected from the NetBird network. Please run the UP command first to connect: \n\n" + + " netbird up \n\n") + return nil + } + + sig := make(chan os.Signal, 1) + signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT) + sshctx, cancel := context.WithCancel(ctx) + + go func() { + if err := runSSH(sshctx); err != nil { + log.Print(err) + } + cancel() + }() + + select { + case <-sig: + cancel() + case <-sshctx.Done(): + } + + return nil + }, +} + +func runSSH(ctx context.Context) error { + c, err := nbssh.DialWithKeyFile("128.199.61.79:43767", "root", "/home/braginini/.ssh/id_rsa") + if err != nil { + return err + } + go func() { + <-ctx.Done() + err = c.Close() + if err != nil { + return + } + }() + + err = c.OpenTerminal() + if err != nil { + return err + } + + return nil +} diff --git a/client/ssh/client.go b/client/ssh/client.go new file mode 100644 index 00000000000..68063837444 --- /dev/null +++ b/client/ssh/client.go @@ -0,0 +1,119 @@ +package ssh + +import ( + "fmt" + "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/terminal" + "io/ioutil" + "net" + "os" +) + +// Client wraps crypto/ssh Client to simplify usage +type Client struct { + client *ssh.Client +} + +// Close closes the wrapped SSH Client +func (c *Client) Close() error { + return c.client.Close() +} + +// OpenTerminal starts interactive terminal session with the remote SSH server +func (c *Client) OpenTerminal() error { + session, err := c.client.NewSession() + if err != nil { + return fmt.Errorf("failed to open new session: %v", err) + } + defer func() { + err := session.Close() + if err != nil { + return + } + }() + + fd := int(os.Stdin.Fd()) + state, err := terminal.MakeRaw(fd) + if err != nil { + return fmt.Errorf("failed to make terminal raw: %s", err) + } + defer func() { + err := terminal.Restore(fd, state) + if err != nil { + return + } + }() + + w, h, err := terminal.GetSize(fd) + if err != nil { + return fmt.Errorf("terminal get size: %s", err) + } + + modes := ssh.TerminalModes{ + ssh.ECHO: 1, + ssh.TTY_OP_ISPEED: 14400, + ssh.TTY_OP_OSPEED: 14400, + } + + term := os.Getenv("TERM") + if term == "" { + term = "xterm-256color" + } + if err := session.RequestPty(term, h, w, modes); err != nil { + return fmt.Errorf("failed requesting pty session with xterm: %s", err) + } + + session.Stdout = os.Stdout + session.Stderr = os.Stderr + session.Stdin = os.Stdin + + if err := session.Shell(); err != nil { + return fmt.Errorf("failed to start login shell on the remote host: %s", err) + } + + if err := session.Wait(); err != nil { + if e, ok := err.(*ssh.ExitError); ok { + switch e.ExitStatus() { + case 130: + return nil + } + } + return fmt.Errorf("failed running SSH session: %s", err) + } + + return nil +} + +// DialWithKeyFile reads and parses the provided SSH key file, and connects to the remote SSH server +func DialWithKeyFile(addr, user, keyfile string) (*Client, error) { + key, err := ioutil.ReadFile(keyfile) + if err != nil { + return nil, err + } + + signer, err := ssh.ParsePrivateKey(key) + if err != nil { + return nil, err + } + + config := &ssh.ClientConfig{ + User: user, + Auth: []ssh.AuthMethod{ + ssh.PublicKeys(signer), + }, + HostKeyCallback: ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error { return nil }), + } + + return Dial("tcp", addr, config) +} + +// Dial connects to the remote SSH server. +func Dial(network, addr string, config *ssh.ClientConfig) (*Client, error) { + client, err := ssh.Dial(network, addr, config) + if err != nil { + return nil, err + } + return &Client{ + client: client, + }, nil +} diff --git a/client/ssh/server.go b/client/ssh/server.go index 09e7da12908..f563321f37b 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -106,7 +106,7 @@ func (srv *Server) sessionHandler(s ssh.Session) { return } } else { - io.WriteString(s, "No PTY requested.\n") + io.WriteString(s, "Only PTY is supported.\n") s.Exit(1) } } diff --git a/go.mod b/go.mod index 1e5f5be6dc7..7a926183bab 100644 --- a/go.mod +++ b/go.mod @@ -17,8 +17,8 @@ require ( github.com/spf13/cobra v1.3.0 github.com/spf13/pflag v1.0.5 github.com/vishvananda/netlink v1.1.0 - golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 - golang.org/x/sys v0.0.0-20220412211240-33da011f77ad + golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a golang.zx2c4.com/wireguard v0.0.0-20211209221555-9c9e7e272434 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de golang.zx2c4.com/wireguard/windows v0.5.1 @@ -96,6 +96,7 @@ require ( golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/net v0.0.0-20220412020605-290c469a71a5 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 // indirect golang.org/x/tools v0.1.10 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect diff --git a/go.sum b/go.sum index bb02cc71c7c..4330b426330 100644 --- a/go.sum +++ b/go.sum @@ -650,12 +650,15 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 h1:71vQrMauZZhcTVK6KdYM+rklehEEwb3E+ZhaE5jrPrE= golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -897,6 +900,9 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= From bf69b1c61ad9c09caaa45e1b8028ff2b71eb341f Mon Sep 17 00:00:00 2001 From: braginini Date: Sun, 12 Jun 2022 12:11:03 +0200 Subject: [PATCH 09/51] Add SSH cmd port flag --- client/cmd/ssh.go | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/client/cmd/ssh.go b/client/cmd/ssh.go index 32cf2e1c15b..9aeaa217700 100644 --- a/client/cmd/ssh.go +++ b/client/cmd/ssh.go @@ -2,6 +2,7 @@ package cmd import ( "context" + "errors" "fmt" "github.com/netbirdio/netbird/client/internal" "github.com/netbirdio/netbird/client/proto" @@ -14,8 +15,19 @@ import ( "syscall" ) +var ( + port int + user = "netbird" +) + var sshCmd = &cobra.Command{ - Use: "ssh", + Use: "ssh", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("requires a host argument") + } + return nil + }, Short: "connect to a remote SSH server", RunE: func(cmd *cobra.Command, args []string) error { SetFlagsFromEnvVars() @@ -62,7 +74,7 @@ var sshCmd = &cobra.Command{ sshctx, cancel := context.WithCancel(ctx) go func() { - if err := runSSH(sshctx); err != nil { + if err := runSSH(args[0], sshctx); err != nil { log.Print(err) } cancel() @@ -78,8 +90,8 @@ var sshCmd = &cobra.Command{ }, } -func runSSH(ctx context.Context) error { - c, err := nbssh.DialWithKeyFile("128.199.61.79:43767", "root", "/home/braginini/.ssh/id_rsa") +func runSSH(addr string, ctx context.Context) error { + c, err := nbssh.DialWithKeyFile(fmt.Sprintf("%s:%d", addr, port), user, "") if err != nil { return err } @@ -98,3 +110,7 @@ func runSSH(ctx context.Context) error { return nil } + +func init() { + sshCmd.PersistentFlags().IntVarP(&port, "port", "p", 2222, "set remote SSH port. Defaults to NetBird's 2222") +} From 71e82b74859ddd201d3eb53a5b27c3836d8daa2e Mon Sep 17 00:00:00 2001 From: braginini Date: Sun, 12 Jun 2022 13:30:04 +0200 Subject: [PATCH 10/51] Use ED25519 keys --- client/internal/config.go | 21 ++++-------- client/internal/connect.go | 7 ++-- client/internal/login.go | 6 +--- client/ssh/util.go | 68 +++++++++++++++++++++++--------------- 4 files changed, 51 insertions(+), 51 deletions(-) diff --git a/client/internal/config.go b/client/internal/config.go index 1bbcad7d8e5..f0f16dd5dce 100644 --- a/client/internal/config.go +++ b/client/internal/config.go @@ -4,16 +4,15 @@ import ( "context" "fmt" "github.com/netbirdio/netbird/client/ssh" + "github.com/netbirdio/netbird/iface" mgm "github.com/netbirdio/netbird/management/client" + "github.com/netbirdio/netbird/util" + log "github.com/sirupsen/logrus" + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "net/url" "os" - - "github.com/netbirdio/netbird/iface" - "github.com/netbirdio/netbird/util" - log "github.com/sirupsen/logrus" - "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) var managementURLDefault *url.URL @@ -45,7 +44,7 @@ type Config struct { // createNewConfig creates a new config generating a new Wireguard key and saving to file func createNewConfig(managementURL, adminURL, configPath, preSharedKey string) (*Config, error) { wgKey := generateKey() - pem, err := generateSSHKey() + pem, err := ssh.GeneratePrivateKey(ssh.ED25519) if err != nil { return nil, err } @@ -75,14 +74,6 @@ func createNewConfig(managementURL, adminURL, configPath, preSharedKey string) ( return config, nil } -func generateSSHKey() ([]byte, error) { - sshKey, err := ssh.GeneratePrivateKey(4096) - if err != nil { - return nil, err - } - return ssh.EncodePrivateKeyToPEM(sshKey), nil -} - func parseURL(serviceName, managementURL string) (*url.URL, error) { parsedMgmtURL, err := url.ParseRequestURI(managementURL) if err != nil { @@ -141,7 +132,7 @@ func ReadConfig(managementURL, adminURL, configPath string, preSharedKey *string refresh = true } if config.SSHKey == "" { - pem, err := generateSSHKey() + pem, err := ssh.GeneratePrivateKey(ssh.ED25519) if err != nil { return nil, err } diff --git a/client/internal/connect.go b/client/internal/connect.go index 4d285db3b34..85975869e18 100644 --- a/client/internal/connect.go +++ b/client/internal/connect.go @@ -63,11 +63,8 @@ func RunClient(ctx context.Context, config *Config) error { engineCtx, cancel := context.WithCancel(ctx) defer cancel() - key, err := ssh.DecodePrivateKeyFromPEM([]byte(config.SSHKey)) - if err != nil { - return err - } - publicKey, err := ssh.GeneratePublicKey(&key.PublicKey) + + publicKey, err := ssh.GeneratePublicKey([]byte(config.SSHKey)) if err != nil { return err } diff --git a/client/internal/login.go b/client/internal/login.go index d55457b70d2..7d146818809 100644 --- a/client/internal/login.go +++ b/client/internal/login.go @@ -40,11 +40,7 @@ func Login(ctx context.Context, config *Config, setupKey string, jwtToken string return err } - key, err := ssh.DecodePrivateKeyFromPEM([]byte(config.SSHKey)) - if err != nil { - return err - } - publicKey, err := ssh.GeneratePublicKey(&key.PublicKey) + publicKey, err := ssh.GeneratePublicKey([]byte(config.SSHKey)) if err != nil { return err } diff --git a/client/ssh/util.go b/client/ssh/util.go index 248c8f97183..7b4e86e7b9d 100644 --- a/client/ssh/util.go +++ b/client/ssh/util.go @@ -1,63 +1,79 @@ package ssh import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/pem" "fmt" + "golang.org/x/crypto/ed25519" gossh "golang.org/x/crypto/ssh" + "strings" ) +type KeyType string + +const ED25519 KeyType = "ed25519" +const ECDSA KeyType = "ecdsa" +const RSA KeyType = "rsa" + +const RSAKeySize = 2048 + // GeneratePrivateKey creates RSA Private Key of specified byte size -func GeneratePrivateKey(bitSize int) (*rsa.PrivateKey, error) { - privateKey, err := rsa.GenerateKey(rand.Reader, bitSize) +func GeneratePrivateKey(t KeyType) ([]byte, error) { + + var key crypto.Signer + var err error + switch t { + case ED25519: + _, key, err = ed25519.GenerateKey(rand.Reader) + case ECDSA: + key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + case RSA: + key, err = rsa.GenerateKey(rand.Reader, RSAKeySize) + default: + return nil, fmt.Errorf("unsupported ket type %s", t) + } if err != nil { return nil, err } - err = privateKey.Validate() + pemBytes, err := EncodePrivateKeyToPEM(key) if err != nil { return nil, err } - return privateKey, nil + return pemBytes, nil } -// GeneratePublicKey takes a rsa.PublicKey and return bytes suitable for writing to .pub file -// returns the key in format format "ssh-rsa ..." -func GeneratePublicKey(key *rsa.PublicKey) ([]byte, error) { - publicRsaKey, err := gossh.NewPublicKey(key) +// GeneratePublicKey returns the public part of the private key +func GeneratePublicKey(key []byte) ([]byte, error) { + signer, err := gossh.ParsePrivateKey(key) if err != nil { return nil, err } - return gossh.MarshalAuthorizedKey(publicRsaKey), nil -} - -func DecodePrivateKeyFromPEM(k []byte) (key *rsa.PrivateKey, err error) { - block, _ := pem.Decode(k) - if block == nil { - return nil, fmt.Errorf("failed to parse PEM block containing the key") - } - - return x509.ParsePKCS1PrivateKey(block.Bytes) + strKey := strings.TrimSpace(string(gossh.MarshalAuthorizedKey(signer.PublicKey()))) + return []byte(strKey), nil } // EncodePrivateKeyToPEM encodes Private Key from RSA to PEM format -func EncodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte { - // Get ASN.1 DER format - privDER := x509.MarshalPKCS1PrivateKey(privateKey) +func EncodePrivateKeyToPEM(privateKey crypto.Signer) ([]byte, error) { + mk, err := x509.MarshalPKCS8PrivateKey(privateKey) + if err != nil { + return nil, err + } // pem.Block privBlock := pem.Block{ - Type: "RSA PRIVATE KEY", - Headers: nil, - Bytes: privDER, + Type: "PRIVATE KEY", + Bytes: mk, } // Private key in PEM format privatePEM := pem.EncodeToMemory(&privBlock) - - return privatePEM + return privatePEM, nil } From 414fafad05b7dbb8cac9d8e3b16bfbd60757654a Mon Sep 17 00:00:00 2001 From: braginini Date: Sun, 12 Jun 2022 13:48:50 +0200 Subject: [PATCH 11/51] Fix build on windows --- client/ssh/server.go | 7 ------- client/ssh/window_unix.go | 14 ++++++++++++++ client/ssh/window_windows.go | 9 +++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 client/ssh/window_unix.go create mode 100644 client/ssh/window_windows.go diff --git a/client/ssh/server.go b/client/ssh/server.go index f563321f37b..2534c84a24a 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -12,8 +12,6 @@ import ( "os/exec" "strings" "sync" - "syscall" - "unsafe" ) var shell string @@ -125,8 +123,3 @@ func (srv *Server) Start() error { return nil } - -func setWinSize(f *os.File, w, h int) { - syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ), - uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0}))) -} diff --git a/client/ssh/window_unix.go b/client/ssh/window_unix.go new file mode 100644 index 00000000000..368c6f06557 --- /dev/null +++ b/client/ssh/window_unix.go @@ -0,0 +1,14 @@ +//go:build linux || darwin + +package ssh + +import ( + "os" + "syscall" + "unsafe" +) + +func setWinSize(f *os.File, w, h int) { + syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ), + uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0}))) +} diff --git a/client/ssh/window_windows.go b/client/ssh/window_windows.go new file mode 100644 index 00000000000..49f28450a7e --- /dev/null +++ b/client/ssh/window_windows.go @@ -0,0 +1,9 @@ +package ssh + +import ( + "os" +) + +func setWinSize(f *os.File, w, h int) { + +} From c53e3b13d3a17ce931a12cb47ad67b8296524f7d Mon Sep 17 00:00:00 2001 From: braginini Date: Sun, 12 Jun 2022 14:12:25 +0200 Subject: [PATCH 12/51] Use local SSH key to connect to the remote NetBird SSH server --- client/cmd/ssh.go | 28 +++++++++++++++++++++++++--- client/internal/config.go | 2 +- client/ssh/client.go | 13 ++++--------- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/client/cmd/ssh.go b/client/cmd/ssh.go index 9aeaa217700..11511c11403 100644 --- a/client/cmd/ssh.go +++ b/client/cmd/ssh.go @@ -12,12 +12,14 @@ import ( "github.com/spf13/cobra" "os" "os/signal" + "strings" "syscall" ) var ( port int user = "netbird" + host string ) var sshCmd = &cobra.Command{ @@ -26,6 +28,15 @@ var sshCmd = &cobra.Command{ if len(args) < 1 { return errors.New("requires a host argument") } + + split := strings.Split(args[0], "@") + if len(split) == 2 { + user = split[0] + host = split[1] + } else { + host = args[0] + } + return nil }, Short: "connect to a remote SSH server", @@ -39,6 +50,12 @@ var sshCmd = &cobra.Command{ return fmt.Errorf("failed initializing log %v", err) } + if os.Geteuid() != 0 { + //todo make it work on Windows + cmd.Printf("Error: you must be root to run this command\n") + return nil + } + ctx := internal.CtxInitState(cmd.Context()) conn, err := DialClientGRPCServer(ctx, daemonAddr) @@ -69,12 +86,17 @@ var sshCmd = &cobra.Command{ return nil } + config, err := internal.GetConfig("", "", configPath, "") + if err != nil { + return err + } + sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT) sshctx, cancel := context.WithCancel(ctx) go func() { - if err := runSSH(args[0], sshctx); err != nil { + if err := runSSH(host, []byte(config.SSHKey), sshctx); err != nil { log.Print(err) } cancel() @@ -90,8 +112,8 @@ var sshCmd = &cobra.Command{ }, } -func runSSH(addr string, ctx context.Context) error { - c, err := nbssh.DialWithKeyFile(fmt.Sprintf("%s:%d", addr, port), user, "") +func runSSH(addr string, pemKey []byte, ctx context.Context) error { + c, err := nbssh.DialWithKey(fmt.Sprintf("%s:%d", addr, port), user, pemKey) if err != nil { return err } diff --git a/client/internal/config.go b/client/internal/config.go index f0f16dd5dce..35029f037c0 100644 --- a/client/internal/config.go +++ b/client/internal/config.go @@ -158,7 +158,7 @@ func GetConfig(managementURL, adminURL, configPath, preSharedKey string) (*Confi } else { // don't overwrite pre-shared key if we receive asterisks from UI pk := &preSharedKey - if preSharedKey == "**********" { + if preSharedKey == "**********" || preSharedKey == "" { pk = nil } return ReadConfig(managementURL, adminURL, configPath, pk) diff --git a/client/ssh/client.go b/client/ssh/client.go index 68063837444..4aa3d1bc3f4 100644 --- a/client/ssh/client.go +++ b/client/ssh/client.go @@ -4,7 +4,6 @@ import ( "fmt" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/terminal" - "io/ioutil" "net" "os" ) @@ -35,7 +34,7 @@ func (c *Client) OpenTerminal() error { fd := int(os.Stdin.Fd()) state, err := terminal.MakeRaw(fd) if err != nil { - return fmt.Errorf("failed to make terminal raw: %s", err) + return fmt.Errorf("failed to run raw terminal: %s", err) } defer func() { err := terminal.Restore(fd, state) @@ -84,14 +83,10 @@ func (c *Client) OpenTerminal() error { return nil } -// DialWithKeyFile reads and parses the provided SSH key file, and connects to the remote SSH server -func DialWithKeyFile(addr, user, keyfile string) (*Client, error) { - key, err := ioutil.ReadFile(keyfile) - if err != nil { - return nil, err - } +// DialWithKey connects to the remote SSH server with a provided private key file (PEM). +func DialWithKey(addr, user string, privateKey []byte) (*Client, error) { - signer, err := ssh.ParsePrivateKey(key) + signer, err := ssh.ParsePrivateKey(privateKey) if err != nil { return nil, err } From 5491d956d1d1bea444734e8dbcd7ca6a7c20a4f4 Mon Sep 17 00:00:00 2001 From: braginini Date: Sun, 12 Jun 2022 14:15:35 +0200 Subject: [PATCH 13/51] Add missing SSH method to account manager mock --- management/server/mock_server/account_mock.go | 68 +++++++++++-------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/management/server/mock_server/account_mock.go b/management/server/mock_server/account_mock.go index 341f7a3f2da..5afad1e4a80 100644 --- a/management/server/mock_server/account_mock.go +++ b/management/server/mock_server/account_mock.go @@ -39,13 +39,14 @@ type MockAccountManager struct { ListRulesFunc func(accountID string) ([]*server.Rule, error) GetUsersFromAccountFunc func(accountID string) ([]*server.UserInfo, error) UpdatePeerMetaFunc func(peerKey string, meta server.PeerSystemMeta) error + UpdatePeerSSHKeyFunc func(peerKey string, sshKey string) error } func (am *MockAccountManager) GetUsersFromAccount(accountID string) ([]*server.UserInfo, error) { if am.GetUsersFromAccountFunc != nil { return am.GetUsersFromAccountFunc(accountID) } - return nil, status.Errorf(codes.Unimplemented, "method GetUsersFromAccount not implemented") + return nil, status.Errorf(codes.Unimplemented, "method GetUsersFromAccount is not implemented") } func (am *MockAccountManager) GetOrCreateAccountByUser( @@ -56,7 +57,7 @@ func (am *MockAccountManager) GetOrCreateAccountByUser( } return nil, status.Errorf( codes.Unimplemented, - "method GetOrCreateAccountByUser not implemented", + "method GetOrCreateAccountByUser is not implemented", ) } @@ -64,7 +65,7 @@ func (am *MockAccountManager) GetAccountByUser(userId string) (*server.Account, if am.GetAccountByUserFunc != nil { return am.GetAccountByUserFunc(userId) } - return nil, status.Errorf(codes.Unimplemented, "method GetAccountByUser not implemented") + return nil, status.Errorf(codes.Unimplemented, "method GetAccountByUser is not implemented") } func (am *MockAccountManager) AddSetupKey( @@ -76,7 +77,7 @@ func (am *MockAccountManager) AddSetupKey( if am.AddSetupKeyFunc != nil { return am.AddSetupKeyFunc(accountId, keyName, keyType, expiresIn) } - return nil, status.Errorf(codes.Unimplemented, "method AddSetupKey not implemented") + return nil, status.Errorf(codes.Unimplemented, "method AddSetupKey is not implemented") } func (am *MockAccountManager) RevokeSetupKey( @@ -86,7 +87,7 @@ func (am *MockAccountManager) RevokeSetupKey( if am.RevokeSetupKeyFunc != nil { return am.RevokeSetupKeyFunc(accountId, keyId) } - return nil, status.Errorf(codes.Unimplemented, "method RevokeSetupKey not implemented") + return nil, status.Errorf(codes.Unimplemented, "method RevokeSetupKey is not implemented") } func (am *MockAccountManager) RenameSetupKey( @@ -97,14 +98,14 @@ func (am *MockAccountManager) RenameSetupKey( if am.RenameSetupKeyFunc != nil { return am.RenameSetupKeyFunc(accountId, keyId, newName) } - return nil, status.Errorf(codes.Unimplemented, "method RenameSetupKey not implemented") + return nil, status.Errorf(codes.Unimplemented, "method RenameSetupKey is not implemented") } func (am *MockAccountManager) GetAccountById(accountId string) (*server.Account, error) { if am.GetAccountByIdFunc != nil { return am.GetAccountByIdFunc(accountId) } - return nil, status.Errorf(codes.Unimplemented, "method GetAccountById not implemented") + return nil, status.Errorf(codes.Unimplemented, "method GetAccountById is not implemented") } func (am *MockAccountManager) GetAccountByUserOrAccountId( @@ -115,7 +116,7 @@ func (am *MockAccountManager) GetAccountByUserOrAccountId( } return nil, status.Errorf( codes.Unimplemented, - "method GetAccountByUserOrAccountId not implemented", + "method GetAccountByUserOrAccountId is not implemented", ) } @@ -127,7 +128,7 @@ func (am *MockAccountManager) GetAccountWithAuthorizationClaims( } return nil, status.Errorf( codes.Unimplemented, - "method GetAccountWithAuthorizationClaims not implemented", + "method GetAccountWithAuthorizationClaims is not implemented", ) } @@ -135,21 +136,21 @@ func (am *MockAccountManager) AccountExists(accountId string) (*bool, error) { if am.AccountExistsFunc != nil { return am.AccountExistsFunc(accountId) } - return nil, status.Errorf(codes.Unimplemented, "method AccountExists not implemented") + return nil, status.Errorf(codes.Unimplemented, "method AccountExists is not implemented") } func (am *MockAccountManager) GetPeer(peerKey string) (*server.Peer, error) { if am.GetPeerFunc != nil { return am.GetPeerFunc(peerKey) } - return nil, status.Errorf(codes.Unimplemented, "method GetPeer not implemented") + return nil, status.Errorf(codes.Unimplemented, "method GetPeer is not implemented") } func (am *MockAccountManager) MarkPeerConnected(peerKey string, connected bool) error { if am.MarkPeerConnectedFunc != nil { return am.MarkPeerConnectedFunc(peerKey, connected) } - return status.Errorf(codes.Unimplemented, "method MarkPeerConnected not implemented") + return status.Errorf(codes.Unimplemented, "method MarkPeerConnected is not implemented") } func (am *MockAccountManager) RenamePeer( @@ -160,28 +161,28 @@ func (am *MockAccountManager) RenamePeer( if am.RenamePeerFunc != nil { return am.RenamePeerFunc(accountId, peerKey, newName) } - return nil, status.Errorf(codes.Unimplemented, "method RenamePeer not implemented") + return nil, status.Errorf(codes.Unimplemented, "method RenamePeer is not implemented") } func (am *MockAccountManager) DeletePeer(accountId string, peerKey string) (*server.Peer, error) { if am.DeletePeerFunc != nil { return am.DeletePeerFunc(accountId, peerKey) } - return nil, status.Errorf(codes.Unimplemented, "method DeletePeer not implemented") + return nil, status.Errorf(codes.Unimplemented, "method DeletePeer is not implemented") } func (am *MockAccountManager) GetPeerByIP(accountId string, peerIP string) (*server.Peer, error) { if am.GetPeerByIPFunc != nil { return am.GetPeerByIPFunc(accountId, peerIP) } - return nil, status.Errorf(codes.Unimplemented, "method GetPeerByIP not implemented") + return nil, status.Errorf(codes.Unimplemented, "method GetPeerByIP is not implemented") } func (am *MockAccountManager) GetNetworkMap(peerKey string) (*server.NetworkMap, error) { if am.GetNetworkMapFunc != nil { return am.GetNetworkMapFunc(peerKey) } - return nil, status.Errorf(codes.Unimplemented, "method GetNetworkMap not implemented") + return nil, status.Errorf(codes.Unimplemented, "method GetNetworkMap is not implemented") } func (am *MockAccountManager) AddPeer( @@ -192,96 +193,103 @@ func (am *MockAccountManager) AddPeer( if am.AddPeerFunc != nil { return am.AddPeerFunc(setupKey, userId, peer) } - return nil, status.Errorf(codes.Unimplemented, "method AddPeer not implemented") + return nil, status.Errorf(codes.Unimplemented, "method AddPeer is not implemented") } func (am *MockAccountManager) GetGroup(accountID, groupID string) (*server.Group, error) { if am.GetGroupFunc != nil { return am.GetGroupFunc(accountID, groupID) } - return nil, status.Errorf(codes.Unimplemented, "method GetGroup not implemented") + return nil, status.Errorf(codes.Unimplemented, "method GetGroup is not implemented") } func (am *MockAccountManager) SaveGroup(accountID string, group *server.Group) error { if am.SaveGroupFunc != nil { return am.SaveGroupFunc(accountID, group) } - return status.Errorf(codes.Unimplemented, "method SaveGroup not implemented") + return status.Errorf(codes.Unimplemented, "method SaveGroup is not implemented") } func (am *MockAccountManager) DeleteGroup(accountID, groupID string) error { if am.DeleteGroupFunc != nil { return am.DeleteGroupFunc(accountID, groupID) } - return status.Errorf(codes.Unimplemented, "method DeleteGroup not implemented") + return status.Errorf(codes.Unimplemented, "method DeleteGroup is not implemented") } func (am *MockAccountManager) ListGroups(accountID string) ([]*server.Group, error) { if am.ListGroupsFunc != nil { return am.ListGroupsFunc(accountID) } - return nil, status.Errorf(codes.Unimplemented, "method ListGroups not implemented") + return nil, status.Errorf(codes.Unimplemented, "method ListGroups is not implemented") } func (am *MockAccountManager) GroupAddPeer(accountID, groupID, peerKey string) error { if am.GroupAddPeerFunc != nil { return am.GroupAddPeerFunc(accountID, groupID, peerKey) } - return status.Errorf(codes.Unimplemented, "method GroupAddPeer not implemented") + return status.Errorf(codes.Unimplemented, "method GroupAddPeer is not implemented") } func (am *MockAccountManager) GroupDeletePeer(accountID, groupID, peerKey string) error { if am.GroupDeletePeerFunc != nil { return am.GroupDeletePeerFunc(accountID, groupID, peerKey) } - return status.Errorf(codes.Unimplemented, "method GroupDeletePeer not implemented") + return status.Errorf(codes.Unimplemented, "method GroupDeletePeer is not implemented") } func (am *MockAccountManager) GroupListPeers(accountID, groupID string) ([]*server.Peer, error) { if am.GroupListPeersFunc != nil { return am.GroupListPeersFunc(accountID, groupID) } - return nil, status.Errorf(codes.Unimplemented, "method GroupListPeers not implemented") + return nil, status.Errorf(codes.Unimplemented, "method GroupListPeers is not implemented") } func (am *MockAccountManager) GetRule(accountID, ruleID string) (*server.Rule, error) { if am.GetRuleFunc != nil { return am.GetRuleFunc(accountID, ruleID) } - return nil, status.Errorf(codes.Unimplemented, "method GetRule not implemented") + return nil, status.Errorf(codes.Unimplemented, "method GetRule is not implemented") } func (am *MockAccountManager) SaveRule(accountID string, rule *server.Rule) error { if am.SaveRuleFunc != nil { return am.SaveRuleFunc(accountID, rule) } - return status.Errorf(codes.Unimplemented, "method SaveRule not implemented") + return status.Errorf(codes.Unimplemented, "method SaveRule is not implemented") } func (am *MockAccountManager) DeleteRule(accountID, ruleID string) error { if am.DeleteRuleFunc != nil { return am.DeleteRuleFunc(accountID, ruleID) } - return status.Errorf(codes.Unimplemented, "method DeleteRule not implemented") + return status.Errorf(codes.Unimplemented, "method DeleteRule is not implemented") } func (am *MockAccountManager) ListRules(accountID string) ([]*server.Rule, error) { if am.ListRulesFunc != nil { return am.ListRulesFunc(accountID) } - return nil, status.Errorf(codes.Unimplemented, "method ListRules not implemented") + return nil, status.Errorf(codes.Unimplemented, "method ListRules is not implemented") } func (am *MockAccountManager) UpdatePeerMeta(peerKey string, meta server.PeerSystemMeta) error { if am.UpdatePeerMetaFunc != nil { return am.UpdatePeerMetaFunc(peerKey, meta) } - return status.Errorf(codes.Unimplemented, "method UpdatePeerMetaFunc not implemented") + return status.Errorf(codes.Unimplemented, "method UpdatePeerMetaFunc is not implemented") } func (am *MockAccountManager) IsUserAdmin(claims jwtclaims.AuthorizationClaims) (bool, error) { if am.IsUserAdminFunc != nil { return am.IsUserAdminFunc(claims) } - return false, status.Errorf(codes.Unimplemented, "method IsUserAdmin not implemented") + return false, status.Errorf(codes.Unimplemented, "method IsUserAdmin is not implemented") +} + +func (am *MockAccountManager) UpdatePeerSSHKey(peerKey string, sshKey string) error { + if am.UpdatePeerSSHKeyFunc != nil { + return am.UpdatePeerSSHKeyFunc(peerKey, sshKey) + } + return status.Errorf(codes.Unimplemented, "method UpdatePeerSSHKey is is not implemented") } From 3a2ea469c0d6dbb54f99236e4964f2c41965c8af Mon Sep 17 00:00:00 2001 From: braginini Date: Sun, 12 Jun 2022 14:29:34 +0200 Subject: [PATCH 14/51] Fix golint issues --- client/internal/engine.go | 1 + client/ssh/client.go | 8 ++++---- client/ssh/server.go | 36 ++++++++++++++++++++++++++++-------- client/ssh/window_unix.go | 2 +- go.mod | 2 +- 5 files changed, 35 insertions(+), 14 deletions(-) diff --git a/client/internal/engine.go b/client/internal/engine.go index 80961ae7298..cbaf9603864 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -434,6 +434,7 @@ func (e *Engine) updateSSH(sshConf *mgmProto.SSHConfig) error { err = e.sshServer.Start() if err != nil { // will throw error when we stop it even if it is a graceful stop + log.Debugf("stopped SSH server with error %v", err) } log.Infof("stopped SSH server") }() diff --git a/client/ssh/client.go b/client/ssh/client.go index 4aa3d1bc3f4..18c88fbff81 100644 --- a/client/ssh/client.go +++ b/client/ssh/client.go @@ -3,7 +3,7 @@ package ssh import ( "fmt" "golang.org/x/crypto/ssh" - "golang.org/x/crypto/ssh/terminal" + "golang.org/x/term" "net" "os" ) @@ -32,18 +32,18 @@ func (c *Client) OpenTerminal() error { }() fd := int(os.Stdin.Fd()) - state, err := terminal.MakeRaw(fd) + state, err := term.MakeRaw(fd) if err != nil { return fmt.Errorf("failed to run raw terminal: %s", err) } defer func() { - err := terminal.Restore(fd, state) + err := term.Restore(fd, state) if err != nil { return } }() - w, h, err := terminal.GetSize(fd) + w, h, err := term.GetSize(fd) if err != nil { return fmt.Errorf("terminal get size: %s", err) } diff --git a/client/ssh/server.go b/client/ssh/server.go index 2534c84a24a..4c13d52938b 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -92,23 +92,43 @@ func (srv *Server) sessionHandler(s ssh.Session) { setWinSize(f, win.Width, win.Height) } }() - go func() { - io.Copy(f, s) // stdin - }() - go func() { - io.Copy(s, f) // stdout - }() + + srv.stdInOut(f, s) err = cmd.Wait() if err != nil { return } } else { - io.WriteString(s, "Only PTY is supported.\n") - s.Exit(1) + _, err := io.WriteString(s, "Only PTY is supported.\n") + if err != nil { + return + } + err = s.Exit(1) + if err != nil { + return + } } } +func (srv *Server) stdInOut(f *os.File, s ssh.Session) { + go func() { + // stdin + _, err := io.Copy(f, s) + if err != nil { + return + } + }() + + go func() { + // stdout + _, err := io.Copy(s, f) + if err != nil { + return + } + }() +} + // Start starts SSH server. Blocking func (srv *Server) Start() error { log.Debugf("starting SSH server") diff --git a/client/ssh/window_unix.go b/client/ssh/window_unix.go index 368c6f06557..4a09512b5a2 100644 --- a/client/ssh/window_unix.go +++ b/client/ssh/window_unix.go @@ -9,6 +9,6 @@ import ( ) func setWinSize(f *os.File, w, h int) { - syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ), + syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ), //nolint uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0}))) } diff --git a/go.mod b/go.mod index 7a926183bab..35433d0034f 100644 --- a/go.mod +++ b/go.mod @@ -39,6 +39,7 @@ require ( github.com/rs/xid v1.3.0 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/stretchr/testify v1.7.1 + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 ) require ( @@ -96,7 +97,6 @@ require ( golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/net v0.0.0-20220412020605-290c469a71a5 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 // indirect golang.org/x/tools v0.1.10 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect From 2cf8ed67ad158899e5ecf2186467db2e546142c4 Mon Sep 17 00:00:00 2001 From: braginini Date: Sun, 12 Jun 2022 14:32:41 +0200 Subject: [PATCH 15/51] Fix codacy --- client/cmd/ssh.go | 4 ++-- client/ssh/server.go | 1 + client/ssh/util.go | 7 +++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/client/cmd/ssh.go b/client/cmd/ssh.go index 11511c11403..dc6afa47be8 100644 --- a/client/cmd/ssh.go +++ b/client/cmd/ssh.go @@ -96,7 +96,7 @@ var sshCmd = &cobra.Command{ sshctx, cancel := context.WithCancel(ctx) go func() { - if err := runSSH(host, []byte(config.SSHKey), sshctx); err != nil { + if err := runSSH(sshctx, host, []byte(config.SSHKey)); err != nil { log.Print(err) } cancel() @@ -112,7 +112,7 @@ var sshCmd = &cobra.Command{ }, } -func runSSH(addr string, pemKey []byte, ctx context.Context) error { +func runSSH(ctx context.Context, addr string, pemKey []byte) error { c, err := nbssh.DialWithKey(fmt.Sprintf("%s:%d", addr, port), user, pemKey) if err != nil { return err diff --git a/client/ssh/server.go b/client/ssh/server.go index 4c13d52938b..32af11d2874 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -23,6 +23,7 @@ func init() { } } +// Server is the embedded NetBird SSh server type Server struct { listener net.Listener allowedKeys map[string]ssh.PublicKey diff --git a/client/ssh/util.go b/client/ssh/util.go index 7b4e86e7b9d..02ac1021175 100644 --- a/client/ssh/util.go +++ b/client/ssh/util.go @@ -14,12 +14,19 @@ import ( "strings" ) +// KeyType is a type of SSH key type KeyType string +// ED25519 is key of type ed25519 const ED25519 KeyType = "ed25519" + +// ECDSA is key of type ecdsa const ECDSA KeyType = "ecdsa" + +// RSA is key of type rsa const RSA KeyType = "rsa" +// RSAKeySize is a size of newly generated RSA key const RSAKeySize = 2048 // GeneratePrivateKey creates RSA Private Key of specified byte size From 47c7993980274186aee0ef6ad6ffe073b71da950 Mon Sep 17 00:00:00 2001 From: braginini Date: Sun, 12 Jun 2022 14:37:01 +0200 Subject: [PATCH 16/51] Fix codacy --- management/server/mock_server/account_mock.go | 1 + 1 file changed, 1 insertion(+) diff --git a/management/server/mock_server/account_mock.go b/management/server/mock_server/account_mock.go index 5afad1e4a80..3831674bc91 100644 --- a/management/server/mock_server/account_mock.go +++ b/management/server/mock_server/account_mock.go @@ -287,6 +287,7 @@ func (am *MockAccountManager) IsUserAdmin(claims jwtclaims.AuthorizationClaims) return false, status.Errorf(codes.Unimplemented, "method IsUserAdmin is not implemented") } +// UpdatePeerSSHKey mocks UpdatePeerSSHKey function of the account manager func (am *MockAccountManager) UpdatePeerSSHKey(peerKey string, sshKey string) error { if am.UpdatePeerSSHKeyFunc != nil { return am.UpdatePeerSSHKeyFunc(peerKey, sshKey) From 136dedd502a1d2b1fab3dee3c820054ffc1c4ada Mon Sep 17 00:00:00 2001 From: braginini Date: Mon, 13 Jun 2022 20:58:55 +0200 Subject: [PATCH 17/51] Associate public WG key with SSH key --- client/internal/engine.go | 5 ++++- client/ssh/server.go | 28 ++++++++++++++++++---------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/client/internal/engine.go b/client/internal/engine.go index cbaf9603864..ccf0c70535a 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -304,6 +304,9 @@ func (e *Engine) removePeer(peerKey string) error { } } } + if e.sshServer != nil { + e.sshServer.RemoveAuthorizedKey(peerKey) + } return nil } @@ -568,7 +571,7 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error { if e.sshServer != nil { for _, config := range networkMap.GetRemotePeers() { if config.GetSshConfig() != nil && config.GetSshConfig().GetSshPubKey() != nil { - err := e.sshServer.AddAuthorizedKey(string(config.GetSshConfig().GetSshPubKey())) + err := e.sshServer.AddAuthorizedKey(config.WgPubKey, string(config.GetSshConfig().GetSshPubKey())) if err != nil { log.Warnf("failed adding authroized key to SSH Server %v", err) } diff --git a/client/ssh/server.go b/client/ssh/server.go index 32af11d2874..8a32b443c86 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -5,12 +5,10 @@ import ( "github.com/creack/pty" "github.com/gliderlabs/ssh" log "github.com/sirupsen/logrus" - gossh "golang.org/x/crypto/ssh" "io" "net" "os" "os/exec" - "strings" "sync" ) @@ -23,9 +21,10 @@ func init() { } } -// Server is the embedded NetBird SSh server +// Server is the embedded NetBird SSH server type Server struct { - listener net.Listener + listener net.Listener + // allowedKeys is ssh pub key indexed by peer WireGuard public key allowedKeys map[string]ssh.PublicKey mu sync.Mutex hostKeyPEM []byte @@ -41,8 +40,18 @@ func NewSSHServer(hostKeyPEM []byte, addr string) (*Server, error) { return &Server{listener: ln, mu: sync.Mutex{}, hostKeyPEM: hostKeyPEM, allowedKeys: allowedKeys}, nil } -// AddAuthorizedKey add given key as authorized key to the server -func (srv *Server) AddAuthorizedKey(newKey string) error { +// RemoveAuthorizedKey removes a key of a given peer from the authorized keys +func (srv *Server) RemoveAuthorizedKey(peer string) error { + srv.mu.Lock() + defer srv.mu.Unlock() + + srv.allowedKeys[peer] = nil + return nil + +} + +// AddAuthorizedKey add a given peer key to server authorized keys +func (srv *Server) AddAuthorizedKey(peer, newKey string) error { srv.mu.Lock() defer srv.mu.Unlock() @@ -50,8 +59,8 @@ func (srv *Server) AddAuthorizedKey(newKey string) error { if err != nil { return err } - strKey := strings.TrimSpace(string(gossh.MarshalAuthorizedKey(parsedKey))) - srv.allowedKeys[strKey] = parsedKey + + srv.allowedKeys[peer] = parsedKey return nil } @@ -68,8 +77,7 @@ func (srv *Server) publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool { srv.mu.Lock() defer srv.mu.Unlock() - k := strings.TrimSpace(string(gossh.MarshalAuthorizedKey(key))) - if allowed, ok := srv.allowedKeys[k]; ok { + for _, allowed := range srv.allowedKeys { if ssh.KeysEqual(allowed, key) { return true } From 5f233827f25033d6453b11f4ffdd6b53b0080f3b Mon Sep 17 00:00:00 2001 From: braginini Date: Mon, 13 Jun 2022 21:06:45 +0200 Subject: [PATCH 18/51] Minor refactor --- client/internal/engine.go | 8 ++++---- client/ssh/server.go | 14 ++++++-------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/client/internal/engine.go b/client/internal/engine.go index ccf0c70535a..ae4239817af 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -288,7 +288,7 @@ func (e *Engine) removeAllPeers() error { return nil } -// removePeer closes an existing peer connection and removes a peer +// removePeer closes an existing peer connection, removes a peer, and clears authorized key of the SSH server func (e *Engine) removePeer(peerKey string) error { log.Debugf("removing peer from engine %s", peerKey) conn, exists := e.peerConns[peerKey] @@ -445,11 +445,11 @@ func (e *Engine) updateSSH(sshConf *mgmProto.SSHConfig) error { log.Debugf("SSH server is already running") } } else { - // stop SSH server if it was running + // Disable SSH server request, so stop it if it was running if e.sshServer != nil { err := e.sshServer.Stop() if err != nil { - log.Warnf("failed stopping SSH server %v", err) + log.Warnf("failed to stop SSH server %v", err) } e.sshServer = nil } @@ -472,7 +472,7 @@ func (e *Engine) updateConfig(conf *mgmProto.PeerConfig) error { if conf.GetSshConfig() != nil { err := e.updateSSH(conf.GetSshConfig()) if err != nil { - log.Warnf("failed handling SSH setup %v", e) + log.Warnf("failed handling SSH server setup %v", e) } } diff --git a/client/ssh/server.go b/client/ssh/server.go index 8a32b443c86..485fdf42fd4 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -40,14 +40,12 @@ func NewSSHServer(hostKeyPEM []byte, addr string) (*Server, error) { return &Server{listener: ln, mu: sync.Mutex{}, hostKeyPEM: hostKeyPEM, allowedKeys: allowedKeys}, nil } -// RemoveAuthorizedKey removes a key of a given peer from the authorized keys -func (srv *Server) RemoveAuthorizedKey(peer string) error { +// RemoveAuthorizedKey removes SSH key of a given peer from the authorized keys +func (srv *Server) RemoveAuthorizedKey(peer string) { srv.mu.Lock() defer srv.mu.Unlock() - srv.allowedKeys[peer] = nil - return nil - + delete(srv.allowedKeys, peer) } // AddAuthorizedKey add a given peer key to server authorized keys @@ -64,7 +62,7 @@ func (srv *Server) AddAuthorizedKey(peer, newKey string) error { return nil } -// Stop stops SSH server. Blocking +// Stop stops SSH server. func (srv *Server) Stop() error { err := srv.listener.Close() if err != nil { @@ -109,7 +107,7 @@ func (srv *Server) sessionHandler(s ssh.Session) { return } } else { - _, err := io.WriteString(s, "Only PTY is supported.\n") + _, err := io.WriteString(s, "only PTY is supported.\n") if err != nil { return } @@ -140,7 +138,7 @@ func (srv *Server) stdInOut(f *os.File, s ssh.Session) { // Start starts SSH server. Blocking func (srv *Server) Start() error { - log.Debugf("starting SSH server") + log.Infof("starting SSH server") publicKeyOption := ssh.PublicKeyAuth(srv.publicKeyHandler) hostKeyPEM := ssh.HostKeyPEM(srv.hostKeyPEM) From e40c84d701fac4865e193546c9611eeb8cd2b189 Mon Sep 17 00:00:00 2001 From: braginini Date: Mon, 13 Jun 2022 21:20:48 +0200 Subject: [PATCH 19/51] Add Remove and Add authorized keys test --- client/ssh/server.go | 16 ++++----- client/ssh/server_test.go | 68 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 client/ssh/server_test.go diff --git a/client/ssh/server.go b/client/ssh/server.go index 485fdf42fd4..e6347411522 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -24,10 +24,10 @@ func init() { // Server is the embedded NetBird SSH server type Server struct { listener net.Listener - // allowedKeys is ssh pub key indexed by peer WireGuard public key - allowedKeys map[string]ssh.PublicKey - mu sync.Mutex - hostKeyPEM []byte + // authorizedKeys is ssh pub key indexed by peer WireGuard public key + authorizedKeys map[string]ssh.PublicKey + mu sync.Mutex + hostKeyPEM []byte } // NewSSHServer creates new server with provided host key @@ -37,7 +37,7 @@ func NewSSHServer(hostKeyPEM []byte, addr string) (*Server, error) { return nil, err } allowedKeys := make(map[string]ssh.PublicKey) - return &Server{listener: ln, mu: sync.Mutex{}, hostKeyPEM: hostKeyPEM, allowedKeys: allowedKeys}, nil + return &Server{listener: ln, mu: sync.Mutex{}, hostKeyPEM: hostKeyPEM, authorizedKeys: allowedKeys}, nil } // RemoveAuthorizedKey removes SSH key of a given peer from the authorized keys @@ -45,7 +45,7 @@ func (srv *Server) RemoveAuthorizedKey(peer string) { srv.mu.Lock() defer srv.mu.Unlock() - delete(srv.allowedKeys, peer) + delete(srv.authorizedKeys, peer) } // AddAuthorizedKey add a given peer key to server authorized keys @@ -58,7 +58,7 @@ func (srv *Server) AddAuthorizedKey(peer, newKey string) error { return err } - srv.allowedKeys[peer] = parsedKey + srv.authorizedKeys[peer] = parsedKey return nil } @@ -75,7 +75,7 @@ func (srv *Server) publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool { srv.mu.Lock() defer srv.mu.Unlock() - for _, allowed := range srv.allowedKeys { + for _, allowed := range srv.authorizedKeys { if ssh.KeysEqual(allowed, key) { return true } diff --git a/client/ssh/server_test.go b/client/ssh/server_test.go new file mode 100644 index 00000000000..6b782c57dad --- /dev/null +++ b/client/ssh/server_test.go @@ -0,0 +1,68 @@ +package ssh + +import ( + "github.com/stretchr/testify/assert" + "golang.org/x/crypto/ssh" + "strings" + "testing" +) + +func TestServer_AddAuthorizedKey(t *testing.T) { + key, err := GeneratePrivateKey(ED25519) + if err != nil { + t.Fatal(err) + } + server, err := NewSSHServer(key, "localhost:") + if err != nil { + t.Fatal(err) + } + + remotePrivKey, err := GeneratePrivateKey(ED25519) + if err != nil { + t.Fatal(err) + } + remotePubKey, err := GeneratePublicKey(remotePrivKey) + if err != nil { + t.Fatal(err) + } + + err = server.AddAuthorizedKey("remotePeer", string(remotePubKey)) + if err != nil { + t.Error(err) + } + + k, ok := server.authorizedKeys["remotePeer"] + assert.True(t, ok, "expected remotePeer key to be found in authorizedKeys") + + assert.Equal(t, string(remotePubKey), strings.TrimSpace(string(ssh.MarshalAuthorizedKey(k)))) +} + +func TestServer_RemoveAuthorizedKey(t *testing.T) { + key, err := GeneratePrivateKey(ED25519) + if err != nil { + t.Fatal(err) + } + server, err := NewSSHServer(key, "localhost:") + if err != nil { + t.Fatal(err) + } + + remotePrivKey, err := GeneratePrivateKey(ED25519) + if err != nil { + t.Fatal(err) + } + remotePubKey, err := GeneratePublicKey(remotePrivKey) + if err != nil { + t.Fatal(err) + } + + err = server.AddAuthorizedKey("remotePeer", string(remotePubKey)) + if err != nil { + t.Error(err) + } + + server.RemoveAuthorizedKey("remotePeer") + + _, ok := server.authorizedKeys["remotePeer"] + assert.False(t, ok, "expected remotePeer's SSH key to be removed") +} From 30683d7cd1e120ee6fbf5b74aa36d35458fc3631 Mon Sep 17 00:00:00 2001 From: braginini Date: Mon, 13 Jun 2022 21:34:01 +0200 Subject: [PATCH 20/51] Test Multiple SSH keys --- client/ssh/server_test.go | 83 ++++++++++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 15 deletions(-) diff --git a/client/ssh/server_test.go b/client/ssh/server_test.go index 6b782c57dad..b3d00a998a1 100644 --- a/client/ssh/server_test.go +++ b/client/ssh/server_test.go @@ -1,6 +1,7 @@ package ssh import ( + "fmt" "github.com/stretchr/testify/assert" "golang.org/x/crypto/ssh" "strings" @@ -17,24 +18,34 @@ func TestServer_AddAuthorizedKey(t *testing.T) { t.Fatal(err) } - remotePrivKey, err := GeneratePrivateKey(ED25519) - if err != nil { - t.Fatal(err) - } - remotePubKey, err := GeneratePublicKey(remotePrivKey) - if err != nil { - t.Fatal(err) - } + // add multiple keys + keys := map[string][]byte{} + for i := 0; i < 10; i++ { + peer := fmt.Sprintf("%s-%d", "remotePeer", i) + remotePrivKey, err := GeneratePrivateKey(ED25519) + if err != nil { + t.Fatal(err) + } + remotePubKey, err := GeneratePublicKey(remotePrivKey) + if err != nil { + t.Fatal(err) + } - err = server.AddAuthorizedKey("remotePeer", string(remotePubKey)) - if err != nil { - t.Error(err) + err = server.AddAuthorizedKey(peer, string(remotePubKey)) + if err != nil { + t.Error(err) + } + keys[peer] = remotePubKey } - k, ok := server.authorizedKeys["remotePeer"] - assert.True(t, ok, "expected remotePeer key to be found in authorizedKeys") + // make sure that all keys have been added + for peer, remotePubKey := range keys { + k, ok := server.authorizedKeys[peer] + assert.True(t, ok, "expecting remotePeer key to be found in authorizedKeys") + + assert.Equal(t, string(remotePubKey), strings.TrimSpace(string(ssh.MarshalAuthorizedKey(k)))) + } - assert.Equal(t, string(remotePubKey), strings.TrimSpace(string(ssh.MarshalAuthorizedKey(k)))) } func TestServer_RemoveAuthorizedKey(t *testing.T) { @@ -64,5 +75,47 @@ func TestServer_RemoveAuthorizedKey(t *testing.T) { server.RemoveAuthorizedKey("remotePeer") _, ok := server.authorizedKeys["remotePeer"] - assert.False(t, ok, "expected remotePeer's SSH key to be removed") + assert.False(t, ok, "expecting remotePeer's SSH key to be removed") +} + +func TestServer_PubKeyHandler(t *testing.T) { + key, err := GeneratePrivateKey(ED25519) + if err != nil { + t.Fatal(err) + } + server, err := NewSSHServer(key, "localhost:") + if err != nil { + t.Fatal(err) + } + + var keys []ssh.PublicKey + for i := 0; i < 10; i++ { + peer := fmt.Sprintf("%s-%d", "remotePeer", i) + remotePrivKey, err := GeneratePrivateKey(ED25519) + if err != nil { + t.Fatal(err) + } + remotePubKey, err := GeneratePublicKey(remotePrivKey) + if err != nil { + t.Fatal(err) + } + + remoteParsedPubKey, _, _, _, err := ssh.ParseAuthorizedKey(remotePubKey) + if err != nil { + t.Fatal(err) + } + + err = server.AddAuthorizedKey(peer, string(remotePubKey)) + if err != nil { + t.Error(err) + } + keys = append(keys, remoteParsedPubKey) + } + + for _, key := range keys { + accepted := server.publicKeyHandler(nil, key) + + assert.Truef(t, accepted, "expecting SSH connection to be accepted for a given SSH key %s", string(ssh.MarshalAuthorizedKey(key))) + } + } From 1f42452472b469b523dbd8327072f2883681daaf Mon Sep 17 00:00:00 2001 From: braginini Date: Mon, 13 Jun 2022 22:02:50 +0200 Subject: [PATCH 21/51] Add Engine test for SSH server --- client/internal/engine.go | 15 +++--- client/internal/engine_test.go | 84 ++++++++++++++++++++++++++++++++++ client/ssh/server.go | 3 ++ 3 files changed, 96 insertions(+), 6 deletions(-) diff --git a/client/internal/engine.go b/client/internal/engine.go index ae4239817af..ddf8886eff6 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -406,12 +406,6 @@ func (e *Engine) handleSync(update *mgmProto.SyncResponse) error { } if update.GetNetworkMap() != nil { - if update.GetNetworkMap().GetPeerConfig() != nil { - err := e.updateConfig(update.GetNetworkMap().GetPeerConfig()) - if err != nil { - return err - } - } // only apply new changes and ignore old ones err := e.updateNetworkMap(update.GetNetworkMap()) if err != nil { @@ -537,6 +531,15 @@ func (e *Engine) updateTURNs(turns []*mgmProto.ProtectedHostConfig) error { } func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error { + + // intentionally leave it before checking serial because for now it can happen that peer IP changed but serial didn't + if networkMap.GetPeerConfig() != nil { + err := e.updateConfig(networkMap.GetPeerConfig()) + if err != nil { + return err + } + } + serial := networkMap.GetSerial() if e.networkSerial > serial { log.Debugf("received outdated NetworkMap with serial %d, ignoring", serial) diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index 1b499ecd25a..11001446016 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -3,6 +3,8 @@ package internal import ( "context" "fmt" + "github.com/netbirdio/netbird/iface" + "github.com/stretchr/testify/assert" "net" "os" "path/filepath" @@ -40,6 +42,87 @@ var ( } ) +func TestEngine_SSH(t *testing.T) { + + key, err := wgtypes.GeneratePrivateKey() + if err != nil { + t.Fatal(err) + return + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + engine := NewEngine(ctx, cancel, &signal.MockClient{}, &mgmt.MockClient{}, &EngineConfig{ + WgIfaceName: "utun100", + WgAddr: "100.64.0.1/24", + WgPrivateKey: key, + WgPort: 33100, + }) + //engine.wgInterface, err = iface.NewWGIFace("utun100", "100.64.0.1/24", iface.DefaultMTU) + err = engine.Start() + if err != nil { + t.Fatal(err) + } + + peerWithSSH := &mgmtProto.RemotePeerConfig{ + WgPubKey: "MNHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=", + AllowedIps: []string{"100.64.0.21/24"}, + SshConfig: &mgmtProto.SSHConfig{ + SshPubKey: []byte("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFATYCqaQw/9id1Qkq3n16JYhDhXraI6Pc1fgB8ynEfQ"), + }, + } + + // SSH server is not enabled so SSH config of a remote peer should be ignored + networkMap := &mgmtProto.NetworkMap{ + Serial: 6, + PeerConfig: nil, + RemotePeers: []*mgmtProto.RemotePeerConfig{peerWithSSH}, + RemotePeersIsEmpty: false, + } + + err = engine.updateNetworkMap(networkMap) + if err != nil { + t.Fatal(err) + } + + assert.Nil(t, engine.sshServer) + + // SSH server is enabled, therefore SSH config should be applied + networkMap = &mgmtProto.NetworkMap{ + Serial: 7, + PeerConfig: &mgmtProto.PeerConfig{Address: "100.64.0.1/24", + SshConfig: &mgmtProto.SSHConfig{SshEnabled: true}}, + RemotePeers: []*mgmtProto.RemotePeerConfig{peerWithSSH}, + RemotePeersIsEmpty: false, + } + + err = engine.updateNetworkMap(networkMap) + if err != nil { + t.Fatal(err) + } + + time.Sleep(500 * time.Millisecond) + assert.NotNil(t, engine.sshServer) + + // now disable SSH server + networkMap = &mgmtProto.NetworkMap{ + Serial: 8, + PeerConfig: &mgmtProto.PeerConfig{Address: "100.64.0.1/24", + SshConfig: &mgmtProto.SSHConfig{SshEnabled: false}}, + RemotePeers: []*mgmtProto.RemotePeerConfig{peerWithSSH}, + RemotePeersIsEmpty: false, + } + + err = engine.updateNetworkMap(networkMap) + if err != nil { + t.Fatal(err) + } + + assert.Nil(t, engine.sshServer) + +} + func TestEngine_UpdateNetworkMap(t *testing.T) { // test setup key, err := wgtypes.GeneratePrivateKey() @@ -57,6 +140,7 @@ func TestEngine_UpdateNetworkMap(t *testing.T) { WgPrivateKey: key, WgPort: 33100, }) + engine.wgInterface, err = iface.NewWGIFace("utun100", "100.64.0.1/24", iface.DefaultMTU) type testCase struct { name string diff --git a/client/ssh/server.go b/client/ssh/server.go index e6347411522..d1a3c6a5425 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -21,6 +21,9 @@ func init() { } } +type Srv interface { +} + // Server is the embedded NetBird SSH server type Server struct { listener net.Listener From e6773d18dc89922f0dc9291ec3c9c7fd0826f813 Mon Sep 17 00:00:00 2001 From: braginini Date: Mon, 13 Jun 2022 22:08:35 +0200 Subject: [PATCH 22/51] Add SSH server interface --- client/internal/engine.go | 4 ++-- client/ssh/server.go | 29 +++++++++++++++++------------ 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/client/internal/engine.go b/client/internal/engine.go index ddf8886eff6..751fafbdb5f 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -91,7 +91,7 @@ type Engine struct { // networkSerial is the latest CurrentSerial (state ID) of the network sent by the Management service networkSerial uint64 - sshServer *ssh.Server + sshServer *ssh.DefaultServer } // Peer is an instance of the Connection Peer @@ -576,7 +576,7 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error { if config.GetSshConfig() != nil && config.GetSshConfig().GetSshPubKey() != nil { err := e.sshServer.AddAuthorizedKey(config.WgPubKey, string(config.GetSshConfig().GetSshPubKey())) if err != nil { - log.Warnf("failed adding authroized key to SSH Server %v", err) + log.Warnf("failed adding authroized key to SSH DefaultServer %v", err) } } } diff --git a/client/ssh/server.go b/client/ssh/server.go index d1a3c6a5425..0af002870d5 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -21,11 +21,16 @@ func init() { } } -type Srv interface { +// Server is an interface of a SSH server +type Server interface { + Stop() error + Start() error + RemoveAuthorizedKey(peer string) + AddAuthorizedKey(peer, newKey string) error } -// Server is the embedded NetBird SSH server -type Server struct { +// DefaultServer is the embedded NetBird SSH server +type DefaultServer struct { listener net.Listener // authorizedKeys is ssh pub key indexed by peer WireGuard public key authorizedKeys map[string]ssh.PublicKey @@ -34,17 +39,17 @@ type Server struct { } // NewSSHServer creates new server with provided host key -func NewSSHServer(hostKeyPEM []byte, addr string) (*Server, error) { +func NewSSHServer(hostKeyPEM []byte, addr string) (*DefaultServer, error) { ln, err := net.Listen("tcp", addr) if err != nil { return nil, err } allowedKeys := make(map[string]ssh.PublicKey) - return &Server{listener: ln, mu: sync.Mutex{}, hostKeyPEM: hostKeyPEM, authorizedKeys: allowedKeys}, nil + return &DefaultServer{listener: ln, mu: sync.Mutex{}, hostKeyPEM: hostKeyPEM, authorizedKeys: allowedKeys}, nil } // RemoveAuthorizedKey removes SSH key of a given peer from the authorized keys -func (srv *Server) RemoveAuthorizedKey(peer string) { +func (srv *DefaultServer) RemoveAuthorizedKey(peer string) { srv.mu.Lock() defer srv.mu.Unlock() @@ -52,7 +57,7 @@ func (srv *Server) RemoveAuthorizedKey(peer string) { } // AddAuthorizedKey add a given peer key to server authorized keys -func (srv *Server) AddAuthorizedKey(peer, newKey string) error { +func (srv *DefaultServer) AddAuthorizedKey(peer, newKey string) error { srv.mu.Lock() defer srv.mu.Unlock() @@ -66,7 +71,7 @@ func (srv *Server) AddAuthorizedKey(peer, newKey string) error { } // Stop stops SSH server. -func (srv *Server) Stop() error { +func (srv *DefaultServer) Stop() error { err := srv.listener.Close() if err != nil { return err @@ -74,7 +79,7 @@ func (srv *Server) Stop() error { return nil } -func (srv *Server) publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool { +func (srv *DefaultServer) publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool { srv.mu.Lock() defer srv.mu.Unlock() @@ -88,7 +93,7 @@ func (srv *Server) publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool { } // sessionHandler handles SSH session post auth -func (srv *Server) sessionHandler(s ssh.Session) { +func (srv *DefaultServer) sessionHandler(s ssh.Session) { ptyReq, winCh, isPty := s.Pty() if isPty { cmd := exec.Command(shell) @@ -121,7 +126,7 @@ func (srv *Server) sessionHandler(s ssh.Session) { } } -func (srv *Server) stdInOut(f *os.File, s ssh.Session) { +func (srv *DefaultServer) stdInOut(f *os.File, s ssh.Session) { go func() { // stdin _, err := io.Copy(f, s) @@ -140,7 +145,7 @@ func (srv *Server) stdInOut(f *os.File, s ssh.Session) { } // Start starts SSH server. Blocking -func (srv *Server) Start() error { +func (srv *DefaultServer) Start() error { log.Infof("starting SSH server") publicKeyOption := ssh.PublicKeyAuth(srv.publicKeyHandler) From aa57c3dbad3639c95dc5e5d371f58ddd19b0c655 Mon Sep 17 00:00:00 2001 From: braginini Date: Mon, 13 Jun 2022 22:09:48 +0200 Subject: [PATCH 23/51] Fix codacy issues --- client/internal/engine.go | 2 +- client/ssh/server.go | 8 ++++++-- client/ssh/server_test.go | 6 +++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/client/internal/engine.go b/client/internal/engine.go index 751fafbdb5f..be5d03f2213 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -422,7 +422,7 @@ func (e *Engine) updateSSH(sshConf *mgmProto.SSHConfig) error { if e.sshServer == nil { //nil sshServer means it has not yet been started var err error - e.sshServer, err = ssh.NewSSHServer(e.config.SSHKey, + e.sshServer, err = ssh.DefaultSSHServer(e.config.SSHKey, fmt.Sprintf("%s:%d", e.wgInterface.Address.IP.String(), 2222)) if err != nil { return err diff --git a/client/ssh/server.go b/client/ssh/server.go index 0af002870d5..44b36d8dc6e 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -23,9 +23,13 @@ func init() { // Server is an interface of a SSH server type Server interface { + // Stop stops SSH server. Stop() error + // Start starts SSH server. Blocking Start() error + // RemoveAuthorizedKey removes SSH key of a given peer from the authorized keys RemoveAuthorizedKey(peer string) + // AddAuthorizedKey add a given peer key to server authorized keys AddAuthorizedKey(peer, newKey string) error } @@ -38,8 +42,8 @@ type DefaultServer struct { hostKeyPEM []byte } -// NewSSHServer creates new server with provided host key -func NewSSHServer(hostKeyPEM []byte, addr string) (*DefaultServer, error) { +// DefaultSSHServer creates new server with provided host key +func DefaultSSHServer(hostKeyPEM []byte, addr string) (*DefaultServer, error) { ln, err := net.Listen("tcp", addr) if err != nil { return nil, err diff --git a/client/ssh/server_test.go b/client/ssh/server_test.go index b3d00a998a1..961bcf3e398 100644 --- a/client/ssh/server_test.go +++ b/client/ssh/server_test.go @@ -13,7 +13,7 @@ func TestServer_AddAuthorizedKey(t *testing.T) { if err != nil { t.Fatal(err) } - server, err := NewSSHServer(key, "localhost:") + server, err := DefaultSSHServer(key, "localhost:") if err != nil { t.Fatal(err) } @@ -53,7 +53,7 @@ func TestServer_RemoveAuthorizedKey(t *testing.T) { if err != nil { t.Fatal(err) } - server, err := NewSSHServer(key, "localhost:") + server, err := DefaultSSHServer(key, "localhost:") if err != nil { t.Fatal(err) } @@ -83,7 +83,7 @@ func TestServer_PubKeyHandler(t *testing.T) { if err != nil { t.Fatal(err) } - server, err := NewSSHServer(key, "localhost:") + server, err := DefaultSSHServer(key, "localhost:") if err != nil { t.Fatal(err) } From c14e26b37f2ec32e6ee86874db164f7ed6181b44 Mon Sep 17 00:00:00 2001 From: braginini Date: Mon, 13 Jun 2022 22:33:49 +0200 Subject: [PATCH 24/51] Verify SSH server usage in Engine --- client/internal/engine.go | 14 +++++++----- client/internal/engine_test.go | 38 ++++++++++++++++++++++++++++--- client/ssh/server.go | 11 ++++++--- client/ssh/server_mock.go | 41 ++++++++++++++++++++++++++++++++++ client/ssh/server_test.go | 6 ++--- 5 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 client/ssh/server_mock.go diff --git a/client/internal/engine.go b/client/internal/engine.go index be5d03f2213..b90f5d309ea 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -91,7 +91,8 @@ type Engine struct { // networkSerial is the latest CurrentSerial (state ID) of the network sent by the Management service networkSerial uint64 - sshServer *ssh.DefaultServer + sshServerFunc func(hostKeyPEM []byte, addr string) (ssh.Server, error) + sshServer ssh.Server } // Peer is an instance of the Connection Peer @@ -116,6 +117,7 @@ func NewEngine( STUNs: []*ice.URL{}, TURNs: []*ice.URL{}, networkSerial: 0, + sshServerFunc: ssh.DefaultSSHServer, } } @@ -291,6 +293,11 @@ func (e *Engine) removeAllPeers() error { // removePeer closes an existing peer connection, removes a peer, and clears authorized key of the SSH server func (e *Engine) removePeer(peerKey string) error { log.Debugf("removing peer from engine %s", peerKey) + + if e.sshServer != nil { + e.sshServer.RemoveAuthorizedKey(peerKey) + } + conn, exists := e.peerConns[peerKey] if exists { delete(e.peerConns, peerKey) @@ -304,9 +311,6 @@ func (e *Engine) removePeer(peerKey string) error { } } } - if e.sshServer != nil { - e.sshServer.RemoveAuthorizedKey(peerKey) - } return nil } @@ -422,7 +426,7 @@ func (e *Engine) updateSSH(sshConf *mgmProto.SSHConfig) error { if e.sshServer == nil { //nil sshServer means it has not yet been started var err error - e.sshServer, err = ssh.DefaultSSHServer(e.config.SSHKey, + e.sshServer, err = e.sshServerFunc(e.config.SSHKey, fmt.Sprintf("%s:%d", e.wgInterface.Address.IP.String(), 2222)) if err != nil { return err diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index 11001446016..f6d86ac65f9 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -3,6 +3,7 @@ package internal import ( "context" "fmt" + "github.com/netbirdio/netbird/client/ssh" "github.com/netbirdio/netbird/iface" "github.com/stretchr/testify/assert" "net" @@ -59,7 +60,21 @@ func TestEngine_SSH(t *testing.T) { WgPrivateKey: key, WgPort: 33100, }) - //engine.wgInterface, err = iface.NewWGIFace("utun100", "100.64.0.1/24", iface.DefaultMTU) + + var sshKeysAdded []string + var sshPeersRemoved []string + + engine.sshServerFunc = func(hostKeyPEM []byte, addr string) (ssh.Server, error) { + return &ssh.MockServer{ + AddAuthorizedKeyFunc: func(peer, newKey string) error { + sshKeysAdded = append(sshKeysAdded, newKey) + return nil + }, + RemoveAuthorizedKeyFunc: func(peer string) { + sshPeersRemoved = append(sshPeersRemoved, peer) + }, + }, nil + } err = engine.Start() if err != nil { t.Fatal(err) @@ -102,12 +117,29 @@ func TestEngine_SSH(t *testing.T) { t.Fatal(err) } - time.Sleep(500 * time.Millisecond) + time.Sleep(250 * time.Millisecond) + assert.NotNil(t, engine.sshServer) + assert.Contains(t, sshKeysAdded, "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFATYCqaQw/9id1Qkq3n16JYhDhXraI6Pc1fgB8ynEfQ") + + // now remove peer + networkMap = &mgmtProto.NetworkMap{ + Serial: 8, + RemotePeers: []*mgmtProto.RemotePeerConfig{}, + RemotePeersIsEmpty: false, + } + + err = engine.updateNetworkMap(networkMap) + if err != nil { + t.Fatal(err) + } + + //time.Sleep(250 * time.Millisecond) assert.NotNil(t, engine.sshServer) + assert.Contains(t, sshPeersRemoved, "MNHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=") // now disable SSH server networkMap = &mgmtProto.NetworkMap{ - Serial: 8, + Serial: 9, PeerConfig: &mgmtProto.PeerConfig{Address: "100.64.0.1/24", SshConfig: &mgmtProto.SSHConfig{SshEnabled: false}}, RemotePeers: []*mgmtProto.RemotePeerConfig{peerWithSSH}, diff --git a/client/ssh/server.go b/client/ssh/server.go index 44b36d8dc6e..b47f4d1730f 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -21,7 +21,12 @@ func init() { } } -// Server is an interface of a SSH server +// DefaultSSHServer is a function that creates DefaultServer +func DefaultSSHServer(hostKeyPEM []byte, addr string) (Server, error) { + return newDefaultServer(hostKeyPEM, addr) +} + +// Server is an interface of SSH server type Server interface { // Stop stops SSH server. Stop() error @@ -42,8 +47,8 @@ type DefaultServer struct { hostKeyPEM []byte } -// DefaultSSHServer creates new server with provided host key -func DefaultSSHServer(hostKeyPEM []byte, addr string) (*DefaultServer, error) { +// newDefaultServer creates new server with provided host key +func newDefaultServer(hostKeyPEM []byte, addr string) (*DefaultServer, error) { ln, err := net.Listen("tcp", addr) if err != nil { return nil, err diff --git a/client/ssh/server_mock.go b/client/ssh/server_mock.go new file mode 100644 index 00000000000..8aac426271d --- /dev/null +++ b/client/ssh/server_mock.go @@ -0,0 +1,41 @@ +package ssh + +// MockServer mocks ssh.Server +type MockServer struct { + StopFunc func() error + StartFunc func() error + AddAuthorizedKeyFunc func(peer, newKey string) error + RemoveAuthorizedKeyFunc func(peer string) +} + +// RemoveAuthorizedKey removes SSH key of a given peer from the authorized keys +func (srv *MockServer) RemoveAuthorizedKey(peer string) { + if srv.RemoveAuthorizedKeyFunc == nil { + return + } + srv.RemoveAuthorizedKeyFunc(peer) +} + +// AddAuthorizedKey add a given peer key to server authorized keys +func (srv *MockServer) AddAuthorizedKey(peer, newKey string) error { + if srv.AddAuthorizedKeyFunc == nil { + return nil + } + return srv.AddAuthorizedKeyFunc(peer, newKey) +} + +// Stop stops SSH server. +func (srv *MockServer) Stop() error { + if srv.StopFunc == nil { + return nil + } + return srv.StopFunc() +} + +// Start starts SSH server. Blocking +func (srv *MockServer) Start() error { + if srv.StartFunc == nil { + return nil + } + return srv.StartFunc() +} diff --git a/client/ssh/server_test.go b/client/ssh/server_test.go index 961bcf3e398..5caca18340e 100644 --- a/client/ssh/server_test.go +++ b/client/ssh/server_test.go @@ -13,7 +13,7 @@ func TestServer_AddAuthorizedKey(t *testing.T) { if err != nil { t.Fatal(err) } - server, err := DefaultSSHServer(key, "localhost:") + server, err := newDefaultServer(key, "localhost:") if err != nil { t.Fatal(err) } @@ -53,7 +53,7 @@ func TestServer_RemoveAuthorizedKey(t *testing.T) { if err != nil { t.Fatal(err) } - server, err := DefaultSSHServer(key, "localhost:") + server, err := newDefaultServer(key, "localhost:") if err != nil { t.Fatal(err) } @@ -83,7 +83,7 @@ func TestServer_PubKeyHandler(t *testing.T) { if err != nil { t.Fatal(err) } - server, err := DefaultSSHServer(key, "localhost:") + server, err := newDefaultServer(key, "localhost:") if err != nil { t.Fatal(err) } From fecb0c9e6e6527287c509187a777aa429ac51ed1 Mon Sep 17 00:00:00 2001 From: braginini Date: Tue, 14 Jun 2022 07:48:04 +0200 Subject: [PATCH 25/51] Stop engine after SSH test finished --- client/internal/engine_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index f6d86ac65f9..3c0e56c3fa4 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -80,6 +80,13 @@ func TestEngine_SSH(t *testing.T) { t.Fatal(err) } + defer func() { + err := engine.Stop() + if err != nil { + return + } + }() + peerWithSSH := &mgmtProto.RemotePeerConfig{ WgPubKey: "MNHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=", AllowedIps: []string{"100.64.0.21/24"}, From 81f475a6104f4f9d8c70830b5355a1cf43564209 Mon Sep 17 00:00:00 2001 From: braginini Date: Tue, 14 Jun 2022 10:47:23 +0200 Subject: [PATCH 26/51] go mod tidy --- go.sum | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/go.sum b/go.sum index 911250dfe59..333fdf26b82 100644 --- a/go.sum +++ b/go.sum @@ -119,7 +119,6 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -651,14 +650,13 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 h1:NUzdAbFtCJSXU20AOXgeqaUwg8Ypg4MPYmL+d+rsB5c= +golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -900,9 +898,6 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a h1:N2T1jUrTQE9Re6TFF5PhvEHXHCguynGhKjWVsIUt5cY= golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= From 49997fdd429f5701c00b53946c186a910a1ac67b Mon Sep 17 00:00:00 2001 From: braginini Date: Tue, 14 Jun 2022 12:38:50 +0200 Subject: [PATCH 27/51] Trigger network map sync when SSH key has been added --- management/server/grpcserver.go | 5 ++-- management/server/peer.go | 47 ++++++++++++++++++++------------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/management/server/grpcserver.go b/management/server/grpcserver.go index 83d238be822..ddc00e27d2a 100644 --- a/management/server/grpcserver.go +++ b/management/server/grpcserver.go @@ -186,8 +186,9 @@ func (s *Server) registerPeer(peerKey wgtypes.Key, req *proto.LoginRequest) (*Pe } peer, err := s.accountManager.AddPeer(reqSetupKey, userId, &Peer{ - Key: peerKey.String(), - Name: meta.GetHostname(), + Key: peerKey.String(), + Name: meta.GetHostname(), + SSHKey: string(req.SshPubKey), Meta: PeerSystemMeta{ Hostname: meta.GetHostname(), GoOS: meta.GetGoOS(), diff --git a/management/server/peer.go b/management/server/peer.go index d170a2f2757..f3d46cf9d8c 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -49,19 +49,22 @@ type Peer struct { UserID string // SSHKey is a public SSH key of the peer SSHKey string + // SSHEnabled indicated whether SSH server is enabled on the peer + SSHEnabled bool } // Copy copies Peer object func (p *Peer) Copy() *Peer { return &Peer{ - Key: p.Key, - SetupKey: p.SetupKey, - IP: p.IP, - Meta: p.Meta, - Name: p.Name, - Status: p.Status, - UserID: p.UserID, - SSHKey: p.SSHKey, + Key: p.Key, + SetupKey: p.SetupKey, + IP: p.IP, + Meta: p.Meta, + Name: p.Name, + Status: p.Status, + UserID: p.UserID, + SSHKey: p.SSHKey, + SSHEnabled: p.SSHEnabled, } } @@ -288,13 +291,15 @@ func (am *DefaultAccountManager) AddPeer( } newPeer := &Peer{ - Key: peer.Key, - SetupKey: upperKey, - IP: nextIp, - Meta: peer.Meta, - Name: peer.Name, - UserID: userID, - Status: &PeerStatus{Connected: false, LastSeen: time.Now()}, + Key: peer.Key, + SetupKey: upperKey, + IP: nextIp, + Meta: peer.Meta, + Name: peer.Name, + UserID: userID, + Status: &PeerStatus{Connected: false, LastSeen: time.Now()}, + SSHEnabled: false, + SSHKey: peer.SSHKey, } // add peer to 'All' group @@ -340,7 +345,9 @@ func (am *DefaultAccountManager) UpdatePeerSSHKey(peerKey string, sshKey string) if err != nil { return err } - return nil + + // trigger network map update + return am.updateAccountPeers(account) } // UpdatePeerMeta updates peer's system metadata @@ -373,7 +380,7 @@ func (am *DefaultAccountManager) UpdatePeerMeta(peerKey string, meta PeerSystemM return nil } -// getPeersByACL allowed for given peer by ACL +// getPeersByACL returns all peers that given peer has access to. func (am *DefaultAccountManager) getPeersByACL(account *Account, peerKey string) []*Peer { var peers []*Peer srcRules, err := am.Store.GetPeerSrcRules(account.Id, peerKey) @@ -437,7 +444,8 @@ func (am *DefaultAccountManager) getPeersByACL(account *Account, peerKey string) return peers } -// updateAccountPeers network map constructed by ACL +// updateAccountPeers updates all peers that belong to an account. +// Should be called when changes have to be synced to peers. func (am *DefaultAccountManager) updateAccountPeers(account *Account) error { // notify other peers of the change peers, err := am.Store.GetAccountPeers(account.Id) @@ -450,7 +458,7 @@ func (am *DefaultAccountManager) updateAccountPeers(account *Account) error { err = am.peersUpdateManager.SendUpdate(p.Key, &UpdateMessage{ Update: &proto.SyncResponse{ - // fill those field for backward compatibility + // fill deprecated fields for backward compatibility RemotePeers: update, RemotePeersIsEmpty: len(update) == 0, // new field @@ -458,6 +466,7 @@ func (am *DefaultAccountManager) updateAccountPeers(account *Account) error { Serial: account.Network.CurrentSerial(), RemotePeers: update, RemotePeersIsEmpty: len(update) == 0, + PeerConfig: toPeerConfig(p), }, }, }) From 59444b41f3da85713e38502841da73e9595df808 Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 15 Jun 2022 10:55:51 +0200 Subject: [PATCH 28/51] Fix shell type when SSH to the embedded server --- client/ssh/client.go | 10 +++++----- client/ssh/server.go | 19 +++++++++---------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/client/ssh/client.go b/client/ssh/client.go index 18c88fbff81..2a93fd0152a 100644 --- a/client/ssh/client.go +++ b/client/ssh/client.go @@ -18,7 +18,7 @@ func (c *Client) Close() error { return c.client.Close() } -// OpenTerminal starts interactive terminal session with the remote SSH server +// OpenTerminal starts an interactive terminal session with the remote SSH server func (c *Client) OpenTerminal() error { session, err := c.client.NewSession() if err != nil { @@ -54,11 +54,11 @@ func (c *Client) OpenTerminal() error { ssh.TTY_OP_OSPEED: 14400, } - term := os.Getenv("TERM") - if term == "" { - term = "xterm-256color" + terminal := os.Getenv("TERM") + if terminal == "" { + terminal = "xterm-256color" } - if err := session.RequestPty(term, h, w, modes); err != nil { + if err := session.RequestPty(terminal, h, w, modes); err != nil { return fmt.Errorf("failed requesting pty session with xterm: %s", err) } diff --git a/client/ssh/server.go b/client/ssh/server.go index b47f4d1730f..57bb430fcdd 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -12,15 +12,6 @@ import ( "sync" ) -var shell string - -func init() { - shell = os.Getenv("SHELL") - if shell == "" { - shell = "sh" - } -} - // DefaultSSHServer is a function that creates DefaultServer func DefaultSSHServer(hostKeyPEM []byte, addr string) (Server, error) { return newDefaultServer(hostKeyPEM, addr) @@ -101,11 +92,19 @@ func (srv *DefaultServer) publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) b return false } +func getShellType() string { + shell := os.Getenv("SHELL") + if shell == "" { + shell = "sh" + } + return shell +} + // sessionHandler handles SSH session post auth func (srv *DefaultServer) sessionHandler(s ssh.Session) { ptyReq, winCh, isPty := s.Pty() if isPty { - cmd := exec.Command(shell) + cmd := exec.Command(getShellType()) cmd.Env = append(cmd.Env, fmt.Sprintf("TERM=%s", ptyReq.Term)) f, err := pty.Start(cmd) if err != nil { From 6bc11dbff7d48337046b8d62ecaf28d126812acd Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 15 Jun 2022 11:12:29 +0200 Subject: [PATCH 29/51] Use a separate obj for peer keys in gRPC protocol --- management/client/grpc.go | 12 +- management/proto/management.pb.go | 581 +++++++++++++++++------------- management/proto/management.proto | 13 +- management/server/grpcserver.go | 16 +- management/server/peer.go | 5 + 5 files changed, 369 insertions(+), 258 deletions(-) diff --git a/management/client/grpc.go b/management/client/grpc.go index 247c46a4951..117263aa73d 100644 --- a/management/client/grpc.go +++ b/management/client/grpc.go @@ -234,12 +234,20 @@ func (c *GrpcClient) login(serverKey wgtypes.Key, req *proto.LoginRequest) (*pro // Takes care of encrypting and decrypting messages. // This method will also collect system info and send it with the request (e.g. hostname, os, etc) func (c *GrpcClient) Register(serverKey wgtypes.Key, setupKey string, jwtToken string, sysInfo *system.Info, sshKey []byte) (*proto.LoginResponse, error) { - return c.login(serverKey, &proto.LoginRequest{SetupKey: setupKey, Meta: infoToMetaData(sysInfo), JwtToken: jwtToken, SshPubKey: sshKey}) + keys := &proto.PeerKeys{ + SshPubKey: sshKey, + WgPubKey: []byte(c.key.PublicKey().String()), + } + return c.login(serverKey, &proto.LoginRequest{SetupKey: setupKey, Meta: infoToMetaData(sysInfo), JwtToken: jwtToken, PeerKeys: keys}) } // Login attempts login to Management Server. Takes care of encrypting and decrypting messages. func (c *GrpcClient) Login(serverKey wgtypes.Key, sysInfo *system.Info, sshKey []byte) (*proto.LoginResponse, error) { - return c.login(serverKey, &proto.LoginRequest{Meta: infoToMetaData(sysInfo), SshPubKey: sshKey}) + keys := &proto.PeerKeys{ + SshPubKey: sshKey, + WgPubKey: []byte(c.key.PublicKey().String()), + } + return c.login(serverKey, &proto.LoginRequest{Meta: infoToMetaData(sysInfo), PeerKeys: keys}) } // GetDeviceAuthorizationFlow returns a device authorization flow information. diff --git a/management/proto/management.pb.go b/management/proto/management.pb.go index c30898fb3c9..8fae8560ffd 100644 --- a/management/proto/management.pb.go +++ b/management/proto/management.pb.go @@ -73,7 +73,7 @@ func (x HostConfig_Protocol) Number() protoreflect.EnumNumber { // Deprecated: Use HostConfig_Protocol.Descriptor instead. func (HostConfig_Protocol) EnumDescriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{9, 0} + return file_management_proto_rawDescGZIP(), []int{10, 0} } type DeviceAuthorizationFlowProvider int32 @@ -116,7 +116,7 @@ func (x DeviceAuthorizationFlowProvider) Number() protoreflect.EnumNumber { // Deprecated: Use DeviceAuthorizationFlowProvider.Descriptor instead. func (DeviceAuthorizationFlowProvider) EnumDescriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{16, 0} + return file_management_proto_rawDescGZIP(), []int{17, 0} } type EncryptedMessage struct { @@ -319,8 +319,8 @@ type LoginRequest struct { Meta *PeerSystemMeta `protobuf:"bytes,2,opt,name=meta,proto3" json:"meta,omitempty"` // SSO token (can be empty) JwtToken string `protobuf:"bytes,3,opt,name=jwtToken,proto3" json:"jwtToken,omitempty"` - // sshPubKey represents a public SSH key of the peer. Can be absent. - SshPubKey []byte `protobuf:"bytes,4,opt,name=sshPubKey,proto3" json:"sshPubKey,omitempty"` + // Can be absent for now. + PeerKeys *PeerKeys `protobuf:"bytes,4,opt,name=peerKeys,proto3" json:"peerKeys,omitempty"` } func (x *LoginRequest) Reset() { @@ -376,14 +376,73 @@ func (x *LoginRequest) GetJwtToken() string { return "" } -func (x *LoginRequest) GetSshPubKey() []byte { +func (x *LoginRequest) GetPeerKeys() *PeerKeys { + if x != nil { + return x.PeerKeys + } + return nil +} + +// PeerKeys is additional peer info like SSH pub key and WireGuard public key. +// This message is sent on Login or register requests, or when a key rotation has to happen. +type PeerKeys struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // sshPubKey represents a public SSH key of the peer. Can be absent. + SshPubKey []byte `protobuf:"bytes,1,opt,name=sshPubKey,proto3" json:"sshPubKey,omitempty"` + // wgPubKey represents a public WireGuard key of the peer. Can be absent. + WgPubKey []byte `protobuf:"bytes,2,opt,name=wgPubKey,proto3" json:"wgPubKey,omitempty"` +} + +func (x *PeerKeys) Reset() { + *x = PeerKeys{} + if protoimpl.UnsafeEnabled { + mi := &file_management_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PeerKeys) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PeerKeys) ProtoMessage() {} + +func (x *PeerKeys) ProtoReflect() protoreflect.Message { + mi := &file_management_proto_msgTypes[4] + 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 PeerKeys.ProtoReflect.Descriptor instead. +func (*PeerKeys) Descriptor() ([]byte, []int) { + return file_management_proto_rawDescGZIP(), []int{4} +} + +func (x *PeerKeys) GetSshPubKey() []byte { if x != nil { return x.SshPubKey } return nil } -// Peer machine meta data +func (x *PeerKeys) GetWgPubKey() []byte { + if x != nil { + return x.WgPubKey + } + return nil +} + +// PeerSystemMeta is machine meta data like OS and version. type PeerSystemMeta struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -402,7 +461,7 @@ type PeerSystemMeta struct { func (x *PeerSystemMeta) Reset() { *x = PeerSystemMeta{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[4] + mi := &file_management_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -415,7 +474,7 @@ func (x *PeerSystemMeta) String() string { func (*PeerSystemMeta) ProtoMessage() {} func (x *PeerSystemMeta) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[4] + mi := &file_management_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -428,7 +487,7 @@ func (x *PeerSystemMeta) ProtoReflect() protoreflect.Message { // Deprecated: Use PeerSystemMeta.ProtoReflect.Descriptor instead. func (*PeerSystemMeta) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{4} + return file_management_proto_rawDescGZIP(), []int{5} } func (x *PeerSystemMeta) GetHostname() string { @@ -501,7 +560,7 @@ type LoginResponse struct { func (x *LoginResponse) Reset() { *x = LoginResponse{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[5] + mi := &file_management_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -514,7 +573,7 @@ func (x *LoginResponse) String() string { func (*LoginResponse) ProtoMessage() {} func (x *LoginResponse) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[5] + mi := &file_management_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -527,7 +586,7 @@ func (x *LoginResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use LoginResponse.ProtoReflect.Descriptor instead. func (*LoginResponse) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{5} + return file_management_proto_rawDescGZIP(), []int{6} } func (x *LoginResponse) GetWiretrusteeConfig() *WiretrusteeConfig { @@ -560,7 +619,7 @@ type ServerKeyResponse struct { func (x *ServerKeyResponse) Reset() { *x = ServerKeyResponse{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[6] + mi := &file_management_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -573,7 +632,7 @@ func (x *ServerKeyResponse) String() string { func (*ServerKeyResponse) ProtoMessage() {} func (x *ServerKeyResponse) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[6] + mi := &file_management_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -586,7 +645,7 @@ func (x *ServerKeyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ServerKeyResponse.ProtoReflect.Descriptor instead. func (*ServerKeyResponse) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{6} + return file_management_proto_rawDescGZIP(), []int{7} } func (x *ServerKeyResponse) GetKey() string { @@ -619,7 +678,7 @@ type Empty struct { func (x *Empty) Reset() { *x = Empty{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[7] + mi := &file_management_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -632,7 +691,7 @@ func (x *Empty) String() string { func (*Empty) ProtoMessage() {} func (x *Empty) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[7] + mi := &file_management_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -645,7 +704,7 @@ func (x *Empty) ProtoReflect() protoreflect.Message { // Deprecated: Use Empty.ProtoReflect.Descriptor instead. func (*Empty) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{7} + return file_management_proto_rawDescGZIP(), []int{8} } // WiretrusteeConfig is a common configuration of any Wiretrustee peer. It contains STUN, TURN, Signal and Management servers configurations @@ -665,7 +724,7 @@ type WiretrusteeConfig struct { func (x *WiretrusteeConfig) Reset() { *x = WiretrusteeConfig{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[8] + mi := &file_management_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -678,7 +737,7 @@ func (x *WiretrusteeConfig) String() string { func (*WiretrusteeConfig) ProtoMessage() {} func (x *WiretrusteeConfig) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[8] + mi := &file_management_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -691,7 +750,7 @@ func (x *WiretrusteeConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use WiretrusteeConfig.ProtoReflect.Descriptor instead. func (*WiretrusteeConfig) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{8} + return file_management_proto_rawDescGZIP(), []int{9} } func (x *WiretrusteeConfig) GetStuns() []*HostConfig { @@ -729,7 +788,7 @@ type HostConfig struct { func (x *HostConfig) Reset() { *x = HostConfig{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[9] + mi := &file_management_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -742,7 +801,7 @@ func (x *HostConfig) String() string { func (*HostConfig) ProtoMessage() {} func (x *HostConfig) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[9] + mi := &file_management_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -755,7 +814,7 @@ func (x *HostConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use HostConfig.ProtoReflect.Descriptor instead. func (*HostConfig) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{9} + return file_management_proto_rawDescGZIP(), []int{10} } func (x *HostConfig) GetUri() string { @@ -787,7 +846,7 @@ type ProtectedHostConfig struct { func (x *ProtectedHostConfig) Reset() { *x = ProtectedHostConfig{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[10] + mi := &file_management_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -800,7 +859,7 @@ func (x *ProtectedHostConfig) String() string { func (*ProtectedHostConfig) ProtoMessage() {} func (x *ProtectedHostConfig) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[10] + mi := &file_management_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -813,7 +872,7 @@ func (x *ProtectedHostConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use ProtectedHostConfig.ProtoReflect.Descriptor instead. func (*ProtectedHostConfig) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{10} + return file_management_proto_rawDescGZIP(), []int{11} } func (x *ProtectedHostConfig) GetHostConfig() *HostConfig { @@ -855,7 +914,7 @@ type PeerConfig struct { func (x *PeerConfig) Reset() { *x = PeerConfig{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[11] + mi := &file_management_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -868,7 +927,7 @@ func (x *PeerConfig) String() string { func (*PeerConfig) ProtoMessage() {} func (x *PeerConfig) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[11] + mi := &file_management_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -881,7 +940,7 @@ func (x *PeerConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use PeerConfig.ProtoReflect.Descriptor instead. func (*PeerConfig) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{11} + return file_management_proto_rawDescGZIP(), []int{12} } func (x *PeerConfig) GetAddress() string { @@ -926,7 +985,7 @@ type NetworkMap struct { func (x *NetworkMap) Reset() { *x = NetworkMap{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[12] + mi := &file_management_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -939,7 +998,7 @@ func (x *NetworkMap) String() string { func (*NetworkMap) ProtoMessage() {} func (x *NetworkMap) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[12] + mi := &file_management_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -952,7 +1011,7 @@ func (x *NetworkMap) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkMap.ProtoReflect.Descriptor instead. func (*NetworkMap) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{12} + return file_management_proto_rawDescGZIP(), []int{13} } func (x *NetworkMap) GetSerial() uint64 { @@ -1001,7 +1060,7 @@ type RemotePeerConfig struct { func (x *RemotePeerConfig) Reset() { *x = RemotePeerConfig{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[13] + mi := &file_management_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1014,7 +1073,7 @@ func (x *RemotePeerConfig) String() string { func (*RemotePeerConfig) ProtoMessage() {} func (x *RemotePeerConfig) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[13] + mi := &file_management_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1027,7 +1086,7 @@ func (x *RemotePeerConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use RemotePeerConfig.ProtoReflect.Descriptor instead. func (*RemotePeerConfig) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{13} + return file_management_proto_rawDescGZIP(), []int{14} } func (x *RemotePeerConfig) GetWgPubKey() string { @@ -1067,7 +1126,7 @@ type SSHConfig struct { func (x *SSHConfig) Reset() { *x = SSHConfig{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[14] + mi := &file_management_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1080,7 +1139,7 @@ func (x *SSHConfig) String() string { func (*SSHConfig) ProtoMessage() {} func (x *SSHConfig) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[14] + mi := &file_management_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1093,7 +1152,7 @@ func (x *SSHConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use SSHConfig.ProtoReflect.Descriptor instead. func (*SSHConfig) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{14} + return file_management_proto_rawDescGZIP(), []int{15} } func (x *SSHConfig) GetSshEnabled() bool { @@ -1120,7 +1179,7 @@ type DeviceAuthorizationFlowRequest struct { func (x *DeviceAuthorizationFlowRequest) Reset() { *x = DeviceAuthorizationFlowRequest{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[15] + mi := &file_management_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1133,7 +1192,7 @@ func (x *DeviceAuthorizationFlowRequest) String() string { func (*DeviceAuthorizationFlowRequest) ProtoMessage() {} func (x *DeviceAuthorizationFlowRequest) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[15] + mi := &file_management_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1146,7 +1205,7 @@ func (x *DeviceAuthorizationFlowRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeviceAuthorizationFlowRequest.ProtoReflect.Descriptor instead. func (*DeviceAuthorizationFlowRequest) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{15} + return file_management_proto_rawDescGZIP(), []int{16} } // DeviceAuthorizationFlow represents Device Authorization Flow information @@ -1165,7 +1224,7 @@ type DeviceAuthorizationFlow struct { func (x *DeviceAuthorizationFlow) Reset() { *x = DeviceAuthorizationFlow{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[16] + mi := &file_management_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1178,7 +1237,7 @@ func (x *DeviceAuthorizationFlow) String() string { func (*DeviceAuthorizationFlow) ProtoMessage() {} func (x *DeviceAuthorizationFlow) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[16] + mi := &file_management_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1191,7 +1250,7 @@ func (x *DeviceAuthorizationFlow) ProtoReflect() protoreflect.Message { // Deprecated: Use DeviceAuthorizationFlow.ProtoReflect.Descriptor instead. func (*DeviceAuthorizationFlow) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{16} + return file_management_proto_rawDescGZIP(), []int{17} } func (x *DeviceAuthorizationFlow) GetProvider() DeviceAuthorizationFlowProvider { @@ -1227,7 +1286,7 @@ type ProviderConfig struct { func (x *ProviderConfig) Reset() { *x = ProviderConfig{} if protoimpl.UnsafeEnabled { - mi := &file_management_proto_msgTypes[17] + mi := &file_management_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1240,7 +1299,7 @@ func (x *ProviderConfig) String() string { func (*ProviderConfig) ProtoMessage() {} func (x *ProviderConfig) ProtoReflect() protoreflect.Message { - mi := &file_management_proto_msgTypes[17] + mi := &file_management_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1253,7 +1312,7 @@ func (x *ProviderConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderConfig.ProtoReflect.Descriptor instead. func (*ProviderConfig) Descriptor() ([]byte, []int) { - return file_management_proto_rawDescGZIP(), []int{17} + return file_management_proto_rawDescGZIP(), []int{18} } func (x *ProviderConfig) GetClientID() string { @@ -1317,7 +1376,7 @@ var file_management_proto_rawDesc = []byte{ 0x74, 0x79, 0x12, 0x36, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x52, 0x0a, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x22, 0x94, 0x01, 0x0a, 0x0c, 0x4c, + 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x22, 0xa8, 0x01, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, @@ -1325,149 +1384,155 @@ var file_management_proto_rawDesc = []byte{ 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x6a, 0x77, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x77, 0x74, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, - 0x79, 0x22, 0xe6, 0x01, 0x0a, 0x0e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x67, 0x6f, 0x4f, 0x53, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x67, 0x6f, 0x4f, 0x53, 0x12, 0x16, 0x0a, 0x06, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, - 0x63, 0x6f, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x72, 0x65, - 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x0e, 0x0a, 0x02, - 0x4f, 0x53, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x4f, 0x53, 0x12, 0x2e, 0x0a, 0x12, - 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, - 0x75, 0x73, 0x74, 0x65, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, - 0x75, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x75, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x94, 0x01, 0x0a, 0x0d, 0x4c, - 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x11, - 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x57, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, - 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x65, 0x65, - 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x22, 0x79, 0x0a, 0x11, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x38, 0x0a, 0x09, 0x65, 0x78, 0x70, 0x69, - 0x72, 0x65, 0x73, 0x41, 0x74, 0x18, 0x02, 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, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, - 0x41, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x07, 0x0a, 0x05, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0xa8, 0x01, 0x0a, 0x11, 0x57, 0x69, 0x72, 0x65, 0x74, 0x72, - 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2c, 0x0a, 0x05, 0x73, - 0x74, 0x75, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, + 0x6b, 0x65, 0x6e, 0x12, 0x30, 0x0a, 0x08, 0x70, 0x65, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x08, 0x70, 0x65, 0x65, + 0x72, 0x4b, 0x65, 0x79, 0x73, 0x22, 0x44, 0x0a, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4b, 0x65, 0x79, + 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, + 0x1a, 0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0xe6, 0x01, 0x0a, 0x0e, + 0x50, 0x65, 0x65, 0x72, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1a, + 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x6f, + 0x4f, 0x53, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x67, 0x6f, 0x4f, 0x53, 0x12, 0x16, + 0x0a, 0x06, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, + 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, + 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x4f, 0x53, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x4f, 0x53, 0x12, 0x2e, 0x0a, 0x12, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x65, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x12, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x69, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x69, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x94, 0x01, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x11, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x57, + 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x52, 0x11, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x79, 0x0a, 0x11, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x38, 0x0a, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x18, + 0x02, 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, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x12, 0x18, 0x0a, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, + 0xa8, 0x01, 0x0a, 0x11, 0x57, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2c, 0x0a, 0x05, 0x73, 0x74, 0x75, 0x6e, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x73, 0x74, + 0x75, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x05, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x52, 0x05, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x05, 0x73, 0x74, 0x75, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x05, 0x74, 0x75, 0x72, - 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x48, - 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x74, 0x75, 0x72, 0x6e, 0x73, - 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x69, 0x67, 0x52, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x22, 0x98, 0x01, 0x0a, 0x0a, 0x48, + 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x3b, 0x0a, 0x08, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x3b, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, + 0x03, 0x54, 0x43, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x02, + 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x44, + 0x54, 0x4c, 0x53, 0x10, 0x04, 0x22, 0x7d, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a, + 0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, - 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, - 0x22, 0x98, 0x01, 0x0a, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, - 0x69, 0x12, 0x3b, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x3b, - 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, - 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, - 0x48, 0x54, 0x54, 0x50, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, - 0x03, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x54, 0x4c, 0x53, 0x10, 0x04, 0x22, 0x7d, 0x0a, 0x13, 0x50, - 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, - 0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, - 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1a, - 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x6d, 0x0a, 0x0a, 0x50, 0x65, - 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x64, 0x6e, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, - 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xcc, 0x01, 0x0a, 0x0a, 0x4e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x69, - 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, - 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x65, - 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3e, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, - 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, - 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x72, 0x65, 0x6d, - 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x72, 0x65, 0x6d, 0x6f, - 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, - 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x83, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6d, - 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, - 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, - 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, - 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x73, 0x73, 0x68, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x49, - 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x73, - 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, - 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, - 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x20, 0x0a, 0x1e, 0x44, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xbf, 0x01, 0x0a, 0x17, - 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x48, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x12, 0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x16, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x00, 0x22, 0x84, 0x01, - 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, - 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, - 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x75, 0x64, 0x69, - 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x75, 0x64, 0x69, - 0x65, 0x6e, 0x63, 0x65, 0x32, 0xf7, 0x02, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x4c, 0x6f, - 0x67, 0x69, 0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, + 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, + 0x77, 0x6f, 0x72, 0x64, 0x22, 0x6d, 0x0a, 0x0a, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, + 0x64, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6e, 0x73, 0x12, 0x33, + 0x0a, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, + 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x22, 0xcc, 0x01, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, + 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x65, + 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x3e, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, + 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, + 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, + 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x83, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, + 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, + 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, + 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, + 0x49, 0x70, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x73, + 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x49, 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, + 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, + 0x4b, 0x65, 0x79, 0x22, 0x20, 0x0a, 0x1e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xbf, 0x01, 0x0a, 0x17, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, + 0x77, 0x12, 0x48, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x0e, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, + 0x16, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x48, + 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x00, 0x22, 0x84, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x43, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x32, 0xf7, + 0x02, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1c, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x04, 0x53, + 0x79, 0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, - 0x00, 0x12, 0x46, 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, - 0x09, 0x69, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, - 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, - 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, - 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x42, 0x08, - 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, 0x69, 0x73, 0x48, 0x65, 0x61, + 0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x1a, + 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, + 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1483,7 +1548,7 @@ func file_management_proto_rawDescGZIP() []byte { } var file_management_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 18) +var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 19) var file_management_proto_goTypes = []interface{}{ (HostConfig_Protocol)(0), // 0: management.HostConfig.Protocol (DeviceAuthorizationFlowProvider)(0), // 1: management.DeviceAuthorizationFlow.provider @@ -1491,57 +1556,59 @@ var file_management_proto_goTypes = []interface{}{ (*SyncRequest)(nil), // 3: management.SyncRequest (*SyncResponse)(nil), // 4: management.SyncResponse (*LoginRequest)(nil), // 5: management.LoginRequest - (*PeerSystemMeta)(nil), // 6: management.PeerSystemMeta - (*LoginResponse)(nil), // 7: management.LoginResponse - (*ServerKeyResponse)(nil), // 8: management.ServerKeyResponse - (*Empty)(nil), // 9: management.Empty - (*WiretrusteeConfig)(nil), // 10: management.WiretrusteeConfig - (*HostConfig)(nil), // 11: management.HostConfig - (*ProtectedHostConfig)(nil), // 12: management.ProtectedHostConfig - (*PeerConfig)(nil), // 13: management.PeerConfig - (*NetworkMap)(nil), // 14: management.NetworkMap - (*RemotePeerConfig)(nil), // 15: management.RemotePeerConfig - (*SSHConfig)(nil), // 16: management.SSHConfig - (*DeviceAuthorizationFlowRequest)(nil), // 17: management.DeviceAuthorizationFlowRequest - (*DeviceAuthorizationFlow)(nil), // 18: management.DeviceAuthorizationFlow - (*ProviderConfig)(nil), // 19: management.ProviderConfig - (*timestamp.Timestamp)(nil), // 20: google.protobuf.Timestamp + (*PeerKeys)(nil), // 6: management.PeerKeys + (*PeerSystemMeta)(nil), // 7: management.PeerSystemMeta + (*LoginResponse)(nil), // 8: management.LoginResponse + (*ServerKeyResponse)(nil), // 9: management.ServerKeyResponse + (*Empty)(nil), // 10: management.Empty + (*WiretrusteeConfig)(nil), // 11: management.WiretrusteeConfig + (*HostConfig)(nil), // 12: management.HostConfig + (*ProtectedHostConfig)(nil), // 13: management.ProtectedHostConfig + (*PeerConfig)(nil), // 14: management.PeerConfig + (*NetworkMap)(nil), // 15: management.NetworkMap + (*RemotePeerConfig)(nil), // 16: management.RemotePeerConfig + (*SSHConfig)(nil), // 17: management.SSHConfig + (*DeviceAuthorizationFlowRequest)(nil), // 18: management.DeviceAuthorizationFlowRequest + (*DeviceAuthorizationFlow)(nil), // 19: management.DeviceAuthorizationFlow + (*ProviderConfig)(nil), // 20: management.ProviderConfig + (*timestamp.Timestamp)(nil), // 21: google.protobuf.Timestamp } var file_management_proto_depIdxs = []int32{ - 10, // 0: management.SyncResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig - 13, // 1: management.SyncResponse.peerConfig:type_name -> management.PeerConfig - 15, // 2: management.SyncResponse.remotePeers:type_name -> management.RemotePeerConfig - 14, // 3: management.SyncResponse.NetworkMap:type_name -> management.NetworkMap - 6, // 4: management.LoginRequest.meta:type_name -> management.PeerSystemMeta - 10, // 5: management.LoginResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig - 13, // 6: management.LoginResponse.peerConfig:type_name -> management.PeerConfig - 20, // 7: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp - 11, // 8: management.WiretrusteeConfig.stuns:type_name -> management.HostConfig - 12, // 9: management.WiretrusteeConfig.turns:type_name -> management.ProtectedHostConfig - 11, // 10: management.WiretrusteeConfig.signal:type_name -> management.HostConfig - 0, // 11: management.HostConfig.protocol:type_name -> management.HostConfig.Protocol - 11, // 12: management.ProtectedHostConfig.hostConfig:type_name -> management.HostConfig - 16, // 13: management.PeerConfig.sshConfig:type_name -> management.SSHConfig - 13, // 14: management.NetworkMap.peerConfig:type_name -> management.PeerConfig - 15, // 15: management.NetworkMap.remotePeers:type_name -> management.RemotePeerConfig - 16, // 16: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig - 1, // 17: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider - 19, // 18: management.DeviceAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig - 2, // 19: management.ManagementService.Login:input_type -> management.EncryptedMessage - 2, // 20: management.ManagementService.Sync:input_type -> management.EncryptedMessage - 9, // 21: management.ManagementService.GetServerKey:input_type -> management.Empty - 9, // 22: management.ManagementService.isHealthy:input_type -> management.Empty - 2, // 23: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage - 2, // 24: management.ManagementService.Login:output_type -> management.EncryptedMessage - 2, // 25: management.ManagementService.Sync:output_type -> management.EncryptedMessage - 8, // 26: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse - 9, // 27: management.ManagementService.isHealthy:output_type -> management.Empty - 2, // 28: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage - 24, // [24:29] is the sub-list for method output_type - 19, // [19:24] is the sub-list for method input_type - 19, // [19:19] is the sub-list for extension type_name - 19, // [19:19] is the sub-list for extension extendee - 0, // [0:19] is the sub-list for field type_name + 11, // 0: management.SyncResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig + 14, // 1: management.SyncResponse.peerConfig:type_name -> management.PeerConfig + 16, // 2: management.SyncResponse.remotePeers:type_name -> management.RemotePeerConfig + 15, // 3: management.SyncResponse.NetworkMap:type_name -> management.NetworkMap + 7, // 4: management.LoginRequest.meta:type_name -> management.PeerSystemMeta + 6, // 5: management.LoginRequest.peerKeys:type_name -> management.PeerKeys + 11, // 6: management.LoginResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig + 14, // 7: management.LoginResponse.peerConfig:type_name -> management.PeerConfig + 21, // 8: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp + 12, // 9: management.WiretrusteeConfig.stuns:type_name -> management.HostConfig + 13, // 10: management.WiretrusteeConfig.turns:type_name -> management.ProtectedHostConfig + 12, // 11: management.WiretrusteeConfig.signal:type_name -> management.HostConfig + 0, // 12: management.HostConfig.protocol:type_name -> management.HostConfig.Protocol + 12, // 13: management.ProtectedHostConfig.hostConfig:type_name -> management.HostConfig + 17, // 14: management.PeerConfig.sshConfig:type_name -> management.SSHConfig + 14, // 15: management.NetworkMap.peerConfig:type_name -> management.PeerConfig + 16, // 16: management.NetworkMap.remotePeers:type_name -> management.RemotePeerConfig + 17, // 17: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig + 1, // 18: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider + 20, // 19: management.DeviceAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig + 2, // 20: management.ManagementService.Login:input_type -> management.EncryptedMessage + 2, // 21: management.ManagementService.Sync:input_type -> management.EncryptedMessage + 10, // 22: management.ManagementService.GetServerKey:input_type -> management.Empty + 10, // 23: management.ManagementService.isHealthy:input_type -> management.Empty + 2, // 24: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage + 2, // 25: management.ManagementService.Login:output_type -> management.EncryptedMessage + 2, // 26: management.ManagementService.Sync:output_type -> management.EncryptedMessage + 9, // 27: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse + 10, // 28: management.ManagementService.isHealthy:output_type -> management.Empty + 2, // 29: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage + 25, // [25:30] is the sub-list for method output_type + 20, // [20:25] is the sub-list for method input_type + 20, // [20:20] is the sub-list for extension type_name + 20, // [20:20] is the sub-list for extension extendee + 0, // [0:20] is the sub-list for field type_name } func init() { file_management_proto_init() } @@ -1599,7 +1666,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeerSystemMeta); i { + switch v := v.(*PeerKeys); i { case 0: return &v.state case 1: @@ -1611,7 +1678,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LoginResponse); i { + switch v := v.(*PeerSystemMeta); i { case 0: return &v.state case 1: @@ -1623,7 +1690,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ServerKeyResponse); i { + switch v := v.(*LoginResponse); i { case 0: return &v.state case 1: @@ -1635,7 +1702,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Empty); i { + switch v := v.(*ServerKeyResponse); i { case 0: return &v.state case 1: @@ -1647,7 +1714,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WiretrusteeConfig); i { + switch v := v.(*Empty); i { case 0: return &v.state case 1: @@ -1659,7 +1726,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HostConfig); i { + switch v := v.(*WiretrusteeConfig); i { case 0: return &v.state case 1: @@ -1671,7 +1738,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProtectedHostConfig); i { + switch v := v.(*HostConfig); i { case 0: return &v.state case 1: @@ -1683,7 +1750,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeerConfig); i { + switch v := v.(*ProtectedHostConfig); i { case 0: return &v.state case 1: @@ -1695,7 +1762,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetworkMap); i { + switch v := v.(*PeerConfig); i { case 0: return &v.state case 1: @@ -1707,7 +1774,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemotePeerConfig); i { + switch v := v.(*NetworkMap); i { case 0: return &v.state case 1: @@ -1719,7 +1786,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SSHConfig); i { + switch v := v.(*RemotePeerConfig); i { case 0: return &v.state case 1: @@ -1731,7 +1798,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeviceAuthorizationFlowRequest); i { + switch v := v.(*SSHConfig); i { case 0: return &v.state case 1: @@ -1743,7 +1810,7 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeviceAuthorizationFlow); i { + switch v := v.(*DeviceAuthorizationFlowRequest); i { case 0: return &v.state case 1: @@ -1755,6 +1822,18 @@ func file_management_proto_init() { } } file_management_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeviceAuthorizationFlow); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_management_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ProviderConfig); i { case 0: return &v.state @@ -1773,7 +1852,7 @@ func file_management_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_management_proto_rawDesc, NumEnums: 2, - NumMessages: 18, + NumMessages: 19, NumExtensions: 0, NumServices: 1, }, diff --git a/management/proto/management.proto b/management/proto/management.proto index 67bbea0a925..b9522878a61 100644 --- a/management/proto/management.proto +++ b/management/proto/management.proto @@ -71,12 +71,21 @@ message LoginRequest { PeerSystemMeta meta = 2; // SSO token (can be empty) string jwtToken = 3; + // Can be absent for now. + PeerKeys peerKeys = 4; + +} +// PeerKeys is additional peer info like SSH pub key and WireGuard public key. +// This message is sent on Login or register requests, or when a key rotation has to happen. +message PeerKeys { // sshPubKey represents a public SSH key of the peer. Can be absent. - bytes sshPubKey = 4; + bytes sshPubKey = 1; + // wgPubKey represents a public WireGuard key of the peer. Can be absent. + bytes wgPubKey = 2; } -// Peer machine meta data +// PeerSystemMeta is machine meta data like OS and version. message PeerSystemMeta { string hostname = 1; string goOS = 2; diff --git a/management/server/grpcserver.go b/management/server/grpcserver.go index ddc00e27d2a..dbdf66512ae 100644 --- a/management/server/grpcserver.go +++ b/management/server/grpcserver.go @@ -185,10 +185,15 @@ func (s *Server) registerPeer(peerKey wgtypes.Key, req *proto.LoginRequest) (*Pe return nil, status.Errorf(codes.InvalidArgument, "peer meta data was not provided") } + var sshKey []byte + if req.GetPeerKeys() != nil { + sshKey = req.GetPeerKeys().GetSshPubKey() + } + peer, err := s.accountManager.AddPeer(reqSetupKey, userId, &Peer{ Key: peerKey.String(), Name: meta.GetHostname(), - SSHKey: string(req.SshPubKey), + SSHKey: string(sshKey), Meta: PeerSystemMeta{ Hostname: meta.GetHostname(), GoOS: meta.GetGoOS(), @@ -292,8 +297,13 @@ func (s *Server) Login(ctx context.Context, req *proto.EncryptedMessage) (*proto } } - if loginReq.GetSshPubKey() != nil { - err = s.accountManager.UpdatePeerSSHKey(peerKey.String(), string(loginReq.GetSshPubKey())) + var sshKey []byte + if loginReq.GetPeerKeys() != nil { + sshKey = loginReq.GetPeerKeys().GetSshPubKey() + } + + if len(sshKey) > 0 { + err = s.accountManager.UpdatePeerSSHKey(peerKey.String(), string(sshKey)) if err != nil { return nil, err } diff --git a/management/server/peer.go b/management/server/peer.go index f3d46cf9d8c..8bae1680c85 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -328,6 +328,11 @@ func (am *DefaultAccountManager) UpdatePeerSSHKey(peerKey string, sshKey string) am.mux.Lock() defer am.mux.Unlock() + if sshKey == "" { + log.Debugf("empty SSH key provided for peer %s, skipping update", peerKey) + return nil + } + peer, err := am.Store.GetPeer(peerKey) if err != nil { return err From 09146c787313f09b7c943e074d2e0de4ac0b2d7a Mon Sep 17 00:00:00 2001 From: braginini Date: Thu, 16 Jun 2022 22:44:18 +0200 Subject: [PATCH 30/51] Add UpdatePeer method --- management/server/account.go | 1 + management/server/http/api/openapi.yml | 7 ++++++ management/server/http/api/types.gen.go | 6 ++++- management/server/http/handler/peers.go | 21 +++++++++------- management/server/mock_server/account_mock.go | 9 +++++++ management/server/peer.go | 25 +++++++++++++++++++ 6 files changed, 59 insertions(+), 10 deletions(-) diff --git a/management/server/account.go b/management/server/account.go index 0d1a8a20d4a..5e619bd22cc 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -66,6 +66,7 @@ type AccountManager interface { UpdateRule(accountID string, ruleID string, operations []RuleUpdateOperation) (*Rule, error) DeleteRule(accountId, ruleID string) error ListRules(accountId string) ([]*Rule, error) + UpdatePeer(accountID string, peer *Peer) (*Peer, error) } type DefaultAccountManager struct { diff --git a/management/server/http/api/openapi.yml b/management/server/http/api/openapi.yml index f8af2710546..d5bf580e547 100644 --- a/management/server/http/api/openapi.yml +++ b/management/server/http/api/openapi.yml @@ -85,6 +85,9 @@ components: required: - type - value + ssh_enabled: + description: Indicates whether SSH server is enabled on this peer + type: boolean required: - ip - connected @@ -93,6 +96,7 @@ components: - version - groups - activated_by + - ssh_enabled SetupKey: type: object properties: @@ -397,8 +401,11 @@ paths: properties: name: type: string + ssh_enabled: + type: boolean required: - name + - ssh_enabled responses: '200': description: A Peer object diff --git a/management/server/http/api/types.gen.go b/management/server/http/api/types.gen.go index a58b3352279..8c37a484b97 100644 --- a/management/server/http/api/types.gen.go +++ b/management/server/http/api/types.gen.go @@ -115,6 +115,9 @@ type Peer struct { // Peer's operating system and version Os string `json:"os"` + // Indicates whether SSH server is enabled on this peer + SshEnabled bool `json:"ssh_enabled"` + // Peer's daemon or cli version Version string `json:"version"` } @@ -265,7 +268,8 @@ type PutApiGroupsIdJSONBody struct { // PutApiPeersIdJSONBody defines parameters for PutApiPeersId. type PutApiPeersIdJSONBody struct { - Name string `json:"name"` + Name string `json:"name"` + SshEnabled bool `json:"ssh_enabled"` } // PostApiRulesJSONBody defines parameters for PostApiRules. diff --git a/management/server/http/handler/peers.go b/management/server/http/handler/peers.go index 2765372a4dc..dd7592d2fc8 100644 --- a/management/server/http/handler/peers.go +++ b/management/server/http/handler/peers.go @@ -34,7 +34,9 @@ func (h *Peers) updatePeer(account *server.Account, peer *server.Peer, w http.Re http.Error(w, err.Error(), http.StatusBadRequest) return } - peer, err = h.accountManager.RenamePeer(account.Id, peer.Key, req.Name) + + update := &server.Peer{Key: peer.Key, SSHEnabled: req.SshEnabled, Name: req.Name} + peer, err = h.accountManager.UpdatePeer(account.Id, update) if err != nil { log.Errorf("failed updating peer %s under account %s %v", peerIp, account.Id, err) http.Redirect(w, r, "/", http.StatusInternalServerError) @@ -133,13 +135,14 @@ func toPeerResponse(peer *server.Peer, account *server.Account) *api.Peer { } } return &api.Peer{ - Id: peer.IP.String(), - Name: peer.Name, - Ip: peer.IP.String(), - Connected: peer.Status.Connected, - LastSeen: peer.Status.LastSeen, - Os: fmt.Sprintf("%s %s", peer.Meta.OS, peer.Meta.Core), - Version: peer.Meta.WtVersion, - Groups: groupsInfo, + Id: peer.IP.String(), + Name: peer.Name, + Ip: peer.IP.String(), + Connected: peer.Status.Connected, + LastSeen: peer.Status.LastSeen, + Os: fmt.Sprintf("%s %s", peer.Meta.OS, peer.Meta.Core), + Version: peer.Meta.WtVersion, + Groups: groupsInfo, + SshEnabled: peer.SSHEnabled, } } diff --git a/management/server/mock_server/account_mock.go b/management/server/mock_server/account_mock.go index e945397141b..5e85bf2e619 100644 --- a/management/server/mock_server/account_mock.go +++ b/management/server/mock_server/account_mock.go @@ -42,6 +42,7 @@ type MockAccountManager struct { GetUsersFromAccountFunc func(accountID string) ([]*server.UserInfo, error) UpdatePeerMetaFunc func(peerKey string, meta server.PeerSystemMeta) error UpdatePeerSSHKeyFunc func(peerKey string, sshKey string) error + UpdatePeerFunc func(accountID string, peer *server.Peer) (*server.Peer, error) } // GetUsersFromAccount mock implementation of GetUsersFromAccount from server.AccountManager interface @@ -342,3 +343,11 @@ func (am *MockAccountManager) UpdatePeerSSHKey(peerKey string, sshKey string) er } return status.Errorf(codes.Unimplemented, "method UpdatePeerSSHKey is is not implemented") } + +// UpdatePeer mocks UpdatePeerFunc function of the account manager +func (am *MockAccountManager) UpdatePeer(accountID string, peer *server.Peer) (*server.Peer, error) { + if am.UpdatePeerFunc != nil { + return am.UpdatePeerFunc(accountID, peer) + } + return nil, status.Errorf(codes.Unimplemented, "method UpdatePeerFunc is is not implemented") +} diff --git a/management/server/peer.go b/management/server/peer.go index 8bae1680c85..294ab5d9659 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -106,6 +106,31 @@ func (am *DefaultAccountManager) MarkPeerConnected(peerKey string, connected boo return nil } +// UpdatePeer updates peer. Only Peer.Name and Peer.SSHEnabled can be updated. +func (am *DefaultAccountManager) UpdatePeer(accountID string, update *Peer) (*Peer, error) { + am.mux.Lock() + defer am.mux.Unlock() + + peer, err := am.Store.GetPeer(update.Key) + if err != nil { + return nil, err + } + + peerCopy := peer.Copy() + if peer.Name != "" { + peerCopy.Name = update.Name + } + peerCopy.SSHEnabled = update.SSHEnabled + + err = am.Store.SavePeer(accountID, peerCopy) + if err != nil { + return nil, err + } + + return peerCopy, nil + +} + // RenamePeer changes peer's name func (am *DefaultAccountManager) RenamePeer( accountId string, From 3e740900f9c98acc6fe4f93057519cf353e6a861 Mon Sep 17 00:00:00 2001 From: braginini Date: Tue, 21 Jun 2022 13:46:40 +0200 Subject: [PATCH 31/51] Sync network map when updating peer --- management/server/grpcserver.go | 2 +- management/server/peer.go | 10 ++++++++++ management/server/updatechannel.go | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/management/server/grpcserver.go b/management/server/grpcserver.go index dbdf66512ae..0fa8a40262e 100644 --- a/management/server/grpcserver.go +++ b/management/server/grpcserver.go @@ -385,7 +385,7 @@ func toWiretrusteeConfig(config *Config, turnCredentials *TURNCredentials) *prot func toPeerConfig(peer *Peer) *proto.PeerConfig { return &proto.PeerConfig{ Address: fmt.Sprintf("%s/%d", peer.IP.String(), SubnetSize), // take it from the network - SshConfig: &proto.SSHConfig{SshEnabled: true}, //TODO REMOVE THIS HARDCODED VALUE + SshConfig: &proto.SSHConfig{SshEnabled: peer.SSHEnabled}, } } diff --git a/management/server/peer.go b/management/server/peer.go index 294ab5d9659..feb832ef470 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -127,6 +127,16 @@ func (am *DefaultAccountManager) UpdatePeer(accountID string, update *Peer) (*Pe return nil, err } + account, err := am.GetAccountById(accountID) + if err != nil { + return nil, err + } + + err = am.updateAccountPeers(account) + if err != nil { + return nil, err + } + return peerCopy, nil } diff --git a/management/server/updatechannel.go b/management/server/updatechannel.go index d4e7d922090..ba443c8c1fb 100644 --- a/management/server/updatechannel.go +++ b/management/server/updatechannel.go @@ -9,6 +9,7 @@ import ( type UpdateMessage struct { Update *proto.SyncResponse } + type PeersUpdateManager struct { peerChannels map[string]chan *UpdateMessage channelsMux *sync.Mutex From 20d90594c32d3e2a08ea0feb436fd5bde1237ba2 Mon Sep 17 00:00:00 2001 From: braginini Date: Tue, 21 Jun 2022 17:41:56 +0200 Subject: [PATCH 32/51] Fix deadlock when distributing network updates --- client/cmd/ssh.go | 2 +- management/server/peer.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/cmd/ssh.go b/client/cmd/ssh.go index dc6afa47be8..36a167de958 100644 --- a/client/cmd/ssh.go +++ b/client/cmd/ssh.go @@ -86,7 +86,7 @@ var sshCmd = &cobra.Command{ return nil } - config, err := internal.GetConfig("", "", configPath, "") + config, err := internal.ReadConfig("", "", configPath, nil) if err != nil { return err } diff --git a/management/server/peer.go b/management/server/peer.go index feb832ef470..9fafb346cfd 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -111,6 +111,11 @@ func (am *DefaultAccountManager) UpdatePeer(accountID string, update *Peer) (*Pe am.mux.Lock() defer am.mux.Unlock() + account, err := am.Store.GetAccount(accountID) + if err != nil { + return nil, status.Errorf(codes.NotFound, "account not found") + } + peer, err := am.Store.GetPeer(update.Key) if err != nil { return nil, err @@ -127,11 +132,6 @@ func (am *DefaultAccountManager) UpdatePeer(accountID string, update *Peer) (*Pe return nil, err } - account, err := am.GetAccountById(accountID) - if err != nil { - return nil, err - } - err = am.updateAccountPeers(account) if err != nil { return nil, err From f748e57544e6a834d7ed17c24ea1a1e18db4df7c Mon Sep 17 00:00:00 2001 From: braginini Date: Tue, 21 Jun 2022 17:46:18 +0200 Subject: [PATCH 33/51] Let pre-shared key to be removed --- client/internal/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/internal/config.go b/client/internal/config.go index 35029f037c0..f0f16dd5dce 100644 --- a/client/internal/config.go +++ b/client/internal/config.go @@ -158,7 +158,7 @@ func GetConfig(managementURL, adminURL, configPath, preSharedKey string) (*Confi } else { // don't overwrite pre-shared key if we receive asterisks from UI pk := &preSharedKey - if preSharedKey == "**********" || preSharedKey == "" { + if preSharedKey == "**********" { pk = nil } return ReadConfig(managementURL, adminURL, configPath, pk) From 14bcfea22727535257546476616d1c403521592e Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 22 Jun 2022 15:28:27 +0200 Subject: [PATCH 34/51] Clean SSH server when failed to start --- client/internal/engine.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/internal/engine.go b/client/internal/engine.go index b90f5d309ea..e8349f9b5ba 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -437,6 +437,9 @@ func (e *Engine) updateSSH(sshConf *mgmProto.SSHConfig) error { // will throw error when we stop it even if it is a graceful stop log.Debugf("stopped SSH server with error %v", err) } + e.syncMsgMux.Lock() + defer e.syncMsgMux.Unlock() + e.sshServer = nil log.Infof("stopped SSH server") }() } else { From 11d7c2e553c4ceb012e3fb9daa2bb051e949c357 Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 22 Jun 2022 15:32:29 +0200 Subject: [PATCH 35/51] Use different interface name in engine test --- client/internal/engine_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index 3c0e56c3fa4..521c751f2a2 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -55,7 +55,7 @@ func TestEngine_SSH(t *testing.T) { defer cancel() engine := NewEngine(ctx, cancel, &signal.MockClient{}, &mgmt.MockClient{}, &EngineConfig{ - WgIfaceName: "utun100", + WgIfaceName: "utun101", WgAddr: "100.64.0.1/24", WgPrivateKey: key, WgPort: 33100, @@ -174,12 +174,12 @@ func TestEngine_UpdateNetworkMap(t *testing.T) { defer cancel() engine := NewEngine(ctx, cancel, &signal.MockClient{}, &mgmt.MockClient{}, &EngineConfig{ - WgIfaceName: "utun100", + WgIfaceName: "utun102", WgAddr: "100.64.0.1/24", WgPrivateKey: key, WgPort: 33100, }) - engine.wgInterface, err = iface.NewWGIFace("utun100", "100.64.0.1/24", iface.DefaultMTU) + engine.wgInterface, err = iface.NewWGIFace("utun102", "100.64.0.1/24", iface.DefaultMTU) type testCase struct { name string @@ -354,7 +354,7 @@ func TestEngine_Sync(t *testing.T) { } engine := NewEngine(ctx, cancel, &signal.MockClient{}, &mgmt.MockClient{SyncFunc: syncFunc}, &EngineConfig{ - WgIfaceName: "utun100", + WgIfaceName: "utun103", WgAddr: "100.64.0.1/24", WgPrivateKey: key, WgPort: 33100, From 1d803a4bddcc8f18908816c0a460580973b28ff7 Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 22 Jun 2022 15:37:38 +0200 Subject: [PATCH 36/51] Rename sshKey to pubSSHKey not to confuse with a private key --- client/internal/config.go | 3 ++- client/internal/connect.go | 8 ++++---- client/internal/engine.go | 1 + client/internal/login.go | 14 +++++++------- management/client/grpc.go | 8 ++++---- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/client/internal/config.go b/client/internal/config.go index f0f16dd5dce..dcb26ee367c 100644 --- a/client/internal/config.go +++ b/client/internal/config.go @@ -38,7 +38,8 @@ type Config struct { AdminURL *url.URL WgIface string IFaceBlackList []string - SSHKey string + // SSHKey is a private SSH key in a PEM format + SSHKey string } // createNewConfig creates a new config generating a new Wireguard key and saving to file diff --git a/client/internal/connect.go b/client/internal/connect.go index 85975869e18..074f275e15d 100644 --- a/client/internal/connect.go +++ b/client/internal/connect.go @@ -64,13 +64,13 @@ func RunClient(ctx context.Context, config *Config) error { engineCtx, cancel := context.WithCancel(ctx) defer cancel() - publicKey, err := ssh.GeneratePublicKey([]byte(config.SSHKey)) + publicSSHKey, err := ssh.GeneratePublicKey([]byte(config.SSHKey)) if err != nil { return err } // connect (just a connection, no stream yet) and login to Management Service to get an initial global Wiretrustee config mgmClient, loginResp, err := connectToManagement(engineCtx, config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled, - publicKey) + publicSSHKey) if err != nil { log.Debug(err) if s, ok := status.FromError(err); ok && s.Code() == codes.PermissionDenied { @@ -186,7 +186,7 @@ func connectToSignal(ctx context.Context, wtConfig *mgmProto.WiretrusteeConfig, } // connectToManagement creates Management Services client, establishes a connection, logs-in and gets a global Wiretrustee config (signal, turn, stun hosts, etc) -func connectToManagement(ctx context.Context, managementAddr string, ourPrivateKey wgtypes.Key, tlsEnabled bool, sshKey []byte) (*mgm.GrpcClient, *mgmProto.LoginResponse, error) { +func connectToManagement(ctx context.Context, managementAddr string, ourPrivateKey wgtypes.Key, tlsEnabled bool, pubSSHKey []byte) (*mgm.GrpcClient, *mgmProto.LoginResponse, error) { log.Debugf("connecting to Management Service %s", managementAddr) client, err := mgm.NewClient(ctx, managementAddr, ourPrivateKey, tlsEnabled) if err != nil { @@ -200,7 +200,7 @@ func connectToManagement(ctx context.Context, managementAddr string, ourPrivateK } sysInfo := system.GetInfo(ctx) - loginResp, err := client.Login(*serverPublicKey, sysInfo, sshKey) + loginResp, err := client.Login(*serverPublicKey, sysInfo, pubSSHKey) if err != nil { return nil, nil, err } diff --git a/client/internal/engine.go b/client/internal/engine.go index e8349f9b5ba..961f40ae07a 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -56,6 +56,7 @@ type EngineConfig struct { // UDPMuxSrflxPort default value 0 - the system will pick an available port UDPMuxSrflxPort int + // SSHKey is a private SSH key in a PEM format SSHKey []byte } diff --git a/client/internal/login.go b/client/internal/login.go index 7d146818809..a09bd3c212f 100644 --- a/client/internal/login.go +++ b/client/internal/login.go @@ -40,11 +40,11 @@ func Login(ctx context.Context, config *Config, setupKey string, jwtToken string return err } - publicKey, err := ssh.GeneratePublicKey([]byte(config.SSHKey)) + pubSSHKey, err := ssh.GeneratePublicKey([]byte(config.SSHKey)) if err != nil { return err } - _, err = loginPeer(ctx, *serverKey, mgmClient, setupKey, jwtToken, publicKey) + _, err = loginPeer(ctx, *serverKey, mgmClient, setupKey, jwtToken, pubSSHKey) if err != nil { log.Errorf("failed logging-in peer on Management Service : %v", err) return err @@ -60,13 +60,13 @@ func Login(ctx context.Context, config *Config, setupKey string, jwtToken string } // loginPeer attempts to login to Management Service. If peer wasn't registered, tries the registration flow. -func loginPeer(ctx context.Context, serverPublicKey wgtypes.Key, client *mgm.GrpcClient, setupKey string, jwtToken string, sshKey []byte) (*mgmProto.LoginResponse, error) { +func loginPeer(ctx context.Context, serverPublicKey wgtypes.Key, client *mgm.GrpcClient, setupKey string, jwtToken string, pubSSHKey []byte) (*mgmProto.LoginResponse, error) { sysInfo := system.GetInfo(ctx) - loginResp, err := client.Login(serverPublicKey, sysInfo, sshKey) + loginResp, err := client.Login(serverPublicKey, sysInfo, pubSSHKey) if err != nil { if s, ok := status.FromError(err); ok && s.Code() == codes.PermissionDenied { log.Debugf("peer registration required") - return registerPeer(ctx, serverPublicKey, client, setupKey, jwtToken, sshKey) + return registerPeer(ctx, serverPublicKey, client, setupKey, jwtToken, pubSSHKey) } else { return nil, err } @@ -79,7 +79,7 @@ func loginPeer(ctx context.Context, serverPublicKey wgtypes.Key, client *mgm.Grp // registerPeer checks whether setupKey was provided via cmd line and if not then it prompts user to enter a key. // Otherwise tries to register with the provided setupKey via command line. -func registerPeer(ctx context.Context, serverPublicKey wgtypes.Key, client *mgm.GrpcClient, setupKey string, jwtToken string, sshKey []byte) (*mgmProto.LoginResponse, error) { +func registerPeer(ctx context.Context, serverPublicKey wgtypes.Key, client *mgm.GrpcClient, setupKey string, jwtToken string, pubSSHKey []byte) (*mgmProto.LoginResponse, error) { validSetupKey, err := uuid.Parse(setupKey) if err != nil && jwtToken == "" { return nil, status.Errorf(codes.InvalidArgument, "invalid setup-key or no sso information provided, err: %v", err) @@ -87,7 +87,7 @@ func registerPeer(ctx context.Context, serverPublicKey wgtypes.Key, client *mgm. log.Debugf("sending peer registration request to Management Service") info := system.GetInfo(ctx) - loginResp, err := client.Register(serverPublicKey, validSetupKey.String(), jwtToken, info, sshKey) + loginResp, err := client.Register(serverPublicKey, validSetupKey.String(), jwtToken, info, pubSSHKey) if err != nil { log.Errorf("failed registering peer %v,%s", err, validSetupKey.String()) return nil, err diff --git a/management/client/grpc.go b/management/client/grpc.go index 117263aa73d..39e94bc9636 100644 --- a/management/client/grpc.go +++ b/management/client/grpc.go @@ -233,18 +233,18 @@ func (c *GrpcClient) login(serverKey wgtypes.Key, req *proto.LoginRequest) (*pro // Register registers peer on Management Server. It actually calls a Login endpoint with a provided setup key // Takes care of encrypting and decrypting messages. // This method will also collect system info and send it with the request (e.g. hostname, os, etc) -func (c *GrpcClient) Register(serverKey wgtypes.Key, setupKey string, jwtToken string, sysInfo *system.Info, sshKey []byte) (*proto.LoginResponse, error) { +func (c *GrpcClient) Register(serverKey wgtypes.Key, setupKey string, jwtToken string, sysInfo *system.Info, pubSSHKey []byte) (*proto.LoginResponse, error) { keys := &proto.PeerKeys{ - SshPubKey: sshKey, + SshPubKey: pubSSHKey, WgPubKey: []byte(c.key.PublicKey().String()), } return c.login(serverKey, &proto.LoginRequest{SetupKey: setupKey, Meta: infoToMetaData(sysInfo), JwtToken: jwtToken, PeerKeys: keys}) } // Login attempts login to Management Server. Takes care of encrypting and decrypting messages. -func (c *GrpcClient) Login(serverKey wgtypes.Key, sysInfo *system.Info, sshKey []byte) (*proto.LoginResponse, error) { +func (c *GrpcClient) Login(serverKey wgtypes.Key, sysInfo *system.Info, pubSSHKey []byte) (*proto.LoginResponse, error) { keys := &proto.PeerKeys{ - SshPubKey: sshKey, + SshPubKey: pubSSHKey, WgPubKey: []byte(c.key.PublicKey().String()), } return c.login(serverKey, &proto.LoginRequest{Meta: infoToMetaData(sysInfo), PeerKeys: keys}) From 12a6e0798ca8236f0f399258a1312a306964a825 Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 22 Jun 2022 15:39:01 +0200 Subject: [PATCH 37/51] Avoid single letter variable definition --- client/ssh/util.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ssh/util.go b/client/ssh/util.go index 02ac1021175..8ecb77c9367 100644 --- a/client/ssh/util.go +++ b/client/ssh/util.go @@ -30,11 +30,11 @@ const RSA KeyType = "rsa" const RSAKeySize = 2048 // GeneratePrivateKey creates RSA Private Key of specified byte size -func GeneratePrivateKey(t KeyType) ([]byte, error) { +func GeneratePrivateKey(keyType KeyType) ([]byte, error) { var key crypto.Signer var err error - switch t { + switch keyType { case ED25519: _, key, err = ed25519.GenerateKey(rand.Reader) case ECDSA: @@ -42,7 +42,7 @@ func GeneratePrivateKey(t KeyType) ([]byte, error) { case RSA: key, err = rsa.GenerateKey(rand.Reader, RSAKeySize) default: - return nil, fmt.Errorf("unsupported ket type %s", t) + return nil, fmt.Errorf("unsupported ket type %s", keyType) } if err != nil { return nil, err From 99b1ddd4ec5f6530f2a704bd1c7a7bce9379c904 Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 22 Jun 2022 15:39:50 +0200 Subject: [PATCH 38/51] Avoid single letter variable definition --- client/ssh/window_unix.go | 4 ++-- client/ssh/window_windows.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ssh/window_unix.go b/client/ssh/window_unix.go index 4a09512b5a2..46408d11a57 100644 --- a/client/ssh/window_unix.go +++ b/client/ssh/window_unix.go @@ -8,7 +8,7 @@ import ( "unsafe" ) -func setWinSize(f *os.File, w, h int) { - syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ), //nolint +func setWinSize(file *os.File, w, h int) { + syscall.Syscall(syscall.SYS_IOCTL, file.Fd(), uintptr(syscall.TIOCSWINSZ), //nolint uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0}))) } diff --git a/client/ssh/window_windows.go b/client/ssh/window_windows.go index 49f28450a7e..6d39c78df63 100644 --- a/client/ssh/window_windows.go +++ b/client/ssh/window_windows.go @@ -4,6 +4,6 @@ import ( "os" ) -func setWinSize(f *os.File, w, h int) { +func setWinSize(file *os.File, w, h int) { } From 75052a1cf12a835dc90f4cf13a05fd4259248da0 Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 22 Jun 2022 16:18:29 +0200 Subject: [PATCH 39/51] Replace SSH port to 44337 --- client/cmd/ssh.go | 2 +- client/internal/engine.go | 10 +++++----- client/ssh/server.go | 4 +++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/client/cmd/ssh.go b/client/cmd/ssh.go index 36a167de958..2bbce6759f5 100644 --- a/client/cmd/ssh.go +++ b/client/cmd/ssh.go @@ -134,5 +134,5 @@ func runSSH(ctx context.Context, addr string, pemKey []byte) error { } func init() { - sshCmd.PersistentFlags().IntVarP(&port, "port", "p", 2222, "set remote SSH port. Defaults to NetBird's 2222") + sshCmd.PersistentFlags().IntVarP(&port, "port", "p", nbssh.DefaultSSHPort, "Sets remote SSH port. Defaults to "+fmt.Sprint(nbssh.DefaultSSHPort)) } diff --git a/client/internal/engine.go b/client/internal/engine.go index 961f40ae07a..445fe9efe9f 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -3,7 +3,7 @@ package internal import ( "context" "fmt" - "github.com/netbirdio/netbird/client/ssh" + nbssh "github.com/netbirdio/netbird/client/ssh" "math/rand" "net" "strings" @@ -92,8 +92,8 @@ type Engine struct { // networkSerial is the latest CurrentSerial (state ID) of the network sent by the Management service networkSerial uint64 - sshServerFunc func(hostKeyPEM []byte, addr string) (ssh.Server, error) - sshServer ssh.Server + sshServerFunc func(hostKeyPEM []byte, addr string) (nbssh.Server, error) + sshServer nbssh.Server } // Peer is an instance of the Connection Peer @@ -118,7 +118,7 @@ func NewEngine( STUNs: []*ice.URL{}, TURNs: []*ice.URL{}, networkSerial: 0, - sshServerFunc: ssh.DefaultSSHServer, + sshServerFunc: nbssh.DefaultSSHServer, } } @@ -428,7 +428,7 @@ func (e *Engine) updateSSH(sshConf *mgmProto.SSHConfig) error { //nil sshServer means it has not yet been started var err error e.sshServer, err = e.sshServerFunc(e.config.SSHKey, - fmt.Sprintf("%s:%d", e.wgInterface.Address.IP.String(), 2222)) + fmt.Sprintf("%s:%d", e.wgInterface.Address.IP.String(), nbssh.DefaultSSHPort)) if err != nil { return err } diff --git a/client/ssh/server.go b/client/ssh/server.go index 57bb430fcdd..783c8f07a57 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -12,6 +12,8 @@ import ( "sync" ) +const DefaultSSHPort = 44337 + // DefaultSSHServer is a function that creates DefaultServer func DefaultSSHServer(hostKeyPEM []byte, addr string) (Server, error) { return newDefaultServer(hostKeyPEM, addr) @@ -154,7 +156,7 @@ func (srv *DefaultServer) stdInOut(f *os.File, s ssh.Session) { // Start starts SSH server. Blocking func (srv *DefaultServer) Start() error { - log.Infof("starting SSH server") + log.Infof("starting SSH server on addr: %s", srv.listener.Addr().String()) publicKeyOption := ssh.PublicKeyAuth(srv.publicKeyHandler) hostKeyPEM := ssh.HostKeyPEM(srv.hostKeyPEM) From 98b003f2c01f785cbf70b7acd24b676c76e5c20a Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 22 Jun 2022 16:20:54 +0200 Subject: [PATCH 40/51] Replace SSH port to 44338 --- client/ssh/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ssh/server.go b/client/ssh/server.go index 783c8f07a57..53c5beb60e4 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -12,7 +12,7 @@ import ( "sync" ) -const DefaultSSHPort = 44337 +const DefaultSSHPort = 44338 // DefaultSSHServer is a function that creates DefaultServer func DefaultSSHServer(hostKeyPEM []byte, addr string) (Server, error) { From 56a6277c2c65fce666565bc1b40e9d9755cd4302 Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 22 Jun 2022 16:30:37 +0200 Subject: [PATCH 41/51] Fix engine test on failing SSH server --- client/internal/engine.go | 3 ++- client/internal/engine_test.go | 13 +++++++++++++ client/ssh/server_mock.go | 3 +++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/client/internal/engine.go b/client/internal/engine.go index 445fe9efe9f..55187dd7bbe 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -433,10 +433,11 @@ func (e *Engine) updateSSH(sshConf *mgmProto.SSHConfig) error { return err } go func() { + // blocking err = e.sshServer.Start() if err != nil { // will throw error when we stop it even if it is a graceful stop - log.Debugf("stopped SSH server with error %v", err) + log.Errorf("stopped SSH server with error %v", err) } e.syncMsgMux.Lock() defer e.syncMsgMux.Unlock() diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index 521c751f2a2..aba7fd79eb0 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -64,8 +64,21 @@ func TestEngine_SSH(t *testing.T) { var sshKeysAdded []string var sshPeersRemoved []string + sshCtx, cancel := context.WithCancel(context.Background()) + engine.sshServerFunc = func(hostKeyPEM []byte, addr string) (ssh.Server, error) { return &ssh.MockServer{ + Ctx: sshCtx, + StopFunc: func() error { + cancel() + return nil + }, + StartFunc: func() error { + select { + case <-ctx.Done(): + } + return nil + }, AddAuthorizedKeyFunc: func(peer, newKey string) error { sshKeysAdded = append(sshKeysAdded, newKey) return nil diff --git a/client/ssh/server_mock.go b/client/ssh/server_mock.go index 8aac426271d..cc080ffdb0c 100644 --- a/client/ssh/server_mock.go +++ b/client/ssh/server_mock.go @@ -1,7 +1,10 @@ package ssh +import "context" + // MockServer mocks ssh.Server type MockServer struct { + Ctx context.Context StopFunc func() error StartFunc func() error AddAuthorizedKeyFunc func(peer, newKey string) error From bfdde7998b3917ca5f03127e0e6250d1f7c643f3 Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 22 Jun 2022 16:41:46 +0200 Subject: [PATCH 42/51] Fix lint issues --- client/internal/engine_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index aba7fd79eb0..2a6467fe906 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -74,10 +74,8 @@ func TestEngine_SSH(t *testing.T) { return nil }, StartFunc: func() error { - select { - case <-ctx.Done(): - } - return nil + <-ctx.Done() + return ctx.Err() }, AddAuthorizedKeyFunc: func(peer, newKey string) error { sshKeysAdded = append(sshKeysAdded, newKey) From 9cb50c208d5e0b3eedf7eb61f9a6cb4afb62a74a Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 22 Jun 2022 17:00:37 +0200 Subject: [PATCH 43/51] Decrease log level --- client/internal/engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/internal/engine.go b/client/internal/engine.go index 55187dd7bbe..666c352ef0f 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -437,7 +437,7 @@ func (e *Engine) updateSSH(sshConf *mgmProto.SSHConfig) error { err = e.sshServer.Start() if err != nil { // will throw error when we stop it even if it is a graceful stop - log.Errorf("stopped SSH server with error %v", err) + log.Debugf("stopped SSH server with error %v", err) } e.syncMsgMux.Lock() defer e.syncMsgMux.Unlock() From 823a3437e7825d21b19273225213e6c9314d3eb8 Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 22 Jun 2022 17:08:18 +0200 Subject: [PATCH 44/51] Close SSH sessions on SSH server stop --- client/ssh/server.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/client/ssh/server.go b/client/ssh/server.go index 53c5beb60e4..58a5b0dace0 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -38,6 +38,7 @@ type DefaultServer struct { authorizedKeys map[string]ssh.PublicKey mu sync.Mutex hostKeyPEM []byte + sessions []ssh.Session } // newDefaultServer creates new server with provided host key @@ -47,7 +48,7 @@ func newDefaultServer(hostKeyPEM []byte, addr string) (*DefaultServer, error) { return nil, err } allowedKeys := make(map[string]ssh.PublicKey) - return &DefaultServer{listener: ln, mu: sync.Mutex{}, hostKeyPEM: hostKeyPEM, authorizedKeys: allowedKeys}, nil + return &DefaultServer{listener: ln, mu: sync.Mutex{}, hostKeyPEM: hostKeyPEM, authorizedKeys: allowedKeys, sessions: make([]ssh.Session, 0)}, nil } // RemoveAuthorizedKey removes SSH key of a given peer from the authorized keys @@ -74,10 +75,19 @@ func (srv *DefaultServer) AddAuthorizedKey(peer, newKey string) error { // Stop stops SSH server. func (srv *DefaultServer) Stop() error { + srv.mu.Lock() + defer srv.mu.Unlock() err := srv.listener.Close() if err != nil { return err } + for _, session := range srv.sessions { + err := session.Close() + if err != nil { + log.Warnf("failed closing SSH session from %v", err) + } + } + return nil } @@ -104,6 +114,9 @@ func getShellType() string { // sessionHandler handles SSH session post auth func (srv *DefaultServer) sessionHandler(s ssh.Session) { + srv.mu.Lock() + srv.sessions = append(srv.sessions, s) + srv.mu.Unlock() ptyReq, winCh, isPty := s.Pty() if isPty { cmd := exec.Command(getShellType()) @@ -160,7 +173,6 @@ func (srv *DefaultServer) Start() error { publicKeyOption := ssh.PublicKeyAuth(srv.publicKeyHandler) hostKeyPEM := ssh.HostKeyPEM(srv.hostKeyPEM) - err := ssh.Serve(srv.listener, srv.sessionHandler, publicKeyOption, hostKeyPEM) if err != nil { return err From d370cd45792706c4bc5ed8623a8292959554c83b Mon Sep 17 00:00:00 2001 From: braginini Date: Wed, 22 Jun 2022 17:47:45 +0200 Subject: [PATCH 45/51] Fix Codacy issues --- client/ssh/server.go | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ssh/server.go b/client/ssh/server.go index 58a5b0dace0..a7effea4b5d 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -12,6 +12,7 @@ import ( "sync" ) +// DefaultSSHPort is the default SSH port of the NetBird's embedded SSH server const DefaultSSHPort = 44338 // DefaultSSHServer is a function that creates DefaultServer From 10829cbb4ea8f6916111185168a3589559816dc8 Mon Sep 17 00:00:00 2001 From: braginini Date: Thu, 23 Jun 2022 12:40:34 +0200 Subject: [PATCH 46/51] Check if admin when running SSH --- client/cmd/ssh.go | 5 ++--- util/membership_unix.go | 12 ++++++++++++ util/membership_windows.go | 12 ++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 util/membership_unix.go create mode 100644 util/membership_windows.go diff --git a/client/cmd/ssh.go b/client/cmd/ssh.go index 2bbce6759f5..b8ae20b9f86 100644 --- a/client/cmd/ssh.go +++ b/client/cmd/ssh.go @@ -50,9 +50,8 @@ var sshCmd = &cobra.Command{ return fmt.Errorf("failed initializing log %v", err) } - if os.Geteuid() != 0 { - //todo make it work on Windows - cmd.Printf("Error: you must be root to run this command\n") + if !util.IsAdmin() { + cmd.Printf("error: you must have Administrator privileges to run this command\n") return nil } diff --git a/util/membership_unix.go b/util/membership_unix.go new file mode 100644 index 00000000000..82237461c75 --- /dev/null +++ b/util/membership_unix.go @@ -0,0 +1,12 @@ +//go:build linux || darwin + +package util + +import ( + "os" +) + +// IsAdmin returns true if user is root +func IsAdmin() bool { + return os.Geteuid() == 0 +} diff --git a/util/membership_windows.go b/util/membership_windows.go new file mode 100644 index 00000000000..67a04577ea9 --- /dev/null +++ b/util/membership_windows.go @@ -0,0 +1,12 @@ +package util + +import "golang.zx2c4.com/wireguard/windows/elevate" + +// IsAdmin returns true if user has admin privileges +func IsAdmin() bool { + adminDesktop, err := elevate.IsAdminDesktop() + if err == nil && adminDesktop { + return true + } + return false +} From 9a188ef06f2e94a9594b7b11680f6d1c8a2ffdd8 Mon Sep 17 00:00:00 2001 From: braginini Date: Thu, 23 Jun 2022 15:34:13 +0200 Subject: [PATCH 47/51] Fix SSH client on windows --- client/ssh/client.go | 2 +- go.mod | 4 ++-- go.sum | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/client/ssh/client.go b/client/ssh/client.go index 2a93fd0152a..486a8f8038b 100644 --- a/client/ssh/client.go +++ b/client/ssh/client.go @@ -31,7 +31,7 @@ func (c *Client) OpenTerminal() error { } }() - fd := int(os.Stdin.Fd()) + fd := int(os.Stdout.Fd()) state, err := term.MakeRaw(fd) if err != nil { return fmt.Errorf("failed to run raw terminal: %s", err) diff --git a/go.mod b/go.mod index 87e1a3ffb02..8cfd0e43eba 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/spf13/pflag v1.0.5 github.com/vishvananda/netlink v1.1.0 golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 - golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a + golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664 golang.zx2c4.com/wireguard v0.0.0-20211209221555-9c9e7e272434 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de golang.zx2c4.com/wireguard/windows v0.5.1 @@ -39,7 +39,7 @@ require ( github.com/rs/xid v1.3.0 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/stretchr/testify v1.7.1 - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 + golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 ) require ( diff --git a/go.sum b/go.sum index 333fdf26b82..bcf23d9dc58 100644 --- a/go.sum +++ b/go.sum @@ -898,9 +898,13 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a h1:N2T1jUrTQE9Re6TFF5PhvEHXHCguynGhKjWVsIUt5cY= golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664 h1:wEZYwx+kK+KlZ0hpvP2Ls1Xr4+RWnlzGFwPP0aiDjIU= +golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 8acfc30aa8ffc9578a4fb07d0fc12bbd9d917a9a Mon Sep 17 00:00:00 2001 From: braginini Date: Thu, 23 Jun 2022 15:47:22 +0200 Subject: [PATCH 48/51] Remove panic when error on starting pty --- client/ssh/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ssh/server.go b/client/ssh/server.go index a7effea4b5d..db6a81ca31e 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -124,7 +124,7 @@ func (srv *DefaultServer) sessionHandler(s ssh.Session) { cmd.Env = append(cmd.Env, fmt.Sprintf("TERM=%s", ptyReq.Term)) f, err := pty.Start(cmd) if err != nil { - panic(err) + log.Errorf("failed starting SSH server %v", err) } go func() { for win := range winCh { From fbed2aea0a02ad8ebd0635eeb120eb4ce733df81 Mon Sep 17 00:00:00 2001 From: braginini Date: Thu, 23 Jun 2022 15:54:49 +0200 Subject: [PATCH 49/51] Disable SSH server on Windows - not supported --- client/internal/engine.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/internal/engine.go b/client/internal/engine.go index 666c352ef0f..0271bd599dd 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -6,6 +6,7 @@ import ( nbssh "github.com/netbirdio/netbird/client/ssh" "math/rand" "net" + "runtime" "strings" "sync" "time" @@ -423,6 +424,10 @@ func (e *Engine) handleSync(update *mgmProto.SyncResponse) error { func (e *Engine) updateSSH(sshConf *mgmProto.SSHConfig) error { if sshConf.GetSshEnabled() { + if runtime.GOOS == "windows" { + log.Warnf("running SSH server on Windows is not supported") + return nil + } // start SSH server if it wasn't running if e.sshServer == nil { //nil sshServer means it has not yet been started From 60c9c4524f45accf5fa163368ad56acce3152421 Mon Sep 17 00:00:00 2001 From: braginini Date: Thu, 23 Jun 2022 16:13:15 +0200 Subject: [PATCH 50/51] Skip SSH server test on Windows --- client/internal/engine_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index 2a6467fe906..e7b767929d1 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -45,6 +45,10 @@ var ( func TestEngine_SSH(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("skipping TestEngine_SSH on Windows") + } + key, err := wgtypes.GeneratePrivateKey() if err != nil { t.Fatal(err) From b92d9a93c15b819df7630a5eea9fbf69351c5937 Mon Sep 17 00:00:00 2001 From: braginini Date: Thu, 23 Jun 2022 16:27:39 +0200 Subject: [PATCH 51/51] Avoid single letter variables --- client/ssh/server.go | 24 ++++++++++++------------ client/ssh/window_unix.go | 4 ++-- client/ssh/window_windows.go | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/client/ssh/server.go b/client/ssh/server.go index db6a81ca31e..9180f0bf92c 100644 --- a/client/ssh/server.go +++ b/client/ssh/server.go @@ -114,46 +114,46 @@ func getShellType() string { } // sessionHandler handles SSH session post auth -func (srv *DefaultServer) sessionHandler(s ssh.Session) { +func (srv *DefaultServer) sessionHandler(session ssh.Session) { srv.mu.Lock() - srv.sessions = append(srv.sessions, s) + srv.sessions = append(srv.sessions, session) srv.mu.Unlock() - ptyReq, winCh, isPty := s.Pty() + ptyReq, winCh, isPty := session.Pty() if isPty { cmd := exec.Command(getShellType()) - cmd.Env = append(cmd.Env, fmt.Sprintf("TERM=%s", ptyReq.Term)) - f, err := pty.Start(cmd) + cmd.Env = append(cmd.Env, fmt.Sprintf("TERM=%session", ptyReq.Term)) + file, err := pty.Start(cmd) if err != nil { log.Errorf("failed starting SSH server %v", err) } go func() { for win := range winCh { - setWinSize(f, win.Width, win.Height) + setWinSize(file, win.Width, win.Height) } }() - srv.stdInOut(f, s) + srv.stdInOut(file, session) err = cmd.Wait() if err != nil { return } } else { - _, err := io.WriteString(s, "only PTY is supported.\n") + _, err := io.WriteString(session, "only PTY is supported.\n") if err != nil { return } - err = s.Exit(1) + err = session.Exit(1) if err != nil { return } } } -func (srv *DefaultServer) stdInOut(f *os.File, s ssh.Session) { +func (srv *DefaultServer) stdInOut(file *os.File, session ssh.Session) { go func() { // stdin - _, err := io.Copy(f, s) + _, err := io.Copy(file, session) if err != nil { return } @@ -161,7 +161,7 @@ func (srv *DefaultServer) stdInOut(f *os.File, s ssh.Session) { go func() { // stdout - _, err := io.Copy(s, f) + _, err := io.Copy(session, file) if err != nil { return } diff --git a/client/ssh/window_unix.go b/client/ssh/window_unix.go index 46408d11a57..2891eb70e1b 100644 --- a/client/ssh/window_unix.go +++ b/client/ssh/window_unix.go @@ -8,7 +8,7 @@ import ( "unsafe" ) -func setWinSize(file *os.File, w, h int) { +func setWinSize(file *os.File, width, height int) { syscall.Syscall(syscall.SYS_IOCTL, file.Fd(), uintptr(syscall.TIOCSWINSZ), //nolint - uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0}))) + uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(height), uint16(width), 0, 0}))) } diff --git a/client/ssh/window_windows.go b/client/ssh/window_windows.go index 6d39c78df63..5abd41f271a 100644 --- a/client/ssh/window_windows.go +++ b/client/ssh/window_windows.go @@ -4,6 +4,6 @@ import ( "os" ) -func setWinSize(file *os.File, w, h int) { +func setWinSize(file *os.File, width, height int) { }