#!/usr/bin/env bash # # JbTecWiz Support Centre -- generated fix script # # Fault : sudo is broken -- "syntax error in /etc/sudoers" and no way back in # Fix : Recover from the console # Source: https://jbtecwiz.com/support/lnx-sec-sudoers # # Run as : Physical or hypervisor console # Expect : 40 minutes # Risk : high # Reversible : yes # # WHEN THIS IS THE RIGHT FIX # No root session anywhere. # # HOW TO UNDO IT # Nothing else was changed. Note that if the disk is encrypted, the # passphrase is still required. # # Walks the fix one step at a time and asks before each. Steps with no # command are yours to do -- it prints those and waits. DRYRUN=1 prints # without executing; UNATTENDED=1 does not ask. # # -------------------------------------------------------------------- # NO WARRANTY - USE AT YOUR OWN RISK # # This script is provided by JbTecWiz as-is and with no warranty of any # kind, express or implied. You run it entirely at your own risk. # # JbTecWiz accepts no liability for any loss or damage arising from its # use, including but not limited to data loss, downtime, or configuration # changes that turn out to be wrong for your system. # # You are responsible for reading this script before running it, for # satisfying yourself that it suits the machine in front of you, and for # having a working backup first. Some steps cannot be undone. # -------------------------------------------------------------------- set -uo pipefail DRYRUN="${DRYRUN:-0}" UNATTENDED="${UNATTENDED:-0}" failed=0 rule() { printf "\n%s\n" "$(printf '-%.0s' $(seq 1 70))"; if [ $# -gt 0 ]; then echo "$1"; fi; } prose() { echo "$1" | fold -s -w 74 | sed "s/^/ /"; } # Returns 0 when the caller should run the command, 1 when it should not. # A manual step always returns 1 -- there is nothing for the caller to run. step() { # step [command lines...] local n="$1" dotext="$2" why="$3" mode="$4"; shift 4 rule " Step $n of 6" prose "$dotext" if [ -n "$why" ]; then echo; prose "$why"; fi if [ "$mode" = "manual" ]; then echo; echo " -> Do this yourself, then press Enter to carry on." if [ "$UNATTENDED" = "0" ] && [ "$DRYRUN" = "0" ]; then read -r _; fi return 1 fi echo; printf " %s\n" "$@"; echo if [ "$DRYRUN" = "1" ]; then echo " (dry run -- not executed)"; return 1; fi if [ "$UNATTENDED" = "0" ]; then read -r -p " Run this step? [Y]es / [S]kip / [Q]uit " a case "$a" in [Qq]*) echo " Stopped at your request."; exit 0 ;; [Ss]*) echo " Skipped."; return 1 ;; esac fi return 0 } rule echo " sudo is broken -- "syntax error in /etc/sudoers" and no way back in" echo " Recover from the console" echo echo " Risk: high Reversible 40 minutes" echo prose 'No warranty. Use at your own risk - JbTecWiz accepts no liability. Read it before you run it, and have a backup.' rule echo if [ "$UNATTENDED" = "0" ] && [ "$DRYRUN" = "0" ]; then read -r -p " Ready? [y/N] " go case "$go" in [Yy]*) ;; *) echo " Nothing was changed."; exit 0;; esac fi step 1 'Reboot and interrupt the boot loader. At the GRUB menu, press e on the default entry.' '' manual || true if step 2 'Add init=/bin/bash to the end of the linux line, then boot with Ctrl-X.' 'This starts a root shell in place of init, before any service or authentication runs. It requires console access, which is precisely why physical and console access must be treated as equivalent to root.' cmd 'linux /vmlinuz-... root=/dev/mapper/vg0-root ro init=/bin/bash'; then linux /vmlinuz-... root=/dev/mapper/vg0-root ro init=/bin/bash if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 2 failed. The rest of the fix may depend on it." >&2 fi fi if step 3 'Remount the root filesystem writable.' '' cmd 'mount -o remount,rw /'; then mount -o remount,rw / if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 3 failed. The rest of the fix may depend on it." >&2 fi fi if step 4 'Fix the file, then verify before rebooting.' '' cmd 'visudo -c' 'chmod 440 /etc/sudoers'; then visudo -c chmod 440 /etc/sudoers if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 4 failed. The rest of the fix may depend on it." >&2 fi fi if step 5 'Reboot cleanly.' '' cmd 'exec /sbin/init'; then exec /sbin/init if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 5 failed. The rest of the fix may depend on it." >&2 fi fi step 6 'On a cloud instance where the console cannot do this, attach the volume to another instance, repair the file there, and reattach.' '' manual || true rule " Confirm it worked" prose 'The machine boots normally and sudo works.' if [ "$DRYRUN" = "0" ]; then sudo -v && echo ok fi rule if [ "$failed" -gt 0 ]; then echo " Finished with $failed failed step(s)." echo " Read the full write-up at https://jbtecwiz.com/support/lnx-sec-sudoers" else echo " Finished." fi echo prose 'To undo: Nothing else was changed. Note that if the disk is encrypted, the passphrase is still required.' rule