(λ (x) (create x) '(knowledge))

Building an Alpine NAS

Abandoning RHEL's Ship ยท July 16, 2020

For a few years I've been running a RHEL server for my bulk storage, and as a small home hypervisor. It started as RHEL7, upgraded to RHEL8, and honestly has been a wonderful experience to administer in a lot of ways. Red Hat goes well out of their way to provide a smooth experience.

However after the server being offline for months thanks to my recent move, and my RHEL subscription running out, I can't apply security updates or much of anything. Of course the system will keep running along just fine without those, but I don't think I can bring myself to just sit back and accept that. So it's good bye RHEL, hello more Alpine! As if I need even more excuses to run Alpine systems.

Amusingly enough that server was only truly hosting a few Alpine virtual machines for package maintenance/system development, and dockerized services, which were almost all Alpine based as well. In reality I barely used the "RHEL" components, outside of the fantastic Selinux configuration that's provided out of the box.

After the base installation I enabled the edge main/community repos, just to get access to some of the packages I maintain and dived right in. Right out the gate we need to install the basics.

#Update the repo, and the base install
apk update
apk upgrade

#Grab basic sysop tools
apk add shadow coreutils util-linux htop bmon fennel emacs clamd

#Grab Samba, Docker, and MD
apk add docker samba mdadm

All I truly need to get going in this list is emacs, docker, samba, and mdadm. My primary goal being to setup a RAID1 array on two 4TB discs, mount the resultant array to /data, and serve the file system using Samba. That entails user configuration, service configuration, monitoring configuration. Quite a bit. And on top of all of that I'm moving about a terabyte of data from the old array. So once the actual file system is rebuilt I've got sort and move all of the old data, make it compliant with the permissions I decide will be on the new file system. It all seems to get overwhelming pretty quickly, but in honesty it's pretty cut and dry!

Firstly I'll ensure that I have the RAID1 modules loaded and enabled when the kernel comes up.

#Load raid1 Module
modprobe raid1

#Enable at start
echo raid1 | tee -a /etc/modules

Once we've loaded the kernel modules we can create the array. mdadm makes the entire process painless, we're really just passing our two discs to it, and then creating a file system on the resulting /dev/md0 array.

#Build raid array
mdadm --create --level=1 --raid-devices=2 /dev/md0 /dev/sda1 /dev/sdb1
#You can check this process with watch "cat /proc/mdstat" if you're curious

#Persist new configuration
mdadm --detail --scan >> /etc/mdadm.conf

#Create file system
mkfs.ext4 /dev/md0

rc-service mdadm-raid start
rc-update add mdadm-raid

After that I can create my users, I'll just do me for now, but this should apply for all of my users in general.

#Create users
adduser -h /home/wsinatra -s /bin/ash wsinatra

#Create drive groups
groupadd civis

#add users to groups
usermod -a -G civis wsinatra

Instead of trying to control directories per user, since this is just a home NAS, I went ahead and configured a catch all group. The Civis user group will be applied to all directories and files in the Samba drive, and anyone in that group will have read and write access to those files.

comment = NAS
path = /data
browseable = yes
writeable = yes
valid users = @civis
force group = civis
read only = no
create mask = 0644
force directory mode 0775

#enable and start the services
rc-service samba start
rc-update add samba

#create a samba credentials
smbpasswd -a wsinatra

To explain this a little bit, enabling a mask of 0644 ensures that all files are served to anyone in the civis group as -rw--r--r--, and any directory as rwxrwx-rx. But the real magic happens with the group settings. When we set force group = civis, then any files created by clients will be automatically owned by the user & the civis group. That's to say if I access the NAS and create a file it'll show up as wsinatra:civis, if johnny does, it'll still show up as johnny:civis, and we can read and write both of those files. If you want tighter control than that you can create different directory shares and assign them to various groups, or any other access paradigm you wish.

Now since I'm migrating things that means I've got data to deal with. Fortunately sorted data, but lacking permissions for the new array. Once everything is copied over it's a simple process to fix things up.

#change the owners of the new NAS directory
chown -R root:civis /data

#Set file perms
find /data/ -type f -exec chmod 0644 {} \;

#Set directory perms
find /data/ -type d -exec chmod 0775 {} \;

Once we've set the permissions we should technically be done! Everything should work just fine as intended, but there's a couple of simple Quality of Life tweaks we can make to our new little NAS. Like a quick MoTD heads up system, auto updates, and array monitoring. There's plenty of ways to configure all of these, and below isn't what I would call an "enterprise" solution, but it's a great stop gap until I can get a Nagios container together.

#Add altering dependencies
apk add msmtp mailx

#Create monitor script
mdadm --monitor --mail=email-you-read@whatever.com --syslog --delay=600 /dev/md0 --daemonize

#Set to start at boot in crontab
@reboot sleep 300 && /usr/local/bin/raid-mon.sh

#Configure /etc/msmtprc
auth on
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
syslog on

account alerting
host smtp.gmail.com
port 587
from alerting-email@gmail.com
user alerting-email@gmail.com
password $(gmail-app-password)

account default: alerting
aliases /etc/aliases

#Configure Email Aliases
root: alerting-email@gmail.com
alerting: alerting-email@gmail.com
defaults: alerting

#link msmtp over sendmail with the an openrc start script
#Create /etc/local.d/msmtp.start

ln -sf /usr/bin/msmtp /usr/sbin/sendmail

#Ensure our new scripts are executable
chmod +x /etc/local.d/msmtp.start
chmod +x /usr/local/bin/raid-mon.sh

#test mdadm raid + email
mdadm --monitor --scan --test --mail=some-mail-you-read@whatever.com

To explain that briefly, we're using the OpenRC start scripts to ensure that anytime an msmtp service is started that we force /usr/sbin/sendmail to be overwritten by /usr/bin/msmtp. That's not strictly necessary, but it makes sure that we don't accidentally kick off sendmail by some weird accident.

And for our MoTD generator we'll use Atentu a little generator of my own design. The output is a simple load/memory/disc usage type deal, eventually I'll add service status and raid information to it. All of that said we can finally wrap everything up in a nice crontab bow.

#Setup QoL crontab stuff
*/15 * * * * /usr/bin/atentu > /etc/motd	  
0 3 * * 5 /sbin/apk -U upgrade
0 5 * * 5 /sbin/reboot
0 22 * * * clamscan -r -i /* | msmtp -a default mail-you-read@whatever.com
@reboot sleep 300 && /usr/local/bin/raid-mon.sh

We enable a few simple services to run, automatic updates (because we like to live on the edge!) (and can't be bothered to do this manually), our RAID monitor, and a reoccuring AV scan that kicks off at 11pm. We wouldn't want our Windows users catching anything nasty would we? Even if something that gets put on the NAS doesn't affect us directly, we should care about our users.

This post is a little longer than usual, but was the culmination of a poorly implemented personal server that gathered way too much cruft, a massive move from Georgia to Maine, and a pleasant feeling of confidence knowing that something like this is readily within my grasp.