2024-06-29

OpenBSD Power Management

 

OpenBSD's power management features are powerful and plenty. My current setup floats the battery at 80% charged, to reduce battery wear during the work week, and it doesn't suspend when I close the lid as long as it's plugged in. On battery power, it adjusts the CPU speed under load to optimize battery life without sacrificing performance when I need it, and it will automatically suspend at 5% battery to save me from the system powering off unexpectedly. When I plan to head out, I can use a quick alias to allow the battery to charge all the way, which takes about 20 minutes from 80%. We'll go through how I have it set up.

Long-time readers of HiR will not be surprised I'm running OpenBSD as my primary general-purpose operating system on my ThinkPad X1 Carbon. Over on Instagram, people do a double-take. One of the surprises was the fact that in a NeoFetch screenshot, it was noted that my CPU was 400 MHz because I was on battery power without anything heavy running. Power management on OpenBSD also blew some minds.

Out of the box, power management is disabled on OpenBSD. It's one of the few things that do not "just work" by default. The vast majority of OpenBSD systems do not need power management features, but it's a must for laptops.

Understanding apmd

apmd is the Advanced Power Management daemon. If you just set apmd to start with no options, on a well-supported laptop like the 8th Generation Lenovo ThinkPad X1 Carbon I'm using, your laptop will probably suspend when you close the lid or use "zzz" on the command line. And it will wake up when you open the lid or mess with the keyboard. apmd will also enable automatic performance adjustment mode -- clocking down the CPU when there's not much load.  That's a pretty good start. There will likely be no warning when your battery is close to dying, and there won't be anything there to stop it from just turning off abruptly when it hits 0%. That's not optimal. Also, when you close the laptop lid and it's plugged in, you might want the system to remain active. You can do that by tweaking machdep.lidaction with sysctl, but there's a much better way.

Looking at the manual page for apmd, we have a lot of useful options.

We can start apmd in high-performance mode (-H) to get the most processing power out of the system, low-performance mode (-L) to extend battery life at the expense of CPU speed, or force automatic performance adjustment mode (-A) which happens to be the default.

The -a option (lowercase) will block incoming BIOS suspend requests, such as those coming from closing the lid, if the system is plugged in. You can still manually suspend through your window manager or the command line zzz utility.

The -z [percent] option will automatically suspend the system if it is not plugged in and the battery is at or below the threshold percentage.

Enable and configure apmd. I assume that you've configured doas. I've covered this on several pages, like my OpenBSD webserver article. I explicitly set automatic performance mode (-A), blocking suspend when plugged in (-a), and set it to automatically suspend at 5% battery (-z 5). Feel free to change this however you please.

doas rcctl enable apmd
doas rcctl set apmd flags -A -a -z 5

The apm command line utility

Simply running the apm utility will provide battery and charge status

apm
Battery state: high, 79% remaining, 153 minutes life estimate
AC adapter state: not connected
Performance adjustment mode: auto (400 MHz)


There are a number of display flags that you can pass to APM to get specific details, for instance, apm -m will display the number of minutes of estimated battery life (or time to achieve a full charge). See the man page for apm for all the details. This is useful if you are making scripts to determine power management status such as for tmux/powerline or custom status bar scripts.

You can also adjust the performance mode on-the-fly, using apm -H, apm -L or apm -A to enable high performance, low-performance or automatic performance modes respectively, without restarting apmd.

sysctl, power management and sensors

This will dump out everything from the hw.sensors tree in sysctl:
sysctl hw.sensors

From here, we can see fan speeds, temperatures, the number of battery charge cycles and even things like the battery's factory design capacity and last fully-charged capacity in Watt-hours. 



By dividing last full capacity by the design capacity, you can see how far below the rated capacity your battery has deteriorated, in a way a measure of battery health. For example, my battery's design capacity is 51 Wh, but my last full charge was 39.76 Wh. 39.76 / 51 is about 0.78, so my "full" capacity is about 78% of what it was when new. That's not bad for a 4-year-old laptop on the original battery with about 500 discharge cycles.

Extending battery health with charging optimization

Many devices are now limiting the battery charge to about 80% when the system spends a lot of time plugged in. The MacBook my employer issued to me does this using some kind of magic algorithm that determines if it hasn't been running on battery power much lately. Leaving your battery slightly discharged like this actually extends the life of the battery substantially if you use it while plugged in most of the time.

OpenBSD can do the same thing, to an extent. The maximum charge level can be set with sysctl, so you can place the following line in /etc/sysctl.conf so that it's set immediately when booting up:

hw.battery.chargestop=80

Then, manually set it with sysctl, or reboot:

doas sysctl hw.battery.chargestop=80

If your battery is fully charged, it won't do anything, but the next time you run the battery down below 80% and plug it back in, it will stop charging the battery once it hits 80%. As far as I know, the battery will still charge to 100% if you turn the system off, though.

You can set or adjust the maximum charge level from the command line as well. For instance, if you know you're going to be on the go later today and want to actually charge the battery to 100%, running this command will take care of things:

doas sysctl hw.battery.chargestop=100

There are also additional hw.battery.chargemode options and an hw.battery.chargestart variable, for advanced use cases that I haven't needed. You can reference the hw.battery section of the detailed manual page for the sysctl API to read more about these settings.

Finally, I've been using XFCE lately, and the package "xfce4-battery" does a decent job, allowing me to place a battery widget in any of the XFCE panels. There may be additional widgets or plugins for your GUI of choice.

2024-05-04

Running your own Wireguard VPN server and Travel Router

If you travel, or work from the road a lot, you probably have a good reason to set up a travel router and VPN. Travel routers let you create a private network for all of your personal devices. Paired with a VPN, you can obscure the nature of your activity from the local network, and evade IP address or geographical restrictions. 

