Ansible step zero

In my previous article I showed the steps to take to build an ansible repository that you could grow to fit your existing infrastructure. The first step here to setup the repository that you built to self-bootstrap. For that you’ll need to flesh out your inventory and build your first playbook.

Building Inventory

Ansible is driven off of an inventory. The inventory specifies the elements of your infrastructure and the groups them. This is to make things easy to manage. Ansible is compatible with three kinds of inventory: Inventory specified as a Windows style .ini formatted static file, or specified in a yaml file, or finally specified dynamically. Dynamic inventory is the holy grail. I recommend starting with a yaml inventory.

Although both yaml and ini style inventories have roughly the same capabilities, I prefer yaml because if you work with ansible, you’re going to become good friends with yaml no matter what. If you aren’t familiar with yaml format, find some time to study it. yaml is just a markup format that allows you to structure things. I didn’t really get yaml until I played with the python yaml module. I realized that yaml, like json, allows you to write python variables into a file in a structured fashion. the python yaml module can read a properly formatted yaml file and will return a python variable containing the contents of the yaml “document” or it can take any python variable, an array, a dict, a static, and write it such that another python program could read it. Yaml differs from json in that it’s generally parseable and readable by human beings. If the consumer of your data is program, use json. If a human is expected to read it, use yaml.

Your starting yaml inventory should look something like this:

---
all:
  children:
    maestro-test:
      vars:
        std_pkg:
          - ansible
          - terraform
          - git
        add_pkg:
          - emacs

      hosts:
        192.168.100.3:
          my_domain: mydomain.com
          my_host: maestro-test

This defines an inventory with one group: maestro-test. It includes one machine at IP address 192.168.100.3. and it defines some variables for the group. This should be stored in an approriately named file:

base-maestro-inventory.yml

In the Ansible directory.

The first playbook

With an inventory, you can build a playbook. The first playbook looks like this:

---
- hosts: maestro-test
  
- tasks:
    - name: Install standard packages
      package:
        name: "{{ item }}"
        state: latest
      with_items: "{{ std_pkg }}"

    - name: Install additional packages
      package:
        name: "{{ item }}"
        state: latest
      with_items: "{{ add_pkg }}"

This should be installed in a file named something like:

base-maestro-playbook.yml

in the Ansible directory. At this point presuming that you have a machine, physical or virtual at 192.168.100.3 into which you can ssh, as root, you can bootstrap your maestro as follows:

chris $ ansible-playbook -i base-maestro-inventory.yml --user root base-maestro-playbook.yml

And that should install the correct packages onto your maestro test box. I’ll revisit this article later to add users.

Getting started with Ansible, et al

For admins, young and old, getting started with orchestration tools like ansible I believe that the wise man’s first move is to create an orchestration workstation. This machine will have: ansible, terraform, git, and  your favorite editor. You are going to use this machine as the basis for infrastructure as code for your organization for the short term future. Basically, you’ll stop using this machine for infrastructure as code once you get to the point where you can repeatably automate the creation and change management of things. At that point the role of this machine will be testing infrastructure changes. And there will be another machine exactly like this one that controls your production infrastructure.

The first thing that this machine should be able to do is replicate itself. That’s a simple task. In Unix terms you are looking at a box that can:

  • allows you to log in via ssh keys
  • allows you to edit the ansible and terraform configurations which
  • are stored in git so that they are version controlled

That really specifies three users, you, ansible, and terraform. Also, as specified before, you need a hand full of packages: ansible, git, and your favorite editor. The whole thing looks pretty similar to this:

chris $ mkdir Ansible
chris $ git init Ansible
chris $ cd Ansible
chris $ mkdir -p files/global group_vars host_vars roles/dot.template/{defaults,files,handlers,tasks,templates,tests}
chris $ find * -type d -exec touch {}/Readme.md \;
chris $ touch Readme.md
chris $ git add . && git commit -m 'Initial revision.'

That builds an ansible configuration as a git repository and checks in the first revision. It also populates the ansible repository with directories that  roughly correspond to ansible best practices. This will be a working repository which you are going to build out to support your infrastructure. You’ll do this by adding inventory, playbooks and roles bespoke to your needs.

More on this later.

Changing VMware Fusion network settings

