From af665758f527f7abb574f398132498cbe7ae865f Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Fri, 10 Feb 2023 14:22:33 -0500 Subject: [PATCH 001/231] backport of commit 34b3d0406d34586b201512fee739581007a30156 (#19136) Co-authored-by: Tom Proctor --- sdk/logical/event.pb.go | 110 +++++++++++++++++++++------------------- sdk/logical/event.proto | 3 +- 2 files changed, 60 insertions(+), 53 deletions(-) diff --git a/sdk/logical/event.pb.go b/sdk/logical/event.pb.go index 4f26233a3727..d09020236414 100644 --- a/sdk/logical/event.pb.go +++ b/sdk/logical/event.pb.go @@ -9,6 +9,7 @@ package logical import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + structpb "google.golang.org/protobuf/types/known/structpb" timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" @@ -130,7 +131,7 @@ type EventData struct { // as a hash of other fields with sufficient uniqueness. Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // Arbitrary non-secret data. Optional. - Metadata []byte `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"` + Metadata *structpb.Struct `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"` // Any IDs that the event relates to, i.e., UUIDs, paths. EntityIds []string `protobuf:"bytes,3,rep,name=entity_ids,json=entityIds,proto3" json:"entity_ids,omitempty"` // Human-readable note. @@ -176,7 +177,7 @@ func (x *EventData) GetId() string { return "" } -func (x *EventData) GetMetadata() []byte { +func (x *EventData) GetMetadata() *structpb.Struct { if x != nil { return x.Metadata } @@ -284,47 +285,50 @@ var File_sdk_logical_event_proto protoreflect.FileDescriptor var file_sdk_logical_event_proto_rawDesc = []byte{ 0x0a, 0x17, 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6c, 0x6f, 0x67, 0x69, 0x63, - 0x61, 0x6c, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0xd1, 0x01, 0x0a, 0x0f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, - 0x75, 0x6e, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0d, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, - 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x16, - 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, - 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, - 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x6a, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x44, 0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x73, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x6f, 0x74, 0x65, 0x22, 0xeb, 0x01, 0x0a, 0x0d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x63, - 0x65, 0x69, 0x76, 0x65, 0x64, 0x12, 0x28, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, - 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1d, 0x0a, - 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x39, 0x0a, 0x0b, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x70, 0x6c, 0x75, - 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x42, 0x28, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, - 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x61, 0x6c, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0xd1, 0x01, 0x0a, 0x0f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x63, + 0x6c, 0x61, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x1d, 0x0a, + 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x83, 0x01, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x22, 0xeb, 0x01, 0x0a, 0x0d, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x12, 0x28, 0x0a, + 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, + 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x69, + 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6f, 0x67, 0x69, + 0x63, 0x61, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x28, 0x5a, 0x26, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, + 0x63, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -344,17 +348,19 @@ var file_sdk_logical_event_proto_goTypes = []interface{}{ (*EventPluginInfo)(nil), // 0: logical.EventPluginInfo (*EventData)(nil), // 1: logical.EventData (*EventReceived)(nil), // 2: logical.EventReceived - (*timestamppb.Timestamp)(nil), // 3: google.protobuf.Timestamp + (*structpb.Struct)(nil), // 3: google.protobuf.Struct + (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp } var file_sdk_logical_event_proto_depIdxs = []int32{ - 1, // 0: logical.EventReceived.event:type_name -> logical.EventData - 0, // 1: logical.EventReceived.plugin_info:type_name -> logical.EventPluginInfo - 3, // 2: logical.EventReceived.timestamp:type_name -> google.protobuf.Timestamp - 3, // [3:3] is the sub-list for method output_type - 3, // [3:3] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 3, // 0: logical.EventData.metadata:type_name -> google.protobuf.Struct + 1, // 1: logical.EventReceived.event:type_name -> logical.EventData + 0, // 2: logical.EventReceived.plugin_info:type_name -> logical.EventPluginInfo + 4, // 3: logical.EventReceived.timestamp:type_name -> google.protobuf.Timestamp + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name } func init() { file_sdk_logical_event_proto_init() } diff --git a/sdk/logical/event.proto b/sdk/logical/event.proto index e6cc304cd178..789f1b8d4db1 100644 --- a/sdk/logical/event.proto +++ b/sdk/logical/event.proto @@ -4,6 +4,7 @@ option go_package = "github.com/hashicorp/vault/sdk/logical"; package logical; +import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; // EventPluginInfo contains data related to the plugin that generated an event. @@ -33,7 +34,7 @@ message EventData { // as a hash of other fields with sufficient uniqueness. string id = 1; // Arbitrary non-secret data. Optional. - bytes metadata = 2; + google.protobuf.Struct metadata = 2; // Any IDs that the event relates to, i.e., UUIDs, paths. repeated string entity_ids = 3; // Human-readable note. From cfb8f089eea60a85d5cfb6f8bbf5933c94b408db Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Fri, 10 Feb 2023 15:40:23 -0500 Subject: [PATCH 002/231] backport of commit ef765d3761cd5c7feeda9e2d5fd0326a02a17e40 (#19137) Co-authored-by: Tom Proctor --- http/events.go | 4 ++-- http/handler.go | 5 ----- http/logical.go | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/http/events.go b/http/events.go index aba3bb45cec8..478f31704250 100644 --- a/http/events.go +++ b/http/events.go @@ -81,8 +81,8 @@ func handleEventsSubscribe(core *vault.Core) http.Handler { } prefix := "/v1/sys/events/subscribe/" - if ns.ID != "root" { - prefix = fmt.Sprintf("/v1/%s/sys/events/subscribe/", ns.Path) + if ns.ID != namespace.RootNamespaceID { + prefix = fmt.Sprintf("/v1/%ssys/events/subscribe/", ns.Path) } eventTypeStr := strings.TrimSpace(strings.TrimPrefix(r.URL.Path, prefix)) if eventTypeStr == "" { diff --git a/http/handler.go b/http/handler.go index 601aa803779a..34c4b12cf19c 100644 --- a/http/handler.go +++ b/http/handler.go @@ -26,7 +26,6 @@ import ( "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/go-sockaddr" "github.com/hashicorp/go-uuid" - "github.com/hashicorp/vault/helper/experiments" "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/internalshared/configutil" "github.com/hashicorp/vault/sdk/helper/consts" @@ -177,10 +176,6 @@ func handler(props *vault.HandlerProperties) http.Handler { mux.Handle("/v1/sys/storage/raft/join", handleSysRaftJoin(core)) mux.Handle("/v1/sys/internal/ui/feature-flags", handleSysInternalFeatureFlags(core)) - if core.IsExperimentEnabled(experiments.VaultExperimentEventsAlpha1) { - mux.Handle("/v1/sys/events/subscribe/", handleEventsSubscribe(core)) - } - for _, path := range injectDataIntoTopRoutes { mux.Handle(path, handleRequestForwarding(core, handleLogicalWithInjector(core))) } diff --git a/http/logical.go b/http/logical.go index 6cdf6bb07110..7dc3ad9e832e 100644 --- a/http/logical.go +++ b/http/logical.go @@ -14,6 +14,7 @@ import ( "time" uuid "github.com/hashicorp/go-uuid" + "github.com/hashicorp/vault/helper/experiments" "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/sdk/helper/consts" "github.com/hashicorp/vault/sdk/logical" @@ -346,6 +347,24 @@ func handleLogicalInternal(core *vault.Core, injectDataIntoTopLevel bool, noForw return } + // Websockets need to be handled at HTTP layer instead of logical requests. + if core.IsExperimentEnabled(experiments.VaultExperimentEventsAlpha1) { + ns, err := namespace.FromContext(r.Context()) + if err != nil { + respondError(w, http.StatusInternalServerError, err) + return + } + nsPath := ns.Path + if ns.ID == namespace.RootNamespaceID { + nsPath = "" + } + if strings.HasPrefix(r.URL.Path, fmt.Sprintf("/v1/%ssys/events/subscribe/", nsPath)) { + handler := handleEventsSubscribe(core) + handler.ServeHTTP(w, r) + return + } + } + // Make the internal request. We attach the connection info // as well in case this is an authentication request that requires // it. Vault core handles stripping this if we need to. This also From 13313d059ca0c3c120ac52ea664167a61d10585f Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Fri, 10 Feb 2023 16:26:38 -0500 Subject: [PATCH 003/231] backport of commit 5ff44bd1cb6117d11dd72d9bfae0b7dd6072f7df (#19144) Co-authored-by: Christopher Swenson --- http/events.go | 17 +++++++++++++++-- http/events_test.go | 23 ++++++++++++++++++++++- http/logical.go | 4 ++-- vault/request_handling.go | 6 +++--- 4 files changed, 42 insertions(+), 8 deletions(-) diff --git a/http/events.go b/http/events.go index 478f31704250..22dfe35630db 100644 --- a/http/events.go +++ b/http/events.go @@ -2,6 +2,7 @@ package http import ( "context" + "errors" "fmt" "net/http" "strconv" @@ -66,13 +67,25 @@ func handleEventsSubscribeWebsocket(args eventSubscribeArgs) (websocket.StatusCo } } -func handleEventsSubscribe(core *vault.Core) http.Handler { +func handleEventsSubscribe(core *vault.Core, req *logical.Request) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { logger := core.Logger().Named("events-subscribe") - logger.Debug("Got request to", "url", r.URL, "version", r.Proto) ctx := r.Context() + + // ACL check + _, _, err := core.CheckToken(ctx, req, false) + if err != nil { + if errors.Is(err, logical.ErrPermissionDenied) { + respondError(w, http.StatusUnauthorized, logical.ErrPermissionDenied) + return + } + logger.Debug("Error validating token", "error", err) + respondError(w, http.StatusInternalServerError, fmt.Errorf("error validating token")) + return + } + ns, err := namespace.FromContext(ctx) if err != nil { logger.Info("Could not find namespace", "error", err) diff --git a/http/events_test.go b/http/events_test.go index d3729781df4c..645ecd403a4a 100644 --- a/http/events_test.go +++ b/http/events_test.go @@ -2,6 +2,7 @@ package http import ( "context" + "net/http" "strings" "sync/atomic" "testing" @@ -21,6 +22,15 @@ func TestEventsSubscribe(t *testing.T) { ln, addr := TestServer(t, core) defer ln.Close() + // unseal the core + keys, token := vault.TestCoreInit(t, core) + for _, key := range keys { + _, err := core.Unseal(key) + if err != nil { + t.Fatal(err) + } + } + stop := atomic.Bool{} eventType := "abc" @@ -53,7 +63,18 @@ func TestEventsSubscribe(t *testing.T) { t.Cleanup(cancelFunc) wsAddr := strings.Replace(addr, "http", "ws", 1) - conn, _, err := websocket.Dial(ctx, wsAddr+"/v1/sys/events/subscribe/"+eventType+"?json=true", nil) + + // check that the connection fails if we don't have a token + _, _, err := websocket.Dial(ctx, wsAddr+"/v1/sys/events/subscribe/"+eventType+"?json=true", nil) + if err == nil { + t.Error("Expected websocket error but got none") + } else if !strings.HasSuffix(err.Error(), "401") { + t.Errorf("Expected 401 websocket but got %v", err) + } + + conn, _, err := websocket.Dial(ctx, wsAddr+"/v1/sys/events/subscribe/"+eventType+"?json=true", &websocket.DialOptions{ + HTTPHeader: http.Header{"x-vault-token": []string{token}}, + }) if err != nil { t.Fatal(err) } diff --git a/http/logical.go b/http/logical.go index 7dc3ad9e832e..6b12a26f3bfe 100644 --- a/http/logical.go +++ b/http/logical.go @@ -13,7 +13,7 @@ import ( "strings" "time" - uuid "github.com/hashicorp/go-uuid" + "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/experiments" "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/sdk/helper/consts" @@ -359,7 +359,7 @@ func handleLogicalInternal(core *vault.Core, injectDataIntoTopLevel bool, noForw nsPath = "" } if strings.HasPrefix(r.URL.Path, fmt.Sprintf("/v1/%ssys/events/subscribe/", nsPath)) { - handler := handleEventsSubscribe(core) + handler := handleEventsSubscribe(core, req) handler.ServeHTTP(w, r) return } diff --git a/vault/request_handling.go b/vault/request_handling.go index 1d56d488a7ef..ff041838db2a 100644 --- a/vault/request_handling.go +++ b/vault/request_handling.go @@ -260,7 +260,7 @@ func (c *Core) fetchACLTokenEntryAndEntity(ctx context.Context, req *logical.Req return acl, te, entity, identityPolicies, nil } -func (c *Core) checkToken(ctx context.Context, req *logical.Request, unauth bool) (*logical.Auth, *logical.TokenEntry, error) { +func (c *Core) CheckToken(ctx context.Context, req *logical.Request, unauth bool) (*logical.Auth, *logical.TokenEntry, error) { defer metrics.MeasureSince([]string{"core", "check_token"}, time.Now()) var acl *ACL @@ -857,7 +857,7 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp } // Validate the token - auth, te, ctErr := c.checkToken(ctx, req, false) + auth, te, ctErr := c.CheckToken(ctx, req, false) if ctErr == logical.ErrRelativePath { return logical.ErrorResponse(ctErr.Error()), nil, ctErr } @@ -1272,7 +1272,7 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re // Do an unauth check. This will cause EGP policies to be checked var auth *logical.Auth var ctErr error - auth, _, ctErr = c.checkToken(ctx, req, true) + auth, _, ctErr = c.CheckToken(ctx, req, true) if ctErr == logical.ErrPerfStandbyPleaseForward { return nil, nil, ctErr } From 980754455f511421f00ca5c11b53a4b769a9f9a2 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Fri, 10 Feb 2023 17:17:04 -0500 Subject: [PATCH 004/231] backport of commit c90a024adfcda5a723b60039ddc49b3f9d709f15 (#19152) Co-authored-by: Tom Proctor --- changelog/19145.txt | 4 ++++ go.mod | 4 ++-- go.sum | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 changelog/19145.txt diff --git a/changelog/19145.txt b/changelog/19145.txt new file mode 100644 index 000000000000..9cca8e85d634 --- /dev/null +++ b/changelog/19145.txt @@ -0,0 +1,4 @@ +```release-note:improvement +secrets/kv: Emit events on write if events system enabled +``` + diff --git a/go.mod b/go.mod index f3615c02aaae..4eb32d8d7712 100644 --- a/go.mod +++ b/go.mod @@ -132,7 +132,7 @@ require ( github.com/hashicorp/vault-plugin-secrets-gcp v0.15.0 github.com/hashicorp/vault-plugin-secrets-gcpkms v0.14.0 github.com/hashicorp/vault-plugin-secrets-kubernetes v0.3.0 - github.com/hashicorp/vault-plugin-secrets-kv v0.14.0 + github.com/hashicorp/vault-plugin-secrets-kv v0.14.2 github.com/hashicorp/vault-plugin-secrets-mongodbatlas v0.9.1 github.com/hashicorp/vault-plugin-secrets-openldap v0.10.0 github.com/hashicorp/vault-plugin-secrets-terraform v0.7.0 @@ -140,7 +140,7 @@ require ( github.com/hashicorp/vault/api v1.9.0 github.com/hashicorp/vault/api/auth/approle v0.1.0 github.com/hashicorp/vault/api/auth/userpass v0.1.0 - github.com/hashicorp/vault/sdk v0.8.0 + github.com/hashicorp/vault/sdk v0.8.1 github.com/hashicorp/vault/vault/hcp_link/proto v0.0.0-20230201201504-b741fa893d77 github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab github.com/jackc/pgx/v4 v4.15.0 diff --git a/go.sum b/go.sum index e41d9e4fa012..5a4031f07c2c 100644 --- a/go.sum +++ b/go.sum @@ -1166,8 +1166,8 @@ github.com/hashicorp/vault-plugin-secrets-gcpkms v0.14.0 h1:eUC5ltK+1bkc+SVMzAUq github.com/hashicorp/vault-plugin-secrets-gcpkms v0.14.0/go.mod h1:86YCY86XuiQesV1jfjnV4icgoaxQdoUHONzDru+XQHA= github.com/hashicorp/vault-plugin-secrets-kubernetes v0.3.0 h1:Joz9SBwjpEOGu+Ynv60JC3fAA4UuLJzu7NcrKm6wMMs= github.com/hashicorp/vault-plugin-secrets-kubernetes v0.3.0/go.mod h1:NJeYBRgLVqjvkrVyZEe42oaqP3+xvVNMYdJoMWVoByU= -github.com/hashicorp/vault-plugin-secrets-kv v0.14.0 h1:PbveQUraOp9Bj7SVvFfssnmNYvlNTSHC6d/eLS+Am0c= -github.com/hashicorp/vault-plugin-secrets-kv v0.14.0/go.mod h1:YLsIcn9enkcyTqtuxmCXZ94nr2aeJCZhC+neHacX8SQ= +github.com/hashicorp/vault-plugin-secrets-kv v0.14.2 h1:13p50RIltQM/JH32uWZe9sAp16Uaj0zCLmVGPvS09qo= +github.com/hashicorp/vault-plugin-secrets-kv v0.14.2/go.mod h1:cAxt2o3BjRT5CbNLtgXuxTReaejvrgN/qk+no+DnwJ8= github.com/hashicorp/vault-plugin-secrets-mongodbatlas v0.9.1 h1:WkW8fyHxEdz1wGSTxCnSCrzXvgLXqXr8Iqp7upa/s4E= github.com/hashicorp/vault-plugin-secrets-mongodbatlas v0.9.1/go.mod h1:p96IECNtVwpvTq8RAw3dLlAYRWpG1n06XOoo0TkJnuk= github.com/hashicorp/vault-plugin-secrets-openldap v0.10.0 h1:Q3nKBbHQ6E/kOa3amKvcbhYTbkz4U25BBTwH66LnF+0= From 4d1ef8d582a49bcadabf41fba03de42c95f499de Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Fri, 10 Feb 2023 20:24:08 -0500 Subject: [PATCH 005/231] backport of commit 9acd846e72309635ccd603c98f99b00aa87cd8ab (#19154) Co-authored-by: Austin Gebauer <34121980+austingebauer@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4eb32d8d7712..7895a2aea6ea 100644 --- a/go.mod +++ b/go.mod @@ -121,7 +121,7 @@ require ( github.com/hashicorp/vault-plugin-auth-oci v0.13.1 github.com/hashicorp/vault-plugin-database-couchbase v0.9.0 github.com/hashicorp/vault-plugin-database-elasticsearch v0.13.0 - github.com/hashicorp/vault-plugin-database-mongodbatlas v0.8.0 + github.com/hashicorp/vault-plugin-database-mongodbatlas v0.9.0 github.com/hashicorp/vault-plugin-database-redis v0.2.0 github.com/hashicorp/vault-plugin-database-redis-elasticache v0.2.0 github.com/hashicorp/vault-plugin-database-snowflake v0.7.0 diff --git a/go.sum b/go.sum index 5a4031f07c2c..a1b7fff05162 100644 --- a/go.sum +++ b/go.sum @@ -1144,8 +1144,8 @@ github.com/hashicorp/vault-plugin-database-couchbase v0.9.0 h1:hJOHJ9yZ9kt1/DuRa github.com/hashicorp/vault-plugin-database-couchbase v0.9.0/go.mod h1:skmG6MgIG6fjIOlOEgVKOcNlr1PcgHPUb9q1YQ5+Q9k= github.com/hashicorp/vault-plugin-database-elasticsearch v0.13.0 h1:NwcbzQB529WtB/m7tZKxKiB6pQc0IyD3L80tk3mtBl8= github.com/hashicorp/vault-plugin-database-elasticsearch v0.13.0/go.mod h1:wO8EPQs5bsBERD6MSQ+7Az+YJ4TFclCNxBo3r3VKeao= -github.com/hashicorp/vault-plugin-database-mongodbatlas v0.8.0 h1:wx/9Dh9YGGU7GiijwRfwPFBlWdmBEdf6n2VhgTdRtJU= -github.com/hashicorp/vault-plugin-database-mongodbatlas v0.8.0/go.mod h1:eWwd1Ba7aLU1tIAtmFsEhu9E023jkkypHawxhnAbZfc= +github.com/hashicorp/vault-plugin-database-mongodbatlas v0.9.0 h1:wlWrg1z5Pyx+FTUCOzA9yh0FTI+pfA9tMrsFPFBcjjA= +github.com/hashicorp/vault-plugin-database-mongodbatlas v0.9.0/go.mod h1:4Ew6RNnA1NXtpLV0ijkwpE6pJE46G+suDKnTVMm+kXA= github.com/hashicorp/vault-plugin-database-redis v0.2.0 h1:Fg1inevnDhj58+/y5SY1CihLftytG1D+3QqbUJbHYUM= github.com/hashicorp/vault-plugin-database-redis v0.2.0/go.mod h1:hPj1vvjzsJ+g9PChP7iKqEJX7ttr03oz/RDEYsq8zZY= github.com/hashicorp/vault-plugin-database-redis-elasticache v0.2.0 h1:dgTT7E8xj56hjktMxHNAgFpy7pchpoQ20cIhDsBcgz8= From 32be0a9625a80c243b3c59dac1bc2ef1b058f9a4 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Mon, 13 Feb 2023 12:11:37 -0500 Subject: [PATCH 006/231] backport of commit 68f219c07e5d0155a026edfeb491cacb469c4a6a (#19161) Co-authored-by: mickael-hc <86245626+mickael-hc@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 7895a2aea6ea..5fd997992b78 100644 --- a/go.mod +++ b/go.mod @@ -279,7 +279,7 @@ require ( github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1 // indirect github.com/containerd/cgroups v1.0.3 // indirect - github.com/containerd/containerd v1.5.13 // indirect + github.com/containerd/containerd v1.5.17 // indirect github.com/containerd/continuity v0.3.0 // indirect github.com/coreos/go-oidc v2.2.1+incompatible // indirect github.com/coreos/go-oidc/v3 v3.1.0 // indirect @@ -293,7 +293,7 @@ require ( github.com/digitalocean/godo v1.7.5 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/docker/cli v20.10.18+incompatible // indirect - github.com/docker/distribution v2.7.1+incompatible // indirect + github.com/docker/distribution v2.8.1+incompatible // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect github.com/emicklei/go-restful/v3 v3.8.0 // indirect @@ -382,7 +382,7 @@ require ( github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-ieproxy v0.0.1 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mediocregopher/radix/v4 v4.1.1 // indirect github.com/miekg/dns v1.1.41 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect diff --git a/go.sum b/go.sum index a1b7fff05162..d527ad727ac5 100644 --- a/go.sum +++ b/go.sum @@ -391,8 +391,8 @@ github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09Zvgq github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= -github.com/containerd/containerd v1.5.13 h1:XqvKw9i4P7/mFrC3TSM7yV5cwFZ9avXe6M3YANKnzEE= -github.com/containerd/containerd v1.5.13/go.mod h1:3AlCrzKROjIuP3JALsY14n8YtntaUDBu7vek+rPN5Vc= +github.com/containerd/containerd v1.5.17 h1:NLDEI//zhMZpR3DS/AP0qiN+dzYKNAwJaNXCnCmYcgY= +github.com/containerd/containerd v1.5.17/go.mod h1:7IN9MtIzTZH4WPEmD1gNH8bbTQXVX68yd3ZXxSHYCis= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -519,8 +519,9 @@ github.com/docker/cli v20.10.18+incompatible h1:f/GQLsVpo10VvToRay2IraVA1wHz9Kkt github.com/docker/cli v20.10.18+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= +github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v1.4.2-0.20200319182547-c7ad2b866182/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= @@ -1405,8 +1406,9 @@ github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= github.com/mediocregopher/radix/v4 v4.1.1 h1:JkZBEp0y8pWGNZkmO3RR5oEO5huwd4zKKt4rh1C+P8s= github.com/mediocregopher/radix/v4 v4.1.1/go.mod h1:ajchozX/6ELmydxWeWM6xCFHVpZ4+67LXHOTOVR0nCE= From ef9f9c0f2d22eaf03a7debe7f5bf1dbeb11039d6 Mon Sep 17 00:00:00 2001 From: Kuba Wieczorek Date: Mon, 13 Feb 2023 19:03:35 +0000 Subject: [PATCH 007/231] Update version prerelease to rc1 (#19164) --- version/version_base.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version/version_base.go b/version/version_base.go index e45626e2cd8a..6353fc5a0d0b 100644 --- a/version/version_base.go +++ b/version/version_base.go @@ -12,6 +12,6 @@ var ( CgoEnabled bool Version = "1.13.0" - VersionPrerelease = "dev1" + VersionPrerelease = "rc1" VersionMetadata = "" ) From dbdbe9547c7def93c6fe69d8fab1e31cd5db65d3 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Mon, 13 Feb 2023 14:34:42 -0500 Subject: [PATCH 008/231] backport of commit 063a782e2d6485c6e74870215fc12c565d89954e (#19169) Co-authored-by: Christopher Swenson --- helper/namespace/namespace.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/helper/namespace/namespace.go b/helper/namespace/namespace.go index 93d68622dec5..c1226a5547f9 100644 --- a/helper/namespace/namespace.go +++ b/helper/namespace/namespace.go @@ -61,11 +61,8 @@ func RootContext(ctx context.Context) context.Context { return ContextWithNamespace(ctx, RootNamespace) } -// This function caches the ns to avoid doing a .Value lookup over and over, -// because it's called a *lot* in the request critical path. .Value is -// concurrency-safe so uses some kind of locking/atomicity, but it should never -// be read before first write, plus we don't believe this will be called from -// different goroutines, so it should be safe. +// FromContext retrieves the namespace from a context, or an error +// if there is no namespace in the context. func FromContext(ctx context.Context) (*Namespace, error) { if ctx == nil { return nil, errors.New("context was nil") From 3e4710d7ad576bc58403933756f4b12d83b041cc Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Tue, 14 Feb 2023 09:23:50 -0500 Subject: [PATCH 009/231] backport of commit d08de3e78eb583c3410dd22b0bf36bfed9d2ed88 (#19178) Co-authored-by: Kit Haines --- changelog/18499.txt | 3 + command/commands.go | 5 + command/pki_issue_intermediate.go | 28 ++- command/pki_reissue_intermediate.go | 297 +++++++++++++++++++++++ command/pki_reissue_intermediate_test.go | 195 +++++++++++++++ 5 files changed, 517 insertions(+), 11 deletions(-) create mode 100644 changelog/18499.txt create mode 100644 command/pki_reissue_intermediate.go create mode 100644 command/pki_reissue_intermediate_test.go diff --git a/changelog/18499.txt b/changelog/18499.txt new file mode 100644 index 000000000000..b329ed0db08b --- /dev/null +++ b/changelog/18499.txt @@ -0,0 +1,3 @@ +```release-note:improvement +cli/pki: Added "Reissue" command which allows extracting fields from an existing certificate to create a new certificate. +``` \ No newline at end of file diff --git a/command/commands.go b/command/commands.go index 2938ee1bb902..f441c3ee1d05 100644 --- a/command/commands.go +++ b/command/commands.go @@ -555,6 +555,11 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) map[string]cli.Co BaseCommand: getBaseCommand(), }, nil }, + "pki reissue": func() (cli.Command, error) { + return &PKIReIssueCACommand{ + BaseCommand: getBaseCommand(), + }, nil + }, "pki verify-sign": func() (cli.Command, error) { return &PKIVerifySignCommand{ BaseCommand: getBaseCommand(), diff --git a/command/pki_issue_intermediate.go b/command/pki_issue_intermediate.go index 90bb04fc95be..b2ca13330308 100644 --- a/command/pki_issue_intermediate.go +++ b/command/pki_issue_intermediate.go @@ -89,6 +89,14 @@ func (c *PKIIssueCACommand) Run(args []string) int { return 1 } + parentMountIssuer := sanitizePath(args[0]) // /pki/issuer/default + + intermediateMount := sanitizePath(args[1]) + + return pkiIssue(c.BaseCommand, parentMountIssuer, intermediateMount, c.flagNewIssuerName, c.flagKeyStorageSource, data) +} + +func pkiIssue(c *BaseCommand, parentMountIssuer string, intermediateMount string, flagNewIssuerName string, flagKeyStorageSource string, data map[string]interface{}) int { // Check We Have a Client client, err := c.Client() if err != nil { @@ -97,8 +105,6 @@ func (c *PKIIssueCACommand) Run(args []string) int { } // Sanity Check the Parent Issuer - parentMountIssuer := sanitizePath(args[0]) // /pki/issuer/default - _, parentIssuerName := paths.Split(parentMountIssuer) if !strings.Contains(parentMountIssuer, "/issuer/") { c.UI.Error(fmt.Sprintf("Parent Issuer %v is Not a PKI Issuer Path of the format /mount/issuer/issuer-ref", parentMountIssuer)) } @@ -108,16 +114,15 @@ func (c *PKIIssueCACommand) Run(args []string) int { } // Set-up Failure State (Immediately Before First Write Call) - intermediateMount := sanitizePath(args[1]) failureState := inCaseOfFailure{ intermediateMount: intermediateMount, parentMount: strings.Split(parentMountIssuer, "/issuer/")[0], parentIssuer: parentMountIssuer, - newName: c.flagNewIssuerName, + newName: flagNewIssuerName, } // Generate Certificate Signing Request - csrResp, err := client.Logical().Write(intermediateMount+"/intermediate/generate/"+c.flagKeyStorageSource, data) + csrResp, err := client.Logical().Write(intermediateMount+"/intermediate/generate/"+flagKeyStorageSource, data) if err != nil { if strings.Contains(err.Error(), "no handler for route") { // Mount Given Does Not Exist c.UI.Error(fmt.Sprintf("Given Intermediate Mount %v Does Not Exist: %v", intermediateMount, err)) @@ -129,21 +134,21 @@ func (c *PKIIssueCACommand) Run(args []string) int { return 1 } // Parse CSR Response, Also Verifies that this is a PKI Mount - // (eg. calling the above call on cubbyhole/ won't return an error response) + // (e.g. calling the above call on cubbyhole/ won't return an error response) csrPemRaw, present := csrResp.Data["csr"] if !present { c.UI.Error(fmt.Sprintf("Failed to Generate Intermediate CSR on %v, got response: %v", intermediateMount, csrResp)) return 1 } keyIdRaw, present := csrResp.Data["key_id"] - if !present && c.flagKeyStorageSource == "internal" { + if !present && flagKeyStorageSource == "internal" { c.UI.Error(fmt.Sprintf("Failed to Generate Key on %v, got response: %v", intermediateMount, csrResp)) return 1 } // If that all Parses, then we've successfully generated a CSR! Save It (and the Key-ID) failureState.csrGenerated = true - if c.flagKeyStorageSource == "internal" { + if flagKeyStorageSource == "internal" { failureState.createdKeyId = keyIdRaw.(string) } csr := csrPemRaw.(string) @@ -171,7 +176,7 @@ func (c *PKIIssueCACommand) Run(args []string) int { // Next Import Certificate certificate := rootResp.Data["certificate"].(string) - issuerId, err := importIssuerWithName(client, intermediateMount, certificate, c.flagNewIssuerName) + issuerId, err := importIssuerWithName(client, intermediateMount, certificate, flagNewIssuerName) failureState.certIssuerId = issuerId if err != nil { if strings.Contains(err.Error(), "error naming issuer") { @@ -189,6 +194,7 @@ func (c *PKIIssueCACommand) Run(args []string) int { // Then Import Issuing Certificate issuingCa := rootResp.Data["issuing_ca"].(string) + _, parentIssuerName := paths.Split(parentMountIssuer) _, err = importIssuerWithName(client, intermediateMount, issuingCa, parentIssuerName) if err != nil { if strings.Contains(err.Error(), "error naming issuer") { @@ -215,12 +221,12 @@ func (c *PKIIssueCACommand) Run(args []string) int { failureState.caChainImported = true // Finally we read our newly issued certificate in order to tell our caller about it - c.readAndOutputNewCertificate(client, intermediateMount, issuerId) + readAndOutputNewCertificate(client, intermediateMount, issuerId, c) return 0 } -func (c *PKIIssueCACommand) readAndOutputNewCertificate(client *api.Client, intermediateMount string, issuerId string) { +func readAndOutputNewCertificate(client *api.Client, intermediateMount string, issuerId string, c *BaseCommand) { resp, err := client.Logical().Read(sanitizePath(intermediateMount + "/issuer/" + issuerId)) if err != nil || resp == nil { c.UI.Error(fmt.Sprintf("Error Reading Fully Imported Certificate from %v : %v", diff --git a/command/pki_reissue_intermediate.go b/command/pki_reissue_intermediate.go new file mode 100644 index 000000000000..be7e57152eb2 --- /dev/null +++ b/command/pki_reissue_intermediate.go @@ -0,0 +1,297 @@ +package command + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "crypto/x509" + "encoding/hex" + "encoding/pem" + "fmt" + "io" + "net" + "net/url" + "os" + "strings" + + "github.com/posener/complete" +) + +type PKIReIssueCACommand struct { + *BaseCommand + + flagConfig string + flagReturnIndicator string + flagDefaultDisabled bool + flagList bool + + flagKeyStorageSource string + flagNewIssuerName string +} + +func (c *PKIReIssueCACommand) Synopsis() string { + return "Uses a parent certificate and a template certificate to create a new issuer on a child mount" +} + +func (c *PKIReIssueCACommand) Help() string { + helpText := ` +Usage: vault pki reissue PARENT TEMPLATE CHILD_MOUNT options +` + return strings.TrimSpace(helpText) +} + +func (c *PKIReIssueCACommand) Flags() *FlagSets { + set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat) + f := set.NewFlagSet("Command Options") + + f.StringVar(&StringVar{ + Name: "type", + Target: &c.flagKeyStorageSource, + Default: "internal", + EnvVar: "", + Usage: `Options are “existing” - to use an existing key inside vault, “internal” - to generate a new key inside vault, or “kms” - to link to an external key. Exported keys are not available through this API.`, + Completion: complete.PredictSet("internal", "existing", "kms"), + }) + + f.StringVar(&StringVar{ + Name: "issuer_name", + Target: &c.flagNewIssuerName, + Default: "", + EnvVar: "", + Usage: `If present, the newly created issuer will be given this name`, + }) + + return set +} + +func (c *PKIReIssueCACommand) Run(args []string) int { + // Parse Args + f := c.Flags() + if err := f.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + args = f.Args() + + if len(args) < 3 { + c.UI.Error("Not enough arguments: expected parent issuer and child-mount location and some key_value argument") + return 1 + } + + stdin := (io.Reader)(os.Stdin) + userData, err := parseArgsData(stdin, args[3:]) + if err != nil { + c.UI.Error(fmt.Sprintf("Failed to parse K=V data: %s", err)) + return 1 + } + + // Check We Have a Client + client, err := c.Client() + if err != nil { + c.UI.Error(fmt.Sprintf("Failed to obtain client: %v", err)) + return 1 + } + + parentIssuer := sanitizePath(args[0]) // /pki/issuer/default + intermediateMount := sanitizePath(args[2]) + + templateCertificateResp, err := client.Logical().Read(sanitizePath(args[1])) + if err != nil { + c.UI.Error(fmt.Sprintf("Error fetching template certificate %v : %v", sanitizePath(args[1]), err)) + return 1 + } + templateCertificateRaw, ok := templateCertificateResp.Data["certificate"] + if !ok { + c.UI.Error(fmt.Sprintf("No Certificate Field Found at %v instead found : %v", sanitizePath(args[1]), templateCertificateResp)) + return 1 + } + certificatePemString := templateCertificateRaw.(string) + certificatePem := []byte(certificatePemString) + certificateBlock, _ := pem.Decode(certificatePem) + certificate, err := x509.ParseCertificate(certificateBlock.Bytes) + if err != nil { + c.UI.Error(fmt.Sprintf("Error parsing template certificate at %v : %v", sanitizePath(args[1]), err)) + return 1 + } + + useExistingKey := c.flagKeyStorageSource == "existing" + keyRef := "" + if useExistingKey { // TODO: Better Error information + keyRef = templateCertificateResp.Data["key_id"].(string) + } + + templateData, err := parseTemplateCertificate(*certificate, useExistingKey, keyRef) + data := updateTemplateWithData(templateData, userData) + + return pkiIssue(c.BaseCommand, parentIssuer, intermediateMount, c.flagNewIssuerName, c.flagKeyStorageSource, data) +} + +func updateTemplateWithData(template map[string]interface{}, changes map[string]interface{}) map[string]interface{} { + data := map[string]interface{}{} + + for key, value := range template { + data[key] = value + } + + // ttl and not_after set the same thing. Delete template ttl if using not_after: + if _, ok := changes["not_after"]; ok { + delete(data, "ttl") + } + + // If we are updating the key_type, do not set key_bits + if _, ok := changes["key_type"]; ok && changes["key_type"] != template["key_type"] { + delete(data, "key_bits") + } + + for key, value := range changes { + data[key] = value + } + + return data +} + +func parseTemplateCertificate(certificate x509.Certificate, useExistingKey bool, keyRef string) (templateData map[string]interface{}, err error) { + // Generate Certificate Signing Parameters + templateData = map[string]interface{}{ + "common_name": certificate.Subject.CommonName, + "alt_names": makeAltNamesCommaSeparatedString(certificate.DNSNames, certificate.EmailAddresses), + "ip_sans": makeIpAddressCommaSeparatedString(certificate.IPAddresses), + "uri_sans": makeUriCommaSeparatedString(certificate.URIs), + // other_sans (string: "") - Specifies custom OID/UTF8-string SANs. These must match values specified on the role in allowed_other_sans (see role creation for allowed_other_sans globbing rules). The format is the same as OpenSSL: ;: where the only current valid type is UTF8. This can be a comma-delimited list or a JSON string slice. + // Punting on Other_SANs, shouldn't really be on CAs + "signature_bits": findSignatureBits(certificate.SignatureAlgorithm), + "exclude_cn_from_sans": determineExcludeCnFromSans(certificate), + "ou": certificate.Subject.OrganizationalUnit, + "organization": certificate.Subject.Organization, + "country": certificate.Subject.Country, + "locality": certificate.Subject.Locality, + "province": certificate.Subject.Province, + "street_address": certificate.Subject.StreetAddress, + "postal_code": certificate.Subject.PostalCode, + "serial_number": certificate.Subject.SerialNumber, + "ttl": (certificate.NotAfter.Sub(certificate.NotBefore)).String(), + "max_path_length": certificate.MaxPathLen, + "permitted_dns_domains": strings.Join(certificate.PermittedDNSDomains, ","), + "use_pss": isPSS(certificate.SignatureAlgorithm), + } + + if useExistingKey { + templateData["skid"] = hex.EncodeToString(certificate.SubjectKeyId) // TODO: Double Check this with someone + if keyRef == "" { + return nil, fmt.Errorf("unable to create certificate template for existing key without a key_id") + } + templateData["key_ref"] = keyRef + } else { + templateData["key_type"] = getKeyType(certificate.PublicKeyAlgorithm.String()) + templateData["key_bits"] = findBitLength(certificate.PublicKey) + } + + return templateData, nil +} + +func isPSS(algorithm x509.SignatureAlgorithm) bool { + switch algorithm { + case x509.SHA384WithRSAPSS, x509.SHA512WithRSAPSS, x509.SHA256WithRSAPSS: + return true + default: + return false + } +} + +func makeAltNamesCommaSeparatedString(names []string, emails []string) string { + return strings.Join(names, ",") + "," + strings.Join(emails, ",") +} + +func makeUriCommaSeparatedString(uris []*url.URL) string { + stringAddresses := make([]string, len(uris)) + for i, uri := range uris { + stringAddresses[i] = uri.String() + } + return strings.Join(stringAddresses, ",") +} + +func makeIpAddressCommaSeparatedString(addresses []net.IP) string { + stringAddresses := make([]string, len(addresses)) + for i, address := range addresses { + stringAddresses[i] = address.String() + } + return strings.Join(stringAddresses, ",") +} + +func determineExcludeCnFromSans(certificate x509.Certificate) bool { + cn := certificate.Subject.CommonName + if cn == "" { + return false + } + + emails := certificate.EmailAddresses + for _, email := range emails { + if email == cn { + return false + } + } + + dnses := certificate.DNSNames + for _, dns := range dnses { + if dns == cn { + return false + } + } + + return true +} + +func findBitLength(publicKey any) int { + if publicKey == nil { + return 0 + } + switch pub := publicKey.(type) { + case *rsa.PublicKey: + return pub.N.BitLen() + case *ecdsa.PublicKey: + switch pub.Curve { + case elliptic.P224(): + return 224 + case elliptic.P256(): + return 256 + case elliptic.P384(): + return 384 + case elliptic.P521(): + return 521 + default: + return 0 + } + default: + return 0 + } +} + +func findSignatureBits(algo x509.SignatureAlgorithm) int { + switch algo { + case x509.MD2WithRSA, x509.MD5WithRSA, x509.SHA1WithRSA, x509.DSAWithSHA1, x509.ECDSAWithSHA1: + return -1 + case x509.SHA256WithRSA, x509.DSAWithSHA256, x509.ECDSAWithSHA256, x509.SHA256WithRSAPSS: + return 256 + case x509.SHA384WithRSA, x509.ECDSAWithSHA384, x509.SHA384WithRSAPSS: + return 384 + case x509.SHA512WithRSA, x509.SHA512WithRSAPSS, x509.ECDSAWithSHA512: + return 512 + case x509.PureEd25519: + return 0 + default: + return -1 + } +} + +func getKeyType(goKeyType string) string { + switch goKeyType { + case "RSA": + return "rsa" + case "ECDSA": + return "ec" + case "Ed25519": + return "ed25519" + default: + return "" + } +} diff --git a/command/pki_reissue_intermediate_test.go b/command/pki_reissue_intermediate_test.go new file mode 100644 index 000000000000..928449344cb6 --- /dev/null +++ b/command/pki_reissue_intermediate_test.go @@ -0,0 +1,195 @@ +package command + +import ( + "bytes" + "testing" + + "github.com/hashicorp/vault/api" +) + +// TestPKIReIssueIntermediate tests that the pki reissue command line tool accurately copies information from the +// template certificate to the newly issued certificate, by issuing and reissuing several certificates and seeing how +// they related to each other. +func TestPKIReIssueIntermediate(t *testing.T) { + t.Parallel() + + client, closer := testVaultServer(t) + defer closer() + + // Relationship Map to Create + // pki-root | pki-newroot | pki-empty + // RootX1 RootX2 RootX4 RootX3 + // | | + // ---------------------------------------------- + // v v + // IntX1 IntX2 pki-int + // | | + // v v + // IntX3 (-----------------------) IntX3 + // + // Here X1,X2 have the same name (same mount) + // RootX4 uses the same key as RootX1 (but a different common_name/subject) + // RootX3 has the same name, and is on a different mount + // RootX1 has issued IntX1; RootX3 has issued IntX2 + createComplicatedIssuerSetUpWithReIssueIntermediate(t, client) + + runPkiVerifySignTests(t, client) + + runPkiListIntermediateTests(t, client) +} + +func createComplicatedIssuerSetUpWithReIssueIntermediate(t *testing.T, client *api.Client) { + // Relationship Map to Create + // pki-root | pki-newroot | pki-empty + // RootX1 RootX2 RootX4 RootX3 + // | | + // ---------------------------------------------- + // v v + // IntX1 IntX2 pki-int + // | | + // v v + // IntX3 (-----------------------) IntX3 + // + // Here X1,X2 have the same name (same mount) + // RootX4 uses the same key as RootX1 (but a different common_name/subject) + // RootX3 has the same name, and is on a different mount + // RootX1 has issued IntX1; RootX3 has issued IntX2 + + if err := client.Sys().Mount("pki-root", &api.MountInput{ + Type: "pki", + Config: api.MountConfigInput{ + MaxLeaseTTL: "36500d", + }, + }); err != nil { + t.Fatalf("pki mount error: %#v", err) + } + + if err := client.Sys().Mount("pki-newroot", &api.MountInput{ + Type: "pki", + Config: api.MountConfigInput{ + MaxLeaseTTL: "36500d", + }, + }); err != nil { + t.Fatalf("pki mount error: %#v", err) + } + + if err := client.Sys().Mount("pki-int", &api.MountInput{ + Type: "pki", + Config: api.MountConfigInput{ + MaxLeaseTTL: "36500d", + }, + }); err != nil { + t.Fatalf("pki mount error: %#v", err) + } + + // Used to check handling empty list responses: Not Used for Any Issuers / Certificates + if err := client.Sys().Mount("pki-empty", &api.MountInput{ + Type: "pki", + Config: api.MountConfigInput{}, + }); err != nil { + t.Fatalf("pki mount error: %#v", err) + } + + resp, err := client.Logical().Write("pki-root/root/generate/internal", map[string]interface{}{ + "key_type": "ec", + "common_name": "Root X", + "ttl": "3650d", + "issuer_name": "rootX1", + "key_name": "rootX1", + }) + if err != nil || resp == nil { + t.Fatalf("failed to prime CA: %v", err) + } + + resp, err = client.Logical().Write("pki-root/root/generate/internal", map[string]interface{}{ + "key_type": "ec", + "common_name": "Root X", + "ttl": "3650d", + "issuer_name": "rootX2", + }) + if err != nil || resp == nil { + t.Fatalf("failed to prime CA: %v", err) + } + + if resp, err := client.Logical().Write("pki-newroot/root/generate/internal", map[string]interface{}{ + "key_type": "ec", + "common_name": "Root X", + "ttl": "3650d", + "issuer_name": "rootX3", + }); err != nil || resp == nil { + t.Fatalf("failed to prime CA: %v", err) + } + + if resp, err := client.Logical().Write("pki-root/root/generate/existing", map[string]interface{}{ + "common_name": "Root X4", + "ttl": "3650d", + "issuer_name": "rootX4", + "key_ref": "rootX1", + }); err != nil || resp == nil { + t.Fatalf("failed to prime CA: %v", err) + } + + stdout := bytes.NewBuffer(nil) + stderr := bytes.NewBuffer(nil) + runOpts := &RunOptions{ + Stdout: stdout, + Stderr: stderr, + Client: client, + } + + // Intermediate X1 + intX1CallArgs := []string{ + "pki", "issue", "-format=json", "-issuer_name=intX1", + "pki-root/issuer/rootX1", + "pki-int/", + "key_type=rsa", + "common_name=Int X1", + "ou=thing", + "ttl=3650d", + } + codeOut := RunCustom(intX1CallArgs, runOpts) + if codeOut != 0 { + t.Fatalf("error issuing intermediate X1, code: %d \n stdout: %v \n stderr: %v", codeOut, stdout, stderr) + } + + // Intermediate X2 - using ReIssue + intX2CallArgs := []string{ + "pki", "reissue", "-format=json", "-issuer_name=intX2", + "pki-newroot/issuer/rootX3", + "pki-int/issuer/intX1", + "pki-int/", + "key_type=ec", + "common_name=Int X2", + } + codeOut = RunCustom(intX2CallArgs, runOpts) + if codeOut != 0 { + t.Fatalf("error issuing intermediate X2, code: %d \n stdout: %v \n stderr: %v", codeOut, stdout, stderr) + } + + // Intermediate X3 + intX3OriginalCallArgs := []string{ + "pki", "issue", "-format=json", "-issuer_name=intX3", + "pki-int/issuer/intX1", + "pki-int/", + "key_type=ec", + "use_pss=true", // This is meaningful because rootX1 is an RSA key + "signature_bits=512", + "common_name=Int X3", + "ttl=3650d", + } + codeOut = RunCustom(intX3OriginalCallArgs, runOpts) + if codeOut != 0 { + t.Fatalf("error issuing intermediate X3, code: %d \n stdout: %v \n stderr: %v", codeOut, stdout, stderr) + } + + intX3AdaptedCallArgs := []string{ + "pki", "reissue", "-format=json", "-issuer_name=intX3also", "-type=existing", + "pki-int/issuer/intX2", // This is a EC key + "pki-int/issuer/intX3", // This template includes use_pss = true which can't be accomodated + "pki-int/", + } + codeOut = RunCustom(intX3AdaptedCallArgs, runOpts) + if codeOut != 0 { + t.Fatalf("error issuing intermediate X3also, code: %d \n stdout: %v \n stderr: %v", codeOut, stdout, stderr) + } +} From 02bc25417a79d585d3234ec52a519bb44f8bcb7d Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Tue, 14 Feb 2023 09:51:12 -0500 Subject: [PATCH 010/231] Trap errors related to vault pki list-intermediate issuer reading (#19165) (#19177) * Rename files to match test suite and existing pattern * Factor out issuer loading into a dedicated function - Add a little more checks/validation when loading the a PKI issuer - Factor out the issuer loading into a dedicated function - Leverage existing health check code to parse issuer certificates * Read parent issuer once instead of reloading it for every child - Read in our parent issuer once instead of running it for every child we want to compare against - Provides clearer error message that we have failed reading from which path to the end user * PR Feedback - Rename a variable for clarity - Use readIssuer in the validation of the parent issuer within pkiIssuer - Add some missing return 1 statements in error handlers that had been missed Co-authored-by: Steven Clark --- command/healthcheck/pki.go | 8 +- .../pki_allow_if_modified_since.go | 4 +- command/healthcheck/pki_audit_visibility.go | 6 +- command/healthcheck/shared.go | 2 +- command/pki_issue_intermediate.go | 5 +- ...te_command.go => pki_list_intermediate.go} | 8 +- command/pki_reissue_intermediate.go | 29 ++-- ...ify_sign_command.go => pki_verify_sign.go} | 151 +++++++++++++----- 8 files changed, 140 insertions(+), 73 deletions(-) rename command/{pki_list_intermediate_command.go => pki_list_intermediate.go} (97%) rename command/{pki_verify_sign_command.go => pki_verify_sign.go} (60%) diff --git a/command/healthcheck/pki.go b/command/healthcheck/pki.go index edec1523c4b6..406163b355cb 100644 --- a/command/healthcheck/pki.go +++ b/command/healthcheck/pki.go @@ -47,7 +47,7 @@ func parsePEM(contents string) ([]byte, error) { return pemBlock.Bytes, nil } -func parsePEMCert(contents string) (*x509.Certificate, error) { +func ParsePEMCert(contents string) (*x509.Certificate, error) { parsed, err := parsePEM(contents) if err != nil { return nil, err @@ -89,7 +89,7 @@ func pkiFetchIssuer(e *Executor, issuer string, versionError func()) (bool, *Pat } if len(issuerRet.ParsedCache) == 0 { - cert, err := parsePEMCert(issuerRet.Secret.Data["certificate"].(string)) + cert, err := ParsePEMCert(issuerRet.Secret.Data["certificate"].(string)) if err != nil { return true, issuerRet, nil, fmt.Errorf("unable to parse issuer %v's certificate: %w", issuer, err) } @@ -114,7 +114,7 @@ func pkiFetchIssuerEntry(e *Executor, issuer string, versionError func()) (bool, } if len(issuerRet.ParsedCache) == 0 { - cert, err := parsePEMCert(issuerRet.Secret.Data["certificate"].(string)) + cert, err := ParsePEMCert(issuerRet.Secret.Data["certificate"].(string)) if err != nil { return true, issuerRet, nil, fmt.Errorf("unable to parse issuer %v's certificate: %w", issuer, err) } @@ -222,7 +222,7 @@ func pkiFetchLeaf(e *Executor, serial string, versionError func()) (bool, *PathF } if len(leafRet.ParsedCache) == 0 { - cert, err := parsePEMCert(leafRet.Secret.Data["certificate"].(string)) + cert, err := ParsePEMCert(leafRet.Secret.Data["certificate"].(string)) if err != nil { return true, leafRet, nil, fmt.Errorf("unable to parse leaf %v's certificate: %w", serial, err) } diff --git a/command/healthcheck/pki_allow_if_modified_since.go b/command/healthcheck/pki_allow_if_modified_since.go index 59f96611b118..c9a1b0cbdff2 100644 --- a/command/healthcheck/pki_allow_if_modified_since.go +++ b/command/healthcheck/pki_allow_if_modified_since.go @@ -64,12 +64,12 @@ func (h *AllowIfModifiedSince) Evaluate(e *Executor) (results []*Result, err err return []*Result{&ret}, nil } - req, err := stringList(h.TuneData["passthrough_request_headers"]) + req, err := StringList(h.TuneData["passthrough_request_headers"]) if err != nil { return nil, fmt.Errorf("unable to parse value from server for passthrough_request_headers: %w", err) } - resp, err := stringList(h.TuneData["allowed_response_headers"]) + resp, err := StringList(h.TuneData["allowed_response_headers"]) if err != nil { return nil, fmt.Errorf("unable to parse value from server for allowed_response_headers: %w", err) } diff --git a/command/healthcheck/pki_audit_visibility.go b/command/healthcheck/pki_audit_visibility.go index 24f9be4f6a1f..5fb761d6081d 100644 --- a/command/healthcheck/pki_audit_visibility.go +++ b/command/healthcheck/pki_audit_visibility.go @@ -83,7 +83,7 @@ func (h *AuditVisibility) DefaultConfig() map[string]interface{} { func (h *AuditVisibility) LoadConfig(config map[string]interface{}) error { var err error - coerced, err := stringList(config["ignored_parameters"]) + coerced, err := StringList(config["ignored_parameters"]) if err != nil { return fmt.Errorf("error parsing %v.ignored_parameters: %v", h.Name(), err) } @@ -128,7 +128,7 @@ func (h *AuditVisibility) Evaluate(e *Executor) (results []*Result, err error) { "audit_non_hmac_response_keys": VisibleRespParams, } for source, visibleList := range sourceMap { - actual, err := stringList(h.TuneData[source]) + actual, err := StringList(h.TuneData[source]) if err != nil { return nil, fmt.Errorf("error parsing %v from server: %v", source, err) } @@ -158,7 +158,7 @@ func (h *AuditVisibility) Evaluate(e *Executor) (results []*Result, err error) { "audit_non_hmac_response_keys": HiddenRespParams, } for source, hiddenList := range sourceMap { - actual, err := stringList(h.TuneData[source]) + actual, err := StringList(h.TuneData[source]) if err != nil { return nil, fmt.Errorf("error parsing %v from server: %v", source, err) } diff --git a/command/healthcheck/shared.go b/command/healthcheck/shared.go index e9d6a5a9964e..17f8859ae2d4 100644 --- a/command/healthcheck/shared.go +++ b/command/healthcheck/shared.go @@ -6,7 +6,7 @@ import ( "github.com/hashicorp/vault/sdk/logical" ) -func stringList(source interface{}) ([]string, error) { +func StringList(source interface{}) ([]string, error) { if source == nil { return nil, nil } diff --git a/command/pki_issue_intermediate.go b/command/pki_issue_intermediate.go index b2ca13330308..7fd881aac1bb 100644 --- a/command/pki_issue_intermediate.go +++ b/command/pki_issue_intermediate.go @@ -107,10 +107,12 @@ func pkiIssue(c *BaseCommand, parentMountIssuer string, intermediateMount string // Sanity Check the Parent Issuer if !strings.Contains(parentMountIssuer, "/issuer/") { c.UI.Error(fmt.Sprintf("Parent Issuer %v is Not a PKI Issuer Path of the format /mount/issuer/issuer-ref", parentMountIssuer)) + return 1 } - _, err = client.Logical().Read(parentMountIssuer + "/json") + _, err = readIssuer(client, parentMountIssuer) if err != nil { c.UI.Error(fmt.Sprintf("Unable to access parent issuer %v: %v", parentMountIssuer, err)) + return 1 } // Set-up Failure State (Immediately Before First Write Call) @@ -231,6 +233,7 @@ func readAndOutputNewCertificate(client *api.Client, intermediateMount string, i if err != nil || resp == nil { c.UI.Error(fmt.Sprintf("Error Reading Fully Imported Certificate from %v : %v", intermediateMount+"/issuer/"+issuerId, err)) + return } OutputSecret(c.UI, resp) diff --git a/command/pki_list_intermediate_command.go b/command/pki_list_intermediate.go similarity index 97% rename from command/pki_list_intermediate_command.go rename to command/pki_list_intermediate.go index a54c5ef77952..2de5a8e8ab76 100644 --- a/command/pki_list_intermediate_command.go +++ b/command/pki_list_intermediate.go @@ -186,10 +186,16 @@ func (c *PKIListIntermediateCommand) Run(args []string) int { "signature_match": c.flagSignatureMatch, } + issuerResp, err := readIssuer(client, issuer) + if err != nil { + c.UI.Error(fmt.Sprintf("Failed to read parent issuer on path %s: %s", issuer, err.Error())) + return 1 + } + for _, child := range issued { path := sanitizePath(child) if path != "" { - err, verifyResults := verifySignBetween(client, issuer, path) + verifyResults, err := verifySignBetween(client, issuerResp, path) if err != nil { c.UI.Error(fmt.Sprintf("Failed to run verification on path %v: %v", path, err)) return 1 diff --git a/command/pki_reissue_intermediate.go b/command/pki_reissue_intermediate.go index be7e57152eb2..4c6659cf3770 100644 --- a/command/pki_reissue_intermediate.go +++ b/command/pki_reissue_intermediate.go @@ -6,7 +6,6 @@ import ( "crypto/rsa" "crypto/x509" "encoding/hex" - "encoding/pem" "fmt" "io" "net" @@ -93,31 +92,25 @@ func (c *PKIReIssueCACommand) Run(args []string) int { } parentIssuer := sanitizePath(args[0]) // /pki/issuer/default + templateIssuer := sanitizePath(args[1]) intermediateMount := sanitizePath(args[2]) - templateCertificateResp, err := client.Logical().Read(sanitizePath(args[1])) + templateIssuerBundle, err := readIssuer(client, templateIssuer) if err != nil { - c.UI.Error(fmt.Sprintf("Error fetching template certificate %v : %v", sanitizePath(args[1]), err)) - return 1 - } - templateCertificateRaw, ok := templateCertificateResp.Data["certificate"] - if !ok { - c.UI.Error(fmt.Sprintf("No Certificate Field Found at %v instead found : %v", sanitizePath(args[1]), templateCertificateResp)) - return 1 - } - certificatePemString := templateCertificateRaw.(string) - certificatePem := []byte(certificatePemString) - certificateBlock, _ := pem.Decode(certificatePem) - certificate, err := x509.ParseCertificate(certificateBlock.Bytes) - if err != nil { - c.UI.Error(fmt.Sprintf("Error parsing template certificate at %v : %v", sanitizePath(args[1]), err)) + c.UI.Error(fmt.Sprintf("Error fetching template certificate %v : %v", templateIssuer, err)) return 1 } + certificate := templateIssuerBundle.certificate useExistingKey := c.flagKeyStorageSource == "existing" keyRef := "" - if useExistingKey { // TODO: Better Error information - keyRef = templateCertificateResp.Data["key_id"].(string) + if useExistingKey { + keyRef = templateIssuerBundle.keyId + + if keyRef == "" { + c.UI.Error(fmt.Sprintf("Template issuer %s did not have a key id field set in response which is required", templateIssuer)) + return 1 + } } templateData, err := parseTemplateCertificate(*certificate, useExistingKey, keyRef) diff --git a/command/pki_verify_sign_command.go b/command/pki_verify_sign.go similarity index 60% rename from command/pki_verify_sign_command.go rename to command/pki_verify_sign.go index fc38583a3f95..cee6ae00d1a0 100644 --- a/command/pki_verify_sign_command.go +++ b/command/pki_verify_sign.go @@ -8,9 +8,10 @@ import ( "strconv" "strings" + "github.com/hashicorp/vault/command/healthcheck" + "github.com/ghodss/yaml" "github.com/hashicorp/vault/api" - "github.com/hashicorp/vault/sdk/helper/certutil" "github.com/ryanuber/columnize" ) @@ -93,7 +94,13 @@ func (c *PKIVerifySignCommand) Run(args []string) int { return 1 } - err, results := verifySignBetween(client, issuer, issued) + issuerResp, err := readIssuer(client, issuer) + if err != nil { + c.UI.Error(fmt.Sprintf("Failed to read issuer: %s: %s", issuer, err.Error())) + return 1 + } + + results, err := verifySignBetween(client, issuerResp, issued) if err != nil { c.UI.Error(fmt.Sprintf("Failed to run verification: %v", err)) return pkiRetUsage @@ -104,60 +111,34 @@ func (c *PKIVerifySignCommand) Run(args []string) int { return 0 } -func verifySignBetween(client *api.Client, issuerPath string, issuedPath string) (error, map[string]bool) { +func verifySignBetween(client *api.Client, issuerResp *issuerResponse, issuedPath string) (map[string]bool, error) { // Note that this eats warnings - // Fetch and Parse the Potential Issuer: - issuerResp, err := client.Logical().Read(issuerPath) - if err != nil { - return fmt.Errorf("error: unable to fetch issuer %v: %w", issuerPath, err), nil - } - issuerCertPem := issuerResp.Data["certificate"].(string) - issuerCertBundle, err := certutil.ParsePEMBundle(issuerCertPem) - if err != nil { - return err, nil - } - issuerKeyId := issuerCertBundle.Certificate.SubjectKeyId + issuerCert := issuerResp.certificate + issuerKeyId := issuerCert.SubjectKeyId // Fetch and Parse the Potential Issued Cert - issuedCertResp, err := client.Logical().Read(issuedPath) - if err != nil { - return fmt.Errorf("error: unable to fetch issuer %v: %w", issuerPath, err), nil - } - if len(issuedPath) <= 2 { - return fmt.Errorf("%v", issuedPath), nil - } - caChainRaw := issuedCertResp.Data["ca_chain"] - if caChainRaw == nil { - return fmt.Errorf("no ca_chain information on %v", issuedPath), nil - } - caChainCast := caChainRaw.([]interface{}) - caChain := make([]string, len(caChainCast)) - for i, cert := range caChainCast { - caChain[i] = cert.(string) - } - issuedCertPem := issuedCertResp.Data["certificate"].(string) - issuedCertBundle, err := certutil.ParsePEMBundle(issuedCertPem) + issuedCertBundle, err := readIssuer(client, issuedPath) if err != nil { - return err, nil + return nil, fmt.Errorf("error: unable to fetch issuer %v: %w", issuedPath, err) } - parentKeyId := issuedCertBundle.Certificate.AuthorityKeyId + parentKeyId := issuedCertBundle.certificate.AuthorityKeyId // Check the Chain-Match rootCertPool := x509.NewCertPool() - rootCertPool.AddCert(issuerCertBundle.Certificate) + rootCertPool.AddCert(issuerCert) checkTrustPathOptions := x509.VerifyOptions{ Roots: rootCertPool, } trust := false - trusts, err := issuedCertBundle.Certificate.Verify(checkTrustPathOptions) + trusts, err := issuedCertBundle.certificate.Verify(checkTrustPathOptions) if err != nil && !strings.Contains(err.Error(), "certificate signed by unknown authority") { - return err, nil + return nil, err } else if err == nil { for _, chain := range trusts { // Output of this Should Only Have One Trust with Chain of Length Two (Child followed by Parent) for _, cert := range chain { - if issuedCertBundle.Certificate.Equal(cert) { + if issuedCertBundle.certificate.Equal(cert) { trust = true break } @@ -166,29 +147,113 @@ func verifySignBetween(client *api.Client, issuerPath string, issuedPath string) } pathMatch := false - for _, cert := range caChain { - if strings.TrimSpace(cert) == strings.TrimSpace(issuerCertPem) { // TODO: Decode into ASN1 to Check + for _, cert := range issuedCertBundle.caChain { + if bytes.Equal(cert.Raw, issuerCert.Raw) { pathMatch = true break } } signatureMatch := false - err = issuedCertBundle.Certificate.CheckSignatureFrom(issuerCertBundle.Certificate) + err = issuedCertBundle.certificate.CheckSignatureFrom(issuerCert) if err == nil { signatureMatch = true } result := map[string]bool{ // This comparison isn't strictly correct, despite a standard ordering these are sets - "subject_match": bytes.Equal(issuerCertBundle.Certificate.RawSubject, issuedCertBundle.Certificate.RawIssuer), + "subject_match": bytes.Equal(issuerCert.RawSubject, issuedCertBundle.certificate.RawIssuer), "path_match": pathMatch, "trust_match": trust, // TODO: Refactor into a reasonable function "key_id_match": bytes.Equal(parentKeyId, issuerKeyId), "signature_match": signatureMatch, } - return nil, result + return result, nil +} + +type issuerResponse struct { + keyId string + certificate *x509.Certificate + caChain []*x509.Certificate +} + +func readIssuer(client *api.Client, issuerPath string) (*issuerResponse, error) { + issuerResp, err := client.Logical().Read(issuerPath) + if err != nil { + return nil, err + } + issuerCertPem, err := requireStrRespField(issuerResp, "certificate") + if err != nil { + return nil, err + } + issuerCert, err := healthcheck.ParsePEMCert(issuerCertPem) + if err != nil { + return nil, fmt.Errorf("unable to parse issuer %v's certificate: %w", issuerPath, err) + } + + caChainPem, err := requireStrListRespField(issuerResp, "ca_chain") + if err != nil { + return nil, fmt.Errorf("unable to parse issuer %v's CA chain: %w", issuerPath, err) + } + + var caChain []*x509.Certificate + for _, pem := range caChainPem { + trimmedPem := strings.TrimSpace(pem) + if trimmedPem == "" { + continue + } + cert, err := healthcheck.ParsePEMCert(trimmedPem) + if err != nil { + return nil, err + } + caChain = append(caChain, cert) + } + + keyId := optStrRespField(issuerResp, "key_id") + + return &issuerResponse{ + keyId: keyId, + certificate: issuerCert, + caChain: caChain, + }, nil +} + +func optStrRespField(resp *api.Secret, reqField string) string { + if resp == nil || resp.Data == nil { + return "" + } + if val, present := resp.Data[reqField]; !present { + return "" + } else if strVal, castOk := val.(string); !castOk || strVal == "" { + return "" + } else { + return strVal + } +} + +func requireStrRespField(resp *api.Secret, reqField string) (string, error) { + if resp == nil || resp.Data == nil { + return "", fmt.Errorf("nil response received, %s field unavailable", reqField) + } + if val, present := resp.Data[reqField]; !present { + return "", fmt.Errorf("response did not contain field: %s", reqField) + } else if strVal, castOk := val.(string); !castOk || strVal == "" { + return "", fmt.Errorf("field %s value was blank or not a string: %v", reqField, val) + } else { + return strVal, nil + } +} + +func requireStrListRespField(resp *api.Secret, reqField string) ([]string, error) { + if resp == nil || resp.Data == nil { + return nil, fmt.Errorf("nil response received, %s field unavailable", reqField) + } + if val, present := resp.Data[reqField]; !present { + return nil, fmt.Errorf("response did not contain field: %s", reqField) + } else { + return healthcheck.StringList(val) + } } func (c *PKIVerifySignCommand) outputResults(results map[string]bool, potentialParent, potentialChild string) error { From 105881d5e1a2a37a178e075560c9918827e41994 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Tue, 14 Feb 2023 18:16:38 -0500 Subject: [PATCH 011/231] backport of commit 93f7b4f33c772c11f3bf43b1f4af0bc1e8906082 (#19189) Co-authored-by: Jordan Reimer --- ui/app/adapters/kubernetes/role.js | 2 +- ui/app/serializers/kubernetes/config.js | 6 ++++ .../kubernetes/page/configure-test.js | 32 ++++++++++++++++++- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/ui/app/adapters/kubernetes/role.js b/ui/app/adapters/kubernetes/role.js index 1bb268d2e201..301ae6419888 100644 --- a/ui/app/adapters/kubernetes/role.js +++ b/ui/app/adapters/kubernetes/role.js @@ -32,7 +32,7 @@ export default class KubernetesRoleAdapter extends NamedPathAdapter { } generateCredentials(backend, data) { const generateCredentialsUrl = `${this.buildURL()}/${encodePath(backend)}/creds/${data.role}`; - + delete data.role; return this.ajax(generateCredentialsUrl, 'POST', { data }).then((response) => { const { lease_id, lease_duration, data } = response; diff --git a/ui/app/serializers/kubernetes/config.js b/ui/app/serializers/kubernetes/config.js index fccf63dce6ff..3de98f8a2f61 100644 --- a/ui/app/serializers/kubernetes/config.js +++ b/ui/app/serializers/kubernetes/config.js @@ -7,6 +7,12 @@ export default class KubernetesConfigSerializer extends ApplicationSerializer { const json = super.serialize(...arguments); // remove backend value from payload delete json.backend; + // ensure that values from a previous manual configuration are unset + if (json.disable_local_ca_jwt === false) { + json.kubernetes_ca_cert = null; + json.kubernetes_host = null; + json.service_account_jwt = null; + } return json; } } diff --git a/ui/tests/integration/components/kubernetes/page/configure-test.js b/ui/tests/integration/components/kubernetes/page/configure-test.js index d3bc1e824af0..439907e7f127 100644 --- a/ui/tests/integration/components/kubernetes/page/configure-test.js +++ b/ui/tests/integration/components/kubernetes/page/configure-test.js @@ -32,6 +32,12 @@ module('Integration | Component | kubernetes | Page::Configure', function (hooks { label: 'kubernetes', route: 'overview' }, { label: 'configure' }, ]; + this.expectedInferred = { + disable_local_ca_jwt: false, + kubernetes_ca_cert: null, + kubernetes_host: null, + service_account_jwt: null, + }; }); test('it should display proper options when toggling radio cards', async function (assert) { @@ -223,7 +229,7 @@ module('Integration | Component | kubernetes | Page::Configure', function (hooks this.server.get('/:path/check', () => new Response(204, {})); this.server.post('/:path/config', (schema, req) => { const json = JSON.parse(req.requestBody); - assert.deepEqual(json, { disable_local_ca_jwt: false }, 'Values are passed to create endpoint'); + assert.deepEqual(json, this.expectedInferred, 'Values are passed to create endpoint'); return new Response(204, {}); }); @@ -241,4 +247,28 @@ module('Integration | Component | kubernetes | Page::Configure', function (hooks 'Transitions to configuration route on save success' ); }); + + test('it should unset manual config values when saving local cluster option', async function (assert) { + assert.expect(1); + + this.server.get('/:path/check', () => new Response(204, {})); + this.server.post('/:path/config', (schema, req) => { + const json = JSON.parse(req.requestBody); + assert.deepEqual(json, this.expectedInferred, 'Manual config values are unset in server payload'); + return new Response(204, {}); + }); + + await render(hbs``, { + owner: this.engine, + }); + + await click('[data-test-radio-card="manual"]'); + await fillIn('[data-test-input="kubernetesHost"]', this.existingConfig.kubernetes_host); + await fillIn('[data-test-input="serviceAccountJwt"]', this.existingConfig.service_account_jwt); + await fillIn('[data-test-input="kubernetesCaCert"]', this.existingConfig.kubernetes_ca_cert); + + await click('[data-test-radio-card="local"]'); + await click('[data-test-config] button'); + await click('[data-test-config-save]'); + }); }); From 882038831777aab4f3e570132f017ce7beec7231 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Wed, 15 Feb 2023 10:19:33 -0500 Subject: [PATCH 012/231] backport of commit 8fd34ca479dcdadc4dbfe7d8b54cec404d8a8c4e (#19197) Co-authored-by: Steven Clark --- changelog/19196.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelog/19196.txt diff --git a/changelog/19196.txt b/changelog/19196.txt new file mode 100644 index 000000000000..aab2638ceac7 --- /dev/null +++ b/changelog/19196.txt @@ -0,0 +1,5 @@ +```release-note:feature +**PKI Cross-Cluster Revocations**: Revocation information can now be +synchronized across primary and performance replica clusters offering +a unified CRL/OCSP view of revocations across cluster boundaries. +``` From a41a24bf252675c211238818c44466cd79959219 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Wed, 15 Feb 2023 14:16:14 -0500 Subject: [PATCH 013/231] backport of commit 7143c5639b7618d2244ea871f0ff1c52371371cc (#19202) Co-authored-by: Austin Gebauer <34121980+austingebauer@users.noreply.github.com> --- changelog/19018.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog/19018.txt b/changelog/19018.txt index 45ea82785e6c..bd79dbd15911 100644 --- a/changelog/19018.txt +++ b/changelog/19018.txt @@ -1,5 +1,5 @@ -```release-note:improvement -secrets/gcp: added support for impersonated accounts +```release-note:feature +**GCP Secrets Impersonated Account Support**: Add support for GCP service account impersonation, allowing callers to generate a GCP access token without requiring Vault to store or retrieve a GCP service account key for each role. ``` ```release-note:bug From 48a7feb891631e77d07eb7e65c7732a9b0d1a22e Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Wed, 15 Feb 2023 17:54:44 -0500 Subject: [PATCH 014/231] backport of commit db822c05d417025765f9fd66f8d6ab09c2eb893e (#19204) Co-authored-by: Christopher Swenson --- vault/eventbus/bus.go | 64 ++++++++++++++++++-- vault/eventbus/bus_test.go | 117 +++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 6 deletions(-) diff --git a/vault/eventbus/bus.go b/vault/eventbus/bus.go index 60db2c939b4b..cf789ef26d9d 100644 --- a/vault/eventbus/bus.go +++ b/vault/eventbus/bus.go @@ -5,9 +5,11 @@ import ( "errors" "net/url" "strings" + "sync" "sync/atomic" "time" + "github.com/armon/go-metrics" "github.com/hashicorp/eventlogger" "github.com/hashicorp/eventlogger/formatter_filters/cloudevents" "github.com/hashicorp/go-hclog" @@ -17,9 +19,13 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" ) -var ErrNotStarted = errors.New("event broker has not been started") +const defaultTimeout = 60 * time.Second -var cloudEventsFormatterFilter *cloudevents.FormatterFilter +var ( + ErrNotStarted = errors.New("event broker has not been started") + cloudEventsFormatterFilter *cloudevents.FormatterFilter + subscriptions atomic.Int64 // keeps track of event subscription count in all event buses +) // EventBus contains the main logic of running an event broker for Vault. // Start() must be called before the EventBus will accept events for sending. @@ -28,6 +34,7 @@ type EventBus struct { broker *eventlogger.Broker started atomic.Bool formatterNodeID eventlogger.NodeID + timeout time.Duration } type pluginEventBus struct { @@ -42,6 +49,13 @@ type asyncChanNode struct { ch chan *logical.EventReceived namespace *namespace.Namespace logger hclog.Logger + + // used to close the connection + closeOnce sync.Once + cancelFunc context.CancelFunc + pipelineID eventlogger.PipelineID + eventType eventlogger.EventType + broker *eventlogger.Broker } var ( @@ -79,6 +93,10 @@ func (bus *EventBus) SendInternal(ctx context.Context, ns *namespace.Namespace, Timestamp: timestamppb.New(time.Now()), } bus.logger.Info("Sending event", "event", eventReceived) + + // We can't easily know when the Send is complete, so we can't call the cancel function. + // But, it is called automatically after bus.timeout, so there won't be any leak as long as bus.timeout is not too long. + ctx, _ = context.WithTimeout(ctx, bus.timeout) _, err := bus.broker.Send(ctx, eventlogger.EventType(eventType), eventReceived) if err != nil { // if no listeners for this event type are registered, that's okay, the event @@ -142,6 +160,7 @@ func NewEventBus(logger hclog.Logger) (*EventBus, error) { logger: logger, broker: broker, formatterNodeID: formatterNodeID, + timeout: defaultTimeout, }, nil } @@ -178,7 +197,18 @@ func (bus *EventBus) Subscribe(ctx context.Context, ns *namespace.Namespace, eve defer cancel() return nil, nil, err } - return asyncNode.ch, cancel, nil + addSubscriptions(1) + // add info needed to cancel the subscription + asyncNode.pipelineID = eventlogger.PipelineID(pipelineID) + asyncNode.eventType = eventlogger.EventType(eventType) + asyncNode.cancelFunc = cancel + return asyncNode.ch, asyncNode.Close, nil +} + +// SetSendTimeout sets the timeout of sending events. If the events are not accepted by the +// underlying channel before this timeout, then the channel closed. +func (bus *EventBus) SetSendTimeout(timeout time.Duration) { + bus.timeout = timeout } func newAsyncNode(ctx context.Context, namespace *namespace.Namespace, logger hclog.Logger) *asyncChanNode { @@ -190,8 +220,21 @@ func newAsyncNode(ctx context.Context, namespace *namespace.Namespace, logger hc } } +// Close tells the bus to stop sending us events. +func (node *asyncChanNode) Close() { + node.closeOnce.Do(func() { + defer node.cancelFunc() + if node.broker != nil { + err := node.broker.RemovePipeline(node.eventType, node.pipelineID) + if err != nil { + node.logger.Warn("Error removing pipeline for closing node", "error", err) + } + } + addSubscriptions(-1) + }) +} + func (node *asyncChanNode) Process(ctx context.Context, e *eventlogger.Event) (*eventlogger.Event, error) { - // TODO: add timeout on sending to node.ch // sends to the channel async in another goroutine go func() { eventRecv := e.Payload.(*logical.EventReceived) @@ -200,12 +243,17 @@ func (node *asyncChanNode) Process(ctx context.Context, e *eventlogger.Event) (* if eventRecv.Namespace != node.namespace.Path { return } + var timeout bool select { case node.ch <- eventRecv: case <-ctx.Done(): - return + timeout = errors.Is(ctx.Err(), context.DeadlineExceeded) case <-node.ctx.Done(): - return + timeout = errors.Is(node.ctx.Err(), context.DeadlineExceeded) + } + if timeout { + node.logger.Info("Subscriber took too long to process event, closing", "ID", eventRecv.Event.ID()) + node.Close() } }() return e, nil @@ -218,3 +266,7 @@ func (node *asyncChanNode) Reopen() error { func (node *asyncChanNode) Type() eventlogger.NodeType { return eventlogger.NodeTypeSink } + +func addSubscriptions(delta int64) { + metrics.SetGauge([]string{"events", "subscriptions"}, float32(subscriptions.Add(delta))) +} diff --git a/vault/eventbus/bus_test.go b/vault/eventbus/bus_test.go index 8275efcaf16b..94b3dd2c5ecb 100644 --- a/vault/eventbus/bus_test.go +++ b/vault/eventbus/bus_test.go @@ -2,6 +2,8 @@ package eventbus import ( "context" + "fmt" + "sync/atomic" "testing" "time" @@ -9,6 +11,7 @@ import ( "github.com/hashicorp/vault/sdk/logical" ) +// TestBusBasics tests that basic event sending and subscribing function. func TestBusBasics(t *testing.T) { bus, err := NewEventBus(nil) if err != nil { @@ -62,6 +65,7 @@ func TestBusBasics(t *testing.T) { } } +// TestNamespaceFiltering verifies that events for other namespaces are filtered out by the bus. func TestNamespaceFiltering(t *testing.T) { bus, err := NewEventBus(nil) if err != nil { @@ -121,6 +125,7 @@ func TestNamespaceFiltering(t *testing.T) { } } +// TestBus2Subscriptions verifies that events of different types are successfully routed to the correct subscribers. func TestBus2Subscriptions(t *testing.T) { bus, err := NewEventBus(nil) if err != nil { @@ -180,3 +185,115 @@ func TestBus2Subscriptions(t *testing.T) { t.Error("Timeout waiting for event2") } } + +// TestBusSubscriptionsCancel verifies that canceled subscriptions are cleaned up. +func TestBusSubscriptionsCancel(t *testing.T) { + testCases := []struct { + cancel bool + }{ + {cancel: true}, + {cancel: false}, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("cancel=%v", tc.cancel), func(t *testing.T) { + subscriptions.Store(0) + bus, err := NewEventBus(nil) + if err != nil { + t.Fatal(err) + } + ctx := context.Background() + if !tc.cancel { + // set the timeout very short to make the test faster if we aren't canceling explicitly + bus.SetSendTimeout(100 * time.Millisecond) + } + bus.Start() + + // create and stop a bunch of subscriptions + const create = 100 + const stop = 50 + + eventType := logical.EventType("someType") + + var channels []<-chan *logical.EventReceived + var cancels []context.CancelFunc + stopped := atomic.Int32{} + + received := atomic.Int32{} + + for i := 0; i < create; i++ { + ch, cancelFunc, err := bus.Subscribe(ctx, namespace.RootNamespace, eventType) + if err != nil { + t.Fatal(err) + } + t.Cleanup(cancelFunc) + channels = append(channels, ch) + cancels = append(cancels, cancelFunc) + + go func(i int32) { + <-ch // always receive one message + received.Add(1) + // continue receiving messages as long as are not stopped + for i < int32(stop) { + <-ch + received.Add(1) + } + if tc.cancel { + cancelFunc() // stop explicitly to unsubscribe + } + stopped.Add(1) + }(int32(i)) + } + + // check that all channels receive a message + event, err := logical.NewEvent() + if err != nil { + t.Fatal(err) + } + err = bus.SendInternal(ctx, namespace.RootNamespace, nil, eventType, event) + if err != nil { + t.Error(err) + } + waitFor(t, 1*time.Second, func() bool { return received.Load() == int32(create) }) + waitFor(t, 1*time.Second, func() bool { return stopped.Load() == int32(stop) }) + + // send another message, but half should stop receiving + event, err = logical.NewEvent() + if err != nil { + t.Fatal(err) + } + err = bus.SendInternal(ctx, namespace.RootNamespace, nil, eventType, event) + if err != nil { + t.Error(err) + } + waitFor(t, 1*time.Second, func() bool { return received.Load() == int32(create*2-stop) }) + // the sends should time out and the subscriptions should drop when cancelFunc is called or the context cancels + waitFor(t, 1*time.Second, func() bool { return subscriptions.Load() == int64(create-stop) }) + }) + } +} + +// waitFor waits for a condition to be true, up to the maximum timeout. +// It waits with a capped exponential backoff starting at 1ms. +// It is guaranteed to try f() at least once. +func waitFor(t *testing.T, maxWait time.Duration, f func() bool) { + t.Helper() + start := time.Now() + + if f() { + return + } + sleepAmount := 1 * time.Millisecond + for time.Now().Sub(start) <= maxWait { + left := time.Now().Sub(start) + sleepAmount = sleepAmount * 2 + if sleepAmount > left { + sleepAmount = left + } + time.Sleep(sleepAmount) + if f() { + return + } + } + t.Error("Timeout waiting for condition") +} From 0490c4c5080a844c91e392932289ddd0e3e2c098 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Thu, 16 Feb 2023 11:23:11 -0500 Subject: [PATCH 015/231] backport of commit ba832de1f1b7b01755909351691e1a53ffb92919 (#19212) Co-authored-by: Peter Wilson --- website/content/docs/agent/index.mdx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/website/content/docs/agent/index.mdx b/website/content/docs/agent/index.mdx index 755d888a15a0..b5c2dd695f88 100644 --- a/website/content/docs/agent/index.mdx +++ b/website/content/docs/agent/index.mdx @@ -155,22 +155,18 @@ See the [caching](/vault/docs/agent/caching#api) page for details on the cache A file path of `/var/log/vault-agent-{timestamp}.log`. `log-file` can be combined with [`-log-rotate-bytes`](#_log_rotate_bytes) and [`-log-rotate-duration`](#_log_rotate_duration) for a fine-grained log rotation experience. - This can also be specified via the `VAULT_LOG_FILE` environment variable. - `-log-rotate-bytes` ((#\_log_rotate_bytes)) - to specify the number of bytes that should be written to a log before it needs to be rotated. Unless specified, there is no limit to the number of bytes that can be written to a log file. - This can also be specified via the `VAULT_LOG_ROTATE_BYTES` environment variable. - `-log-rotate-duration` ((#\_log_rotate_duration)) - to specify the maximum duration a log should be written to before it needs to be rotated. Must be a duration value such as 30s. Defaults to 24h. - This can also be specified via the `VAULT_LOG_ROTATE_DURATION` environment variable. - `-log-rotate-max-files` ((#\_log_rotate_max_files)) - to specify the maximum number of older log file archives to keep. Defaults to `0` (no files are ever deleted). Set to `-1` to discard old log files when a new one is created. - This can also be specified via the `VAULT_LOG_ROTATE_MAX_FILES` environment variable. ### Configuration File Options From 6c84c14e1a7b748b7cb1a6d250073c23c6024e19 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Thu, 16 Feb 2023 13:40:57 -0500 Subject: [PATCH 016/231] backport of commit 6946556e96c418fe2b485faf3c4b4f2a25a14446 (#19225) Co-authored-by: Angel Garbarino --- changelog/19190.txt | 3 +++ ui/app/templates/components/database-role-edit.hbs | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 changelog/19190.txt diff --git a/changelog/19190.txt b/changelog/19190.txt new file mode 100644 index 000000000000..480006b1ebc8 --- /dev/null +++ b/changelog/19190.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: show Get credentials button for static roles detail page when a user has the proper permissions. +``` diff --git a/ui/app/templates/components/database-role-edit.hbs b/ui/app/templates/components/database-role-edit.hbs index 5528c6f6ea95..f6b1e85a5fad 100644 --- a/ui/app/templates/components/database-role-edit.hbs +++ b/ui/app/templates/components/database-role-edit.hbs @@ -36,12 +36,17 @@ Rotate credentials {{/if}} - {{#if @model.canGenerateCredentials}} + {{#if + (or + (and (eq @model.type "static") @model.canGetCredentials) + (and (eq @model.type "dynamic") @model.canGenerateCredentials) + ) + }} From 9a3c8f4a3736944063a59a9d280e824d0a3992ef Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Thu, 16 Feb 2023 18:00:38 -0500 Subject: [PATCH 017/231] backport of commit b3bc6542497cdaada77c45ca10a387a1ffcc4e6c (#19236) Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com> --- website/content/api-docs/system/auth.mdx | 8 ++++---- website/content/api-docs/system/mounts.mdx | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/website/content/api-docs/system/auth.mdx b/website/content/api-docs/system/auth.mdx index a4381088d89f..64e71efc2f25 100644 --- a/website/content/api-docs/system/auth.mdx +++ b/website/content/api-docs/system/auth.mdx @@ -127,10 +127,10 @@ For example, enable the "foo" auth method will make it accessible at in the UI-specific listing endpoint. Valid values are `"unauth"` or `"hidden"`, with the default `""` being equivalent to `"hidden"`. - - `passthrough_request_headers` `(array: [])` - List of headers to whitelist + - `passthrough_request_headers` `(array: [])` - List of headers to allow and pass from the request to the plugin. - - `allowed_response_headers` `(array: [])` - List of headers to whitelist, + - `allowed_response_headers` `(array: [])` - List of headers to allow, allowing a plugin to include them in the response. - `plugin_version` `(string: "")` – Specifies the semantic version of the plugin @@ -325,10 +325,10 @@ can be achieved without `sudo` via `sys/mounts/auth/[auth-path]/tune`._ in the UI-specific listing endpoint. Valid values are `"unauth"` or `"hidden"`, with the default `""` being equivalent to `"hidden"`. -- `passthrough_request_headers` `(array: [])` - List of headers to whitelist +- `passthrough_request_headers` `(array: [])` - List of headers to allow and pass from the request to the plugin. -- `allowed_response_headers` `(array: [])` - List of headers to whitelist, +- `allowed_response_headers` `(array: [])` - List of headers to allow, allowing a plugin to include them in the response. - `token_type` `(string: "")` – Specifies the type of tokens that should be diff --git a/website/content/api-docs/system/mounts.mdx b/website/content/api-docs/system/mounts.mdx index 284f59cc6ca8..55d64f9e5835 100644 --- a/website/content/api-docs/system/mounts.mdx +++ b/website/content/api-docs/system/mounts.mdx @@ -160,10 +160,10 @@ This endpoint enables a new secrets engine at the given path. in the UI-specific listing endpoint. Valid values are `"unauth"` or `"hidden"`. If not set, behaves like `"hidden"`. - - `passthrough_request_headers` `(array: [])` - List of headers to whitelist + - `passthrough_request_headers` `(array: [])` - List of headers to allow and pass from the request to the plugin. - - `allowed_response_headers` `(array: [])` - List of headers to whitelist, + - `allowed_response_headers` `(array: [])` - List of headers to allow, allowing a plugin to include them in the response. - `plugin_version` `(string: "")` – Specifies the semantic version of the plugin @@ -367,10 +367,10 @@ This endpoint tunes configuration parameters for a given mount point. the UI-specific listing endpoint. Valid values are `"unauth"` or `"hidden"`. If not set, behaves like `"hidden"`. -- `passthrough_request_headers` `(array: [])` - List of headers to whitelist +- `passthrough_request_headers` `(array: [])` - List of headers to allow and pass from the request to the plugin. -- `allowed_response_headers` `(array: [])` - List of headers to whitelist, +- `allowed_response_headers` `(array: [])` - List of headers to allow, allowing a plugin to include them in the response. - `allowed_managed_keys` `(array: [])` - List of managed key registry entry names From eb2d03ee8f2e698b54891220f21ad3be8c971b1a Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Thu, 16 Feb 2023 18:30:16 -0500 Subject: [PATCH 018/231] backport of commit 8f36d0daa4cb9c35a2f6108af2e3ef9bf2d8d307 (#19222) Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com> --- changelog/19216.txt | 3 +++ ui/app/models/mount-config.js | 7 +++++++ ui/app/models/secret-engine.js | 14 ++++++++------ .../vault/cluster/secrets/backend/list.hbs | 9 ++++----- ui/lib/core/addon/helpers/options-for-backend.js | 7 ++++--- .../settings/mount-secret-backend-test.js | 8 ++++---- ui/tests/unit/models/secret-engine-test.js | 10 +++++----- 7 files changed, 35 insertions(+), 23 deletions(-) create mode 100644 changelog/19216.txt diff --git a/changelog/19216.txt b/changelog/19216.txt new file mode 100644 index 000000000000..e03e866e08b4 --- /dev/null +++ b/changelog/19216.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: adds allowed_response_headers as param for secret engine mount config +``` diff --git a/ui/app/models/mount-config.js b/ui/app/models/mount-config.js index 7ef0a9567d42..edc624c2fae4 100644 --- a/ui/app/models/mount-config.js +++ b/ui/app/models/mount-config.js @@ -42,6 +42,13 @@ export default class MountConfigModel extends Model { }) passthroughRequestHeaders; + @attr({ + label: 'Allowed response headers', + helpText: 'Headers to allow, allowing a plugin to include them in the response.', + editType: 'stringArray', + }) + allowedResponseHeaders; + @attr('string', { label: 'Token Type', helpText: diff --git a/ui/app/models/secret-engine.js b/ui/app/models/secret-engine.js index 640296c8f873..b0251c46a32b 100644 --- a/ui/app/models/secret-engine.js +++ b/ui/app/models/secret-engine.js @@ -83,7 +83,9 @@ export default SecretEngineModel.extend({ const fields = ['type', 'path', 'description', 'accessor', 'local', 'sealWrap']; // no ttl options for keymgmt const ttl = type !== 'keymgmt' ? 'defaultLeaseTtl,maxLeaseTtl,' : ''; - fields.push(`config.{${ttl}auditNonHmacRequestKeys,auditNonHmacResponseKeys,passthroughRequestHeaders}`); + fields.push( + `config.{${ttl}auditNonHmacRequestKeys,auditNonHmacResponseKeys,passthroughRequestHeaders,allowedResponseHeaders}` + ); if (type === 'kv' || type === 'generic') { fields.push('version'); } @@ -105,14 +107,14 @@ export default SecretEngineModel.extend({ optionFields = [ 'version', ...CORE_OPTIONS, - `config.{defaultLeaseTtl,maxLeaseTtl,auditNonHmacRequestKeys,auditNonHmacResponseKeys,passthroughRequestHeaders}`, + `config.{defaultLeaseTtl,maxLeaseTtl,auditNonHmacRequestKeys,auditNonHmacResponseKeys,passthroughRequestHeaders,allowedResponseHeaders}`, ]; break; case 'generic': optionFields = [ 'version', ...CORE_OPTIONS, - `config.{defaultLeaseTtl,maxLeaseTtl,auditNonHmacRequestKeys,auditNonHmacResponseKeys,passthroughRequestHeaders}`, + `config.{defaultLeaseTtl,maxLeaseTtl,auditNonHmacRequestKeys,auditNonHmacResponseKeys,passthroughRequestHeaders,allowedResponseHeaders}`, ]; break; case 'database': @@ -120,21 +122,21 @@ export default SecretEngineModel.extend({ defaultFields = ['path', 'config.{defaultLeaseTtl}', 'config.{maxLeaseTtl}']; optionFields = [ ...CORE_OPTIONS, - 'config.{auditNonHmacRequestKeys,auditNonHmacResponseKeys,passthroughRequestHeaders}', + 'config.{auditNonHmacRequestKeys,auditNonHmacResponseKeys,passthroughRequestHeaders,allowedResponseHeaders}', ]; break; case 'keymgmt': // no ttl options for keymgmt optionFields = [ ...CORE_OPTIONS, - 'config.{auditNonHmacRequestKeys,auditNonHmacResponseKeys,passthroughRequestHeaders}', + 'config.{auditNonHmacRequestKeys,auditNonHmacResponseKeys,passthroughRequestHeaders,allowedResponseHeaders}', ]; break; default: defaultFields = ['path']; optionFields = [ ...CORE_OPTIONS, - `config.{defaultLeaseTtl,maxLeaseTtl,auditNonHmacRequestKeys,auditNonHmacResponseKeys,passthroughRequestHeaders}`, + `config.{defaultLeaseTtl,maxLeaseTtl,auditNonHmacRequestKeys,auditNonHmacResponseKeys,passthroughRequestHeaders,allowedResponseHeaders}`, ]; break; } diff --git a/ui/app/templates/vault/cluster/secrets/backend/list.hbs b/ui/app/templates/vault/cluster/secrets/backend/list.hbs index 90768826e79f..e4e314f94da8 100644 --- a/ui/app/templates/vault/cluster/secrets/backend/list.hbs +++ b/ui/app/templates/vault/cluster/secrets/backend/list.hbs @@ -129,10 +129,7 @@ {{else}} {{#if (eq this.baseKey.id "")}} {{#if (and options.firstStep (not this.tab))}} - + Date: Fri, 17 Feb 2023 07:39:30 -0500 Subject: [PATCH 019/231] backport of commit 184939e90a0419fb7a130adb96ec36a09b5e8ecd (#19234) Co-authored-by: Tom Proctor --- http/events.go | 23 +++++----- vault/eventbus/bus.go | 78 +++++++++++++++++++++++----------- vault/eventbus/bus_test.go | 86 +++++++++++++++++++++++++++++++++++--- vault/events_test.go | 2 +- 4 files changed, 147 insertions(+), 42 deletions(-) diff --git a/http/events.go b/http/events.go index 22dfe35630db..e957cd7a3798 100644 --- a/http/events.go +++ b/http/events.go @@ -20,13 +20,13 @@ import ( ) type eventSubscribeArgs struct { - ctx context.Context - logger hclog.Logger - events *eventbus.EventBus - ns *namespace.Namespace - eventType logical.EventType - conn *websocket.Conn - json bool + ctx context.Context + logger hclog.Logger + events *eventbus.EventBus + ns *namespace.Namespace + pattern string + conn *websocket.Conn + json bool } // handleEventsSubscribeWebsocket runs forever, returning a websocket error code and reason @@ -34,7 +34,7 @@ type eventSubscribeArgs struct { func handleEventsSubscribeWebsocket(args eventSubscribeArgs) (websocket.StatusCode, string, error) { ctx := args.ctx logger := args.logger - ch, cancel, err := args.events.Subscribe(ctx, args.ns, args.eventType) + ch, cancel, err := args.events.Subscribe(ctx, args.ns, args.pattern) if err != nil { logger.Info("Error subscribing", "error", err) return websocket.StatusUnsupportedData, "Error subscribing", nil @@ -97,12 +97,11 @@ func handleEventsSubscribe(core *vault.Core, req *logical.Request) http.Handler if ns.ID != namespace.RootNamespaceID { prefix = fmt.Sprintf("/v1/%ssys/events/subscribe/", ns.Path) } - eventTypeStr := strings.TrimSpace(strings.TrimPrefix(r.URL.Path, prefix)) - if eventTypeStr == "" { + pattern := strings.TrimSpace(strings.TrimPrefix(r.URL.Path, prefix)) + if pattern == "" { respondError(w, http.StatusBadRequest, fmt.Errorf("did not specify eventType to subscribe to")) return } - eventType := logical.EventType(eventTypeStr) json := false jsonRaw := r.URL.Query().Get("json") @@ -135,7 +134,7 @@ func handleEventsSubscribe(core *vault.Core, req *logical.Request) http.Handler } }() - closeStatus, closeReason, err := handleEventsSubscribeWebsocket(eventSubscribeArgs{ctx, logger, core.Events(), ns, eventType, conn, json}) + closeStatus, closeReason, err := handleEventsSubscribeWebsocket(eventSubscribeArgs{ctx, logger, core.Events(), ns, pattern, conn, json}) if err != nil { closeStatus = websocket.CloseStatus(err) if closeStatus == -1 { diff --git a/vault/eventbus/bus.go b/vault/eventbus/bus.go index cf789ef26d9d..cc954435fc93 100644 --- a/vault/eventbus/bus.go +++ b/vault/eventbus/bus.go @@ -16,10 +16,17 @@ import ( "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/sdk/logical" + "github.com/ryanuber/go-glob" "google.golang.org/protobuf/types/known/timestamppb" ) -const defaultTimeout = 60 * time.Second +const ( + // eventTypeAll is purely internal to the event bus. We use it to send all + // events down one big firehose, and pipelines define their own filtering + // based on what each subscriber is interested in. + eventTypeAll = "*" + defaultTimeout = 60 * time.Second +) var ( ErrNotStarted = errors.New("event broker has not been started") @@ -45,16 +52,14 @@ type pluginEventBus struct { type asyncChanNode struct { // TODO: add bounded deque buffer of *EventReceived - ctx context.Context - ch chan *logical.EventReceived - namespace *namespace.Namespace - logger hclog.Logger + ctx context.Context + ch chan *logical.EventReceived + logger hclog.Logger // used to close the connection closeOnce sync.Once cancelFunc context.CancelFunc pipelineID eventlogger.PipelineID - eventType eventlogger.EventType broker *eventlogger.Broker } @@ -97,7 +102,7 @@ func (bus *EventBus) SendInternal(ctx context.Context, ns *namespace.Namespace, // We can't easily know when the Send is complete, so we can't call the cancel function. // But, it is called automatically after bus.timeout, so there won't be any leak as long as bus.timeout is not too long. ctx, _ = context.WithTimeout(ctx, bus.timeout) - _, err := bus.broker.Send(ctx, eventlogger.EventType(eventType), eventReceived) + _, err := bus.broker.Send(ctx, eventTypeAll, eventReceived) if err != nil { // if no listeners for this event type are registered, that's okay, the event // will just not be sent anywhere @@ -164,32 +169,42 @@ func NewEventBus(logger hclog.Logger) (*EventBus, error) { }, nil } -func (bus *EventBus) Subscribe(ctx context.Context, ns *namespace.Namespace, eventType logical.EventType) (<-chan *logical.EventReceived, context.CancelFunc, error) { +func (bus *EventBus) Subscribe(ctx context.Context, ns *namespace.Namespace, pattern string) (<-chan *logical.EventReceived, context.CancelFunc, error) { // subscriptions are still stored even if the bus has not been started pipelineID, err := uuid.GenerateUUID() if err != nil { return nil, nil, err } - nodeID, err := uuid.GenerateUUID() + filterNodeID, err := uuid.GenerateUUID() + if err != nil { + return nil, nil, err + } + + filterNode := newFilterNode(ns, pattern) + err = bus.broker.RegisterNode(eventlogger.NodeID(filterNodeID), filterNode) + if err != nil { + return nil, nil, err + } + + sinkNodeID, err := uuid.GenerateUUID() if err != nil { return nil, nil, err } - // TODO: should we have just one node per namespace, and handle all the routing ourselves? ctx, cancel := context.WithCancel(ctx) asyncNode := newAsyncNode(ctx, ns, bus.logger) - err = bus.broker.RegisterNode(eventlogger.NodeID(nodeID), asyncNode) + err = bus.broker.RegisterNode(eventlogger.NodeID(sinkNodeID), asyncNode) if err != nil { defer cancel() return nil, nil, err } - nodes := []eventlogger.NodeID{bus.formatterNodeID, eventlogger.NodeID(nodeID)} + nodes := []eventlogger.NodeID{eventlogger.NodeID(filterNodeID), bus.formatterNodeID, eventlogger.NodeID(sinkNodeID)} pipeline := eventlogger.Pipeline{ PipelineID: eventlogger.PipelineID(pipelineID), - EventType: eventlogger.EventType(eventType), + EventType: eventTypeAll, NodeIDs: nodes, } err = bus.broker.RegisterPipeline(pipeline) @@ -197,10 +212,10 @@ func (bus *EventBus) Subscribe(ctx context.Context, ns *namespace.Namespace, eve defer cancel() return nil, nil, err } + addSubscriptions(1) // add info needed to cancel the subscription asyncNode.pipelineID = eventlogger.PipelineID(pipelineID) - asyncNode.eventType = eventlogger.EventType(eventType) asyncNode.cancelFunc = cancel return asyncNode.ch, asyncNode.Close, nil } @@ -211,12 +226,32 @@ func (bus *EventBus) SetSendTimeout(timeout time.Duration) { bus.timeout = timeout } +func newFilterNode(ns *namespace.Namespace, pattern string) *eventlogger.Filter { + return &eventlogger.Filter{ + Predicate: func(e *eventlogger.Event) (bool, error) { + eventRecv := e.Payload.(*logical.EventReceived) + + // Drop if event is not in our namespace. + // TODO: add wildcard/child namespace processing here in some cases? + if eventRecv.Namespace != ns.Path { + return false, nil + } + + // Filter for correct event type, including wildcards. + if !glob.Glob(pattern, eventRecv.EventType) { + return false, nil + } + + return true, nil + }, + } +} + func newAsyncNode(ctx context.Context, namespace *namespace.Namespace, logger hclog.Logger) *asyncChanNode { return &asyncChanNode{ - ctx: ctx, - ch: make(chan *logical.EventReceived), - namespace: namespace, - logger: logger, + ctx: ctx, + ch: make(chan *logical.EventReceived), + logger: logger, } } @@ -225,7 +260,7 @@ func (node *asyncChanNode) Close() { node.closeOnce.Do(func() { defer node.cancelFunc() if node.broker != nil { - err := node.broker.RemovePipeline(node.eventType, node.pipelineID) + err := node.broker.RemovePipeline(eventTypeAll, node.pipelineID) if err != nil { node.logger.Warn("Error removing pipeline for closing node", "error", err) } @@ -238,11 +273,6 @@ func (node *asyncChanNode) Process(ctx context.Context, e *eventlogger.Event) (* // sends to the channel async in another goroutine go func() { eventRecv := e.Payload.(*logical.EventReceived) - // drop if event is not in our namespace - // TODO: add wildcard processing here in some cases? - if eventRecv.Namespace != node.namespace.Path { - return - } var timeout bool select { case node.ch <- eventRecv: diff --git a/vault/eventbus/bus_test.go b/vault/eventbus/bus_test.go index 94b3dd2c5ecb..cf7d26e318eb 100644 --- a/vault/eventbus/bus_test.go +++ b/vault/eventbus/bus_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/hashicorp/go-secure-stdlib/strutil" "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/sdk/logical" ) @@ -38,7 +39,7 @@ func TestBusBasics(t *testing.T) { t.Errorf("Expected no error sending: %v", err) } - ch, cancel, err := bus.Subscribe(ctx, namespace.RootNamespace, eventType) + ch, cancel, err := bus.Subscribe(ctx, namespace.RootNamespace, string(eventType)) if err != nil { t.Fatal(err) } @@ -81,7 +82,7 @@ func TestNamespaceFiltering(t *testing.T) { t.Fatal(err) } - ch, cancel, err := bus.Subscribe(ctx, namespace.RootNamespace, eventType) + ch, cancel, err := bus.Subscribe(ctx, namespace.RootNamespace, string(eventType)) if err != nil { t.Fatal(err) } @@ -137,13 +138,13 @@ func TestBus2Subscriptions(t *testing.T) { eventType2 := logical.EventType("someType2") bus.Start() - ch1, cancel1, err := bus.Subscribe(ctx, namespace.RootNamespace, eventType1) + ch1, cancel1, err := bus.Subscribe(ctx, namespace.RootNamespace, string(eventType1)) if err != nil { t.Fatal(err) } defer cancel1() - ch2, cancel2, err := bus.Subscribe(ctx, namespace.RootNamespace, eventType2) + ch2, cancel2, err := bus.Subscribe(ctx, namespace.RootNamespace, string(eventType2)) if err != nil { t.Fatal(err) } @@ -222,7 +223,7 @@ func TestBusSubscriptionsCancel(t *testing.T) { received := atomic.Int32{} for i := 0; i < create; i++ { - ch, cancelFunc, err := bus.Subscribe(ctx, namespace.RootNamespace, eventType) + ch, cancelFunc, err := bus.Subscribe(ctx, namespace.RootNamespace, string(eventType)) if err != nil { t.Fatal(err) } @@ -297,3 +298,78 @@ func waitFor(t *testing.T, maxWait time.Duration, f func() bool) { } t.Error("Timeout waiting for condition") } + +// TestBusWildcardSubscriptions tests that a single subscription can receive +// multiple event types using * for glob patterns. +func TestBusWildcardSubscriptions(t *testing.T) { + bus, err := NewEventBus(nil) + if err != nil { + t.Fatal(err) + } + ctx := context.Background() + + fooEventType := logical.EventType("kv/foo") + barEventType := logical.EventType("kv/bar") + bus.Start() + + ch1, cancel1, err := bus.Subscribe(ctx, namespace.RootNamespace, "kv/*") + if err != nil { + t.Fatal(err) + } + defer cancel1() + + ch2, cancel2, err := bus.Subscribe(ctx, namespace.RootNamespace, "*/bar") + if err != nil { + t.Fatal(err) + } + defer cancel2() + + event1, err := logical.NewEvent() + if err != nil { + t.Fatal(err) + } + event2, err := logical.NewEvent() + if err != nil { + t.Fatal(err) + } + + err = bus.SendInternal(ctx, namespace.RootNamespace, nil, barEventType, event2) + if err != nil { + t.Error(err) + } + err = bus.SendInternal(ctx, namespace.RootNamespace, nil, fooEventType, event1) + if err != nil { + t.Error(err) + } + + timeout := time.After(1 * time.Second) + // Expect to receive both events on ch1, which subscribed to kv/* + var ch1Seen []string + for i := 0; i < 2; i++ { + select { + case message := <-ch1: + ch1Seen = append(ch1Seen, message.Event.ID()) + case <-timeout: + t.Error("Timeout waiting for event1") + } + } + if len(ch1Seen) != 2 { + t.Errorf("Expected 2 events but got: %v", ch1Seen) + } else { + if !strutil.StrListContains(ch1Seen, event1.ID()) { + t.Errorf("Did not find %s event1 ID in ch1seen", event1.ID()) + } + if !strutil.StrListContains(ch1Seen, event2.ID()) { + t.Errorf("Did not find %s event2 ID in ch1seen", event2.ID()) + } + } + // Expect to receive just kv/bar on ch2, which subscribed to */bar + select { + case message := <-ch2: + if message.Event.ID() != event2.ID() { + t.Errorf("Got unexpected message: %v", message) + } + case <-timeout: + t.Error("Timeout waiting for event2") + } +} diff --git a/vault/events_test.go b/vault/events_test.go index d57be82325be..107be1913efb 100644 --- a/vault/events_test.go +++ b/vault/events_test.go @@ -19,7 +19,7 @@ func TestCanSendEventsFromBuiltinPlugin(t *testing.T) { if err != nil { t.Fatal(err) } - ch, cancel, err := c.events.Subscribe(ctx, namespace.RootNamespace, logical.EventType(eventType)) + ch, cancel, err := c.events.Subscribe(ctx, namespace.RootNamespace, eventType) if err != nil { t.Fatal(err) } From fcfc583ae5f5996bf6142a3cfb402da63f5664dc Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Fri, 17 Feb 2023 10:57:54 -0500 Subject: [PATCH 020/231] backport of commit 6984f23d94e7cb43e86060f9ddaba4a78503c717 (#19233) Co-authored-by: Tom Proctor --- changelog/19194.txt | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelog/19194.txt diff --git a/changelog/19194.txt b/changelog/19194.txt new file mode 100644 index 000000000000..b2a5ff383f12 --- /dev/null +++ b/changelog/19194.txt @@ -0,0 +1,4 @@ +```release-note:feature +**Event System (Alpha)**: Vault has a new opt-in experimental event system. Not yet suitable for production use. Events are currently only generated on writes to the KV secrets engine, but external plugins can also be updated to start generating events. +``` + From fe4d56c86479c65cb6cfe3a83bdf73e4e80610b7 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Fri, 17 Feb 2023 11:20:54 -0500 Subject: [PATCH 021/231] backport of commit b08ecd76fac621bd5a57489f43fe850806448cd5 (#19241) Co-authored-by: John-Michael Faircloth --- website/content/docs/upgrading/upgrade-to-1.12.x.mdx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/website/content/docs/upgrading/upgrade-to-1.12.x.mdx b/website/content/docs/upgrading/upgrade-to-1.12.x.mdx index e97dce34e05a..f36f575875ec 100644 --- a/website/content/docs/upgrading/upgrade-to-1.12.x.mdx +++ b/website/content/docs/upgrading/upgrade-to-1.12.x.mdx @@ -21,6 +21,14 @@ Vault Enterprise will now perform a supported storage check at startup. There is @include 'consul-dataplane-upgrade-note.mdx' +### External Plugin Loading + +Vault 1.12.0 introduced a change to how external plugins are loaded. Prior to +Vault 1.12.0 plugins were lazy loaded on startup. This means that plugin +processes were killed after a successful mount and then respawned when a +request is routed to them. Vault 1.12.0 introduced auto mutual TLS for +secrets/auth plugins so we do not lazy load them on startup anymore. + ## Known Issues ### Pinning to builtin plugin versions may cause failure on upgrade From 1ec3397899b6b7286720f95bfe716f54aef2bd42 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Fri, 17 Feb 2023 14:31:58 -0500 Subject: [PATCH 022/231] backport of commit 0c2fadca9e7aafa23fd8f26cbac992fd16b8cd63 (#19251) Co-authored-by: Alexander Scheel --- website/content/docs/auth/cert.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/content/docs/auth/cert.mdx b/website/content/docs/auth/cert.mdx index 385e60bdaf1d..d63a50da3613 100644 --- a/website/content/docs/auth/cert.mdx +++ b/website/content/docs/auth/cert.mdx @@ -11,7 +11,9 @@ description: >- @include 'x509-sha1-deprecation.mdx' The `cert` auth method allows authentication using SSL/TLS client certificates -which are either signed by a CA or self-signed. +which are either signed by a CA or self-signed. SSL/TLS client certificates +are defined as having an `ExtKeyUsage` extension with the usage set to either +`ClientAuth` or `Any`. The trusted certificates and CAs are configured directly to the auth method using the `certs/` path. This method cannot read trusted certificates from an From c9eb3c72513a04809d7fae29e79aaade71f646c9 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Fri, 17 Feb 2023 15:14:48 -0500 Subject: [PATCH 023/231] events: WS protobuf messages should be binary (#19232) (#19256) The [WebSockets spec](https://www.rfc-editor.org/rfc/rfc6455) states that text messages must be valid UTF-8 encoded strings, which protobuf messages virtually never are. This now correctly sends the protobuf events as binary messages. We change the format to correspond to CloudEvents, as originally intended, and remove a redundant timestamp and newline. We also bump the eventlogger to fix a race condition that this code triggers. Co-authored-by: Christopher Swenson --- go.mod | 2 +- go.sum | 4 +- http/events.go | 18 ++++-- http/events_test.go | 53 ++++++++++------- sdk/logical/event.pb.go | 113 ++++++++++++++++--------------------- sdk/logical/event.proto | 2 - sdk/logical/events.go | 4 +- vault/eventbus/bus.go | 17 +++--- vault/eventbus/bus_test.go | 23 ++++---- vault/events_test.go | 3 +- 10 files changed, 119 insertions(+), 120 deletions(-) diff --git a/go.mod b/go.mod index 5fd997992b78..7499f44d1cec 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/hashicorp/consul-template v0.29.5 github.com/hashicorp/consul/api v1.17.0 github.com/hashicorp/errwrap v1.1.0 - github.com/hashicorp/eventlogger v0.1.0 + github.com/hashicorp/eventlogger v0.1.1 github.com/hashicorp/go-cleanhttp v0.5.2 github.com/hashicorp/go-discover v0.0.0-20210818145131-c573d69da192 github.com/hashicorp/go-gcp-common v0.8.0 diff --git a/go.sum b/go.sum index d527ad727ac5..c4b535ced3a2 100644 --- a/go.sum +++ b/go.sum @@ -969,8 +969,8 @@ github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FK github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/eventlogger v0.1.0 h1:S6xc4gZVzewuDUP4R4Ngko419h/CGDuV/b4ADL3XLik= -github.com/hashicorp/eventlogger v0.1.0/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= +github.com/hashicorp/eventlogger v0.1.1 h1:zyCjxsy7KunFsMPZKU5PnwWEakSrp1zjj2vPFmrDaeo= +github.com/hashicorp/eventlogger v0.1.1/go.mod h1://CHt6/j+Q2lc0NlUB5af4aS2M0c0aVBg9/JfcpAyhM= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= diff --git a/http/events.go b/http/events.go index e957cd7a3798..f516b3ccbc33 100644 --- a/http/events.go +++ b/http/events.go @@ -15,7 +15,6 @@ import ( "github.com/hashicorp/vault/sdk/logical" "github.com/hashicorp/vault/vault" "github.com/hashicorp/vault/vault/eventbus" - "google.golang.org/protobuf/encoding/protojson" "nhooyr.io/websocket" ) @@ -47,19 +46,26 @@ func handleEventsSubscribeWebsocket(args eventSubscribeArgs) (websocket.StatusCo logger.Info("Websocket context is done, closing the connection") return websocket.StatusNormalClosure, "", nil case message := <-ch: - logger.Debug("Sending message to websocket", "message", message) + logger.Debug("Sending message to websocket", "message", message.Payload) var messageBytes []byte + var messageType websocket.MessageType if args.json { - messageBytes, err = protojson.Marshal(message) + var ok bool + messageBytes, ok = message.Format("cloudevents-json") + if !ok { + logger.Warn("Could not get cloudevents JSON format") + return 0, "", errors.New("could not get cloudevents JSON format") + } + messageType = websocket.MessageText } else { - messageBytes, err = proto.Marshal(message) + messageBytes, err = proto.Marshal(message.Payload.(*logical.EventReceived)) + messageType = websocket.MessageBinary } if err != nil { logger.Warn("Could not serialize websocket event", "error", err) return 0, "", err } - messageString := string(messageBytes) + "\n" - err = args.conn.Write(ctx, websocket.MessageText, []byte(messageString)) + err = args.conn.Write(ctx, messageType, messageBytes) if err != nil { return 0, "", err } diff --git a/http/events_test.go b/http/events_test.go index 645ecd403a4a..3fe5d68e96c1 100644 --- a/http/events_test.go +++ b/http/events_test.go @@ -2,6 +2,7 @@ package http import ( "context" + "fmt" "net/http" "strings" "sync/atomic" @@ -64,27 +65,39 @@ func TestEventsSubscribe(t *testing.T) { wsAddr := strings.Replace(addr, "http", "ws", 1) - // check that the connection fails if we don't have a token - _, _, err := websocket.Dial(ctx, wsAddr+"/v1/sys/events/subscribe/"+eventType+"?json=true", nil) - if err == nil { - t.Error("Expected websocket error but got none") - } else if !strings.HasSuffix(err.Error(), "401") { - t.Errorf("Expected 401 websocket but got %v", err) - } + testCases := []struct { + json bool + }{{true}, {false}} - conn, _, err := websocket.Dial(ctx, wsAddr+"/v1/sys/events/subscribe/"+eventType+"?json=true", &websocket.DialOptions{ - HTTPHeader: http.Header{"x-vault-token": []string{token}}, - }) - if err != nil { - t.Fatal(err) - } + for _, testCase := range testCases { + url := fmt.Sprintf("%s/v1/sys/events/subscribe/%s?json=%v", wsAddr, eventType, testCase.json) + // check that the connection fails if we don't have a token + _, _, err := websocket.Dial(ctx, url, nil) + if err == nil { + t.Error("Expected websocket error but got none") + } else if !strings.HasSuffix(err.Error(), "401") { + t.Errorf("Expected 401 websocket but got %v", err) + } - _, msg, err := conn.Read(ctx) - if err != nil { - t.Fatal(err) - } - msgJson := strings.TrimSpace(string(msg)) - if !strings.HasPrefix(msgJson, "{") || !strings.HasSuffix(msgJson, "}") { - t.Errorf("Expected to get JSON event but got: %v", msgJson) + conn, _, err := websocket.Dial(ctx, url, &websocket.DialOptions{ + HTTPHeader: http.Header{"x-vault-token": []string{token}}, + }) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + conn.Close(websocket.StatusNormalClosure, "") + }) + + _, msg, err := conn.Read(ctx) + if err != nil { + t.Fatal(err) + } + if testCase.json { + msgJson := strings.TrimSpace(string(msg)) + if !strings.HasPrefix(msgJson, "{") || !strings.HasSuffix(msgJson, "}") { + t.Errorf("Expected to get JSON event but got: %v", msgJson) + } + } } } diff --git a/sdk/logical/event.pb.go b/sdk/logical/event.pb.go index d09020236414..1925c6ae9870 100644 --- a/sdk/logical/event.pb.go +++ b/sdk/logical/event.pb.go @@ -10,7 +10,6 @@ import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" structpb "google.golang.org/protobuf/types/known/structpb" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" ) @@ -207,10 +206,9 @@ type EventReceived struct { Event *EventData `protobuf:"bytes,1,opt,name=event,proto3" json:"event,omitempty"` // namespace path - Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` - EventType string `protobuf:"bytes,3,opt,name=event_type,json=eventType,proto3" json:"event_type,omitempty"` - PluginInfo *EventPluginInfo `protobuf:"bytes,4,opt,name=plugin_info,json=pluginInfo,proto3" json:"plugin_info,omitempty"` - Timestamp *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` + EventType string `protobuf:"bytes,3,opt,name=event_type,json=eventType,proto3" json:"event_type,omitempty"` + PluginInfo *EventPluginInfo `protobuf:"bytes,4,opt,name=plugin_info,json=pluginInfo,proto3" json:"plugin_info,omitempty"` } func (x *EventReceived) Reset() { @@ -273,13 +271,6 @@ func (x *EventReceived) GetPluginInfo() *EventPluginInfo { return nil } -func (x *EventReceived) GetTimestamp() *timestamppb.Timestamp { - if x != nil { - return x.Timestamp - } - return nil -} - var File_sdk_logical_event_proto protoreflect.FileDescriptor var file_sdk_logical_event_proto_rawDesc = []byte{ @@ -287,48 +278,42 @@ var file_sdk_logical_event_proto_rawDesc = []byte{ 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x22, 0xd1, 0x01, 0x0a, 0x0f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x63, - 0x6c, 0x61, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x1d, 0x0a, - 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x6c, - 0x75, 0x67, 0x69, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x83, 0x01, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x44, - 0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x08, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x65, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x22, 0xeb, 0x01, 0x0a, 0x0d, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x12, 0x28, 0x0a, - 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, - 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, - 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x69, - 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6f, 0x67, 0x69, - 0x63, 0x61, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, - 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x28, 0x5a, 0x26, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, - 0x63, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x22, 0xd1, 0x01, 0x0a, 0x0f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x63, 0x6c, + 0x61, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x1d, 0x0a, 0x0a, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x83, 0x01, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x61, + 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x08, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x49, 0x64, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x22, 0xb1, 0x01, 0x0a, 0x0d, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x12, 0x28, 0x0a, 0x05, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6f, + 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, + 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, + 0x61, 0x6c, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x0a, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x28, + 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, + 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -345,22 +330,20 @@ func file_sdk_logical_event_proto_rawDescGZIP() []byte { var file_sdk_logical_event_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_sdk_logical_event_proto_goTypes = []interface{}{ - (*EventPluginInfo)(nil), // 0: logical.EventPluginInfo - (*EventData)(nil), // 1: logical.EventData - (*EventReceived)(nil), // 2: logical.EventReceived - (*structpb.Struct)(nil), // 3: google.protobuf.Struct - (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp + (*EventPluginInfo)(nil), // 0: logical.EventPluginInfo + (*EventData)(nil), // 1: logical.EventData + (*EventReceived)(nil), // 2: logical.EventReceived + (*structpb.Struct)(nil), // 3: google.protobuf.Struct } var file_sdk_logical_event_proto_depIdxs = []int32{ 3, // 0: logical.EventData.metadata:type_name -> google.protobuf.Struct 1, // 1: logical.EventReceived.event:type_name -> logical.EventData 0, // 2: logical.EventReceived.plugin_info:type_name -> logical.EventPluginInfo - 4, // 3: logical.EventReceived.timestamp:type_name -> google.protobuf.Timestamp - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_sdk_logical_event_proto_init() } diff --git a/sdk/logical/event.proto b/sdk/logical/event.proto index 789f1b8d4db1..594bcf1dde09 100644 --- a/sdk/logical/event.proto +++ b/sdk/logical/event.proto @@ -5,7 +5,6 @@ option go_package = "github.com/hashicorp/vault/sdk/logical"; package logical; import "google/protobuf/struct.proto"; -import "google/protobuf/timestamp.proto"; // EventPluginInfo contains data related to the plugin that generated an event. message EventPluginInfo { @@ -49,5 +48,4 @@ message EventReceived { string namespace = 2; string event_type = 3; EventPluginInfo plugin_info = 4; - google.protobuf.Timestamp timestamp = 5; } diff --git a/sdk/logical/events.go b/sdk/logical/events.go index 9840a9ca47c2..e96e6d709005 100644 --- a/sdk/logical/events.go +++ b/sdk/logical/events.go @@ -7,8 +7,8 @@ import ( ) // ID is an alias to GetId() for CloudEvents compatibility. -func (x *EventData) ID() string { - return x.GetId() +func (x *EventReceived) ID() string { + return x.Event.GetId() } // NewEvent returns an event with a new, random EID. diff --git a/vault/eventbus/bus.go b/vault/eventbus/bus.go index cc954435fc93..917638018ece 100644 --- a/vault/eventbus/bus.go +++ b/vault/eventbus/bus.go @@ -17,7 +17,6 @@ import ( "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/sdk/logical" "github.com/ryanuber/go-glob" - "google.golang.org/protobuf/types/known/timestamppb" ) const ( @@ -53,7 +52,7 @@ type pluginEventBus struct { type asyncChanNode struct { // TODO: add bounded deque buffer of *EventReceived ctx context.Context - ch chan *logical.EventReceived + ch chan *eventlogger.Event logger hclog.Logger // used to close the connection @@ -95,7 +94,6 @@ func (bus *EventBus) SendInternal(ctx context.Context, ns *namespace.Namespace, Namespace: ns.Path, EventType: string(eventType), PluginInfo: pluginInfo, - Timestamp: timestamppb.New(time.Now()), } bus.logger.Info("Sending event", "event", eventReceived) @@ -169,7 +167,7 @@ func NewEventBus(logger hclog.Logger) (*EventBus, error) { }, nil } -func (bus *EventBus) Subscribe(ctx context.Context, ns *namespace.Namespace, pattern string) (<-chan *logical.EventReceived, context.CancelFunc, error) { +func (bus *EventBus) Subscribe(ctx context.Context, ns *namespace.Namespace, pattern string) (<-chan *eventlogger.Event, context.CancelFunc, error) { // subscriptions are still stored even if the bus has not been started pipelineID, err := uuid.GenerateUUID() if err != nil { @@ -193,7 +191,7 @@ func (bus *EventBus) Subscribe(ctx context.Context, ns *namespace.Namespace, pat } ctx, cancel := context.WithCancel(ctx) - asyncNode := newAsyncNode(ctx, ns, bus.logger) + asyncNode := newAsyncNode(ctx, bus.logger) err = bus.broker.RegisterNode(eventlogger.NodeID(sinkNodeID), asyncNode) if err != nil { defer cancel() @@ -247,10 +245,10 @@ func newFilterNode(ns *namespace.Namespace, pattern string) *eventlogger.Filter } } -func newAsyncNode(ctx context.Context, namespace *namespace.Namespace, logger hclog.Logger) *asyncChanNode { +func newAsyncNode(ctx context.Context, logger hclog.Logger) *asyncChanNode { return &asyncChanNode{ ctx: ctx, - ch: make(chan *logical.EventReceived), + ch: make(chan *eventlogger.Event), logger: logger, } } @@ -272,17 +270,16 @@ func (node *asyncChanNode) Close() { func (node *asyncChanNode) Process(ctx context.Context, e *eventlogger.Event) (*eventlogger.Event, error) { // sends to the channel async in another goroutine go func() { - eventRecv := e.Payload.(*logical.EventReceived) var timeout bool select { - case node.ch <- eventRecv: + case node.ch <- e: case <-ctx.Done(): timeout = errors.Is(ctx.Err(), context.DeadlineExceeded) case <-node.ctx.Done(): timeout = errors.Is(node.ctx.Err(), context.DeadlineExceeded) } if timeout { - node.logger.Info("Subscriber took too long to process event, closing", "ID", eventRecv.Event.ID()) + node.logger.Info("Subscriber took too long to process event, closing", "ID", e.Payload.(*logical.EventReceived).Event.Id) node.Close() } }() diff --git a/vault/eventbus/bus_test.go b/vault/eventbus/bus_test.go index cf7d26e318eb..e0ac46c83fc2 100644 --- a/vault/eventbus/bus_test.go +++ b/vault/eventbus/bus_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/hashicorp/eventlogger" "github.com/hashicorp/go-secure-stdlib/strutil" "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/sdk/logical" @@ -58,7 +59,7 @@ func TestBusBasics(t *testing.T) { timeout := time.After(1 * time.Second) select { case message := <-ch: - if message.Event.ID() != event.ID() { + if message.Payload.(*logical.EventReceived).Event.Id != event.Id { t.Errorf("Got unexpected message: %+v", message) } case <-timeout: @@ -117,7 +118,7 @@ func TestNamespaceFiltering(t *testing.T) { timeout = time.After(1 * time.Second) select { case message := <-ch: - if message.Event.ID() != event.ID() { + if message.Payload.(*logical.EventReceived).Event.Id != event.Id { t.Errorf("Got unexpected message %+v but was waiting for %+v", message, event) } @@ -171,7 +172,7 @@ func TestBus2Subscriptions(t *testing.T) { timeout := time.After(1 * time.Second) select { case message := <-ch1: - if message.Event.ID() != event1.ID() { + if message.Payload.(*logical.EventReceived).Event.Id != event1.Id { t.Errorf("Got unexpected message: %v", message) } case <-timeout: @@ -179,7 +180,7 @@ func TestBus2Subscriptions(t *testing.T) { } select { case message := <-ch2: - if message.Event.ID() != event2.ID() { + if message.Payload.(*logical.EventReceived).Event.Id != event2.Id { t.Errorf("Got unexpected message: %v", message) } case <-timeout: @@ -216,7 +217,7 @@ func TestBusSubscriptionsCancel(t *testing.T) { eventType := logical.EventType("someType") - var channels []<-chan *logical.EventReceived + var channels []<-chan *eventlogger.Event var cancels []context.CancelFunc stopped := atomic.Int32{} @@ -348,7 +349,7 @@ func TestBusWildcardSubscriptions(t *testing.T) { for i := 0; i < 2; i++ { select { case message := <-ch1: - ch1Seen = append(ch1Seen, message.Event.ID()) + ch1Seen = append(ch1Seen, message.Payload.(*logical.EventReceived).Event.Id) case <-timeout: t.Error("Timeout waiting for event1") } @@ -356,17 +357,17 @@ func TestBusWildcardSubscriptions(t *testing.T) { if len(ch1Seen) != 2 { t.Errorf("Expected 2 events but got: %v", ch1Seen) } else { - if !strutil.StrListContains(ch1Seen, event1.ID()) { - t.Errorf("Did not find %s event1 ID in ch1seen", event1.ID()) + if !strutil.StrListContains(ch1Seen, event1.Id) { + t.Errorf("Did not find %s event1 ID in ch1seen", event1.Id) } - if !strutil.StrListContains(ch1Seen, event2.ID()) { - t.Errorf("Did not find %s event2 ID in ch1seen", event2.ID()) + if !strutil.StrListContains(ch1Seen, event2.Id) { + t.Errorf("Did not find %s event2 ID in ch1seen", event2.Id) } } // Expect to receive just kv/bar on ch2, which subscribed to */bar select { case message := <-ch2: - if message.Event.ID() != event2.ID() { + if message.Payload.(*logical.EventReceived).Event.Id != event2.Id { t.Errorf("Got unexpected message: %v", message) } case <-timeout: diff --git a/vault/events_test.go b/vault/events_test.go index 107be1913efb..97d1968e0015 100644 --- a/vault/events_test.go +++ b/vault/events_test.go @@ -37,7 +37,8 @@ func TestCanSendEventsFromBuiltinPlugin(t *testing.T) { // check that the event is routed to the subscription select { - case received := <-ch: + case receivedEvent := <-ch: + received := receivedEvent.Payload.(*logical.EventReceived) if event.Id != received.Event.Id { t.Errorf("Got wrong event: %+v, expected %+v", received, event) } From 6ae50fee9374fbca8705ec622fd0a5ec7b87bd0e Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Fri, 17 Feb 2023 15:44:12 -0500 Subject: [PATCH 024/231] backport of commit add3659f39358a159a4efd7662e9ccffa6a9e22b (#19242) Co-authored-by: Scott Miller --- go.mod | 8 ++++---- go.sum | 15 ++++++++------- sdk/go.mod | 2 +- sdk/go.sum | 4 ++-- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index 7499f44d1cec..2285d45e1c89 100644 --- a/go.mod +++ b/go.mod @@ -71,12 +71,12 @@ require ( github.com/hashicorp/go-discover v0.0.0-20210818145131-c573d69da192 github.com/hashicorp/go-gcp-common v0.8.0 github.com/hashicorp/go-hclog v1.4.0 - github.com/hashicorp/go-kms-wrapping/v2 v2.0.7 + github.com/hashicorp/go-kms-wrapping/v2 v2.0.8 github.com/hashicorp/go-kms-wrapping/wrappers/aead/v2 v2.0.7-1 github.com/hashicorp/go-kms-wrapping/wrappers/alicloudkms/v2 v2.0.1 github.com/hashicorp/go-kms-wrapping/wrappers/awskms/v2 v2.0.7 github.com/hashicorp/go-kms-wrapping/wrappers/azurekeyvault/v2 v2.0.7 - github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.7 + github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.8 github.com/hashicorp/go-kms-wrapping/wrappers/ocikms/v2 v2.0.7 github.com/hashicorp/go-kms-wrapping/wrappers/transit/v2 v2.0.7 github.com/hashicorp/go-memdb v1.3.3 @@ -214,11 +214,11 @@ require ( ) require ( - cloud.google.com/go v0.105.0 // indirect + cloud.google.com/go v0.107.0 // indirect cloud.google.com/go/compute v1.14.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v0.8.0 // indirect - cloud.google.com/go/kms v1.6.0 // indirect + cloud.google.com/go/kms v1.8.0 // indirect code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f // indirect github.com/Azure/azure-pipeline-go v0.2.3 // indirect github.com/Azure/azure-sdk-for-go v67.2.0+incompatible // indirect diff --git a/go.sum b/go.sum index c4b535ced3a2..c7d498499cd1 100644 --- a/go.sum +++ b/go.sum @@ -16,8 +16,8 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y= -cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go v0.107.0 h1:qkj22L7bgkl6vIeZDlOY2po43Mx/TIa2Wsa7VR+PEww= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -32,8 +32,8 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7 cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk= cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/kms v1.6.0 h1:OWRZzrPmOZUzurjI2FBGtgY2mB1WaJkqhw6oIwSj0Yg= -cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= +cloud.google.com/go/kms v1.8.0 h1:VrJLOsMRzW7IqTTYn+OYupqF3iKSE060Nrn+PECrYjg= +cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= cloud.google.com/go/monitoring v1.8.0 h1:c9riaGSPQ4dUKWB+M1Fl0N+iLxstMbCktdEwYSPGDvA= cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= @@ -995,8 +995,9 @@ github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJ github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.0 h1:pSjQfW3vPtrOTcasTUKgCTQT7OGPPTTMVRrOfU6FJD8= github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.0/go.mod h1:xvb32K2keAc+R8DSFG2IwDcydK9DBQE+fGA5fsw6hSk= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.7 h1:P+dh3M6k5aNl2wXrA9s6zquMHWPaYIkotCffiMIYt6U= github.com/hashicorp/go-kms-wrapping/v2 v2.0.7/go.mod h1:sDQAfwJGv25uGPZA04x87ERglCG6avnRcBT9wYoMII8= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.8 h1:9Q2lu1YbbmiAgvYZ7Pr31RdlVonUpX+mmDL7Z7qTA2U= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.8/go.mod h1:qTCjxGig/kjuj3hk1z8pOUrzbse/GxB1tGfbrq8tGJg= github.com/hashicorp/go-kms-wrapping/wrappers/aead/v2 v2.0.7-1 h1:ZV26VJYcITBom0QqYSUOIj4HOHCVPEFjLqjxyXV/AbA= github.com/hashicorp/go-kms-wrapping/wrappers/aead/v2 v2.0.7-1/go.mod h1:b99cDSA+OzcyRoBZroSf174/ss/e6gUuS45wue9ZQfc= github.com/hashicorp/go-kms-wrapping/wrappers/alicloudkms/v2 v2.0.1 h1:ydUCtmr8f9F+mHZ1iCsvzqFTXqNVpewX3s9zcYipMKI= @@ -1005,8 +1006,8 @@ github.com/hashicorp/go-kms-wrapping/wrappers/awskms/v2 v2.0.7 h1:E3eEWpkofgPNrY github.com/hashicorp/go-kms-wrapping/wrappers/awskms/v2 v2.0.7/go.mod h1:j5vefRoguQUG7iM4reS/hKIZssU1lZRqNPM5Wow6UnM= github.com/hashicorp/go-kms-wrapping/wrappers/azurekeyvault/v2 v2.0.7 h1:X27JWuPW6Gmi2l7NMm0pvnp7z7hhtns2TeIOQU93mqI= github.com/hashicorp/go-kms-wrapping/wrappers/azurekeyvault/v2 v2.0.7/go.mod h1:i7Dt9mDsVUQG/I639jtdQerliaO2SvvPnpYPhZ8CGZ4= -github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.7 h1:GXp8P2xb8SE6X/Iw+22nw6fkbkb9LPQlKC8NPOutXN8= -github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.7/go.mod h1:GTK8CQ239rq7u3XNw4Mooqb7hFZzewtwgoJONAXGcRE= +github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.8 h1:16I8OqBEuxZIowwn3jiLvhlx+z+ia4dJc9stvz0yUBU= +github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.8/go.mod h1:6QUMo5BrXAtbzSuZilqmx0A4px2u6PeFK7vfp2WIzeM= github.com/hashicorp/go-kms-wrapping/wrappers/ocikms/v2 v2.0.7 h1:KeG3QGrbxbr2qAqCJdf3NR4ijAYwdcWLTmwSbR0yusM= github.com/hashicorp/go-kms-wrapping/wrappers/ocikms/v2 v2.0.7/go.mod h1:rXxYzjjGw4HltEwxPp9zYSRIo6R+rBf1MSPk01bvodc= github.com/hashicorp/go-kms-wrapping/wrappers/transit/v2 v2.0.7 h1:G25tZFw/LrAzJWxvS0/BFI7V1xAP/UsAIsgBwiE0mwo= diff --git a/sdk/go.mod b/sdk/go.mod index 5097dc4bf771..297a8b665967 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -15,7 +15,7 @@ require ( github.com/hashicorp/go-hclog v0.16.2 github.com/hashicorp/go-immutable-radix v1.3.1 github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.0 - github.com/hashicorp/go-kms-wrapping/v2 v2.0.7 + github.com/hashicorp/go-kms-wrapping/v2 v2.0.8 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-plugin v1.4.5 github.com/hashicorp/go-retryablehttp v0.5.3 diff --git a/sdk/go.sum b/sdk/go.sum index 0dd2bfc05e8a..3b02ce469f16 100644 --- a/sdk/go.sum +++ b/sdk/go.sum @@ -96,8 +96,8 @@ github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJ github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.0 h1:pSjQfW3vPtrOTcasTUKgCTQT7OGPPTTMVRrOfU6FJD8= github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.0/go.mod h1:xvb32K2keAc+R8DSFG2IwDcydK9DBQE+fGA5fsw6hSk= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.7 h1:P+dh3M6k5aNl2wXrA9s6zquMHWPaYIkotCffiMIYt6U= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.7/go.mod h1:sDQAfwJGv25uGPZA04x87ERglCG6avnRcBT9wYoMII8= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.8 h1:9Q2lu1YbbmiAgvYZ7Pr31RdlVonUpX+mmDL7Z7qTA2U= +github.com/hashicorp/go-kms-wrapping/v2 v2.0.8/go.mod h1:qTCjxGig/kjuj3hk1z8pOUrzbse/GxB1tGfbrq8tGJg= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= From 6e323b6a5a5134806f4840993e3d3b5d37419d53 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Mon, 20 Feb 2023 14:15:58 -0500 Subject: [PATCH 025/231] backport of commit 4c11d090cdfdd2aa6498c099ca0cd88f5e6af488 (#19262) Co-authored-by: Tom Proctor --- http/events.go | 2 +- http/events_test.go | 82 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 70 insertions(+), 14 deletions(-) diff --git a/http/events.go b/http/events.go index f516b3ccbc33..e5eaa78be972 100644 --- a/http/events.go +++ b/http/events.go @@ -84,7 +84,7 @@ func handleEventsSubscribe(core *vault.Core, req *logical.Request) http.Handler _, _, err := core.CheckToken(ctx, req, false) if err != nil { if errors.Is(err, logical.ErrPermissionDenied) { - respondError(w, http.StatusUnauthorized, logical.ErrPermissionDenied) + respondError(w, http.StatusForbidden, logical.ErrPermissionDenied) return } logger.Debug("Error validating token", "error", err) diff --git a/http/events_test.go b/http/events_test.go index 3fe5d68e96c1..4cba7d1bd1c3 100644 --- a/http/events_test.go +++ b/http/events_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/hashicorp/go-uuid" + "github.com/hashicorp/vault/api" "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/sdk/logical" "github.com/hashicorp/vault/vault" @@ -34,7 +35,7 @@ func TestEventsSubscribe(t *testing.T) { stop := atomic.Bool{} - eventType := "abc" + const eventType = "abc" // send some events go func() { @@ -43,7 +44,10 @@ func TestEventsSubscribe(t *testing.T) { if err != nil { core.Logger().Info("Error generating UUID, exiting sender", "error", err) } - err = core.Events().SendInternal(namespace.RootContext(context.Background()), namespace.RootNamespace, nil, logical.EventType(eventType), &logical.EventData{ + pluginInfo := &logical.EventPluginInfo{ + MountPath: "secret", + } + err = core.Events().SendInternal(namespace.RootContext(context.Background()), namespace.RootNamespace, pluginInfo, logical.EventType(eventType), &logical.EventData{ Id: id, Metadata: nil, EntityIds: nil, @@ -60,9 +64,7 @@ func TestEventsSubscribe(t *testing.T) { stop.Store(true) }) - ctx, cancelFunc := context.WithTimeout(context.Background(), 5*time.Second) - t.Cleanup(cancelFunc) - + ctx := context.Background() wsAddr := strings.Replace(addr, "http", "ws", 1) testCases := []struct { @@ -71,14 +73,6 @@ func TestEventsSubscribe(t *testing.T) { for _, testCase := range testCases { url := fmt.Sprintf("%s/v1/sys/events/subscribe/%s?json=%v", wsAddr, eventType, testCase.json) - // check that the connection fails if we don't have a token - _, _, err := websocket.Dial(ctx, url, nil) - if err == nil { - t.Error("Expected websocket error but got none") - } else if !strings.HasSuffix(err.Error(), "401") { - t.Errorf("Expected 401 websocket but got %v", err) - } - conn, _, err := websocket.Dial(ctx, url, &websocket.DialOptions{ HTTPHeader: http.Header{"x-vault-token": []string{token}}, }) @@ -101,3 +95,65 @@ func TestEventsSubscribe(t *testing.T) { } } } + +// TestEventsSubscribeAuth tests that unauthenticated and unauthorized subscriptions +// fail correctly. +func TestEventsSubscribeAuth(t *testing.T) { + core := vault.TestCore(t) + ln, addr := TestServer(t, core) + defer ln.Close() + + // unseal the core + keys, root := vault.TestCoreInit(t, core) + for _, key := range keys { + _, err := core.Unseal(key) + if err != nil { + t.Fatal(err) + } + } + + var nonPrivilegedToken string + // Fetch a valid non privileged token. + { + config := api.DefaultConfig() + config.Address = addr + + client, err := api.NewClient(config) + if err != nil { + t.Fatal(err) + } + client.SetToken(root) + + secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{Policies: []string{"default"}}) + if err != nil { + t.Fatal(err) + } + if secret.Auth.ClientToken == "" { + t.Fatal("Failed to fetch a non privileged token") + } + nonPrivilegedToken = secret.Auth.ClientToken + } + + ctx := context.Background() + wsAddr := strings.Replace(addr, "http", "ws", 1) + + // Get a 403 with no token. + _, resp, err := websocket.Dial(ctx, wsAddr+"/v1/sys/events/subscribe/abc", nil) + if err == nil { + t.Error("Expected websocket error but got none") + } + if resp == nil || resp.StatusCode != http.StatusForbidden { + t.Errorf("Expected 403 but got %+v", resp) + } + + // Get a 403 with a non privileged token. + _, resp, err = websocket.Dial(ctx, wsAddr+"/v1/sys/events/subscribe/abc", &websocket.DialOptions{ + HTTPHeader: http.Header{"x-vault-token": []string{nonPrivilegedToken}}, + }) + if err == nil { + t.Error("Expected websocket error but got none") + } + if resp == nil || resp.StatusCode != http.StatusForbidden { + t.Errorf("Expected 403 but got %+v", resp) + } +} From e53ac2633b8803bd47f77e1020bcda79537ddcdc Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Mon, 20 Feb 2023 15:03:17 -0500 Subject: [PATCH 026/231] backport of commit 100ec9a7006e87e1505141835776c547d3afdd9d (#19203) Co-authored-by: Alexander Scheel --- command/pki_health_check.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/command/pki_health_check.go b/command/pki_health_check.go index c47adb2148b5..d2bc87e97f52 100644 --- a/command/pki_health_check.go +++ b/command/pki_health_check.go @@ -135,7 +135,7 @@ default unless enabled by the configuration file explicitly.`, Default: false, EnvVar: "", Usage: `When specified, no health checks are run, but all known health -checks are printed. Still requires a positional mount argument.`, +checks are printed.`, }) return set @@ -170,10 +170,10 @@ func (c *PKIHealthCheckCommand) Run(args []string) int { } args = f.Args() - if len(args) < 1 { + if !c.flagList && len(args) < 1 { c.UI.Error("Not enough arguments (expected mount path, got nothing)") return pkiRetUsage - } else if len(args) > 1 { + } else if !c.flagList && len(args) > 1 { c.UI.Error(fmt.Sprintf("Too many arguments (expected only mount path, got %d arguments)", len(args))) for _, arg := range args { if strings.HasPrefix(arg, "-") { @@ -196,7 +196,14 @@ func (c *PKIHealthCheckCommand) Run(args []string) int { return pkiRetUsage } - mount := sanitizePath(args[0]) + // When listing is enabled, we lack an argument here, but do not contact + // the server at all, so we're safe to use a hard-coded default here. + pkiPath := "" + if len(args) == 1 { + pkiPath = args[0] + } + + mount := sanitizePath(pkiPath) executor := healthcheck.NewExecutor(client, mount) executor.AddCheck(healthcheck.NewCAValidityPeriodCheck()) executor.AddCheck(healthcheck.NewCRLValidityPeriodCheck()) From 8ffa3349469672491a4e3d1cbfbfdfd4cc816593 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Tue, 21 Feb 2023 09:18:22 -0500 Subject: [PATCH 027/231] backport of commit 4ea5c581ad1383eac327acc0cae4e3fbb5ecd736 (#19268) Co-authored-by: Steven Clark --- changelog/19265.txt | 3 +++ command/pki_health_check.go | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 changelog/19265.txt diff --git a/changelog/19265.txt b/changelog/19265.txt new file mode 100644 index 000000000000..23d957e2d594 --- /dev/null +++ b/changelog/19265.txt @@ -0,0 +1,3 @@ +```release-note:bug +cli/pki: Decode integer values properly in health-check configuration file +``` diff --git a/command/pki_health_check.go b/command/pki_health_check.go index d2bc87e97f52..1551dd1c110d 100644 --- a/command/pki_health_check.go +++ b/command/pki_health_check.go @@ -243,13 +243,16 @@ func (c *PKIHealthCheckCommand) Run(args []string) int { // Handle config merging. external_config := map[string]interface{}{} if c.flagConfig != "" { - contents, err := os.ReadFile(c.flagConfig) + contents, err := os.Open(c.flagConfig) if err != nil { c.UI.Error(fmt.Sprintf("Failed to read configuration file %v: %v", c.flagConfig, err)) return pkiRetUsage } - if err := json.Unmarshal(contents, &external_config); err != nil { + decoder := json.NewDecoder(contents) + decoder.UseNumber() // Use json.Number instead of float64 values as we are decoding to an interface{}. + + if err := decoder.Decode(&external_config); err != nil { c.UI.Error(fmt.Sprintf("Failed to parse configuration file %v: %v", c.flagConfig, err)) return pkiRetUsage } From d11b31d46e17d937ef1fcdb512356386e3cae13b Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Tue, 21 Feb 2023 12:15:23 -0500 Subject: [PATCH 028/231] backport of commit 46dd007b3a6b03f4ff0f43d98300364f16a2b121 (#19270) Co-authored-by: Christopher Swenson --- website/content/docs/concepts/events.mdx | 169 +++++++++++++++++++++++ website/data/docs-nav-data.json | 11 +- 2 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 website/content/docs/concepts/events.mdx diff --git a/website/content/docs/concepts/events.mdx b/website/content/docs/concepts/events.mdx new file mode 100644 index 000000000000..50be813ea29c --- /dev/null +++ b/website/content/docs/concepts/events.mdx @@ -0,0 +1,169 @@ +--- +layout: docs +page_title: Events +description: >- + Events are an experimental feature that allows Vault and plugins to exchange arbitrary activity data + within Vault and with external subscribers via WebSockets. +--- + +# Events + +~> **Important:** Events are experimental, and may have backwards incompatible changes introduced in subsequent versions of Vault, or be removed altogether. + +Events are arbitrary, **non-secret** data that can be exchanged between producers (Vault and plugins) +and subscribers (Vault components and external users via the API). +Events are delivered on a *best-effort* basis, so do not have delivery guarantees at this time. + +As of Vault 1.13, events are not considered production-ready, and so are disabled by default when starting a Vault server. +Events can be enabled via the `events.alpha1` [experiment option](/vault/docs/configuration#experiments) in the Vault +configuration or on the command-line: + +```shell-session +$ vault server -experiment events.alpha1 +``` + +-> Note: Experiments can only be enabled at startup, and cannot be disabled or enabled at runtime. + +## Event Types + + + +Internal components of Vault as well as external plugins can generate events. +These are published to "event types", sometimes called "topics" in some event systems. +All events of a specific event type will have the same format for their +additional `metadata` field. + +The following events are currently generated by Vault and its builtin plugins automatically: + +| Plugin | Event Type | Vault version | +| ------ | ----------------------- | ------------- | +| kv | `kv-v1/delete` | 1.13 | +| kv | `kv-v1/write` | 1.13 | +| kv | `kv-v2/config-write` | 1.13 | +| kv | `kv-v2/data-delete` | 1.13 | +| kv | `kv-v2/data-patch` | 1.13 | +| kv | `kv-v2/data-write` | 1.13 | +| kv | `kv-v2/delete` | 1.13 | +| kv | `kv-v2/destroy` | 1.13 | +| kv | `kv-v2/metadata-delete` | 1.13 | +| kv | `kv-v2/metadata-patch` | 1.13 | +| kv | `kv-v2/metadata-read` | 1.13 | +| kv | `kv-v2/metadata-write` | 1.13 | +| kv | `kv-v2/undelete` | 1.13 | + + +## Event Format + +Events may be formatted in protobuf binary format or as JSON. +See `EventReceived` in [`sdk/logical/event.proto`](https://github.com/hashicorp/vault/blob/main/sdk/logical/event.proto) in the relevant Vault version for the protobuf schema. + +When formatted as JSON, the event conforms to the [CloudEvents](https://cloudevents.io/) specification. + +- `id` `(string)` - CloudEvents unique identifier for the event. The `id` is unique for each event, and events with the same `id` represent the same event. + +- `source` `(string)` - CloudEvents source, which is currently `https://vaultproject.io`. + +- `specversion` `(string)` - The CloudEvents specification version this conforms to. + +- `type` `(string)` - CloudEvents type this event corresponds to, which is currently always `*`. + +- ` datacontenttype` `(string)` - CloudEvents content type of the event, which is currently always `application/json`. + +- `time` `(string)` - ISO 8601-formatted timestamp for when the event was generated. + +- ` data` `(object)` - Vault-specific data. + + - `event` `(Event)` - contains the event that happened. + + - `id` `(string)` - (repeat of the `id` parameter) + + - `metadata` `(object)` - arbitrary extra data customized for the event type. + + - `eventType` `(string)` - the event type that was published. + + - `pluginInfo` `(PluginInfo)` - information about the plugin that generated the event, if applicable. + + - `mountClass` `(string)` - the class of plugin, e.g., `secret`, `auth`. + + - `mountAccessor` `(string)` - the unique ID of the mounted plugin. + + - `mountPath` `(string)` - the path that the plugin is mounted at. + + - `plugin` `(string)` - the name of the plugin, e.g., `kv`. + +Here is an example event in JSON format: + +```json +{ + "id": "901f2388-aabb-a385-7bc0-0b09d5fa060b", + "source": "https://vaultproject.io/", + "specversion": "1.0", + "type": "*", + "data": { + "event": { + "id": "901f2388-aabb-a385-7bc0-0b09d5fa060b", + "metadata": { + "current_version": "1", + "oldest_version": "0", + "path": "data/foo" + } + }, + "event_type": "kv-v2/data-write", + "plugin_info": { + "mount_class": "secret", + "mount_accessor": "kv_a6081d01", + "mount_path": "secret/", + "plugin": "kv" + } + }, + "datacontentype": "application/cloudevents", + "time": "2023-02-17T13:11:39.227341-08:00" +} +``` + +## Subscribing to Events + +Vault has an API endpoint, `/v1/sys/events/subscribe/{eventType}`, that allows users to subscribe to events via a +WebSocket stream. +This endpoint supports the standard authentication and authorization workflows used by other Vault endpoints. +The `{eventType}` parameter is a non-empty string of what event type to subscribe to, which may contain wildcards (`*`) +to subscribe to multiple events, e.g., `kv-v2/data-*`. + +By default, the events are delivered in protobuf binary format. +The endpoint can also format the data as JSON if the `json` query parameter is set to `true`: + +```shell-session +$ wscat -H "X-Vault-Token: $(vault print token)" --connect 'ws://127.0.0.1:8200/v1/sys/events/subscribe/kv-v2/data-write?json=true +{"id":"901f2388-aabb-a385-7bc0-0b09d5fa060b","source":"https://vaultproject.io/","specversion":"1.0","type":"*","data":{"event":{"id":"901f2388-aabb-a385-7bc0-0b09d5fa060b","metadata":{"current_version":"1","oldest_version":"0","path":"data/foo"}},"event_type":"kv-v2/data-write","plugin_info":{"mount_class":"secret","mount_accessor":"kv_a6081d01","mount_path":"secret/","plugin":"kv"}},"datacontentype":"application/cloudevents","time":"2023-02-17T13:11:39.227341-08:00"} +... +``` + +The Vault CLI support this endpoint via the `events subscribe` command, which will output a stream of +JSON for the requested events (one line per event): + +```shell-session +$ vault events subscribe kv-v2/data-write +{"id":"901f2388-aabb-a385-7bc0-0b09d5fa060b","source":"https://vaultproject.io/","specversion":"1.0","type":"*","data":{"event":{"id":"901f2388-aabb-a385-7bc0-0b09d5fa060b","metadata":{"current_version":"1","oldest_version":"0","path":"data/foo"}},"event_type":"kv-v2/data-write","plugin_info":{"mount_class":"secret","mount_accessor":"kv_a6081d01","mount_path":"secret/","plugin":"kv"}},"datacontentype":"application/cloudevents","time":"2023-02-17T13:11:39.227341-08:00"} +... +``` + +## Policies + +To subscribe, the `read` capability must be granted by a [policy](/vault/docs/concepts/policies) +on the `/v1/sys/events/subscribe/{eventType}` path, where `{eventType}` is the event type that will be +subscribed to. The path may contain wildcards. + +An example blanket policy is: +```hcl +path "sys/events/subscribe/*" { + capabilities = ["read"] +} +``` + + +## Supported Versions + +| Vault Version | Support | +| ------------- | ------------------------------------------- | +| <= 1.12 | Not supported | +| 1.13 | Supported (with `events.alpha1` experiment) | \ No newline at end of file diff --git a/website/data/docs-nav-data.json b/website/data/docs-nav-data.json index 2fb224fc05b8..06ce320ae12b 100644 --- a/website/data/docs-nav-data.json +++ b/website/data/docs-nav-data.json @@ -198,7 +198,16 @@ { "title": "User Lockout", "path": "concepts/user-lockout" - } + }, + { + "title": "Events", + "path": "concepts/events", + "badge": { + "text": "ALPHA", + "type": "outlined", + "color": "neutral" + } + } ] }, { From 2c6e89904c82fb0cc932dd62b925e316421ae79f Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Tue, 21 Feb 2023 12:44:35 -0500 Subject: [PATCH 029/231] backport of commit 88dcb04623a295d9ff3520147de287ed77af0c98 (#19272) Co-authored-by: Tom Proctor --- http/events_test.go | 43 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/http/events_test.go b/http/events_test.go index 4cba7d1bd1c3..d3debcde6bab 100644 --- a/http/events_test.go +++ b/http/events_test.go @@ -2,6 +2,7 @@ package http import ( "context" + "encoding/json" "fmt" "net/http" "strings" @@ -88,10 +89,46 @@ func TestEventsSubscribe(t *testing.T) { t.Fatal(err) } if testCase.json { - msgJson := strings.TrimSpace(string(msg)) - if !strings.HasPrefix(msgJson, "{") || !strings.HasSuffix(msgJson, "}") { - t.Errorf("Expected to get JSON event but got: %v", msgJson) + event := map[string]interface{}{} + err = json.Unmarshal(msg, &event) + if err != nil { + t.Fatal(err) + } + t.Log(string(msg)) + data := event["data"].(map[string]interface{}) + if actualType := data["event_type"].(string); actualType != eventType { + t.Fatalf("Expeced event type %s, got %s", eventType, actualType) + } + pluginInfo, ok := data["plugin_info"].(map[string]interface{}) + if !ok || pluginInfo == nil { + t.Fatalf("No plugin_info object: %v", data) + } + mountPath, ok := pluginInfo["mount_path"].(string) + if !ok || mountPath != "secret" { + t.Fatalf("Wrong mount_path: %v", data) + } + innerEvent := data["event"].(map[string]interface{}) + if innerEvent["id"].(string) != event["id"].(string) { + t.Fatalf("IDs don't match, expected %s, got %s", innerEvent["id"].(string), event["id"].(string)) + } + if innerEvent["note"].(string) != "testing" { + t.Fatalf("Expected 'testing', got %s", innerEvent["note"].(string)) } + + checkRequiredCloudEventsFields(t, event) + } + } +} + +func checkRequiredCloudEventsFields(t *testing.T, event map[string]interface{}) { + t.Helper() + for _, attr := range []string{"id", "source", "specversion", "type"} { + if v, ok := event[attr]; !ok { + t.Errorf("Missing attribute %s", attr) + } else if str, ok := v.(string); !ok { + t.Errorf("Expected %s to be string but got %T", attr, v) + } else if str == "" { + t.Errorf("%s was empty string", attr) } } } From 4bb0139dd6526a60ad026e115d417232df8a3d87 Mon Sep 17 00:00:00 2001 From: akshya96 <87045294+akshya96@users.noreply.github.com> Date: Tue, 21 Feb 2023 10:29:03 -0800 Subject: [PATCH 030/231] Brute force changelog entry (#19230) * add changelog * rename changelog file * edit description * change changelog entry name * add new feature name --- changelog/19230.txt | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelog/19230.txt diff --git a/changelog/19230.txt b/changelog/19230.txt new file mode 100644 index 000000000000..ab2853d45edb --- /dev/null +++ b/changelog/19230.txt @@ -0,0 +1,4 @@ +```release-note:feature +**User Lockout**: Adds support to configure the user-lockout behaviour for failed logins to prevent +brute force attacks for userpass, approle and ldap auth methods. +``` \ No newline at end of file From 88e9f551997fb30a98c740bf058e93597a60d0e1 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Tue, 21 Feb 2023 14:24:22 -0500 Subject: [PATCH 031/231] backport of commit 9c4e65986ffae4781ea62c47c33aa589e355bacb (#19273) Co-authored-by: Steven Clark --- changelog/19269.txt | 3 +++ command/pki_health_check.go | 21 ++++++++++----------- 2 files changed, 13 insertions(+), 11 deletions(-) create mode 100644 changelog/19269.txt diff --git a/changelog/19269.txt b/changelog/19269.txt new file mode 100644 index 000000000000..57ff2072a18c --- /dev/null +++ b/changelog/19269.txt @@ -0,0 +1,3 @@ +```release-note:improvement +cli/pki: Change the pki health-check --list default config output to JSON so it's a usable configuration file +``` diff --git a/command/pki_health_check.go b/command/pki_health_check.go index 1551dd1c110d..39d3ba73f963 100644 --- a/command/pki_health_check.go +++ b/command/pki_health_check.go @@ -223,20 +223,19 @@ func (c *PKIHealthCheckCommand) Run(args []string) int { // Handle listing, if necessary. if c.flagList { - c.UI.Output("Health Checks:") + c.UI.Output("Default health check config:") + config := map[string]map[string]interface{}{} for _, checker := range executor.Checkers { - c.UI.Output(" - " + checker.Name()) - - prefix := " " - cfg := checker.DefaultConfig() - marshaled, err := json.MarshalIndent(cfg, prefix, " ") - if err != nil { - c.UI.Error(fmt.Sprintf("Failed to marshal default config for check: %v", err)) - return pkiRetUsage - } - c.UI.Output(prefix + string(marshaled)) + config[checker.Name()] = checker.DefaultConfig() + } + + marshaled, err := json.MarshalIndent(config, "", " ") + if err != nil { + c.UI.Error(fmt.Sprintf("Failed to marshal default config for check: %v", err)) + return pkiRetUsage } + c.UI.Output(string(marshaled)) return pkiRetOK } From 8cad3a3426147c476c5f20469afe1d61b0ea9fcb Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Tue, 21 Feb 2023 15:15:16 -0500 Subject: [PATCH 032/231] backport of commit 1b33b9925350f1301196bf03007e8b9f443824cf (#19275) Co-authored-by: Steven Clark --- changelog/19274.txt | 3 +++ command/healthcheck/pki_role_allows_glob_wildcards.go | 2 +- command/healthcheck/pki_role_allows_localhost.go | 2 +- command/healthcheck/pki_role_no_store_false.go | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 changelog/19274.txt diff --git a/changelog/19274.txt b/changelog/19274.txt new file mode 100644 index 000000000000..a7f5d8c29293 --- /dev/null +++ b/changelog/19274.txt @@ -0,0 +1,3 @@ +```release-note:bug +cli/pki: Fix path for role health-check warning messages +``` diff --git a/command/healthcheck/pki_role_allows_glob_wildcards.go b/command/healthcheck/pki_role_allows_glob_wildcards.go index c78fad8653c3..6978f3517bda 100644 --- a/command/healthcheck/pki_role_allows_glob_wildcards.go +++ b/command/healthcheck/pki_role_allows_glob_wildcards.go @@ -141,7 +141,7 @@ func (h *RoleAllowsGlobWildcards) Evaluate(e *Executor) (results []*Result, err ret := Result{ Status: ResultWarning, - Endpoint: "/{{mount}}/role/" + role, + Endpoint: "/{{mount}}/roles/" + role, Message: fmt.Sprintf("Role currently allows wildcard issuance while allowing globs in allowed_domains (%v). Because globs can expand to one or more wildcard character, including wildcards under additional subdomains, these options are dangerous to enable together. If glob domains are required to be enabled, it is suggested to either disable wildcard issuance if not desired, or create two separate roles -- one with wildcard issuance for specified domains and one with glob matching enabled for concrete domain identifiers.", allowedDomains), } diff --git a/command/healthcheck/pki_role_allows_localhost.go b/command/healthcheck/pki_role_allows_localhost.go index 570ffdf90651..c725f86d1c68 100644 --- a/command/healthcheck/pki_role_allows_localhost.go +++ b/command/healthcheck/pki_role_allows_localhost.go @@ -115,7 +115,7 @@ func (h *RoleAllowsLocalhost) Evaluate(e *Executor) (results []*Result, err erro ret := Result{ Status: ResultWarning, - Endpoint: "/{{mount}}/role/" + role, + Endpoint: "/{{mount}}/roles/" + role, Message: fmt.Sprintf("Role currently allows localhost issuance with a non-empty allowed_domains (%v): this role is intended for issuing other hostnames and the allow_localhost=true option may be overlooked by operators. If this role is intended to issue certificates valid for localhost, consider setting allow_localhost=false and explicitly adding localhost to the list of allowed domains.", allowedDomains), } diff --git a/command/healthcheck/pki_role_no_store_false.go b/command/healthcheck/pki_role_no_store_false.go index 6e13e222d2cf..67d686a7a3fd 100644 --- a/command/healthcheck/pki_role_no_store_false.go +++ b/command/healthcheck/pki_role_no_store_false.go @@ -159,7 +159,7 @@ func (h *RoleNoStoreFalse) Evaluate(e *Executor) (results []*Result, err error) ret := Result{ Status: ResultWarning, - Endpoint: "/{{mount}}/role/" + role, + Endpoint: "/{{mount}}/roles/" + role, Message: "Role currently stores every issued certificate (no_store=false). Too many issued and/or revoked certificates can exceed Vault's storage limits and make operations slow. It is encouraged to enable auto-rebuild of CRLs to prevent every revocation from creating a new CRL, and to limit the number of certificates issued under roles with no_store=false: use shorter lifetimes and/or BYOC revocation instead.", } From 871dd067e36a73b8ab65cf49320ce0ebbce42533 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Wed, 22 Feb 2023 09:32:18 -0500 Subject: [PATCH 033/231] backport of commit fe7eedafc33f9b21956b7f999134d9707e6cdc31 (#19286) Co-authored-by: Steven Clark --- changelog/19276.txt | 3 ++ .../pki_allow_if_modified_since.go | 35 ++++++++++++++----- command/healthcheck/pki_audit_visibility.go | 34 +++++++++++++----- command/healthcheck/pki_tidy_last_run.go | 2 +- command/healthcheck/shared.go | 4 +-- 5 files changed, 59 insertions(+), 19 deletions(-) create mode 100644 changelog/19276.txt diff --git a/changelog/19276.txt b/changelog/19276.txt new file mode 100644 index 000000000000..373199478f92 --- /dev/null +++ b/changelog/19276.txt @@ -0,0 +1,3 @@ +```release-note:bug +cli/pki: Properly report permission issues within health-check mount tune checks +``` diff --git a/command/healthcheck/pki_allow_if_modified_since.go b/command/healthcheck/pki_allow_if_modified_since.go index c9a1b0cbdff2..1cff1cda5d58 100644 --- a/command/healthcheck/pki_allow_if_modified_since.go +++ b/command/healthcheck/pki_allow_if_modified_since.go @@ -12,6 +12,7 @@ type AllowIfModifiedSince struct { UnsupportedVersion bool TuneData map[string]interface{} + Fetcher *PathFetch } func NewAllowIfModifiedSinceCheck() Check { @@ -42,15 +43,16 @@ func (h *AllowIfModifiedSince) LoadConfig(config map[string]interface{}) error { } func (h *AllowIfModifiedSince) FetchResources(e *Executor) error { - exit, _, data, err := fetchMountTune(e, func() { + var exit bool + var err error + + exit, h.Fetcher, h.TuneData, err = fetchMountTune(e, func() { h.UnsupportedVersion = true }) - if exit { + + if exit || err != nil { return err } - - h.TuneData = data - return nil } @@ -59,11 +61,28 @@ func (h *AllowIfModifiedSince) Evaluate(e *Executor) (results []*Result, err err ret := Result{ Status: ResultInvalidVersion, Endpoint: "/sys/mounts/{{mount}}/tune", - Message: "This health check requires Vault 1.9+ but an earlier version of Vault Server was contacted, preventing this health check from running.", + Message: "This health check requires Vault 1.12+ but an earlier version of Vault Server was contacted, preventing this health check from running.", } return []*Result{&ret}, nil } + if h.Fetcher.IsSecretPermissionsError() { + ret := Result{ + Status: ResultInsufficientPermissions, + Endpoint: "/sys/mounts/{{mount}}/tune", + Message: "Without this information, this health check is unable to function.", + } + + if e.Client.Token() == "" { + ret.Message = "No token available so unable read the tune endpoint for this mount. " + ret.Message + } else { + ret.Message = "This token lacks permission to read the tune endpoint for this mount. " + ret.Message + } + + results = append(results, &ret) + return + } + req, err := StringList(h.TuneData["passthrough_request_headers"]) if err != nil { return nil, fmt.Errorf("unable to parse value from server for passthrough_request_headers: %w", err) @@ -74,7 +93,7 @@ func (h *AllowIfModifiedSince) Evaluate(e *Executor) (results []*Result, err err return nil, fmt.Errorf("unable to parse value from server for allowed_response_headers: %w", err) } - var foundIMS bool = false + foundIMS := false for _, param := range req { if strings.EqualFold(param, "If-Modified-Since") { foundIMS = true @@ -82,7 +101,7 @@ func (h *AllowIfModifiedSince) Evaluate(e *Executor) (results []*Result, err err } } - var foundLM bool = false + foundLM := false for _, param := range resp { if strings.EqualFold(param, "Last-Modified") { foundLM = true diff --git a/command/healthcheck/pki_audit_visibility.go b/command/healthcheck/pki_audit_visibility.go index 5fb761d6081d..1984fb97d7ad 100644 --- a/command/healthcheck/pki_audit_visibility.go +++ b/command/healthcheck/pki_audit_visibility.go @@ -58,6 +58,7 @@ type AuditVisibility struct { IgnoredParameters map[string]bool TuneData map[string]interface{} + Fetcher *PathFetch } func NewAuditVisibilityCheck() Check { @@ -100,29 +101,46 @@ func (h *AuditVisibility) LoadConfig(config map[string]interface{}) error { } func (h *AuditVisibility) FetchResources(e *Executor) error { - exit, _, data, err := fetchMountTune(e, func() { + var exit bool + var err error + + exit, h.Fetcher, h.TuneData, err = fetchMountTune(e, func() { h.UnsupportedVersion = true }) - if exit { + + if exit || err != nil { return err } - - h.TuneData = data - return nil } func (h *AuditVisibility) Evaluate(e *Executor) (results []*Result, err error) { if h.UnsupportedVersion { - // Shouldn't happen; /certs has been around forever. ret := Result{ Status: ResultInvalidVersion, - Endpoint: "/{{mount}}/certs", - Message: "This health check requires Vault 1.11+ but an earlier version of Vault Server was contacted, preventing this health check from running.", + Endpoint: "/sys/mounts/{{mount}}/tune", + Message: "This health check requires Vault 1.9+ but an earlier version of Vault Server was contacted, preventing this health check from running.", } return []*Result{&ret}, nil } + if h.Fetcher.IsSecretPermissionsError() { + ret := Result{ + Status: ResultInsufficientPermissions, + Endpoint: "/sys/mounts/{{mount}}/tune", + Message: "Without this information, this health check is unable to function.", + } + + if e.Client.Token() == "" { + ret.Message = "No token available so unable read the tune endpoint for this mount. " + ret.Message + } else { + ret.Message = "This token lacks permission to read the tune endpoint for this mount. " + ret.Message + } + + results = append(results, &ret) + return + } + sourceMap := map[string][]string{ "audit_non_hmac_request_keys": VisibleReqParams, "audit_non_hmac_response_keys": VisibleRespParams, diff --git a/command/healthcheck/pki_tidy_last_run.go b/command/healthcheck/pki_tidy_last_run.go index e079212333d9..6fed74d33964 100644 --- a/command/healthcheck/pki_tidy_last_run.go +++ b/command/healthcheck/pki_tidy_last_run.go @@ -93,7 +93,7 @@ func (h *TidyLastRun) Evaluate(e *Executor) (results []*Result, err error) { ret := Result{ Status: ResultInsufficientPermissions, Endpoint: "/{{mount}}/tidy-status", - Message: "Without this information, this health check is unable tof unction.", + Message: "Without this information, this health check is unable to function.", } if e.Client.Token() == "" { diff --git a/command/healthcheck/shared.go b/command/healthcheck/shared.go index 17f8859ae2d4..9f2b05051766 100644 --- a/command/healthcheck/shared.go +++ b/command/healthcheck/shared.go @@ -35,7 +35,7 @@ func StringList(source interface{}) ([]string, error) { func fetchMountTune(e *Executor, versionError func()) (bool, *PathFetch, map[string]interface{}, error) { tuneRet, err := e.FetchIfNotFetched(logical.ReadOperation, "/sys/mounts/{{mount}}/tune") if err != nil { - return true, nil, nil, err + return true, nil, nil, fmt.Errorf("failed to fetch mount tune information: %w", err) } if !tuneRet.IsSecretOK() { @@ -43,7 +43,7 @@ func fetchMountTune(e *Executor, versionError func()) (bool, *PathFetch, map[str versionError() } - return true, nil, nil, nil + return true, tuneRet, nil, nil } var data map[string]interface{} = nil From 6d1b7baaf7920a9f5be3f649e621bfa7171d0dd2 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Wed, 22 Feb 2023 10:49:17 -0500 Subject: [PATCH 034/231] backport of UI: Remove Wizard (#19239) Co-authored-by: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com> --- changelog/19220.txt | 3 ++ ui/app/app.js | 13 +------- ui/app/components/auth-config-form/config.js | 4 --- ui/app/components/auth-config-form/options.js | 4 --- ui/app/components/auth-info.js | 6 ---- ui/app/components/database-connection.js | 8 ----- ui/app/components/database-role-edit.js | 7 ----- ui/app/components/generate-credentials.js | 23 +++----------- ui/app/components/keymgmt/distribute.js | 11 ------- ui/app/components/mount-backend-form.js | 14 --------- ui/app/components/role-edit.js | 17 ----------- ui/app/components/secret-create-or-update.js | 4 --- ui/app/components/secret-edit.js | 5 ---- ui/app/components/splash-page.js | 2 +- ui/app/components/tool-actions-form.js | 4 --- ui/app/components/transit-edit.js | 8 ----- ui/app/controllers/vault/cluster/init.js | 5 ---- .../vault/cluster/policies/index.js | 4 --- .../controllers/vault/cluster/policy/edit.js | 4 --- .../vault/cluster/settings/auth/enable.js | 3 -- .../cluster/settings/mount-secret-backend.js | 5 +--- ui/app/controllers/vault/cluster/unseal.js | 9 +----- ui/app/routes/application.js | 24 --------------- .../vault/cluster/access/method/item.js | 1 - .../vault/cluster/access/method/item/list.js | 1 - .../vault/cluster/access/method/section.js | 7 +---- ui/app/routes/vault/cluster/auth.js | 12 -------- ui/app/routes/vault/cluster/init.js | 11 +------ .../routes/vault/cluster/policies/create.js | 8 ----- ui/app/routes/vault/cluster/policies/index.js | 7 ----- ui/app/routes/vault/cluster/policy/edit.js | 11 +------ ui/app/routes/vault/cluster/policy/show.js | 7 ----- .../cluster/secrets/backend/configuration.js | 4 --- .../cluster/secrets/backend/create-root.js | 8 ----- ui/app/routes/vault/cluster/tools/tool.js | 6 ---- ui/app/routes/vault/cluster/unseal.js | 10 +------ ui/app/templates/components/auth-info.hbs | 5 ---- .../components/mount-backend-form.hbs | 1 - ui/app/templates/components/splash-page.hbs | 30 +++++++++---------- ui/app/templates/vault/cluster.hbs | 16 +++++----- ui/app/templates/vault/cluster/unseal.hbs | 1 - ui/lib/kmip/addon/engine.js | 1 - ui/lib/pki/addon/engine.js | 1 - .../addon/components/replication-summary.js | 1 - ui/lib/replication/addon/engine.js | 11 +------ 45 files changed, 37 insertions(+), 310 deletions(-) create mode 100644 changelog/19220.txt diff --git a/changelog/19220.txt b/changelog/19220.txt new file mode 100644 index 000000000000..cbfe7e5a9336 --- /dev/null +++ b/changelog/19220.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: remove wizard +``` diff --git a/ui/app/app.js b/ui/app/app.js index ee85a0150fba..115474cda763 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -15,16 +15,7 @@ export default class App extends Application { }, replication: { dependencies: { - services: [ - 'auth', - 'flash-messages', - 'namespace', - 'replication-mode', - 'router', - 'store', - 'version', - 'wizard', - ], + services: ['auth', 'flash-messages', 'namespace', 'replication-mode', 'router', 'store', 'version'], externalRoutes: { replication: 'vault.cluster.replication.index', }, @@ -41,7 +32,6 @@ export default class App extends Application { 'router', 'store', 'version', - 'wizard', 'secret-mount-path', ], externalRoutes: { @@ -69,7 +59,6 @@ export default class App extends Application { 'secret-mount-path', 'store', 'version', - 'wizard', ], externalRoutes: { secrets: 'vault.cluster.secrets.backends', diff --git a/ui/app/components/auth-config-form/config.js b/ui/app/components/auth-config-form/config.js index 0e21139f2ca1..bb0c2bc77be4 100644 --- a/ui/app/components/auth-config-form/config.js +++ b/ui/app/components/auth-config-form/config.js @@ -23,7 +23,6 @@ const AuthConfigBase = Component.extend({ flashMessages: service(), router: service(), - wizard: service(), saveModel: task( waitFor(function* () { try { @@ -36,9 +35,6 @@ const AuthConfigBase = Component.extend({ } return; } - if (this.wizard.currentMachine === 'authentication' && this.wizard.featureState === 'config') { - this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE'); - } this.router.transitionTo('vault.cluster.access.methods').followRedirects(); this.flashMessages.success('The configuration was saved successfully.'); }) diff --git a/ui/app/components/auth-config-form/options.js b/ui/app/components/auth-config-form/options.js index 191c9c136514..a50bf96bee76 100644 --- a/ui/app/components/auth-config-form/options.js +++ b/ui/app/components/auth-config-form/options.js @@ -20,7 +20,6 @@ import { waitFor } from '@ember/test-waiters'; export default AuthConfigComponent.extend({ flashMessages: service(), router: service(), - wizard: service(), saveModel: task( waitFor(function* () { @@ -49,9 +48,6 @@ export default AuthConfigComponent.extend({ } return; } - if (this.wizard.currentMachine === 'authentication' && this.wizard.featureState === 'config') { - this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE'); - } this.router.transitionTo('vault.cluster.access.methods').followRedirects(); this.flashMessages.success('The configuration was saved successfully.'); }) diff --git a/ui/app/components/auth-info.js b/ui/app/components/auth-info.js index 1ca7f9cef994..37430e69bebb 100644 --- a/ui/app/components/auth-info.js +++ b/ui/app/components/auth-info.js @@ -17,7 +17,6 @@ import { tracked } from '@glimmer/tracking'; */ export default class AuthInfoComponent extends Component { @service auth; - @service wizard; @service router; @tracked fakeRenew = false; @@ -36,11 +35,6 @@ export default class AuthInfoComponent extends Component { this.router.transitionTo(...arguments); } - @action - restartGuide() { - this.wizard.restartGuide(); - } - @action renewToken() { this.fakeRenew = true; diff --git a/ui/app/components/database-connection.js b/ui/app/components/database-connection.js index c71c7ac776f1..ea32bcc77b9a 100644 --- a/ui/app/components/database-connection.js +++ b/ui/app/components/database-connection.js @@ -19,7 +19,6 @@ export default class DatabaseConnectionEdit extends Component { @service store; @service router; @service flashMessages; - @service wizard; @tracked showPasswordField = false; // used for edit mode @@ -27,13 +26,6 @@ export default class DatabaseConnectionEdit extends Component { @tracked showSaveModal = false; // used for create mode - constructor() { - super(...arguments); - if (this.wizard.featureState === 'details' || this.wizard.featureState === 'connection') { - this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE', 'database'); - } - } - rotateCredentials(backend, name) { const adapter = this.store.adapterFor('database/connection'); return adapter.rotateRootCredentials(backend, name); diff --git a/ui/app/components/database-role-edit.js b/ui/app/components/database-role-edit.js index 64ea98eacebe..70e8077faf56 100644 --- a/ui/app/components/database-role-edit.js +++ b/ui/app/components/database-role-edit.js @@ -9,17 +9,10 @@ const SHOW_ROUTE = 'vault.cluster.secrets.backend.show'; export default class DatabaseRoleEdit extends Component { @service router; @service flashMessages; - @service wizard; @service store; constructor() { super(...arguments); - if ( - this.wizard.featureState === 'displayConnection' || - this.wizard.featureState === 'displayRoleDatabase' - ) { - this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE', 'database'); - } if (this.args.initialKey) { this.args.model.database = [this.args.initialKey]; } diff --git a/ui/app/components/generate-credentials.js b/ui/app/components/generate-credentials.js index 74526b209443..7d832a1d2cd6 100644 --- a/ui/app/components/generate-credentials.js +++ b/ui/app/components/generate-credentials.js @@ -26,7 +26,6 @@ const MODEL_TYPES = { }; export default Component.extend({ - wizard: service(), store: service(), router: service(), // set on the component @@ -58,13 +57,6 @@ export default Component.extend({ this.createOrReplaceModel(); }, - didReceiveAttrs() { - this._super(); - if (this.wizard.featureState === 'displayRole') { - this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE', this.backendType); - } - }, - willDestroy() { if (!this.model.isDestroyed && !this.model.isDestroying) { this.model.unloadRecord(); @@ -98,17 +90,10 @@ export default Component.extend({ create() { const model = this.model; this.set('loading', true); - this.model - .save() - .catch(() => { - if (this.wizard.featureState === 'credentials') { - this.wizard.transitionFeatureMachine(this.wizard.featureState, 'ERROR', this.backendType); - } - }) - .finally(() => { - model.set('hasGenerated', true); - this.set('loading', false); - }); + this.model.save().finally(() => { + model.set('hasGenerated', true); + this.set('loading', false); + }); }, codemirrorUpdated(attr, val, codemirror) { diff --git a/ui/app/components/keymgmt/distribute.js b/ui/app/components/keymgmt/distribute.js index f64620720d34..798a39435644 100644 --- a/ui/app/components/keymgmt/distribute.js +++ b/ui/app/components/keymgmt/distribute.js @@ -35,7 +35,6 @@ export default class KeymgmtDistribute extends Component { @service store; @service flashMessages; @service router; - @service wizard; @tracked keyModel; @tracked isNewKey = false; @@ -57,14 +56,6 @@ export default class KeymgmtDistribute extends Component { this.getKeyInfo(this.args.key); } this.formData.operations = []; - this.updateWizard('nextStep'); - } - - updateWizard(key) { - // wizard will pause unless we manually continue it -- verify that keymgmt tutorial is in progress - if (this.wizard[key] === 'distribute') { - this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE', 'keymgmt'); - } } get keyTypes() { @@ -198,8 +189,6 @@ export default class KeymgmtDistribute extends Component { this.store.clearDataset('keymgmt/key'); const providerModel = this.store.peekRecord('keymgmt/provider', provider); providerModel.fetchKeys(providerModel.keys?.meta?.currentPage || 1); - // move wizard forward if tutorial is in progress - this.updateWizard('featureState'); this.args.onClose(); }) .catch((e) => { diff --git a/ui/app/components/mount-backend-form.js b/ui/app/components/mount-backend-form.js index 8d5f2d741c2c..585c91a17633 100644 --- a/ui/app/components/mount-backend-form.js +++ b/ui/app/components/mount-backend-form.js @@ -21,7 +21,6 @@ import { methods } from 'vault/helpers/mountable-auth-methods'; export default class MountBackendForm extends Component { @service store; - @service wizard; @service flashMessages; // validation related properties @@ -139,22 +138,9 @@ export default class MountBackendForm extends Component { this.args.mountModel[name] = value; } - @action - onTypeChange(path, value) { - if (path === 'type') { - this.wizard.set('componentState', value); - } - } - @action setMountType(value) { this.args.mountModel.type = value; this.checkPathChange(value); - if (value) { - this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE', this.args.mountModel.type); - } else if (this.wizard.featureState === 'idle') { - // resets wizard - this.wizard.transitionFeatureMachine(this.wizard.featureState, 'RESET', this.args.mountModel.type); - } } } diff --git a/ui/app/components/role-edit.js b/ui/app/components/role-edit.js index 861e3d7d125a..aead8aaeabec 100644 --- a/ui/app/components/role-edit.js +++ b/ui/app/components/role-edit.js @@ -12,7 +12,6 @@ const SHOW_ROUTE = 'vault.cluster.secrets.backend.show'; export default Component.extend(FocusOnInsertMixin, { router: service(), - wizard: service(), mode: null, emptyData: '{\n}', @@ -21,19 +20,6 @@ export default Component.extend(FocusOnInsertMixin, { model: null, requestInFlight: or('model.isLoading', 'model.isReloading', 'model.isSaving'), - didReceiveAttrs() { - this._super(...arguments); - if ( - (this.wizard.featureState === 'details' && this.mode === 'create') || - (this.wizard.featureState === 'role' && this.mode === 'show') - ) { - this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE', this.backendType); - } - if (this.wizard.featureState === 'displayRole') { - this.wizard.transitionFeatureMachine(this.wizard.featureState, 'NOOP', this.backendType); - } - }, - willDestroyElement() { this._super(...arguments); if (this.model && this.model.isError) { @@ -69,9 +55,6 @@ export default Component.extend(FocusOnInsertMixin, { const model = this.model; return model[method]().then(() => { if (!model.isError) { - if (this.wizard.featureState === 'role') { - this.wizard.transitionFeatureMachine('role', 'CONTINUE', this.backendType); - } successCallback(model); } }); diff --git a/ui/app/components/secret-create-or-update.js b/ui/app/components/secret-create-or-update.js index 634fb4b05ffe..7e93fd0eb82c 100644 --- a/ui/app/components/secret-create-or-update.js +++ b/ui/app/components/secret-create-or-update.js @@ -48,7 +48,6 @@ export default class SecretCreateOrUpdate extends Component { @service controlGroup; @service router; @service store; - @service wizard; @action setup(elem, [secretData, model, mode]) { @@ -164,9 +163,6 @@ export default class SecretCreateOrUpdate extends Component { }); } saveComplete(callback, key) { - if (this.wizard.featureState === 'secret') { - this.wizard.transitionFeatureMachine('secret', 'CONTINUE'); - } callback(key); } transitionToRoute() { diff --git a/ui/app/components/secret-edit.js b/ui/app/components/secret-edit.js index 2a9ab3a0efe8..1818bbadec84 100644 --- a/ui/app/components/secret-edit.js +++ b/ui/app/components/secret-edit.js @@ -27,7 +27,6 @@ import { maybeQueryRecord } from 'vault/macros/maybe-query-record'; import { alias, or } from '@ember/object/computed'; export default class SecretEdit extends Component { - @service wizard; @service store; @tracked secretData = null; @@ -43,10 +42,6 @@ export default class SecretEdit extends Component { } this.secretData = KVObject.create({ content: [] }).fromJSON(model.secretData); this.codemirrorString = this.secretData.toJSONString(); - if (this.wizard.featureState === 'details' && this.args.mode === 'create') { - const engine = model.backend.includes('kv') ? 'kv' : model.backend; - this.wizard.transitionFeatureMachine('details', 'CONTINUE', engine); - } } @maybeQueryRecord( diff --git a/ui/app/components/splash-page.js b/ui/app/components/splash-page.js index 8a1d4f5461cd..e5560f76dcf5 100644 --- a/ui/app/components/splash-page.js +++ b/ui/app/components/splash-page.js @@ -9,7 +9,7 @@ * content here * { if (!key.isError) { - if (this.wizard.featureState === 'secret') { - this.wizard.transitionFeatureMachine('secret', 'CONTINUE'); - } else { - if (this.wizard.featureState === 'encryption') { - this.wizard.transitionFeatureMachine('encryption', 'CONTINUE', 'transit'); - } - } successCallback(key); } }); diff --git a/ui/app/controllers/vault/cluster/init.js b/ui/app/controllers/vault/cluster/init.js index 9c4439bfa6af..4707bb071d88 100644 --- a/ui/app/controllers/vault/cluster/init.js +++ b/ui/app/controllers/vault/cluster/init.js @@ -1,5 +1,4 @@ import { computed } from '@ember/object'; -import { inject as service } from '@ember/service'; import Controller from '@ember/controller'; const DEFAULTS = { @@ -12,8 +11,6 @@ const DEFAULTS = { }; export default Controller.extend(DEFAULTS, { - wizard: service(), - reset() { this.setProperties(DEFAULTS); }, @@ -22,8 +19,6 @@ export default Controller.extend(DEFAULTS, { this.set('loading', false); this.set('keyData', resp); this.model.reload(); - this.wizard.set('initEvent', 'SAVE'); - this.wizard.transitionTutorialMachine(this.wizard.currentState, 'TOSAVE'); }, initError(e) { diff --git a/ui/app/controllers/vault/cluster/policies/index.js b/ui/app/controllers/vault/cluster/policies/index.js index b75e2703a92a..e8d617f755e2 100644 --- a/ui/app/controllers/vault/cluster/policies/index.js +++ b/ui/app/controllers/vault/cluster/policies/index.js @@ -4,7 +4,6 @@ import Controller from '@ember/controller'; export default Controller.extend({ flashMessages: service(), - wizard: service(), queryParams: { page: 'page', @@ -58,9 +57,6 @@ export default Controller.extend({ // this will clear the dataset cache on the store this.send('reload'); flash.success(`${policyType.toUpperCase()} policy "${name}" was successfully deleted.`); - if (this.wizard.featureState === 'delete') { - this.wizard.transitionFeatureMachine('delete', 'CONTINUE', policyType); - } }) .catch((e) => { const errors = e.errors ? e.errors.join('') : e.message; diff --git a/ui/app/controllers/vault/cluster/policy/edit.js b/ui/app/controllers/vault/cluster/policy/edit.js index 927e70ea44ef..dd5deff9bfcc 100644 --- a/ui/app/controllers/vault/cluster/policy/edit.js +++ b/ui/app/controllers/vault/cluster/policy/edit.js @@ -5,7 +5,6 @@ import { inject as service } from '@ember/service'; export default class PolicyEditController extends Controller { @service router; @service flashMessages; - @service wizard; @action async deletePolicy() { @@ -14,9 +13,6 @@ export default class PolicyEditController extends Controller { await this.model.destroyRecord(); this.flashMessages.success(`${policyType.toUpperCase()} policy "${name}" was successfully deleted.`); this.router.transitionTo('vault.cluster.policies', policyType); - if (this.wizard.featureState === 'delete') { - this.wizard.transitionFeatureMachine('delete', 'CONTINUE', policyType); - } } catch (error) { this.model.rollbackAttributes(); const errors = error.errors ? error.errors.join('. ') : error.message; diff --git a/ui/app/controllers/vault/cluster/settings/auth/enable.js b/ui/app/controllers/vault/cluster/settings/auth/enable.js index d0e02c950797..da57dddab4b5 100644 --- a/ui/app/controllers/vault/cluster/settings/auth/enable.js +++ b/ui/app/controllers/vault/cluster/settings/auth/enable.js @@ -1,11 +1,8 @@ -import { inject as service } from '@ember/service'; import Controller from '@ember/controller'; export default Controller.extend({ - wizard: service(), actions: { onMountSuccess: function (type, path) { - this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE', type); const transition = this.transitionToRoute('vault.cluster.settings.auth.configure', path); return transition.followRedirects(); }, diff --git a/ui/app/controllers/vault/cluster/settings/mount-secret-backend.js b/ui/app/controllers/vault/cluster/settings/mount-secret-backend.js index 4b2c306c36e5..0991ac9f33af 100644 --- a/ui/app/controllers/vault/cluster/settings/mount-secret-backend.js +++ b/ui/app/controllers/vault/cluster/settings/mount-secret-backend.js @@ -7,7 +7,6 @@ import { action } from '@ember/object'; const SUPPORTED_BACKENDS = supportedSecretBackends(); export default class MountSecretBackendController extends Controller { - @service wizard; @service router; @action @@ -27,8 +26,6 @@ export default class MountSecretBackendController extends Controller { } else { transition = this.router.transitionTo('vault.cluster.secrets.backends'); } - return transition.followRedirects().then(() => { - this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE', type); - }); + return transition.followRedirects(); } } diff --git a/ui/app/controllers/vault/cluster/unseal.js b/ui/app/controllers/vault/cluster/unseal.js index 8d1e76adbdc0..ea5d4a8ce6a2 100644 --- a/ui/app/controllers/vault/cluster/unseal.js +++ b/ui/app/controllers/vault/cluster/unseal.js @@ -1,22 +1,15 @@ -import { inject as service } from '@ember/service'; import Controller from '@ember/controller'; export default Controller.extend({ - wizard: service(), showLicenseError: false, actions: { - transitionToCluster(resp) { + transitionToCluster() { return this.model.reload().then(() => { - this.wizard.transitionTutorialMachine(this.wizard.currentState, 'CONTINUE', resp); return this.transitionToRoute('vault.cluster', this.model.name); }); }, - setUnsealState(resp) { - this.wizard.set('componentState', resp); - }, - isUnsealed(data) { return data.sealed === false; }, diff --git a/ui/app/routes/application.js b/ui/app/routes/application.js index 1f8c8ec09852..c5baa0a055b3 100644 --- a/ui/app/routes/application.js +++ b/ui/app/routes/application.js @@ -1,12 +1,10 @@ import { inject as service } from '@ember/service'; -import { next } from '@ember/runloop'; import Route from '@ember/routing/route'; import ControlGroupError from 'vault/lib/control-group-error'; export default Route.extend({ controlGroup: service(), routing: service('router'), - wizard: service(), namespaceService: service('namespace'), featureFlagService: service('featureFlag'), @@ -60,28 +58,6 @@ export default Route.extend({ return true; }, - didTransition() { - const wizard = this.wizard; - - if (wizard.get('currentState') !== 'active.feature') { - return true; - } - next(() => { - const applicationURL = this.routing.currentURL; - const activeRoute = this.routing.currentRouteName; - - if (this.wizard.setURLAfterTransition) { - this.set('wizard.setURLAfterTransition', false); - this.set('wizard.expectedURL', applicationURL); - this.set('wizard.expectedRouteName', activeRoute); - } - const expectedRouteName = this.wizard.expectedRouteName; - if (this.routing.isActive(expectedRouteName) === false) { - wizard.transitionTutorialMachine(wizard.get('currentState'), 'PAUSE'); - } - }); - return true; - }, }, async beforeModel() { diff --git a/ui/app/routes/vault/cluster/access/method/item.js b/ui/app/routes/vault/cluster/access/method/item.js index 9371a9a7e501..f0416d8ab119 100644 --- a/ui/app/routes/vault/cluster/access/method/item.js +++ b/ui/app/routes/vault/cluster/access/method/item.js @@ -3,7 +3,6 @@ import Route from '@ember/routing/route'; import { singularize } from 'ember-inflector'; export default Route.extend({ - wizard: service(), pathHelp: service('path-help'), beforeModel() { diff --git a/ui/app/routes/vault/cluster/access/method/item/list.js b/ui/app/routes/vault/cluster/access/method/item/list.js index d2e4315e891d..8ae1ef61359d 100644 --- a/ui/app/routes/vault/cluster/access/method/item/list.js +++ b/ui/app/routes/vault/cluster/access/method/item/list.js @@ -5,7 +5,6 @@ import ListRoute from 'vault/mixins/list-route'; export default Route.extend(ListRoute, { store: service(), - wizard: service(), pathHelp: service('path-help'), getMethodAndModelInfo() { diff --git a/ui/app/routes/vault/cluster/access/method/section.js b/ui/app/routes/vault/cluster/access/method/section.js index 904f2f5190a2..5a5a29c25ccb 100644 --- a/ui/app/routes/vault/cluster/access/method/section.js +++ b/ui/app/routes/vault/cluster/access/method/section.js @@ -1,11 +1,8 @@ import AdapterError from '@ember-data/adapter/error'; import { set } from '@ember/object'; -import { inject as service } from '@ember/service'; import Route from '@ember/routing/route'; export default Route.extend({ - wizard: service(), - model(params) { const { section_name: section } = params; if (section !== 'configuration') { @@ -13,9 +10,7 @@ export default Route.extend({ set(error, 'httpStatus', 404); throw error; } - const backend = this.modelFor('vault.cluster.access.method'); - this.wizard.transitionFeatureMachine(this.wizard.featureState, 'DETAILS', backend.type); - return backend; + return this.modelFor('vault.cluster.access.method'); }, setupController(controller) { diff --git a/ui/app/routes/vault/cluster/auth.js b/ui/app/routes/vault/cluster/auth.js index 1c24097978ff..7bf2e80f933c 100644 --- a/ui/app/routes/vault/cluster/auth.js +++ b/ui/app/routes/vault/cluster/auth.js @@ -10,7 +10,6 @@ export default ClusterRouteBase.extend({ }, flashMessages: service(), version: service(), - wizard: service(), beforeModel() { return this._super().then(() => { return this.version.fetchFeatures(); @@ -30,15 +29,4 @@ export default ClusterRouteBase.extend({ this.flashMessages.stickyInfo(config.welcomeMessage); } }, - activate() { - this.wizard.set('initEvent', 'LOGIN'); - this.wizard.transitionTutorialMachine(this.wizard.currentState, 'TOLOGIN'); - }, - actions: { - willTransition(transition) { - if (transition.targetName !== this.routeName) { - this.wizard.transitionTutorialMachine(this.wizard.currentState, 'INITDONE'); - } - }, - }, }); diff --git a/ui/app/routes/vault/cluster/init.js b/ui/app/routes/vault/cluster/init.js index 820de51eda0d..1495fc503fc3 100644 --- a/ui/app/routes/vault/cluster/init.js +++ b/ui/app/routes/vault/cluster/init.js @@ -1,12 +1,3 @@ -import { inject as service } from '@ember/service'; import ClusterRoute from './cluster-route-base'; -export default ClusterRoute.extend({ - wizard: service(), - - activate() { - // always start from idle instead of using the current state - this.wizard.transitionTutorialMachine('idle', 'INIT'); - this.wizard.set('initEvent', 'START'); - }, -}); +export default ClusterRoute.extend({}); diff --git a/ui/app/routes/vault/cluster/policies/create.js b/ui/app/routes/vault/cluster/policies/create.js index 66f7813dc32b..c4256b05260b 100644 --- a/ui/app/routes/vault/cluster/policies/create.js +++ b/ui/app/routes/vault/cluster/policies/create.js @@ -6,17 +6,9 @@ import UnsavedModelRoute from 'vault/mixins/unsaved-model-route'; export default Route.extend(UnloadModelRoute, UnsavedModelRoute, { store: service(), version: service(), - wizard: service(), model() { const policyType = this.policyType(); - if ( - policyType === 'acl' && - this.wizard.currentMachine === 'policies' && - this.wizard.featureState === 'idle' - ) { - this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE'); - } if (!this.version.hasSentinel && policyType !== 'acl') { return this.transitionTo('vault.cluster.policies', policyType); } diff --git a/ui/app/routes/vault/cluster/policies/index.js b/ui/app/routes/vault/cluster/policies/index.js index ce4cb27e988a..7650e8bc8336 100644 --- a/ui/app/routes/vault/cluster/policies/index.js +++ b/ui/app/routes/vault/cluster/policies/index.js @@ -6,13 +6,6 @@ import ListRoute from 'core/mixins/list-route'; export default Route.extend(ClusterRoute, ListRoute, { store: service(), version: service(), - wizard: service(), - - activate() { - if (this.wizard.featureState === 'details') { - this.wizard.transitionFeatureMachine('details', 'CONTINUE', this.policyType()); - } - }, shouldReturnEmptyModel(policyType, version) { return policyType !== 'acl' && (version.get('isOSS') || !version.get('hasSentinel')); diff --git a/ui/app/routes/vault/cluster/policy/edit.js b/ui/app/routes/vault/cluster/policy/edit.js index 4b22d1d2f72e..b5a186dd986a 100644 --- a/ui/app/routes/vault/cluster/policy/edit.js +++ b/ui/app/routes/vault/cluster/policy/edit.js @@ -1,13 +1,4 @@ import UnsavedModelRoute from 'vault/mixins/unsaved-model-route'; import ShowRoute from './show'; -import { inject as service } from '@ember/service'; -export default ShowRoute.extend(UnsavedModelRoute, { - wizard: service(), - - activate() { - if (this.wizard.featureState === 'details') { - this.wizard.transitionFeatureMachine('details', 'CONTINUE', this.policyType()); - } - }, -}); +export default ShowRoute.extend(UnsavedModelRoute, {}); diff --git a/ui/app/routes/vault/cluster/policy/show.js b/ui/app/routes/vault/cluster/policy/show.js index 89c27a2b2581..e6d1e9ac2b68 100644 --- a/ui/app/routes/vault/cluster/policy/show.js +++ b/ui/app/routes/vault/cluster/policy/show.js @@ -5,13 +5,6 @@ import { inject as service } from '@ember/service'; export default Route.extend(UnloadModelRoute, { store: service(), - wizard: service(), - - activate() { - if (this.wizard.featureState === 'create') { - this.wizard.transitionFeatureMachine('create', 'CONTINUE', this.policyType()); - } - }, beforeModel() { const params = this.paramsFor(this.routeName); diff --git a/ui/app/routes/vault/cluster/secrets/backend/configuration.js b/ui/app/routes/vault/cluster/secrets/backend/configuration.js index 446be4b4477c..a2f32af08834 100644 --- a/ui/app/routes/vault/cluster/secrets/backend/configuration.js +++ b/ui/app/routes/vault/cluster/secrets/backend/configuration.js @@ -2,13 +2,9 @@ import { inject as service } from '@ember/service'; import Route from '@ember/routing/route'; export default Route.extend({ - wizard: service(), store: service(), async model() { const backend = this.modelFor('vault.cluster.secrets.backend'); - if (this.wizard.featureState === 'list') { - this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE', backend.get('type')); - } if (backend.isV2KV) { const canRead = await this.store .findRecord('capabilities', `${backend.id}/config`) diff --git a/ui/app/routes/vault/cluster/secrets/backend/create-root.js b/ui/app/routes/vault/cluster/secrets/backend/create-root.js index 5041d60fcec1..ebaa2585b2fc 100644 --- a/ui/app/routes/vault/cluster/secrets/backend/create-root.js +++ b/ui/app/routes/vault/cluster/secrets/backend/create-root.js @@ -29,7 +29,6 @@ const transformModel = (queryParams) => { export default EditBase.extend({ store: service(), - wizard: service(), createModel(transition) { const { backend } = this.paramsFor('vault.cluster.secrets.backend'); @@ -44,9 +43,6 @@ export default EditBase.extend({ modelType = 'database/role'; } if (modelType !== 'secret' && modelType !== 'secret-v2') { - if (this.wizard.featureState === 'details' && this.wizard.componentState === 'transit') { - this.wizard.transitionFeatureMachine('details', 'CONTINUE', 'transit'); - } return this.store.createRecord(modelType); } // create record in capabilities that checks for access to create metadata @@ -59,10 +55,6 @@ export default EditBase.extend({ }, model(params, transition) { - // wizard will pause unless we manually continue it -- verify that keymgmt tutorial is in progress - if (params.itemType === 'provider' && this.wizard.nextStep === 'provider') { - this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE', 'keymgmt'); - } return hash({ secret: this.createModel(transition), capabilities: {}, diff --git a/ui/app/routes/vault/cluster/tools/tool.js b/ui/app/routes/vault/cluster/tools/tool.js index c5030f822e9a..0c5ecfde7af6 100644 --- a/ui/app/routes/vault/cluster/tools/tool.js +++ b/ui/app/routes/vault/cluster/tools/tool.js @@ -1,10 +1,7 @@ -import { inject as service } from '@ember/service'; import Route from '@ember/routing/route'; import { toolsActions } from 'vault/helpers/tools-actions'; export default Route.extend({ - wizard: service(), - beforeModel(transition) { const supportedActions = toolsActions(); const { selected_action: selectedAction } = this.paramsFor(this.routeName); @@ -26,9 +23,6 @@ export default Route.extend({ actions: { didTransition() { const params = this.paramsFor(this.routeName); - if (this.wizard.currentMachine === 'tools') { - this.wizard.transitionFeatureMachine(this.wizard.featureState, params.selected_action.toUpperCase()); - } /* eslint-disable-next-line ember/no-controller-access-in-routes */ this.controller.setProperties(params); return true; diff --git a/ui/app/routes/vault/cluster/unseal.js b/ui/app/routes/vault/cluster/unseal.js index 917588e09426..1495fc503fc3 100644 --- a/ui/app/routes/vault/cluster/unseal.js +++ b/ui/app/routes/vault/cluster/unseal.js @@ -1,11 +1,3 @@ -import { inject as service } from '@ember/service'; import ClusterRoute from './cluster-route-base'; -export default ClusterRoute.extend({ - wizard: service(), - - activate() { - this.wizard.set('initEvent', 'UNSEAL'); - this.wizard.transitionTutorialMachine(this.wizard.currentState, 'TOUNSEAL'); - }, -}); +export default ClusterRoute.extend({}); diff --git a/ui/app/templates/components/auth-info.hbs b/ui/app/templates/components/auth-info.hbs index bed119be924a..29cca1d20d33 100644 --- a/ui/app/templates/components/auth-info.hbs +++ b/ui/app/templates/components/auth-info.hbs @@ -23,11 +23,6 @@ {{/if}} -
  • - -
  • diff --git a/ui/app/templates/components/splash-page.hbs b/ui/app/templates/components/splash-page.hbs index 58645f03b2af..105b08e9fcab 100644 --- a/ui/app/templates/components/splash-page.hbs +++ b/ui/app/templates/components/splash-page.hbs @@ -12,26 +12,24 @@ {{/if}} -{{! bypass UiWizard and container styling }} +{{! bypass container styling }} {{#if @hasAltContent}} {{yield (hash altContent=(component "splash-page/splash-content"))}} {{else}} - -
    -
    -
    -
    - {{yield (hash header=(component "splash-page/splash-header"))}} -
    -
    - {{yield (hash sub-header=(component "splash-page/splash-header"))}} -
    - - {{yield (hash footer=(component "splash-page/splash-content"))}} +
    +
    +
    +
    + {{yield (hash header=(component "splash-page/splash-header"))}}
    +
    + {{yield (hash sub-header=(component "splash-page/splash-header"))}} +
    + + {{yield (hash footer=(component "splash-page/splash-content"))}}
    - +
    {{/if}} \ No newline at end of file diff --git a/ui/app/templates/vault/cluster.hbs b/ui/app/templates/vault/cluster.hbs index c7561ca784fe..99c72125aefb 100644 --- a/ui/app/templates/vault/cluster.hbs +++ b/ui/app/templates/vault/cluster.hbs @@ -132,15 +132,13 @@ {{else}} {{#if this.showNav}} - -
    -
    - - {{outlet}} - -
    -
    -
    +
    +
    + + {{outlet}} + +
    +
    {{else}} {{outlet}} {{/if}} diff --git a/ui/app/templates/vault/cluster/unseal.hbs b/ui/app/templates/vault/cluster/unseal.hbs index 27409790ae12..74ad89b1903b 100644 --- a/ui/app/templates/vault/cluster/unseal.hbs +++ b/ui/app/templates/vault/cluster/unseal.hbs @@ -50,7 +50,6 @@
    Date: Wed, 22 Feb 2023 12:16:04 -0500 Subject: [PATCH 035/231] Backport of Update x/net and x/crypto/ssh into release/1.13.x (#19285) * Update x/net and x/crypto/ssh * go mod tidy --------- Co-authored-by: Tom Proctor --- api/go.mod | 8 ++++---- api/go.sum | 16 ++++++++-------- go.mod | 10 +++++----- go.sum | 18 +++++++++++------- sdk/go.mod | 10 +++++----- sdk/go.sum | 20 ++++++++++---------- 6 files changed, 43 insertions(+), 39 deletions(-) diff --git a/api/go.mod b/api/go.mod index 3aa9e832615c..62288bd371f0 100644 --- a/api/go.mod +++ b/api/go.mod @@ -17,7 +17,7 @@ require ( github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 github.com/hashicorp/hcl v1.0.0 github.com/mitchellh/mapstructure v1.5.0 - golang.org/x/net v0.5.0 + golang.org/x/net v0.7.0 golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 gopkg.in/square/go-jose.v2 v2.5.1 ) @@ -30,7 +30,7 @@ require ( github.com/mattn/go-isatty v0.0.12 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect - golang.org/x/crypto v0.5.0 // indirect - golang.org/x/sys v0.4.0 // indirect - golang.org/x/text v0.6.0 // indirect + golang.org/x/crypto v0.6.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect ) diff --git a/api/go.sum b/api/go.sum index b89be959556b..808c56f7fe08 100644 --- a/api/go.sum +++ b/api/go.sum @@ -62,19 +62,19 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= -golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/go.mod b/go.mod index 2285d45e1c89..245f89f337b1 100644 --- a/go.mod +++ b/go.mod @@ -194,12 +194,12 @@ require ( go.opentelemetry.io/otel/trace v1.11.2 go.uber.org/atomic v1.9.0 go.uber.org/goleak v1.1.12 - golang.org/x/crypto v0.5.0 - golang.org/x/net v0.5.0 + golang.org/x/crypto v0.6.0 + golang.org/x/net v0.7.0 golang.org/x/oauth2 v0.4.0 golang.org/x/sync v0.1.0 - golang.org/x/sys v0.4.0 - golang.org/x/term v0.4.0 + golang.org/x/sys v0.5.0 + golang.org/x/term v0.5.0 golang.org/x/tools v0.1.12 google.golang.org/api v0.109.0 google.golang.org/grpc v1.51.0 @@ -444,7 +444,7 @@ require ( go.uber.org/multierr v1.7.0 // indirect go.uber.org/zap v1.19.1 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/text v0.6.0 // indirect + golang.org/x/text v0.7.0 // indirect golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index c7d498499cd1..3321e6ea9550 100644 --- a/go.sum +++ b/go.sum @@ -995,7 +995,6 @@ github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJ github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.0 h1:pSjQfW3vPtrOTcasTUKgCTQT7OGPPTTMVRrOfU6FJD8= github.com/hashicorp/go-kms-wrapping/entropy/v2 v2.0.0/go.mod h1:xvb32K2keAc+R8DSFG2IwDcydK9DBQE+fGA5fsw6hSk= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.7/go.mod h1:sDQAfwJGv25uGPZA04x87ERglCG6avnRcBT9wYoMII8= github.com/hashicorp/go-kms-wrapping/v2 v2.0.8 h1:9Q2lu1YbbmiAgvYZ7Pr31RdlVonUpX+mmDL7Z7qTA2U= github.com/hashicorp/go-kms-wrapping/v2 v2.0.8/go.mod h1:qTCjxGig/kjuj3hk1z8pOUrzbse/GxB1tGfbrq8tGJg= github.com/hashicorp/go-kms-wrapping/wrappers/aead/v2 v2.0.7-1 h1:ZV26VJYcITBom0QqYSUOIj4HOHCVPEFjLqjxyXV/AbA= @@ -1970,8 +1969,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220313003712-b769efc7c000/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -2077,8 +2076,10 @@ golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220725212005-46097bf591d3/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2227,15 +2228,17 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2248,8 +2251,9 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/sdk/go.mod b/sdk/go.mod index 297a8b665967..a51b3f919be3 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -37,8 +37,8 @@ require ( github.com/ryanuber/go-glob v1.0.0 github.com/stretchr/testify v1.7.0 go.uber.org/atomic v1.9.0 - golang.org/x/crypto v0.5.0 - golang.org/x/text v0.6.0 + golang.org/x/crypto v0.6.0 + golang.org/x/text v0.7.0 google.golang.org/grpc v1.41.0 google.golang.org/protobuf v1.27.1 ) @@ -59,9 +59,9 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/stretchr/objx v0.1.1 // indirect - golang.org/x/net v0.5.0 // indirect - golang.org/x/sys v0.4.0 // indirect - golang.org/x/term v0.4.0 // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/term v0.5.0 // indirect google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) diff --git a/sdk/go.sum b/sdk/go.sum index 3b02ce469f16..b33acfc9cfe3 100644 --- a/sdk/go.sum +++ b/sdk/go.sum @@ -223,8 +223,8 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -239,8 +239,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -264,15 +264,15 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= From 41e384f8050c2b19b55301cf5c4573d991413983 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Wed, 22 Feb 2023 14:53:55 -0500 Subject: [PATCH 036/231] backport of commit 719391684925715019d7e0efdf07ae48133a0db7 (#19298) Co-authored-by: Alexander Scheel --- website/content/api-docs/secret/pki.mdx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/website/content/api-docs/secret/pki.mdx b/website/content/api-docs/secret/pki.mdx index 66abcb6aca9d..3b9c14e09dce 100644 --- a/website/content/api-docs/secret/pki.mdx +++ b/website/content/api-docs/secret/pki.mdx @@ -3790,6 +3790,17 @@ expiration time. performance of OCSP and CRL building, by shifting work to a tidy operation instead. +~> Note: With multiple issuers, a CA which issued a particular revoked + certificate may be removed and re-added, resulting in a different issuer + ID value. When building CRLs, these links are automatically updated for any + missing or added issuers, but during OCSP this value is computed and then + discarded, potentially causing a performance penalty on each request. + During regular CA operations, it is not necessary to run this operation. +

    + It is suggested to run this tidy when removing or importing new issuers and + on the first upgrade to a post-1.11 Vault version, but otherwise not to run + it during automatic tidy operations. + - `tidy_expired_issuers` `(bool: false)` - Set to true to automatically remove expired issuers after the `issuer_safety_buffer` duration has elapsed. We log the issuer certificate on removal to allow recovery; no keys are removed From b12fcad4d28ab7243544ff71a4d7d6401d94b042 Mon Sep 17 00:00:00 2001 From: claire bontempo <68122737+hellobontempo@users.noreply.github.com> Date: Wed, 22 Feb 2023 13:32:59 -0800 Subject: [PATCH 037/231] UI: fixes validation bug in sign certificate form (#19280) (#19293) * move validations to base certificate * add test --- ui/app/models/pki/certificate/generate.js | 6 ----- .../pki/addon/components/pki-role-generate.ts | 12 +++------ .../components/pki-role-generate-test.js | 25 +------------------ .../models/pki/certificate/generate.d.ts | 8 ++---- .../vault/models/pki/certificate/sign.d.ts | 3 +++ 5 files changed, 9 insertions(+), 45 deletions(-) create mode 100644 ui/types/vault/models/pki/certificate/sign.d.ts diff --git a/ui/app/models/pki/certificate/generate.js b/ui/app/models/pki/certificate/generate.js index dafefde4057c..d508e7eb52ac 100644 --- a/ui/app/models/pki/certificate/generate.js +++ b/ui/app/models/pki/certificate/generate.js @@ -1,6 +1,5 @@ import { attr } from '@ember-data/model'; import { withFormFields } from 'vault/decorators/model-form-fields'; -import { withModelValidations } from 'vault/decorators/model-validations'; import PkiCertificateBaseModel from './base'; const generateFromRole = [ @@ -21,11 +20,6 @@ const generateFromRole = [ ], }, ]; -const validations = { - commonName: [{ type: 'presence', message: 'Common name is required.' }], -}; - -@withModelValidations(validations) @withFormFields(null, generateFromRole) export default class PkiCertificateGenerateModel extends PkiCertificateBaseModel { getHelpUrl(backend) { diff --git a/ui/lib/pki/addon/components/pki-role-generate.ts b/ui/lib/pki/addon/components/pki-role-generate.ts index d8928bdc4c70..99cc3e1649f3 100644 --- a/ui/lib/pki/addon/components/pki-role-generate.ts +++ b/ui/lib/pki/addon/components/pki-role-generate.ts @@ -9,10 +9,11 @@ import errorMessage from 'vault/utils/error-message'; import FlashMessageService from 'vault/services/flash-messages'; import DownloadService from 'vault/services/download'; import PkiCertificateGenerateModel from 'vault/models/pki/certificate/generate'; +import PkiCertificateSignModel from 'vault/models/pki/certificate/sign'; interface Args { onSuccess: CallableFunction; - model: PkiCertificateGenerateModel; + model: PkiCertificateGenerateModel | PkiCertificateSignModel; type: string; } @@ -24,7 +25,6 @@ export default class PkiRoleGenerate extends Component { @tracked errorBanner = ''; @tracked invalidFormAlert = ''; - @tracked modelValidations = null; get verb() { return this.args.type === 'sign' ? 'sign' : 'generate'; @@ -35,18 +35,12 @@ export default class PkiRoleGenerate extends Component { evt.preventDefault(); this.errorBanner = ''; const { model, onSuccess } = this.args; - const { isValid, state, invalidFormMessage } = model.validate(); - - this.modelValidations = isValid ? null : state; - this.invalidFormAlert = invalidFormMessage; - - if (!isValid) return; - try { yield model.save(); onSuccess(); } catch (err) { this.errorBanner = errorMessage(err, `Could not ${this.verb} certificate. See Vault logs for details.`); + this.invalidFormAlert = 'There was an error submitting this form.'; } } diff --git a/ui/tests/integration/components/pki-role-generate-test.js b/ui/tests/integration/components/pki-role-generate-test.js index 35b90507a8c2..216a0b275efe 100644 --- a/ui/tests/integration/components/pki-role-generate-test.js +++ b/ui/tests/integration/components/pki-role-generate-test.js @@ -1,6 +1,6 @@ import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; -import { render, fillIn, click } from '@ember/test-helpers'; +import { render, fillIn } from '@ember/test-helpers'; import { hbs } from 'ember-cli-htmlbars'; import { setupMirage } from 'ember-cli-mirage/test-support'; import { setupEngine } from 'ember-engines/test-support'; @@ -69,27 +69,4 @@ module('Integration | Component | pki-role-generate', function (hooks) { assert.dom(SELECTORS.certificate).exists({ count: 1 }, 'shows certificate info row'); assert.dom(SELECTORS.serialNumber).hasText('abcd-efgh-ijkl', 'shows serial number info row'); }); - - test('it should display validation errors', async function (assert) { - assert.expect(3); - - await render( - hbs` -
    - -
    - `, - { owner: this.engine } - ); - await click(SELECTORS.generateButton); - - assert - .dom(SELECTORS.commonNameInlineError) - .hasText('Common name is required.', 'Common name validation error renders'); - assert.dom(SELECTORS.inlineAlert).hasText('There is an error with this form.', 'Alert renders'); - assert.dom(SELECTORS.commonNameErrorBorder).hasClass('has-error-border'); - }); }); diff --git a/ui/types/vault/models/pki/certificate/generate.d.ts b/ui/types/vault/models/pki/certificate/generate.d.ts index 089261a09a82..ab7c9d983ad4 100644 --- a/ui/types/vault/models/pki/certificate/generate.d.ts +++ b/ui/types/vault/models/pki/certificate/generate.d.ts @@ -1,14 +1,10 @@ -import Model from '@ember-data/model'; import { FormField } from 'vault/app-types'; +import PkiCertificateBaseModel from './base'; -export default class PkiCertificateGenerateModel extends Model { +export default class PkiCertificateGenerateModel extends PkiCertificateBaseModel { name: string; - backend: string; - serialNumber: string; - certificate: string; formFields: FormField[]; formFieldsGroup: { [k: string]: FormField[]; }[]; - validate(): ModelValidations; } diff --git a/ui/types/vault/models/pki/certificate/sign.d.ts b/ui/types/vault/models/pki/certificate/sign.d.ts new file mode 100644 index 000000000000..27d4b166d4a8 --- /dev/null +++ b/ui/types/vault/models/pki/certificate/sign.d.ts @@ -0,0 +1,3 @@ +import PkiCertificateBaseModel from './base'; + +export default class PkiCertificateGenerateModel extends PkiCertificateBaseModel {} From 5b2f60914e770e34dabf52c8c961ad59256a8146 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Thu, 23 Feb 2023 10:34:48 -0500 Subject: [PATCH 038/231] backport of commit 34a93f1a3a3706d0cf7aded20a714495938ad006 (#19309) Co-authored-by: Scott Miller --- website/content/api-docs/secret/transform.mdx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/website/content/api-docs/secret/transform.mdx b/website/content/api-docs/secret/transform.mdx index 3fb592e0c401..bfb161d11e88 100644 --- a/website/content/api-docs/secret/transform.mdx +++ b/website/content/api-docs/secret/transform.mdx @@ -333,6 +333,13 @@ transformation exists, it will be updated with the new attributes. for all plaintexts to be decoded via the export-decoded endpoint in an emergency. +- `convergent` `(bool: false)` - + Specifies whether to use convergent tokenization, where tokenization of the same + plaintext more than once results in the same token. Defaults to false as unique + tokens are more desirable from a security standpoint if there isn't a use-case + need for convergence. This property cannot be changed after the transform + is created. + - `max_ttl`: `(duration: "0")` - The maximum TTL of a token. If 0 or unspecified, tokens may have no expiration. From d21564e5391b25dc44b884ae1f80b091ef02e360 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Thu, 23 Feb 2023 12:13:14 -0500 Subject: [PATCH 039/231] backport of commit 7d52daf715c9738d32c3a388d60fddc09550ac31 (#19308) Co-authored-by: Peter Wilson --- command/agent.go | 2 +- command/server.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/command/agent.go b/command/agent.go index bf153c8529a6..fd1ea68105fe 100644 --- a/command/agent.go +++ b/command/agent.go @@ -1253,7 +1253,7 @@ func (c *AgentCommand) newLogger() (log.InterceptLogger, error) { } logCfg := &logging.LogConfig{ - Name: "vault-agent", + Name: "agent", LogLevel: logLevel, LogFormat: logFormat, LogFilePath: c.config.LogFile, diff --git a/command/server.go b/command/server.go index dd5526eb7299..a15f1b7122db 100644 --- a/command/server.go +++ b/command/server.go @@ -1721,7 +1721,6 @@ func (c *ServerCommand) configureLogging(config *server.Config) (hclog.Intercept } logCfg := &loghelper.LogConfig{ - Name: "vault", LogLevel: logLevel, LogFormat: logFormat, LogFilePath: config.LogFile, From 62eeda8d74dec74248570a7da87053198cd03ac9 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Thu, 23 Feb 2023 12:41:04 -0500 Subject: [PATCH 040/231] backport of commit f4f1762991680e26f854db0aabb4927b5cfe58cd (#19317) Co-authored-by: John-Michael Faircloth --- vault/external_tests/plugin/plugin_test.go | 2 ++ vault/testing.go | 3 +++ 2 files changed, 5 insertions(+) diff --git a/vault/external_tests/plugin/plugin_test.go b/vault/external_tests/plugin/plugin_test.go index b72d0f9a7c01..c0380bf56d62 100644 --- a/vault/external_tests/plugin/plugin_test.go +++ b/vault/external_tests/plugin/plugin_test.go @@ -423,6 +423,8 @@ func testPlugin_continueOnError(t *testing.T, btype logical.BackendType, mismatc // Get the registered plugin req := logical.TestRequest(t, logical.ReadOperation, fmt.Sprintf("sys/plugins/catalog/%s/mock-plugin", pluginType)) + // We are using the mock backend from vault/sdk/plugin/mock/backend.go which sets the plugin version. + req.Data["version"] = "v0.0.0+mock" req.ClientToken = core.Client.Token() resp, err := core.HandleRequest(namespace.RootContext(testCtx), req) if err != nil || resp == nil || (resp != nil && resp.IsError()) { diff --git a/vault/testing.go b/vault/testing.go index da3c72e33cab..1d4a18a22428 100644 --- a/vault/testing.go +++ b/vault/testing.go @@ -539,6 +539,9 @@ func TestAddTestPlugin(t testing.T, c *Core, name string, pluginType consts.Plug if err != nil { t.Fatal(err) } + // Ensure that the file is closed and written. This seems to be + // necessary on Linux systems. + out.Close() dirPath = tempDir } From 6a73f37ba43daba8e940d43683ed879ed89970c7 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Thu, 23 Feb 2023 13:29:52 -0500 Subject: [PATCH 041/231] backport of commit 20b347e3cd6fe7b024af8a672056e23290b6e42c (#19315) Co-authored-by: miagilepner --- changelog/19311.txt | 3 ++ command/server/config.go | 18 +++++-- helper/osutil/fileinfo.go | 14 +++++ helper/osutil/fileinfo_test.go | 96 ++++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 changelog/19311.txt diff --git a/changelog/19311.txt b/changelog/19311.txt new file mode 100644 index 000000000000..5ad6e2c01a81 --- /dev/null +++ b/changelog/19311.txt @@ -0,0 +1,3 @@ +```release-note:bug +server/config: Use file.Stat when checking file permissions when VAULT_ENABLE_FILE_PERMISSIONS_CHECK is enabled +``` diff --git a/command/server/config.go b/command/server/config.go index d2d30b40ed3e..7338923525b1 100644 --- a/command/server/config.go +++ b/command/server/config.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "math" "os" "path/filepath" @@ -465,9 +464,14 @@ func LoadConfig(path string) (*Config, error) { return nil, errors.New("Error parsing the environment variable VAULT_ENABLE_FILE_PERMISSIONS_CHECK") } } + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() if enableFilePermissionsCheck { - err = osutil.OwnerPermissionsMatch(path, 0, 0) + err = osutil.OwnerPermissionsMatchFile(f, 0, 0) if err != nil { return nil, err } @@ -496,8 +500,14 @@ func CheckConfig(c *Config, e error) (*Config, error) { // LoadConfigFile loads the configuration from the given file. func LoadConfigFile(path string) (*Config, error) { + // Open the file + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() // Read the file - d, err := ioutil.ReadFile(path) + d, err := io.ReadAll(f) if err != nil { return nil, err } @@ -518,7 +528,7 @@ func LoadConfigFile(path string) (*Config, error) { if enableFilePermissionsCheck { // check permissions of the config file - err = osutil.OwnerPermissionsMatch(path, 0, 0) + err = osutil.OwnerPermissionsMatchFile(f, 0, 0) if err != nil { return nil, err } diff --git a/helper/osutil/fileinfo.go b/helper/osutil/fileinfo.go index 4b6ba7910f50..59b99859ebae 100644 --- a/helper/osutil/fileinfo.go +++ b/helper/osutil/fileinfo.go @@ -64,3 +64,17 @@ func OwnerPermissionsMatch(path string, uid int, permissions int) error { return nil } + +// OwnerPermissionsMatchFile checks if vault user is the owner and permissions are secure for the input file +func OwnerPermissionsMatchFile(file *os.File, uid int, permissions int) error { + info, err := file.Stat() + if err != nil { + return fmt.Errorf("file stat error on path %q: %w", file.Name(), err) + } + err = checkPathInfo(info, file.Name(), uid, permissions) + if err != nil { + return err + } + + return nil +} diff --git a/helper/osutil/fileinfo_test.go b/helper/osutil/fileinfo_test.go index 0c77d4873ed1..febd11966a35 100644 --- a/helper/osutil/fileinfo_test.go +++ b/helper/osutil/fileinfo_test.go @@ -4,6 +4,7 @@ import ( "io/fs" "os" "os/user" + "path/filepath" "runtime" "strconv" "testing" @@ -82,3 +83,98 @@ func TestCheckPathInfo(t *testing.T) { } } } + +// TestOwnerPermissionsMatchFile creates a file and verifies that the current user of the process is the owner of the +// file +func TestOwnerPermissionsMatchFile(t *testing.T) { + currentUser, err := user.Current() + if err != nil { + t.Fatal("failed to get current user", err) + } + uid, err := strconv.ParseInt(currentUser.Uid, 0, 64) + if err != nil { + t.Fatal("failed to convert uid", err) + } + dir := t.TempDir() + path := filepath.Join(dir, "foo") + f, err := os.Create(path) + if err != nil { + t.Fatal("failed to create test file", err) + } + defer f.Close() + + info, err := os.Stat(path) + if err != nil { + t.Fatal("failed to stat test file", err) + } + + if err := OwnerPermissionsMatchFile(f, int(uid), int(info.Mode())); err != nil { + t.Fatalf("expected no error but got %v", err) + } +} + +// TestOwnerPermissionsMatchFile_OtherUser creates a file using the user that started the current process and verifies +// that a different user is not the owner of the file +func TestOwnerPermissionsMatchFile_OtherUser(t *testing.T) { + currentUser, err := user.Current() + if err != nil { + t.Fatal("failed to get current user", err) + } + uid, err := strconv.ParseInt(currentUser.Uid, 0, 64) + if err != nil { + t.Fatal("failed to convert uid", err) + } + dir := t.TempDir() + path := filepath.Join(dir, "foo") + f, err := os.Create(path) + if err != nil { + t.Fatal("failed to create test file", err) + } + defer f.Close() + + info, err := os.Stat(path) + if err != nil { + t.Fatal("failed to stat test file", err) + } + + if err := OwnerPermissionsMatchFile(f, int(uid)+1, int(info.Mode())); err == nil { + t.Fatalf("expected error but none") + } +} + +// TestOwnerPermissionsMatchFile_Symlink creates a file and a symlink to that file. The test verifies that the current +// user of the process is the owner of the file +func TestOwnerPermissionsMatchFile_Symlink(t *testing.T) { + currentUser, err := user.Current() + if err != nil { + t.Fatal("failed to get current user", err) + } + uid, err := strconv.ParseInt(currentUser.Uid, 0, 64) + if err != nil { + t.Fatal("failed to convert uid", err) + } + dir := t.TempDir() + path := filepath.Join(dir, "foo") + f, err := os.Create(path) + if err != nil { + t.Fatal("failed to create test file", err) + } + defer f.Close() + + symlink := filepath.Join(dir, "symlink") + err = os.Symlink(path, symlink) + if err != nil { + t.Fatal("failed to symlink file", err) + } + symlinkedFile, err := os.Open(symlink) + if err != nil { + t.Fatal("failed to open file", err) + } + info, err := os.Stat(symlink) + if err != nil { + t.Fatal("failed to stat test file", err) + } + if err := OwnerPermissionsMatchFile(symlinkedFile, int(uid), int(info.Mode())); err != nil { + t.Fatalf("expected no error but got %v", err) + } +} From 97892590430236f3103cd4d4d1a7f3fb5b4aa745 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Thu, 23 Feb 2023 14:26:19 -0500 Subject: [PATCH 042/231] backport of commit 72bc8203af8063fcbb8a1c7470f6ee44db4bbedf (#19322) Co-authored-by: Angel Garbarino --- changelog/19290.txt | 3 +++ ui/app/components/auth-config-form/options.js | 2 +- ui/app/models/mount-config.js | 6 +++--- .../settings/auth/configure/section-test.js | 8 ++++++++ .../components/mount-backend-form-test.js | 17 +++++++++++++++++ 5 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 changelog/19290.txt diff --git a/changelog/19290.txt b/changelog/19290.txt new file mode 100644 index 000000000000..1a4511590c69 --- /dev/null +++ b/changelog/19290.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Remove `default` and add `default-service` and `default-batch` to UI token_type for auth mount and tuning. +``` diff --git a/ui/app/components/auth-config-form/options.js b/ui/app/components/auth-config-form/options.js index a50bf96bee76..b5d5fed67d29 100644 --- a/ui/app/components/auth-config-form/options.js +++ b/ui/app/components/auth-config-form/options.js @@ -26,7 +26,7 @@ export default AuthConfigComponent.extend({ const data = this.model.config.serialize(); data.description = this.model.description; - // token_type should not be tuneable for the token auth method, default is 'default-service' + // token_type should not be tuneable for the token auth method. if (this.model.type === 'token') { delete data.token_type; } diff --git a/ui/app/models/mount-config.js b/ui/app/models/mount-config.js index edc624c2fae4..b2de642e946e 100644 --- a/ui/app/models/mount-config.js +++ b/ui/app/models/mount-config.js @@ -52,9 +52,9 @@ export default class MountConfigModel extends Model { @attr('string', { label: 'Token Type', helpText: - "The type of token that should be generated via this role. Can be `service`, `batch`, or `default` to use the mount's default (which unless changed will be `service` tokens).", - possibleValues: ['default', 'batch', 'service'], - defaultFormValue: 'default', + 'The type of token that should be generated via this role. For `default-service` and `default-batch` service and batch tokens will be issued respectively, unless the auth method explicitly requests a different type.', + possibleValues: ['default-service', 'default-batch', 'batch', 'service'], + noDefault: true, }) tokenType; } diff --git a/ui/tests/acceptance/settings/auth/configure/section-test.js b/ui/tests/acceptance/settings/auth/configure/section-test.js index b9b1bfba1c61..0807bc8c0755 100644 --- a/ui/tests/acceptance/settings/auth/configure/section-test.js +++ b/ui/tests/acceptance/settings/auth/configure/section-test.js @@ -1,6 +1,7 @@ import { module, test } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; import { create } from 'ember-cli-page-object'; +import { fillIn } from '@ember/test-helpers'; import enablePage from 'vault/tests/pages/settings/auth/enable'; import page from 'vault/tests/pages/settings/auth/configure/section'; import indexPage from 'vault/tests/pages/settings/auth/configure/index'; @@ -29,6 +30,10 @@ module('Acceptance | settings/auth/configure/section', function (hooks) { await enablePage.enable(type, path); await page.visit({ path, section }); await page.fillInTextarea('description', 'This is AppRole!'); + assert + .dom('[data-test-input="config.tokenType"]') + .hasValue('default-service', 'as default the token type selected is default-service.'); + await fillIn('[data-test-input="config.tokenType"]', 'batch'); await page.save(); assert.strictEqual( page.flash.latestMessage, @@ -40,8 +45,11 @@ module('Acceptance | settings/auth/configure/section', function (hooks) { `/v1/sys/mounts/auth/${path}/tune` )[0]; const keys = Object.keys(JSON.parse(tuneRequest.requestBody)); + const token_type = JSON.parse(tuneRequest.requestBody).token_type; + assert.strictEqual(token_type, 'batch', 'passes new token type'); assert.ok(keys.includes('default_lease_ttl'), 'passes default_lease_ttl on tune'); assert.ok(keys.includes('max_lease_ttl'), 'passes max_lease_ttl on tune'); + assert.ok(keys.includes('description'), 'passes updated description on tune'); }); for (const type of ['aws', 'azure', 'gcp', 'github', 'kubernetes']) { diff --git a/ui/tests/integration/components/mount-backend-form-test.js b/ui/tests/integration/components/mount-backend-form-test.js index 53ac56daca2a..5fdc7357401b 100644 --- a/ui/tests/integration/components/mount-backend-form-test.js +++ b/ui/tests/integration/components/mount-backend-form-test.js @@ -82,6 +82,23 @@ module('Integration | Component | mount backend form', function (hooks) { assert.strictEqual(component.pathValue, 'newpath', 'keeps custom path value'); }); + test('it does not show a selected token type when first mounting an auth method', async function (assert) { + await render( + hbs`` + ); + await component.selectType('github'); + await component.next(); + await component.toggleOptions(); + assert + .dom('[data-test-input="config.tokenType"]') + .hasValue('', 'token type does not have a default value.'); + const selectOptions = document.querySelector('[data-test-input="config.tokenType"]').options; + assert.strictEqual(selectOptions[1].text, 'default-service', 'first option is default-service'); + assert.strictEqual(selectOptions[2].text, 'default-batch', 'second option is default-batch'); + assert.strictEqual(selectOptions[3].text, 'batch', 'third option is batch'); + assert.strictEqual(selectOptions[4].text, 'service', 'fourth option is service'); + }); + test('it calls mount success', async function (assert) { assert.expect(3); From 4406c2b3cd45cc3d02a6f58d5879e45974d658a9 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Thu, 23 Feb 2023 15:44:32 -0500 Subject: [PATCH 043/231] backport of commit f2a47b0e402d76284b63847f410730f28316349a (#19328) Co-authored-by: Jason O'Donnell <2160810+jasonodonnell@users.noreply.github.com> --- vault/cluster/inmem_layer.go | 1 + 1 file changed, 1 insertion(+) diff --git a/vault/cluster/inmem_layer.go b/vault/cluster/inmem_layer.go index 538dc374a42d..36053ec96bbb 100644 --- a/vault/cluster/inmem_layer.go +++ b/vault/cluster/inmem_layer.go @@ -132,6 +132,7 @@ func (l *InmemLayer) Dial(addr string, timeout time.Duration, tlsConfig *tls.Con // gRPC sets a deadline of 20 seconds on the dail attempt, so // matching that here. time.Sleep(time.Second * 20) + l.l.Unlock() return nil, deadlineError("i/o timeout") } From 01ed88918bf94380b81784ce9bd97ca8d13d7ae7 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Thu, 23 Feb 2023 18:36:43 -0500 Subject: [PATCH 044/231] backport of commit 794eb8b2e9dbc1e8fa2f13e1dc14fc6ed9e82894 (#19333) Co-authored-by: Christopher Swenson --- vault/testing.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vault/testing.go b/vault/testing.go index 1d4a18a22428..d8b243e6d9b2 100644 --- a/vault/testing.go +++ b/vault/testing.go @@ -526,6 +526,9 @@ func TestAddTestPlugin(t testing.T, c *Core, name string, pluginType consts.Plug // Copy over the file to the temp dir dst := filepath.Join(tempDir, fileName) + + // delete the file first to avoid notary failures in macOS + _ = os.Remove(dst) // ignore error out, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fi.Mode()) if err != nil { t.Fatal(err) From 9063cfe1133255a535fe92277d92f4db29fef97b Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Thu, 23 Feb 2023 19:41:02 -0500 Subject: [PATCH 045/231] backport of commit 431b4243e726fdd4cd83e1879f93ae46365212b3 (#19335) Co-authored-by: Austin Gebauer <34121980+austingebauer@users.noreply.github.com> --- changelog/19334.txt | 3 +++ helper/builtinplugins/registry.go | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 changelog/19334.txt diff --git a/changelog/19334.txt b/changelog/19334.txt new file mode 100644 index 000000000000..7df68268aabe --- /dev/null +++ b/changelog/19334.txt @@ -0,0 +1,3 @@ +```release-note:deprecation +secrets/ad: Marks the Active Directory (AD) secrets engine as deprecated. +``` \ No newline at end of file diff --git a/helper/builtinplugins/registry.go b/helper/builtinplugins/registry.go index 24539af5cfe7..8a24a58c34ad 100644 --- a/helper/builtinplugins/registry.go +++ b/helper/builtinplugins/registry.go @@ -146,7 +146,10 @@ func newRegistry() *registry { "snowflake-database-plugin": {Factory: dbSnowflake.New}, }, logicalBackends: map[string]logicalBackend{ - "ad": {Factory: logicalAd.Factory}, + "ad": { + Factory: logicalAd.Factory, + DeprecationStatus: consts.Deprecated, + }, "alicloud": {Factory: logicalAlicloud.Factory}, "aws": {Factory: logicalAws.Factory}, "azure": {Factory: logicalAzure.Factory}, From 854587607695dd2e76ae1a187df4016a7083a6d3 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Fri, 24 Feb 2023 13:45:19 -0500 Subject: [PATCH 046/231] backport of commit 3adb416da1bdbd46a633c2aae25fc7cd44082724 (#19352) Co-authored-by: Alexander Scheel --- sdk/helper/ocsp/client.go | 112 ++++++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 34 deletions(-) diff --git a/sdk/helper/ocsp/client.go b/sdk/helper/ocsp/client.go index e54fdeface46..f30da3ec5f14 100644 --- a/sdk/helper/ocsp/client.go +++ b/sdk/helper/ocsp/client.go @@ -24,6 +24,7 @@ import ( "time" "github.com/hashicorp/go-hclog" + "github.com/hashicorp/go-multierror" "github.com/hashicorp/go-retryablehttp" lru "github.com/hashicorp/golang-lru" "github.com/hashicorp/vault/sdk/helper/certutil" @@ -283,12 +284,8 @@ func (c *Client) retryOCSP( headers map[string]string, reqBody []byte, issuer *x509.Certificate, -) (ocspRes *ocsp.Response, ocspResBytes []byte, ocspS *ocspStatus, err error) { - origHost := *ocspHost +) (ocspRes *ocsp.Response, ocspResBytes []byte, ocspS *ocspStatus, retErr error) { doRequest := func(request *retryablehttp.Request) (*http.Response, error) { - if err != nil { - return nil, err - } if request != nil { request = request.WithContext(ctx) for k, v := range headers { @@ -303,43 +300,90 @@ func (c *Client) retryOCSP( return res, err } - ocspHost.Path = ocspHost.Path + "/" + base64.StdEncoding.EncodeToString(reqBody) - var res *http.Response - request, err := req("GET", ocspHost.String(), nil) - if err != nil { - return nil, nil, nil, err - } - if res, err = doRequest(request); err != nil { - return nil, nil, nil, err - } else { - defer res.Body.Close() - } - if res.StatusCode == http.StatusMethodNotAllowed { - request, err := req("POST", origHost.String(), bytes.NewBuffer(reqBody)) + for _, method := range []string{"GET", "POST"} { + reqUrl := *ocspHost + var body []byte + + switch method { + case "GET": + reqUrl.Path = reqUrl.Path + "/" + base64.StdEncoding.EncodeToString(reqBody) + case "POST": + body = reqBody + default: + // Programming error; all request/systems errors are multierror + // and appended. + return nil, nil, nil, fmt.Errorf("unknown request method: %v", method) + } + + var res *http.Response + request, err := req(method, reqUrl.String(), bytes.NewBuffer(body)) if err != nil { - return nil, nil, nil, err + err = fmt.Errorf("error creating %v request: %w", method, err) + retErr = multierror.Append(retErr, err) + continue } - if res, err := doRequest(request); err != nil { - return nil, nil, nil, err + if res, err = doRequest(request); err != nil { + err = fmt.Errorf("error doing %v request: %w", method, err) + retErr = multierror.Append(retErr, err) + continue } else { defer res.Body.Close() } + + if res.StatusCode != http.StatusOK { + err = fmt.Errorf("HTTP code is not OK on %v request. %v: %v", method, res.StatusCode, res.Status) + retErr = multierror.Append(retErr, err) + continue + } + + ocspResBytes, err = io.ReadAll(res.Body) + if err != nil { + err = fmt.Errorf("error reading %v request body: %w", method, err) + retErr = multierror.Append(retErr, err) + continue + } + + // Reading an OCSP response shouldn't be fatal. A misconfigured + // endpoint might return invalid results for e.g., GET but return + // valid results for POST on retry. This could happen if e.g., the + // server responds with JSON. + ocspRes, err = ocsp.ParseResponse(ocspResBytes, issuer) + if err != nil { + err = fmt.Errorf("error parsing %v OCSP response: %w", method, err) + retErr = multierror.Append(retErr, err) + continue + } + + // While we haven't validated the signature on the OCSP response, we + // got what we presume is a definitive answer and simply changing + // methods will likely not help us in that regard. Use this status + // to return without retrying another method, when it looks definitive. + // + // We don't accept ocsp.Unknown here: presumably, we could've hit a CDN + // with static mapping of request->responses, with a default "unknown" + // handler for everything else. By retrying here, we use POST, which + // could hit a live OCSP server with fresher data than the cached CDN. + if ocspRes.Status == ocsp.Good || ocspRes.Status == ocsp.Revoked { + break + } + + // Here, we didn't have a valid response. Even though we didn't get an + // error, we should inform the user that this (valid-looking) response + // wasn't utilized. + err = fmt.Errorf("fetched %v OCSP response of status %v; wanted either good (%v) or revoked (%v)", method, ocspRes.Status, ocsp.Good, ocsp.Revoked) + retErr = multierror.Append(retErr, err) } - if res.StatusCode != http.StatusOK { - return nil, nil, nil, fmt.Errorf("HTTP code is not OK. %v: %v", res.StatusCode, res.Status) - } - ocspResBytes, err = io.ReadAll(res.Body) - if err != nil { - return nil, nil, nil, err - } - ocspRes, err = ocsp.ParseResponse(ocspResBytes, issuer) - if err != nil { - return nil, nil, nil, err + + if ocspRes != nil && ocspResBytes != nil { + // Clear retErr, because we have one parseable-but-maybe-not-quite-correct + // OCSP response. + retErr = nil + ocspS = &ocspStatus{ + code: ocspSuccess, + } } - return ocspRes, ocspResBytes, &ocspStatus{ - code: ocspSuccess, - }, nil + return } // GetRevocationStatus checks the certificate revocation status for subject using issuer certificate. From c496011eed2559b51bb23a43b0689b35610de568 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Fri, 24 Feb 2023 14:11:43 -0500 Subject: [PATCH 047/231] backport of commit d08bf5616db060e43b2280a7dcab2defa16fe227 (#19347) Co-authored-by: Steven Clark --- command/healthcheck/healthcheck.go | 4 ++ .../healthcheck/pki_hardware_backed_root.go | 26 +++++++++- .../pki_role_allows_glob_wildcards.go | 39 ++++++++++---- .../healthcheck/pki_role_allows_localhost.go | 40 ++++++++++---- .../healthcheck/pki_role_no_store_false.go | 52 ++++++++++++------- command/healthcheck/pki_root_issued_leaves.go | 31 ++++++++++- command/healthcheck/pki_too_many_certs.go | 22 +++++++- command/pki_health_check.go | 10 +++- command/pki_health_check_test.go | 8 ++- 9 files changed, 187 insertions(+), 45 deletions(-) diff --git a/command/healthcheck/healthcheck.go b/command/healthcheck/healthcheck.go index 1949de04e57b..2ce9c2deea5d 100644 --- a/command/healthcheck/healthcheck.go +++ b/command/healthcheck/healthcheck.go @@ -119,6 +119,10 @@ func (e *Executor) Execute() (map[string][]*Result, error) { return nil, fmt.Errorf("failed to evaluate %v: %w", checker.Name(), err) } + if results == nil { + results = []*Result{} + } + for _, result := range results { result.Endpoint = e.templatePath(result.Endpoint) result.StatusDisplay = ResultStatusNameMap[result.Status] diff --git a/command/healthcheck/pki_hardware_backed_root.go b/command/healthcheck/pki_hardware_backed_root.go index 199a781fe9bb..89d3550eaf9b 100644 --- a/command/healthcheck/pki_hardware_backed_root.go +++ b/command/healthcheck/pki_hardware_backed_root.go @@ -13,12 +13,14 @@ type HardwareBackedRoot struct { UnsupportedVersion bool + FetchIssues map[string]*PathFetch IssuerKeyMap map[string]string KeyIsManaged map[string]string } func NewHardwareBackedRootCheck() Check { return &HardwareBackedRoot{ + FetchIssues: make(map[string]*PathFetch), IssuerKeyMap: make(map[string]string), KeyIsManaged: make(map[string]string), } @@ -64,6 +66,7 @@ func (h *HardwareBackedRoot) FetchResources(e *Executor) error { if err != nil { return err } + h.FetchIssues[issuer] = ret continue } @@ -83,13 +86,15 @@ func (h *HardwareBackedRoot) FetchResources(e *Executor) error { } h.IssuerKeyMap[issuer] = keyId - skip, _, keyEntry, err := pkiFetchKeyEntry(e, keyId, func() { + skip, ret, keyEntry, err := pkiFetchKeyEntry(e, keyId, func() { h.UnsupportedVersion = true }) if skip || err != nil || keyEntry == nil { if err != nil { return err } + + h.FetchIssues[issuer] = ret continue } @@ -112,6 +117,25 @@ func (h *HardwareBackedRoot) Evaluate(e *Executor) (results []*Result, err error return []*Result{&ret}, nil } + for issuer, fetchPath := range h.FetchIssues { + if fetchPath != nil && fetchPath.IsSecretPermissionsError() { + delete(h.IssuerKeyMap, issuer) + ret := Result{ + Status: ResultInsufficientPermissions, + Endpoint: fetchPath.Path, + Message: "Without this information, this health check is unable to function.", + } + + if e.Client.Token() == "" { + ret.Message = "No token available so unable for the endpoint for this mount. " + ret.Message + } else { + ret.Message = "This token lacks permission for the endpoint for this mount. " + ret.Message + } + + results = append(results, &ret) + } + } + for name, keyId := range h.IssuerKeyMap { var ret Result ret.Status = ResultInformational diff --git a/command/healthcheck/pki_role_allows_glob_wildcards.go b/command/healthcheck/pki_role_allows_glob_wildcards.go index 6978f3517bda..34fb09927ce5 100644 --- a/command/healthcheck/pki_role_allows_glob_wildcards.go +++ b/command/healthcheck/pki_role_allows_glob_wildcards.go @@ -10,14 +10,16 @@ import ( type RoleAllowsGlobWildcards struct { Enabled bool UnsupportedVersion bool - NoPerms bool - RoleEntryMap map[string]map[string]interface{} + RoleListFetchIssue *PathFetch + RoleFetchIssues map[string]*PathFetch + RoleEntryMap map[string]map[string]interface{} } func NewRoleAllowsGlobWildcardsCheck() Check { return &RoleAllowsGlobWildcards{ - RoleEntryMap: make(map[string]map[string]interface{}), + RoleFetchIssues: make(map[string]*PathFetch), + RoleEntryMap: make(map[string]map[string]interface{}), } } @@ -49,7 +51,7 @@ func (h *RoleAllowsGlobWildcards) FetchResources(e *Executor) error { }) if exit || err != nil { if f != nil && f.IsSecretPermissionsError() { - h.NoPerms = true + h.RoleListFetchIssue = f } return err } @@ -60,7 +62,7 @@ func (h *RoleAllowsGlobWildcards) FetchResources(e *Executor) error { }) if skip || err != nil || entry == nil { if f != nil && f.IsSecretPermissionsError() { - h.NoPerms = true + h.RoleFetchIssues[role] = f } if err != nil { return err @@ -84,18 +86,37 @@ func (h *RoleAllowsGlobWildcards) Evaluate(e *Executor) (results []*Result, err } return []*Result{&ret}, nil } - if h.NoPerms { + if h.RoleListFetchIssue != nil && h.RoleListFetchIssue.IsSecretPermissionsError() { ret := Result{ Status: ResultInsufficientPermissions, - Endpoint: "/{{mount}}/roles", - Message: "lacks permission either to list the roles or to read a specific role. This may restrict the ability to fully execute this health check.", + Endpoint: h.RoleListFetchIssue.Path, + Message: "lacks permission either to list the roles. This restricts the ability to fully execute this health check.", } if e.Client.Token() == "" { ret.Message = "No token available and so this health check " + ret.Message } else { ret.Message = "This token " + ret.Message } - results = append(results, &ret) + return []*Result{&ret}, nil + } + + for role, fetchPath := range h.RoleFetchIssues { + if fetchPath != nil && fetchPath.IsSecretPermissionsError() { + delete(h.RoleEntryMap, role) + ret := Result{ + Status: ResultInsufficientPermissions, + Endpoint: fetchPath.Path, + Message: "Without this information, this health check is unable to function.", + } + + if e.Client.Token() == "" { + ret.Message = "No token available so unable for the endpoint for this mount. " + ret.Message + } else { + ret.Message = "This token lacks permission the endpoint for this mount. " + ret.Message + } + + results = append(results, &ret) + } } for role, entry := range h.RoleEntryMap { diff --git a/command/healthcheck/pki_role_allows_localhost.go b/command/healthcheck/pki_role_allows_localhost.go index c725f86d1c68..568aa3a5f857 100644 --- a/command/healthcheck/pki_role_allows_localhost.go +++ b/command/healthcheck/pki_role_allows_localhost.go @@ -9,14 +9,16 @@ import ( type RoleAllowsLocalhost struct { Enabled bool UnsupportedVersion bool - NoPerms bool - RoleEntryMap map[string]map[string]interface{} + RoleListFetchIssue *PathFetch + RoleFetchIssues map[string]*PathFetch + RoleEntryMap map[string]map[string]interface{} } func NewRoleAllowsLocalhostCheck() Check { return &RoleAllowsLocalhost{ - RoleEntryMap: make(map[string]map[string]interface{}), + RoleFetchIssues: make(map[string]*PathFetch), + RoleEntryMap: make(map[string]map[string]interface{}), } } @@ -48,7 +50,7 @@ func (h *RoleAllowsLocalhost) FetchResources(e *Executor) error { }) if exit || err != nil { if f != nil && f.IsSecretPermissionsError() { - h.NoPerms = true + h.RoleListFetchIssue = f } return err } @@ -59,7 +61,7 @@ func (h *RoleAllowsLocalhost) FetchResources(e *Executor) error { }) if skip || err != nil || entry == nil { if f != nil && f.IsSecretPermissionsError() { - h.NoPerms = true + h.RoleFetchIssues[role] = f } if err != nil { return err @@ -83,18 +85,38 @@ func (h *RoleAllowsLocalhost) Evaluate(e *Executor) (results []*Result, err erro } return []*Result{&ret}, nil } - if h.NoPerms { + + if h.RoleListFetchIssue != nil && h.RoleListFetchIssue.IsSecretPermissionsError() { ret := Result{ Status: ResultInsufficientPermissions, - Endpoint: "/{{mount}}/roles", - Message: "lacks permission either to list the roles or to read a specific role. This may restrict the ability to fully execute this health check", + Endpoint: h.RoleListFetchIssue.Path, + Message: "lacks permission either to list the roles. This restricts the ability to fully execute this health check.", } if e.Client.Token() == "" { ret.Message = "No token available and so this health check " + ret.Message } else { ret.Message = "This token " + ret.Message } - results = append(results, &ret) + return []*Result{&ret}, nil + } + + for role, fetchPath := range h.RoleFetchIssues { + if fetchPath != nil && fetchPath.IsSecretPermissionsError() { + delete(h.RoleEntryMap, role) + ret := Result{ + Status: ResultInsufficientPermissions, + Endpoint: fetchPath.Path, + Message: "Without this information, this health check is unable to function.", + } + + if e.Client.Token() == "" { + ret.Message = "No token available so unable for the endpoint for this mount. " + ret.Message + } else { + ret.Message = "This token lacks permission the endpoint for this mount. " + ret.Message + } + + results = append(results, &ret) + } } for role, entry := range h.RoleEntryMap { diff --git a/command/healthcheck/pki_role_no_store_false.go b/command/healthcheck/pki_role_no_store_false.go index 67d686a7a3fd..4fa7ba5ac68d 100644 --- a/command/healthcheck/pki_role_no_store_false.go +++ b/command/healthcheck/pki_role_no_store_false.go @@ -11,19 +11,20 @@ import ( type RoleNoStoreFalse struct { Enabled bool UnsupportedVersion bool - NoPerms bool AllowedRoles map[string]bool - CertCounts int - RoleEntryMap map[string]map[string]interface{} - CRLConfig *PathFetch + RoleListFetchIssue *PathFetch + RoleFetchIssues map[string]*PathFetch + RoleEntryMap map[string]map[string]interface{} + CRLConfig *PathFetch } func NewRoleNoStoreFalseCheck() Check { return &RoleNoStoreFalse{ - AllowedRoles: make(map[string]bool), - RoleEntryMap: make(map[string]map[string]interface{}), + RoleFetchIssues: make(map[string]*PathFetch), + AllowedRoles: make(map[string]bool), + RoleEntryMap: make(map[string]map[string]interface{}), } } @@ -64,7 +65,7 @@ func (h *RoleNoStoreFalse) FetchResources(e *Executor) error { }) if exit || err != nil { if f != nil && f.IsSecretPermissionsError() { - h.NoPerms = true + h.RoleListFetchIssue = f } return err } @@ -75,7 +76,7 @@ func (h *RoleNoStoreFalse) FetchResources(e *Executor) error { }) if skip || err != nil || entry == nil { if f != nil && f.IsSecretPermissionsError() { - h.NoPerms = true + h.RoleFetchIssues[role] = f } if err != nil { return err @@ -86,14 +87,6 @@ func (h *RoleNoStoreFalse) FetchResources(e *Executor) error { h.RoleEntryMap[role] = entry } - exit, _, leaves, err := pkiFetchLeavesList(e, func() { - h.UnsupportedVersion = true - }) - if exit || err != nil { - return err - } - h.CertCounts = len(leaves) - // Check if the issuer is fetched yet. configRet, err := e.FetchIfNotFetched(logical.ReadOperation, "/{{mount}}/config/crl") if err != nil { @@ -116,18 +109,37 @@ func (h *RoleNoStoreFalse) Evaluate(e *Executor) (results []*Result, err error) return []*Result{&ret}, nil } - if h.NoPerms { + if h.RoleListFetchIssue != nil && h.RoleListFetchIssue.IsSecretPermissionsError() { ret := Result{ Status: ResultInsufficientPermissions, - Endpoint: "/{{mount}}/roles", - Message: "lacks permission either to list the roles or to read a specific role. This may restrict the ability to fully execute this health check", + Endpoint: h.RoleListFetchIssue.Path, + Message: "lacks permission either to list the roles. This restricts the ability to fully execute this health check.", } if e.Client.Token() == "" { ret.Message = "No token available and so this health check " + ret.Message } else { ret.Message = "This token " + ret.Message } - results = append(results, &ret) + return []*Result{&ret}, nil + } + + for role, fetchPath := range h.RoleFetchIssues { + if fetchPath != nil && fetchPath.IsSecretPermissionsError() { + delete(h.RoleEntryMap, role) + ret := Result{ + Status: ResultInsufficientPermissions, + Endpoint: fetchPath.Path, + Message: "Without this information, this health check is unable to function.", + } + + if e.Client.Token() == "" { + ret.Message = "No token available so unable for the endpoint for this mount. " + ret.Message + } else { + ret.Message = "This token lacks permission the endpoint for this mount. " + ret.Message + } + + results = append(results, &ret) + } } crlAutoRebuild := false diff --git a/command/healthcheck/pki_root_issued_leaves.go b/command/healthcheck/pki_root_issued_leaves.go index 3252a91fb041..e858794b621c 100644 --- a/command/healthcheck/pki_root_issued_leaves.go +++ b/command/healthcheck/pki_root_issued_leaves.go @@ -14,12 +14,14 @@ type RootIssuedLeaves struct { CertsToFetch int + FetchIssues map[string]*PathFetch RootCertMap map[string]*x509.Certificate LeafCertMap map[string]*x509.Certificate } func NewRootIssuedLeavesCheck() Check { return &RootIssuedLeaves{ + FetchIssues: make(map[string]*PathFetch), RootCertMap: make(map[string]*x509.Certificate), LeafCertMap: make(map[string]*x509.Certificate), } @@ -64,9 +66,10 @@ func (h *RootIssuedLeaves) FetchResources(e *Executor) error { } for _, issuer := range issuers { - skip, _, cert, err := pkiFetchIssuer(e, issuer, func() { + skip, pathFetch, cert, err := pkiFetchIssuer(e, issuer, func() { h.UnsupportedVersion = true }) + h.FetchIssues[issuer] = pathFetch if skip || err != nil { if err != nil { return err @@ -85,10 +88,15 @@ func (h *RootIssuedLeaves) FetchResources(e *Executor) error { h.RootCertMap[issuer] = cert } - exit, _, leaves, err := pkiFetchLeavesList(e, func() { + exit, f, leaves, err := pkiFetchLeavesList(e, func() { h.UnsupportedVersion = true }) if exit || err != nil { + if f != nil && f.IsSecretPermissionsError() { + for _, issuer := range issuers { + h.FetchIssues[issuer] = f + } + } return err } @@ -130,6 +138,25 @@ func (h *RootIssuedLeaves) Evaluate(e *Executor) (results []*Result, err error) return []*Result{&ret}, nil } + for issuer, fetchPath := range h.FetchIssues { + if fetchPath != nil && fetchPath.IsSecretPermissionsError() { + delete(h.RootCertMap, issuer) + ret := Result{ + Status: ResultInsufficientPermissions, + Endpoint: fetchPath.Path, + Message: "Without this information, this health check is unable to function.", + } + + if e.Client.Token() == "" { + ret.Message = "No token available so unable for the endpoint for this mount. " + ret.Message + } else { + ret.Message = "This token lacks permission for the endpoint for this mount. " + ret.Message + } + + results = append(results, &ret) + } + } + issuerHasLeaf := make(map[string]bool) for serial, leaf := range h.LeafCertMap { if len(issuerHasLeaf) == len(h.RootCertMap) { diff --git a/command/healthcheck/pki_too_many_certs.go b/command/healthcheck/pki_too_many_certs.go index 8bd61003bceb..6b07b5dfe38c 100644 --- a/command/healthcheck/pki_too_many_certs.go +++ b/command/healthcheck/pki_too_many_certs.go @@ -14,6 +14,7 @@ type TooManyCerts struct { CountWarning int CertCounts int + FetchIssue *PathFetch } func NewTooManyCertsCheck() Check { @@ -60,7 +61,9 @@ func (h *TooManyCerts) FetchResources(e *Executor) error { exit, leavesRet, _, err := pkiFetchLeavesList(e, func() { h.UnsupportedVersion = true }) - if exit { + h.FetchIssue = leavesRet + + if exit || err != nil { return err } @@ -80,6 +83,23 @@ func (h *TooManyCerts) Evaluate(e *Executor) (results []*Result, err error) { return []*Result{&ret}, nil } + if h.FetchIssue != nil && h.FetchIssue.IsSecretPermissionsError() { + ret := Result{ + Status: ResultInsufficientPermissions, + Endpoint: h.FetchIssue.Path, + Message: "Without this information, this health check is unable to function.", + } + + if e.Client.Token() == "" { + ret.Message = "No token available so unable to list the endpoint for this mount. " + ret.Message + } else { + ret.Message = "This token lacks permission to list the endpoint for this mount. " + ret.Message + } + + results = append(results, &ret) + return + } + ret := Result{ Status: ResultOK, Endpoint: "/{{mount}}/certs", diff --git a/command/pki_health_check.go b/command/pki_health_check.go index 39d3ba73f963..8c56a2c1f0ed 100644 --- a/command/pki_health_check.go +++ b/command/pki_health_check.go @@ -223,7 +223,15 @@ func (c *PKIHealthCheckCommand) Run(args []string) int { // Handle listing, if necessary. if c.flagList { - c.UI.Output("Default health check config:") + uiFormat := Format(c.UI) + if uiFormat == "yaml" { + c.UI.Error("YAML output format is not supported by the --list command") + return pkiRetUsage + } + + if uiFormat != "json" { + c.UI.Output("Default health check config:") + } config := map[string]map[string]interface{}{} for _, checker := range executor.Checkers { config[checker.Name()] = checker.DefaultConfig() diff --git a/command/pki_health_check_test.go b/command/pki_health_check_test.go index bdd491a0497d..af3cf337a468 100644 --- a/command/pki_health_check_test.go +++ b/command/pki_health_check_test.go @@ -271,6 +271,8 @@ func testPKIHealthCheckCommand(tb testing.TB) (*cli.MockUi, *PKIHealthCheckComma } func execPKIHC(t *testing.T, client *api.Client, ok bool) (int, string, map[string][]map[string]interface{}) { + t.Helper() + stdout := bytes.NewBuffer(nil) stderr := bytes.NewBuffer(nil) runOpts := &RunOptions{ @@ -295,6 +297,8 @@ func execPKIHC(t *testing.T, client *api.Client, ok bool) (int, string, map[stri } func validateExpectedPKIHC(t *testing.T, expected, results map[string][]map[string]interface{}) { + t.Helper() + for test, subtest := range expected { actual, ok := results[test] require.True(t, ok, fmt.Sprintf("expected top-level test %v to be present", test)) @@ -615,7 +619,7 @@ var expectedNoPerm = map[string][]map[string]interface{}{ }, "root_issued_leaves": { { - "status": "ok", + "status": "insufficient_permissions", }, }, "tidy_last_run": { @@ -625,7 +629,7 @@ var expectedNoPerm = map[string][]map[string]interface{}{ }, "too_many_certs": { { - "status": "ok", + "status": "insufficient_permissions", }, }, } From 3804125973faaa3ffee7805a0554e28f6c4487dd Mon Sep 17 00:00:00 2001 From: Steven Clark Date: Fri, 24 Feb 2023 15:02:32 -0500 Subject: [PATCH 048/231] Update Go to 1.20.1 for 1.13.0 (#19357) --- .circleci/config.yml | 22 +++++++++++----------- .circleci/config/executors/@executors.yml | 2 +- .go-version | 2 +- changelog/_go-ver-1130.txt | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index bd21ec984863..3ae52c52e856 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -120,13 +120,13 @@ jobs: root: . environment: - CIRCLECI_CLI_VERSION: 0.1.5546 - - GO_IMAGE: docker.mirror.hashicorp.services/cimg/go:1.20 + - GO_IMAGE: docker.mirror.hashicorp.services/cimg/go:1.20.1 - GO_TAGS: '' - GOFUMPT_VERSION: 0.3.1 - GOTESTSUM_VERSION: 0.5.2 test-go-remote-docker: docker: - - image: docker.mirror.hashicorp.services/cimg/go:1.20 + - image: docker.mirror.hashicorp.services/cimg/go:1.20.1 resource_class: medium working_directory: /home/circleci/go/src/github.com/hashicorp/vault parallelism: 8 @@ -352,7 +352,7 @@ jobs: path: /tmp/testlogs environment: - CIRCLECI_CLI_VERSION: 0.1.5546 - - GO_IMAGE: docker.mirror.hashicorp.services/cimg/go:1.20 + - GO_IMAGE: docker.mirror.hashicorp.services/cimg/go:1.20.1 - GO_TAGS: '' - GOFUMPT_VERSION: 0.3.1 - GOTESTSUM_VERSION: 0.5.2 @@ -396,13 +396,13 @@ jobs: name: make fmt environment: - CIRCLECI_CLI_VERSION: 0.1.5546 - - GO_IMAGE: docker.mirror.hashicorp.services/cimg/go:1.20 + - GO_IMAGE: docker.mirror.hashicorp.services/cimg/go:1.20.1 - GO_TAGS: '' - GOFUMPT_VERSION: 0.3.1 - GOTESTSUM_VERSION: 0.5.2 test-go-race: docker: - - image: docker.mirror.hashicorp.services/cimg/go:1.20 + - image: docker.mirror.hashicorp.services/cimg/go:1.20.1 resource_class: xlarge working_directory: /home/circleci/go/src/github.com/hashicorp/vault parallelism: 8 @@ -618,13 +618,13 @@ jobs: path: /tmp/testlogs environment: - CIRCLECI_CLI_VERSION: 0.1.5546 - - GO_IMAGE: docker.mirror.hashicorp.services/cimg/go:1.20 + - GO_IMAGE: docker.mirror.hashicorp.services/cimg/go:1.20.1 - GO_TAGS: '' - GOFUMPT_VERSION: 0.3.1 - GOTESTSUM_VERSION: 0.5.2 test-go: docker: - - image: docker.mirror.hashicorp.services/cimg/go:1.20 + - image: docker.mirror.hashicorp.services/cimg/go:1.20.1 resource_class: large working_directory: /home/circleci/go/src/github.com/hashicorp/vault parallelism: 8 @@ -840,7 +840,7 @@ jobs: path: /tmp/testlogs environment: - CIRCLECI_CLI_VERSION: 0.1.5546 - - GO_IMAGE: docker.mirror.hashicorp.services/cimg/go:1.20 + - GO_IMAGE: docker.mirror.hashicorp.services/cimg/go:1.20.1 - GO_TAGS: '' - GOFUMPT_VERSION: 0.3.1 - GOTESTSUM_VERSION: 0.5.2 @@ -947,13 +947,13 @@ jobs: - /home/circleci/go/pkg/mod environment: - CIRCLECI_CLI_VERSION: 0.1.5546 - - GO_IMAGE: docker.mirror.hashicorp.services/cimg/go:1.20 + - GO_IMAGE: docker.mirror.hashicorp.services/cimg/go:1.20.1 - GO_TAGS: '' - GOFUMPT_VERSION: 0.3.1 - GOTESTSUM_VERSION: 0.5.2 test-go-race-remote-docker: docker: - - image: docker.mirror.hashicorp.services/cimg/go:1.20 + - image: docker.mirror.hashicorp.services/cimg/go:1.20.1 resource_class: medium working_directory: /home/circleci/go/src/github.com/hashicorp/vault parallelism: 8 @@ -1179,7 +1179,7 @@ jobs: path: /tmp/testlogs environment: - CIRCLECI_CLI_VERSION: 0.1.5546 - - GO_IMAGE: docker.mirror.hashicorp.services/cimg/go:1.20 + - GO_IMAGE: docker.mirror.hashicorp.services/cimg/go:1.20.1 - GO_TAGS: '' - GOFUMPT_VERSION: 0.3.1 - GOTESTSUM_VERSION: 0.5.2 diff --git a/.circleci/config/executors/@executors.yml b/.circleci/config/executors/@executors.yml index 3cba4dbbb85c..586fd70e7363 100644 --- a/.circleci/config/executors/@executors.yml +++ b/.circleci/config/executors/@executors.yml @@ -4,7 +4,7 @@ references: GOTESTSUM_VERSION: 0.5.2 # Pin gotestsum to patch version (ex: 1.2.3) GOFUMPT_VERSION: 0.3.1 # Pin gofumpt to patch version (ex: 1.2.3) GO_TAGS: "" - GO_IMAGE: &GO_IMAGE "docker.mirror.hashicorp.services/cimg/go:1.20" + GO_IMAGE: &GO_IMAGE "docker.mirror.hashicorp.services/cimg/go:1.20.1" go-machine: machine: image: ubuntu-2004:2022.10.1 diff --git a/.go-version b/.go-version index 5fb5a6b4f547..0044d6cb9691 100644 --- a/.go-version +++ b/.go-version @@ -1 +1 @@ -1.20 +1.20.1 diff --git a/changelog/_go-ver-1130.txt b/changelog/_go-ver-1130.txt index c63e249c4588..d1b6a1489383 100644 --- a/changelog/_go-ver-1130.txt +++ b/changelog/_go-ver-1130.txt @@ -1,3 +1,3 @@ ```release-note:change -core: Bump Go version to 1.20. +core: Bump Go version to 1.20.1. ``` From c6c35dcde638872334dcd791789b7b1f7aadfd99 Mon Sep 17 00:00:00 2001 From: Nick Cabatoff Date: Fri, 24 Feb 2023 15:57:59 -0500 Subject: [PATCH 049/231] Revert "updated raft-autopilot to v0.2.0 (#17848)" (#19362) This reverts commit 21cab77be8df948af147c11758f7fa0620ae8be6. --- changelog/17848.txt | 3 --- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 3 insertions(+), 6 deletions(-) delete mode 100644 changelog/17848.txt diff --git a/changelog/17848.txt b/changelog/17848.txt deleted file mode 100644 index 40579e4e184c..000000000000 --- a/changelog/17848.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -autopilot: Update version to v.0.2.0 to add better support for respecting min quorum -``` \ No newline at end of file diff --git a/go.mod b/go.mod index 245f89f337b1..6afb5f685c93 100644 --- a/go.mod +++ b/go.mod @@ -107,7 +107,7 @@ require ( github.com/hashicorp/hcp-sdk-go v0.23.0 github.com/hashicorp/nomad/api v0.0.0-20220707195938-75f4c2237b28 github.com/hashicorp/raft v1.3.10 - github.com/hashicorp/raft-autopilot v0.2.0 + github.com/hashicorp/raft-autopilot v0.1.6 github.com/hashicorp/raft-boltdb/v2 v2.0.0-20210421194847-a7e34179d62c github.com/hashicorp/raft-snapshot v1.0.4 github.com/hashicorp/vault-plugin-auth-alicloud v0.14.0 diff --git a/go.sum b/go.sum index 3321e6ea9550..6c95e456ffa2 100644 --- a/go.sum +++ b/go.sum @@ -1114,8 +1114,8 @@ github.com/hashicorp/raft v1.1.2-0.20191002163536-9c6bd3e3eb17/go.mod h1:vPAJM8A github.com/hashicorp/raft v1.2.0/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= github.com/hashicorp/raft v1.3.10 h1:LR5QZX1VQd0DFWZfeCwWawyeKfpS/Tm1yjnJIY5X4Tw= github.com/hashicorp/raft v1.3.10/go.mod h1:J8naEwc6XaaCfts7+28whSeRvCqTd6e20BlCU3LtEO4= -github.com/hashicorp/raft-autopilot v0.2.0 h1:2/R2RPgamgRKgNWGQioULZvjeKXQZmDuw5Ty+6c+H7Y= -github.com/hashicorp/raft-autopilot v0.2.0/go.mod h1:q6tZ8UAZ5xio2gv2JvjgmtOlh80M6ic8xQYBe2Egkg8= +github.com/hashicorp/raft-autopilot v0.1.6 h1:C1q3RNF2FfXNZfHWbvVAu0QixaQK8K5pX4O5lh+9z4I= +github.com/hashicorp/raft-autopilot v0.1.6/go.mod h1:Af4jZBwaNOI+tXfIqIdbcAnh/UyyqIMj/pOISIfhArw= github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= github.com/hashicorp/raft-boltdb/v2 v2.0.0-20210421194847-a7e34179d62c h1:oiKun9QlrOz5yQxMZJ3tf1kWtFYuKSJzxzEDxDPevj4= github.com/hashicorp/raft-boltdb/v2 v2.0.0-20210421194847-a7e34179d62c/go.mod h1:kiPs9g148eLShc2TYagUAyKDnD+dH9U+CQKsXzlY9xo= From a0beacda37cea0dd577421407c5083854a7228bc Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Fri, 24 Feb 2023 16:33:37 -0500 Subject: [PATCH 050/231] Backport of add nil check for secret id entry on delete via accessor into release/1.13.x (#19351) * backport of commit 8154be65a9d2c9703b808a8482ec47e1f547b94a * empty commit to re-run tests --------- Co-authored-by: davidadeleon <56207066+davidadeleon@users.noreply.github.com> --- builtin/credential/approle/path_role.go | 13 ++++- builtin/credential/approle/path_role_test.go | 56 ++++++++++++++++++++ changelog/19186.txt | 3 ++ 3 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 changelog/19186.txt diff --git a/builtin/credential/approle/path_role.go b/builtin/credential/approle/path_role.go index 35e3bfbbb686..7759c07037bb 100644 --- a/builtin/credential/approle/path_role.go +++ b/builtin/credential/approle/path_role.go @@ -1964,12 +1964,21 @@ func (b *backend) pathRoleSecretIDAccessorDestroyUpdateDelete(ctx context.Contex return nil, fmt.Errorf("failed to create HMAC of role_name: %w", err) } - entryIndex := fmt.Sprintf("%s%s/%s", role.SecretIDPrefix, roleNameHMAC, accessorEntry.SecretIDHMAC) - lock := b.secretIDLock(accessorEntry.SecretIDHMAC) lock.Lock() defer lock.Unlock() + // Verify we have a valid SecretID Storage Entry + entry, err := b.nonLockedSecretIDStorageEntry(ctx, req.Storage, role.SecretIDPrefix, roleNameHMAC, accessorEntry.SecretIDHMAC) + if err != nil { + return nil, err + } + if entry == nil { + return logical.ErrorResponse("invalid secret id accessor"), logical.ErrPermissionDenied + } + + entryIndex := fmt.Sprintf("%s%s/%s", role.SecretIDPrefix, roleNameHMAC, accessorEntry.SecretIDHMAC) + // Delete the accessor of the SecretID first if err := b.deleteSecretIDAccessorEntry(ctx, req.Storage, secretIDAccessor, role.SecretIDPrefix); err != nil { return nil, err diff --git a/builtin/credential/approle/path_role_test.go b/builtin/credential/approle/path_role_test.go index 3b201eefb4aa..d6ceed858154 100644 --- a/builtin/credential/approle/path_role_test.go +++ b/builtin/credential/approle/path_role_test.go @@ -2696,3 +2696,59 @@ func TestAppRole_SecretID_WithTTL(t *testing.T) { }) } } + +// TestAppRole_RoleSecretIDAccessorCrossDelete tests deleting a secret id via +// secret id accessor belonging to a different role +func TestAppRole_RoleSecretIDAccessorCrossDelete(t *testing.T) { + var resp *logical.Response + var err error + b, storage := createBackendWithStorage(t) + + // Create First Role + createRole(t, b, storage, "role1", "a,b") + _ = b.requestNoErr(t, &logical.Request{ + Operation: logical.UpdateOperation, + Storage: storage, + Path: "role/role1/secret-id", + }) + + // Create Second Role + createRole(t, b, storage, "role2", "a,b") + _ = b.requestNoErr(t, &logical.Request{ + Operation: logical.UpdateOperation, + Storage: storage, + Path: "role/role2/secret-id", + }) + + // Get role2 secretID Accessor + resp = b.requestNoErr(t, &logical.Request{ + Operation: logical.ListOperation, + Storage: storage, + Path: "role/role2/secret-id", + }) + + // Read back role2 secretID Accessor information + hmacSecretID := resp.Data["keys"].([]string)[0] + _ = b.requestNoErr(t, &logical.Request{ + Operation: logical.UpdateOperation, + Storage: storage, + Path: "role/role2/secret-id-accessor/lookup", + Data: map[string]interface{}{ + "secret_id_accessor": hmacSecretID, + }, + }) + + // Attempt to destroy role2 secretID accessor using role1 path + _, err = b.HandleRequest(context.Background(), &logical.Request{ + Operation: logical.UpdateOperation, + Storage: storage, + Path: "role/role1/secret-id-accessor/destroy", + Data: map[string]interface{}{ + "secret_id_accessor": hmacSecretID, + }, + }) + + if err == nil { + t.Fatalf("expected error") + } +} diff --git a/changelog/19186.txt b/changelog/19186.txt new file mode 100644 index 000000000000..cb3b59a9f92c --- /dev/null +++ b/changelog/19186.txt @@ -0,0 +1,3 @@ +```release-note:bug +auth/approle: Add nil check for the secret ID entry when deleting via secret id accessor preventing cross role secret id deletion +``` From a5edc660984090520fa14b067fef00013eff5202 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Mon, 27 Feb 2023 13:52:36 -0500 Subject: [PATCH 051/231] backport of commit d35be2d0de3d1c036248570c538c2051c4c1dc57 (#19375) Co-authored-by: Alexander Scheel --- changelog/19373.txt | 3 + command/transit_import_key.go | 56 ++++-- command/transit_import_key_test.go | 186 ++++++++++++++++++ command/transit_import_key_version.go | 18 +- .../content/docs/commands/transit/import.mdx | 62 ++++++ .../content/docs/commands/transit/index.mdx | 32 +++ website/data/docs-nav-data.json | 13 ++ 7 files changed, 347 insertions(+), 23 deletions(-) create mode 100644 changelog/19373.txt create mode 100644 command/transit_import_key_test.go create mode 100644 website/content/docs/commands/transit/import.mdx create mode 100644 website/content/docs/commands/transit/index.mdx diff --git a/changelog/19373.txt b/changelog/19373.txt new file mode 100644 index 000000000000..87751805e7d8 --- /dev/null +++ b/changelog/19373.txt @@ -0,0 +1,3 @@ +```release-note:bug +cli/transit: Fix import, import-version command invocation +``` diff --git a/command/transit_import_key.go b/command/transit_import_key.go index 04b42c0b7097..7acc90f2243c 100644 --- a/command/transit_import_key.go +++ b/command/transit_import_key.go @@ -8,6 +8,7 @@ import ( "encoding/base64" "encoding/pem" "fmt" + "os" "regexp" "strings" @@ -38,17 +39,20 @@ func (c *TransitImportCommand) Help() string { Usage: vault transit import PATH KEY [options...] Using the Transit or Transform key wrapping system, imports key material from - the base64 encoded KEY, into a new key whose API path is PATH. To import a new version - into an existing key, use import_version. The remaining options after KEY (key=value style) are passed - on to the transit/transform create key endpoint. If your system or device natively supports - the RSA AES key wrap mechanism, you should use it directly rather than this command. + the base64 encoded KEY (either directly on the CLI or via @path notation), + into a new key whose API path is PATH. To import a new version into an + existing key, use import_version. The remaining options after KEY (key=value + style) are passed on to the transit/transform create key endpoint. If your + system or device natively supports the RSA AES key wrap mechanism (such as + the PKCS#11 mechanism CKM_RSA_AES_KEY_WRAP), you should use it directly + rather than this command. ` + c.Flags().Help() return strings.TrimSpace(helpText) } func (c *TransitImportCommand) Flags() *FlagSets { - return c.flagSet(FlagSetHTTP | FlagSetOutputField | FlagSetOutputFormat) + return c.flagSet(FlagSetHTTP) } func (c *TransitImportCommand) AutocompleteArgs() complete.Predictor { @@ -60,13 +64,20 @@ func (c *TransitImportCommand) AutocompleteFlags() complete.Flags { } func (c *TransitImportCommand) Run(args []string) int { - return importKey(c.BaseCommand, "import", args) + return importKey(c.BaseCommand, "import", c.Flags(), args) } // error codes: 1: user error, 2: internal computation error, 3: remote api call error -func importKey(c *BaseCommand, operation string, args []string) int { - if len(args) != 2 { - c.UI.Error(fmt.Sprintf("Incorrect argument count (expected 2, got %d)", len(args))) +func importKey(c *BaseCommand, operation string, flags *FlagSets, args []string) int { + // Parse and validate the arguments. + if err := flags.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + args = flags.Args() + if len(args) < 2 { + c.UI.Error(fmt.Sprintf("Incorrect argument count (expected 2+, got %d). Wanted PATH to import into and KEY material.", len(args))) return 1 } @@ -89,7 +100,18 @@ func importKey(c *BaseCommand, operation string, args []string) int { path := parts[1] keyName := parts[2] - key, err := base64.StdEncoding.DecodeString(args[1]) + keyMaterial := args[1] + if keyMaterial[0] == '@' { + keyMaterialBytes, err := os.ReadFile(keyMaterial[1:]) + if err != nil { + c.UI.Error(fmt.Sprintf("error reading key material file: %v", err)) + return 1 + } + + keyMaterial = string(keyMaterialBytes) + } + + key, err := base64.StdEncoding.DecodeString(keyMaterial) if err != nil { c.UI.Error(fmt.Sprintf("error base64 decoding source key material: %v", err)) return 1 @@ -126,15 +148,19 @@ func importKey(c *BaseCommand, operation string, args []string) int { } combinedCiphertext := append(wrappedAESKey, wrappedTargetKey...) importCiphertext := base64.StdEncoding.EncodeToString(combinedCiphertext) + // Parse all the key options - data := map[string]interface{}{ - "ciphertext": importCiphertext, + data, err := parseArgsData(os.Stdin, args[2:]) + if err != nil { + c.UI.Error(fmt.Sprintf("Failed to parse extra K=V data: %s", err)) + return 1 } - for _, v := range args[2:] { - parts := strings.Split(v, "=") - data[parts[0]] = parts[1] + if data == nil { + data = make(map[string]interface{}, 1) } + data["ciphertext"] = importCiphertext + c.UI.Output("Submitting wrapped key to Vault transit.") // Finally, call import _, err = client.Logical().Write(path+"/keys/"+keyName+"/"+operation, data) diff --git a/command/transit_import_key_test.go b/command/transit_import_key_test.go new file mode 100644 index 000000000000..d13c032048ff --- /dev/null +++ b/command/transit_import_key_test.go @@ -0,0 +1,186 @@ +package command + +import ( + "bytes" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "testing" + + "github.com/hashicorp/vault/api" + + "github.com/stretchr/testify/require" +) + +// Validate the `vault transit import` command works. +func TestTransitImport(t *testing.T) { + t.Parallel() + + client, closer := testVaultServer(t) + defer closer() + + if err := client.Sys().Mount("transit", &api.MountInput{ + Type: "transit", + }); err != nil { + t.Fatalf("transit mount error: %#v", err) + } + + rsa1, rsa2, aes128, aes256 := generateKeys(t) + + type testCase struct { + variant string + path string + key []byte + args []string + shouldFail bool + } + tests := []testCase{ + { + "import", + "transit/keys/rsa1", + rsa1, + []string{"type=rsa-2048"}, + false, /* first import */ + }, + { + "import", + "transit/keys/rsa1", + rsa2, + []string{"type=rsa-2048"}, + true, /* already exists */ + }, + { + "import-version", + "transit/keys/rsa1", + rsa2, + []string{"type=rsa-2048"}, + false, /* new version */ + }, + { + "import", + "transit/keys/rsa2", + rsa2, + []string{"type=rsa-4096"}, + true, /* wrong type */ + }, + { + "import", + "transit/keys/rsa2", + rsa2, + []string{"type=rsa-2048"}, + false, /* new name */ + }, + { + "import", + "transit/keys/aes1", + aes128, + []string{"type=aes128-gcm96"}, + false, /* first import */ + }, + { + "import", + "transit/keys/aes1", + aes256, + []string{"type=aes256-gcm96"}, + true, /* already exists */ + }, + { + "import-version", + "transit/keys/aes1", + aes256, + []string{"type=aes256-gcm96"}, + true, /* new version, different type */ + }, + { + "import-version", + "transit/keys/aes1", + aes128, + []string{"type=aes128-gcm96"}, + false, /* new version */ + }, + { + "import", + "transit/keys/aes2", + aes256, + []string{"type=aes128-gcm96"}, + true, /* wrong type */ + }, + { + "import", + "transit/keys/aes2", + aes256, + []string{"type=aes256-gcm96"}, + false, /* new name */ + }, + } + + for index, tc := range tests { + t.Logf("Running test case %d: %v", index, tc) + execTransitImport(t, client, tc.variant, tc.path, tc.key, tc.args, tc.shouldFail) + } +} + +func execTransitImport(t *testing.T, client *api.Client, method string, path string, key []byte, data []string, expectFailure bool) { + t.Helper() + + keyBase64 := base64.StdEncoding.EncodeToString(key) + + var args []string + args = append(args, "transit") + args = append(args, method) + args = append(args, path) + args = append(args, keyBase64) + args = append(args, data...) + + stdout := bytes.NewBuffer(nil) + stderr := bytes.NewBuffer(nil) + runOpts := &RunOptions{ + Stdout: stdout, + Stderr: stderr, + Client: client, + } + + code := RunCustom(args, runOpts) + combined := stdout.String() + stderr.String() + + if code != 0 { + if !expectFailure { + t.Fatalf("Got unexpected failure from test (ret %d): %v", code, combined) + } + } else { + if expectFailure { + t.Fatalf("Expected failure, got success from test (ret %d): %v", code, combined) + } + } +} + +func generateKeys(t *testing.T) (rsa1 []byte, rsa2 []byte, aes128 []byte, aes256 []byte) { + t.Helper() + + priv1, err := rsa.GenerateKey(rand.Reader, 2048) + require.NotNil(t, priv1, "failed generating RSA 1 key") + require.NoError(t, err, "failed generating RSA 1 key") + + rsa1, err = x509.MarshalPKCS8PrivateKey(priv1) + require.NotNil(t, rsa1, "failed marshaling RSA 1 key") + require.NoError(t, err, "failed marshaling RSA 1 key") + + priv2, err := rsa.GenerateKey(rand.Reader, 2048) + require.NotNil(t, priv2, "failed generating RSA 2 key") + require.NoError(t, err, "failed generating RSA 2 key") + + rsa2, err = x509.MarshalPKCS8PrivateKey(priv2) + require.NotNil(t, rsa2, "failed marshaling RSA 2 key") + require.NoError(t, err, "failed marshaling RSA 2 key") + + aes128 = make([]byte, 128/8) + _, err = rand.Read(aes128) + require.NoError(t, err, "failed generating AES 128 key") + + aes256 = make([]byte, 256/8) + _, err = rand.Read(aes256) + require.NoError(t, err, "failed generating AES 256 key") + + return +} diff --git a/command/transit_import_key_version.go b/command/transit_import_key_version.go index ee84a35efeea..7e5a5601997e 100644 --- a/command/transit_import_key_version.go +++ b/command/transit_import_key_version.go @@ -22,21 +22,23 @@ func (c *TransitImportVersionCommand) Synopsis() string { func (c *TransitImportVersionCommand) Help() string { helpText := ` -Usage: vault transit import-version PATH KEY +Usage: vault transit import-version PATH KEY [...] Using the Transit or Transform key wrapping system, imports key material from - the base64 encoded KEY, into a new key whose API path is PATH. To import a new transit/transform key, - use import. The remaining options after KEY (key=value style) are passed on to the transit/transform create key - endpoint. - If your system or device natively supports the RSA AES key wrap mechanism, you should use it directly - rather than this command. + the base64 encoded KEY (either directly on the CLI or via @path notation), + into a new key whose API path is PATH. To import a new transit/transform + key, use the import command instead. The remaining options after KEY + (key=value style) are passed on to the transit/transform create key endpoint. + If your system or device natively supports the RSA AES key wrap mechanism + (such as the PKCS#11 mechanism CKM_RSA_AES_KEY_WRAP), you should use it + directly rather than this command. ` + c.Flags().Help() return strings.TrimSpace(helpText) } func (c *TransitImportVersionCommand) Flags() *FlagSets { - return c.flagSet(FlagSetHTTP | FlagSetOutputField | FlagSetOutputFormat) + return c.flagSet(FlagSetHTTP) } func (c *TransitImportVersionCommand) AutocompleteArgs() complete.Predictor { @@ -48,5 +50,5 @@ func (c *TransitImportVersionCommand) AutocompleteFlags() complete.Flags { } func (c *TransitImportVersionCommand) Run(args []string) int { - return importKey(c.BaseCommand, "import_version", args) + return importKey(c.BaseCommand, "import_version", c.Flags(), args) } diff --git a/website/content/docs/commands/transit/import.mdx b/website/content/docs/commands/transit/import.mdx new file mode 100644 index 000000000000..2a8c72055602 --- /dev/null +++ b/website/content/docs/commands/transit/import.mdx @@ -0,0 +1,62 @@ +--- +layout: docs +page_title: transit import and transit import-version - Command +description: |- + The "transit import" and "transit import-version" commands import the + specified key into Transit, via the Transit BYOK mechanism. +--- + +# transit import and transit import-version + +The `transit import` and `transit import-version` commands import the +specified key into Transit, via the [Transit BYOK +mechanism](/vault/docs/secrets/transit#bring-your-own-key-byok). The former +imports this key as a new key, failing if it already exists, whereas the +latter will only update an existing key in Transit to a new version of the +key material. + +This needs access to read the transit mount's wrapping key (at +`transit/wrapping_key`) and the ability to write to either import +endpoints (either `transit/keys/:name/import` or +`transit/keys/:name/import_version`). + +## Examples + +Imports a 2048-bit RSA key as a new key: + +``` +$ vault transit import transit/keys/test-key @test-key type=rsa-2048 +Retrieving transit wrapping key. +Wrapping source key with ephemeral key. +Encrypting ephemeral key with transit wrapping key. +Submitting wrapped key to Vault transit. +Success! +``` + +Imports a new version of an existing key: + +``` +$ vault transit import-version transit/keys/test-key @test-key-updated +Retrieving transit wrapping key. +Wrapping source key with ephemeral key. +Encrypting ephemeral key with transit wrapping key. +Submitting wrapped key to Vault transit. +Success! +``` + +## Usage + +This command does not have any unique flags and respects core Vault CLI +commands. See `vault transit import -help` for more information. + +This command requires two positional arguments: + + 1. `PATH`, the path to the transit key to import in the format of + `/keys/`, where `` is the path to the mount + (using `-namespace=` to specify any namespaces), and `` + is the desired name of the key. + 2. `KEY`, the key material to import in Standard Base64 encoding (either + of a raw key in the case of symmetric keys such as AES, or of the DER + encoded format for asymmetric keys such as RSA). If the value for `KEY` + begins with an `@`, the CLI argument is assumed to be a path to a file + on disk to be read. diff --git a/website/content/docs/commands/transit/index.mdx b/website/content/docs/commands/transit/index.mdx new file mode 100644 index 000000000000..72f8291eb07d --- /dev/null +++ b/website/content/docs/commands/transit/index.mdx @@ -0,0 +1,32 @@ +--- +layout: docs +page_title: transit - Command +description: |- + The "transit" command groups subcommands for interacting with Vault's Transit + secrets engine. +--- + +# transit + +The `transit` command groups subcommands for interacting with Vault's +[Transit Secrets Engine](/vault/docs/secrets/transit). + +## Syntax + +Option flags for a given subcommand are provided after the subcommand, but before the arguments. + +## Examples + +To [import](/vault/docs/commands/transit/import) keys into a mount via the +[Transit BYOK](/vault/docs/secrets/transit#bring-your-own-key-byok) +mechanism, use the `vault transit import ` or +`vault transit import-version ` commands: + +``` +$ vault transit import transit/keys/test-key @test-key type=rsa-2048 +Retrieving transit wrapping key. +Wrapping source key with ephemeral key. +Encrypting ephemeral key with transit wrapping key. +Submitting wrapped key to Vault transit. +Success! +``` diff --git a/website/data/docs-nav-data.json b/website/data/docs-nav-data.json index 06ce320ae12b..5c65bcd63bdb 100644 --- a/website/data/docs-nav-data.json +++ b/website/data/docs-nav-data.json @@ -828,6 +828,19 @@ } ] }, + { + "title": "transit", + "routes": [ + { + "title": "Overview", + "path": "commands/transit" + }, + { + "title": "import and import-version", + "path": "commands/transit/import" + } + ] + }, { "title": "unwrap", "path": "commands/unwrap" From 478b6f15ebcb3bc211883a1e25c422bb6df57af8 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Mon, 27 Feb 2023 16:09:14 -0500 Subject: [PATCH 052/231] backport of commit 7b2ff1f111b95786528bd578fea5f25b88afb119 (#19382) Co-authored-by: Yoko Hyakuna --- website/content/docs/release-notes/1.13.0.mdx | 113 ++++++++++++++++++ website/data/docs-nav-data.json | 4 + 2 files changed, 117 insertions(+) create mode 100644 website/content/docs/release-notes/1.13.0.mdx diff --git a/website/content/docs/release-notes/1.13.0.mdx b/website/content/docs/release-notes/1.13.0.mdx new file mode 100644 index 000000000000..915dcb23e52b --- /dev/null +++ b/website/content/docs/release-notes/1.13.0.mdx @@ -0,0 +1,113 @@ +--- +layout: docs +page_title: 1.13.0 +description: |- + This page contains release notes for Vault 1.13.0 +--- + +# Vault 1.13.0 Release Notes + +**Software Release date:** March 1, 2023 + +**Summary:** Vault Release 1.13.0 offers features and enhancements that improve +the user experience while solving critical issues previously encountered by our +customers. We are providing an overview of improvements in this set of release +notes. + +We encourage you to [upgrade](/vault/docs/upgrading) to the latest release of +Vault to take advantage of the new benefits provided. With this latest release, +we offer solutions to critical feature gaps that were identified previously. +Please refer to the +[Changelog](https://github.com/hashicorp/vault/blob/main/CHANGELOG.md#1130-rc1) +within the Vault release for further information on product improvements, +including a comprehensive list of bug fixes. + +Some of these enhancements and changes in this release include the following: + +- **PKI improvements:** + - **Cross Cluster PKI Certificate Revocation:** Introducing a new unified + OCSP responder and CRL builder that enables a certificate revocations and + CRL view across clusters for a given PKI mount. + - **PKI UI Beta:** New UI introducing cross-signing flow, overview page, + roles and keys view. + - **Health Checks:** Provide a health overview of PKI mounts for proactive + actions and troubleshooting. + - **Command Line:** Simplified CLI to discover, rotate issuers and related + commands for PKI mounts + +- **Azure Auth Improvements:** + - **Rotate-root support:** Add the ability to rotate the root account's + client secret defined in the auth method's configuration via the new + `rotate-root` endpoint. + - **Managed Identities authentication:** The auth method now allows any Azure + resource that supports managed identities to authenticate with Vault. + - **VMSS Flex authentication:** Add support for Virtual Machine Scale Set + (VMSS) Flex authentication. + +- **GCP Secrets Impersonated Account Support:** Add support for GCP service + account impersonation, allowing callers to generate a GCP access token without + requiring Vault to store or retrieve a GCP service account key for each role. +- **Managed Keys in Transit Engine:** Support for offloading Transit Key + operations to HSMs/external KMS. +- **KMIP Secret Engine Enhancements:** Implemented Asymmetric Key Lifecycle + Server and Advanced Cryptographic Server profiles. Added support for RSA keys + and operations such as: MAC, MAC Verify, Sign, Sign Verify, RNG Seed and RNG + Retrieve. +- **Vault as a SSM:** Support is planned for an upcoming Vault PKCS#11 Provider + version to include mechanisms for encryption, decryption, signing and + signature verification for AES and RSA keys. +- **Replication (enterprise):** We fixed a bug that could cause a cluster to + wind up in a permanent merkle-diff/merkle-sync loop and never enter + stream-wals, particularly in cases of high write loads on the primary cluster. +- **Share Secrets in Independent Namespaces (enterprise):** You can now add + users from namespaces outside a namespace hierarchy to a group in a given + namespace hierarchy. For Vault Agent, you can now grant it access to secrets + outside the namespace where it authenticated, and reduce the number of Agents + you need to run. +- **User Lockout:** Vault now supports configuration to lock out users when they + have consecutive failed login attempts. +- **Event System (Alpha):** Vault has a new experimental event system. Events + are currently only generated on writes to the KV secrets engine, but external + plugins can also be updated to start generating events. +- **Kubernetes authentication plugin bug fix:** Ensures a consistent TLS + configuration for all k8s API requests. This fixes a bug where it was possible + for the http.Client's Transport to be missing the necessary root CAs to ensure + that all TLS connections between the auth engine and the Kubernetes API were + validated against the configured set of CA certificates. +- **Kubernetes Secretes Engine on Vault UI:** Introducing Kubernetes secret + engine support on the UI +- **Client Count UI improvements:** Combining current month and previous history + into one dashboard +- **OCSP Support in the TLS Certificate Auth Method:** The auth method now can + check for revoked certificates using the OCSP protocol. +- **UI Wizard removal:** The UI Wizard has been removed from the UI since the + information was occasionally out-of-date and did not align with the latest + changes. A new and enhanced UI experience is planned in a future release. + +- **Vault Agent improvements:** + - Auto-auth introduced `token_file` method which reads an existing token from + a file. The token file method is designed for development and testing. It + is not suitable for production deployment. + - Listeners for the Vault Agent can define a role set to `metrics_only` so + that a service can be configured to listen on a particular port to collect + metrics. + - Vault Agent can read configurations from multiple files. + - Users can specify the log file path using the `-log-file` command flag or + `VAULT_LOG_FILE` environment variable. This is particularly useful when + Vault Agent is running as a Windows service. + +- **OpenAPI-based Go & .NET Client Libraries (Public Beta):** Use the new Go & + .NET client libraries to interact with the Vault API from your applications. + - [OpenAPI-based Go client library](https://github.com/hashicorp/vault-client-go/) + - [OpenAPI-based .NET client library](https://github.com/hashicorp/vault-client-dotnet/) + +## Known issues + +There are no known issues documented for this release. + +## Feature Deprecations and EOL + +Please refer to the [Deprecation Plans and Notice](/vault/docs/deprecation) page +for up-to-date information on feature deprecations and plans. A [Feature +Deprecation FAQ](/vault/docs/deprecation/faq) page addresses questions about +decisions made about Vault feature deprecations. \ No newline at end of file diff --git a/website/data/docs-nav-data.json b/website/data/docs-nav-data.json index 5c65bcd63bdb..1e4b4939f271 100644 --- a/website/data/docs-nav-data.json +++ b/website/data/docs-nav-data.json @@ -1997,6 +1997,10 @@ "title": "Overview", "path": "release-notes" }, + { + "title": "1.13.0", + "path": "release-notes/1.13.0" + }, { "title": "1.12.0", "path": "release-notes/1.12.0" From 1240c8c78edd0ae81049cfd2051766285343c8b9 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Mon, 27 Feb 2023 21:00:44 -0500 Subject: [PATCH 053/231] backport of commit 538bb799e49ba12e6b6fec9877d7a03b2225d239 (#19381) Co-authored-by: Rowan Smith <86935689+rowansmithhc@users.noreply.github.com> --- website/content/api-docs/auth/approle.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/content/api-docs/auth/approle.mdx b/website/content/api-docs/auth/approle.mdx index 2e10ac7e6bca..24363a85965e 100644 --- a/website/content/api-docs/auth/approle.mdx +++ b/website/content/api-docs/auth/approle.mdx @@ -60,7 +60,8 @@ enabled while creating or updating a role. ### Parameters -- `role_name` `(string: )` - Name of the AppRole. Must be less than 4096 bytes. +- `role_name` `(string: )` - Name of the AppRole. Must be less than 4096 bytes, accepted characters +include a-Z, 0-9, space, hyphen, underscore and periods. - `bind_secret_id` `(bool: true)` - Require `secret_id` to be presented when logging in using this AppRole. - `secret_id_bound_cidrs` `(array: [])` - Comma-separated string or list of CIDR From b3dc15fe7449edb7566f8e84b2e07416ca996b7e Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Tue, 28 Feb 2023 10:12:36 -0500 Subject: [PATCH 054/231] backport of commit ba013912b1b2fd75fd7776fecb5e5f0329cb21e4 (#19396) Co-authored-by: Alexander Scheel --- command/commands.go | 5 ++++ command/pki.go | 2 +- command/transit.go | 39 +++++++++++++++++++++++++++ command/transit_import_key.go | 3 ++- command/transit_import_key_version.go | 5 ++-- 5 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 command/transit.go diff --git a/command/commands.go b/command/commands.go index f441c3ee1d05..21da8141bc54 100644 --- a/command/commands.go +++ b/command/commands.go @@ -704,6 +704,11 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) map[string]cli.Co BaseCommand: getBaseCommand(), }, nil }, + "transit": func() (cli.Command, error) { + return &TransitCommand{ + BaseCommand: getBaseCommand(), + }, nil + }, "transit import": func() (cli.Command, error) { return &TransitImportCommand{ BaseCommand: getBaseCommand(), diff --git a/command/pki.go b/command/pki.go index 4212ee6f86ab..8ae5eae4a64e 100644 --- a/command/pki.go +++ b/command/pki.go @@ -13,7 +13,7 @@ type PKICommand struct { } func (c *PKICommand) Synopsis() string { - return "Interact with Vault's Key-Value storage" + return "Interact with Vault's PKI Secrets Engine" } func (c *PKICommand) Help() string { diff --git a/command/transit.go b/command/transit.go new file mode 100644 index 000000000000..9b4b3050161f --- /dev/null +++ b/command/transit.go @@ -0,0 +1,39 @@ +package command + +import ( + "strings" + + "github.com/mitchellh/cli" +) + +var _ cli.Command = (*TransitCommand)(nil) + +type TransitCommand struct { + *BaseCommand +} + +func (c *TransitCommand) Synopsis() string { + return "Interact with Vault's Transit Secrets Engine" +} + +func (c *TransitCommand) Help() string { + helpText := ` +Usage: vault transit [options] [args] + + This command has subcommands for interacting with Vault's Transit Secrets + Engine. Here are some simple examples, and more detailed examples are + available in the subcommands or the documentation. + + To import a key into the specified Transit or Transform mount: + + $ vault transit import transit/keys/newly-imported @path/to/key type=rsa-2048 + + Please see the individual subcommand help for detailed usage information. +` + + return strings.TrimSpace(helpText) +} + +func (c *TransitCommand) Run(args []string) int { + return cli.RunResultHelp +} diff --git a/command/transit_import_key.go b/command/transit_import_key.go index 7acc90f2243c..56e72f835f2b 100644 --- a/command/transit_import_key.go +++ b/command/transit_import_key.go @@ -42,10 +42,11 @@ Usage: vault transit import PATH KEY [options...] the base64 encoded KEY (either directly on the CLI or via @path notation), into a new key whose API path is PATH. To import a new version into an existing key, use import_version. The remaining options after KEY (key=value - style) are passed on to the transit/transform create key endpoint. If your + style) are passed on to the Transit or Transform create key endpoint. If your system or device natively supports the RSA AES key wrap mechanism (such as the PKCS#11 mechanism CKM_RSA_AES_KEY_WRAP), you should use it directly rather than this command. + ` + c.Flags().Help() return strings.TrimSpace(helpText) diff --git a/command/transit_import_key_version.go b/command/transit_import_key_version.go index 7e5a5601997e..7b38f7dc7689 100644 --- a/command/transit_import_key_version.go +++ b/command/transit_import_key_version.go @@ -26,12 +26,13 @@ Usage: vault transit import-version PATH KEY [...] Using the Transit or Transform key wrapping system, imports key material from the base64 encoded KEY (either directly on the CLI or via @path notation), - into a new key whose API path is PATH. To import a new transit/transform + into a new key whose API path is PATH. To import a new Transit or Transform key, use the import command instead. The remaining options after KEY - (key=value style) are passed on to the transit/transform create key endpoint. + (key=value style) are passed on to the Transit or Transform create key endpoint. If your system or device natively supports the RSA AES key wrap mechanism (such as the PKCS#11 mechanism CKM_RSA_AES_KEY_WRAP), you should use it directly rather than this command. + ` + c.Flags().Help() return strings.TrimSpace(helpText) From 7383b52b80d9fbb121841d854906d6063289dd3c Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Tue, 28 Feb 2023 10:47:52 -0500 Subject: [PATCH 055/231] backport of commit 52bbf65ae7232e9306c8c8d7d392399f82d24f04 (#19397) Co-authored-by: Alexander Scheel --- .../content/docs/enterprise/fips/index.mdx | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/website/content/docs/enterprise/fips/index.mdx b/website/content/docs/enterprise/fips/index.mdx index 42428a78a891..0c5a82407408 100644 --- a/website/content/docs/enterprise/fips/index.mdx +++ b/website/content/docs/enterprise/fips/index.mdx @@ -22,3 +22,27 @@ can be found on the [FIPS 140-2 Inside](/vault/docs/enterprise/fips/fips1402) pa Before our FIPS Inside effort, Vault [depended on](https://www.hashicorp.com/vault-compliance) an external HSM for FIPS 140-2 compliance. This uses the [Seal Wrap](/vault/docs/enterprise/fips/sealwrap) functionality to wrap security relevant keys in an extra layer of encryption. + +## Comparison of Versions + +The below table attempts to documents the FIPS compliance of various Vault +operations between FIPS Inside and FIPS Seal Wrap. This table is by no means +an official evaluation of either product; refer to the Leidos Letters of +Attestation for that information. + +| Feature | FIPS Inside | FIPS Seal Wrap | +| :-------------------------------- | :----------------------- | :--------------------------------------- | +| Entropy Augmentation | Not Supported | Yes | +| TLS Listener | Yes | No | +| Vault HA/DR/Raft TLS | Yes | No | +| Barrier Storage | Yes | No | +| Seal Wrapping of CSPs | With FIPS-Compliant HSM | With FIPS-Compliant HSM | +| SSH CA Operations | Yes with FIPS algorithms | No | +| Transit Operations | Yes with FIPS algorithms | With Managed Keys and FIPS-Compliant HSM | +| PKI Operations | Yes with FIPS algorithms | With Managed Keys and FIPS-Compliant HSM | +| KMIP (Key Creation & Use) | Yes with FIPS algorithms | No | +| Transform Tokenization | Yes | No | +| Vault Agent TLS & Internal Crypto | Yes | No | +| Vault to External Plugin TLS | Yes from Vault's side | No | +| Plugin to third-party service TLS | Yes from Vault's side | No | +| Auth Plugins' Internal Crypto | Yes with FIPS algorithms | No | From 20e201bdc3038ab740b7ad29ca959dcd17715d1b Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Tue, 28 Feb 2023 13:55:57 -0500 Subject: [PATCH 056/231] backport of commit da31528fdc0d9b043a21b1676694eecfaef130db (#19405) Co-authored-by: Austin Gebauer <34121980+austingebauer@users.noreply.github.com> --- website/content/api-docs/secret/ad.mdx | 2 + website/content/docs/deprecation/faq.mdx | 2 +- website/content/docs/deprecation/index.mdx | 3 +- .../docs/secrets/{ad.mdx => ad/index.mdx} | 2 + .../docs/secrets/ad/migration-guide.mdx | 163 ++++++++++++++++++ .../docs/upgrading/upgrade-to-1.13.x.mdx | 8 + .../partials/ad-secrets-deprecation.mdx | 5 + website/data/docs-nav-data.json | 11 +- 8 files changed, 193 insertions(+), 3 deletions(-) rename website/content/docs/secrets/{ad.mdx => ad/index.mdx} (99%) create mode 100644 website/content/docs/secrets/ad/migration-guide.mdx create mode 100644 website/content/partials/ad-secrets-deprecation.mdx diff --git a/website/content/api-docs/secret/ad.mdx b/website/content/api-docs/secret/ad.mdx index fa2c1b5ba6f4..61c7b99bf282 100644 --- a/website/content/api-docs/secret/ad.mdx +++ b/website/content/api-docs/secret/ad.mdx @@ -6,6 +6,8 @@ description: This is the API documentation for the Vault Active Directory secret # Active Directory Secrets Engine (API) +@include 'ad-secrets-deprecation.mdx' + @include 'x509-sha1-deprecation.mdx' This is the API documentation for the Vault AD secrets engine. For general diff --git a/website/content/docs/deprecation/faq.mdx b/website/content/docs/deprecation/faq.mdx index 88fc18b175d9..c953ef5a576a 100644 --- a/website/content/docs/deprecation/faq.mdx +++ b/website/content/docs/deprecation/faq.mdx @@ -3,7 +3,7 @@ layout: docs page_title: Feature Deprecation FAQ sidebar_title: FAQ description: |- - An FAQ page to communicate frequently asked questions concering feature deprecations. + An FAQ page to communicate frequently asked questions concerning feature deprecations. --- # Feature Deprecation FAQ diff --git a/website/content/docs/deprecation/index.mdx b/website/content/docs/deprecation/index.mdx index 057783250d83..7dbe52d92958 100644 --- a/website/content/docs/deprecation/index.mdx +++ b/website/content/docs/deprecation/index.mdx @@ -20,9 +20,10 @@ This announcement page is maintained and updated periodically to communicate imp | Feature | Deprecation announcement | End of Support | Feature Removal | Migration Path/Impact | Resources | | ------------------------------------------------- | ------------------------ | -------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Active Directory Secrets Engine | 1.13 | v1.18 | v1.19 | Use the [LDAP Secrets Engine](/vault/docs/secrets/ldap) with the `ad` [schema](/vault/api-docs/secret/ldap#schema) | [Migration Guide](/vault/docs/secrets/ad/migration-guide) | | Vault Enterprise storage backend | N/A | v1.12 | N/A | Use [Integrated Storage](/vault/docs/configuration/storage/raft) or [Consul](/vault/docs/configuration/storage/consul) as your Vault's storage backend. Vault Enterprise will no longer start up if configured to use a storage backend other than Integrated Storage or Consul. | [Upgrade Guide](/vault/docs/upgrading/upgrade-to-1.12.x) | | Vault generation of Dynamic SSH Keys | v0.7.1 | N/A | v1.13 | Use the alternative [signed SSH certificates](/vault/docs/secrets/ssh/signed-ssh-certificates) feature which supports key pair generation as of Vault 1.12. SSH certificates do not require an external connection from Vault to provision the key/certificate and more secure than having Vault provision dynamic SSH keys. | [SSH Certificates](/vault/docs/secrets/ssh/signed-ssh-certificates) | -| [Duplicative Docker Images](https://hub.docker.com/_/vault) | v1.12 | 1.13 | v1.14 | Upon feature removal, the `vault` Docker image will no longer be updated. Only the [Verified Publisher](https://hub.docker.com/r/hashicorp/vault) `hashicorp/vault` image will be updated on DockerHub. Users of [Official Images](https://hub.docker.com/_/vault) will need to use `docker pull hashicorp/vault:` instead of `docker pull vault:version` to get newer versions of Vault in Docker images. Currently, HashiCorp publishes and updates identical Docker images of Vault as Verified Publisher and Official images separately. | [The Verified Publisher Program](https://docs.docker.com/docker-hub/publish/) +| [Duplicative Docker Images](https://hub.docker.com/_/vault) | v1.12 | 1.13 | v1.14 | Upon feature removal, the `vault` Docker image will no longer be updated. Only the [Verified Publisher](https://hub.docker.com/r/hashicorp/vault) `hashicorp/vault` image will be updated on DockerHub. Users of [Official Images](https://hub.docker.com/_/vault) will need to use `docker pull hashicorp/vault:` instead of `docker pull vault:version` to get newer versions of Vault in Docker images. Currently, HashiCorp publishes and updates identical Docker images of Vault as Verified Publisher and Official images separately. | [The Verified Publisher Program](https://docs.docker.com/docker-hub/publish/) | | Etcd V2 API (OSS) | v1.9 | N/A | v1.10 | The Etcd v2 has been deprecated with the release of Etcd v3.5, and will be decomissioned by Etcd v3.6. Etcd v2 API has been removed in Vaut 1.10. Users of Etcd storage backend must migrate Vault storage to an Etcd V3 cluster prior to upgrading to Vault 1.10. All storage migrations should be backed up prior to migration. | [Etcd Storage Backend](/vault/docs/configuration/storage/etcd) | | Licenses in storage (ENT) | v1.8 | v1.10 | v1.11 | Migrate to [Autoloading](/vault/docs/enterprise/license/autoloading) by v1.11. | [Vault License](/vault/docs/enterprise/license) [System Backend](/vault/api-docs/system/license) [FAQ](/vault/docs/enterprise/license/faq) | | Mount Filters (ENT) | v1.3 | v1.10 | v1.11 | Use the alternative feature: [Path Filters](/vault/api-docs/system/replication/replication-performance#create-paths-filter). | [API Deprecation Notice](/vault/api-docs/system/replication/replication-performance#create-mounts-filter-deprecated) [Filter Mount Replication Deprecation Notice](/vault/docs/upgrading/upgrade-to-1.3.0#filtered-mount-replication-deprecation) | diff --git a/website/content/docs/secrets/ad.mdx b/website/content/docs/secrets/ad/index.mdx similarity index 99% rename from website/content/docs/secrets/ad.mdx rename to website/content/docs/secrets/ad/index.mdx index a7bfcb07e877..8c94477f49bc 100644 --- a/website/content/docs/secrets/ad.mdx +++ b/website/content/docs/secrets/ad/index.mdx @@ -7,6 +7,8 @@ description: >- # Active Directory Secrets Engine +@include 'ad-secrets-deprecation.mdx' + @include 'x509-sha1-deprecation.mdx' The Active Directory (AD) secrets engine is a plugin residing [here](https://github.com/hashicorp/vault-plugin-secrets-active-directory). diff --git a/website/content/docs/secrets/ad/migration-guide.mdx b/website/content/docs/secrets/ad/migration-guide.mdx new file mode 100644 index 000000000000..5af91330f4b0 --- /dev/null +++ b/website/content/docs/secrets/ad/migration-guide.mdx @@ -0,0 +1,163 @@ +--- +layout: docs +page_title: Migration Guide - Active Directory - Secrets Engines +description: >- + The guide for migrating from the Active Directory secrets engine to the LDAP secrets engine. +--- + +# Migration Guide - Active Directory Secrets Engine + +The Vault [Active Directory secrets engine](/vault/docs/secrets/ad) has been deprecated as +of the Vault 1.13 release. This document provides guidance for migrating from the Active +Directory secrets engine to the [LDAP secrets engine](/vault/docs/secrets/ldap) that was +introduced in Vault 1.12. + +## Deprecation Timeline + +Beginning from the Vault 1.13 release, we will continue to support the Active Directory (AD) +secrets engine in maintenance mode for six major Vault releases. Maintenance mode means that +we will fix bugs and security issues, but no new features will be added. All new feature +development efforts will go towards the unified LDAP secrets engine. At Vault 1.18, we will +mark the AD secrets engine as [pending removal](/vault/docs/deprecation/faq#pending-removal). +At this time, Vault will begin to strongly signal operators that they need to migrate off of +the AD secrets engine. At Vault 1.19, we will mark the AD secrets engine as +[removed](/vault/docs/deprecation/faq#removed). At this time, the AD secrets engine will be +removed from Vault. Vault will not start up with the AD secrets engine mounts enabled. + +## Migration Steps + +The following sections detail how to migrate the AD secrets engine configuration and +applications consuming secrets to the new LDAP secrets engine. + +### 1. Enable LDAP Secrets Engine + +The LDAP secrets engine needs to be enabled in order to have a target for migration of +existing AD secrets engine mounts. AD secrets engine mounts should be mapped 1-to-1 with +new LDAP secrets engine mounts. + +To enable the LDAP secrets engine: + +```shell-session +$ vault secrets enable ldap +``` + +To enable at a custom path: + +```shell-session +$ vault secrets enable -path= ldap +``` + +If enabled at a custom path, the `/ldap/` path segment in API paths must be replaced with +the custom path value. + +### 2. Migrate Configuration + +The AD secrets engine [configuration](/vault/api-docs/secret/ad#configuration) +will need to be migrated to an LDAP secrets engine [configuration](/vault/api-docs/secret/ldap#configuration-management). +The API paths and parameters will need to be considered during the migration. + +#### API Path + +| AD Secrets Engine | LDAP Secrets Engine | +| ----------------- |-------------------- | +| [/ad/config](/vault/api-docs/secret/ad#configuration) | [/ldap/config](/vault/api-docs/secret/ad#configuration) | + +#### Parameters + +The parameters from existing AD secrets engine configurations can generally be mapped 1-to-1 +to LDAP secrets engine configuration. The following LDAP secrets engine parameters are the +exception and must be considered during the migration. + +| AD Secrets Engine | LDAP Secrets Engine | Details | +| ----------------- | ------------------- | ------- | +| N/A | [schema](/vault/api-docs/secret/ldap#schema) | Must be set to the `ad` option on the LDAP secrets engine configuration. | +| [userdn](/vault/api-docs/secret/ad#userdn) | [userdn](/vault/api-docs/secret/ad#userdn) | Required to be set if using the [library sets](#4-migrate-library-sets) check-out feature. It can be optionally set if using the [static roles](#3-migrate-roles) feature without providing a distinguished name ([dn](/vault/api-docs/secret/ldap#dn)). | +| [ttl](/vault/api-docs/secret/ad#ttl) | N/A | Replaced by static role [rotation_period](/vault/api-docs/secret/ldap#rotation_period). | +| [max_ttl](/vault/api-docs/secret/ad#max_ttl) | N/A | Not supported for [static roles](#3-migrate-roles). Can be set using [max_ttl](/vault/api-docs/secret/ldap#max_ttl-1) for library sets. | +| [last_rotation_tolerance](/vault/api-docs/secret/ad#last_rotation_tolerance) | N/A | Not supported by the LDAP secrets engine. Passwords will be rotated based on the static role [rotation_period](/vault/api-docs/secret/ldap#rotation_period). | + +### 3. Migrate Roles + +AD secrets engine [roles](/vault/api-docs/secret/ad#role-management) will need to be migrated +to LDAP secrets engine [static roles](/vault/api-docs/secret/ldap#static-roles). The API paths, +parameters, and rotation periods will need to be considered during the migration. + +#### API Path + +| AD Secrets Engine | LDAP Secrets Engine | +| ----------------- | ------------------- | +| [/ad/roles/:role_name](/vault/api-docs/secret/ad#role-management) | [/ldap/static-role/:role_name](/vault/api-docs/secret/ldap#static-roles) | + +#### Parameters + +The following parameters must be migrated. + +| AD Secrets Engine | LDAP Secrets Engine | Details | +| ----------------- | ------------------- | ------- | +| [ttl](/vault/api-docs/secret/ad#ttl-1) | [rotation_period](/vault/api-docs/secret/ldap#rotation_period) | N/A | +| [service_account_name](/vault/api-docs/secret/ad#service_account_name) | [username](/vault/api-docs/secret/ldap#username) | If `username` is set without setting the [dn](/vault/api-docs/secret/ldap#dn) value, then the configuration [userdn](/vault/api-docs/secret/ldap#userdn) must also be set. | + +#### Rotation Periods + +Rotations that occur from AD secrets engine [roles](/vault/api-docs/secret/ad#role-management) +may conflict with rotations performed by LDAP secrets engine [static roles](/vault/api-docs/secret/ldap#static-roles) +during the migration process. This could cause applications consuming passwords to read a +password that gets invalidated by a rotation shortly after. To mitigate this, it's recommended +to set an initial [rotation_period](/vault/api-docs/secret/ldap#rotation_period) that provides +a large enough window to complete [application migrations](#5-migrate-applications) to minimize +the chance of this happening. Additionally, tuning the AD secrets engine [last_rotation_tolerance](/vault/api-docs/secret/ad#last_rotation_tolerance) +parameter could help mitigate applications reading stale passwords, since the parameter allows +rotation of the password if it's been rotated out-of-band within a given duration. + +### 4. Migrate Library Sets + +AD secrets engine [library sets](/vault/api-docs/secret/ad#library-management) will need to +be migrated to LDAP secrets engine [library sets](/vault/api-docs/secret/ldap#library-set-management). +The API paths and parameters will need to be considered during the migration. + +#### API Path + +| AD Secrets Engine | LDAP Secrets Engine | +| ----------------- | ------------------- | +| [/ad/library/:set_name](/vault/api-docs/secret/ad#library-management) | [/ldap/library/:set_name](/vault/api-docs/secret/ldap#library-set-management) | + +#### Parameters + +The parameters from existing AD secrets engine library sets can be exactly mapped 1-to-1 +to LDAP secrets engine library sets. There are no exceptions to consider. + +### 5. Migrate Applications + +The AD secrets engine provides APIs to obtain credentials for AD users and service accounts. +Applications, or Vault clients, are typically the consumer of these credentials. For applications +to successfully migrate, they must begin using new API paths and response formats provided +by the LDAP secrets engine. Additionally, they must obtain a Vault [token](/vault/docs/concepts/tokens) +with an ACL [policy](/vault/docs/concepts/policies) that authorizes access to the new APIs. +The following section details credential-providing APIs and how their response formats differ +between the AD secrets engine and LDAP secrets engine. + +#### API Paths + +| AD Secrets Engine | LDAP Secrets Engine | Details | +| ----------------- | ------------------- | ------- | +| [/ad/creds/:role_name](/vault/api-docs/secret/ad#retrieving-passwords) | [/ldap/static-cred/:role_name](/vault/api-docs/secret/ldap#static-role-passwords) | Response formats differ. Namely, `current_password` is now `password`. See [AD response](/vault/api-docs/secret/ad#sample-get-response) and [LDAP response](/vault/api-docs/secret/ldap#sample-get-response-1) for the difference. | +| [/ad/library/:set_name/check-out](/vault/api-docs/secret/ad#check-a-credential-out) | [/ldap/library/:set_name/check-out](/vault/api-docs/secret/ldap#check-out-management) | Response formats do not differ. | +| [/ad/library/:set_name/check-in](/vault/api-docs/secret/ad#check-a-credential-in) | [/ldap/library/:set_name/check-in](/vault/api-docs/secret/ldap#check-in-management) | Response formats do not differ. | + +### 6. Disable AD Secrets Engines + +AD secrets engine mounts can be disabled after successful migration of configuration and +applications to the LDAP secrets engine. Note that disabling the secrets engine will erase +its configuration from storage. This cannot be reversed. + +To disable the AD secrets engine: + +```shell-session +$ vault secrets disable ad +``` + +To disable at a custom path: + +```shell-session +$ vault secrets disable +``` diff --git a/website/content/docs/upgrading/upgrade-to-1.13.x.mdx b/website/content/docs/upgrading/upgrade-to-1.13.x.mdx index d0fd7fe22091..885e76c50838 100644 --- a/website/content/docs/upgrading/upgrade-to-1.13.x.mdx +++ b/website/content/docs/upgrading/upgrade-to-1.13.x.mdx @@ -18,6 +18,14 @@ for Vault 1.13.x compared to 1.12. Please read it carefully. @include 'consul-dataplane-upgrade-note.mdx' +### Active Directory Secrets Engine Deprecation + +The Active Directory (AD) secrets engine has been deprecated as of the Vault 1.13 release. +We will continue to support the AD secrets engine in maintenance mode for six major Vault +releases. Maintenance mode means that we will fix bugs and security issues but will not add +new features. For additional information, see the [deprecation table](/vault/docs/deprecation) +and [migration guide](/vault/docs/secrets/ad/migration-guide). + ### AliCloud Auth Role Parameter The AliCloud auth plugin will now require the `role` parameter on login. This diff --git a/website/content/partials/ad-secrets-deprecation.mdx b/website/content/partials/ad-secrets-deprecation.mdx new file mode 100644 index 000000000000..85732c38350e --- /dev/null +++ b/website/content/partials/ad-secrets-deprecation.mdx @@ -0,0 +1,5 @@ +~> **Note**: The Active Directory (AD) secrets engine has been deprecated as of the Vault +1.13 release. We will continue to support the AD secrets engine in maintenance mode for +six major Vault releases. Maintenance mode means that we will fix bugs and security issues +but will not add new features. For additional information, see the [deprecation table](/vault/docs/deprecation) +and [migration guide](/vault/docs/secrets/ad/migration-guide). diff --git a/website/data/docs-nav-data.json b/website/data/docs-nav-data.json index 1e4b4939f271..8b0f7f506e8f 100644 --- a/website/data/docs-nav-data.json +++ b/website/data/docs-nav-data.json @@ -993,7 +993,16 @@ }, { "title": "Active Directory", - "path": "secrets/ad" + "routes": [ + { + "title": "Overview", + "path": "secrets/ad" + }, + { + "title": "Migration Guide", + "path": "secrets/ad/migration-guide" + } + ] }, { "title": "AliCloud", From 75f1ea2985d575532092f31db385a70c5a2c12c5 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Tue, 28 Feb 2023 14:55:09 -0500 Subject: [PATCH 057/231] backport of commit eb70bfdc5bfb0dd4c47326e1933b94bd93602c56 (#19407) Co-authored-by: Jordan Reimer --- ui/app/components/auth-form.js | 3 +- ui/app/utils/identity-manager.js | 9 +++--- ui/package.json | 3 +- .../integration/components/auth-form-test.js | 32 +++++++++++++++++++ ui/tests/unit/serializers/cluster-test.js | 14 ++++++++ ui/yarn.lock | 5 +++ 6 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 ui/tests/unit/serializers/cluster-test.js diff --git a/ui/app/components/auth-form.js b/ui/app/components/auth-form.js index 48df029f7929..3eb77e9ea171 100644 --- a/ui/app/components/auth-form.js +++ b/ui/app/components/auth-form.js @@ -8,6 +8,7 @@ import { computed } from '@ember/object'; import { supportedAuthBackends } from 'vault/helpers/supported-auth-backends'; import { task, timeout } from 'ember-concurrency'; import { waitFor } from '@ember/test-waiters'; +import { v4 as uuidv4 } from 'uuid'; const BACKENDS = supportedAuthBackends(); @@ -307,7 +308,7 @@ export default Component.extend(DEFAULTS, { } // add nonce field for okta backend if (backend.type === 'okta') { - data.nonce = crypto.randomUUID(); + data.nonce = uuidv4(); // add a default path of okta if it doesn't exist to be used for Okta Number Challenge if (!data.path) { data.path = 'okta'; diff --git a/ui/app/utils/identity-manager.js b/ui/app/utils/identity-manager.js index 756fd1cae742..4a11c019cdfa 100644 --- a/ui/app/utils/identity-manager.js +++ b/ui/app/utils/identity-manager.js @@ -1,3 +1,5 @@ +import { v4 as uuidv4 } from 'uuid'; + // manage a set of unique ids export default class { constructor() { @@ -12,11 +14,10 @@ export default class { * @public */ fetch() { - let uuid = crypto.randomUUID(); - // odds are incredibly low that we'll run into a duplicate using crypto.randomUUID() - // but just to be safe... + let uuid = uuidv4(); + // odds are incredibly low that we'll run into a duplicate but just to be safe... while (this.ids.has(uuid)) { - uuid = crypto.randomUUID(); + uuid = uuidv4(); } this.ids.add(uuid); return uuid; diff --git a/ui/package.json b/ui/package.json index a1bd52758e13..314c0bdf3917 100644 --- a/ui/package.json +++ b/ui/package.json @@ -256,6 +256,7 @@ "highlight.js": "^10.4.1", "js-yaml": "^3.13.1", "lodash": "^4.17.13", - "node-notifier": "^8.0.1" + "node-notifier": "^8.0.1", + "uuid": "^9.0.0" } } diff --git a/ui/tests/integration/components/auth-form-test.js b/ui/tests/integration/components/auth-form-test.js index 9d2084931ea2..d09283138b45 100644 --- a/ui/tests/integration/components/auth-form-test.js +++ b/ui/tests/integration/components/auth-form-test.js @@ -10,6 +10,7 @@ import sinon from 'sinon'; import Pretender from 'pretender'; import { create } from 'ember-cli-page-object'; import authForm from '../../pages/components/auth-form'; +import { validate } from 'uuid'; const component = create(authForm); @@ -314,4 +315,35 @@ module('Integration | Component | auth form', function (hooks) { server.shutdown(); }); + + test('it should set nonce value as uuid for okta method type', async function (assert) { + assert.expect(1); + + const server = new Pretender(function () { + this.post('/v1/auth/okta/login/foo', (req) => { + const { nonce } = JSON.parse(req.requestBody); + assert.true(validate(nonce), 'Nonce value passed as uuid for okta login'); + return [ + 200, + { 'content-type': 'application/json' }, + JSON.stringify({ + auth: { + client_token: '12345', + }, + }), + ]; + }); + this.get('/v1/sys/internal/ui/mounts', this.passthrough); + }); + + this.set('cluster', EmberObject.create({})); + await render(hbs``); + + await component.selectMethod('okta'); + await component.username('foo'); + await component.password('bar'); + await component.login(); + + server.shutdown(); + }); }); diff --git a/ui/tests/unit/serializers/cluster-test.js b/ui/tests/unit/serializers/cluster-test.js new file mode 100644 index 000000000000..daff99c39b9d --- /dev/null +++ b/ui/tests/unit/serializers/cluster-test.js @@ -0,0 +1,14 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; +import { validate } from 'uuid'; + +module('Unit | Serializer | cluster', function (hooks) { + setupTest(hooks); + + test('it should generate ids for replication attributes', async function (assert) { + const serializer = this.owner.lookup('serializer:cluster'); + const data = {}; + serializer.setReplicationId(data); + assert.true(validate(data.id), 'UUID is generated for replication attribute'); + }); +}); diff --git a/ui/yarn.lock b/ui/yarn.lock index e5a6b8a2f516..19776c2cae8a 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -18579,6 +18579,11 @@ uuid@^8.3.0, uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + v8-compile-cache@^2.0.3, v8-compile-cache@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" From 0a42f2aea58f05a0af9852dd111c28a36004c37f Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Tue, 28 Feb 2023 16:07:42 -0500 Subject: [PATCH 058/231] backport of commit 9bb8321a5bf5b26beae865eb6290bd17aabc159f (#19409) Co-authored-by: Max Winslow <43095669+maxiscoding28@users.noreply.github.com> --- website/content/api-docs/secret/identity/lookup.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/content/api-docs/secret/identity/lookup.mdx b/website/content/api-docs/secret/identity/lookup.mdx index 87d88191d506..1a45f3b5491f 100644 --- a/website/content/api-docs/secret/identity/lookup.mdx +++ b/website/content/api-docs/secret/identity/lookup.mdx @@ -8,7 +8,7 @@ description: |- ## Lookup an Entity -This endpoint queries the entity based on the given criteria. The criteria can +This endpoint looks up an entity based on the given criteria. The criteria can be `name`, `id`, `alias_id`, or a combination of `alias_name` and `alias_mount_accessor`. @@ -70,7 +70,7 @@ $ curl \ ## Lookup a Group -This endpoint queries the group based on the given criteria. The criteria can +This endpoint looks up a group based on the given criteria. The criteria can be `name`, `id`, `alias_id`, or a combination of `alias_name` and `alias_mount_accessor`. From a4cf0dc4437de35fce4860857b64569d092a9b5a Mon Sep 17 00:00:00 2001 From: Nick Cabatoff Date: Wed, 1 Mar 2023 09:58:13 -0500 Subject: [PATCH 059/231] Remove rc1 prerelease tag. (#19417) --- version/version_base.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version/version_base.go b/version/version_base.go index 6353fc5a0d0b..ed69ce18b6c3 100644 --- a/version/version_base.go +++ b/version/version_base.go @@ -12,6 +12,6 @@ var ( CgoEnabled bool Version = "1.13.0" - VersionPrerelease = "rc1" + VersionPrerelease = "" VersionMetadata = "" ) From 3167443d78747349f1492eb4d26836d6277141dc Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Wed, 1 Mar 2023 15:22:31 -0500 Subject: [PATCH 060/231] backport of commit 0d52c0ed4f1dd29bf48d252d5a4732bc3ee98d87 (#19420) Co-authored-by: Mark Sailes <45629314+msailes@users.noreply.github.com> --- website/content/docs/platform/aws/lambda-extension.mdx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/website/content/docs/platform/aws/lambda-extension.mdx b/website/content/docs/platform/aws/lambda-extension.mdx index 6ae1a6f4011d..61299421dce7 100644 --- a/website/content/docs/platform/aws/lambda-extension.mdx +++ b/website/content/docs/platform/aws/lambda-extension.mdx @@ -268,10 +268,6 @@ synchronously refresh its own token before proxying requests if the token is expired (including a grace window), and it will attempt to renew its token if the token is nearly expired but renewable. -~> **Note**: The Vault Lambda Extension is currently incompatible with -[AWS SnapStart](https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html). -This is because AWS SnapStart does not support the Lambda Extensions API. - ## Performance impact AWS Lambda pricing is based on [number of invocations, time of execution and memory From 27c8b3c1b68ac7e4d6b14e297e6cadab66c6a242 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Thu, 2 Mar 2023 09:51:31 -0500 Subject: [PATCH 061/231] backport of commit 94406d19179b7e5ba615fd4f10de06b9b620cfb5 (#19427) Co-authored-by: Malte S. Stretz --- website/content/docs/configuration/listener/tcp.mdx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/website/content/docs/configuration/listener/tcp.mdx b/website/content/docs/configuration/listener/tcp.mdx index 9627219673d8..db2639c35764 100644 --- a/website/content/docs/configuration/listener/tcp.mdx +++ b/website/content/docs/configuration/listener/tcp.mdx @@ -133,6 +133,13 @@ default value in the `"/sys/config/ui"` [API endpoint](/vault/api-docs/system/co `tls_min_version` and `tls_max_version` parameters) are widely considered insecure. +- `tls_max_version` `(string: "tls13")` – Specifies the maximum supported + version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13". + +~> **Warning**: TLS 1.1 and lower (`tls10` and `tls11` values for the + `tls_min_version` and `tls_max_version` parameters) are widely considered + insecure. + - `tls_cipher_suites` `(string: "")` – Specifies the list of supported ciphersuites as a comma-separated-list. The list of all available ciphersuites is available in the [Golang TLS documentation][golang-tls]. From 78c25fd66874a7208f57f418e410d5edb14c3361 Mon Sep 17 00:00:00 2001 From: Hamid Ghaf <83242695+hghaf099@users.noreply.github.com> Date: Thu, 2 Mar 2023 13:21:19 -0800 Subject: [PATCH 062/231] update version 1.13.1 (#19440) --- version/version_base.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version/version_base.go b/version/version_base.go index ed69ce18b6c3..740db690b4bc 100644 --- a/version/version_base.go +++ b/version/version_base.go @@ -11,7 +11,7 @@ var ( // Whether cgo is enabled or not; set at build time CgoEnabled bool - Version = "1.13.0" + Version = "1.13.1" VersionPrerelease = "" VersionMetadata = "" ) From 9f545bdc453814b58e149ed82b086cab3050c102 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Thu, 2 Mar 2023 17:46:07 -0500 Subject: [PATCH 063/231] backport of commit 7ef729708b29112db885d0f77ef55c2d7409a1f4 (#19423) Co-authored-by: Kianna <30884335+kiannaquach@users.noreply.github.com> --- ui/tests/unit/adapters/clients-activity-test.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ui/tests/unit/adapters/clients-activity-test.js b/ui/tests/unit/adapters/clients-activity-test.js index 64da61d29318..1d67df047894 100644 --- a/ui/tests/unit/adapters/clients-activity-test.js +++ b/ui/tests/unit/adapters/clients-activity-test.js @@ -9,10 +9,11 @@ module('Unit | Adapter | clients activity', function (hooks) { setupMirage(hooks); hooks.beforeEach(function () { + const date = new Date(); this.store = this.owner.lookup('service:store'); this.modelName = 'clients/activity'; - this.startDate = subMonths(new Date(), 6); - this.endDate = new Date(); + this.startDate = subMonths(date, 6); + this.endDate = date; this.readableUnix = (unix) => parseAPITimestamp(fromUnixTime(unix).toISOString(), 'MMMM dd yyyy'); }); @@ -63,9 +64,9 @@ module('Unit | Adapter | clients activity', function (hooks) { test('it formats end_time only if only start_time is a timestamp string', async function (assert) { assert.expect(2); - const twoMothsAgo = subMonths(this.endDate, 2); - const month = twoMothsAgo.getMonth() - 2; - const year = twoMothsAgo.getFullYear(); + const twoMonthsAgo = subMonths(this.endDate, 2); + const month = twoMonthsAgo.getMonth(); + const year = twoMonthsAgo.getFullYear(); const dayOfMonth = format(lastDayOfMonth(new Date(year, month, 10)), 'dd'); const queryParams = { start_time: { @@ -95,9 +96,9 @@ module('Unit | Adapter | clients activity', function (hooks) { assert.expect(2); const startDate = subMonths(this.startDate, 2); const endDate = addMonths(this.endDate, 2); - const startMonth = startDate.getMonth() + 2; + const startMonth = startDate.getMonth(); const startYear = startDate.getFullYear(); - const endMonth = endDate.getMonth() - 2; + const endMonth = endDate.getMonth(); const endYear = endDate.getFullYear(); const endDay = format(lastDayOfMonth(new Date(endYear, endMonth, 10)), 'dd'); const queryParams = { From 90d3dc641f7fef040c9dee777550bd12bbc2ef65 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Thu, 2 Mar 2023 18:15:37 -0500 Subject: [PATCH 064/231] backport of commit 16e9c146a69d84dc8d9cf7e072bc69c0d132d6d8 (#19442) Co-authored-by: Jordan Reimer --- changelog/19428.txt | 3 +++ ui/app/initializers/ember-data-identifiers.js | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 changelog/19428.txt create mode 100644 ui/app/initializers/ember-data-identifiers.js diff --git a/changelog/19428.txt b/changelog/19428.txt new file mode 100644 index 000000000000..c1ae6d54bbcb --- /dev/null +++ b/changelog/19428.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fixes crypto.randomUUID error in unsecure contexts from third party ember-data library +``` \ No newline at end of file diff --git a/ui/app/initializers/ember-data-identifiers.js b/ui/app/initializers/ember-data-identifiers.js new file mode 100644 index 000000000000..c22c2af7fe63 --- /dev/null +++ b/ui/app/initializers/ember-data-identifiers.js @@ -0,0 +1,26 @@ +import { setIdentifierGenerationMethod } from '@ember-data/store'; +import { dasherize } from '@ember/string'; +import { v4 as uuidv4 } from 'uuid'; + +export function initialize() { + // see this GH issue for more information https://github.com/emberjs/data/issues/8106 + // Ember Data uses uuidv4 library to generate ids which relies on the crypto API which is no available in unsecure contexts + // the suggested polyfill was added in 4.6.2 so until we upgrade we need to define our own id generation method + // https://api.emberjs.com/ember-data/4.5/classes/IdentifierCache/methods/getOrCreateRecordIdentifier?anchor=getOrCreateRecordIdentifier + // the uuid library was brought in to replace other usages of crypto in the app so it is safe to use in unsecure contexts + // adapted from defaultGenerationMethod -- https://github.com/emberjs/data/blob/v4.5.0/packages/store/addon/-private/identifier-cache.ts#LL82-L94C2 + setIdentifierGenerationMethod((data) => { + if (data.lid) { + return data.lid; + } + if (data.id) { + return `@lid:${dasherize(data.type)}-${data.id}`; + } + return uuidv4(); + }); +} + +export default { + name: 'ember-data-identifiers', + initialize, +}; From b317bbf0db796ca0d1df299f63f0944cac2166a3 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Fri, 3 Mar 2023 11:41:51 -0500 Subject: [PATCH 065/231] backport of commit c5d99edfeccf7d51a74eea140d88cf71b0c97d5d (#19453) Co-authored-by: Max Winslow <43095669+maxiscoding28@users.noreply.github.com> --- website/content/api-docs/secret/identity/entity-alias.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/content/api-docs/secret/identity/entity-alias.mdx b/website/content/api-docs/secret/identity/entity-alias.mdx index f8ee39a6bd75..7c4eeb4eae5e 100644 --- a/website/content/api-docs/secret/identity/entity-alias.mdx +++ b/website/content/api-docs/secret/identity/entity-alias.mdx @@ -135,14 +135,14 @@ This endpoint is used to update an existing entity alias. - `id` `(string: )` – Identifier of the entity alias. -- `name` `(string: )` - Name of the alias. Name should be the identifier +- `name` `(string: "")` - Name of the alias. Name should be the identifier of the client in the authentication source. For example, if the alias belongs to userpass backend, the name should be a valid username within userpass backend. If alias belongs to GitHub, it should be the GitHub username. -- `canonical_id` `(string: )` - Entity ID to which this alias belongs to. +- `canonical_id` `(string: "")` - Entity ID to which this alias belongs to. -- `mount_accessor` `(string: )` - Accessor of the mount to which the +- `mount_accessor` `(string: "")` - Accessor of the mount to which the alias should belong to. - `custom_metadata` `(map: )` - A map of arbitrary string to string valued From fa204b5ac93a6f4013356b63472418c1dd340fa5 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Fri, 3 Mar 2023 13:37:54 -0500 Subject: [PATCH 066/231] backport of commit 3e4262f57167444a41ddb0e2325c9e0d5a4d3700 (#19455) Co-authored-by: prabhat-hashi <111032280+prabhat-hashi@users.noreply.github.com> --- website/content/docs/secrets/ldap.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/content/docs/secrets/ldap.mdx b/website/content/docs/secrets/ldap.mdx index 86f5b072120d..f9d6a5e44f92 100644 --- a/website/content/docs/secrets/ldap.mdx +++ b/website/content/docs/secrets/ldap.mdx @@ -225,6 +225,8 @@ password and enable the account. Windows NT systems and has a limit of 20 characters. Keep this in mind when defining your `username_template`. See [here](https://docs.microsoft.com/en-us/windows/win32/adschema/a-samaccountname) for additional details. +Since the default `username_template` is longer than 20 characters which follows the template of `v_{{.DisplayName}}_{{.RoleName}}_{{random 10}}_{{unix_time}}`, we recommend customising the `username_template` on the role configuration to generate accounts with names less than 20 characters. Please refer to the [username templating document](/vault/docs/concepts/username-templating) for more information. + With regard to adding dynamic users to groups, AD doesn't let you directly modify a user's `memberOf` attribute. The `member` attribute of a group and `memberOf` attribute of a user are [linked attributes](https://docs.microsoft.com/en-us/windows/win32/ad/linked-attributes). Linked attributes are From 174c5d469a7e8fea88e1c2318f6a52d0d4535a39 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Fri, 3 Mar 2023 18:04:20 -0500 Subject: [PATCH 067/231] backport of commit fbe09168e9abdc9b81f5d9d5690c5199561bbaf0 (#19457) Co-authored-by: Yoko Hyakuna --- website/content/docs/release-notes/index.mdx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/website/content/docs/release-notes/index.mdx b/website/content/docs/release-notes/index.mdx index d140285d63b0..2d35e21e8ce9 100644 --- a/website/content/docs/release-notes/index.mdx +++ b/website/content/docs/release-notes/index.mdx @@ -7,3 +7,11 @@ description: Release notes for new Vault versions. # Release Notes Release notes describe major updates in new versions of Vault. + +Choose a version from the navigation sidebar to view the release notes for each +of the major software packages in the Vault product line. + +Please refer to the +[Changelog](https://github.com/hashicorp/vault/blob/main/CHANGELOG.md) for +further information on product improvements, including a comprehensive list of +bug fixes. \ No newline at end of file From 97528fed232720be60205adc223dff932671b0af Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Mon, 6 Mar 2023 13:56:58 -0500 Subject: [PATCH 068/231] backport of commit 401b338a6c1c8f8e5f089a4393f711e008ba3c5d (#19466) Co-authored-by: Phil Renaud --- website/content/docs/secrets/identity/oidc-provider.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/website/content/docs/secrets/identity/oidc-provider.mdx b/website/content/docs/secrets/identity/oidc-provider.mdx index a150df532da7..3ad049eb5e9c 100644 --- a/website/content/docs/secrets/identity/oidc-provider.mdx +++ b/website/content/docs/secrets/identity/oidc-provider.mdx @@ -150,6 +150,7 @@ for details on configuring OIDC authentication for other HashiCorp products: - [Boundary](/boundary/tutorials/access-management/oidc-auth) - [Consul](/consul/docs/security/acl/auth-methods/oidc) - [Waypoint](/waypoint/docs/server/auth/oidc) +- [Nomad](/nomad/tutorials/single-sign-on/sso-oidc-vault) Otherwise, refer to the documentation of the specific OIDC relying party for usage details. From 03cf4dc73144cfbe1d1c74dfedbab0dbd4ead7db Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Wed, 8 Mar 2023 13:04:41 -0500 Subject: [PATCH 069/231] backport of commit 75efaf09fbe17405e84428de91507f36249bf786 (#19484) Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com> --- changelog/19448.txt | 3 ++ ui/app/components/configure-ssh-secret.js | 22 ++++++---- .../settings/configure-secret-backend.js | 4 +- .../components/configure-ssh-secret.hbs | 18 ++++----- .../settings/configure-secret-backend.hbs | 7 +++- .../configure-ssh-secret-test.js | 40 +++++++++++++++++++ 6 files changed, 76 insertions(+), 18 deletions(-) create mode 100644 changelog/19448.txt create mode 100644 ui/tests/acceptance/settings/configure-secret-backends/configure-ssh-secret-test.js diff --git a/changelog/19448.txt b/changelog/19448.txt new file mode 100644 index 000000000000..8c75b79f140c --- /dev/null +++ b/changelog/19448.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: fixes SSH engine config deletion +``` diff --git a/ui/app/components/configure-ssh-secret.js b/ui/app/components/configure-ssh-secret.js index 41832e10ee06..961bbe4267e8 100644 --- a/ui/app/components/configure-ssh-secret.js +++ b/ui/app/components/configure-ssh-secret.js @@ -6,20 +6,28 @@ import { action } from '@ember/object'; * * @example * ```js - * + * * ``` * * @param {string} model - ssh secret engine model * @param {Function} saveConfig - parent action which updates the configuration - * + * @param {boolean} loading - property in parent that updates depending on status of parent's action + * */ export default class ConfigureSshSecretComponent extends Component { @action - saveConfig(data, event) { + delete() { + this.args.saveConfig({ delete: true }); + } + + @action + saveConfig(event) { event.preventDefault(); - this.args.saveConfig(data); + this.args.saveConfig({ delete: false }); } } diff --git a/ui/app/controllers/vault/cluster/settings/configure-secret-backend.js b/ui/app/controllers/vault/cluster/settings/configure-secret-backend.js index 402eb873b26b..69829cffdb05 100644 --- a/ui/app/controllers/vault/cluster/settings/configure-secret-backend.js +++ b/ui/app/controllers/vault/cluster/settings/configure-secret-backend.js @@ -31,7 +31,6 @@ export default Controller.extend(CONFIG_ATTRS, { this.model .saveCA({ isDelete }) .then(() => { - this.set('loading', false); this.send('refreshRoute'); this.set('configured', !isDelete); if (isDelete) { @@ -43,6 +42,9 @@ export default Controller.extend(CONFIG_ATTRS, { .catch((error) => { const errorMessage = error.errors ? error.errors.join('. ') : error; this.flashMessages.danger(errorMessage); + }) + .finally(() => { + this.set('loading', false); }); } }, diff --git a/ui/app/templates/components/configure-ssh-secret.hbs b/ui/app/templates/components/configure-ssh-secret.hbs index ae0823b082d5..94136fe9bc85 100644 --- a/ui/app/templates/components/configure-ssh-secret.hbs +++ b/ui/app/templates/components/configure-ssh-secret.hbs @@ -31,14 +31,14 @@ Delete
    {{else}} -
    +
    @@ -58,13 +58,13 @@
    -