Active Directory Needs Friends!

For those of you who didn’t read my predecessor post on setting up a full-blown Active Directory infrastructure on my home network with home directories, roaming user profiles and group policy using only open source software, take a read through that. This is a follow-on post where I have added a second Active Directory domain controller in a private cloud environment and then bridged that private cloud network to my secure home network using WireGuard.

Bridging The Networks

To start off, since I’m using the bleeding-edge Ubuntu version on my primary domain controller, I set up a virtual server in my cloud provider of choice using 21.10 as well. For the private network, I put it on its own private network that does not collide with my home network ( In this case it is

My VPS provider allows me to supply SSH keys at their web console that restricts who can ssh into the remote virtual machine to only those who have the private key that corresponds to the public keys you upload and select. This ensures that I can securely log into the machine as root-level access without fear. The first thing do to, however, when I log into the new server is to update the packages installed on it:

# apt update
# apt upgrade
# reboot

Now for the wireguard setup on the remote virtual machine. For the purposes of this section, we will call it the “server”:

# apt install wireguard wireguard-tools
# wg genkey | sudo tee /etc/wireguard/server_private.key
# wg pubkey | sudo tee /etc/wireguard/server_public.key
# echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
# echo "net.ipv6.conf.all.forwarding=1" >> /etc/sysctl.conf
# sysctl -p
# vim /etc/wireguard/wg0.conf
Address =
ListenPort = 51820
PrivateKey = *** contents of /etc/wireguard/server_private.key ***
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT

PublicKey = *** contents of /etc/wireguard/server_public.key from remote ***
Endpoint = # IP address of remote
AllowedIPs =,

Since my local network is on a residential ISP, I need to use the tools on my ISP’s router to port map the Wireguard port that comes in on the public IP address to the OpenBSD router. Now, we will need to set up the WireGuard configuration on the OpenBSD 7.0 router that I use for my secure network at home (private IP is

# pkg_add wireguard-tools
# sysctl net.inet.ip.forwarding=1
# echo 'net.inet.ip.forwarding=1' | tee -a /etc/sysctl.conf
# mkdir /etc/wireguard
# chmod 700 /etc/wireguard
# openssl rand -base64 32 > /etc/wireguard/server_private.key
# wg pubkey < /etc/wireguard/server_private.key > /etc/wireguard/server_public.key
# vim /etc/hostname.wg0
!/usr/local/bin/wg setconf wg0 /etc/wireguard/wg0.conf
!route add -inet
# vim /etc/wireguard/wg0.conf
PrivateKey = *** contents of /etc/wireguard/server_private.key ***
ListenPort = 51820

PublicKey = *** contents of /etc/wireguard/server/public.key from remote ***
Endpoint = # public IP address of remote
AllowedIPs =,
# vim /etc/pf.conf
... add to end...
pass in on egress proto udp from any to any port 51820 keep state
pass on wg0
pass out on egress inet from (wg0:network) to any nat-to (egress:0)
# pfctl -f /etc/pf.conf
# sh /etc/netstart wg0

Now, run the following command on the remote Linux box to start the Wireguard service:

# systemctl enable wg-quick@wg0.service
# systemctl start wg-quick@wg0.service

At this point, you should be able to check the status of the Wireguard network on both sides with the command wg show and that should show both ends connected. You should be able to ping hosts on the remote network from each end.

So far, the only problem I have found with this setup to bridge the networks, is that my Windows machines that are multi-homed (i.e. one interface – wired ethernet – is connected to my ISP’s network and one – wireless – is connected to my secure network) needs to have a route manually added as follows:

C:\WINDOWS\system32> route add -p MASK

In this case, the network is the remote network and the IP references my OpenBSD 7.0 router.

Remote Samba Active Directory Server

Now that we have a remote network that is securely bridged to our local private network on which the current Samba Active Directory infrastructure is running, it is time to create the VPC virtual server that will be running our Active Directory remote server. My particular VPC service allows me to create a server that is on the same private network as my remote “router” that is running Wireguard, so I create such a server and call it (put in your own AD domain name there).

First things first, the remote AD server must have a route to the Wireguard network. This is not a necessary step on the home network side because the Wireguard server is running on the OpenBSD 7.0 router and by definition is the default route for the servers on that network. This is not the case for the servers on the private network at the VPC. To do this, we simply need to add a persistent route. So as to not mess things up with the default network configuration on the remote host, I decided to create a (yuck) SystemD (blech) service:

# apt update
# apt upgrade
# apt install network-tools
# vim /usr/sbin/
#! /bin/sh
/usr/sbin/route add -net gw eth1
# chmod +x /usr/sbin/route
# vim /etc/systemd/system/MY-NETWORK.service
Description=Route to Wireguard server


# systemctl daemon-reload
# systemctl enable MY-NETWORK.service
# systemctl start MY-NETWORK.service

At this point, you should be able to ping the domain controller on the remote (home) network and from that domain controller, you should be able to ping the new host.

Now we need to do the standard networking configuration ‘stuff’ that Samba likes. First, edit the /etc/hosts file to remove the “ DC2” line and replace it with one tying it to the static private IP address that has been assigned to this virtual host. In this case, “ DC2”.

Here we need to add the necessary packages to host an Active Directory domain controller:

# apt install acl attr samba samba-dsdb-modules samba-vfs-modules winbind libpam-winbind libnss-winbind libpam-krb5 krb5-config krb5-user dnsutils net-tools smbclient

Next, disable systemd’s resolver and add the remote AD server as the DNS name server and also add the Active Directory domain:

# systemctl stop systemd-resolved
# systemctl disable systemd-resolved
# unlink /etc/resolv.conf
# vim /etc/resolv.conf

Now, go ahead and reboot the remote machine and when you log back into it, test to see if DNS is working properly:

# nslookup
# nslookup    name =
# host -t SRV has SRV record 0 100 389

Rename the /etc/krb5.conf file and the /etc/samba/smb.conf file like you did when you created the domain controller on your local network. Then, create a new /etc/krb5.conf file:

    default_realm = AD.EXAMPLE.COM
    dns_lookup_realm = false
    dns_lookup_kdc = true

At this point, we need to set up an NTP server and sync it to the one at our original Active Directory domain controller:

# apt install chrony ntpdate
# ntpdate
# echo "server minpoll 0 maxpoll 5 maxdelay .05" > /etc/chrony/chrony.conf
# systemctl enable chrony
# systemctl start chrony

Now we need to authenticate against Kerberos and get a ticket:

# kinit administrator
... provide your AD\Administrator password ...
# klist

At this point, it’s time to join the domain as a new domain controller:

# samba-tool domain join DC -U"AD\administrator"

After the tool finishes (it produces a lot of output), you need to copy the generated Kerberos configuration file to the /etc directory:

# cp /var/lib/samba/private/krb5.conf /etc/krb5.conf

You need to manually create the systemd service and set things up so that everything fires up when you reboot the server:

# systemctl mask smbd nmbd winbind
# systemctl disable smbd nmbd winbind
# systemctl stop smbd nmbd winbind
# systemctl unmask samba-ad-dc
# vim /etc/systemd/system/samba-ad-dc.service
Description=Samba Active Directory Domain Controller

ExecStart=/usr/sbin/samba -D
ExecReload=/bin/kill -HUP $MAINPID

# systemctl daemon-reload
# systemctl enable samba-ad-dc
# systemctl start samba-ad-dc

OK. At this point we have a Samba Active Directory domain controller running. We need to get SysVol replication going now to ensure that the two controllers are bidirectionally synchronized.

Bidirectional SysVol Replication

To get the SysVol replication going bidirectionally, I followed the guide here. First, you need some tools installed on both DCs:

# apt install rsync unison

Generate an ssh key on both domain controllers:

# ssh-keygen -t rsa

Now, copy the /root/.ssh/ contents from one server into the /root/.ssh/authorized_keys file on the other and vice-versa. Verify that you can log in without passwords from one server to the other. If you are prompted for a password, then edit your /etc/ssh/sshd_config file and add the line “PasswordAuthentication no” and then restart the ssh service. Now you should be able to log in just using public keys and no password from one server to the other and back.

Now, copy the /root/.ssh/ contents from one server into the /root/.ssh/authorized_keys file on the other and vice-versa. Verify that you can log in without passwords from one server to the other. If you are prompted for a password, then edit your /etc/ssh/sshd_config file and add the line “PasswordAuthentication no” and then restart the ssh service. Now you should be able to log in just using public keys and no password from one server to the other and back.

On your new remote DC (DC2 in my example), do the following to ensure that your incoming ssh connection isn’t rate limited:

# mkdir /root/.ssh/ctl
cat << EOF > /root/.ssh/ctl/config
Host *
ControlMaster auto
ControlPath ~/.ssh/ctl/%h_%p_%r
ControlPersist 1

Now, to be able to log what happens during the sync on the local DC (DC1 in my example), do the following to create the appropriate log files:

# touch /var/log/sysvol-sync.log
# chmod 640 /var/log/sysvol-sync.log

Now, do the following on the local DC (DC1 in my example):

install -o root -g root -m 0750 -d /root/.unison
cat << EOF > /root/.unison/default.prf
# Unison preferences file
# Roots of the synchronization
# copymax & maxthreads params were set to 1 for easier troubleshooting.
# Have to experiment to see if they can be increased again.
root = /var/lib/samba
# Note that 2 x / behind DC2, it is required
root = ssh://root@DC2//var/lib/samba 
# Paths to synchronize
path = sysvol
#ignore = Path stats    ## ignores /var/www/stats
copyprog = /usr/bin/rsync -XAavz --rsh='ssh -p 22' --inplace --compress
copyprogrest = /usr/bin/rsync -XAavz --rsh='ssh -p 22' --partial --inplace --compress
copyquoterem = true
copymax = 1
logfile = /var/log/sysvol-sync.log

Now, run the following command on your local DC (DC1 in my example):

# /usr/bin/rsync -XAavz --log-file /var/log/sysvol-sync.log --delete-after -f"+ */" -f"- *"  /var/lib/samba/sysvol root@DC2:/var/lib/samba  &&  /usr/bin/unison

This should synchronize the two sysvols. If you followed my previous how-to and set up Group Policy, this can take some time as there are a lot of files involved that are stored on the SysVol. After it is complete, you can verify this by doing the following on your remote DC (DC2 in my example):

# ls /var/lib/samba/sysvol/

You should see the same file structure under that directory on both servers. This will copy everything including your group policy stuff as well.

Now that you have done the initial sync, just add the following to your crontab on the local DC (DC1 in my example):

# crontab -e
*/5 * * * * /usr/bin/unison -silent

You should monitor /var/log/sysvol-sync.log on your local DC (DC1 in my example) to ensure that everything is synchronizing and staying that way over time.

Hope this little “how-to” helps folks!

Active Directory Says What?

Many of the long-time readers of this blog are going to probably have a panic attack when they read this article because they are going to be asking themselves the question, “Why in the heck does he want to install Active Directory in his life?” The reason, like so many answers to so many of these questions I ask myself is “Because I can!” LOL!!

So I have a small home network that is my playground for learning new technologies and practicing and growing my security skills. I try to keep it segregated from my true home network that my family uses because I don’t want my latest experiment to get in the way of any of them connecting to the Internet successfully.

Just for fun, however, I’m going to start on a path to try a new experiment – I’d like to have the ability to add a new machine to my network and not have to spend half a day setting it up. Furthermore, I’d like to put everything I can either on a local file server that backs up to the cloud or in the cloud that backs up to a local file server in such a way that I can totally destroy any of my machines and be able to reproduce it at the push of a button. The ultimate in home disaster recovery.

What does this buy me? Well, for one, it lets me be even more aggressive in my experimentation. If I lay waste to a machine because of a failed experiment, no big deal – I just nuke and automatically repave it. For another, it makes it way easier to recover a family member’s setup when something goes wrong. I can just rebuild the machine and know they won’t lose anything. That alone will save me lots of time troubleshooting the latest problems with stuff.

So, why Active Directory? I choose this technology because pretty much everything (OpenBSD is going to be interesting) will authenticate centrally with it and yes, I do have to run some Windows and Mac machines on my network, I can’t do it all on OpenBSD and Linux so it’s a good common ground.

Now, I will die before installing a Windows Server in my infrastructure (LOL) so I have been very careful saying “Active Directory” and not “Windows Server”, or “Azure AD”. I’m going to see how far Samba 4 has come since the last time I played with it. If I can do the full meal deal of authentication, authorization, roaming user profiles and network home directories on a Windows machine, then I can fill in around the edges on my non Windows machines using NFS and other techniques.

Setting up Ubuntu

First things first, I want to start with a clean install of my domain controller. To this end, I’ll nuke and repave my 32-core Threadripper box in my basement with the latest Ubuntu 21.10 build on it and install samba on bare metal. I had originally thought about doing this on a VM or on a Docker container, but I want the reliability and control-ability of a bare metal install with a static IP address, etc. Therefore, after carefully backing up the local files that I wanted to save off of this machine (ha – that’s a lie, I just booted from a USB thumb drive and Gparted the drives with new partition tables), I installed a fresh copy of Ubuntu 21.10 with 3rd party drivers for my graphics card.

Once I had the base OS laid down, I used the canonical documentation from (not documentation from Canonical, the owner of Ubuntu <g>), along with some blog posts (1), (2), and (3) to determine my full course of action. I’ll outline the various steps below.

Active Directory Domain Controller

First things first, we need to get the network set up the way Samba wants it on this machine. That consists of setting up a static IP address on the two NICs in my server (one for my “secure” home network and one for my insecure “family” network) and setting the hostname and /etc/hosts file changes. Specifically, I used NetworkManager from the Ubuntu desktop to set the static IPs, the gateway and the netmasks and then modified /etc/hosts as follows:    localhost    DC1

It is important to note that Ubuntu will put in an additional line for your host and you need to (apparently, per the documentation) remove that. I then modified my /etc/hostname file as follows:

Now for a fun one. We need to permanently change /etc/resolv.conf and not have Ubuntu overwrite it on the next boot. To do that, we have to:

# systemctl stop systemd-resolved
# systemctl disable systemd-resolved
# unlink /etc/resolv.conf
# vim /etc/resolv.conf

At this point, you should have the networking changes in place you need for now. You’ll have to later loop back around and change /etc/resolv.conf to use this machine’s IP address as the nameserver once you have Samba running with it’s built-in DNS server up and running but we don’t want to lose name resolution in the meanwhile so I’ve hard coded it to point to my local DNS server on OpenBSD.

Now it’s time to install the necessary packages to make this machine an active directory domain controller:

# apt update
# apt install acl attr samba samba-dsdb-modules samba-vfs-modules winbind libpam-winbind libnss-winbind libpam-krb5 krb5-config krb5-user dnsutils net-tools smbclient

Specify the FQDN of your server when prompted on the ugly purple screens for things like your Kerberos server and your Administrative server.

Now, it’s time to create the configuration files for Kerberos and Samba. To do this, I ran the following commands:

# mv /etc/krb5.conf /etc/krb5.conf.orig
# mv /etc/samba/smb.conf /etc/samba/smb.conf.orig
# samba-tool domain provision --use-rfc2307 --interactive

I take the defaults, being careful to double-check the DNS forwarder IP address (that’s where the DNS server that will be serving your AD network will forward requests it cannot resolve) and then entered my Administrator password. Keep in mind that be default, the password complexity requirements are set pretty high (which I like) so pick a good one.

Now use the following command to move the Kerberos configuration file that was generated by the Samba provisioning process to its correct location:

# cp /var/lib/samba/private/krb5.conf /etc/krb5.conf

Next, we need to set things up so that the right services are started when you reboot the machine. To do that, issue the following commands:

# systemctl mask smbd nmbd winbind
# systemctl disable smbd nmbd winbind
# systemctl stop smbd nmbd winbind
# systemctl unmask samba-ad-dc
# vim /etc/systemd/system/samba-ad-dc.service
Description=Samba Active Directory Domain Controller

ExecStart=/usr/sbin/samba -D
ExecReload=/bin/kill -HUP $MAINPID

# systemctl daemon-reload
# systemctl enable samba-ad-dc
# systemctl start samba-ad-dc

Now go back and update the /etc/resolv.conf file to use the new Samba-supplied DNS service:

# vim /etc/resolv.conf

This is probably a good time to reboot your machine. When you do so, don’t forget to check that /etc/resolv.conf hasn’t been messed with by Ubuntu. If it has, double-check the work you did above and keep trying reboots until it sticks.

Now we need to create the reverse zone for DNS:

# samba-tool dns zonecreate -U Administrator
# samba-tool dns add 2.1 PTR -U Administrator

If you have multiple NICs in your AD server, you will need to repeat this process for their networks. At this point, double-check that the DNS responder is coming back with what it needs to in order to serve the black magic of the Active Directory clients:

# nslookup


# nslookup        name =

# host -t SRV has SRV record 0 100 389
# host -t SRV has SRV record 0 100 88
# host -t A has address

If you have multiple NICs in your AD server, you might want to double-check the DNS A records that are returned are reachable from the networks your clients typically use. Since I have a “home” network and a “secure” network, I can manage DNS and DHCP on my secure network so I tend to make sure that my domain controller hostname resolves to an IP address on the secure network. The Windows DHCP admin tools are pretty handy for checking on this and making changes.

Verify that the Samba service has file serving running correctly by listing all of the shares from this server as an anonymous user:

# smbclient -L localhost -N

You should see sysvol, netlogon and IPC$ listed. Any error about SMB1 being disabled is actually a good thing. Validate that a user can successfully log in:

# smbclient //localhost/netlogon -UAdministrator -c 'ls'

You should see a listing of the netlogon share directory which should be empty. Now check that you can successfully authenticate against Kerberos:

# kinit administrator
# klist

You should see a message about when your administrator password will expire if you are successfully authenticated by Kerberos. The klist command should show the ticket that was generated by you logging in as Administrator.

If you look at the documentation in the Samba Wiki, you’ll see that ntp seems to be a better service to use over chrony or optnntpd. If you look at the documentation for chrony (which everyone seems to use), you’ll get a different story. However, when I used chrony, I kept getting NTP errors on my Windows clients so I’m configuring in this post with ntp.

# apt install ntp
# samba -b | grep 'NTP'
    NTP_SIGND_SOCKET_DIR: /var/lib/samba/ntp_signd
# chown root:ntp /var/lib/samba/ntp_signd/
# chmod 750 /var/lib/samba/ntp_signd/
# vim /etc/ntp.conf
restrict mask nomodify notrap
disable auth
# systemctl restart ntp

To be clear, the lines I’m showing after editing the ntp.conf file are lines that you ADD to the file. Also, if you have more than one NIC in the server, you’ll need to add them in on the restrict and broadcast lines as a second line for each.

Now, let’s test that everything is working by enrolling a Windows 10 machine into the domain. Ensure first that you are on the right network and just for safety’s sake, do a reboot so you pick up the DNS server, etc. I have modified the DHCP server on my network to pass the correct information that a client needs as follows (from /etc/dhcpd.conf in OpenBSD):

option domain-name "";
option domain-name-servers;
option ntp-servers;

Microsoft has done a bang-up job of hiding this in the UI compared to where it has been for literally decades (“get off my lawn!!”). I prefer the old-fashioned way so I ran the following using Windows key + R to get the old UI I’m most comfortable with:


Press the “Change” button and then select “Domain” and enter “” as the name of your domain. That should prompt you for your admin credentials. I typically use AD\administrator as my userid just to be safe. In a matter of seconds, you should be welcomed to the domain.

For safety’s sake, I recommend clearing out your application and system event logs on that machine, rebooting and logging in as your domain admin. Once that’s done, examine the event viewer to ensure that you aren’t seeing any errors that might indicate something isn’t configured correctly on the server. Remember to click the “other user” button on the Windows 10 login screen and use the AD\Administrator to tell Windows which domain you want to log into.

There is a warning (DNS Client Events, Event ID: 8020) that I see in the System event log. This appears to be a problem where the Windows machine tries to re-register with dynamic DNS in Samba with exactly the same info that is already registered for it and Samba returns an error. You can still resolve the client machine from the server so it worked the first time, I think it can be safely ignored for now.

For ease of maintenance you might want to install the “Windows RSAT Tools” on your Windows machine that give you a good UI for managing all of the fun stuff that Active Directory brings to the table. They are a free download.

I really do NOT recommend using your domain controller as a file server. To set that up on another machine, please see the next section.

Samba File Server in a Domain

Thankfully, the wonderful documentation on the Samba WIKI has an entire entry dedicated to setting up Samba as a domain member. First things first, we need to configure the network settings on our file server to use the Active Directory server as the DNS server.

As I did with the domain controller above, I used NetworkManager from the Ubuntu desktop to set the static IPs, the gateway and the netmasks and then modified /etc/hosts as follows:    localhost    NAS

It is important to note that Ubuntu will put in an additional line for your host and you need to (apparently, per the documentation) remove that. I then modified my /etc/hostname file as follows:

We need to permanently change /etc/resolv.conf and not have Ubuntu overwrite it on the next boot. To do that, we have to:

# systemctl stop systemd-resolved
# systemctl disable systemd-resolved
# unlink /etc/resolv.conf
# vim /etc/resolv.conf

After a quick reboot and verification that the resolv.conf changes survived, we need to install some packages:

# apt install acl attr samba samba-dsdb-modules samba-vfs-modules winbind libpam-winbind libnss-winbind libpam-krb5 krb5-config krb5-user smbclient

Now we need to now configure Kerberos and Samba. First, if there are files currently at /etc/krb5.conf and/or /etc/samba/smb.conf, remove them. Create a new /etc/krb5.conf file with the following contents:

    default_realm = AD.EXAMPLE.COM
    dns_lookup_realm = false
    dns_lookup_kdc = true

Next, it will be necessary to synchronize time to the domain controller. Since this server won’t be broadcasting network time to client machines (i.e. it isn’t a domain controller), I’ll be setting it up with chrony which is built into Ubuntu.

# apt install chrony ntpdate
# ntpdate
# vim /etc/chrony/chrony.conf
server minpoll 0 maxpoll 5 maxdelay .05
# systemctl enable chrony
# systemctl start chrony

That line under the vim command should be the only line in the file. To validate that everything is working, a call to systemctl status chrony should show that it is active and running. First things first, we need to set up the /etc/samba/smb.conf file:

    workgroup = AD        
    security = ADS        
    realm = AD.EXAMPLE.COM       
    netbios name = NAS
    domain master = no
    local master = no
    preferred master = no

    idmap config * : backend = tdb
    idmap config * : range = 50000-100000
    vfs objects = acl_xattr        
    map acl inherit = Yes        
    store dos attributes = Yes

    winbind use default domain = true
    winbind offline logon = false
    winbind nss info = rfc2307
    winbind refresh tickets = Yes
    winbind enum users = Yes
    winbind enum groups = Yes

Now we will need to join the domain:

# kinit administrator
# samba-tool domain join AD -U AD\\Administrator
# net ads join -U AD\\Administrator

You’ll probably get a DNS error when you join the domain. Regardless, add an A record and a PTR record for the server into the DNS as follows:

# samba-tool dns add 3.1 PTR -U Administrator
# samba-tool dns add A

If you have multiple NICs in your file server, make sure you repeat the process for the IP address ranges assigned to them. Now, add the “winbind” parameter as follows to /etc/nsswitch.conf:

# vim /etc/nsswitch.conf
passwd: files winbind systemd
group: files winbind systemd
shadow: files winbind

Next, we will need to enable and start and restart some services:

# systemctl enable smbd nmbd winbind
# systemctl start smbd nmbd winbind
# pam-auth-update

Before proceeding any further, you should probably reboot the machine. Now for some tests to make sure that everything is working ok:

# wbinfo --ping-dc
checking the NETLOGON for domain[AD] dc connection to "" succeeded.
# wbinfo -g
... list of domain groups ...
# wbinfo -u
... list of domain users ...
# getent group
... list of Linux groups and Windows groups...
# getent passwd
... list of Linux users and Windows users...

Windows Home Directories

A common configuration done by Windows Domain administrators is to create a default “Home” drive (typically mapped to the H: drive letter) for users. To do this, we will want to first set up a file share on the server. The goal will be to set up a mapped “HOME” directory for each domain user. We’ll start off by adding the following to the /etc/samba/smb.conf file:

    comment = Home directories
    path = /path/to/folder
    read only = no
    acl_xattr:ignore system acls = yes

After issuing an “smbcontrol all reload-config” on the file server to reload the changes to the config file, you should now be able to see a share called \\nas\users. When you create the directory on the filesystem, issue the following commands:

# chown "Administrator":"Domain Users" /path/to/folder/
# chmod 0770 /path/to/folder/

It is important to grant the “SeDiskOperatorPrivilege” to the “Domain Admins” group as follows. This has to be done on the file server itself.

# net rpc rights grant "AD\\Domain Admins" SeDiskOperatorPrivilege -U "AD\administrator"

Finally, from the “Active Directory Users and Groups”, select the user in the “Users” folder, right click and select “Properties”. After changing to the “Profile” tab, select the “Connect” radio button under the “Home folder”, choose H: as the drive letter and put in \\nas\users\{user name} for the “To:” entry field. This should automatically create the directory and set the correct permissions on it.

Now log out of the domain and back in as the user account you modified above and you should automatically get an H: drive that maps to that folder on the file server.

User Profiles

OK, so the cool kids on their Windows networks also have this thing called a “Roaming User Profile” that allows you to put their user profile on a file server and then they can move from one machine to another and simply access their stuff as if it was all the same machine. I wanted to see how Samba handled this and sure enough, I got a hit in the Samba wiki that indicated it was possible.

First things first, we need to create a share on our file server to hold the profiles, so I added this to my /etc/samba/smb.conf file:

    comment = Users profiles
    path = /path/to/profile/directory
    browseable = No
    read only = No
    csc policy = disable
    vfs objects = acl_xattr
    acl_xattr:ignore system acls = yes

After making that change, I need to create the directory to hold the profiles and set the UNIX ownership and permissions like I did with the home directories above:

# mkdir /path/to/profile/directory
# chown "AD\Administrator":"AD\Domain Users" /path/to/profile/directory
# chmod 0700 /path/to/profile/directory

After a quick “smbcontrol all reload-config” to pull the new changes in, we now have a share on the file server called “profiles” that will hold the resulting Windows user profiles. I used the “Active Directory Users & Computers” tool on my Windows machine (logged in as Administrator), opened the property dialog for my users, navigated to the “Profile” tab and entered the UNC name for the profile directory \\NAS\profiles\{user-name}. The key is to know that, depending on the version of Windows, the system will add a suffix (in my case “.v6”) to that directory name and it will initially be created empty. When you log out, it will actually copy the stuff into the directory and you should see the directories and files show up on your file server. It seems this is the consistent behavior. For example, saving a file into the “Documents” directory on the Windows machine isn’t propagated to the server’s file system until that user logs out.

It really was that easy!

Group Policy

Given the fact that I had, at this point a fully functional Active Directory infrastructure with network home directories, roaming user profiles and all of it was running on Open Source platforms, I thought I’d really try to push it over the edge and dip my toe in the water around Group Policy. Group Policy is some magic stuff based on LDAP that, in the Windows world, allows you to automatically configure an end-user’s workstation. I found documentation in the Samba wiki that indicated it was possible to make this work so I thought I’d give it a try and see what I needed to do.

It looked like the first thing I needed to do was load the Samba “ADMX” templates into the AD domain controller. To do that, I used the following command:

# samba-tool gpo admxload -H -U Administrator

Sure enough, logging into my Windows machine as a domain admin, I was able to see that the command had indeed injected the Samba files into the Sysvol:

H:\> dir \\DC1\SysVol\\Policies\PolicyDefinitions

That command aove should show you the en-US directory and the samba.admx file. Now we need to download the Microsoft ADMX templates and install them:

# apt install msitools
# cd /tmp
# wget ''
# msiextract Administrative\ Templates\ \(.admx\)\ for\ Windows\ 10\ October\ 2020\ Update.msi
# samba-tool gpo admxload -U Administrator --admx-dir=Program\ Files/Microsoft\ Group\ Policy/Windows\ 10\ October\ 2020\ Update\ \(20H2\)/PolicyDefinitions/

The last line will take a few seconds as it processes the files and loads them into the SysVol. You can again confirm the presence of the new policies using the “dir” command above from your Windows machine. At this point, you have the group policies set up and installed into your environment and should be able to manipulate them using the “Group Policy Management Console” on your Windows workstation.


While this is probably one of my stranger, and more technical posts, I think this is a cool example of how you can totally eliminate paid software from your server infrastructure and yet still have the full functionality of something like Active Directory in your tool belt.

Thinkpad T14 (AMD) Gen 2 – A Brave New World!

As long-time readers of this blog are aware, I’m a bit of a Thinkpad fanatic. I fell in love with these durable machines when I was working for IBM back in the late 90’s and accidentally had one fall out of my bag, bounce down the jetway stairs and hit the runway hard – amazingly enough it had a few scuffs but zero damage! After the purchase of the brand by Lenovo, I was a bit worried, but they continue to crank out (at least in the Thinkpad T and X model lines) high-quality, powerful machines.

Thinkpad T480 – RIP

I ran into a nasty problem with my Thinkpad T480 where the software on the machine actually physically damaged the hardware. I know! I thought that was impossible too (other than the 70’s PET machine that had a software-controlled relay on the motherboard that you could trigger continuously until it burned out) but nope – the problem is real.

Essentially, the Thunderbolt I/O port on the machine is driven by firmware running out of an NVRAM chip on the motherboard that can be software-updated as new firmware comes out. As with any NVRAM chip, there are a finite number of write-cycles before the chip dies, but the number of times you will update your firmware is pretty small so it works out well.

Unfortunately, Lenovo pushed out a firmware update that wrote continuously to the NVRAM chip and if you didn’t patch fast enough (they did release an urgent/critical update), then the write-cycles would be exceeded, the chip would fail and the bring-up code would not detect the presence of the bus and thus you had no more Thunderbolt on the laptop. Well, I didn’t update fast enough so “boom” – it is now a Thunderbolt-less laptop.

The New T124 (AMD) Gen 2

Well, enter the need for a new laptop. I decided to jump ship from the Intel train and try life out on the “other side” but ordering a Thinkpad T14 (AMD) Gen 2 machine with 16gb of soldered RAM (there is a slot that I will be populating today that can take it up to 48gb max – I’m going with 32gb total by installing an $80 16gb DIMM) and the Ryzen Pro 5650U that has 6 cores and 12 threads of execution. The screen was a 1920×1080 400 nit panel and looks really nice.

When the laptop showed up, I booted the OpenBSD installer from 6.9-current and grabbed a dmesg and discovered that I lost the Lenovo lottery and had a Realtek WiFi card in the machine. Well, the good news was that I had upgraded the card in my T480 to an Intel AX200 so I swapped it for the one I took out of the T480 and then used it in the T14 to replace the Realtek card. Worked like a charm.

The Ethernet interface on this machine is a bit odd. It’s a Realtek chipset as well, but it shows up as two interfaces (re0 and re1). The deal is that re0 is the interface that is exposed when the machine is plugged into a side-connecting docking station and re1 is the interface that is connected to the built-in Ethernet port. The device driver code that is in 6.9-current as of this writing works just fine with it, however, so I’m happy.

Now for the bad news. Every Thinkpad I have owned for the last decade allows me to plug an m.2 2240 SATA drive into the WWAN slot and it works great. I assumed that would be the case with this machine. While I had the bottom off to replace the WiFi card, I slipped the 1TB drive from the WWAN slot of my T480 into the WWAN slot of the T14 and booted up. I was immediately presented with an error message stating effectively that the WWAN slot was white-listed by Lenovo and would only accept “approved” network cards. I was beyond frustrated by this.

Given that I want to get this machine into my production workflow, I decided that I’d slog along for the time being by putting a larger m.2 2280 NVMe drive in, installing rEFInd to allow me to boot multiple partitions from a single drive and then clone the 512gb drive that is in the machine to the 1GB drive out of the T480. Then, the remaining space on the new drive will contain an encrypted partition for my OpenBSD install.

Installing rEFInd

I followed the instructions from the rEFInd site on how to manually install under Windows 10 and the steps I followed included downloading and unpacking the ZIP file and then running the following commands from an administrative command prompt:

C:\Users\xxxx\Downloads\refind-bin-0.13.2\> mountvol R: /s
C:\Users\xxxx\Downloads\refind-bin-0.13.2\> xcopy /E refind R:\EFI\refind\
C:\Users\xxxx\Downloads\refind-bin-0.13.2\> r:
R:\> cd \EFI\refind
R:\EFI\refind\> del /s drivers_aa64
R:\EFI\refind\> del /s drivers_ia32
R:\EFI\refind\> del /s tools_aa64
R:\EFI\refind\> del /s tools_ia32
R:\EFI\refind\> del refind_aa64.efi
R:\EFI\refind\> del refind_x64.efi
R:\EFI\refind\> rmdir drivers_aa64
R:\EFI\refind\> rmdir drivers_ia32
R:\EFI\refind\> rmdir tools_aa64
R:\EFI\refind\> rmdir tools_ia32R:\EFI\refind\> rename refind.conf-sample refind.conf
R:\EFI\refind\> mkdir images
R:\EFI\refind\> copy C:\Users\xxx\Pictures\mtstmichel.jpg images
R:\EFI\refind\> bcdedit /set "{bootmgr}" path \EFI\refind\refind_x64.efi

That next to the last line is because I wanted to have a picture of my “happy place” (Mount Saint Michel off of the northern coast of France) as the background for rEFInd. I edited the refind.conf file and added the following lines:

banner images\mtstmichel.jpg
banner_scale fillscreen

A quick reboot shows that rEFInd is installed correctly and has my customized background. Don’t be alarmed that the first time you boot up with rEFInd is slow, I think it is doing some scanning and processing and caching because the second and subsequent boots are faster.

Cloning the Drives

The process that I am going to follow, at a high level, is to first clone the contents of my primary 1TB 2280 NVMe drive in my T480 to a spare 256GB drive. I will then erase the 1TB drive and clone the contents of my T14’s drive to it (it’s only 512GB). I will then erase the 512GB drive and clone the 256GB drive back to it. Finally, for good operational security (OpSec) purposes, I’ll use the open source Windows program Eraser erase the 256GB drive. At this point I should have a bootable T480 (with a fried Thunderbolt bus – grr…) on the 512GB drive, and a bootable T14 on the 1TB drive.

I’m using Clonezilla, an open source tool that I burn to a bootable USB drive to do the cloning. For hardware that I am using to accomplish all of this, first I use a Star Tech device that allows me to plug m.2 drives into a little box that then acts as a 2.5 inch SSD drive. I plug that into a Wavlink USB drive docking station that can hold either 3.5″ or 2.5″ drives.

Another piece of software that I use as part of this process is GPartEd Live – an open source tool that allows you to create a USB drive that boots into the GPartEd software (the Gnu Partition Editor). This allows me to view the partition structure of one drive and create an analagous partition structure on another drive. The built-in tools for Windows to do this work (Disk Manager for example) can create hidden partitions under the covers that can cause problems with this process. I prefer to use GPartEd to ensure that I can see and control everything that is going on.

Step One is to take the T480, boot it into Windows and connect the Wavlink device to it with the 256GB NVMe drive plugged into it via the StarTech adapter. While I’m using Eraser to wipe the 256GB drive, I also go into Windows settings and decrypt the Windows disk by turning off BitLocker for it. This may not be necessary but it makes me feel more comfortable to do the cloning with unencrypted Windows drives because the key for the encryption is store in the TPM device on the motherboard and I’m not sure if the fact that the underlying hardware changes would muck that up. After the erase and decrypt is finished, I shrank the partition using “Disk Management” on Windows to be smaller than the new physical disk. If you don’t do this, then Clonezilla won’t allow you to clone from a larger partition to a smaller one.

Next we will need to reboot the machine to GPartEd Live. For the destination drive, you will need to use the “Device” menu and create a new GPT partition table. Take a look at the source drive and make a note of the various partitions, their flags, and their sizes. On the destination drive, recreate that partition structure with the same flags and the same or slightly larger size. I generally bump up the size of the partition by just a bit in order to avoid getting into trouble with rounding the size for display on the screen. If you get it wrong, don’t worry, Clonezilla will yell at you and you’ll have to go back and do this over again. 🙂

When launching Clonezilla, since I have the high resolution display on the T480 (a mistake I’ll never make again, HiDPI is a PITA in everything but Windows) I had to use my cell phone to zoom in on the microscopic text and select the “use 800×600 with large fonts from RAM” option. With readable text, I then make sure that I’m choosing “device-device” from the first menu (not the default). Next, select “Beginner Mode” to reduce the complexity of the choices you’ll have to make. After that, you want to select “part_to_local_part” to clone from one partition on the source drive to the corresponding partition on the destination drive. Finally, select the source partition and the destination partition. I recommend you do the smaller partitions first and then let the main C: partition (the largest one) grind because it can take a long time to clone.

After cloning the T480 drive, I removed it from the machine and was ready to clone the T14’s drive to it. This is where I ran into a “keying” problem with m.2 drives. Some are “B” keyed, and some are “B+M” keyed. This refers to the number of cutouts where they plug into the slot. Well, it looks like the NVMe drives in both the T480 and the T14 don’t fit the StarTech adapter. After some juggling around I found an old 256MB drive that I was able to use to get the swap completed.

Creating the OpenBSD Partition

To do this, I will use “Disk Manager” on Windows and shrink the NTFS partition (if necessary) to make room for OpenBSD and then create a new partition on the drive that takes up the remaining space. If you check the “don’t assign a drive letter” box and the “don’t format the partition” box, you’ll get a raw, unformatted partition that takes up the remaining space on the disk.

That new raw partition will be changed in OpenBSD to be the home of the encrypted slice on which I’ll be installing the operating system. After creating that partition, it’s time to download the 6.9-current .IMG file for the latest snapshot and use Rufus on Windows to create the USB drive and reboot from it.

Once in the OpenBSD installer, drop immediately to the shell and convert that NTFS partition into an OpenBSD partition. That will be where we we put the encrypted slice that we will be installing to. To do this, run the following commands:

# cd /dev
# sh ./MAKEDEV sd0
# fdisk -E sd0

sd0: 1> print
sd0: 1> edit 4
Partition id: A6
Partition offset <ENTER>
Partition size <ENTER>Partition name: OpenBSD
sd0*: 1> write
sd0: 1> exit

The print command above should show you the 4 partitions on your drive (the EFI partition, the Windows partition, the WindowsRecovery partition and your fourth partition that will hold OpenBSD that you created above).

Now that you have a partition for OpenBSD, you’ll want to copy the EFI bootloader over to your EFI drive. You’ll later make a configuration change in rEFInd to not only display it on the screen, but also show a cool OpenBSD “Puffy” logo for it!

# cd /dev
# sh ./MAKEDEV sd1
# mount /dev/sd1i /mnt
# mkdir /mnt2
# mount /dev/sd0i /mnt2
# mkdir /mnt2/EFI/OpenBSD
# cp /mnt/efi/boot/* /mnt2/EFI/OpenBSD
# umount /mnt
# umount /mnt2

Now that you have an OpenBSD EFI bootloader in its own directory on the EFI partition, you’ll want to create the encrypted slice for the operating system install:

# disklabel -E sd0

sd0> a a
sd0> offset: <ENTER>
sd0> size: <ENTER>
sd0> FS type: RAID
sd0*> w
sd0> q

# bioctl -c C -l sd0a softraid0
New passphrase: <your favorite passphrase>
Re-type passphrase: <your favorite passphrase>

Pay attention to the virtual device name that bioctl spits out for your new encrypted “drive”. That’s what you will tell the OpenBSD installer to use. To re-enter the installer, type “exit” at the command prompt. Do your install of the operating system as you normally do. When you reboot, go into Windows.

First, download an icon for OpenBSD from here (or pick your favorite elsewhere). Next, bring up an administrative command prompt and use the following commands to mount the EFI partition and add the icon for OpenBSD:

C:\Windows\system32> mountvol R: /s
C:\Windows\system32> r:
R:> cd \EFI\refind
R:\EFI\refind> copy "C:\Users\<YOUR USER>\Download\495_openbsd_icon.png" icons\os_openbsd.png

Save your changes, exit notepad and then reboot. rEFInd is smart enough to find your OpenBSD partition and use the icon you just added. When you select it from the rEFInd UI, you should be presented with your OpenBSD encrypted disk password and be able to boot for the first time. I ran into a weird thing with my snapshot where it couldn’t download the firmware. I formatted a USB thumb drive as FAT32, downloaded the amdgpu, iwx, uvideo and vmm firmware from the site, mounted the drive in my OpenBSD system and ran fw_update -p /mnt to get the firmware.

At this point, you should be able to reboot and select either Windows or OpenBSD from your rEFInd interface. My hope is that Lenovo will remove this absurd white-listing of the WWAN devices from their UEFI/BIOS code and I’ll be able to plug drives into it again; however, if (and this is more likely) they do not, I’ll at some point buy a 2TB m.2 NVMe drive for this machine, repeat this process and be able to add Linux to it.

I hope folks find this guide helpful.

OpenBSD 6.9 – Help with the “Failed to install bootblocks” issue

Hi everyone!

I purposely chose a non-catchy title so that it would be more easily found by the search engines as this one has been a challenge for me in my last several laptop installs and I always manage to fix it after fiddling around for a while. This time around, I thought I’d actually produce a decent (hopefully!) write-up on just how I go about addressing the problem from scratch. This will provide two benefits: 1) I’ll have a nice step by step the next time I install my machine <grin>; and 2) It might help some other intrepid soul who is running into the same issue!

