feat(ansible): support authorized_key path options
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
ce60a583f3
commit
988c0e53ca
3 changed files with 78 additions and 8 deletions
|
|
@ -1359,6 +1359,8 @@ func moduleAuthorizedKeyWithClient(_ *Executor, client sshRunner, args map[strin
|
|||
key := getStringArg(args, "key", "")
|
||||
state := getStringArg(args, "state", "present")
|
||||
exclusive := getBoolArg(args, "exclusive", false)
|
||||
manageDir := getBoolArg(args, "manage_dir", true)
|
||||
pathArg := getStringArg(args, "path", "")
|
||||
|
||||
if user == "" || key == "" {
|
||||
return nil, mockError("moduleAuthorizedKeyWithClient", "authorized_key: user and key required")
|
||||
|
|
@ -1377,7 +1379,17 @@ func moduleAuthorizedKeyWithClient(_ *Executor, client sshRunner, args map[strin
|
|||
}
|
||||
}
|
||||
|
||||
authKeysPath := joinPath(home, ".ssh", "authorized_keys")
|
||||
authKeysPath := pathArg
|
||||
if authKeysPath == "" {
|
||||
authKeysPath = joinPath(home, ".ssh", "authorized_keys")
|
||||
} else if corexHasPrefix(authKeysPath, "~/") {
|
||||
authKeysPath = joinPath(home, corexTrimPrefix(authKeysPath, "~/"))
|
||||
} else if authKeysPath == "~" {
|
||||
authKeysPath = home
|
||||
}
|
||||
if authKeysPath == "" {
|
||||
authKeysPath = joinPath(home, ".ssh", "authorized_keys")
|
||||
}
|
||||
|
||||
if state == "absent" {
|
||||
// Remove the exact key line when present.
|
||||
|
|
@ -1387,9 +1399,11 @@ func moduleAuthorizedKeyWithClient(_ *Executor, client sshRunner, args map[strin
|
|||
return &TaskResult{Changed: true}, nil
|
||||
}
|
||||
|
||||
// Ensure .ssh directory exists (best-effort)
|
||||
_, _, _, _ = client.Run(context.Background(), sprintf("mkdir -p %q && chmod 700 %q && chown %s:%s %q",
|
||||
pathDir(authKeysPath), pathDir(authKeysPath), user, user, pathDir(authKeysPath)))
|
||||
if manageDir {
|
||||
// Ensure the parent directory exists (best-effort).
|
||||
_, _, _, _ = client.Run(context.Background(), sprintf("mkdir -p %q && chmod 700 %q && chown %s:%s %q",
|
||||
pathDir(authKeysPath), pathDir(authKeysPath), user, user, pathDir(authKeysPath)))
|
||||
}
|
||||
|
||||
if exclusive {
|
||||
cmd := sprintf("printf '%%s\\n' %q > %q", key, authKeysPath)
|
||||
|
|
|
|||
22
modules.go
22
modules.go
|
|
@ -2877,6 +2877,8 @@ func (e *Executor) moduleAuthorizedKey(ctx context.Context, client sshExecutorCl
|
|||
key := getStringArg(args, "key", "")
|
||||
state := getStringArg(args, "state", "present")
|
||||
exclusive := getBoolArg(args, "exclusive", false)
|
||||
manageDir := getBoolArg(args, "manage_dir", true)
|
||||
pathArg := getStringArg(args, "path", "")
|
||||
|
||||
if user == "" || key == "" {
|
||||
return nil, coreerr.E("Executor.moduleAuthorizedKey", "user and key required", nil)
|
||||
|
|
@ -2895,7 +2897,17 @@ func (e *Executor) moduleAuthorizedKey(ctx context.Context, client sshExecutorCl
|
|||
}
|
||||
}
|
||||
|
||||
authKeysPath := joinPath(home, ".ssh", "authorized_keys")
|
||||
authKeysPath := pathArg
|
||||
if authKeysPath == "" {
|
||||
authKeysPath = joinPath(home, ".ssh", "authorized_keys")
|
||||
} else if corexHasPrefix(authKeysPath, "~/") {
|
||||
authKeysPath = joinPath(home, corexTrimPrefix(authKeysPath, "~/"))
|
||||
} else if authKeysPath == "~" {
|
||||
authKeysPath = home
|
||||
}
|
||||
if authKeysPath == "" {
|
||||
authKeysPath = joinPath(home, ".ssh", "authorized_keys")
|
||||
}
|
||||
|
||||
if state == "absent" {
|
||||
// Remove the exact key line when present.
|
||||
|
|
@ -2905,9 +2917,11 @@ func (e *Executor) moduleAuthorizedKey(ctx context.Context, client sshExecutorCl
|
|||
return &TaskResult{Changed: true}, nil
|
||||
}
|
||||
|
||||
// Ensure .ssh directory exists (best-effort)
|
||||
_, _, _, _ = client.Run(ctx, sprintf("mkdir -p %q && chmod 700 %q && chown %s:%s %q",
|
||||
pathDir(authKeysPath), pathDir(authKeysPath), user, user, pathDir(authKeysPath)))
|
||||
if manageDir {
|
||||
// Ensure the parent directory exists (best-effort).
|
||||
_, _, _, _ = client.Run(ctx, sprintf("mkdir -p %q && chmod 700 %q && chown %s:%s %q",
|
||||
pathDir(authKeysPath), pathDir(authKeysPath), user, user, pathDir(authKeysPath)))
|
||||
}
|
||||
|
||||
if exclusive {
|
||||
cmd := sprintf("printf '%%s\\n' %q > %q", key, authKeysPath)
|
||||
|
|
|
|||
|
|
@ -433,6 +433,48 @@ func TestModulesAdv_ModuleAuthorizedKey_Good_ExclusiveRewritesFile(t *testing.T)
|
|||
assert.False(t, mock.hasExecuted(`grep -qF`))
|
||||
}
|
||||
|
||||
func TestModulesAdv_ModuleAuthorizedKey_Good_CustomPath(t *testing.T) {
|
||||
e, mock := newTestExecutorWithMock("host1")
|
||||
testKey := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDcT... user@host"
|
||||
mock.expectCommand(`getent passwd deploy`, "/home/deploy", "", 0)
|
||||
mock.expectCommand(`mkdir -p "/srv/keys"`, "", "", 0)
|
||||
mock.expectCommand(`grep -qF`, "", "", 1)
|
||||
mock.expectCommand(`echo`, "", "", 0)
|
||||
mock.expectCommand(`chmod 600`, "", "", 0)
|
||||
|
||||
result, err := moduleAuthorizedKeyWithClient(e, mock, map[string]any{
|
||||
"user": "deploy",
|
||||
"key": testKey,
|
||||
"path": "/srv/keys/deploy_keys",
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.True(t, result.Changed)
|
||||
assert.False(t, result.Failed)
|
||||
assert.True(t, mock.containsSubstring("/srv/keys/deploy_keys"))
|
||||
assert.True(t, mock.hasExecuted(`mkdir -p "/srv/keys"`))
|
||||
}
|
||||
|
||||
func TestModulesAdv_ModuleAuthorizedKey_Good_ManageDirDisabled(t *testing.T) {
|
||||
e, mock := newTestExecutorWithMock("host1")
|
||||
testKey := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDcT... user@host"
|
||||
mock.expectCommand(`getent passwd deploy`, "/home/deploy", "", 0)
|
||||
mock.expectCommand(`grep -qF`, "", "", 1)
|
||||
mock.expectCommand(`echo`, "", "", 0)
|
||||
mock.expectCommand(`chmod 600`, "", "", 0)
|
||||
|
||||
result, err := moduleAuthorizedKeyWithClient(e, mock, map[string]any{
|
||||
"user": "deploy",
|
||||
"key": testKey,
|
||||
"manage_dir": false,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.True(t, result.Changed)
|
||||
assert.False(t, result.Failed)
|
||||
assert.False(t, mock.hasExecuted(`mkdir -p`))
|
||||
}
|
||||
|
||||
func TestModulesAdv_ModuleAuthorizedKey_Good_RootUserFallback(t *testing.T) {
|
||||
e, mock := newTestExecutorWithMock("host1")
|
||||
testKey := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDcT... admin@host"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue