Linux, Unix, and whatever they call that world these days

My SSH Crypto Settings

With ever-expanding number of scripts on my NAS I noticed that pretty much every one had similar, but not quite the same parameters. For example, my automatic replication would use one set of encryption parameters while my Mikrotik router backup script would use other, and my website backup script would use a third variant.

So I decided to see if I could still keep the reasonable security but consolidate all these to a single type.

For key exchange, I had choice of diffie-hellman-group-exchange-sha256, diffie-hellman-group-exchange-sha1, diffie-hellman-group14-sha1, and diffie-hellman-group1-sha1. Unfortunately there is no curve25519-sha256@libssh.org or similar algorithms that are considered more secure.

For a while I considered using diffie-hellman-group14-sha1 as it uses 2048 bit prime but its abandonment by modern SSH versions made me go with diffie-hellman-group-exchange-sha256. As this method allows for custom groups, it should be theoretically better but it also allows server to setup connection with known weak parameters. As servers are in my control, that should not pose an huge issue here.

For cipher my hands were extremely tied - Mikrotik, my router of choice, supports only aes256-ctr and aes192-ctr. Both are of acceptable security so I went with faster: aes192-ctr.

For authentication Mikrotik was again extremely limited - only hmac-sha2-256 and hmac-sha1 were supported. While I was tempted to go with hmac-sha1 which is still secure enough despite SHA1 being broken (HMAC part really does make a difference), I went with hmac-sha2-256 as former might get obsoleted soon.

My final set of “standard” parameters is as follows:

-2 -o KexAlgorithms=diffie-hellman-group-exchange-sha256 -c aes192-ctr -o MACs=hmac-sha2-256

Additional parameter is not strictly encryption related but I find it very reasonable to enforce SSH protocol version 2.

Running Script Without Forking