For those that run VMware Fusion, the “/Library/Preferences/VMware Fusion”  directory on the Mac is a wealth of information.

$ cd /Library/Preferences/VMware\ Fusion/
$ ls -l
total 40
-r--r--r-- 1 root wheel 31 Nov 17 11:01 lastLocationUsed
-rw-r--r-- 1 root wheel 548 May 5 2018 license-fusion-100-e3-201704
-rw-r--r-- 1 root wheel 689 May 5 2018 license-fusion-100-e4-201704
-rw-r--r-- 1 root wheel 547 Dec 6 2013 license-fusion-50-e3-201202
-rw-r--r-- 1 root wheel 547 Apr 10 2014 license-fusion-60-e3-201303
-rw-r--r-- 1 root wheel 547 Oct 31 2014 license-fusion-70-e3-201404
-rw-r--r-- 1 root wheel 688 Oct 25 2014 license-fusion-70-e4-201404
-rw-r--r-- 1 root wheel 547 Jun 23 2016 license-fusion-80-e3-201505
-rw-r--r-- 1 root wheel 740 Nov 3 05:54 networking
-rw-r--r-- 1 root wheel 740 Aug 7 20:23 networking.bak.0
drwxr-xr-x 10 root wheel 340 Nov 17 11:01 thnuclnt
drwxr-xr-x 4 root wheel 136 Dec 6 2013 vmnet1
drwxr-xr-x 7 root wheel 238 Dec 6 2013 vmnet8
$

The license-fusion… files have your license keys as well as other information in them. But today, the jewel for me is the networking file:

$ cat networking
VERSION=1,0
answer VNET_1_DHCP yes
answer VNET_1_DHCP_CFG_HASH E08B... ...D0D8
answer VNET_1_HOSTONLY_NETMASK 255.255.255.0
answer VNET_1_HOSTONLY_SUBNET 172.a.b.0
answer VNET_1_VIRTUAL_ADAPTER yes
answer VNET_8_DHCP yes
answer VNET_8_DHCP_CFG_HASH 2031... ...F498
answer VNET_8_HOSTONLY_NETMASK 255.255.255.0
answer VNET_8_HOSTONLY_SUBNET 10.c.d.0
answer VNET_8_NAT yes
answer VNET_8_VIRTUAL_ADAPTER yes
...

This file defines the networks that your host-only and nat network adapters use. VMware appears smart enough to avoid network collisions e.g. using 192.168.1.0/255 for the NAT adapter at vmnet8 when that’s also the network configured on your home router.

According to this article (VMware login required), simply editing this file and restarting VMware Fusion’s networking component should change the dhcp setting that your machine uses. Any skilled system or network administrator should be able to get their hands around that.

Finally, the lines that specify DHCP hashes appear to be the mechanism that VMware uses to detect changes in the networking file. If you dig deeper, there’s a directory for vmnet1.

$ ls -l vmnet1
total 8
-rw-r--r--  1 root  wheel  1575 Nov 17 11:01 dhcpd.conf
-rw-r--r--  1 root  wheel  1575 Nov 17 11:01 dhcpd.conf.bak
$ cat vmnet1/dhcpd.conf
# Configuration file for ISC 2.0 vmnet-dhcpd operating on vmnet1.
#
# This file was automatically generated by the VMware configuration program.
# See Instructions below if you want to modify it.
#
# We set domain-name-servers to make some DHCP clients happy
# (dhclient as configured in SuSE, TurboLinux, etc.).
# We also supply a domain name to make pump (Red Hat 6.x) happy.
#

###### VMNET DHCP Configuration. Start of "DO NOT MODIFY SECTION" #####
# Modification Instructions: This section of the configuration file contains
# information generated by the configuration program. Do not modify this
# section.
# You are free to modify everything else. Also, this section must start
# on a new line
# This file will get backed up with a different name in the same directory
# if this section is edited and you try to configure DHCP again.

# Written at: 11/17/2018 11:01:21
allow unknown-clients;
default-lease-time 1800; # default is 30 minutes
max-lease-time 7200; # default is 2 hours

