| package rpc |
| |
| import ( |
| "context" |
| "fmt" |
| |
| "google.golang.org/grpc" |
| "google.golang.org/grpc/codes" |
| "google.golang.org/grpc/status" |
| |
| epb "source.monogon.dev/metropolis/proto/ext" |
| ) |
| |
| type Permissions map[epb.Permission]bool |
| |
| // PeerInfo represents the Metropolis-level information about the remote side |
| // of a gRPC RPC, ie. about the calling client in server handlers and about the |
| // handling server in client code. |
| // |
| // Exactly one of {Node, User, Unauthenticated} will be non-nil. |
| type PeerInfo struct { |
| // Node is the information about a peer Node, and identifies that the other side |
| // of the connection is either a Node servicng gRPC requests for a cluster, or a |
| // Node connecting to a gRPC service. |
| Node *PeerInfoNode |
| // User is the information about a peer User, and identifies that the other side |
| // of the connection is a Metropolis user or manager (eg. owner). This will only |
| // be set in service handlers, as users cannot serve gRPC connections. |
| User *PeerInfoUser |
| // Unauthenticated is set for incoming gRPC connections which that have the |
| // Unauthenticated authorization extension set to true, and mark that the other |
| // side of the connection has not been verified at all. |
| Unauthenticated *PeerInfoUnauthenticated |
| } |
| |
| // PeerInfoNode contains information about a Node on the other side of a gRPC |
| // connection. |
| type PeerInfoNode struct { |
| // PublicKey is the ED25519 public key bytes of the node. |
| PublicKey []byte |
| |
| // Permissions are the set of permissions this node has. |
| Permissions Permissions |
| } |
| |
| // PeerInfoUser contains information about a user on the other side of a gRPC |
| // connection. |
| type PeerInfoUser struct { |
| // Identity is an opaque identifier for the user. MVP: Currently this is always |
| // "manager". |
| Identity string |
| } |
| |
| type PeerInfoUnauthenticated struct { |
| // SelfSignedPublicKey is the ED25519 public key bytes of the other side of the |
| // connection, if that side presented a self-signed certificate to prove control |
| // of a private key corresponding to this public key. If it did not present a |
| // self-signed certificate that can be parsed for such a key, this will be nil. |
| // |
| // This can be used by code with expects Unauthenticated RPCs but wants to |
| // authenticate the connection based on ownership of some keypair, for example |
| // in the AAA.Escrow method. |
| SelfSignedPublicKey []byte |
| } |
| |
| // GetPeerInfo returns the PeerInfo of the peer of a gRPC connection, or nil if |
| // this connection does not carry any PeerInfo. |
| func GetPeerInfo(ctx context.Context) *PeerInfo { |
| if pi, ok := ctx.Value(peerInfoKey).(*PeerInfo); ok { |
| return pi |
| } |
| return nil |
| } |
| |
| func (p *PeerInfo) CheckPermissions(need Permissions) error { |
| if p.Unauthenticated != nil { |
| // This generally shouldn't happen, as unauthenticated users shouldn't be |
| // allowed to reach this part of the code - methods with Need != nil will not be |
| // processed as unauthenticated for security, and will instead act as |
| // authenticated methods and reject unauthenticated connections. |
| for _, v := range need { |
| if v { |
| return status.Error(codes.Unauthenticated, "unauthenticated connection") |
| } |
| } |
| return nil |
| } else if p.User != nil { |
| // MVP: all permissions are granted to all users. |
| // TODO(q3k): check authz.Need once we have a user/identity system implemented. |
| return nil |
| } else if p.Node != nil { |
| for n, v := range need { |
| if v && !p.Node.Permissions[n] { |
| return status.Errorf(codes.PermissionDenied, "node missing %s permission", n.String()) |
| } |
| } |
| return nil |
| } |
| |
| return fmt.Errorf("invalid PeerInfo: neither Unauthenticated, User nor Node is set") |
| } |
| |
| type peerInfoKeyType string |
| |
| // peerInfoKey is the context key for storing PeerInfo. |
| const peerInfoKey = peerInfoKeyType("peerInfo") |
| |
| // apply returns the given context with itself stored under a unique key, that |
| // can be later retrieved via GetPeerInfo. |
| func (p *PeerInfo) apply(ctx context.Context) context.Context { |
| return context.WithValue(ctx, peerInfoKey, p) |
| } |
| |
| // peerInfoServerStream is a grpc.ServerStream wrapper which contains some |
| // PeerInfo, and returns it as part of the Context() of the ServerStream. |
| type peerInfoServerStream struct { |
| grpc.ServerStream |
| pi *PeerInfo |
| } |
| |
| func (p *peerInfoServerStream) Context() context.Context { |
| return p.pi.apply(p.ServerStream.Context()) |
| } |
| |
| // serverStream wraps a grpc.ServerStream with a structure that attaches this |
| // PeerInfo in all contexts returned by Context(). |
| func (p *PeerInfo) serverStream(ss grpc.ServerStream) grpc.ServerStream { |
| return &peerInfoServerStream{ss, p} |
| } |