Default way of running scripts in Linux is that shell forks new process based on hashbang (#!) found in the first line and gives rest of content to that process. And this works beautifully most of the time.

But what if we really need something found only in our current shell?

Fortunately, as long you are using bash, it is easy to run script without creating a separate shell. Just prefix it with dot (.):

./myScript

Some restrictions apply of course - the biggest gotcha being that script should be either bash or with only simple commands as content will be executed directly regardless of hash-bang (#!) specified.

PS: Yes, this works with other shells too, I use bash here as it is most common shell by far.

Micro CA

If you decide to handle your own certificate authority for the purposes of internal certificates, you will be annoyed by all the house keeping tasks involved. This will ring especially true if you need a new certificate just few times a year and having a separate, always-ready machine is way too much overhead to handle.

As pretty much all above applies to me, I decided to create a helper script to ensure I setup stuff the same every time and I kept it really close to how I would do it manually.

First action is to create root CA certificate (will be saved in ca.cer/ca.key):

./microca.sh -r

Then we can give out, for example, TLS client and server certificates or just something for testing:

./microca.sh -u Client myclient
./microca.sh -u Server myserver
./microca.sh mytest

It is even possible to create an intermediate CA and use it to create other certificates:

./microca.sh -a intermediate
./microca.sh -c intermediate -u Client myclient
./microca.sh -c intermediate -u Server myserver
./microca.sh -c intermediate mytest

You can download script from GitHub alongside with brief documentation and it works on both Linux and Windows (via Git Bash).

[2017-03-17: Setting subjectAltName is also supported.] [2018-12-16: MicroCA has its own page now.]

Creating Your Own Certificate Authority

If you are playing a lot with SSL/TLS, at certain point it starts making sense to create your own self-signed certificate authority. However, where to make something that needs to be as secret as possible?

For CA creation I personally love to use Raspberry Pi with Raspbian Jessie Lite for this. Once you create SD image, and assuming you don’t plug the network in, it is as close to secure computer as it can get.

While ideally (and in this guide) you are not going to save anything to disk without password, if you do slip it is really easy to recover on Raspberry Pi. Just destroy the SD card you used and no leaks will occur. Of course, paranoid should destroy SD card regardless. Just in case. :)

But let’s get back to creation of our certificate authority.

First we need to create a key. With OpenSSL you can even choose to protect it with password from the very start. Guess what? We’re gonna use that:

openssl genrsa -aes256 -out ca.key 2048

Once key is there, we need to sign it. Considering RSA 2048 key (approximately 112 bits of security) is considered by NIST to be acceptable until 2030, 10 years duration seems reasonable. You can fill as much or as little information as you wish. If nothing else, fill out common name to simplify your life:

openssl req -new -x509 -key ca.key -sha256 -days 3650 -extensions myext -config <(cat /etc/ssl/openssl.cnf <(echo -e "\n[myext]\nbasicConstraints=CA:true\nkeyUsage=cRLSign,keyCertSign")) -out ca.cer
 Country Name (2 letter code) [AU]: .
 State or Province Name (full name) [Some-State]: .
 Locality Name (eg, city) []: .
 Organization Name (eg, company) [Internet Widgits Pty Ltd]: .
 Organizational Unit Name (eg, section) []: .
 Common Name (e.g. server FQDN or YOUR name []: .
 Email Address []: .

With this our certificate is ready. I still like to make a PKCS #12 packet for safe keeping. Do not forget to set the passphrase here too (it will ask):

openssl pkcs12 -export -in ca.cer -inkey ca.key -out ca.p12

To get all this out of Pi, we can cheat and use FAT32 partition on SD card:

sudo mkdir /boot/ca
sudo cp *.cer /boot/ca/
sudo cp *.p12 /boot/ca/

Now we have a self-signed CA we can use it to create other server or client keys.

Escaping Backtick in (Perl)grep

It all started with a simple list of name='value' and name=`value` entries, all within the same line. My wish was to color a few matching entries for the devious purpose of making them more visible. First stab at solution was extremely easy:

something | grep --color=always -P "somename='.*?'"

Two things to note here: I had to use PERL-style grep as I needed a non-greedy matching and secondly this didn’t fulfill its task. Yep, it didn’t match backtick (`) character.

Simple regex adjustment one might think:

something | grep --color=always -P "somename=['`].*?['`]"

But no - backtick is a tricky one as it serves a special purpose in bash.

I tried a bunch of escaping methods before I remembered that hexadecimal characters are still a thing. Wouldn’t you know it - that worked. To match a backtick, instead of using character itself, one can always use its hexadecimal escape code:

something | grep --color=always -P "somename=['\x60].*?['\x60]"

Git and Missing ANSI Colors

Illustration

While I have quite a few of my projects exposed on GitHub, I also have a private stash on my FreeBSD-based file server.

After a bit of mess-up I had to modify repository directly on server. No biggie, I connected via Putty only to be greeted with a bunch of ESC nonsense upon every Git command execution. From output it was obvious that something was wrong with ANSI color support.

Adding --no-color to each Git command helped a bit but at the cost of color. Some commands that have no support for omitting color I just redirected to tee null. But that also came in black and white only.

After a while I noticed a pattern - Git commands that needed paging were messy while simple ones showed color just fine. On a whim I set Git pager to less:

git config --global core.pager less

And it worked - despite the fact less is generally a Git’s default to start with.

My best guess is that package I installed doesn’t agree well with my environment and that a few switches might actually solve it without Git reconfiguration. However, this worked and I had no will to continue chasing the rainbow any more. :)

Setting Up Private Internet Access on Mint, 2016 Edition

I have already written about getting Private Internet Access running on Linux Mint back in 2013. It was for then current version 16 and it still works. However, there are some possible improvements to be made.

As OpenVPN client is installed by default these days, we only need to download PIA’s OpenVPN configuration files. More careful ones will notice these files are slightly different than recommended default. These have VPN server IP instead of DNS name. While this might cause long term issues if that IP ever changes, it does help a lot with firewall setup as we won’t need to poke a hole for DNS over our eth0 adapter.

From downloaded archive select .ovpn file with desired destination (usually going with one closest to you gives the best results) and also get both .crt and .pem file. Copy them all to your desktop and we’ll use them later for setup. Yes, you can use any other directory too - this is just one I prefer.

With this done we can go into configuring VPN from Terminal window:

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

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

Now we can test our VPN connection:

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

Assuming that this last step ended with Initialization Sequence Completed, we just need to verify whether this connection is actually used and I’ve found whatismyipaddress.com quite helpful here. Just check if IP detected there is different then IP you usually get without VPN.

Stop the test connection using Ctrl+C so we can configure automatic startup and test it.

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

Lastly you can think about firewall and disabling default interface when VPN is not active. This means allowing traffic only on tun0 interface (VPN) and allowing only port 1198 (it used to be 1194).

sudo ufw reset
sudo ufw default deny incoming
sudo ufw default deny outgoing
sudo ufw allow out on tun0
sudo ufw allow out 1198/udp
sudo ufw enable

Assuming all went well, VPN should be happily running.

PS: For nitpickers, you can actually make firewall for VPN a bit more strict. This assumes 1.2.3.4 is address of your VPN destination (check in client.conf under remote) and that your network interface is eth0 (check with ip addr).

sudo ufw reset
sudo ufw default deny incoming
sudo ufw default deny outgoing
sudo ufw allow out on tun0
sudo ufw allow out on eth0 proto udp to 1.2.3.4 port 1198
sudo ufw enable

[2017-08-18: Newer guide is available for Linux Mint 18.]

Brother MFC in Isolated Guest Network

Illustration

I have already written how to poke holes in guest network for Chromecast and that method is sufficient for vast majority of devices. However, occasionally you might stumble upon device presenting a bit more challenge. One example is my Brother MFC-J475DW or better said pretty much anything in Brother’s MFC printer lineup.

In order to determine why my printer wouldn’t work despite explicitly allowing for its MAC address, I snooped all traffic using Wireshark. As I knew printer was using IPv4 address, that was my Wireshark filter (ip.version == 4).

After playing with printer for a while (trying printing, scanning, rebooting, etc.), I stopped snoop and started going over captured packets. One packet stood out from the bunch - it was a name query packet for something looking suspiciously like my printer’s name. That packet was broadcasted to my whole network from my computer. As that packet went unanswered, my PC though there is no printer.

Armed with that knowledge, firewall-start script can be adjusted not only to allow traffic from and to the MAC address belonging to the printer (as done for the Chromecast adventure) but also to allow broadcast traffic on the first 2.4 GHz guest WiFi interface:

echo "#!/bin/sh" > /jffs/scripts/firewall-start
echo "ebtables -I FORWARD -p ARP -i ! eth0 -o wl0.1 -j ACCEPT" >> /jffs/scripts/firewall-start
echo "ebtables -I FORWARD -s ^^34:68:95:A7:64:F5^^ ``-i wl0.1`` -o ! eth0 -j ACCEPT" >> /jffs/scripts/firewall-start
echo "ebtables -I FORWARD -d ^^34:68:95:A7:64:F5^^ -i ! eth0 ``-o wl0.1`` -j ACCEPT" >> /jffs/scripts/firewall-start
echo "ebtables -I FORWARD -d ^^ff:ff:ff:ff:ff:ff^^ -i ! eth0 ``-o wl0.1`` -j ACCEPT" >> /jffs/scripts/firewall-start
echo "logger Poked hole for Brother MFC printer" >> /jffs/scripts/firewall-start
chmod a+x /jffs/scripts/firewall-start
reboot

PS: During snooping do close all other programs that are using network and try to keep any non-printer activity to a minimum. Makes snoop analysis much easier.

Chromecast in Isolated Guest Network

My home network pretty much revolves around Asus RT-AC56U with Asuswrt-Merlin firmware. Nice, stable, and full of features. One feature I absolutely love is multiple isolated guests networks. And I do not use it only for guests.

While my computers are all in main network, all my devices (Chromcast, printer, IoT, …) are in guest network without any intranet access. They can get on Internet but they cannot access my internal network. Considering all is done on the same router, it is not ideal, but it does increase security considerably.

Initially one device presented some trouble. You see, to cast YouTube from my computer I had to have it in my main network. But I didn’t want to. I wanted it to be in isolated guest LAN together with all other devices. But I did want to access it from my internal network. Since the whole network isolation for guest networks is done via firewall rules, that meant it was time for some hole poking.

Prerequisite is to have Asus JFFS enabled. This will enable saving scripts so they can be executed upon startup. Yes, you can do it manually every boot but that gets old quite quickly. For the actual firewall setup, the only thing needed is MAC address of Chromecast device and we can make it an exception to the rule. I prefer to do it via script:

echo "#!/bin/sh" > /jffs/scripts/firewall-start
echo "ebtables -I FORWARD -p ARP -o ! eth0 -j ACCEPT" >> /jffs/scripts/firewall-start
echo "ebtables -I FORWARD -s ^^A4:77:33:33:48:85^^ -o ! eth0 -j ACCEPT" >> /jffs/scripts/firewall-start
echo "ebtables -I FORWARD -d ^^A4:77:33:33:48:85^^ -i ! eth0 -j ACCEPT" >> /jffs/scripts/firewall-start
echo "logger Poked hole for Chromecast" >> /jffs/scripts/firewall-start
chmod a+x /jffs/scripts/firewall-start
reboot

First rule pokes a hole through isolation in order to allow for ARP requests. Second two rules allow everything coming from and to specific MAC address. Everything else is a bit of plumbing making script run on router’s startup.

To make this a bit more secure, one might want to restrict this only to interface where device is actually located. Every router firmware might do things a bit differently but guest networks on mine were setup in a reasonable fashion. They all followed formal wlX.Y where X was 0 for 2.4 GHz guest networks and 1 for 5 GHz. Y was number between 1 and 3 directly corresponding to guest network index.

Since my Chromecast device was first guest network in 5 GHz range, its designation was wl1.1 and thus hole could be made a bit smaller:

echo "#!/bin/sh" > /jffs/scripts/firewall-start
echo "ebtables -I FORWARD -p ARP -i ! eth0 ``-o wl1.1`` -j ACCEPT" >> /jffs/scripts/firewall-start
echo "ebtables -I FORWARD -s ^^A4:77:33:33:48:85^^ ``-i wl1.1`` -o ! eth0 -j ACCEPT" >> /jffs/scripts/firewall-start
echo "ebtables -I FORWARD -d ^^A4:77:33:33:48:85^^ -i ! eth0 ``-o wl1.1`` -j ACCEPT" >> /jffs/scripts/firewall-start
echo "logger Poked hole for Chromecast" >> /jffs/scripts/firewall-start
chmod a+x /jffs/scripts/firewall-start
reboot

To make these firewall rules even stricter, one can also restrict ARP to allow only certain IPs but I will leave this as an exercise for some other time.

PS: Using pretty much the same basic procedure one can get any device accessible to other isolated guest networks. This is not only nice for Chromecast but really useful for getting printer working too.

SDR on Raspberry Pi 3 (Software)

Illustration

[This is a post 2 in series, for hardware setup go here]

The first piece of software we need to get Raspberry Pi running is of course its operating system. If you have a monitor and keyboard attached, you can proceed with NOOBS install without any trouble. If you don’t want to deal with imaging SD card, this is the best as you can even buy it pre-programmed.

However, I like to complicate and thus decided to go with the headless install of Raspbian Jessie Lite. The most noticeable difference between normal Raspbian and Raspbian Lite is that you get a graphical interface only in one of them. Guess which is which.

After creating image and booting machine up with the prepared SD card, one challenge is how to find it. I personally use Import function of MagiWOL to find a new IP around, but you can also log into your router and figure IP from there. Chickens can even attach monitor, I won’t judge. With IP in hand just connect to machine via SSH client (e.g. PuTTY). User name and password are usual pi/raspberry.

First action after fresh install would be to update to the latest packages:

sudo apt-get update

Next you proceed with installing all the goodies (steps taken from Ham Radio Science):

sudo apt-get install -y git cmake libusb-1.0-0.dev build-essential
git clone git://git.osmocom.org/rtl-sdr.git
cd rtl-sdr/
mkdir build
cd build
cmake ../ -DINSTALL_UDEV_RULES=ON -DDETACH_KERNEL_DRIVER=ON
make
sudo make install
sudo ldconfig

Now we want to create two scripts. Yes, you can run stuff manually but these scripts are going to make it easier if you want to do it automatically on startup. Notice each is just a single cat command with a lot of data:

cat > /home/pi/rtl-sdr/build/rtl_tcp.sh <<- EOF
#!/bin/bash
INTERFACE=eth0
IP=`ip addr show $INTERFACE | grep inet | grep -v inet6 | awk '{print $2}' | cut -d'/' -f1`
if [[ $IP != '' ]] ; then
    /usr/local/bin/rtl_tcp -a $IP
else
    echo "Cannot find IPv4 address on $INTERFACE interface." >&2
    exit 1
fi
EOF

# cat > /home/pi/rtl-sdr/build/rtl_tcp.service <<- EOF
[Unit]
After=network-online.target

[Service]
Type=simple
ExecStart=/home/pi/rtl-sdr/build/rtl_tcp.sh
ExecStop=killall rtl_tcp
Restart=on-failure
RestartSec=10
User=pi

[Install]
WantedBy=multi-user.target
EOF

Finally we have a chutzpah of steps for the remaining details (including disabling TV driver for the SDR device):

chmod +x /home/pi/rtl-sdr/build/rtl_tcp.sh
sudo cp /home/pi/rtl-sdr/rtl-sdr.rules /etc/udev/rules.d
sudo bash -c 'echo "blacklist dvb_usb_rtl28xxu" > /etc/modprobe.d/dvb_usb_rtl28xxu.conf'

If all steps are completed, we can now run our newly created script:

# /home/pi/rtl-sdr/build/rtl_tcp.sh
 Found 1 device(s):
   0:  Realtek, RTL2838UHIDIR, SN: 00000001
 Using device 0: Generic RTL2832U OEM``
 Found Rafael Micro R820T tuner``
 [R82XX] PLL not locked!
 Tuned to 100000000 Hz.
 listening...
 Use the device argument 'rtl_tcp=192.168.200.47:1234' in OsmoSDR (gr-osmosdr) source
 to receive samples in GRC and control rtl_tcp parameters (frequency, gain, ...).

If you wish program to start automatically, you can also make use of the newly created service file:

sudo systemctl enable /home/pi/rtl-sdr/build/rtl_tcp.service
sudo init 6

After machine is booted back up (it should take less than a minute), you can use your favorite SDR program with a support for RTL-SDR over TCP to connect on port 1234. I personally find SDR# well behaved and problem-free but any other will do too.

PS: All this was done on a default partition that has under 200 MB free. To increase partition size run sudo raspi-config and choose Expand Filesystem.

[2017-01-16: Adjusted file creation commands to work better with shell copy/paste] [2020-11-11: Added extra cmake parameters.]