While the FAQ is always the best place to go for the most up to date steps on formatting and installing a system, I tend to run a “weird” setup that it seems like confounds the installer and most easily-accessible information. What I normally do in my Thinkpad laptops is install a second (or third) SSD or NVMe drive and then dedicate the entire disk to a given operating system. For example, if I’m running Windows 10 and OpenBSD 6.9 on my Thinkpad T480, I install Windows on the first drive (so that if my machine falls into evil hands and they power it on, it will just default boot into Windows and they might not even suspect OpenBSD is on the machine) and then I install OpenBSD onto the second drive. I then use the UEFI or BIOS boot menu to choose the OpenBSD drive to boot from.

Install Windows

I started off by installing Windows from a USB key to the primary drive in the laptop. As is my custom, after install, I put on all of the drivers and used the group policy editor to increase the BitLocker encryption from 128-bit AES to 256-bit AES. I also edited the registry to allow Outlook’s OST file to expand beyond the pitiful limit that it defaults to. After a reboot, I start the BitLocker encryption process and connect my email accounts.

If you are installing OpenBSD on a drive that has previously had something on it, it’s always a good idea to erase that drive. I use an open source tool for Windows called Eraser if I’m on Windows or good old dd if I’m on Linux. Eraser’s UI is a bit weird. It requires that you create a task that you can “run manually”, select the disk to be erased (in my cased “Hard disk 1”) and then select an erasure method (I use Pseudorandom 1-pass), then run the task manually.

