Skip to content

Node OS Setup Guide Debian 7

Matt Magoffin edited this page Oct 22, 2019 · 2 revisions

SolarNode OS Setup Guide Debian 7

⚠️ WARNING: this is a very old guide, meant for advanced developers. If you are looking to get started with a SolarNode device, there are pre-built OS images for many popular devices.

This guide describes the steps I took to create a "minimal" Debian 7 based system with base SolarNode deployment configured on an eBox 3300 (NorhTec JrMX) 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. Monit daemon for SolarNode monitoring.
  7. Boot from SD card.
  8. Java 7 JRE.

With these goals in mind, let's dive in. You'll need a Linux-based system to work with, I used a Fedora system but any modern distribution should do.

Note: Binary images for the Node OS are also available here: http://sourceforge.net/projects/solarnetwork/files/solarnode 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 Debian 7 and copy to USB stick

Download the Debian 7 (wheezy) installer CD. This should be the full 650MB CD, not the netboot image, so it can install the base system without needing the network. You can get the image from

http://www.debian.org/CD/http-ftp/#stable

Once you have the .iso image downloaded, use unetbootin to copy the image to a USB stick. This is a GUI tool that will copy the installer image to the USB stick and make sure it can boot (in theory dd can be used as well, if you're comfortable with that program already). Modern Linux distributions provide unetbootin, e.g. apt-get install unetbootin on Debian-based systems or yum install unetbootin on Fedora-based ones.

Insert your USB stick into your system, run unetbootin as root, and select the Debian .iso image file you downloaded. After a little while, it will complete and offer to reboot your system, which you can decline and then remove the USB stick.

Boot into the Debian installer

Insert the Debian installer USB stick into the USB port on the back of the eBox and a 512MB or larger SD card into the SD slot. You'll need a monitor and keyboard (USB or PS/2) plugged in as well, but no mouse is required. Turn the node on, and quickly press the Delete key to enter the BIOS setup utility.

Make sure the BIOS boots from the USB stick first, under the Boot menu. The BIOS should list both the USB and SD disks, as USB: and HDD: options, respectively.

Next go to the Advanced > IDE Configuration screen and make sure the Standard IDE Compatible setting is set to Enabled.

Now you can exit the BIOS configuration - F10 to save and exit - and the unetbootin boot menu should appear. Choose the Expert install mode, and let the Debian installer load.

Debian install options

First I manually partitioned and formatted an ext4 filesystem, using a guide. I found the estimated block size of my SD card to be 4MB, so I halved all the settings from the guide:

  • First sector: 16384 (8M)
  • Size: 952M (divisible by 8M and 4M)
  • File system: mkfs.ext4 -O ^has_journal -E stride=2,stripe-width=512 -b 2048 -L SOLARNODE /dev/mmcblk0p1

Then here are some important options I chose during the Debian install:

  1. DHCP for network configuration, with solarnode.localdomain as the host name.
  2. No root user, with a single solar user.
  3. No NTP time synchronization. The installer will install ntpd but we want to install openntpd manually later, which is smaller and easier to manage.
  4. Partition the SD card with a single ext4 filesystem, with a label SOLARNODE and the noatime option enabled. Also mark this partition bootable. If your card is larger than 512MB, you can create a partition sized smaller than the full SD card, so the partition can be cloned more easily. I created a 1GB partition (the SD card was 2GB).
  5. No swap partition. This is to minimize wear on the SD card.
  6. Select the most recent available i386 kernel.
  7. Configure a local apt mirror, without the non-free and without the contrib repositories used (they are not needed).
  8. No to adding/selecting software now. We'll do this manually later.
  9. Install the Grub boot loader.

At this point, the installer should complete. You can reboot the node, but un-plug the USB stick before the BIOS RAM check completes, so the node boots from the newly minted SD card.

Post install tasks

  1. Edit /etc/fstab to:

  2. Change the options for root filesystem to noatime,nodiratime,errors=remount-ro, it should look like this:

       LABEL=SOLARNODE / ext4 noatime,nodiratime,errors=remount-ro 0 1
    
  3. Add a manual entry for /run/shm. Debian provides this by default, but adds the noexec flag, which causes bundles with native libraries in them to fail to load. Add an entry like this:

       tmpfs /run/shm tmpfs rw,nosuid,nodev,exec,relatime,size=102860k 0 0
    
  4. Edit /etc/apt/sources.list to:

  5. Remove the cdrom entry (which is really the USB stick).

  6. Remove all -src sources.

  7. Run apt-get update.

  8. Add solar user to dialout group, to allow use of serial ports.

     usermod -a -G dialout solar
    
  9. Edit /etc/default/tmpfs and set RAMTMP=yes to enable /tmp as a RAM filesystem.

  10. Disable persistent history in bash, by editing /etc/bash.bashrc and adding

     unset HISTFILE
    

Software setup

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

  1. Replace rsyslog with busybox-syslogd, to minimize writing to the SD card:

     apt-get remove --purge rsyslog
     apt-get install busybox-syslogd
    
  2. Install localepurge to remove excess locale data:#!sh

     apt-get install localepurge
    
  3. Install monit

  4. apt-get install monit

  5. Configure /etc/default/monit to enable, e.g. START=yes

  6. Configure /etc/monit/monitrc with startup delay, e.g. set daemon 120 with start delay 240; change the statefile, idfile, and eventqueue basedir paths to start with /var/run instead of /var/lib, e.g.

    set idfile /var/run/monit/id set statefile /var/run/monit/state set eventqueue basedir /var/run/monit/events

  7. Add /etc/init.d/monit-prep.sh script that makes sure /var/run/monit exists at startup, as /var/run is a tmpfs filesystem.

       #!/bin/sh
       
       ### BEGIN INIT INFO
       # Provides:          monit-prep
       # Required-Start:    $remote_fs
       # Required-Stop:     $remote_fs
       # X-Start-Before:    monit
       # Default-Start:     2 3 4 5
       # Default-Stop:      0 1 6
       # Short-Description: prepare for monit
       ### END INIT INFO
       
       DIR=/var/run/monit
       EDIR=$DIR/events
       NAME=monit-prep
       
       if [ ! -d $DIR ]; then
       	mkdir -p $DIR
       	chmod 755 $DIR
       fi 
       
       if [ ! -d $EDIR ]; then
       	mkdir -p $EDIR
       	chmod 700 $EDIR
       fi 
    
       exit 0
    
  8. chmod 755 /etc/init.d/monit-prep.sh

  9. update-rc.d monit-prep.sh defaults

  10. Install sshd

  11. apt-get install ssh

  12. Add a Monit script to keep sshd running:

       check process sshd with pidfile /var/run/sshd.pid
          group system
          group sshd
          start program = "/etc/init.d/ssh start"
          stop  program = "/etc/init.d/ssh stop"
          if failed host localhost port 22 with proto ssh then restart
          if 5 restarts with 5 cycles then timeout
          depend on sshd_bin
          depend on sftp_bin
          depend on sshd_rc
    
       check file sshd_bin with path /usr/sbin/sshd
          group sshd
          include /etc/monit/templates/rootbin
    
       check file sftp_bin with path /usr/lib/openssh/sftp-server
          group sshd
          include /etc/monit/templates/rootbin
    
       check file sshd_rc with path /etc/ssh/sshd_config
          group sshd
          include /etc/monit/templates/rootrc
    
  13. Install OpenNTPD

  14. apt-get install openntpd

  15. Edit /etc/default/openntpd and add -s to the boot parameters. This will allow NTP to adjust the clock when the system starts up, even if the time difference is very large.

  16. With the -s flag, if NTP can't connect to the network it can prevent the node from booting for a long time. Instead of letting the OS start OpenNTP, we can let Monit do this later on. Run update-rc.d openntpd remove to remove it from system startup, then create a Monit script /etc/monit/conf.d/openntpd with the following:

       check process openntpd matching /usr/sbin/ntpd
       	group system
       	group ntpd
       	start program = "/etc/init.d/openntpd start"
       	stop  program = "/etc/init.d/openntpd stop"
       	if 4 restarts within 12 cycles then timeout
       	depend ntpd_bin
       	depend ntpd_rc
    
       check file ntpd_bin with path /usr/sbin/ntpd
       	group ntpd
       	include /etc/monit/templates/rootbin
    
       check file ntpd_rc with path /etc/init.d/openntpd
       	group ntpd
       	include /etc/monit/templates/rootbin
    
  17. Remove nano and docs

     apt-get remove --purge nano info manpages
    
  18. Install Java (this pulls in a whopping 160MB+ of stuff) with apt-get install openjdk-7-jre-headless

  19. Install rsync (required for node database backups)

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

     apt-get install librxtx-java libjna-java
    
  21. Install daemon package, which is used to manage the SolarNode process:

    apt-get install deamon
    

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

WiFi configuration

If you plan to use WiFi, you'll need to install some firmware for the radio and install the wpasupplicant package.

  1. apt-get install wpasupplicant

  2. You need the rtl8712u.bin firmware file, which is available in git. You can clone this repository via

     git clone git://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git
    

    Then copy the linux-firmware/rtlwifi/rtl8712u.bin file to /lib/firmware/rtlwifi/rtl8712u.bin.

  3. Configure a wlan0 device in /etc/network/interfaces to match your network settings, e.g.

     # WiFi
     auto wlan0
     iface wlan0 inet dhcp
       wpa-ssid mywifi
       # hexadecimal psk, use wpa_passphrase to generate
       wpa-psk c0c5618c9d2efc45d02db5b460...
       # can enable debugging with:
       # wpa-debug-level 3
    

Use filesystem labels instead of device UUIDs in boot configuration

The Debian installer will have configured both fstab and Grub to use device UUIDs to specify which device to use for the root filesystem. In order to make this system easier to clone onto other SD cards, I tweaked some of the settings so that the SOLARNODE filesystem label I set up during installation is referenced instead.

  1. Update /etc/fstab to use labels instead of UUID:

  2. For the root filesystem, change UUID=XXX to LABEL=SOLARNODE, e.g.

       LABEL=SOLARNODE / ext4 noatime,nodiratime,errors=remount-ro 0 1
    
  3. Update Grub to use labels instead of UUIDs. See http://ubuntuforums.org/showpost.php?p=9585951&postcount=13 for reference.

  4. Change /etc/default/grub so that GRUB_DISABLE_LINUX_UUID="true"

  5. Change /etc/grub.d/10_linux to insert this line

       linux_root_device_thisversion="LABEL=`e2label ${GRUB_DEVICE_BOOT} 2>/dev/null`"
    

    directly before these lines (around line 117):

         cat << EOF
       	echo	'$message'
       	linux	${rel_dirname}/${basename} root=${linux_root_device_thisversion} ro ${args}
       EOF
    
  6. Change /usr/lib/grub/grub-mkconfig_lib to replace these lines (around line 153):

     # If there's a filesystem UUID that GRUB is capable of identifying, use it;
     # otherwise set root as per value in device.map.
     echo "set root='`${grub_probe} --device ${device} --target=drive`'"
     if fs_uuid="`${grub_probe} --device ${device} --target=fs_uuid 2> /dev/null`" ; then
       echo "search --no-floppy --fs-uuid --set ${fs_uuid}"
     fi
    

    to these lines:

     # If there's a filesystem UUID that GRUB is capable of identifying, use it;
     # otherwise set root as per value in device.map.
     echo "set root='`${grub_probe} --device ${device} --target=drive`'"
     if auto_label="`e2label ${device} 2>/dev/null`"; then
       echo "search --no-floppy --label ${auto_label} --set root"
     elif fs_uuid="`${grub_probe} --device ${device} --target=fs_uuid 2> /dev/null`" ; then
       echo "search --no-floppy --fs-uuid --set ${fs_uuid}"
     fi
    
  7. Change /boot/grub/device.map to the following:

     (hd0)	/dev/disk/by-label/SOLARNODE
    
  8. Run update-grub to regenerate /boot/grub/grub.cfg. Verify you ended up with a menu entry in /boot/grub/grub.cfg with lines similar to the following (note the --label SOLARNODE and LABEL=SOLARNODE parts):

     search --no-floppy --label SOLARNODE --set root
     echo	'Loading Linux 2.6.32-5-486 ...'
     linux	/boot/vmlinuz-2.6.32-5-486 root=LABEL=SOLARNODE ro  quiet
    

Add udev rule file to remove MAC address associations

The Debian installer will have set up a udev rule that associates the ethernet and 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 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="lan%n"
SUBSYSTEM=="net", DRIVERS=="?*", KERNEL=="wlan*", NAME="wlan%n"

This will map ethernet devices to lanX and WiFi to wlanX where X starts at 0. This thus provides the lan0 and wlan0 network device names.

Finally, edit /etc/network/interfaces to rename all references to eth0 to lan0. There should be only two, e.g.

# The primary network interface
allow-hotplug lan0
iface lan0 inet dhcp

Alternative udev rule file to support multiple ethernet devices

The previous udev rules will only work for the first ethernet device, unfortunately. If you must support multiple ethernet devices, you can take a slightly different approach, and create a /etc/udev/rules.d/lan-count.sh script containing:

#!/bin/sh
egrep '[^w]lan[0-9]+' /proc/net/dev |wc -l

Make sure it is executable, i.e. chmod 755 /etc/udev/rules.d/lan-count.sh. Then update the first line in /etc/udev/rules.d/a10-solarnode.rules so the contents look like this:

# Rename network interfaces NOT using MAC addresses, so this image can be copied to other devices
SUBSYSTEM=="net", DRIVERS=="?*", KERNEL=="eth*", PROGRAM="/etc/udev/rules.d/lan-count.sh", NAME:="lan%c"
SUBSYSTEM=="net", DRIVERS=="?*", KERNEL=="wlan*", NAME="wlan%n"

Remove extra console ttys

Free up RAM by disabling some extra console TTYs. Edit /etc/inittab and comment out the TTYs you don't need, e.g.

1:2345:respawn:/sbin/getty 38400 tty1
2:23:respawn:/sbin/getty 38400 tty2
#3:23:respawn:/sbin/getty 38400 tty3
#4:23:respawn:/sbin/getty 38400 tty4
#5:23:respawn:/sbin/getty 38400 tty5
#6:23:respawn:/sbin/getty 38400 tty6

Install ifplugd to manage network interfaces

Sometimes the networking on a node can be unreliable. Using ifplugd can help ensure the interface is running.

  1. apt-get install ifplugd
  2. Edit /etc/default/ifplugd, and update the INTERFACES setting, e.g. INTERFACES="lan0 wlan0"
  3. Check /etc/network/interfaces to ensure the ones managed by ifplugd don't have auto set, e.g. comment out (or remove) lines like #auto 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. First create a configuration file suitable for iptables-restore to read, at /etc/iptables.rules:

*filter

# 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 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

Then create an executable file /etc/network/if-pre-up.d/iptables with

#!/bin/sh
/sbin/iptables-restore </etc/iptables.rules

You can manually load these rules by executing this file directly, too.

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 script to recreate the keys the next time the OS boots. First, delete the keys:

rm -f /etc/ssh/ssh_host_*

Then, add the following to /etc/rc.local (before the final exit 0 line):

# Make sure SSH keys are generated if they don't exist
if [ -f /etc/ssh/sshd_config -a ! -f /etc/ssh/ssh_host_rsa_key ]; then
	dpkg-reconfigure openssh-server
fi

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