commit c70faec7d109a122d8b7357dc393dfc46478ea60 from: gonzalo date: Tue May 23 10:25:57 2023 UTC Initial commit fart-init commit - /dev/null commit + c70faec7d109a122d8b7357dc393dfc46478ea60 blob - /dev/null blob + 677e34861b15ef0c8aa988f2fdb751d321c74d38 (mode 644) --- /dev/null +++ LICENSE @@ -0,0 +1,21 @@ +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 @@ -0,0 +1,151 @@ +``` + /$$$$$$ /$$ /$$ /$$ /$$ + /$$__ $$ | $$ |__/ |__/ | $$ +| $$ \__//$$$$$$ /$$$$$$ /$$$$$$ /$$ /$$$$$$$ /$$ /$$$$$$ +| $$$$ |____ $$ /$$__ $$|_ $$_//$$$$$$| $$| $$__ $$| $$|_ $$_/ +| $$_/ /$$$$$$$| $$ \__/ | $$ |______/| $$| $$ \ $$| $$ | $$ +| $$ /$$__ $$| $$ | $$ /$$ | $$| $$ | $$| $$ | $$ /$$ +| $$ | $$$$$$$| $$ | $$$$/ | $$| $$ | $$| $$ | $$$$/ +|__/ \_______/|__/ \___/ |__/|__/ |__/|__/ \___/ + +``` + +## 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 @@ -0,0 +1,2 @@ +instance-id: 999/fart-init +local-hostname: fart-init blob - /dev/null blob + f44d6d3e7d8cad6668d70bb0324180ebd59e4acf (mode 644) --- /dev/null +++ examples/network-data-dhcp @@ -0,0 +1,12 @@ +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 @@ -0,0 +1,15 @@ +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 @@ -0,0 +1,7 @@ +#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 @@ -0,0 +1,247 @@ +#!/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