commit - /dev/null
commit + c70faec7d109a122d8b7357dc393dfc46478ea60
blob - /dev/null
blob + 677e34861b15ef0c8aa988f2fdb751d321c74d38 (mode 644)
--- /dev/null
+++ LICENSE
+MIT License
+
+Copyright (c) 2023 gonzalo@x61.sh
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
blob - /dev/null
blob + 0c9f9045883ac6d2df95e283d8d02f6f5b925d41 (mode 644)
--- /dev/null
+++ README.md
+```
+ /$$$$$$ /$$ /$$ /$$ /$$
+ /$$__ $$ | $$ |__/ |__/ | $$
+| $$ \__//$$$$$$ /$$$$$$ /$$$$$$ /$$ /$$$$$$$ /$$ /$$$$$$
+| $$$$ |____ $$ /$$__ $$|_ $$_//$$$$$$| $$| $$__ $$| $$|_ $$_/
+| $$_/ /$$$$$$$| $$ \__/ | $$ |______/| $$| $$ \ $$| $$ | $$
+| $$ /$$__ $$| $$ | $$ /$$ | $$| $$ | $$| $$ | $$ /$$
+| $$ | $$$$$$$| $$ | $$$$/ | $$| $$ | $$| $$ | $$$$/
+|__/ \_______/|__/ \___/ |__/|__/ |__/|__/ \___/
+
+```
+
+## What is it?
+
+fart-init it's a little script that try to get cloud-init files (meta-data, user-data and network-data)
+to configure the server with it, after that, it will clean up the server and itself. The idea is to
+create a new and clean server from a template [proxmox/qemu](https://x61.sh/log/2023/05/17052023102313-qemu_proxmox_openbsd_template.html) with a
+basic configuration.
+
+## What can it do?
+
+fart-init can do:
+
+- set a main user and ssh-keys
+- set a root password
+- set the network (dhcp or static)
+- install a list of packages (soon)
+
+what it can't do:
+
+- it won't resize or change partitions on your VM
+- configure any service on base system (you should do this with something else)
+
+what it will do by default for you:
+
+- enable unwind(8) as resolver
+- disable sndiod(8) since I asume this is a server
+- clean up all packages installed
+- delete all residual files in the server that are not standard
+- delete the user given as main and created it again
+
+## Setup
+
+I based fart-init on cloud-init's files, so for it, you will need 3 files meta-data, user-data and network-data, each one
+of them has the information that it will be extract by fart-init to set up the server.
+
+You can give these 3 files in 2 different ways to fart-init:
+
+- Over the cloud-init option in Proxmox (VM -> Cloud-init -> Edit -> Regenerate)
+- Over a webserver reacheable from the VM
+
+*** CAVEATS: Proxmox is a linux server and their password hashing is different from the one we use in OpenBSD, so the password given
+over Proxmox for the user won't work, use the ssh-key instead and set up a root password inside fart-init, or just use the default one
+for root which is "fart-init"
+
+If you will serve the files by Proxmox just fill the "Cloud-init" information as you want and regenerate it, that should be enough, if you
+want to serve the files over a webserver, they should look like this:
+
+```
+$ cat user-data
+#cloud-config
+hostname: fart-init
+username: gonzalo
+password: puffy01
+ssh-key: "ssh-ed25519 AAAAC3NzaFsTghZaSAAIPxVebz+gL0DqbsikzlMBA0SM059VkOmEGly3b24SnNH"
+chpasswd:
+ expire: False
+```
+
+```
+$ cat meta-data
+instance-id: 999/fart-init
+local-hostname: fart-init
+```
+
+network-data - dhcp
+```
+$ cat network-data
+version: 1
+config:
+ - type: physical
+ name: eth0
+ mac_address: '16:70:0d:8e:dd:a4'
+ subnets:
+ - type: dhcp4
+ - type: nameserver
+ address:
+ - '9.9.9.9'
+ search:
+ - 'fart.home'
+```
+network-data - static
+```
+$ cat network-data
+version: 1
+config:
+ - type: physical
+ name: eth0
+ mac_address: '16:70:0d:8e:dd:a4'
+ subnets:
+ - type: static
+ address: '192.168.0.211'
+ netmask: '255.255.255.0'
+ gateway: '192.168.0.1'
+ - type: nameserver
+ address:
+ - '192.168.0.123'
+ search:
+ - 'fart.home'
+```
+
+As I said you can serve these files with a webserver (httpd(8)) or just using python for example like:
+
+```
+proxmox:~/files# ls -al
+total 20
+drwxr-xr-x 2 root root 4096 May 22 19:12 .
+drwx------ 5 root root 4096 May 22 15:54 ..
+-rw-r--r-- 1 root root 53 May 22 15:58 meta-data
+-rw-r--r-- 1 root root 345 May 22 18:54 network-config
+-rw-r--r-- 1 root root 186 May 22 19:12 user-data
+proxmox:~/files# python3 -m http.server --directory .
+Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
+```
+
+Adjust the line `FART_SRV="10.0.2.2:8000"` inside fart-init to your needs and be sure the port is open on your firewall.
+
+## How to use it?
+
+After created your [OpenBSD template](https://x61.sh/log/2023/05/17052023102313-qemu_proxmox_openbsd_template.html), you need to download
+fart-init and give it permissions, something like this (assuming your proxmox or virtual network is 10.0.2.0/24):
+
+```
+vm_template# cd /usr/local/sbin/
+vm_template# ftp -V http://10.0.2.2:8000/fart-init
+vm_template# chmod 755 fart-init
+vm_template# echo '/usr/local/sbin/fart-init 2>&1 | tee /var/log/fart-init.log' > /etc/rc.local
+```
+
+And that is pretty much of it, keep in mind this is a very early version of it and it could be fail, I ran it several times on my setup without
+issues but this could change in yours.
+
+During the first booting process you will see something like this:
+
+![alt text](https://github.com/gonzalo-/fart-init/blob/main/imgs/fart-init_booting.png?raw=true)
+
+And on the second boot:
+
+![alt text](https://github.com/gonzalo-/fart-init/blob/main/imgs/fart-init_booted.png?raw=true)
+
+Have fun!
blob - /dev/null
blob + e1dd89b2bbd9deda0ba91542038d91bccbc65584 (mode 644)
--- /dev/null
+++ examples/meta-data
+instance-id: 999/fart-init
+local-hostname: fart-init
blob - /dev/null
blob + f44d6d3e7d8cad6668d70bb0324180ebd59e4acf (mode 644)
--- /dev/null
+++ examples/network-data-dhcp
+version: 1
+config:
+ - type: physical
+ name: eth0
+ mac_address: '16:70:0d:8e:dd:a4'
+ subnets:
+ - type: dhcp4
+ - type: nameserver
+ address:
+ - '9.9.9.9'
+ search:
+ - 'fart.home'
blob - /dev/null
blob + 636b3417bcb48f6b5e09eb85151f4ebd56d6ae0c (mode 644)
--- /dev/null
+++ examples/network-data-static
+version: 1
+config:
+ - type: physical
+ name: eth0
+ mac_address: '16:70:0d:8e:dd:a4'
+ subnets:
+ - type: static
+ address: '192.168.0.211'
+ netmask: '255.255.255.0'
+ gateway: '192.168.0.1'
+ - type: nameserver
+ address:
+ - '192.168.0.123'
+ search:
+ - 'fart.home'
blob - /dev/null
blob + f7c9f700bdf04ebc6ef8201af6bca68c8108ede1 (mode 644)
--- /dev/null
+++ examples/user-data
+#cloud-config
+hostname: fart-init
+username: gonzalo
+password: puffy01
+ssh-key: "ssh-ed25519 AAAAC3NzaFsTghZaSAAIPxVebz+gL0DqbsikzlMBA0SM059VkOmEGly3b24SnNH"
+chpasswd:
+ expire: False
blob - /dev/null
blob + 5b6231b00b7fb1bbfcf71ef8e8a03994b8a38b62 (mode 755)
--- /dev/null
+++ fart-init
+#!/bin/sh
+
+# Copyright (c) 2023 gonzalo@x61.sh
+
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+SHELL=/bin/sh
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin
+
+### Deploy me.
+## cd /usr/local/sbin/
+## ftp http://10.0.2.2:8000/fart-init
+## chmod 755 fart-init
+## echo '/usr/local/sbin/fart-init 2>&1 | tee /var/log/fart-init.log' > /etc/rc.local
+
+FART_SRV="10.0.2.2:8000"
+CDROM=$(dmesg | grep 'QEMU DVD-ROM' | uniq | awk '{print $1}')
+YO=/usr/local/sbin/fart-init
+PING_WWW=$(ping -c1 -w1 google.com | wc -l)
+PING=$(ping -c1 -w1 "${FART_SRV}" | wc -l)
+NET_FILE=/tmp/network-config
+USR_FILE=/tmp/user-data
+MET_FILE=/tmp/meta-data
+
+## Check root
+if test "$(whoami)" != root; then
+ doas "$0" "$@"
+ exit $?
+fi
+
+## Debug
+#set -x
+
+logo() {
+cat << "EOF"
+
+ /$$$$$$ /$$ /$$ /$$ /$$
+ /$$__ $$ | $$ |__/ |__/ | $$
+| $$ \__//$$$$$$ /$$$$$$ /$$$$$$ /$$ /$$$$$$$ /$$ /$$$$$$
+| $$$$ |____ $$ /$$__ $$|_ $$_//$$$$$$| $$| $$__ $$| $$|_ $$_/
+| $$_/ /$$$$$$$| $$ \__/ | $$ |______/| $$| $$ \ $$| $$ | $$
+| $$ /$$__ $$| $$ | $$ /$$ | $$| $$ | $$| $$ | $$ /$$
+| $$ | $$$$$$$| $$ | $$$$/ | $$| $$ | $$| $$ | $$$$/
+|__/ \_______/|__/ \___/ |__/|__/ |__/|__/ \___/
+
+EOF
+}
+
+info() {
+ echo 'printf "\n\n:.: FART-INIT INFO\n"' >> /etc/rc.firsttime
+ echo 'printf "\n:. My name: `hostname`\n"' >> /etc/rc.firsttime
+ echo 'printf ":. My ip: `ifconfig vio0 | grep inet | awk '\''{print $2}'\''`\n"' >> /etc/rc.firsttime
+ echo 'printf ":. My gw: `route -n show | grep default | awk '\''{print $2}'\''`\n"' >> /etc/rc.firsttime
+ echo 'printf "\n\n### Check my log /var/log/fart-init.log for more info ###\n\n"' >> /etc/rc.firsttime
+}
+
+logo
+
+## Handling errors
+error() {
+ echo "Error: $1" >&2
+ exit 1
+}
+
+## We add the ssh-key for remote login
+ssh_key() {
+ printf ":.: Setting SSH key for the main user...\n"
+ SSHPUB=$(grep ssh- "${USR_FILE}" | cut -d'-' -f2,3 | sed "s/^[ \t]*//")
+ mkdir -p /home/"${USR}"/.ssh
+ mkdir -p /root/.ssh
+ echo "${SSHPUB}" >> /home/"${USR}"/.ssh/authorized_keys
+ echo "${SSHPUB}" >> /root/.ssh/authorized_keys
+}
+
+## Let's check first if we have a cdrom with
+## the cloud-init files
+if [ "${CDROM}" -eq "cd0" ]; then
+ printf ":.: We have a CDROM. Are we running on QEMU or Proxmox? Mounting ${CDROM}\n"
+ if mount /dev/"${CDROM}"a /mnt; then
+ if [ -f "/mnt/user-data" ]; then
+ printf ":.: Found cloud-init files. Copying them...\n"
+ cp /mnt/{meta-data,network-config,user-data} /tmp || exit 1
+ umount /mnt
+ else
+ printf ":.: No user-data in ${CDROM}. Trying network initialization...\n"
+ fi
+ else
+ error "Failed to mount ${CDROM}. No cloud-init information in the CDROM"
+ fi
+else
+ printf ":.: No CDROM found. Checking network access...\n"
+ if [ "${PING}" -lt 1 ]; then
+ printf "[+] No local server connection.\n"
+ exit 2
+ else
+ printf ":.: Access to local server available.\n"
+ ## We download the cloud-init files
+ printf ":.: Downloading cloud-init files from "${FART_SRV}"...\n"
+ cd /tmp && \
+ ftp -V http://"${FART_SRV}"/meta-data || error "No meta-data file in the server"
+ ftp -V http://"${FART_SRV}"/user-data || error "No user-data file in the server"
+ ftp -V http://"${FART_SRV}"/network-config
+ fi
+fi
+
+## Delete everything just-in-case
+# packages
+if [ "${PING_WWW}" -lt 1 ]; then
+ printf "[+] No internet connection.\n"
+else
+ printf ":.: Installing sysclean...\n"
+ pkg_add sysclean
+ # Extra files
+ printf ":.: Adding myself to /etc/sysclean.ignore...\n"
+ echo "${YO}" > /etc/sysclean.ignore
+ printf ":.: Deleting garbage with sysclean...\n"
+ sysclean -a | xargs rm -rf
+ rm /etc/sysclean.ignore
+fi
+
+printf ":.: Deleting packages and residual files...\n"
+pkg_delete -cqqxX /var/db/pkg/*-firmware-[0-9]* >/dev/null
+rm -rf /usr/local/{.[!.],}* /var/db/pkg/{.[!.],}* /usr/ports/pobj \
+ /usr/ports/distfiles/* /usr/ports/packages/*
+mtree -qdef /etc/mtree/4.4BSD.dist -p / -U > /dev/null 2>&1
+mtree -qdef /etc/mtree/BSD.x11.dist -p / -U > /dev/null 2>&1
+ln -sf /etc/X11/app-defaults /usr/local/lib/X11/app-defaults > /dev/null 2>&1
+ldconfig -RU > /dev/null 2>&1
+
+# ssh keys
+printf ":.: Deleting previous ssh keys and files...\n"
+rm -v /etc/ssh/ssh_host_*
+rm -v /etc/random.seed
+rm -v /var/db/host.random
+rm -v /etc/isakmpd/local.pub
+rm -v /etc/isakmpd/private/local.key
+rm -v /etc/iked/private/local.key
+rm -v /etc/iked/local.pub
+
+## We want to be god
+printf ":.: Setting a basic /etc/doas.conf file...\n"
+echo 'permit keepenv :wheel' > /etc/doas.conf
+
+## We set a nice root passwd
+printf ":.: Setting default root password...\n"
+## which is 'fart-init'
+## change it by doing: echo YourPassWord | encrypt
+## and put the hash between 'root:' and ':0:0'
+chpass -a 'root:$2b$10$ywl6wTuA8L7Z1yHIU7n8S.ZptV2GDHA8THYX65HV9imc8HStW7Lam:0:0:daemon:0:0:Charlie &:/root:/bin/ksh'
+
+## We need a resolver
+printf ":.: Enabling unwind as resolver...\n"
+rcctl enable unwind > /dev/null 2>&1
+rcctl start unwind > /dev/null 2>&1
+## We don't need sound
+rcctl disable sndiod > /dev/null 2>&1
+
+## Fun starts
+printf ":.: Setting hostname...\n"
+LOCAL_HOSTNAME=$(grep hostname "${USR_FILE}" | cut -d':' -f2 | tr -d '[:blank:]')
+if [ -n "${LOCAL_HOSTNAME}" ]; then
+ echo "${LOCAL_HOSTNAME}" > /etc/myname
+else
+ ## if there is no hostname
+ ## we generate some random one
+ openssl rand -base64 15 | sed 's/[^[:alnum:]]//g' | cut -c -10 > /etc/myname
+fi
+
+## We create new key for next boot
+printf ":.: Setting the creating of ssh-keys on next boot...\n"
+echo 'ssh-keygen -A' >> /etc/rc.firsttime
+
+## We create the main usr/pass
+USR=$(grep -E 'user:|username:' "${USR_FILE}" | awk '{print $2}' | tr -d '\047' | head -1)
+printf ":.: Setting the main user...\n"
+if [ -n "${USR}" ]; then
+ PASS=$(cat "${USR_FILE}" | awk '$1=="password:" {print $2}' | tr -d '\047' | head -1)
+ if [ -n "${PASS}" ]; then
+ HASH=$(echo "${PASS}" | encrypt)
+ userdel -r "${USR}"
+ useradd -m -G wheel -s /bin/ksh -p "${HASH}" -L staff "${USR}"
+ ssh_key
+ else
+ printf ":.: No password for the user?\n"
+ fi
+else
+ printf ":.: No regular user set, you should use root instead\n"
+fi
+
+## We setup the networking
+printf ":.: Setting up the network\n"
+if [ -f "${NET_FILE}" ]; then
+ DHCP=$(grep dhcp "${NET_FILE}" | wc -l)
+ if [ "${DHCP}" -eq "1" ]; then
+ printf ":.: We'll use dhcp on vio0\n"
+ echo 'inet autoconf' > /etc/hostname.vio0
+ else
+ printf ":.: We'll use static IP on vio0\n"
+ ST_IP=$(cat "${NET_FILE}" | awk '$1=="address:" {print $2}' | tr -d '\047' | head -1)
+ MASK=$(cat "${NET_FILE}" | awk '$1=="netmask:" {print $2}' | tr -d '\047' | head -1)
+ GW=$(cat "${NET_FILE}" | awk '$1=="gateway:" {print $2}' | tr -d '\047' | head -1)
+ echo inet "${ST_IP}" "${MASK}" > /etc/hostname.vio0 || exit 1
+ echo "${GW}" > /etc/mygate || exit 1
+ fi
+else
+ printf ":.: No network file, I will set dhcp...\n"
+ echo 'inet autoconf' > /etc/hostname.vio0
+fi
+
+## Final clean-up
+printf ":.: Deleting everything even myself...\n"
+rm /etc/rc.local /tmp/{meta-data,user-data}
+
+printf ":.: Runing syspatch...\n"
+## Let's install patches just-in-case
+CURRENT=$(sysctl kern.version | grep current | wc -l)
+if [ "${CURRENT}" -eq "1" ]; then
+ printf ":.: This is a -current system, you need to run sysupgrade(8)\n"
+else
+ syspatch
+fi
+
+printf ":.: *** MY LOG IS /var/log/fart-init.log ***\n"
+
+## Show basic info on console after first reboot
+info
+
+printf ":.: Reboot time :)\n"
+## Reboot
+reboot
blob - /dev/null
blob + 928415367530b61941539489ba10c308f4bde467 (mode 644)
Binary files /dev/null and img/fart-init_booted.png differ
blob - /dev/null
blob + 40ee41e8a5736355f0cd80b2735caf3f138f7323 (mode 644)
Binary files /dev/null and img/fart-init_booting.png differ