This commit is contained in:
Shiz 2022-04-02 15:06:10 +02:00
commit 04e6c3c3b1
11 changed files with 1393 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/askpass
/secrets/*

11
LICENSE Normal file
View File

@ -0,0 +1,11 @@
Copyright (c) 2010-2018, Piotr Karbowski <piotr.karbowski@gmail.com>
Copyright (c) 2020-2022, Shiz <hi@shiz.me>
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Piotr Karbowski nor Shiz nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

418
askpass.c Normal file
View File

@ -0,0 +1,418 @@
/*
* askpass.c - prompts a user for a passphrase using any suitable method
* and prints the result to stdout.
*
* Copyright (C) 2008 David Härdeman <david@hardeman.nu>
*
* This package is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this package; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* 2012 - Piotr Karbowski <piotr.karbowski@gmail.com>
* * Dropped splasy code.
*/
#define _GNU_SOURCE
#define _BSD_SOURCE
#define _POSIX_C_SOURCE 1
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <termios.h>
#include <sys/klog.h>
#include <sys/select.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <dirent.h>
#include <linux/vt.h>
#include <sys/un.h>
#include <sys/uio.h>
#define DEBUG 0
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
static bool disable_method(const char *method);
/*****************************************************************************
* Utility functions *
*****************************************************************************/
static void
debug(const char *fmt, ...)
{
va_list ap;
static bool first = true;
static FILE *dbgfile;
if (!DEBUG)
return;
if (first) {
first = false;
dbgfile = fopen("/tmp/askpass.debug", "a");
}
if (!dbgfile)
return;
va_start(ap, fmt);
vfprintf(dbgfile, fmt, ap);
va_end(ap);
}
static void
usage(const char *arg0, const char *errmsg)
{
if (errmsg)
fprintf(stderr, "Error: %s\nUsage: %s PROMPT\n", errmsg, arg0);
else
fprintf(stderr, "Usage: %s PROMPT\n", arg0);
exit(EXIT_FAILURE);
}
static void
fifo_common_finish(int fd, char **buf, size_t *used, size_t *size)
{
if (fd >= 0)
close(fd);
if (!*buf)
return;
memset(*buf, '\0', *size);
free(*buf);
*buf = NULL;
*used = 0;
*size = 0;
}
static bool
fifo_common_read(int fd, char **buf, size_t *used, size_t *size)
{
ssize_t result;
again:
if ((*size - *used) == 0) {
*size += 4096;
*buf = realloc(*buf, *size);
if (!*buf) {
*size = 0;
*used = 0;
debug("Failed to allocate memory for passphrase\n");
return false;
}
}
reread:
result = read(fd, *buf + *used, *size - *used);
if (result < 0) {
if (errno == EAGAIN)
return false;
if (errno == EINTR)
goto reread;
debug("Error when reading from fifo\n");
return false;
}
debug("Read %i bytes from fifo\n", (int)result);
*used += result;
if (result == 0)
return true;
goto again;
}
/*****************************************************************************
* fifo functions *
*****************************************************************************/
#define FIFO_PATH "/luks_passfifo"
static size_t fifoused = 0;
static size_t fifosize = 0;
static char *fifobuf = NULL;
static void
fifo_finish(int fd)
{
fifo_common_finish(fd, &fifobuf, &fifoused, &fifosize);
}
static bool
fifo_read(int fd, char **buf, size_t *size)
{
debug("In fifo_read\n");
if (fifo_common_read(fd, &fifobuf, &fifoused, &fifosize)) {
*buf = fifobuf;
*size = fifoused;
return true;
}
return false;
}
static int
fifo_prepare(const char *prompt)
{
int ret;
ret = mkfifo(FIFO_PATH, 0600);
if (ret && errno != EEXIST)
return -1;
return open(FIFO_PATH, O_RDONLY | O_NONBLOCK);
}
/*****************************************************************************
* console functions *
*****************************************************************************/
#define CONSOLE_PATH "/dev/console"
static struct termios term_old;
static bool term_set = false;
static char *consolebuf = NULL;
static size_t consolebuflen = 0;
static void
console_finish(int fd)
{
if (consolebuf) {
memset(consolebuf, '\0', consolebuflen);
free(consolebuf);
consolebuf = NULL;
consolebuflen = 0;
}
if (!term_set || fd < 0)
return;
term_set = false;
tcsetattr(fd, TCSAFLUSH, &term_old);
fprintf(stderr, "\n");
klogctl(7, NULL, 0);
}
bool
console_read(int fd, char **buf, size_t *size)
{
ssize_t nread;
/* Console is in ICANON mode so we'll get entire lines */
nread = getline(&consolebuf, &consolebuflen, stdin);
if (nread < 0)
return NULL;
/* Strip trailing newline, if any */
if (nread > 0 && consolebuf[nread - 1] == '\n') {
nread--;
consolebuf[nread] = '\0';
}
*size = nread;
*buf = consolebuf;
return true;
}
static int
console_prepare(const char *prompt)
{
struct termios term_new;
char *prompt_ptr = prompt;
char *newline = NULL;
if (!isatty(STDIN_FILENO)) {
if (access(CONSOLE_PATH, R_OK | W_OK)) {
debug("No access to console device " CONSOLE_PATH "\n");
return -1;
}
if (!freopen(CONSOLE_PATH, "r", stdin) ||
!freopen(CONSOLE_PATH, "a", stdout) ||
!freopen(CONSOLE_PATH, "a", stderr) ||
!isatty(STDIN_FILENO)) {
debug("Failed to open console\n");
return -1;
}
}
if (tcgetattr(STDIN_FILENO, &term_old)) {
debug("Failed to get terminal settings\n");
return -1;
}
term_new = term_old;
term_new.c_lflag &= ~ECHO;
term_new.c_lflag |= ICANON;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_new)) {
debug("Failed to disable echoing\n");
return -1;
}
/* handle any non-literal embedded newlines in prompt */
while ( (newline = strstr(prompt_ptr,"\\n")) != NULL ) {
/* Calculate length of string leading up to newline. */
int line_len = newline - prompt_ptr;
/* Force trimming of prompt to location of newline. */
if (fwrite(prompt_ptr, line_len, 1, stderr) < 1 ||
fwrite("\n", 1, 1, stderr) < 1) {
debug("Failed to print prompt\n");
tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_old);
return -1;
}
/* Skip over newline. */
prompt_ptr = newline + 2;
}
if (fputs(prompt_ptr, stderr) < 0) {
debug("Failed to print prompt\n");
tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_old);
return -1;
}
/* Disable printk to console */
klogctl(6, NULL, 0);
term_set = true;
return STDIN_FILENO;
}
/*****************************************************************************
* main functions *
*****************************************************************************/
struct method {
const char *name;
int (*prepare)(const char *prompt);
bool (*read)(int fd, char **buf, size_t *size);
void (*finish)(int fd);
bool active;
bool enabled;
int fd;
};
static struct method methods[] = {
{ "fifo", fifo_prepare, fifo_read, fifo_finish, false, true, -1 },
{ "console", console_prepare, console_read, console_finish, false, true, -1 }
};
static bool
disable_method(const char *method)
{
int i;
bool result = false;
debug("Disabling method %s\n", method ? method : "ALL");
for (i = 0; i < ARRAY_SIZE(methods); i++) {
/* A NULL method means all methods should be disabled */
if (method && strcmp(methods[i].name, method))
continue;
if (!methods[i].enabled)
continue;
if (methods[i].active)
methods[i].finish(methods[i].fd);
methods[i].active = false;
methods[i].fd = -1;
methods[i].enabled = false;
result = true;
}
return result;
}
int
main(int argc, char **argv, char **envp)
{
char *pass = NULL;
size_t passlen = 0;
int i;
int nfds;
fd_set fds;
int ret;
bool done = false;
sigset_t sigset;
if (argc != 2)
usage(argv[0], "incorrect number of arguments");
sigfillset(&sigset);
sigprocmask(SIG_BLOCK, &sigset, NULL);
for (i = 0; i < ARRAY_SIZE(methods); i++) {
if (!methods[i].enabled)
continue;
debug("Enabling method %s\n", methods[i].name);
methods[i].fd = methods[i].prepare(argv[1]);
if (methods[i].fd < 0)
methods[i].active = false;
else
methods[i].active = true;
}
while (!done) {
nfds = 0;
FD_ZERO(&fds);
for (i = 0; i < ARRAY_SIZE(methods); i++) {
if (!methods[i].enabled || methods[i].fd < 0)
continue;
debug("method %i has fd %i and name %s\n", i, methods[i].fd, methods[i].name);
FD_SET(methods[i].fd, &fds);
if (methods[i].fd + 1 > nfds)
nfds = methods[i].fd + 1;
}
if (nfds == 0) {
debug("All methods disabled\n");
exit(EXIT_FAILURE);
}
debug("Starting select with nfds %i\n", nfds);
ret = select(nfds, &fds, NULL, NULL, NULL);
if (ret <= 0) {
if (ret == 0 || errno == EINTR)
continue;
debug("Select failed\n");
disable_method(NULL);
exit(EXIT_FAILURE);
}
for (i = 0; i < ARRAY_SIZE(methods); i++) {
if (!methods[i].enabled || methods[i].fd < 0)
continue;
if (!FD_ISSET(methods[i].fd, &fds))
continue;
if (methods[i].read(methods[i].fd, &pass, &passlen) && pass) {
done = true;
break;
}
}
}
debug("Writing %i bytes to stdout\n", (int)passlen);
write(STDOUT_FILENO, pass, passlen);
disable_method(NULL);
exit(EXIT_SUCCESS);
}

