Preventing users from changing their password with PAM

Blocking AD users from using passwd

I had to design a system recently for a client which has a mixture of local users and remote users that are authenticated using LDAP against Active Directory (actually, with Quest Authentication Services running in between). One of the requirements was that AD users should not be able to change their password using the passwd command as they had an external management system for users that fed into AD (and other things). I needed to allow normal users to operate normally but fail AD users with some polite message to tell them what was going on, rather than just a random error that would cause them to call support every time. Trolling the web didn’t seem to reveal much apart from doing nasty things to /bin/passwd like chattr’ing it, or moving it to /sbin… since we’re not in the 90’s anymore I was sure there was a way to do this with PAM.

My PAM knowledge is limited to say the least, and maybe my google-fu isn’t up to much because I struggled to find anything that did exactly what I wanted.

Eventually, after some tweaking, I came up with the following which seems to work on CentOS…

Edit /etc/pam.d/passwd and change it to read :-

This should work normally for root and local users but give a warning and fail to AD users.

Follow and share if you liked this

Puppet – working with define based virtuals

Define Based Virtuals

Define-based virtuals are quite a powerful feature of Puppet, but many people either don’t understand them or don’t know how to apply them effectivly to their manifests. Due to Puppet’s normalized configuration structure, you can’t configure the same resource in two different places. For example, if the user mysql is configured in your database class, and then you realise that your webserver class also needs the mysql user, then, short of including the whhole database class, you cannot just configure the user here. The answer to this lies in resource virtualization.

Resources that are virtualized are effectivly non-real, they won’t be included in any configuration until they are explicitly realized. The good news is you can realize your resource multiple times in your manifests, so by defining the resource as virtual in a seperate class, both your webserver class and databasse class can realize it.

Version 0.23 of Puppet took this one step further, with the ability to virtualize your definitions rather than just native resource types. Now we can define a collection of resources, virtualize them, and realize them at whim in our manifests. I’ve seen a few Puppet installations and this method is far under used, and under appreciated.

To demonstrate, this is an example of how to manage your users with virtual define-based resources.

User management with defines and virtuals.

I’ve seen a few ways of managing users within a Puppet estate. The following is a quick guide to get you started managing users, passwords and ssh keys for your users. My favourite approach is to use define-based virtuals and split it out into several sections

  • Back end module to specify the definition for a user
  • A class with a list of virtualized users
  • Realizing your users ad-hoc in your classes

The advantage of this approach is that you can keep a central place for users whilst still being able to easily pick and choose which users get deployed with which classes. Lets break it down into our components;

Users module

As we want to manage users as well as ssh keys it makes sense to wrap this up into a definition, which we’ll call localuser. We create a module called users::virtual where we define the following class.

This class sets up our localuser definition in users::virtual, other resources can also be added here that will affect every user.

Creating our users class.

Now you can create a users.pp class that imports your module and defines the specific users you want to configure. We configure these as virtuals to give us the option of realizing them whenever we want.

The passord should be the fully encrypted string as would appear in the shadow file, don’t forget to use single quotes if your password string contains $ to prevent it from being interpereted as a variable.

sshey is optional (note the conditional in our users module), and, if defined will set up the key in ~/.ssh/authorized_keys. Now you have a central list of users as virtuals that call our custom define, they can be easily included, or realized, at whim in your other classes.

Realize the user

Within your manifests for your server profile, whenever you require the user bobsmith to be part of the configuration, you simply realize it like this

The above example realizes the user by the name of “bobsmith”. Another very useful way to realise users is by using Puppet’s collection syntax. For instance, to realize all the users in the users group, you would simply replace the realize statement with:

… and all your virtual users that match the gid of users will be included.

Conclusion

What we’ve acheived by using virtual define-based resources here is primarily,

  • An abstracted module for defining what a user is, rather than who
  • A central place to manage all of our virtual users
  • A very flexible and easy way to pull users into our classes based on name or attribute

For more information on working with Puppet virtuals, check out the documentation here

Follow and share if you liked this

Part 3: Installing puppet-dashboard on CentOS / Puppet 2.6.1

Puppet Dashboard

Puppet dashboard is a fairly new app with loads of future potential and is great for monitoring your puppet estate. This is a quick guide to getting it running on puppet 2.6.1. Be sure you have the correct yum repos and ruby versions installed, see Part 1 and Part 2 for more details.

Install the puppet-dashboard package.

Create a MySQL database for puppet-dashboard

Create a database for puppet-dashboard to use and set up a user with all privileges to use it. This can be done on a seperate host.

Configure database.yaml

Add your database parameters to the development section, note that host: can be ommitted if you are using local sockets to connect to MySQL.

Migrate the database

Copy reports module to site_ruby



I hate doing this but puppetmasterd explicitly looks for reports in puppet/reports and so far I haven’t found a clean workaround to tell it to look in /usr/share/puppet-dashboard for it. If anyone knows of a way, please email me.

Edit your puppet.conf

Include the following in the [master] section, changing punch.craigdunn.org to your puppet server

Restart puppetmaster and start puppet-dashboard

Test web GUI

Go to the following link in your browser (replacing the hostname with your fqdn)

Configure the client

Edit puppet.conf

Make sure the following things are set in the [agent] section of puppet.conf on your client node.

Run puppet in noop mode on the client

Refresh browser

If all has gone well, you should now see your reports in puppet dashboard for your client node.

Follow and share if you liked this

Part 2: Puppet 2.6.1, configure puppetmaster and puppetd

