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
import (
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/internal/config"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,6 +5,7 @@ import (
"github.com/edgelesssys/constellation/cli/proto"
"github.com/edgelesssys/constellation/coordinator/atls"
"github.com/edgelesssys/constellation/coordinator/pubapi/pubproto"
"github.com/edgelesssys/constellation/coordinator/state"
)
@ -12,5 +13,5 @@ type protoClient interface {
Connect(endpoint string, validators []atls.Validator) error
Close() 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/coordinator/atls"
"github.com/edgelesssys/constellation/coordinator/pubapi/pubproto"
"github.com/edgelesssys/constellation/coordinator/state"
)
@ -26,6 +27,7 @@ type stubProtoClient struct {
activateCoordinatorIPs []string
activateAutoscalingNodeGroups []string
cloudServiceAccountURI string
sshUserKeys []*pubproto.SSHUserKey
}
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
}
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.activateMasterSecret = masterSecret
c.activateNodeIPs = nodeIPs
c.activateCoordinatorIPs = coordinatorIPs
c.activateAutoscalingNodeGroups = autoscalingNodeGroups
c.cloudServiceAccountURI = cloudServiceAccountURI
c.sshUserKeys = sshUserKeys
return c.respClient, c.activateErr
}
@ -126,7 +129,7 @@ func (c *fakeProtoClient) GetState(_ context.Context) (state.State, error) {
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 {
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/cloudprovider"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/cli/proto"
"github.com/edgelesssys/constellation/coordinator/util"
"github.com/edgelesssys/constellation/internal/config"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/state"
"github.com/spf13/afero"
"github.com/spf13/cobra"

View File

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

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@ import (
"testing"
"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/cobra"
"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.
// 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.
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 {
return nil, errors.New("client is not connected")
}
@ -100,6 +100,7 @@ func (c *Client) Activate(ctx context.Context, userPublicKey, masterSecret []byt
KeyEncryptionKeyId: "",
UseExistingKek: false,
CloudServiceAccountUri: cloudServiceAccountURI,
SshUserKeys: sshUserKeys,
}
client, err := c.pubapi.ActivateAsCoordinator(ctx, req)

View File

@ -163,7 +163,7 @@ func TestActivate(t *testing.T) {
if tc.pubAPIClient != nil {
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 {
assert.Error(err)
} else {

View File

@ -9,7 +9,6 @@ import (
"os"
"strings"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/attestation/azure"
"github.com/edgelesssys/constellation/coordinator/attestation/gcp"
"github.com/edgelesssys/constellation/coordinator/attestation/qemu"
@ -27,6 +26,7 @@ import (
"github.com/edgelesssys/constellation/coordinator/util"
"github.com/edgelesssys/constellation/coordinator/util/grpcutil"
"github.com/edgelesssys/constellation/coordinator/wireguard"
"github.com/edgelesssys/constellation/internal/file"
grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
"github.com/spf13/afero"
"go.uber.org/zap"
@ -174,5 +174,5 @@ func main() {
netDialer := &net.Dialer{}
dialer := grpcutil.NewDialer(validator, netDialer)
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"
"sync"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/atls"
"github.com/edgelesssys/constellation/coordinator/attestation/vtpm"
"github.com/edgelesssys/constellation/coordinator/core"
@ -17,9 +16,12 @@ import (
"github.com/edgelesssys/constellation/coordinator/util/grpcutil"
"github.com/edgelesssys/constellation/coordinator/vpnapi"
"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_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags"
"github.com/spf13/afero"
"go.uber.org/zap"
"google.golang.org/grpc"
"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,
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()
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,
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 {
zapLoggerCore.Fatal("failed to create core", zap.Error(err))
}

View File

@ -8,7 +8,6 @@ import (
"sync"
"testing"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/atls"
"github.com/edgelesssys/constellation/coordinator/attestation/simulator"
"github.com/edgelesssys/constellation/coordinator/core"
@ -21,6 +20,8 @@ import (
"github.com/edgelesssys/constellation/coordinator/util/testdialer"
"github.com/edgelesssys/constellation/coordinator/vpnapi"
"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"
"github.com/spf13/afero"
"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) {
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(cor.AdvanceState(state.AcceptingInit, nil, nil))

View File

@ -6,11 +6,12 @@ import (
"testing"
"time"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/attestation/simulator"
"github.com/edgelesssys/constellation/coordinator/kubernetes"
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
"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/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -171,7 +172,8 @@ func TestInitCluster(t *testing.T) {
zapLogger, err := zap.NewDevelopment()
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)
kubeconfig, err := core.InitCluster(tc.autoscalingNodeGroups, "cloud-service-account-uri")
@ -286,7 +288,8 @@ func TestJoinCluster(t *testing.T) {
zapLogger, err := zap.NewDevelopment()
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)
joinReq := &kubeadm.BootstrapTokenDiscovery{

View File

@ -9,7 +9,6 @@ import (
"sync"
"time"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/attestation/vtpm"
"github.com/edgelesssys/constellation/coordinator/config"
"github.com/edgelesssys/constellation/coordinator/nodestate"
@ -18,6 +17,8 @@ import (
"github.com/edgelesssys/constellation/coordinator/store"
"github.com/edgelesssys/constellation/coordinator/storewrapper"
"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"
kmsSetup "github.com/edgelesssys/constellation/kms/server/setup"
"go.uber.org/zap"
@ -44,12 +45,13 @@ type Core struct {
initialVPNPeersRetriever initialVPNPeersRetriever
lastHeartbeats map[string]time.Time
fileHandler file.Handler
linuxUserManager user.LinuxUserManager
}
// NewCore creates and initializes a new Core object.
func NewCore(vpn VPN, kube Cluster,
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) {
stor := store.NewStdStore()
c := &Core{
@ -68,6 +70,7 @@ func NewCore(vpn VPN, kube Cluster,
initialVPNPeersRetriever: getInitialVPNPeers,
lastHeartbeats: make(map[string]time.Time),
fileHandler: fileHandler,
linuxUserManager: linuxUserManager,
}
if err := c.data().IncrementPeersResourceVersion(); err != nil {
return nil, err

View File

@ -6,7 +6,6 @@ import (
"net"
"testing"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/attestation/simulator"
"github.com/edgelesssys/constellation/coordinator/attestation/vtpm"
"github.com/edgelesssys/constellation/coordinator/nodestate"
@ -16,6 +15,8 @@ import (
"github.com/edgelesssys/constellation/coordinator/store"
"github.com/edgelesssys/constellation/coordinator/util/grpcutil"
"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"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
@ -36,7 +37,8 @@ func TestGetNextNodeIP(t *testing.T) {
assert := assert.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(core.InitializeStoreIPs())
@ -79,7 +81,8 @@ func TestSwitchToPersistentStore(t *testing.T) {
require := require.New(t)
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(err)
@ -93,7 +96,8 @@ func TestGetIDs(t *testing.T) {
assert := assert.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)
_, _, err = core.GetIDs(nil)
@ -117,7 +121,8 @@ func TestNotifyNodeHeartbeat(t *testing.T) {
assert := assert.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)
const ip = "192.0.2.1"
@ -130,7 +135,8 @@ func TestDeriveKey(t *testing.T) {
assert := assert.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)
// error when no kms is set up
@ -200,14 +206,15 @@ func TestInitialize(t *testing.T) {
if tc.initializePCRs {
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 {
require.NoError((&nodestate.NodeState{
Role: tc.role,
VPNPrivKey: []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7},
}).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)
core.initialVPNPeersRetriever = fakeInitializeVPNPeersRetriever
// prepare store to emulate initialized KMS
@ -265,7 +272,7 @@ func TestPersistNodeState(t *testing.T) {
require.NoError(err)
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)
err = core.PersistNodeState(role.Coordinator, "192.0.2.1", []byte("owner-id"), []byte("cluster-id"))
if tc.wantErr {

View File

@ -4,7 +4,8 @@ import (
"errors"
"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/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -43,7 +44,8 @@ func TestGetDiskUUID(t *testing.T) {
uuidErr: tc.uuidErr,
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)
uuid, err := core.GetDiskUUID()
if tc.wantErr {
@ -85,7 +87,8 @@ func TestUpdateDiskPassphrase(t *testing.T) {
openErr: tc.openErr,
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)
err = core.UpdateDiskPassphrase("passphrase")
if tc.wantErr {

View File

@ -9,7 +9,6 @@ import (
"sync"
"testing"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/atls"
"github.com/edgelesssys/constellation/coordinator/attestation/simulator"
"github.com/edgelesssys/constellation/coordinator/pubapi"
@ -18,6 +17,8 @@ import (
"github.com/edgelesssys/constellation/coordinator/util/grpcutil"
"github.com/edgelesssys/constellation/coordinator/vpnapi"
"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"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
@ -131,7 +132,8 @@ func newMockCoreWithDialer(bufDialer *bufconnDialer) (*Core, *pubapi.API, error)
getPublicAddr := func() (string, error) {
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 {
return nil, nil, err
}

View File

@ -4,8 +4,9 @@ import (
"errors"
"testing"
"github.com/edgelesssys/constellation/cli/file"
"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/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -53,7 +54,8 @@ func TestGetPeers(t *testing.T) {
assert := assert.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)
// prepare store
@ -113,7 +115,8 @@ func TestAddPeer(t *testing.T) {
assert := assert.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)
err = core.AddPeer(tc.peer)

View File

@ -5,13 +5,14 @@ import (
"errors"
"testing"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/atls"
"github.com/edgelesssys/constellation/coordinator/peer"
"github.com/edgelesssys/constellation/coordinator/pubapi/pubproto"
"github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/coordinator/util/grpcutil"
"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"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
@ -79,7 +80,8 @@ func TestReinitializeAsNode(t *testing.T) {
go server.Serve(netDialer.GetListener("192.0.2.1:9000"))
defer server.Stop()
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)
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"))
defer server.Stop()
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)
// prepare store to emulate initialized KMS
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"
"testing"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/attestation/simulator"
"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/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -65,7 +66,8 @@ func TestAdvanceState(t *testing.T) {
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)
assert.Equal(state.Uninitialized, core.GetState())
core.state = tc.initialState

View File

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

View File

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

View File

@ -12,6 +12,7 @@ import (
"github.com/edgelesssys/constellation/coordinator/pubapi/pubproto"
"github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/coordinator/state"
"github.com/edgelesssys/constellation/internal/deploy/ssh"
"github.com/edgelesssys/constellation/state/keyservice/keyproto"
"go.uber.org/zap"
"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)
}
// 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 ...")
kubeconfig, err := a.core.InitCluster(in.AutoscalingNodeGroups, in.CloudServiceAccountUri)
if err != nil {

View File

@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"sync"
@ -19,8 +20,11 @@ import (
"github.com/edgelesssys/constellation/coordinator/state"
"github.com/edgelesssys/constellation/coordinator/util/grpcutil"
"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"
"github.com/edgelesssys/constellation/state/keyservice/keyproto"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"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}
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}
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 {
nodes []*stubPeer
@ -49,6 +61,7 @@ func TestActivateAsCoordinator(t *testing.T) {
wantPeers []peer.Peer
wantState state.State
adminVPNIP string
sshKeys []*ssh.UserKey
}{
"0 nodes": {
state: state.AcceptingInit,
@ -77,6 +90,13 @@ func TestActivateAsCoordinator(t *testing.T) {
wantState: state.ActivatingNodes,
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": {
nodes: []*stubPeer{testNode1},
state: state.ActivatingNodes,
@ -117,7 +137,7 @@ func TestActivateAsCoordinator(t *testing.T) {
autoscalingNodeGroups := []string{"ang1", "ang2"}
keyEncryptionKeyID := "constellation"
fs := afero.NewMemMapFs()
core := &fakeCore{
state: tc.state,
vpnPubKey: coordinatorPubKey,
@ -125,7 +145,9 @@ func TestActivateAsCoordinator(t *testing.T) {
kubeconfig: []byte("kubeconfig"),
ownerID: []byte("ownerID"),
clusterID: []byte("clusterID"),
linuxUserManager: user.NewLinuxUserManagerFake(fs),
}
netDialer := testdialer.NewBufconnDialer()
dialer := grpcutil.NewDialer(fakeValidator{}, netDialer)
@ -162,6 +184,7 @@ func TestActivateAsCoordinator(t *testing.T) {
UseExistingKek: false,
KmsUri: kms.ClusterKMSURI,
StorageUri: kms.NoStoreURI,
SshUserKeys: ssh.ToProtoSlice(tc.sshKeys),
}, stream)
assert.Equal(tc.wantState, core.state)
@ -194,6 +217,25 @@ func TestActivateAsCoordinator(t *testing.T) {
assert.Equal(autoscalingNodeGroups, core.autoscalingNodeGroups)
assert.Equal(keyEncryptionKeyID, core.kekID)
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/role"
"github.com/edgelesssys/constellation/coordinator/state"
"github.com/edgelesssys/constellation/internal/deploy/ssh"
kms "github.com/edgelesssys/constellation/kms/server/setup"
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
)
@ -36,6 +37,8 @@ type Core interface {
AddPeerToVPN(peer.Peer) error
UpdatePeers([]peer.Peer) error
CreateSSHUsers([]ssh.UserKey) error
InitCluster(autoscalingNodeGroups []string, cloudServiceAccountURI string) ([]byte, 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/role"
"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"
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
)
@ -36,6 +38,7 @@ type fakeCore struct {
kekID string
dataKey []byte
getDataKeyErr error
linuxUserManager user.LinuxUserManager
}
func (c *fakeCore) GetVPNPubKey() ([]byte, error) {
@ -154,3 +157,16 @@ func (c *fakeCore) GetDiskUUID() (string, error) {
func (c *fakeCore) UpdateDiskPassphrase(passphrase string) error {
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
unknownFields protoimpl.UnknownFields
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"`
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"`
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"`
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"`
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"`
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"`
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"`
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"`
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"`
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"`
SshUserKeys []*SSHUserKey `protobuf:"bytes,11,rep,name=ssh_user_keys,json=sshUserKeys,proto3" json:"ssh_user_keys,omitempty"`
}
func (x *ActivateAsCoordinatorRequest) Reset() {
@ -224,6 +225,13 @@ func (x *ActivateAsCoordinatorRequest) GetCloudServiceAccountUri() string {
return ""
}
func (x *ActivateAsCoordinatorRequest) GetSshUserKeys() []*SSHUserKey {
if x != nil {
return x.SshUserKeys
}
return nil
}
type ActivateAsCoordinatorResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -1520,6 +1528,61 @@ func (x *Peer) GetRole() uint32 {
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_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,
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,
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,
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,
@ -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,
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,
0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x55, 0x72, 0x69, 0x22, 0x85, 0x01, 0x0a,
0x1d, 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, 0x12, 0x38,
0x0a, 0x0c, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x64,
0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x64, 0x6d,
0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1f, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x4c,
0x6f, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x6f, 0x6e,
0x74, 0x65, 0x6e, 0x74, 0x22, 0x9b, 0x01, 0x0a, 0x15, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74,
0x65, 0x41, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f,
0x0a, 0x0f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69,
0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x49,
0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52,
0x0e, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
0x26, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x6b, 0x65,
0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x65,
0x44, 0x69, 0x73, 0x6b, 0x4b, 0x65, 0x79, 0x42, 0x09, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x22, 0x9c, 0x01, 0x0a, 0x1c, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41,
0x73, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x76, 0x70, 0x6e, 0x5f,
0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x56, 0x70,
0x6e, 0x49, 0x70, 0x12, 0x22, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x02, 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, 0x03, 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, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49,
0x64, 0x22, 0x79, 0x0a, 0x16, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x4e,
0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x6e,
0x6f, 0x64, 0x65, 0x5f, 0x76, 0x70, 0x6e, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x56, 0x70, 0x6e,
0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f,
0x64, 0x69, 0x73, 0x6b, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48,
0x00, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x6b, 0x55, 0x75, 0x69, 0x64,
0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, 0x0a, 0x1e,
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, 0x12, 0x26,
0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x70,
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x50, 0x75, 0x62,
0x6c, 0x69, 0x63, 0x49, 0x70, 0x73, 0x22, 0x40, 0x0a, 0x1f, 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, 0x12, 0x1d, 0x0a, 0x03, 0x6c, 0x6f, 0x67,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e,
0x4c, 0x6f, 0x67, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x22, 0xfc, 0x01, 0x0a, 0x26, 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, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f,
0x76, 0x70, 0x6e, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x73,
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,
0x63, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x55, 0x72, 0x69, 0x12, 0x36, 0x0a, 0x0d,
0x73, 0x73, 0x68, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x0b, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x53, 0x48,
0x55, 0x73, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x0b, 0x73, 0x73, 0x68, 0x55, 0x73, 0x65, 0x72,
0x4b, 0x65, 0x79, 0x73, 0x22, 0x85, 0x01, 0x0a, 0x1d, 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, 0x12, 0x38, 0x0a, 0x0c, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f,
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70,
0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x12, 0x1f, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e,
0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6c, 0x6f,
0x67, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x9b, 0x01, 0x0a,
0x15, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x0f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61,
0x6c, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x24, 0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74,
0x65, 0x41, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x65,
0x5f, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48,
0x00, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x6b, 0x4b, 0x65, 0x79, 0x42,
0x09, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x9c, 0x01, 0x0a, 0x1c, 0x41,
0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x69,
0x74, 0x69, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x6e,
0x6f, 0x64, 0x65, 0x5f, 0x76, 0x70, 0x6e, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x56, 0x70, 0x6e, 0x49, 0x70, 0x12, 0x22, 0x0a, 0x05, 0x70,
0x65, 0x65, 0x72, 0x73, 0x18, 0x02, 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, 0x03, 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, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09,
0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x79, 0x0a, 0x16, 0x41, 0x63, 0x74,
0x69, 0x76, 0x61, 0x74, 0x65, 0x41, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x76, 0x70, 0x6e, 0x5f,
0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52,
0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x56, 0x70, 0x6e, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x28,
0x0a, 0x0f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x75, 0x75, 0x69,
0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x65,
0x44, 0x69, 0x73, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, 0x0a, 0x1e, 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, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x70,
0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52,
0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x70, 0x73, 0x22, 0x40,
0x0a, 0x1f, 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, 0x12, 0x1d, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b,
0x2e, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x03, 0x6c, 0x6f, 0x67,
0x22, 0xfc, 0x01, 0x0a, 0x26, 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,
0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x61,
0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x76, 0x70, 0x6e, 0x5f, 0x69, 0x70, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x73, 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, 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,
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, 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 (
@ -1774,7 +1845,7 @@ func file_pubapi_proto_rawDescGZIP() []byte {
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{}{
(*GetStateRequest)(nil), // 0: pubapi.GetStateRequest
(*GetStateResponse)(nil), // 1: pubapi.GetStateResponse
@ -1804,45 +1875,47 @@ var file_pubapi_proto_goTypes = []interface{}{
(*AdminConfig)(nil), // 25: pubapi.AdminConfig
(*Log)(nil), // 26: pubapi.Log
(*Peer)(nil), // 27: pubapi.Peer
(*SSHUserKey)(nil), // 28: pubapi.SSHUserKey
}
var file_pubapi_proto_depIdxs = []int32{
25, // 0: pubapi.ActivateAsCoordinatorResponse.admin_config:type_name -> pubapi.AdminConfig
26, // 1: pubapi.ActivateAsCoordinatorResponse.log:type_name -> pubapi.Log
5, // 2: pubapi.ActivateAsNodeRequest.initial_request:type_name -> pubapi.ActivateAsNodeInitialRequest
27, // 3: pubapi.ActivateAsNodeInitialRequest.peers:type_name -> pubapi.Peer
26, // 4: pubapi.ActivateAdditionalNodesResponse.log:type_name -> pubapi.Log
27, // 5: pubapi.ActivateAsAdditionalCoordinatorRequest.activating_coordinator_data:type_name -> pubapi.Peer
27, // 6: pubapi.ActivateAsAdditionalCoordinatorRequest.peers:type_name -> pubapi.Peer
27, // 7: pubapi.GetVPNPeersResponse.peers:type_name -> pubapi.Peer
0, // 8: pubapi.API.GetState:input_type -> pubapi.GetStateRequest
2, // 9: pubapi.API.ActivateAsCoordinator:input_type -> pubapi.ActivateAsCoordinatorRequest
4, // 10: pubapi.API.ActivateAsNode:input_type -> pubapi.ActivateAsNodeRequest
7, // 11: pubapi.API.ActivateAdditionalNodes:input_type -> pubapi.ActivateAdditionalNodesRequest
9, // 12: pubapi.API.ActivateAsAdditionalCoordinator:input_type -> pubapi.ActivateAsAdditionalCoordinatorRequest
11, // 13: pubapi.API.ActivateAdditionalCoordinator:input_type -> pubapi.ActivateAdditionalCoordinatorRequest
13, // 14: pubapi.API.JoinCluster:input_type -> pubapi.JoinClusterRequest
15, // 15: pubapi.API.TriggerNodeUpdate:input_type -> pubapi.TriggerNodeUpdateRequest
17, // 16: pubapi.API.TriggerCoordinatorUpdate:input_type -> pubapi.TriggerCoordinatorUpdateRequest
21, // 17: pubapi.API.GetPeerVPNPublicKey:input_type -> pubapi.GetPeerVPNPublicKeyRequest
23, // 18: pubapi.API.GetVPNPeers:input_type -> pubapi.GetVPNPeersRequest
19, // 19: pubapi.API.RequestStateDiskKey:input_type -> pubapi.RequestStateDiskKeyRequest
1, // 20: pubapi.API.GetState:output_type -> pubapi.GetStateResponse
3, // 21: pubapi.API.ActivateAsCoordinator:output_type -> pubapi.ActivateAsCoordinatorResponse
6, // 22: pubapi.API.ActivateAsNode:output_type -> pubapi.ActivateAsNodeResponse
8, // 23: pubapi.API.ActivateAdditionalNodes:output_type -> pubapi.ActivateAdditionalNodesResponse
10, // 24: pubapi.API.ActivateAsAdditionalCoordinator:output_type -> pubapi.ActivateAsAdditionalCoordinatorResponse
12, // 25: pubapi.API.ActivateAdditionalCoordinator:output_type -> pubapi.ActivateAdditionalCoordinatorResponse
14, // 26: pubapi.API.JoinCluster:output_type -> pubapi.JoinClusterResponse
16, // 27: pubapi.API.TriggerNodeUpdate:output_type -> pubapi.TriggerNodeUpdateResponse
18, // 28: pubapi.API.TriggerCoordinatorUpdate:output_type -> pubapi.TriggerCoordinatorUpdateResponse
22, // 29: pubapi.API.GetPeerVPNPublicKey:output_type -> pubapi.GetPeerVPNPublicKeyResponse
24, // 30: pubapi.API.GetVPNPeers:output_type -> pubapi.GetVPNPeersResponse
20, // 31: pubapi.API.RequestStateDiskKey:output_type -> pubapi.RequestStateDiskKeyResponse
20, // [20:32] is the sub-list for method output_type
8, // [8:20] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
28, // 0: pubapi.ActivateAsCoordinatorRequest.ssh_user_keys:type_name -> pubapi.SSHUserKey
25, // 1: pubapi.ActivateAsCoordinatorResponse.admin_config:type_name -> pubapi.AdminConfig
26, // 2: pubapi.ActivateAsCoordinatorResponse.log:type_name -> pubapi.Log
5, // 3: pubapi.ActivateAsNodeRequest.initial_request:type_name -> pubapi.ActivateAsNodeInitialRequest
27, // 4: pubapi.ActivateAsNodeInitialRequest.peers:type_name -> pubapi.Peer
26, // 5: pubapi.ActivateAdditionalNodesResponse.log:type_name -> pubapi.Log
27, // 6: pubapi.ActivateAsAdditionalCoordinatorRequest.activating_coordinator_data:type_name -> pubapi.Peer
27, // 7: pubapi.ActivateAsAdditionalCoordinatorRequest.peers:type_name -> pubapi.Peer
27, // 8: pubapi.GetVPNPeersResponse.peers:type_name -> pubapi.Peer
0, // 9: pubapi.API.GetState:input_type -> pubapi.GetStateRequest
2, // 10: pubapi.API.ActivateAsCoordinator:input_type -> pubapi.ActivateAsCoordinatorRequest
4, // 11: pubapi.API.ActivateAsNode:input_type -> pubapi.ActivateAsNodeRequest
7, // 12: pubapi.API.ActivateAdditionalNodes:input_type -> pubapi.ActivateAdditionalNodesRequest
9, // 13: pubapi.API.ActivateAsAdditionalCoordinator:input_type -> pubapi.ActivateAsAdditionalCoordinatorRequest
11, // 14: pubapi.API.ActivateAdditionalCoordinator:input_type -> pubapi.ActivateAdditionalCoordinatorRequest
13, // 15: pubapi.API.JoinCluster:input_type -> pubapi.JoinClusterRequest
15, // 16: pubapi.API.TriggerNodeUpdate:input_type -> pubapi.TriggerNodeUpdateRequest
17, // 17: pubapi.API.TriggerCoordinatorUpdate:input_type -> pubapi.TriggerCoordinatorUpdateRequest
21, // 18: pubapi.API.GetPeerVPNPublicKey:input_type -> pubapi.GetPeerVPNPublicKeyRequest
23, // 19: pubapi.API.GetVPNPeers:input_type -> pubapi.GetVPNPeersRequest
19, // 20: pubapi.API.RequestStateDiskKey:input_type -> pubapi.RequestStateDiskKeyRequest
1, // 21: pubapi.API.GetState:output_type -> pubapi.GetStateResponse
3, // 22: pubapi.API.ActivateAsCoordinator:output_type -> pubapi.ActivateAsCoordinatorResponse
6, // 23: pubapi.API.ActivateAsNode:output_type -> pubapi.ActivateAsNodeResponse
8, // 24: pubapi.API.ActivateAdditionalNodes:output_type -> pubapi.ActivateAdditionalNodesResponse
10, // 25: pubapi.API.ActivateAsAdditionalCoordinator:output_type -> pubapi.ActivateAsAdditionalCoordinatorResponse
12, // 26: pubapi.API.ActivateAdditionalCoordinator:output_type -> pubapi.ActivateAdditionalCoordinatorResponse
14, // 27: pubapi.API.JoinCluster:output_type -> pubapi.JoinClusterResponse
16, // 28: pubapi.API.TriggerNodeUpdate:output_type -> pubapi.TriggerNodeUpdateResponse
18, // 29: pubapi.API.TriggerCoordinatorUpdate:output_type -> pubapi.TriggerCoordinatorUpdateResponse
22, // 30: pubapi.API.GetPeerVPNPublicKey:output_type -> pubapi.GetPeerVPNPublicKeyResponse
24, // 31: pubapi.API.GetVPNPeers:output_type -> pubapi.GetVPNPeersResponse
20, // 32: pubapi.API.RequestStateDiskKey:output_type -> pubapi.RequestStateDiskKeyResponse
21, // [21:33] is the sub-list for method output_type
9, // [9:21] is the sub-list for method input_type
9, // [9:9] is the sub-list for extension 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() }
@ -2187,6 +2260,18 @@ func file_pubapi_proto_init() {
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{}{
(*ActivateAsCoordinatorResponse_AdminConfig)(nil),
@ -2206,7 +2291,7 @@ func file_pubapi_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pubapi_proto_rawDesc,
NumEnums: 0,
NumMessages: 28,
NumMessages: 29,
NumExtensions: 0,
NumServices: 1,
},

View File

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

View File

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

View File

@ -5,9 +5,9 @@ import (
"fmt"
"io/fs"
"github.com/edgelesssys/constellation/cli/file"
"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.
@ -17,7 +17,7 @@ type CDBGConfig struct {
// ConstellationDebugdConfig is the cdbg specific configuration.
type ConstellationDebugdConfig struct {
AuthorizedKeys []ssh.SSHKey `yaml:"authorizedKeys"`
AuthorizedKeys []ssh.UserKey `yaml:"authorizedKeys"`
CoordinatorPath string `yaml:"coordinatorPath"`
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/fallback"
"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"
"golang.org/x/net/context"
)
@ -20,9 +22,10 @@ import (
func main() {
wg := &sync.WaitGroup{}
streamer := coordinator.NewFileStreamer(afero.NewOsFs())
fs := afero.NewOsFs()
streamer := coordinator.NewFileStreamer(fs)
serviceManager := deploy.NewServiceManager()
ssh := deploy.NewSSHAccess(afero.NewOsFs())
ssh := ssh.NewSSHAccess(user.NewLinuxUserManager(fs))
ctx, cancel := context.WithCancel(context.Background())
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"
gcpcloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/gcp"
"github.com/edgelesssys/constellation/coordinator/core"
"github.com/edgelesssys/constellation/debugd/ssh"
"github.com/edgelesssys/constellation/internal/deploy/ssh"
)
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.
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)
if err != nil {
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 _, 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"
"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/require"
)
@ -70,7 +70,7 @@ func TestFetchSSHKeys(t *testing.T) {
testCases := map[string]struct {
meta stubMetadata
wantKeys []ssh.SSHKey
wantKeys []ssh.UserKey
wantErr bool
}{
"fetch works": {
@ -81,10 +81,10 @@ func TestFetchSSHKeys(t *testing.T) {
SSHKeys: map[string][]string{"bob": {"ssh-rsa bobskey"}},
},
},
wantKeys: []ssh.SSHKey{
wantKeys: []ssh.UserKey{
{
Username: "bob",
KeyValue: "ssh-rsa bobskey",
Username: "bob",
PublicKey: "ssh-rsa bobskey",
},
},
},

View File

@ -3,7 +3,7 @@ package fallback
import (
"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.
@ -14,7 +14,7 @@ func (f Fetcher) DiscoverDebugdIPs(ctx context.Context) ([]string, error) {
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
return nil, nil
}

View File

@ -9,13 +9,13 @@ import (
"time"
"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.
type Fetcher interface {
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.
@ -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.
func (s *Scheduler) deploySSHKeys(ctx context.Context, keys []ssh.SSHKey) {
func (s *Scheduler) deploySSHKeys(ctx context.Context, keys []ssh.UserKey) {
for _, key := range keys {
err := s.ssh.DeploySSHAuthorizedKey(ctx, key)
if err != nil {
@ -137,5 +137,5 @@ type downloader 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"
"time"
"github.com/edgelesssys/constellation/debugd/ssh"
"github.com/edgelesssys/constellation/internal/deploy/ssh"
"github.com/stretchr/testify/assert"
)
@ -17,23 +17,23 @@ func TestSchedulerStart(t *testing.T) {
ssh stubSSHDeployer
downloader stubDownloader
timeout time.Duration
wantSSHKeys []ssh.SSHKey
wantSSHKeys []ssh.UserKey
wantDebugdDownloads []string
}{
"scheduler works and calls fetcher functions at least once": {},
"ssh keys are fetched": {
fetcher: stubFetcher{
keys: []ssh.SSHKey{
keys: []ssh.UserKey{
{
Username: "test",
KeyValue: "testkey",
Username: "test",
PublicKey: "testkey",
},
},
},
wantSSHKeys: []ssh.SSHKey{
wantSSHKeys: []ssh.UserKey{
{
Username: "test",
KeyValue: "testkey",
Username: "test",
PublicKey: "testkey",
},
},
},
@ -95,7 +95,7 @@ type stubFetcher struct {
fetchSSHKeysCalls int
ips []string
keys []ssh.SSHKey
keys []ssh.UserKey
discoverErr error
fetchSSHKeysErr error
}
@ -105,18 +105,18 @@ func (s *stubFetcher) DiscoverDebugdIPs(ctx context.Context) ([]string, error) {
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++
return s.keys, s.fetchSSHKeysErr
}
type stubSSHDeployer struct {
sshKeys []ssh.SSHKey
sshKeys []ssh.UserKey
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)
return s.deployErr

View File

@ -13,7 +13,7 @@ import (
"github.com/edgelesssys/constellation/debugd/debugd"
"github.com/edgelesssys/constellation/debugd/debugd/deploy"
pb "github.com/edgelesssys/constellation/debugd/service"
"github.com/edgelesssys/constellation/debugd/ssh"
"github.com/edgelesssys/constellation/internal/deploy/ssh"
"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) {
log.Println("Uploading authorized 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)
return &pb.UploadAuthorizedKeysResponse{
Status: pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_FAILURE,
@ -117,7 +117,7 @@ func Start(wg *sync.WaitGroup, serv pb.DebugdServer) {
}
type sshDeployer interface {
DeploySSHAuthorizedKey(ctx context.Context, sshKey ssh.SSHKey) error
DeploySSHAuthorizedKey(ctx context.Context, sshKey ssh.UserKey) error
}
type serviceManager interface {

View File

@ -12,7 +12,7 @@ import (
"github.com/edgelesssys/constellation/debugd/coordinator"
"github.com/edgelesssys/constellation/debugd/debugd/deploy"
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/require"
"google.golang.org/grpc"
@ -28,7 +28,7 @@ func TestUploadAuthorizedKeys(t *testing.T) {
request *pb.UploadAuthorizedKeysRequest
wantErr bool
wantResponseStatus pb.UploadAuthorizedKeysStatus
wantKeys []ssh.SSHKey
wantKeys []ssh.UserKey
}{
"upload authorized keys works": {
request: &pb.UploadAuthorizedKeysRequest{
@ -40,10 +40,10 @@ func TestUploadAuthorizedKeys(t *testing.T) {
},
},
wantResponseStatus: pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_SUCCESS,
wantKeys: []ssh.SSHKey{
wantKeys: []ssh.UserKey{
{
Username: "testuser",
KeyValue: "teskey",
Username: "testuser",
PublicKey: "teskey",
},
},
},
@ -58,10 +58,10 @@ func TestUploadAuthorizedKeys(t *testing.T) {
},
ssh: stubSSHDeployer{deployErr: errors.New("ssh key deployment error")},
wantResponseStatus: pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_FAILURE,
wantKeys: []ssh.SSHKey{
wantKeys: []ssh.UserKey{
{
Username: "testuser",
KeyValue: "teskey",
Username: "testuser",
PublicKey: "teskey",
},
},
},
@ -323,12 +323,12 @@ func TestUploadSystemServiceUnits(t *testing.T) {
}
type stubSSHDeployer struct {
sshKeys []ssh.SSHKey
sshKeys []ssh.UserKey
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)
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/ec2"
awsClient "github.com/edgelesssys/constellation/cli/ec2/client"
"github.com/edgelesssys/constellation/cli/file"
gcpClient "github.com/edgelesssys/constellation/cli/gcp/client"
"github.com/edgelesssys/constellation/coordinator/attestation/vtpm"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/deploy/ssh"
"github.com/edgelesssys/constellation/internal/file"
"google.golang.org/protobuf/proto"
)
@ -46,6 +47,7 @@ type Config struct {
AutoscalingNodeGroupsMax *int `yaml:"autoscalingNodeGroupsMax,omitempty"`
StateDiskSizeGB *int `yaml:"StateDisksizeGB,omitempty"`
Provider *ProviderConfig `yaml:"provider,omitempty"`
SSHUsers []*ssh.UserKey `yaml:"sshUsers,omitempty"`
}
// Default returns a struct with the default config.

View File

@ -4,9 +4,9 @@ import (
"testing"
"github.com/edgelesssys/constellation/cli/cloud/cloudtypes"
"github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/cli/gcp/client"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"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 (
"context"
@ -7,46 +7,43 @@ import (
"os"
"sync"
"github.com/edgelesssys/constellation/debugd/debugd/deploy/createuser"
"github.com/edgelesssys/constellation/debugd/debugd/deploy/passwd"
"github.com/edgelesssys/constellation/debugd/ssh"
"github.com/spf13/afero"
"github.com/edgelesssys/constellation/internal/deploy/user"
)
// 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.
type SSHAccess struct {
fs afero.Fs
userManager LinuxUserManager
userManager user.LinuxUserManager
authorized map[string]bool
mux sync.Mutex
}
// NewSSHAccess creates a new SSHAccess.
func NewSSHAccess(fs afero.Fs) *SSHAccess {
func NewSSHAccess(userManager user.LinuxUserManager) *SSHAccess {
return &SSHAccess{
fs: fs,
userManager: LinuxUserManager{
fs: fs,
passwd: passwd.Passwd{},
creator: createuser.Unix{},
},
mux: sync.Mutex{},
authorized: map[string]bool{},
userManager: userManager,
mux: sync.Mutex{},
authorized: map[string]bool{},
}
}
// alreadyAuthorized checks if key was written to authorized keys before.
func (s *SSHAccess) alreadyAuthorized(sshKey ssh.SSHKey) bool {
_, ok := s.authorized[fmt.Sprintf("%s:%s", sshKey.Username, sshKey.KeyValue)]
func (s *SSHAccess) alreadyAuthorized(sshKey UserKey) bool {
_, ok := s.authorized[fmt.Sprintf("%s:%s", sshKey.Username, sshKey.PublicKey)]
return ok
}
// rememberAuthorized marks this key as already written to authorized keys..
func (s *SSHAccess) rememberAuthorized(sshKey ssh.SSHKey) {
s.authorized[fmt.Sprintf("%s:%s", sshKey.Username, sshKey.KeyValue)] = true
func (s *SSHAccess) rememberAuthorized(sshKey UserKey) {
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
s.mux.Lock()
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/*
sshFolder := fmt.Sprintf("%s/.ssh", user.Home)
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
}
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
}
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
}
authorizedKeysPath := fmt.Sprintf("%s/debugd", authorized_keys_d)
authorizedKeysFile, err := s.fs.OpenFile(authorizedKeysPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
authorizedKeysPath := fmt.Sprintf("%s/ssh-keys", authorized_keys_d)
authorizedKeysFile, err := s.userManager.Fs.OpenFile(authorizedKeysPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
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 {
return err
}
if err := authorizedKeysFile.Close(); err != nil {
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
}
if err := s.fs.Chmod(authorizedKeysPath, 0o644); err != nil {
if err := s.userManager.Fs.Chmod(authorizedKeysPath, 0o644); err != nil {
return err
}
s.rememberAuthorized(sshKey)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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