@@ -3,11 +3,11 @@ package agentssh
33import (
44"bufio"
55"context"
6- "crypto/rand"
76"crypto/rsa"
87"errors"
98"fmt"
109"io"
10+ "math/rand"
1111"net"
1212"os"
1313"os/exec"
@@ -131,14 +131,15 @@ func NewServer(ctx context.Context, logger slog.Logger, prometheusRegistry *prom
131131// Clients' should ignore the host key when connecting.
132132// The agent needs to authenticate with coderd to SSH,
133133// so SSH authentication doesn't improve security.
134- randomHostKey , err := rsa . GenerateKey ( rand . Reader , 2048 )
135- if err != nil {
136- return nil , err
137- }
138- randomSigner ,err := gossh . NewSignerFromKey ( randomHostKey )
134+
135+ // Create a fixed host key, as at least one host key has to
136+ // exist for the SSH server to start. We'll later replace
137+ // the host key with one that's based off the workspace uuid.
138+ coderSigner ,err := coderSigner ( 42 )
139139if err != nil {
140140return nil ,err
141141}
142+
142143if config == nil {
143144config = & Config {}
144145}
@@ -206,7 +207,7 @@ func NewServer(ctx context.Context, logger slog.Logger, prometheusRegistry *prom
206207slog .Error (err ))
207208},
208209Handler :s .sessionHandler ,
209- HostSigners : []ssh.Signer {randomSigner },
210+ HostSigners : []ssh.Signer {coderSigner },
210211LocalPortForwardingCallback :func (ctx ssh.Context ,destinationHost string ,destinationPort uint32 )bool {
211212// Allow local port forwarding all!
212213s .logger .Debug (ctx ,"local port forward" ,
@@ -1099,3 +1100,32 @@ func userHomeDir() (string, error) {
10991100}
11001101return u .HomeDir ,nil
11011102}
1103+
1104+ // UpdateHostSigner updates the host signer with a new key generated from the provided seed.
1105+ // This function replaces the previously used host key with the newly generated one.
1106+ func (s * Server )UpdateHostSigner (seed int64 )error {
1107+ key ,err := coderSigner (seed )
1108+ if err != nil {
1109+ return err
1110+ }
1111+
1112+ s .mu .Lock ()
1113+ defer s .mu .Unlock ()
1114+
1115+ s .srv .AddHostKey (key )
1116+
1117+ return nil
1118+ }
1119+
1120+ // coderSigner generates a deterministic SSH signer based on the provided seed.
1121+ // It uses RSA with a key size of 2048 bits.
1122+ func coderSigner (seed int64 ) (gossh.Signer ,error ) {
1123+ // nolint: gosec
1124+ deterministicRand := rand .New (rand .NewSource (seed ))
1125+ coderHostKey ,err := rsa .GenerateKey (deterministicRand ,2048 )
1126+ if err != nil {
1127+ return nil ,err
1128+ }
1129+ coderSigner ,err := gossh .NewSignerFromKey (coderHostKey )
1130+ return coderSigner ,err
1131+ }