Compare commits

16 Commits

8 changed files with 140 additions and 312 deletions

27
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,27 @@
before_script:
# Ensure prerequisites are installed
- 'which ssh-agent || (apk update && apk add openssh-client git)'
# Launch ssh agent and add our key
- eval $(ssh-agent -s)
- echo "${SSH_PRIVATE_KEY}" | tr -d '\r' | ssh-add - > /dev/null
# Ensure known hosts exists and is populated
- mkdir ~/.ssh && chmod 700 ~/.ssh
- ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts && chmod 644 ~/.ssh/known_hosts
push_github:
type: deploy
environment:
name: github
url: github.com/jmhbnz/raspi-k3s
script:
# Output git status
- git checkout "${CI_COMMIT_BRANCH}" && git pull && git status
# Add a new remote for github
- git remote add github git@github.com:jmhbnz/raspi-k3s.git || true
# Push to the new remote
- git push github "${CI_COMMIT_BRANCH}"

BIN
images/imager-distribution.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
images/imager-finished.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@ -1,67 +0,0 @@
#================================================================
# Package installation
#================================================================
preset=server
firmware_packages=1
packages=nano,curl,htop,git,smartmontools,fail2ban,knockd,iptables,glusterfs-server,sed,iptables-persistent,rsync
#================================================================
#================================================================
# Localisation
#================================================================
keyboad_layout=us
locales=en_NZ.UTF-8
timezone=Pacific/Auckland
system_default_locale=en_NZ.UTF-8
#================================================================
#================================================================
# Security hardening
#================================================================
ssh_pwlogin=0
root_ssh_pwlogin=0
# Provide an ssh public key
user_ssh_pubkey="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC2t1GmNmhoV2WvrA5dKS9nhLlBExD949coqQnYnqvgQ7l+ODRXxifg8wcnp7PXTcs6CW3tlwA6W5LWMK5gtjNXfZH9jpMPD2m1p7koysoYmC2eMWREF+IN4dS1MsaL9VU1WrqolP3qbvI2WWmECrzA0xakDhAZXuIzl3HRng1YeQV8gi8YoG9Jm0ZXd+RMqxBGyUw1+O4o1NfFwQ9dIUNFRyUScl/yzQv0AfXqOi2IrdA5IpdtlpAZsD1cnB3kUFlluxaqCuXQKGO1wPo3wTh2Po+upCyMi2XXCakVXgRaIuBLvmZPYVMYlwbbDL0dMexhc+pccgJ3sE0JDzm1+oe2uY9pIIsnfrD6fzjN+JO0rbGtrWpERf0amIzJFj4i7zvPk4eY7OskYSInf7Qj5He8eLv0VBBxQc2ikDTPQtS/cIomLXA1tAsA1dqHNl438AHG4r4NaPNDAoRUql3BOYEZjyza5eV8b26Yw3s/x64g4oc0Zhoc6/UB7zmbou0a1w+oC1qXloJIYnYb6ntiaZKt5HLuUUP7V0WIMRco35Rt1NtaKKwqmo7z40VwvYPWTtaNwm3GFmMi9E603TudzuJB+katlfROXFOI/8Ks5S1juFAMcI+KVo0Ndl0G9iYRbZyrHB+HdxS1HVaWn4CTy9WCvQBHWB/cr1dnjVviqAZ0iw== james@james-ultrabook"
#================================================================
#================================================================
# Setup my user
#================================================================
username=james
userpw=[PASSWORD HERE]
userperms_admin=1
usergpu=1
rootpw=[PASSWORD HERE]
#================================================================
#================================================================
# Optimise system power and ram usage
#================================================================
gpu_mem=32
cmdline="dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 elevator=deadline fsck.repair=yes cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory"
#================================================================
#================================================================
# Configure networking
#================================================================
hostname=rasp-worker2
ifname=wlan0
ip_addr=192.168.43.122
ip_netmask=255.255.255.0
ip_gateway=192.168.43.1
ip_nameservers=1.1.1.1
wlan_country=NZ
wlan_ssid="James Phone"
wlan_psk="[PASSWORD HERE]"
#================================================================

7
network-config Normal file
View File

@ -0,0 +1,7 @@
version: 2
ethernets:
eth0:
addresses:
- 192.168.1.142
gateway4: 192.168.1.1
nameservers: 1.1.1.3

View File

