December 06, 2017

November 22, 2017

Areca update: arcmsr(4)DragonFly BSD Digest

The Areca driver, arcmsr(4), has been updated to version  This comes right from the company, too, which is very nice of them.

Pre-DragonFly 4.4 users, take noteDragonFly BSD Digest

If you happen to be running an old version of DragonFly, you may need to do an intermediate upgrade to move to releases after 5.0.  This is in part because of commits to support C++14.  This only applies to version of DragonFly before 4.4.

November 21, 2017

Routing traffic with multiple OpenVPNVigdis (danj@)

On my home router, I have multiple instances of OpenVPN, and here I describe how I route my traffic

Locking microtestsDragonFly BSD Digest

There’s several ‘lockmgr’ test programs in DragonFly that can be used to test locking performance.  Matthew Dillon used them recently to test some locking optimizations.

Removed, vomit-0.2Removed OpenBSD packages
voice over misconfigured internet telephones
New package, hs-temporary- packages
portable temporary file and directory support
New package, fierce-1.2.0OpenBSD packages
DNS reconnaissance tool for locating non-contiguous IP space
New package, hs-colour-2.3.3OpenBSD packages
model for human colour/color perception
New package, hs-stringsearch- packages
fast ByteString search and replace functions
New package, hs-wl-pprint-text- packages
Wadler/Leijen pretty printer for text values

November 20, 2017

BSD News 20/11/2017Discovering the Way of the BSD

Last week in BSD

News: BSDSec, FreeBSD, OpenZFS, BSDnow, OpenBSD, DragonFly BSD, C5, p2k17
Releases: HardenedBSD


Stable release: HardenedBSD-stable 11-STABLE v1100054

Tthis is a security update and reinstallation of pkgs/ports are required due LibreSSL upgrade!
- Changed AT_PAXFLAG auxvector position (4c04e4a613679510cd16bb13d7974c18e3f54460)
- Properly bzero kldstat structure to prevent kernel information leak. (3ff3ec467d4eb11cdbf706cf386935d5e58c2e91) [FreeBSD-SA-17:10.kldstat, CVE-2017-1088]
- CloudABI 0.17 (cf6ac9b4efa43a9c64c5ab311666080a0e8632b1)
- MFH (r325010): don't bother verifying a password that we know is too long. (b242fe393914310e50673eb62d480ce03706d745) [CVE-2016-6210]


FreeBSD Security Advisory FreeBSD-SA-17:10.kldstat
FreeBSD Security Advisory FreeBSD-SA-17:09.shm
FreeBSD Security Advisory FreeBSD-SA-17:08.ptrace


Opening ZFS in 2017 | BSD Now 220

We have a first PS4 kernel exploit, the long awaited OpenZFS devsummit report by Allan, DragonflyBSD 5.0 is out, we show you vmadm to manage jails, parallel processing with Unix tools & more!

Official OpenBSD 6.2 CD set - the only one to be made!

Our dear friend Bob Beck (beck@) writes:
So, again this release the tradition of making Theo do art has continued!
Up for sale by auction to the highest bidder on Ebay is the only OpenBSD 6.2 CD set to be produced.
The case and CD's feature the 6.2 artwork, custom drawn ans signed by Theo.
All proceeds to support OpenBSD
Go have a look at the auction
As with previous OpenBSD auctions, if you are not the successful bidder, we would like to encourage you to donate the equivalent of you highest bid to the project.

Code stuff

rdist out of DragonFly
DragonFly, IPv6, and direct routes
In Other BSDs for 2017/11/18

Interesting Articles

FreeBSD/EC2 on C5 instances
Never too much RAM for DragonFly

p2k17 Hackathon

OpenBSD is holding hackathons as an attempt to get new changes into the source tree quickly. Here are some reports from the latest: 
Florian Obser on network stack progress, kernel relinking and more
Landry Breuil on Mozilla things and much more

Wallpaper of the week


p2k17 Hackathon report: Antoine Jacoutot on ports+packages progressUndeadly
Another p2k17 hackathon report is just in, from Antoine Jacoutot (ajacoutot@), who writes:

Nothing fancy, just ports work, but my first time in Berlin was a blast!

It's been a while since I attended a ports hackathon where I ended up exclusively working on ports, and P2K17 was one of them.

Read more…

