Skip to content

Commit

Permalink
adds host_key_checking attr to ansible_options and better document th…
Browse files Browse the repository at this point in the history
…e default behavior dictated by ansible runner (#69)
  • Loading branch information
marshallford authored Dec 16, 2024
1 parent 918f23d commit e0ab832
Show file tree
Hide file tree
Showing 13 changed files with 131 additions and 40 deletions.
3 changes: 2 additions & 1 deletion docs/data-sources/navigator_run.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ output "resolv_conf" {
Optional:

- `force_handlers` (Boolean) Run handlers even if a task fails.
- `known_hosts` (List of String) SSH known host entries. Can help protect against man-in-the-middle attacks by verifying the identity of hosts. Ansible variable `ansible_ssh_known_hosts_file` set to path of `known_hosts` file. If unspecified will be set to contents of `known_hosts` file after run.
- `host_key_checking` (Boolean) SSH host key checking. Can help protect against man-in-the-middle attacks by verifying the identity of hosts. Ansible runner (library used by `ansible-navigator`) defaults this option to `false` explicitly.
- `known_hosts` (List of String) SSH known host entries. Ansible variable `ansible_ssh_known_hosts_file` set to path of `known_hosts` file and SSH option `UserKnownHostsFile` must be configured to said path. Defaults to all of the `known_hosts` entries recorded.
- `limit` (List of String) Further limit selected hosts to an additional pattern.
- `private_keys` (Attributes List) SSH private keys used for authentication in addition to the [automatically mounted](https://ansible.readthedocs.io/projects/navigator/faq/#how-do-i-use-my-ssh-keys-with-an-execution-environment) default named keys and SSH agent socket path. (see [below for nested schema](#nestedatt--ansible_options--private_keys))
- `skip_tags` (List of String) Only run plays and tasks whose tags do not match these values.
Expand Down
3 changes: 2 additions & 1 deletion docs/resources/navigator_run.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,8 @@ pipelining=True
Optional:

- `force_handlers` (Boolean) Run handlers even if a task fails.
- `known_hosts` (List of String) SSH known host entries. Can help protect against man-in-the-middle attacks by verifying the identity of hosts. Ansible variable `ansible_ssh_known_hosts_file` set to path of `known_hosts` file. If unspecified will be set to contents of `known_hosts` file after run.
- `host_key_checking` (Boolean) SSH host key checking. Can help protect against man-in-the-middle attacks by verifying the identity of hosts. Ansible runner (library used by `ansible-navigator`) defaults this option to `false` explicitly.
- `known_hosts` (List of String) SSH known host entries. Ansible variable `ansible_ssh_known_hosts_file` set to path of `known_hosts` file and SSH option `UserKnownHostsFile` must be configured to said path. Defaults to all of the `known_hosts` entries recorded.
- `limit` (List of String) Further limit selected hosts to an additional pattern.
- `private_keys` (Attributes List) SSH private keys used for authentication in addition to the [automatically mounted](https://ansible.readthedocs.io/projects/navigator/faq/#how-do-i-use-my-ssh-keys-with-an-execution-environment) default named keys and SSH agent socket path. (see [below for nested schema](#nestedatt--ansible_options--private_keys))
- `skip_tags` (List of String) Only run plays and tasks whose tags do not match these values.
Expand Down
50 changes: 29 additions & 21 deletions internal/provider/navigator_run_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ type ExecutionEnvironmentModel struct {
}

type AnsibleOptionsModel struct {
ForceHandlers types.Bool `tfsdk:"force_handlers"`
SkipTags types.List `tfsdk:"skip_tags"`
StartAtTask types.String `tfsdk:"start_at_task"`
Limit types.List `tfsdk:"limit"`
Tags types.List `tfsdk:"tags"`
PrivateKeys types.List `tfsdk:"private_keys"`
KnownHosts types.List `tfsdk:"known_hosts"`
ForceHandlers types.Bool `tfsdk:"force_handlers"`
SkipTags types.List `tfsdk:"skip_tags"`
StartAtTask types.String `tfsdk:"start_at_task"`
Limit types.List `tfsdk:"limit"`
Tags types.List `tfsdk:"tags"`
PrivateKeys types.List `tfsdk:"private_keys"`
KnownHosts types.List `tfsdk:"known_hosts"`
HostKeyChecking types.Bool `tfsdk:"host_key_checking"`
}

type PrivateKeyModel struct {
Expand Down Expand Up @@ -119,27 +120,29 @@ func (PrivateKeyModel) AttrTypes() map[string]attr.Type {

func (AnsibleOptionsModel) AttrTypes() map[string]attr.Type {
return map[string]attr.Type{
"force_handlers": types.BoolType,
"skip_tags": types.ListType{ElemType: types.StringType},
"start_at_task": types.StringType,
"limit": types.ListType{ElemType: types.StringType},
"tags": types.ListType{ElemType: types.StringType},
"private_keys": types.ListType{ElemType: types.ObjectType{AttrTypes: PrivateKeyModel{}.AttrTypes()}},
"known_hosts": types.ListType{ElemType: types.StringType},
"force_handlers": types.BoolType,
"skip_tags": types.ListType{ElemType: types.StringType},
"start_at_task": types.StringType,
"limit": types.ListType{ElemType: types.StringType},
"tags": types.ListType{ElemType: types.StringType},
"private_keys": types.ListType{ElemType: types.ObjectType{AttrTypes: PrivateKeyModel{}.AttrTypes()}},
"known_hosts": types.ListType{ElemType: types.StringType},
"host_key_checking": types.BoolType,
}
}

func (AnsibleOptionsModel) Defaults() basetypes.ObjectValue {
return types.ObjectValueMust(
AnsibleOptionsModel{}.AttrTypes(),
map[string]attr.Value{
"force_handlers": types.BoolNull(),
"skip_tags": types.ListNull(types.StringType),
"start_at_task": types.StringNull(),
"limit": types.ListNull(types.StringType),
"tags": types.ListNull(types.StringType),
"private_keys": types.ListNull(types.ObjectType{AttrTypes: PrivateKeyModel{}.AttrTypes()}),
"known_hosts": types.ListUnknown(types.StringType),
"force_handlers": types.BoolNull(),
"skip_tags": types.ListNull(types.StringType),
"start_at_task": types.StringNull(),
"limit": types.ListNull(types.StringType),
"tags": types.ListNull(types.StringType),
"private_keys": types.ListNull(types.ObjectType{AttrTypes: PrivateKeyModel{}.AttrTypes()}),
"known_hosts": types.ListUnknown(types.StringType),
"host_key_checking": types.BoolNull(),
},
)
}
Expand Down Expand Up @@ -182,6 +185,11 @@ func (m AnsibleOptionsModel) Value(ctx context.Context, options *ansible.Options

options.KnownHosts = m.KnownHosts.IsUnknown() || len(m.KnownHosts.Elements()) > 0

options.HostKeyChecking = m.HostKeyChecking.ValueBool()
if m.HostKeyChecking.IsNull() {
options.HostKeyChecking = ansible.RunnerDefaultHostKeyChecking
}

return diags
}

Expand Down
9 changes: 7 additions & 2 deletions internal/provider/navigator_run_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,15 +379,20 @@ func (d *NavigatorRunDataSource) Schema(ctx context.Context, req datasource.Sche
},
},
"known_hosts": schema.ListAttribute{
Description: fmt.Sprintf("SSH known host entries. Can help protect against man-in-the-middle attacks by verifying the identity of hosts. Ansible variable '%s' set to path of 'known_hosts' file. If unspecified will be set to contents of 'known_hosts' file after run.", ansible.SSHKnownHostsFileVar),
MarkdownDescription: fmt.Sprintf("SSH known host entries. Can help protect against man-in-the-middle attacks by verifying the identity of hosts. Ansible variable `%s` set to path of `known_hosts` file. If unspecified will be set to contents of `known_hosts` file after run.", ansible.SSHKnownHostsFileVar),
Description: fmt.Sprintf("SSH known host entries. Ansible variable '%s' set to path of 'known_hosts' file and SSH option 'UserKnownHostsFile' must be configured to said path. Defaults to all of the 'known_hosts' entries recorded.", ansible.SSHKnownHostsFileVar),
MarkdownDescription: fmt.Sprintf("SSH known host entries. Ansible variable `%s` set to path of `known_hosts` file and SSH option `UserKnownHostsFile` must be configured to said path. Defaults to all of the `known_hosts` entries recorded.", ansible.SSHKnownHostsFileVar),
Optional: true,
Computed: true,
ElementType: types.StringType,
Validators: []validator.List{
listvalidator.ValueStringsAre(stringIsSSHKnownHost()),
},
},
"host_key_checking": schema.BoolAttribute{
Description: fmt.Sprintf("SSH host key checking. Can help protect against man-in-the-middle attacks by verifying the identity of hosts. Ansible runner (library used by '%s') defaults this option to '%t' explicitly.", ansible.NavigatorProgram, ansible.RunnerDefaultHostKeyChecking),
MarkdownDescription: fmt.Sprintf("SSH host key checking. Can help protect against man-in-the-middle attacks by verifying the identity of hosts. Ansible runner (library used by `%s`) defaults this option to `%t` explicitly.", ansible.NavigatorProgram, ansible.RunnerDefaultHostKeyChecking),
Optional: true,
},
},
},
"timezone": schema.StringAttribute{
Expand Down
9 changes: 7 additions & 2 deletions internal/provider/navigator_run_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,15 +341,20 @@ func (r *NavigatorRunResource) Schema(ctx context.Context, req resource.SchemaRe
},
},
"known_hosts": schema.ListAttribute{
Description: fmt.Sprintf("SSH known host entries. Can help protect against man-in-the-middle attacks by verifying the identity of hosts. Ansible variable '%s' set to path of 'known_hosts' file. If unspecified will be set to contents of 'known_hosts' file after run.", ansible.SSHKnownHostsFileVar),
MarkdownDescription: fmt.Sprintf("SSH known host entries. Can help protect against man-in-the-middle attacks by verifying the identity of hosts. Ansible variable `%s` set to path of `known_hosts` file. If unspecified will be set to contents of `known_hosts` file after run.", ansible.SSHKnownHostsFileVar),
Description: fmt.Sprintf("SSH known host entries. Ansible variable '%s' set to path of 'known_hosts' file and SSH option 'UserKnownHostsFile' must be configured to said path. Defaults to all of the 'known_hosts' entries recorded.", ansible.SSHKnownHostsFileVar),
MarkdownDescription: fmt.Sprintf("SSH known host entries. Ansible variable `%s` set to path of `known_hosts` file and SSH option `UserKnownHostsFile` must be configured to said path. Defaults to all of the `known_hosts` entries recorded.", ansible.SSHKnownHostsFileVar),
Optional: true,
Computed: true,
ElementType: types.StringType,
Validators: []validator.List{
listvalidator.ValueStringsAre(stringIsSSHKnownHost()),
},
},
"host_key_checking": schema.BoolAttribute{
Description: fmt.Sprintf("SSH host key checking. Can help protect against man-in-the-middle attacks by verifying the identity of hosts. Ansible runner (library used by '%s') defaults this option to '%t' explicitly.", ansible.NavigatorProgram, ansible.RunnerDefaultHostKeyChecking),
MarkdownDescription: fmt.Sprintf("SSH host key checking. Can help protect against man-in-the-middle attacks by verifying the identity of hosts. Ansible runner (library used by `%s`) defaults this option to `%t` explicitly.", ansible.NavigatorProgram, ansible.RunnerDefaultHostKeyChecking),
Optional: true,
},
},
},
"timezone": schema.StringAttribute{
Expand Down
21 changes: 21 additions & 0 deletions internal/provider/navigator_run_resource_errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,27 @@ func TestAccNavigatorRunResource_errors_command_output(t *testing.T) {
})
}