@ -1,116 +0,0 @@
#================================================================
# Post install ssh configuration
#================================================================
echo "Changing ssh port..."
sed -i "s/#Port 22/Port 2122/" /rootfs/etc/ssh/sshd_config
echo "Turning off ssh pam..."
sed -i "s/UsePAM yes/UsePAM no/" /rootfs/etc/ssh/sshd_config
echo "Restarting ssh service..."
systemctl restart sshd
#================================================================
#================================================================
# Post install setup docker
#================================================================
echo "Installing docker via helper script."
curl -fsSL https://get.docker.com -o get-docker.sh | sh -
echo "Adding docker permissions for standard user."
usermod -aG docker james
#================================================================
#================================================================
# Post install fail2ban configuration
#================================================================
echo "Ensure fail2ban service is enabled..."
systemctl enable fail2ban
echo "Configure fail2ban ssh jail..."
touch /rootfs/etc/fail2ban/jail.local
echo '[ssh]' >> /rootfs/etc/fail2ban/jail.local
echo 'enabled=true' >> /rootfs/etc/fail2ban/jail.local
echo 'port=2122' >> /rootfs/etc/fail2ban/jail.local
echo 'filter=sshd' >> /rootfs/etc/fail2ban/jail.local
echo 'logpath=/var/log/auth.log' >> /rootfs/etc/fail2ban/jail.local
echo 'bantime=1800' >> /rootfs/etc/fail2ban/jail.local
echo 'banaction=iptables-allports' >> /rootfs/etc/fail2ban/jail.local
echo 'findtime=900' >> /rootfs/etc/fail2ban/jail.local
echo 'maxretry=3' >> /rootfs/etc/fail2ban/jail.local
echo "Restart fail2ban service..."
sudo systemctl restart fail2ban
#================================================================
#================================================================
# Post install bash configuration
#================================================================
echo "Configuring bash prompt..."
echo "PS1='\[\033[02;31m\]\u@\H:\[\033[01;34m\]\w\$\[\033[00m\] '" >> /rootfs/home/james/.bashrc
#================================================================
#================================================================
# Post install firewall configuration
#================================================================
echo "Allowing local traffic in iptables"
iptables -A INPUT -i lo -j ACCEPT
echo "Allow all established connections in iptables"
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
echo "Drop all other traffic"
iptables -A INPUT -j DROP
echo "Ensure iptables-persistent is started"
systemctl start netfilter-persistent
echo "Ensure iptables-persistent is enabled"
systemctl enable netfilter-persistent
echo "Ensure firewall rules are saved"
netfilter-persistent save
#================================================================
#================================================================
# Post install knockd configuration
#================================================================
echo "Writing port knocking configuration file..."
cat << EOF > /rootfs/etc/knockd.conf
[options]
UseSysLog
logfile = /var/log/knockd.log
interface=wlan0
[ssh]
sequence =
seq_timeout = 15
start_command = /sbin/iptables -I INPUT 1 -s %IP% -p tcp --dport 2122 -j ACCEPT
tcpflags = syn
cmd_timeout = 60
stop_command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 2122 -j ACCEPT
EOF
echo "Enabling knockd.service ..."
sed -i "s/START_KNOCKD=0/START_KNOCKD=1/" /rootfs/etc/default/knockd
cat << EOF >> /rootfs/lib/systemd/system/knockd.service
[Install]
WantedBy=multi-user.target
Alias=knockd.service
EOF
systemctl enable knockd.service
echo "Start knockd service..."
systemctl start knockd.service
#================================================================

View File