Forcing the password gropers through a smaller hole with OpenBSD's PF queuesPeter Hansteen
While preparing material for the upcoming BSDCan PF and networking tutorial, I realized that the pop3 gropers were actually not much fun to watch anymore. So I used the traffic shaping features of my OpenBSD firewall to let the miscreants inflict some pain on themselves. Watching logs became fun again.

Yes, in between a number of other things I am currently in the process of creating material for new and hopefully better PF and networking session.

I've been fishing for suggestions for topics to include in the tutorials on relevant mailing lists, and one suggestion that keeps coming up (even though it's actually covered in the existling slides as well as The Book of PF) is using traffic shaping features to punish undesirable activity, such as

What Dan had in mind here may very well end up in the new slides, but in the meantime I will show you how to punish abusers of essentially any service with the tools at hand in your OpenBSD firewall.

Regular readers will know that I'm responsible for maintaining a set of mail services including a pop3 service, and that our site sees pretty much round-the-clock attempts at logging on to that service with user names that come mainly from the local part of the spamtrap addresses that are part of the system to produce our hourly list of greytrapped IP addresses.

But do not let yourself be distracted by this bizarre collection of items that I've maintained and described in earlier columns. The actual useful parts of this article follow - take this as a walkthrough of how to mitigate a wide range of threats and annoyances.

First, analyze the behavior that you want to defend against. In our case that's fairly obvious: We have a service that's getting a volume of unwanted traffic, and looking at our logs the attempts come fairly quickly with a number of repeated attempts from each source address. This similar enough to both the traditional ssh bruteforce attacks and for that matter to Dan's website scenario that we can reuse some of the same techniques in all of the configurations.

I've written about the rapid-fire ssh bruteforce attacks and their mitigation before (and of course it's in The Book of PF) as well as the slower kind where those techniques actually come up short. The traditional approach to ssh bruteforcers has been to simply block their traffic, and the state-tracking features of PF let you set up overload criteria that add the source addresses to the table that holds the addresses you want to block.

I have rules much like the ones in the example in place where there I have a SSH service running, and those bruteforce tables are never totally empty.

For the system that runs our pop3 service, we also have a PF ruleset in place with queues for traffic shaping. For some odd reason that ruleset is fairly close to the HFSC traffic shaper example in The Book of PF, and it contains a queue that I set up mainly as an experiment to annoy spammers (as in, the ones that are already for one reason or the other blacklisted by our spamd).

The queue is defined like this:

   queue spamd parent rootq bandwidth 1K min 0K max 1K qlimit 300

yes, that's right. A queue with a maximum throughput of 1 kilobit per second. I have been warned that this is small enough that the code may be unable to strictly enforce that limit due to the timer resolution in the HFSC code. But that didn't keep me from trying.

And now that I had another group of hosts that I wanted to just be a little evil to, why not let the password gropers and the spammers share the same small patch of bandwidth?

Now a few small additions to the ruleset are needed for the good to put the evil to the task. We start with a table to hold the addresses we want to mess with. Actually, I'll add two, for reasons that will become clear later:

table <longterm> persist counters
table <popflooders> persist counters 

The rules that use those tables are:

block drop log (all) quick from <longterm> 

pass in quick log (all) on egress proto tcp from <popflooders> to port pop3 flags S/SA keep state \ 
(max-src-conn 1, max-src-conn-rate 1/1, overload <longterm> flush global, pflow) set queue spamd 

pass in log (all) on egress proto tcp to port pop3 flags S/SA keep state \ 
(max-src-conn 5, max-src-conn-rate 6/3, overload <popflooders> flush global, pflow) 
The last one lets anybody connect to the pop3 service, but any one source address can have only open five simultaneous connections and at a rate of six over three seconds.

Any source that trips up one of these restrictions is overloaded into the popflooders table, the flush global part means any existing connections that source has are terminated, and when they get to try again, they will instead match the quick rule that assigns the new traffic to the 1 kilobyte queue.

The quick rule here has even stricter limits on the number of allowed simultaneous connections, and this time any breach will lead to membership of the longterm table and the block drop treatment.

The for the longterm table I already had in place a four week expiry (see man pfctl for detail on how to do that), and I haven't gotten around to deciding what, if any, expiry I will set up for the popflooders.

