Skip to content

Node OS Setup Guide BBG Debian 8

Matt Magoffin edited this page Aug 29, 2016 · 1 revision

SolarNode OS Setup Guide: Beagle Bone, Debian 8

This guide describes the steps I took to create a "minimal" Debian 8 based system with base SolarNode deployment configured on a Beagle Bone Green (BBG) computer. The overall goals were these:

  1. No X window system.
  2. No development tools.
  3. No daemons or servers unless required by Debian or SolarNode.
  4. SSH daemon for network access.
  5. NTP daemon for time synchronization.
  6. Boot from SD card.
  7. Java 8 JRE.

With these goals in mind, let's dive in. You'll need a Linux-based system to work with.

Note: Binary images for the SolarNode OS are also available here: http://sourceforge.net/projects/solarnetwork/files/solarnode/bbone These are great if you are after a quick OS setup (but they do not always contain the latest updates), if you want the latest and greatest you should continue reading below.

Download BBG Console Image

Download the latest BBG image from https://rcn-ee.com/rootfs/. Also download the associated .bmap file, so you can use the bmaptool program to more quickly copy the image to an SD card. The bmaptool program is available in the bmap-tools package.

Once you have everything downloaded, copy the image to a SD card, for example:

	bmaptool copy bone-debian-8.2-console-armhf-2016-01-14-2gb.img.xz /dev/sdb

Boot the BBG

Insert the SD card into the BBG, connect an ethernet cable, and power on the board. Eventually the system will be available via SSH, using the credentials debian / temppwd. You probably need to consult your router/DHCP server to find out the IP address of the board.