I then download the install69.img file from my favorite mirror ( and use Rufus to transfer it to a bootable USB drive. I reboot, hit <F12> to get a boot menu from the UEFI, select my USB drive and then boot into the OpenBSD installer.

Install OpenBSD

The first thing I do is look at my dmesg to see what devices my drives have been attached to:

# dmesg | grep -i sd

This shows (in my case) that my Windows drive is connected to sd0, my blank drive that I will put OpenBSD on is connected to sd1 and my USB installer device is connected to sd2. Next, I need to create the necessary /dev devices:

# cd /dev
# sh ./MAKEDEV sd1
# sh ./MAKEDEV sd2

If you do a quick ls, you should see that the MAKEDEV script created the necessary device files and you should be good to proceed to the next step. Next, we want to initialize the sd1 drive to a GPT partitioning scheme and create the initial EFI partition on the disk. Fun fact, the EFI partition (while its own partition type) is formatted using FAT32 so thanks Windows 95! Here’s how you do this:

# fdisk -iy -g -b 960 sd1
# newfs_msdos /dev/rsd1i

Note my use of the /dev/r device (the raw device) and not the /dev/sd1i (normal device) in that second command. I’m not entirely sure if that is necessary, but the nice Reddit post that sparked me to think about how to do this did so why not, eh? If you get a weird error message trying to run newfs_msdos, it is likely that you have some previous partitioning data on that drive and it would be a good idea to completely erase it (see above).

Now, we need to mount the new partition, create the necessary directory structure that UEFI looks for and put the UEFI loader file from our installer USB drive into that directory:

# mount /dev/sd2i /mnt
# mount /dev/swd1i /mnt2
# mkdir -p /mnt2/efi/boot
# cp /mnt/efi/boot/* /mnt2/efi/boot

Now, we need to create the slice in the OpenBSD partition for the encrypted filesystem (you can skip this if you want to not have an encrypted drive):

# disklabel -E sd1

a a [ENTER]
offset: the default given
size: *
type: RAID

At this point, we have a slice set up as type “RAID” so we need to use the bioctl program to set up the encryption information along with the drive’s encryption password:

# bioctl -c C -l /dev/sd1a softraid0

You should see in the response to the above command the name of the new “virtual” encrypted disk. That is the disk that you will be installing OpenBSD onto. When you reach the question in the installation program about “Which disk is the root disk?”, enter that value (in my case, sd4). When i tasks whether or not you want to “Use (W)hole disk MBR, whole disk (G)PT or (E)dit?”, pick the MBR option (I know, this is counter intuitive but trust me here).

After the installer reboots the system, I press the [F12] key to get the boot menu (your key might be different if you aren’t running a Thinkpad) and select the disk I have installed OpenBSD on. I am immediately presented with the password prompt to decrypt the encrypted slice “virtual” disk and, upon entering it, I get the boot prompt. Everything proceeds as normal from that point forward and I am presented with the login prompt for my new system.

Updated Laptop Setup

If you are still with me and want to see how I set up my OpenBSD desktop (I get criticized slightly for making it “too heavy” with “too many packages” but I have to use Ubuntu as well for what I do and I like to have the UI be as consistent across the two operating systems as I can. Therefore I install Gnome 3 along with some gnome tweaks and plugins that give me the same theme and dock as Ubuntu.

To start out, I log in as root and enable my user account:

# echo "permit persist keepenv [my_non_root_user] as root" > /etc/doas.conf

At this point, I log out and back in as my unprivileged user account and work from there using the doas command to escalate privileges when needed. I start out by updating my system:

$ doas syspatch

Now, set up power management (this is a laptop):

$ doas rcctl enable apmd
$ doas rcctl set apmd flags -A
$ doas rcctl start apmd

I also add the following line to /etc/rc.conf.local (I haven’t cracked the code on how to do this with rcctl yet):


Now I need to make sure that I have the right level of resources available to my non-privileged user for tools like nextcloudclient (which opens a TON of files during its synchronization process). To do this I typically put myself in the “staff” and “operator” groups:

$ doas usermod -G staff MY_USERNAME
$ doas usermod -G operator MY_USERNAME
$ doas usermod -L staff MY_USERNAME

I then make the following changes to the “staff” section in /etc/login.conf:


I then have to add a line to /etc/sysctl.conf to take complete the work on allowing more open files on this system:


Now that I have modified all of this stuff and patched the system, it’s a good time to reboot.

Next, I add all of the packages I can’t live without (I know it seems like a small list, but they pull in a lot of others):

$ doas pkg_add gnome gnome-tweaks gnome-extras firefox chromium libreoffice nextcloudclient keepassxc \
   aisleriot evolution evolution-ews tor-browser shotwell gimp vim colorls cups reposync

A few changes to /etc/rc.conf.local are needed to boot into Gnome3:

$ doas rcctl disable xenodm
$ doas rcctl enable multicast messagebus avahi_daemon gdm cupsd

To avoid taking a kernel panic in my use-case (I have multiple monitors connected through a Lenovo Thunderbolt/USB-C dock), I have to manually switch to the Intel DRM driver in my /etc/X11/xorg.conf by adding the following section:

Section "Device"
  Identifier "Intel Graphics"
  Driver "intel"

At this point, it’s time to reboot and go into GUI land. If you run into a situation where you have a monitor mirrored and no way to turn that feature off, I have found that turning all of the monitors off and back on generally fixes things. Once I have everything the way I would like it, I then download the yaru-remix-complete theme and install it manually by doing this:

$ cd ~
$ mkdir .themes
$ cd .themes
$ mv ~/Downloads/yaru-remix-complete-20.04.tar.xz .
$ unxz yaru-remix-complete-20.04.tar.xz
$ tar xf yaru-remix-complete-20.04.tar
$ mv themes/* .
$ rmdir themes
% doas mv icons/* /usr/local/share/icons
$ rmdir icons
$ doas mv wallpaper/* /usr/local/share/backgrounds/gnome
$ rmdir wallpaper
$ rm yaru-remix-complete-20.04.tar

Now launch gnome-tweaks and from the “Extensions” tab, turn on “user-themes”. Restart gnome-tweaks, go to the “Appearance” tab and select “Yaru-remixt” for applications, icons, and shell. On the “Top Bar” tab, enable “Battery Percentage” and “Weekday”. In the “Window Titlebars” tab, enable “Maximize” and “Minimize”.

Next, we want to put the wonderful extension Dash-To-Dock into the environment. To download it, go to and pick the right sehll version and extension version to match your install of Gnome shell. You will have to manually install it because the Gnome shell extension integration doesn’t appear to be enabled for OpenBSD:

$ cd ~/Downloads
$ unzip
$ cat metadata.json

The value for “uuid” in that file is what you want to use in the next step:

$ mkdir -p ~/.local/share/gnome-shell/extensions/
$ cd ~/.local/share/gnome-shell/extensions/
$ unzip ~/Downloads/

At this point, reboot to pick up the changes you’ve made, log in and launch gnome-tweaks again. On the “Extensions” tab, enable dash to dock. From the settings gear icon, select “extend to edge” and “show on all monitors” and you should have a very serviceable dock that is quite similar to the one in Ubuntu.

I then switch the terminal to “White on Black” for a better look and a 16-point font, and pin my favorite apps to the dock. Now for some terminal-level tweaks. I typically edit my ~/.profile file and add a couple of things:

export PS1="\[033[01;32m\]\u@\h\[\033[00m\]:\[033[01;34m\]\w[\033[00m\]$ "
export ENV=$HOME/.kshrc
export CVSROOT=/home/cvs

I then edit the ~/.kshrc file to add some aliases:

alias ls="colorls -G"
alias vi="vim"

A couple of other changes I typically make include turning off suspend when I’m plugged in (Settings | Power | Automatic Suspend), setting Firefox as my default browser (Settings | Default Applications), and setting my Time Format to “AM/PM” instead of “24-hour” (Settings | Date & Time).

I also take a moment to switch to “View -> User Interface -> Tabbed” in the Write, Calc, and Present applications in LibreOffice. This gives an interface reminiscent of the one in Microsoft Office – which I find helpful in terms of standardizing my workflow across operating systems.

After installing the appropriate browser security plugins and configuration changes from my favorite site, it’s time to set up CVS on my system for development purposes. To do this, I always double-check the AnonCVS link from the OpenBSD website left navigation panel and follow the steps to:

  1. Pre-load the source tree (for src, sys, ports and xenocara)
  2. Follow the instructions to give your non-root user write access to the src, ports and xenocara directories
  3. Mirror the repository with reposync (Note: I have had the best luck using as my mirror)

I then typically will add a crontab entry to keep things in sync:

$ doas crontab -e

0    */4    *    *    *    *    -n su -m cvs -c "reposync rsync:// /home/cvs"

After syncing up my NextCloud data and my email data, I now have what I consider to be a secure, fully-functional OpenBSD laptop, configured the way I like it.