The results were immediately visible. Monitoring the queues using pfctl -vvsq shows the tiny queue works as expected:

 queue spamd parent rootq bandwidth 1K, max 1K qlimit 300
  [ pkts:     196136  bytes:   12157940  dropped pkts: 398350 bytes: 24692564 ]
  [ qlength: 300/300 ]
  [ measured:     2.0 packets/s, 999.13 b/s ]

and looking at the pop3 daemon's log entries, a typical encounter looks like this:

Apr 19 22:39:33 skapet spop3d[44875]: connect from
Apr 19 22:39:33 skapet spop3d[75112]: connect from
Apr 19 22:39:34 skapet spop3d[57116]: connect from
Apr 19 22:39:34 skapet spop3d[65982]: connect from
Apr 19 22:39:34 skapet spop3d[58964]: connect from
Apr 19 22:40:34 skapet spop3d[12410]: autologout time elapsed -
Apr 19 22:40:34 skapet spop3d[63573]: autologout time elapsed -
Apr 19 22:40:34 skapet spop3d[76113]: autologout time elapsed -
Apr 19 22:40:34 skapet spop3d[23524]: autologout time elapsed -
Apr 19 22:40:34 skapet spop3d[16916]: autologout time elapsed -

here the miscreant comes in way too fast and only manages to get five connections going before they're shunted to the tiny queue to fight it out with known spammers for a share of bandwidth.

I've been running with this particular setup since Monday evening around 20:00 CEST, and by late Wednesday evening the number of entries in the popflooders table had reached approximately 300.

I will decide on an expiry policy at some point, I promise. In fact, I welcome your input on what the expiry period should be.

One important takeaway from this, and possibly the most important point of this article, is that it does not take a lot of imagination to retool this setup to watch for and protect against undesirable activity directed at essentially any network service.

You pick the service and the ports it uses, then figure out what are the parameters that determine what is acceptable behavior. Once you have those parameters defined, you can choose to assign to a minimal queue like in this example, block outright, redirect to something unpleasant or even pass with a low probability.

All of those possibilities are part of the normal pf.conf toolset on your OpenBSD system. If you want, you can supplement these mechanisms with a bit of log file parsing that produces output suitable for feeding to pfctl to add to the table of miscreants. The only limits are, as always, the limits of your imagination (and possibly your programming abilities). If you're wondering why I like OpenBSD so much, you can find at least a partial answer in my OpenBSD and you presentation.

FreeBSD users will be pleased to know that something similar is possible on their systems too, only substituting the legacy ALTQ traffic shaping with its somewhat arcane syntax for the modern queues rules in this article.

Will you be attending our PF and networking session in Ottawa, or will you want to attend one elsewhere later? Please let us know at the email address in the tutorial description.

Update 2017-04-23: A truly unexpiring table, and downloadable datasets made available

Soon after publishing this article I realized that what I had written could easily be taken as a promise to keep a collection of POP3 gropers' IP addresses around indefinitely, in a table where the entries never expire.

Table entries do not expire unless you use a pfctl(8) command like the ones mentioned in the book and other resources I referenced earlier in the article, but on the other hand table entries will not survive a reboot either unless you arrange to have table contents stored to somewhere more permanent and restored from there. Fortunately our favorite toolset has a feature that implements at least the restoring part.

Changing the table definition quoted earler to read

 table <popflooders> persist counters file "/var/tmp/popflooders"

takes part of the restoring, and the backing up is a matter of setting up a cron(8) job to dump current contents of the table to the file that will be loaded into the table at ruleset load.

Then today I made another tiny change and made the data available for download. The popflooders table is dumped at five past every full hour to pop3gropers.txt, a file desiged to be read by anything that takes a list of IP addresses and ignores lines starting with the # comment character. I am sure you can think of suitable applications.

In addition, the same script does a verbose dump, including table statistiscs for each entry, to pop3gropers_full.txt for readers who are interested in such things as when an entry was created and how much traffic those hosts produced, keeping in mind that those hosts are not actually blocked here, only subjected to a tiny bandwidth.

As it says in the comment at the top of both files, you may use the data as you please for your own purposes, for any re-publishing or integration into other data sets please contact me via the means listed in the whois record.

As usual I will answer any reasonable requests for further data such as log files, but do not expect prompt service and keep in mind that I am usually in the Central European time zone (CEST at the moment).

