Skip to content

Commit d74cb78

Browse files
committed
fix(agent/agentssh): pin random seed for RSA key generation
Change-Id: I8c7e3070324e5d558374fd6891eea9d48660e1e9 Signed-off-by: Thomas Kosiewski <tk@coder.com>
1 parent 4732f08 commit d74cb78

File tree

2 files changed

+33
-8
lines changed

2 files changed

+33
-8
lines changed

agent/agent.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"encoding/json"
77
"errors"
88
"fmt"
9+
"hash/fnv"
910
"io"
1011
"net/http"
1112
"net/netip"
@@ -272,6 +273,7 @@ func (a *agent) init() {
272273
UpdateEnv: a.updateCommandEnv,
273274
WorkingDirectory: func() string { return a.manifest.Load().Directory },
274275
BlockFileTransfer: a.blockFileTransfer,
276+
RandomSeed: stringToInt64Hash(&a.manifest),
275277
})
276278
if err != nil {
277279
panic(err)
@@ -372,7 +374,6 @@ func (a *agent) collectMetadata(ctx context.Context, md codersdk.WorkspaceAgentM
372374
// Important: if the command times out, we may see a misleading error like
373375
// "exit status 1", so it's important to include the context error.
374376
err = errors.Join(err, ctx.Err())
375-
376377
if err != nil {
377378
result.Error = fmt.Sprintf("run cmd: %+v", err)
378379
}
@@ -1850,3 +1851,18 @@ func PrometheusMetricsHandler(prometheusRegistry *prometheus.Registry, logger sl
18501851
}
18511852
})
18521853
}
1854+
1855+
// stringToInt64Hash converts the manifest's AgentID and WorkspaceID to an int64 hash.
1856+
// This uses the FNV-1a hash algorithm which provides decent distribution and collision
1857+
// resistance for string inputs.
1858+
func stringToInt64Hash(manifest *atomic.Pointer[agentsdk.Manifest]) int64 {
1859+
m := manifest.Load()
1860+
if m == nil {
1861+
return 42
1862+
}
1863+
1864+
h := fnv.New64a()
1865+
h.Write(m.AgentID[:])
1866+
h.Write(m.WorkspaceID[:])
1867+
return int64(h.Sum64())
1868+
}

agent/agentssh/agentssh.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ package agentssh
33
import (
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"
@@ -85,6 +85,10 @@ type Config struct {
8585
X11DisplayOffset *int
8686
// BlockFileTransfer restricts use of file transfer applications.
8787
BlockFileTransfer bool
88+
89+
// RandomSeed is a random seed value exclusively used to generate a
90+
// deterministic SSH host key.
91+
RandomSeed int64
8892
}
8993

9094
type Server struct {
@@ -112,20 +116,25 @@ type Server struct {
112116
}
113117

114118
func NewServer(ctx context.Context, logger slog.Logger, prometheusRegistry *prometheus.Registry, fs afero.Fs, execer agentexec.Execer, config *Config) (*Server, error) {
119+
if config == nil {
120+
config = &Config{}
121+
}
122+
115123
// Clients' should ignore the host key when connecting.
116124
// The agent needs to authenticate with coderd to SSH,
117125
// so SSH authentication doesn't improve security.
118-
randomHostKey, err := rsa.GenerateKey(rand.Reader, 2048)
126+
127+
// Create a deterministic random source
128+
// nolint: gosec
129+
deterministicRand := rand.New(rand.NewSource(config.RandomSeed))
130+
coderHostKey, err := rsa.GenerateKey(deterministicRand, 2048)
119131
if err != nil {
120132
return nil, err
121133
}
122-
randomSigner, err := gossh.NewSignerFromKey(randomHostKey)
134+
coderSigner, err := gossh.NewSignerFromKey(coderHostKey)
123135
if err != nil {
124136
return nil, err
125137
}
126-
if config == nil {
127-
config = &Config{}
128-
}
129138
if config.X11DisplayOffset == nil {
130139
offset := X11DefaultDisplayOffset
131140
config.X11DisplayOffset = &offset
@@ -190,7 +199,7 @@ func NewServer(ctx context.Context, logger slog.Logger, prometheusRegistry *prom
190199
slog.Error(err))
191200
},
192201
Handler: s.sessionHandler,
193-
HostSigners: []ssh.Signer{randomSigner},
202+
HostSigners: []ssh.Signer{coderSigner},
194203
LocalPortForwardingCallback: func(ctx ssh.Context, destinationHost string, destinationPort uint32) bool {
195204
// Allow local port forwarding all!
196205
s.logger.Debug(ctx, "local port forward",

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy