Ansible and PF, plus NTP

It seems that ntpd has turned into the latest DDOS amplifier. I run a lot of servers, and most of them use the standard ntp client. I need to verify that none of my servers can be used for DDOS amplification. To do this, I need to give all the clients a standard NTP configuration, pointing at my personal NTP servers.

While my internal addresses need access to the port 123 on my servers, the public doesn’t. And I occasionally add internal addresses. Automating PF and NTP configuration via Ansible will simplify my life in the future.

I’ve used Ansible templates to configure services before, but packet filters are a little different. Packet filtering rules involve lots of information about the local host, such as interface names and the various system roles. It’s entirely possible to write an Ansible template that expresses your PF ruleset, it just took a little work.

First, I define an Ansible group for the NTP servers (as well as other server duties). The time servers run FreeBSD 9.2.

Here’s the playbook, with added NTP.

---
- hosts: ntp-servers
  user: ansible
  sudo: yes

  tasks:

  - name: enable ntpdate
    action: command sysrc ntpdate_enable=yes
  - name: enable ntpdate server
    action: command sysrc ntpdate_hosts=pool.ntp.org
  - name: enable ntpd
    action: command sysrc ntpd_enable=yes

  - name: copy ntp.server.conf to servers
    action: copy src=/home/ansible/freebsd/etc/ntp.server.conf
      dest=/etc/ntp.conf owner=root group=wheel mode=0644
    notify:
      - restart ntpd

  - include: tasks/pf-compile.yml

  handlers:

    - include: handlers/restarts.yml

Simple enough, no?

Except there’s nothing in here about the packet filter. Or restarting ntp.

These functions are pretty common, so I’ve moved them to separate files. I might need to rebuild the packet filter rules for any number of playbooks, after all.

The file tasks/pf-compile.yml looks like this.

---
#build pf.conf from template

  - name: configure firewall
    template: src=/home/ansible/freebsd/etc/pf.conf.j2
      dest=/etc/pf.conf owner=0 group=0 mode=0444
      validate='/sbin/pfctl -nf %s'
    notify:
      - reload pf

This task uses a jinja2 template to build a pf.conf specifically for this host, copies it to the host, validates its syntax, puts it in place, and triggers a PF reload. Always validate your files before deploying them. Ansible doesn’t prevent mistakes, but rather allows you to deploy mistakes faster than ever.

Similarly, I’ve split the “restart services” handlers off into the file handlers/restarts.yml. Here are the relevant bits.

---
#restart assorted services

  - name: restart ntpd
    service: name=ntpd state=restarted

  - name: reload pf
    action: shell /sbin/pfctl -f /etc/pf.conf

So, where is this firewall template? That’s probably what dragged you here.

#{{ ansible_managed }}
#$Id: pf.conf.j2,v 1.2 2014/01/16 16:10:54 mwlucas Exp $

ext_if="{{ ansible_default_ipv4.device }}"

include "/etc/pf.mgmt.conf"
include "/etc/pf.ournets.conf"

set block-policy return
set loginterface $ext_if
set skip on lo0

scrub in all
block in all

pass in on $ext_if proto icmp all
pass in on $ext_if proto icmp6 all

#this host may initiate anything
pass out on $ext_if from any to any

#mgmt networks can talk to this host on any service
pass in on $ext_if from  to any

#Allowed services, in port order

{% if inventory_hostname in groups['dns'] %}
#DNS access
pass in on $ext_if proto {tcp, udp} from any to any port 53
{% endif %}

{% if inventory_hostname in groups['tftpd'] %}
#allow tftp from the world
pass in on $ext_if proto udp from any to any port 69
{% endif %}

{% if inventory_hostname in groups['ntp-servers'] %}
#allow time from our networks
pass in on $ext_if proto udp from  to any port 123
{% endif %}

#end of services

The first bit of new (to me) trickery in this is getting the interface name. I use an Ansible-provided variable for this. Get the complete list of Ansible-provided variables for a host by running ansible -m setup hostname. The variable ansible_default_ipv4.device contains the network interface name. (If your host has multiple network-facing interfaces, you’ll need to modify this.)

This PF ruleset pulls in two external files, one containing a list of management addresses and one containing the complete list of my internal networks.

I allow access from my management networks, allow ICMP, default block, all the routine packet filter stuff. The next interesting bit is the allowed services. I check for the host’s presence in a group, and if it’s there’ I add a rule to permit the access that protocol needs.

One detail that gave me trouble made me use inventory_hostname rather than ansible_fqdn or ansible_hostname to check group membership. I manage systems in several domains, and many of them have one name in our management systems and another in DNS. I put machines in Ansible by their fully qualified domain name. Using ansible_fqdn to get a hostname returns the hostname given in reverse DNS. inventory_hostname returns the hostname as it appears in the hosts file. If ansible_fqdn doesn’t match the hostname in the hosts file, the group comparison fails. Using inventory_hostname gave me one consistent set of hostnames for comparisons.