func TestAccNavigatorRunResource_errors_host_key_checking(t *testing.T) {
t.Parallel()

_, serverPrivateKey := testSSHKeygen(t)
port := testSSHServer(t, "", serverPrivateKey)

resource.Test(t, resource.TestCase{
PreCheck: func() { testPreCheck(t) },
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testTerraformFile(t, filepath.Join("navigator_run_resource", "errors", "host_key_checking")),
ConfigVariables: testConfigVariables(t, config.Variables{
"ssh_port": config.IntegerVariable(port),
}),
ExpectError: regexp.MustCompile("Host key verification failed"),
},
},
})
}

func TestAccNavigatorRunResource_errors(t *testing.T) {
t.Parallel()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ data "ansible_navigator_run" "test" {
"--net=host",
]
}
ansible_options = {}
ansible_options = {
host_key_checking = true
}
}

variable "ssh_port" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ data "ansible_navigator_run" "test" {
known_hosts = [
"[127.0.0.1]:${var.ssh_port} ${var.server_public_key_data}",
]
host_key_checking = true
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
resource "ansible_navigator_run" "test" {
ansible_navigator_binary = var.ansible_navigator_binary
playbook = <<-EOT
- hosts: test
gather_facts: false
become: false
tasks:
- ansible.builtin.raw: test
register: connect
EOT
inventory = yamlencode({
all = {
hosts = {
test = {
ansible_host = "127.0.0.1"
ansible_port = var.ssh_port
ansible_ssh_common_args = "-o StrictHostKeyChecking=yes -o UserKnownHostsFile={{ ansible_ssh_known_hosts_file }}"
}
}
}
})
execution_environment = {
container_options = [
"--net=host",
]
}
ansible_options = {
host_key_checking = true
}
}

variable "ssh_port" {
type = number
nullable = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ resource "ansible_navigator_run" "test" {
"--net=host",
]
}
ansible_options = {
host_key_checking = true
}
}

