Disable SSH key deployment with debugd / cdbg

This commit is contained in:
Malte Poll 2022-10-20 09:56:45 +02:00 committed by Malte Poll
parent b57b25fdaa
commit 6a1405f7c9
5 changed files with 158 additions and 135 deletions

View File

@ -117,20 +117,24 @@ func deployOnEndpoint(ctx context.Context, in deployOnEndpointInput) error {
defer conn.Close() defer conn.Close()
client := pb.NewDebugdClient(conn) client := pb.NewDebugdClient(conn)
log.Println("Uploading authorized keys") if len(in.authorizedKeys) > 0 {
pbKeys := []*pb.AuthorizedKey{} log.Println("Warning: Uploading authorized keys is currently disabled.")
for _, key := range in.authorizedKeys {
pbKeys = append(pbKeys, &pb.AuthorizedKey{
Username: key.Username,
KeyValue: key.PublicKey,
})
}
authorizedKeysResponse, err := client.UploadAuthorizedKeys(ctx, &pb.UploadAuthorizedKeysRequest{Keys: pbKeys}, grpc.WaitForReady(true))
if err != nil || authorizedKeysResponse.Status != pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_SUCCESS {
return fmt.Errorf("uploading authorized keys to instance %v failed: %v / %w", in.debugdEndpoint, authorizedKeysResponse, err)
} }
// TODO (stateless-ssh): re-enable once ssh keys can be deployed on readonly rootfs.
// log.Println("Uploading authorized keys")
// pbKeys := []*pb.AuthorizedKey{}
// for _, key := range in.authorizedKeys {
// pbKeys = append(pbKeys, &pb.AuthorizedKey{
// Username: key.Username,
// KeyValue: key.PublicKey,
// })
// }
// authorizedKeysResponse, err := client.UploadAuthorizedKeys(ctx, &pb.UploadAuthorizedKeysRequest{Keys: pbKeys}, grpc.WaitForReady(true))
// if err != nil || authorizedKeysResponse.Status != pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_SUCCESS {
// return fmt.Errorf("uploading authorized keys to instance %v failed: %v / %w", in.debugdEndpoint, authorizedKeysResponse, err)
// }
stream, err := client.UploadBootstrapper(ctx) stream, err := client.UploadBootstrapper(ctx, grpc.WaitForReady(true))
if err != nil { if err != nil {
return fmt.Errorf("starting bootstrapper upload to instance %v: %w", in.debugdEndpoint, err) return fmt.Errorf("starting bootstrapper upload to instance %v: %w", in.debugdEndpoint, err)
} }

View File

@ -50,9 +50,11 @@ func NewScheduler(log *logger.Logger, fetcher Fetcher, ssh sshDeployer, download
func (s *Scheduler) Start(ctx context.Context, wg *sync.WaitGroup) { func (s *Scheduler) Start(ctx context.Context, wg *sync.WaitGroup) {
defer wg.Done() defer wg.Done()
wg.Add(2) wg.Add(1)
go s.discoveryLoop(ctx, wg) go s.discoveryLoop(ctx, wg)
go s.sshLoop(ctx, wg) // TODO (stateless-ssh): re-enable once
// ssh keys can be deployed on readonly rootfs
// go s.sshLoop(ctx, wg)
} }
// discoveryLoop discovers new debugd endpoints from cloud-provider metadata periodically. // discoveryLoop discovers new debugd endpoints from cloud-provider metadata periodically.
@ -90,33 +92,35 @@ func (s *Scheduler) discoveryLoop(ctx context.Context, wg *sync.WaitGroup) {
} }
// sshLoop discovers new ssh keys from cloud provider metadata periodically. // sshLoop discovers new ssh keys from cloud provider metadata periodically.
func (s *Scheduler) sshLoop(ctx context.Context, wg *sync.WaitGroup) { // TODO (stateless-ssh): re-enable once ssh keys can be deployed on readonly rootfs
defer wg.Done() // func (s *Scheduler) sshLoop(ctx context.Context, wg *sync.WaitGroup) {
// defer wg.Done()
ticker := time.NewTicker(debugd.SSHCheckInterval) // ticker := time.NewTicker(debugd.SSHCheckInterval)
defer ticker.Stop() // defer ticker.Stop()
for { // for {
keys, err := s.fetcher.FetchSSHKeys(ctx) // keys, err := s.fetcher.FetchSSHKeys(ctx)
if err != nil { // if err != nil {
s.log.With(zap.Error(err)).Errorf("Fetching SSH keys failed") // s.log.With(zap.Error(err)).Errorf("Fetching SSH keys failed")
} else { // } else {
s.deploySSHKeys(ctx, keys) // s.deploySSHKeys(ctx, keys)
} // }
select { // select {
case <-ticker.C: // case <-ticker.C:
case <-ctx.Done(): // case <-ctx.Done():
return // return
} // }
} // }
} // }
// downloadDeployment tries to download deployment from a list of ips and logs errors encountered. // downloadDeployment tries to download deployment from a list of ips and logs errors encountered.
func (s *Scheduler) downloadDeployment(ctx context.Context, ips []string) (success bool) { func (s *Scheduler) downloadDeployment(ctx context.Context, ips []string) (success bool) {
for _, ip := range ips { for _, ip := range ips {
keys, err := s.downloader.DownloadDeployment(ctx, ip) _, err := s.downloader.DownloadDeployment(ctx, ip)
if err == nil { if err == nil {
s.deploySSHKeys(ctx, keys) // TODO (stateless-ssh): re-enable once ssh keys can be deployed on readonly rootfs
// s.deploySSHKeys(ctx, keys)
return true return true
} }
if errors.Is(err, fs.ErrExist) { if errors.Is(err, fs.ErrExist) {
@ -129,16 +133,17 @@ func (s *Scheduler) downloadDeployment(ctx context.Context, ips []string) (succe
return false return false
} }
// TODO (stateless-ssh): re-enable once ssh keys can be deployed on readonly rootfs
// deploySSHKeys tries to deploy a list of SSH keys and logs errors encountered. // deploySSHKeys tries to deploy a list of SSH keys and logs errors encountered.
func (s *Scheduler) deploySSHKeys(ctx context.Context, keys []ssh.UserKey) { // func (s *Scheduler) deploySSHKeys(ctx context.Context, keys []ssh.UserKey) {
for _, key := range keys { // for _, key := range keys {
err := s.ssh.DeployAuthorizedKey(ctx, key) // err := s.ssh.DeployAuthorizedKey(ctx, key)
if err != nil { // if err != nil {
s.log.With(zap.Error(err), zap.Any("key", key)).Errorf("Deploying SSH key failed") // s.log.With(zap.Error(err), zap.Any("key", key)).Errorf("Deploying SSH key failed")
continue // continue
} // }
} // }
} // }
type downloader interface { type downloader interface {
DownloadDeployment(ctx context.Context, ip string) ([]ssh.UserKey, error) DownloadDeployment(ctx context.Context, ip string) ([]ssh.UserKey, error)

View File

@ -36,12 +36,13 @@ func TestSchedulerStart(t *testing.T) {
wantDebugdDownloads []string wantDebugdDownloads []string
}{ }{
"scheduler works and calls fetcher functions at least once": {}, "scheduler works and calls fetcher functions at least once": {},
"ssh keys are fetched": { // TODO (stateless-ssh): re-enable once ssh keys can be deployed on readonly rootfs.
fetcher: stubFetcher{ // "ssh keys are fetched": {
keys: []ssh.UserKey{{Username: "test", PublicKey: "testkey"}}, // fetcher: stubFetcher{
}, // keys: []ssh.UserKey{{Username: "test", PublicKey: "testkey"}},
wantSSHKeys: []ssh.UserKey{{Username: "test", PublicKey: "testkey"}}, // },
}, // wantSSHKeys: []ssh.UserKey{{Username: "test", PublicKey: "testkey"}},
// },
"download for discovered debugd ips is started": { "download for discovered debugd ips is started": {
fetcher: stubFetcher{ fetcher: stubFetcher{
ips: []string{"192.0.2.1", "192.0.2.2"}, ips: []string{"192.0.2.1", "192.0.2.2"},
@ -58,9 +59,10 @@ func TestSchedulerStart(t *testing.T) {
"endpoint discovery can fail": { "endpoint discovery can fail": {
fetcher: stubFetcher{discoverErr: someErr}, fetcher: stubFetcher{discoverErr: someErr},
}, },
"ssh key fetch can fail": { // TODO (stateless-ssh): re-enable once ssh keys can be deployed on readonly rootfs.
fetcher: stubFetcher{fetchSSHKeysErr: someErr}, // "ssh key fetch can fail": {
}, // fetcher: stubFetcher{fetchSSHKeysErr: someErr},
// },
} }
for name, tc := range testCases { for name, tc := range testCases {
@ -80,10 +82,12 @@ func TestSchedulerStart(t *testing.T) {
go scheduler.Start(ctx, wg) go scheduler.Start(ctx, wg)
wg.Wait() wg.Wait()
assert.Equal(tc.wantSSHKeys, tc.ssh.sshKeys) // TODO (stateless-ssh): re-enable once ssh keys can be deployed on readonly rootfs.
// assert.Equal(tc.wantSSHKeys, tc.ssh.sshKeys)
assert.Equal(tc.wantDebugdDownloads, tc.downloader.ips) assert.Equal(tc.wantDebugdDownloads, tc.downloader.ips)
assert.Greater(tc.fetcher.discoverCalls, 0) assert.Greater(tc.fetcher.discoverCalls, 0)
assert.Greater(tc.fetcher.fetchSSHKeysCalls, 0) // TODO (stateless-ssh): re-enable once ssh keys can be deployed on readonly rootfs.
// assert.Greater(tc.fetcher.fetchSSHKeysCalls, 0)
}) })
} }
} }

View File

@ -46,17 +46,27 @@ func New(log *logger.Logger, ssh sshDeployer, serviceManager serviceManager, str
} }
} }
// TODO (stateless-ssh): re-enable once ssh keys can be deployed on readonly rootfs.
// UploadAuthorizedKeys receives a list of authorized keys and forwards them to a channel.
//
// func (s *debugdServer) UploadAuthorizedKeys(ctx context.Context, in *pb.UploadAuthorizedKeysRequest) (*pb.UploadAuthorizedKeysResponse, error) {
// s.log.Infof("Uploading authorized keys")
// for _, key := range in.Keys {
// if err := s.ssh.DeployAuthorizedKey(ctx, ssh.UserKey{Username: key.Username, PublicKey: key.KeyValue}); err != nil {
// s.log.With(zap.Error(err)).Errorf("Uploading authorized keys failed")
// return &pb.UploadAuthorizedKeysResponse{
// Status: pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_FAILURE,
// }, nil
// }
// }
// return &pb.UploadAuthorizedKeysResponse{
// Status: pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_SUCCESS,
// }, nil
// }
//
// UploadAuthorizedKeys receives a list of authorized keys and forwards them to a channel. // UploadAuthorizedKeys receives a list of authorized keys and forwards them to a channel.
func (s *debugdServer) UploadAuthorizedKeys(ctx context.Context, in *pb.UploadAuthorizedKeysRequest) (*pb.UploadAuthorizedKeysResponse, error) { func (s *debugdServer) UploadAuthorizedKeys(ctx context.Context, in *pb.UploadAuthorizedKeysRequest) (*pb.UploadAuthorizedKeysResponse, error) {
s.log.Infof("Uploading authorized keys") s.log.Infof("Uploading authorized keys (Disabled feature)")
for _, key := range in.Keys {
if err := s.ssh.DeployAuthorizedKey(ctx, ssh.UserKey{Username: key.Username, PublicKey: key.KeyValue}); err != nil {
s.log.With(zap.Error(err)).Errorf("Uploading authorized keys failed")
return &pb.UploadAuthorizedKeysResponse{
Status: pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_FAILURE,
}, nil
}
}
return &pb.UploadAuthorizedKeysResponse{ return &pb.UploadAuthorizedKeysResponse{
Status: pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_SUCCESS, Status: pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_SUCCESS,
}, nil }, nil

View File

@ -33,84 +33,84 @@ func TestMain(m *testing.M) {
goleak.VerifyTestMain(m) goleak.VerifyTestMain(m)
} }
func TestUploadAuthorizedKeys(t *testing.T) { // func TestUploadAuthorizedKeys(t *testing.T) {
endpoint := "192.0.2.1:" + strconv.Itoa(constants.DebugdPort) // endpoint := "192.0.2.1:" + strconv.Itoa(constants.DebugdPort)
testCases := map[string]struct { // testCases := map[string]struct {
ssh stubSSHDeployer // ssh stubSSHDeployer
serviceManager stubServiceManager // serviceManager stubServiceManager
request *pb.UploadAuthorizedKeysRequest // request *pb.UploadAuthorizedKeysRequest
wantErr bool // wantErr bool
wantResponseStatus pb.UploadAuthorizedKeysStatus // wantResponseStatus pb.UploadAuthorizedKeysStatus
wantKeys []ssh.UserKey // wantKeys []ssh.UserKey
}{ // }{
"upload authorized keys works": { // "upload authorized keys works": {
request: &pb.UploadAuthorizedKeysRequest{ // request: &pb.UploadAuthorizedKeysRequest{
Keys: []*pb.AuthorizedKey{ // Keys: []*pb.AuthorizedKey{
{ // {
Username: "testuser", // Username: "testuser",
KeyValue: "teskey", // KeyValue: "teskey",
}, // },
}, // },
}, // },
wantResponseStatus: pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_SUCCESS, // wantResponseStatus: pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_SUCCESS,
wantKeys: []ssh.UserKey{ // wantKeys: []ssh.UserKey{
{ // {
Username: "testuser", // Username: "testuser",
PublicKey: "teskey", // PublicKey: "teskey",
}, // },
}, // },
}, // },
"deploy fails": { // "deploy fails": {
request: &pb.UploadAuthorizedKeysRequest{ // request: &pb.UploadAuthorizedKeysRequest{
Keys: []*pb.AuthorizedKey{ // Keys: []*pb.AuthorizedKey{
{ // {
Username: "testuser", // Username: "testuser",
KeyValue: "teskey", // KeyValue: "teskey",
}, // },
}, // },
}, // },
ssh: stubSSHDeployer{deployErr: errors.New("ssh key deployment error")}, // ssh: stubSSHDeployer{deployErr: errors.New("ssh key deployment error")},
wantResponseStatus: pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_FAILURE, // wantResponseStatus: pb.UploadAuthorizedKeysStatus_UPLOAD_AUTHORIZED_KEYS_FAILURE,
wantKeys: []ssh.UserKey{ // wantKeys: []ssh.UserKey{
{ // {
Username: "testuser", // Username: "testuser",
PublicKey: "teskey", // PublicKey: "teskey",
}, // },
}, // },
}, // },
} // }
for name, tc := range testCases { // for name, tc := range testCases {
t.Run(name, func(t *testing.T) { // t.Run(name, func(t *testing.T) {
assert := assert.New(t) // assert := assert.New(t)
require := require.New(t) // require := require.New(t)
serv := debugdServer{ // serv := debugdServer{
log: logger.NewTest(t), // log: logger.NewTest(t),
ssh: &tc.ssh, // ssh: &tc.ssh,
serviceManager: &tc.serviceManager, // serviceManager: &tc.serviceManager,
streamer: &fakeStreamer{}, // streamer: &fakeStreamer{},
} // }
grpcServ, conn, err := setupServerWithConn(endpoint, &serv) // grpcServ, conn, err := setupServerWithConn(endpoint, &serv)
require.NoError(err) // require.NoError(err)
defer conn.Close() // defer conn.Close()
client := pb.NewDebugdClient(conn) // client := pb.NewDebugdClient(conn)
resp, err := client.UploadAuthorizedKeys(context.Background(), tc.request) // resp, err := client.UploadAuthorizedKeys(context.Background(), tc.request)
grpcServ.GracefulStop() // grpcServ.GracefulStop()
if tc.wantErr { // if tc.wantErr {
assert.Error(err) // assert.Error(err)
return // return
} // }
require.NoError(err) // require.NoError(err)
assert.Equal(tc.wantResponseStatus, resp.Status) // assert.Equal(tc.wantResponseStatus, resp.Status)
assert.ElementsMatch(tc.ssh.sshKeys, tc.wantKeys) // assert.ElementsMatch(tc.ssh.sshKeys, tc.wantKeys)
}) // })
} // }
} // }
func TestUploadBootstrapper(t *testing.T) { func TestUploadBootstrapper(t *testing.T) {
endpoint := "192.0.2.1:" + strconv.Itoa(constants.DebugdPort) endpoint := "192.0.2.1:" + strconv.Itoa(constants.DebugdPort)