How to dynamically change sudoers

465 views
Skip to first unread message

James Perry

unread,
Apr 21, 2017, 4:10:07 PM4/21/17
to Puppet Users
I'm at an impasse. 

Due to changing requirements we have different local service accounts being added 'ad hoc' to various servers. Each needs their own set of sudoers lines.  When moving from Puppet 0.25 to Puppet 4 I had to kludge something together in a hurry. It works, but not well. 

I looked at defining classes for each set of lines that needed to be added and have it create a separate file for that class in /etc/sudoers.d/.  Due to SOX compliance we can't have any sudo permissions defined for accounts not on the server. So if i remove the class that creates /etc/sudoers.d/foo, the /etc/sudoers.d/foo file still remains. If I try to clean out all non-needed files, I either have to do:
  1. Remove all files, but that causes Puppet to always recreate the files.
  2. Create some way to remove a file based on knowing if the class is defined for this node, which forum posts show as problematic. 

I did see the Puppet-concat module, but haven't had the time to really dig into it to see if the would solve the problem. In this case it would be modifying / creating the main sudoers file, which is fine. 

Another option would be to use something like file_line to make sure a specific line(s) are in the sudoers file after the initial template creates our default /etc/sudoers file. 

Has anyone solved this type of issue?  I know there are ways to do it, but I really want to do it right and forget it. Wen we need a new sudo setup for a new account, we create the required class and the rest is "magic" based on the classes defined for that node. 

In the mean time I will be doing more deep Google dives and serious RTFM. 

Thanks! 

Rob Nelson

