Signing Documents With SSH Key

It turns out you can use openssh keys to sign and verify files and documents.

The sender needs to do this:

  • Create a file /tmp/whatever with the text to verify, then sign it with:
openssl dgst -sha512 -sign ~/.ssh/id_rsa /tmp/whatever > /tmp/whatever.sig
  • Then base64-encode the sig and the whatever file:
base64 /tmp/whatever >/tmp/whatever.b64
base64 /tmp/whatever.sig >/tmp/whatever.sig.b64

generate checksums: sha512sum whatever* > checksums

And send the whatever.b64, whatever.sig.b64, and checksums files.

The checksum files allow verification that the content didn’t get mangled, because it’s easy for it to get borked in transit and this technique doesn’t provide a lot of integrity validation like e.g. GPG would.

Then the verifier does:

  • Obtain the ssh key in question. I use this technique with Launchpad public keys, so this works:
curl https://launchpad.net/~whoever/+sshkeys | grep "blah@42.us.org" > /tmp/who.pub
  • Convert the key to an openssl-compatible key:
ssh-keygen -e -f /tmp/who.pub -m pkcs8 > /tmp/who.openssl.pub
  • Finally verify the documents (which you previously integrity-checked using sha512sum and the checksums file you received):
openssl dgst -sha512 -verify /tmp/who.openssl.pub -signature /tmp/whatever.sig
/tmp/whatever

A single script to demo the technique against my public Launchpad key:

#!/bin/bash
KEY_ON_DISK=~/.ssh/id_rsa
LAUNCHPAD_USER=roadmr
KEY_IDENTIFIER="name of the key"
KEY_EXCLUDER="something-you-dont-want"

mkdir ssh-attestation
pushd ssh-attestation

# Signing procedure
echo "This is me" > attest
openssl dgst -sha512 -sign $KEY_ON_DISK attest > attest.sig
base64 attest > attest.b64
base64 attest.sig > attest.sig.b64
sha512sum attest* > shasums

# Verification procedure
echo "Verifying"
sha512sum -c shasums
# Brittle - maybe select by line number instead?
curl "https://launchpad.net/~$LAUNCHPAD_USER/+sshkeys" | grep "$KEY_IDENTIFIER" | grep -v "$KEY_EXCLUDER" > lp-key.pub
ssh-keygen -e -f lp-key.pub -m pkcs8 > lp-key.openssl.pub
openssl dgst -sha512 -verify lp-key.openssl.pub -signature attest.sig attest
popd

Ubuntu 20.04 Deep Sleep

How else to get this

Suspend - the best way to get this kind of uptime

In an email thread with some colleagues, one of them mentioned that they had to force their laptop to use “deep” sleep instead of “s2idle”. This got me thinking - I’ve had the impression that my XPS13 sometimes doesn’t really suspend, and/or wakes up in the middle of the night, because the battery capacity sometimes is lower than it should be after being suspended overnight (going to sleep with 100% battery and having about 70% the morning afeter).

So I quickly looked up “s2idle” and landed here.

From here it looks like indeed s2idle is not really suspend-suspend and the system can wake up of its own accord according to unclear rules (or maybe they are clear, but I don’t care, I want the system to go to full sleep and only wake up when I open the lid or something similar). And it seems the way to get it to real deep-sleep is to write “deep” into /sys/power/mem_sleep. It further mentions that indeed s2idle is the default (which I confirmed by checking the above file), but it can be overridden by setting mem_sleep_default in the kernel command line.

It’s been a while since I last had to muck with GRUB configuration…

Just for kicks I also looked up mem_sleep_default and lo and behold, came upon a thread for the XPS 13 9310 (exactly the one I have).

Revamping Note Taking With Vimwiki

Inspired by some article on the Internet, I set out to modernize the way I take daily notes. Up until now I had simply been dumping date-named files in a single directory. This allows me to use standard tools such as grep for finding stuff in my notes, also referencing them by date when appropriate, preparing notes in advance by creating the file for a date in the future, and have comfortable aliases/commands to do things.