Post install tasks

  1. Create solar user, with solar password:

     useradd -c 'SolarNode' -s /bin/bash -G dialout,sudo -m -U solar
     passwd solar
    
  2. Edit /etc/apt/sources.list to:

  3. Replace us domain mirrors with nz.

  4. Add deb http://ftp.nz.debian.org/debian jessie-backports main to support Java 8.

  5. Run apt-get update.

  6. Make /tmp mounted as a tmpfs (RAM) filesystem with:

     systemctl enable tmp.mount
     rm -rf /tmp/*
     systemctl start tmp.mount
    
  7. Disable persistent history in bash, by editing /etc/bash.bashrc and adding

     unset HISTFILE
    

    You'll need to log out, then log back in and then delete the .bash_history files that exist for the debian and root users.

  8. Set local time zone:

     timedatectl set-timezone Pacific/Auckland
    
  9. Set the hostname in /etc/hostname to solarnode.

  10. Delete the debian user:

     userdel -r debian
    

Software setup

Now I manually removed and added the software I deemed appropriate for the node.

  1. Disable recommended and suggested packages by default, by creating a new file /etc/apt/apt.conf.d/99-norecommend with:

     APT::Install-Recommends "0";
     APT::Install-Suggests "0";
    
  2. Replace rsyslog with busybox-syslogd, to minimize writing to the SD card:

     apt-get remove --purge rsyslog
     apt-get install busybox-syslogd
    
  3. Install localepurge to remove excess locale data:

     apt-get install localepurge
    
  4. Configure systemd-timesyncd

  5. Edit /etc/systemd/timesyncd.conf and uncomment the SERVERS= line, optionally editing the default server list.

  6. Run timedatectl set-ntp true

  7. Remove docs... any anything else you can, like exim, gcc, perl, python, etc.

     apt-get remove --purge info manpages
    
  8. Install Oracle Java. OpenJDK on ARM, unfortunately, uses the Zero runtime which is painfully slow. This requires a whopping amount of space:

     wget --header "Cookie: oraclelicense=accept-securebackup-cookie" \
     http://download.oracle.com/otn-pub/java/jdk/8u65-b17/jdk-8u65-linux-arm32-vfp-hflt.tar.gz
    
     mkdir /opt/jdk
     tar -xC /opt/jdk -f jdk-8u65-linux-arm32-vfp-hflt.tar.gz
     update-alternatives --install /usr/bin/java java /opt/jdk/jdk1.8.0_65/bin/java 100
    

    Note the URL will change as new releases are put out. Find the URL for the release at the Java download site. Adjust the listed commands as necessary to match the downloaded version.

  9. Install rsync (required for node database backups)

     apt-get install rsync
    
  10. Install RXTX Java libraries, to support serial ports in Java:

     apt-get install librxtx-java
     ln -s /usr/share/java/RXTXcomm.jar \
     /opt/jdk/jdk1.8.0_65/jre/lib/ext/RXTXcomm.jar
    

    See the RXTX setup guide for more details.

Further savings can be found by installing the deborphan and debfoster packages. Use those to identify non-essential packages and remove them.

  1. Install YASDI libraries, to support SMA inverters. Follow the YASDI guide.

  2. Add telnet via apt-get install telnet.

Install rng-tools for faster entropy gathering

The SolarNode application can get stuck starting up if /dev/random does not have enough entropy added to it. Found the rng-tools package which can increase the available entropy and speed startup times via the rngd daemon:

apt-get install rng-tools

Unfortunately, I found that rng-tools does not provide a native systemd unit file, and when started via the SysV init compatibility feature of systemd, started before the /dev/hwrng device was available and promptly crashed. Thus I created a manual unit file in /lib/systemd/system/rng-tools.path with:

[Unit]
Description=Hardware RNG Device Check

[Path]
PathExists=/dev/hwrng

[Install]
WantedBy=paths.target

And then enable via:

systemctl enable rng-tools.path
systemctl enable rng-tools
systemctl start rng-tools.path

Add udev rule file to remove MAC address associations

The Debian installer will have set up a udev rule that associates the ethernet and WiFi devices with persistent device names, eth0 and wlan0, based on those devices' hardware MAC addresses. In order to make this system easier to clone onto other SD cards, I disabled the automatic generation of this file:

# Disable the automatic, MAC-based naming rules file generation
rm /etc/udev/rules.d/70-persistent-net.rules
ln -s /dev/null /etc/udev/rules.d/70-persistent-net.rules

Next I added a custom udev rules file /etc/udev/rules.d/a10-solarnode.rules with the following content:

# Rename network interfaces NOT using MAC addresses, so this image can be copied to other devices
SUBSYSTEM=="net", DRIVERS=="?*", KERNEL=="eth*", NAME="eth%n"
SUBSYSTEM=="net", DRIVERS=="?*", KERNEL=="usb*", NAME="usb%n"
SUBSYSTEM=="net", DRIVERS=="?*", KERNEL=="wlan*", NAME="wlan%n"

This will map ethernet devices to ethX and USB devices to usbX and WiFi to wlanX where X starts at 0. This thus provides the eth0 and usb0 and wlan0 network device names.

Use systemd-networkd for network configuration

Create /etc/systemd/network/eth.network with:

[Match]
Name=eth0

[Network]
DHCP=yes

[DHCP]
RouteMetric=10

Then to support WiFi, which will have lower precedence to the wired network, create /etc/systemd/network/wlan.network with:

[Match]
Name=wlan0

[Network]
DHCP=yes

[DHCP]
RouteMetric=20

Create unit /lib/systemd/system/wpa_supplicant@.service with:

[Unit]
Description=WPA supplicant daemon (interface-specific version)
Requires=sys-subsystem-net-devices-%i.device
After=sys-subsystem-net-devices-%i.device

[Service]
Type=simple
ExecStart=/sbin/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-%I.conf -i%I -Dwext

[Install]
Alias=multi-user.target.wants/wpa_supplicant@%i.service

Create WPA configuration /etc/wpa_supplicant/wpa_supplicant-wlan0.conf with:

network={
	ssid="ssid"
	#psk="passphrase"
	psk=2b1d17284c5410ee5eaae7151290e9744af2182b0eb8af20dd4ebb415928f726

	# if the SSID is hidden, add
	#scan_ssid=1
}

Then,

systemctl enable systemd-networkd
systemctl enable wpa_supplicant\@wlan0
systemctl enable systemd-resolved
systemctl start systemd-networkd
systemctl start wpa_supplicant\@wlan0
systemctl start systemd-resolved

Now map resolve.conf to systemd-resolved:

rm /etc/resolv.conf
ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf

Finally, can remove ifupdown:

apt-get purge ifupdown

Note as the WPA settings are just placeholders, after testing the setup works the service was disabled:

systemctl disable wpa_supplicant\@wlan0

Add firewall and redirect port 80

The SolarNode web application runs on port 8080 by default, but we'd like to be able to access it via the standard HTTP port, 80. We can use iptables to both provide a firewall for the node as well as setup NAT to translate port 80 into 8080 for us. This is adapted from Arch Linux. In addition we will configure support for SSH password brute-force mitigation by dynamically blocking IP addresses after failed password login attempts via SSH.

First create a configuration file suitable for iptables-restore to read, at /etc/iptables/iptables.rules:

*filter

# Create chain for SSH brute-force password cracking mitigation
-N dropBrute

# Allows all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0
-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT

# Allow all established inbound connections
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Allow outbound traffic
-A OUTPUT -j ACCEPT

# Allows HTTP
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 8080 -j ACCEPT

# Allow SSH
-A INPUT -p tcp -m state --state NEW --dport 22 -j dropBrute
-A INPUT -p tcp -m state --state NEW --dport 22 -m limit --limit 6/min --limit-burst 6 -j ACCEPT

# Allow ping
-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT

# log iptables denied calls (access via 'dmesg' command)
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7

-A INPUT -j REJECT
-A FORWARD -j REJECT

COMMIT

*nat

# Redirect port 80 to 8080 for SolarNode
-A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080

COMMIT

Create /lib/systemd/system/iptables.service with:

[Unit]
Description=Packet Filtering Framework
DefaultDependencies=no
After=systemd-sysctl.service
Before=sysinit.target

[Service]
Type=oneshot
ExecStart=/sbin/iptables-restore /etc/iptables/iptables.rules
ExecReload=/sbin/iptables-restore /etc/iptables/iptables.rules
ExecStop=/lib/systemd/scripts/iptables-flush
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

Create /lib/systemd/scripts/iptables-flush with:

#!/bin/bash
#
# Usage: iptables-flush [6]
#

iptables=ip$1tables
if ! type -p "$iptables"; then
  echo "error: invalid argument"
  exit 1
fi

while read -r table; do
  tables+=("/var/lib/$iptables/empty-$table.rules")
done <"/proc/net/ip$1_tables_names"

if (( ${#tables[*]} )); then
  cat "${tables[@]}" | "$iptables-restore"
fi

Make it executable:

chmod 755 /lib/systemd/scripts/iptables-flush

Create /var/lib/iptables/empty-filter.rules with:

# Empty iptables filter table rule file
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT

Create /var/lib/iptables/empty-nat.rules with:

# Empty iptables nat table rules file
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT

Now enable the iptables service:

systemctl enable iptables
systemctl start iptables

You can check the filter and nat tables with:

iptables -L
iptables -L -t nat

Configure SSH password brute-force attack mitigation

Create a /usr/share/solarnode/drop-brute.sh script using the script on GitHub.

Make this executable:

chmod 755 /usr/share/solarnode/drop-brute.sh

Then add a file /etc/cron.d/drop-brute with the following content, to run the drop-brute.sh script every 2 minutes:

# SSH password brute-force mitigation
*/2 * * * * root /usr/share/solarnode/drop-brute.sh >/dev/null 2>&1

