1
0
mirror of https://gitlab.alpinelinux.org/alpine/cloud/tiny-cloud.git synced 2025-12-14 19:02:45 +03:00
Jake Buchholz Göktürk be890f7f16 logging/comment updates
2024-11-30 14:31:54 -08:00

299 lines
7.1 KiB
Bash

# Tiny Cloud - Init Functions
# vim:set filetype=sh:
# shellcheck shell=sh
# set defaults
: "${PREFIX:=/usr}"
: "${LIBDIR:=$PREFIX/lib}"
. "$LIBDIR/tiny-cloud/common"
: "${SKIP_INIT_ACTIONS:=}"
### default phase actions (without leading 'init__')
DEFAULT_ACTIONS_BOOT="
expand_root
set_ephemeral_network
set_default_interfaces
enable_sshd
"
DEFAULT_ACTIONS_EARLY="
save_userdata
"
DEFAULT_ACTIONS_MAIN="
create_default_user
set_hostname
set_ssh_keys
"
DEFAULT_ACTIONS_FINAL=""
: "${INIT_ACTIONS_BOOT=$DEFAULT_ACTIONS_BOOT}"
: "${INIT_ACTIONS_EARLY=$DEFAULT_ACTIONS_EARLY}"
: "${INIT_ACTIONS_MAIN=$DEFAULT_ACTIONS_MAIN}"
: "${INIT_ACTIONS_FINAL=$DEFAULT_ACTIONS_FINAL}"
### standard boot phase functions...
init__expand_root() {
local dev=$(awk '$2 == "/" {print $1}' "$PROC"/mounts 2>/dev/null)
local filesystem=$(awk '$2 == "/" {print $3}' "$PROC"/mounts 2>/dev/null)
local partition=$(cat "$SYS/class/block/${dev#/dev/}/partition" 2>/dev/null)
# only support ext2/ext3/ext4 for now
case "$filesystem" in
ext*) ;;
*) return;;
esac
if [ -n "$partition" ]; then
# it's a partition, resize it
local volume=$(readlink -f "$SYS/class/block/${dev#/dev/}/..")
volume="/dev/${volume##*/}"
echo ", +" | $MOCK sfdisk -q --no-reread -N "$partition" "$volume"
$MOCK partx -u "$volume"
fi
# resize filesystem
if [ -e "$dev" ] || [ -n "$MOCK" ]; then
$MOCK resize2fs "$dev"
fi
}
# collect ethernet interfaces, sorted by index
ethernets() {
for i in "$SYS/class/net/"*; do
local iface="${i##*/}"
case "$iface" in
eth*) echo "$(cat "$i/ifindex") $iface";;
esac
done | sort -n | awk '{print $2}'
}
# find the interface that is has operstate up
find_first_interface_up() {
local n=0
[ $# -eq 0 ] && return
while [ $n -le ${TINY_CLOUD_LINK_WAIT_MAX:-10} ]; do
for i in "$@"; do
if [ "$(cat "$SYS/class/net/$i/operstate")" = "up" ]; then
echo "$i"
return
fi
done
sleep 0.1
n=$((n+1))
done
}
# auto detect which network interface to auto configure
# check which is connected or fallback to first
# This will set link to down to all eth* except the found
auto_detect_ethernet_interface() {
local ifaces="$(ethernets)"
[ -z "$ifaces" ] && return
# find first connected interface
for i in $ifaces; do
$MOCK ip link set dev $i up >/dev/null
done
local iface="$(find_first_interface_up $ifaces)"
# use first if all are disconnected
if [ -z "$iface" ]; then
set -- $ifaces
iface="$1"
fi
# we will use the found interface later so lets keep it up
for i in $ifaces; do
if [ "$i" != "$iface" ]; then
$MOCK ip link set dev $i down >/dev/null
fi
done
echo "$iface"
}
# may be overridded by provider
want_ephemeral_network() {
false
}
init__set_ephemeral_network() {
if ! want_ephemeral_network; then
return
fi
local iface="$(auto_detect_ethernet_interface)"
if [ -z "$iface" ]; then
return
fi
$MOCK udhcpc -i "$iface" -f -q
}
init__set_default_interfaces() {
if [ -f "$ETC"/network/interfaces ]; then
log -i -t "$phase" info "$ACTION: already set up"
return
fi
mkdir -p "$ETC/network"
printf "%s\n%s\n\n" \
"auto lo" \
"iface lo inet loopback" \
> "$ETC/network/interfaces"
local iface="$(auto_detect_ethernet_interface)"
if [ -z "$iface" ]; then
# TODO: message/log?
return
fi
printf "%s\n%s\n\t%s\n\n" \
"auto $iface" \
"iface $iface" \
"use dhcp" >> "$ETC/network/interfaces"
}
init__create_default_user() {
local user="$CLOUD_USER"
if [ "$user" = "none" ] || [ -z "$user" ]; then
log -i -t "$phase" info "$ACTION: skip"
return
fi
# don't do anything if it already exists
if getent passwd "$user" >/dev/null; then
log -i -t "$phase" info "$ACTION: already exists"
return
fi
$MOCK addgroup "$user"
$MOCK adduser -h "/home/$user" -s /bin/sh -G "$user" -D "$user"
$MOCK addgroup "$user" wheel
echo "$user:*" | $MOCK chpasswd -e
# setup sudo and/or doas
if [ -d "$ETC/sudoers.d" ]; then
echo '%wheel ALL=(ALL) NOPASSWD: ALL' > "$ETC/sudoers.d/wheel"
fi
if [ -d "$ETC/doas.d" ]; then
echo 'permit nopass :wheel' > "$TARGET/etc/doas.d/wheel.conf"
elif [ -f "$ETC/doas.conf" ]; then
add_once "$TARGET/etc/doas.conf" "permit nopass :wheel"
fi
}
init__enable_sshd() {
$MOCK rc-update add sshd default
# in case something else has enabled/disabled dservices
$MOCK rc-update --update
}
### standard early phase functions
init__save_userdata() {
local userdata="$TINY_CLOUD_VAR/user-data"
local tmpfile=$(mktemp "$userdata.XXXXXX")
imds -e @userdata > "$tmpfile"
if printf '\037\213\010' | cmp -s -n 3 "$tmpfile"; then
gzip -dc "$tmpfile" > "$userdata"
elif printf 'BZh' | cmp -s -n 3 "$tmpfile"; then
bzip2 -dc "$tmpfile" > "$userdata"
elif printf '\375\067\172\130\132\000' | cmp -s -n 6 "$tmpfile"; then
unxz -c "$tmpfile" > "$userdata"
elif printf '\135\000\000' | cmp -s -n 3 "$tmpfile"; then
lzma -dc "$tmpfile" > "$userdata"
elif printf '\211\114\132' | cmp -s -n 3 "$tmpfile"; then
lzop -dc "$tmpfile" > "$userdata"
elif printf '\004\042\115\030' | cmp -s -n 4 "$tmpfile"; then
lz4 -dc "$tmpfile" > "$userdata"
elif printf '(\265/\375' | cmp -s -n 4 "$tmpfile"; then
zstd -dc "$tmpfile" > "$userdata"
else
cp "$tmpfile" "$userdata"
fi
rm "$tmpfile"
}
### standard main phase functions
init__set_hostname() {
local fqdn=$(imds @hostname)
if [ -z "$fqdn" ]; then
log -i -t "$phase" info "$ACTION: no hostname set"
return
fi
local host="${fqdn%%\.*}"
if [ -z "$host" ]; then
log -i -t "$phase" warning "$ACTION: invalid hostname '$fqdn'"
return 1
fi
mkdir -p "$ETC"
echo "$host" > "$ETC"/hostname
$MOCK hostname -F "$ETC"/hostname
echo -e "127.0.1.1\t$fqdn $host" >> "$ETC"/hosts
}
init__set_ssh_keys() {
local sshkeys="$(imds @ssh-keys)"
if [ -z "$sshkeys" ]; then
log -i -t "$phase" info "$ACTION: no ssh key found"
return
fi
local user="$CLOUD_USER"
local pwent="$(getent passwd "$user")"
if [ -z "$pwent" ]; then
log -i -t "$phase" err "$ACTION: failed to find user $user"
return 1
fi
local group=$(echo "$pwent" | cut -d: -f4)
local ssh_dir="${ROOT}$(echo "$pwent" | cut -d: -f6)/.ssh"
local keys_file="$ssh_dir/authorized_keys"
if [ ! -d "$ssh_dir" ]; then
mkdir -p "$ssh_dir"
chmod 700 "$ssh_dir"
fi
touch "$keys_file"
chmod 600 "$keys_file"
$MOCK chown -R "$user:$group" "$ssh_dir"
echo "$sshkeys" > "$keys_file"
}
### standard final phase functions would be here, if there were any
### load cloud-specific init functions / vars (potentially overriding)
if [ "$CLOUD" = "alpine" ]; then
log -i -t "$phase" warning "CLOUD provider alpine is deprecated. Use nocloud"
CLOUD="nocloud"
fi
if [ -f "$LIBDIR/tiny-cloud/cloud/$CLOUD/init" ]; then
. "$LIBDIR/tiny-cloud/cloud/$CLOUD/init"
fi
### load user-data type-specific init functions / vars (potentially overriding)
userdata_type() {
if [ ! -f "$TINY_CLOUD_VAR/user-data" ]; then
echo missing
return
fi
header=$(head -n1 "$TINY_CLOUD_VAR/user-data" | sed -e 's/[[:space:]].*//g')
case "$header" in
'#!'*) echo script;;
'#'*) echo ${header#\#};;
*) echo unknown;;
esac
}
USERDATA_TYPE="$(userdata_type)"
if [ -f "$LIBDIR/tiny-cloud/user-data/$USERDATA_TYPE" ]; then
. "$LIBDIR/tiny-cloud/user-data/$USERDATA_TYPE"
fi