When going to a new environment, it would be nice if someone had already thought out the networking and authentication needs. It would be nice if we had a sane DNS solution and a good LDAP server. Regrettably, Life in operations is not always that nice. We are frequently asked to quickly set up a test environment to test/sandbox/proof-of-concept something. When the test environment inevitably becomes the production environment, we need the following common Ansible roles:

Fake DNS

It is common for us to fake real DNS by filling in /etc/hosts files. Take care that the FQDN you want is the first one in the list. We have two goals with this role:

  1. Get IP addresses from the Ansible inventory
    Example inventory:
    
    [jenkins]
     192.168.1.2 hostname="jenkins-master1"
     
     [sonar]
     192.168.1.3 hostname="sonar1"
     
     [nexus]
     192.168.1.4 hostname="nexus1"
     
     [subdomain:children]
     jenkins
     sonar
     nexus
     
     [subdomain:vars]
     domain="devops.local"
  2. Allow for aliases (fake CNAMEs) in group variables
    Example Group Variable:

    etc_hosts_alias:
      192.168.1.2: [ jenkins ]
      192.168.1.3: [ sonar ]
      192.168.1.4: [ nexus ]

Pulling in IP addresses from the inventory allows us to abide by the DRY principle.

For a couple of reasons, the real work gets done in the custom Jinja2 filters. At first, I was tempted to push some of these dictionary merges to the template, but fighting that read-only scoping is a losing battle and just makes the template unnecessarily complicated. Once we are willing to offload the hard work to python, our tasks are easier to read because they have more descriptive function calls (the filter names), and the template looks more like a template because we can focus on layout and not variable manipulation.

The expected result from the template (/etc/hosts):

 192.168.1.2 jenkins-master1.devops-local jenkins-master1 jenkins-master
 192.168.1.3 sonar1.devops-local sonar1 sonar
 192.168.1.4 nexus1.devops-local nexus1 nexus

Users with SSH key authentication

While it is better to have centralized authentication in the new environment, setting up an LDAP server may take a while (especially if another team is responsible). So during those intervening months, security will probably let you get away with pushing out ssh keys instead. For this role, I had three goals:

  1. Create a user who can be authenticated with ssh keys
  2. Modify “password” expiration because PAM will prevent login when the password (that the user doesn’t have… HAH!) “expires”.
  3. Give admin privileges (sudo) with group membership

I’d like to be able to list my users as ansible facts like so:

users:
   jmalachowski:
 key: ssh-rsa AAAAB3Nza…== jmalacho@example.com
 groups: [ 'wheel' ]
 users_blacklist: [ 'centos ']

The only challenge for this is setting relative expiration because the only program I know that does it, “chage,” required the argument in “days since the linux Epoc.” I was able to find some python that could calculate this value, so into a filter it goes!

Standard Security Update

Security updating is always one of my favorite examples since package updating and configuration file templating are usually all we need. However, when certain OS packages are updated, we also need to reboot. Rebooting is something that ansible is better suited for than its competitors which run inside the machine. For the final feature in this role, we are going to wait for the machine to come back from reboot. At that point, we can begin validating / health checking the environment.

 

X