Tag Archives: debian

qBittorrent on Raspberry Pi

…in case you don’t want to spend too much time and have a nice torrent client with web interface installed on your Raspberry Pi, qBittorrent is probably a good option (even full of settings).

Of course, we need to install the package (we install the one with NO GUI, only web interface):

apt install qbittorrent-nox

We also want to create a user with minimum privileges to run this software.
We are going to create a user called torrent part of a new group called torrent as well, within a home directory in /var/torrent
After that, we will add our own user (e.g. user1 to the torrent group, in order to allow you to access the downloaded files.
We are also creating a downloads folder, setting the right permissions.

adduser --system --group --home /var/torrent torrent
adduser user1 torrent
mkdir -p /var/torrent/downloads
chown torrent:torrent /var/torrent/downloads
chmod 770 /var/torrent/downloads

Now we’re going to create a new systemd service file: /etc/systemd/system/qbittorrent-nox.service

Description=BitTorrent Client

ExecStart=/usr/bin/qbittorrent-nox -d --webui-port=8080


Enable and start the service:

systemctl enable qbittorrent-nox
systemctl start qbittorrent-nox

You should now be able to connect to your raspberry pi’s IP, on port 8080.

You will be asked to login.
Note: default username isย admin,ย and the password isย adminadmin.
Once logged in, under Settings > WebUI, you can whitelist your local network (e.g. to skip authentication.

…and yes, that’s it ๐Ÿ˜‰

I told you it was a quick one!


Reverse SSH Tunnel

To allow LOCAL_SERVER behind a firewall/NAT/Home Router to be accessible via SSH from a REMOTE_SERVER you can use a ssh tunnel (reverse).

Basically, from your LOCAL_SERVER you forward port 22 (ssh) to another port on REMOTE_SERVER, for example 8000 and you can ssh into your LOCAL_SERVER from the public IP of the REMOTE_SERVER via port 8000.

To do so, you need to run the following from LOCAL_SERVER:

 local-server: ~ ssh -fNR 8000:localhost:22 <user>@<REMOTE_SERVER>

On REMOTE_SERVER you can use netstat -nlpt to check if there is a service listening on port 8000.


remote-server ~# netstat -nplt | grep 8000
tcp        0      0  *               LISTEN      1396/sshd: root
tcp6       0      0 :::8000                 :::*                    LISTEN      1396/sshd: root

In this case, the REMOTE_SERVER allows connection from ALL the interfaces ( to port 8000.
This means that, if the REMOTE_SERVER has IP, if you can connect to LOCAL_SERVER from a THIRD_SERVER using the following:

third-server: ~ ssh -p 8000 <user_local_server>@

NOTE. If you see that the LISTEN connection on REMOTE_SERVER is bound to and not to, it is probably related to the setting GatewayPorts set to no in /etc/ssh/sshd_config on REMOTE_SERVER.
Best setting is clientspecified (rather than yes) as per this post.

Set this value to yes and restart sshd service.

With that setting, you can potentially allow only connection from the REMOTE_SERVER to the LOCAL_SERVER, to increase security.
To do so, you need to use the following ssh command from LOCAL_SERVER:

 local-server: ~ ssh -fNR <user>@<REMOTE_SERVER>

With netstat, you’ll see now this:

remote-server:~# netstat -nplt | grep 8000
tcp        0      0*               LISTEN      1461/sshd: root

With this forward, you will be able to access LOCAL_SERVER ONLY from the REMOTE_SERVER itself:

remote-server: ~ ssh -p 8000 <user_local_server>@localhost

I hope this helps ๐Ÿ™‚

Happy tunnelling!

Virtualhost and Letsencrypt

Quick guideline about how to install multiple sites on a single server using Virtualhosting, and have the SSL certificate installed and automatically renewed using Letsencrypt.

There are plenty of how to online, but I wanted to have a quick reference page for myself ๐Ÿ™‚

Firstly, this has been tested on Debian 12, but it should work on previous Debian versions and Ubuntu too.

Apache setup and virtualhosts

Firstly, install Apache and other packages that you will mostly likely need, especially if you run WordPress or any php based framework:

apt-get install apache2 php php-mysql libapache2-mod-php php-gd php-curl net-tools telnet dos2unix

Now, you should create the folder structure to host your sites. I used /var/www/virtualhosts/<site>/public_html

I made sure permissions were set correctly too:

chown -R www-data:www-data /var/www/
find /var/www -type -d -exec chmod 775 {} \;

Now, create a virtualhost file for each site. In the following example we are going to create the conf file for site1.

Create /etc/apache2/sites-available/site1.conf

<VirtualHost *:80>
    ServerName site1.com
    ServerAlias www.site1.com
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/virtualhosts/site1/public_html

    <Directory /var/www/virtualhosts/site1/public_html>
        Options -Indexes +FollowSymLinks
        AllowOverride All

    ErrorLog ${APACHE_LOG_DIR}/site1-error.log
    CustomLog ${APACHE_LOG_DIR}/site1-access.log combined

Do the same for all the sites you have.

Once done, upload the content of your sites in public_html folder.

Disable all the default Apache sites and enable the ones you have created. You can use the commands a2dissite and a2ensite or manually create symbolic links into /etc/apache2/sites-enabled/

Check that all the virtualhosts are properly loaded:

source /etc/apache2/envvars
apache2 -S

You should see all your sites under *80 section.
Right now we have enabled only Apache on port 80 to return the sites we have hosted. No 443 yet.

Now, you can use curl to do some tests to see if the virtual hosts are responding correctly.

~ curl -IH'Host: site1.com' http://<server_IP>  # to get the header of site1.com
~ curl -H'Host: site1.com' http://<server_IP>  # to get the full page of site1.com

Hopefully all works (if not, troubleshoot it heheh), let’s point our DNS to our server, and test directly using the domain names.

All good? Cool!

Make sure now that your firewall allows port 80 and port 443. Even if you’re considering to serve your site ONLY over SSL (port 443), the certbot tool that does the auto-renewal of the certificate needs port 80 open.

Installation and configuration of certbot – Letsencrypt

As root, issue the below commands:

apt-get install snapd
snap install core
snap refresh core
snap install --classic certbot
ln -s /snap/bin/certbot /usr/bin/certbot

You have now the certbot tool installed.

Following the above example of site1.com, we are going now to get the SSL certificate for that site (even the www.site1.com one), and let the tool install and configure everything automatically.

certbot --apache -d site1.com -d www.site1.com

Hopefully all goes well ๐Ÿ™‚ Repeat for each of your sites accordingly.

Once done with all the sites, just to make sure the auto-renewal works, you can also issue a dry-run check:

certbot renew --dry-run

Letsencrypt certificates last 90 days (afaik), but the certbot tool installed in this way does the auto-renewal in an automatic fashion.
If you’re curios where this is written (you might think about cron but unable to find anything – like it happend to me).
If this is the case, you can try to run this command, and you may find the certbot listed:

systemctl list-timers

More information are available on the official website at this address.

You can now test using curl again, but hitting https instead of http:

~ curl -IH'Host: site1.com' https://<server_IP>  # to get the header of site1.com
~ curl -H'Host: site1.com' https://<server_IP>  # to get the full page of site1.com

Oh, one note.
By default, at least at the time when I’m writing this article, once you install the certificate, the *80 virtualhost of your site will be modified, adding the following lines, which force a 302 redirect from http to https.

RewriteEngine on
RewriteCond %{SERVER_NAME} =www.site1.com [OR]
RewriteCond %{SERVER_NAME} =site1.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]

If it’s what you want – cool.
If you still want to serve your site on http AND https, comment out (or delete) those new lines.

Happy virtualhosting and ssl’ing! ๐Ÿ™‚

Migrate Linux Subsystem from one PC to another

Are you enjoying your favorite Linux distro running within the Windows 10 Linux Subsystem?

Have you configured all nicely?

What happened if you get a new pc and you’d like to migrate your VM across?

This is what happened to me. And looking around, I found this post that gave me this kinda-dirty way, but did work!

After that, I decided to review the steps, and I’ve added these directories in the exclude’s list, to make clearer the process of export/import:


Of course, if you have important data in these folders and you want to move across too, just update the one-liner below accordingly. ๐Ÿ˜‰

On your OLD PC

  • Open your Linux VM
  • Get inside your Downloads directory (replace <user> with your username):

    cd /mnt/c/Users/<user>/Downloads
  • Make sure to be root (sudo su -)
  • Run:

    tar -cvpzf backup.tar.gz --exclude=/backup.tar.gz --exclude=/dev --exclude=/proc --exclude=/sys --exclude=/run --exclude=/tmp --exclude=/media --exclude=/mnt --exclude=/var/cache --exclude=/var/run --one-file-system /

    NOTE: you could achieve the same using the option --exclude-from=file.txt, and having the list of exclusions in this file. I used a one-liner as it’s quicker to copy and paste.
  • Once done, close your Linux VM
  • Verify that you have a new file called backup.tar.bz in Downloads

On your NEW PC

  • Install from Microsoft Store the same Linux VM (or reinstall in the same way you have done originally on your old pc)
  • Copy across your backup.tar.bz within your new Downloads folder
  • Open the VM that you’ve just installed (minimal setup – this will be completely overwritten, so don’t be bothered too much)
  • Once you’re inside and your backup.tar.bz is in Download, run the following (replace <user> with your username):

    sudo tar -xpzf /mnt/c/Users/<user>/Downloads/backup.tar.gz -C / --numeric-owner
  • Ignore the errors
  • Close and re-open the VM: DONE! ๐Ÿ™‚

Happy migration! ๐Ÿ˜‰

Auto mount an encrypted IMG file stored on NFS share

Yes, here we are again.
Now that I have a NAS at home, it’s about time to get rid of all these single USB disks connected to my Raspberry PIs.

I have a share called nfsshare available from my NAS (IP: The full share path is My NAS handles NFS version 4.

So, here what I’ve done, to setup my Banana Pro Pi with Armbian based on Debian 10 (buster).

Configure NFS client

First of all, we need to create the mount point where we’re going to access the nfs share (let’s use /nfs) and install the packages for NFS.

mkdir /nfs
apt-get install nfs-common

Once done, a minimal tuning of idmapd.conf, if you have defined a domain/workgroup within your network. In this example I’m using mydomain.loc.

sed -i 's/#Domain = local.domain.edu/Domain = mydomain.loc/' /etc/idmapd.conf

Update our /etc/fstab file, to make sure it mounts at boot, and test if all works as expected: /nfs nfs4 auto,_netdev,nofail,noatime,nolock 0 0

I have used _netdev to make sure that the system understands that this needs the network up before trying to mount, and, if something goes wrong, the boot continues (nofail). This is very handy on systems without a proper monitor where you rely on ssh connections.

Now, with a simple mount /nfs command, you should be able to get the share mounted. df -Th or mount commands are what I would you to verify.

Cool, we have now the share mounted. Issue a quick shutdown -r now to see if all works as expected. Once your device is back online, ssh into it and check with df -Th or mount commands again. Hopefully, you can see your nfs share mounted to /nfs.

Create and configure your Encrypted “space”

I have already discussed something about encrypted devices in another post. This will be a revised version of the previous post, without custom scripts, but simply using what Debian offers out of the box.

Create an empty IMG file to host our encrypted space

I have decided to create 500GB of encrypted space to store my data. To do so, I did the following:

  • install the required software for encryption
  • create a sparsefile (on my /nfs share)
  • encrypt it
  • format it (ext4)
  • setup the auto mount
apt-get install cryptsetup

dd of=/nfs/file_container.img bs=1 count=0 seek=500G

cryptsetup -y luksFormat /nfs/file_container.img
cryptsetup luksOpen /nfs/file_container.img cryptcontainer

mkfs.ext4 -L cryptarchive /dev/mapper/cryptcontainer

During the above steps, you will be asked to set a passphrase, and use it to open the IMG file. Pretty straight forward, isn’t it?

Cool. Now we have 500GB sparsefile called file_container.img store on our share /nfs ready to be mounted somewhere and utilised.

To make sure we can mount at boot, we need a secret key that we are going to use to decrypt the IMG file without typing any passphrase.

Let’s create this key stored under /root (in this example). You can store wherever you want, as long as it’s accessible before the decryption start. Another good place is /boot.

dd if=/dev/urandom of=/root/keyfile bs=1024 count=4
chmod 0400 /root/keyfile

Now we need to add this key within the IMG file

cryptsetup luksAddKey /nfs/file_container.img /root/keyfile

Next step, is to instruct /etc/crypttab, with the details about our encrypted file and its key.
Just add the following line at the end of /etc/crypttab file.

cryptcontainer /nfs/file_container.img /root/keyfile luks

Now, there is a problem. Your OS needs to know that the IMG file isn’t stored locally and has a dependency on the NFS share. In fact, if the OS tries to decrypt the IMG file before mounting the NFS share, it will fail, and you might get stuck in a no-end booting, forcing sometimes to get your mini monitor for troubleshooting, a spare keyboard and anti-human positions to reach your small Pi etc etc… basically, a nightmare!

So, here a trick that seems working.
In Debian, there is a file called /etc/default/cryptdisks
Within this file, we are going to make sure that CRYPTDISKS_ENABLE is set to yes and CRYPTDISKS_MOUNT is set to our NFS mount (/nfs). In this way, the service that handles the encryption/decription will wait for /nfs mounted before starting.
IMPORTANT: this must be a mountpoint within /etc/fstab

Here the content of my /etc/default/cryptdisks file

# Run cryptdisks initscripts at startup? Default is Yes.

# Mountpoints to mount, before cryptsetup is invoked at initscripts. Takes
# mountpoins which are configured in /etc/fstab as arguments. Separate
# mountpoints by space.
# This is useful for keyfiles on removable media. Default is unset.

# Default check script. Takes effect, if the 'check' option is set in crypttab
# without a value.

Amazing! Now, just the last bit: update /etc/fstab with the reference of our device. Because now we have setup all the necessary to open the encrypted IMG file and associate it to a mountable device. But we haven’t mounted it yet!

Create the mount point:

mkdir /cryptoarchive

Update /etc/fstab, appending this line:

/dev/mapper/cryptcontainer /cryptoarchive ext4 defaults,nofail 0 2

Again, the nofail, as for the NFS share, to avoid the boot process to get stuck in case of errors, and allow you to ssh into the device and troubleshoot.

Now we’re ready to try a mount /cryptoarchive, a df -Th and mount checks, and also a shutdown -r now, to verify that the NFS share gets mounted and the IMG encrypted disk mounted and available too.

Happy playing! ๐Ÿ˜‰

Fail2ban Debian 9

Scratch pad with conf files to configure Fail2ban on Debian 9

This setup will configure Fail2ban to monitor SSH and keep track of theย bad guys. Every time an IP gets banned, it will be stored in


ย .
This files gets processed every time Fail2ban restarts.
A cron will sanitise the file daily.


1) Create a custom action file:

# Fail2Ban configuration file


before = iptables-common.confhttps://docs.google.com/document/d/1DjP5z7tvkaMWJMZXVAnMOCgfynfQNHvRkqJyxQdEB84/edit?usp=sharing


# Option:  actionstart
# Notes.:  command executed once at the start of Fail2Ban.
# Values:  CMD
actionstart = <iptables> -N f2b-<name>
              <iptables> -A f2b-<name> -j <returntype>
              <iptables> -I <chain> -p <protocol> -j f2b-<name>
              # Persistent banning of IPs
              cat /etc/fail2ban/ip.blacklist | grep -v ^\s*#|awk '{print $1}' | while read IP; do <iptables> -I f2b-<name> 1 -s $IP -j DROP; done

# Option:  actionstop
# Notes.:  command executed once at the end of Fail2Ban
# Values:  CMD
actionstop = <iptables> -D <chain> -p <protocol> -j f2b-<name>
             <iptables> -F f2b-<name>
             <iptables> -X f2b-<name>

# Option:  actioncheck
# Notes.:  command executed once before each actionban command
# Values:  CMD
actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'

# Option:  actionban
# Notes.:  command executed when banning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    See jail.conf(5) man page
# Values:  CMD
actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
            # Persistent banning of IPs
            echo '<ip>' >> /etc/fail2ban/ip.blacklist

# Option:  actionunban
# Notes.:  command executed when unbanning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    See jail.conf(5) man page
# Values:  CMD
actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>


2) Create

# Fail2Ban custom configuration file.


# "ignoreip" can be an IP address, a CIDR mask or a DNS host
ignoreip =

# Ban forever => -1

# Ban 3 days => 259200
bantime = 259200 

# A host is banned if it has generated "maxretry" during the last "findtime" seconds.
findtime = 30

banaction = iptables-allports-CUSTOM

enabled = true
filter = sshd
logfile = /var/log/auth.log
maxretry = 3

3) Remove the default debian jail configuration (is integrated in the above custom jail.local file):

