diff --git a/CLAUDE.md b/CLAUDE.md index 4d60f14b9..6467f03c0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -257,15 +257,21 @@ All commits must meet these criteria: Code reviews are required for all submissions via GitHub pull requests. -### AI Disclosure +### AI Disclosure (REQUIRED) -If AI assisted in producing any part of a contribution, disclose it in the PR description. Add an `Assisted-by:` trailer to each affected commit: +**This is mandatory, not optional.** If AI assisted in producing any part of a contribution: -``` -Assisted-by: GitHub Copilot -Assisted-by: Claude Code -Assisted-by: ChatGPT o3 -``` +1. **Every affected commit** MUST include an `Assisted-by:` trailer in the message body, e.g.: + + ``` + Assisted-by: GitHub Copilot + Assisted-by: Claude Code + Assisted-by: ChatGPT o3 + ``` + +2. **The PR description** MUST disclose the AI assistance. + +Apply this on the *first* commit/PR you create — do not wait for review feedback to add it. If you amend or rebase a commit that previously lacked the trailer, add it as part of the amend. See [AI_POLICY.md](AI_POLICY.md) for the full AI contribution policy. diff --git a/app/controlplane/configs/config.devel.yaml b/app/controlplane/configs/config.devel.yaml index 7bb7958c8..07976cfca 100644 --- a/app/controlplane/configs/config.devel.yaml +++ b/app/controlplane/configs/config.devel.yaml @@ -120,3 +120,6 @@ enable_profiler: true # url: http://localhost:8002/v1/authorize ui_dashboard_url: http://localhost:3000 + +attestations: + skip_db_storage: true diff --git a/app/controlplane/configs/samples/config.yaml b/app/controlplane/configs/samples/config.yaml index cef88ce29..04c5f3f63 100644 --- a/app/controlplane/configs/samples/config.yaml +++ b/app/controlplane/configs/samples/config.yaml @@ -101,4 +101,11 @@ enable_profiler: true # This means that the controlplane will send the JWT token to a remote endpoint to verify it # federated_authentication: # enabled: true -# url: http://localhost:8002/machine-identity/verify-token \ No newline at end of file +# url: http://localhost:8002/machine-identity/verify-token + +# Attestation storage and processing options +# attestations: +# # When true, skip writing the attestation bundle to the database; the bundle is +# # stored exclusively in the configured CAS backend. Has no effect when the +# # workflow run's CAS backend is inline. +# skip_db_storage: false \ No newline at end of file diff --git a/app/controlplane/internal/conf/controlplane/config/v1/conf.pb.go b/app/controlplane/internal/conf/controlplane/config/v1/conf.pb.go index 296f77b5e..038059240 100644 --- a/app/controlplane/internal/conf/controlplane/config/v1/conf.pb.go +++ b/app/controlplane/internal/conf/controlplane/config/v1/conf.pb.go @@ -82,8 +82,10 @@ type Bootstrap struct { UiDashboardUrl string `protobuf:"bytes,19,opt,name=ui_dashboard_url,json=uiDashboardUrl,proto3" json:"ui_dashboard_url,omitempty"` // Optional external operation authorization provider OperationAuthorizationProvider *OperationAuthorizationProvider `protobuf:"bytes,20,opt,name=operation_authorization_provider,json=operationAuthorizationProvider,proto3" json:"operation_authorization_provider,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Attestation storage and processing options + Attestations *Attestations `protobuf:"bytes,21,opt,name=attestations,proto3" json:"attestations,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *Bootstrap) Reset() { @@ -257,6 +259,63 @@ func (x *Bootstrap) GetOperationAuthorizationProvider() *OperationAuthorizationP return nil } +func (x *Bootstrap) GetAttestations() *Attestations { + if x != nil { + return x.Attestations + } + return nil +} + +type Attestations struct { + state protoimpl.MessageState `protogen:"open.v1"` + // When true, skip writing the attestation bundle to the per-run row in + // the attestation table; the bundle is stored exclusively in the + // configured CAS backend. The digest is still recorded on the workflow + // run so the bundle can be retrieved from CAS on read. Has no effect + // when the workflow run's CAS backend is inline, since inline backends + // do not store attestation bundles externally. + SkipDbStorage bool `protobuf:"varint,1,opt,name=skip_db_storage,json=skipDbStorage,proto3" json:"skip_db_storage,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Attestations) Reset() { + *x = Attestations{} + mi := &file_controlplane_config_v1_conf_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Attestations) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Attestations) ProtoMessage() {} + +func (x *Attestations) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_config_v1_conf_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Attestations.ProtoReflect.Descriptor instead. +func (*Attestations) Descriptor() ([]byte, []int) { + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{1} +} + +func (x *Attestations) GetSkipDbStorage() bool { + if x != nil { + return x.SkipDbStorage + } + return false +} + type OperationAuthorizationProvider struct { state protoimpl.MessageState `protogen:"open.v1"` // URL of the authorization endpoint @@ -269,7 +328,7 @@ type OperationAuthorizationProvider struct { func (x *OperationAuthorizationProvider) Reset() { *x = OperationAuthorizationProvider{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[1] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -281,7 +340,7 @@ func (x *OperationAuthorizationProvider) String() string { func (*OperationAuthorizationProvider) ProtoMessage() {} func (x *OperationAuthorizationProvider) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[1] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -294,7 +353,7 @@ func (x *OperationAuthorizationProvider) ProtoReflect() protoreflect.Message { // Deprecated: Use OperationAuthorizationProvider.ProtoReflect.Descriptor instead. func (*OperationAuthorizationProvider) Descriptor() ([]byte, []int) { - return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{1} + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{2} } func (x *OperationAuthorizationProvider) GetUrl() string { @@ -323,7 +382,7 @@ type FederatedAuthentication struct { func (x *FederatedAuthentication) Reset() { *x = FederatedAuthentication{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[2] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -335,7 +394,7 @@ func (x *FederatedAuthentication) String() string { func (*FederatedAuthentication) ProtoMessage() {} func (x *FederatedAuthentication) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[2] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -348,7 +407,7 @@ func (x *FederatedAuthentication) ProtoReflect() protoreflect.Message { // Deprecated: Use FederatedAuthentication.ProtoReflect.Descriptor instead. func (*FederatedAuthentication) Descriptor() ([]byte, []int) { - return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{2} + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{3} } func (x *FederatedAuthentication) GetUrl() string { @@ -382,7 +441,7 @@ type PolicyProvider struct { func (x *PolicyProvider) Reset() { *x = PolicyProvider{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[3] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -394,7 +453,7 @@ func (x *PolicyProvider) String() string { func (*PolicyProvider) ProtoMessage() {} func (x *PolicyProvider) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[3] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -407,7 +466,7 @@ func (x *PolicyProvider) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyProvider.ProtoReflect.Descriptor instead. func (*PolicyProvider) Descriptor() ([]byte, []int) { - return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{3} + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{4} } func (x *PolicyProvider) GetName() string { @@ -455,7 +514,7 @@ type ReferrerSharedIndex struct { func (x *ReferrerSharedIndex) Reset() { *x = ReferrerSharedIndex{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[4] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -467,7 +526,7 @@ func (x *ReferrerSharedIndex) String() string { func (*ReferrerSharedIndex) ProtoMessage() {} func (x *ReferrerSharedIndex) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[4] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -480,7 +539,7 @@ func (x *ReferrerSharedIndex) ProtoReflect() protoreflect.Message { // Deprecated: Use ReferrerSharedIndex.ProtoReflect.Descriptor instead. func (*ReferrerSharedIndex) Descriptor() ([]byte, []int) { - return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{4} + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{5} } func (x *ReferrerSharedIndex) GetEnabled() bool { @@ -509,7 +568,7 @@ type Server struct { func (x *Server) Reset() { *x = Server{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[5] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -521,7 +580,7 @@ func (x *Server) String() string { func (*Server) ProtoMessage() {} func (x *Server) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[5] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -534,7 +593,7 @@ func (x *Server) ProtoReflect() protoreflect.Message { // Deprecated: Use Server.ProtoReflect.Descriptor instead. func (*Server) Descriptor() ([]byte, []int) { - return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{5} + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{6} } func (x *Server) GetHttp() *Server_HTTP { @@ -567,7 +626,7 @@ type Data struct { func (x *Data) Reset() { *x = Data{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[6] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -579,7 +638,7 @@ func (x *Data) String() string { func (*Data) ProtoMessage() {} func (x *Data) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[6] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -592,7 +651,7 @@ func (x *Data) ProtoReflect() protoreflect.Message { // Deprecated: Use Data.ProtoReflect.Descriptor instead. func (*Data) Descriptor() ([]byte, []int) { - return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{6} + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{7} } func (x *Data) GetDatabase() *Data_Database { @@ -617,7 +676,7 @@ type Auth struct { func (x *Auth) Reset() { *x = Auth{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[7] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -629,7 +688,7 @@ func (x *Auth) String() string { func (*Auth) ProtoMessage() {} func (x *Auth) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[7] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -642,7 +701,7 @@ func (x *Auth) ProtoReflect() protoreflect.Message { // Deprecated: Use Auth.ProtoReflect.Descriptor instead. func (*Auth) Descriptor() ([]byte, []int) { - return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{7} + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{8} } func (x *Auth) GetGeneratedJwsHmacSecret() string { @@ -694,7 +753,7 @@ type TSA struct { func (x *TSA) Reset() { *x = TSA{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[8] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -706,7 +765,7 @@ func (x *TSA) String() string { func (*TSA) ProtoMessage() {} func (x *TSA) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[8] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -719,7 +778,7 @@ func (x *TSA) ProtoReflect() protoreflect.Message { // Deprecated: Use TSA.ProtoReflect.Descriptor instead. func (*TSA) Descriptor() ([]byte, []int) { - return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{8} + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{9} } func (x *TSA) GetUrl() string { @@ -760,7 +819,7 @@ type CA struct { func (x *CA) Reset() { *x = CA{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[9] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -772,7 +831,7 @@ func (x *CA) String() string { func (*CA) ProtoMessage() {} func (x *CA) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[9] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -785,7 +844,7 @@ func (x *CA) ProtoReflect() protoreflect.Message { // Deprecated: Use CA.ProtoReflect.Descriptor instead. func (*CA) Descriptor() ([]byte, []int) { - return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{9} + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{10} } func (x *CA) GetCa() isCA_Ca { @@ -847,7 +906,7 @@ type PrometheusIntegrationSpec struct { func (x *PrometheusIntegrationSpec) Reset() { *x = PrometheusIntegrationSpec{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[10] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -859,7 +918,7 @@ func (x *PrometheusIntegrationSpec) String() string { func (*PrometheusIntegrationSpec) ProtoMessage() {} func (x *PrometheusIntegrationSpec) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[10] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -872,7 +931,7 @@ func (x *PrometheusIntegrationSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use PrometheusIntegrationSpec.ProtoReflect.Descriptor instead. func (*PrometheusIntegrationSpec) Descriptor() ([]byte, []int) { - return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{10} + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{11} } func (x *PrometheusIntegrationSpec) GetOrgName() string { @@ -892,7 +951,7 @@ type Bootstrap_Observability struct { func (x *Bootstrap_Observability) Reset() { *x = Bootstrap_Observability{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[11] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -904,7 +963,7 @@ func (x *Bootstrap_Observability) String() string { func (*Bootstrap_Observability) ProtoMessage() {} func (x *Bootstrap_Observability) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[11] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -954,7 +1013,7 @@ type Bootstrap_CASServer struct { func (x *Bootstrap_CASServer) Reset() { *x = Bootstrap_CASServer{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[12] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -966,7 +1025,7 @@ func (x *Bootstrap_CASServer) String() string { func (*Bootstrap_CASServer) ProtoMessage() {} func (x *Bootstrap_CASServer) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[12] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1027,7 +1086,7 @@ type Bootstrap_NatsServer struct { func (x *Bootstrap_NatsServer) Reset() { *x = Bootstrap_NatsServer{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[13] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1039,7 +1098,7 @@ func (x *Bootstrap_NatsServer) String() string { func (*Bootstrap_NatsServer) ProtoMessage() {} func (x *Bootstrap_NatsServer) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[13] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1106,7 +1165,7 @@ type Bootstrap_Observability_Sentry struct { func (x *Bootstrap_Observability_Sentry) Reset() { *x = Bootstrap_Observability_Sentry{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[14] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1118,7 +1177,7 @@ func (x *Bootstrap_Observability_Sentry) String() string { func (*Bootstrap_Observability_Sentry) ProtoMessage() {} func (x *Bootstrap_Observability_Sentry) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[14] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1165,7 +1224,7 @@ type Bootstrap_Observability_Tracing struct { func (x *Bootstrap_Observability_Tracing) Reset() { *x = Bootstrap_Observability_Tracing{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[15] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1177,7 +1236,7 @@ func (x *Bootstrap_Observability_Tracing) String() string { func (*Bootstrap_Observability_Tracing) ProtoMessage() {} func (x *Bootstrap_Observability_Tracing) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[15] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1235,7 +1294,7 @@ type Server_HTTP struct { func (x *Server_HTTP) Reset() { *x = Server_HTTP{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[16] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1247,7 +1306,7 @@ func (x *Server_HTTP) String() string { func (*Server_HTTP) ProtoMessage() {} func (x *Server_HTTP) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[16] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1260,7 +1319,7 @@ func (x *Server_HTTP) ProtoReflect() protoreflect.Message { // Deprecated: Use Server_HTTP.ProtoReflect.Descriptor instead. func (*Server_HTTP) Descriptor() ([]byte, []int) { - return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{5, 0} + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{6, 0} } func (x *Server_HTTP) GetNetwork() string { @@ -1302,7 +1361,7 @@ type Server_TLS struct { func (x *Server_TLS) Reset() { *x = Server_TLS{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[17] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1314,7 +1373,7 @@ func (x *Server_TLS) String() string { func (*Server_TLS) ProtoMessage() {} func (x *Server_TLS) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[17] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1327,7 +1386,7 @@ func (x *Server_TLS) ProtoReflect() protoreflect.Message { // Deprecated: Use Server_TLS.ProtoReflect.Descriptor instead. func (*Server_TLS) Descriptor() ([]byte, []int) { - return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{5, 1} + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{6, 1} } func (x *Server_TLS) GetCertificate() string { @@ -1359,7 +1418,7 @@ type Server_GRPC struct { func (x *Server_GRPC) Reset() { *x = Server_GRPC{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[18] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1371,7 +1430,7 @@ func (x *Server_GRPC) String() string { func (*Server_GRPC) ProtoMessage() {} func (x *Server_GRPC) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[18] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1384,7 +1443,7 @@ func (x *Server_GRPC) ProtoReflect() protoreflect.Message { // Deprecated: Use Server_GRPC.ProtoReflect.Descriptor instead. func (*Server_GRPC) Descriptor() ([]byte, []int) { - return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{5, 2} + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{6, 2} } func (x *Server_GRPC) GetNetwork() string { @@ -1438,7 +1497,7 @@ type Data_Database struct { func (x *Data_Database) Reset() { *x = Data_Database{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[19] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1450,7 +1509,7 @@ func (x *Data_Database) String() string { func (*Data_Database) ProtoMessage() {} func (x *Data_Database) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[19] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1463,7 +1522,7 @@ func (x *Data_Database) ProtoReflect() protoreflect.Message { // Deprecated: Use Data_Database.ProtoReflect.Descriptor instead. func (*Data_Database) Descriptor() ([]byte, []int) { - return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{6, 0} + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{7, 0} } func (x *Data_Database) GetDriver() string { @@ -1515,7 +1574,7 @@ type Auth_OIDC struct { func (x *Auth_OIDC) Reset() { *x = Auth_OIDC{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[20] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1527,7 +1586,7 @@ func (x *Auth_OIDC) String() string { func (*Auth_OIDC) ProtoMessage() {} func (x *Auth_OIDC) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[20] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1540,7 +1599,7 @@ func (x *Auth_OIDC) ProtoReflect() protoreflect.Message { // Deprecated: Use Auth_OIDC.ProtoReflect.Descriptor instead. func (*Auth_OIDC) Descriptor() ([]byte, []int) { - return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{7, 0} + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{8, 0} } func (x *Auth_OIDC) GetDomain() string { @@ -1582,7 +1641,7 @@ type CA_FileCA struct { func (x *CA_FileCA) Reset() { *x = CA_FileCA{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[21] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1594,7 +1653,7 @@ func (x *CA_FileCA) String() string { func (*CA_FileCA) ProtoMessage() {} func (x *CA_FileCA) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[21] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1607,7 +1666,7 @@ func (x *CA_FileCA) ProtoReflect() protoreflect.Message { // Deprecated: Use CA_FileCA.ProtoReflect.Descriptor instead. func (*CA_FileCA) Descriptor() ([]byte, []int) { - return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{9, 0} + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{10, 0} } func (x *CA_FileCA) GetCertPath() string { @@ -1648,7 +1707,7 @@ type CA_EJBCA struct { func (x *CA_EJBCA) Reset() { *x = CA_EJBCA{} - mi := &file_controlplane_config_v1_conf_proto_msgTypes[22] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1660,7 +1719,7 @@ func (x *CA_EJBCA) String() string { func (*CA_EJBCA) ProtoMessage() {} func (x *CA_EJBCA) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[22] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1673,7 +1732,7 @@ func (x *CA_EJBCA) ProtoReflect() protoreflect.Message { // Deprecated: Use CA_EJBCA.ProtoReflect.Descriptor instead. func (*CA_EJBCA) Descriptor() ([]byte, []int) { - return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{9, 1} + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{10, 1} } func (x *CA_EJBCA) GetServerUrl() string { @@ -1729,7 +1788,7 @@ var File_controlplane_config_v1_conf_proto protoreflect.FileDescriptor const file_controlplane_config_v1_conf_proto_rawDesc = "" + "\n" + - "!controlplane/config/v1/conf.proto\x12\x16controlplane.config.v1\x1a\x1bbuf/validate/validate.proto\x1a#controlplane/config/v1/config.proto\x1a\x1bcredentials/v1/config.proto\x1a\x1egoogle/protobuf/duration.proto\"\xab\x11\n" + + "!controlplane/config/v1/conf.proto\x12\x16controlplane.config.v1\x1a\x1bbuf/validate/validate.proto\x1a#controlplane/config/v1/config.proto\x1a\x1bcredentials/v1/config.proto\x1a\x1egoogle/protobuf/duration.proto\"\xf5\x11\n" + "\tBootstrap\x126\n" + "\x06server\x18\x01 \x01(\v2\x1e.controlplane.config.v1.ServerR\x06server\x120\n" + "\x04data\x18\x02 \x01(\v2\x1c.controlplane.config.v1.DataR\x04data\x120\n" + @@ -1756,7 +1815,8 @@ const file_controlplane_config_v1_conf_proto_rawDesc = "" + "\x18federated_authentication\x18\x10 \x01(\v2/.controlplane.config.v1.FederatedAuthenticationR\x17federatedAuthentication\x122\n" + "\x15restrict_org_creation\x18\x12 \x01(\bR\x13restrictOrgCreation\x12(\n" + "\x10ui_dashboard_url\x18\x13 \x01(\tR\x0euiDashboardUrl\x12\x80\x01\n" + - " operation_authorization_provider\x18\x14 \x01(\v26.controlplane.config.v1.OperationAuthorizationProviderR\x1eoperationAuthorizationProvider\x1a\x8d\x03\n" + + " operation_authorization_provider\x18\x14 \x01(\v26.controlplane.config.v1.OperationAuthorizationProviderR\x1eoperationAuthorizationProvider\x12H\n" + + "\fattestations\x18\x15 \x01(\v2$.controlplane.config.v1.AttestationsR\fattestations\x1a\x8d\x03\n" + "\rObservability\x12N\n" + "\x06sentry\x18\x01 \x01(\v26.controlplane.config.v1.Bootstrap.Observability.SentryR\x06sentry\x12Q\n" + "\atracing\x18\x02 \x01(\v27.controlplane.config.v1.Bootstrap.Observability.TracingR\atracing\x1a<\n" + @@ -1779,7 +1839,9 @@ const file_controlplane_config_v1_conf_proto_rawDesc = "" + "\x03uri\x18\x01 \x01(\tB\a\xbaH\x04r\x02\x10\x01R\x03uri\x12\x1f\n" + "\x05token\x18\x02 \x01(\tB\a\xbaH\x04r\x02\x10\x01H\x00R\x05token\x12\x1a\n" + "\breplicas\x18\x03 \x01(\x05R\breplicasB\x10\n" + - "\x0eauthentication\"V\n" + + "\x0eauthentication\"6\n" + + "\fAttestations\x12&\n" + + "\x0fskip_db_storage\x18\x01 \x01(\bR\rskipDbStorage\"V\n" + "\x1eOperationAuthorizationProvider\x12\x1a\n" + "\x03url\x18\x01 \x01(\tB\b\xbaH\x05r\x03\x88\x01\x01R\x03url\x12\x18\n" + "\aenabled\x18\x02 \x01(\bR\aenabled\"O\n" + @@ -1873,73 +1935,75 @@ func file_controlplane_config_v1_conf_proto_rawDescGZIP() []byte { return file_controlplane_config_v1_conf_proto_rawDescData } -var file_controlplane_config_v1_conf_proto_msgTypes = make([]protoimpl.MessageInfo, 23) +var file_controlplane_config_v1_conf_proto_msgTypes = make([]protoimpl.MessageInfo, 24) var file_controlplane_config_v1_conf_proto_goTypes = []any{ (*Bootstrap)(nil), // 0: controlplane.config.v1.Bootstrap - (*OperationAuthorizationProvider)(nil), // 1: controlplane.config.v1.OperationAuthorizationProvider - (*FederatedAuthentication)(nil), // 2: controlplane.config.v1.FederatedAuthentication - (*PolicyProvider)(nil), // 3: controlplane.config.v1.PolicyProvider - (*ReferrerSharedIndex)(nil), // 4: controlplane.config.v1.ReferrerSharedIndex - (*Server)(nil), // 5: controlplane.config.v1.Server - (*Data)(nil), // 6: controlplane.config.v1.Data - (*Auth)(nil), // 7: controlplane.config.v1.Auth - (*TSA)(nil), // 8: controlplane.config.v1.TSA - (*CA)(nil), // 9: controlplane.config.v1.CA - (*PrometheusIntegrationSpec)(nil), // 10: controlplane.config.v1.PrometheusIntegrationSpec - (*Bootstrap_Observability)(nil), // 11: controlplane.config.v1.Bootstrap.Observability - (*Bootstrap_CASServer)(nil), // 12: controlplane.config.v1.Bootstrap.CASServer - (*Bootstrap_NatsServer)(nil), // 13: controlplane.config.v1.Bootstrap.NatsServer - (*Bootstrap_Observability_Sentry)(nil), // 14: controlplane.config.v1.Bootstrap.Observability.Sentry - (*Bootstrap_Observability_Tracing)(nil), // 15: controlplane.config.v1.Bootstrap.Observability.Tracing - (*Server_HTTP)(nil), // 16: controlplane.config.v1.Server.HTTP - (*Server_TLS)(nil), // 17: controlplane.config.v1.Server.TLS - (*Server_GRPC)(nil), // 18: controlplane.config.v1.Server.GRPC - (*Data_Database)(nil), // 19: controlplane.config.v1.Data.Database - (*Auth_OIDC)(nil), // 20: controlplane.config.v1.Auth.OIDC - (*CA_FileCA)(nil), // 21: controlplane.config.v1.CA.FileCA - (*CA_EJBCA)(nil), // 22: controlplane.config.v1.CA.EJBCA - (*v1.Credentials)(nil), // 23: credentials.v1.Credentials - (*v11.OnboardingSpec)(nil), // 24: controlplane.config.v1.OnboardingSpec - (*v11.AllowList)(nil), // 25: controlplane.config.v1.AllowList - (*durationpb.Duration)(nil), // 26: google.protobuf.Duration + (*Attestations)(nil), // 1: controlplane.config.v1.Attestations + (*OperationAuthorizationProvider)(nil), // 2: controlplane.config.v1.OperationAuthorizationProvider + (*FederatedAuthentication)(nil), // 3: controlplane.config.v1.FederatedAuthentication + (*PolicyProvider)(nil), // 4: controlplane.config.v1.PolicyProvider + (*ReferrerSharedIndex)(nil), // 5: controlplane.config.v1.ReferrerSharedIndex + (*Server)(nil), // 6: controlplane.config.v1.Server + (*Data)(nil), // 7: controlplane.config.v1.Data + (*Auth)(nil), // 8: controlplane.config.v1.Auth + (*TSA)(nil), // 9: controlplane.config.v1.TSA + (*CA)(nil), // 10: controlplane.config.v1.CA + (*PrometheusIntegrationSpec)(nil), // 11: controlplane.config.v1.PrometheusIntegrationSpec + (*Bootstrap_Observability)(nil), // 12: controlplane.config.v1.Bootstrap.Observability + (*Bootstrap_CASServer)(nil), // 13: controlplane.config.v1.Bootstrap.CASServer + (*Bootstrap_NatsServer)(nil), // 14: controlplane.config.v1.Bootstrap.NatsServer + (*Bootstrap_Observability_Sentry)(nil), // 15: controlplane.config.v1.Bootstrap.Observability.Sentry + (*Bootstrap_Observability_Tracing)(nil), // 16: controlplane.config.v1.Bootstrap.Observability.Tracing + (*Server_HTTP)(nil), // 17: controlplane.config.v1.Server.HTTP + (*Server_TLS)(nil), // 18: controlplane.config.v1.Server.TLS + (*Server_GRPC)(nil), // 19: controlplane.config.v1.Server.GRPC + (*Data_Database)(nil), // 20: controlplane.config.v1.Data.Database + (*Auth_OIDC)(nil), // 21: controlplane.config.v1.Auth.OIDC + (*CA_FileCA)(nil), // 22: controlplane.config.v1.CA.FileCA + (*CA_EJBCA)(nil), // 23: controlplane.config.v1.CA.EJBCA + (*v1.Credentials)(nil), // 24: credentials.v1.Credentials + (*v11.OnboardingSpec)(nil), // 25: controlplane.config.v1.OnboardingSpec + (*v11.AllowList)(nil), // 26: controlplane.config.v1.AllowList + (*durationpb.Duration)(nil), // 27: google.protobuf.Duration } var file_controlplane_config_v1_conf_proto_depIdxs = []int32{ - 5, // 0: controlplane.config.v1.Bootstrap.server:type_name -> controlplane.config.v1.Server - 6, // 1: controlplane.config.v1.Bootstrap.data:type_name -> controlplane.config.v1.Data - 7, // 2: controlplane.config.v1.Bootstrap.auth:type_name -> controlplane.config.v1.Auth - 11, // 3: controlplane.config.v1.Bootstrap.observability:type_name -> controlplane.config.v1.Bootstrap.Observability - 23, // 4: controlplane.config.v1.Bootstrap.credentials_service:type_name -> credentials.v1.Credentials - 12, // 5: controlplane.config.v1.Bootstrap.cas_server:type_name -> controlplane.config.v1.Bootstrap.CASServer - 4, // 6: controlplane.config.v1.Bootstrap.referrer_shared_index:type_name -> controlplane.config.v1.ReferrerSharedIndex - 9, // 7: controlplane.config.v1.Bootstrap.certificate_authority:type_name -> controlplane.config.v1.CA - 9, // 8: controlplane.config.v1.Bootstrap.certificate_authorities:type_name -> controlplane.config.v1.CA - 8, // 9: controlplane.config.v1.Bootstrap.timestamp_authorities:type_name -> controlplane.config.v1.TSA - 24, // 10: controlplane.config.v1.Bootstrap.onboarding:type_name -> controlplane.config.v1.OnboardingSpec - 10, // 11: controlplane.config.v1.Bootstrap.prometheus_integration:type_name -> controlplane.config.v1.PrometheusIntegrationSpec - 3, // 12: controlplane.config.v1.Bootstrap.policy_providers:type_name -> controlplane.config.v1.PolicyProvider - 13, // 13: controlplane.config.v1.Bootstrap.nats_server:type_name -> controlplane.config.v1.Bootstrap.NatsServer - 2, // 14: controlplane.config.v1.Bootstrap.federated_authentication:type_name -> controlplane.config.v1.FederatedAuthentication - 1, // 15: controlplane.config.v1.Bootstrap.operation_authorization_provider:type_name -> controlplane.config.v1.OperationAuthorizationProvider - 16, // 16: controlplane.config.v1.Server.http:type_name -> controlplane.config.v1.Server.HTTP - 18, // 17: controlplane.config.v1.Server.grpc:type_name -> controlplane.config.v1.Server.GRPC - 16, // 18: controlplane.config.v1.Server.http_metrics:type_name -> controlplane.config.v1.Server.HTTP - 19, // 19: controlplane.config.v1.Data.database:type_name -> controlplane.config.v1.Data.Database - 25, // 20: controlplane.config.v1.Auth.allow_list:type_name -> controlplane.config.v1.AllowList - 20, // 21: controlplane.config.v1.Auth.oidc:type_name -> controlplane.config.v1.Auth.OIDC - 21, // 22: controlplane.config.v1.CA.file_ca:type_name -> controlplane.config.v1.CA.FileCA - 22, // 23: controlplane.config.v1.CA.ejbca_ca:type_name -> controlplane.config.v1.CA.EJBCA - 14, // 24: controlplane.config.v1.Bootstrap.Observability.sentry:type_name -> controlplane.config.v1.Bootstrap.Observability.Sentry - 15, // 25: controlplane.config.v1.Bootstrap.Observability.tracing:type_name -> controlplane.config.v1.Bootstrap.Observability.Tracing - 18, // 26: controlplane.config.v1.Bootstrap.CASServer.grpc:type_name -> controlplane.config.v1.Server.GRPC - 26, // 27: controlplane.config.v1.Server.HTTP.timeout:type_name -> google.protobuf.Duration - 26, // 28: controlplane.config.v1.Server.GRPC.timeout:type_name -> google.protobuf.Duration - 17, // 29: controlplane.config.v1.Server.GRPC.tls_config:type_name -> controlplane.config.v1.Server.TLS - 26, // 30: controlplane.config.v1.Data.Database.max_conn_idle_time:type_name -> google.protobuf.Duration - 31, // [31:31] is the sub-list for method output_type - 31, // [31:31] is the sub-list for method input_type - 31, // [31:31] is the sub-list for extension type_name - 31, // [31:31] is the sub-list for extension extendee - 0, // [0:31] is the sub-list for field type_name + 6, // 0: controlplane.config.v1.Bootstrap.server:type_name -> controlplane.config.v1.Server + 7, // 1: controlplane.config.v1.Bootstrap.data:type_name -> controlplane.config.v1.Data + 8, // 2: controlplane.config.v1.Bootstrap.auth:type_name -> controlplane.config.v1.Auth + 12, // 3: controlplane.config.v1.Bootstrap.observability:type_name -> controlplane.config.v1.Bootstrap.Observability + 24, // 4: controlplane.config.v1.Bootstrap.credentials_service:type_name -> credentials.v1.Credentials + 13, // 5: controlplane.config.v1.Bootstrap.cas_server:type_name -> controlplane.config.v1.Bootstrap.CASServer + 5, // 6: controlplane.config.v1.Bootstrap.referrer_shared_index:type_name -> controlplane.config.v1.ReferrerSharedIndex + 10, // 7: controlplane.config.v1.Bootstrap.certificate_authority:type_name -> controlplane.config.v1.CA + 10, // 8: controlplane.config.v1.Bootstrap.certificate_authorities:type_name -> controlplane.config.v1.CA + 9, // 9: controlplane.config.v1.Bootstrap.timestamp_authorities:type_name -> controlplane.config.v1.TSA + 25, // 10: controlplane.config.v1.Bootstrap.onboarding:type_name -> controlplane.config.v1.OnboardingSpec + 11, // 11: controlplane.config.v1.Bootstrap.prometheus_integration:type_name -> controlplane.config.v1.PrometheusIntegrationSpec + 4, // 12: controlplane.config.v1.Bootstrap.policy_providers:type_name -> controlplane.config.v1.PolicyProvider + 14, // 13: controlplane.config.v1.Bootstrap.nats_server:type_name -> controlplane.config.v1.Bootstrap.NatsServer + 3, // 14: controlplane.config.v1.Bootstrap.federated_authentication:type_name -> controlplane.config.v1.FederatedAuthentication + 2, // 15: controlplane.config.v1.Bootstrap.operation_authorization_provider:type_name -> controlplane.config.v1.OperationAuthorizationProvider + 1, // 16: controlplane.config.v1.Bootstrap.attestations:type_name -> controlplane.config.v1.Attestations + 17, // 17: controlplane.config.v1.Server.http:type_name -> controlplane.config.v1.Server.HTTP + 19, // 18: controlplane.config.v1.Server.grpc:type_name -> controlplane.config.v1.Server.GRPC + 17, // 19: controlplane.config.v1.Server.http_metrics:type_name -> controlplane.config.v1.Server.HTTP + 20, // 20: controlplane.config.v1.Data.database:type_name -> controlplane.config.v1.Data.Database + 26, // 21: controlplane.config.v1.Auth.allow_list:type_name -> controlplane.config.v1.AllowList + 21, // 22: controlplane.config.v1.Auth.oidc:type_name -> controlplane.config.v1.Auth.OIDC + 22, // 23: controlplane.config.v1.CA.file_ca:type_name -> controlplane.config.v1.CA.FileCA + 23, // 24: controlplane.config.v1.CA.ejbca_ca:type_name -> controlplane.config.v1.CA.EJBCA + 15, // 25: controlplane.config.v1.Bootstrap.Observability.sentry:type_name -> controlplane.config.v1.Bootstrap.Observability.Sentry + 16, // 26: controlplane.config.v1.Bootstrap.Observability.tracing:type_name -> controlplane.config.v1.Bootstrap.Observability.Tracing + 19, // 27: controlplane.config.v1.Bootstrap.CASServer.grpc:type_name -> controlplane.config.v1.Server.GRPC + 27, // 28: controlplane.config.v1.Server.HTTP.timeout:type_name -> google.protobuf.Duration + 27, // 29: controlplane.config.v1.Server.GRPC.timeout:type_name -> google.protobuf.Duration + 18, // 30: controlplane.config.v1.Server.GRPC.tls_config:type_name -> controlplane.config.v1.Server.TLS + 27, // 31: controlplane.config.v1.Data.Database.max_conn_idle_time:type_name -> google.protobuf.Duration + 32, // [32:32] is the sub-list for method output_type + 32, // [32:32] is the sub-list for method input_type + 32, // [32:32] is the sub-list for extension type_name + 32, // [32:32] is the sub-list for extension extendee + 0, // [0:32] is the sub-list for field type_name } func init() { file_controlplane_config_v1_conf_proto_init() } @@ -1947,21 +2011,21 @@ func file_controlplane_config_v1_conf_proto_init() { if File_controlplane_config_v1_conf_proto != nil { return } - file_controlplane_config_v1_conf_proto_msgTypes[9].OneofWrappers = []any{ + file_controlplane_config_v1_conf_proto_msgTypes[10].OneofWrappers = []any{ (*CA_FileCa)(nil), (*CA_EjbcaCa)(nil), } - file_controlplane_config_v1_conf_proto_msgTypes[13].OneofWrappers = []any{ + file_controlplane_config_v1_conf_proto_msgTypes[14].OneofWrappers = []any{ (*Bootstrap_NatsServer_Token)(nil), } - file_controlplane_config_v1_conf_proto_msgTypes[15].OneofWrappers = []any{} + file_controlplane_config_v1_conf_proto_msgTypes[16].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_controlplane_config_v1_conf_proto_rawDesc), len(file_controlplane_config_v1_conf_proto_rawDesc)), NumEnums: 0, - NumMessages: 23, + NumMessages: 24, NumExtensions: 0, NumServices: 0, }, diff --git a/app/controlplane/internal/conf/controlplane/config/v1/conf.proto b/app/controlplane/internal/conf/controlplane/config/v1/conf.proto index d2a0774c3..4b5f7a4ba 100644 --- a/app/controlplane/internal/conf/controlplane/config/v1/conf.proto +++ b/app/controlplane/internal/conf/controlplane/config/v1/conf.proto @@ -123,6 +123,19 @@ message Bootstrap { // Optional external operation authorization provider OperationAuthorizationProvider operation_authorization_provider = 20; + + // Attestation storage and processing options + Attestations attestations = 21; +} + +message Attestations { + // When true, skip writing the attestation bundle to the per-run row in + // the attestation table; the bundle is stored exclusively in the + // configured CAS backend. The digest is still recorded on the workflow + // run so the bundle can be retrieved from CAS on read. Has no effect + // when the workflow run's CAS backend is inline, since inline backends + // do not store attestation bundles externally. + bool skip_db_storage = 1; } message OperationAuthorizationProvider { diff --git a/app/controlplane/internal/service/attestation.go b/app/controlplane/internal/service/attestation.go index a2cfd9573..2cc125fe3 100644 --- a/app/controlplane/internal/service/attestation.go +++ b/app/controlplane/internal/service/attestation.go @@ -16,6 +16,7 @@ package service import ( + "bytes" "context" "encoding/json" "fmt" @@ -280,6 +281,25 @@ func (s *AttestationService) Store(ctx context.Context, req *cpAPI.AttestationSe }, nil } +// casAttestationUploadMaxElapsedTime caps the total time spent retrying the +// CAS upload across both the sync (skipDbStorage) and async paths. +const casAttestationUploadMaxElapsedTime = 1 * time.Minute + +// uploadAttestationToCASWithRetry uploads the attestation bundle to CAS with +// exponential backoff bounded by casAttestationUploadMaxElapsedTime. It is +// shared by the synchronous (skipDbStorage) and asynchronous paths. +func (s *AttestationService) uploadAttestationToCASWithRetry(ctx context.Context, bundle []byte, casBackend *biz.CASBackend, workflowRunID string, digest v1.Hash) error { + b := backoff.NewExponentialBackOff() + b.MaxElapsedTime = casAttestationUploadMaxElapsedTime + if err := backoff.Retry(func() error { + return s.attestationUseCase.UploadAttestationToCAS(ctx, bundle, casBackend, workflowRunID, digest) + }, b); err != nil { + return err + } + s.log.Infow("msg", "attestation uploaded to CAS", "digest", digest, "runID", workflowRunID) + return nil +} + // storeAttestation stores and processes a Sigstore attestation bundle. func (s *AttestationService) storeAttestation(ctx context.Context, bundle []byte, robotAccount *usercontext.RobotAccount, wf *biz.Workflow, wfRun *biz.WorkflowRun, markAsReleased *bool) (*v1.Hash, error) { workflowRunID := wfRun.ID.String() @@ -291,40 +311,45 @@ func (s *AttestationService) storeAttestation(ctx context.Context, bundle []byte return nil, handleUseCaseErr(err, s.log) } - // Store the attestation - digest, err := s.wrUseCase.SaveAttestation(ctx, workflowRunID, bundle) - if err != nil { - return nil, handleUseCaseErr(err, s.log) + // Inline backends have no external CAS to fall back to, so the bundle + // always stays in the DB regardless of the flag. + skipDB := s.bootstrapConfig.GetAttestations().GetSkipDbStorage() && !casBackend.Inline + + if !casBackend.Inline && casBackend.ValidationStatus != biz.CASBackendValidationOK { + if err = s.casUC.PerformValidation(ctx, casBackend.ID.String()); err != nil { + return nil, cpAPI.ErrorCasBackendErrorReasonInvalid("your CAS backend can't be reached") + } } - // If we have an external CAS backend, we will push there the attestation - if !casBackend.Inline { - // Check the validation status of the backend. The backend might be different from the one configured as default - if casBackend.ValidationStatus != biz.CASBackendValidationOK { - // Try to re-validate the backend; if it still fails, return an error - if err = s.casUC.PerformValidation(ctx, casBackend.ID.String()); err != nil { - return nil, cpAPI.ErrorCasBackendErrorReasonInvalid("your CAS backend can't be reached") - } + var digest *v1.Hash + if skipDB { + digestHash, _, hashErr := v1.SHA256(bytes.NewReader(bundle)) + if hashErr != nil { + return nil, handleUseCaseErr(hashErr, s.log) + } + + if err = s.uploadAttestationToCASWithRetry(ctx, bundle, casBackend, workflowRunID, digestHash); err != nil { + return nil, handleUseCaseErr(err, s.log) + } + + digest, err = s.wrUseCase.SaveAttestation(ctx, workflowRunID, bundle, biz.WithSkipBundlePersistence()) + if err != nil { + return nil, handleUseCaseErr(err, s.log) + } + } else { + digest, err = s.wrUseCase.SaveAttestation(ctx, workflowRunID, bundle) + if err != nil { + return nil, handleUseCaseErr(err, s.log) + } + + if !casBackend.Inline { + // Detach from the request context so the upload survives request completion. + go func(digest v1.Hash) { + if err := s.uploadAttestationToCASWithRetry(context.Background(), bundle, casBackend, workflowRunID, digest); err != nil { + _ = handleUseCaseErr(err, s.log) + } + }(*digest) } - go func() { - b := backoff.NewExponentialBackOff() - b.MaxElapsedTime = 1 * time.Minute - err := backoff.Retry( - func() error { - // reset context - ctx := context.Background() - if err := s.attestationUseCase.UploadAttestationToCAS(ctx, bundle, casBackend, workflowRunID, *digest); err != nil { - return err - } - - s.log.Infow("msg", "attestation uploaded to CAS", "digest", digest, "runID", workflowRunID) - return nil - }, b) - - if err != nil { - _ = handleUseCaseErr(err, s.log) - } - }() } // Store the exploded attestation referrer information in the DB diff --git a/app/controlplane/pkg/biz/mocks/WorkflowRunRepo.go b/app/controlplane/pkg/biz/mocks/WorkflowRunRepo.go index 7eaf82b84..0c41eb7f5 100644 --- a/app/controlplane/pkg/biz/mocks/WorkflowRunRepo.go +++ b/app/controlplane/pkg/biz/mocks/WorkflowRunRepo.go @@ -743,6 +743,69 @@ func (_c *WorkflowRunRepo_SaveAttestationBundle_Call) RunAndReturn(run func(ctx return _c } +// SaveAttestationDigest provides a mock function for the type WorkflowRunRepo +func (_mock *WorkflowRunRepo) SaveAttestationDigest(ctx context.Context, ID uuid.UUID, digest string) error { + ret := _mock.Called(ctx, ID, digest) + + if len(ret) == 0 { + panic("no return value specified for SaveAttestationDigest") + } + + var r0 error + if returnFunc, ok := ret.Get(0).(func(context.Context, uuid.UUID, string) error); ok { + r0 = returnFunc(ctx, ID, digest) + } else { + r0 = ret.Error(0) + } + return r0 +} + +// WorkflowRunRepo_SaveAttestationDigest_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SaveAttestationDigest' +type WorkflowRunRepo_SaveAttestationDigest_Call struct { + *mock.Call +} + +// SaveAttestationDigest is a helper method to define mock.On call +// - ctx context.Context +// - ID uuid.UUID +// - digest string +func (_e *WorkflowRunRepo_Expecter) SaveAttestationDigest(ctx interface{}, ID interface{}, digest interface{}) *WorkflowRunRepo_SaveAttestationDigest_Call { + return &WorkflowRunRepo_SaveAttestationDigest_Call{Call: _e.mock.On("SaveAttestationDigest", ctx, ID, digest)} +} + +func (_c *WorkflowRunRepo_SaveAttestationDigest_Call) Run(run func(ctx context.Context, ID uuid.UUID, digest string)) *WorkflowRunRepo_SaveAttestationDigest_Call { + _c.Call.Run(func(args mock.Arguments) { + var arg0 context.Context + if args[0] != nil { + arg0 = args[0].(context.Context) + } + var arg1 uuid.UUID + if args[1] != nil { + arg1 = args[1].(uuid.UUID) + } + var arg2 string + if args[2] != nil { + arg2 = args[2].(string) + } + run( + arg0, + arg1, + arg2, + ) + }) + return _c +} + +func (_c *WorkflowRunRepo_SaveAttestationDigest_Call) Return(err error) *WorkflowRunRepo_SaveAttestationDigest_Call { + _c.Call.Return(err) + return _c +} + +func (_c *WorkflowRunRepo_SaveAttestationDigest_Call) RunAndReturn(run func(ctx context.Context, ID uuid.UUID, digest string) error) *WorkflowRunRepo_SaveAttestationDigest_Call { + _c.Call.Return(run) + return _c +} + // UpdatePolicyStatus provides a mock function for the type WorkflowRunRepo func (_mock *WorkflowRunRepo) UpdatePolicyStatus(ctx context.Context, ID uuid.UUID, summary *chainloop.PolicyStatusSummary) error { ret := _mock.Called(ctx, ID, summary) diff --git a/app/controlplane/pkg/biz/workflowrun.go b/app/controlplane/pkg/biz/workflowrun.go index 24027b5be..084081cc1 100644 --- a/app/controlplane/pkg/biz/workflowrun.go +++ b/app/controlplane/pkg/biz/workflowrun.go @@ -100,6 +100,10 @@ type WorkflowRunRepo interface { FindByIDInOrg(ctx context.Context, orgID, ID uuid.UUID) (*WorkflowRun, error) MarkAsFinished(ctx context.Context, ID uuid.UUID, status WorkflowRunStatus, reason string) error SaveAttestationBundle(ctx context.Context, ID uuid.UUID, digest string, bundle []byte) error + // SaveAttestationDigest records the attestation digest on the workflow run + // without writing a row in the attestation table. Used when the bundle is + // stored exclusively in CAS. + SaveAttestationDigest(ctx context.Context, ID uuid.UUID, digest string) error GetBundle(ctx context.Context, wrID uuid.UUID) ([]byte, error) UpdatePolicyStatus(ctx context.Context, ID uuid.UUID, summary *chainloop.PolicyStatusSummary) error List(ctx context.Context, orgID uuid.UUID, f *RunListFilters, p *pagination.CursorOptions) ([]*WorkflowRun, string, error) @@ -343,10 +347,32 @@ func (uc *WorkflowRunUseCase) MarkAsFinished(ctx context.Context, id string, sta return uc.wfRunRepo.MarkAsFinished(ctx, runID, status, reason) } -func (uc *WorkflowRunUseCase) SaveAttestation(ctx context.Context, id string, bundle []byte) (*v1.Hash, error) { +// SaveAttestationOption customises how SaveAttestation persists the bundle. +type SaveAttestationOption func(*saveAttestationOptions) + +type saveAttestationOptions struct { + skipBundlePersistence bool +} + +// WithSkipBundlePersistence instructs SaveAttestation to record the digest on +// the workflow run without writing the bundle bytes to the attestation table. +// The caller must ensure the bundle is durably stored elsewhere (typically in +// CAS) before invoking SaveAttestation with this option. +func WithSkipBundlePersistence() SaveAttestationOption { + return func(o *saveAttestationOptions) { + o.skipBundlePersistence = true + } +} + +func (uc *WorkflowRunUseCase) SaveAttestation(ctx context.Context, id string, bundle []byte, opts ...SaveAttestationOption) (*v1.Hash, error) { ctx, span := otelx.Start(ctx, workflowRunTracer, "WorkflowRunUseCase.SaveAttestation") defer span.End() + options := &saveAttestationOptions{} + for _, opt := range opts { + opt(options) + } + runID, err := uuid.Parse(id) if err != nil { return nil, NewErrInvalidUUID(err) @@ -399,8 +425,14 @@ func (uc *WorkflowRunUseCase) SaveAttestation(ctx context.Context, id string, bu } } - if err := uc.wfRunRepo.SaveAttestationBundle(ctx, runID, digest.String(), bundle); err != nil { - return nil, fmt.Errorf("saving attestation bundle: %w", err) + if options.skipBundlePersistence { + if err := uc.wfRunRepo.SaveAttestationDigest(ctx, runID, digest.String()); err != nil { + return nil, fmt.Errorf("saving attestation digest: %w", err) + } + } else { + if err := uc.wfRunRepo.SaveAttestationBundle(ctx, runID, digest.String(), bundle); err != nil { + return nil, fmt.Errorf("saving attestation bundle: %w", err) + } } // Compute and persist the canonical policy status summary so the list diff --git a/app/controlplane/pkg/biz/workflowrun_integration_test.go b/app/controlplane/pkg/biz/workflowrun_integration_test.go index 41ea15b42..030cf1d74 100644 --- a/app/controlplane/pkg/biz/workflowrun_integration_test.go +++ b/app/controlplane/pkg/biz/workflowrun_integration_test.go @@ -178,6 +178,29 @@ func (s *workflowRunIntegrationTestSuite) TestSaveAttestation() { assert.NoError(err) assert.True(exists) }) + + s.Run("WithSkipBundlePersistence stores digest only", func() { + run, err := s.WorkflowRun.Create(ctx, &biz.WorkflowRunCreateOpts{ + WorkflowID: s.workflowOrg1.ID.String(), ContractRevision: s.contractVersion, CASBackendID: s.casBackend.ID, + }) + assert.NoError(err) + + d, err := s.WorkflowRun.SaveAttestation(ctx, run.ID.String(), bundleBytes, biz.WithSkipBundlePersistence()) + assert.NoError(err) + assert.Equal(bundleHash, *d) + + // digest is recorded on the workflow run + err = s.WorkflowRun.MarkAsFinished(ctx, run.ID.String(), biz.WorkflowRunSuccess, "") + assert.NoError(err) + stored, err := s.WorkflowRun.GetByIDInOrgOrPublic(ctx, s.org.ID, run.ID.String()) + assert.NoError(err) + assert.Equal(bundleHash.String(), stored.Attestation.Digest) + + // no row was created in the attestation table + exists, err := s.Data.DB.Attestation.Query().Where(attestation2.WorkflowrunID(run.ID)).Exist(ctx) + assert.NoError(err) + assert.False(exists) + }) } func (s *workflowRunIntegrationTestSuite) TestGetByIDInOrgOrPublic() { diff --git a/app/controlplane/pkg/data/workflowrun.go b/app/controlplane/pkg/data/workflowrun.go index a466c7bc0..31ff13b19 100644 --- a/app/controlplane/pkg/data/workflowrun.go +++ b/app/controlplane/pkg/data/workflowrun.go @@ -240,6 +240,16 @@ func (r *WorkflowRunRepo) SaveAttestationBundle(ctx context.Context, id uuid.UUI }) } +func (r *WorkflowRunRepo) SaveAttestationDigest(ctx context.Context, id uuid.UUID, digest string) error { + if err := r.data.DB.WorkflowRun.UpdateOneID(id).SetAttestationDigest(digest).Exec(ctx); err != nil { + if ent.IsNotFound(err) { + return biz.NewErrNotFound(fmt.Sprintf("workflow run with id %s not found", id)) + } + return err + } + return nil +} + // UpdatePolicyStatus persists the canonical policy status summary plus the // legacy has_policy_violations bool (kept populated for back-compat with older // clients still reading WorkflowRunItem.has_policy_violations).