Make Your Own Private Bitcoin Node to Anonymize Transactions over Tor

This article has been updated to include instructions to update Tor to the latest version, as I discovered it was using a really old version in the repositories. There was also an addition to tell the systemd services to safely shutdown bitcoin when it is stopped. There has also been an amendment to the bitcoin.conf file I made. You can find the amendments in the Tor, Installing Bitcoin Core, and Autostart sections

Chances are you’ve heard of Bitcoin, the anonymous and secure cryptocurrency which has made waves over many years. One of the main issues I see is that people are trusting others to handle their transactions. So I set about purchasing a tiny Dell netbook with a measly Intel Atom CPU, 2GB of RAM, and a 240GB SSD to act as my primary wallet for cryptocurrency, and is more or less my bank. The laptop has a fully encrypted drive, and I back up the keys for my wallet and have them in three different places. However when you are running a full version of the core wallets, that means you have to store a whole copy of the entire block chain on the device using it. Currently the Bitcoin blockchain is around 200GB if I recall, and that’s a lot of data to hold onto just to transact. Not to mention the whole idea of my netbook was to only be on when I needed to transact as it’s most secure when it is powered off. So obviously running the blockchain on the laptop was not the most ideal option as it would always have to be on. Not to mention I want to further anonymize the connections coming in and out, so I wanted to tunnel all of the traffic for the node over a VPN such as Private Internet Access, with a VPN killswitch so if the VPN doesn’t work it won’t connect, as well as bolt The Onion Relay (TOR) on top of it to further anonymize the entire transactions. The advantage of all this will be to allow any device on my LAN to transact with the blockchain network directly using my node to send and receive my transactions rather than trusting other people. The other advantage this has is since I am running a full copy of the blockchain I am also helping support the Bitcoin network by providing another peer with a full copy of the blockchain. However this guide will not cover the VPN aspect, but we will cover how to bolt on Tor as well as build your own node entirely.

I originally got the idea from pinode.co.uk where they have a lot of these projects, and I am already running a Monero node on a Pi 3B+ with a 128GB flash drive. But for this tutorial I am merging the ideas from pinode, along with the Thundroid tutorial, and adding some of my own twists and spins. I chose an Odroid Home Cloud 2 as it allows for a native SATA hard drive, and the HC2 variant allows for 3.5″ disks where as the HC1 allows for 2.5″ disks. Either version is fine, and if you really wanted you could probably go for the straight XU4 version, or even a Raspberry Pi 3B+ if you use a 512GB flash drive or larger, or a kit to allow additional drives. But for the sake of this tutorial we will be discussing the Odroid platform, however you can use whatever platform you like. Technically if you wanted to, you could use a full dedicated PC, but that seems like a waste of hardware and will be far less power efficient. I prefer the Odroid over the Raspberry Pi as it’s a more powerful hardware platform.

Hardware (all links are to Ameri Droid as I am in the USA):

Odroid Home Cloud 2 with RTC battery, 1TB Seagate Iron Wolf HDD, and Model 3 Wifi NIC.

The reason I specified using a NAS drive is that this drive will be on 24/7 and always writing as well as reading data. NAS drives are specifically optimized for this kind of behavior, and will therefore be more reliable. You can use a non-NAS drive just fine, but in the long term a NAS drive is best.

Odroid HC2 pictured in the clear acryllic case option with the wireless NIC inserted, and 16GB flash card before it was flashed with the Ubuntu 18.04.1 OS

Optional Hardware:

Odroid HC2 being worked on at my desk with the UART connector kit pictured.

First things first we have to connect it to the internet. So if you are planning on using wifi please follow the wiki here for nmcli for the Odroid. If using the UART console connection follow this tutorial here. You will need to flash Ubuntu 18.04 minimal image which can be downloaded here and then use Etcher to flash it to a MicroSD. Once that is down put it in the Odroid, and boot it up and either SSH or connect via console to it. Either way the credentials on start are:
username: root
password: odroid

For Raspberry Pi users you will have to look up the credentials for the image you are using.