So now I can easily deploy a secure NTP configuration to my servers. When I have to deploy some other service that requires updating the packet filter, I can include the same task file. And the handlers are now similarly reusable.

Configuring the clients across several different operating systems will probably require Ansible roles, however. I’d best get on that next…

Wanted: a VAX

No, not for me. If I bring another piece of obsolete hardware into this house, it can have my chair because I’ll be sitting out at the curb.

The OpenBSD Project builds all their packages on native hardware. Yes, it might take a month to build a complete package set on some of their platforms, but that’s okay.

Their VAX recently died. They need a new one.

I know that some of you have a VAX in storage, that you’ve been reluctant to surrender because it’s a cool toy. Now you can send it to a good home. You can tell everyone that you, personally, saved OpenBSD’s VAX support.

How often can your pack rat tendencies make you a hero? This is your chance. Take it.

The Desktop of Doom

I’m sick of scrounging hardware for writing books. I’m sick of waiting for things to compile, managing disk space, and running out of memory. I finally got so sick of it that I decided to invest some serious cash in a research machine, in the hope that I wouldn’t need to hunt hardware piecemeal for the next five to six years.

I solicited hardware advice from my modest horde of Twitter followers. After diving through realms of documentation, I came to realize that I didn’t want to build my own computer. I rarely configure, buy, or even touch hardware these days unless something goes unspeakably wrong, and there’s enough options out there that were I to attempt to do so, I’d waste both money and time. I certainly could figure it out, but that’s not where my strength is and the expertise I would acquire would be largely wasted outside this one task.

I wanted eight hard drives, so that when the time is right I can write about assorted ZFS configurations. I wanted some flash disk on top of that. I wanted enough oomph to run umpteen virtual machines simultaneously. And I didn’t want to spend a whole lot.

After wandering around the Internet, using assorted online system configuration tools to develop a rough idea of price, I contacted iX Systems and bought a system.

I now have a shiny new desktop system that can “portsnap fetch extract” in two minutes. Because SATA3 is pretty sweet. My only problem is that the portable version of cwm that @blooouuup found for me is really slow & clunky when resizing windows, but I’m sure if I whine at the right people someone will fix it for me. (Now to figure out who the right people are.)

For all those who asked me to share my shopping results: here’s a FreeBSD 11 dmesg. Thanks to bhyve, I can now run whatever I need. It lives under my desk, and is surprisingly quiet when I’m not building x11/xorg in 30 minutes or less. With WITNESS enabled.

iX Systems might not advertise desktop systems, but they’ll do a nice one for you. Even if yours isn’t as awesome as mine.


