Skip to content

Commit f6bf6c6

Browse files
authored
fix!: use names not IDs for agent SSH key seed (#17258)
Changes the SSH host key seeding to use the owner username, workspace name, and agent name. This prevents SSH from complaining about a mismatched host key if you use Coder Desktop to connect, and delete and recreate your workspace with the same name. Previously this would generate a different key because the workspace ID changed. We also include the owner's username in anticipation of using Coder Desktop to access shared workspaces (or as a superuser) down the road, so that workspaces with the same name owned by different users will not have the same key. This change is **BREAKING** in a limited sense that early access users of Coder Desktop will see their SSH clients complain about host keys changing the first time each workspace is rebuilt with this code. It can be resolved by clearing your `.ssh/known_hosts` file of the Coder workspaces you access this way.
1 parent 3a0e8dd commit f6bf6c6

File tree

2 files changed

+28
-6
lines changed

2 files changed

+28
-6
lines changed

agent/agent.go

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,9 +1186,9 @@ func (a *agent) createOrUpdateNetwork(manifestOK, networkOK *checkpoint) func(co
11861186
network := a.network
11871187
a.closeMutex.Unlock()
11881188
if network == nil {
1189-
keySeed, err := WorkspaceKeySeed(manifest.WorkspaceID, manifest.AgentName)
1189+
keySeed, err := SSHKeySeed(manifest.OwnerName, manifest.WorkspaceName, manifest.AgentName)
11901190
if err != nil {
1191-
return xerrors.Errorf("generate seed from workspace id: %w", err)
1191+
return xerrors.Errorf("generate SSH key seed: %w", err)
11921192
}
11931193
// use the graceful context here, because creating the tailnet is not itself tied to the
11941194
// agent API.
@@ -2068,12 +2068,31 @@ func PrometheusMetricsHandler(prometheusRegistry *prometheus.Registry, logger sl
20682068
})
20692069
}
20702070

2071-
// WorkspaceKeySeed converts a WorkspaceID UUID and agent name to an int64 hash.
2071+
// SSHKeySeed converts an owner userName, workspaceName and agentName to an int64 hash.
20722072
// This uses the FNV-1a hash algorithm which provides decent distribution and collision
20732073
// resistance for string inputs.
2074-
func WorkspaceKeySeed(workspaceID uuid.UUID, agentName string) (int64, error) {
2074+
//
2075+
// Why owner username, workspace name, and agent name? These are the components that are used in hostnames for the
2076+
// workspace over SSH, and so we want the workspace to have a stable key with respect to these. We don't use the
2077+
// respective UUIDs. The workspace UUID would be different if you delete and recreate a workspace with the same name.
2078+
// The agent UUID is regenerated on each build. Since Coder's Tailnet networking is handling the authentication, we
2079+
// should not be showing users warnings about host SSH keys.
2080+
func SSHKeySeed(userName, workspaceName, agentName string) (int64, error) {
20752081
h := fnv.New64a()
2076-
_, err := h.Write(workspaceID[:])
2082+
_, err := h.Write([]byte(userName))
2083+
if err != nil {
2084+
return 42, err
2085+
}
2086+
// null separators between strings so that (dog, foodstuff) is distinct from (dogfood, stuff)
2087+
_, err = h.Write([]byte{0})
2088+
if err != nil {
2089+
return 42, err
2090+
}
2091+
_, err = h.Write([]byte(workspaceName))
2092+
if err != nil {
2093+
return 42, err
2094+
}
2095+
_, err = h.Write([]byte{0})
20772096
if err != nil {
20782097
return 42, err
20792098
}

cli/ssh_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,9 @@ func TestSSH(t *testing.T) {
479479
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
480480
defer cancel()
481481

482+
user, err := client.User(ctx, codersdk.Me)
483+
require.NoError(t, err)
484+
482485
inv, root := clitest.New(t, "ssh", "--stdio", workspace.Name)
483486
clitest.SetupConfig(t, client, root)
484487
inv.Stdin = clientOutput
@@ -490,7 +493,7 @@ func TestSSH(t *testing.T) {
490493
assert.NoError(t, err)
491494
})
492495

493-
keySeed, err := agent.WorkspaceKeySeed(workspace.ID, "dev")
496+
keySeed, err := agent.SSHKeySeed(user.Username, workspace.Name, "dev")
494497
assert.NoError(t, err)
495498

496499
signer, err := agentssh.CoderSigner(keySeed)

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