I suppose we should see this as a tiny, incremental evolution of the "Cybercrime Robot Torture As A Service" (CRTAAS) concept.

Update 2017-04-29: While the world was not looking, I supplemented the IP address dumps with versions including one with geoiplocation data added and a per country summary based on the geoiplocation data.

Spending a few minutes with an IP address dump like the one described here and whois data is a useful excersise for anyone investigating incidents of this type. This .csv file is based on the 2017-04-29T1105 dump (preserved for reference), and reveals that not only is the majority of attempts from one country but also a very limited number of organizations within that country are responsible for the most active networks.

The spammer blacklist (see this post for background) was of course ripe for the same treatment, so now in addition to the familiar blacklist, that too comes with a geoiplocation annotated version and a per country summary.

Note that all of those files except the .csv file with whois data are products of automatic processes. Please contact me (the email address in the files works) if you have any questions or concerns.

Update 2017-05-17: After running with the autofilling tables for approximately a month, and I must confess, extracting bad login attempts that didn't actually trigger the overload at semi-random but roughly daily intervals, I thought I'd check a few things about the catch. I already knew roughly how many hosts total, but how many were contactin us via IPv6? Let's see:

[Wed May 17 19:38:02] peter@skapet:~$ doas pfctl -t popflooders -T show | wc -l
[Wed May 17 19:38:42] peter@skapet:~$ doas pfctl -t popflooders -T show | grep -c \: | wc -l

Meaning, that of a total 5239 miscreants trapped, only 77, or just short of 1.5 per cent tried contacting us via IPv6. The cybercriminals, or at least the literal bottom feeders like the pop3 password gropers, are still behind the times in a number of ways.

Update 2017-06-13: BSDCan 2017 past, and the PF and networking tutorial with OpenBSD session had 19 people signed up for it. We made the slides available on the net here during the presentation and announced them on Twitter and elsewhere just after the session concluded. The revised tutorial was fairly well received, and it is likely that we will be offering roughly equivalent but not identical sessions at future BSD events or other occasions as demand dictates.

Update 2017-07-05: Updated the overload criteria for the longterm table to what I've had running for a while: max-src-conn 1, max-src-conn-rate 1/1.

Update 2017-10-07: I've decided to publish a bit more of the SSH bruteforcer data. The origin is from two gateways in my care, both with these entries in their pf.conf:

table persist counters file "/var/tmp/bruteforce"
block drop log (all) quick from

supplemented with cron jobs that dump the current data to the file so the data survives a reboot. After years of advocating 24-hour expiry on blacklists, I recently changed my mind so, both hosts now run with 28-day expiry. For further severity on my part, the hosts also exchange updates to their bruteforce tables via cron jobs that dump table contents to file, fetch the partner's data and load into their own local table. In addition, a manual process extracts (at quasi-random but approximately daily intervals) addresses of failures that do not reach the limits and add those to the tables as well.

The data comes in three varieties: a raw address list, (with a #-prepended comment at the start) suitable for importing into such things as a PF table you block traffic from, the address list with the country code for each entry appended, and finally a summary of list entries per country code. All varieties are generated twice per hour. 
The difference of loopback packets on Linux and OpenBSDNan Xiao
Capture the packets on loopback network card on Linux: # tcpdump -i lo -w lo.pcap port 33333 tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes ...... Download it onto Windows and use wireshark to analyze it: We can see every packet conforms to standard ethernet format. Capture lookback packets on OpenBSD: # […]

November 19, 2017

Anyone with ARM laptop experience?Lobsters

The cloudflare qualcomm vs intel comparison caught my eye and renewed my interest in ARM outside of mobile. I’m thinking of getting an ARM laptop, but I’d love to hear any experiences and insights y’all have, especially with running Linux or BSD on ARM.

Lazy Reading for 2017/11/19DragonFly BSD Digest

No theme this week.

November 18, 2017

Official OpenBSD 6.2 CD set - the only one to be made!Undeadly
Our dear friend Bob Beck (beck@) writes:

So, again this release the tradition of making Theo do art has continued!

Up for sale by auction to the highest bidder on Ebay is the only OpenBSD 6.2 CD set to be produced.

The case and CD's feature the 6.2 artwork, custom drawn ans signed by Theo.

All proceeds to support OpenBSD

Go have a look at the auction

As with previous OpenBSD auctions, if you are not the successful bidder, we would like to encourage you to donate the equivalent of you highest bid to the project.
OtherLife reviewTed Unangst (tedu@)

Memories are nothing but chemicals. At that level, there’s no difference between a real and a fake memory, so if we have the appropriate nanotech, we can write some code to create memories, virtual experiences indistinguishable from those taking place in reality. Such is the concept of OtherLife. The movie’s titular company plans to sell vacation memories, allowing users to experience a day’s worth of snowboarding in less than a minute of real time. Why not take a vacation every day before going to work, arriving fresh and relaxed?

So far we’re in Total Recall territory, but the twist here isn’t secret double agents on Mars. Just a tech startup that needs funding. One founder, our heroine, created the company to further develop the tech and perhaps revive her brother from a coma. The other founder, in order to secure some necessary bridge funding days before launch, is in some shady talks with the prison bureau to develop a line of virtual imprisonment. Very near term, contemporary science fiction. It’s actually set in 2017. Just some stealth mode startup you haven’t heard about yet.

From here we explore the nature and ethics of memories. Is subjecting someone to a year of virtual solitary confinement that elapses in one real minute more or less ethical than taking away a year of their actual life? How do we know what’s real? Shades of Inception here, but with much less boom boom gusto. Very Black Mirror, with a bit of a twist, but a touch less pessimism. We’ve seen this concept before, but this might be the most grounded, without trying too hard to impress.

November 17, 2017

Switching from 1Password to BitwardenJoshua Stein (jcs@)

I've been using an OpenBSD laptop as my workstation a lot more lately, probably because most of my hardware just works now and I don't have to think too much about it. The touchpad works when I touch it, I can be confident that when I close the lid, the laptop will fully suspend and then fully resume again when I open it, WiFi works all throughout my house (although it's not terribly fast), and my web browser is fast and stable. What amazing times we live in.

In the past, one thing that frequently kept me going back to my Mac, aside from iOS and Android development, was 1Password. I have a ton of logins for websites and servers, and because my browsers are all configured to clear cookies for most websites after I close their tabs, I need frequent access to passwords synced across my laptops and phones, and 1Password has great apps for all of those except OpenBSD.

All of 1Password's syncing currently works through my Dropbox account. My Mac has 1Password configured to store its encrypted database in my local Dropbox directory, Dropbox does its automatic syncing of that directory to their servers, and 1Password on my phone uses Dropbox's API to pick up any changed files. It's been reliable for years, I have local versioned backups of my database, it works when my devices are offline, and I know I can access that data years in the future.

Using 1Password on OpenBSD

To bring OpenBSD into the mix, there are 3rd-party command-line apps which can read local 1Password files such as 1pass written in Go. Getting the 1Password files onto OpenBSD was left as an exercise to the reader, sending me down the rabbit hole of trying to add OpenBSD support to various FUSE-based packages that could provide a local filesystem view of my Dropbox directory. I managed to hack one of them into semi-working shape, but eventually I gave up and used rclone to do one-way fetching of my Dropbox directory on demand.

While this allowed me to at least view and copy passwords, the process was less than ideal. Browsing in Firefox, I'd have to open a terminal, type 1pass copy <some website>, choose the right one, and then go back to Firefox and paste it in the proper field. Tedious, error-prone, vulnerable to phishing, and now my password is hanging out in clear-text on the clipboard.

A Firefox add-on called Passcards from the developer of 1pass seemed encouraging, as it did Dropbox syncing on its own and supported auto-filling passwords in the browser, but I could never get it to work. The hard-coded Dropbox API token in the add-on doesn't work and the mess of Node dependencies to build a local version failed miserably on OpenBSD.

1Password Lock-In

Meanwhile, AgileBits, the 80-person company developing 1Password, has been pushing their new hosted, subscription-based model for 1Password going forward. Instead of users being in control of their data files, 1Password will store them on AgileBits' servers and users pay a monthly subscription fee for the privilege, forever.

I'm an app developer, I get it. A big company can't sustain development of a product that users only pay for once. However, I've paid for 1Password and all of its major version upgrades, and the $10 or whatever it was to unlock the "pro" features of the iOS app. I'm not opposed to paying money for apps, or for upgrades, or even for a subscription, but I don't want to pay to host my passwords on AgileBits' servers. Security concerns aside, there is an issue of lock-in and now having to make my OpenBSD hacks work with AgileBits' new API (is there even one?) instead of just accessing and backing up files from Dropbox.

Since I wasn't sure how long 1Password would keep working with its non-subscription-based syncing and I was still missing first-class OpenBSD support, I started looked into migrating to something else.

Finding Bitwarden

The main competitor to 1Password is LastPass, which looks nice and works well as a standalone Firefox add-on on OpenBSD, but it has the same lock-in problem and server-side security concerns as 1Password.

KeePass is a popular open-source alternative but its use case seems focused on a single machine. I don't need a stand-alone GUI and I do need browser extensions and mobile apps that can all sync reliably. And honestly, looking at their plugins page left me with a bit of decision fatigue: which ones are good, which ones are secure, which ones are still maintained? Does the browser extension have to read files from my home directory or talk to a daemon that my unprivileged Firefox won't be able to do?

There are various command-line concoctions such as pass which stores PGP-encrypted files in a Git repo, but that doesn't improve my situation over 1Password. I would still have to manually look up passwords and copy them to the clipboard. These command-line packages also lack mobile apps and syncing.

Eventually I stumbled upon Bitwarden which is similar to LastPass but is entirely open-source and its primary developer is funded by users paying for subscriptions to store their data on Bitwarden's servers. However, all of their browser extensions and phone apps support setting a custom API URL before logging in, to allow for private installations. The iOS app and Firefox extension that I tried out looked fairly well polished, but I was more concerned with it being an open platform so I could fix bugs, add features, and host my own data.

Unfortunately, the open-source backend for these apps is written in .NET and expects to talk to a Microsoft SQL Server, requiring a big Docker image to deploy a private installation on Linux.

Since I was expecting to run my own API server on OpenBSD without all of that overhead, I decided to write my own compatible server. Sadly, there is no documentation on Bitwarden's API (outside of its .NET code) so I was not even able to figure out what my server would need to provide.


Rather than wade through lots of .NET code, I decided to go for a black-box approach. I wrote a simple proxy in Sinatra that I could point the Bitwarden Firefox add-on to as its private API URL. The proxy would intercept each request, print it out to the console, then send it to Bitwarden's actual API, print out the response, and send it back to the Firefox add-on.

With that tool I was able to document all of the API calls that the Firefox extension and iOS app made and the response each was expecting. The encryption key derivation scheme used to actually encrypt and decrypt all of the data took a bit longer to figure out, but because everything is open source, I was able to read the Javascript code of the Firefox add-on to understand what it was doing to encrypt a string with a given password.

With my documentation in-hand, I wrote a new Sinatra server that implements all of the API calls needed by the Firefox extension and iOS app. I deployed it to a server with Unicorn behind nginx, and used Let's Encrypt to get a TLS certificate for it.

My API server is now small and easy to understand, it has a much smaller attack surface than the .NET version, and all of my data is stored in a SQLite database that I can backup and version with cp. No lock-in, a first-class experience on OpenBSD and Firefox, and I feel better understanding the details of how my data is encrypted.

Migrating from 1Password

The Bitwarden web client (not the Firefox add-on) supports directly importing 1Password data files for users subcribed to Bitwarden's hosted service. Since I'm not using Bitwarden's web client, I wrote a command-line 1Password conversion tool that can read a 1Password Interchange Format file, encrypt the passwords using Bitwarden's format, and insert them into the database that bitwarden-ruby uses.

After importing more than 700 logins from my 1Password file, I noticed that the Bitwarden Firefox add-on was quite sluggish on OpenBSD. Unlocking it with my master password would take four or five seconds to parse everything before showing the large list of logins.

Since this data was years of migrated 1Password installations and other password stores, I decided to spend a few hours cleaning it up. After deleting some 300 logins and moving others into various folders, the add-on seems a bit snappier though still leaves something to be desired. I'd also like to change its keyboard shortcut to Alt+\ like Cmd+\ is for 1Password, but Firefox's new WebExtension system doesn't support changing these hard-coded keyboard shortcuts yet like Chrome does.

At this point I've been using Bitwarden's iOS app and Firefox extension exclusively. I have some minor things left to implement in my server My server now has TOTP support, and everything seems to be working well.

Fetch the code from GitHub if you want to check it out.