subnet 172.a.b.0 netmask 255.255.255.0 {
range 172.a.b.128 172.a.b.254;
option broadcast-address 172.a.b.255;
option domain-name-servers 172.a.b.1;
option domain-name localdomain;
default-lease-time 1800; # default is 30 minutes
max-lease-time 7200; # default is 2 hours
}
host vmnet1 {
hardware ethernet 00:50:56:x:y:z;
fixed-address 172.a.b.1;
option domain-name-servers 0.0.0.0;
option domain-name "";
}
####### VMNET DHCP Configuration. End of "DO NOT MODIFY SECTION" #######
$

 

This is just a standard dhcpd.conf file as you would see if you ran isc-dhcpd. The interesting thing is that the hash is what you get if you do this:
$ sed -ne '/VMNET DHCP.*Start/,/VMNET DHCP.*End/ p' vmnet1/dhcpd.conf | shasum
e08b... ...d0d8 -

The more you know…

Testing python scripts

You have a python script that has some unit tests that you want to run:

python -c "m = __import__("my-script"); m.unittest()"

Runs the unittest() function within your script. N.B. this still works if your script has a hypen in the name which is my style.

Mutt account passwords

First, to give credit where it’s due, I started here. That said, here’s how I store and access account passwords in mutt on Linux.

## -- Passwords: encrypted by gpg --------------------------------------------------------------

source “/bin/gpg -d ~/.keychain/mutt.password.neopost.gpg 2>/dev/null |”

The source line in gpg tells mutt to decrypt a file at startup. The file .keychain/mutt… contains two mutt configuration lines:

set imap_pass = "<my_email_password>"
set smtp_pass = "<my_email_password>"

I created it as follows:

$ cat <<EOF | gpg -r <my_gpg_id> ~/.keychain/mutt.password.neopost.gpg
set imap_pass = "<my_email_password>"
set smtp_pass = "<my_email_password>"
EOF
$

Gpg knows how to decrypt this file and retrieve the plain text configuration. Note well that I used a “Here” document to create the file. This keeps mail password out of the filesystem. Simple stuff, at mutt startup the first time I use it, gpg-agent asks for my gpg key and unlocks the configuration snippet.

Submission brutes

Brush aside vandals attacking my submission daemon with a little sed:


submission_brutes=$(bzcat /var/log/maillog.0.bz2 | \
cat - /var/log/maillog | \
sed -Ene '/postfix\/submission\/smtpd.*errors after AUTH/s/^.*[^0-9]+(([0-9]+\.){3}[0-9]*).*$/\1/p' | sort -u)
[[ ! -z "${submission_brutes}" ]] && pfctl -t blackhole -T add ${submission_brutes}

pf required: pass proto ipv6-frag all

FreeBSD’s pf has serious problems with ipv6 fragment handling. The problems cascade into other issues like named axfr time outs.  Add this, “pass proto ipv6-frag all”, to your ruleset somewhere near your antispoof rules to fix this.

Much of the issue is that the FreeBSD team has diverged their version of the pf firewall so far from the OpenBSD version that they cannot incorporate upstream fixes. I’m not making my situation any better by sticking with FreeBSD 9. Some of this is probably addressed in FreeBSD 10.

While this persists the best course of action is probably to make sure it works on OpenBSD first, then figure out how to deal with any FreeBSD issues.

More WiFi isn’t always better WiFi

I generally like my Cable TV company. This is mostly because they have realized that they won’t be a Cable TV company in ten years and they are running their business according to that mantra. In English, this means that they are putting their effort in being a very very good consumer grade ISP. One of the side benefits that Cable Internet has been offering is access to public hotspots. As a consortium these companies have nearly every urban area that I’ve been to covered in WiFi. On the surface this seems to be a good thing but it has some downsides. Most WiFi uses the 2.4GHz frequency band. WiFi routers ship with a range in open air of about 450m. There are only three non-overlapping channels in the 2.4GHz band. If you’ve debugged WiFi in an urban setting, you are probably painfully aware of these three characteristics of 2.4GHz WiFi. When they Cable TV company starts deploying open hotspots in your neighborhood, they aren’t pushing the system to a solution for this problem.

I have to qualify this as a rant though since there isn’t much that anyone can do to fix the problem. Especially since the Cable TV companies have started enhancing their WiFi offering by giving away high powered WiFi routers which that offer dual SSID to their customers. It won’t be long before anyone who want’s to do anything will be in 5GHz.