My ZFS Settings

In multiple posts so far I’ve created a ZFS pool using pretty much the same parameters. But I never bothered to explain why I chose them. Until now…

From my latest ZFS-related post, I have the following pool creation command:

zpool create -o ashift=12 -o autotrim=on \
    -O compression=lz4 -O normalization=formD \
    -O acltype=posixacl -O xattr=sa -O dnodesize=auto -O atime=off \
    -O encryption=aes-256-gcm -O keylocation=prompt -O keyformat=passphrase \
    -O canmount=off -O mountpoint=none $POOL $DISK

ashift=12

This setting controls the block size of your pool and should match whatever your (spinning) disk uses. Realistically, you’ll probably use 4K sectors thus 12 is a good starting value. Why the heck 12? Well, this is expressed as 2ⁿ and 2¹² is 4 KB. I like to force it because often ZFS might wrongly auto-detect value 9 (512 bytes) which shouldn’t be really used these days. This is not really ZFS’ fault but consequence of some disks being darn liars to preserve compatibility.

Even if you do have 512-byte disks today, any replacement down the road will be at least 4K. Since the only way to change this option is to recreate the pool one should think ahead and go with 4K immediately.

When it comes to SSD setups there might be some benefit in going even higher since SSD usually use 8K or even larger erase blocks. However, since SSDs are much more forgiving when it comes to the random access, most of time it’s simply not worth it because large block sizes will cause other issues (e.g., slack space).

autotrim=on

Support for trim is really important for SSD and completely irrelevant when it comes to the spinning rust. Since my NAS uses good-old hard drives, this setting really doesn’t apply. But I also use ZFS on my laptop and there it makes a huge difference. So I include it always just not to forget it by accident when it matters.

compression=lz4

While zstd seems to be a compression darling, I still prefer lz4 for my local datasets because it’s much easier on the CPU. There’s also an option to turn off compression completely, but I honestly cannot determine any speed improvement in a general case. Using compression is like receiving free space, so why not?

normalization=formD

As ZFS uses Unicode (UTF-8 more specifically), it has an interesting problem that two filenames might look the same but they might have two different expressions. Most known example might be Å which can be expressed either as Å or as combination of A and a separate ring mark. From the point of user, both these are the same. But they have a different binary expression (U+00C5 vs U0041 U+030A).

Setting normalization explicitly just ensures each file name is stored in its canonical Unicode representation and thus things that look the same are going to be the same. I personally like formD on a philosophical level but any normalization will do the same. Just don’t stick with default value of none.

acltype=posixacl

This option allows you to store extra access attributes not covered by a “standard” user/group/world affair. The most common need for these attributes is with SELinux. However, even if you’re not using SELinux, you should enable it as it doesn’t really impact anything if not used. And you might consider using SELinux in the future.

xattr=sa

This option will tell ZFS to store extra access attributes (see above) with the metadata. This is a huge performance boost if you use them. If you don’t use them it has no effect so you might as well future-proof your setup.

dnodesize=auto

Assuming you already save all these extra attributes, it’s obvious they cannot really fit nicely in one metadata node. Unless it’s a big one. Once set, this option (assuming feature@large_dnode=enabled) will allow larger than normal metadata at the cost of some compatibility. Assuming you have ZFS 0.8.4 or above, you really have nothing to worry about.

atime=off

Posix standard specifies that one should always update access time whenever file or directory is accessed. You went into your home directory - update. You opened a file without changing anything - update. These darn updates really stack up and there is really no general use case where you would need to know when the file was read. This flag will turn off these updates.

encryption=aes-256-gcm

I like my datasets encrypted. Ideally one would use full disk encryption but using ZFS native encryption is a close second with unique benefits at a cost of minor data leaks (essentially only ZFS dataset names). And GCM encryption is usually the fastest here.

keyformat=passphrase

Call me old-fashioned but I prefer a passphrase to a binary key. Reason is that I can enter passphrase more easily in a pinch.

keylocation=prompt

For my laptop I keep prompt as a key source so I can easily type it. For servers, I use file:// syntax here since I keep my passphrase on a TmpUsb USB drive. This allows me to reboot server without entering key every time but in the case it’s ever stolen my data is inaccessible.

