Changing FAT12 Label Under NAS4Free

Illustration

Part of my encrypted NAS setup is also key storage on TmpUsb. Idea of such USB is to self-erase its data (and thus my encryption keys) when power is lost. It’s not a full-proof system but enough to ensure any thief will get away with hardware only.

TmpUsb device behaves just as a normal USB drive with a twist that label changes are triggering various special behaviors. If one is setting up such disk remotely, the most significant operation is done by changing label to “ARMED” to actually make it erasable upon power loss.

And therein lies the problem - NAS4Free contains no command that can change the label for FAT12 disk. Well, I guess we’ll have to make such command ourselves.

First a bit about FAT12 disk layout. Looking at the raw drive, the first 512-byte sector is occupied by master boot record. There we have layout of our partitions and their types. The only byte I am interested in is the partition type for the first partition located at the 451th position and holding a value 0x01 and denoting FAT12. This byte is important as to avoid accidentally overwriting some other disk upon label change.

The next two sectors belong to the FAT12 boot sector and are filled by a lot of important information. However, the label we’re searching for cannot be found albeit there is a very similar volume label field. Here we can also see, starting at byte 54, a type of file system. This time it’s written as FAT12 and is ideal as another double-check.

Nope, the paydirt is in fourth sector, the first 11 bytes are ones holding the disk label we need. You can see the whole sector using dd and hexdump:

dd if=/dev/da0 bs=512 skip=3 count=1 2>/dev/null | hexdump -Cv

The rest of sector (and a few afterward) are filled with directory entries using both short 8.3 MS-DOS name and long Unicode one. Quite interesting topic actually, but we need only to care about the first 11 bytes here.

The final script I ended up with actually uses dd internally and allows for setting custom label under NAS4Free’s limited FreeBSD environment. As it’s using bog standard bash, I expect it’s perfectly portable to other platforms too and modifying to do other manipulations of FAT structures should be easy enough.

And, as always, it’s available for download.

[2018-07-22: NAS4Free has been renamed to XigmaNAS as of July 2018]

Slow Ansible Response Using External IP

As I was playing with Ansible’s ad-hoc commands, I’ve noticed that one host was struggling. Any command I tried took 15 seconds more for it than for any other host.

ansible all -a date

Adding IP to /etc/hosts actually solved that trouble which strongly indicated toward issue actually being connected to DNS setup. But, why the hell was my temporary lab even using DNS? It was just a bunch of virtual machines and I definitely didn’t want to add all hosts by name. I needed another solution…

After a bit of investigation, I discovered the actual culprit - SSH daemon was trying to resolve IP address. For which purpose you ask? Just to write host name to its log.

The final solution was easy - just adding UseDNS no to /etc/ssh/sshd_config:

echo -e "\nUseDNS no" | sudo tee -a /etc/ssh/sshd_config'
sudo systemctl restart sshd

VPN-only Internet Access on Linux Mint 18.3 Via Private Internet Access

Setting up Private Internet Access VPN is usually not a problem these days as Linux version is readily available among the supported clients. However, such installation requires GUI. What if we don’t want or need one?

For setup to work independently of GUI, one approach is to use OpenVPN client usually installed by default. Also needed are PIA’s IP-based OpenVPN configuration files. While this might cause issues down the road if that IP changes, it does help a lot with security as we won’t need to poke an unencrypted hole (and thus leak information) for DNS.

From the downloaded archive extract .crt and .pem files followed by your choice of .ovpn file (usually going with the one physically closest to you). Copy them all to your desktop to be used later. Yes, you can use any other directory - this is just the one I’ll use in example commands below.

Rest of the VPN configuration needs to be done from the Bash (replacing username and password with actual values):