Prep-Work

We’ll need to take care of some things first before we actually make it a Bitcoin node. So first let’s create a new user with a secure password and superuser rights and change the root password. Don’t forget to change “USER” to what you want.

[email protected]:~# passwd
[email protected]:~# adduser USER
[email protected]:~# usermod -aG sudo USER
[email protected]:~# adduser bitcoin

Now we need to update the system and change the timezone and locale data, as well as change the hostname in both /etc/hosts and /etc/hostname to match. I named mine “btcdroid” but you can make it whatever you want:

[email protected]:~# apt update
[email protected]:~# apt dist-upgrade -y
[email protected]:~# apt install htop git curl bash-completion jq
[email protected]:~# dpkg-reconfigure tzdata
[email protected]:~# dpkg-reconfigure locales
[email protected]:~# nano /etc/hosts
[email protected]:~# nano /etc/hostname

Mount the Hard Drive

Now we need to mount the hard drive. In my case the hard drive was brand new and unformatted, so I had to do that first, you can follow the instructions here at Digital Ocean if you are in the same situation. Regardless, once you have a formatted drive compatible with Linux we can proceed.

We will need to get the UUID of the partition that has been created. To do that is very simple we run the lsblk command and it will spit out the names and UUID of all drives.

[email protected]:~# lsblk --fs

After running that command you should see something like this. We will need to notate the UUID it has given us for the next steps.

Now we need to edit the fstab with nano and add a whole new line. Replace 123456 with the UUID given from the command above

[email protected]:~# nano /etc/fstab
# New Line in /etc/fstab
UUID=123456 /mnt/hdd ext4 noexec,defaults 0 0 

Awesome now the fstab has been modified, now we need to create the mount point, mount it, check it, and set the owner.

r[email protected]:~# mkdir /mnt/hdd
[email protected]:~# mount -a
[email protected]:~# df /mnt/hdd 

At this point if everything was done correctly you should see something similar to this.


Now let’s give permissions to that entire hard drive to the bitcoin user we made earlier

[email protected]:~# chown -R bitcoin:bitcoin /mnt/hdd/ 

Moving Swap to the HDD

Now we need to move the swap file to the HDD. So we need to install a package and then do some configuration changes.

[email protected]:~# apt install dphys-swapfile
[email protected]:~# nano /etc/dphys-swapfile
#Add the following lines
CONF_SWAPFILE=/mnt/hdd/swapfile
CONF_SWAPSIZE=2048
[email protected]:~# dphys-swapfile setup
[email protected]:~# dphys-swapfile swapon
[email protected]:~# shutdown -r now 

Hardening The Security

Now it should be reconfigured to have a 2GB swap file on the hard drive, and should be rebooting. At this point log back in as the regular user and not as root. Because we are about to disable root via SSH, however if you are using the optional UART serial connection kit, you can still login as root that way. Now let’s continue on and remove the old swap file.

SSH Hardening

We need to lock down remote access to SSH, and Digital Ocean has a great guide going over SSH security. I highly recommend disabling password logins and requiring an SSH key pair to be generated. You can read the tutorial here, but we will definitely need to be disabling root access as well. It’s a major security risk if root is allowed, as everyone knows Linux has a root user.

Type the following command to edit the sshd_config file.

[email protected]:~$ sudo nano /etc/ssh/sshd_config

#Find the following line PermitRootLogin yes #Change it to no so it looks like below PermitRootLogin no #Save and quit
[email protected]:~$ sudo service sshd restart

That will disable root login, but again I highly recommend making it only allow logins with SSH key pairs as it is far more secure than a password.

Firewall

So one of my favorite tools, which I have written about before is uncomplicated firewall. We are going to allow only pinholes for the firewall to allow communication through as well as limit ssh connections so it is less likely to be brute forced. We will also be adding some defenses for brute forcing in a bit.

The line ufw allow from 192.168.0.0/24, below assumes that the IP address of your btcdroid is something like 192.168.0.xxx, the xxx being any number from 0 to 255. If your IP address is 12.34.56.78, you must adapt this line to ufw allow from 12.34.56.0/24. Otherwise you will lock yourself out for good unless you connect the UART serial connection kit.