canmount=off, mountpoint=none

As a rule, I try not to have top-level dataset mountable. I just use it to set defaults and data goes only in sub-datasets.

And that’s all the explanation I’m ready to offer.

Rabbit Managed

One of my first vivid memories when it comes to having fun while programming definitely contains me implementing RC4 encryption in QBasic. Algorithm was perfect. It worked on per-byte text, was simple enough to have at least basic understanding what the heck was happening, and it gave me a bit of “el bandito” feeling as algorithm was leaked. Going over RC4 encouraged me to reinvent the wheel and was a direct cause of my love into creating own encryption algorithms. And I’ve created quite a few…

In time I learned a bit more about encryption. Or at least enough to understand why “rolling your own” is generally a bad idea. With time my beloved RC4 got its ass kicked by cryptoanalysis to be finally deprecated in 2015. My focus meantime went toward block algorithms, most notably Twofish and later AES version of Rijndael. My mind decided to go block cipher route but my heart still longed for good old stream cipher times.

As someone following crypto-world as a hobby, I was surprised I missed a whole stream cipher competition - eSTREAM. At this time it’s already an ancient news but results of that competition are still available in the form of 4 secure software stream ciphers: HC-128, Rabbit, Salsa20/12, and SOSEMANUK. All these eSTREAM finalists are still secure, completely free, and really nostalgia inducing for those with a soft spot for stream ciphers.

One that immediately drew my attention was Rabbit. As name suggests, this one was really fast. Additionally, it has quite understandable method of operation, uses no “exotic” operations, it has a reasonably small state (513 bits), and it’s specified in RFC4503. The only thing I couldn’t find was a C# implementation wrapping it into a SymmetricAlgorithm so it can be easily used with CryptoStream. Well, now there’s one…

If you want to use the Rabbit from C#, take a look at my RabbitManaged class. It derives from SymmetricAlgorithm and exposes ICryptoTransform interface so it can be used with CryptoStream. While it uses 128-bit blocks internally, it also allows usage without padding (i.e. more like a traditional stream algorithm). It also allows for all standard paddings.

Considering wide prevalence of AES, its usage will be limited at best but I believe into not having all eggs in one basket and you might find usage for it still. But damn, it was fun to implement this little gem.

PS: For nostalgia, I also have a SymmetricAlgorithm implementation of RC4.

Turning Off Narrator in Windows 10

My problem started with a cat. She loves enforcing my laptop breaks and having some laptop time herself. Whenever that happens, I lock my laptop and let her be for 5 minutes. Without fail she’ll manage to turn off wireless and enable the darn Windows Narrator. And no, turning off the Narrator shortcut doesn’t help.

Issue here is that lock screen works under completely different environment and permanently disabling narrator shortcut for your user will do nothing. No, solving this requires a bit more interaction and the easiest way I found is through registry editing.

To turn off the Narrator, there is a well documented WinEnterLaunchEnabled registry value. For our user we can find this at HKEY_CURRENT_USER\Software\Microsoft\Narrator\NoRoam but for logon user this hides at HKEY_USERS\S-1-5-18\Software\Microsoft\Narrator\NoRoam. Setting this DWORD value to 0 sorts the issue even without going for restart.

However, since my cat also plays with touchpad, I decided to remove the whole Ease of Access portion to ensure erroneous touchpad movements as cat lays down cannot turn anything on. For this we need the [BrandingNeutral](https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-embedded-embeddedlogon-brandingneutral) value. This can be found at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Embedded\EmbeddedLogon. Setting DWORD value to 8 followed by a restart sorts out that issue.

Those full of trust can download registry file with these settings here and import them automatically while others can do the registry changes manually. In either case Windows 10 will become a bit more cat friendly.

Using Proper NTP on Android Phone

Illustration

As a time connoisseur, I really get really pissed off when my phone’s time is off by a second or two. Considering phone receives time from the radio network, one would expect this not to happen. However, for some reason, it seems no USA network provider actually cares to have their radio time straight. The solution to this problem would be using an NTP server instead of the time provided by the network. However, with an Android devices that’s not as straightforward as one would expect. But it is possible…