@ -2,7 +2,7 @@
#+TITLE: Raspberry pi kubernetes cluster guide #+TITLE: Raspberry pi kubernetes cluster guide
#+AUTHOR: James Blair #+AUTHOR: James Blair
#+EMAIL: mail@jamesblair.net #+EMAIL: mail@jamesblair.net
#+DATE: 27th March 2021 #+DATE: 26th June 2021
This file serves as a complete step by step guide for creating a bare metal raspberry pi kubernetes cluster using [[https://k3s.io/][k3s]] from [[https://rancher.com/][Rancher]]. This file serves as a complete step by step guide for creating a bare metal raspberry pi kubernetes cluster using [[https://k3s.io/][k3s]] from [[https://rancher.com/][Rancher]].
@ -14,6 +14,8 @@ Additionally in future I would like the cluster to be portable and operate via 3
I chose k3s as it incredibly lightweight but still CNCF certified and production grade software that is optimised for resource constraints of raspberry pis. I chose k3s as it incredibly lightweight but still CNCF certified and production grade software that is optimised for resource constraints of raspberry pis.
* Hardware pre-requisites * Hardware pre-requisites
** Cluster machines ** Cluster machines
@ -37,136 +39,58 @@ For this cluster I am using power over ethernet using [[https://www.pbtech.co.nz
* Step 1 - Prepare boot media for master * Step 1 - Prepare boot media for master
** Download the latest release ** Download sd card imaging utility
Our first step is to create the bootable SD Card with a minimal install of [[https://www.raspbian.org/][Raspbian]], which is a free operating system based on [[https://www.debian.org/][Debian]] and is optimised for Raspberry Pi hardware. Our first step is to create the bootable SD Card with a minimal install of [[https://ubuntu.com/download/raspberry-pi][Ubuntu Server 20.04 x64]], which is a free and open source operating system based on [[https://www.debian.org/][Debian]].
Rather than doing an installation and configuration of an os image from scratch I found [[https://github.com/FooDeas/raspberrypi-ua-netinst][this project]] on Github which automates the install and configuration process nicely. To simplify the sd card creation process we can use the open source [[https://github.com/raspberrypi/rpi-imager][rpi-imager]] utility, the code snippet below will download the latest release:
#+NAME: Download the latest release zip #+NAME: Download rpi-imager utility
#+begin_src tmate #+begin_src bash
cd ~/Documents/raspi-k3s
echo Downloading latest release zip from github echo Downloading latest release zip from github
curl -s https://api.github.com/repos/foodeas/raspberrypi-ua-netinst/releases/latest \ curl -s https://api.github.com/repos/raspberrypi/rpi-imager/releases/latest \
| grep "browser_download_url.*zip" \ | grep "browser_download_url.*exe" \
| cut -d : -f 2,3 \ | cut -d : -f 2,3 \
| tr -d \" \ | tr -d \" \
| wget -i - | wget -O /mnt/c/Users/$USER/Downloads/imager.exe -i -
echo Checking file is now present echo Checking file is now present
ls -l | grep *.zip ls -l /mnt/c/Users/$USER/Downloads/imager.exe
echo Extracting the zip file
unzip -q -d installer *.zip
ls -l | grep installer
#+end_src #+end_src
#+RESULTS: Download the latest release zip With the software downloaded, let's fire it up the installer and get it setup.
#+begin_example
Downloading latest release zip from github
Checking file is now present
-rw-rw-rw- 1 james james 60299545 Aug 12 08:35 raspberrypi-ua-netinst-v2.4.0.zip
Extracting the zip file
drwxrwxrwx 1 james james 4096 Jan 20 11:12 installer
-rwxrwxrwx 1 james james 2863 Jan 10 17:04 installer-config.txt
#+end_example
#+NAME: Open imager software installer
** Apply custom install configuration #+begin_src shell :results silent
cmd.exe /mnt/c/Users/$USER/Downloads/imager.exe
Our next step after downloading the latest release is to apply our own installation configuration using a simple txt file.
There is great documentation online showing what configuration options are available [[https://github.com/malignus/raspberrypi-ua-netinst/blob/master/doc/INSTALL_CUSTOM.md][here]].
For our purposes we just over-write the file downloaded and extracted in the previous step with one we have prepared earlier :)
#+NAME: Overwrite installer configuration file
#+begin_src tmate
echo Display wordcount of file after copy to validate update wordcount of original file for comparison
wc installer/raspberrypi-ua-netinst/config/installer-config.txt
echo Overwriting /installer/raspberrypi-ua-netinst/config/installer-config.txt
cp installer-config.txt installer/raspberrypi-ua-netinst/config/
echo Display wordcount of file after copy to validate update
wc installer/raspberrypi-ua-netinst/config/installer-config.txt
#+end_src #+end_src
#+RESULTS: Overwrite installer configuration file Once the software is installed let's run it, the code snippet below will launch the application from the default install directory, or alternatively just launch it from your start menu or preferred shortcut.
#+begin_example
Display wordcount of original file for comparison
3 23 157 installer/raspberrypi-ua-netinst/config/installer-config.txt
Overwriting /installer/raspberrypi-ua-netinst/config/installer-config.txt
Display wordcount of file after copy to validate update
67 85 2863 installer/raspberrypi-ua-netinst/config/installer-config.txt
#+end_example
#+NAME: Launch rpi-imager utility
** Apply custom post install script #+begin_src shell :results silent
cd /mnt/c/Program\ Files\ \(x86\)/Raspberry\ Pi\ Imager/ && cmd.exe /k rpi-imager.exe
The final step is to supply a post install script which completes additional security hardening and production readiness automatically.
To supply a script we can provide an additional ~post-install.txt~ file as documented [[https://github.com/FooDeas/raspberrypi-ua-netinst/blob/devel/doc/INSTALL_ADVANCED.md][here]].
I have a hardening script prepared in this repository that we can copy in.
#+NAME: Copy in post-install script
#+begin_src tmate
echo Copying in post-install.txt
cp post-install.txt installer/raspberrypi-ua-netinst/config/
echo Display wordcount of file after copy to validate
wc installer/raspberrypi-ua-netinst/config/post-install.txt
#+end_src #+end_src
#+RESULTS: Copy in post-install script
#+begin_example ** Image sd card
Copying in post-install.txt
Display wordcount of file after copy to validate Now that we have sd card imaging software installed and running let's put the first sd card into our system and begin the imaging process.
98 282 3429 installer/raspberrypi-ua-netinst/config/post-install.txt
#+end_example First select an Operating System, click ~CHOOSE OS~ --> ~Other general purpose OS~ --> ~Ubuntu Server 20.04.2 LTS x64~.
[[./images/imager-distribution.png]]
Once you've selected the operating system and sd card, click ~WRITE~. The process will take a few minutes to complete.
[[./images/imager-finished.png]]
* Step 2 - Copy the install media to sd card * Step 2 - Copy the install media to sd card
Our next step is to copy the contents of the ~installer/~ folder to a *FAT32* formatted removable media i.e. SD Card. Our next step is to copy the custom ~user-data~ and ~network-config~ files included in this repository to the newly created SD Card.
Unfortunately this is currently a windows step as my dev environment is a Windows 10 laptop with Debian via Windows Subsystem for Linux which does not support ~lsblk~ or other disk management commands. Note: The code block below assumes the SD Card boot partition will be ~D:\~. You may need to adjust for your environment.
** Obtain sd card partition information
Our first step is to insert the SD Card and ensure it is formatted correctly as ~FAT32~. To do that we need to know the number of the disk we want to format, we can find that via powershell.
#+NAME: Get disks via windows powershell
#+begin_src tmate
echo Retrieving disk list via powershell
powershell.exe -nologo -command "get-disk | select Number, FriendlyName, Size"
#+end_src
#+NAME: Get partitions via windows powershell
#+begin_src tmate
echo Retrieving partition list via powershell
powershell.exe -nologo -command "get-disk | get-partition | select PartitionNumber, DriveLetter, Size, Type"
#+end_src
** Create and format sd card partition
Once we know the number of the disk we want to format we can proceed. In the example above I have a 32GB SD Card which shows as number ~1~.
Checking the disk we can see some partitions that exist already from previous use of the card. To delete these partitions you can use the ~Remove-Partition -DiskNumber X -PartitionNumber Y~ command where ~X~ and ~Y~ relate to the output of your disk and partition number.
Due to the risk of data loss this step is not automated. Once existing partitions have been cleared we can use the following block to:
- Create a new partition using maximum available space
- Assign a free drive letter in windows
- Mount the disk in WSL so we can copy to it
- Copy the install media over to the partition
#+NAME: Create sd card partition
#+begin_src tmate
echo Use powershell to create new partition and format
powershell.exe -nologo -command "new-partition -disknumber 1 -usemaximumsize -driveletter d; format-volume -driveletter d -filesystem FAT32 -newfilesystemlabel sd"
#+end_src
#+NAME: Mount and copy the new media #+NAME: Mount and copy the new media
#+begin_src tmate #+begin_src tmate
@ -175,8 +99,9 @@ sudo mkdir /mnt/d
sudo mount -t drvfs d: /mnt/d/ sudo mount -t drvfs d: /mnt/d/
echo Copy the contents of installer to sd echo Copy the contents of installer to sd
cp -r installer/* /mnt/d/ cp network-config /mnt/d/
cp user-data /mnt/d/
# We need to wait before we can eject # We need to wait before we can eject
sleep 5 sleep 5
sudo umount /mnt/d sudo umount /mnt/d
@ -189,26 +114,19 @@ powershell.exe -nologo -command "(new-object -comobject shell.application).names
* Step 3 - Boot the pi and remotely connect * Step 3 - Boot the pi and remotely connect
Provided the configuration on the sd card is valid and the pi has been able to successfully obtain an ip address via dhcp on boot then following a 10-20minute net install process the pi will be online and accessible via ssh using the private key corresponding to the public key we supplied in our ~installer-config.txt~ file. Provided the configuration on the sd card is valid and the pi has been able to successfully configure networking then following a brief install process the pi will be online and accessible via ssh using the private key corresponding to the public key we supplied in our ~user-data~ file.
** Port knock and enter
Now we can port knock and connect. #+NAME: Connect to the pi
Note: There seems to be a tiny delay required between port knocks being transmitted and ssh being able to connect which is why a short sleep is included in the knock and enter command.
#+NAME: Knock and enter
#+begin_src tmate #+begin_src tmate
# Setup machine variables # Setup machine variables
export port=2124 export port=2142
export machineip=192.168.1.124 export machineip=192.168.1.142
export knocksequence="[SEQUENCE HERE]"
# Gather ssh keys if not already known # Gather ssh keys if not already known
ssh-keyscan -p $port $machineip >> ~/.ssh/known_hosts ssh-keyscan -p $port $machineip >> ~/.ssh/known_hosts
# Knock and enter # Connect via ssh
knock $machineip $knocksequence && sleep 2 && ssh -p $port $machineip ssh -p $port $machineip
#+end_src #+end_src

59
user-data Normal file
View File

@ -0,0 +1,59 @@
#cloud-config
hostname: raspi01
# ==================================================================
# Add users to the system
# ==================================================================
users:
- name: james
gecos: James Blair
primary_group: james
groups: users, admin
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCsYhu2xE5cxq+sA7bNyHjZUk9IHKXHsd58ZCFLfCHbK5nnWLgJwxFnF1NzBylyOJviJ2v54VYQoXvsWLTbehlyH/kqJS8icmo0iu2mUFcp09n/3NcGw2BJefwMbK+mofxBBR78RRNI8DG3yk7Apa19BrLpFWaL/VljGidgR61WhPH7FbXjTh5NuQR494LG3yBRn16yIPNN+xZhf0TW7uoVCiSr77kFELgrTqjpPyoYiYLZZdKqJZ7PDgOEcLq5oDEZfYME8sGRPyufGByH7tnK9fgFaZ9wW747wTNN2naUIhCNzJLxKgr8fMMRBXuqeUjk+5/EzxGFXjxE+4a+dhD51OO5mSN1ctG/061HIQjJwZ2Zk6CACypBEv6nLVREaMqKQtcEPPooZ2SK4SdiMtwC8XLCZ6wRQDVskMentI1uy3bbCwV9AG0auiLA3sfbyKI8093p5NLsLEiR+BScu4/tLx7kzPetl89QOKzTI60GXzBaSnBXhAtQTijZZFrOGbQ1NQ1deWb6pT8foOPOs3P2F0a4Y/nY/xlomBuNqTI48Vi8MZJjhTvAe8BF+Y7C8HlSaCZeH1DrnymkbLhpXvVH7Tuir/DLOyhrwnXqNuxpMyWsfy5UrTfe67GP2+jzriFxteTMbvZjmgbF2UDMYs5U59NaYPdAYxjwdzH5nHoIWw== james@james-desktop
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
# ==================================================================
# ==================================================================
# Add packages to the system
# ==================================================================
package_upgrade: true
packages:
- nano
- curl
- htop
- git
- smartmontools
- fail2ban
- knockd
- glusterfs-server
- sed
- rsync
# ==================================================================
# ==================================================================
# Localisation
# ==================================================================
user-data:
timezone: Pacific/Auckland
locale: en_NZ
# ==================================================================
# ==================================================================
# Harden ssh and set bash prompt
# ==================================================================
runcmd:
- sed -i -e '/^#Port/s/^.*$/Port 2142/' /etc/ssh/sshd_config
- sed -i -e '/^#PermitRootLogin/s/^.*$/PermitRootLogin no/' /etc/ssh/sshd_config
- sed -i -e '$aAllowUsers james' /etc/ssh/sshd_config
- restart ssh
- echo "PS1='\[\033[02;31m\]\u@\H:\[\033[01;34m\]\w\$\[\033[00m\] '" >> /home/james/.bashrc
# ==================================================================