Authorized_key for multiple keys (contribution proposal)

Dear all,

I am new to this list, so sorry in advance for any mistakes.

I have been using the Ansible Python API to develop a simple tool that manages server access for our infrastructure. I am using the authorized_key module for that. One improvement I would like to make is to manage list of keys per user instead of managing on a key per key basis. That would also allow to add a security option to enforce all the keys and ONLY those to be present on the server.

When digging into the code I realise that the module somehow handles list of keys but in a strange way, since its primary purpose is to manage a single key.

Two questions for you, honourable ansible-devel member:

  • should I try a pull request on the authorized_key module for this need or should i start a new module authorized_keys ?
  • how would you name the “all the keys must be present and ONLY those keys” state? The current states are “present” or “absent”. Possible choices could be:
  • “replace”?
  • “exclusive”?
  • “only”?

Thank you for your feedback!

Fabrice

To answer a message that has been sent to me in private (thanks for the feedback): “Why not directly manage the authorized_keys file instead of going through the authorized_key module if you want to manage all keys at once?”. It is a good question… I guess for one project it is not worth doing more than just pushing the authorized_keys file. In my case where I am managing different servers for different ssh users and different keys, I think I will find it useful to benefit from the power of the authorized_key module with useful functions like “keyfile” and “parse_options”.

As for the option name to replace all keys, after some thought I was thinking about calling it “replace_all” which seems self-explanatory to me.

What do you think?

Fabrice

“One improvement I would like to make is to manage list of keys per user instead of managing on a key per key basis.”

This is commonly done by having a user datastructure and a list of keys.

Example:

users:

  • name: user1
    keys:

  • user1.pub
    groups:

  • admins

  • webmasters

  • name: authorized_keys
    authorized_key: >
    user={{ item.0.name }}
    key=“{{ lookup(‘file’, ‘…/…/…/keys/ssh/’ + item.0.name + ‘.pub’) }}”
    manage_dir=yes
    with_subelements:

  • users

  • keys

In the above, what additional capabilities would you be looking for?

Hi Michael,

thanks for the feedback. To answer your question I could do something like
this already today:

users:
  - name: www-data
    keys:
      - ssh-rsa AAAABXXX..XXXIRSffY5+++j
       - ssh-rsa BAAABXXX..XXXIRSffY5+++j
       - ssh-rsa CAAABXXX..XXXIRSffY5+++j
    groups:
      - admins
      - webmasters

- name: authorized_keys
  authorized_key: >
    user={{ item.name }}
    key="{{ item.keys | join("\n") }}" # this already works in the code
but is not documented
    state = "replace_all" # this is an option I want to
implement
  with_items:
    - users

(Sorry if I have made a mistake, I use more the Ansible Python API at the
moment)

I wanted to do something like this and then realised it already worked in
the code even though it was not documented! But I also had a hard time
reading the current code, certainly because the code grew organically and
the list was hacked on top at some point. It needed some refactoring and
this is the object of my current pull request: assume that key can be a
list and make the code much more readable around that as a first step.

And then in a second step add features like the state "replace_all" for
example

What do you think ?

Best wishes,

Fabrice

Theodo

48, boulevard des Batignolles
75017 PARIS
Fabrice Bernhard

Fondateur et Directeur Technique
fabriceb@theodo.fr
Tél : +33 6 23 52 11 95

Without changing how authorized_keys processes loops, the replace_all wouldn’t do what you want. Here it would replace all except for the last item in the loop, so you’d be left with a file with only one key in it.

-jlk

Hi Jesse,

the feature “replace_all” does not exist yet.

The way I want to implement it is simple, it would write the authorized_keys file with the new keys without reusing the former existing keys. It fits in less than five additional lines of code (if my refactoring pull requests gets accepted).

Fabrice

So you’re changing how the task authorized_keys deals with with_items, to make it more like apt and yum modules, where the entire list is bundled up and transferred over for one module execution? this is different than other modules where every item in the list results in a new invocation of the module, with just that one item passed along as an argument.

-jlk

this is in the queue and seems to do what you ask.
https://github.com/ansible/ansible-modules-core/pull/42

in regards to yum: it makes it faster, but it is highly anoying that it is different then other modules in this sense

Hi Jesse and Jonathan,

my example is confusing because I was answering to Michael who was himself responding to my more complicated than average usecase. But to answer your question: no it is not about refactoring a list like for yum or apt. To illustrate this, here is a simple usecase which is already supported but not documented:

keys:

  • ssh-rsa AAAABXXX…XXXIRSffY5+++j

  • ssh-rsa BAAABXXX…XXXIRSffY5+++j

  • ssh-rsa CAAABXXX…XXXIRSffY5+++j

  • name: authorized_keys
    authorized_key: >
    user=“www-data”
    key=“{{ keys | join(”\n") }}" # this already works in the code but is not documented

This is possible because the key option is already split into a list if multiline. As you can see there is no “with_items” involved in the basic use case.

Fabrice

Hi Brian,

thanks for the pull request, I had not seen that somebody had already tried to implement the feature.

I added a comment on this pull request because it currently breaks with keys containing commas, which can happen inside the options or comments.

I myself took quite some time to master the current code, this is why I was quite enthusiastic about the way I had managed to make it much more explicit. But I guess this is not a priority unless I make the pull request obvious…

Fabrice

The clear_keys things seems a bit tricky, as if you don’t call it on the first parameter, you end up with clearing all your access away too easily.

It seems to be easier to template the authorized key file for more advanced needs, IMHO, or even just have a copy of that file and use the copy module.

Maybe I’m wrong about this, but it seems safer and easier.

I wish you all a very merry festive season!