The good use cases for “privacy” focused VPN services are vanishing. Improved encryption and protocols prevent many of the ways a casual attacker can spy on you with wifi. On top of that, many such providers have been caught selling user data to third parties and turning over information to authorities under subpoena, making them possibly worse than any attacker you’re sharing the hotel wifi with.

Running your own cloud VPN is easy and affordable. Once you know how to set it up, you can run it on most hosting providers anywhere in the world, or set it up at home so that you can virtually hop on your home network while you’re out and about. Actually installing Wireguard is the main part that’s different between operating systems. 

OpenBSD Server

It's probably no surprise that I run Wireguard on my OpenBSD Servers. OpenBSD has had full kernel support for Wireguard for years, so it's just a matter of installing the userland tools, and setting up the interface.

doas pkg_add wireguard-tools

/etc/hostname.wg0:

inet 10.0.0.1 255.255.255.0 NONE
up
!/usr/local/bin/wg setconf wg0 /etc/wireguard/wg0.conf

Amazon Linux

Amazon Linux is just one easy example I found of a Red Hat-based system. These steps should work similarly on others like Rocky or Alma. 

sudo wget -O /etc/yum.repos.d/wireguard.repo https://copr.fedorainfracloud.org/coprs/jdoss/wireguard/repo/epel-7/jdoss-wireguard-epel-7.repo

sudo yum upgrade

sudo yum clean all

sudo yum install wireguard-tools wireguard-dkms iptables-services


Debian Linux


As the root of many other distributions like Ubuntu and RaspiOS, it made sense to also cover Debian since these instructions will also likely work on many distributions.

sudo apt update

sudo apt install wireguard

 

Generating Public and Private Keys

Most of the travel routers I've seen don't have a way to generate Wireguard keys on the device if you're manually configuring it. These can be generated on your VPN server and imported. We're changing the umask here to ensure the files are not world or group readable. We're going to be editing files as root, so just use sudo -i (linux) or doas -s  (OpenBSD)

sudo -i

umask 077 

Create the client keys:

wg genkey | tee client-private.key | wg pubkey > client-public.key

And then server keys:
cd /etc/wireguard
wg genkey | tee private.key | wg pubkey > public.key

Figure out your main network interface:

ip a

In Amazon AWS EC2, the interface was enX0 but it may very well be eth0 or something ridiculous like enp37s8lmaowtf depending on your configuration. You'll need this interface name for your iptables rules.

Using this example skeleton configuration file as a template, paste it into /etc/wireguard/wg0.conf and edit the interface name and fill in the appropriate public and private keys.  You can pick any port number you wish. There is no standardized port for Wireguard.

/etc/wireguard/wg0.conf

[Interface]
PrivateKey = [the contents of /etc/wireguard/private.key]
ListenPort = 57609
Address = 10.0.0.1/24
PostUp = iptables -t nat -I POSTROUTING -o [Interface] -j MASQUERADE
PostUp = ip6tables -t nat -I POSTROUTING -o [Interface] -j MASQUERADE
PreDown = iptables -t nat -D POSTROUTING -o [Interface] -j MASQUERADE
PreDown = ip6tables -t nat -D POSTROUTING -o [Interface] -j MASQUERADE

[Peer]
PublicKey = [the contents of client-public.key]
AllowedIPs = 10.0.0.2/32

Final Setup and starting the server

OpenBSD

For OpenBSD, you won't need the Address or IPTables entries in wg0.conf above. You'll need to tell PF to NAT traffic for wg0, though. Again, you'll need the primary interface name, which you can find with ifconfig. Place the following lines into /etc/pf.conf AFTER the "pass" and before the block commands at the end of the file and restart pf.

pass in on wg0
pass in inet proto udp from any to any port 51820
pass out on egress inet from (wg0:network) nat-to ([Interface]:0)

doas pfctl -f /etc/pf.conf

Enable IP Forwarding by adding these lines to /etc/sysctl.conf:

net.inet.ip.forwarding=1
net.inet6.ip6.forwarding=1 

To start Wireguard, run the following commands, or reboot:

doas sysctl net.inet.ip.forwarding=1

doas net.inet6.ip6.forwarding=1

doas sh /etc/netstart wg0

Linux

For Amazon Linux or Debian, it's also similar. Add these to /etc/sysctl.conf:

net.ipv4.ip_forward=1

net.ipv6.conf.all.forwarding=1 

Reload sysctl:

sudo sysctl -p

Enable and start the Wireguard service with systemctl

sudo systemctl enable wg-quick@wg0.service

sudo systemctl start wg-quick@wg0.service

Travel Router Configuration

I've been using GL.iNet routers with Wireguard for about 3 years. The example screenshots are from my GL-SFT1200 "Opal" travel router. Manually configure the Wireguard client and set these values:

Interface

IP Address: 10.0.0.2 (or your "peer" address from the Wireguard server config)

Private key: Contents of client-private.key file we generated earlier

Peer

Public Key: Contents of /etc/wireguard/public.key from the wireguard server

Endpont host: IP address and port of your wireguard server (e.g. 3.45.67.89:57609)

Allowed IPs: 0.0.0.0/0 (or, all IP addresses are allowed through the Wireguard server)

 

 


Once you have configured the Wireguard client, you can connect to the VPN. Browse to an IP address checking site like whatismyip.com to verify you're coming from the VPN server's IP address.

Many travel routers have a mode switch on the side that allows you to easily change how the router works. I set up my Opal router so that the mode switch enables or disables Wireguard on the fly so I have more flexibility without worrying about having to log into the admin control panel and change settings.