Configure Puppetmaster

For installing puppetmaster 2.4.1 on CentOS please click here for Part 1

In Part 1 we covered installing the Puppetmaster and Puppetd packages on Centos 5.5. We will now configure a very basic client/server model to serve the /etc/resolv.conf file to our client. Simple enough!

Create your first module

Our first module will be called networking::resolver, it’s job will be to push out a resolve.conf file to clients.

Create the directory structure under /etc/puppet

Create your resolv.conf file

Create your module manifest

Configure your site and nodes

Create a minimal site.pp


Create a tempates file

Create your node file

Don’t forget to replace judy.craigdunn.org with the fqdn of your client server

Set up puppetmaster parameters

Create default configuration

This is a minimal puppet.conf file, a more detailed file can be produced with puppetmasterd –genconfig

The autosign will automatically sign certs for new clients, this is discouraged in a production environment but useful for testing. For information on running puppetmaster without autosign see the puppetca documentation.

Set permissions for your fileserver.
Note that this allows everything, you should restrict this in a production environment.

Start puppetmaster

The puppet client

Configure puppetd
On your client, edit puppet.conf and add the following in the [agent] section, remembering to change punch.craigdunn.org to the fqdn of your Puppetmaster.

Allow puppetrunner

Create a file called namespaceauth.conf and add the following, note in a production environment this should be restricted to the fqdn of your puppet master

Start puppetd

View pending changes

Use –test along with –noop to do a dry run to view the changes that puppetd will make

Now you can run puppetd without –noop to pull in your new resolv.conf file

This is a very basic demonstration of creating a server/client pair with puppet. There is much more documentation on configuring and managing puppet here

Next: Installing Puppet Dashboard

Follow and share if you liked this

Part 1: Installing puppet 2.6.1 on CentOS with YUM/RPM

Installing Puppetmaster 2.6.1

Assuming, like me, the thought of letting rubygems vommit all over your filesystem is not a pleasant one, then how to get the latest puppet 2.6.1 installed on CentOS 5.5 with yum isn’t very clear. Things may differ on other peoples systems, but the below worked for me.

Set up yum repositories.

Do this on both the client and the server
Add the following files and save them to /etc/yum.repos.d/

puppet.repo


epel.repo


ruby.repo


Note that we include ruby and puppetlabs as the next steps in this tutorial will be to configure puppet and install puppet-dashboard. We want to upgrade to ruby 1.8.6 in order to run puppet-dashboard, doing this now will save you some pain down the line.

Upgrade Ruby to 1.8.6

Do this on both the client and the server
As mentioned above, use the ruby repo to upgrade.

Install Puppet Server

On your puppetmaster server:


On your puppet client

That’s it, in part 2 and 3 we will install our client and server and install dashboard.

Part 2 Configuring Puppetmaster and Puppetd

Follow and share if you liked this

Remote control with expect-lite

I’ve recently been playing around with expect-lite, a wrapper for expect that allows you to automatically login to hosts with telnet or ssh to run commands on remote hosts without getting too much under the bonnet of expect syntax.

But for my purposes I needed to do a little tweaking to get it to do what I wanted to do.

The Problem

In this scenario, I need to run a series of commands across an estate of hundreds of servers, as root. I must login as a normal user and sudo is not an option, so that leaves me with becoming root using the expect script – the challenge here is how to accomplish this without storing the root password anywhere, or having it show up in the output of ps.

The solution

A little digging around the code and the method I chose was to have a shell script read my password on stdin, and pass it as an environment variable to expect-lite to use in my script. By default you can’t directly pass environment variables from your local shell to be used remotely by the script without specifically stating them in the command – eg ./expect-lite foo=$BAR – the problem here is the value of $BAR will show up in ps.

Implementation

Modifications to expect-lite

Firstly, we need to make 2 small modifications to the expect-lite script itself. The first is another option to the supplied arguments to allow me to pass an environment variable by name (not value) to the expect script. To do this you can use this small patch


--- expect-lite 2010-07-06 15:21:06.271119000 +0000
+++ expect-lite.new 2010-07-06 15:34:04.490118000 +0000
@@ -416,6 +416,8 @@
u {set user $user_value }
user_password -
p {set pass $user_value }
+ localenv -
+ e { set cli_namespace($user_value) $env($user_value) }
default {
set cli_namespace($user_var) $user_value
puts "Const: $user_var = $cli_namespace($user_var)"

This patch will add an option “e” or “localenv” that tells expect-lite to get that environment variable and make it available in the namespace of the expect script.

Secondly, in order to prevent the root password from being echoed to STDOUT during execution, make sure DEBUG, DEBUG_LOG and INFO are all set to 0 in the script.

Wrapper script

Now for your wrapper script, this script will read your root password without outputting the characters to the terminal and read a list of files from hosts.txt and execute your expect-lite script for each host. Here is an example.


#!/bin/bash

echo -n "Enter root password: "
stty -echo
read rootpwd
echo
stty echo
export rootpwd

for host in cat hosts.txt; do
./expect-lite remote_host=$host c=myscript.elt u=username e=rootpwd
done

Expect script

Now create an expect-lite script called myscript.elt to implement the root password, heres an example


@30
> su -
>$rootpwd
>touch /tmp/testfile.txt

Finally

… create a text file called hosts.txt listing your hosts and run scan.sh, it should prompt you for your root password and go off and execute commands as root on your remote hosts.

Follow and share if you liked this