variable "ssh_port" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ resource "ansible_navigator_run" "test" {
known_hosts = [
"[127.0.0.1]:${var.ssh_port} ${var.server_public_key_data}",
]
host_key_checking = true
}
}

Expand Down
7 changes: 4 additions & 3 deletions pkg/ansible/navigator.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import (
)

const (
NavigatorProgram = "ansible-navigator"
PlaybookProgram = "ansible-playbook"
ContainerEngineAuto = "auto"
NavigatorProgram = "ansible-navigator"
PlaybookProgram = "ansible-playbook"
RunnerDefaultHostKeyChecking = false
ContainerEngineAuto = "auto"
)

var (
Expand Down
25 changes: 16 additions & 9 deletions pkg/ansible/navigator_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ const (
)

type Options struct {
ForceHandlers bool
SkipTags []string
StartAtTask string
Limit []string
Tags []string
PrivateKeys []string
KnownHosts bool
ForceHandlers bool
SkipTags []string
StartAtTask string
Limit []string
Tags []string
PrivateKeys []string
KnownHosts bool
HostKeyChecking bool
}

func GenerateNavigatorRunCommand(runDir string, workingDir string, ansibleNavigatorBinary string, eeEnabled bool, options *Options) *exec.Cmd {
Expand All @@ -42,7 +43,10 @@ func GenerateNavigatorRunCommand(runDir string, workingDir string, ansibleNaviga
command.Dir = workingDir

// TODO allow setting env vars directly for when EE is disabled
command.Env = append(os.Environ(), fmt.Sprintf("ANSIBLE_NAVIGATOR_CONFIG=%s", filepath.Join(runDir, navigatorSettingsFilename)))
command.Env = append(
os.Environ(),
fmt.Sprintf("ANSIBLE_NAVIGATOR_CONFIG=%s", filepath.Join(runDir, navigatorSettingsFilename)),
)
command.WaitDelay = commandWaitDelay

if options.ForceHandlers {
Expand Down Expand Up @@ -70,10 +74,13 @@ func GenerateNavigatorRunCommand(runDir string, workingDir string, ansibleNaviga
}

if options.KnownHosts {
command.Env = append(command.Env, "ANSIBLE_HOST_KEY_CHECKING=true") // ansible-runner sets to false if unset
command.Args = append(command.Args, "--extra-vars", fmt.Sprintf("%s=%s", SSHKnownHostsFileVar, knownHostsPath(runDir, eeEnabled)))
}

if options.HostKeyChecking != RunnerDefaultHostKeyChecking { //nolint:gosimple
command.Env = append(command.Env, fmt.Sprintf("ANSIBLE_HOST_KEY_CHECKING=%t", options.HostKeyChecking))
}

return command
}

Expand Down

0 comments on commit e0ab832

Please sign in to comment.