Getting out of git hell easily

Everyone has war stories about git. They almost always involve letting a feature/topic branch get far out of date from the feature branch’s parent. My friend Sebastian has actually figured out a quick way to get out of what I call git hell.The best way to avoid that situation is incorporate git rebase and git rebase -i into your normal workflow. Basically, use git rebase periodically and right before you submit your merge request to make sure that your branch will cleanly play onto it’s parent integration branch. And use git rebase -i and git push –force if you need it, on your topic branch to keep a concise commit history as you build your topic deltas.

No matter what happens, you or someone on your team will end up in git hell where you are replaying a stack of commits so you can generate a clean commit history and publish your deltas. If you end up there, you need to understand that git merge is actually your friend. Sebastian suggests the following:

## Be safe, do this work on a test branch.

git checkout topic/branch
git checkout -b test-topic/branch

## Make sure the local copy of master is up-to-date.
git checkout master
git pull master

## Go back to your test branch and merge in master.

git checkout test-topic/branch
git merge master

## Reset your state to be that of master. This leaves your changes as 
## an unstaged blob against master.

git reset master

At this point your working directory should have all the changes from your topic branch unstaged against the HEAD of master

git diff

should confirm this. Now you can add what you want and write a new commit that performs the changes that you want comfortable in the knowledge that you aren’t undoing upstream changes. You’ll use git add and git commit to accomplish this. The difference is that your new set of change should apply cleanly to master. From here you can:

## When you are comfortable that you your test branch captures your
## deltas.

git checkout test-topic/branch
git branch -D topic/branch
git checkout -b topic/branch
git push --force
git branch -D test-topic/branch

Sebastian says “Have fun!”

SSL Everywhere? Maybe not cups

Last night I made the aggravating discovery that cups has gone SSL. The option to have cups protected by SSL is wonderful but I’m not sure that SSL by default is a good thing for printing services. I discovered this because printing from my Apple machines was failing with no log messages from my the Apple machines on my network. At first I thought this might be an IPv6 issue. Using tcpdump I quickly determined that cups on my Mac was not only using IPv6 but that it was using the semi-random “private/temporary” address of my cups server. But continued debugging showed that IPv6 wasn’t the issue, and the private/temporary address wasn’t it either. Disabling Encryption with the:

DefaultEncryption Never

Did the trick. This is clearly not safe. What would be best would be cutting a certificate for my cups server. That’s problematic because two years from now when the certificate expires, how long will it take me to figure out why printing stops working. Perhaps best would be to encrypt requests that need a password and allow cleartext communications for plain printing.

Turn off arp change noise on FreeBSD

If you run a FreeBSD server on a machine with any Apple infrastructure, Airport, AppleTV, etc then you are probably used to seeing lots of messages like this:

+arp: moved from --- somewhere --- to - somewhere else- on em0
+arp: moved from - somewhere else- to --- somewhere --- on em0

This is the Bonjour Sleep Proxy service in action. A device that provides a sleep proxy attempts to make Bonjour services available on your network at all times by advertising  the proxy’s IP address  as  the service destination while the  true provider is sleeping. For example, if you have an older, non-networked shared printer connected to an iMac Desktop, the sleep proxy will advertise it’s own address as the destination for your share printer. If someone sends a print request to your printer, the sleep proxy intercepts the request, sends a wake up packet to your iMac, and then the printing can actually go on.

This activity looks a lot like an arp poisoning attack. If you want to check for that look at the mac address of the devices in question. You can look up the first three octets of the mac address at Google. Those are a manufacturer ID. If one or both of the devices is from Apple, it’s more likely that you have a Bonjour Sleep Proxy working on your network.

Over time these messages are disruptive on a FreeBSD server because they blow valid information out of the kernel’s dmesg buffer. You can still the kernel’s boot dmesg by groveling through sysctl if you have a disk drive that’s misbehaving, that information will be lost in a day or two.

To turn these messages off, do the following:

$ sudo su -
# ## Fix this for this kernel boot session...
# sysctl -w 1 -> 0
# ## Fix this permanently.
# echo '' >> /etc/sysctl.conf

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:

          - ansible
          - terraform
          - git
          - emacs

          my_host: maestro-test

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


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
        name: "{{ item }}"
        state: latest
      with_items: "{{ std_pkg }}"

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

This should be installed in a file named something like:


in the Ansible directory. At this point presuming that you have a machine, physical or virtual at 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 {}/ \;
chris $ touch
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
answer VNET_1_DHCP yes
answer VNET_1_DHCP_CFG_HASH E08B... ...D0D8
answer VNET_1_HOSTONLY_SUBNET 172.a.b.0
answer VNET_8_DHCP yes
answer VNET_8_DHCP_CFG_HASH 2031... ...F498
answer VNET_8_HOSTONLY_SUBNET 10.c.d.0
answer VNET_8_NAT 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 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 {
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;
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>"

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.