I discussed how I was working on building an Ansible training environment on FreeBSD recently, and the system was put to use this week. Ten participants who ran redesigned lab exercises on the machines, didn’t once complain that anything was too slow (as they previously had on occasion when using the old VirtualBox installation). I am tickled pink.

plot of vmstat during labs

I confess I was a bit concerned how snappy the 16GB T430 Thinkpad would be on running “heavyweight” Ansible on Python at 30 jails, but I needn’t have been. While students were running their labs I occasionally ran a playbook of my own and didn’t notice any lag.

I had vmstat(8) running the whole time and later chopped off thousands of lines containing an idle time of 99 or 100, and created the plot to visualize the data which shows only the periods when people were actually working.

vmstat 5 | tee -a vmstat.txt
...
grep -v r vmstat.txt |
    awk '{ if ($NF < 99 ) { print; }}' > vmstat.data

I couldn’t resist asking when the training was finished, whether students would mind commenting on the setup. They looked at me a bit curiously, and when I told them they’d all been working together on one laptop, I saw the odd jaw open. All in all, only positive comments on the setup.

freebsd, ansible, and training :: 20 Feb 2020 :: e-mail

BIND 9.16.0 has just been released, and it’s full of lovely new features including one for devopsy types:

$ dig +dnssec +yaml jpmens.net SOA
-
  type: MESSAGE
  message:
    type: RECURSIVE_RESPONSE
    query_time: !!timestamp 2020-02-20T17:06:06.091Z
    response_time: !!timestamp 2020-02-20T17:06:06.091Z
    message_size: 257b
    socket_family: INET
    socket_protocol: UDP
    response_address: 127.0.0.1
    response_port: 53
    query_address: 0.0.0.0
    query_port: 61944
    response_message_data:
      opcode: QUERY
      status: NOERROR
      id: 61207
      flags: qr rd ra ad
      QUESTION: 1
      ANSWER: 2
      AUTHORITY: 0
      ADDITIONAL: 1
      OPT_PSEUDOSECTION:
        EDNS:
          version: 0
          flags: do
          udp: 4096
      QUESTION_SECTION:
        - jpmens.net. IN SOA
      ANSWER_SECTION:
        - jpmens.net. 3445 IN SOA u.six53.net. noc.six53.net. 7562 86400 7200 3600000 3600
        - jpmens.net. 3445 IN RRSIG SOA 8 2 3600 20200305000000 20200220000000 17145 jpmens.net. Fvi1sUEvEBQjrp1KzioO5kdn7PUM+WlHuVxQDBolyPRRiGCPXdHTBtDz 6CV+l0lXmyK10IHo5OukBXyNu0QBLSCB+bIHP4GJ+yslWFuWzvzxHigM LLiRvbCPUMjGIjMKweJa/o6W3o1Vc2EzZoKdbibZy5gAqqfwkrkVtuGu u7s=

There we go! dig, mdig, and delv can now all take a +yaml option to print output in a detailed YAML format.

And we can quickly produce JSON by piping the output to something like this:

python -c 'import yaml,json,sys; print(json.dumps(yaml.safe_load(sys.stdin.read()), indent=4, default=str))'
bind, dig, and yaml :: 20 Feb 2020 :: e-mail

One of the labs my future Ansible training students will be running on the new FreeBSD training machine will be setting up a Gitea instance in a jail, and in order to access that instance with a Web browser they’ll need the external address of the host, on which nginx then redirects into students’ jails.

I wanted to be able to query this address (and use it programatically) on the host, so I decided I’d have the host add it dynamically to the DNS with a dynamic DNS update using something like the script I used for displaying the IP address of a machine on its console. A simple rc script did the trick but the result didn’t really satisfy me because the rc scripts works on boot only. I thought there must be a better way.

I found resolvconf(8), which did the trick, in particular to stop the resolver configuration from being updated, as I want my own:

# disable resolvconf from updating resolv.conf
echo "resolvconf=NO" > /etc/resolvconf.conf

I thought that script would be a good solution, but I was pointed at dhclient-script(8) which made more sense, and I whipped up a small script script to update the local BIND server running on the host via dynamic DNS.

I create the shell script /etc/dhclient-enter-hooks which is later sourced by dhclient-script(8). When the machine boots or a lease expires my hook is invoked with a number of environment variables which give me the data I need:

interface=em0
new_broadcast_address=172.16.23.255
new_dhcp_lease_time=10800
new_dhcp_message_type=5
new_dhcp_server_identifier=172.16.16.1
new_domain_name_servers=172.16.16.1
new_expiry=1580174627
new_ip_address=172.16.23.168
new_network_number=172.16.16.0
new_routers=172.16.16.1
new_subnet_mask=255.255.248.0
reason=REBOOT

All I then do is use $new_ip_address as the A resource record in my DNS entry.

