diff --git a/airootfs/etc/systemd/scripts/sysrescue-initialize.py b/airootfs/etc/systemd/scripts/sysrescue-initialize.py index f923acd..e32e2d1 100755 --- a/airootfs/etc/systemd/scripts/sysrescue-initialize.py +++ b/airootfs/etc/systemd/scripts/sysrescue-initialize.py @@ -1,123 +1,234 @@ -#! /usr/bin/env python3 +#!/usr/bin/env python3 + +# SPDX-License-Identifier: GPL-3.0-or-later + import subprocess +import yaml +import glob import os import sys import re -print(f"Script {sys.argv[0]} starting ...") +# ============================================================================== +# Initialization +# ============================================================================== +print(f"====> Script {sys.argv[0]} starting ...") errcnt = 0 +# ============================================================================== +# Define the default configuration +# ============================================================================== +config_global = { + 'dostartx': False, + 'dovnc': False, + 'noautologin': False, + 'nofirewall': False, + 'rootshell': None, + 'rootpass': None, + 'rootcryptpass': None, + 'setkmap': None, + 'vncpass': None, +} + +# ============================================================================== +# Load configuration from the yaml files +# ============================================================================== +print(f"====> Loading configuration from yaml files located on the boot device ...") +yamlconfdirs = ["/run/archiso/bootmnt/config.d", "/run/archiso/copytoram/config.d"] + +def parse_config_file(yamlfile): + print(f"Parsing yaml file: {yamlfile} ...") + with open(yamlfile) as myfile: + try: + curconfig = yaml.safe_load(myfile) + if 'global' in curconfig: + curglobal = curconfig['global'] + for entry in config_global: + if entry in curglobal: + config_global[entry] = curglobal[entry] + return True + except yaml.YAMLError as err: + print(err) + errcnt+=1 + return False + +for yamlconfdir in yamlconfdirs: + if os.path.isdir(yamlconfdir): + conffiles = glob.glob(os.path.join(yamlconfdir, '*.yaml'), recursive=True) + conffiles.sort() # Load yaml files in the alphabetical order + for curfile in conffiles: + parse_config_file(curfile) + +# ============================================================================== +# Load configuration from the boot command line +# ============================================================================== +print(f"====> Parsing configuration from the boot command line ...") + bootcmdline = open("/proc/cmdline","r").readline() bootopts = bootcmdline.split() for curopt in bootopts: - # Configure keyboard layout if requested in the boot command line + # Configure keyboard layout match = re.search(r"^setkmap=(\S+)$", curopt) if match != None: - curval = match.group(1) - print(f"=> Found option '{curopt}' on the boot command line") - p = subprocess.run(["localectl", "set-keymap", curval], text=True) - if p.returncode == 0: - print (f"Have changed the keymap successfully") - else: - print (f"Failed to change keymap") - errcnt+=1 + print(f"Found option '{curopt}' on the boot command line") + config_global['setkmap'] = match.group(1) - # Configure root login shell if requested in the boot command line + # Configure root login shell match = re.search(r"^rootshell=(\S+)$", curopt) if match != None: - curval = match.group(1) - print(f"=> Found option '{curopt}' on the boot command line") - p = subprocess.run(["chsh", "--shell", curval, "root"], text=True) - if p.returncode == 0: - print (f"Have changed the root shell successfully") - else: - print (f"Failed to change the root shell") - errcnt+=1 + print(f"Found option '{curopt}' on the boot command line") + config_global['rootshell'] = match.group(1) # Set the system root password from a clear password match = re.search(r"^rootpass=(\S+)$", curopt) if match != None: - curval = match.group(1) - print(f"=> Found option 'rootpass=******' on the boot command line") - p = subprocess.run(["chpasswd", "--crypt-method", "SHA512"], text=True, input=f"root:{curval}") - if p.returncode == 0: - print (f"Have changed the root password successfully") - else: - print (f"Failed to change the root password") - errcnt+=1 + print(f"Found option 'rootpass=******' on the boot command line") + config_global['rootpass'] = match.group(1) # Set the system root password from an encrypted password - # A password can be encrypted using a one-line python3 command such as: - # python3 -c 'import crypt; print(crypt.crypt("MyPassWord123", crypt.mksalt(crypt.METHOD_SHA512)))' match = re.search(r"^rootcryptpass=(\S+)$", curopt) if match != None: - curval = match.group(1) - print(f"=> Found option 'rootcryptpass=******' on the boot command line") - p = subprocess.run(["chpasswd", "--encrypted"], text=True, input=f"root:{curval}") - if p.returncode == 0: - print (f"Have changed the root password successfully") - else: - print (f"Failed to change the root password") - errcnt+=1 + print(f"Found option 'rootcryptpass=******' on the boot command line") + config_global['rootcryptpass'] = match.group(1) # Disable the firewall match = re.search(r"^nofirewall$", curopt) if match != None: - print(f"=> Found option 'nofirewall' on the boot command line") - # The firewall service(s) must be in the Before-section of sysrescue-initialize.service - p = subprocess.run(["systemctl", "disable", "--now", "iptables.service", "ip6tables.service"], text=True) - if p.returncode == 0: - print (f"Have disabled the firewall successfully") - else: - print (f"Failed to disable the firewall") - errcnt+=1 + print(f"Found option '{curopt}' on the boot command line") + config_global['nofirewall'] = True # Auto-start the graphical environment (tty1 only), dovnc implies dostartx - match = re.search(r"^dostartx|dovnc$", curopt) + match = re.search(r"^dostartx$", curopt) if match != None: - print(f"=> Found option '{match.group(0)}' on the boot command line") - str = '[[ ! $DISPLAY ]] && [[ ! $SSH_TTY ]] && [[ $XDG_VTNR == 1 ]] && startx' - if (os.path.exists("/root/.bash_profile") == False) or (open("/root/.bash_profile", 'r').read().find(str) == -1): - file1 = open("/root/.bash_profile", "a") - file1.write(f"{str}\n") - file1.close() - file2 = open("/root/.zlogin", "w") - file2.write(f"{str}\n") - file2.close() + print(f"Found option '{curopt}' on the boot command line") + config_global['dostartx'] = True # Require authenticated console access match = re.search(r"^noautologin$", curopt) if match != None: - print(f"=> Found option '{match.group(0)}' on the boot command line") - p = subprocess.run(["systemctl", "revert", "getty@.service", "serial-getty@.service"], text=True) - if p.returncode == 0: - print (f"Have enabled authenticated console access successfully") - else: - print (f"Failed to enable authenticated console access") - errcnt+=1 + print(f"Found option '{curopt}' on the boot command line") + config_global['noautologin'] = True # Set the VNC password from a clear password match = re.search(r"^vncpass=(\S+)$", curopt) if match != None: - curval = match.group(1) - print(f"=> Found option 'vncpass=******' on the boot command line") - os.makedirs("/root/.vnc", exist_ok = True) - p = subprocess.run(["x11vnc", "-storepasswd", curval, "/root/.vnc/passwd"], text=True) - if p.returncode == 0: - print (f"Have changed the vnc password successfully") - else: - print (f"Failed to change the vnc password") - errcnt+=1 + print(f"Found option 'vncpass=******' on the boot command line") + config_global['vncpass'] = match.group(1) - # Auto-start x11vnc with the graphical environment + # Auto-start x11vnc with the graphical environment, "dovnc" implies "dostartx" match = re.search(r"^dovnc$", curopt) if match != None: - # No need to print "Found option 'dovnc' on the boot command line" a second time - file = open("/root/.xprofile", "w") - file.write("""[ -f ~/.vnc/passwd ] && pwopt="-usepw" || pwopt="-nopw"\n""") - file.write("""x11vnc $pwopt -nevershared -forever -logfile /var/log/x11vnc.log &\n""") - file.close() + print(f"Found option '{curopt}' on the boot command line") + config_global['dovnc'] = True + config_global['dostartx'] = True +# ============================================================================== +# Show the effective configuration +# ============================================================================== +print(f"====> Showing the effective global configuration (except clear passwords) ...") +print(f"config['setkmap']={config_global['setkmap']}") +print(f"config['rootshell']={config_global['rootshell']}") +print(f"config['rootcryptpass']={config_global['rootcryptpass']}") +print(f"config['nofirewall']={config_global['nofirewall']}") +print(f"config['dostartx']={config_global['dostartx']}") +print(f"config['noautologin']={config_global['noautologin']}") +print(f"config['dovnc']={config_global['dovnc']}") + +# ============================================================================== +# Apply the effective configuration +# ============================================================================== +print(f"====> Applying configuration ...") + +# Configure keyboard layout if requested in the configuration +if config_global['setkmap'] != None: + p = subprocess.run(["localectl", "set-keymap", config_global['setkmap']], text=True) + if p.returncode == 0: + print (f"Have changed the keymap successfully") + else: + print (f"Failed to change keymap") + errcnt+=1 + +# Configure root login shell if requested in the configuration +if config_global['rootshell'] != None: + p = subprocess.run(["chsh", "--shell", config_global['rootshell'], "root"], text=True) + if p.returncode == 0: + print (f"Have changed the root shell successfully") + else: + print (f"Failed to change the root shell") + errcnt+=1 + +# Set the system root password from a clear password +if config_global['rootpass'] != None: + p = subprocess.run(["chpasswd", "--crypt-method", "SHA512"], text=True, input=f"root:{config_global['rootpass']}") + if p.returncode == 0: + print (f"Have changed the root password successfully") + else: + print (f"Failed to change the root password") + errcnt+=1 + +# Set the system root password from an encrypted password +# A password can be encrypted using a one-line python3 command such as: +# python3 -c 'import crypt; print(crypt.crypt("MyPassWord123", crypt.mksalt(crypt.METHOD_SHA512)))' +if config_global['rootcryptpass'] != None: + p = subprocess.run(["chpasswd", "--encrypted"], text=True, input=f"root:{config_global['rootcryptpass']}") + if p.returncode == 0: + print (f"Have changed the root password successfully") + else: + print (f"Failed to change the root password") + errcnt+=1 + +# Disable the firewall +if config_global['nofirewall'] == True: + # The firewall service(s) must be in the Before-section of sysrescue-initialize.service + p = subprocess.run(["systemctl", "disable", "--now", "iptables.service", "ip6tables.service"], text=True) + if p.returncode == 0: + print (f"Have disabled the firewall successfully") + else: + print (f"Failed to disable the firewall") + errcnt+=1 + +# Auto-start the graphical environment (tty1 only) +if config_global['dostartx'] == True: + str = '[[ ! $DISPLAY ]] && [[ ! $SSH_TTY ]] && [[ $XDG_VTNR == 1 ]] && startx' + if (os.path.exists("/root/.bash_profile") == False) or (open("/root/.bash_profile", 'r').read().find(str) == -1): + file1 = open("/root/.bash_profile", "a") + file1.write(f"{str}\n") + file1.close() + file2 = open("/root/.zlogin", "w") + file2.write(f"{str}\n") + file2.close() + +# Require authenticated console access +if config_global['noautologin'] == True: + p = subprocess.run(["systemctl", "revert", "getty@.service", "serial-getty@.service"], text=True) + if p.returncode == 0: + print (f"Have enabled authenticated console access successfully") + else: + print (f"Failed to enable authenticated console access") + errcnt+=1 + +# Set the VNC password from a clear password +if config_global['vncpass'] != None: + os.makedirs("/root/.vnc", exist_ok = True) + p = subprocess.run(["x11vnc", "-storepasswd", config_global['vncpass'], "/root/.vnc/passwd"], text=True) + if p.returncode == 0: + print (f"Have changed the vnc password successfully") + else: + print (f"Failed to change the vnc password") + errcnt+=1 + +# Auto-start x11vnc with the graphical environment +if config_global['dovnc'] == True: + print (f"Enabling VNC Server in /root/.xprofile ...") + file = open("/root/.xprofile", "w") + file.write("""[ -f ~/.vnc/passwd ] && pwopt="-usepw" || pwopt="-nopw"\n""") + file.write("""x11vnc $pwopt -nevershared -forever -logfile /var/log/x11vnc.log &\n""") + file.close() + +# ============================================================================== +# End of the script +# ============================================================================== +print(f"====> Script {sys.argv[0]} completed with {errcnt} errors ...") sys.exit(errcnt) diff --git a/build.sh b/build.sh index a101a0c..9ec1093 100755 --- a/build.sh +++ b/build.sh @@ -84,7 +84,7 @@ determine_snapshot_date() { snapshot_date=`cat ${work_dir}/build.snapshot_date` return fi - + if [[ -z "$snapshot_date" ]]; then # while archive.archlinux.org offers lastsync files we could read out, archive.archlinux32.org doesn't # so use the current date (UTC), check if it's dir exists on the mirror, use the day before if not @@ -111,7 +111,7 @@ determine_snapshot_date() { fi # we got a snapshot date that looks valid, use it without further network tests fi - + echo "$snapshot_date" >${work_dir}/build.snapshot_date } @@ -132,7 +132,7 @@ make_pacman_conf() { s|^Architecture\s*=.*$|Architecture = ${arch}|; s|^Include =.*$|Include = ${work_dir}/mirrorlist|g" \ ${script_path}/pacman.conf > ${work_dir}/pacman.conf - + sed "s|%SNAPSHOT_DATE%|${snapshot_date}|g;" \ ${script_path}/${archive_mirrorlist_file} > ${work_dir}/mirrorlist } @@ -155,19 +155,19 @@ make_documentation() { echo "ERROR: current version not in changelog. Did you update the website submodule?" exit 1 fi - + mkdir -p "${work_dir}/${arch}/airootfs/${documentation_dir}" # parameters are all relative to --source dir /usr/bin/hugo --source "website/" --config "config-offline.toml" --gc --verbose \ --destination "../${work_dir}/${arch}/airootfs/${documentation_dir}" RET=$? - + if ! [ "$RET" -eq 0 ]; then echo "error generating offline documentation (returned $RET), aborting" exit 1 fi - + # post-process hugo output and add index.hmtl to all directory links # required until https://github.com/gohugoio/hugo/issues/4428 is implemented find "${work_dir}/${arch}/airootfs/${documentation_dir}" -name "*.html" \ @@ -192,7 +192,7 @@ make_customize_airootfs() { s|%ISO_ARCH%|${arch}|g; s|%INSTALL_DIR%|${install_dir}|g" \ ${script_path}/airootfs/etc/issue > ${work_dir}/${arch}/airootfs/etc/issue - + # delete the target file first because it is a symlink rm -f ${work_dir}/${arch}/airootfs/etc/os-release sed "s|%ARCHISO_LABEL%|${iso_label}|g; @@ -206,18 +206,18 @@ make_customize_airootfs() { sed "s|%SNAPSHOT_DATE%|${snapshot_date}|g;" \ ${script_path}/${archive_mirrorlist_file} > ${work_dir}/${arch}/airootfs/etc/pacman.d/mirrorlist-snapshot - + mkdir -p ${work_dir}/${arch}/airootfs/var/lib/pacman-rolling/local - + setarch ${arch} mkarchiso ${verbose} -w "${work_dir}/${arch}" -C "${work_dir}/pacman.conf" -D "${install_dir}" -r '/root/customize_airootfs.sh' run - + rm -f ${work_dir}/${arch}/airootfs/root/customize_airootfs.sh # change pacman config in airootfs to use snapshot repo by default # we can just do this after the mkarchiso run, it would flatten the symlink otherwise rm -f ${work_dir}/${arch}/airootfs/etc/pacman.conf ln -s pacman-snapshot.conf ${work_dir}/${arch}/airootfs/etc/pacman.conf - + # strip large binaries find ${work_dir}/${arch}/airootfs/usr/lib -type f -name "lib*.so.*" -exec strip --strip-all {} \; } @@ -345,6 +345,7 @@ make_prepare() { # Build ISO make_iso() { cp ${version_file} ${work_dir}/iso/${install_dir}/ + cp -r config.d/ ${work_dir}/iso/ ( shopt -s nullglob rm -vf ${work_dir}/iso/${install_dir}/*.srm diff --git a/config.d/01-sysrescue.yaml b/config.d/01-sysrescue.yaml new file mode 100644 index 0000000..60b8af6 --- /dev/null +++ b/config.d/01-sysrescue.yaml @@ -0,0 +1,4 @@ +--- +global: + dostartx: false + nofirewall: false diff --git a/packages b/packages index 0bf2295..64c57f2 100644 --- a/packages +++ b/packages @@ -178,6 +178,7 @@ pv python python-llfuse python-pip +python-yaml qemu-guest-agent rdesktop rdiff-backup diff --git a/patches/archiso-v43-06-autorun-copytoram.patch b/patches/archiso-v43-06-autorun-copytoram.patch deleted file mode 100644 index d3dd36d..0000000 --- a/patches/archiso-v43-06-autorun-copytoram.patch +++ /dev/null @@ -1,19 +0,0 @@ -diff --git a/archiso/initcpio/hooks/archiso b/archiso/initcpio/hooks/archiso -index 3eb7ac7..e4585af 100644 ---- a/archiso/initcpio/hooks/archiso -+++ b/archiso/initcpio/hooks/archiso -@@ -247,6 +247,14 @@ archiso_mount_handler() { - - _mnt_sfs "/run/archiso/bootmnt/${archisobasedir}/${arch}/airootfs.sfs" "/run/archiso/sfs/airootfs" - -+ if [[ "${copytoram}" == "y" ]] && ls -lh /run/archiso/bootmnt/autorun* >/dev/null 2>/dev/null; then -+ msg -n ":: Copying autorun scripts to RAM..." -+ if ! cp /run/archiso/bootmnt/autorun* /run/archiso/copytoram/ ; then -+ echo "ERROR: while copy '/run/archiso/bootmnt/autorun*' to '/run/archiso/copytoram/'" -+ launch_interactive_shell -+ fi -+ fi -+ - [[ "${loadsrm}" == "y" ]] && _mnt_srm "/run/archiso/bootmnt/${archisobasedir}" - - if [[ -f "/run/archiso/sfs/airootfs/airootfs.img" ]]; then diff --git a/patches/archiso-v43-06-copytoram.patch b/patches/archiso-v43-06-copytoram.patch new file mode 100644 index 0000000..d48d055 --- /dev/null +++ b/patches/archiso-v43-06-copytoram.patch @@ -0,0 +1,27 @@ +diff -urN archiso-43-a/archiso/initcpio/hooks/archiso archiso-43-b/archiso/initcpio/hooks/archiso +--- archiso-43-a/archiso/initcpio/hooks/archiso 2021-12-31 23:32:15.547000000 +0000 ++++ archiso-43-b/archiso/initcpio/hooks/archiso 2021-12-31 23:36:53.081000000 +0000 +@@ -247,6 +247,23 @@ + + _mnt_sfs "/run/archiso/bootmnt/${archisobasedir}/${arch}/airootfs.sfs" "/run/archiso/sfs/airootfs" + ++ if [[ "${copytoram}" == "y" ]]; then ++ if ls -lh /run/archiso/bootmnt/autorun* >/dev/null 2>/dev/null; then ++ msg -n ":: Copying autorun scripts to RAM ..." ++ if ! cp /run/archiso/bootmnt/autorun* /run/archiso/copytoram/ ; then ++ echo "ERROR: failed to copy '/run/archiso/bootmnt/autorun*' to '/run/archiso/copytoram/'" ++ launch_interactive_shell ++ fi ++ fi ++ if ls -lh /run/archiso/bootmnt/config.d >/dev/null 2>/dev/null; then ++ msg -n ":: Copying configuration files to RAM ..." ++ if ! cp -r /run/archiso/bootmnt/config.d /run/archiso/copytoram/ ; then ++ echo "ERROR: failed to copy '/run/archiso/bootmnt/config.d' to '/run/archiso/copytoram/'" ++ launch_interactive_shell ++ fi ++ fi ++ fi ++ + [[ "${loadsrm}" == "y" ]] && _mnt_srm "/run/archiso/bootmnt/${archisobasedir}" + + if [[ -f "/run/archiso/sfs/airootfs/airootfs.img" ]]; then