diff options
| -rw-r--r-- | .gitignore | 7 | ||||
| -rw-r--r-- | Makefile | 9 | ||||
| -rw-r--r-- | README.md | 20 | ||||
| -rw-r--r-- | installiso.8 | 140 | ||||
| -rwxr-xr-x | installiso.ksh | 293 | 
5 files changed, 469 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0da69c6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.iso +*.qcow2 +disklabel_template +install.conf +site +tmp +upgrade.conf diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..bfc0afc --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +MAN=    installiso.8 +BINDIR= /usr/local/bin +MANDIR= /usr/local/man/man + +beforeinstall: +	${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ +		${.CURDIR}/installiso.ksh ${DESTDIR}${BINDIR}/installiso + +.include <bsd.prog.mk> diff --git a/README.md b/README.md new file mode 100644 index 0000000..e1d6211 --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# Installiso + +Installiso is a utility for OpenBSD that facilitates creating custom ISO 9660 +installation images for unattended installation. See +[this](https://www.skreutz.com/posts/first-release-of-installiso/) blog post +for an introduction, and refer to the man page for details. + +## Install + +Run `make install` as root to install the `installiso` utility and man page. + +## Release + +Create an annotated tag, and an archive as follows, replacing `0.1.0` with the +current version. + +    $ git tag --annotate --message "Version 0.1.0" 0.1.0 +    $ git archive --format=tar.gz --prefix=installiso-0.1.0/ \ +        --output installiso-0.1.0.tar.gz 0.1.0 + diff --git a/installiso.8 b/installiso.8 new file mode 100644 index 0000000..c2348d5 --- /dev/null +++ b/installiso.8 @@ -0,0 +1,140 @@ +.\" Copyright (c) 2021 Stefan Kreutz <mail@skreutz.com> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.Dd $Mdocdate: July 24 2021 $ +.Dt INSTALLISO 8 +.Os +.Sh NAME +.Nm installiso +.Nd Customize OpenBSD installation images +.Sh SYNOPSIS +.Nm installiso +.Op Fl v +.Ar command +.Op Ar arg ... +.Sh DESCRIPTION +The +.Nm +utility facilitates creating custom ISO 9660 installation images for the +unattended installation of +.Ox . +.Pp +The common options are as follows: +.Bl -tag -width Ds +.It Fl v +Verbose mode. +Causes +.Nm +to print informative messages. +Multiple +.Fl v +options increase the verbosity. +The maximum is 2. +By default, +.Nm +is quiet. +.El +.Pp +The commands are as follows: +.Bl -tag -width Ds +.It Cm fetch Oo Fl f Oc Oo Fl m Ar mirror Oc Oo Fl r Ar release Oc Oo Fl p Ar key Oc Oo Fl o Ar output Oc +Download and verify an official ISO 9660 installation image. +.Bl -tag -width 15n +.It Fl f +Force overwriting existing files. +By default, +.Cm fetch +will not overwrite existing files. +.It Fl m Ar mirror +The HTTP(S) +.Ox +.Ar mirror +to use. +Defaults to the mirror specified by +.Xr installurl 5 +or else +.Lk https://cdn.openbsd.org/pub/OpenBSD/ . +.It Fl r Ar release +The +.Ox +.Ar release . +Defaults to the latest development snapshot of -current. +.It Fl p Ar key +The public +.Xr signify 1 +.Ar key +used to verify the downloaded installation image. +Defaults to the key +.Pa /etc/signify/openbsd-*-base.pub +corresponding to the downloaded release. +.El +.It Cm patch Oo Fl f Oc Oo Fl i Ar install_conf Oc Oo Fl u Ar upgrade_conf Oc Oo Fl s Ar site_dir Oc Ar input Ar output +Patch an ISO 9660 installation image. +.Bl -tag -width 15n +.It Fl f +Force overwriting existing files. +By default, +.Cm patch +will not overwrite existing files. +.It Fl i Ar install_conf +Insert an +.Xr autoinstall 8 +response file for unattended installation. +.It Fl u Ar upgrade_conf +Insert an +.Xr autoinstall 8 +response file for unattended upgrade. +.It Fl s Ar site +Package and insert the directory +.Ar site +as a site-specific file set. +.It Ar input +The +.Ar input +installation image file. +.It Ar output +The +.Ar output +installation image file. +.El +.El +.Sh EXIT STATUS +.Ex -std installiso +.Sh EXAMPLES +Fetch the latest development snapshot: +.Bd -literal -offset indent +$ installiso -v fetch +.Ed +.Pp +Fetch a specific release: +.Bd -literal -offset indent +$ installiso -v fetch -r 6.9 +.Ed +.Pp +Create a custom image for unattended installation: +.Bd -literal -offset indent +$ doas installiso -v patch -i install.conf install69.iso custom.iso +.Ed +.Sh SEE ALSO +.Xr autoinstall 8 , +.Xr mkhybrid 8 , +.Xr rdsetroot 8 , +.Xr sysupgrade 8 , +.Xr vnconfig 8 +.Sh AUTHORS +.An Stefan Kreutz Aq Mt mail@skreutz.com +.Sh BUGS +The +.Nm +utility is currently limited to the amd64 architecture because it wasn't tested +on any other architecture. diff --git a/installiso.ksh b/installiso.ksh new file mode 100755 index 0000000..f28a60e --- /dev/null +++ b/installiso.ksh @@ -0,0 +1,293 @@ +#! /bin/ksh + +# Copyright (c) 2021 Stefan Kreutz <mail@skreutz.com> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# shellcheck disable=SC2001 + +set -o errexit +set -o nounset +set -o pipefail + +verbosity=0 +tmp= +vnode= + +function main { +  trap 'cleanup' EXIT ERR INT + +  while getopts :v option; do +    case "$option" in +      v) verbosity=$((verbosity+1)) ;; +      :) err_exit "missing argument for option -$OPTARG" ;; +      ?) err_exit "illegal option: -$OPTARG" ;; +    esac +  done +  shift $((OPTIND-1)) + +  if [ $# -eq 0 ]; then +    err_exit "missing command" +  fi +  local _cmd="$1" +  shift 1 + +  tmp="$( readlink -f "$( mktemp -d -t )" )" +  debug "Created temporary directory $tmp" + +  case "$_cmd" in +    fetch|patch) "$_cmd" "$@" ;; +    *) err_exit "undefined command: $_cmd" ;; +  esac +} + +function fetch { +  local _force=0 +  local _mirror _arch _release _pubkey _output _url _ftp_options _rel _iso + +  while getopts :fm:r:p:o: option; do +    case "$option" in +      f) _force=1 ;; +      m) _mirror="$OPTARG" ;; +      r) _release="$OPTARG" ;; +      p) _pubkey="$OPTARG" ;; +      o) _output="$OPTARG" ;; +      :) err_exit "missing argument for option -$OPTARG" ;; +      ?) err_exit "illegal option: -$OPTARG" ;; +    esac +  done +  shift $((OPTIND-1)) + +  if [ $# -ne 0 ]; then +    err_exit "too many arguments: $*" +  fi + +  _arch="$( uname -p )" +  if [ "$_arch" != amd64 ]; then +    err_exit "unsupported architecture: $_arch" +  fi + +  _mirror="${_mirror:-"$( get_mirror )"}" +  case "$_mirror" in +    http://*|https://*) ;; +    *) err_exit "unsupported mirror: $_mirror" ;; +  esac + +  _url="${_mirror%/}/${_release:-snapshots}/$_arch" +  info "Fetching from $_url/" + +  case $verbosity in +    0) _ftp_options="-VM" ;; +    1) _ftp_options="-V" ;; +    *) _ftp_options="-v" ;; +  esac + +  debug "Downloading SHA256.sig ..." +  ftp "$_ftp_options" -o "$tmp/SHA256.sig" "$_url/SHA256.sig" +  debug "Downloaded SHA256.sig" + +  _rel="$( sed -n 's/^SHA256 (install\([0-9][0-9]\).iso) = .*/\1/p' "$tmp/SHA256.sig" )" +  _iso="install${_rel}.iso" +  _output="${_output:-"$_iso"}" + +  if [ -e "$_output" ] && [ $_force -eq 0 ]; then +    err_exit "file already exists: $_output" +  fi + +  debug "Downloading $_iso ..." +  ftp "$_ftp_options" -o "$tmp/$_iso" "$_url/$_iso" +  debug "Downloaded $_iso" + +  _pubkey="${_pubkey:-"/etc/signify/openbsd-${_rel}-base.pub"}" +  debug "Verifying $_iso using $_pubkey ..." +  ( cd "$tmp" && signify -C -p "$_pubkey" -x SHA256.sig -q -- "$_iso" ) +  debug "Verified $_iso" + +  mv "$tmp/$_iso" "$_output" +  if [ "$_iso" = "$_output" ]; then +    info "Fetched $_iso" +  else +    info "Fetched $_iso as $_output" +  fi +} + +function patch { +  local _force=0 +  local _compressed=0 +  local _install_conf _upgrade_conf _site_dir _input_iso _output_iso +  local _release _rel _arch _mime _rd_path + +  while getopts :fi:u:s: option; do +    case "$option" in +      f) _force=1 ;; +      i) _install_conf="$OPTARG" ;; +      u) _upgrade_conf="$OPTARG" ;; +      s) _site_dir="$OPTARG" ;; +      :) err_exit "missing argument for option -$OPTARG" ;; +      ?) err_exit "illegal option: -$OPTARG" ;; +    esac +  done +  shift $((OPTIND-1)) + +  if [ $# -lt 2 ]; then +    err_exit "missing arguments" +  fi +  _input_iso="$1" +  _output_iso="$2" +  shift 2 + +  if [ $# -ne 0 ]; then +    err_exit "too many arguments: $*" +  fi + +  info "Unpacking ISO 9660 image ..." +  vnode="$( vnconfig "$_input_iso" )" +  debug "Configured $_input_iso as vnode disk $vnode" +  mkdir "$tmp/mnt" +  mount -t cd9660 "/dev/${vnode}c" "$tmp/mnt" +  debug "Mounted vnode disk $vnode" +  mkdir "$tmp/cd" +  tar -C "$tmp/mnt" -c -f - . | tar -C "$tmp/cd" -x -p -f - +  debug "Copied contents of $_input_iso to disk" +  umount "$tmp/mnt" +  debug "Unmounted vnode disk $vnode" +  vnconfig -u "$vnode" +  vnode= +  debug "Unconfigured vnode disk $vnode" +  info "Unpacked ISO 9660 image" + +  debug "Inspecting directory layout ..." +  _rd_path="$( cd "$tmp/cd" && echo */*/bsd.rd )" +  _release="$( echo "$_rd_path" | sed 's,^\([0-9]\)\.\([0-9]\)/\([^/]*\)/bsd.rd,\1.\2,' )" +  _rel="$( echo "$_rd_path" | sed 's,^\([0-9]\)\.\([0-9]\)/\([^/]*\)/bsd.rd,\1\2,' )" +  debug "Inferred release $_release resp. $_rel" +  _arch="$( echo "$_rd_path" | sed 's,^\([0-9]\)\.\([0-9]\)/\([^/]*\)/bsd.rd,\3,' )" +  debug "Inferred arch $_arch" + +  if [ "$_arch" != amd64 ]; then +    err_exit "unsupported architecture: $_arch" +  fi + +  if [ -e "$_output_iso" ] && [ $_force -eq 0 ]; then +    err_exit "file already exists: $_output_iso" +  fi + +  info "Patching RAMDISK kernel bsd.rd ..." +  _mime="$( file --brief --mime "$tmp/cd/$_release/$_arch/bsd.rd" )" +  if [ "$_mime" = "application/x-gzip" ]; then +    _compressed=1 +    gzip -d -o "$tmp/bsd.rd" "$tmp/cd/$_release/$_arch/bsd.rd" +    debug "Uncompressed RAMDISK kernel bsd.rd" +  else +    cp "$tmp/cd/$_release/$_arch/bsd.rd" "$tmp/bsd.rd" +  fi +  rdsetroot -x "$tmp/bsd.rd" "$tmp/disk.fs" +  debug "Extracted disk image from RAMDISK kernel bsd.rd" +  vnode="$( vnconfig "$tmp/disk.fs" )" +  debug "Configured RAMDISK kernel's disk image as vnode disk $vnode" +  mount "/dev/${vnode}a" "$tmp/mnt" +  debug "Mounted vnode disk $vnode" +  if [ -n "${_install_conf:-}" ]; then +    install -o root -g wheel -m 0644 -C "$_install_conf" "$tmp/mnt/auto_install.conf" +    debug "Installed autoinstall(8) install response file $_install_conf" +  fi +  if [ -n "${_upgrade_conf:-}" ]; then +    install -o root -g wheel -m 0644 -C "$_upgrade_conf" "$tmp/mnt/auto_upgrade.conf" +    debug "Installed autoinstall(8) upgrade response file $_upgrade_conf" +  fi +  umount "$tmp/mnt" +  debug "Unmounted vnode disk $vnode" +  vnconfig -u "$vnode" +  vnode= +  debug "Unconfigured vnode disk $vnode" +  rdsetroot "$tmp/bsd.rd" "$tmp/disk.fs" +  debug "Inserted modified disk image into RAMDISK kernel bsd.rd" +  debug "Patched RAMDISK kernel" +  if [ $_compressed -eq 1 ]; then +    gzip -9fnq "$tmp/bsd.rd" +    mv "$tmp/bsd.rd.gz" "$tmp/bsd.rd" +    debug "Compressed patched RAMDISK kernel bsd.rd" +  fi + +  info "Patching ISO 9660 image contents ..." +  install -o root -g 2000 -m 0755 -C "$tmp/bsd.rd" "$tmp/cd/$_release/$_arch/bsd.rd" +  debug "Installed modified RAMDISK kernel bsd.rd" +  if [ -n "${_site_dir:-}" ]; then +    if [ -e "$_site_dir/install.site" ] && [ ! -x "$_site_dir/install.site" ]; then +      warn "$_site_dir/install.site is not executable" +    fi +    ( cd "$_site_dir" && tar -c -z -f "$tmp/cd/$_release/$_arch/site${_rel}.tgz" . ) +    ( cd "$tmp/cd/$_release/$_arch" && ls -l > index.txt ) +    debug "Installed site-specific file set $_site_dir" +  fi +  debug "Patched ISO 9660 image contents" + +  info "Creating bootable ISO 9660 image ..." +  # Source: https://github.com/openbsd/src/blob/1bc16d1a27cb5482308dc0201812e706df3d7287/distrib/amd64/iso/Makefile#L80 +  if ! mkhybrid -a -R -T -L -l -d -D -N -o "${_output_iso}" \ +    -A "Custom OpenBSD ${_release} ${_arch} Install CD" \ +    -P "Copyright (c) $(date +%Y) Theo de Raadt, The OpenBSD project" \ +    -p "Generated using installiso(8)" \ +    -b "${_release}/${_arch}/cdbr" -c "${_release}/${_arch}/boot.catalog" \ +    "${tmp}/cd" +  then +    rm -f "$_output_iso" +    err_exit "failed to create bootable ISO 9660 image" +  fi +  info "Created bootable ISO 9660 image $_output_iso" +} + +function cleanup { +  trap - EXIT ERR INT +  set +o errexit +  debug "Cleaning up ..." +  if [ -n "${vnode:-}" ]; then +    if mount | grep -qe "^/dev/$vnode" ; then +      umount "/dev/$vnode" && debug "Unmounted vnode disk $vnode" +    fi +    vnconfig -u "$vnode" && debug "Unconfigured vnode disk $vnode" +  fi +  if [ -n "${tmp:-}" ]; then +    rm -rf "$tmp" && debug "Removed temporary directory $tmp" +  fi +} + +function get_mirror { +  grep -v -e '^[:space:]*$' -e '^#' /etc/installurl | head -n 1 || +    echo "https://cdn.openbsd.org/pub/OpenBSD/" +} + +function err_exit { +  print -u2 -- "$*" +  exit 1 +} + +function warn { +  print -u2 -- "$*" +} + +function info { +  if [ $verbosity -lt 1 ]; then +    return +  fi +  print -- "$*" +} + +function debug { +  if [ $verbosity -lt 2 ]; then +    return +  fi +  print -- "$*" +} + +main "$@"  |