Copyright (c) 1992-2013 The FreeBSD Project.
Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994
The Regents of the University of California. All rights reserved.
FreeBSD is a registered trademark of The FreeBSD Foundation.
FreeBSD 11.0-CURRENT #0 r260064: Mon Dec 30 16:28:06 UTC 2013
root@grind.freebsd.org:/usr/obj/usr/src/sys/GENERIC amd64
FreeBSD clang version 3.3 (tags/RELEASE_33/final 183502) 20130610
WARNING: WITNESS option enabled, expect reduced performance.
CPU: Intel(R) Xeon(R) CPU E5-1620 v2 @ 3.70GHz (3700.08-MHz K8-class CPU)
Origin = "GenuineIntel" Id = 0x306e4 Family = 0x6 Model = 0x3e Stepping = 4
Features=0xbfebfbff
Features2=0x7fbee3ff
AMD Features=0x2c100800
AMD Features2=0x1
Standard Extended Features=0x281
TSC: P-state invariant, performance statistics
real memory = 34368126976 (32776 MB)
avail memory = 33227055104 (31687 MB)
Event timer "LAPIC" quality 600
ACPI APIC Table: < >
FreeBSD/SMP: Multiprocessor System Detected: 8 CPUs
FreeBSD/SMP: 1 package(s) x 4 core(s) x 2 SMT threads
cpu0 (BSP): APIC ID: 0
cpu1 (AP): APIC ID: 1
cpu2 (AP): APIC ID: 2
cpu3 (AP): APIC ID: 3
cpu4 (AP): APIC ID: 4
cpu5 (AP): APIC ID: 5
cpu6 (AP): APIC ID: 6
cpu7 (AP): APIC ID: 7
ioapic0 irqs 0-23 on motherboard
ioapic1 irqs 24-47 on motherboard
kbd1 at kbdmux0
random: initialized
acpi0: on motherboard
acpi0: Power Button (fixed)
acpi0: reservation of 0, a0000 (3) failed
cpu0: on acpi0
cpu1: on acpi0
cpu2: on acpi0
cpu3: on acpi0
cpu4: on acpi0
cpu5: on acpi0
cpu6: on acpi0
cpu7: on acpi0
attimer0: port 0x40-0x43 irq 0 on acpi0
Timecounter "i8254" frequency 1193182 Hz quality 0
Event timer "i8254" frequency 1193182 Hz quality 100
atrtc0: port 0x70-0x71 irq 8 on acpi0
Event timer "RTC" frequency 32768 Hz quality 0
hpet0: iomem 0xfed00000-0xfed003ff on acpi0
Timecounter "HPET" frequency 14318180 Hz quality 950
Event timer "HPET" frequency 14318180 Hz quality 550
Timecounter "ACPI-fast" frequency 3579545 Hz quality 900
acpi_timer0: <24-bit timer at 3.579545MHz> port 0x408-0x40b on acpi0
pcib0: port 0xcf8-0xcff on acpi0
pci0: on pcib0
pcib1: irq 27 at device 1.0 on pci0
pci1: on pcib1
pcib2: irq 33 at device 2.0 on pci0
pci2: on pcib2
pcib3: irq 33 at device 2.2 on pci0
pci3: on pcib3
pcib4: irq 43 at device 3.0 on pci0
pci4: on pcib4
pcib5: irq 43 at device 3.1 on pci0
pci5: on pcib5
pcib6: irq 43 at device 3.2 on pci0
pci6: on pcib6
pci0: at device 4.0 (no driver attached)
pci0: at device 4.1 (no driver attached)
pci0: at device 4.2 (no driver attached)
pci0: at device 4.3 (no driver attached)
pci0: at device 4.4 (no driver attached)
pci0: at device 4.5 (no driver attached)
pci0: at device 4.6 (no driver attached)
pci0: at device 4.7 (no driver attached)
pci0: at device 5.0 (no driver attached)
pci0: at device 5.2 (no driver attached)
pcib7: irq 16 at device 17.0 on pci0
pci7: on pcib7
isci0: port 0xe000-0xe0ff mem 0xfa47c000-0xfa47ffff,0xfa000000-0xfa3fffff irq 16 at device 0.0 on pci7
pci0: at device 22.0 (no driver attached)
pci0: at device 22.1 (no driver attached)
ehci0: mem 0xfbb23000-0xfbb233ff irq 16 at device 26.0 on pci0
usbus0: EHCI version 1.0
usbus0 on ehci0
pcib8: irq 17 at device 28.0 on pci0
pci8: on pcib8
pcib9: irq 18 at device 28.6 on pci0
pci9: on pcib9
em0: port 0xd000-0xd01f mem 0xfba00000-0xfba1ffff,0xfba20000-0xfba23fff irq 18 at device 0.0 on pci9
em0: Using MSIX interrupts with 3 vectors
em0: Ethernet address: 00:25:90:db:d5:94
pcib10: irq 19 at device 28.7 on pci0
pci10: on pcib10
em1: port 0xc000-0xc01f mem 0xfb900000-0xfb91ffff,0xfb920000-0xfb923fff irq 19 at device 0.0 on pci10
em1: Using MSIX interrupts with 3 vectors
em1: Ethernet address: 00:25:90:db:d5:95
ehci1: mem 0xfbb22000-0xfbb223ff irq 23 at device 29.0 on pci0
usbus1: EHCI version 1.0
usbus1 on ehci1
pcib11: at device 30.0 on pci0
pci11: on pcib11
vgapci0: mem 0xf9000000-0xf9ffffff,0xfb800000-0xfb803fff,0xfb000000-0xfb7fffff irq 16 at device 4.0 on pci11
vgapci0: Boot video device
isab0: at device 31.0 on pci0
isa0: on isab0
ahci0: port 0xf050-0xf057,0xf040-0xf043,0xf030-0xf037,0xf020-0xf023,0xf000-0xf01f mem 0xfbb21000-0xfbb217ff irq 18 at device 31.2 on pci0
ahci0: AHCI v1.30 with 6 6Gbps ports, Port Multiplier not supported
ahcich0: at channel 0 on ahci0
ahcich1: at channel 1 on ahci0
ahcich2: at channel 2 on ahci0
ahcich3: at channel 3 on ahci0
ahcich4: at channel 4 on ahci0
ahcich5: at channel 5 on ahci0
ahciem0: on ahci0
pci0: at device 31.3 (no driver attached)
pci0: at device 31.6 (no driver attached)
pcib12: on acpi0
pci255: on pcib12
pci255: at device 8.0 (no driver attached)
pci255: at device 9.0 (no driver attached)
pci255: at device 10.0 (no driver attached)
pci255: at device 10.1 (no driver attached)
pci255: at device 10.2 (no driver attached)
pci255: at device 10.3 (no driver attached)
pci255: at device 11.0 (no driver attached)
pci255: at device 11.3 (no driver attached)
pci255: at device 12.0 (no driver attached)
pci255: at device 12.1 (no driver attached)
pci255: at device 13.0 (no driver attached)
pci255: at device 13.1 (no driver attached)
pci255: at device 14.0 (no driver attached)
pci255: at device 14.1 (no driver attached)
pci255: at device 15.0 (no driver attached)
pci255: at device 15.1 (no driver attached)
pci255: at device 15.2 (no driver attached)
pci255: at device 15.3 (no driver attached)
pci255: at device 15.4 (no driver attached)
pci255: at device 15.5 (no driver attached)
pci255: at device 16.0 (no driver attached)
pci255: at device 16.1 (no driver attached)
pci255: at device 16.2 (no driver attached)
pci255: at device 16.3 (no driver attached)
pci255: at device 16.4 (no driver attached)
pci255: at device 16.5 (no driver attached)
pci255: at device 16.6 (no driver attached)
pci255: at device 16.7 (no driver attached)
pci255: at device 19.0 (no driver attached)
pci255: at device 19.1 (no driver attached)
pci255: at device 19.4 (no driver attached)
pci255: at device 19.5 (no driver attached)
pci255: at device 22.0 (no driver attached)
pci255: at device 22.1 (no driver attached)
pci255: at device 22.2 (no driver attached)
acpi_button0: on acpi0
uart0: <16550 or compatible> port 0x3f8-0x3ff irq 4 flags 0x10 on acpi0
uart1: <16550 or compatible> port 0x2f8-0x2ff irq 3 on acpi0
uart2: <16550 or compatible> port 0x3e8-0x3ef irq 10 on acpi0
orm0: at iomem 0xc0000-0xc7fff,0xc8000-0xc8fff on isa0
sc0: at flags 0x100 on isa0
sc0: VGA <16 virtual consoles, flags=0x300>
vga0: at port 0x3c0-0x3df iomem 0xa0000-0xbffff on isa0
ppc0: cannot reserve I/O port range
est0: on cpu0
p4tcc0: on cpu0
est1: on cpu1
p4tcc1: on cpu1
est2: on cpu2
p4tcc2: on cpu2
est3: on cpu3
p4tcc3: on cpu3
est4: on cpu4
p4tcc4: on cpu4
est5: on cpu5
p4tcc5: on cpu5
est6: on cpu6
p4tcc6: on cpu6
est7: on cpu7
p4tcc7: on cpu7
ZFS filesystem version: 5
ZFS storage pool version: features support (5000)
Timecounters tick every 1.000 msec
random: unblocking device.
usbus0: 480Mbps High Speed USB v2.0
usbus1: 480Mbps High Speed USB v2.0
ugen0.1: at usbus0
uhub0: on usbus0
ugen1.1: at usbus1
uhub1: on usbus1
uhub1: 2 ports with 2 removable, self powered
uhub0: 2 ports with 2 removable, self powered
ses0 at ahciem0 bus 0 scbus7 target 0 lun 0
ses0: SEMB S-E-S 2.00 device
ses0: SEMB SES Device
da2 at isci0 bus 0 scbus0 target 2 lun 0
da2: Fixed Direct Access SCSI-5 device
da2: Serial Number WD-WCAW36477223
da2: 300.000MB/s transfers
da2: Command Queueing enabled
da2: 953869MB (1953525168 512 byte sectors: 255H 63S/T 121601C)
da0 at isci0 bus 0 scbus0 target 0 lun 0
da0: Fixed Direct Access SCSI-5 device
da0: Serial Number WD-WCAW36478143
da0: 300.000MB/s transfers
da0: Command Queueing enabled
da0: 953869MB (1953525168 512 byte sectors: 255H 63S/T 121601C)
da3 at isci0 bus 0 scbus0 target 3 lun 0
da3: Fixed Direct Access SCSI-5 device
da3: Serial Number WD-WCAW36477062
da3: 300.000MB/s transfers
da3: Command Queueing enabled
da3: 953869MB (1953525168 512 byte sectors: 255H 63S/T 121601C)
da1 at isci0 bus 0 scbus0 target 1 lun 0
da1: Fixed Direct Access SCSI-5 device
da1: Serial Number WD-WCAW36498185
da1: 300.000MB/s transfers
da1: Command Queueing enabled
da1: 953869MB (1953525168 512 byte sectors: 255H 63S/T 121601C)
ada0 at ahcich0 bus 0 scbus1 target 0 lun 0
ada0: ATA-8 SATA 3.x device
ada0: Serial Number WD-WCAW36478129
ada0: 600.000MB/s transfers (SATA 3.x, UDMA6, PIO 8192bytes)
ada0: Command Queueing enabled
ada0: 953869MB (1953525168 512 byte sectors: 16H 63S/T 16383C)
ada0: Previously was known as ad4
ada1 at ahcich1 bus 0 scbus2 target 0 lun 0
ada1: ATA-8 SATA 3.x device
ada1: Serial Number WD-WCAW36477352
ada1: 600.000MB/s transfers (SATA 3.x, UDMA6, PIO 8192bytes)
ada1: Command Queueing enabled
ada1: 953869MB (1953525168 512 byte sectors: 16H 63S/T 16383C)
ada1: Previously was known as ad6
ada2 at ahcich2 bus 0 scbus3 target 0 lun 0
ada2: ATA-8 SATA 3.x device
ada2: Serial Number WD-WCAW36477139
ada2: 300.000MB/s transfers (SATA 2.x, UDMA6, PIO 8192bytes)
ada2: Command Queueing enabled
ada2: 953869MB (1953525168 512 byte sectors: 16H 63S/T 16383C)
ada2: Previously was known as ad8
ada3 at ahcich3 bus 0 scbus4 target 0 lun 0
ada3: ATA-8 SATA 3.x device
ada3: Serial Number WD-WCAW36477141
ada3: 300.000MB/s transfers (SATA 2.x, UDMA6, PIO 8192bytes)
ada3: Command Queueing enabled
ada3: 953869MB (1953525168 512 byte sectors: 16H 63S/T 16383C)
ada3: Previously was known as ad10
ada4 at ahcich4 bus 0 scbus5 target 0 lun 0
ada4: ATA-8 SATA 2.x device
ada4: Serial Number 20131111AA300000000E
ada4: 300.000MB/s transfers (SATA 2.x, UDMA6, PIO 8192bytes)
ada4: Command Queueing enabled
ada4: 61057MB (125045424 512 byte sectors: 16H 63S/T 16383C)
ada4: Previously was known as ad12
ada5 at ahcich5 bus 0 scbus6 target 0 lun 0
ada5: ATA-8 SATA 2.x device
ada5: Serial Number 20131111AA3000000007
ada5: 300.000MB/s transfers (SATA 2.x, UDMA6, PIO 8192bytes)
ada5: Command Queueing enabled
ada5: 61057MB (125045424 512 byte sectors: 16H 63S/T 16383C)
ada5: Previously was known as ad14
ugen0.2: at usbus0
uhub2: on usbus0
ugen1.2: at usbus1
uhub3: on usbus1
Netvsc initializing... done!
SMP: AP CPU #1 Launched!
SMP: AP CPU #3 Launched!
SMP: AP CPU #4 Launched!
SMP: AP CPU #2 Launched!
SMP: AP CPU #6 Launched!
SMP: AP CPU #7 Launched!
SMP: AP CPU #5 Launched!
Timecounter "TSC-low" frequency 1850039224 Hz quality 1000
WARNING: WITNESS option enabled, expect reduced performance.
GEOM_RAID: Intel-ee147409: Array Intel-ee147409 created.
GEOM_RAID: Intel-ee147409: Disk ada4 state changed from NONE to ACTIVE.
GEOM_RAID: Intel-ee147409: Subdisk Volume0:0-ada4 state changed from NONE to ACTIVE.
GEOM_RAID: Intel-ee147409: Disk ada5 state changed from NONE to ACTIVE.
GEOM_RAID: Intel-ee147409: Subdisk Volume0:1-ada5 state changed from NONE to ACTIVE.
GEOM_RAID: Intel-ee147409: Array started.
GEOM_RAID: Intel-ee147409: Volume Volume0 state changed from STARTING to OPTIMAL.
GEOM_RAID: Intel-ee147409: Provider raid/r0 for volume Volume0 created.