For example, mn (make note) creates or opens the notes file for the current day, while wmatrix creates or opens a weekly file with progress updates from the team, which I use to keep track of daily standup items and follow up if necessary.

The random article in question talks about Obsidian and how it provides nice rendered Markdown notes with collapsible GUI controls, a ton of keyboard shortcuts, a modern and friendly graphical display, and it can render link relations in the notes to display a “knowledge graph”. It does all this on top of a local folder containing .md files, so it’s quite future-proof and portable, and provides mobile apps and some synchronization (which I haven’t explored) to keep notes updated.

I tried it and it felt quite heavyweight for a glorified note taker. I found the controls distracting, the general application not providing a ton of value over a single directory of files, and most importantly, the extensive keyboard navigability requires me learning another set of keybindings which at this point I’d really rather avoid.

Obsidian in action

The same random article, however, also pointed me in the direction of vimwiki. In essence, Obsidian’s knowledge base is a local wiki, and this is exactly what vimwiki provides - indeed, in the article they are identified as complementary to each other, with vimwiki providing editability and access via the command line, and Obsidian allowing remote access and a graphical view, when needed.

I configured vimwiki to use Markdown and store files next to my old notes folder:

let g:vimwiki_list = [{'path': '~/Documents/w/vimwiki',
                      \ 'syntax': 'markdown', 'ext': '.md'}]

The experience from vim is fantastic; \ww opens a wiki index, and from there any single word can become a link by hitting ENTER in command mode on top of it. Vimwiki automatically creates the target and you can start writing Markdown in it, which is nicely syntax-colored. Vimwiki automatically handles indenting and formatting of lists, tables and markdown headings, and nicely collapses and linkifies URLs. It can even manage to-do lists in brackets [ ] and indent/dedent hierarchical table entries.

Vimwiki also has the concept of “diary” entries which are automatically created under the current date’s name, as well as useful shortcuts for “yesterday” and “tomorrow” pages, and can auto-generate an index for all diary/dated entries.

Vimwiki has wiki-wide search functionality via :VWS, though of course one can still shell out and use grep in a directory of raw .md files for ultimate portability.

I updated my mn and wmatrix aliases to integrate with vimwiki and have been happily using it for the past few days. The experience is pretty close to what I had with my old raw notes folder with some niceties added on top; I’ll probably start leveraging them more as I get more familiar with the setup.

Run Command on File Update

This is a poor man’s “watch a file for changes and when it does, run a command”.


while true; do inotifywait --event modify a-document.tex ; pdflatex a-document.tex ; done

Keychron K3 Function Keys

The Keychron K2 and K3 have dual-purpose media/function keys. The accompanying card says to use fn+x+l to change modes, but I tried it and it didn’t work. I need my function keys, I’m already used to pressing fn when I do need to access multimedia functionality.

I found this repo which explains how to set up a systemd service to configure the keys by writing a value to a driver configuration file. This works, but I was also able to get this changed immediately (though not persistently) by doing:


# Set the keys to operate in Fx mode
echo 0 | sudo tee /sys/module/hid_apple/parameters/fnmode

# Set the keys to operate in multimedia mode

echo 1 | sudo tee /sys/module/hid_apple/parameters/fnmode

new minidlna server broke my network

Categories: Uncategorized

We added a new device which can expose a connected USB drive via DLNA, internally it uses minidlna which uses SSDP for service discovery. For some strange reason that rendered my *existing* minidlna (hosted on a raspberry pi) invisible. When researching the problem, it looks like neighbor discovery (which didn’t happen before as there were no other devices) uses a multicast 239.0.0.0/8 address which my rpi was blocking due to reasons (only allows traffic via the local network and a vpn gateway). My theory is that the new minidlna device took over as “primary” and then couldn’t find other peers and so the old server wasn’t visible anymore. The solution was to allow the specific multicast address used by SSDP.