sudo mv ~/Desktop/*.crt /etc/openvpn/
sudo mv ~/Desktop/*.pem /etc/openvpn/
sudo mv ~/Desktop/*.ovpn /etc/openvpn/client.conf

sudo sed -i "s*ca *ca /etc/openvpn/*" /etc/openvpn/client.conf
sudo sed -i "s*crl-verify *crl-verify /etc/openvpn/*" /etc/openvpn/client.conf

sudo echo "auth-user-pass /etc/openvpn/client.login" >> /etc/openvpn/client.conf
sudo echo "mssfix 1400" >> /etc/openvpn/client.conf
sudo echo "dhcp-option DNS 209.222.18.218" >> /etc/openvpn/client.conf
sudo echo "dhcp-option DNS 209.222.18.222" >> /etc/openvpn/client.conf
sudo echo "script-security 2" >> /etc/openvpn/client.conf
sudo echo "up /etc/openvpn/update-resolv-conf" >> /etc/openvpn/client.conf
sudo echo "down /etc/openvpn/update-resolv-conf" >> /etc/openvpn/client.conf

unset HISTFILE
echo '^^username^^' | sudo tee -a /etc/openvpn/client.login
echo '^^password^^' | sudo tee -a /etc/openvpn/client.login
sudo chmod 500 /etc/openvpn/client.login

To test VPN connection execute:

sudo openvpn --config /etc/openvpn/client.conf

Assuming test was successful (i.e. resulted in Initialization Sequence Completed message), we can further make sure data is actually traversing VPN. I’ve found whatismyipaddress.com quite helpful here. Just check if IP detected is different then IP you usually get without VPN.

Stop the test connection using Ctrl+C and proceed to configure OpenVPN’s auto-startup:

echo "AUTOSTART=all" | sudo tee -a /etc/default/openvpn
sudo reboot

Once computer has booted and no further VPN issues have been observed, you can also look into disabling the default interface when VPN is not active. Essentially this means traffic is either going through VPN or not going at all.

Firewall rules are to allow data flow only via VPN’s tun0 interface with only encrypted VPN traffic being allowed on port 1198.

sudo ufw reset
sudo ufw default deny incoming
sudo ufw default deny outgoing
sudo ufw allow out on tun0
sudo ufw allow out on `route | grep '^default' | grep -v "tun0$" | grep -o '[^ ]*$'` proto udp to `cat /etc/openvpn/client.conf | grep "^remote " | grep -o ' [^ ]* '` port 1198
sudo ufw enable

This should give you quite secure setup without the need for GUI.

Monitoring Certificate Expiration

Once you get Let’s Encrypt certificate setup, there are two more things needed. First one is setting up renewal as our certificates don’t last more than 90 days. The second one is often overlooked - actually monitoring how long before certificate expires. If anything prevents your certificate renewing, you definitely want to know it.

My approach to this problem is introducing an extra step in my daily e-mail report (I will assume here you have one setup already). This bash code will connect to a server, enumerate all certificates within /etc/letsencrypt/ directory, extract their name, and give an extra warning if certificate is expiring in less than 15 days.

Without the further ado, here is the code excerpt:

NOW=`date +%s`

PEMS=`ssh ^^myuser^^@^^myserver.example.com^^ find /etc/letsencrypt/ -name "cert.pem" -print`
for PEM in $PEMS
do
  NAME=`echo $PEM | rev | cut -d'/' -f2 | rev`
  EXPIRY_RAW=`ssh ^^myuser^^@^^myserver.example.com^^ openssl x509 -enddate -noout -in "$PEM" | cut -d= -f 2`
  EXPIRY=`date -jf "%b %d %T %Y %Z" "$EXPIRY_RAW" "+%s"`
  REMAINING=$(( EXPIRY - NOW ))
  REMAINING_DAYS=$(( REMAINING / 86400 ))

  if (( REMAINING_DAYS >= 15 ))
  then
    echo "• $NAME expires in $REMAINING_DAYS days"
  else
    if (( REMAINING_DAYS < 0 ))
    then
      echo "‼ $NAME expiry cannot be determined"
    else
      echo "‼ $NAME expires in $REMAINING_DAYS days"
    fi
  fi
done

TimeSpan and HH:mm

Illustration

Time printing and parsing is always “fun” regardless of language but, for most of time C#, actually has it much better than one would expect. However, sometime one can get surprised by small details.

One application had the following (extremely simplified) code writing time entries:

var text = someDate.ToString("HH:mm", CultureInfo.InvariantCulture);

Other application had the (also simplified) parsing code:

var someTime = TimeSpan.ParseExact(text, "HH:mm", CultureInfo.InvariantCulture);

And all I got was System.FormatException: 'Input string was not in a correct format.'

Issue was not in text, all entries were valid time. Nope, issue was in a slight difference between custom DataTime and custom TimeSpan specifiers.

Reading documentation it’s really easy to note the first error as these is a clear warning: “The custom TimeSpan format specifiers do not include placeholder separator symbols.” In short that means we cannot use colon (:) as a TimeSpan separator. Nope, we need to escape it using backslash (\) character. I find this one mildly annoying as not implementing time separators into TimeSpan seems like a pretty basic functionality.

But there is also a second error that’s be invisible if you’re not watching carefully. TimeSpan has no knowledge of H specifier. It understands only its lowercase brother h. I would personally argue this is wrong. If any of those two had to be used, it should have been H as it denotes time more uniquely.

In any case, the corrected code was as follows:

var someTime = TimeSpan.ParseExact(text, "hh\\:mm", CultureInfo.InvariantCulture);

Ideally one would always want to use the same format for output as the input but just because semantics of one class are the same as the other for your use case, you cannot count of format specifier to be the same.