rm -fย /etc/fail2ban/jail.d/defaults-debian.conf

4) Set this cron:

# Daily rotate of ip.blacklist
0 20 * * * tail -100 /etc/fail2ban/ip.blacklist | sort | uniq > /tmp/ip.blacklist ; cat /tmp/ip.blacklist > /etc/fail2ban/ip.blacklist ; rm -f /tmp/ip.blacklist > /dev/null 2>&1

5) Run the cron manually once, just to be sure all works AND to have an empty file

6) Restart Fail2ban … and good luck ๐Ÿ˜‰



Automatic Updates on Raspberry Pi

How to configure automatic updates on your raspberry pi and make sure it reboots in the night (if required)

apt-get install unattended-upgrades apt-listchanges

sed -i 's/^\/\/      "o=Raspbian,n=jessie"/      "o=Raspbian,n=jessie"/g' /etc/apt/apt.conf.d/50unattended-upgrades
sed -i 's/^\/\/Unattended-Upgrade::Mail "root";/Unattended-Upgrade::Mail "root";/g' /etc/apt/apt.conf.d/50unattended-upgrades
sed -i 's/^\/\/Unattended-Upgrade::Automatic-Reboot "false";/Unattended-Upgrade::Automatic-Reboot "true";/g' /etc/apt/apt.conf.d/50unattended-upgrades
sed -i 's/^\/\/Unattended-Upgrade::Automatic-Reboot-Time "02:00";/Unattended-Upgrade::Automatic-Reboot-Time "02:00";/g' /etc/apt/apt.conf.d/50unattended-upgrades