Before starting anything, we first need to turn on Developer Options (usually tapping Build Number 7 times). Once this is enabled, we need to enable USB Debugging (Settings > System > Advanced > Developer Options > USB debugging). With this done, we can finally download Platform Tools and check if our device is visible:

adb devices
 List of devices attached
 0A281JCCBA0317  device

Once connectivity is tested, we can immediately go onto setting the NTP server followed by a reboot:

adb shell settings put global ntp_server ^^time.medo64.com^^
adb reboot

Once device has rebooted we can check the value:

adb shell settings get global ntp_server 
 time.medo64.com

And that’s it. Now your device will use the defined time server instead of the unreliable network time.

Getting IPv6 Going on RouterOS 7.1

I already did a post about IPv6 on Mikrotik but with RouterOS 7 going out, some things have slightly changed. So, it’s time for an updated guide. And, as one might expect, things are pretty much the same.

As before, prerequisite is that you get at least /64 prefix from your ISP (Comcast in my case) via DHCPv6. Also assumed is empty IPv6 configuration.

The first thing I like doing is disabling the default neighbor discovery interface. Blasting IPv6 router advertisements on all interfaces is not necessarily a good idea:

/ipv6 nd
set [ find default=yes ] disabled=yes

The next step is to setup DHCP client. Within a few seconds, you should see the prefix being allocated:

/ipv6 dhcp-client
add add-default-route=yes interface=ether1 pool-name=^^general-pool6^^ request=prefix  use-peer-dns=no

:delay 5s
print
 Flags: D - dynamic, X - disabled, I - invalid
  #    INTERFACE             STATUS        REQUEST             PREFIX
  0    ether1                bound         prefix              ^^2601:db8:9780:ee2c::/64^^, 3d14h41m41s

At this time I love to allocate address ending with ::1 to the router itself:

/ipv6 address
add address=::1 from-pool=^^general-pool6^^ interface=^^bridge1^^ advertise=yes

Now it should be possible to ping its address from external computer (in this example address would be 2601:db8:9780:ee2c::1). If this doesn’t work, do check if you have link-local addresses. If none are present, reboot the router and they will be regenerated.

With router reachable, it is time to delegate IPv6 prefix to internal machines too. For this purpose, setup RA (router announcement) over the bridge. While default interval settings are just fine, I like to make them a bit shorter (20-60 seconds):

/ipv6 nd
add interface=^^bridge1^^ ra-interval=20s-60s

And that’s all. Now your computers behind the router will have direct IPv6 route to the Internet. Do not forget to setup both router firewall and firewall of individual devices. There is no NAT to save your butt here.

PS: Here is the basic IPv6 firewall allowing all connections out while allowing only established back in:

/ipv6 firewall filter

add chain=input action=drop connection-state=invalid comment="Drop invalid"
add chain=input action=accept connection-state=established,related comment="Accept established"
add chain=input action=accept in-interface=ether1 protocol=udp src-port=547 limit=10,20:packet
add chain=input action=drop in-interface=ether1 protocol=udp src-port=547 comment="Drop ext DHCP >10/sec"
add chain=input action=accept in-interface=ether1 protocol=icmpv6 limit=10,20:packet
add chain=input action=drop in-interface=ether1 protocol=icmpv6 comment="Drop ext ICMP >10/sec"
add chain=input action=accept in-interface=!ether1 protocol=icmpv6 comment="Accept internal ICMP"
add chain=input action=drop in-interface=ether1 comment="Drop external"
add chain=input action=reject comment="Reject everything else"

add chain=output action=accept comment="Accept all"

add chain=forward action=drop connection-state=invalid comment="Drop invalid"
add chain=forward action=accept connection-state=established,related comment="Accept established"
add chain=forward action=accept in-interface=ether1 protocol=icmpv6 limit=20,50:packet"
add chain=forward action=drop in-interface=ether1 protocol=icmpv6 comment="Drop ext ICMP >20/sec"
add chain=forward action=accept in-interface=!ether1 comment="Accept internal"
add chain=forward action=accept out-interface=ether1 comment="Accept outgoing"
add chain=forward action=drop in-interface=ether1 comment="Drop external"
add chain=forward action=reject comment="Reject everything else"