#!/bin/bash
iptables -F
#Tunnel interface
iptables -A INPUT -i tun+ -j ACCEPT
iptables -A OUTPUT -o tun+ -j ACCEPT
#Localhost and local networks
iptables -A INPUT -s 127.0.0.0/16 -j ACCEPT
iptables -A OUTPUT -d 127.0.0.0/16 -j ACCEPT
iptables -A INPUT -s 192.168.0.0/16 -j ACCEPT
iptables -A OUTPUT -d 192.168.0.0/16 -j ACCEPT
#multicast for minidlna/SSSP
iptables -I OUTPUT -d 239.255.255.250 -j ACCEPT
iptables -I INPUT -d 239.255.255.250 -j ACCEPT
#Allow VPN establishment, this is the port in the config's #remote
iptables -A OUTPUT -p udp --dport 1198 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A INPUT -p udp --sport 1198 -m state --state ESTABLISHED,RELATED -j ACCEPT
#Drop everything else
iptables -A INPUT -j DROP
iptables -A OUTPUT -j DROP

Converting only one stream in a mkv file.

Categories: English Geeky

These mkv files have h.265 hevc video which my media player can’t read, so I’d like to convert only the video stream to h.264, while leaving all other streams (2 audio tracks in aac, 2 subtitle tracks) intact.

ffmpeg -i some-x265-video.mkv -map 0 -c:v libx264 -c:a copy /tmp/x264-version.mkv

Cross-timezone date calculations using the “date”command

Categories: English Geeky

Working remotely for a timezone-distributed company poses an interesting challenge: that of having to figure out dates and times for people in different timezones. This involves not only the relatively trivial “what time is it now in A_FARAWAY_PLACE”, but “what time, in FARAWAY_PLACE_X, will it be in FARAWAY_PLACE_Z” and other fun things.

There are a handful of websites that have handy tools to do these conversions for you; but a problem I’ve found is that the web is going to the crapper, and these sites often have confusing UIs concocted by some javascript-crazed, CSS-infected webmonkey; and often they are completely swamped and rendered unusable by a rising tide of ads and other aggressive content (oh and some won’t let you do anything until you agree to them storing information in cookies in your browser – which they then bafflingly don’t use to store the PREFERENCE you have selected , so like a forgetful vampire, they ask you every single time if you want to accept their silly cookies).

I’ve known how to use the date command to show the date on a different place/timezone, which is already a huge timesaver:

$ TZ="Taiwan/Taipei" date
Fri Apr 12 19:25:31 Taiwan 2019

but – today I was trying to answer “what time in TZ=”America/Chicago” is 1 PM, on Tuesday, in “UK/London“. This is interesting because it’s conversion between two timezones which are not the one I’m in, of a date/time in the future. So I was checking date’s man page for “how to convert a specific point in time”, when I realized date can do this for you! Right in the man page there’s this example:

Show the local time for 9AM next Friday on the west coast of the US

$ date --date='TZ="America/Los_Angeles" 09:00 next Fri'

so then I combined that with the earlier one to come up with:

$ TZ="America/Chicago" date --date='TZ="UK/London" 1:00 PM next Tue'
Tue Apr 16 08:00:00 CDT 2019

This combines:

  • TZ argument to calculate dates for a specific timezone, not the current one
  • --date parameter to “display time described by STRING, not ‘now’”
  • Descriptive time specifications (1:00 PM next Tuesday – this is a pseudo-human-readable format which is not entirely intuitive – info date has the specifics)
  • TZ support inside the descriptive specification

And a list of known timezones can be obtained with timedatectl list-timezones.

Remote display of a KVM virtual machine

Categories: English Geeky

In this case I’m hosting the VM on a fast server and trying to access the display on another system (a laptop).

One way to do it is by simply SSHing with X forwarding and running KVM like so:

qemu-system-x86_64 -boot d -cdrom ubuntu-18.04.2-live-server-amd64.iso -m 8192 -enable-kvm