tee /etc/apt/apt.conf.d/20auto-upgrades > /dev/null <<EOF
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

Check the next day the log /var/log/unattended-upgrades/unattended-upgrades.log to see if it worked ๐Ÿ™‚


Source:ย here

Improve/Ubuntu-like Font Rendering in Debian using Infinality Font


#Infinality Fonts
deb http://ppa.launchpad.net/no1wantdthisname/ppa/ubuntu saucy main

apt-get install fontconfig-infinality

cd /etc/fonts/infinality/
bash infctl.sh setstyle

chose 3 (i.e. linux).

In /etc/profile.d/infinality-settings.sh --> search for โ€œUSE_STYLEโ€ or scroll (around line 710) till you see the option to set the style => USE_STYLE=โ€UBUNTUโ€

Settings -> Appearance.

    Tick the checkbox to Enable anti-aliasing
    Set Sub-pixel order to RGB
    Set Hinting to Slight


Personally installed before also: cabextract fonts-liberation ttf-mscorefonts-installer


Other fonts:

1) Install pretty fonts (you'll need non-free for mscorefonts): apt-get install ttf-dejavu ttf-liberation ttf-mscorefonts-installer xfonts-terminus
2) dpkg-reconfigure fontconfig-config, select Autohinter, Automatic and No
3) dpkg-reconfigure fontconfig
4) Restart Xorg
5) Gnome-specific (these are largely personal preferences): System -> Preferences -> Appearances -> Fonts: Enable 'Best Shapes', Details -> Dots per Inch: 110, Smoothing -> Grayscale


