diff --git a/go/control/api/api.go b/go/control/api/api.go index 1a7f5f6897a..1923cb3fd39 100644 --- a/go/control/api/api.go +++ b/go/control/api/api.go @@ -19,6 +19,7 @@ import ( upgrade "github.com/oasisprotocol/oasis-core/go/upgrade/api" commonWorker "github.com/oasisprotocol/oasis-core/go/worker/common/api" executorWorker "github.com/oasisprotocol/oasis-core/go/worker/compute/executor/api" + keymanagerWorker "github.com/oasisprotocol/oasis-core/go/worker/keymanager/api" storageWorker "github.com/oasisprotocol/oasis-core/go/worker/storage/api" ) @@ -74,6 +75,9 @@ type Status struct { // Registration is the node's registration status. Registration RegistrationStatus `json:"registration"` + // Keymanager is the node's key manager worker status in case this node is a key manager node. + Keymanager *keymanagerWorker.Status `json:"keymanager,omitempty"` + // PendingUpgrades are the node's pending upgrades. PendingUpgrades []*upgrade.PendingUpgrade `json:"pending_upgrades"` } @@ -170,6 +174,9 @@ type ControlledNode interface { // GetRuntimeStatus returns the node's current per-runtime status. GetRuntimeStatus(ctx context.Context) (map[common.Namespace]RuntimeStatus, error) + // GetKeyManagerStatus returns the node's key manager worker status. + GetKeymanagerStatus(ctx context.Context) (*keymanagerWorker.Status, error) + // GetPendingUpgrade returns the node's pending upgrades. GetPendingUpgrades(ctx context.Context) ([]*upgrade.PendingUpgrade, error) } diff --git a/go/control/control.go b/go/control/control.go index 762ae1a737e..1420097be4f 100644 --- a/go/control/control.go +++ b/go/control/control.go @@ -98,6 +98,11 @@ func (c *nodeController) GetStatus(ctx context.Context) (*control.Status, error) return nil, fmt.Errorf("failed to get runtime status: %w", err) } + kms, err := c.node.GetKeymanagerStatus(ctx) + if err != nil { + return nil, fmt.Errorf("failed to get key manager worker status: %w", err) + } + pendingUpgrades, err := c.node.GetPendingUpgrades(ctx) if err != nil { return nil, fmt.Errorf("failed to get pending upgrades: %w", err) @@ -124,6 +129,7 @@ func (c *nodeController) GetStatus(ctx context.Context) (*control.Status, error) }, Consensus: *cs, Runtimes: runtimes, + Keymanager: kms, Registration: *rs, PendingUpgrades: pendingUpgrades, }, nil diff --git a/go/oasis-node/cmd/node/control.go b/go/oasis-node/cmd/node/control.go index 10e80e4d94b..c044930bfe1 100644 --- a/go/oasis-node/cmd/node/control.go +++ b/go/oasis-node/cmd/node/control.go @@ -11,6 +11,7 @@ import ( roothash "github.com/oasisprotocol/oasis-core/go/roothash/api" storage "github.com/oasisprotocol/oasis-core/go/storage/api" upgrade "github.com/oasisprotocol/oasis-core/go/upgrade/api" + keymanagerWorker "github.com/oasisprotocol/oasis-core/go/worker/keymanager/api" "github.com/oasisprotocol/oasis-core/go/worker/registration" ) @@ -183,6 +184,14 @@ func (n *Node) GetRuntimeStatus(ctx context.Context) (map[common.Namespace]contr return runtimes, nil } +// GetKeymanagerStatus implements control.ControlledNode. +func (n *Node) GetKeymanagerStatus(ctx context.Context) (*keymanagerWorker.Status, error) { + if n.KeymanagerWorker == nil || !n.KeymanagerWorker.Enabled() { + return nil, nil + } + return n.KeymanagerWorker.GetStatus(ctx) +} + // GetPendingUpgrades implements control.ControlledNode. func (n *Node) GetPendingUpgrades(ctx context.Context) ([]*upgrade.PendingUpgrade, error) { return n.Upgrader.PendingUpgrades(ctx) diff --git a/go/worker/keymanager/api/api.go b/go/worker/keymanager/api/api.go new file mode 100644 index 00000000000..e68f1a7b75f --- /dev/null +++ b/go/worker/keymanager/api/api.go @@ -0,0 +1,100 @@ +package api + +import ( + "fmt" + + core "github.com/libp2p/go-libp2p-core" + + "github.com/oasisprotocol/oasis-core/go/common" +) + +// StatusState is the concise status state of the key manager worker. +type StatusState uint8 + +const ( + // StatusStateReady is the ready status state. + StatusStateReady StatusState = iota + // StatusStateStarting is the starting status state. + StatusStateStarting + // StatusStateStopped is the stopped status state. + StatusStateStopped + // StatusStateDisabled is the disabled status state. + StatusStateDisabled +) + +// String returns a string representation of a status state. +func (s StatusState) String() string { + switch s { + case StatusStateReady: + return "ready" + case StatusStateStarting: + return "starting" + case StatusStateStopped: + return "stopped" + case StatusStateDisabled: + return "disabled" + default: + return "[invalid status state]" + } +} + +// MarshalText encodes a StatusState into text form. +func (s StatusState) MarshalText() ([]byte, error) { + switch s { + case StatusStateReady: + return []byte(StatusStateReady.String()), nil + case StatusStateStarting: + return []byte(StatusStateStarting.String()), nil + case StatusStateStopped: + return []byte(StatusStateStopped.String()), nil + case StatusStateDisabled: + return []byte(StatusStateDisabled.String()), nil + default: + return nil, fmt.Errorf("invalid StatusState: %d", s) + } +} + +// UnmarshalText decodes a text slice into a StatusState. +func (s *StatusState) UnmarshalText(text []byte) error { + switch string(text) { + case StatusStateReady.String(): + *s = StatusStateReady + case StatusStateStarting.String(): + *s = StatusStateStarting + case StatusStateStopped.String(): + *s = StatusStateStopped + case StatusStateDisabled.String(): + *s = StatusStateDisabled + default: + return fmt.Errorf("invalid StatusState: %s", string(text)) + } + return nil +} + +// RuntimeAccessList is an access control lists for a runtime. +type RuntimeAccessList struct { + // RuntimeID is the runtime ID of the runtime this access list is for. + RuntimeID common.Namespace `json:"runtime_id"` + + // Peers is a list of peers that are allowed to call protected methods. + Peers []core.PeerID `json:"peers"` +} + +// Status is the key manager worker status. +type Status struct { + // Status is a concise status of the key manager worker. + Status StatusState `json:"status"` + + // MayGenerate returns whether the enclave can generate a master secret. + MayGenerate bool `json:"may_generate"` + + // RuntimeID is the runtime ID of the key manager. + RuntimeID *common.Namespace `json:"runtime_id"` + // ClientRuntimes is a list of compute runtimes that use this key manager. + ClientRuntimes []common.Namespace `json:"client_runtimes"` + + // AccessList is per-runtime list of peers that are allowed to call protected methods. + AccessList []RuntimeAccessList `json:"access_list"` + // PrivatePeers is a list of peers that are always allowed to call protected methods. + PrivatePeers []core.PeerID `json:"private_peers"` +} diff --git a/go/worker/keymanager/status.go b/go/worker/keymanager/status.go new file mode 100644 index 00000000000..b0552466edf --- /dev/null +++ b/go/worker/keymanager/status.go @@ -0,0 +1,73 @@ +package keymanager + +import ( + "context" + + "github.com/libp2p/go-libp2p-core/peer" + + "github.com/oasisprotocol/oasis-core/go/common" + "github.com/oasisprotocol/oasis-core/go/worker/keymanager/api" +) + +// GetStatus returns the key manager worker status. +func (w *Worker) GetStatus(ctx context.Context) (*api.Status, error) { + var initialized, stopped bool + select { + case <-w.Initialized(): + initialized = true + default: + } + select { + case <-w.Quit(): + stopped = true + default: + } + + var ss api.StatusState + switch { + case !w.enabled: + ss = api.StatusStateDisabled + case stopped: + ss = api.StatusStateStopped + case initialized: + ss = api.StatusStateReady + default: + ss = api.StatusStateStarting + } + + var rid *common.Namespace + if w.runtime != nil { + id := w.runtime.ID() + rid = &id + } + + rts := make([]common.Namespace, 0, len(w.clientRuntimes)) + for rt := range w.clientRuntimes { + rts = append(rts, rt) + } + + ps := make([]peer.ID, 0, len(w.privatePeers)) + for p := range w.privatePeers { + ps = append(ps, p) + } + + w.RLock() + al := make([]api.RuntimeAccessList, 0, len(w.accessListByRuntime)) + for rt, ps := range w.accessListByRuntime { + ral := api.RuntimeAccessList{ + RuntimeID: rt, + Peers: ps, + } + al = append(al, ral) + } + w.RUnlock() + + return &api.Status{ + Status: ss, + MayGenerate: w.mayGenerate, + RuntimeID: rid, + ClientRuntimes: rts, + AccessList: al, + PrivatePeers: ps, + }, nil +}