From c689af43068c3b8519185dbdc230cff3da38a075 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 10 Sep 2020 23:25:40 -0400 Subject: [PATCH] Implement a TOFU mechanism on auth to worker (#348) This sets us up to better support multiple connections per session at some future point. --- internal/cmd/commands/proxy/proxy.go | 15 ++ .../proto/local/worker/proxy/v1/proxy.proto | 8 + internal/proxy/proxy.pb.go | 151 ++++++++++++++++++ internal/servers/worker/handler.go | 14 ++ 4 files changed, 188 insertions(+) create mode 100644 internal/proto/local/worker/proxy/v1/proxy.proto create mode 100644 internal/proxy/proxy.pb.go diff --git a/internal/cmd/commands/proxy/proxy.go b/internal/cmd/commands/proxy/proxy.go index f6ad37b1d..680f4175b 100644 --- a/internal/cmd/commands/proxy/proxy.go +++ b/internal/cmd/commands/proxy/proxy.go @@ -18,10 +18,13 @@ import ( "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/cmd/base" "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "github.com/hashicorp/boundary/internal/proxy" "github.com/hashicorp/go-cleanhttp" + "github.com/hashicorp/vault/sdk/helper/base62" "github.com/mitchellh/cli" "github.com/posener/complete" "nhooyr.io/websocket" + "nhooyr.io/websocket/wspb" ) var _ cli.Command = (*Command)(nil) @@ -109,6 +112,13 @@ func (c *Command) Run(args []string) int { return 1 } + var handshake proxy.Handshake + var err error + if handshake.TofuToken, err = base62.Random(20); err != nil { + c.UI.Error(fmt.Errorf("Could not derive random bytes for tofu token: %w", err).Error()) + return 1 + } + if c.flagListenAddr == "" { c.flagListenAddr = "127.0.0.1" } @@ -227,6 +237,11 @@ func (c *Command) Run(args []string) int { return 1 } + if err := wspb.Write(c.Context, conn, &handshake); err != nil { + c.UI.Error(fmt.Errorf("error sending tofu token to worker: %w", err).Error()) + return 1 + } + // Get a wrapped net.Conn so we can use io.Copy netConn := websocket.NetConn(c.Context, conn, websocket.MessageBinary) diff --git a/internal/proto/local/worker/proxy/v1/proxy.proto b/internal/proto/local/worker/proxy/v1/proxy.proto new file mode 100644 index 000000000..dbc2cfe1f --- /dev/null +++ b/internal/proto/local/worker/proxy/v1/proxy.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; + +package worker.proxy.v1; +option go_package = "github.com/hashicorp/boundary/internal/proxy;proxy"; + +message Handshake { + string tofu_token = 10; +} diff --git a/internal/proxy/proxy.pb.go b/internal/proxy/proxy.pb.go new file mode 100644 index 000000000..992442ee2 --- /dev/null +++ b/internal/proxy/proxy.pb.go @@ -0,0 +1,151 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0 +// protoc v3.12.4 +// source: worker/proxy/v1/proxy.proto + +package proxy + +import ( + proto "github.com/golang/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type Handshake struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TofuToken string `protobuf:"bytes,10,opt,name=tofu_token,json=tofuToken,proto3" json:"tofu_token,omitempty"` +} + +func (x *Handshake) Reset() { + *x = Handshake{} + if protoimpl.UnsafeEnabled { + mi := &file_worker_proxy_v1_proxy_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Handshake) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Handshake) ProtoMessage() {} + +func (x *Handshake) ProtoReflect() protoreflect.Message { + mi := &file_worker_proxy_v1_proxy_proto_msgTypes[0] + 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 Handshake.ProtoReflect.Descriptor instead. +func (*Handshake) Descriptor() ([]byte, []int) { + return file_worker_proxy_v1_proxy_proto_rawDescGZIP(), []int{0} +} + +func (x *Handshake) GetTofuToken() string { + if x != nil { + return x.TofuToken + } + return "" +} + +var File_worker_proxy_v1_proxy_proto protoreflect.FileDescriptor + +var file_worker_proxy_v1_proxy_proto_rawDesc = []byte{ + 0x0a, 0x1b, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, + 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x77, + 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x31, 0x22, 0x2a, + 0x0a, 0x09, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, + 0x6f, 0x66, 0x75, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x74, 0x6f, 0x66, 0x75, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, + 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x3b, 0x70, 0x72, 0x6f, 0x78, 0x79, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_worker_proxy_v1_proxy_proto_rawDescOnce sync.Once + file_worker_proxy_v1_proxy_proto_rawDescData = file_worker_proxy_v1_proxy_proto_rawDesc +) + +func file_worker_proxy_v1_proxy_proto_rawDescGZIP() []byte { + file_worker_proxy_v1_proxy_proto_rawDescOnce.Do(func() { + file_worker_proxy_v1_proxy_proto_rawDescData = protoimpl.X.CompressGZIP(file_worker_proxy_v1_proxy_proto_rawDescData) + }) + return file_worker_proxy_v1_proxy_proto_rawDescData +} + +var file_worker_proxy_v1_proxy_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_worker_proxy_v1_proxy_proto_goTypes = []interface{}{ + (*Handshake)(nil), // 0: worker.proxy.v1.Handshake +} +var file_worker_proxy_v1_proxy_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_worker_proxy_v1_proxy_proto_init() } +func file_worker_proxy_v1_proxy_proto_init() { + if File_worker_proxy_v1_proxy_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_worker_proxy_v1_proxy_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Handshake); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_worker_proxy_v1_proxy_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_worker_proxy_v1_proxy_proto_goTypes, + DependencyIndexes: file_worker_proxy_v1_proxy_proto_depIdxs, + MessageInfos: file_worker_proxy_v1_proxy_proto_msgTypes, + }.Build() + File_worker_proxy_v1_proxy_proto = out.File + file_worker_proxy_v1_proxy_proto_rawDesc = nil + file_worker_proxy_v1_proxy_proto_goTypes = nil + file_worker_proxy_v1_proxy_proto_depIdxs = nil +} diff --git a/internal/servers/worker/handler.go b/internal/servers/worker/handler.go index d5c3c874c..81b647811 100644 --- a/internal/servers/worker/handler.go +++ b/internal/servers/worker/handler.go @@ -7,8 +7,10 @@ import ( "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "github.com/hashicorp/boundary/internal/proxy" "github.com/hashicorp/shared-secure-libs/configutil" "nhooyr.io/websocket" + "nhooyr.io/websocket/wspb" ) type HandlerProperties struct { @@ -67,6 +69,18 @@ func (w *Worker) handleProxy() http.HandlerFunc { cancel.(context.CancelFunc)() }() + var handshake proxy.Handshake + if err := wspb.Read(connCtx, conn, &handshake); err != nil { + w.logger.Error("error reading nonce from client", "error", err) + wr.WriteHeader(http.StatusBadRequest) + return + } + if len(handshake.GetTofuToken()) < 20 { + w.logger.Error("invalid tofu token") + wr.WriteHeader(http.StatusBadRequest) + return + } + switch conn.Subprotocol() { case globals.TcpProxyV1: w.handleTcpProxyV1(connCtx, conn, jobInfo)