Jailing FreeBSD 4 on FreeBSD 10

We have an in-house application that was written for FreeBSD 4 and antediluvian versions of PHP, Perl, OpenSSL, and so forth. Most of the features have migrated into other applications, but a few critical functions remain.

An old operating system isn’t sufficiently bad, though. The hardware terrifies me. Not only is it over a decade old, it’s repurposed desktop hardware.

Virtualize it? Maybe. But device drivers have changed over the intervening decade, and a ten-year-old de(4) or fxp(4) driver works poorly on any of my virtualization systems. Virtio is right out.

Port it to a current OS, PHP, and perl? That would be a painful prospect if I knew what I was doing. I’m a sysadmin, not a programmer. I have no bloody clue what I’m doing.

But theoretically, FreeBSD 4 systems should run almost unchanged in a jail on FreeBSD 10. Can they? Let’s find out!

First I tarred up the entire 4.10 system, except for /proc. (Yes, FreeBSD 4.10 used /proc. Those were the days, eh?) I did no preparatory cleaning, and even included port work directories, /usr/obj, /var/tmp, and so on, as I have NO idea what I might need. Yes, I’m sure I can find a PHP 5.0.whatever tarball out on the Internet, but that would involve work.

Create my jail directory, and untar the copied server

# mkdir /var/jail/oldserver
# cd oldserver
# tar -xvpf $HOME/oldserver.tgz

