Setting up a Bitcoin node
A short guide to setting up a Bitcoin node in Linux.

Setting up a Bitcoin node can be a bit daunting, especially considering the amount of disc space required and that the node needs to be always connected. However, once configured maintenance can be relatively hands-off. For more information about the minimum requirements please see here.

This tutorial will be split into two stages. One: configuring the server itself to be relatively secure and resilient against basic attacks and two: configuring the Bitcoin daemon on the server.

Stage one: securing the server

Let's get the system up to date and then configure the stateful firewall.

# yum upgrade
# yum install vim iptables-service

And we'll move SSH to a different port so we can reduce the number of login attempts considerably. As this is CentOS, SELinux will need to be informed of the change to allow the SSH daemon to bind to the new port.

# vim /etc/ssh/sshd_config
Set Port to 1234 or something non-standard
# semanage port -a -t ssh_port_t -p tcp 1234 
# systemctl reload sshd

And log back in using the new port to take a look at the network interfaces.

[user@local] $ ssh root@bitcoin -p 1234
$ ip addr 
Now let's understand the current network topology.
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0:  mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 92:53:fb:96:86:27 brd ff:ff:ff:ff:ff:ff
    inet 128.199.93.101/18 brd 128.199.127.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet 10.15.0.5/16 brd 10.15.255.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 2400:6180:0:d0::1f6:2001/64 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::9053:fbff:fe96:8627/64 scope link
       valid_lft forever preferred_lft forever

We can see there are two network interfaces - lo, the loopback interface, and eth0, the Internet facing interface. For the loopback (lo), it's assigned the address 127.0.0.1/8 (IPv4) and ::1/128 (IPv6). For the Ethernet (eth0), it has four addresses. The first two are the public and private IPv4 addresses and the second two are the public and private IPv6 addresses, respectively.

We won't be needing an networking within a private LAN so we'll remove the internal addresses from the list of routes.

# ip addr del 10.15.0.5/16 dev eth0 # ip addr del fe80::9053:fbff:fe96:8627/64 dev eth0

Next we'll enable a simple stateful firewall to prevent errant access to the box. Copy this to the root directory and use `iptables-restore < iptables` to use it. Make sure you set the correct SSH port as you'll be needing it to log into the box.

 # iptables IPv4 simple config (bitcoin node)
 # v0.0.1
 # use at your own risk
 *filter
 # 1. Basics, loopback communication, ICMP packets, established connections
 -A INPUT -i lo -j ACCEPT
 -A INPUT -p icmp --icmp-type any -j ACCEPT
 -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
 # 2. Ensuring connections made are valid (syn checks, fragments, xmas, and null packets)
 -A INPUT -p tcp ! --syn -m state --state NEW -j DROP
 -A INPUT -f -j DROP
 -A INPUT -p tcp --tcp-flags ALL ALL -j DROP
 -A INPUT -p tcp --tcp-flags ALL NONE -j DROP
 # 3. Connections for various services, including SSH and Bitcoin
 -A INPUT -p tcp -m conntrack --ctstate NEW --dport 5555 -j ACCEPT
 -A INPUT -p tcp -m conntrack --ctstate NEW --dport 8333 -j ACCEPT
 -A INPUT -p tcp -m conntrack --ctstate NEW --dport 18333 -j ACCEPT
 #4. Log problems and set default policies for anything else
 -A INPUT -j LOG --log-level 7 --log-prefix "iptables dropped: "
 -P OUTPUT ACCEPT
 -P FORWARD DROP
 -P INPUT DROP
 COMMIT

Once loaded, make sure the iptables service starts on every boot.

 # yum install iptables-services
 # systemctl start iptables
 # systemctl enable iptables
 # iptables-restore < iptables
 # iptables -L

