OpenBSD on Ubiquiti USG

Ubiquiti Unifi Security Gateway (USG) is a great device to run at the edge of a home network. It becomes even better when it's running OpenBSD. This guide documents how to setup such a gateway. There are accompanying git repos to somewhat automate the process as well.

Why?

This is a follow up to OpenBSD Network Gateway on EdgeRouter Lite. Since the EdgeRouter Lite 3 (ERL) and USG are very similar devices, the install process is nearly identical as well.

Required Tools

The following two cables connect to each other so you can connect from USB on your Mac to the serial port on USG.

  • USB to Serial interface cable

  • Serial to RJ45 Console Adapter Cable for Cisco Routers

You also need:

  • screen installed on your Mac

  • Phillips head screwdriver size 0

  • Wired network with DHCP server to use during install

Download OpenBSD

At the time of writing the latest release of OpenBSD was 6.3 so that's what I downloaded.

$ curl -O http://ftp.openbsd.org/pub/OpenBSD/6.3/octeon/miniroot63.fs

Hardware Setup and First Boot

Once you've downloaded the file it's time to write it to a USB drive. There are two options: overwrite the original drive in the USG or buy a new drive.

I tried the second option and wrote to a new Samsung 32GB USB 3.0 Flash Drive Fit (MUF-32BB/AM).

It is easy to take the drive out of the USG enclosure. Take off the rubber feet and take out four screws from the back to open the enclosure. Wiggle the USB drive up and down with a little more than gentle force and it'll slide out.

Even though I didn't use the original drive that came in the USG, I still created a backup image using dd. On my macOS it showed up as /dev/disk2 upon insertion. Replace rdrive596870 with the name of the drive on your machine.

$ diskutil list
$ diskutil unmountdrive /dev/rdrive596870
$ sudo dd if=/dev/rdrive596870 of=original-erl.img

Overwrite the replacement drive (Samsung in my case) with the downloaded OpenBSD mini root file.

$ sudo dd if=miniroot63.fs of=/dev/rdrive596870 bs=1m && sync
$ diskutil eject /dev/rdrive596870

Reinsert the drive, put back the enclosure, and put back the screws.

DO NOT power on USG just yet.

Connect an ethernet cable to WLAN 1 that's connected to your network. We need a DHCP server to give an IP address to USG.

Connect to the serial port.

Check which device your Mac has identified for the serial connection. In my case it was /dev/tty.usbserial.