Nothing doing. It wouldn’t work, until I realized by judicious use of ps and other friendly utilities, that the name server just wasn’t yet up when my dhclient-exit-hooks script ran.

if [ "$interface" = "em0" ]; then
        if [ "$reason" = "BOUND" -o "$reason" = "RENEW" -o \
             "$reason" = "REBOOT" -o "$reason" = "REBIND" ]; then
                (
                        sleep 30
                        logger "dhclient-enter-hooks (reason: $reason, iface: $interface)"
                        ip="${new_ip_address:-127.0.0.2}"
                        ttl="${new_dhcp_lease_time:-86400}"
                        /usr/local/bin/nsupdate -k /etc/ourkeys/manager.tsig <<-EOF
                                server 127.0.0.1
                                update del iface.example.org.
                                update add iface.example.org. $ttl A $ip
                                send
                                quit
                        EOF
                ) &
        fi
fi

It is now.

Pay particular attention to the 30 and the & at the end of the inner sub-shell. It’s a beaut, innit? Well, maybe not, but it has recorded a number of DHCP renewals. :-)

freebsd and rfc2136 :: 13 Feb 2020 :: e-mail

I’m rewriting portions of the Ansible training I do, and I noticed that I pay little attention to the YAML inventory format. I start by teaching the so-called INI file format, and later do an overview over what dynamic inventory is and the data it must return, but the YAML file I basically just gloss over. (It being YAML is not the reason.)

twitter poll

The unrepresentative poll didn’t really surprise me, but I got a few responses I will partially quote here out of interest.

I didn’t expect this:

I migrated from INI to YAML; easier to read and parse.

I did expect responses like these:

INI file, because it was used in the Ansible book I learnt from. My server fleet rarely changes so dynamic would be of minimal use and I wasn’t aware you could use YAML for inventories.

I had to switch from ini to yaml recently (fiddling with vault) and my opinion is different. Ini file is light and simple

For simple projects the ini environment, but for most projects a custom dynamic which is written in python and and is hooked into different cmdb’ish things

Usually ini, but for @Hetzner_Online -cloud-related setups I use custom (Python) dynamic inventories, sometimes in combination with a static ini. For my private OpenStack cloud I’m doing fine with static ini inventories so far.

Postgres based cmdb with a wrapper script for ansible-playbook that lets us create selective inventory on the fly

I pointed the responder in the (I hope) right direction. The answer to the question is, of course, yes it’s possible, but it requires a bit of programming. (This ad inventory may be a start.)

I did not know there was a dynamic inventory option. Do you know if you can use Windows AD OU’s as the inventory?

This might interest others:

I turned to using yaml inventories, at least as a base setup. which might include dynamic things (and inventory plugins configs are also yaml now). I really like how you can define everything (hosts, groups and their variables) in one single format, all set in folders & files.

Dynamic with YAML doing mapping. Details here

Then there were a few responses from people using ready-made dynamic inventory scripts:

Foreman

I very much like this train of thought:

In my current work infra: inventory directory with #yaml files based on major group. Previously: dynamic from the monitoring tool. I liked the idea that a host had to be declared in monitoring first to exist. @ansible was activating the monitoring but it had to be declared first.

and

Dynamic, what else? From Zabbix inventory.

The generator plugin

Nice that we also have FreeBSD users:

I’d like to have dynamic inventory where I point it to my list of FreeBSD hosts and it could just auto discover all iocage jails and categorize them based on local things (subsection of host name or local file or zfs properties), but I’ve yet to see this actually work.

And finally one person who’s jumped onto the new 2.4 inventory plugins:

It’s being slowly replaced by inventory plugins, I learned it the hard way when sending a PR for an old JSON based inventory script.

ansible :: 11 Feb 2020 :: e-mail

Today I learned via Tristan about a library which is able to read and write Ansible vault files. I had a quick look because this might come in handy.

#!/usr/bin/env python3.7

from ansible_vault import Vault

vault = Vault('supersecurepassword')
data = vault.load(open('data.file').read())
print(data)

This is not an official Ansible project, according to the project page, but it appears to do its job:

$ echo seekr1t > data.file
$ ansible-vault encrypt data.file
New Vault password:
Confirm New Vault password:
Encryption successful

$ head -1 data.file
$ANSIBLE_VAULT;1.1;AES256

$ ./jp.py
seekr1t

$ echo seekr1t > data.file
$ ansible-vault encrypt --vault-id PROD@prompt data.file
New vault password (PROD):
Confirm new vault password (PROD):
Encryption successful

$ head -1 data.file
$ANSIBLE_VAULT;1.2;AES256;PROD

$ ./jp.py
seekr1t

The code requires Ansible to be installed.

ansible and vault :: 08 Feb 2020 :: e-mail

Other recent entries