Source: http://linuxpanda.wordpress.com/2014/03/14/improve-ubuntu-like-font-rendering-in-debian-using-infinality-font/

Dynamic MOTD on Debian

Here a simple script that setup a dynamic MOTD message ‘ubuntu-like’ on Debian servers:


# Script to install Dynamic MOTD on Debian servers

# This version uses figlet

if [[ $EUID -ne 0 ]]; then
   echo "This script must be run as root or using sudo"
   exit 1

if [ ! -f /etc/debian_version ] ; then
   echo "This script can run ONLY on Debian OS"
   exit 1

# Install required packages:
# - figlet: ASCII art for hostname
# - lsb-release: get distro details (fallback)
apt-get install figlet lsb-release

# Backup MOTD file
mv -f /etc/motd{,.ORIG}

# Symlink dynamic MOTD file
ln -s /var/run/motd /etc/motd

# Create dynamic motd environment
mkdir /etc/update-motd.d/
cd /etc/update-motd.d/

cat <<'EOF' > 00-header
#    00-header - create the header of the MOTD
[ -r /etc/os-release ] && . /etc/os-release

if [ -z "$OS" ] && [ -x /usr/bin/lsb_release ]; then
        OS=$(lsb_release -s -d)

figlet -f slant $(hostname)
printf "\n"
printf "\t- %s\n\t- OS version %s\n\t- Kernel %s\n" "$OS" "$(cat /etc/debian_version)" "$(uname -r)"
printf "\n"