You should now see the policies enabled. Let's do the same for IPv6.

 *filter
 :INPUT DROP [0:0]
 :FORWARD DROP [0:0]
 :OUTPUT ACCEPT [0:0]
 -A INPUT -i lo -j ACCEPT
 -A INPUT -p ipv6-icmp -j ACCEPT
 -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
 -A INPUT -d fe80::/64 -p udp -m udp --dport 546 -m state --state NEW -j ACCEPT
 -A INPUT -p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -m state --state NEW -j DROP
 -A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,SYN,RST,PSH,ACK,URG -j DROP
 -A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG NONE -j DROP
 -A INPUT -p tcp -m conntrack --ctstate NEW -m tcp --dport 5555 -j ACCEPT
 -A INPUT -p tcp -m conntrack --ctstate NEW -m tcp --dport 8333 -j ACCEPT
 -A INPUT -p tcp -m conntrack --ctstate NEW -m tcp --dport 18333 -j ACCEPT
 -A INPUT -j LOG --log-prefix "ip6tables dropped: " --log-level 7
 -A INPUT -j REJECT --reject-with icmp6-adm-prohibited
 -A FORWARD -j REJECT --reject-with icmp6-adm-prohibited
 COMMIT

Good so far. Let's make these the default rules.

# iptables-restore > /etc/sysconfig/iptables
# ip6tables-restore > /etc/sysconfig/ip6tables

Stage two: configuring the Bitcoin node

Now, let's get started with configuring the Bitcoin node. Begin by creating a local user account you'll use to manage the service from now on.

 # adduser user
 # passwd user
 # gpasswd -a u er wheel
 # visudo // check that wheel is enabled on Centos

Login as the user and download and configure Bitcoin.

$ curl -O https://bitcoin.org/bin/bitcoin-core-0.15.1/bitcoin-0.15.1-x86_64-linux-gnu.tar.gz
$ curl -O https://bitcoin.org/laanwj-releases.asc
$ curl -O https://bitcoin.org/bin/bitcoin-core-0.15.1/SHA256SUMS.asc
$ gpg --quiet --with-fingerprint laanwj-releases.asc
$ gpg --import laanwj-releases.asc
$ gpg --verify SHA256SUMS.asc

The blockchain will be stored on an attached 250GB storage drive. We'll mount it, format it, and configure it for hosting the blockchain. Additionally, we'll add it to fstab so it is attached at boot.

$ sudo mkfs.ext4 -F /dev/disk/by-id/scsi-01
$ sudo mkdir -p /mnt/xbt-blockchain
$ sudo mount /dev/disk/by-id/scsi-01 /mnt/xbt-blockchain
$ sudo chown user:user /mnt/xbt-blockchain
$ echo '/dev/disk/by-id/scsi-01 /mnt/xbt-blockchain ext4 defaults 0 0' | sudo tee -a /etc/fstab

Next, we'll configure bitcoin.conf to starting the daemon on the testnet first.

 $ tar xf bitcoin-0.15.1-x86_64-linux-gnu.tar.gz ~/
 $ touch /mnt/xbt-blockchain/bitcoin.conf
 $ vim /mnt/xbt-blockchain/bitcoin.conf

 # bitcoin.conf
 # v0.0.1
 # Use at your own risk
 listen=1
 server=1
 rpcport=8332
 rpcallowip=127.0.0.1
 listenonion=0
 maxconnections=16
 datadir=/mnt/xbt-blockchain
 testnet=1
 disablewallet=1
 # if low on memory
 dbcache=20
 maxmempool=300

Let's test the configuration.

$ ~/bitcoin-0.15.1/bin/bitcoind -datadir=/mnt/xbt-blockchain &
$ ~/bitcoin-0.15.1/bin/bitcoin-cli -datadir=/mnt/xbt-blockchain
> uptime

Everything should be looking good at this point. Now, let's enable the daemon to connect to mainnet. Set the testnet=1 boolean to 0 in the bitcoin.conf file and restart the daemon.

Congratulations -- you've configured a full node. It will take a while to sync.