build-alpine
This commit is contained in:
commit
eaab627be2
|
@ -0,0 +1,20 @@
|
|||
# Use the latest Alpine Linux image
|
||||
FROM alpine:latest
|
||||
|
||||
# Install necessary packages including bash
|
||||
RUN apk update && apk add --no-cache alpine-sdk squashfs-tools xorriso openssh wget bash p7zip
|
||||
|
||||
# Set environment variable for SSH public key (replace with your actual key)
|
||||
ENV SSH_PUBLIC_KEY="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDsKFCdYNW+ShGM89hrN1lRBOeH7Am1DM3RuF5ODl9B3ZBJyL3qbixWipys3twNuHF8PxjRdE4OeXawY8w5U/FdYx9WtZ+/jfnpkIt85hzRP6wGm2Gb3YvYlukMoWRJTrPzY9B1wWZ0lUdwjryh6rsrIiuL/nYi4DMJWVfLLM5i8Pjzqq4rFT0hJDUg4I2cXspCec1YoRFKMKCqIGoduWpjhGm6P23fjz+8GXuB1DfhQcu3TkJF90tn3DUpiQ2BW4PreCtyTzcDdbhIo1O9x3abAeAkOnZtIK2DXdmbtgjCJg9yHgtjQQDV3WT0YkpHGsj3x9/xmhIr2Mcf18Ohjoo81hfL4raWAzGHrQFdc5bHop9M95iqp+0GbxGUhc4Wvy9iptuqfqclZdibOMGeX2vGJsqEkdwuWHpjtQ/12dGTGI9crRP+d/Nch+rc1Dz+cuLfDMuNcS0bqAk4eSeFbgsoOBqZeWcMG0m+ZKgyl4Q+uhBgUoHCoC4FHSJUnMfkKhs= root@868a158393be"
|
||||
|
||||
# Copy build script into container
|
||||
COPY build-alpine.sh /build-alpine.sh
|
||||
|
||||
# Make the build script executable
|
||||
RUN chmod +x /build-alpine.sh
|
||||
|
||||
# Create output directory
|
||||
RUN mkdir /output
|
||||
|
||||
# Set the default command to run the build script using bash
|
||||
CMD ["/bin/bash", "/build-alpine.sh"]
|
|
@ -0,0 +1,744 @@
|
|||
#!/bin/sh
|
||||
# vim: set ts=4 sw=4:
|
||||
# SPDX-FileCopyrightText: © 2017 Jakub Jirutka <jakub@jirutka.cz>
|
||||
# SPDX-License-Identifier: MIT
|
||||
#---help---
|
||||
# Usage: alpine-make-vm-image [options] [--] <image> [<script> [<script-opts...>]]
|
||||
#
|
||||
# This script creates a bootable Alpine Linux disk image for virtual machines.
|
||||
# If running on Alpine system (detected by file /etc/alpine-release), then it
|
||||
# also installs needed packages on the host system. On other systems you must
|
||||
# install them yourself: qemu-img, qemu-nbd, rsync, sfdisk, dosfstools (for UEFI
|
||||
# mode) and mkfs utility for the chosen ROOTFS. If $APK is not available on the
|
||||
# host system, then static apk-tools specified by $APK_TOOLS_URI is downloaded
|
||||
# and used.
|
||||
#
|
||||
# Note that in BIOS mode, it does not create any partitions by default (it's
|
||||
# really not needed), filesystem is created directly on the image. If you must
|
||||
# use some platform that foolishly requires partitions in images, use
|
||||
# --partition.
|
||||
#
|
||||
# Arguments:
|
||||
# <image> Path of block device or disk image file to use or create
|
||||
# if it does not exist.
|
||||
#
|
||||
# <script> Path of script to execute after installing base system in
|
||||
# the mounted image and before umounting it.
|
||||
#
|
||||
# <script-opts> Arguments to pass to the script.
|
||||
#
|
||||
# Options and Environment Variables:
|
||||
# -a --arch ARCH Target CPU architecture. If it doesn't match the host's
|
||||
# architecture, you must register an interpreter (emulator)
|
||||
# for the target architecture in binfmt, e.g. qemu userspace
|
||||
# emulator. Options: x86_64, aarch64 (Alpine v3.19+ / edge).
|
||||
#
|
||||
# -B --boot-mode BOOT_MODE Either "BIOS" (default on x86/x86_64) or "UEFI" (default
|
||||
# on others). "BIOS" is supported only on x86/x86_64.
|
||||
#
|
||||
# -b --branch ALPINE_BRANCH Alpine branch to install; used only when
|
||||
# --repositories-file is not specified. Default is
|
||||
# latest-stable.
|
||||
#
|
||||
# -S --fs-skel-dir FS_SKEL_DIR Path of directory which content to recursively copy
|
||||
# (using rsync) into / in the image.
|
||||
#
|
||||
# --fs-skel-chown FS_SKEL_CHOWN Force all files from FS_SKEL_DIR to be owned by the
|
||||
# specified USER:GROUP.
|
||||
#
|
||||
# -P --partition (PARTITION) Create GUID Partition Table (GPT) with a single partition.
|
||||
# GPT is always created if --boot-mode is UEFI.
|
||||
#
|
||||
# -f --image-format IMAGE_FORMAT Format of the disk image (see qemu-img --help). Not
|
||||
# applicable if the target image is a block device.
|
||||
#
|
||||
# -s --image-size IMAGE_SIZE Size of the disk image to create in bytes or with suffix
|
||||
# (e.g. 1G, 1024M). Default is 2G. Not applicable if the
|
||||
# target image is a block device.
|
||||
#
|
||||
# -i --initfs-features INITFS_FEATURES List of additional mkinitfs features (basically modules)
|
||||
# to be included in initramfs (see mkinitfs -L). "base" and
|
||||
# $ROOTFS is always included, don't specify them here.
|
||||
# Default is "kms scsi virtio".
|
||||
#
|
||||
# -k --kernel-flavor KERNEL_FLAVOR The kernel flavour to install; virt (default), lts
|
||||
# (Alpine >=3.11), or vanilla (Alpine <3.11).
|
||||
#
|
||||
# --keys-dir KEYS_DIR Path of directory with Alpine keys to copy into the image.
|
||||
# Default is /etc/apk/keys if --arch matches the host arch.
|
||||
# If does not exist, keys for aarch64 and x86_64 embedded in
|
||||
# this script will be used.
|
||||
#
|
||||
# -m --mirror-uri ALPINE_MIRROR URI of the Aports mirror to fetch packages; used only
|
||||
# when --repositories-file is not specified. Default is
|
||||
# http://dl-cdn.alpinelinux.org/alpine.
|
||||
#
|
||||
# -C --no-cleanup (CLEANUP) Don't umount and disconnect image when done.
|
||||
#
|
||||
# -p --packages PACKAGES Additional packages to install into the image.
|
||||
#
|
||||
# -r --repositories-file REPOS_FILE Path of the repositories file to copy into the rootfs.
|
||||
# If not provided, Alpine's main and community repositories
|
||||
# on --mirror-uri will be used.
|
||||
#
|
||||
# --rootfs ROOTFS Filesystem to create on the image. Default is ext4.
|
||||
#
|
||||
# -c --script-chroot (SCRIPT_CHROOT) Bind <script>'s directory at /mnt inside image and chroot
|
||||
# into the image before executing <script>.
|
||||
#
|
||||
# -t --serial-console (SERIAL_CONSOLE) Add configuration for a serial console on ttyS0.
|
||||
#
|
||||
# -h --help Show this help message and exit.
|
||||
#
|
||||
# -V --version Print version and exit.
|
||||
#
|
||||
# APK APK command to use. Default is "apk".
|
||||
#
|
||||
# APK_OPTS Options to pass into apk on each execution.
|
||||
# Default is "--no-progress".
|
||||
#
|
||||
# APK_TOOLS_URI URL of apk-tools binary to download if $APK is not found
|
||||
# on the host system. Default is apk.static from
|
||||
# https://gitlab.alpinelinux.org/alpine/apk-tools/-/packages.
|
||||
#
|
||||
# APK_TOOLS_SHA256 SHA-256 checksum of $APK_TOOLS_URI.
|
||||
#
|
||||
# Each option can be also provided by environment variable. If both option and
|
||||
# variable is specified and the option accepts only one argument, then the
|
||||
# option takes precedence.
|
||||
#
|
||||
# https://github.com/alpinelinux/alpine-make-vm-image
|
||||
#---help---
|
||||
set -eu
|
||||
|
||||
# Some distros (e.g. Arch Linux) does not have /bin or /sbin in PATH.
|
||||
PATH="$PATH:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
|
||||
readonly PROGNAME='alpine-make-vm-image'
|
||||
readonly VERSION='0.13.0'
|
||||
readonly VIRTUAL_PKG=".make-$PROGNAME"
|
||||
# An opaque string used to detect changes in resolv.conf.
|
||||
readonly RESOLVCONF_MARK="### created by $PROGNAME ###"
|
||||
|
||||
# Alpine APK keys for verification of packages for x86_64 and aarch64.
|
||||
readonly ALPINE_KEYS='
|
||||
4a6a0840:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1yHJxQgsHQREclQu4Ohe\nqxTxd1tHcNnvnQTu/UrTky8wWvgXT+jpveroeWWnzmsYlDI93eLI2ORakxb3gA2O\nQ0Ry4ws8vhaxLQGC74uQR5+/yYrLuTKydFzuPaS1dK19qJPXB8GMdmFOijnXX4SA\njixuHLe1WW7kZVtjL7nufvpXkWBGjsfrvskdNA/5MfxAeBbqPgaq0QMEfxMAn6/R\nL5kNepi/Vr4S39Xvf2DzWkTLEK8pcnjNkt9/aafhWqFVW7m3HCAII6h/qlQNQKSo\nGuH34Q8GsFG30izUENV9avY7hSLq7nggsvknlNBZtFUcmGoQrtx3FmyYsIC8/R+B\nywIDAQAB
|
||||
5261cecb:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwlzMkl7b5PBdfMzGdCT0\ncGloRr5xGgVmsdq5EtJvFkFAiN8Ac9MCFy/vAFmS8/7ZaGOXoCDWbYVLTLOO2qtX\nyHRl+7fJVh2N6qrDDFPmdgCi8NaE+3rITWXGrrQ1spJ0B6HIzTDNEjRKnD4xyg4j\ng01FMcJTU6E+V2JBY45CKN9dWr1JDM/nei/Pf0byBJlMp/mSSfjodykmz4Oe13xB\nCa1WTwgFykKYthoLGYrmo+LKIGpMoeEbY1kuUe04UiDe47l6Oggwnl+8XD1MeRWY\nsWgj8sF4dTcSfCMavK4zHRFFQbGp/YFJ/Ww6U9lA3Vq0wyEI6MCMQnoSMFwrbgZw\nwwIDAQAB
|
||||
6165ee59:MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAutQkua2CAig4VFSJ7v54\nALyu/J1WB3oni7qwCZD3veURw7HxpNAj9hR+S5N/pNeZgubQvJWyaPuQDm7PTs1+\ntFGiYNfAsiibX6Rv0wci3M+z2XEVAeR9Vzg6v4qoofDyoTbovn2LztaNEjTkB+oK\ntlvpNhg1zhou0jDVYFniEXvzjckxswHVb8cT0OMTKHALyLPrPOJzVtM9C1ew2Nnc\n3848xLiApMu3NBk0JqfcS3Bo5Y2b1FRVBvdt+2gFoKZix1MnZdAEZ8xQzL/a0YS5\nHd0wj5+EEKHfOd3A75uPa/WQmA+o0cBFfrzm69QDcSJSwGpzWrD1ScH3AK8nWvoj\nv7e9gukK/9yl1b4fQQ00vttwJPSgm9EnfPHLAtgXkRloI27H6/PuLoNvSAMQwuCD\nhQRlyGLPBETKkHeodfLoULjhDi1K2gKJTMhtbnUcAA7nEphkMhPWkBpgFdrH+5z4\nLxy+3ek0cqcI7K68EtrffU8jtUj9LFTUC8dERaIBs7NgQ/LfDbDfGh9g6qVj1hZl\nk9aaIPTm/xsi8v3u+0qaq7KzIBc9s59JOoA8TlpOaYdVgSQhHHLBaahOuAigH+VI\nisbC9vmqsThF2QdDtQt37keuqoda2E6sL7PUvIyVXDRfwX7uMDjlzTxHTymvq2Ck\nhtBqojBnThmjJQFgZXocHG8CAwEAAQ==
|
||||
58199dcc:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3v8/ye/V/t5xf4JiXLXa\nhWFRozsnmn3hobON20GdmkrzKzO/eUqPOKTpg2GtvBhK30fu5oY5uN2ORiv2Y2ht\neLiZ9HVz3XP8Fm9frha60B7KNu66FO5P2o3i+E+DWTPqqPcCG6t4Znk2BypILcit\nwiPKTsgbBQR2qo/cO01eLLdt6oOzAaF94NH0656kvRewdo6HG4urbO46tCAizvCR\nCA7KGFMyad8WdKkTjxh8YLDLoOCtoZmXmQAiwfRe9pKXRH/XXGop8SYptLqyVVQ+\ntegOD9wRs2tOlgcLx4F/uMzHN7uoho6okBPiifRX+Pf38Vx+ozXh056tjmdZkCaV\naQIDAQAB
|
||||
616ae350:MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyduVzi1mWm+lYo2Tqt/0\nXkCIWrDNP1QBMVPrE0/ZlU2bCGSoo2Z9FHQKz/mTyMRlhNqTfhJ5qU3U9XlyGOPJ\npiM+b91g26pnpXJ2Q2kOypSgOMOPA4cQ42PkHBEqhuzssfj9t7x47ppS94bboh46\nxLSDRff/NAbtwTpvhStV3URYkxFG++cKGGa5MPXBrxIp+iZf9GnuxVdST5PGiVGP\nODL/b69sPJQNbJHVquqUTOh5Ry8uuD2WZuXfKf7/C0jC/ie9m2+0CttNu9tMciGM\nEyKG1/Xhk5iIWO43m4SrrT2WkFlcZ1z2JSf9Pjm4C2+HovYpihwwdM/OdP8Xmsnr\nDzVB4YvQiW+IHBjStHVuyiZWc+JsgEPJzisNY0Wyc/kNyNtqVKpX6dRhMLanLmy+\nf53cCSI05KPQAcGj6tdL+D60uKDkt+FsDa0BTAobZ31OsFVid0vCXtsbplNhW1IF\nHwsGXBTVcfXg44RLyL8Lk/2dQxDHNHzAUslJXzPxaHBLmt++2COa2EI1iWlvtznk\nOk9WP8SOAIj+xdqoiHcC4j72BOVVgiITIJNHrbppZCq6qPR+fgXmXa+sDcGh30m6\n9Wpbr28kLMSHiENCWTdsFij+NQTd5S47H7XTROHnalYDuF1RpS+DpQidT5tUimaT\nJZDr++FjKrnnijbyNF8b98UCAwEAAQ==
|
||||
'
|
||||
|
||||
readonly HOST_ARCH="$(uname -m)"
|
||||
|
||||
# SHA256 checksum of $APK_TOOLS_URI for each architecture.
|
||||
case "$HOST_ARCH" in
|
||||
aarch64) : ${APK_TOOLS_SHA256:="d49a63b8b6780fc1342d3e7e14862aa006c30bafbf74beec8e1dfe99e6f89471"};;
|
||||
x86_64) : ${APK_TOOLS_SHA256:="1c65115a425d049590bec7c729c7fd88357fbb090a6fc8c31d834d7b0bc7d6f2"};;
|
||||
esac
|
||||
|
||||
: ${APK_TOOLS_URI:="https://gitlab.alpinelinux.org/api/v4/projects/5/packages/generic/v2.14.0/$HOST_ARCH/apk.static"}
|
||||
: ${APK:="apk"}
|
||||
: ${APK_OPTS:="--no-progress"}
|
||||
|
||||
|
||||
# For compatibility with systems that does not have "realpath" command.
|
||||
if ! command -v realpath >/dev/null; then
|
||||
alias realpath='readlink -f'
|
||||
fi
|
||||
|
||||
die() {
|
||||
printf '\033[1;31mERROR:\033[0m %s\n' "$@" >&2 # bold red
|
||||
exit 1
|
||||
}
|
||||
|
||||
einfo() {
|
||||
printf '\n\033[1;36m> %s\033[0m\n' "$@" >&2 # bold cyan
|
||||
}
|
||||
|
||||
# Prints help and exists with the specified status.
|
||||
help() {
|
||||
sed -En '/^#---help---/,/^#---help---/p' "$0" | sed -E 's/^# ?//; 1d;$d;'
|
||||
exit ${1:-0}
|
||||
}
|
||||
|
||||
# Cleans the host system. This function is executed before exiting the script.
|
||||
cleanup() {
|
||||
set +eu
|
||||
trap '' EXIT HUP INT TERM # unset trap to avoid loop
|
||||
|
||||
cd /
|
||||
if [ -d "$temp_dir" ]; then
|
||||
rm -Rf "$temp_dir"
|
||||
fi
|
||||
if [ "$mount_dir" ]; then
|
||||
umount_recursively "$mount_dir" \
|
||||
|| die "Failed to unmount $mount_dir; unmount it and disconnect $disk_dev manually"
|
||||
rm -Rf "$mount_dir"
|
||||
fi
|
||||
if [ "$disk_dev" ] && ! [ -b "$IMAGE_FILE" ]; then
|
||||
qemu-nbd --disconnect "$disk_dev" \
|
||||
|| die "Failed to disconnect $disk_dev; disconnect it manually"
|
||||
fi
|
||||
if [ "$INSTALL_HOST_PKGS" = yes ]; then
|
||||
_apk del $VIRTUAL_PKG
|
||||
fi
|
||||
}
|
||||
|
||||
_apk() {
|
||||
"$APK" $APK_OPTS "$@"
|
||||
}
|
||||
|
||||
# Attaches the specified image as a NBD block device and prints its path.
|
||||
attach_image() {
|
||||
local image="$1"
|
||||
local format="${2:-}"
|
||||
local disk_dev
|
||||
|
||||
disk_dev=$(get_available_nbd) || {
|
||||
modprobe nbd max_part=16
|
||||
sleep 1
|
||||
disk_dev=$(get_available_nbd)
|
||||
} || die 'No available nbd device found!'
|
||||
|
||||
qemu-nbd --connect="$disk_dev" --cache=writeback \
|
||||
${format:+--format=$format} "$image" \
|
||||
&& echo "$disk_dev"
|
||||
}
|
||||
|
||||
# Prints UUID of filesystem on the specified block device.
|
||||
blk_uuid() {
|
||||
local dev="$1"
|
||||
blkid "$dev" | sed -En 's/.*\bUUID="([^"]+)".*/\1/p'
|
||||
}
|
||||
|
||||
# Creates GPT parition table on the device $1 for boot mode $2 (BIOS or UEFI).
|
||||
create_gpt() {
|
||||
local dev="$1"
|
||||
local mode="$2"
|
||||
|
||||
case "$mode" in
|
||||
BIOS) printf '%s\n' \
|
||||
'label: gpt' \
|
||||
'name=system,type=L,bootable,attrs=LegacyBIOSBootable' \
|
||||
| sfdisk "$dev" ;;
|
||||
UEFI) printf '%s\n' \
|
||||
'label: gpt' \
|
||||
'name=efi,type=U,size=128M,bootable' \
|
||||
'name=system,type=L' \
|
||||
| sfdisk "$disk_dev" ;;
|
||||
*) die "Invalid mode: $mode" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Writes Alpine APK keys embedded in this script into directory $1.
|
||||
dump_alpine_keys() {
|
||||
local dest_dir="$1"
|
||||
local content id line
|
||||
|
||||
mkdir -p "$dest_dir"
|
||||
for line in $ALPINE_KEYS; do
|
||||
id=${line%%:*}
|
||||
content=${line#*:}
|
||||
|
||||
printf -- "-----BEGIN PUBLIC KEY-----\n$content\n-----END PUBLIC KEY-----\n" \
|
||||
> "$dest_dir/alpine-devel@lists.alpinelinux.org-$id.rsa.pub"
|
||||
done
|
||||
}
|
||||
|
||||
# Prints path of available nbdX device, or returns 1 if not any.
|
||||
get_available_nbd() {
|
||||
local dev; for dev in $(find /dev -maxdepth 2 -name 'nbd[0-9]*'); do
|
||||
if [ "$(blockdev --getsize64 "$dev")" -eq 0 ]; then
|
||||
echo "$dev"; return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Prints name of the package needed for creating the specified filesystem.
|
||||
fs_progs_pkg() {
|
||||
local fs="$1" # filesystem name
|
||||
|
||||
case "$fs" in
|
||||
ext4) echo 'e2fsprogs';;
|
||||
btrfs) echo 'btrfs-progs';;
|
||||
xfs) echo 'xfsprogs';;
|
||||
esac
|
||||
}
|
||||
|
||||
# Tests if the specified command exists on the system.
|
||||
has_cmd() {
|
||||
command -v "$1" >/dev/null
|
||||
}
|
||||
|
||||
# Binds the directory $1 at the mountpoint $2 and sets propagation to private.
|
||||
mount_bind() {
|
||||
mkdir -p "$2"
|
||||
mount --bind "$1" "$2"
|
||||
mount --make-private "$2"
|
||||
}
|
||||
|
||||
# Prepares chroot at the specified path.
|
||||
prepare_chroot() {
|
||||
local dest="$1"
|
||||
|
||||
mkdir -p "$dest"/proc
|
||||
mount -t proc none "$dest"/proc
|
||||
mount_bind /dev "$dest"/dev
|
||||
mount_bind /sys "$dest"/sys
|
||||
|
||||
install -D -m 644 /etc/resolv.conf "$dest"/etc/resolv.conf
|
||||
echo "$RESOLVCONF_MARK" >> "$dest"/etc/resolv.conf
|
||||
}
|
||||
|
||||
# Adds specified services to the runlevel. Current working directory must be
|
||||
# root of the image.
|
||||
rc_add() {
|
||||
local runlevel="$1"; shift # runlevel name
|
||||
local services="$*" # names of services
|
||||
|
||||
local svc; for svc in $services; do
|
||||
mkdir -p etc/runlevels/$runlevel
|
||||
ln -s /etc/init.d/$svc etc/runlevels/$runlevel/$svc
|
||||
echo " * service $svc added to runlevel $runlevel"
|
||||
done
|
||||
}
|
||||
|
||||
# Ensures that the specified device node exists.
|
||||
settle_dev_node() {
|
||||
local dev="$1"
|
||||
|
||||
[ -e "$dev" ] && return 0
|
||||
|
||||
sleep 1 # give hotplug handler some time to kick in
|
||||
[ -e "$dev" ] && return 0
|
||||
|
||||
if has_cmd udevadm; then
|
||||
udevadm settle --exit-if-exists="$dev"
|
||||
elif has_cmd mdev; then
|
||||
mdev -s
|
||||
fi
|
||||
[ -e "$dev" ] && return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Installs and configures extlinux.
|
||||
setup_extlinux() {
|
||||
local mnt="$1" # path of directory where is root device currently mounted
|
||||
local root_dev="$2" # root device
|
||||
local modules="$3" # modules which should be loaded before pivot_root
|
||||
local kernel_flavor="$4" # name of default kernel to boot
|
||||
local serial_port="$5" # serial port number for serial console
|
||||
local default_kernel="$kernel_flavor"
|
||||
local kernel_opts=''
|
||||
|
||||
[ -z "$serial_port" ] || kernel_opts="console=$serial_port"
|
||||
|
||||
if [ "$kernel_flavor" = 'virt' ]; then
|
||||
_apk search --root . --exact --quiet linux-lts | grep -q . \
|
||||
&& default_kernel='lts' \
|
||||
|| default_kernel='vanilla'
|
||||
fi
|
||||
|
||||
sed -Ei \
|
||||
-e "s|^[# ]*(root)=.*|\1=$root_dev|" \
|
||||
-e "s|^[# ]*(default_kernel_opts)=.*|\1=\"$kernel_opts\"|" \
|
||||
-e "s|^[# ]*(modules)=.*|\1=\"$modules\"|" \
|
||||
-e "s|^[# ]*(default)=.*|\1=$default_kernel|" \
|
||||
-e "s|^[# ]*(serial_port)=.*|\1=$serial_port|" \
|
||||
"$mnt"/etc/update-extlinux.conf
|
||||
|
||||
chroot "$mnt" extlinux --install /boot
|
||||
chroot "$mnt" update-extlinux --warn-only 2>&1 \
|
||||
| { grep -Fv 'extlinux: cannot open device /dev' ||:; } >&2
|
||||
}
|
||||
|
||||
# Configures mkinitfs.
|
||||
setup_mkinitfs() {
|
||||
local mnt="$1" # path of directory where is root device currently mounted
|
||||
local features="$2" # list of mkinitfs features
|
||||
|
||||
features=$(printf '%s\n' $features | sort | uniq | xargs)
|
||||
|
||||
sed -Ei "s|^[# ]*(features)=.*|\1=\"$features\"|" \
|
||||
"$mnt"/etc/mkinitfs/mkinitfs.conf
|
||||
}
|
||||
|
||||
# Unmounts all filesystem under the specified directory tree.
|
||||
umount_recursively() {
|
||||
local mount_point="$1"
|
||||
test -n "$mount_point" || return 1
|
||||
|
||||
cat /proc/mounts \
|
||||
| cut -d ' ' -f 2 \
|
||||
| grep "^$mount_point" \
|
||||
| sort -r \
|
||||
| xargs umount -rn
|
||||
}
|
||||
|
||||
# Downloads the specified file using wget and checks checksum.
|
||||
wgets() (
|
||||
local url="$1"
|
||||
local sha256="$2"
|
||||
local dest="${3:-.}"
|
||||
|
||||
cd "$dest" \
|
||||
&& wget -T 10 --no-verbose "$url" \
|
||||
&& echo "$sha256 ${url##*/}" | sha256sum -c
|
||||
)
|
||||
|
||||
|
||||
#============================= M a i n ==============================#
|
||||
|
||||
opts=$(getopt -n $PROGNAME -o a:b:B:cCf:hi:k:m:p:Pr:s:S:tV \
|
||||
-l arch:,boot-mode:,branch:,fs-skel-chown:,fs-skel-dir:,image-format:,image-size:,initfs-features:,kernel-flavor:,keys-dir:,mirror-uri:,no-cleanup,packages:,partition,repositories-file:,rootfs:,script-chroot,serial-console,help,version \
|
||||
-- "$@") || help 1 >&2
|
||||
|
||||
eval set -- "$opts"
|
||||
while [ $# -gt 0 ]; do
|
||||
n=2
|
||||
case "$1" in
|
||||
-a | --arch) ARCH="$2";;
|
||||
-B | --boot-mode) BOOT_MODE="$2";;
|
||||
-b | --branch) ALPINE_BRANCH="$2";;
|
||||
-S | --fs-skel-dir) FS_SKEL_DIR="$(realpath "$2")";;
|
||||
--fs-skel-chown) FS_SKEL_CHOWN="$2";;
|
||||
-f | --image-format) IMAGE_FORMAT="$2";;
|
||||
-s | --image-size) IMAGE_SIZE="$2";;
|
||||
-i | --initfs-features) INITFS_FEATURES="${INITFS_FEATURES:-} $2";;
|
||||
-k | --kernel-flavor) KERNEL_FLAVOR="$2";;
|
||||
--keys-dir) KEYS_DIR="$(realpath "$2")";;
|
||||
-m | --mirror-uri) ALPINE_MIRROR="$2";;
|
||||
-C | --no-cleanup) CLEANUP='no'; n=1;;
|
||||
-p | --packages) PACKAGES="${PACKAGES:-} $2";;
|
||||
-P | --partition) PARTITION='yes'; n=1;;
|
||||
-r | --repositories-file) REPOS_FILE="$(realpath "$2")";;
|
||||
--rootfs) ROOTFS="$2";;
|
||||
-t | --serial-console) SERIAL_CONSOLE='yes'; n=1;;
|
||||
-c | --script-chroot) SCRIPT_CHROOT='yes'; n=1;;
|
||||
-h | --help) help 0;;
|
||||
-V | --version) echo "$PROGNAME $VERSION"; exit 0;;
|
||||
--) shift; break;;
|
||||
esac
|
||||
shift $n
|
||||
done
|
||||
|
||||
: ${ALPINE_BRANCH:="latest-stable"}
|
||||
: ${ALPINE_MIRROR:="http://dl-cdn.alpinelinux.org/alpine"}
|
||||
: ${ARCH:=}
|
||||
: ${CLEANUP:="yes"}
|
||||
: ${FS_SKEL_CHOWN:=}
|
||||
: ${FS_SKEL_DIR:=}
|
||||
: ${IMAGE_FORMAT:=}
|
||||
: ${IMAGE_SIZE:="2G"}
|
||||
# kms includes virtio-gpu drivers used for graphical output. This is needed
|
||||
# for aarch64 where '-vga std' is not supported, but useful even for x86_64.
|
||||
: ${INITFS_FEATURES:="kms scsi virtio"}
|
||||
: ${KERNEL_FLAVOR:="virt"}
|
||||
: ${PACKAGES:=}
|
||||
: ${PARTITION:="no"}
|
||||
: ${REPOS_FILE:=}
|
||||
: ${ROOTFS:="ext4"}
|
||||
: ${SCRIPT_CHROOT:="no"}
|
||||
: ${SERIAL_CONSOLE:="no"}
|
||||
|
||||
case "$ALPINE_BRANCH" in
|
||||
[0-9]*) ALPINE_BRANCH="v$ALPINE_BRANCH";;
|
||||
esac
|
||||
|
||||
if [ -z "$ARCH" ] || [ "$HOST_ARCH" = "$ARCH" ]; then
|
||||
: ${KEYS_DIR:="/etc/apk/keys"}
|
||||
else
|
||||
: ${KEYS_DIR:=}
|
||||
fi
|
||||
|
||||
case "${ARCH:-"$HOST_ARCH"}" in
|
||||
x86*) : ${BOOT_MODE:="BIOS"};;
|
||||
*) : ${BOOT_MODE:="UEFI"}
|
||||
[ "$BOOT_MODE" = 'BIOS' ] && die 'BIOS boot mode is supported only on x86/x86_64';;
|
||||
esac
|
||||
|
||||
case "$BOOT_MODE" in
|
||||
BIOS | UEFI) ;;
|
||||
*) die "Invalid boot-mode: $BOOT_MODE";;
|
||||
esac
|
||||
|
||||
if [ -f /etc/alpine-release ]; then
|
||||
: ${INSTALL_HOST_PKGS:="yes"}
|
||||
else
|
||||
: ${INSTALL_HOST_PKGS:="no"}
|
||||
fi
|
||||
|
||||
SERIAL_PORT=
|
||||
[ "$SERIAL_CONSOLE" = 'no' ] || SERIAL_PORT='ttyS0'
|
||||
|
||||
[ $# -ne 0 ] || help 1 >&2
|
||||
|
||||
IMAGE_FILE="$1"; shift
|
||||
SCRIPT=
|
||||
[ $# -eq 0 ] || { SCRIPT=$(realpath "$1"); shift; }
|
||||
|
||||
[ "$CLEANUP" = no ] || trap cleanup EXIT HUP INT TERM
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
if [ "$INSTALL_HOST_PKGS" = yes ]; then
|
||||
einfo 'Installing needed packages on host system'
|
||||
|
||||
# We need load btrfs module to avoid the error message:
|
||||
# 'failed to open /dev/btrfs-control'
|
||||
if ! grep -q -w "$ROOTFS" /proc/filesystems; then
|
||||
modprobe $ROOTFS
|
||||
fi
|
||||
|
||||
[ "$BOOT_MODE" = 'UEFI' ] && vfatpkg='dosfstools' || vfatpkg=
|
||||
_apk add -t $VIRTUAL_PKG qemu-img $(fs_progs_pkg "$ROOTFS") $vfatpkg rsync sfdisk
|
||||
fi
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
temp_dir=''
|
||||
if ! command -v "$APK" >/dev/null; then
|
||||
einfo "$APK not found, downloading static apk-tools"
|
||||
|
||||
temp_dir="$(mktemp -d /tmp/$PROGNAME.XXXXXX)"
|
||||
wgets "$APK_TOOLS_URI" "$APK_TOOLS_SHA256" "$temp_dir"
|
||||
APK="$temp_dir/apk.static"
|
||||
chmod +x "$APK"
|
||||
fi
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
if [ ! -f "$IMAGE_FILE" ] && [ ! -b "$IMAGE_FILE" ]; then
|
||||
einfo "Creating $IMAGE_FORMAT image of size $IMAGE_SIZE"
|
||||
qemu-img create ${IMAGE_FORMAT:+-f $IMAGE_FORMAT} "$IMAGE_FILE" "$IMAGE_SIZE"
|
||||
fi
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
if [ -b "$IMAGE_FILE" ]; then
|
||||
echo "Provided target $IMAGE_FILE is a block device" >&2
|
||||
|
||||
disk_dev="$IMAGE_FILE"
|
||||
else
|
||||
einfo "Attaching image $IMAGE_FILE as a NBD device"
|
||||
|
||||
disk_dev=$(attach_image "$IMAGE_FILE" "$IMAGE_FORMAT")
|
||||
fi
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
if [ "$PARTITION" = yes ] || [ "$BOOT_MODE" = 'UEFI' ]; then
|
||||
einfo 'Creating GPT partition table'
|
||||
|
||||
create_gpt "$disk_dev" "$BOOT_MODE"
|
||||
|
||||
if [ "$BOOT_MODE" = 'BIOS' ]; then
|
||||
root_dev="${disk_dev}p1"
|
||||
else
|
||||
esp_dev="${disk_dev}p1"
|
||||
root_dev="${disk_dev}p2"
|
||||
fi
|
||||
# This is needed when running in a container.
|
||||
settle_dev_node "$root_dev" || die "system didn't create $root_dev node"
|
||||
else
|
||||
root_dev="$disk_dev"
|
||||
fi
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
einfo 'Creating filesystem(s)'
|
||||
|
||||
if [ "$BOOT_MODE" = 'UEFI' ]; then
|
||||
mkfs.fat -F32 -n EFI "$esp_dev"
|
||||
fi
|
||||
|
||||
# syslinux 6.0.3 cannot boot from ext4 w/ 64bit feature enabled.
|
||||
# -E nodiscard / -K - do not attempt to discard blocks at mkfs time (it's
|
||||
# useless for NBD image and prints confusing error).
|
||||
[ "$ROOTFS" = ext4 ] && mkfs_args='-O ^64bit -E nodiscard' || mkfs_args='-K'
|
||||
|
||||
mkfs.$ROOTFS -L root $mkfs_args "$root_dev"
|
||||
|
||||
root_uuid=$(blk_uuid "$root_dev")
|
||||
mount_dir=$(mktemp -d /tmp/$PROGNAME.XXXXXX)
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
einfo "Mounting image at $mount_dir"
|
||||
|
||||
mount "$root_dev" "$mount_dir"
|
||||
|
||||
if [ "$BOOT_MODE" = 'UEFI' ]; then
|
||||
install -d -m 000 "$mount_dir"/boot
|
||||
mount -t vfat "$esp_dev" "$mount_dir"/boot
|
||||
fi
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
einfo 'Installing base system'
|
||||
|
||||
cd "$mount_dir"
|
||||
|
||||
mkdir -p etc/apk/keys
|
||||
if [ "$REPOS_FILE" ]; then
|
||||
install -m 644 "$REPOS_FILE" etc/apk/repositories
|
||||
else
|
||||
cat > etc/apk/repositories <<-EOF
|
||||
$ALPINE_MIRROR/$ALPINE_BRANCH/main
|
||||
$ALPINE_MIRROR/$ALPINE_BRANCH/community
|
||||
EOF
|
||||
fi
|
||||
if [ -d "$KEYS_DIR" ]; then
|
||||
cp "$KEYS_DIR"/* etc/apk/keys/
|
||||
else
|
||||
dump_alpine_keys etc/apk/keys/
|
||||
fi
|
||||
|
||||
# Use APK cache if available.
|
||||
if [ -L /etc/apk/cache ]; then
|
||||
ln -s "$(realpath /etc/apk/cache)" etc/apk/cache
|
||||
fi
|
||||
|
||||
_apk add --root . ${ARCH:+--arch "$ARCH"} --update-cache --initdb alpine-base
|
||||
|
||||
fsprogs_pkgs=
|
||||
# btrfs doesn't use fsck, so we don't have to install btrfs-progs.
|
||||
[ "$ROOTFS" != 'btrfs' ] && fsprogs_pkgs="$(fs_progs_pkg "$ROOTFS")"
|
||||
[ "$BOOT_MODE" = 'UEFI' ] && fsprogs_pkgs="$fsprogs_pkgs dosfstools"
|
||||
[ "$fsprogs_pkgs" ] && _apk add --root . $fsprogs_pkgs
|
||||
|
||||
prepare_chroot .
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
einfo "Installing kernel linux-$KERNEL_FLAVOR"
|
||||
|
||||
if [ "$KERNEL_FLAVOR" = 'virt' ]; then
|
||||
_apk add --root . linux-$KERNEL_FLAVOR
|
||||
else
|
||||
# Avoid installing *all* linux-firmware-* packages (see #21).
|
||||
_apk add --root . linux-$KERNEL_FLAVOR linux-firmware-none
|
||||
fi
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
einfo "Installing and configuring mkinitfs"
|
||||
|
||||
_apk add --root . mkinitfs
|
||||
setup_mkinitfs . "base $ROOTFS $INITFS_FEATURES"
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
if [ "$BOOT_MODE" = 'BIOS' ]; then
|
||||
einfo 'Setting up extlinux bootloader'
|
||||
|
||||
_apk add --root . --no-scripts syslinux
|
||||
setup_extlinux . "UUID=$root_uuid" "$ROOTFS" "$KERNEL_FLAVOR" "$SERIAL_PORT"
|
||||
|
||||
if [ "$PARTITION" = yes ]; then
|
||||
dd bs=440 count=1 conv=notrunc if=usr/share/syslinux/gptmbr.bin of="$disk_dev"
|
||||
sync
|
||||
fi
|
||||
fi
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
einfo 'Configuring system'
|
||||
|
||||
cat > etc/fstab <<-EOF
|
||||
# <fs> <mountpoint> <type> <opts> <dump/pass>
|
||||
UUID=$root_uuid / $ROOTFS relatime 0 1
|
||||
EOF
|
||||
|
||||
if [ "$BOOT_MODE" = 'UEFI' ]; then
|
||||
echo "LABEL=EFI /boot vfat\
|
||||
rw,relatime,fmask=0133,codepage=437,iocharset=ascii,shortname=mixed,utf8,errors=remount-ro 0 2" \
|
||||
>> etc/fstab
|
||||
|
||||
# This script is interpreted by UEFI.
|
||||
# NOTE: When console=$SERIAL_PORT is added here, no messages are printed to
|
||||
# console on QEMU after switch_root. 'console=tty0' must be explicitly set
|
||||
# for aarch64 (it's the default on x86_64).
|
||||
echo "vmlinuz-$KERNEL_FLAVOR" \
|
||||
"initrd=initramfs-$KERNEL_FLAVOR" \
|
||||
"root=UUID=$root_uuid" \
|
||||
"rootfstype=$ROOTFS" \
|
||||
"modules=$(echo $INITFS_FEATURES | tr ' ' ',')" \
|
||||
'console=tty0' \
|
||||
> boot/startup.nsh
|
||||
fi
|
||||
|
||||
# We just prepare this config, but don't start the networking service.
|
||||
cat > etc/network/interfaces <<-EOF
|
||||
auto eth0
|
||||
iface eth0 inet dhcp
|
||||
|
||||
post-up /etc/network/if-post-up.d/*
|
||||
post-down /etc/network/if-post-down.d/*
|
||||
EOF
|
||||
|
||||
mkdir -p etc/network/if-post-up.d
|
||||
mkdir -p etc/network/if-post-down.d
|
||||
|
||||
if [ "$SERIAL_PORT" ]; then
|
||||
echo "$SERIAL_PORT" >> etc/securetty
|
||||
sed -Ei "s|^[# ]*($SERIAL_PORT:.*)|\1|" etc/inittab
|
||||
fi
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
einfo 'Enabling base system services'
|
||||
|
||||
rc_add sysinit devfs dmesg mdev hwdrivers
|
||||
[ -e etc/init.d/cgroups ] && rc_add sysinit cgroups ||: # since v3.8
|
||||
|
||||
rc_add boot modules hwclock swap hostname sysctl bootmisc syslog
|
||||
# urandom was renamed to seedrng in v3.17
|
||||
[ -e etc/init.d/seedrng ] && rc_add boot seedrng || rc_add boot urandom
|
||||
|
||||
rc_add shutdown killprocs savecache mount-ro
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
if [ "$PACKAGES" ]; then
|
||||
einfo 'Installing additional packages'
|
||||
_apk add --root . $PACKAGES
|
||||
fi
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
if [ -L /etc/apk/cache ]; then
|
||||
rm etc/apk/cache >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
if [ "$FS_SKEL_DIR" ]; then
|
||||
einfo "Copying content of $FS_SKEL_DIR into image"
|
||||
|
||||
[ "$FS_SKEL_CHOWN" ] \
|
||||
&& rsync_opts="--chown $FS_SKEL_CHOWN" \
|
||||
|| rsync_opts='--numeric-ids'
|
||||
rsync --archive --info=NAME2 --whole-file $rsync_opts "$FS_SKEL_DIR"/ . >&2
|
||||
|
||||
# rsync may modify perms of the rootfs dir itself, so make sure it's correct.
|
||||
install -d -m 0755 -o root -g root .
|
||||
fi
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
if [ "$SCRIPT" ]; then
|
||||
script_name="${SCRIPT##*/}"
|
||||
|
||||
if [ "$SCRIPT_CHROOT" = 'no' ]; then
|
||||
einfo "Executing script: $script_name $*"
|
||||
"$SCRIPT" "$@" || die 'Script failed'
|
||||
else
|
||||
einfo "Executing script in chroot: $script_name $*"
|
||||
mount_bind "${SCRIPT%/*}" mnt/
|
||||
chroot . /bin/sh -c "cd /mnt && ./$script_name \"\$@\"" -- "$@" \
|
||||
|| die 'Script failed'
|
||||
fi
|
||||
fi
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
if grep -qw "$RESOLVCONF_MARK" etc/resolv.conf 2>/dev/null; then
|
||||
cat > etc/resolv.conf <<-EOF
|
||||
# Default nameservers, replace them with your own.
|
||||
nameserver 1.1.1.1
|
||||
nameserver 2606:4700:4700::1111
|
||||
EOF
|
||||
fi
|
||||
|
||||
rm -Rf var/cache/apk/* ||:
|
||||
|
||||
einfo 'Completed'
|
||||
|
||||
cd - >/dev/null
|
||||
ls -lh "$IMAGE_FILE"
|
|
@ -0,0 +1,60 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# Install necessary packages in the container
|
||||
apk update
|
||||
apk add alpine-sdk squashfs-tools xorriso wget p7zip
|
||||
|
||||
# Define variables
|
||||
ISO_OUTPUT_DIR="/output"
|
||||
ARCH="x86_64"
|
||||
WORK_DIR="/mnt/alpine-rootfs" # Use a directory within the container
|
||||
|
||||
# Create directories
|
||||
mkdir -p "$ISO_OUTPUT_DIR"
|
||||
mkdir -p "$WORK_DIR"
|
||||
|
||||
# Fetch the latest standard image filename using a BusyBox-compatible command
|
||||
LATEST_STANDARD_ISO=$(wget -qO- http://dl-cdn.alpinelinux.org/alpine/latest-stable/releases/$ARCH/latest-releases.yaml | grep -o 'alpine-standard-[0-9.]*-x86_64.iso' | head -n 1)
|
||||
|
||||
# Construct the download URL using the filename
|
||||
STANDARD_ISO_URL="http://dl-cdn.alpinelinux.org/alpine/latest-stable/releases/$ARCH/$LATEST_STANDARD_ISO"
|
||||
|
||||
# Download the latest Alpine standard ISO
|
||||
wget "$STANDARD_ISO_URL" -O alpine-standard.iso
|
||||
|
||||
# Extract ISO contents using 7z
|
||||
7z x alpine-standard.iso -o"$WORK_DIR"
|
||||
|
||||
# Ensure necessary directories exist in the extracted root filesystem
|
||||
mkdir -p "$WORK_DIR/proc" "$WORK_DIR/sys" "$WORK_DIR/dev" "$WORK_DIR/root/.ssh" "$WORK_DIR/etc/init.d"
|
||||
|
||||
# Initialize APK in the root filesystem
|
||||
mkdir -p "$WORK_DIR/etc/apk" "$WORK_DIR/var/lib/apk" "$WORK_DIR/var/cache/apk"
|
||||
cp -r /etc/apk/repositories "$WORK_DIR/etc/apk/"
|
||||
|
||||
# Update repository index in the root filesystem and install `openssh`
|
||||
apk update --root="$WORK_DIR"
|
||||
apk add --root="$WORK_DIR" openssh
|
||||
|
||||
# Modify SSH configuration directly in the WORK_DIR
|
||||
echo 'PermitRootLogin prohibit-password' >> "$WORK_DIR/etc/ssh/sshd_config"
|
||||
echo 'PasswordAuthentication no' >> "$WORK_DIR/etc/ssh/sshd_config"
|
||||
echo 'PermitRootLogin yes' >> "$WORK_DIR/etc/ssh/sshd_config"
|
||||
echo "$SSH_PUBLIC_KEY" > "$WORK_DIR/root/.ssh/authorized_keys"
|
||||
chmod 700 "$WORK_DIR/root/.ssh"
|
||||
chmod 600 "$WORK_DIR/root/.ssh/authorized_keys"
|
||||
|
||||
# Add OpenSSH server to the list of services to start automatically
|
||||
echo "/etc/init.d/sshd start" >> "$WORK_DIR/etc/local.d/ssh.start"
|
||||
chmod +x "$WORK_DIR/etc/local.d/ssh.start"
|
||||
|
||||
# Create squashfs
|
||||
mksquashfs "$WORK_DIR" "$ISO_OUTPUT_DIR/alpine-rootfs.squashfs" -comp xz
|
||||
|
||||
# Create ISO using xorriso
|
||||
xorriso -as mkisofs -R -J -l -o "$ISO_OUTPUT_DIR/alpine-custom.iso" \
|
||||
-b boot/grub/grub.cfg \
|
||||
"$ISO_OUTPUT_DIR"
|
||||
|
||||
echo "Custom Alpine ISO built successfully and stored at $ISO_OUTPUT_DIR/alpine-custom.iso"
|
|
@ -0,0 +1,51 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Exit immediately if a command exits with a non-zero status
|
||||
set -e
|
||||
|
||||
# Define variables
|
||||
WORK_DIR="/root/build-alpine"
|
||||
OUTPUT_DIR="$WORK_DIR/output"
|
||||
SCRIPT_NAME="alpine-make-vm-image"
|
||||
SCRIPT_URL="https://raw.githubusercontent.com/alpinelinux/alpine-make-vm-image/v0.13.0/$SCRIPT_NAME"
|
||||
SCRIPT_SHA1="0fe2deca927bc91eb8ab32584574eee72a23d033"
|
||||
ALPINE_IMAGE_NAME="alpine-custom.qcow2"
|
||||
SETUP_SCRIPT="vm-setup.sh"
|
||||
CHROOT_PATH="/mnt" # Path where the root filesystem is mounted in chroot
|
||||
CHROOT_SCRIPT_PATH="$CHROOT_PATH/$SETUP_SCRIPT"
|
||||
|
||||
# Create working and output directories
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
cd "$WORK_DIR"
|
||||
|
||||
# Install necessary dependencies
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y qemu-utils qemu-system-x86 rsync util-linux e2fsprogs dosfstools
|
||||
|
||||
# Download the alpine-make-vm-image script
|
||||
wget "$SCRIPT_URL" -O "$SCRIPT_NAME"
|
||||
|
||||
# Verify the script SHA1 hash
|
||||
echo "$SCRIPT_SHA1 $SCRIPT_NAME" | sha1sum -c || exit 1
|
||||
|
||||
# Make the alpine-make-vm-image script executable
|
||||
chmod +x "$SCRIPT_NAME"
|
||||
|
||||
# Ensure vm-setup.sh is executable
|
||||
chmod +x "$SETUP_SCRIPT"
|
||||
|
||||
# Run the alpine-make-vm-image script to create the VM image
|
||||
./"$SCRIPT_NAME" \
|
||||
-f qcow2 \
|
||||
-s 2G \
|
||||
-a x86_64 \
|
||||
-B BIOS \
|
||||
-b latest-stable \
|
||||
-p "openssh-server htop curl nano zsh" \
|
||||
-S "$WORK_DIR" \
|
||||
--fs-skel-chown root:root \
|
||||
--script-chroot \
|
||||
"$OUTPUT_DIR/$ALPINE_IMAGE_NAME" \
|
||||
"$SETUP_SCRIPT" # Removed chroot path, the script will be copied and executed directly
|
||||
|
||||
echo "Alpine Linux VM image created at: $OUTPUT_DIR/$ALPINE_IMAGE_NAME"
|
|
@ -0,0 +1,14 @@
|
|||
services:
|
||||
alpine_builder:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
volumes:
|
||||
- ./output:/output
|
||||
environment:
|
||||
- SSH_PUBLIC_KEY=ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDsKFCdYNW+ShGM89hrN1lRBOeH7Am1DM3RuF5ODl9B3ZBJyL3qbixWipys3twNuHF8PxjRdE4OeXawY8w5U/FdYx9WtZ+/jfnpkIt85hzRP6wGm2Gb3YvYlukMoWRJTrPzY9B1wWZ0lUdwjryh6rsrIiuL/nYi4DMJWVfLLM5i8Pjzqq4rFT0hJDUg4I2cXspCec1YoRFKMKCqIGoduWpjhGm6P23fjz+8GXuB1DfhQcu3TkJF90tn3DUpiQ2BW4PreCtyTzcDdbhIo1O9x3abAeAkOnZtIK2DXdmbtgjCJg9yHgtjQQDV3WT0YkpHGsj3x9/xmhIr2Mcf18Ohjoo81hfL4raWAzGHrQFdc5bHop9M95iqp+0GbxGUhc4Wvy9iptuqfqclZdibOMGeX2vGJsqEkdwuWHpjtQ/12dGTGI9crRP+d/Nch+rc1Dz+cuLfDMuNcS0bqAk4eSeFbgsoOBqZeWcMG0m+ZKgyl4Q+uhBgUoHCoC4FHSJUnMfkKhs= root@868a158393be
|
||||
container_name: alpine_iso_builder
|
||||
dns:
|
||||
- 8.8.8.8
|
||||
- 1.1.1.1
|
||||
privileged: true # Add this line
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Add SSH public key to authorized_keys
|
||||
mkdir -p /root/.ssh
|
||||
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDsKFCdYNW+ShGM89hrN1lRBOeH7Am1DM3RuF5ODl9B3ZBJyL3qbixWipys3twNuHF8PxjRdE4OeXawY8w5U/FdYx9WtZ+/jfnpkIt85hzRP6wGm2Gb3YvYlukMoWRJTrPzY9B1wWZ0lUdwjryh6rsrIiuL/nYi4DMJWVfLLM5i8Pjzqq4rFT0hJDUg4I2cXspCec1YoRFKMKCqIGoduWpjhGm6P23fjz+8GXuB1DfhQcu3TkJF90tn3DUpiQ2BW4PreCtyTzcDdbhIo1O9x3abAeAkOnZtIK2DXdmbtgjCJg9yHgtjQQDV3WT0YkpHGsj3x9/xmhIr2Mcf18Ohjoo81hfL4raWAzGHrQFdc5bHop9M95iqp+0GbxGUhc4Wvy9iptuqfqclZdibOMGeX2vGJsqEkdwuWHpjtQ/12dGTGI9crRP+d/Nch+rc1Dz+cuLfDMuNcS0bqAk4eSeFbgsoOBqZeWcMG0m+ZKgyl4Q+uhBgUoHCoC4FHSJUnMfkKhs= root@868a158393be" > /root/.ssh/authorized_keys
|
||||
chmod 700 /root/.ssh
|
||||
chmod 600 /root/.ssh/authorized_keys
|
||||
|
||||
# Configure SSH server
|
||||
echo 'PermitRootLogin prohibit-password' >> /etc/ssh/sshd_config
|
||||
echo 'PasswordAuthentication no' >> /etc/ssh/sshd_config
|
||||
echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config
|
||||
|
||||
# Ensure SSH starts on boot
|
||||
rc-update add sshd
|
Loading…
Reference in New Issue