51
mkbase.sh Executable file
View File

@ -0,0 +1,51 @@
#!/bin/sh
set -e
if [ $# -lt 1 ]; then
echo "usage: $0 <image> [cmdline] [arch]"
exit 1
fi
image="$1"
cmdline="$2"
arch="$3"
staging=$(mktemp -d)
trap "rm -rf '$staging'" EXIT
if [ -z "$arch" ]; then
arch=$(apk --print-arch)
fi
apk add --root="$staging" --arch "$arch" --repositories-file /etc/apk/repositories --keys-dir /etc/apk/keys --initdb \
alpine-baselayout busybox busybox-suid cryptsetup zfs dropbear
cp -R rootfs/* "$staging"
cc -static askpass.c -o "$staging"/bin/askpass
git_tag="$(git describe --tags --exact-match HEAD 2>/dev/null || true)"
git_branch="$(git symbolic-ref HEAD 2>/dev/null || true)"
git_latestcommit="$(git log -1 --date='short' --format='%h-%ad' || true)"
if [ -n "$git_tag" ]; then
version="$git_tag"
elif [ -n "$git_latestcommit" ]; then
version="$git_branch-$git_latestcommit"
elif [ -n "$git_branch" ]; then
version="$git_branch-???"
else
version="???"
fi
echo "$version" > "$staging"/VERSION
cp secrets/authorized_keys "$staging"
mkdir -p "$staging"/etc/dropbear
for x in rsa ecdsa; do
hostkey=secrets/dropbear_${x}_host_key
if ! [ -f "$hostkey" ]; then
"$staging"/usr/bin/dropbearkey -t $x -f "$hostkey"
fi
cp "$hostkey" "$staging"/etc/dropbear
done
echo " $cmdline" >> "$staging"/cmdline
(cd "$staging" && find . | cpio -H newc -o | gzip -9) > "$image"

42
mkmod.sh Executable file
View File

@ -0,0 +1,42 @@
#!/bin/sh
set -e
if [ $# -lt 1 ]; then
echo "usage: $0 <dest.img> [version]"
exit 1
fi
dest="$1"
version="${2:-$(uname -r)}"
basedir="/lib/modules/$version"
stagedir=$(mktemp -d)
trap "rm -rf '$stagedir'" EXIT
# Copy modules
mkdir -p "$stagedir/etc"
mkdir -p "$stagedir/$basedir"
cp "$basedir"/modules.* "$stagedir/$basedir"
grep -v '^#' /etc/modules | while read module; do
modprobe -S "$version" -D "$module" | while read -r action file; do
mkdir -p "$stagedir/$(dirname "$file")"
cp "$file" "$stagedir/$file"
done
echo "$module" >> "$stagedir"/etc/modules
done
# Copy loader
mkdir -p "$stagedir"/hooks/early
cat <<'EOF' > "$stagedir"/hooks/early/modules
#!/bin/sh
if [ -f /etc/modules ]; then
while read module; do
modprobe "$module"
done < /etc/modules
fi
EOF
chmod +x "$stagedir"/hooks/early/modules
# Make initramfs
(cd "$stagedir" && find . | cpio --quiet -H newc -o | gzip -9) > "$dest"

35
rootfs/bin/resume-boot Executable file
View File

@ -0,0 +1,35 @@
#!/bin/sh
# better-initramfs project
# https://bitbucket.org/piotrkarbowski/better-initramfs
# Copyright (c) 2010-2013, Piotr Karbowski <piotr.karbowski@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of the Piotr Karbowski nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE
# A small script to resume boot process.
# Kill regular rescueshell (which is exected on error OR forced via 'rescueshell' boot option'
# And remove /remote-rescueshell.lock what is executed when you login over ssh in the sshd_wait period
if [ -f '/rescueshell.pid' ]; then kill -9 "$(cat /rescueshell.pid)"; fi
if [ -f '/remote-rescueshell.lock' ]; then rm '/remote-rescueshell.lock'; fi

46
rootfs/bin/unlock-luks Executable file
View File

@ -0,0 +1,46 @@
#!/bin/sh
# better-initramfs project
# https://bitbucket.org/piotrkarbowski/better-initramfs
# Copyright (c) 2010-2013, Piotr Karbowski <piotr.karbowski@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of the Piotr Karbowski nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE
# Read password with hidden input and pass it to fifo.
. /functions.sh
IFS=''
if ! [ -e '/luks_passfifo' ]; then
eerror "There is no '/luks_passfifo'."
exit 1
fi
printf 'Enter password: '
stty -echo
read -r luks_password
stty echo
printf "%s" "${luks_password}" >/luks_passfifo
printf '\n'

0
rootfs/cmdline Normal file
View File

686
rootfs/functions.sh Executable file
View File

@ -0,0 +1,686 @@
#!/bin/sh
# better-initramfs project
# https://bitbucket.org/piotrkarbowski/better-initramfs
# Copyright (c) 2010-2013, Piotr Karbowski <piotr.karbowski@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of the Piotr Karbowski nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE
einfo() { echo -ne "\033[1;30m>\033[0;36m>\033[1;36m> \033[0m${*}\n" ;}
ewarn() { echo -ne "\033[1;30m>\033[0;33m>\033[1;33m> \033[0m${*}\n" >&2;}
eerror() { echo -ne "\033[1;30m>\033[0;31m>\033[1;31m> ${*}\033[0m\n" >&2 ;}
die() { eerror "$*"; rescueshell; }
InitializeBusybox() {
einfo "Create all the symlinks to /bin/busybox."
run /bin/busybox --install -s
}
rescueshell() {
if [ "$rescueshell" != 'true' ]; then
# If we did not forced rescueshell by kernel opt, print additional message.
ewarn "Dropping to rescueshell because of above error."
fi
ewarn "Rescue Shell (busybox's /bin/sh)"
ewarn "To reboot, press 'control-alt-delete'."
ewarn "If you wish resume booting process, run 'resume-boot'."
if [ "$console" ] && [ -c "/dev/${console}" ]; then
setsid sh -c "exec sh --login </dev/"${console}" >/dev/${console} 2>&1"
elif [ -c '/dev/tty1' ]; then
setsid sh -c 'exec sh --login </dev/tty1 >/dev/tty1 2>&1'
else
sh --login
fi
echo
:> /rescueshell.pid
}
was_shell() {
[ -f '/rescueshell.pid' ]
return $?
}
run() {
if "$@"; then
echo "Executed: '$@'" >> /init.log
else
eerror "'$@' failed."
echo "Failed: '$@'" >> /init.log
echo "$@" >> /.ash_history
rescueshell
fi
}
get_opt() {
echo "${*#*=}"
}
run_hooks() {
# Support for optional code injection via hooks placed into
# multiple initramfs images.
if [ -d "/hooks/$1" ]; then
for i in /hooks/$1/*; do
[ "$i" = "/hooks/$1/*" ] && break
einfo "Running '$i' hook ..."
[ -x "$i" ] && . "$i"
done
fi
}
populate_dev_disk_by_label_and_uuid() {
# Create /dev/disk/by-{uuid,label} symlinks.
# We could run it with mdev as a trigger on event,
# but binit uses devtmpfs by default.
# It is possible that later whatever manages /dev
# will not probe block device and create symlinks,
# so we should do it before we move /dev to /newroot/dev
# Fix for an issue reported under Gentoo's bug #559026.
local block_device blkid_output LABEL UUID TYPE
local vars
einfo "Populating /dev/disk/by-{uuid,label} ..."
dodir /dev/disk /dev/disk/by-uuid /dev/disk/by-label
for block_device in /sys/class/block/*; do
unset blkid_output LABEL UUID TYPE
block_device="${block_device##*/}"
blkid_output="$(blkid "/dev/${block_device}")"
[ "${blkid_output}" ] || continue
vars="${blkid_output#*:}"
eval "${vars}"
[ "${LABEL}" ] && ! [ -e "/dev/disk/by-label/${LABEL}" ] && run ln -s "../../${block_device}" "/dev/disk/by-label/${LABEL}"
[ "${UUID}" ] && ! [ -e "/dev/disk/by-uuid/${UUID}" ] && run ln -s "../../${block_device}" "/dev/disk/by-uuid/${UUID}"
done
}
resolve_device() {
# This function will check if variable at $1 contain LABEL or UUID and then, if LABEL/UUID is vaild.
device="$(eval echo \$$1)"
case "${device}" in
LABEL\=*|UUID\=*)
eval $1="$(findfs $device)"
if [ -z "$(eval echo \$$1)" ]; then
eerror "Wrong UUID or LABEL."
rescueshell
fi
;;
esac
}
process_commandline_options() {
for i in $(cat /cmdline /proc/cmdline); do
case "${i}" in
initramfsdebug)
set -x
;;
root\=*)
root=$(get_opt $i)
;;
init\=*)
init=$(get_opt $i)
;;
enc_root\=*)
enc_root=$(get_opt $i)
;;
zfs)
zfs=true
;;
luks)
luks=true
;;
lvm)
lvm=true
;;
softraid)
softraid=true
;;
rescueshell)
rescueshell=true
;;
swsusp)
swsusp=true
;;
uswsusp)
uswsusp=true
;;
tuxonice)
tuxonice=true
;;
resume\=*)
resume=$(get_opt $i)
;;
rootfstype\=*)
rootfstype=$(get_opt $i)
;;
rootflags\=*)
rootfsmountparams="-o $(get_opt $i)"
;;
ro|rw)
root_rw_ro=$i
;;
sshd)
sshd=true
;;
sshd_wait\=*)
sshd_wait=$(get_opt $i)
;;
sshd_port\=*)
sshd_port=$(get_opt $i)
;;
sshd_interface\=*)
# deprecated
sshd_interface=$(get_opt $i)
;;
sshd_ipv4\=*)
# deprecated
sshd_ipv4=$(get_opt $i)
;;
sshd_ipv4_gateway\=*)
# deprecated
sshd_ipv4_gateway=$(get_opt $i)
;;
binit_net_if\=*)
binit_net_if=$(get_opt $i)
;;
binit_net_addr\=*)
binit_net_addr=$(get_opt $i)
;;
binit_net_route\=*)
binit_net_routes="${binit_net_routes} $(get_opt $i)"
;;
binit_net_gw\=*)
binit_net_gw=$(get_opt $i)
;;
rootdelay\=*)
rootdelay=$(get_opt $i)
;;
console\=*)
console=$(get_opt $i)
console="${console%,*}"
;;
mdev)
mdev=true
;;
luks_no_discards)
luks_no_discards=true
;;
bcache)
bcache=true
;;
esac
done
}
use() {
name="$(eval echo \$$1)"
# Check if $name isn't empty and if $name isn't set to false or zero.
if [ -n "${name}" ] && [ "${name}" != 'false' ] && [ "${name}" != '0' ]; then
if [ -n "$2" ]; then
$2
else
return 0
fi
else
return 1
fi
}
musthave() {
while [ -n "$1" ]; do
# We can handle it by use() function, yay!
if ! use "$1"; then
eerror "The \"$1\" variable is empty, set to false or zero but shoudn't be."
local missing_variable='true'
fi
shift
done
if [ "${missing_variable}" = 'true' ]; then
rescueshell
fi
}
dodir() {
for dir in "$@"; do
run mkdir -m 700 -p "${dir}"
done
}
loadkeymap() {
if [ -f /keymap ]; then
loadkmap < /keymap
fi
}
get_majorminor() {
local device="$1"
musthave device
local major_hex="$(stat -L -c '%t' "${device}")"
local minor_hex="$(stat -L -c '%T' "${device}")"
musthave major_hex minor_hex
if [ -n "${major_hex}" ] && [ -n "${minor_hex}" ]; then
printf '%u:%u\n' "0x${major_hex}" "0x${minor_hex}"
fi
}
which() {
command -v "$1"
}
exists() {
which "$1" >/dev/null
}
InitializeLUKS() {
if ! exists cryptsetup ; then
eerror "There is no cryptsetup binary into initramfs image."
rescueshell
fi
musthave enc_root
local enc_num='1'
local dev_name="enc_root"
# We will use : to separate devices but we need normal IFS inside the for loop anyway.
local IFS=":"
for enc_dev in ${enc_root}; do
IFS="${default_ifs}"
if ! [ "${enc_num}" = '1' ]; then
dev_name="enc_root${enc_num}"
fi
resolve_device enc_dev
einfo "Opening encrypted partition '${enc_dev##*/}' and mapping to '/dev/mapper/${dev_name}'."
# Hack for cryptsetup which trying to run /sbin/udevadm.
run echo -e "#!/bin/sh\nexit 0" > /sbin/udevadm
run chmod 755 /sbin/udevadm
local cryptsetup_args=""
if ! use luks_no_discards; then
cryptsetup_args="${cryptsetup_args} --allow-discards"
fi
if use sshd; then
askpass "Enter passphrase for ${enc_dev}: " | run cryptsetup --tries 1 --key-file=- luksOpen ${cryptsetup_args} "${enc_dev}" "${dev_name}"
# Remove the fifo, askpass will create new if needed (ex multiple devices).
rm '/luks_passfifo'
else
run cryptsetup luksOpen --tries 25 ${cryptsetup_args} "${enc_dev}" "${dev_name}"
fi
enc_num="$((enc_num+1))"
done
}
InitializeLVM() {
einfo "Scaning all disks for volume groups."
# We have to ensure that cache does not exist so vgchange will run 'vgscan' itself.
if [ -d '/etc/lvm/cache' ]; then run rm -rf '/etc/lvm/cache'; fi
run lvm vgchange -a y
}
InitializeSoftwareRaid() {
einfo "Scaning for software raid arrays."
if ! [ -f '/etc/mdadm.conf' ]; then
run mdadm --examine --scan > /etc/mdadm.conf
fi
run mdadm --assemble --scan
# The software raid arrays may have partitions on them.
rereadpt
}
InitializeZFS() {
einfo "Initializing ZFS."
modprobe zfs
run zpool import -a
}
rereadpt() {
# Check for partition table on all block devices.
for i in /dev/*; do
if [ -b "${i}" ]; then
blockdev --rereadpt "${i}" >/dev/null 2>&1
fi
done
}
SwsuspResume() {
musthave resume
resolve_device resume
if [ -f '/sys/power/resume' ]; then
local resume_majorminor="$(get_majorminor "${resume}")"
musthave resume_majorminor
einfo 'Sending resume device to /sys/power/resume ...'
echo "${resume_majorminor}" > /sys/power/resume
else
ewarn "Apparently this kernel does not support suspend."
fi
}
UswsuspResume() {
musthave resume
resolve_device resume
if [ -f '/sys/power/resume' ]; then
run resume --resume_device "${resume}"
else
ewarn "Apparently this kernel does not support suspend."
fi
}
TuxOnIceResume() {
musthave resume
if [ -f '/sys/power/tuxonice/do_resume' ]; then
einfo "Sending do_resume signal to TuxOnIce."
run echo 1 > /sys/power/tuxonice/do_resume
else
ewarn "Apparently this kernel does not support TuxOnIce."
fi
}
register_bcache_devices() {
# Push all the block devices to register_quiet
# If its bcache, it will bring it up, if not, it will simply ignore it.
if ! [ -e /sys/fs/bcache/register_quiet ]; then
ewarn "There's no bcache interface. Missing kernel driver?"
return 0
fi
for i in $(awk '$4 !~ /^(name$|$)/ { print $4 }' /proc/partitions); do
if [ -e "/dev/${i}" ]; then
echo "/dev/${i}" >/sys/fs/bcache/register_quiet
else
echo "Looks like there's no '/dev/${i}', but should be."
fi
done
}
SetupNetwork() {
# Defaults ...
vlan='false'
# backward compatibility
if [ "${sshd_interface}" ]; then
ewarn "sshd_interface is deprecated, check README"
ewarn "and switch to binit_net_if!"
binit_net_if="${sshd_interface}"
binit_net_addr="${sshd_ipv4}"
binit_net_gw="${sshd_ipv4_gateway}"
fi
# setting _interface is the trigger.
# after dropping backward compatibility there should be
# a 'use FOO && SetupNetwork' in init script.
use binit_net_if || return
musthave binit_net_addr
run ip link set up dev lo
case "${binit_net_if}" in
*'.'*)
vlan='true'
binit_net_physif="${binit_net_if%%.*}"
local binit_net_vlan="${binit_net_if##*.}"
einfo "Bringing up ${binit_net_physif} interface ..."
run ip link set up dev "${binit_net_physif}"
einfo "Adding VLAN ${binit_net_vlan} on ${binit_net_physif} interface ..."
run vconfig add "${binit_net_physif}" "${binit_net_vlan}"
;;
esac
einfo "Bringing up ${binit_net_if} interface ..."
run ip link set up dev "${binit_net_if}"
einfo "Setting ${binit_net_addr} on ${binit_net_if} ..."
run ip addr add "${binit_net_addr}" dev "${binit_net_if}"
if [ -n "${binit_net_routes}" ]; then
for route in ${binit_net_routes}; do
einfo "Adding additional route '${route}' ..."
run ip route add "${route}" dev "${binit_net_if}"
done
fi
if [ -n "${binit_net_gw}" ]; then
einfo "Setting default routing via '${binit_net_gw}' ..."
run ip route add default via "${binit_net_gw}" dev "${binit_net_if}"
fi
}
setup_sshd() {
# Prepare /dev/pts.
einfo "Mounting /dev/pts ..."
if ! [ -d /dev/pts ]; then run mkdir /dev/pts; fi
run mount -t devpts none /dev/pts
# Prepare dirs.
dodir /etc/dropbear /var/log /var/run /root/.ssh
# Generate host keys.
einfo "Generating dropbear ssh host keys ..."
for x in rsa ecdsa; do
test -f /etc/dropbear/dropbear_$x_host_key || \
run dropbearkey -t $x -f /etc/dropbear/dropbear_$x_host_key > /dev/null
done
# Prepare root account.
run echo 'root:x:0:0:root:/root:/bin/sh' > /etc/passwd
if [ -f /authorized_keys ]; then
run cp /authorized_keys /root/.ssh/authorized_keys
else
eerror "Missing /authorized_keys file, you will be no able login via sshd."
rescueshell
fi
einfo 'Starting dropbear sshd ...'
run dropbear -s -p "${binit_net_addr%/*}:${sshd_port:-22}"
}
wait_sshd() {
if use sshd_wait; then
# sshd_wait exist, now we should sleep for X sec.
if [ "${sshd_wait}" -gt 0 2>/dev/null ]; then
einfo "Waiting ${sshd_wait}s (sshd_wait)"
while [ ${sshd_wait} -gt 0 ]; do
[ -f '/remote-rescueshell.lock' ] && break
sshd_wait=$((sshd_wait - 1))
sleep 1
done
else
ewarn "\$sshd_wait variable must be numeric and greater than zero. Skipping sshd_wait."
fi
fi
}
cleanup() {
if use binit_net_if; then
if [ -f '/remote-rescueshell.lock' ]; then
ewarn "The lockfile at '/remote-rescueshell.lock' exist."
ewarn "The boot process will be paused until the lock is removed."
while true; do
if [ -f '/remote-rescueshell.lock' ]; then
sleep 1
else
break
fi
done
fi
fi
was_shell && rm /rescueshell.pid
if use sshd; then
run pkill -9 dropbear > /dev/null 2>&1
fi
if use binit_net_if; then
run ip addr flush dev "${binit_net_if}"
run ip route flush dev "${binit_net_if}"
run ip link set down dev "${binit_net_if}"
if use vlan; then
run ip link del "${binit_net_if}"
run ip link set down dev "${binit_net_physif}"
fi
fi
}
boot_newroot() {
init="${init:-/sbin/init}"
einfo "Switching root to /newroot and executing ${init}."
if ! [ -x "/newroot/${init}" ]; then die "There is no executable '/newroot/${init}'."; fi
exec env -i \
TERM="${TERM:-linux}" \
PATH="${PATH:-/bin:/sbin:/usr/bin:/usr/sbin}" \
switch_root /newroot "${init}"
}
emount() {
# All mounts into one place is good idea.
local mountparams
while [ "$#" -gt 0 ]; do
case $1 in
'/newroot')
if mountpoint -q '/newroot'; then
einfo "/newroot already mounted, skipping..."
else
einfo "Mounting /newroot..."
musthave root
if [ "${rootfsmountparams}" ]; then
mountparams="${rootfsmountparams}"
fi
if [ -n "${rootfstype}" ]; then
mountparams="${mountparams} -t ${rootfstype}"
fi
resolve_device root
run mount -o ${root_rw_ro:-ro} ${mountparams} "${root}" '/newroot'
fi
;;
'/newroot/usr')
if [ -f '/newroot/etc/fstab' ]; then
while read device mountpoint fstype fsflags _; do
if [ "${mountpoint}" = '/usr' ]; then
if [ -d '/newroot/usr' ]; then
einfo "Mounting /newroot/usr..."
run mount -o "${fsflags},${root_rw_ro:-ro}" -t "${fstype}" "${device}" '/newroot/usr'
else
die "/usr in fstab present but no mountpoint /newroot/usr found."
fi
break
fi
done < '/newroot/etc/fstab'
else
ewarn "No /newroot/etc/fstab present."
ewarn "Early mouting of /usr will not be done."
fi
;;
'/dev')
local devmountopts='nosuid,relatime,size=10240k,mode=755'
if grep -q 'devtmpfs' '/proc/filesystems' && ! use mdev; then
einfo "Mounting /dev (devtmpfs)..."
run mount -t devtmpfs -o ${devmountopts} devtmpfs /dev
else
einfo "Mounting /dev (mdev over tmpfs)..."
run mount -t tmpfs -o ${devmountopts} dev /dev
run touch /etc/mdev.conf
run echo /sbin/mdev > /proc/sys/kernel/hotplug
run mdev -s
# Looks like mdev create /dev/pktcdvd as a file when both udev and devtmpfs do it as a dir.
# We will do it 'better' to avoid non-fatal-error while starting udev after switching to /newroot.
# TODO: Can mdev.conf handle it?
if [ -c '/dev/pktcdvd' ]; then
run rm '/dev/pktcdvd'
run mkdir '/dev/pktcdvd'
run mknod '/dev/pktcdvd/control' c 10 61
fi
fi
;;
'/proc')
einfo "Mounting /proc..."
run mount -t proc proc /proc
;;
'/sys')
einfo "Mounting /sys..."
run mount -t sysfs sysfs /sys
;;
*)
eerror "emount() does not understand \"$1\""
;;
esac
shift
done
}
eumount() {
while [ "$#" -gt 0 ]; do
case "$1" in
*)
einfo "Unmounting ${1}..."
run umount "$1"
;;
esac
shift
done
}
moveDev() {
einfo "Moving /dev to /newroot/dev..."
if mountpoint -q /dev/pts; then umount -l /dev/pts; fi
if use mdev; then run echo '' > /proc/sys/kernel/hotplug; fi
run mount --move /dev /newroot/dev
}
rootdelay() {
if [ "${rootdelay}" -gt 0 2>/dev/null ]; then
einfo "Waiting ${rootdelay}s (rootdelay)"
run sleep ${rootdelay}
else
ewarn "\$rootdelay variable must be numeric and greater than zero. Skipping rootdelay."
fi
}
# vim: noexpandtab

102
rootfs/init Executable file
View File

@ -0,0 +1,102 @@
#!/bin/sh
# better-initramfs project
# https://bitbucket.org/piotrkarbowski/better-initramfs
# Copyright (c) 2010-2013, Piotr Karbowski <piotr.karbowski@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of the Piotr Karbowski nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE
: ${PATH:=/bin:/sbin:/usr/bin:/usr/sbin}
# Load functions.
. /functions.sh
run_hooks init
# Prepare dirs
run dodir /dev /newroot /sbin /proc /sys /etc /var/log /var/run
# Basic /dev content, we need it as fast as possible.
[ ! -e /dev/console ] && run mknod /dev/console c 5 1
[ ! -e /dev/null ] && run mknod /dev/null c 1 3
[ ! -e /dev/tty ] && run mknod /dev/tty c 5 0
[ ! -e /dev/urandom ] && run mknod /dev/urandom c 1 9
[ ! -e /dev/random ] && run mknod /dev/random c 1 8
[ ! -e /dev/zero ] && run mknod /dev/zero c 1 5
# Clear the screen
clear
# Save IFS so we can easly restore it if we ever change it.
readonly default_ifs="${IFS}"
# Basic
kernelver="$(uname -r)"
ver="$(cat /VERSION)"
einfo "better-initramfs ${ver}. Linux kernel ${kernelver}.\n"
echo -e "\nWelcome to better-initramfs ${ver}. Linux kernel ${kernelver}.\n" > /etc/motd
emount /proc /sys
# Disable kernel messages from popping onto the screen
echo 0 > /proc/sys/kernel/printk
process_commandline_options
InitializeBusybox
emount /dev
run_hooks early
SetupNetwork
use sshd && setup_sshd
loadkeymap
# Let's try to support all possible combinations of storage layers.
use bcache register_bcache_devices
use softraid InitializeSoftwareRaid && use bcache register_bcache_devices
use lvm InitializeLVM && use bcache register_bcache_devices
use luks InitializeLUKS && use lvm InitializeLVM && use bcache register_bcache_devices
use zfs InitializeZFS
populate_dev_disk_by_label_and_uuid
use rescueshell rescueshell
use swsusp && SwsuspResume
use uswsusp && UswsuspResume
use tuxonice TuxOnIceResume
run_hooks pre_newroot_mount
use rootdelay rootdelay
emount /newroot
emount /newroot/usr
# Wait for sshd connection only if rescueshell have never occur.
use sshd && was_shell || wait_sshd
cleanup
moveDev
eumount /sys /proc
run_hooks pre_switch_root
boot_newroot
ewarn "Something went wrong."
rescueshell
# vim: noexpandtab

0
secrets/.keep Normal file
View File