0

I started to learn Puppet this week and trying hard to implement user's keys to /etc/ssh/sudo_authorized_keys.

I have a dictionary of users with keys in sudo_users.yaml:

core::sudo_users_keys:
  kate:
    keys:
      key1:
        type: "ssh-ed25519"
        key: "AAAAC3N..."
  john:
    keys:
      key1:
        type: "ssh-ed25519"
        key: "AAAAC..."
  marvin:
    keys:
      key1:
        type: "ssh-ed25519"
        key: "AAAAC3Nza..."

Then I create this in sudokeys.pp file:

class core::sudokeys {
    file { "/etc/ssh/sudo_authorized_keys":
      ensure  => file,
      mode    => "0440",
      content => template("core/sudo_authorized_keys.erb"),
    }

As you can see, I want to implement template with iteration. This is my current template:

<%- scope['core::sudo_users_keys'].each | user | -%>
    {
        <%- if user[1] -%>
        <%- $user[1]['keys'].each do | key | -%>
        <%= $key[1]['type'] $key[1]['key'] -%> 
        <%- end end -%>
      }
    <%- end -%>

I have the same dictionary with id_rsa keys to ssh and use interaction as below. It works perfectly for ssh_authorized_key, but I can use it in this case by adding keys to /etc/ssh/sudo_authorized_keys. That's why I decided to use a template and only inject keys inside the sudo_authorized_keys file.

class core::sshkeys {
  lookup("core::sudo_users_keys").each | $user | {
    if $user[1] {
      $user[1]["keys"].each | $key | {
        ssh_authorized_key { "${user[0]}-${key[0]}":
          ensure => present,
          user   => $user[0],
          type   => $key[1]["type"],
          key    => $key[1]["key"],
        }
      }
    }
  }
}

Puppet documentation doesn't include this kind of more complicated iterations and I feel like wandering in the fog.

Currently, I'm getting this error when deploying my template, but I feel like the way I prepare this will not work as I wanted.

Internal Server Error: org.jruby.exceptions.SyntaxError: (SyntaxError) /etc/puppetlabs/code/environments/test/modules/core/templates/sudo_authorized_keys.erb:2: syntax error, unexpected tSTRING_BEG
_erbout.<< "    {\n".freeze
           ^

I will appreciate any suggestions about template construction. What should I change to make it work and extract only type and key values?

2
  • I am not sure if I understand this condition in your code if user[1]. What do you want to check with that? Commented Sep 23, 2022 at 8:13
  • Actually, the only thing I need is the value of key and type from core::sudo_users_keys and put it inside the /etc/ssh/sudo_authorized_keys, so you are probably right that this user[1] is useless here. I'm not sure how to properly access this 2 values that I need. Commented Sep 23, 2022 at 8:50

2 Answers 2

1

Your approach is workable, but there are multiple issues with your code, among them

  • the scope object in a template provides access to Puppet variables, not to Hiera data. Probably the easiest way to address that would be to

    1. give your core::sudokeys class a class parameter to associate with the data ($userkeys, say),
    2. change the Hiera key for the data so that it will be automatically bound to the new class parameter, and
    3. in the template, access the data via the class parameter (which doesn't even require using the scope object in that case).
  • in the template, you seem to be assuming a different structure for your data than it actually has in Hiera. Your data is a hashes of hashes of hashes, but you are accessing parts of it as if there were arrays involved somewhere (user[1], key[1]). Also, if there were one or more arrays, then do be aware that Ruby array indexes start at 0, not at 1.

  • Scriptlet code in an ERB template is (only) Ruby. Your scriptlet code appears to mix Ruby and Puppet syntax. In particular, Ruby variable names are not prefixed with a $, and Ruby block parameters appear inside the block, not outside it.

  • <%= ... %> tags a for outputting the value of one Ruby expression each. You appear to be trying to emit multiple expressions with the same tag.

Also, it's worth noting that scriptlet code can span multiple lines. That can make it clearer. Additionally, you may need to pay attention to whitespace and newlines in your template to get the output you want. ERB has options to consume unwanted whitespace around scriptlet tags, if needed, but sometimes its at least as easy to just avoid putting in whitespace that you don't want. On the other hand, be sure not to suppress whitespace that you actually wanted.

So, here's one form that all of the above might take:

sudo_users.yaml

core::sudokeys::users_keys:
  kate:
    keys:
      key1:
        type: "ssh-ed25519"
        key: "AAAAC3N..."
  john:
    keys:
      key1:
        type: "ssh-ed25519"
        key: "AAAAC..."
  marvin:
    keys:
      key1:
        type: "ssh-ed25519"
        key: "AAAAC3Nza..."

sudokeys.pp

class core::sudokeys($users_keys) {
    file { "/etc/ssh/sudo_authorized_keys":
      ensure  => file,
      mode    => "0440",
      content => template("core/sudo_authorized_keys.erb"),
    }
}

sudo_authorized_keys.erb

<% @users_keys.each do | user |
      if user.has_key?('keys') do
        user['keys'].each do |key|
          %><%= key['type'] %> <%= key['key'] %>
<%        # don't consume preceding whitespace here
        end
      end
    end
%>
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for the exclamation! Looks good, but there is some problem with template as I'm getting: sudo_authorized_keys.erb:9: syntax error, unexpected end-of-file. I will try to fix it and update the proper template here for others.
Ok, I was trying to fix this template, but really have no idea what is wrong. I was trying to add <% and %> in every line, merge ends to one line <% end end end %> and many other approaches, but still getting syntax error, unexpected end-of-file _erbout. This template looks legit, so I don't even know what to change to make it work.
@Brzozova, that error message probably means that you have one or more unclosed tags in your ERB. That could arise from omitting or mistyping one of the %> scriptlet closures, or possibly from a missing or mismatched quotation mark (' or "). I'm pretty sure none of those issues appear in the template code posted in this answer, but if ERB disagrees then the code comment is the first thing I would remove.
I copy pasted your code, so strange, but nvm. I will try to find out what's wrong.
0

I used John Bollinger answer, however template wasn't perfect in my case, because I was constantly getting errors like eg. syntax error, unexpected end-of-file _erbout.

The proper sudo_authorized_keys.erb file that worked for me is this one:

<%- @users_keys.each do | user, config | -%>
   <%- if config.has_key?('keys') -%>
     <%- config['keys'].each do | name, value | -%>
<%= value['type'] %> <%= value['key'] %> <%= user %>
     <%- end -%>
   <%- end -%>
<%- end -%>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.