Configure systemd-tmpfiles

Create a memory-only location for application files under a /run/solar directory, by adding a /usr/lib/tmpfiles.d/solarnode.conf file with the following content:

# SolarNode tmpfile configuration

# Type Path Mode UID GID Age Argument

# Create primary work area
d /run/solar 0755 solar solar -

# For history compatibility, create symlink
L /run/shm/solar - - - - /run/solar

# Do not clean up any files in these areas
x /run/solar/*
x /run/shm/solar/*

Configure kernel to reboot on error

If the SolarNode process (or any process) becomes unresponsive, we can make the kernel panic and reboot by adding a file /etc/sysctl.d/solarnode.conf with the following content:

# Reboot 5 seconds after panic
kernel.panic = 5

# Panic if a hung task was found
kernel.hung_task_panic = 1

# Setup timeout for hung task to 300 seconds
kernel.hung_task_timeout_secs = 300

Delete SSH key and set up to get created at boot

If you're creating this as an image for many nodes to copy from, then you should delete the SSH key Debian generated for you, and add a small systemd unit to recreate the keys the next time the OS boots. First, delete the keys:

rm -f /etc/ssh/ssh_host_*

Then add a /lib/systemd/system/sshdgenkeys.service unit file with:

[Unit]
Description=SSH Key Generation
ConditionPathExists=|!/etc/ssh/ssh_host_key
ConditionPathExists=|!/etc/ssh/ssh_host_key.pub
ConditionPathExists=|!/etc/ssh/ssh_host_rsa_key
ConditionPathExists=|!/etc/ssh/ssh_host_rsa_key.pub
ConditionPathExists=|!/etc/ssh/ssh_host_dsa_key
ConditionPathExists=|!/etc/ssh/ssh_host_dsa_key.pub
ConditionPathExists=|!/etc/ssh/ssh_host_ecdsa_key
ConditionPathExists=|!/etc/ssh/ssh_host_ecdsa_key.pub
ConditionPathExists=|!/etc/ssh/ssh_host_ed25519_key
ConditionPathExists=|!/etc/ssh/ssh_host_ed25519_key.pub

[Service]
ExecStart=/usr/bin/ssh-keygen -A
Type=oneshot
RemainAfterExit=yes

Create /etc/systemd/system/ssh.service.d/local.conf to add dependency on sshdgenkeys.service:

[Unit]
Wants=sshdgenkeys.service
After=network.target auditd.service sshdgenkeys.service

Note the Wants line was added, while the After line was edited to add sshdgenkeys.service. Finally, reload the configuration:

systemctl daemon-reload

Modify login banners

Remove unnecessary info from /etc/motd and /etc/issue.net.

Clean Up

As a final clean up to free space, run apt-get clean. The sfill program, available in the secure-delete package, can help make compressed images created with dd smaller by forcing all unused blocks to 0. Run sfill -f -z -I -ll / to zero out all unused space. This can take some time to run.

Install base SolarNode platform

Now we'll deploy a basic SolarNode platform, and configure it to startup when the node boots. See Deploying the SolarNode application for more information.

Clone this wiki locally