Be sure to use the -p flag, to preserve permissions.

Now to edit some configuration files, to change this system from hardware to jail.

  • Check rc.conf. Remove functions handled by the host server: firewalls, timekeeping, SSH, and so on. This jailed host only needs the functions that directly support the application (the Apache web server and the associated MySQL database).
  • Check /etc/fstab for anything you need to retain, like NFS filesystems or /proc. Comment out everything else.
  • Remove the old /dev. You cannot use FreeBSD 4 device nodes on a FreeBSD 10 host. (I spent an hour repeatedly bashing my head on the brick wall intensively testing these two sentences, so you can rest assured that it’s true.)

    Now on to the jail server. FreeBSD 9 and above has a jail-specific configuration file, /etc/jail.conf. I define some basic characteristics here.

    While that’s running, do some basic jail setup. I would normally recommend ezjail, but this is a fairly special case: no ZFS, the server will never be upgraded, and I specifically want a very minimal system. Also, ezjail doesn’t seem to have been updated for the Jail New World Order. So I’ll configure this the hard way, which isn’t terribly hard.

    FreeBSD 9 and above has a jail-specific configuration file, /etc/jail.conf. Here’s a configuration for my old server.

    exec.start = "/bin/sh /etc/rc";
    exec.stop = "/bin/sh /etc/rc.shutdown";

    oldserver {
    path = /var/jail/oldserver;
    host.hostname = oldserver.mwlucas.org;
    ip4.addr = 10.0.16.31;
    interface = vtnet0;
    };

    Now for the real test:

    # service jail start oldserver
    Starting jails: oldserver
    #

    Is it that easy?

    Of course not. Several processes didn’t start. I had to edit some configuration files to account for the change of IP address. That’s pretty minor, though.

    To log into the jail, get the jail ID and run a shell prompt in that server.

    host# jls
    JID IP Address Hostname Path
    7 10.0.16.31 oldserver.mwlucas.org /var/toolkit
    host# jexec 7 /bin/tcsh
    oldserver#

    The advantage to hosting this ancient system as a jail is that I can use the host operating system to control access to the legacy application. I don’t have to use the ancient IPF included on FreeBSD 4.10; I protect this host with PF from FreeBSD 10. (Admittedly, there’s newer PF out there, but even the older version in FreeBSD 10 is better than IPF.) FreeBSD 4.10 includes OpenSSH 3.5, which has roughly the same security as Windows XP. To log into the old server now, you must authenticate to the up-to-date FreeBSD 10 system and get a jail command prompt.

    The disadvantage to this system is that you can’t run ps(1) inside the jail. The ps command reads kernel data structures. While the FreeBSD 10 kernel includes FreeBSD 4 compatibility, that compatibility doesn’t extend to ps(1) and similar commands. You must use ps(1) from the jail host.

    That’s an acceptable trade-off, if it means I don’t have to touch actual PHP code. That stuff has cooties.

  • Amazon gets it wrong

    Amazon sends us Kindle owners an email every few days saying “You bought this and that, so we think you’ll like these items.” They’re right an annoying large portion of the time, but when they get it wrong they really blow it.

    Like I'd buy any of these
    Amazon Recommendation Failure

    Okay, yes, the book of PF is pretty good. But I have a copy. And I actually paid cold hard cash for the FreeBSD Design & Implementation book, and waited in line to get it signed. Either of these would be excellent gifts for the BSD aficionado in your life, except that they probably already have them.

    But the others? The author has never said a nice word about me. In fact, every time I get near him he calls me a lazy good-for-nothing who ought to be working harder, better, and smarter, eating less, and in general doing more for humanity. Flavored with a whole lot of really bad language. One of these days, I’m going to punch that bastard in the face.

    The good news is, Amazon doesn’t know everything about us. Yet.

    Now, if you’ll excuse me, I’m off to buy everything I need for the next month via my Amazon Prime subscription.

    Happy holidays, everyone!

    Sudo Mastery and DNSSEC Mastery now at OpenBSD bookstore

    The official OpenBSD bookstore now carries both DNSSEC Mastery and Sudo Mastery print. If you buy the print from them, you get the ebook as well. These books sell for list price, but sales support the OpenBSD Project.

    I am not donating all my proceeds from these books to OpenBSD, however. I did that with SSH Mastery, because the OpenBSD guys kind of take it in the shorts with OpenSSH. One of my goals is to have the ability to make a living writing without needing survival tips like “There’s great cardboard boxes behind the appliance store” and “Get to Qdoba right after they close, because the night guy will give you a bag rather making you scrounge in the dumpster.” But the negotiations on this were pretty strange:

    Me: Sorry, guys, but I have to charge a couple bucks for myself on this.

    Them: No, you don’t. You charge us what you would charge any other direct order.

    Me: Oh, I can do better than that for you.

    Them: No, we want to pay what anyone else would pay.

    Me: Really, a discount isn’t a problem.

    Them: TAKE THE MONEY. DO NOT ANGER US.

    Me: [gulp] As you command.

    The end deal they got is better than what I charge Amazon. (Or, to be more accurate, it’s a better deal than the tithe Amazon extracts before condescending to carry my books.) If you want TWP books in bulk quantities, let me know.

    And if your company uses OpenSSH, get them to donate something. OpenSSH has ridiculous market share, and the developers basically work for tips. You wouldn’t leave a restaurant after a fabulous meal without tipping the wait staff, would you? Then why would you do that with software?

    On asking me to write for you

    [posted for later reference]

    In the first eleven days of December 2013, I have received eight requests for me to write for a periodical such as a web site or a magazine. This is nice. I struggled for many years to get published. To have publishers knock on my door and ask for my work gives me a certain warm fuzzy feeling. They’re trying to fill in their 2014 editorial calendars, and want me to be part of it? That’s kind of cool.

    There’s only one problem: they want to pay me with a subscription. The more generous ones offer advertising space. I address this in my FAQ, but it seems these people either don’t read the answer, don’t comprehend the answer, or think the answer doesn’t apply to them.

    Here’s an explanation with more detail.

    My writing time is completely occupied, either with work that I expect will return financial rewards or “writing of the heart” — projects that I really want to do, but that I accept will not pay.

    Generally speaking, if you’re contacting me with a request to write for you, you expect to make money off of my writing. That makes this a business transaction. This means I expect to get paid an amount that is roughly equivalent to the amount I would make if I expend that amount of effort on other paying channels. A thousand-word article is almost certainly more than $50 of my time.

    But it’s also important to not be a jerk. The world is a small place.

    From now on, I’ll answer these requests with a form letter.

    Hi,

    Thanks for your interest.

    At this time, I am completely occupied with paying writing work, so I cannot take your offer. But thanks for thinking of me.

    Regards,
    ==ml

    I’m not a total mercenary. I put a fair amount of technology content up in this blog, free for anyone who can use a search engine. But: I have a day job. My writing time is taken away from family and friends. I might choose to give up some of that time for someone. But that “someone” will be a person, not a business.

    I know other people will write for these periodicals. Someone always will. But that’s their choice. I choose otherwise.

    FreeBSD authentication against Samba 4 LDAP

    After years of only needing central auth for Unix-like systems, I need to integrate Windows clients into my auth mix. Rather than munging my current OpenLDAP directory to contain Windows information, I elected to migrate to Samba 4. Samba 4 can act as a Windows domain controller and also exposes an LDAP interface for Unix clients.

    I assume that you’ve read the FreeBSD and Samba documentation on LDAP auth. This article is meant as a bridge between the two information sets.

    Most of the tutorials out there cover using LDAP and Kerberos with Samba. Kerberos requires that all hosts be in a single domain. My employer manages hosts within a variety of domains and business units, so Kerberos is a no-go. I need to use pure LDAP authentication.

    I configured my samba4 domain and ensured that Windows clients could join the domain and that the general Microsoft-esque features worked, such as failover to a backup domain controller. Once it appeared that everything worked, I set up a couple of OpenLDAP proxies exactly by the Samba documentation. (My domain controllers are in private address space, and I’m not willing to expose them to the larger network.)

    Then I created an account for services to bind with to perform basic queries. My binding account is called unixstuff. It’s not a member of any AD groups.

    Then I can configure OpenLDAP on the client. Install the various LDAP utilities as in the FreeBSD documentation.

    Configure the OpenLDAP tools to query your directory first. I use a private CA, so I set assorted TLS options.

    BASE dc=internal,dc=mwlucas,dc=org
    URI ldap://snarky.mwlucas.org
    ssl start tls
    tls_cacert /usr/local/etc/ssl/mwlucas.crt

    At this point I should be able to use ldapsearch on the Samba directory.

    # ldapsearch -WxD "cn=unixstuff,cn=users,dc=internal,dc=mwlucas,dc=org"
    Enter LDAP Password:
    ...

    If you enter the correct password, the directory should spill its guts.

    Once that works, we can configure LDAP authentication in /usr/local/etc/ldap.conf.

    host snarky.mwlucas.org
    base dc=internal,dc=mwlucas,dc=org
    ldap_version 3
    binddn cn=unixstuff,cn=users,dc=internal,dc=mwlucas,dc=org
    bindpw WhyHardCodePasswords

    pam_filter objectclass=posixAccount
    pam_login_attribute uid
    pam_member_attribute member
    pam_lookup_policy yes
    nss_base_passwd cn=users,dc=internal,dc=mwlucas,dc=org
    nss_base_group cn=users,dc=internal,dc=mwlucas,dc=org
    pam_groupdn cn=sysadmins,cn=users,dc=internal,dc=mwlucas,dc=org

    # Services for UNIX 3.5 mappings
    nss_map_attribute homeDirectory unixHomeDirectory
    nss_map_objectclass posixAccount User
    nss_map_objectclass shadowAccount User
    nss_map_attribute uid msSFU30Name
    nss_map_attribute uniqueMember msSFU30PosixMember
    nss_map_attribute userPassword msSFU30Password
    nss_map_objectclass posixGroup Group
    pam_filter objectclass=User
    pam_password ad

    With this set, and LDAP activated in /etc/nsswitch.conf, I can now “getent passwd” and “getent group” and get responses from the directory.

    That still leaves PAM. I’m no PAM expert, but I’ve hacked together something that permits LDAP access, and falls back to the local password file when the LDAP servers are unavailable. Here’s my /etc/pam.d/system:


    auth sufficient /usr/local/lib/pam_ldap.so
    auth required pam_unix.so no_warn try_first_pass nullok

    account required pam_login_access.so
    account sufficient /usr/local/lib/pam_ldap.so
    account required pam_unix.so

    session required /usr/local/lib/pam_ldap.so
    session required pam_lastlog.so no_fail

    password required pam_unix.so no_warn try_first_pass

    This gets you basic access. Realistically, though, users want a home directory. Install pam_mkhomedir and enable it on a per-service basis.

    auth sufficient /usr/local/lib/pam_ldap.so no_warn
    auth required pam_unix.so no_warn try_first_pass

    account required pam_nologin.so
    account required pam_login_access.so
    account required pam_unix.so
    account required /usr/local/lib/pam_ldap.so no_warn ignore_authinfo_unavail ignore_unknown_user

    session required pam_permit.so
    session required /usr/local/lib/pam_mkhomedir.so

    password required pam_unix.so no_warn try_first_pass

    With this set, users in the “sysadmins” group in AD have shell access to the servers. So far, this works well. But if you’re a Samba, LDAP, or PAM expert and see a problem, please let me know in the comments.

    FreeBSD-update seems to hang on 10.0-BETA2

    I’m setting up a new FreeBSD web server. As 10.0 is just around the corner, I installed 10.0-BETA2. BETA4 is out, so it’s time to upgrade.

    # freebsd-update -r 10.0-BETA4 upgrade
    Looking up update.FreeBSD.org mirrors... 5 mirrors found.
    ...

    That all looks good. Then I installed the update

    # freebsd-update install
    ...

    And the install hung. Overnight. Why?

    Turns out that there are multiple errata on freebsd-update. While you can upgrade to BETA2-p2, you can’t jump directly to the next version.

    So you need to do:

    # freebsd-update fetch
    # freebsd-update install
    # reboot
    # freebsd-update -r 10.0-BETA4 upgrade
    # freebsd-update install

    When freebsd-update appears to hang, use ^T to print the current status.

    # freebsd-update install
    Installing updates...
    ^T
    load: 0.17 cmd: install 2796 [nanslp] 156.82r 0.01u 0.02s 0% 6220k

    Here we see that the install process, PID 2796, is running. Wait a few minutes and try it again.

    ^T
    load: 0.16 cmd: install 2803 [nanslp] 26.09r 0.01u 0.01s 0% 6160k

    There’s been no visible output, and install is still running, but it’s now PID 2803. It’s a new install process. The update is proceeding, if not as quickly as you’d like.

    So, be patient and let the machine do its work.

    Wanted: BSDCan Volunteers

    Gossip, rumor, innuendo, and Twitter say that the BSDCan 2014 Call for Papers should be out in the next few days. (I trust the first three sources more than the last, but what the heck, let’s go for completeness.)

    For the last ten years, Dan Langille has single-handedly coordinated BSDCan. In that time, BSDCan has become one of the critical events of the BSD community. It’s the largest, most consistently held gathering of BSD folks on this side of the planet.

    Last year, Dan asked for additional help organizing, coordinating, and running BSDCan.

    We had some great volunteers for the program committee, and several attendees offered their tender hides for assorted roles. A lot can change in a year, though, and not everyone in the world was in the room at the time, so I’m throwing this out to the general public.

    We could use some additional help with the grunt work. It would be nice to have someone assigned to sit in each presentation room to make sure that the presenters have everything that they need and that everything goes reasonably well. One of the big time sinks is coordinating arrangements with speakers. Broken up between a few people it’s not a huge task, but it requires a certain attention to detail.

    We’d also like to have someone local to the University of Ottawa to indoctrinate introduce more students into the BSD community.

    Want to help? Sign up for the BSDCan-volunteers mailing list.