[email protected]:~$ sudo apt install ufw
[email protected]:~$ sudo ufw default deny incoming
[email protected]:~$ sudo ufw default allow outgoing

# make sure to use the correct subnet mask and IP ranges. (see warning above)
[email protected]:~$ sudo ufw allow from 192.168.0.0/24 to any port 22 comment 'allow SSH from local LAN'
[email protected]:~$ sudo ufw allow 9735 comment 'allow Lightning'
[email protected]:~$ sudo ufw allow 8333 comment 'allow Bitcoin mainnet'
[email protected]:~$ sudo ufw allow 18333 comment 'allow Bitcoin testnet'
[email protected]:~$ sudo ufw enable
[email protected]:~$ sudo systemctl enable ufw
[email protected]:~$ sudo ufw status

Now we should install Fail2Ban, which I have talked about often. This will make it so after five unsuccessful attempts at SSH it blocks the IP for ten minutes. Making a brute force almost impossible to conduct.

[email protected]:~$ sudo apt install fail2ban

Increase open file limit

In case your BTCDroid is swamped with internet requests (honest or malicious due to a DDoS attack), you will quickly encounter the can't accept connection: too many open files error. This is due to a limit on open files (representing individual tcp connections) that is set too low.

Edit the following three files, add the additional line(s) right before the end comment, save and exit.

[email protected]:~$ sudo nano /etc/security/limits.conf
#add/change the following lines
*    soft nofile 128000
*    hard nofile 128000
root soft nofile 128000
root hard nofile 128000

[email protected]:~$ sudo nano /etc/pam.d/common-session #add the following session required pam_limits.so
[email protected]:~$ sudo nano /etc/pam.d/common-session-noninteractive #add the following session required pam_limits.so

Installing Bitcoin Core

We’re finally ready to start with the fun parts. These parts were mostly derived from pinode.co.uk, but seem to work perfectly fine for the Odroid HC2, albeit with some tweaks we have already performed specific to the Odroid platform.

First we need to install our dependencies:

[email protected]:~$ sudo apt install autoconf libevent-dev libtool libssl-dev libboost-all-dev libminiupnpc-dev -y 

Now we need to make a directory to download our files into, and ultimately download those files using git

[email protected]:~$ mkdir ~/bin
[email protected]:~$ cd ~/bin
[email protected]:~$ git clone -b 0.17 https://github.com/bitcoin/bitcoin.git

Now after it’s downloaded we are going to configure, compile, and install the files. Now I tell it in the final command to run six jobs at the same time since the Odroid has eight cores so it can run faster. You may want to reduce that number to two with a Raspberry Pi. You can also run it without the “-jX” switch to just run as a single job, although that may take a couple hours. Once you run the make command, go make dinner or something because this will take an hour or two even on the Odroid XU4’s eight core Samsung Exynos 5422 CPU.

[email protected]:~$ cd bitcoin
[email protected]:~$ ./autogen.sh
[email protected]:~$ ./configure --enable-upnp-default --disable-wallet
[email protected]:~$ make -j6
[email protected]:~$ sudo make install

Now we need to prepare the Bitcoin directory, we’re going to switch into the non super user we created earlier which we named bitcoin, although you can name it whatever you want. The most important thing is that this user only have permissions to administrate the bitcoin node itself and not able to make any system changes. This is the great thing about Linux in regards to security and permissions versus Windows. This in theory should isolate an attack from at work messing with just the bitcoin systems and not the system itself.

We use the Bitcoin daemon, called “bitcoind”, that runs in the background without user interface and stores all data in a the directory /home/bitcoin/.bitcoin. Instead of creating a real directory, we create a link that points to a directory on the external hard disk.

[email protected]:~$ sudo su bitcoin

# add symbolic link that points to the external hard drive
[email protected]:~$ mkdir /mnt/hdd/bitcoin
[email protected]:~$ ln -s /mnt/hdd/bitcoin /home/bitcoin/.bitcoin