$ ls -ltr /dev/*usb* | grep tty

Start a screen session on port 115200.

$ screen /dev/tty.usbserial 115200

Power on USG and watch it boot on the screen session.

Install OpenBSD

Once the device is powered up and USG has booted successfully, you'll see this prompt.

Octeon ubnt_e120#

Load the miniroot.

Octeon ubnt_e120# fatload usb 0 $loadaddr bsd.rd
reading bsd.rd
..........................
.................
8789365 bytes read

Boot the image.

Octeon ubnt_e120# bootoctlinux

After a lot of text scrolling across you'll be asked to make a choice from the following menu.

Welcome to the OpenBSD/octeon 6.3 installation program.
(I)nstall, (U)pgrade, (A)utoinstall or (S)hell?

Follow each prompt and answer appropriately. A sample session of my install is below.

Welcome to the OpenBSD/octeon 6.3 installation program.
(I)nstall, (U)pgrade, (A)utoinstall or (S)hell? i
At any prompt except password prompts you can escape to a shell by
typing '!'. Default answers are shown in []'s and are selected by
pressing RETURN.  You can exit this program at any time by pressing
Control-C, but this can leave your system in an inconsistent state.

Terminal type? [vt220]
System hostname? (short form, e.g. 'foo') usg

Available network interfaces are: cnmac0 cnmac1 cnmac2 vlan0.
Which network interface do you wish to configure? (or 'done') [cnmac0] cnmac0
IPv4 address for cnmac0? (or 'dhcp' or 'none') [dhcp] dhcp
cnmac0: no link ..... got link
DHCPDISCOVER on cnmac0 - interval 1
DHCPOFFER from 10.10.10.1 (REDACTED_FOR_PRIVACY)
DHCPREQUEST on cnmac0 to 255.255.255.255
DHCPACK from 10.10.10.1 (REDACTED_FOR_PRIVACY)
bound to 10.10.10.74 -- renewal in 21600 seconds.
IPv6 address for cnmac0? (or 'autoconf' or 'none') [none] autoconf
Available network interfaces are: cnmac0 cnmac1 cnmac2.
Which network interface do you wish to configure? (or 'done') [done] done
DNS domain name? (e.g. 'bar.com') [my.domain] my.example.com
Using DNS nameservers at 10.10.10.1 8.8.8.8

Password for root account? (will not echo)
Password for root account? (again)
Start sshd(8) by default? [yes]
Setup a user? (enter a lower-case loginname, or 'no') [no] ubnt
Full name for user ubnt? [ubnt]
Password for user ubnt? (will not echo)
Password for user ubnt? (again)
WARNING: root is targeted by password guessing attacks, pubkeys are safer.
Allow root ssh login? (yes, no, prohibit-password) [no] yes
What timezone are you in? ('?' for list) [UTC] America/Los_Angeles

Available disks are: sd0.
Which disk is the root disk? ('?' for details) [sd0]
Disk: sd0       geometry: 487/255/63 [7831552 Sectors]
Offset: 0       Signature: 0xAA55
            Starting         Ending         LBA Info:
#: id      C   H   S -      C   H   S [       start:        size ]
-------------------------------------------------------------------------------
*0: 0C      0   1   2 -      1 103  38 [          64:       22528 ] FAT32L
1: 00      0   0   0 -      0   0   0 [           0:           0 ] unused
2: 00      0   0   0 -      0   0   0 [           0:           0 ] unused
3: 00      0   0   0 -      0   0   0 [           0:           0 ] unused
Use (W)hole disk or (E)dit the MBR? [whole] W
Creating a FAT partition and an OpenBSD partition for rest of sd0...done.
/dev/rsd0i: 65372 sectors in 16343 FAT16 clusters (2048 bytes/cluster)
bps=512 spc=4 res=1 nft=2 rde=512 mid=0xf8 spf=64 spt=63 hds=255 hid=64 bsec=65536
The auto-allocated layout for sd0 is:
#                size           offset  fstype [fsize bsize   cpg]
a:          1024.0M            65600  4.2BSD   2048 16384     1 # /
b:           768.0M          2162752    swap
c:         30592.0M                0  unused
d:          1914.1M          3735616  4.2BSD   2048 16384     1 # /tmp
e:          2995.4M          7655616  4.2BSD   2048 16384     1 # /var
f:          2021.3M         13790112  4.2BSD   2048 16384     1 # /usr
g:          1024.0M         17929696  4.2BSD   2048 16384     1 # /usr/X11R6
h:          4387.9M         20026848  4.2BSD   2048 16384     1 # /usr/local
i:            32.0M               64   MSDOS
j:          1748.5M         29013216  4.2BSD   2048 16384     1 # /usr/src
k:          3969.0M         32594176  4.2BSD   2048 16384     1 # /usr/obj
l:         10707.8M         40722752  4.2BSD   2048 16384     1 # /home
Use (A)uto layout, (E)dit auto layout, or create (C)ustom layout? [a] a
Rounding size to bsize (32 sectors): 3920000
Rounding size to bsize (32 sectors): 6134496
Rounding size to bsize (32 sectors): 4139584
Rounding size to bsize (32 sectors): 8986368
Rounding size to bsize (32 sectors): 3580960
Rounding size to bsize (32 sectors): 8128576
/dev/rsd0a: 1024.0MB in 2097152 sectors of 512 bytes
6 cylinder groups of 202.47MB, 12958 blocks, 25984 inodes each
/dev/rsd0l: 10707.8MB in 21929664 sectors of 512 bytes
53 cylinder groups of 202.47MB, 12958 blocks, 25984 inodes each
/dev/rsd0d: 1914.1MB in 3920000 sectors of 512 bytes
10 cylinder groups of 202.47MB, 12958 blocks, 25984 inodes each
/dev/rsd0f: 2021.3MB in 4139584 sectors of 512 bytes
10 cylinder groups of 202.47MB, 12958 blocks, 25984 inodes each
/dev/rsd0g: 1024.0MB in 2097152 sectors of 512 bytes
6 cylinder groups of 202.47MB, 12958 blocks, 25984 inodes each
/dev/rsd0h: 4387.9MB in 8986368 sectors of 512 bytes
22 cylinder groups of 202.47MB, 12958 blocks, 25984 inodes each
/dev/rsd0k: 3969.0MB in 8128576 sectors of 512 bytes
20 cylinder groups of 202.47MB, 12958 blocks, 25984 inodes each
/dev/rsd0j: 1748.5MB in 3580960 sectors of 512 bytes
9 cylinder groups of 202.47MB, 12958 blocks, 25984 inodes each
/dev/rsd0e: 2995.4MB in 6134496 sectors of 512 bytes
15 cylinder groups of 202.47MB, 12958 blocks, 25984 inodes each
/dev/sd0a (33d72d1625866818.a) on /mnt type ffs (rw, asynchronous, local)
/dev/sd0l (33d72d1625866818.l) on /mnt/home type ffs (rw, asynchronous, local, nodev, nosuid)
/dev/sd0d (33d72d1625866818.d) on /mnt/tmp type ffs (rw, asynchronous, local, nodev, nosuid)
/dev/sd0f (33d72d1625866818.f) on /mnt/usr type ffs (rw, asynchronous, local, nodev)
/dev/sd0g (33d72d1625866818.g) on /mnt/usr/X11R6 type ffs (rw, asynchronous, local, nodev)
/dev/sd0h (33d72d1625866818.h) on /mnt/usr/local type ffs (rw, asynchronous, local, nodev)
/dev/sd0k (33d72d1625866818.k) on /mnt/usr/obj type ffs (rw, asynchronous, local, nodev, nosuid)
/dev/sd0j (33d72d1625866818.j) on /mnt/usr/src type ffs (rw, asynchronous, local, nodev, nosuid)
/dev/sd0e (33d72d1625866818.e) on /mnt/var type ffs (rw, asynchronous, local, nodev, nosuid)

Let's install the sets!
Location of sets? (disk http nfs or 'done') [http] http
HTTP proxy URL? (e.g. 'http://proxy:8080', or 'none') [none]
HTTP Server? (hostname, list#, 'done' or '?') [ftp.OpenBSD.org]
Server directory? [pub/OpenBSD/6.3/octeon]

Select sets by entering a set name, a file name pattern or 'all'. De-select
sets by prepending a '-', e.g.: '-game*'. Selected sets are labelled '[X]'.
    [X] bsd           [X] comp63.tgz    [X] xbase63.tgz   [X] xserv63.tgz
    [X] bsd.rd        [X] man63.tgz     [X] xshare63.tgz
    [X] base63.tgz    [X] game63.tgz    [X] xfont63.tgz
Set name(s)? (or 'abort' or 'done') [done] -game63.tgz
Set name(s)? (or 'abort' or 'done') [done] -game63.tgz
    [X] bsd           [X] comp63.tgz    [X] xbase63.tgz   [X] xserv63.tgz
    [X] bsd.rd        [X] man63.tgz     [X] xshare63.tgz
    [X] base63.tgz    [ ] game63.tgz    [X] xfont63.tgz
Set name(s)? (or 'abort' or 'done') [done]
Get/Verify SHA256.sig   100% |**************************|  1376       00:00
Signature Verified
Get/Verify bsd          100% |**************************|  5654 KB    00:04
Get/Verify bsd.rd       100% |**************************|  8583 KB    00:06
Get/Verify base63.tgz   100% |**************************| 86597 KB    00:29
Get/Verify comp63.tgz   100% |**************************| 49971 KB    00:17
Get/Verify man63.tgz    100% |**************************|  7031 KB    00:05
Get/Verify xbase63.tgz  100% |**************************| 16789 KB    00:06
Get/Verify xshare63.tgz 100% |**************************|  4421 KB    00:03
Get/Verify xfont63.tgz  100% |**************************| 39349 KB    00:13
Get/Verify xserv63.tgz  100% |**************************|  5036 KB    00:04
Installing bsd          100% |**************************|  5654 KB    00:00
Installing bsd.rd       100% |**************************|  8583 KB    00:02
Installing base63.tgz   100% |**************************| 86597 KB    01:13
Extracting etc.tgz      100% |**************************|   260 KB    00:01
Installing comp63.tgz   100% |**************************| 49971 KB    00:54
Installing man63.tgz    100% |**************************|  7031 KB    00:13
Installing xbase63.tgz  100% |**************************| 16789 KB    00:21
Extracting xetc.tgz     100% |**************************|  7049       00:00
Installing xshare63.tgz 100% |**************************|  4421 KB    00:09
Installing xfont63.tgz  100% |**************************| 39349 KB    00:50
Installing xserv63.tgz  100% |**************************|  5036 KB    00:05
Location of sets? (disk http nfs or 'done') [done]
Time appears wrong.  Set to 'Wed Apr  4 22:14:34 PDT 2018'? [yes]
Saving configuration files...done.
Making all device nodes...done.
Relinking to create unique kernel...done.

CONGRATULATIONS! Your OpenBSD install has been successfully completed!

When you login to your new system the first time, please read your mail
using the 'mail' command.


INSTALL.octeon describes how to configure U-Boot to boot OpenBSD.
Exit to (S)hell, (H)alt or (R)eboot? [reboot] reboot

I configured root ssh access because I use Ansible later on and found it worked better when it logged in as root. Feel free to disable root ssh for better security.

I created a regular user ubnt to use for ssh access.

I did not install game63.tgz just because. I didn't feel it necessary to install it.

Swap the Bootloader

Once USG has rebooted you'll be at the Octeon ubnt_e120# prompt again because the bootloader is not at the expected location.

We'll swap the bootloader. But first, let's get a list of the existing environment variables. Save this output and keep in a safe place. You may need to undo risky steps ahead.

Octeon ubnt_e120# printenv
bootcmd=fatload usb 0 $loadaddr vmlinux.64;bootoctlinux $loadaddr coremask=0x3 root=/dev/sda2 rootdelay=15 rw rootsqimg=squashfs.img rootsqwdir=w mtdparts=phys_mapped_flash:512k(boot0),512k(boot1),64k@1024k(eeprom)
bootdelay=0
baudrate=115200
download_baudrate=115200
nuke_env=protect off $(env_addr) +$(env_size);erase $(env_addr) +$(env_size)
autoload=n
ethact=octeth0
loadaddr=0x9f00000
numcores=2
stdin=serial
stdout=serial
stderr=serial
env_addr=0x1fbfe000
env_size=0x2000
flash_base_addr=0x1f400000
flash_size=0x800000
uboot_flash_addr=0x1f480000
uboot_flash_size=0x70000
flash_unused_addr=0x1f4f0000
flash_unused_size=0x710000
bootloader_flash_update=bootloaderupdate

Notice bootcmd above. That's what we'll swap.

Octeon ubnt_e120# setenv old_bootcmd ${bootcmd}
Octeon ubnt_e120# setenv bootcmd 'fatload usb 0 $loadaddr bsd;bootoctlinux rootdev=/dev/sd0'
Octeon ubnt_e120# setenv bootdelay 5
Octeon ubnt_e120# saveenv
Octeon ubnt_e120# reset

After USG reboots it should happily boot OpenBSD.

A sample session is below.

U-Boot 1.1.1 (UBNT Build ID: REDACTED) (Build time: Jun  9 2014 - 14:38:01)

BIST check passed.
UBNT_E120 r1:0, r2:16, f:8/135, serial #: REDACTED_FOR_PRIVACY
MPR 13-02044-16
Core clock: 500 MHz, DDR clock: 266 MHz (532 Mhz data rate)
DRAM:  512 MB
Clearing DRAM....... done
Flash:  8 MB
Net:   octeth0, octeth1, octeth2

USB:   (port 0) scanning bus for devices... 1 USB Devices found
    scanning bus for storage devices...
Device 0: Vendor: Samsung  Prod.: Flash Drive FIT  Rev: 1100
        Type: Removable Hard Disk
            Capacity: 30592.0 MB = 29.8 GB (62652416 x 512)                                                                                             0
reading bsd
.............................

5790035 bytes read
ELF file is 64 bit
Allocating memory for ELF segment: addr: 0xffffffff81000000 (adjusted to: 0x1000000), size 0x5796f0
Allocated memory for ELF segment: addr: 0xffffffff81000000, size 0x5796f0
Processing PHDR 0
Loading 4ebb88 bytes at ffffffff81000000
Clearing 8db68 bytes at ffffffff814ebb88
## Loading Linux kernel with entry point: 0xffffffff81000000 ...
Bootloader: Done loading app on coremask: 0x1
bootmem desc 0x24108 version 3.0
avail phys mem 0x0000000000100290 - 0x0000000000fffce0
avail phys mem 0x00000000015796f0 - 0x0000000008100000
avail phys mem 0x0000000008100010 - 0x000000000fffdc00
avail phys mem 0x0000000410000000 - 0x000000041ff00000
Total DRAM Size 0x0000000020000000
mem_layout[0] page 0x0000000000000041 -> 0x00000000000003FF
mem_layout[1] page 0x000000000000055F -> 0x0000000000002040
mem_layout[2] page 0x0000000000002041 -> 0x0000000000003FFF
mem_layout[3] page 0x0000000000104000 -> 0x0000000000107FC0
boot_desc->Initial setup done, switching console.
boot_desc->desc_ver:7
boot_desc->desc_size:400
boot_desc->stack_top:0
boot_desc->heap_start:0
boot_desc->heap_end:0
boot_desc->argc:2
boot_desc->flags:0x5
boot_desc->core_mask:0x1
boot_desc->dram_size:512
boot_desc->phy_mem_desc_addr:0
boot_desc->debugger_flag_addr:0xa44
boot_desc->eclock:500000000
boot_desc->boot_info_addr:0x1001f0
boot_info->ver_major:1
boot_info->ver_minor:2
boot_info->stack_top:0
boot_info->heap_start:0
boot_info->heap_end:0
boot_info->boot_desc_addr:0
boot_info->exception_base_addr:0x1000
boot_info->stack_size:0
boot_info->flags:0x5
boot_info->core_mask:0x1
boot_info->dram_size:512
boot_info->phys_mem_desc_addr:0x24108
boot_info->debugger_flags_addr:0
boot_info->eclock:500000000
boot_info->dclock:266000000
boot_info->board_type:20004
boot_info->board_rev_major:0
boot_info->board_rev_minor:16
boot_info->mac_addr_count:3
boot_info->cf_common_addr:0
boot_info->cf_attr_addr:0
boot_info->led_display_addr:0
boot_info->dfaclock:0
boot_info->config_flags:0x8
Copyright (c) 1982, 1986, 1989, 1991, 1993
        The Regents of the University of California.  All rights reserved.
Copyright (c) 1995-2018 OpenBSD. All rights reserved.  https://www.OpenBSD.org

OpenBSD 6.3 (GENERIC) #0: Sun Mar 25 19:48:22 UTC 2018
    visa@octeon:/usr/src/sys/arch/octeon/compile/GENERIC
real mem = 536870912 (512MB)
avail mem = 523943936 (499MB)
mainbus0 at root: board 20004 rev 0.16
cpu0 at mainbus0: CN50xx CPU rev 0.1 500 MHz, Software FP emulation
cpu0: cache L1-I 32KB 4 way D 16KB 64 way, L2 128KB 8 way
clock0 at mainbus0: int 5
iobus0 at mainbus0
simplebus0 at iobus0: "soc"
octciu0 at simplebus0
cn30xxsmi0 at simplebus0
com0 at simplebus0: ns16550a, 64 byte fifo
com0: console
dwctwo0 at iobus0 base 0x1180068000000 irq 56
usb0 at dwctwo0: USB revision 2.0
uhub0 at usb0 configuration 1 interface 0 "Octeon DWC2 root hub" rev 2.00/1.00 addr 1
octrng0 at iobus0 base 0x1400000000000 irq 0
cn30xxgmx0 at iobus0 base 0x1180008000000
cnmac0 at cn30xxgmx0: RGMII, address REDACTED_FOR_PRIVACY
atphy0 at cnmac0 phy 7: AR8035 10/100/1000 PHY, rev. 2
cnmac1 at cn30xxgmx0: RGMII, address REDACTED_FOR_PRIVACY
atphy1 at cnmac1 phy 6: AR8035 10/100/1000 PHY, rev. 2
cnmac2 at cn30xxgmx0: RGMII, address REDACTED_FOR_PRIVACY
atphy2 at cnmac2 phy 5: AR8035 10/100/1000 PHY, rev. 2
/dev/ksyms: Symbol table not valid.
umass0 at uhub0 port 1 configuration 1 interface 0 "Samsung Flash Drive FIT" rev 2.10/11.00 addr 2
umass0: using SCSI over Bulk-Only
scsibus0 at umass0: 2 targets, initiator 0
sd0 at scsibus0 targ 1 lun 0: <Samsung, Flash Drive FIT, 1100> SCSI4 0/direct removable serial.REDACTED_FOR_PRIVACY
sd0: 30592MB, 512 bytes/sector, 62652416 sectors
vscsi0 at root
scsibus1 at vscsi0: 256 targets
softraid0 at root
scsibus2 at softraid0: 256 targets
boot device: sd0
root on sd0a (33d72d1625866818.a) swap on sd0b dump on sd0b
WARNING: No TOD clock, believing file system.
WARNING: CHECK AND RESET THE DATE!
Automatic boot in progress: starting file system checks.
/dev/sd0a (33d72d1625866818.a): file system is clean; not checking
/dev/sd0l (33d72d1625866818.l): file system is clean; not checking
/dev/sd0d (33d72d1625866818.d): file system is clean; not checking
/dev/sd0f (33d72d1625866818.f): file system is clean; not checking
/dev/sd0g (33d72d1625866818.g): file system is clean; not checking
/dev/sd0h (33d72d1625866818.h): file system is clean; not checking
/dev/sd0k (33d72d1625866818.k): file system is clean; not checking
/dev/sd0j (33d72d1625866818.j): file system is clean; not checking
/dev/sd0e (33d72d1625866818.e): file system is clean; not checking
setting tty flags
pf enabled
starting network
cnmac0: no link .... got link
cnmac0: bound to 10.10.10.190 from 10.10.10.1 (REDACTED_FOR_PRIVACY)
reordering libraries: done.
openssl: generating isakmpd/iked RSA keys... done.
ssh-keygen: generating new host keys: RSA DSA ECDSA ED25519
starting early daemons: syslogd pflogd ntpd.
starting RPC daemons:.
savecore: /bsd: kvm_read: version misread
checking quotas: done.
kvm_mkdb: can't open /dev/ksyms
clearing /tmp
kern.securelevel: 0 -> 1
creating runtime link editor directory cache.
preserving editor files.
starting network daemons: sshd smtpd sndiod.
running rc.firsttime
Path to firmware: http://firmware.openbsd.org/firmware/6.3/
No devices found which need firmware files to be downloaded.
starting local daemons: cron.
Wed Apr  4 22:18:55 PDT 2018

OpenBSD/octeon (octeon1) (console)

login:

Configure as Gateway and Firewall

The device is configured exactly as OpenBSD Network Gateway on EdgeRouter Lite.

You can use Ansible roles I created for that post if you're so inclined. They're available on GitHub - openbsd-on-erl.

Conclusion

I'm pretty happy with USG and OpenBSD. Its setup was nearly identical to ERL 3. Using Ansible means identical configuration can be applied to both kinds of devices to get the same functionality.