unread,
Apr 21, 2017, 4:19:43 PM4/21/17
to puppet...@googlegroups.com
Check out saz/sudo (https://forge.puppet.com/saz/sudo). By default it manages /etc/sudoers.d with `sudo::conf` instances and purges /etc/sudoers.d of anything it didn't create, but if something else is managing files in that directory you can set `sudo::purge: false` so they can share nicely.

--
You received this message because you are subscribed to the Google Groups "Puppet Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to puppet-users+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/puppet-users/db9fabde-a539-4e8a-97b7-b160387df942%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

James Perry

unread,
Apr 21, 2017, 5:33:38 PM4/21/17
to Puppet Users
Thanks. I looked at saz/sudo, but at least they I did it, it didn't for my needs. We have a wide range of hosts that would have oracle, dba and tomcat sudo rules. On another it would only have dba rules.  

I didn't quite get how I would have it setup the sudo::conf blocks to do what I would need. For example one host would have classes that define a content block for dba sudo permissions. Another for oracle's permissions, etc. Based on the classes assigned to the node I would want to have it make the required files with the needed content. 

Besides the examples in the README.md for the saz/sudo module, could post some code that would do something similar to what I need using the saz/sudo module? It is highly likely I'm just not interpreting the doc correctly.

Thanks!

James Perry

unread,
Apr 21, 2017, 5:36:15 PM4/21/17
to Puppet Users
BTW. I am running Foreman 1.14.3 and Puppet 4. All class assignments to nodes are done via Foreman versus the site.pp. 

John Gelnaw

unread,
Apr 21, 2017, 6:43:40 PM4/21/17
to Puppet Users
I use a template for a single /etc/sudoers:

# /etc/sudoers #
# This file MUST be edited with the 'visudo' command as root.
#
# Of course, if you do, Puppet will completely rewrite it 30 minutes later.
#
 
Defaults        env_reset
 
<% unless @cmd_aliases.empty? -%>
# Cmnd alias specification
 
<%- @cmd_aliases.each_pair do |key, val| -%>
Cmnd_Alias <%= key %> = <%= val %>
<%- end -%>
<% end -%>
 
<% unless @host_aliases.empty? -%>
# Host alias specification
 
<%- @host_aliases.each_pair do |key, val| -%>
Host_Alias <%= key %> = <%= val %>
<%- end -%>
<% end -%>
 
<%- if @sudoers -%>
# User alias specification
 
<%- @sudoers.each_pair do |key, val| -%>
User_Alias <%= key %>   = <%= val %>
<%- end -%>
<% end -%>
 
<%- if @user_priv -%>
# User Privilege Specifications
 
<%- @user_priv.each_pair do |key, val| -%>
<%= key %>      <%= val %>
<%- end -%>
<% end -%>

And then in YAML I use a fairly primitive definition and 
load up the variables using a deep merge:

sudo::user_aliases:
  NOPWD
:
   
- user1
sudo
::user_priv:
  NOPWD
:
   
- "ALL = NOPASSWD: ALL"


James Perry

unread,
Apr 21, 2017, 6:49:08 PM4/21/17
to Puppet Users
I will look into that John, thanks.  I haven't gotten to the Yaml level yet, but we already have a temple we use now that is standard across the OS we support. We then add in lines accordingly.  

I had some luck with the sudo::config setup, so I may try to merge the two. With having a class per user I can't re-define the sudo class in each so I had to do an include instead. It might do what I need. Your template does give me some ideas though to better tweak my own. 

Rob Nelson

unread,
Apr 23, 2017, 8:45:31 PM4/23/17
to puppet...@googlegroups.com
James,

Sure, I've whipped up a gist for this in the past at https://gist.github.com/rnelson0/f40719c787639a94d81e23340c5d063b. By setting a deep merge on the key profile::base::linux::sudo_confs, I can add to its hash value wherever I want in my hierarchy and a new sudoers.d configuration snippet is added to the target system. All nodes receive the sysadmin snippet, anything with the `infrastructure` role receives both the sysadmin and the infrastructure snippet. That is all you need to get started with saz/sudo, but I'm sure there's other functionality if you need it.

--
You received this message because you are subscribed to the Google Groups "Puppet Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to puppet-users+unsubscribe@googlegroups.com.

James Perry

unread,
Apr 23, 2017, 10:36:54 PM4/23/17
to puppet...@googlegroups.com

Thanks. That is probably definitely easier than what I planned to try to hack into place.


You received this message because you are subscribed to a topic in the Google Groups "Puppet Users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/puppet-users/zP9zSqbF84M/unsubscribe.
To unsubscribe from this group and all its topics, send an email to puppet-users+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/puppet-users/CAC76iT__BRv5K6bupusZ7DS5KGMZ0g-JpL_7xjqhb3zOxU7HpQ%40mail.gmail.com.

James Perry

unread,
Apr 26, 2017, 1:45:49 PM4/26/17
to Puppet Users
What I seem to be missing is do you have subsequent classes called sudo::user_alias and sudo::user_priv? I get the Hiera yaml file setup, not how to use them selectively to have them added to the sudoers file. 

Thanks!

James Perry

unread,
Apr 26, 2017, 2:45:40 PM4/26/17
to Puppet Users
I guess I should have running a Foreman / Puppet setup, so I am trying to keep from having to go creating <node>.yaml files as well as editing things like site.pp and other files. 

The object I am trying to accomplish is on the KISS principal.

So for example my sudoers.erb has an array block for sudoers_extra_lines that adds a block for a specific user. It is kludgy. I have a case on hostname and it then sets the associated sudo_extra_lines to the sudo::sudo_lines::<user>.  I have also done an array on this to have userX and userQ

Since all of our Puppet code is in a source code repo and requires a change control to push to PROD, I don;t want to have to manually create a per host entry, either via the case statement or a node.yaml file as that requires a full regression test and verification before it moves to PROD. 

Via Foreman I can add puppet classes for userX and userQ to a specific server. As long as sudo::sudoers::userX and sudo::sudoers::userQ are defined in the Puppet code, then no change to modify code or custom hiera yaml files is required. This takes the sudo setups from having to be done per node in code to a point and click for the team that handles the tickets for the host definitions in Foreman. 

I'm not a Puppet coding expert, or even consider I can admit to doing more than basic hacking of other's code.

John's suggestions on a template is similar to what I have now, but requires a way to dynamically build the needed arrays. 

Rob's details on the way to do it via yaml are equally beneficial.

Unfortunately something isn't clicking for me somewhere to bridge the gap between the Puppet Docs and how to make this work. 

If anyone has a better reference for non-expert Puppet programmers to better dumb this down it would be greatly appreciated.

Rob Nelson

unread,
Apr 26, 2017, 3:54:24 PM4/26/17
to puppet...@googlegroups.com
On Wed, Apr 26, 2017 at 10:45 AM James Perry <jjpe...@gmail.com> wrote:
Since all of our Puppet code is in a source code repo and requires a change control to push to PROD, I don;t want to have to manually create a per host entry, either via the case statement or a node.yaml file as that requires a full regression test and verification before it moves to PROD. 

Via Foreman I can add puppet classes for userX and userQ to a specific server. As long as sudo::sudoers::userX and sudo::sudoers::userQ are defined in the Puppet code, then no change to modify code or custom hiera yaml files is required. This takes the sudo setups from having to be done per node in code to a point and click for the team that handles the tickets for the host definitions in Foreman. 

This is a complete aside to sudo, but I think your controls here do not operate as you expect them to. Foreman, like hiera, is just separating your data from your code, which is great. But changing data in either system can have adverse effects in production. For example, I once changed the value for an nfs exports list from an array to a string. That ... did not go well! If only an integration test had been used to catch that, I could have avoided a small outage and a remediation change. 

Personally, I prefer hiera to foreman or the PE Console classifier because it's integrated with version control of the control repo and into my test setup. But the point is, we use the same controls on data as code because they have similar potentials for impacts in production. You may want to revisit your controls, even if it's just to acknowledge the risk. 
--
Rob Nelson

James Perry

unread,
Apr 26, 2017, 4:47:49 PM4/26/17
to Puppet Users
Thanks. I will look at that as we move forward.  

To your NFS issue, that was one of the design decisions we made. I have 3 GIT repos (Dev, Stage, Prod). The Stage and Prod are on the same server. On dev I have every OS we support connected to it to run through all code before we push to Stage.  With Foreman it is easy enough to move a host from PROD to Stage and then test the changes. We move hosts to Stage so we test across the board. Once all testing details are clean and the team  / customer approves, we do a change to push into PROD. 

Our old setup was on a single environment with Puppet 0.25 and a monolithic build. We had a lot of admins hacking the code with no version management. many with no understanding of puppet or even checking their syntax. 

As for Foreman we chose it to keep the first level admins from actually messing with code / configs. They trashed a lot of setups when we let them do that on the old config. 
Reply all
Reply to author
Forward
0 new messages