# Navigate to home directory and check the symbolic link (the target must not be red). 
[email protected]:~$ cd ~
[email protected]:~$ ls -la

Now we need to configure the Bitcoin daemon, and make sure to set an extremely secure password and username seperate from your username and password on the system, and then we will log out of the bitcoin user to setup Tor.

[email protected]:~$ nano /home/bitcoin/.bitcoin/bitcoin.conf

# BTCDroid: bitcoind configuration
# /home/bitcoin/.bitcoin/bitcoin.conf

# Bitcoind options
server=1
daemon=1
txindex=1
disablewallet=1

# Connection settings
rpcuser=SECURE_USERNAME
rpcpassword=SECURE_PASSWORD

# Optimizations for Odroid Hardware
dbcache=200 
maxorphantx=60
maxmempool=200
maxconnections=80
maxuploadtarget=5000



#Optimizations for Raspberry Pi 3B.
#I commented out the ones for the ones I recommend for a Raspberry Pi 3B, just uncomment those, and comment out the Odroid ones for it to work
#dbcache=100
#maxorphantx=10
#maxmempool=50
#maxconnections=40
#maxuploadtarget=5000

[email protected]:~$ exit

Tor IT Up

Now we get to install Tor to encapsulate all the traffic and encrypt as well as anonymize everything. So we are going to install Tor, but also add a repository to give us the most up to date Tor version, as the one in the default repositories is really old.

First we will be adding a couple entries to /etc/apt/sources.list.d/, add the GPG key to accept it, and then update our repository, and finally install Tor.

[email protected]:~$ sudo nano /etc/apt/sources.list.d/tor.list
#Add the following lines and then save and close
deb https://deb.torproject.org/torproject.org bionic main
deb-src https://deb.torproject.org/torproject.org bionic main
#save and exit
[email protected]:~$ curl https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | gpg --import
[email protected]:~$ gpg --export A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 | sudo apt-key add -
[email protected]:~$  apt update
[email protected]:~$  apt install tor deb.torproject.org-keyring tor-arm nyx

Now we need to configure Tor

[email protected]:~$ sudo nano /etc/tor/torrc
#add these settings to the bottom of the file
ControlPort 9051
CookieAuthentication 1
CookieAuthFileGroupReadable 1
HiddenServiceDir /var/lib/tor/bitcoin-service/
HiddenServicePort 8333 127.0.0.1:8333
#save and exit
[email protected]:~$ sudo systemctl restart tor.service
#Get your Tor hostname
[email protected]:~$ sudo cat /var/lib/tor/bitcoin-service/hostname

That hostname it spits out with a “.onion” address, we’re going to need that, so notate what it just gave us with the last command.

configure everything to autostart

Now we need to program everything to start on boot, so we will make a SystemD service that will start our Bitcoin node how we would like it to start with it running as the bitcoin user and passing it through to Tor. Now I will give you the option at this point to either have it run only on Tor, or to allow it to run over Tor, IPv4, and IPv6. The Tor only option is more anonymous, but the other mode is like a dual mode so if Tor is down it can still sync, but it also will sync faster. The choice is yours, just remove the comment for the one you want where it says ExecStart and don’t forget to insert your hostname.onion we pulled from earlier where it asks for it in the ExecStart command. After that we will reboot and see if everything works. Make sure to put your username we created earlier on where it says USER_NAME

[email protected]:~$ sudo nano /etc/systemd/system/bitcoind.service

# BTCdroid systemd unit for bitcoind
# /etc/systemd/system/bitcoind.service

[Unit]
Description=Bitcoin daemon
After=network.target

[Service]
#Uncomment the ExecStart string below to force the node to only run over Tor
#ExecStart= /usr/local/bin/bitcoind -datadir=/home/bitcoin/.bitcoin/data -daemon -proxy=127.0.0.1:9050 -externalip=HOSTNAME.onion -conf=/home/bitcoin/.bitcoin/bitcoin.conf -listen -bind=127.0.0.1 -pid=/run/bitcoind/bitcoind.pid