This by default uses a terminal window, but it’s quite slow.

Another option is to start the KVM machine in nographic mode and enable a VNC server:

qemu-system-x86_64 -nographic -vnc :5 -boot d -cdrom ubuntu-14.04.6-desktop-amd64.iso -m 8192 -enable-kvm

then on the desktop system use a vnc client to connect to the magic port:

xtightvncviewer thehost.local:5905

KVM bridged to the LAN with DHCP

Categories: English Geeky Uncategorized

The goal here is to instantiate VMs with a br0 interface grabbing an IP from the LAN DHCP, so in turn the VM can instantiate LXD containers whose IP is also exposed to the LAN. That way everything is visible on the same network segment and this makes some experimentation easier.

Host configuration

Some info taken from this URL.

The metal host is running Ubuntu 18.04, which uses netplan. Here’s the netplan.yaml file:

network:
    ethernets:
        enp7s0:
            addresses: []
            dhcp4: no
            dhcp6: no
            optional: true
    bridges:
        br0:
            dhcp4: true
            dhcp6: no
            interfaces:
                - enp7s0
            parameters:
                stp: false
                forward-delay: 0
    version: 2

With this, on boot the system grabs an address from the network’s DHCP service (from my home router) and puts it on the br0 interface (which bridges enp7s0, a Gigabit Ethernet port).

The system also has avahi-daemon installed so I can ssh the-server.local easily.

VM configuration

Next, the VM which I created using uvt-kvm:

# Get a Xenial cloud image
uvt-simplestreams-libvirt --verbose sync release=xenial arch=amd64
# Create/launch a VM
PARAMS='--memory 8192 --disk 32 --cpu 4'
uvt-kvm create the-vm  $PARAMS --bridge br0 --packages avahi-daemon,bridge-utils,haveged --run-script-once setup_network.sh

The setup_network.sh script takes care of setting up the network 🙂 This can more cleanly be done with cloud-init but I’m lazy and wanted something fast.

The script deletes the cloudconfig-created .cfg file, tells cloud-init to NOT reconfigure the network, and drops the config file I actually need in place.

#!/bin/bash

echo "Acquire::http::Proxy \"http://192.168.1.187:3128\"; " >/etc/apt/apt.conf.d/80proxy

# Drop the cloudinit-configured interface
ifdown ens3

# Reconfigure the network...
cat <<EOF >/etc/network/interfaces.d/1-bridge.cfg
auto lo br0

iface lo inet loopback

iface ens3 inet manual

iface br0 inet dhcp
    bridge_ports ens3
    bridge_stp off       # disable Spanning Tree Protocol
    bridge_waitport 0    # no delay before a port becomes available
    bridge_fd 0          # no forwarding delay
EOF

echo "network: {config: disabled}" > /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
rm /etc/network/interfaces.d/50-cloud-init.cfg

# Then bring up the new nice bridge
ifup br0

apt-get remove -y snapd && apt-get -y autoremove

The network config in /etc/network/interfaces.d/1-bridge.cfg should look like:

auto lo br0

iface lo inet loopback

iface ens3 inet manual

iface br0 inet dhcp
    bridge_ports ens3
    bridge_stp off       # disable Spanning Tree Protocol
    bridge_waitport 0    # no delay before a port becomes available
    bridge_fd 0          # no forwarding delay

LXD configuration

Finally,  install lxd. When asked to configure the lxd bridge, respond “no”, and on the next question you’ll be asked whether to supply an existing bridge. Respond “yes” and specify “br0”.

Now, when an lxd container is instantiated, it’ll by default appear on the same network (the home network!) as the VM and the main host, getting its DHCP from the home router.

When things break

Suddenly the bridge interface stopped working. I checked this to help diagnose it. But that wasn’t it. Turns out, I’d installed Docker on the main host and Docker messes with the firewall configuration by setting iptables -P FORWARD DROP. I just set it back to ACCEPT to get it working.