@@ -3,11 +3,11 @@ package agentssh
3
3
import (
4
4
"bufio"
5
5
"context"
6
- "crypto/rand"
7
6
"crypto/rsa"
8
7
"errors"
9
8
"fmt"
10
9
"io"
10
+ "math/rand"
11
11
"net"
12
12
"os"
13
13
"os/exec"
@@ -131,14 +131,15 @@ func NewServer(ctx context.Context, logger slog.Logger, prometheusRegistry *prom
131
131
// Clients' should ignore the host key when connecting.
132
132
// The agent needs to authenticate with coderd to SSH,
133
133
// 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 )
139
139
if err != nil {
140
140
return nil , err
141
141
}
142
+
142
143
if config == nil {
143
144
config = & Config {}
144
145
}
@@ -206,7 +207,7 @@ func NewServer(ctx context.Context, logger slog.Logger, prometheusRegistry *prom
206
207
slog .Error (err ))
207
208
},
208
209
Handler : s .sessionHandler ,
209
- HostSigners : []ssh.Signer {randomSigner },
210
+ HostSigners : []ssh.Signer {coderSigner },
210
211
LocalPortForwardingCallback : func (ctx ssh.Context , destinationHost string , destinationPort uint32 ) bool {
211
212
// Allow local port forwarding all!
212
213
s .logger .Debug (ctx , "local port forward" ,
@@ -1099,3 +1100,32 @@ func userHomeDir() (string, error) {
1099
1100
}
1100
1101
return u .HomeDir , nil
1101
1102
}
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
+ }
0 commit comments