AB#2046 : Add option to create SSH users for the first coordinator upon initialization (#133)

* Move `file`, `ssh` and `user` packages to internal
* Rename `SSHKey` to `(ssh.)UserKey`
* Rename KeyValue / Publickey to PublicKey
* Rename SSH key file from "debugd" to "ssh-keys"
* Add CreateSSHUsers function to Core
* Call CreateSSHUsers users on first control-plane node, when defined in config

Tests:
* Make StubUserCreator add entries to /etc/passwd
* Add NewLinuxUserManagerFake for unit tests
* Add unit tests & adjust existing ones to changes
This commit is contained in:
Nils Hanke 2022-05-16 17:32:00 +02:00 committed by GitHub
parent 5dc2e71d80
commit 68092f27dd
63 changed files with 879 additions and 554 deletions

View File

@ -1,9 +1,9 @@
package cmd package cmd
import ( import (
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/internal/config" "github.com/edgelesssys/constellation/internal/config"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"

View File

@ -4,9 +4,9 @@ import (
"bytes" "bytes"
"testing" "testing"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/internal/config" "github.com/edgelesssys/constellation/internal/config"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"

View File

@ -8,10 +8,10 @@ import (
"github.com/edgelesssys/constellation/cli/azure" "github.com/edgelesssys/constellation/cli/azure"
"github.com/edgelesssys/constellation/cli/cloud/cloudcmd" "github.com/edgelesssys/constellation/cli/cloud/cloudcmd"
"github.com/edgelesssys/constellation/cli/cloudprovider" "github.com/edgelesssys/constellation/cli/cloudprovider"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/cli/gcp" "github.com/edgelesssys/constellation/cli/gcp"
"github.com/edgelesssys/constellation/internal/config" "github.com/edgelesssys/constellation/internal/config"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )

View File

@ -9,9 +9,9 @@ import (
"github.com/edgelesssys/constellation/cli/azure" "github.com/edgelesssys/constellation/cli/azure"
"github.com/edgelesssys/constellation/cli/cloudprovider" "github.com/edgelesssys/constellation/cli/cloudprovider"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/cli/gcp" "github.com/edgelesssys/constellation/cli/gcp"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/state" "github.com/edgelesssys/constellation/internal/state"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cobra" "github.com/spf13/cobra"

View File

@ -14,16 +14,18 @@ import (
"github.com/edgelesssys/constellation/cli/azure" "github.com/edgelesssys/constellation/cli/azure"
"github.com/edgelesssys/constellation/cli/cloud/cloudcmd" "github.com/edgelesssys/constellation/cli/cloud/cloudcmd"
"github.com/edgelesssys/constellation/cli/cloudprovider" "github.com/edgelesssys/constellation/cli/cloudprovider"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/cli/gcp" "github.com/edgelesssys/constellation/cli/gcp"
"github.com/edgelesssys/constellation/cli/proto" "github.com/edgelesssys/constellation/cli/proto"
"github.com/edgelesssys/constellation/cli/status" "github.com/edgelesssys/constellation/cli/status"
"github.com/edgelesssys/constellation/cli/vpn" "github.com/edgelesssys/constellation/cli/vpn"
"github.com/edgelesssys/constellation/coordinator/atls" "github.com/edgelesssys/constellation/coordinator/atls"
"github.com/edgelesssys/constellation/coordinator/pubapi/pubproto"
coordinatorstate "github.com/edgelesssys/constellation/coordinator/state" coordinatorstate "github.com/edgelesssys/constellation/coordinator/state"
"github.com/edgelesssys/constellation/coordinator/util" "github.com/edgelesssys/constellation/coordinator/util"
"github.com/edgelesssys/constellation/internal/config" "github.com/edgelesssys/constellation/internal/config"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/deploy/ssh"
"github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/state" "github.com/edgelesssys/constellation/internal/state"
"github.com/kr/text" "github.com/kr/text"
wgquick "github.com/nmiculinic/wg-quick-go" wgquick "github.com/nmiculinic/wg-quick-go"
@ -78,6 +80,8 @@ func initialize(ctx context.Context, cmd *cobra.Command, protCl protoClient, ser
return err return err
} }
protoSSHUserKeys := ssh.ToProtoSlice(config.SSHUsers)
var stat state.ConstellationState var stat state.ConstellationState
err = fileHandler.ReadJSON(constants.StateFilename, &stat) err = fileHandler.ReadJSON(constants.StateFilename, &stat)
if errors.Is(err, fs.ErrNotExist) { if errors.Is(err, fs.ErrNotExist) {
@ -129,6 +133,7 @@ func initialize(ctx context.Context, cmd *cobra.Command, protCl protoClient, ser
coordinatorPrivIPs: coordinators.PrivateIPs()[1:], coordinatorPrivIPs: coordinators.PrivateIPs()[1:],
autoscalingNodeGroups: autoscalingNodeGroups, autoscalingNodeGroups: autoscalingNodeGroups,
cloudServiceAccountURI: serviceAccount, cloudServiceAccountURI: serviceAccount,
sshUserKeys: protoSSHUserKeys,
} }
result, err := activate(ctx, cmd, protCl, input, validators.V()) result, err := activate(ctx, cmd, protCl, input, validators.V())
if err != nil { if err != nil {
@ -166,7 +171,7 @@ func activate(ctx context.Context, cmd *cobra.Command, client protoClient, input
return activationResult{}, err return activationResult{}, err
} }
respCl, err := client.Activate(ctx, input.pubKey, input.masterSecret, input.nodePrivIPs, input.coordinatorPrivIPs, input.autoscalingNodeGroups, input.cloudServiceAccountURI) respCl, err := client.Activate(ctx, input.pubKey, input.masterSecret, input.nodePrivIPs, input.coordinatorPrivIPs, input.autoscalingNodeGroups, input.cloudServiceAccountURI, input.sshUserKeys)
if err != nil { if err != nil {
return activationResult{}, err return activationResult{}, err
} }
@ -216,6 +221,7 @@ type activationInput struct {
coordinatorPrivIPs []string coordinatorPrivIPs []string
autoscalingNodeGroups []string autoscalingNodeGroups []string
cloudServiceAccountURI string cloudServiceAccountURI string
sshUserKeys []*pubproto.SSHUserKey
} }
type activationResult struct { type activationResult struct {

View File

@ -12,10 +12,10 @@ import (
"github.com/edgelesssys/constellation/cli/azure" "github.com/edgelesssys/constellation/cli/azure"
"github.com/edgelesssys/constellation/cli/ec2" "github.com/edgelesssys/constellation/cli/ec2"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/cli/gcp" "github.com/edgelesssys/constellation/cli/gcp"
"github.com/edgelesssys/constellation/cli/qemu" "github.com/edgelesssys/constellation/cli/qemu"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/state" "github.com/edgelesssys/constellation/internal/state"
wgquick "github.com/nmiculinic/wg-quick-go" wgquick "github.com/nmiculinic/wg-quick-go"
"github.com/spf13/afero" "github.com/spf13/afero"

View File

@ -5,6 +5,7 @@ import (
"github.com/edgelesssys/constellation/cli/proto" "github.com/edgelesssys/constellation/cli/proto"
"github.com/edgelesssys/constellation/coordinator/atls" "github.com/edgelesssys/constellation/coordinator/atls"
"github.com/edgelesssys/constellation/coordinator/pubapi/pubproto"
"github.com/edgelesssys/constellation/coordinator/state" "github.com/edgelesssys/constellation/coordinator/state"
) )
@ -12,5 +13,5 @@ type protoClient interface {
Connect(endpoint string, validators []atls.Validator) error Connect(endpoint string, validators []atls.Validator) error
Close() error Close() error
GetState(ctx context.Context) (state.State, error) GetState(ctx context.Context) (state.State, error)
Activate(ctx context.Context, userPublicKey, masterSecret []byte, nodeIPs, coordinatorIPs, autoscalingNodeGroups []string, cloudServiceAccountURI string) (proto.ActivationResponseClient, error) Activate(ctx context.Context, userPublicKey, masterSecret []byte, nodeIPs, coordinatorIPs, autoscalingNodeGroups []string, cloudServiceAccountURI string, sshUsers []*pubproto.SSHUserKey) (proto.ActivationResponseClient, error)
} }

View File

@ -8,6 +8,7 @@ import (
"github.com/edgelesssys/constellation/cli/proto" "github.com/edgelesssys/constellation/cli/proto"
"github.com/edgelesssys/constellation/coordinator/atls" "github.com/edgelesssys/constellation/coordinator/atls"
"github.com/edgelesssys/constellation/coordinator/pubapi/pubproto"
"github.com/edgelesssys/constellation/coordinator/state" "github.com/edgelesssys/constellation/coordinator/state"
) )
@ -26,6 +27,7 @@ type stubProtoClient struct {
activateCoordinatorIPs []string activateCoordinatorIPs []string
activateAutoscalingNodeGroups []string activateAutoscalingNodeGroups []string
cloudServiceAccountURI string cloudServiceAccountURI string
sshUserKeys []*pubproto.SSHUserKey
} }
func (c *stubProtoClient) Connect(_ string, _ []atls.Validator) error { func (c *stubProtoClient) Connect(_ string, _ []atls.Validator) error {
@ -42,13 +44,14 @@ func (c *stubProtoClient) GetState(_ context.Context) (state.State, error) {
return c.getStateState, c.getStateErr return c.getStateState, c.getStateErr
} }
func (c *stubProtoClient) Activate(ctx context.Context, userPublicKey, masterSecret []byte, nodeIPs, coordinatorIPs []string, autoscalingNodeGroups []string, cloudServiceAccountURI string) (proto.ActivationResponseClient, error) { func (c *stubProtoClient) Activate(ctx context.Context, userPublicKey, masterSecret []byte, nodeIPs, coordinatorIPs []string, autoscalingNodeGroups []string, cloudServiceAccountURI string, sshUserKeys []*pubproto.SSHUserKey) (proto.ActivationResponseClient, error) {
c.activateUserPublicKey = userPublicKey c.activateUserPublicKey = userPublicKey
c.activateMasterSecret = masterSecret c.activateMasterSecret = masterSecret
c.activateNodeIPs = nodeIPs c.activateNodeIPs = nodeIPs
c.activateCoordinatorIPs = coordinatorIPs c.activateCoordinatorIPs = coordinatorIPs
c.activateAutoscalingNodeGroups = autoscalingNodeGroups c.activateAutoscalingNodeGroups = autoscalingNodeGroups
c.cloudServiceAccountURI = cloudServiceAccountURI c.cloudServiceAccountURI = cloudServiceAccountURI
c.sshUserKeys = sshUserKeys
return c.respClient, c.activateErr return c.respClient, c.activateErr
} }
@ -126,7 +129,7 @@ func (c *fakeProtoClient) GetState(_ context.Context) (state.State, error) {
return state.IsNode, nil return state.IsNode, nil
} }
func (c *fakeProtoClient) Activate(ctx context.Context, userPublicKey, masterSecret []byte, nodeIPs, coordinatorIPs, autoscalingNodeGroups []string, cloudServiceAccountURI string) (proto.ActivationResponseClient, error) { func (c *fakeProtoClient) Activate(ctx context.Context, userPublicKey, masterSecret []byte, nodeIPs, coordinatorIPs, autoscalingNodeGroups []string, cloudServiceAccountURI string, sshUserKeys []*pubproto.SSHUserKey) (proto.ActivationResponseClient, error) {
if !c.conn { if !c.conn {
return nil, errors.New("client is not connected") return nil, errors.New("client is not connected")
} }

View File

@ -9,11 +9,11 @@ import (
"github.com/edgelesssys/constellation/cli/cloud/cloudcmd" "github.com/edgelesssys/constellation/cli/cloud/cloudcmd"
"github.com/edgelesssys/constellation/cli/cloudprovider" "github.com/edgelesssys/constellation/cli/cloudprovider"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/cli/proto" "github.com/edgelesssys/constellation/cli/proto"
"github.com/edgelesssys/constellation/coordinator/util" "github.com/edgelesssys/constellation/coordinator/util"
"github.com/edgelesssys/constellation/internal/config" "github.com/edgelesssys/constellation/internal/config"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/state" "github.com/edgelesssys/constellation/internal/state"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cobra" "github.com/spf13/cobra"

View File

@ -6,8 +6,8 @@ import (
"errors" "errors"
"testing" "testing"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/state" "github.com/edgelesssys/constellation/internal/state"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"

View File

@ -10,8 +10,8 @@ import (
"go.uber.org/multierr" "go.uber.org/multierr"
"github.com/edgelesssys/constellation/cli/cloud/cloudcmd" "github.com/edgelesssys/constellation/cli/cloud/cloudcmd"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/state" "github.com/edgelesssys/constellation/internal/state"
) )

View File

@ -5,8 +5,8 @@ import (
"errors" "errors"
"testing" "testing"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/state" "github.com/edgelesssys/constellation/internal/state"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"

View File

@ -7,10 +7,10 @@ import (
"github.com/edgelesssys/constellation/cli/cloud/cloudcmd" "github.com/edgelesssys/constellation/cli/cloud/cloudcmd"
"github.com/edgelesssys/constellation/cli/cloudprovider" "github.com/edgelesssys/constellation/cli/cloudprovider"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/cli/proto" "github.com/edgelesssys/constellation/cli/proto"
"github.com/edgelesssys/constellation/internal/config" "github.com/edgelesssys/constellation/internal/config"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cobra" "github.com/spf13/cobra"
rpcStatus "google.golang.org/grpc/status" rpcStatus "google.golang.org/grpc/status"

View File

@ -8,7 +8,7 @@ import (
"testing" "testing"
"github.com/edgelesssys/constellation/cli/cloudprovider" "github.com/edgelesssys/constellation/cli/cloudprovider"
"github.com/edgelesssys/constellation/cli/file" "github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"

View File

@ -73,7 +73,7 @@ func (c *Client) GetState(ctx context.Context) (state.State, error) {
// Activate activates the Constellation coordinator via a grpc call. // Activate activates the Constellation coordinator via a grpc call.
// The handed IP addresses must be the private IP addresses of running AWS or GCP instances, // The handed IP addresses must be the private IP addresses of running AWS or GCP instances,
// and the userPublicKey is the VPN key of the users WireGuard interface. // and the userPublicKey is the VPN key of the users WireGuard interface.
func (c *Client) Activate(ctx context.Context, userPublicKey, masterSecret []byte, nodeIPs, coordinatorIPs, autoscalingNodeGroups []string, cloudServiceAccountURI string) (ActivationResponseClient, error) { func (c *Client) Activate(ctx context.Context, userPublicKey, masterSecret []byte, nodeIPs, coordinatorIPs, autoscalingNodeGroups []string, cloudServiceAccountURI string, sshUserKeys []*pubproto.SSHUserKey) (ActivationResponseClient, error) {
if c.pubapi == nil { if c.pubapi == nil {
return nil, errors.New("client is not connected") return nil, errors.New("client is not connected")
} }
@ -100,6 +100,7 @@ func (c *Client) Activate(ctx context.Context, userPublicKey, masterSecret []byt
KeyEncryptionKeyId: "", KeyEncryptionKeyId: "",
UseExistingKek: false, UseExistingKek: false,
CloudServiceAccountUri: cloudServiceAccountURI, CloudServiceAccountUri: cloudServiceAccountURI,
SshUserKeys: sshUserKeys,
} }
client, err := c.pubapi.ActivateAsCoordinator(ctx, req) client, err := c.pubapi.ActivateAsCoordinator(ctx, req)

View File

@ -163,7 +163,7 @@ func TestActivate(t *testing.T) {
if tc.pubAPIClient != nil { if tc.pubAPIClient != nil {
client.pubapi = tc.pubAPIClient client.pubapi = tc.pubAPIClient
} }
_, err := client.Activate(context.Background(), []byte(tc.userPublicKey), []byte("Constellation"), tc.ips, nil, nil, "serviceaccount://test") _, err := client.Activate(context.Background(), []byte(tc.userPublicKey), []byte("Constellation"), tc.ips, nil, nil, "serviceaccount://test", nil)
if tc.wantErr { if tc.wantErr {
assert.Error(err) assert.Error(err)
} else { } else {

View File

@ -9,7 +9,6 @@ import (
"os" "os"
"strings" "strings"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/attestation/azure" "github.com/edgelesssys/constellation/coordinator/attestation/azure"
"github.com/edgelesssys/constellation/coordinator/attestation/gcp" "github.com/edgelesssys/constellation/coordinator/attestation/gcp"
"github.com/edgelesssys/constellation/coordinator/attestation/qemu" "github.com/edgelesssys/constellation/coordinator/attestation/qemu"
@ -27,6 +26,7 @@ import (
"github.com/edgelesssys/constellation/coordinator/util" "github.com/edgelesssys/constellation/coordinator/util"
"github.com/edgelesssys/constellation/coordinator/util/grpcutil" "github.com/edgelesssys/constellation/coordinator/util/grpcutil"
"github.com/edgelesssys/constellation/coordinator/wireguard" "github.com/edgelesssys/constellation/coordinator/wireguard"
"github.com/edgelesssys/constellation/internal/file"
grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
"github.com/spf13/afero" "github.com/spf13/afero"
"go.uber.org/zap" "go.uber.org/zap"
@ -174,5 +174,5 @@ func main() {
netDialer := &net.Dialer{} netDialer := &net.Dialer{}
dialer := grpcutil.NewDialer(validator, netDialer) dialer := grpcutil.NewDialer(validator, netDialer)
run(issuer, wg, openTPM, util.GetIPAddr, dialer, fileHandler, kube, run(issuer, wg, openTPM, util.GetIPAddr, dialer, fileHandler, kube,
metadata, cloudControllerManager, cloudNodeManager, autoscaler, encryptedDisk, etcdEndpoint, enforceEtcdTls, bindIP, bindPort, zapLoggerCore) metadata, cloudControllerManager, cloudNodeManager, autoscaler, encryptedDisk, etcdEndpoint, enforceEtcdTls, bindIP, bindPort, zapLoggerCore, fs)
} }

View File

@ -7,7 +7,6 @@ import (
"net" "net"
"sync" "sync"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/atls" "github.com/edgelesssys/constellation/coordinator/atls"
"github.com/edgelesssys/constellation/coordinator/attestation/vtpm" "github.com/edgelesssys/constellation/coordinator/attestation/vtpm"
"github.com/edgelesssys/constellation/coordinator/core" "github.com/edgelesssys/constellation/coordinator/core"
@ -17,9 +16,12 @@ import (
"github.com/edgelesssys/constellation/coordinator/util/grpcutil" "github.com/edgelesssys/constellation/coordinator/util/grpcutil"
"github.com/edgelesssys/constellation/coordinator/vpnapi" "github.com/edgelesssys/constellation/coordinator/vpnapi"
"github.com/edgelesssys/constellation/coordinator/vpnapi/vpnproto" "github.com/edgelesssys/constellation/coordinator/vpnapi/vpnproto"
"github.com/edgelesssys/constellation/internal/deploy/user"
"github.com/edgelesssys/constellation/internal/file"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags" grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags"
"github.com/spf13/afero"
"go.uber.org/zap" "go.uber.org/zap"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
@ -29,6 +31,7 @@ var version = "0.0.0"
func run(issuer core.QuoteIssuer, vpn core.VPN, openTPM vtpm.TPMOpenFunc, getPublicIPAddr func() (string, error), dialer *grpcutil.Dialer, fileHandler file.Handler, func run(issuer core.QuoteIssuer, vpn core.VPN, openTPM vtpm.TPMOpenFunc, getPublicIPAddr func() (string, error), dialer *grpcutil.Dialer, fileHandler file.Handler,
kube core.Cluster, metadata core.ProviderMetadata, cloudControllerManager core.CloudControllerManager, cloudNodeManager core.CloudNodeManager, clusterAutoscaler core.ClusterAutoscaler, encryptedDisk core.EncryptedDisk, etcdEndpoint string, etcdTLS bool, bindIP, bindPort string, zapLoggerCore *zap.Logger, kube core.Cluster, metadata core.ProviderMetadata, cloudControllerManager core.CloudControllerManager, cloudNodeManager core.CloudNodeManager, clusterAutoscaler core.ClusterAutoscaler, encryptedDisk core.EncryptedDisk, etcdEndpoint string, etcdTLS bool, bindIP, bindPort string, zapLoggerCore *zap.Logger,
fs afero.Fs,
) { ) {
defer zapLoggerCore.Sync() defer zapLoggerCore.Sync()
zapLoggerCore.Info("starting coordinator", zap.String("version", version)) zapLoggerCore.Info("starting coordinator", zap.String("version", version))
@ -43,7 +46,8 @@ func run(issuer core.QuoteIssuer, vpn core.VPN, openTPM vtpm.TPMOpenFunc, getPub
ForceTLS: etcdTLS, ForceTLS: etcdTLS,
Logger: zapLoggerCore.WithOptions(zap.IncreaseLevel(zap.WarnLevel)).Named("etcd"), Logger: zapLoggerCore.WithOptions(zap.IncreaseLevel(zap.WarnLevel)).Named("etcd"),
} }
core, err := core.NewCore(vpn, kube, metadata, cloudControllerManager, cloudNodeManager, clusterAutoscaler, encryptedDisk, zapLoggerCore, openTPM, etcdStoreFactory, fileHandler) linuxUserManager := user.NewLinuxUserManager(fs)
core, err := core.NewCore(vpn, kube, metadata, cloudControllerManager, cloudNodeManager, clusterAutoscaler, encryptedDisk, zapLoggerCore, openTPM, etcdStoreFactory, fileHandler, linuxUserManager)
if err != nil { if err != nil {
zapLoggerCore.Fatal("failed to create core", zap.Error(err)) zapLoggerCore.Fatal("failed to create core", zap.Error(err))
} }

View File

@ -8,7 +8,6 @@ import (
"sync" "sync"
"testing" "testing"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/atls" "github.com/edgelesssys/constellation/coordinator/atls"
"github.com/edgelesssys/constellation/coordinator/attestation/simulator" "github.com/edgelesssys/constellation/coordinator/attestation/simulator"
"github.com/edgelesssys/constellation/coordinator/core" "github.com/edgelesssys/constellation/coordinator/core"
@ -21,6 +20,8 @@ import (
"github.com/edgelesssys/constellation/coordinator/util/testdialer" "github.com/edgelesssys/constellation/coordinator/util/testdialer"
"github.com/edgelesssys/constellation/coordinator/vpnapi" "github.com/edgelesssys/constellation/coordinator/vpnapi"
"github.com/edgelesssys/constellation/coordinator/vpnapi/vpnproto" "github.com/edgelesssys/constellation/coordinator/vpnapi/vpnproto"
"github.com/edgelesssys/constellation/internal/deploy/user"
"github.com/edgelesssys/constellation/internal/file"
kms "github.com/edgelesssys/constellation/kms/server/setup" kms "github.com/edgelesssys/constellation/kms/server/setup"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -211,7 +212,8 @@ func TestConcurrent(t *testing.T) {
func spawnPeer(require *require.Assertions, logger *zap.Logger, netDialer *testdialer.BufconnDialer, netw *network, endpoint string) (*grpc.Server, *pubapi.API, *fakeVPN) { func spawnPeer(require *require.Assertions, logger *zap.Logger, netDialer *testdialer.BufconnDialer, netw *network, endpoint string) (*grpc.Server, *pubapi.API, *fakeVPN) {
vpn := newVPN(netw, endpoint) vpn := newVPN(netw, endpoint)
cor, err := core.NewCore(vpn, &core.ClusterFake{}, &core.ProviderMetadataFake{}, &core.CloudControllerManagerFake{}, &core.CloudNodeManagerFake{}, &core.ClusterAutoscalerFake{}, &core.EncryptedDiskFake{}, logger, simulator.OpenSimulatedTPM, fakeStoreFactory{}, file.NewHandler(afero.NewMemMapFs())) fs := afero.NewMemMapFs()
cor, err := core.NewCore(vpn, &core.ClusterFake{}, &core.ProviderMetadataFake{}, &core.CloudControllerManagerFake{}, &core.CloudNodeManagerFake{}, &core.ClusterAutoscalerFake{}, &core.EncryptedDiskFake{}, logger, simulator.OpenSimulatedTPM, fakeStoreFactory{}, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
require.NoError(err) require.NoError(err)
require.NoError(cor.AdvanceState(state.AcceptingInit, nil, nil)) require.NoError(cor.AdvanceState(state.AcceptingInit, nil, nil))

View File

@ -6,11 +6,12 @@ import (
"testing" "testing"
"time" "time"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/attestation/simulator" "github.com/edgelesssys/constellation/coordinator/attestation/simulator"
"github.com/edgelesssys/constellation/coordinator/kubernetes" "github.com/edgelesssys/constellation/coordinator/kubernetes"
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources" "github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
"github.com/edgelesssys/constellation/coordinator/role" "github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/internal/deploy/user"
"github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -171,7 +172,8 @@ func TestInitCluster(t *testing.T) {
zapLogger, err := zap.NewDevelopment() zapLogger, err := zap.NewDevelopment()
require.NoError(err) require.NoError(err)
core, err := NewCore(&stubVPN{}, &tc.cluster, &tc.metadata, &tc.cloudControllerManager, &tc.cloudNodeManager, &tc.clusterAutoscaler, nil, zapLogger, simulator.OpenSimulatedTPM, nil, file.NewHandler(afero.NewMemMapFs())) fs := afero.NewMemMapFs()
core, err := NewCore(&stubVPN{}, &tc.cluster, &tc.metadata, &tc.cloudControllerManager, &tc.cloudNodeManager, &tc.clusterAutoscaler, nil, zapLogger, simulator.OpenSimulatedTPM, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
require.NoError(err) require.NoError(err)
kubeconfig, err := core.InitCluster(tc.autoscalingNodeGroups, "cloud-service-account-uri") kubeconfig, err := core.InitCluster(tc.autoscalingNodeGroups, "cloud-service-account-uri")
@ -286,7 +288,8 @@ func TestJoinCluster(t *testing.T) {
zapLogger, err := zap.NewDevelopment() zapLogger, err := zap.NewDevelopment()
require.NoError(err) require.NoError(err)
core, err := NewCore(&tc.vpn, &tc.cluster, &tc.metadata, &tc.cloudControllerManager, &tc.cloudNodeManager, &tc.clusterAutoscaler, nil, zapLogger, simulator.OpenSimulatedTPM, nil, file.NewHandler(afero.NewMemMapFs())) fs := afero.NewMemMapFs()
core, err := NewCore(&tc.vpn, &tc.cluster, &tc.metadata, &tc.cloudControllerManager, &tc.cloudNodeManager, &tc.clusterAutoscaler, nil, zapLogger, simulator.OpenSimulatedTPM, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
require.NoError(err) require.NoError(err)
joinReq := &kubeadm.BootstrapTokenDiscovery{ joinReq := &kubeadm.BootstrapTokenDiscovery{

View File

@ -9,7 +9,6 @@ import (
"sync" "sync"
"time" "time"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/attestation/vtpm" "github.com/edgelesssys/constellation/coordinator/attestation/vtpm"
"github.com/edgelesssys/constellation/coordinator/config" "github.com/edgelesssys/constellation/coordinator/config"
"github.com/edgelesssys/constellation/coordinator/nodestate" "github.com/edgelesssys/constellation/coordinator/nodestate"
@ -18,6 +17,8 @@ import (
"github.com/edgelesssys/constellation/coordinator/store" "github.com/edgelesssys/constellation/coordinator/store"
"github.com/edgelesssys/constellation/coordinator/storewrapper" "github.com/edgelesssys/constellation/coordinator/storewrapper"
"github.com/edgelesssys/constellation/coordinator/util" "github.com/edgelesssys/constellation/coordinator/util"
"github.com/edgelesssys/constellation/internal/deploy/user"
"github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/kms/kms" "github.com/edgelesssys/constellation/kms/kms"
kmsSetup "github.com/edgelesssys/constellation/kms/server/setup" kmsSetup "github.com/edgelesssys/constellation/kms/server/setup"
"go.uber.org/zap" "go.uber.org/zap"
@ -44,12 +45,13 @@ type Core struct {
initialVPNPeersRetriever initialVPNPeersRetriever initialVPNPeersRetriever initialVPNPeersRetriever
lastHeartbeats map[string]time.Time lastHeartbeats map[string]time.Time
fileHandler file.Handler fileHandler file.Handler
linuxUserManager user.LinuxUserManager
} }
// NewCore creates and initializes a new Core object. // NewCore creates and initializes a new Core object.
func NewCore(vpn VPN, kube Cluster, func NewCore(vpn VPN, kube Cluster,
metadata ProviderMetadata, cloudControllerManager CloudControllerManager, cloudNodeManager CloudNodeManager, clusterAutoscaler ClusterAutoscaler, metadata ProviderMetadata, cloudControllerManager CloudControllerManager, cloudNodeManager CloudNodeManager, clusterAutoscaler ClusterAutoscaler,
encryptedDisk EncryptedDisk, zapLogger *zap.Logger, openTPM vtpm.TPMOpenFunc, persistentStoreFactory PersistentStoreFactory, fileHandler file.Handler, encryptedDisk EncryptedDisk, zapLogger *zap.Logger, openTPM vtpm.TPMOpenFunc, persistentStoreFactory PersistentStoreFactory, fileHandler file.Handler, linuxUserManager user.LinuxUserManager,
) (*Core, error) { ) (*Core, error) {
stor := store.NewStdStore() stor := store.NewStdStore()
c := &Core{ c := &Core{
@ -68,6 +70,7 @@ func NewCore(vpn VPN, kube Cluster,
initialVPNPeersRetriever: getInitialVPNPeers, initialVPNPeersRetriever: getInitialVPNPeers,
lastHeartbeats: make(map[string]time.Time), lastHeartbeats: make(map[string]time.Time),
fileHandler: fileHandler, fileHandler: fileHandler,
linuxUserManager: linuxUserManager,
} }
if err := c.data().IncrementPeersResourceVersion(); err != nil { if err := c.data().IncrementPeersResourceVersion(); err != nil {
return nil, err return nil, err

View File

@ -6,7 +6,6 @@ import (
"net" "net"
"testing" "testing"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/attestation/simulator" "github.com/edgelesssys/constellation/coordinator/attestation/simulator"
"github.com/edgelesssys/constellation/coordinator/attestation/vtpm" "github.com/edgelesssys/constellation/coordinator/attestation/vtpm"
"github.com/edgelesssys/constellation/coordinator/nodestate" "github.com/edgelesssys/constellation/coordinator/nodestate"
@ -16,6 +15,8 @@ import (
"github.com/edgelesssys/constellation/coordinator/store" "github.com/edgelesssys/constellation/coordinator/store"
"github.com/edgelesssys/constellation/coordinator/util/grpcutil" "github.com/edgelesssys/constellation/coordinator/util/grpcutil"
"github.com/edgelesssys/constellation/coordinator/util/testdialer" "github.com/edgelesssys/constellation/coordinator/util/testdialer"
"github.com/edgelesssys/constellation/internal/deploy/user"
"github.com/edgelesssys/constellation/internal/file"
kms "github.com/edgelesssys/constellation/kms/server/setup" kms "github.com/edgelesssys/constellation/kms/server/setup"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -36,7 +37,8 @@ func TestGetNextNodeIP(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
require := require.New(t) require := require.New(t)
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs())) fs := afero.NewMemMapFs()
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
require.NoError(err) require.NoError(err)
require.NoError(core.InitializeStoreIPs()) require.NoError(core.InitializeStoreIPs())
@ -79,7 +81,8 @@ func TestSwitchToPersistentStore(t *testing.T) {
require := require.New(t) require := require.New(t)
storeFactory := &fakeStoreFactory{} storeFactory := &fakeStoreFactory{}
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, storeFactory, file.NewHandler(afero.NewMemMapFs())) fs := afero.NewMemMapFs()
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, storeFactory, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
require.NoError(core.store.Put("test", []byte("test"))) require.NoError(core.store.Put("test", []byte("test")))
require.NoError(err) require.NoError(err)
@ -93,7 +96,8 @@ func TestGetIDs(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
require := require.New(t) require := require.New(t)
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs())) fs := afero.NewMemMapFs()
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
require.NoError(err) require.NoError(err)
_, _, err = core.GetIDs(nil) _, _, err = core.GetIDs(nil)
@ -117,7 +121,8 @@ func TestNotifyNodeHeartbeat(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
require := require.New(t) require := require.New(t)
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs())) fs := afero.NewMemMapFs()
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
require.NoError(err) require.NoError(err)
const ip = "192.0.2.1" const ip = "192.0.2.1"
@ -130,7 +135,8 @@ func TestDeriveKey(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
require := require.New(t) require := require.New(t)
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs())) fs := afero.NewMemMapFs()
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
require.NoError(err) require.NoError(err)
// error when no kms is set up // error when no kms is set up
@ -200,14 +206,15 @@ func TestInitialize(t *testing.T) {
if tc.initializePCRs { if tc.initializePCRs {
require.NoError(vtpm.MarkNodeAsInitialized(openTPM, []byte{0x0, 0x1, 0x2, 0x3}, []byte{0x4, 0x5, 0x6, 0x7})) require.NoError(vtpm.MarkNodeAsInitialized(openTPM, []byte{0x0, 0x1, 0x2, 0x3}, []byte{0x4, 0x5, 0x6, 0x7}))
} }
fileHandler := file.NewHandler(afero.NewMemMapFs()) fs := afero.NewMemMapFs()
fileHandler := file.NewHandler(fs)
if tc.writeNodeState { if tc.writeNodeState {
require.NoError((&nodestate.NodeState{ require.NoError((&nodestate.NodeState{
Role: tc.role, Role: tc.role,
VPNPrivKey: []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}, VPNPrivKey: []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7},
}).ToFile(fileHandler)) }).ToFile(fileHandler))
} }
core, err := NewCore(&stubVPN{}, nil, &ProviderMetadataFake{}, nil, nil, nil, nil, zaptest.NewLogger(t), openTPM, &fakeStoreFactory{}, fileHandler) core, err := NewCore(&stubVPN{}, nil, &ProviderMetadataFake{}, nil, nil, nil, nil, zaptest.NewLogger(t), openTPM, &fakeStoreFactory{}, fileHandler, user.NewLinuxUserManagerFake(fs))
require.NoError(err) require.NoError(err)
core.initialVPNPeersRetriever = fakeInitializeVPNPeersRetriever core.initialVPNPeersRetriever = fakeInitializeVPNPeersRetriever
// prepare store to emulate initialized KMS // prepare store to emulate initialized KMS
@ -265,7 +272,7 @@ func TestPersistNodeState(t *testing.T) {
require.NoError(err) require.NoError(err)
require.NoError(file.Close()) require.NoError(file.Close())
} }
core, err := NewCore(tc.vpn, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, fileHandler) core, err := NewCore(tc.vpn, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, fileHandler, user.NewLinuxUserManagerFake(fs))
require.NoError(err) require.NoError(err)
err = core.PersistNodeState(role.Coordinator, "192.0.2.1", []byte("owner-id"), []byte("cluster-id")) err = core.PersistNodeState(role.Coordinator, "192.0.2.1", []byte("owner-id"), []byte("cluster-id"))
if tc.wantErr { if tc.wantErr {

View File

@ -4,7 +4,8 @@ import (
"errors" "errors"
"testing" "testing"
"github.com/edgelesssys/constellation/cli/file" "github.com/edgelesssys/constellation/internal/deploy/user"
"github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -43,7 +44,8 @@ func TestGetDiskUUID(t *testing.T) {
uuidErr: tc.uuidErr, uuidErr: tc.uuidErr,
uuid: tc.wantUUID, uuid: tc.wantUUID,
} }
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, &diskStub, zapLogger, nil, nil, file.NewHandler(afero.NewMemMapFs())) fs := afero.NewMemMapFs()
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, &diskStub, zapLogger, nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
require.NoError(err) require.NoError(err)
uuid, err := core.GetDiskUUID() uuid, err := core.GetDiskUUID()
if tc.wantErr { if tc.wantErr {
@ -85,7 +87,8 @@ func TestUpdateDiskPassphrase(t *testing.T) {
openErr: tc.openErr, openErr: tc.openErr,
updatePassphraseErr: tc.updatePassphraseErr, updatePassphraseErr: tc.updatePassphraseErr,
} }
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, &diskStub, zapLogger, nil, nil, file.NewHandler(afero.NewMemMapFs())) fs := afero.NewMemMapFs()
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, &diskStub, zapLogger, nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
require.NoError(err) require.NoError(err)
err = core.UpdateDiskPassphrase("passphrase") err = core.UpdateDiskPassphrase("passphrase")
if tc.wantErr { if tc.wantErr {

View File

@ -9,7 +9,6 @@ import (
"sync" "sync"
"testing" "testing"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/atls" "github.com/edgelesssys/constellation/coordinator/atls"
"github.com/edgelesssys/constellation/coordinator/attestation/simulator" "github.com/edgelesssys/constellation/coordinator/attestation/simulator"
"github.com/edgelesssys/constellation/coordinator/pubapi" "github.com/edgelesssys/constellation/coordinator/pubapi"
@ -18,6 +17,8 @@ import (
"github.com/edgelesssys/constellation/coordinator/util/grpcutil" "github.com/edgelesssys/constellation/coordinator/util/grpcutil"
"github.com/edgelesssys/constellation/coordinator/vpnapi" "github.com/edgelesssys/constellation/coordinator/vpnapi"
"github.com/edgelesssys/constellation/coordinator/vpnapi/vpnproto" "github.com/edgelesssys/constellation/coordinator/vpnapi/vpnproto"
"github.com/edgelesssys/constellation/internal/deploy/user"
"github.com/edgelesssys/constellation/internal/file"
kms "github.com/edgelesssys/constellation/kms/server/setup" kms "github.com/edgelesssys/constellation/kms/server/setup"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -131,7 +132,8 @@ func newMockCoreWithDialer(bufDialer *bufconnDialer) (*Core, *pubapi.API, error)
getPublicAddr := func() (string, error) { getPublicAddr := func() (string, error) {
return "192.0.2.1", nil return "192.0.2.1", nil
} }
core, err := NewCore(vpn, kubeFake, metadataFake, ccmFake, cnmFake, autoscalerFake, encryptedDiskFake, zapLogger, simulator.OpenSimulatedTPM, &fakeStoreFactory{}, file.NewHandler(afero.NewMemMapFs())) fs := afero.NewMemMapFs()
core, err := NewCore(vpn, kubeFake, metadataFake, ccmFake, cnmFake, autoscalerFake, encryptedDiskFake, zapLogger, simulator.OpenSimulatedTPM, &fakeStoreFactory{}, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@ -4,8 +4,9 @@ import (
"errors" "errors"
"testing" "testing"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/peer" "github.com/edgelesssys/constellation/coordinator/peer"
"github.com/edgelesssys/constellation/internal/deploy/user"
"github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -53,7 +54,8 @@ func TestGetPeers(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
require := require.New(t) require := require.New(t)
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs())) fs := afero.NewMemMapFs()
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
require.NoError(err) require.NoError(err)
// prepare store // prepare store
@ -113,7 +115,8 @@ func TestAddPeer(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
require := require.New(t) require := require.New(t)
core, err := NewCore(&tc.vpn, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs())) fs := afero.NewMemMapFs()
core, err := NewCore(&tc.vpn, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
require.NoError(err) require.NoError(err)
err = core.AddPeer(tc.peer) err = core.AddPeer(tc.peer)

View File

@ -5,13 +5,14 @@ import (
"errors" "errors"
"testing" "testing"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/atls" "github.com/edgelesssys/constellation/coordinator/atls"
"github.com/edgelesssys/constellation/coordinator/peer" "github.com/edgelesssys/constellation/coordinator/peer"
"github.com/edgelesssys/constellation/coordinator/pubapi/pubproto" "github.com/edgelesssys/constellation/coordinator/pubapi/pubproto"
"github.com/edgelesssys/constellation/coordinator/role" "github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/coordinator/util/grpcutil" "github.com/edgelesssys/constellation/coordinator/util/grpcutil"
"github.com/edgelesssys/constellation/coordinator/util/testdialer" "github.com/edgelesssys/constellation/coordinator/util/testdialer"
"github.com/edgelesssys/constellation/internal/deploy/user"
"github.com/edgelesssys/constellation/internal/file"
kms "github.com/edgelesssys/constellation/kms/server/setup" kms "github.com/edgelesssys/constellation/kms/server/setup"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -79,7 +80,8 @@ func TestReinitializeAsNode(t *testing.T) {
go server.Serve(netDialer.GetListener("192.0.2.1:9000")) go server.Serve(netDialer.GetListener("192.0.2.1:9000"))
defer server.Stop() defer server.Stop()
vpn := &stubVPN{} vpn := &stubVPN{}
core, err := NewCore(vpn, nil, &stubMetadata{listRes: coordinators, supportedRes: true}, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs())) fs := afero.NewMemMapFs()
core, err := NewCore(vpn, nil, &stubMetadata{listRes: coordinators, supportedRes: true}, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
require.NoError(err) require.NoError(err)
err = core.ReinitializeAsNode(context.Background(), dialer, vpnIP, &stubPubAPI{}, 0) err = core.ReinitializeAsNode(context.Background(), dialer, vpnIP, &stubPubAPI{}, 0)
@ -151,7 +153,8 @@ func TestReinitializeAsCoordinator(t *testing.T) {
go server.Serve(netDialer.GetListener("192.0.2.1:9000")) go server.Serve(netDialer.GetListener("192.0.2.1:9000"))
defer server.Stop() defer server.Stop()
vpn := &stubVPN{} vpn := &stubVPN{}
core, err := NewCore(vpn, nil, &stubMetadata{listRes: coordinators, supportedRes: true}, nil, nil, nil, nil, zaptest.NewLogger(t), nil, &fakeStoreFactory{}, file.NewHandler(afero.NewMemMapFs())) fs := afero.NewMemMapFs()
core, err := NewCore(vpn, nil, &stubMetadata{listRes: coordinators, supportedRes: true}, nil, nil, nil, nil, zaptest.NewLogger(t), nil, &fakeStoreFactory{}, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
require.NoError(err) require.NoError(err)
// prepare store to emulate initialized KMS // prepare store to emulate initialized KMS
require.NoError(core.data().PutKMSData(kms.KMSInformation{StorageUri: kms.NoStoreURI, KmsUri: kms.ClusterKMSURI})) require.NoError(core.data().PutKMSData(kms.KMSInformation{StorageUri: kms.NoStoreURI, KmsUri: kms.ClusterKMSURI}))

21
coordinator/core/ssh.go Normal file
View File

@ -0,0 +1,21 @@
package core
import (
"context"
"github.com/edgelesssys/constellation/internal/deploy/ssh"
)
// CreateSSHUsers creates UNIX users with respective SSH access on the system the coordinator is running on when defined in the config.
func (c *Core) CreateSSHUsers(sshUserKeys []ssh.UserKey) error {
sshAccess := ssh.NewSSHAccess(c.linuxUserManager)
ctx := context.Background()
for _, pair := range sshUserKeys {
if err := sshAccess.DeploySSHAuthorizedKey(ctx, pair); err != nil {
return err
}
}
return nil
}

View File

@ -5,9 +5,10 @@ import (
"io" "io"
"testing" "testing"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/attestation/simulator" "github.com/edgelesssys/constellation/coordinator/attestation/simulator"
"github.com/edgelesssys/constellation/coordinator/state" "github.com/edgelesssys/constellation/coordinator/state"
"github.com/edgelesssys/constellation/internal/deploy/user"
"github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -65,7 +66,8 @@ func TestAdvanceState(t *testing.T) {
return simulator.OpenSimulatedTPM() return simulator.OpenSimulatedTPM()
} }
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), openTPM, nil, file.NewHandler(afero.NewMemMapFs())) fs := afero.NewMemMapFs()
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), openTPM, nil, file.NewHandler(fs), user.NewLinuxUserManagerFake(fs))
require.NoError(err) require.NoError(err)
assert.Equal(state.Uninitialized, core.GetState()) assert.Equal(state.Uninitialized, core.GetState())
core.state = tc.initialState core.state = tc.initialState

View File

@ -3,8 +3,8 @@ package nodestate
import ( import (
"fmt" "fmt"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/role" "github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/internal/file"
) )
const nodeStatePath = "/run/state/constellation/node_state.json" const nodeStatePath = "/run/state/constellation/node_state.json"

View File

@ -4,8 +4,8 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/role" "github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"

View File

@ -12,6 +12,7 @@ import (
"github.com/edgelesssys/constellation/coordinator/pubapi/pubproto" "github.com/edgelesssys/constellation/coordinator/pubapi/pubproto"
"github.com/edgelesssys/constellation/coordinator/role" "github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/coordinator/state" "github.com/edgelesssys/constellation/coordinator/state"
"github.com/edgelesssys/constellation/internal/deploy/ssh"
"github.com/edgelesssys/constellation/state/keyservice/keyproto" "github.com/edgelesssys/constellation/state/keyservice/keyproto"
"go.uber.org/zap" "go.uber.org/zap"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
@ -88,6 +89,15 @@ func (a *API) ActivateAsCoordinator(in *pubproto.ActivateAsCoordinatorRequest, s
return status.Errorf(codes.Internal, "adding the coordinator to store/vpn: %v", err) return status.Errorf(codes.Internal, "adding the coordinator to store/vpn: %v", err)
} }
// Setup SSH users for the first coordinator, if defined
if len(in.SshUserKeys) != 0 {
logToCLI("Creating SSH users on first control-plane node...")
sshUserKeys := ssh.FromProtoSlice(in.SshUserKeys)
if err := a.core.CreateSSHUsers(sshUserKeys); err != nil {
return status.Errorf(codes.Internal, "creating SSH users: %v", err)
}
}
logToCLI("Initializing Kubernetes ...") logToCLI("Initializing Kubernetes ...")
kubeconfig, err := a.core.InitCluster(in.AutoscalingNodeGroups, in.CloudServiceAccountUri) kubeconfig, err := a.core.InitCluster(in.AutoscalingNodeGroups, in.CloudServiceAccountUri)
if err != nil { if err != nil {

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io" "io"
"net" "net"
"sync" "sync"
@ -19,8 +20,11 @@ import (
"github.com/edgelesssys/constellation/coordinator/state" "github.com/edgelesssys/constellation/coordinator/state"
"github.com/edgelesssys/constellation/coordinator/util/grpcutil" "github.com/edgelesssys/constellation/coordinator/util/grpcutil"
"github.com/edgelesssys/constellation/coordinator/util/testdialer" "github.com/edgelesssys/constellation/coordinator/util/testdialer"
"github.com/edgelesssys/constellation/internal/deploy/ssh"
"github.com/edgelesssys/constellation/internal/deploy/user"
kms "github.com/edgelesssys/constellation/kms/server/setup" kms "github.com/edgelesssys/constellation/kms/server/setup"
"github.com/edgelesssys/constellation/state/keyservice/keyproto" "github.com/edgelesssys/constellation/state/keyservice/keyproto"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest"
@ -40,6 +44,14 @@ func TestActivateAsCoordinator(t *testing.T) {
wantNode3 := peer.Peer{PublicIP: "192.0.2.13", VPNIP: "10.118.0.13", VPNPubKey: []byte{3, 4, 5}, Role: role.Node} wantNode3 := peer.Peer{PublicIP: "192.0.2.13", VPNIP: "10.118.0.13", VPNPubKey: []byte{3, 4, 5}, Role: role.Node}
wantCoord := peer.Peer{PublicIP: "192.0.2.1", VPNIP: "10.118.0.1", VPNPubKey: coordinatorPubKey, Role: role.Coordinator} wantCoord := peer.Peer{PublicIP: "192.0.2.1", VPNIP: "10.118.0.1", VPNPubKey: coordinatorPubKey, Role: role.Coordinator}
adminPeer := peer.Peer{VPNPubKey: []byte{7, 8, 9}, Role: role.Admin} adminPeer := peer.Peer{VPNPubKey: []byte{7, 8, 9}, Role: role.Admin}
sshUser1 := &ssh.UserKey{
Username: "test-user-1",
PublicKey: "ssh-rsa abcdefg",
}
sshUser2 := &ssh.UserKey{
Username: "test-user-2",
PublicKey: "ssh-ed25519 hijklmn",
}
testCases := map[string]struct { testCases := map[string]struct {
nodes []*stubPeer nodes []*stubPeer
@ -49,6 +61,7 @@ func TestActivateAsCoordinator(t *testing.T) {
wantPeers []peer.Peer wantPeers []peer.Peer
wantState state.State wantState state.State
adminVPNIP string adminVPNIP string
sshKeys []*ssh.UserKey
}{ }{
"0 nodes": { "0 nodes": {
state: state.AcceptingInit, state: state.AcceptingInit,
@ -77,6 +90,13 @@ func TestActivateAsCoordinator(t *testing.T) {
wantState: state.ActivatingNodes, wantState: state.ActivatingNodes,
adminVPNIP: "10.118.0.14", adminVPNIP: "10.118.0.14",
}, },
"coordinator with SSH users": {
state: state.AcceptingInit,
wantPeers: []peer.Peer{wantCoord},
wantState: state.ActivatingNodes,
adminVPNIP: "10.118.0.11",
sshKeys: []*ssh.UserKey{sshUser1, sshUser2},
},
"already activated": { "already activated": {
nodes: []*stubPeer{testNode1}, nodes: []*stubPeer{testNode1},
state: state.ActivatingNodes, state: state.ActivatingNodes,
@ -117,7 +137,7 @@ func TestActivateAsCoordinator(t *testing.T) {
autoscalingNodeGroups := []string{"ang1", "ang2"} autoscalingNodeGroups := []string{"ang1", "ang2"}
keyEncryptionKeyID := "constellation" keyEncryptionKeyID := "constellation"
fs := afero.NewMemMapFs()
core := &fakeCore{ core := &fakeCore{
state: tc.state, state: tc.state,
vpnPubKey: coordinatorPubKey, vpnPubKey: coordinatorPubKey,
@ -125,7 +145,9 @@ func TestActivateAsCoordinator(t *testing.T) {
kubeconfig: []byte("kubeconfig"), kubeconfig: []byte("kubeconfig"),
ownerID: []byte("ownerID"), ownerID: []byte("ownerID"),
clusterID: []byte("clusterID"), clusterID: []byte("clusterID"),
linuxUserManager: user.NewLinuxUserManagerFake(fs),
} }
netDialer := testdialer.NewBufconnDialer() netDialer := testdialer.NewBufconnDialer()
dialer := grpcutil.NewDialer(fakeValidator{}, netDialer) dialer := grpcutil.NewDialer(fakeValidator{}, netDialer)
@ -162,6 +184,7 @@ func TestActivateAsCoordinator(t *testing.T) {
UseExistingKek: false, UseExistingKek: false,
KmsUri: kms.ClusterKMSURI, KmsUri: kms.ClusterKMSURI,
StorageUri: kms.NoStoreURI, StorageUri: kms.NoStoreURI,
SshUserKeys: ssh.ToProtoSlice(tc.sshKeys),
}, stream) }, stream)
assert.Equal(tc.wantState, core.state) assert.Equal(tc.wantState, core.state)
@ -194,6 +217,25 @@ func TestActivateAsCoordinator(t *testing.T) {
assert.Equal(autoscalingNodeGroups, core.autoscalingNodeGroups) assert.Equal(autoscalingNodeGroups, core.autoscalingNodeGroups)
assert.Equal(keyEncryptionKeyID, core.kekID) assert.Equal(keyEncryptionKeyID, core.kekID)
assert.Equal([]role.Role{role.Coordinator}, core.persistNodeStateRoles) assert.Equal([]role.Role{role.Coordinator}, core.persistNodeStateRoles)
// Test SSH user & key creation. Both cases: "supposed to add" and "not supposed to add"
// This slightly differs from a real environment (e.g. missing /home) but should be fine in the stub context with a virtual file system
if tc.sshKeys != nil {
passwd := user.Passwd{}
entries, err := passwd.Parse(fs)
require.NoError(err)
for _, singleEntry := range entries {
username := singleEntry.Gecos
_, err := fs.Stat(fmt.Sprintf("/home/%s/.ssh/authorized_keys.d/ssh-keys", username))
assert.NoError(err)
}
} else {
passwd := user.Passwd{}
_, err := passwd.Parse(fs)
assert.EqualError(err, "open /etc/passwd: file does not exist")
_, err = fs.Stat("/home")
assert.EqualError(err, "open /home: file does not exist")
}
}) })
} }
} }

View File

@ -6,6 +6,7 @@ import (
"github.com/edgelesssys/constellation/coordinator/peer" "github.com/edgelesssys/constellation/coordinator/peer"
"github.com/edgelesssys/constellation/coordinator/role" "github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/coordinator/state" "github.com/edgelesssys/constellation/coordinator/state"
"github.com/edgelesssys/constellation/internal/deploy/ssh"
kms "github.com/edgelesssys/constellation/kms/server/setup" kms "github.com/edgelesssys/constellation/kms/server/setup"
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3" kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
) )
@ -36,6 +37,8 @@ type Core interface {
AddPeerToVPN(peer.Peer) error AddPeerToVPN(peer.Peer) error
UpdatePeers([]peer.Peer) error UpdatePeers([]peer.Peer) error
CreateSSHUsers([]ssh.UserKey) error
InitCluster(autoscalingNodeGroups []string, cloudServiceAccountURI string) ([]byte, error) InitCluster(autoscalingNodeGroups []string, cloudServiceAccountURI string) ([]byte, error)
JoinCluster(joinToken *kubeadm.BootstrapTokenDiscovery, certificateKey string, role role.Role) error JoinCluster(joinToken *kubeadm.BootstrapTokenDiscovery, certificateKey string, role role.Role) error
} }

View File

@ -8,6 +8,8 @@ import (
"github.com/edgelesssys/constellation/coordinator/peer" "github.com/edgelesssys/constellation/coordinator/peer"
"github.com/edgelesssys/constellation/coordinator/role" "github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/coordinator/state" "github.com/edgelesssys/constellation/coordinator/state"
"github.com/edgelesssys/constellation/internal/deploy/ssh"
"github.com/edgelesssys/constellation/internal/deploy/user"
kms "github.com/edgelesssys/constellation/kms/server/setup" kms "github.com/edgelesssys/constellation/kms/server/setup"
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3" kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
) )
@ -36,6 +38,7 @@ type fakeCore struct {
kekID string kekID string
dataKey []byte dataKey []byte
getDataKeyErr error getDataKeyErr error
linuxUserManager user.LinuxUserManager
} }
func (c *fakeCore) GetVPNPubKey() ([]byte, error) { func (c *fakeCore) GetVPNPubKey() ([]byte, error) {
@ -154,3 +157,16 @@ func (c *fakeCore) GetDiskUUID() (string, error) {
func (c *fakeCore) UpdateDiskPassphrase(passphrase string) error { func (c *fakeCore) UpdateDiskPassphrase(passphrase string) error {
return nil return nil
} }
func (c *fakeCore) CreateSSHUsers(sshUserKeys []ssh.UserKey) error {
sshAccess := ssh.NewSSHAccess(c.linuxUserManager)
ctx := context.Background()
for _, pair := range sshUserKeys {
if err := sshAccess.DeploySSHAuthorizedKey(ctx, pair); err != nil {
return err
}
}
return nil
}

View File

@ -110,16 +110,17 @@ type ActivateAsCoordinatorRequest struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
AdminVpnPubKey []byte `protobuf:"bytes,1,opt,name=admin_vpn_pub_key,json=adminVpnPubKey,proto3" json:"admin_vpn_pub_key,omitempty"` AdminVpnPubKey []byte `protobuf:"bytes,1,opt,name=admin_vpn_pub_key,json=adminVpnPubKey,proto3" json:"admin_vpn_pub_key,omitempty"`
NodePublicIps []string `protobuf:"bytes,2,rep,name=node_public_ips,json=nodePublicIps,proto3" json:"node_public_ips,omitempty"` NodePublicIps []string `protobuf:"bytes,2,rep,name=node_public_ips,json=nodePublicIps,proto3" json:"node_public_ips,omitempty"`
CoordinatorPublicIps []string `protobuf:"bytes,3,rep,name=coordinator_public_ips,json=coordinatorPublicIps,proto3" json:"coordinator_public_ips,omitempty"` CoordinatorPublicIps []string `protobuf:"bytes,3,rep,name=coordinator_public_ips,json=coordinatorPublicIps,proto3" json:"coordinator_public_ips,omitempty"`
AutoscalingNodeGroups []string `protobuf:"bytes,4,rep,name=autoscaling_node_groups,json=autoscalingNodeGroups,proto3" json:"autoscaling_node_groups,omitempty"` AutoscalingNodeGroups []string `protobuf:"bytes,4,rep,name=autoscaling_node_groups,json=autoscalingNodeGroups,proto3" json:"autoscaling_node_groups,omitempty"`
MasterSecret []byte `protobuf:"bytes,5,opt,name=master_secret,json=masterSecret,proto3" json:"master_secret,omitempty"` MasterSecret []byte `protobuf:"bytes,5,opt,name=master_secret,json=masterSecret,proto3" json:"master_secret,omitempty"`
KmsUri string `protobuf:"bytes,6,opt,name=kms_uri,json=kmsUri,proto3" json:"kms_uri,omitempty"` KmsUri string `protobuf:"bytes,6,opt,name=kms_uri,json=kmsUri,proto3" json:"kms_uri,omitempty"`
StorageUri string `protobuf:"bytes,7,opt,name=storage_uri,json=storageUri,proto3" json:"storage_uri,omitempty"` StorageUri string `protobuf:"bytes,7,opt,name=storage_uri,json=storageUri,proto3" json:"storage_uri,omitempty"`
KeyEncryptionKeyId string `protobuf:"bytes,8,opt,name=key_encryption_key_id,json=keyEncryptionKeyId,proto3" json:"key_encryption_key_id,omitempty"` KeyEncryptionKeyId string `protobuf:"bytes,8,opt,name=key_encryption_key_id,json=keyEncryptionKeyId,proto3" json:"key_encryption_key_id,omitempty"`
UseExistingKek bool `protobuf:"varint,9,opt,name=use_existing_kek,json=useExistingKek,proto3" json:"use_existing_kek,omitempty"` UseExistingKek bool `protobuf:"varint,9,opt,name=use_existing_kek,json=useExistingKek,proto3" json:"use_existing_kek,omitempty"`
CloudServiceAccountUri string `protobuf:"bytes,10,opt,name=cloud_service_account_uri,json=cloudServiceAccountUri,proto3" json:"cloud_service_account_uri,omitempty"` CloudServiceAccountUri string `protobuf:"bytes,10,opt,name=cloud_service_account_uri,json=cloudServiceAccountUri,proto3" json:"cloud_service_account_uri,omitempty"`
SshUserKeys []*SSHUserKey `protobuf:"bytes,11,rep,name=ssh_user_keys,json=sshUserKeys,proto3" json:"ssh_user_keys,omitempty"`
} }
func (x *ActivateAsCoordinatorRequest) Reset() { func (x *ActivateAsCoordinatorRequest) Reset() {
@ -224,6 +225,13 @@ func (x *ActivateAsCoordinatorRequest) GetCloudServiceAccountUri() string {
return "" return ""
} }
func (x *ActivateAsCoordinatorRequest) GetSshUserKeys() []*SSHUserKey {
if x != nil {
return x.SshUserKeys
}
return nil
}
type ActivateAsCoordinatorResponse struct { type ActivateAsCoordinatorResponse struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -1520,6 +1528,61 @@ func (x *Peer) GetRole() uint32 {
return 0 return 0
} }
type SSHUserKey struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
PublicKey string `protobuf:"bytes,2,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
}
func (x *SSHUserKey) Reset() {
*x = SSHUserKey{}
if protoimpl.UnsafeEnabled {
mi := &file_pubapi_proto_msgTypes[28]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SSHUserKey) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SSHUserKey) ProtoMessage() {}
func (x *SSHUserKey) ProtoReflect() protoreflect.Message {
mi := &file_pubapi_proto_msgTypes[28]
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 SSHUserKey.ProtoReflect.Descriptor instead.
func (*SSHUserKey) Descriptor() ([]byte, []int) {
return file_pubapi_proto_rawDescGZIP(), []int{28}
}
func (x *SSHUserKey) GetUsername() string {
if x != nil {
return x.Username
}
return ""
}
func (x *SSHUserKey) GetPublicKey() string {
if x != nil {
return x.PublicKey
}
return ""
}
var File_pubapi_proto protoreflect.FileDescriptor var File_pubapi_proto protoreflect.FileDescriptor
var file_pubapi_proto_rawDesc = []byte{ var file_pubapi_proto_rawDesc = []byte{
@ -1528,7 +1591,7 @@ var file_pubapi_proto_rawDesc = []byte{
0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x28, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x28, 0x0a, 0x10, 0x47, 0x65, 0x74,
0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a,
0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x74, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x74,
0x61, 0x74, 0x65, 0x22, 0xd6, 0x03, 0x0a, 0x1c, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x61, 0x74, 0x65, 0x22, 0x8e, 0x04, 0x0a, 0x1c, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65,
0x41, 0x73, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x41, 0x73, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x11, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x76, 0x70, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x11, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x76, 0x70,
0x6e, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x6e, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
@ -1557,209 +1620,217 @@ var file_pubapi_proto_rawDesc = []byte{
0x6b, 0x12, 0x39, 0x0a, 0x19, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6b, 0x12, 0x39, 0x0a, 0x19, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x0a, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x0a,
0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x55, 0x72, 0x69, 0x22, 0x85, 0x01, 0x0a, 0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x55, 0x72, 0x69, 0x12, 0x36, 0x0a, 0x0d,
0x1d, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x73, 0x73, 0x68, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x0b, 0x20,
0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x53, 0x48,
0x0a, 0x0c, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x55, 0x73, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x0b, 0x73, 0x73, 0x68, 0x55, 0x73, 0x65, 0x72,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x22, 0x85, 0x01, 0x0a, 0x1d, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74,
0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x64, 0x6d, 0x65, 0x41, 0x73, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65,
0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1f, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0c, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70,
0x6f, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x74, 0x65, 0x6e, 0x74, 0x22, 0x9b, 0x01, 0x0a, 0x15, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x67, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x65, 0x41, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x12, 0x1f, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e,
0x0a, 0x0f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6c, 0x6f,
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x67, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x9b, 0x01, 0x0a,
0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x15, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52,
0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x0f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61,
0x0e, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x6c, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x26, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x6b, 0x65, 0x24, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74,
0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x65, 0x65, 0x41, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65,
0x44, 0x69, 0x73, 0x6b, 0x4b, 0x65, 0x79, 0x42, 0x09, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c,
0x73, 0x74, 0x22, 0x9c, 0x01, 0x0a, 0x1c, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x65,
0x73, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x5f, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48,
0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x76, 0x70, 0x6e, 0x5f, 0x00, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x6b, 0x4b, 0x65, 0x79, 0x42,
0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x56, 0x70, 0x09, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x9c, 0x01, 0x0a, 0x1c, 0x41,
0x6e, 0x49, 0x70, 0x12, 0x22, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x69,
0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x6e,
0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6f, 0x64, 0x65, 0x5f, 0x76, 0x70, 0x6e, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x56, 0x70, 0x6e, 0x49, 0x70, 0x12, 0x22, 0x0a, 0x05, 0x70,
0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x65, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x75, 0x62,
0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12,
0x64, 0x22, 0x79, 0x0a, 0x16, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x4e, 0x19, 0x0a, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x6e, 0x0c, 0x52, 0x07, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c,
0x6f, 0x64, 0x65, 0x5f, 0x76, 0x70, 0x6e, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09,
0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x56, 0x70, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x79, 0x0a, 0x16, 0x41, 0x63, 0x74,
0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x64, 0x69, 0x73, 0x6b, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x76, 0x70, 0x6e, 0x5f,
0x00, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52,
0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, 0x0a, 0x1e, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x56, 0x70, 0x6e, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x28,
0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x0f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x75, 0x75, 0x69,
0x61, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x65,
0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x70, 0x44, 0x69, 0x73, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70,
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x50, 0x75, 0x62, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, 0x0a, 0x1e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65,
0x6c, 0x69, 0x63, 0x49, 0x70, 0x73, 0x22, 0x40, 0x0a, 0x1f, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x52,
0x74, 0x65, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x70,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x73, 0x22, 0x40,
0x4c, 0x6f, 0x67, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x22, 0xfc, 0x01, 0x0a, 0x26, 0x41, 0x63, 0x74, 0x0a, 0x1f, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69,
0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6f, 0x6e, 0x61, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x6c, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x12, 0x1d, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b,
0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x03, 0x6c, 0x6f, 0x67,
0x76, 0x70, 0x6e, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x73, 0x22, 0xfc, 0x01, 0x0a, 0x26, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x41,
0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x70, 0x6e, 0x49, 0x70, 0x12, 0x4c, 0x0a, 0x1b, 0x61,
0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69,
0x6e, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x0c, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x52, 0x19,
0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69,
0x6e, 0x61, 0x74, 0x6f, 0x72, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x05, 0x70, 0x65, 0x65,
0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70,
0x69, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12, 0x19, 0x0a,
0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x07, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73,
0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c,
0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x29, 0x0a, 0x27, 0x41, 0x63, 0x74, 0x69, 0x76,
0x61, 0x74, 0x65, 0x41, 0x73, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43,
0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x5a, 0x0a, 0x24, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x64,
0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61,
0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x6f,
0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x6f, 0x6f, 0x72, 0x64,
0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x22, 0x27,
0x0a, 0x25, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69,
0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0x0a, 0x12, 0x4a, 0x6f, 0x69, 0x6e, 0x43,
0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a,
0x12, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x76, 0x70, 0x6e,
0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6f, 0x72, 0x64,
0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x56, 0x70, 0x6e, 0x49, 0x70, 0x22, 0x15, 0x0a, 0x13, 0x4a,
0x6f, 0x69, 0x6e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x1a, 0x0a, 0x18, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x4e, 0x6f, 0x64,
0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x1b,
0x0a, 0x19, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64,
0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x0a, 0x1f, 0x54,
0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f,
0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x22,
0x0a, 0x20, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e,
0x61, 0x74, 0x6f, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x39, 0x0a, 0x1a, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61,
0x74, 0x65, 0x44, 0x69, 0x73, 0x6b, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x22, 0x1d, 0x0a,
0x1b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73,
0x6b, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x0a, 0x1a,
0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x56, 0x50, 0x4e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63,
0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4d, 0x0a, 0x1b, 0x47, 0x65,
0x74, 0x50, 0x65, 0x65, 0x72, 0x56, 0x50, 0x4e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65,
0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x6f, 0x6f,
0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61,
0x74, 0x6f, 0x72, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x14, 0x0a, 0x12, 0x47, 0x65, 0x74,
0x56, 0x50, 0x4e, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22,
0x39, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x56, 0x50, 0x4e, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18,
0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x50,
0x65, 0x65, 0x72, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x0b, 0x41,
0x64, 0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x20, 0x0a, 0x0c, 0x61, 0x64,
0x6d, 0x69, 0x6e, 0x5f, 0x76, 0x70, 0x6e, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0a, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x56, 0x70, 0x6e, 0x49, 0x70, 0x12, 0x35, 0x0a, 0x17,
0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x76, 0x70, 0x6e, 0x5f,
0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x63,
0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x56, 0x70, 0x6e, 0x50, 0x75, 0x62,
0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x63, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18,
0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d,
0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x1f, 0x0a,
0x03, 0x4c, 0x6f, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x6e,
0x0a, 0x04, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69,
0x63, 0x49, 0x70, 0x12, 0x15, 0x0a, 0x06, 0x76, 0x70, 0x6e, 0x5f, 0x69, 0x70, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x70, 0x6e, 0x49, 0x70, 0x12, 0x1e, 0x0a, 0x0b, 0x76, 0x70,
0x6e, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x09, 0x76, 0x70, 0x6e, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f,
0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x32, 0x8b,
0x09, 0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x3d, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61,
0x74, 0x65, 0x12, 0x17, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53,
0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x70, 0x75,
0x62, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x15, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74,
0x65, 0x41, 0x73, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x24,
0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65,
0x41, 0x73, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63,
0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61,
0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x53, 0x0a,
0x0e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x12,
0x1d, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74,
0x65, 0x41, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e,
0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65,
0x41, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01,
0x30, 0x01, 0x12, 0x6c, 0x0a, 0x17, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x64,
0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x26, 0x2e,
0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41,
0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41,
0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61,
0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01,
0x12, 0x82, 0x01, 0x0a, 0x1f, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x41,
0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e,
0x61, 0x74, 0x6f, 0x72, 0x12, 0x2e, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x61,
0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x76, 0x70, 0x6e, 0x5f, 0x69, 0x70, 0x18, 0x01,
0x61, 0x6c, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x70,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x6e, 0x49, 0x70, 0x12, 0x4c, 0x0a, 0x1b, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6e,
0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x64, 0x61,
0x61, 0x6c, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7c, 0x0a, 0x1d, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x52, 0x19, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69,
0x65, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x6e, 0x67, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x44, 0x61, 0x74,
0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2c, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x61, 0x12, 0x22, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x0c, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x52, 0x05,
0x61, 0x6c, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64,
0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x05,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22,
0x29, 0x0a, 0x27, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x41, 0x64, 0x64,
0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74,
0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5a, 0x0a, 0x24, 0x41, 0x63,
0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c,
0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65,
0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f,
0x65, 0x72, 0x12, 0x1a, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28,
0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x09, 0x52, 0x13, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x50, 0x75,
0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x6c, 0x75, 0x73, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x22, 0x27, 0x0a, 0x25, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61,
0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x54, 0x74, 0x65, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6f, 0x72,
0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x12, 0x20, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x42, 0x0a, 0x12, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65,
0x72, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e,
0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x76, 0x70, 0x6e, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28,
0x67, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x56, 0x70,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x18, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x6e, 0x49, 0x70, 0x22, 0x15, 0x0a, 0x13, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x6c, 0x75, 0x73, 0x74,
0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x0a, 0x18, 0x54, 0x72,
0x65, 0x12, 0x27, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x69, 0x67, 0x67, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52,
0x65, 0x72, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x55, 0x70, 0x64, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x1b, 0x0a, 0x19, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65,
0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x70, 0x75, 0x62, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x61, 0x70, 0x69, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x0a, 0x1f, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x6f,
0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x56, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x22, 0x0a, 0x20, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65,
0x50, 0x4e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x2e, 0x70, 0x75, 0x72, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x55, 0x70, 0x64, 0x61,
0x62, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x56, 0x50, 0x4e, 0x50, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x0a, 0x1a, 0x52, 0x65,
0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x6b, 0x4b, 0x65,
0x23, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b,
0x56, 0x50, 0x4e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x56, 0x50, 0x4e, 0x50, 0x65, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x22, 0x1d, 0x0a, 0x1b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x65, 0x72, 0x73, 0x12, 0x1a, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x6b, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70,
0x56, 0x50, 0x4e, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x56,
0x1b, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x50, 0x4e, 0x50, 0x50, 0x4e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65,
0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x13, 0x73, 0x74, 0x22, 0x4d, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x56, 0x50, 0x4e,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x6b, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x4b, 0x65, 0x79, 0x12, 0x22, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72,
0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x6b, 0x4b, 0x65, 0x79, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x50, 0x75, 0x62, 0x4b, 0x65,
0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x79, 0x22, 0x14, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x56, 0x50, 0x4e, 0x50, 0x65, 0x65, 0x72, 0x73,
0x6b, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x42, 0x5a, 0x40, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x39, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x56, 0x50,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x64, 0x67, 0x65, 0x6c, 0x4e, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22,
0x65, 0x73, 0x73, 0x73, 0x79, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e,
0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x52, 0x05, 0x70, 0x65, 0x65,
0x2f, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x75, 0x62, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x72, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x0b, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x69, 0x67, 0x12, 0x20, 0x0a, 0x0c, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x76, 0x70, 0x6e, 0x5f,
0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x56,
0x70, 0x6e, 0x49, 0x70, 0x12, 0x35, 0x0a, 0x17, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61,
0x74, 0x6f, 0x72, 0x5f, 0x76, 0x70, 0x6e, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74,
0x6f, 0x72, 0x56, 0x70, 0x6e, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x6b,
0x75, 0x62, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x0a, 0x6b, 0x75, 0x62, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x6f,
0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f,
0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65,
0x72, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73,
0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x1f, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x18, 0x0a, 0x07,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x6e, 0x0a, 0x04, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1b,
0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x12, 0x15, 0x0a, 0x06, 0x76,
0x70, 0x6e, 0x5f, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x70, 0x6e,
0x49, 0x70, 0x12, 0x1e, 0x0a, 0x0b, 0x76, 0x70, 0x6e, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65,
0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x76, 0x70, 0x6e, 0x50, 0x75, 0x62, 0x4b,
0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x22, 0x47, 0x0a, 0x0a, 0x53, 0x53, 0x48, 0x55, 0x73, 0x65,
0x72, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65,
0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x32,
0x8b, 0x09, 0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x3d, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74,
0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74,
0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x70,
0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x15, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61,
0x74, 0x65, 0x41, 0x73, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x12,
0x24, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74,
0x65, 0x41, 0x73, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41,
0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e,
0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x53,
0x0a, 0x0e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x4e, 0x6f, 0x64, 0x65,
0x12, 0x1d, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61,
0x74, 0x65, 0x41, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x1e, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74,
0x65, 0x41, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28,
0x01, 0x30, 0x01, 0x12, 0x6c, 0x0a, 0x17, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41,
0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x26,
0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65,
0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e,
0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e,
0x61, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30,
0x01, 0x12, 0x82, 0x01, 0x0a, 0x1f, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73,
0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69,
0x6e, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2e, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41,
0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f,
0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41,
0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f,
0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7c, 0x0a, 0x1d, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61,
0x74, 0x65, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6f, 0x72,
0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2c, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69,
0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f,
0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41,
0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61,
0x6c, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x6c, 0x75, 0x73,
0x74, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x4a, 0x6f, 0x69,
0x6e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x1b, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x43, 0x6c, 0x75,
0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11,
0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74,
0x65, 0x12, 0x20, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67,
0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x72, 0x69,
0x67, 0x67, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x18, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65,
0x72, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x55, 0x70, 0x64, 0x61,
0x74, 0x65, 0x12, 0x27, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x72, 0x69, 0x67,
0x67, 0x65, 0x72, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x55, 0x70,
0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x70, 0x75,
0x62, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x6f, 0x6f, 0x72,
0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72,
0x56, 0x50, 0x4e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x2e, 0x70,
0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x56, 0x50, 0x4e,
0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x23, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65,
0x72, 0x56, 0x50, 0x4e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x56, 0x50, 0x4e, 0x50,
0x65, 0x65, 0x72, 0x73, 0x12, 0x1a, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65,
0x74, 0x56, 0x50, 0x4e, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x1b, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x50, 0x4e,
0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a,
0x13, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73,
0x6b, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x6b, 0x4b, 0x65,
0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70,
0x69, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69,
0x73, 0x6b, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x42, 0x5a,
0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x64, 0x67, 0x65,
0x6c, 0x65, 0x73, 0x73, 0x73, 0x79, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x65, 0x6c, 0x6c,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x6f,
0x72, 0x2f, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x75, 0x62, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@ -1774,7 +1845,7 @@ func file_pubapi_proto_rawDescGZIP() []byte {
return file_pubapi_proto_rawDescData return file_pubapi_proto_rawDescData
} }
var file_pubapi_proto_msgTypes = make([]protoimpl.MessageInfo, 28) var file_pubapi_proto_msgTypes = make([]protoimpl.MessageInfo, 29)
var file_pubapi_proto_goTypes = []interface{}{ var file_pubapi_proto_goTypes = []interface{}{
(*GetStateRequest)(nil), // 0: pubapi.GetStateRequest (*GetStateRequest)(nil), // 0: pubapi.GetStateRequest
(*GetStateResponse)(nil), // 1: pubapi.GetStateResponse (*GetStateResponse)(nil), // 1: pubapi.GetStateResponse
@ -1804,45 +1875,47 @@ var file_pubapi_proto_goTypes = []interface{}{
(*AdminConfig)(nil), // 25: pubapi.AdminConfig (*AdminConfig)(nil), // 25: pubapi.AdminConfig
(*Log)(nil), // 26: pubapi.Log (*Log)(nil), // 26: pubapi.Log
(*Peer)(nil), // 27: pubapi.Peer (*Peer)(nil), // 27: pubapi.Peer
(*SSHUserKey)(nil), // 28: pubapi.SSHUserKey
} }
var file_pubapi_proto_depIdxs = []int32{ var file_pubapi_proto_depIdxs = []int32{
25, // 0: pubapi.ActivateAsCoordinatorResponse.admin_config:type_name -> pubapi.AdminConfig 28, // 0: pubapi.ActivateAsCoordinatorRequest.ssh_user_keys:type_name -> pubapi.SSHUserKey
26, // 1: pubapi.ActivateAsCoordinatorResponse.log:type_name -> pubapi.Log 25, // 1: pubapi.ActivateAsCoordinatorResponse.admin_config:type_name -> pubapi.AdminConfig
5, // 2: pubapi.ActivateAsNodeRequest.initial_request:type_name -> pubapi.ActivateAsNodeInitialRequest 26, // 2: pubapi.ActivateAsCoordinatorResponse.log:type_name -> pubapi.Log
27, // 3: pubapi.ActivateAsNodeInitialRequest.peers:type_name -> pubapi.Peer 5, // 3: pubapi.ActivateAsNodeRequest.initial_request:type_name -> pubapi.ActivateAsNodeInitialRequest
26, // 4: pubapi.ActivateAdditionalNodesResponse.log:type_name -> pubapi.Log 27, // 4: pubapi.ActivateAsNodeInitialRequest.peers:type_name -> pubapi.Peer
27, // 5: pubapi.ActivateAsAdditionalCoordinatorRequest.activating_coordinator_data:type_name -> pubapi.Peer 26, // 5: pubapi.ActivateAdditionalNodesResponse.log:type_name -> pubapi.Log
27, // 6: pubapi.ActivateAsAdditionalCoordinatorRequest.peers:type_name -> pubapi.Peer 27, // 6: pubapi.ActivateAsAdditionalCoordinatorRequest.activating_coordinator_data:type_name -> pubapi.Peer
27, // 7: pubapi.GetVPNPeersResponse.peers:type_name -> pubapi.Peer 27, // 7: pubapi.ActivateAsAdditionalCoordinatorRequest.peers:type_name -> pubapi.Peer
0, // 8: pubapi.API.GetState:input_type -> pubapi.GetStateRequest 27, // 8: pubapi.GetVPNPeersResponse.peers:type_name -> pubapi.Peer
2, // 9: pubapi.API.ActivateAsCoordinator:input_type -> pubapi.ActivateAsCoordinatorRequest 0, // 9: pubapi.API.GetState:input_type -> pubapi.GetStateRequest
4, // 10: pubapi.API.ActivateAsNode:input_type -> pubapi.ActivateAsNodeRequest 2, // 10: pubapi.API.ActivateAsCoordinator:input_type -> pubapi.ActivateAsCoordinatorRequest
7, // 11: pubapi.API.ActivateAdditionalNodes:input_type -> pubapi.ActivateAdditionalNodesRequest 4, // 11: pubapi.API.ActivateAsNode:input_type -> pubapi.ActivateAsNodeRequest
9, // 12: pubapi.API.ActivateAsAdditionalCoordinator:input_type -> pubapi.ActivateAsAdditionalCoordinatorRequest 7, // 12: pubapi.API.ActivateAdditionalNodes:input_type -> pubapi.ActivateAdditionalNodesRequest
11, // 13: pubapi.API.ActivateAdditionalCoordinator:input_type -> pubapi.ActivateAdditionalCoordinatorRequest 9, // 13: pubapi.API.ActivateAsAdditionalCoordinator:input_type -> pubapi.ActivateAsAdditionalCoordinatorRequest
13, // 14: pubapi.API.JoinCluster:input_type -> pubapi.JoinClusterRequest 11, // 14: pubapi.API.ActivateAdditionalCoordinator:input_type -> pubapi.ActivateAdditionalCoordinatorRequest
15, // 15: pubapi.API.TriggerNodeUpdate:input_type -> pubapi.TriggerNodeUpdateRequest 13, // 15: pubapi.API.JoinCluster:input_type -> pubapi.JoinClusterRequest
17, // 16: pubapi.API.TriggerCoordinatorUpdate:input_type -> pubapi.TriggerCoordinatorUpdateRequest 15, // 16: pubapi.API.TriggerNodeUpdate:input_type -> pubapi.TriggerNodeUpdateRequest
21, // 17: pubapi.API.GetPeerVPNPublicKey:input_type -> pubapi.GetPeerVPNPublicKeyRequest 17, // 17: pubapi.API.TriggerCoordinatorUpdate:input_type -> pubapi.TriggerCoordinatorUpdateRequest
23, // 18: pubapi.API.GetVPNPeers:input_type -> pubapi.GetVPNPeersRequest 21, // 18: pubapi.API.GetPeerVPNPublicKey:input_type -> pubapi.GetPeerVPNPublicKeyRequest
19, // 19: pubapi.API.RequestStateDiskKey:input_type -> pubapi.RequestStateDiskKeyRequest 23, // 19: pubapi.API.GetVPNPeers:input_type -> pubapi.GetVPNPeersRequest
1, // 20: pubapi.API.GetState:output_type -> pubapi.GetStateResponse 19, // 20: pubapi.API.RequestStateDiskKey:input_type -> pubapi.RequestStateDiskKeyRequest
3, // 21: pubapi.API.ActivateAsCoordinator:output_type -> pubapi.ActivateAsCoordinatorResponse 1, // 21: pubapi.API.GetState:output_type -> pubapi.GetStateResponse
6, // 22: pubapi.API.ActivateAsNode:output_type -> pubapi.ActivateAsNodeResponse 3, // 22: pubapi.API.ActivateAsCoordinator:output_type -> pubapi.ActivateAsCoordinatorResponse
8, // 23: pubapi.API.ActivateAdditionalNodes:output_type -> pubapi.ActivateAdditionalNodesResponse 6, // 23: pubapi.API.ActivateAsNode:output_type -> pubapi.ActivateAsNodeResponse
10, // 24: pubapi.API.ActivateAsAdditionalCoordinator:output_type -> pubapi.ActivateAsAdditionalCoordinatorResponse 8, // 24: pubapi.API.ActivateAdditionalNodes:output_type -> pubapi.ActivateAdditionalNodesResponse
12, // 25: pubapi.API.ActivateAdditionalCoordinator:output_type -> pubapi.ActivateAdditionalCoordinatorResponse 10, // 25: pubapi.API.ActivateAsAdditionalCoordinator:output_type -> pubapi.ActivateAsAdditionalCoordinatorResponse
14, // 26: pubapi.API.JoinCluster:output_type -> pubapi.JoinClusterResponse 12, // 26: pubapi.API.ActivateAdditionalCoordinator:output_type -> pubapi.ActivateAdditionalCoordinatorResponse
16, // 27: pubapi.API.TriggerNodeUpdate:output_type -> pubapi.TriggerNodeUpdateResponse 14, // 27: pubapi.API.JoinCluster:output_type -> pubapi.JoinClusterResponse
18, // 28: pubapi.API.TriggerCoordinatorUpdate:output_type -> pubapi.TriggerCoordinatorUpdateResponse 16, // 28: pubapi.API.TriggerNodeUpdate:output_type -> pubapi.TriggerNodeUpdateResponse
22, // 29: pubapi.API.GetPeerVPNPublicKey:output_type -> pubapi.GetPeerVPNPublicKeyResponse 18, // 29: pubapi.API.TriggerCoordinatorUpdate:output_type -> pubapi.TriggerCoordinatorUpdateResponse
24, // 30: pubapi.API.GetVPNPeers:output_type -> pubapi.GetVPNPeersResponse 22, // 30: pubapi.API.GetPeerVPNPublicKey:output_type -> pubapi.GetPeerVPNPublicKeyResponse
20, // 31: pubapi.API.RequestStateDiskKey:output_type -> pubapi.RequestStateDiskKeyResponse 24, // 31: pubapi.API.GetVPNPeers:output_type -> pubapi.GetVPNPeersResponse
20, // [20:32] is the sub-list for method output_type 20, // 32: pubapi.API.RequestStateDiskKey:output_type -> pubapi.RequestStateDiskKeyResponse
8, // [8:20] is the sub-list for method input_type 21, // [21:33] is the sub-list for method output_type
8, // [8:8] is the sub-list for extension type_name 9, // [9:21] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension extendee 9, // [9:9] is the sub-list for extension type_name
0, // [0:8] is the sub-list for field type_name 9, // [9:9] is the sub-list for extension extendee
0, // [0:9] is the sub-list for field type_name
} }
func init() { file_pubapi_proto_init() } func init() { file_pubapi_proto_init() }
@ -2187,6 +2260,18 @@ func file_pubapi_proto_init() {
return nil return nil
} }
} }
file_pubapi_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SSHUserKey); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
} }
file_pubapi_proto_msgTypes[3].OneofWrappers = []interface{}{ file_pubapi_proto_msgTypes[3].OneofWrappers = []interface{}{
(*ActivateAsCoordinatorResponse_AdminConfig)(nil), (*ActivateAsCoordinatorResponse_AdminConfig)(nil),
@ -2206,7 +2291,7 @@ func file_pubapi_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pubapi_proto_rawDesc, RawDescriptor: file_pubapi_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 28, NumMessages: 29,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

View File

@ -37,6 +37,7 @@ message ActivateAsCoordinatorRequest {
string key_encryption_key_id = 8; string key_encryption_key_id = 8;
bool use_existing_kek = 9; bool use_existing_kek = 9;
string cloud_service_account_uri = 10; string cloud_service_account_uri = 10;
repeated SSHUserKey ssh_user_keys = 11;
} }
message ActivateAsCoordinatorResponse { message ActivateAsCoordinatorResponse {
@ -152,3 +153,8 @@ message Peer {
bytes vpn_pub_key = 3; bytes vpn_pub_key = 3;
uint32 role = 4; uint32 role = 4;
} }
message SSHUserKey {
string username = 1;
string public_key = 2;
}

View File

@ -8,16 +8,16 @@ import (
"log" "log"
"net" "net"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/debugd/cdbg/config" "github.com/edgelesssys/constellation/debugd/cdbg/config"
"github.com/edgelesssys/constellation/debugd/cdbg/state" "github.com/edgelesssys/constellation/debugd/cdbg/state"
"github.com/edgelesssys/constellation/debugd/coordinator" "github.com/edgelesssys/constellation/debugd/coordinator"
"github.com/edgelesssys/constellation/debugd/debugd" "github.com/edgelesssys/constellation/debugd/debugd"
depl "github.com/edgelesssys/constellation/debugd/debugd/deploy" depl "github.com/edgelesssys/constellation/debugd/debugd/deploy"
pb "github.com/edgelesssys/constellation/debugd/service" pb "github.com/edgelesssys/constellation/debugd/service"
"github.com/edgelesssys/constellation/debugd/ssh"
configc "github.com/edgelesssys/constellation/internal/config" configc "github.com/edgelesssys/constellation/internal/config"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/deploy/ssh"
"github.com/edgelesssys/constellation/internal/file"
statec "github.com/edgelesssys/constellation/internal/state" statec "github.com/edgelesssys/constellation/internal/state"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -105,7 +105,7 @@ type deployOnEndpointInput struct {
debugdEndpoint string debugdEndpoint string
coordinatorPath string coordinatorPath string
reader fileToStreamReader reader fileToStreamReader
authorizedKeys []ssh.SSHKey authorizedKeys []ssh.UserKey
systemdUnits []depl.SystemdUnit systemdUnits []depl.SystemdUnit
} }
@ -126,7 +126,7 @@ func deployOnEndpoint(ctx context.Context, in deployOnEndpointInput) error {
for _, key := range in.authorizedKeys { for _, key := range in.authorizedKeys {
pbKeys = append(pbKeys, &pb.AuthorizedKey{ pbKeys = append(pbKeys, &pb.AuthorizedKey{
Username: key.Username, Username: key.Username,
KeyValue: key.KeyValue, KeyValue: key.PublicKey,
}) })
} }
authorizedKeysResponse, err := client.UploadAuthorizedKeys(ctx, &pb.UploadAuthorizedKeysRequest{Keys: pbKeys}, grpc.WaitForReady(true)) authorizedKeysResponse, err := client.UploadAuthorizedKeys(ctx, &pb.UploadAuthorizedKeysRequest{Keys: pbKeys}, grpc.WaitForReady(true))

View File

@ -5,9 +5,9 @@ import (
"fmt" "fmt"
"io/fs" "io/fs"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/debugd/debugd/deploy" "github.com/edgelesssys/constellation/debugd/debugd/deploy"
"github.com/edgelesssys/constellation/debugd/ssh" "github.com/edgelesssys/constellation/internal/deploy/ssh"
"github.com/edgelesssys/constellation/internal/file"
) )
// CDBGConfig describes the constellation-cli config file. // CDBGConfig describes the constellation-cli config file.
@ -17,7 +17,7 @@ type CDBGConfig struct {
// ConstellationDebugdConfig is the cdbg specific configuration. // ConstellationDebugdConfig is the cdbg specific configuration.
type ConstellationDebugdConfig struct { type ConstellationDebugdConfig struct {
AuthorizedKeys []ssh.SSHKey `yaml:"authorizedKeys"` AuthorizedKeys []ssh.UserKey `yaml:"authorizedKeys"`
CoordinatorPath string `yaml:"coordinatorPath"` CoordinatorPath string `yaml:"coordinatorPath"`
SystemdUnits []deploy.SystemdUnit `yaml:"systemdUnits,omitempty"` SystemdUnits []deploy.SystemdUnit `yaml:"systemdUnits,omitempty"`
} }

View File

@ -13,6 +13,8 @@ import (
"github.com/edgelesssys/constellation/debugd/debugd/metadata/cloudprovider" "github.com/edgelesssys/constellation/debugd/debugd/metadata/cloudprovider"
"github.com/edgelesssys/constellation/debugd/debugd/metadata/fallback" "github.com/edgelesssys/constellation/debugd/debugd/metadata/fallback"
"github.com/edgelesssys/constellation/debugd/debugd/server" "github.com/edgelesssys/constellation/debugd/debugd/server"
"github.com/edgelesssys/constellation/internal/deploy/ssh"
"github.com/edgelesssys/constellation/internal/deploy/user"
"github.com/spf13/afero" "github.com/spf13/afero"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
@ -20,9 +22,10 @@ import (
func main() { func main() {
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
streamer := coordinator.NewFileStreamer(afero.NewOsFs()) fs := afero.NewOsFs()
streamer := coordinator.NewFileStreamer(fs)
serviceManager := deploy.NewServiceManager() serviceManager := deploy.NewServiceManager()
ssh := deploy.NewSSHAccess(afero.NewOsFs()) ssh := ssh.NewSSHAccess(user.NewLinuxUserManager(fs))
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()

View File

@ -1,84 +0,0 @@
package deploy
import (
"context"
"errors"
"fmt"
"strconv"
"github.com/edgelesssys/constellation/debugd/debugd/deploy/createuser"
"github.com/edgelesssys/constellation/debugd/debugd/deploy/passwd"
"github.com/spf13/afero"
)
// ErrUserDoesNotExist is returned by GetLinuxUser if a linux user does not exist yet.
var ErrUserDoesNotExist = errors.New("user does not exist")
type passwdParser interface {
Parse(fs afero.Fs) (passwd.Entries, error)
}
type userCreator interface {
CreateUser(ctx context.Context, username string) error
}
// LinuxUser holds relevant information about a linux user (subset of /etc/passwd).
type LinuxUser struct {
Username string
Home string
Uid int
Gid int
}
// LinuxUserManager can retrieve information on linux users and create new users.
type LinuxUserManager struct {
fs afero.Fs
passwd passwdParser
creator userCreator
}
// NewLinuxUserManager creates a new LinuxUserManager.
func NewLinuxUserManager(fs afero.Fs) *LinuxUserManager {
return &LinuxUserManager{
fs: fs,
passwd: passwd.Passwd{},
creator: createuser.Unix{},
}
}
// getLinuxUser tries to find an existing linux user in /etc/passwd.
func (l *LinuxUserManager) getLinuxUser(username string) (LinuxUser, error) {
entries, err := l.passwd.Parse(l.fs)
if err != nil {
return LinuxUser{}, err
}
if _, ok := entries[username]; !ok {
return LinuxUser{}, ErrUserDoesNotExist
}
entry := entries[username]
uid, err := strconv.Atoi(entry.Uid)
if err != nil {
return LinuxUser{}, fmt.Errorf("failed to parse users uid: %w", err)
}
gid, err := strconv.Atoi(entry.Gid)
if err != nil {
return LinuxUser{}, fmt.Errorf("failed to parse users gid: %w", err)
}
return LinuxUser{
Username: username,
Home: entry.Home,
Uid: uid,
Gid: gid,
}, nil
}
// EnsureLinuxUserExists will try to create the user specified by username and call GetLinuxUser to retrieve user information.
func (l *LinuxUserManager) EnsureLinuxUserExists(ctx context.Context, username string) (LinuxUser, error) {
// try to create user (even if it already exists)
if err := l.creator.CreateUser(ctx, username); err != nil {
return LinuxUser{}, err
}
return l.getLinuxUser(username)
}

View File

@ -7,7 +7,7 @@ import (
azurecloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/azure" azurecloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/azure"
gcpcloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/gcp" gcpcloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/gcp"
"github.com/edgelesssys/constellation/coordinator/core" "github.com/edgelesssys/constellation/coordinator/core"
"github.com/edgelesssys/constellation/debugd/ssh" "github.com/edgelesssys/constellation/internal/deploy/ssh"
) )
type providerMetadata interface { type providerMetadata interface {
@ -72,16 +72,16 @@ func (f *Fetcher) DiscoverDebugdIPs(ctx context.Context) ([]string, error) {
} }
// FetchSSHKeys will query the metadata of the current instance and deploys any SSH keys found. // FetchSSHKeys will query the metadata of the current instance and deploys any SSH keys found.
func (f *Fetcher) FetchSSHKeys(ctx context.Context) ([]ssh.SSHKey, error) { func (f *Fetcher) FetchSSHKeys(ctx context.Context) ([]ssh.UserKey, error) {
self, err := f.metaAPI.Self(ctx) self, err := f.metaAPI.Self(ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("retrieving ssh keys from cloud provider metadata failed: %w", err) return nil, fmt.Errorf("retrieving ssh keys from cloud provider metadata failed: %w", err)
} }
keys := []ssh.SSHKey{} keys := []ssh.UserKey{}
for username, userKeys := range self.SSHKeys { for username, userKeys := range self.SSHKeys {
for _, keyValue := range userKeys { for _, keyValue := range userKeys {
keys = append(keys, ssh.SSHKey{Username: username, KeyValue: keyValue}) keys = append(keys, ssh.UserKey{Username: username, PublicKey: keyValue})
} }
} }

View File

@ -6,7 +6,7 @@ import (
"testing" "testing"
"github.com/edgelesssys/constellation/coordinator/core" "github.com/edgelesssys/constellation/coordinator/core"
"github.com/edgelesssys/constellation/debugd/ssh" "github.com/edgelesssys/constellation/internal/deploy/ssh"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -70,7 +70,7 @@ func TestFetchSSHKeys(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
meta stubMetadata meta stubMetadata
wantKeys []ssh.SSHKey wantKeys []ssh.UserKey
wantErr bool wantErr bool
}{ }{
"fetch works": { "fetch works": {
@ -81,10 +81,10 @@ func TestFetchSSHKeys(t *testing.T) {
SSHKeys: map[string][]string{"bob": {"ssh-rsa bobskey"}}, SSHKeys: map[string][]string{"bob": {"ssh-rsa bobskey"}},
}, },
}, },
wantKeys: []ssh.SSHKey{ wantKeys: []ssh.UserKey{
{ {
Username: "bob", Username: "bob",
KeyValue: "ssh-rsa bobskey", PublicKey: "ssh-rsa bobskey",
}, },
}, },
}, },

View File

@ -3,7 +3,7 @@ package fallback
import ( import (
"context" "context"
"github.com/edgelesssys/constellation/debugd/ssh" "github.com/edgelesssys/constellation/internal/deploy/ssh"
) )
// Fetcher implements metadata.Fetcher interface but does not actually fetch cloud provider metadata. // Fetcher implements metadata.Fetcher interface but does not actually fetch cloud provider metadata.
@ -14,7 +14,7 @@ func (f Fetcher) DiscoverDebugdIPs(ctx context.Context) ([]string, error) {
return nil, nil return nil, nil
} }
func (f Fetcher) FetchSSHKeys(ctx context.Context) ([]ssh.SSHKey, error) { func (f Fetcher) FetchSSHKeys(ctx context.Context) ([]ssh.UserKey, error) {
// Fallback fetcher does not try to fetch ssh keys // Fallback fetcher does not try to fetch ssh keys
return nil, nil return nil, nil
} }

View File

@ -9,13 +9,13 @@ import (
"time" "time"
"github.com/edgelesssys/constellation/debugd/debugd" "github.com/edgelesssys/constellation/debugd/debugd"
"github.com/edgelesssys/constellation/debugd/ssh" "github.com/edgelesssys/constellation/internal/deploy/ssh"
) )
// Fetcher retrieves other debugd IPs and SSH keys from cloud provider metadata. // Fetcher retrieves other debugd IPs and SSH keys from cloud provider metadata.
type Fetcher interface { type Fetcher interface {
DiscoverDebugdIPs(ctx context.Context) ([]string, error) DiscoverDebugdIPs(ctx context.Context) ([]string, error)
FetchSSHKeys(ctx context.Context) ([]ssh.SSHKey, error) FetchSSHKeys(ctx context.Context) ([]ssh.UserKey, error)
} }
// Scheduler schedules fetching of metadata using timers. // Scheduler schedules fetching of metadata using timers.
@ -122,7 +122,7 @@ func (s *Scheduler) downloadCoordinator(ctx context.Context, ips []string) (succ
} }
// deploySSHKeys tries to deploy a list of SSH keys and logs errors encountered. // deploySSHKeys tries to deploy a list of SSH keys and logs errors encountered.
func (s *Scheduler) deploySSHKeys(ctx context.Context, keys []ssh.SSHKey) { func (s *Scheduler) deploySSHKeys(ctx context.Context, keys []ssh.UserKey) {
for _, key := range keys { for _, key := range keys {
err := s.ssh.DeploySSHAuthorizedKey(ctx, key) err := s.ssh.DeploySSHAuthorizedKey(ctx, key)
if err != nil { if err != nil {
@ -137,5 +137,5 @@ type downloader interface {
} }
type sshDeployer interface { type sshDeployer interface {
DeploySSHAuthorizedKey(ctx context.Context, sshKey ssh.SSHKey) error DeploySSHAuthorizedKey(ctx context.Context, sshKey ssh.UserKey) error
} }

View File

@ -7,7 +7,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/edgelesssys/constellation/debugd/ssh" "github.com/edgelesssys/constellation/internal/deploy/ssh"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -17,23 +17,23 @@ func TestSchedulerStart(t *testing.T) {
ssh stubSSHDeployer ssh stubSSHDeployer
downloader stubDownloader downloader stubDownloader
timeout time.Duration timeout time.Duration
wantSSHKeys []ssh.SSHKey wantSSHKeys []ssh.UserKey
wantDebugdDownloads []string wantDebugdDownloads []string
}{ }{
"scheduler works and calls fetcher functions at least once": {}, "scheduler works and calls fetcher functions at least once": {},
"ssh keys are fetched": { "ssh keys are fetched": {
fetcher: stubFetcher{ fetcher: stubFetcher{
keys: []ssh.SSHKey{ keys: []ssh.UserKey{
{ {
Username: "test", Username: "test",
KeyValue: "testkey", PublicKey: "testkey",
}, },
}, },
}, },
wantSSHKeys: []ssh.SSHKey{ wantSSHKeys: []ssh.UserKey{
{ {
Username: "test", Username: "test",
KeyValue: "testkey", PublicKey: "testkey",
}, },
}, },
}, },
@ -95,7 +95,7 @@ type stubFetcher struct {
fetchSSHKeysCalls int fetchSSHKeysCalls int
ips []string ips []string
keys []ssh.SSHKey keys []ssh.UserKey
discoverErr error discoverErr error
fetchSSHKeysErr error fetchSSHKeysErr error
} }
@ -105,18 +105,18 @@ func (s *stubFetcher) DiscoverDebugdIPs(ctx context.Context) ([]string, error) {
return s.ips, s.discoverErr return s.ips, s.discoverErr
} }
func (s *stubFetcher) FetchSSHKeys(ctx context.Context) ([]ssh.SSHKey, error) { func (s *stubFetcher) FetchSSHKeys(ctx context.Context) ([]ssh.UserKey, error) {
s.fetchSSHKeysCalls++ s.fetchSSHKeysCalls++
return s.keys, s.fetchSSHKeysErr return s.keys, s.fetchSSHKeysErr
} }
type stubSSHDeployer struct { type stubSSHDeployer struct {
sshKeys []ssh.SSHKey sshKeys []ssh.UserKey
deployErr error deployErr error
} }
func (s *stubSSHDeployer) DeploySSHAuthorizedKey(ctx context.Context, sshKey ssh.SSHKey) error { func (s *stubSSHDeployer) DeploySSHAuthorizedKey(ctx context.Context, sshKey ssh.UserKey) error {
s.sshKeys = append(s.sshKeys, sshKey) s.sshKeys = append(s.sshKeys, sshKey)
return s.deployErr return s.deployErr

View File

@ -13,7 +13,7 @@ import (
"github.com/edgelesssys/constellation/debugd/debugd" "github.com/edgelesssys/constellation/debugd/debugd"
"github.com/edgelesssys/constellation/debugd/debugd/deploy" "github.com/edgelesssys/constellation/debugd/debugd/deploy"
pb "github.com/edgelesssys/constellation/debugd/service" pb "github.com/edgelesssys/constellation/debugd/service"
"github.com/edgelesssys/constellation/debugd/ssh" "github.com/edgelesssys/constellation/internal/deploy/ssh"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
@ -37,7 +37,7 @@ func New(ssh sshDeployer, serviceManager serviceManager, streamer streamer) pb.D
func (s *debugdServer) UploadAuthorizedKeys(ctx context.Context, in *pb.UploadAuthorizedKeysRequest) (*pb.UploadAuthorizedKeysResponse, error) { func (s *debugdServer) UploadAuthorizedKeys(ctx context.Context, in *pb.UploadAuthorizedKeysRequest) (*pb.UploadAuthorizedKeysResponse, error) {
log.Println("Uploading authorized keys") log.Println("Uploading authorized keys")
for _, key := range in.Keys { for _, key := range in.Keys {
if err := s.ssh.DeploySSHAuthorizedKey(ctx, ssh.SSHKey{Username: key.Username, KeyValue: key.KeyValue}); err != nil { if err := s.ssh.DeploySSHAuthorizedKey(ctx, ssh.UserKey{Username: key.Username, PublicKey: key.KeyValue}); err != nil {
log.Printf("Uploading authorized keys failed: %v\n", err) log.Printf("Uploading authorized keys failed: %v\n", err)
return &pb.UploadAuthorizedKeysResponse{ return &pb.UploadAuthorizedKeysResponse{
Status: pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_FAILURE, Status: pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_FAILURE,
@ -117,7 +117,7 @@ func Start(wg *sync.WaitGroup, serv pb.DebugdServer) {
} }
type sshDeployer interface { type sshDeployer interface {
DeploySSHAuthorizedKey(ctx context.Context, sshKey ssh.SSHKey) error DeploySSHAuthorizedKey(ctx context.Context, sshKey ssh.UserKey) error
} }
type serviceManager interface { type serviceManager interface {

View File

@ -12,7 +12,7 @@ import (
"github.com/edgelesssys/constellation/debugd/coordinator" "github.com/edgelesssys/constellation/debugd/coordinator"
"github.com/edgelesssys/constellation/debugd/debugd/deploy" "github.com/edgelesssys/constellation/debugd/debugd/deploy"
pb "github.com/edgelesssys/constellation/debugd/service" pb "github.com/edgelesssys/constellation/debugd/service"
"github.com/edgelesssys/constellation/debugd/ssh" "github.com/edgelesssys/constellation/internal/deploy/ssh"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/grpc" "google.golang.org/grpc"
@ -28,7 +28,7 @@ func TestUploadAuthorizedKeys(t *testing.T) {
request *pb.UploadAuthorizedKeysRequest request *pb.UploadAuthorizedKeysRequest
wantErr bool wantErr bool
wantResponseStatus pb.UploadAuthorizedKeysStatus wantResponseStatus pb.UploadAuthorizedKeysStatus
wantKeys []ssh.SSHKey wantKeys []ssh.UserKey
}{ }{
"upload authorized keys works": { "upload authorized keys works": {
request: &pb.UploadAuthorizedKeysRequest{ request: &pb.UploadAuthorizedKeysRequest{
@ -40,10 +40,10 @@ func TestUploadAuthorizedKeys(t *testing.T) {
}, },
}, },
wantResponseStatus: pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_SUCCESS, wantResponseStatus: pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_SUCCESS,
wantKeys: []ssh.SSHKey{ wantKeys: []ssh.UserKey{
{ {
Username: "testuser", Username: "testuser",
KeyValue: "teskey", PublicKey: "teskey",
}, },
}, },
}, },
@ -58,10 +58,10 @@ func TestUploadAuthorizedKeys(t *testing.T) {
}, },
ssh: stubSSHDeployer{deployErr: errors.New("ssh key deployment error")}, ssh: stubSSHDeployer{deployErr: errors.New("ssh key deployment error")},
wantResponseStatus: pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_FAILURE, wantResponseStatus: pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_FAILURE,
wantKeys: []ssh.SSHKey{ wantKeys: []ssh.UserKey{
{ {
Username: "testuser", Username: "testuser",
KeyValue: "teskey", PublicKey: "teskey",
}, },
}, },
}, },
@ -323,12 +323,12 @@ func TestUploadSystemServiceUnits(t *testing.T) {
} }
type stubSSHDeployer struct { type stubSSHDeployer struct {
sshKeys []ssh.SSHKey sshKeys []ssh.UserKey
deployErr error deployErr error
} }
func (s *stubSSHDeployer) DeploySSHAuthorizedKey(ctx context.Context, sshKey ssh.SSHKey) error { func (s *stubSSHDeployer) DeploySSHAuthorizedKey(ctx context.Context, sshKey ssh.UserKey) error {
s.sshKeys = append(s.sshKeys, sshKey) s.sshKeys = append(s.sshKeys, sshKey)
return s.deployErr return s.deployErr

View File

@ -1,7 +0,0 @@
package ssh
// SSHKey describes a public ssh key.
type SSHKey struct {
Username string `yaml:"user"`
KeyValue string `yaml:"pubkey"`
}

View File

@ -9,10 +9,11 @@ import (
"github.com/edgelesssys/constellation/cli/cloud/cloudtypes" "github.com/edgelesssys/constellation/cli/cloud/cloudtypes"
"github.com/edgelesssys/constellation/cli/ec2" "github.com/edgelesssys/constellation/cli/ec2"
awsClient "github.com/edgelesssys/constellation/cli/ec2/client" awsClient "github.com/edgelesssys/constellation/cli/ec2/client"
"github.com/edgelesssys/constellation/cli/file"
gcpClient "github.com/edgelesssys/constellation/cli/gcp/client" gcpClient "github.com/edgelesssys/constellation/cli/gcp/client"
"github.com/edgelesssys/constellation/coordinator/attestation/vtpm" "github.com/edgelesssys/constellation/coordinator/attestation/vtpm"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/deploy/ssh"
"github.com/edgelesssys/constellation/internal/file"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )
@ -46,6 +47,7 @@ type Config struct {
AutoscalingNodeGroupsMax *int `yaml:"autoscalingNodeGroupsMax,omitempty"` AutoscalingNodeGroupsMax *int `yaml:"autoscalingNodeGroupsMax,omitempty"`
StateDiskSizeGB *int `yaml:"StateDisksizeGB,omitempty"` StateDiskSizeGB *int `yaml:"StateDisksizeGB,omitempty"`
Provider *ProviderConfig `yaml:"provider,omitempty"` Provider *ProviderConfig `yaml:"provider,omitempty"`
SSHUsers []*ssh.UserKey `yaml:"sshUsers,omitempty"`
} }
// Default returns a struct with the default config. // Default returns a struct with the default config.

View File

@ -4,9 +4,9 @@ import (
"testing" "testing"
"github.com/edgelesssys/constellation/cli/cloud/cloudtypes" "github.com/edgelesssys/constellation/cli/cloud/cloudtypes"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/cli/gcp/client" "github.com/edgelesssys/constellation/cli/gcp/client"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"

View File

@ -0,0 +1,44 @@
package ssh
import (
"github.com/edgelesssys/constellation/coordinator/pubapi/pubproto"
)
// FromProtoSlice converts a SSH UserKey definition from pubproto to the Go flavor.
func FromProtoSlice(input []*pubproto.SSHUserKey) []UserKey {
if input == nil {
return nil
}
output := make([]UserKey, 0)
for _, pair := range input {
singlePair := UserKey{
Username: pair.Username,
PublicKey: pair.PublicKey,
}
output = append(output, singlePair)
}
return output
}
// ToProtoSlice converts a SSH UserKey definition from Go to pubproto flavor.
func ToProtoSlice(input []*UserKey) []*pubproto.SSHUserKey {
if input == nil {
return nil
}
output := make([]*pubproto.SSHUserKey, 0)
for _, pair := range input {
singlePair := pubproto.SSHUserKey{
Username: pair.Username,
PublicKey: pair.PublicKey,
}
output = append(output, &singlePair)
}
return output
}

View File

@ -0,0 +1,36 @@
package ssh
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestToAndFromProtoSlice(t *testing.T) {
assert := assert.New(t)
DemoSSHUser1 := UserKey{
Username: "test-user-2",
PublicKey: "ssh-rsa abcdefg",
}
DemoSSHUser2 := UserKey{
Username: "test-user-2",
PublicKey: "ssh-rsa hijklmnop",
}
// Input usually consists of pointers (from config parsing)
DemoSSHUsersPointers := make([]*UserKey, 0)
DemoSSHUsersPointers = append(DemoSSHUsersPointers, &DemoSSHUser1)
DemoSSHUsersPointers = append(DemoSSHUsersPointers, &DemoSSHUser2)
// Expected output usually does not consist of pointers
DemoSSHUsersNoPointers := make([]UserKey, 0)
DemoSSHUsersNoPointers = append(DemoSSHUsersNoPointers, DemoSSHUser1)
DemoSSHUsersNoPointers = append(DemoSSHUsersNoPointers, DemoSSHUser2)
ToProtoArray := ToProtoSlice(DemoSSHUsersPointers)
FromProtoArray := FromProtoSlice(ToProtoArray)
assert.Equal(DemoSSHUsersNoPointers, FromProtoArray)
}

View File

@ -1,4 +1,4 @@
package deploy package ssh
import ( import (
"context" "context"
@ -7,46 +7,43 @@ import (
"os" "os"
"sync" "sync"
"github.com/edgelesssys/constellation/debugd/debugd/deploy/createuser" "github.com/edgelesssys/constellation/internal/deploy/user"
"github.com/edgelesssys/constellation/debugd/debugd/deploy/passwd"
"github.com/edgelesssys/constellation/debugd/ssh"
"github.com/spf13/afero"
) )
// UserKey describes an user that should be created with a corresponding public SSH key.
type UserKey struct {
Username string `yaml:"user"`
PublicKey string `yaml:"pubkey"`
}
// SSHAccess reads ssh public keys from a channel, creates the specified users if required and writes the public keys to the users authorized_keys file. // SSHAccess reads ssh public keys from a channel, creates the specified users if required and writes the public keys to the users authorized_keys file.
type SSHAccess struct { type SSHAccess struct {
fs afero.Fs userManager user.LinuxUserManager
userManager LinuxUserManager
authorized map[string]bool authorized map[string]bool
mux sync.Mutex mux sync.Mutex
} }
// NewSSHAccess creates a new SSHAccess. // NewSSHAccess creates a new SSHAccess.
func NewSSHAccess(fs afero.Fs) *SSHAccess { func NewSSHAccess(userManager user.LinuxUserManager) *SSHAccess {
return &SSHAccess{ return &SSHAccess{
fs: fs, userManager: userManager,
userManager: LinuxUserManager{ mux: sync.Mutex{},
fs: fs, authorized: map[string]bool{},
passwd: passwd.Passwd{},
creator: createuser.Unix{},
},
mux: sync.Mutex{},
authorized: map[string]bool{},
} }
} }
// alreadyAuthorized checks if key was written to authorized keys before. // alreadyAuthorized checks if key was written to authorized keys before.
func (s *SSHAccess) alreadyAuthorized(sshKey ssh.SSHKey) bool { func (s *SSHAccess) alreadyAuthorized(sshKey UserKey) bool {
_, ok := s.authorized[fmt.Sprintf("%s:%s", sshKey.Username, sshKey.KeyValue)] _, ok := s.authorized[fmt.Sprintf("%s:%s", sshKey.Username, sshKey.PublicKey)]
return ok return ok
} }
// rememberAuthorized marks this key as already written to authorized keys.. // rememberAuthorized marks this key as already written to authorized keys..
func (s *SSHAccess) rememberAuthorized(sshKey ssh.SSHKey) { func (s *SSHAccess) rememberAuthorized(sshKey UserKey) {
s.authorized[fmt.Sprintf("%s:%s", sshKey.Username, sshKey.KeyValue)] = true s.authorized[fmt.Sprintf("%s:%s", sshKey.Username, sshKey.PublicKey)] = true
} }
func (s *SSHAccess) DeploySSHAuthorizedKey(ctx context.Context, sshKey ssh.SSHKey) error { func (s *SSHAccess) DeploySSHAuthorizedKey(ctx context.Context, sshKey UserKey) error {
// allow only one thread to write to authorized keys, create users and update the authorized map at a time // allow only one thread to write to authorized keys, create users and update the authorized map at a time
s.mux.Lock() s.mux.Lock()
defer s.mux.Unlock() defer s.mux.Unlock()
@ -61,31 +58,31 @@ func (s *SSHAccess) DeploySSHAuthorizedKey(ctx context.Context, sshKey ssh.SSHKe
// CoreOS uses https://github.com/coreos/ssh-key-dir to search for ssh keys in ~/.ssh/authorized_keys.d/* // CoreOS uses https://github.com/coreos/ssh-key-dir to search for ssh keys in ~/.ssh/authorized_keys.d/*
sshFolder := fmt.Sprintf("%s/.ssh", user.Home) sshFolder := fmt.Sprintf("%s/.ssh", user.Home)
authorized_keys_d := fmt.Sprintf("%s/authorized_keys.d", sshFolder) authorized_keys_d := fmt.Sprintf("%s/authorized_keys.d", sshFolder)
if err := s.fs.MkdirAll(authorized_keys_d, 0o700); err != nil { if err := s.userManager.Fs.MkdirAll(authorized_keys_d, 0o700); err != nil {
return err return err
} }
if err := s.fs.Chown(sshFolder, user.Uid, user.Gid); err != nil { if err := s.userManager.Fs.Chown(sshFolder, user.Uid, user.Gid); err != nil {
return err return err
} }
if err := s.fs.Chown(authorized_keys_d, user.Uid, user.Gid); err != nil { if err := s.userManager.Fs.Chown(authorized_keys_d, user.Uid, user.Gid); err != nil {
return err return err
} }
authorizedKeysPath := fmt.Sprintf("%s/debugd", authorized_keys_d) authorizedKeysPath := fmt.Sprintf("%s/ssh-keys", authorized_keys_d)
authorizedKeysFile, err := s.fs.OpenFile(authorizedKeysPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) authorizedKeysFile, err := s.userManager.Fs.OpenFile(authorizedKeysPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil { if err != nil {
return err return err
} }
_, err = authorizedKeysFile.WriteString(fmt.Sprintf("%s %s\n", sshKey.KeyValue, sshKey.Username)) _, err = authorizedKeysFile.WriteString(fmt.Sprintf("%s %s\n", sshKey.PublicKey, sshKey.Username))
if err != nil { if err != nil {
return err return err
} }
if err := authorizedKeysFile.Close(); err != nil { if err := authorizedKeysFile.Close(); err != nil {
return err return err
} }
if err := s.fs.Chown(authorizedKeysPath, user.Uid, user.Gid); err != nil { if err := s.userManager.Fs.Chown(authorizedKeysPath, user.Uid, user.Gid); err != nil {
return err return err
} }
if err := s.fs.Chmod(authorizedKeysPath, 0o644); err != nil { if err := s.userManager.Fs.Chmod(authorizedKeysPath, 0o644); err != nil {
return err return err
} }
s.rememberAuthorized(sshKey) s.rememberAuthorized(sshKey)

View File

@ -1,26 +1,24 @@
package deploy package ssh
import ( import (
"context" "context"
"sync" "sync"
"testing" "testing"
"github.com/edgelesssys/constellation/debugd/debugd/deploy/passwd" "github.com/edgelesssys/constellation/internal/deploy/user"
"github.com/edgelesssys/constellation/debugd/ssh"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestDeploySSHAuthorizedKey(t *testing.T) { func TestDeploySSHAuthorizedKey(t *testing.T) {
authorizedKey := ssh.SSHKey{ authorizedKey := UserKey{
Username: "user", Username: "user",
KeyValue: "ssh-rsa testkey", PublicKey: "ssh-rsa testkey",
} }
testCases := map[string]struct { testCases := map[string]struct {
fs afero.Fs fs afero.Fs
userCreator *stubUserCreator
passwdContents string passwdContents string
alreadyDeployed bool alreadyDeployed bool
readonly bool readonly bool
@ -30,40 +28,26 @@ func TestDeploySSHAuthorizedKey(t *testing.T) {
}{ }{
"deploy works": { "deploy works": {
fs: afero.NewMemMapFs(), fs: afero.NewMemMapFs(),
userCreator: &stubUserCreator{},
passwdContents: "user:x:1000:1000:user:/home/user:/bin/bash\n",
wantErr: false, wantErr: false,
wantFile: true, wantFile: true,
wantFileContents: "ssh-rsa testkey user\n", wantFileContents: "ssh-rsa testkey user\n",
}, },
"appending ssh key works": { "appending ssh key works": {
fs: memMapFsWithFile("/home/user/.ssh/authorized_keys.d/debugd", "ssh-rsa preexistingkey user\n"), fs: memMapFsWithFile("/home/user/.ssh/authorized_keys.d/ssh-keys", "ssh-rsa preexistingkey user\n"),
userCreator: &stubUserCreator{},
passwdContents: "user:x:1000:1000:user:/home/user:/bin/bash\n",
wantErr: false, wantErr: false,
wantFile: true, wantFile: true,
wantFileContents: "ssh-rsa preexistingkey user\nssh-rsa testkey user\n", wantFileContents: "ssh-rsa preexistingkey user\nssh-rsa testkey user\n",
}, },
"redeployment avoided": { "redeployment avoided": {
fs: afero.NewMemMapFs(), fs: afero.NewMemMapFs(),
userCreator: &stubUserCreator{},
passwdContents: "user:x:1000:1000:user:/home/user:/bin/bash\n",
wantErr: false, wantErr: false,
alreadyDeployed: true, alreadyDeployed: true,
wantFile: false, wantFile: false,
}, },
"user does not exist": {
fs: afero.NewMemMapFs(),
userCreator: &stubUserCreator{},
passwdContents: "",
wantErr: true,
},
"readonly fs": { "readonly fs": {
fs: afero.NewMemMapFs(), fs: afero.NewMemMapFs(),
userCreator: &stubUserCreator{}, readonly: true,
passwdContents: "user:x:1000:1000:user:/home/user:/bin/bash\n", wantErr: true,
readonly: true,
wantErr: true,
}, },
} }
@ -71,24 +55,20 @@ func TestDeploySSHAuthorizedKey(t *testing.T) {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
require := require.New(t) require := require.New(t)
userManager := user.NewLinuxUserManagerFake(tc.fs)
assert.NoError(afero.WriteFile(tc.fs, "/etc/passwd", []byte(tc.passwdContents), 0o755)) assert.NoError(afero.WriteFile(userManager.Fs, "/etc/passwd", []byte(tc.passwdContents), 0o755))
if tc.readonly { if tc.readonly {
tc.fs = afero.NewReadOnlyFs(tc.fs) userManager.Fs = afero.NewReadOnlyFs(userManager.Fs)
} }
authorized := map[string]bool{} authorized := map[string]bool{}
if tc.alreadyDeployed { if tc.alreadyDeployed {
authorized["user:ssh-rsa testkey"] = true authorized["user:ssh-rsa testkey"] = true
} }
sshAccess := SSHAccess{ sshAccess := SSHAccess{
fs: tc.fs, userManager: userManager,
userManager: LinuxUserManager{ mux: sync.Mutex{},
fs: tc.fs, authorized: authorized,
passwd: passwd.Passwd{},
creator: tc.userCreator,
},
mux: sync.Mutex{},
authorized: authorized,
} }
err := sshAccess.DeploySSHAuthorizedKey(context.Background(), authorizedKey) err := sshAccess.DeploySSHAuthorizedKey(context.Background(), authorizedKey)
@ -98,11 +78,11 @@ func TestDeploySSHAuthorizedKey(t *testing.T) {
} }
require.NoError(err) require.NoError(err)
if tc.wantFile { if tc.wantFile {
fileContents, err := afero.ReadFile(tc.fs, "/home/user/.ssh/authorized_keys.d/debugd") fileContents, err := afero.ReadFile(userManager.Fs, "/home/user/.ssh/authorized_keys.d/ssh-keys")
assert.NoError(err) assert.NoError(err)
assert.Equal(tc.wantFileContents, string(fileContents)) assert.Equal(tc.wantFileContents, string(fileContents))
} else { } else {
exists, err := afero.Exists(tc.fs, "/home/user/.ssh/authorized_keys.d/debugd") exists, err := afero.Exists(userManager.Fs, "/home/user/.ssh/authorized_keys.d/ssh-keys")
assert.NoError(err) assert.NoError(err)
assert.False(exists) assert.False(exists)
} }

View File

@ -1,4 +1,4 @@
package createuser package user
import ( import (
"context" "context"

View File

@ -0,0 +1,147 @@
package user
import (
"context"
"errors"
"fmt"
"os"
"strconv"
"github.com/spf13/afero"
)
// ErrUserDoesNotExist is returned by GetLinuxUser if a linux user does not exist yet.
var ErrUserDoesNotExist = errors.New("user does not exist")
type passwdParser interface {
Parse(fs afero.Fs) (Entries, error)
}
type userCreator interface {
CreateUser(ctx context.Context, username string) error
}
// LinuxUser holds relevant information about a linux user (subset of /etc/passwd).
type LinuxUser struct {
Username string
Home string
Uid int
Gid int
}
// LinuxUserManager can retrieve information on linux users and create new users.
type LinuxUserManager struct {
Fs afero.Fs
Passwd passwdParser
Creator userCreator
}
// NewLinuxUserManager creates a new LinuxUserManager.
func NewLinuxUserManager(fs afero.Fs) LinuxUserManager {
return LinuxUserManager{
Fs: fs,
Passwd: Passwd{},
Creator: Unix{},
}
}
// NewLinuxUserManagerFake creates a new LinuxUserManager that is used for unit tests.
func NewLinuxUserManagerFake(fs afero.Fs) LinuxUserManager {
return LinuxUserManager{
Fs: fs,
Passwd: Passwd{},
Creator: &StubUserCreator{fs: fs},
}
}
// StubUserCreator is used for unit tests.
type StubUserCreator struct {
fs afero.Fs
usernames []string
createUserErr error
currentUID int
}
func (s *StubUserCreator) CreateUser(ctx context.Context, username string) error {
if stringInSlice(username, s.usernames) {
return errors.New("username already exists")
}
// We want created users to start at UID 1000
if s.currentUID == 0 {
s.currentUID = 1000
}
if s.createUserErr != nil {
return s.createUserErr
}
// If no predefined error is supposed to happen, increase the UID (unless the file system code fails)
if s.fs != nil {
lineToWrite := fmt.Sprintf("%s:x:%d:%d:%s:/home/%s:/bin/bash\n", username, s.currentUID, s.currentUID, username, username)
file, err := s.fs.OpenFile("/etc/passwd", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o644)
if err != nil {
return err
}
defer file.Close()
n, err := file.WriteString(lineToWrite)
if err != nil {
return err
} else if n != len(lineToWrite) {
return errors.New("written text too short")
}
}
s.currentUID += 1
s.usernames = append(s.usernames, username)
return nil
}
// getLinuxUser tries to find an existing linux user in /etc/passwd.
func (l *LinuxUserManager) getLinuxUser(username string) (LinuxUser, error) {
entries, err := l.Passwd.Parse(l.Fs)
if err != nil {
return LinuxUser{}, err
}
if _, ok := entries[username]; !ok {
return LinuxUser{}, ErrUserDoesNotExist
}
entry := entries[username]
uid, err := strconv.Atoi(entry.Uid)
if err != nil {
return LinuxUser{}, fmt.Errorf("failed to parse users uid: %w", err)
}
gid, err := strconv.Atoi(entry.Gid)
if err != nil {
return LinuxUser{}, fmt.Errorf("failed to parse users gid: %w", err)
}
return LinuxUser{
Username: username,
Home: entry.Home,
Uid: uid,
Gid: gid,
}, nil
}
// EnsureLinuxUserExists will try to create the user specified by username and call GetLinuxUser to retrieve user information.
func (l *LinuxUserManager) EnsureLinuxUserExists(ctx context.Context, username string) (LinuxUser, error) {
// try to create user (even if it already exists)
if err := l.Creator.CreateUser(ctx, username); err != nil {
return LinuxUser{}, err
}
return l.getLinuxUser(username)
}
// stringInSlice checks if a given string exists in a slice of strings.
func stringInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}

View File

@ -1,11 +1,10 @@
package deploy package user
import ( import (
"context" "context"
"errors" "errors"
"testing" "testing"
"github.com/edgelesssys/constellation/debugd/debugd/deploy/passwd"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -15,13 +14,11 @@ func TestGetLinuxUser(t *testing.T) {
username := "user" username := "user"
testCases := map[string]struct { testCases := map[string]struct {
userCreator *stubUserCreator
passwdContents string passwdContents string
wantErr bool wantErr bool
wantUser LinuxUser wantUser LinuxUser
}{ }{
"get works": { "get works": {
userCreator: &stubUserCreator{},
passwdContents: "user:x:1000:1000:user:/home/user:/bin/bash\n", passwdContents: "user:x:1000:1000:user:/home/user:/bin/bash\n",
wantErr: false, wantErr: false,
wantUser: LinuxUser{ wantUser: LinuxUser{
@ -32,22 +29,18 @@ func TestGetLinuxUser(t *testing.T) {
}, },
}, },
"user does not exist": { "user does not exist": {
userCreator: &stubUserCreator{},
passwdContents: "", passwdContents: "",
wantErr: true, wantErr: true,
}, },
"parse fails": { "parse fails": {
userCreator: &stubUserCreator{},
passwdContents: "invalid contents\n", passwdContents: "invalid contents\n",
wantErr: true, wantErr: true,
}, },
"invalid uid": { "invalid uid": {
userCreator: &stubUserCreator{},
passwdContents: "user:x:invalid:1000:user:/home/user:/bin/bash\n", passwdContents: "user:x:invalid:1000:user:/home/user:/bin/bash\n",
wantErr: true, wantErr: true,
}, },
"invalid gid": { "invalid gid": {
userCreator: &stubUserCreator{},
passwdContents: "user:x:1000:invalid:user:/home/user:/bin/bash\n", passwdContents: "user:x:1000:invalid:user:/home/user:/bin/bash\n",
wantErr: true, wantErr: true,
}, },
@ -60,11 +53,7 @@ func TestGetLinuxUser(t *testing.T) {
fs := afero.NewMemMapFs() fs := afero.NewMemMapFs()
assert.NoError(afero.WriteFile(fs, "/etc/passwd", []byte(tc.passwdContents), 0o755)) assert.NoError(afero.WriteFile(fs, "/etc/passwd", []byte(tc.passwdContents), 0o755))
manager := LinuxUserManager{ manager := NewLinuxUserManagerFake(fs)
fs: fs,
passwd: passwd.Passwd{},
creator: tc.userCreator,
}
user, err := manager.getLinuxUser(username) user, err := manager.getLinuxUser(username)
if tc.wantErr { if tc.wantErr {
@ -81,15 +70,13 @@ func TestEnsureLinuxUserExists(t *testing.T) {
username := "user" username := "user"
testCases := map[string]struct { testCases := map[string]struct {
userCreator *stubUserCreator userCreator *StubUserCreator
passwdContents string wantErr bool
wantErr bool wantUser LinuxUser
wantUser LinuxUser
}{ }{
"create works": { "create works": {
userCreator: &stubUserCreator{}, userCreator: &StubUserCreator{},
passwdContents: "user:x:1000:1000:user:/home/user:/bin/bash\n", wantErr: false,
wantErr: false,
wantUser: LinuxUser{ wantUser: LinuxUser{
Username: "user", Username: "user",
Home: "/home/user", Home: "/home/user",
@ -98,11 +85,10 @@ func TestEnsureLinuxUserExists(t *testing.T) {
}, },
}, },
"create fails": { "create fails": {
userCreator: &stubUserCreator{ userCreator: &StubUserCreator{
createUserErr: errors.New("create fails"), createUserErr: errors.New("create fails"),
}, },
passwdContents: "user:x:1000:1000:user:/home/user:/bin/bash\n", wantErr: true,
wantErr: true,
}, },
} }
@ -112,12 +98,9 @@ func TestEnsureLinuxUserExists(t *testing.T) {
require := require.New(t) require := require.New(t)
fs := afero.NewMemMapFs() fs := afero.NewMemMapFs()
assert.NoError(afero.WriteFile(fs, "/etc/passwd", []byte(tc.passwdContents), 0o755)) manager := NewLinuxUserManagerFake(fs)
manager := LinuxUserManager{ tc.userCreator.fs = fs
fs: fs, manager.Creator = tc.userCreator
passwd: passwd.Passwd{},
creator: tc.userCreator,
}
user, err := manager.EnsureLinuxUserExists(context.Background(), username) user, err := manager.EnsureLinuxUserExists(context.Background(), username)
if tc.wantErr { if tc.wantErr {
@ -131,12 +114,10 @@ func TestEnsureLinuxUserExists(t *testing.T) {
} }
} }
type stubUserCreator struct { func TestStringInSlice(t *testing.T) {
usernames []string assert := assert.New(t)
createUserErr error testSlice := []string{"abc", "efg", "xyz"}
}
func (s *stubUserCreator) CreateUser(ctx context.Context, username string) error { assert.True(stringInSlice("efg", testSlice))
s.usernames = append(s.usernames, username) assert.False(stringInSlice("hij", testSlice))
return s.createUserErr
} }

View File

@ -1,4 +1,4 @@
package passwd package user
import ( import (
"github.com/spf13/afero" "github.com/spf13/afero"

View File

@ -1,4 +1,4 @@
package passwd package user
import ( import (
"testing" "testing"

View File

@ -9,10 +9,10 @@ import (
"path/filepath" "path/filepath"
"syscall" "syscall"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/attestation/vtpm" "github.com/edgelesssys/constellation/coordinator/attestation/vtpm"
"github.com/edgelesssys/constellation/coordinator/config" "github.com/edgelesssys/constellation/coordinator/config"
"github.com/edgelesssys/constellation/coordinator/nodestate" "github.com/edgelesssys/constellation/coordinator/nodestate"
"github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero" "github.com/spf13/afero"
) )

View File

@ -7,10 +7,10 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/attestation/vtpm" "github.com/edgelesssys/constellation/coordinator/attestation/vtpm"
"github.com/edgelesssys/constellation/coordinator/config" "github.com/edgelesssys/constellation/coordinator/config"
"github.com/edgelesssys/constellation/coordinator/nodestate" "github.com/edgelesssys/constellation/coordinator/nodestate"
"github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"