cat <<'EOF' > 10-sysinfo
#    10-sysinfo - generate the system information
load=`cat /proc/loadavg | awk '{print $1}'`
root_usage=`df -h / | awk '/\// {print $(NF-1)}'`
home_usage=`df -h /home | awk '/\// {print $(NF-1)}'`
memory_usage=`free -m | awk 'NR==2{printf "%.2f%%\n", $3*100/$2 }'`
swap_usage=`free -m | awk '/Swap/ { printf("%3.1f%%", "exit !$2;$3/$2*100") }'`
users=`users | wc -w`
time=`uptime | grep -ohe 'up .*' | sed 's/,/\ hours/g' | awk '{ printf $2" "$3 }'`
processes=`ps aux | wc -l`
ip=`ifconfig $(route -n | grep '^' | awk '{ print $8 }') | grep "inet addr" | awk -F: '{print $2}' | awk '{print $1}'`

printf "System information as of %s\n\n" "$date"
printf "IP Address:\t%s\tSystem uptime:\t%s\n" "$ip" "$time"
printf "System load:\t%s\tProcesses:\t%s\n" "$load" "$processes"
printf "RAM used:\t%s\tSwap used:\t%s\n" "$memory_usage" "$swap_usage"
printf "Usage on /:\t%s\tUsage on /home:\t%s\n" "$root_usage" "$home_usage"
printf "Local Users:\t%s\tProcesses:\t%s\n" "$users" "$processes"

cat <<'EOF' > 90-footer
#    90-footer - write the admin's footer to the MOTD
[ -f /etc/motd.tail ] && cat /etc/motd.tail || true

chmod +x /etc/update-motd.d/*

echo "Installation completed. Please logout and login again to test."


If you are running CentOS7, here the how to for that OS.

Banana Pi Pro – WLAN configuration

Add ‘ap6210‘ to /etc/modules to enable WiFi, and use modprobe ap6210 to force load the module.

Check dmesg to see if all has been loaded correctly. If not, reboot and check again.

dmesg|grep WLAN

Install the required packages:

apt-get install wireless-tools iw wpasupplicant

Modify /etc/network/interfaces

# Dinamic IP:
auto wlan0
allow-hotplug wlan0
iface wlan0 inet dhcp
wpa-ap-scan 1
wpa-scan-ssid 1
wpa-psk "WLAN-KEY"

# Static IP:
auto wlan0
allow-hotplug wlan0
iface wlan0 inet static
address 192.168.xx.yy
wpa-ap-scan 1
wpa-scan-ssid 1
wpa-psk "WLAN-KEY"

Bring the interface up:

ifconfig wlan0 up

Source: http://oyox.de/882-wlan-auf-bananian-banana-pi-einrichten/