#Uncomment the ExecStart string below to allow Tor, IPv4, and IPv6 connections
#ExecStart= /usr/local/bin/bitcoind -datadir=/home/bitcoin/.bitcoin/data -daemon -proxy=127.0.0.1:9050 -externalip=HOSTNAME.onion -conf=/home/bitcoin/.bitcoin/bitcoin.conf -listen -discover -pid=/run/bitcoind/bitcoind.pid

#Tells Bitcoin to shutdown safely when stopped. 
ExecStop= /usr/local/bin/bitcoin-cli stop


# Creates /run/bitcoind owned by bitcoin
RuntimeDirectory=bitcoind
User=bitcoin
Group=bitcoin
Type=forking
PIDFile=/run/bitcoind/bitcoind.pid
Restart=on-failure

# Hardening measures
####################

# Provide a private /tmp and /var/tmp.
PrivateTmp=true

# Mount /usr, /boot/ and /etc read-only for the process.
ProtectSystem=full

# Disallow the process and all of its children to gain
# new privileges through execve().
NoNewPrivileges=true

# Use a new /dev namespace only populated with API pseudo devices
# such as /dev/null, /dev/zero and /dev/random.
PrivateDevices=true

# Deny the creation of writable and executable memory mappings.
MemoryDenyWriteExecute=true

[Install]
WantedBy=multi-user.target
#save and exit
[email protected]:~$ sudo systemctl enable bitcoind.service
[email protected]:~$ sudo shutdown -r now 
[email protected]:~$ mkdir /home/USER_NAME/.bitcoin
[email protected]:~$ sudo cp /home/bitcoin/.bitcoin/bitcoin.conf /home/USER_NAME/.bitcoin/
[email protected]:~$ sudo chown USER_NAME:USER_NAME /home/USER_NAME/.bitcoin/bitcoin.conf

Now it should be restarting so give it a minute and reconnect as the user we created in the beginning. It may take a few minutes for the node to get its first connections, and then it will start pulling in the blocks. You can check the status with the bitcoin-cli command.

[email protected]:~$ bitcoin-cli getblockchaininfo 

It should display something like this, and as long as the number of blocks is increasing every few minutes, it is running fine. Bare in mind this could take a few days as we need to download at least 200GB at the time of writing to be up to date with the block chain.

Output of bitcoin-cli

In addition to checking the status of the blockchain download, you can monitor the traffic over Tor with Nyx.

[email protected]:~$ sudo nyx 
Seeing the traffic via Tor on Nyx

Auto Update Security Patches

Since this is a device we are going to leave on and unattended most likely. It’s best we have it auto apply any of the security related patches that may be out there so it can maintain itself. So let’s enable the unattended-upgrades package and configure it. The first step brings up an interactive prompt, and then we proceed to editing the files.

[email protected]:~$ sudo dpkg-reconfigure --priority=low unattended-upgrades
[email protected]:~$ sudo nano /etc/apt/apt.conf.d/50unattended-upgrades
#modify these lines in the file to look like the following, although you can make it reboot whenever you want. Make sure there is a semicolon at the end of each line. You can uncomment the "${distro_id}:${distro_codename}-updates"; line if you want it to update non security related packages too

#near the top of the file
Unattended-Upgrade::Allowed-Origins {
        "${distro_id}:${distro_codename}";
        "${distro_id}:${distro_codename}-security";
        // Extended Security Maintenance; doesn't necessarily exist for
        // every release and this system may not have it installed, but if
        // available, the policy for updates is such that unattended-upgrades
        // should also install from here by default.
        "${distro_id}ESM:${distro_codename}";
//      "${distro_id}:${distro_codename}-updates";
//      "${distro_id}:${distro_codename}-proposed";
//      "${distro_id}:${distro_codename}-backports";
};

#below are spread out in the same file
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "02:30";
#save and exit 

That’s it, you’re all finished. Let me know what you think or if you have any improvements to the project. I may eventually be hosting these on a Supermicro server in my rack with a ZFS array next year.