From c800db149f8c66c31f5737f4c466b321af00729b Mon Sep 17 00:00:00 2001 From: Steven Rhodes Date: Sat, 30 Sep 2023 16:35:21 -0700 Subject: [PATCH] checkpoint again --- auth/opa/rpcauth/hooks.go | 16 ++ auth/opa/rpcauth/input.go | 2 - cmd/proxy-server/main.go | 1 + cmd/sansshell-server/default-policy.rego | 4 + cmd/sansshell-server/main.go | 1 + proxy/proxy/proxy.go | 37 ++- services/mpa/client/client.go | 42 +++- services/mpa/mpa.pb.go | 300 +++++++++++++++-------- services/mpa/mpa.proto | 25 +- services/mpa/mpahooks/interceptors.go | 64 ++--- services/mpa/server/server.go | 47 +++- 11 files changed, 378 insertions(+), 161 deletions(-) diff --git a/auth/opa/rpcauth/hooks.go b/auth/opa/rpcauth/hooks.go index b27a9051..6db9947c 100644 --- a/auth/opa/rpcauth/hooks.go +++ b/auth/opa/rpcauth/hooks.go @@ -115,3 +115,19 @@ func JustificationHook(justificationFunc func(string) error) RPCAuthzHook { return nil }) } + +// PeerPrincipalFromCertHook returns an RPCAuthzHook that sets principal +// information based on the peer's certificate, using the common name as +// the id and the organizational units as the groups. +func PeerPrincipalFromCertHook() RPCAuthzHook { + return RPCAuthzHookFunc(func(_ context.Context, input *RPCAuthInput) error { + if input.Peer == nil || input.Peer.Cert == nil { + return nil + } + input.Host.Principal = &PrincipalAuthInput{ + ID: input.Peer.Cert.Subject.CommonName, + Groups: input.Peer.Cert.Subject.OrganizationalUnit, + } + return nil + }) +} diff --git a/auth/opa/rpcauth/input.go b/auth/opa/rpcauth/input.go index 5dfe8d81..ef061c98 100644 --- a/auth/opa/rpcauth/input.go +++ b/auth/opa/rpcauth/input.go @@ -180,8 +180,6 @@ func PeerInputFromContext(ctx context.Context) *PeerAuthInput { } out.Net = NetInputFromAddr(p.Addr) out.Cert = CertInputFrom(p.AuthInfo) - // DO NOT SUBMIT - out.Principal = &PrincipalAuthInput{ID: out.Cert.Subject.CommonName} return out } diff --git a/cmd/proxy-server/main.go b/cmd/proxy-server/main.go index 29760ccf..b4f81b3f 100644 --- a/cmd/proxy-server/main.go +++ b/cmd/proxy-server/main.go @@ -140,6 +140,7 @@ func main() { server.WithCredSource(*credSource), server.WithHostPort(*hostport), server.WithJustification(*justification), + server.WithAuthzHook(rpcauth.PeerPrincipalFromCertHook()), server.WithRawServerOption(func(s *grpc.Server) { reflection.Register(s) }), server.WithRawServerOption(func(s *grpc.Server) { channelz.RegisterChannelzServiceToServer(s) }), server.WithRawServerOption(srv.Register), diff --git a/cmd/sansshell-server/default-policy.rego b/cmd/sansshell-server/default-policy.rego index 01bc16eb..953b3636 100644 --- a/cmd/sansshell-server/default-policy.rego +++ b/cmd/sansshell-server/default-policy.rego @@ -35,6 +35,10 @@ allow { input.method = "/Mpa.Mpa/Approve" } +allow { + input.method = "/Mpa.Mpa/List" +} + allow { input.type = "LocalFile.ReadActionRequest" input.message.file.filename = "/etc/hosts" diff --git a/cmd/sansshell-server/main.go b/cmd/sansshell-server/main.go index 1410e896..19dfbb40 100644 --- a/cmd/sansshell-server/main.go +++ b/cmd/sansshell-server/main.go @@ -171,6 +171,7 @@ func main() { server.WithHostPort(*hostport), server.WithParsedPolicy(parsed), server.WithJustification(*justification), + server.WithAuthzHook(rpcauth.PeerPrincipalFromCertHook()), server.WithRawServerOption(func(s *grpc.Server) { reflection.Register(s) }), server.WithRawServerOption(func(s *grpc.Server) { channelz.RegisterChannelzServiceToServer(s) }), server.WithDebugPort(*debugport), diff --git a/proxy/proxy/proxy.go b/proxy/proxy/proxy.go index 9e1f4880..d7c84d36 100644 --- a/proxy/proxy/proxy.go +++ b/proxy/proxy/proxy.go @@ -41,6 +41,16 @@ import ( proxypb "github.com/Snowflake-Labs/sansshell/proxy" ) +// Interceptor intercepts the execution of an RPC through the proxy. Interceptors +// can be added to a Conn by modifying its Interceptors field. When an interceptor +// is set on a ClientConn, all proxy RPC invocations are delegated to the interceptor, +// and it is the responsibility of the interceptor to call invoker to complete the +// processing of the RPC. +type Interceptor func(ctx context.Context, conn *Conn, method string, args any, invoker Invoker, opts ...grpc.CallOption) (<-chan *Ret, error) + +// Invoker is called by Interceptor to complete RPCs. +type Invoker func(ctx context.Context, method string, args any, opts ...grpc.CallOption) (<-chan *Ret, error) + // Conn is a grpc.ClientConnInterface which is connected to the proxy // converting calls into RPC the proxy understands. type Conn struct { @@ -55,6 +65,11 @@ type Conn struct { // If this is true we're not proxy but instead direct connect. direct bool + + // Interceptors allow intercepting Invoke and InvokeOneMany calls + // that go through a proxy. + // It is unsafe to modify Intercepters while calls are in progress. + Interceptors []Interceptor } // Ret defines the internal API for getting responses from the proxy. @@ -91,7 +106,7 @@ type proxyStream struct { } // Invoke - see grpc.ClientConnInterface -func (p *Conn) Invoke(ctx context.Context, method string, args interface{}, reply interface{}, opts ...grpc.CallOption) error { +func (p *Conn) Invoke(ctx context.Context, method string, args any, reply any, opts ...grpc.CallOption) error { if p.Direct() { // TODO(jchacon): Add V1 style logging indicating pass through in use. return p.cc.Invoke(ctx, method, args, reply, opts...) @@ -247,7 +262,7 @@ func (p *proxyStream) closeClients() error { } // see grpc.ClientStream -func (p *proxyStream) SendMsg(args interface{}) error { +func (p *proxyStream) SendMsg(args any) error { if p.sendClosed { return status.Error(codes.FailedPrecondition, "sending on a closed connection") } @@ -262,13 +277,13 @@ func (p *proxyStream) SendMsg(args interface{}) error { } // see grpc.ClientStream -func (p *proxyStream) RecvMsg(m interface{}) error { +func (p *proxyStream) RecvMsg(m any) error { // Up front check for nothing left since we closed all streams. if len(p.ids) == 0 { return io.EOF } - // Since the API is an interface{} we can change what this normally + // Since the API is an any we can change what this normally // expects from a proto.Message to a *[]*Ret instead. // // Anything else is an error if we have > 1 target. In the one target @@ -527,7 +542,19 @@ func (p *Conn) createStreams(ctx context.Context, method string) (proxypb.Proxy_ // NOTE: The returned channel must be read until it closes in order to avoid leaking goroutines. // // TODO(jchacon): Should add the ability to specify remote dial timeout in the connection to the proxy. -func (p *Conn) InvokeOneMany(ctx context.Context, method string, args interface{}, opts ...grpc.CallOption) (<-chan *Ret, error) { +func (p *Conn) InvokeOneMany(ctx context.Context, method string, args any, opts ...grpc.CallOption) (<-chan *Ret, error) { + invoke := p.invokeOneMany + for _, intercept := range p.Interceptors { + intercept := intercept + inner := invoke + invoke = func(ctx context.Context, method string, args any, opts ...grpc.CallOption) (<-chan *Ret, error) { + return intercept(ctx, p, method, args, inner, opts...) + } + } + return invoke(ctx, method, args, opts...) +} + +func (p *Conn) invokeOneMany(ctx context.Context, method string, args any, opts ...grpc.CallOption) (<-chan *Ret, error) { requestMsg, ok := args.(proto.Message) if !ok { return nil, status.Error(codes.InvalidArgument, "args must be a proto.Message") diff --git a/services/mpa/client/client.go b/services/mpa/client/client.go index cdf32ca4..ccde2bd6 100644 --- a/services/mpa/client/client.go +++ b/services/mpa/client/client.go @@ -5,6 +5,7 @@ import ( "flag" "fmt" "os" + "strings" "github.com/Snowflake-Labs/sansshell/client" pb "github.com/Snowflake-Labs/sansshell/services/mpa" @@ -136,7 +137,9 @@ func (p *approveCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...inter return subcommands.ExitSuccess } -type listCmd struct{} +type listCmd struct { + verbose bool +} func (*listCmd) Name() string { return "list" } func (*listCmd) Synopsis() string { return "Lists out pending MPA requests on machines" } @@ -146,12 +149,14 @@ func (*listCmd) Usage() string { ` } -func (p *listCmd) SetFlags(f *flag.FlagSet) {} +func (p *listCmd) SetFlags(f *flag.FlagSet) { + f.BoolVar(&p.verbose, "v", false, "Verbose: list full details of MPA request") +} func (p *listCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { state := args[0].(*util.ExecuteState) - if f.NArg() != 1 { - fmt.Fprintln(os.Stderr, "Please specify an ID to approve.") + if f.NArg() != 0 { + fmt.Fprintln(os.Stderr, "List takes no args.") return subcommands.ExitUsageError } @@ -167,11 +172,33 @@ func (p *listCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interfac } for r := range resp { if r.Error != nil { - fmt.Fprintf(state.Err[r.Index], "Failed to wait for approval: %v\n", r.Error) + fmt.Fprintln(state.Err[r.Index], r.Error) continue } - for _, id := range r.Resp.Id { - fmt.Fprintln(state.Out[r.Index], id) + for _, item := range r.Resp.Item { + msg := []string{item.Id} + if p.verbose { + if len(item.Approver) > 0 { + var approvers []string + for _, a := range item.Approver { + approvers = append(approvers, a.Id) + } + msg = append(msg, fmt.Sprintf("(approved by %v)", strings.Join(approvers, ","))) + } + msg = append(msg, protojson.MarshalOptions{UseProtoNames: true}.Format(item.Action)) + } else { + msg = append(msg, item.Action.GetMethod()) + if item.Action.GetUser() != "" { + msg = append(msg, "from", item.Action.GetUser()) + } + if item.Action.GetJustification() != "" { + msg = append(msg, "for", item.Action.GetJustification()) + } + if len(item.Approver) > 0 { + msg = append(msg, "(approved)") + } + } + fmt.Fprintln(state.Out[r.Index], strings.Join(msg, " ")) } } return subcommands.ExitSuccess @@ -257,7 +284,6 @@ func (p *getCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface fmt.Fprintf(state.Err[r.Index], "Error: action was nil when looking up MPA request") continue } - // DO NOT SUBMIT: Pretty print message fmt.Fprintln(state.Out[r.Index], protojson.MarshalOptions{UseProtoNames: true, Multiline: true}.Format(r.Resp)) } return subcommands.ExitSuccess diff --git a/services/mpa/mpa.pb.go b/services/mpa/mpa.pb.go index eb4aedba..ffa971c7 100644 --- a/services/mpa/mpa.pb.go +++ b/services/mpa/mpa.pb.go @@ -24,6 +24,7 @@ package mpa import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" ) @@ -46,8 +47,8 @@ type Action struct { Justification string `protobuf:"bytes,2,opt,name=justification,proto3" json:"justification,omitempty"` // The GRPC method name, as '/Package.Service/Method' Method string `protobuf:"bytes,3,opt,name=method,proto3" json:"method,omitempty"` - // The request protocol buffer, serialized as JSON. - Message []byte `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty"` + // The request protocol buffer. + Message *anypb.Any `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty"` } func (x *Action) Reset() { @@ -103,7 +104,7 @@ func (x *Action) GetMethod() string { return "" } -func (x *Action) GetMessage() []byte { +func (x *Action) GetMessage() *anypb.Any { if x != nil { return x.Message } @@ -174,8 +175,8 @@ type StoreRequest struct { // The GRPC method name, as '/Package.Service/Method' Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` - // The request protocol buffer, serialized as JSON. - Message []byte `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + // The request protocol buffer. + Message *anypb.Any `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` } func (x *StoreRequest) Reset() { @@ -217,7 +218,7 @@ func (x *StoreRequest) GetMethod() string { return "" } -func (x *StoreRequest) GetMessage() []byte { +func (x *StoreRequest) GetMessage() *anypb.Any { if x != nil { return x.Message } @@ -295,6 +296,8 @@ type ApproveRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // Approve takes an action instead of an ID to improve auditability + // and allow richer authorization logic. Action *Action `protobuf:"bytes,1,opt,name=action,proto3" json:"action,omitempty"` } @@ -503,7 +506,7 @@ type ListResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id []string `protobuf:"bytes,1,rep,name=id,proto3" json:"id,omitempty"` + Item []*ListResponse_Item `protobuf:"bytes,1,rep,name=item,proto3" json:"item,omitempty"` } func (x *ListResponse) Reset() { @@ -538,9 +541,9 @@ func (*ListResponse) Descriptor() ([]byte, []int) { return file_mpa_proto_rawDescGZIP(), []int{9} } -func (x *ListResponse) GetId() []string { +func (x *ListResponse) GetItem() []*ListResponse_Item { if x != nil { - return x.Id + return x.Item } return nil } @@ -733,81 +736,157 @@ func (*ClearResponse) Descriptor() ([]byte, []int) { return file_mpa_proto_rawDescGZIP(), []int{13} } +type ListResponse_Item struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Action *Action `protobuf:"bytes,1,opt,name=action,proto3" json:"action,omitempty"` + Approver []*Principal `protobuf:"bytes,2,rep,name=approver,proto3" json:"approver,omitempty"` + Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *ListResponse_Item) Reset() { + *x = ListResponse_Item{} + if protoimpl.UnsafeEnabled { + mi := &file_mpa_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListResponse_Item) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListResponse_Item) ProtoMessage() {} + +func (x *ListResponse_Item) ProtoReflect() protoreflect.Message { + mi := &file_mpa_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListResponse_Item.ProtoReflect.Descriptor instead. +func (*ListResponse_Item) Descriptor() ([]byte, []int) { + return file_mpa_proto_rawDescGZIP(), []int{9, 0} +} + +func (x *ListResponse_Item) GetAction() *Action { + if x != nil { + return x.Action + } + return nil +} + +func (x *ListResponse_Item) GetApprover() []*Principal { + if x != nil { + return x.Approver + } + return nil +} + +func (x *ListResponse_Item) GetId() string { + if x != nil { + return x.Id + } + return "" +} + var File_mpa_proto protoreflect.FileDescriptor var file_mpa_proto_rawDesc = []byte{ 0x0a, 0x09, 0x6d, 0x70, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x4d, 0x70, 0x61, - 0x22, 0x74, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x24, - 0x0a, 0x0d, 0x6a, 0x75, 0x73, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6a, 0x75, 0x73, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x18, 0x0a, 0x07, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x33, 0x0a, 0x09, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, - 0x70, 0x61, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x22, 0x40, 0x0a, 0x0c, 0x53, - 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, - 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, - 0x68, 0x6f, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x70, 0x0a, - 0x0d, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, - 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x23, - 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, - 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x08, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x50, 0x72, 0x69, 0x6e, - 0x63, 0x69, 0x70, 0x61, 0x6c, 0x52, 0x08, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x22, - 0x35, 0x0a, 0x0e, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x23, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x0b, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x11, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x0a, 0x16, 0x57, 0x61, 0x69, - 0x74, 0x46, 0x6f, 0x72, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x22, 0x19, 0x0a, 0x17, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x41, 0x70, - 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0d, - 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x1e, 0x0a, - 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x1c, 0x0a, - 0x0a, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x5e, 0x0a, 0x0b, 0x47, - 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x06, 0x61, 0x63, + 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8a, 0x01, 0x0a, 0x06, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x6a, 0x75, + 0x73, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x6a, 0x75, 0x73, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x2e, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x33, 0x0a, 0x09, 0x50, 0x72, 0x69, 0x6e, + 0x63, 0x69, 0x70, 0x61, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x22, 0x56, 0x0a, + 0x0c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, + 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x2e, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x70, 0x0a, 0x0d, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x23, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x08, 0x61, + 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, + 0x4d, 0x70, 0x61, 0x2e, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x52, 0x08, 0x61, + 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x22, 0x35, 0x0a, 0x0e, 0x41, 0x70, 0x70, 0x72, 0x6f, + 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x06, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x4d, 0x70, 0x61, 0x2e, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x11, + 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x28, 0x0a, 0x16, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x41, 0x70, 0x70, 0x72, + 0x6f, 0x76, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x19, 0x0a, 0x17, 0x57, + 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0d, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa3, 0x01, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, + 0x65, 0x6d, 0x1a, 0x67, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x23, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x08, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, - 0x6c, 0x52, 0x08, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x22, 0x33, 0x0a, 0x0c, 0x43, - 0x6c, 0x65, 0x61, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x06, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x4d, 0x70, - 0x61, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x22, 0x0f, 0x0a, 0x0d, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x32, 0xcc, 0x02, 0x0a, 0x03, 0x4d, 0x70, 0x61, 0x12, 0x30, 0x0a, 0x05, 0x53, 0x74, 0x6f, - 0x72, 0x65, 0x12, 0x11, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x53, 0x74, 0x6f, 0x72, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x07, 0x41, - 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x12, 0x13, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x41, 0x70, 0x70, - 0x72, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x4d, 0x70, - 0x61, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0f, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x41, 0x70, - 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x12, 0x1b, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x57, 0x61, 0x69, - 0x74, 0x46, 0x6f, 0x72, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, - 0x72, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x10, 0x2e, 0x4d, 0x70, - 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, - 0x4d, 0x70, 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x2a, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x0f, 0x2e, 0x4d, 0x70, 0x61, 0x2e, - 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x4d, 0x70, 0x61, - 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x30, - 0x0a, 0x05, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x12, 0x11, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x43, 0x6c, - 0x65, 0x61, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x4d, 0x70, 0x61, - 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x53, - 0x6e, 0x6f, 0x77, 0x66, 0x6c, 0x61, 0x6b, 0x65, 0x2d, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x73, 0x61, - 0x6e, 0x73, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x2f, 0x6d, 0x70, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x6c, 0x52, 0x08, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x1c, 0x0a, 0x0a, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x5e, 0x0a, 0x0b, 0x47, 0x65, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, + 0x08, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x0e, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x52, + 0x08, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x22, 0x33, 0x0a, 0x0c, 0x43, 0x6c, 0x65, + 0x61, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x06, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x4d, 0x70, 0x61, 0x2e, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x0f, + 0x0a, 0x0d, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, + 0xcc, 0x02, 0x0a, 0x03, 0x4d, 0x70, 0x61, 0x12, 0x30, 0x0a, 0x05, 0x53, 0x74, 0x6f, 0x72, 0x65, + 0x12, 0x11, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x07, 0x41, 0x70, 0x70, + 0x72, 0x6f, 0x76, 0x65, 0x12, 0x13, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x6f, + 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x4d, 0x70, 0x61, 0x2e, + 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x4e, 0x0a, 0x0f, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x41, 0x70, 0x70, 0x72, + 0x6f, 0x76, 0x61, 0x6c, 0x12, 0x1b, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x46, + 0x6f, 0x72, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1c, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x41, + 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x2d, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x10, 0x2e, 0x4d, 0x70, 0x61, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x4d, 0x70, + 0x61, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x2a, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x0f, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x47, 0x65, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x30, 0x0a, 0x05, + 0x43, 0x6c, 0x65, 0x61, 0x72, 0x12, 0x11, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x43, 0x6c, 0x65, 0x61, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x4d, 0x70, 0x61, 0x2e, 0x43, + 0x6c, 0x65, 0x61, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x29, + 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x53, 0x6e, 0x6f, + 0x77, 0x66, 0x6c, 0x61, 0x6b, 0x65, 0x2d, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x73, 0x61, 0x6e, 0x73, + 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x2f, 0x6d, 0x70, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -822,7 +901,7 @@ func file_mpa_proto_rawDescGZIP() []byte { return file_mpa_proto_rawDescData } -var file_mpa_proto_msgTypes = make([]protoimpl.MessageInfo, 14) +var file_mpa_proto_msgTypes = make([]protoimpl.MessageInfo, 15) var file_mpa_proto_goTypes = []interface{}{ (*Action)(nil), // 0: Mpa.Action (*Principal)(nil), // 1: Mpa.Principal @@ -838,31 +917,38 @@ var file_mpa_proto_goTypes = []interface{}{ (*GetResponse)(nil), // 11: Mpa.GetResponse (*ClearRequest)(nil), // 12: Mpa.ClearRequest (*ClearResponse)(nil), // 13: Mpa.ClearResponse + (*ListResponse_Item)(nil), // 14: Mpa.ListResponse.Item + (*anypb.Any)(nil), // 15: google.protobuf.Any } var file_mpa_proto_depIdxs = []int32{ - 0, // 0: Mpa.StoreResponse.action:type_name -> Mpa.Action - 1, // 1: Mpa.StoreResponse.approver:type_name -> Mpa.Principal - 0, // 2: Mpa.ApproveRequest.action:type_name -> Mpa.Action - 0, // 3: Mpa.GetResponse.action:type_name -> Mpa.Action - 1, // 4: Mpa.GetResponse.approver:type_name -> Mpa.Principal - 0, // 5: Mpa.ClearRequest.action:type_name -> Mpa.Action - 2, // 6: Mpa.Mpa.Store:input_type -> Mpa.StoreRequest - 4, // 7: Mpa.Mpa.Approve:input_type -> Mpa.ApproveRequest - 6, // 8: Mpa.Mpa.WaitForApproval:input_type -> Mpa.WaitForApprovalRequest - 8, // 9: Mpa.Mpa.List:input_type -> Mpa.ListRequest - 10, // 10: Mpa.Mpa.Get:input_type -> Mpa.GetRequest - 12, // 11: Mpa.Mpa.Clear:input_type -> Mpa.ClearRequest - 3, // 12: Mpa.Mpa.Store:output_type -> Mpa.StoreResponse - 5, // 13: Mpa.Mpa.Approve:output_type -> Mpa.ApproveResponse - 7, // 14: Mpa.Mpa.WaitForApproval:output_type -> Mpa.WaitForApprovalResponse - 9, // 15: Mpa.Mpa.List:output_type -> Mpa.ListResponse - 11, // 16: Mpa.Mpa.Get:output_type -> Mpa.GetResponse - 13, // 17: Mpa.Mpa.Clear:output_type -> Mpa.ClearResponse - 12, // [12:18] is the sub-list for method output_type - 6, // [6:12] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 15, // 0: Mpa.Action.message:type_name -> google.protobuf.Any + 15, // 1: Mpa.StoreRequest.message:type_name -> google.protobuf.Any + 0, // 2: Mpa.StoreResponse.action:type_name -> Mpa.Action + 1, // 3: Mpa.StoreResponse.approver:type_name -> Mpa.Principal + 0, // 4: Mpa.ApproveRequest.action:type_name -> Mpa.Action + 14, // 5: Mpa.ListResponse.item:type_name -> Mpa.ListResponse.Item + 0, // 6: Mpa.GetResponse.action:type_name -> Mpa.Action + 1, // 7: Mpa.GetResponse.approver:type_name -> Mpa.Principal + 0, // 8: Mpa.ClearRequest.action:type_name -> Mpa.Action + 0, // 9: Mpa.ListResponse.Item.action:type_name -> Mpa.Action + 1, // 10: Mpa.ListResponse.Item.approver:type_name -> Mpa.Principal + 2, // 11: Mpa.Mpa.Store:input_type -> Mpa.StoreRequest + 4, // 12: Mpa.Mpa.Approve:input_type -> Mpa.ApproveRequest + 6, // 13: Mpa.Mpa.WaitForApproval:input_type -> Mpa.WaitForApprovalRequest + 8, // 14: Mpa.Mpa.List:input_type -> Mpa.ListRequest + 10, // 15: Mpa.Mpa.Get:input_type -> Mpa.GetRequest + 12, // 16: Mpa.Mpa.Clear:input_type -> Mpa.ClearRequest + 3, // 17: Mpa.Mpa.Store:output_type -> Mpa.StoreResponse + 5, // 18: Mpa.Mpa.Approve:output_type -> Mpa.ApproveResponse + 7, // 19: Mpa.Mpa.WaitForApproval:output_type -> Mpa.WaitForApprovalResponse + 9, // 20: Mpa.Mpa.List:output_type -> Mpa.ListResponse + 11, // 21: Mpa.Mpa.Get:output_type -> Mpa.GetResponse + 13, // 22: Mpa.Mpa.Clear:output_type -> Mpa.ClearResponse + 17, // [17:23] is the sub-list for method output_type + 11, // [11:17] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name } func init() { file_mpa_proto_init() } @@ -1039,6 +1125,18 @@ func file_mpa_proto_init() { return nil } } + file_mpa_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListResponse_Item); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -1046,7 +1144,7 @@ func file_mpa_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_mpa_proto_rawDesc, NumEnums: 0, - NumMessages: 14, + NumMessages: 15, NumExtensions: 0, NumServices: 1, }, diff --git a/services/mpa/mpa.proto b/services/mpa/mpa.proto index 48d2412c..510b67f3 100644 --- a/services/mpa/mpa.proto +++ b/services/mpa/mpa.proto @@ -18,6 +18,8 @@ syntax = "proto3"; option go_package = "github.com/Snowflake-Labs/sansshell/mpa"; +import "google/protobuf/any.proto"; + package Mpa; // The Mpa service definition @@ -57,8 +59,8 @@ message Action { string justification = 2; // The GRPC method name, as '/Package.Service/Method' string method = 3; - // The request protocol buffer, serialized as JSON. - bytes message = 4; + // The request protocol buffer. + google.protobuf.Any message = 4; } message Principal { @@ -72,8 +74,8 @@ message Principal { message StoreRequest { // The GRPC method name, as '/Package.Service/Method' string method = 1; - // The request protocol buffer, serialized as JSON. - bytes message = 2; + // The request protocol buffer. + google.protobuf.Any message = 2; } message StoreResponse { @@ -85,14 +87,25 @@ message StoreResponse { repeated Principal approver = 3; } -message ApproveRequest { Action action = 1; } +message ApproveRequest { + // Approve takes an action instead of an ID to improve auditability + // and allow richer authorization logic. + Action action = 1; +} message ApproveResponse {} message WaitForApprovalRequest { string id = 1; } message WaitForApprovalResponse {} message ListRequest {} -message ListResponse { repeated string id = 1; } +message ListResponse { + message Item { + Action action = 1; + repeated Principal approver = 2; + string id = 3; + } + repeated Item item = 1; +} message GetRequest { string id = 1; } diff --git a/services/mpa/mpahooks/interceptors.go b/services/mpa/mpahooks/interceptors.go index 1a278eb5..dde2c7eb 100644 --- a/services/mpa/mpahooks/interceptors.go +++ b/services/mpa/mpahooks/interceptors.go @@ -14,8 +14,8 @@ import ( "github.com/Snowflake-Labs/sansshell/services/util" "google.golang.org/grpc" "google.golang.org/grpc/metadata" - "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" proxypb "github.com/Snowflake-Labs/sansshell/proxy" ) @@ -32,14 +32,16 @@ func MPAUnaryClientIntercepter() grpc.UnaryClientInterceptor { if !ok { return fmt.Errorf("unable to cast req to proto: %v", req) } - bytes, err := protojson.Marshal(p) - if err != nil { - return err + + var msg anypb.Any + if err := msg.MarshalFrom(p); err != nil { + return fmt.Errorf("unable to marshal into anyproto: %v", err) } + mpaClient := mpa.NewMpaClient(cc) result, err := mpaClient.Store(ctx, &mpa.StoreRequest{ Method: method, - Message: bytes, + Message: &msg, }, opts...) if err != nil { return err @@ -58,7 +60,6 @@ func MPAUnaryClientIntercepter() grpc.UnaryClientInterceptor { } ctx = metadata.AppendToOutgoingContext(ctx, rpcauth.ReqMPAKey, result.Id) - // Complete the call return invoker(ctx, method, req, reply, cc, opts...) } @@ -126,43 +127,44 @@ func MPAStreamClientIntercepter() grpc.StreamClientInterceptor { // ProxyClientMPAInterceptor will perform the MPA flow prior to making the desired RPC // calls through the proxy. -func ProxyClientMPAInterceptor(state *util.ExecuteState) any { - return func(ctx context.Context, conn *proxy.Conn, method string, args proto.Message) error { +func ProxyClientMPAInterceptor(state *util.ExecuteState) proxy.Interceptor { + return func(ctx context.Context, conn *proxy.Conn, method string, args any, invoker proxy.Invoker, opts ...grpc.CallOption) (<-chan *proxy.Ret, error) { // Our hook will run for all gRPC calls, including ones used inside the interceptor. // We need to bail early on MPA-related ones to prevent infinite recursion. if method == "/Mpa.Mpa/Store" || method == "/Mpa.Mpa/WaitForApproval" { - return nil + return invoker(ctx, method, args, opts...) } - bytes, err := protojson.Marshal(args) - if err != nil { - return err + p, ok := args.(proto.Message) + if !ok { + return nil, fmt.Errorf("unable to cast args to proto: %v", args) + } + var msg anypb.Any + if err := msg.MarshalFrom(p); err != nil { + return nil, fmt.Errorf("unable to marshal into anyproto: %v", err) } mpaClient := mpa.NewMpaClientProxy(conn) ch, err := mpaClient.StoreOneMany(ctx, &mpa.StoreRequest{ Method: method, - Message: bytes, + Message: &msg, }) if err != nil { - return err + return nil, err } var mpaID string mpaIdToTargets := make(map[string][]string) + var targetsNeedingApproval []string for r := range ch { if r.Error != nil { fmt.Fprintf(state.Err[r.Index], "Unable to request MPA: %v\n", err) } + mpaIdToTargets[r.Resp.Id] = append(mpaIdToTargets[r.Resp.Id], r.Target) if len(r.Resp.Approver) == 0 { // Only print out messages for not-yet-approved requests - mpaIdToTargets[r.Resp.Id] = append(mpaIdToTargets[r.Resp.Id], r.Target) + targetsNeedingApproval = append(targetsNeedingApproval, r.Target) } mpaID = r.Resp.Id } - if len(mpaIdToTargets) == 0 { - // All approvals are in place, no need to wait - return nil - } - if len(mpaIdToTargets) > 1 { var idMsgs []string for id, targets := range mpaIdToTargets { @@ -173,20 +175,24 @@ func ProxyClientMPAInterceptor(state *util.ExecuteState) any { for _, m := range idMsgs { fmt.Fprintln(os.Stderr, m) } - return errors.New("Multiple MPA IDs generated, command needs to be run separately for each id.") + return nil, errors.New("Multiple MPA IDs generated, command needs to be run separately for each id.") } - fmt.Fprintln(os.Stderr, "Multi party auth requested, ask an approver to run:") - fmt.Fprintf(os.Stderr, " sanssh --proxy %v --targets %v mpa approve %v\n", conn.Proxy().Target(), strings.Join(mpaIdToTargets[mpaID], ","), mpaID) + if len(targetsNeedingApproval) > 0 { + fmt.Fprintln(os.Stderr, "Waiting for multi-party approval on all targets, ask an approver to run:") + fmt.Fprintf(os.Stderr, " sanssh --proxy %v --targets %v mpa approve %v\n", conn.Proxy().Target(), targetsNeedingApproval, mpaID) - // We call WaitForApproval on all targets, even ones already approved. This is silly but not harmful. - waitCh, err := mpaClient.WaitForApprovalOneMany(ctx, &mpa.WaitForApprovalRequest{Id: mpaID}) - for r := range waitCh { - if r.Error != nil { - fmt.Fprintf(state.Err[r.Index], "Error when waiting for MPA approval: %v\n", err) + // We call WaitForApproval on all targets, even ones already approved. This is silly but not harmful. + waitCh, err := mpaClient.WaitForApprovalOneMany(ctx, &mpa.WaitForApprovalRequest{Id: mpaID}) + for r := range waitCh { + if r.Error != nil { + fmt.Fprintf(state.Err[r.Index], "Error when waiting for MPA approval: %v\n", err) + } } } - return nil + // Now that we have our approvals, make our call. + ctx = metadata.AppendToOutgoingContext(ctx, rpcauth.ReqMPAKey, mpaID) + return invoker(ctx, method, args, opts...) } } diff --git a/services/mpa/server/server.go b/services/mpa/server/server.go index 6af309a4..5bc46712 100644 --- a/services/mpa/server/server.go +++ b/services/mpa/server/server.go @@ -12,9 +12,14 @@ import ( "github.com/Snowflake-Labs/sansshell/auth/opa/rpcauth" "github.com/Snowflake-Labs/sansshell/services" "github.com/Snowflake-Labs/sansshell/services/mpa" + "github.com/google/go-cmp/cmp" "google.golang.org/grpc" "google.golang.org/grpc/metadata" + "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/anypb" ) // DO NOT SUBMIT: max size, dedup approvers @@ -35,14 +40,30 @@ func ServerMPAAuthzHook() rpcauth.RPCAuthzHook { if j := input.Metadata[rpcauth.ReqJustKey]; len(j) > 0 { justification = j[0] } + + // Transform the rpcauth input into the original proto + mt, err := protoregistry.GlobalTypes.FindMessageByURL(input.MessageType) + if err != nil { + return fmt.Errorf("unable to find proto type: %v", err) + } + m2 := mt.New().Interface() + if err := protojson.Unmarshal([]byte(input.Message), m2); err != nil { + return fmt.Errorf("could not marshal input into %v: %v", input.Message, err) + } + var msg anypb.Any + if err := msg.MarshalFrom(m2); err != nil { + return fmt.Errorf("unable to marshal into anyproto: %v", err) + } + sentAct := &mpa.Action{ User: input.Peer.Principal.ID, Justification: justification, Method: input.Method, - Message: []byte(input.Message), + Message: &msg, } - if !proto.Equal(resp.Action, sentAct) { - return fmt.Errorf("Improper action: want %v, got %v", resp.Action, sentAct) + // Make sure to use an any-proto-aware comparison + if !cmp.Equal(resp.Action, sentAct, protocmp.Transform()) { + return fmt.Errorf("request doesn't match mpa approval: want %v, got %v", resp.Action, sentAct) } for _, a := range resp.Approver { input.Approvers = append(input.Approvers, &rpcauth.PrincipalAuthInput{ @@ -97,7 +118,7 @@ func callerIdentity(ctx context.Context) (*rpcauth.PrincipalAuthInput, bool) { func (s *server) Store(ctx context.Context, in *mpa.StoreRequest) (*mpa.StoreResponse, error) { var justification string if md, found := metadata.FromIncomingContext(ctx); found && len(md[rpcauth.ReqJustKey]) > 0 { - justification = md[rpcauth.ReqMPAKey][0] + justification = md[rpcauth.ReqJustKey][0] } p, ok := callerIdentity(ctx) @@ -169,12 +190,18 @@ func (s *server) WaitForApproval(ctx context.Context, in *mpa.WaitForApprovalReq func (s *server) List(ctx context.Context, in *mpa.ListRequest) (*mpa.ListResponse, error) { s.mu.Lock() defer s.mu.Unlock() - var ids []string - for id := range s.actions { - ids = append(ids, id) - } - sort.Strings(ids) - return &mpa.ListResponse{Id: ids}, nil + var items []*mpa.ListResponse_Item + for id, action := range s.actions { + items = append(items, &mpa.ListResponse_Item{ + Id: id, + Action: action.action, + Approver: action.approvers, + }) + } + sort.Slice(items, func(i, j int) bool { + return items[i].Id < items[j].Id + }) + return &mpa.ListResponse{Item: items}, nil } func (s *server) Get(ctx context.Context, in *mpa.GetRequest) (*mpa.GetResponse, error) { s.mu.Lock()