From 468e51f5f263fd3aae6b7426d04449ee22918397 Mon Sep 17 00:00:00 2001 From: Christian Weichel Date: Thu, 3 Mar 2022 13:06:48 +0000 Subject: [PATCH] Support private registries --- .../image-builder-api/go/imgbuilder.pb.go | 286 ++++++++++-------- components/image-builder-api/imgbuilder.proto | 3 +- .../typescript/src/imgbuilder_pb.d.ts | 11 +- .../typescript/src/imgbuilder_pb.js | 47 ++- components/image-builder-bob/cmd/proxy.go | 9 +- .../image-builder-bob/pkg/proxy/auth.go | 35 ++- components/image-builder-mk3/debug.sh | 15 +- components/image-builder-mk3/pkg/auth/auth.go | 119 ++++---- .../pkg/orchestrator/orchestrator.go | 37 ++- .../src/workspace/gitpod-server-impl.ts | 15 +- .../src/workspace/image-source-provider.ts | 23 +- .../server/src/workspace/workspace-starter.ts | 52 +++- 12 files changed, 391 insertions(+), 261 deletions(-) diff --git a/components/image-builder-api/go/imgbuilder.pb.go b/components/image-builder-api/go/imgbuilder.pb.go index 5f28b4f2fcae64..cd25d508ccd9d9 100644 --- a/components/image-builder-api/go/imgbuilder.pb.go +++ b/components/image-builder-api/go/imgbuilder.pb.go @@ -502,7 +502,7 @@ type BuildRequest struct { Source *BuildSource `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` Auth *BuildRegistryAuth `protobuf:"bytes,2,opt,name=auth,proto3" json:"auth,omitempty"` - ForceRebuild bool `protobuf:"varint,3,opt,name=forceRebuild,proto3" json:"forceRebuild,omitempty"` + ForceRebuild bool `protobuf:"varint,3,opt,name=force_rebuild,json=forceRebuild,proto3" json:"force_rebuild,omitempty"` } func (x *BuildRequest) Reset() { @@ -566,7 +566,8 @@ type BuildRegistryAuth struct { // Types that are assignable to Mode: // *BuildRegistryAuth_Total // *BuildRegistryAuth_Selective - Mode isBuildRegistryAuth_Mode `protobuf_oneof:"mode"` + Mode isBuildRegistryAuth_Mode `protobuf_oneof:"mode"` + Additional map[string]string `protobuf:"bytes,3,rep,name=additional,proto3" json:"additional,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *BuildRegistryAuth) Reset() { @@ -622,6 +623,13 @@ func (x *BuildRegistryAuth) GetSelective() *BuildRegistryAuthSelective { return nil } +func (x *BuildRegistryAuth) GetAdditional() map[string]string { + if x != nil { + return x.Additional + } + return nil +} + type isBuildRegistryAuth_Mode interface { isBuildRegistryAuth_Mode() } @@ -1223,119 +1231,127 @@ var file_imgbuilder_proto_rawDesc = []byte{ 0x61, 0x73, 0x65, 0x52, 0x65, 0x66, 0x12, 0x2c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x22, 0x90, 0x01, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, + 0x61, 0x74, 0x75, 0x73, 0x22, 0x91, 0x01, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x52, 0x04, 0x61, - 0x75, 0x74, 0x68, 0x12, 0x22, 0x0a, 0x0c, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x52, 0x65, 0x62, 0x75, - 0x69, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x66, 0x6f, 0x72, 0x63, 0x65, - 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x22, 0x99, 0x01, 0x0a, 0x11, 0x42, 0x75, 0x69, 0x6c, - 0x64, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x12, 0x37, 0x0a, - 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x48, 0x00, 0x52, - 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x43, 0x0a, 0x09, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, - 0x69, 0x76, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, - 0x79, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x48, 0x00, - 0x52, 0x09, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x6d, - 0x6f, 0x64, 0x65, 0x22, 0x35, 0x0a, 0x16, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x1b, 0x0a, - 0x09, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x08, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x41, 0x6c, 0x6c, 0x22, 0x87, 0x01, 0x0a, 0x1a, 0x42, - 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, - 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, - 0x6f, 0x77, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x72, 0x65, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x42, 0x61, 0x73, 0x65, 0x72, 0x65, 0x70, 0x12, 0x2d, - 0x0a, 0x12, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x72, 0x65, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x61, 0x6c, 0x6c, 0x6f, - 0x77, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x72, 0x65, 0x70, 0x12, 0x15, 0x0a, - 0x06, 0x61, 0x6e, 0x79, 0x5f, 0x6f, 0x66, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x61, - 0x6e, 0x79, 0x4f, 0x66, 0x22, 0xac, 0x01, 0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, - 0x5f, 0x72, 0x65, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, - 0x52, 0x65, 0x66, 0x12, 0x2c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, - 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x69, - 0x6e, 0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, - 0x6e, 0x66, 0x6f, 0x22, 0x61, 0x0a, 0x0b, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x66, 0x12, - 0x1a, 0x0a, 0x08, 0x63, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x08, 0x63, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x49, 0x64, 0x22, 0x28, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x22, 0x13, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x40, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x69, - 0x6c, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x75, - 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x06, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x22, 0xcd, 0x01, 0x0a, 0x09, 0x42, 0x75, 0x69, 0x6c, - 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, - 0x72, 0x65, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x52, - 0x65, 0x66, 0x12, 0x2c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, - 0x6c, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, - 0x19, 0x0a, 0x08, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x08, 0x6c, 0x6f, - 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, - 0x6c, 0x6f, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x90, 0x01, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x49, - 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x37, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, - 0x2e, 0x4c, 0x6f, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x3a, - 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x2a, 0x4b, 0x0a, 0x0b, 0x42, 0x75, - 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x75, 0x6e, 0x6b, - 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, - 0x67, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x64, 0x6f, 0x6e, 0x65, 0x5f, 0x73, 0x75, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x64, 0x6f, 0x6e, 0x65, 0x5f, 0x66, 0x61, - 0x69, 0x6c, 0x75, 0x72, 0x65, 0x10, 0x03, 0x32, 0x91, 0x03, 0x0a, 0x0c, 0x49, 0x6d, 0x61, 0x67, - 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x59, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, - 0x6c, 0x76, 0x65, 0x42, 0x61, 0x73, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x20, 0x2e, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x42, 0x61, - 0x73, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, - 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, - 0x42, 0x61, 0x73, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x57, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x25, 0x2e, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x57, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x6d, - 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, - 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x15, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, - 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, - 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x37, 0x0a, 0x04, 0x4c, 0x6f, 0x67, - 0x73, 0x12, 0x14, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, - 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x30, 0x01, 0x12, 0x47, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, - 0x12, 0x1a, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, - 0x75, 0x69, 0x6c, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x62, + 0x75, 0x74, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x66, 0x6f, 0x72, 0x63, + 0x65, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x22, 0xa4, 0x02, 0x0a, 0x11, 0x42, 0x75, 0x69, + 0x6c, 0x64, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x12, 0x37, + 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, + 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x48, 0x00, + 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x43, 0x0a, 0x09, 0x73, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x69, 0x76, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x48, + 0x00, 0x52, 0x09, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x4a, 0x0a, 0x0a, + 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2a, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x41, 0x64, 0x64, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x61, 0x64, + 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x64, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x22, + 0x35, 0x0a, 0x16, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, + 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x5f, 0x61, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x41, 0x6c, 0x6c, 0x22, 0x87, 0x01, 0x0a, 0x1a, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x62, + 0x61, 0x73, 0x65, 0x72, 0x65, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x42, 0x61, 0x73, 0x65, 0x72, 0x65, 0x70, 0x12, 0x2d, 0x0a, 0x12, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x72, 0x65, 0x70, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x57, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x72, 0x65, 0x70, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x6e, 0x79, + 0x5f, 0x6f, 0x66, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x61, 0x6e, 0x79, 0x4f, 0x66, + 0x22, 0xac, 0x01, 0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x72, 0x65, 0x66, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x72, 0x65, 0x66, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x66, 0x12, + 0x2c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x14, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, + 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x22, + 0x61, 0x0a, 0x0b, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, + 0x0a, 0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x66, 0x12, 0x1a, 0x0a, 0x08, 0x63, + 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, + 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x49, 0x64, 0x22, 0x28, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x13, 0x0a, 0x11, + 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0x40, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, + 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x73, 0x22, 0xcd, 0x01, 0x0a, 0x09, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x72, 0x65, 0x66, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x72, 0x65, 0x66, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x66, 0x12, 0x2c, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, + 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1d, 0x0a, 0x0a, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x49, + 0x6e, 0x66, 0x6f, 0x22, 0x90, 0x01, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, + 0x6c, 0x12, 0x37, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, + 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x48, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x2a, 0x4b, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, + 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x10, 0x01, 0x12, + 0x10, 0x0a, 0x0c, 0x64, 0x6f, 0x6e, 0x65, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x10, + 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x64, 0x6f, 0x6e, 0x65, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, + 0x65, 0x10, 0x03, 0x32, 0x91, 0x03, 0x0a, 0x0c, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x42, 0x75, 0x69, + 0x6c, 0x64, 0x65, 0x72, 0x12, 0x59, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x42, + 0x61, 0x73, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x20, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x42, 0x61, 0x73, 0x65, 0x49, 0x6d, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x42, 0x61, 0x73, 0x65, + 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x68, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x25, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x26, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, + 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x05, 0x42, 0x75, 0x69, + 0x6c, 0x64, 0x12, 0x15, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, + 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x65, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x37, 0x0a, 0x04, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x14, 0x2e, + 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x4c, 0x6f, + 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x47, + 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x12, 0x1a, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x2f, 0x5a, 0x2d, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, - 0x2d, 0x69, 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2f, 0x69, 0x6d, 0x61, 0x67, 0x65, - 0x2d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x65, 0x72, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2d, 0x69, 0x6f, 0x2f, + 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2d, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1351,7 +1367,7 @@ func file_imgbuilder_proto_rawDescGZIP() []byte { } var file_imgbuilder_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_imgbuilder_proto_msgTypes = make([]protoimpl.MessageInfo, 19) +var file_imgbuilder_proto_msgTypes = make([]protoimpl.MessageInfo, 20) var file_imgbuilder_proto_goTypes = []interface{}{ (BuildStatus)(0), // 0: builder.BuildStatus (*BuildSource)(nil), // 1: builder.BuildSource @@ -1372,13 +1388,14 @@ var file_imgbuilder_proto_goTypes = []interface{}{ (*ListBuildsResponse)(nil), // 16: builder.ListBuildsResponse (*BuildInfo)(nil), // 17: builder.BuildInfo (*LogInfo)(nil), // 18: builder.LogInfo - nil, // 19: builder.LogInfo.HeadersEntry - (*api.WorkspaceInitializer)(nil), // 20: contentservice.WorkspaceInitializer + nil, // 19: builder.BuildRegistryAuth.AdditionalEntry + nil, // 20: builder.LogInfo.HeadersEntry + (*api.WorkspaceInitializer)(nil), // 21: contentservice.WorkspaceInitializer } var file_imgbuilder_proto_depIdxs = []int32{ 2, // 0: builder.BuildSource.ref:type_name -> builder.BuildSourceReference 3, // 1: builder.BuildSource.file:type_name -> builder.BuildSourceDockerfile - 20, // 2: builder.BuildSourceDockerfile.source:type_name -> contentservice.WorkspaceInitializer + 21, // 2: builder.BuildSourceDockerfile.source:type_name -> contentservice.WorkspaceInitializer 9, // 3: builder.ResolveBaseImageRequest.auth:type_name -> builder.BuildRegistryAuth 1, // 4: builder.ResolveWorkspaceImageRequest.source:type_name -> builder.BuildSource 9, // 5: builder.ResolveWorkspaceImageRequest.auth:type_name -> builder.BuildRegistryAuth @@ -1387,27 +1404,28 @@ var file_imgbuilder_proto_depIdxs = []int32{ 9, // 8: builder.BuildRequest.auth:type_name -> builder.BuildRegistryAuth 10, // 9: builder.BuildRegistryAuth.total:type_name -> builder.BuildRegistryAuthTotal 11, // 10: builder.BuildRegistryAuth.selective:type_name -> builder.BuildRegistryAuthSelective - 0, // 11: builder.BuildResponse.status:type_name -> builder.BuildStatus - 17, // 12: builder.BuildResponse.info:type_name -> builder.BuildInfo - 17, // 13: builder.ListBuildsResponse.builds:type_name -> builder.BuildInfo - 0, // 14: builder.BuildInfo.status:type_name -> builder.BuildStatus - 18, // 15: builder.BuildInfo.log_info:type_name -> builder.LogInfo - 19, // 16: builder.LogInfo.headers:type_name -> builder.LogInfo.HeadersEntry - 4, // 17: builder.ImageBuilder.ResolveBaseImage:input_type -> builder.ResolveBaseImageRequest - 6, // 18: builder.ImageBuilder.ResolveWorkspaceImage:input_type -> builder.ResolveWorkspaceImageRequest - 8, // 19: builder.ImageBuilder.Build:input_type -> builder.BuildRequest - 13, // 20: builder.ImageBuilder.Logs:input_type -> builder.LogsRequest - 15, // 21: builder.ImageBuilder.ListBuilds:input_type -> builder.ListBuildsRequest - 5, // 22: builder.ImageBuilder.ResolveBaseImage:output_type -> builder.ResolveBaseImageResponse - 7, // 23: builder.ImageBuilder.ResolveWorkspaceImage:output_type -> builder.ResolveWorkspaceImageResponse - 12, // 24: builder.ImageBuilder.Build:output_type -> builder.BuildResponse - 14, // 25: builder.ImageBuilder.Logs:output_type -> builder.LogsResponse - 16, // 26: builder.ImageBuilder.ListBuilds:output_type -> builder.ListBuildsResponse - 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 + 19, // 11: builder.BuildRegistryAuth.additional:type_name -> builder.BuildRegistryAuth.AdditionalEntry + 0, // 12: builder.BuildResponse.status:type_name -> builder.BuildStatus + 17, // 13: builder.BuildResponse.info:type_name -> builder.BuildInfo + 17, // 14: builder.ListBuildsResponse.builds:type_name -> builder.BuildInfo + 0, // 15: builder.BuildInfo.status:type_name -> builder.BuildStatus + 18, // 16: builder.BuildInfo.log_info:type_name -> builder.LogInfo + 20, // 17: builder.LogInfo.headers:type_name -> builder.LogInfo.HeadersEntry + 4, // 18: builder.ImageBuilder.ResolveBaseImage:input_type -> builder.ResolveBaseImageRequest + 6, // 19: builder.ImageBuilder.ResolveWorkspaceImage:input_type -> builder.ResolveWorkspaceImageRequest + 8, // 20: builder.ImageBuilder.Build:input_type -> builder.BuildRequest + 13, // 21: builder.ImageBuilder.Logs:input_type -> builder.LogsRequest + 15, // 22: builder.ImageBuilder.ListBuilds:input_type -> builder.ListBuildsRequest + 5, // 23: builder.ImageBuilder.ResolveBaseImage:output_type -> builder.ResolveBaseImageResponse + 7, // 24: builder.ImageBuilder.ResolveWorkspaceImage:output_type -> builder.ResolveWorkspaceImageResponse + 12, // 25: builder.ImageBuilder.Build:output_type -> builder.BuildResponse + 14, // 26: builder.ImageBuilder.Logs:output_type -> builder.LogsResponse + 16, // 27: builder.ImageBuilder.ListBuilds:output_type -> builder.ListBuildsResponse + 23, // [23:28] is the sub-list for method output_type + 18, // [18:23] is the sub-list for method input_type + 18, // [18:18] is the sub-list for extension type_name + 18, // [18:18] is the sub-list for extension extendee + 0, // [0:18] is the sub-list for field type_name } func init() { file_imgbuilder_proto_init() } @@ -1647,7 +1665,7 @@ func file_imgbuilder_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_imgbuilder_proto_rawDesc, NumEnums: 1, - NumMessages: 19, + NumMessages: 20, NumExtensions: 0, NumServices: 1, }, diff --git a/components/image-builder-api/imgbuilder.proto b/components/image-builder-api/imgbuilder.proto index a7ea091a51efd1..8592e2508fe53f 100644 --- a/components/image-builder-api/imgbuilder.proto +++ b/components/image-builder-api/imgbuilder.proto @@ -64,7 +64,7 @@ message ResolveWorkspaceImageResponse { message BuildRequest { BuildSource source = 1; BuildRegistryAuth auth = 2; - bool forceRebuild = 3; + bool force_rebuild = 3; } message BuildRegistryAuth { @@ -72,6 +72,7 @@ message BuildRegistryAuth { BuildRegistryAuthTotal total = 1; BuildRegistryAuthSelective selective = 2; } + map additional = 3; } message BuildRegistryAuthTotal { diff --git a/components/image-builder-api/typescript/src/imgbuilder_pb.d.ts b/components/image-builder-api/typescript/src/imgbuilder_pb.d.ts index c1a17cac6dc666..b974dc291878d7 100644 --- a/components/image-builder-api/typescript/src/imgbuilder_pb.d.ts +++ b/components/image-builder-api/typescript/src/imgbuilder_pb.d.ts @@ -215,8 +215,8 @@ export class BuildRequest extends jspb.Message { clearAuth(): void; getAuth(): BuildRegistryAuth | undefined; setAuth(value?: BuildRegistryAuth): BuildRequest; - getForcerebuild(): boolean; - setForcerebuild(value: boolean): BuildRequest; + getForceRebuild(): boolean; + setForceRebuild(value: boolean): BuildRequest; serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): BuildRequest.AsObject; @@ -232,7 +232,7 @@ export namespace BuildRequest { export type AsObject = { source?: BuildSource.AsObject, auth?: BuildRegistryAuth.AsObject, - forcerebuild: boolean, + forceRebuild: boolean, } } @@ -248,6 +248,9 @@ export class BuildRegistryAuth extends jspb.Message { getSelective(): BuildRegistryAuthSelective | undefined; setSelective(value?: BuildRegistryAuthSelective): BuildRegistryAuth; + getAdditionalMap(): jspb.Map; + clearAdditionalMap(): void; + getModeCase(): BuildRegistryAuth.ModeCase; serializeBinary(): Uint8Array; @@ -264,6 +267,8 @@ export namespace BuildRegistryAuth { export type AsObject = { total?: BuildRegistryAuthTotal.AsObject, selective?: BuildRegistryAuthSelective.AsObject, + + additionalMap: Array<[string, string]>, } export enum ModeCase { diff --git a/components/image-builder-api/typescript/src/imgbuilder_pb.js b/components/image-builder-api/typescript/src/imgbuilder_pb.js index cdd94a9affa125..f299c6112235bd 100644 --- a/components/image-builder-api/typescript/src/imgbuilder_pb.js +++ b/components/image-builder-api/typescript/src/imgbuilder_pb.js @@ -1764,7 +1764,7 @@ proto.builder.BuildRequest.toObject = function(includeInstance, msg) { var f, obj = { source: (f = msg.getSource()) && proto.builder.BuildSource.toObject(includeInstance, f), auth: (f = msg.getAuth()) && proto.builder.BuildRegistryAuth.toObject(includeInstance, f), - forcerebuild: jspb.Message.getBooleanFieldWithDefault(msg, 3, false) + forceRebuild: jspb.Message.getBooleanFieldWithDefault(msg, 3, false) }; if (includeInstance) { @@ -1813,7 +1813,7 @@ proto.builder.BuildRequest.deserializeBinaryFromReader = function(msg, reader) { break; case 3: var value = /** @type {boolean} */ (reader.readBool()); - msg.setForcerebuild(value); + msg.setForceRebuild(value); break; default: reader.skipField(); @@ -1860,7 +1860,7 @@ proto.builder.BuildRequest.serializeBinaryToWriter = function(message, writer) { proto.builder.BuildRegistryAuth.serializeBinaryToWriter ); } - f = message.getForcerebuild(); + f = message.getForceRebuild(); if (f) { writer.writeBool( 3, @@ -1945,10 +1945,10 @@ proto.builder.BuildRequest.prototype.hasAuth = function() { /** - * optional bool forceRebuild = 3; + * optional bool force_rebuild = 3; * @return {boolean} */ -proto.builder.BuildRequest.prototype.getForcerebuild = function() { +proto.builder.BuildRequest.prototype.getForceRebuild = function() { return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 3, false)); }; @@ -1957,7 +1957,7 @@ proto.builder.BuildRequest.prototype.getForcerebuild = function() { * @param {boolean} value * @return {!proto.builder.BuildRequest} returns this */ -proto.builder.BuildRequest.prototype.setForcerebuild = function(value) { +proto.builder.BuildRequest.prototype.setForceRebuild = function(value) { return jspb.Message.setProto3BooleanField(this, 3, value); }; @@ -2021,7 +2021,8 @@ proto.builder.BuildRegistryAuth.prototype.toObject = function(opt_includeInstanc proto.builder.BuildRegistryAuth.toObject = function(includeInstance, msg) { var f, obj = { total: (f = msg.getTotal()) && proto.builder.BuildRegistryAuthTotal.toObject(includeInstance, f), - selective: (f = msg.getSelective()) && proto.builder.BuildRegistryAuthSelective.toObject(includeInstance, f) + selective: (f = msg.getSelective()) && proto.builder.BuildRegistryAuthSelective.toObject(includeInstance, f), + additionalMap: (f = msg.getAdditionalMap()) ? f.toObject(includeInstance, undefined) : [] }; if (includeInstance) { @@ -2068,6 +2069,12 @@ proto.builder.BuildRegistryAuth.deserializeBinaryFromReader = function(msg, read reader.readMessage(value,proto.builder.BuildRegistryAuthSelective.deserializeBinaryFromReader); msg.setSelective(value); break; + case 3: + var value = msg.getAdditionalMap(); + reader.readMessage(value, function(message, reader) { + jspb.Map.deserializeBinary(message, reader, jspb.BinaryReader.prototype.readString, jspb.BinaryReader.prototype.readString, null, "", ""); + }); + break; default: reader.skipField(); break; @@ -2113,6 +2120,10 @@ proto.builder.BuildRegistryAuth.serializeBinaryToWriter = function(message, writ proto.builder.BuildRegistryAuthSelective.serializeBinaryToWriter ); } + f = message.getAdditionalMap(true); + if (f && f.getLength() > 0) { + f.serializeBinary(3, writer, jspb.BinaryWriter.prototype.writeString, jspb.BinaryWriter.prototype.writeString); + } }; @@ -2190,6 +2201,28 @@ proto.builder.BuildRegistryAuth.prototype.hasSelective = function() { }; +/** + * map additional = 3; + * @param {boolean=} opt_noLazyCreate Do not create the map if + * empty, instead returning `undefined` + * @return {!jspb.Map} + */ +proto.builder.BuildRegistryAuth.prototype.getAdditionalMap = function(opt_noLazyCreate) { + return /** @type {!jspb.Map} */ ( + jspb.Message.getMapField(this, 3, opt_noLazyCreate, + null)); +}; + + +/** + * Clears values from the map. The map will be non-null. + * @return {!proto.builder.BuildRegistryAuth} returns this + */ +proto.builder.BuildRegistryAuth.prototype.clearAdditionalMap = function() { + this.getAdditionalMap().clear(); + return this;}; + + diff --git a/components/image-builder-bob/cmd/proxy.go b/components/image-builder-bob/cmd/proxy.go index f2917e8605613a..7954892d9fd123 100644 --- a/components/image-builder-bob/cmd/proxy.go +++ b/components/image-builder-bob/cmd/proxy.go @@ -20,6 +20,7 @@ import ( var proxyOpts struct { BaseRef, TargetRef string Auth string + AdditionalAuth string } // proxyCmd represents the build command @@ -30,10 +31,15 @@ var proxyCmd = &cobra.Command{ log.Init("bob", "", true, os.Getenv("SUPERVISOR_DEBUG_ENABLE") == "true") log := log.WithField("command", "proxy") - authP, err := proxy.NewAuthorizerFromEnvVar(proxyOpts.Auth) + authP, err := proxy.NewAuthorizerFromDockerEnvVar(proxyOpts.Auth) if err != nil { log.WithError(err).WithField("auth", proxyOpts.Auth).Fatal("cannot unmarshal auth") } + authA, err := proxy.NewAuthorizerFromEnvVar(proxyOpts.AdditionalAuth) + if err != nil { + log.WithError(err).WithField("auth", proxyOpts.Auth).Fatal("cannot unmarshal auth") + } + authP = authP.AddIfNotExists(authA) baseref, err := reference.ParseNormalizedNamed(proxyOpts.BaseRef) if err != nil { @@ -87,4 +93,5 @@ func init() { proxyCmd.Flags().StringVar(&proxyOpts.BaseRef, "base-ref", os.Getenv("WORKSPACEKIT_BOBPROXY_BASEREF"), "ref of the base image") proxyCmd.Flags().StringVar(&proxyOpts.TargetRef, "target-ref", os.Getenv("WORKSPACEKIT_BOBPROXY_TARGETREF"), "ref of the target image") proxyCmd.Flags().StringVar(&proxyOpts.Auth, "auth", os.Getenv("WORKSPACEKIT_BOBPROXY_AUTH"), "authentication to use") + proxyCmd.Flags().StringVar(&proxyOpts.AdditionalAuth, "additional-auth", os.Getenv("WORKSPACEKIT_BOBPROXY_ADDITIONALAUTH"), "additional authentication to use") } diff --git a/components/image-builder-bob/pkg/proxy/auth.go b/components/image-builder-bob/pkg/proxy/auth.go index 21f4613219792a..7e1ec6a4603674 100644 --- a/components/image-builder-bob/pkg/proxy/auth.go +++ b/components/image-builder-bob/pkg/proxy/auth.go @@ -20,9 +20,9 @@ type authConfig struct { Auth string `json:"auth"` } -type authorizerImpl map[string]authConfig +type MapAuthorizer map[string]authConfig -func (a authorizerImpl) Authorize(host string) (user, pass string, err error) { +func (a MapAuthorizer) Authorize(host string) (user, pass string, err error) { defer func() { log.WithFields(logrus.Fields{ "host": host, @@ -54,11 +54,25 @@ func (a authorizerImpl) Authorize(host string) (user, pass string, err error) { return } +func (a MapAuthorizer) AddIfNotExists(other MapAuthorizer) MapAuthorizer { + res := make(map[string]authConfig) + for k, v := range a { + res[k] = v + } + for k, v := range other { + if _, ok := a[k]; ok { + continue + } + res[k] = v + } + return MapAuthorizer(res) +} + type Authorizer interface { Authorize(host string) (user, pass string, err error) } -func NewAuthorizerFromEnvVar(content string) (auth Authorizer, err error) { +func NewAuthorizerFromDockerEnvVar(content string) (auth MapAuthorizer, err error) { var res struct { Auths map[string]authConfig `json:"auths"` } @@ -66,5 +80,18 @@ func NewAuthorizerFromEnvVar(content string) (auth Authorizer, err error) { if err != nil { return } - return authorizerImpl(res.Auths), nil + return MapAuthorizer(res.Auths), nil +} + +func NewAuthorizerFromEnvVar(content string) (auth MapAuthorizer, err error) { + if content == "" { + return nil, nil + } + + var res map[string]authConfig + err = json.Unmarshal([]byte(content), &res) + if err != nil { + return nil, err + } + return MapAuthorizer(res), nil } diff --git a/components/image-builder-mk3/debug.sh b/components/image-builder-mk3/debug.sh index d1750574b66107..37f8ab13f08131 100755 --- a/components/image-builder-mk3/debug.sh +++ b/components/image-builder-mk3/debug.sh @@ -5,8 +5,15 @@ docker ps &> /dev/null || (echo "You need a working Docker daemon. Maybe set DOCKER_HOST?"; exit 1) gcloud auth list | grep typefox &>/dev/null || (echo "Login using 'gcloud auth login' for the docker push to work"; exit 1) -leeway build .:docker -Dversion=dev -devImage=eu.gcr.io/gitpod-dev/image-builder:dev -kubectl patch deployment image-builder --patch '{"spec": {"template": {"spec": {"containers": [{"name": "service","image": "'$devImage'"}]}}}}' -kubectl get pods --no-headers -o=custom-columns=:metadata.name | grep image-builder | xargs kubectl delete pod \ No newline at end of file +readonly tag +tag="dev-$(date +%s)" +leeway build -v .:docker -Dversion="${tag}" -DimageRepoBase=eu.gcr.io/gitpod-core-dev/build +devImage="eu.gcr.io/gitpod-core-dev/build/image-builder-mk3:${tag}" + +kubectl patch deployment image-builder-mk3 --patch '{"spec": {"template": {"spec": {"containers": [{"name": "image-builder-mk3","imagePullPolicy":"Always","image": "'"$devImage"'"}]}}}}' +kubectl rollout restart deployment/image-builder-mk3 +kubectl rollout status -w deployment/image-builder-mk3 +# give the old pod time to disappear +sleep 20 +gpctl debug logs image-builder-mk3 \ No newline at end of file diff --git a/components/image-builder-mk3/pkg/auth/auth.go b/components/image-builder-mk3/pkg/auth/auth.go index eb79fbed723e5f..fa674661b6c40d 100644 --- a/components/image-builder-mk3/pkg/auth/auth.go +++ b/components/image-builder-mk3/pkg/auth/auth.go @@ -5,7 +5,9 @@ package auth import ( + "encoding/base64" "os" + "strings" "github.com/docker/cli/cli/config/configfile" "github.com/docker/distribution/reference" @@ -67,16 +69,16 @@ type Authentication types.AuthConfig // AllowedAuthFor describes for which repositories authentication may be provided for type AllowedAuthFor struct { - All bool - Explicit []string + All bool + Explicit []string + Additional map[string]string } -var ( - // AllowedAuthForAll means auth for all repositories is allowed - AllowedAuthForAll AllowedAuthFor = AllowedAuthFor{true, nil} - // AllowedAuthForNone means auth for no repositories is allowed - AllowedAuthForNone AllowedAuthFor = AllowedAuthFor{false, nil} -) +// AllowedAuthForAll means auth for all repositories is allowed +func AllowedAuthForAll() AllowedAuthFor { return AllowedAuthFor{true, nil, nil} } + +// AllowedAuthForNone means auth for no repositories is allowed +func AllowedAuthForNone() AllowedAuthFor { return AllowedAuthFor{false, nil, nil} } // IsAllowNone returns true if we are to allow authentication for no repos func (a AllowedAuthFor) IsAllowNone() bool { @@ -96,7 +98,7 @@ func (a AllowedAuthFor) Elevate(ref string) AllowedAuthFor { return a } - return AllowedAuthFor{a.All, append(a.Explicit, reference.Domain(pref))} + return AllowedAuthFor{a.All, append(a.Explicit, reference.Domain(pref)), a.Additional} } // ExplicitlyAll produces an AllowedAuthFor that allows authentication for all @@ -117,7 +119,7 @@ type Resolver struct { // ResolveRequestAuth computes the allowed authentication for a build based on its request func (r Resolver) ResolveRequestAuth(auth *api.BuildRegistryAuth) (authFor AllowedAuthFor) { // by default we allow nothing - authFor = AllowedAuthForNone + authFor = AllowedAuthForNone() if auth == nil { return } @@ -125,9 +127,9 @@ func (r Resolver) ResolveRequestAuth(auth *api.BuildRegistryAuth) (authFor Allow switch ath := auth.Mode.(type) { case *api.BuildRegistryAuth_Total: if ath.Total.AllowAll { - authFor = AllowedAuthForAll + authFor = AllowedAuthForAll() } else { - authFor = AllowedAuthForNone + authFor = AllowedAuthForNone() } case *api.BuildRegistryAuth_Selective: var explicit []string @@ -140,10 +142,13 @@ func (r Resolver) ResolveRequestAuth(auth *api.BuildRegistryAuth) (authFor Allow explicit = append(explicit, reference.Domain(ref)) } explicit = append(explicit, ath.Selective.AnyOf...) - authFor = AllowedAuthFor{false, explicit} + authFor = AllowedAuthFor{false, explicit, nil} default: - authFor = AllowedAuthForNone + authFor = AllowedAuthForNone() } + + authFor.Additional = auth.Additional + return } @@ -157,8 +162,20 @@ func (a AllowedAuthFor) GetAuthFor(auth RegistryAuthenticator, refstr string) (r if err != nil { return nil, xerrors.Errorf("cannot parse image ref: %v", err) } - reg := reference.Domain(ref) + + // If we haven't found authentication using the built-in way, we'll resort to additional auth + // the user sent us. + defer func() { + if err == nil && res == nil { + res = a.additionalAuth(reg) + + if res != nil { + log.WithField("reg", reg).Debug("found additional auth") + } + } + }() + var regAllowed bool if a.IsAllowAll() { // free for all @@ -179,57 +196,45 @@ func (a AllowedAuthFor) GetAuthFor(auth RegistryAuthenticator, refstr string) (r return auth.Authenticate(reg) } +func (a AllowedAuthFor) additionalAuth(domain string) *Authentication { + ath, ok := a.Additional[domain] + if !ok { + return nil + } + + res := &Authentication{ + Auth: ath, + } + dec, err := base64.StdEncoding.DecodeString(ath) + if err == nil { + segs := strings.Split(string(dec), ":") + if len(segs) == 2 { + res.Username = segs[0] + res.Password = segs[1] + } + } + return res +} + // ImageBuildAuth is the format image builds needs type ImageBuildAuth map[string]types.AuthConfig // GetImageBuildAuthFor produces authentication in the format an image builds needs -func (a AllowedAuthFor) GetImageBuildAuthFor(auth RegistryAuthenticator, refstr []string) (res ImageBuildAuth, err error) { - if auth == nil { - return nil, nil - } - +func (a AllowedAuthFor) GetImageBuildAuthFor(blocklist []string) (res ImageBuildAuth) { res = make(ImageBuildAuth) - for _, r := range refstr { - ref, err := reference.ParseNormalizedNamed(r) - if err != nil { - return nil, xerrors.Errorf("cannot parse image ref: %v", err) - } - - reg := reference.Domain(ref) - var regAllowed bool - if a.IsAllowAll() { - // free for all - regAllowed = true - } else { - for _, a := range a.Explicit { - if a == reg { - regAllowed = true - break - } + for reg := range a.Additional { + var blocked bool + for _, blk := range blocklist { + if blk == reg { + blocked = true + break } } - if !regAllowed { + if blocked { continue } - - ra, err := auth.Authenticate(reg) - if err != nil { - return nil, xerrors.Errorf("cannot get registry authentication: %v", err) - } - - res[reg] = types.AuthConfig(*ra) - } - for _, reg := range a.Explicit { - if _, ok := res[reg]; ok { - continue - } - - ra, err := auth.Authenticate(reg) - if err != nil { - return nil, xerrors.Errorf("cannot get registry authentication: %v", err) - } - - res[reg] = types.AuthConfig(*ra) + ath := a.additionalAuth(reg) + res[reg] = types.AuthConfig(*ath) } return diff --git a/components/image-builder-mk3/pkg/orchestrator/orchestrator.go b/components/image-builder-mk3/pkg/orchestrator/orchestrator.go index e753d3aae05121..5acdb3bbaf1a0a 100644 --- a/components/image-builder-mk3/pkg/orchestrator/orchestrator.go +++ b/components/image-builder-mk3/pkg/orchestrator/orchestrator.go @@ -7,6 +7,7 @@ package orchestrator import ( "context" "crypto/sha256" + "encoding/json" "errors" "fmt" "io" @@ -26,6 +27,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" "google.golang.org/grpc/status" + "google.golang.org/protobuf/encoding/protojson" common_grpc "github.com/gitpod-io/gitpod/common-go/grpc" "github.com/gitpod-io/gitpod/common-go/log" @@ -144,9 +146,12 @@ func (o *Orchestrator) Start(ctx context.Context) error { func (o *Orchestrator) ResolveBaseImage(ctx context.Context, req *protocol.ResolveBaseImageRequest) (resp *protocol.ResolveBaseImageResponse, err error) { span, ctx := opentracing.StartSpanFromContext(ctx, "ResolveBaseImage") defer tracing.FinishSpan(span, &err) - tracing.LogRequestSafe(span, req) + reqs, _ := protojson.Marshal(req) + safeReqs, _ := log.RedactJSON(reqs) + log.WithField("req", safeReqs).Debug("ResolveBaseImage") + reqauth := o.AuthResolver.ResolveRequestAuth(req.Auth) refstr, err := o.getAbsoluteImageRef(ctx, req.Ref, reqauth) @@ -165,12 +170,16 @@ func (o *Orchestrator) ResolveWorkspaceImage(ctx context.Context, req *protocol. defer tracing.FinishSpan(span, &err) tracing.LogRequestSafe(span, req) + reqs, _ := protojson.Marshal(req) + safeReqs, _ := log.RedactJSON(reqs) + log.WithField("req", safeReqs).Debug("ResolveWorkspaceImage") + reqauth := o.AuthResolver.ResolveRequestAuth(req.Auth) baseref, err := o.getBaseImageRef(ctx, req.Source, reqauth) if err != nil { return nil, status.Errorf(codes.Internal, "cannot resolve base image: %s", err.Error()) } - refstr, err := o.getWorkspaceImageRef(ctx, baseref, reqauth) + refstr, err := o.getWorkspaceImageRef(ctx, baseref) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "cannot produce image ref: %v", err) } @@ -178,7 +187,7 @@ func (o *Orchestrator) ResolveWorkspaceImage(ctx context.Context, req *protocol. // to check if the image exists we must have access to the image caching registry and the refstr we check here does not come // from the user. Thus we can safely use auth.AllowedAuthForAll here. - auth, err := auth.AllowedAuthForAll.GetAuthFor(o.Auth, refstr) + auth, err := auth.AllowedAuthForAll().GetAuthFor(o.Auth, refstr) if err != nil { return nil, status.Errorf(codes.Internal, "cannot get workspace image authentication: %v", err) } @@ -221,11 +230,11 @@ func (o *Orchestrator) Build(req *protocol.BuildRequest, resp protocol.ImageBuil if err != nil { return status.Errorf(codes.Internal, "cannot resolve base image: %q", err) } - wsrefstr, err := o.getWorkspaceImageRef(ctx, baseref, reqauth) + wsrefstr, err := o.getWorkspaceImageRef(ctx, baseref) if err != nil { return status.Errorf(codes.Internal, "cannot produce workspace image ref: %q", err) } - wsrefAuth, err := auth.AllowedAuthForAll.GetAuthFor(o.Auth, wsrefstr) + wsrefAuth, err := auth.AllowedAuthForAll().GetAuthFor(o.Auth, wsrefstr) if err != nil { return status.Errorf(codes.Internal, "cannot get workspace image authentication: %q", err) } @@ -238,7 +247,7 @@ func (o *Orchestrator) Build(req *protocol.BuildRequest, resp protocol.ImageBuil if exists && !req.GetForceRebuild() { // If the workspace image exists, so should the baseimage if we've built it. // If we didn't build it and the base image doesn't exist anymore, getWorkspaceImageRef will have failed to resolve the baseref. - baserefAbsolute, err := o.getAbsoluteImageRef(ctx, baseref, auth.AllowedAuthForAll) + baserefAbsolute, err := o.getAbsoluteImageRef(ctx, baseref, auth.AllowedAuthForAll()) if err != nil { return status.Errorf(codes.Internal, "cannot resolve base image ref: %q", err) } @@ -317,6 +326,16 @@ func (o *Orchestrator) Build(req *protocol.BuildRequest, resp protocol.ImageBuil } else { bobBaseref += ":latest" } + wsref, err := reference.ParseNamed(wsrefstr) + var additionalAuth []byte + if err == nil { + additionalAuth, err = json.Marshal(reqauth.GetImageBuildAuthFor([]string{ + reference.Domain(wsref), + })) + if err != nil { + return xerrors.Errorf("cannot marshal additional auth: %w", err) + } + } var swr *wsmanapi.StartWorkspaceResponse err = retry(ctx, func(ctx context.Context) (err error) { @@ -360,6 +379,10 @@ func (o *Orchestrator) Build(req *protocol.BuildRequest, resp protocol.ImageBuil Key: ".dockerconfigjson", }, }, + { + Name: "WORKSPACEKIT_BOBPROXY_ADDITIONALAUTH", + Value: string(additionalAuth), + }, {Name: "SUPERVISOR_DEBUG_ENABLE", Value: fmt.Sprintf("%v", log.Log.Logger.IsLevelEnabled(logrus.DebugLevel))}, }, }, @@ -592,7 +615,7 @@ func (o *Orchestrator) getBaseImageRef(ctx context.Context, bs *protocol.BuildSo } } -func (o *Orchestrator) getWorkspaceImageRef(ctx context.Context, baseref string, allowedAuth auth.AllowedAuthFor) (ref string, err error) { +func (o *Orchestrator) getWorkspaceImageRef(ctx context.Context, baseref string) (ref string, err error) { cnt := []byte(fmt.Sprintf("%s\n%d\n", baseref, workspaceBuildProcessVersion)) hash := sha256.New() n, err := hash.Write(cnt) diff --git a/components/server/src/workspace/gitpod-server-impl.ts b/components/server/src/workspace/gitpod-server-impl.ts index 78e71624a001b6..64cc885b53a0de 100644 --- a/components/server/src/workspace/gitpod-server-impl.ts +++ b/components/server/src/workspace/gitpod-server-impl.ts @@ -518,7 +518,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { throw new ResponseError(ErrorCodes.PERMISSION_DENIED, "Cannot (re-)start a deleted workspace."); } const userEnvVars = this.userDB.getEnvVars(user.id); - let projectEnvVarsPromise = this.internalGetPublicProjectEnvVars(workspace.projectId); + let projectEnvVarsPromise = this.internalGetProjectEnvVars(workspace.projectId); await mayStartPromise; @@ -908,7 +908,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { throw err; } - let projectEnvVarsPromise = this.internalGetPublicProjectEnvVars(workspace.projectId); + let projectEnvVarsPromise = this.internalGetProjectEnvVars(workspace.projectId); logContext.workspaceId = workspace.id; traceWI(ctx, { workspaceId: workspace.id }); @@ -1582,7 +1582,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { traceAPIParams(ctx, { projectId }); const user = this.checkAndBlockUser("getProjectEnvironmentVariables"); await this.guardProjectOperation(user, projectId, "get"); - return this.projectsService.getProjectEnvironmentVariables(projectId); + return await this.projectsService.getProjectEnvironmentVariables(projectId); } async deleteProjectEnvironmentVariable(ctx: TraceContext, variableId: string): Promise { @@ -1596,16 +1596,11 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { return this.projectsService.deleteProjectEnvironmentVariable(envVar.id); } - protected async internalGetPublicProjectEnvVars(projectId?: string): Promise { + protected async internalGetProjectEnvVars(projectId?: string): Promise { if (!projectId) { return []; } - const projectEnvVars = await this.projectsService.getProjectEnvironmentVariables(projectId); - // Instead of using an access guard for Project environment variables, we let Project owners decide whether - // a variable should be: - // - exposed in all workspaces (even for non-Project members when the repository is public), or - // - censored from all workspaces (even for Project members) - return projectEnvVars.filter(variable => !variable.censored); + return await this.projectsService.getProjectEnvironmentVariables(projectId); } protected async guardTeamOperation(teamId: string | undefined, op: ResourceAccessOp): Promise { diff --git a/components/server/src/workspace/image-source-provider.ts b/components/server/src/workspace/image-source-provider.ts index 39cad1aa61e320..10fb4b55a02746 100644 --- a/components/server/src/workspace/image-source-provider.ts +++ b/components/server/src/workspace/image-source-provider.ts @@ -5,7 +5,7 @@ */ import { injectable, inject } from "inversify"; -import { ImageBuilderClientProvider, ResolveBaseImageRequest, BuildRegistryAuthTotal, BuildRegistryAuth } from "@gitpod/image-builder/lib"; +import { ImageBuilderClientProvider } from "@gitpod/image-builder/lib"; import { HostContextProvider } from "../auth/host-context-provider"; import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; import { CommitContext, WorkspaceImageSource, WorkspaceConfig, WorkspaceImageSourceReference, WorkspaceImageSourceDocker, ImageConfigFile, ExternalImageConfigFile, User, AdditionalContentContext } from "@gitpod/gitpod-protocol"; @@ -57,27 +57,8 @@ export class ImageSourceProvider { dockerFileHash: lastDockerFileSha, } } else if (typeof (imgcfg) === "string") { - // We resolve this request allowing all configured auth because at this poing we don't have access to the user or permission service. - // If anyone feels like changing this and properly use the REGISTRY_ACCESS permission, be my guest. - // - // This might leak a tiny bit of information in that the workspace start failes - // differently if the image is present as compared to when it's not. This way users can find out if an image exists even if they don't - // have access to the registry themselves. - // - // If feel this issue is negligeble. - const req = new ResolveBaseImageRequest(); - req.setRef(imgcfg); - const allowAll = new BuildRegistryAuthTotal(); - allowAll.setAllowAll(true); - const auth = new BuildRegistryAuth(); - auth.setTotal(allowAll); - req.setAuth(auth); - - const client = this.imagebuilderClientProvider.getDefault(); - const res = await client.resolveBaseImage({ span }, req); - result = { - baseImageResolved: res.getRef() + baseImageResolved: imgcfg } } else { throw new Error("unknown workspace image source config"); diff --git a/components/server/src/workspace/workspace-starter.ts b/components/server/src/workspace/workspace-starter.ts index 7177cd946f173c..5e8ef51fc7ea2f 100644 --- a/components/server/src/workspace/workspace-starter.ts +++ b/components/server/src/workspace/workspace-starter.ts @@ -7,7 +7,7 @@ import { CloneTargetMode, FileDownloadInitializer, GitAuthMethod, GitConfig, GitInitializer, PrebuildInitializer, SnapshotInitializer, WorkspaceInitializer } from "@gitpod/content-service/lib"; import { CompositeInitializer, FromBackupInitializer } from "@gitpod/content-service/lib/initializer_pb"; import { DBUser, DBWithTracing, ProjectDB, TracedUserDB, TracedWorkspaceDB, UserDB, WorkspaceDB } from '@gitpod/gitpod-db/lib'; -import { CommitContext, Disposable, GitpodToken, GitpodTokenType, IssueContext, NamedWorkspaceFeatureFlag, PullRequestContext, RefType, SnapshotContext, StartWorkspaceResult, User, UserEnvVar, UserEnvVarValue, WithEnvvarsContext, WithPrebuild, Workspace, WorkspaceContext, WorkspaceImageSource, WorkspaceImageSourceDocker, WorkspaceImageSourceReference, WorkspaceInstance, WorkspaceInstanceConfiguration, WorkspaceInstanceStatus, WorkspaceProbeContext, Permission, HeadlessWorkspaceEvent, HeadlessWorkspaceEventType, DisposableCollection, AdditionalContentContext, ImageConfigFile, ProjectEnvVar, ImageBuildLogInfo } from "@gitpod/gitpod-protocol"; +import { CommitContext, Disposable, GitpodToken, GitpodTokenType, IssueContext, NamedWorkspaceFeatureFlag, PullRequestContext, RefType, SnapshotContext, StartWorkspaceResult, User, UserEnvVar, UserEnvVarValue, WithEnvvarsContext, WithPrebuild, Workspace, WorkspaceContext, WorkspaceImageSource, WorkspaceImageSourceDocker, WorkspaceImageSourceReference, WorkspaceInstance, WorkspaceInstanceConfiguration, WorkspaceInstanceStatus, WorkspaceProbeContext, Permission, HeadlessWorkspaceEvent, HeadlessWorkspaceEventType, DisposableCollection, AdditionalContentContext, ImageConfigFile, ImageBuildLogInfo, ProjectEnvVar } from "@gitpod/gitpod-protocol"; import { IAnalyticsWriter } from '@gitpod/gitpod-protocol/lib/analytics'; import { log } from '@gitpod/gitpod-protocol/lib/util/logging'; import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; @@ -118,7 +118,8 @@ export class WorkspaceStarter { try { // if we need to build the workspace image we musn't wait for actuallyStartWorkspace to return as that would block the // frontend until the image is built. - needsImageBuild = forceRebuild || await this.needsImageBuild({ span }, user, workspace, instance); + const additionalAuth = await this.getAdditionalImageAuth(projectEnvVars); + needsImageBuild = forceRebuild || await this.needsImageBuild({ span }, user, workspace, instance, additionalAuth); if (needsImageBuild) { instance.status.conditions = { neededImageBuild: true, @@ -157,7 +158,8 @@ export class WorkspaceStarter { try { // build workspace image - instance = await this.buildWorkspaceImage({ span }, user, workspace, instance, forceRebuild, forceRebuild); + const additionalAuth = await this.getAdditionalImageAuth(projectEnvVars); + instance = await this.buildWorkspaceImage({ span }, user, workspace, instance, additionalAuth, forceRebuild, forceRebuild); let type: WorkspaceType = WorkspaceType.REGULAR; if (workspace.type === 'prebuild') { @@ -283,6 +285,23 @@ export class WorkspaceStarter { return undefined; } + protected async getAdditionalImageAuth(projectEnvVars: ProjectEnvVar[]): Promise> { + const res = new Map(); + const imageAuth = projectEnvVars.find(e => e.name === 'GITPOD_IMAGE_AUTH'); + if (!imageAuth) { + return res; + } + + const imageAuthValue = (await this.projectDB.getProjectEnvironmentVariableValues([imageAuth]))[0]; + + (imageAuthValue.value || "") + .split(",") + .map(e => e.trim().split(":")) + .filter(e => e.length == 2) + .forEach(e => res.set(e[0], e[1])); + return res; + } + protected async notifyOnPrebuildQueued(ctx: TraceContext, workspaceId: string) { const span = TraceContext.startSpan("notifyOnPrebuildQueued", ctx); const prebuild = await this.workspaceDb.trace({ span }).findPrebuildByWorkspaceID(workspaceId); @@ -466,7 +485,7 @@ export class WorkspaceStarter { return undefined; } - protected async prepareBuildRequest(ctx: TraceContext, workspace: Workspace, imgsrc: WorkspaceImageSource, user: User, ignoreBaseImageresolvedAndRebuildBase: boolean = false): Promise<{ src: BuildSource, auth: BuildRegistryAuth, disposable?: Disposable }> { + protected async prepareBuildRequest(ctx: TraceContext, workspace: Workspace, imgsrc: WorkspaceImageSource, user: User, additionalAuth: Map, ignoreBaseImageresolvedAndRebuildBase: boolean = false): Promise<{ src: BuildSource, auth: BuildRegistryAuth, disposable?: Disposable }> { const span = TraceContext.startSpan("prepareBuildRequest", ctx); try { @@ -507,6 +526,7 @@ export class WorkspaceStarter { selectiveAuth.setAnyOfList(this.config.defaultBaseImageRegistryWhitelist); auth.setSelective(selectiveAuth); } + additionalAuth.forEach((val, key) => auth.getAdditionalMap().set(key, val)); if (WorkspaceImageSourceDocker.is(imgsrc)) { let source: WorkspaceInitializer; const disp = new DisposableCollection(); @@ -564,11 +584,11 @@ export class WorkspaceStarter { } } - protected async needsImageBuild(ctx: TraceContext, user: User, workspace: Workspace, instance: WorkspaceInstance): Promise { + protected async needsImageBuild(ctx: TraceContext, user: User, workspace: Workspace, instance: WorkspaceInstance, additionalAuth: Map): Promise { const span = TraceContext.startSpan("needsImageBuild", ctx); try { const client = this.imagebuilderClientProvider.getDefault(); - const { src, auth, disposable } = await this.prepareBuildRequest({ span }, workspace, workspace.imageSource!, user); + const { src, auth, disposable } = await this.prepareBuildRequest({ span }, workspace, workspace.imageSource!, user, additionalAuth); const req = new ResolveWorkspaceImageRequest(); req.setSource(src); @@ -588,18 +608,18 @@ export class WorkspaceStarter { } } - protected async buildWorkspaceImage(ctx: TraceContext, user: User, workspace: Workspace, instance: WorkspaceInstance, ignoreBaseImageresolvedAndRebuildBase: boolean = false, forceRebuild: boolean = false): Promise { + protected async buildWorkspaceImage(ctx: TraceContext, user: User, workspace: Workspace, instance: WorkspaceInstance, additionalAuth: Map, ignoreBaseImageresolvedAndRebuildBase: boolean = false, forceRebuild: boolean = false): Promise { const span = TraceContext.startSpan("buildWorkspaceImage", ctx); try { // Start build... const client = this.imagebuilderClientProvider.getDefault(); - const { src, auth, disposable } = await this.prepareBuildRequest({ span }, workspace, workspace.imageSource!, user, ignoreBaseImageresolvedAndRebuildBase || forceRebuild); + const { src, auth, disposable } = await this.prepareBuildRequest({ span }, workspace, workspace.imageSource!, user, additionalAuth, ignoreBaseImageresolvedAndRebuildBase || forceRebuild); const req = new BuildRequest(); req.setSource(src); req.setAuth(auth); - req.setForcerebuild(forceRebuild); + req.setForceRebuild(forceRebuild); // Make sure we persist logInfo as soon as we retrieve it const imageBuildLogInfo = new Deferred(); @@ -636,7 +656,7 @@ export class WorkspaceStarter { if (err && err.message && err.message.includes("base image does not exist") && !ignoreBaseImageresolvedAndRebuildBase) { // we've attempted to add the base layer for a workspace whoose base image has gone missing. // Ignore the previously built (now missing) base image and force a rebuild. - return this.buildWorkspaceImage(ctx, user, workspace, instance, true, forceRebuild); + return this.buildWorkspaceImage(ctx, user, workspace, instance, additionalAuth, true, forceRebuild); } else { throw err; } @@ -698,8 +718,16 @@ export class WorkspaceStarter { } } if (projectEnvVars.length > 0) { - const projectEnvVarsWithValues = await this.projectDB.getProjectEnvironmentVariableValues(projectEnvVars); - allEnvVars = allEnvVars.concat(projectEnvVarsWithValues); + // Instead of using an access guard for Project environment variables, we let Project owners decide whether + // a variable should be: + // - exposed in all workspaces (even for non-Project members when the repository is public), or + // - censored from all workspaces (even for Project members) + let availablePrjEnvVars = projectEnvVars; + if (workspace.type !== 'prebuild') { + availablePrjEnvVars = projectEnvVars.filter(variable => !variable.censored); + } + const withValues = await this.projectDB.getProjectEnvironmentVariableValues(availablePrjEnvVars); + allEnvVars = allEnvVars.concat(withValues); } if (WithEnvvarsContext.is(context)) { allEnvVars = allEnvVars.concat(context.envvars);