maxhoesel-ansible/ansible-collection-smallstep

Bug: `ValueError: No closing quotation` with `step_ca` role

jrsmith3 opened this issue · 5 comments

I am attempting to use the step_ca role to initialize a ca server on a VM. When I attempt to run the role, I get an error that seems to amount to ValueError: No closing quotation. I've tried several different approaches, looked through the source in this repo, and also searched the internet for this error and I'm just not seeing anything. Apologies in advance if I am missing something obvious.

Here is my setup.

Ansible version: core 2.12.2
ansible_python: 3.8.10
Control node: macOS Monterey 12.2.1 (21D62)
Target node: Ubuntu 20.04 (see Vagrantfile below)
ansible_playbook_python: 3.10.2
maxhoesel.smallstep version: 0.4.9 (I've also attempted the HEAD of main in this repo; 08aa754)

Here is the Vagrantfile of the host on which I'm trying to install step ca:

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/focal64"

  # VM definitions
  config.vm.define "certificate_authority" do |machine|
    machine.vm.hostname = "ca-local-dev.local"
  end
end

Here's the ansible play I'm using to install step_ca:

- name: Bootstrap certificate authority using `maxhoesel/smallstep`.
  hosts: ca
  roles:
    - maxhoesel.smallstep.step_ca
  vars:
    step_ca_name: "Homelab CA"
    step_ca_root_password: "an_extremely_secret_value"
  become:
    true

I am happy to post additional error output, but here is what seems to be the relevant part:

The full traceback is:
Traceback (most recent call last):
  File "/var/tmp/ansible-tmp-1646589610.442364-44741-275605781034203/AnsiballZ_command.py", line 107, in <module>
    _ansiballz_main()
  File "/var/tmp/ansible-tmp-1646589610.442364-44741-275605781034203/AnsiballZ_command.py", line 99, in _ansiballz_main
    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
  File "/var/tmp/ansible-tmp-1646589610.442364-44741-275605781034203/AnsiballZ_command.py", line 47, in invoke_module
    runpy.run_module(mod_name='ansible.modules.command', init_globals=dict(_module_fqn='ansible.modules.command', _modlib_path=modlib_path),
  File "/usr/lib/python3.8/runpy.py", line 207, in run_module
    return _run_module_code(code, init_globals, run_name, mod_spec)
  File "/usr/lib/python3.8/runpy.py", line 97, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "/usr/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/tmp/ansible_ansible.legacy.command_payload_gvctg_67/ansible_ansible.legacy.command_payload.zip/ansible/modules/command.py", line 399, in <module>
  File "/tmp/ansible_ansible.legacy.command_payload_gvctg_67/ansible_ansible.legacy.command_payload.zip/ansible/modules/command.py", line 321, in main
  File "/usr/lib/python3.8/shlex.py", line 311, in split
    return list(lex)
  File "/usr/lib/python3.8/shlex.py", line 300, in __next__
    token = self.get_token()
  File "/usr/lib/python3.8/shlex.py", line 109, in get_token
    raw = self.read_token()
  File "/usr/lib/python3.8/shlex.py", line 191, in read_token
    raise ValueError("No closing quotation")
ValueError: No closing quotation
fatal: [certificate_authority]: FAILED! => {
    "changed": false,
    "module_stderr": "Shared connection to 127.0.0.1 closed.\r\n",
    "module_stdout": "Traceback (most recent call last):\r\n  File \"/var/tmp/ansible-tmp-1646589610.442364-44741-275605781034203/AnsiballZ_command.py\", line 107, in <module>\r\n    _ansiballz_main()\r\n  File \"/var/tmp/ansible-tmp-1646589610.442364-44741-275605781034203/AnsiballZ_command.py\", line 99, in _ansiballz_main\r\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\r\n  File \"/var/tmp/ansible-tmp-1646589610.442364-44741-275605781034203/AnsiballZ_command.py\", line 47, in invoke_module\r\n    runpy.run_module(mod_name='ansible.modules.command', init_globals=dict(_module_fqn='ansible.modules.command', _modlib_path=modlib_path),\r\n  File \"/usr/lib/python3.8/runpy.py\", line 207, in run_module\r\n    return _run_module_code(code, init_globals, run_name, mod_spec)\r\n  File \"/usr/lib/python3.8/runpy.py\", line 97, in _run_module_code\r\n    _run_code(code, mod_globals, init_globals,\r\n  File \"/usr/lib/python3.8/runpy.py\", line 87, in _run_code\r\n    exec(code, run_globals)\r\n  File \"/tmp/ansible_ansible.legacy.command_payload_gvctg_67/ansible_ansible.legacy.command_payload.zip/ansible/modules/command.py\", line 399, in <module>\r\n  File \"/tmp/ansible_ansible.legacy.command_payload_gvctg_67/ansible_ansible.legacy.command_payload.zip/ansible/modules/command.py\", line 321, in main\r\n  File \"/usr/lib/python3.8/shlex.py\", line 311, in split\r\n    return list(lex)\r\n  File \"/usr/lib/python3.8/shlex.py\", line 300, in __next__\r\n    token = self.get_token()\r\n  File \"/usr/lib/python3.8/shlex.py\", line 109, in get_token\r\n    raw = self.read_token()\r\n  File \"/usr/lib/python3.8/shlex.py\", line 191, in read_token\r\n    raise ValueError(\"No closing quotation\")\r\nValueError: No closing quotation\r\n",
    "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
    "rc": 1
}

I have also tried replacing the line in roles/step_ca/tasks/init.yml

command: "{{ step_cli_executable }} ca init {{ step_ca_init_args | join(' ') }}"

to

command: "{{ step_cli_executable }} ca init {{ step_ca_init_args | join(' ') | quote }}"

I don't get the error I posted above, but execution seems to just hang with that quote filter. Thanks in advance for any help.

Huh, that's odd. I can't see anything wrong with your configuration, but it seems like something is going very wrong while constructing the parameters for that init command. Could you send me the contents of step_ca_init_args before this action is executed? Just running Ansible in debug mode -vvv flag should be enough.

I won’t be back at my computer until this afternoon (NYC time), but I took a few notes yesterday when I was trying to get this to work. I wrote the following play:

- name: Bootstrap certificate authority using `maxhoesel/smallstep`.
  hosts: ca
  tasks:
    - name: Define the block
      block:
        - name: Attempt to run `maxhoesel.smallstep.step_ca` role.
          ansible.builtin.import_role:
            name: maxhoesel.smallstep.step_ca
      always:
        - name:
          ansible.builtin.debug:
            msg: "{{ step_cli_executable }} ca init {{ step_ca_init_args | join(' ') }}"
  become:
    true

I took the msg value from here, and I got the following result:


TASK [ansible.builtin.debug] ***************************************************
ok: [certificate_authority] => {
    "msg": "step-cli ca init --name='Joshua Ryan Smith's personal CA' --dns='ca-local-dev.local,10.0.2.15' --address=':443' --provisioner='tmp_provisioner' --password-file='/etc/step-ca/.password_root.txt' --provisioner-password-file='/etc/step-ca/.password_root.txt' '' '' '' '' '' '' ''"
}

Oh boy, I see it now. I have an extra ' in the --name argument (I used a dummy value in my original post).

So I think short term, I need to change the name of my CA. Longer term, I think you could throw in a quote filter for the step_ca_name value. I could open a PR to make this change if you like. I probably won’t be able to get to it until this weekend though. Let me know if I should change anything else if you want me to open a PR.

Ah, that explains it! And yea, the current task to generate the init arguments is rather ugly, I don't think I even knew about the quote filter when i wrote it.

I've refactored the task and pushed it to a new branch, can you check whether that fix works for you?

Looks like it worked. Thanks! Any plans to tag a new release with this change?

Also,

the current task to generate the init arguments is rather ugly

I don't know, I thought constructing that list was pretty slick. 😎

Glad to hear that! And yea, I'll be pushing out a new release soon. Still need to finish up a couple of minor things, but with a bit of luck it should be good to tonight.