mirror of
https://github.com/gryf/mkinitramfs.git
synced 2026-03-12 16:25:46 +01:00
Added dropbear support
This commit is contained in:
@@ -69,6 +69,11 @@ similar to those passed via commandline. Consider following example:
|
|||||||
key_path = "/full/path/to/the/keys/dir"
|
key_path = "/full/path/to/the/keys/dir"
|
||||||
key = "laptop.key"
|
key = "laptop.key"
|
||||||
yubikey = true
|
yubikey = true
|
||||||
|
dropbear = true
|
||||||
|
ip = '192.168.0.1'
|
||||||
|
gateway = '192.168.0.254'
|
||||||
|
netmask = '24'
|
||||||
|
authorized_keys = "/full/path/to/the/.ssh/authorized_keys"
|
||||||
|
|
||||||
This will inform mkinitramfs script, that dropbear and yubikey features are
|
This will inform mkinitramfs script, that dropbear and yubikey features are
|
||||||
enabled. Also for network related configuration, there are last three options.
|
enabled. Also for network related configuration, there are last three options.
|
||||||
@@ -82,6 +87,7 @@ The complete list of supported options is listed below:
|
|||||||
- ``disk_label``
|
- ``disk_label``
|
||||||
- ``sdcard``
|
- ``sdcard``
|
||||||
- ``yubikey``
|
- ``yubikey``
|
||||||
|
- ``dropbear``
|
||||||
|
|
||||||
Using key devices
|
Using key devices
|
||||||
-----------------
|
-----------------
|
||||||
|
|||||||
174
mkinitramfs.py
174
mkinitramfs.py
@@ -16,6 +16,7 @@ XDG_DATA_HOME = os.getenv('XDG_DATA_HOME',
|
|||||||
os.path.expanduser('~/.local/share'))
|
os.path.expanduser('~/.local/share'))
|
||||||
CONF_PATH = os.path.join(XDG_CONFIG_HOME, 'mkinitramfs.toml')
|
CONF_PATH = os.path.join(XDG_CONFIG_HOME, 'mkinitramfs.toml')
|
||||||
KEYS_PATH = os.path.join(XDG_DATA_HOME, 'keys')
|
KEYS_PATH = os.path.join(XDG_DATA_HOME, 'keys')
|
||||||
|
ROOT_AK = '/root/.ssh/authorized_keys'
|
||||||
SHEBANG = "#!/bin/bash\n"
|
SHEBANG = "#!/bin/bash\n"
|
||||||
SHEBANG_ASH = "#!/bin/sh\n"
|
SHEBANG_ASH = "#!/bin/sh\n"
|
||||||
DEPS = """
|
DEPS = """
|
||||||
@@ -25,6 +26,7 @@ DEPS=(
|
|||||||
/sbin/cryptsetup
|
/sbin/cryptsetup
|
||||||
%(lvm)s
|
%(lvm)s
|
||||||
%(yubikey)s
|
%(yubikey)s
|
||||||
|
%(dropbear)s
|
||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
# /usr/sbin/dropbear
|
# /usr/sbin/dropbear
|
||||||
@@ -45,6 +47,15 @@ for path in $(find /usr/lib/gcc|grep libgcc_s.so.1); do
|
|||||||
[ "$(basename $(dirname $path))" = '32' ] && continue
|
[ "$(basename $(dirname $path))" = '32' ] && continue
|
||||||
cp $path lib/
|
cp $path lib/
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if %s; then
|
||||||
|
if [ ! -f ~/.cache/askpass ]; then
|
||||||
|
wget "https://bitbucket.org/piotrkarbowski/better-initramfs/downloads/askpass.c"
|
||||||
|
gcc -Os -static askpass.c -o ~/.cache/askpass
|
||||||
|
rm askpass.c
|
||||||
|
fi
|
||||||
|
cp ~/.cache/askpass bin/
|
||||||
|
fi
|
||||||
"""
|
"""
|
||||||
COPY_MODULES = """
|
COPY_MODULES = """
|
||||||
KERNEL=$(readlink /usr/src/linux)
|
KERNEL=$(readlink /usr/src/linux)
|
||||||
@@ -73,9 +84,9 @@ umask 0077
|
|||||||
[ ! -d /mnt ] && mkdir /mnt
|
[ ! -d /mnt ] && mkdir /mnt
|
||||||
[ ! -d /new-root ] && mkdir /new-root
|
[ ! -d /new-root ] && mkdir /new-root
|
||||||
|
|
||||||
|
mount -t devtmpfs -o nosuid,relatime,size=10240k,mode=755 devtmpfs /dev
|
||||||
mount -t proc proc /proc
|
mount -t proc proc /proc
|
||||||
mount -t sysfs sysfs /sys
|
mount -t sysfs sysfs /sys
|
||||||
mount -t devtmpfs devtmpfs /dev
|
|
||||||
|
|
||||||
# clean i/o
|
# clean i/o
|
||||||
exec >/dev/console </dev/console 2>&1
|
exec >/dev/console </dev/console 2>&1
|
||||||
@@ -147,6 +158,49 @@ for counter in $(seq 3); do
|
|||||||
done
|
done
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# optional: dropbear script for mounting device. It will use key if present
|
||||||
|
# and interactively prompt for password
|
||||||
|
DROPBEAR_SCRIPT = """
|
||||||
|
for counter in $(seq 3); do
|
||||||
|
sleep 1
|
||||||
|
$CLEAR
|
||||||
|
for dev in /dev/sd* /dev/nvme*; do
|
||||||
|
if cryptsetup isLuks ${dev}; then
|
||||||
|
if [ $(cryptsetup luksUUID ${dev}) = "${UUID}" ]; then
|
||||||
|
DEVICE=$dev
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[ -n "${DEVICE}" ] && break
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "${DEVICE}" ]; then
|
||||||
|
echo "No LUKS device found to boot from! Giving up."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -b /dev/mapper/root ]; then
|
||||||
|
for i in 0 1 2 ; do
|
||||||
|
askpass 'Enter decryption key: ' |ccrypt -c -k - $KEY | \
|
||||||
|
cryptsetup open --allow-discards $DEVICE root
|
||||||
|
ret=$?
|
||||||
|
[ ${ret} -eq 0 ] && break
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
if [ ! -b /dev/mapper/root ]; then
|
||||||
|
echo "Failed to open encrypted device $DEVICE"
|
||||||
|
exit 2
|
||||||
|
else
|
||||||
|
echo "Successfully opened root device, continue booting."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Kill the process for interactively providing password
|
||||||
|
if [ ${ret} -eq 0 ]; then
|
||||||
|
killall ccrypt
|
||||||
|
fi
|
||||||
|
"""
|
||||||
|
|
||||||
# Open encrypted fs
|
# Open encrypted fs
|
||||||
INIT_OPEN = """
|
INIT_OPEN = """
|
||||||
for counter in $(seq 3); do
|
for counter in $(seq 3); do
|
||||||
@@ -201,14 +255,29 @@ done
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
DROPBEAR = """\
|
||||||
|
mkdir /dev/pts
|
||||||
|
mount devpts /dev/pts -t devpts
|
||||||
|
|
||||||
|
ifconfig eth0 %(ip)s netmask %(netmask)s up
|
||||||
|
route add default gw %(gateway)s eth0
|
||||||
|
|
||||||
|
dropbear -s -g -p 22
|
||||||
|
"""
|
||||||
|
|
||||||
DECRYPT_PASSWORD = """
|
DECRYPT_PASSWORD = """
|
||||||
if [ ! -b /dev/mapper/root ]; then
|
if [ ! -b /dev/mapper/root ]; then
|
||||||
for i in 0 1 2 ; do
|
for i in 0 1 2 ; do
|
||||||
ccrypt -c $KEY | cryptsetup open --allow-discards $DEVICE root
|
ccrypt -c $KEY | cryptsetup open --allow-discards $DEVICE root
|
||||||
ret=$?
|
if [ -b /dev/mapper/root ]; then
|
||||||
[ ${ret} -eq 0 ] && break
|
break
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
if [ ! -b /dev/mapper/root ]; then
|
||||||
|
echo "Failed to open encrypted device. Reboot in 5 seconds."
|
||||||
|
reboot -f -d 5
|
||||||
|
fi
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SWROOT = """
|
SWROOT = """
|
||||||
@@ -230,6 +299,7 @@ exec switch_root /new-root /sbin/init
|
|||||||
class Config:
|
class Config:
|
||||||
defaults = {'copy_modules': False,
|
defaults = {'copy_modules': False,
|
||||||
'disk_label': None,
|
'disk_label': None,
|
||||||
|
'dropbear': False,
|
||||||
'install': False,
|
'install': False,
|
||||||
'key_path': None,
|
'key_path': None,
|
||||||
'lvm': False,
|
'lvm': False,
|
||||||
@@ -265,23 +335,37 @@ class Config:
|
|||||||
# UUID is only available via config file
|
# UUID is only available via config file
|
||||||
self.uuid = toml_.get('uuid')
|
self.uuid = toml_.get('uuid')
|
||||||
|
|
||||||
|
# dropbear conf available only via config file
|
||||||
|
self.ip = toml_.get('ip')
|
||||||
|
self.gateway = toml_.get('gateway')
|
||||||
|
self.netmask = toml_.get('netmask')
|
||||||
|
self.authorized_keys = toml_.get('authorized_keys', ROOT_AK)
|
||||||
|
|
||||||
|
|
||||||
class Initramfs(object):
|
class Initramfs(object):
|
||||||
def __init__(self, conf):
|
def __init__(self, conf):
|
||||||
self.lvm = conf.lvm
|
|
||||||
self.yk = conf.yubikey
|
|
||||||
self.name = args.disk
|
|
||||||
self.modules = conf.copy_modules
|
self.modules = conf.copy_modules
|
||||||
self.key_path = conf.key_path
|
|
||||||
self.disk_label = conf.disk_label
|
self.disk_label = conf.disk_label
|
||||||
self.sdcard = conf.sdcard
|
self.dropbear = conf.dropbear
|
||||||
self.install = conf.install
|
self.install = conf.install
|
||||||
|
self.key_path = conf.key_path
|
||||||
|
self.key = None
|
||||||
|
self.lvm = conf.lvm
|
||||||
self.no_key = conf.no_key
|
self.no_key = conf.no_key
|
||||||
|
self.sdcard = conf.sdcard
|
||||||
|
self.yk = conf.yubikey
|
||||||
|
|
||||||
|
self.uuid = conf.uuid
|
||||||
|
|
||||||
|
self.ip = conf.ip
|
||||||
|
self.gateway = conf.gateway
|
||||||
|
self.netmask = conf.netmask
|
||||||
|
self.authorized_keys = conf.authorized_keys
|
||||||
|
|
||||||
self.dirname = None
|
self.dirname = None
|
||||||
self.kernel_ver = os.readlink('/usr/src/linux').replace('linux-', '')
|
self.kernel_ver = os.readlink('/usr/src/linux').replace('linux-', '')
|
||||||
self._make_tmp()
|
self._make_tmp()
|
||||||
self._disks = conf.drive
|
self._drive = conf.drive
|
||||||
|
|
||||||
def _make_tmp(self):
|
def _make_tmp(self):
|
||||||
self.dirname = tempfile.mkdtemp(prefix='init_')
|
self.dirname = tempfile.mkdtemp(prefix='init_')
|
||||||
@@ -289,7 +373,7 @@ class Initramfs(object):
|
|||||||
|
|
||||||
def _make_dirs(self):
|
def _make_dirs(self):
|
||||||
os.chdir(self.dirname)
|
os.chdir(self.dirname)
|
||||||
for dir_ in ('bin', 'dev', 'etc', 'keys', 'lib64', 'proc',
|
for dir_ in ('bin', 'dev', 'etc', 'keys', 'lib64', 'proc', 'root',
|
||||||
'run/cryptsetup', 'run/lock', 'sys', 'tmp'):
|
'run/cryptsetup', 'run/lock', 'sys', 'tmp'):
|
||||||
os.makedirs(os.path.join(self.dirname, dir_))
|
os.makedirs(os.path.join(self.dirname, dir_))
|
||||||
|
|
||||||
@@ -306,9 +390,11 @@ class Initramfs(object):
|
|||||||
with open(fname, 'w') as fobj:
|
with open(fname, 'w') as fobj:
|
||||||
lvm = '/sbin/lvscan\n/sbin/vgchange' if self.lvm else ''
|
lvm = '/sbin/lvscan\n/sbin/vgchange' if self.lvm else ''
|
||||||
yubikey = '/usr/bin/ykchalresp' if self.yk else ''
|
yubikey = '/usr/bin/ykchalresp' if self.yk else ''
|
||||||
|
dropbear = '/usr/sbin/dropbear' if self.dropbear else ''
|
||||||
fobj.write(SHEBANG)
|
fobj.write(SHEBANG)
|
||||||
fobj.write(DEPS % {'lvm': lvm, 'yubikey': yubikey})
|
fobj.write(DEPS % {'lvm': lvm, 'yubikey': yubikey,
|
||||||
fobj.write(COPY_DEPS)
|
'dropbear': dropbear})
|
||||||
|
fobj.write(COPY_DEPS % 'true' if self.dropbear else 'false')
|
||||||
|
|
||||||
# extra crap, which seems to be needed, but is not direct dependency
|
# extra crap, which seems to be needed, but is not direct dependency
|
||||||
for root, _, fnames in os.walk('/usr/lib'):
|
for root, _, fnames in os.walk('/usr/lib'):
|
||||||
@@ -319,12 +405,57 @@ class Initramfs(object):
|
|||||||
if f.split('.')[0] in additional_libs:
|
if f.split('.')[0] in additional_libs:
|
||||||
shutil.copy(os.path.join(root, f), 'lib64',
|
shutil.copy(os.path.join(root, f), 'lib64',
|
||||||
follow_symlinks=False)
|
follow_symlinks=False)
|
||||||
|
self._copy_dropbear_deps()
|
||||||
|
|
||||||
os.chmod(fname, 0b111101101)
|
os.chmod(fname, 0b111101101)
|
||||||
subprocess.call([fname])
|
subprocess.call([fname])
|
||||||
os.unlink(fname)
|
os.unlink(fname)
|
||||||
os.chdir(self.curdir)
|
os.chdir(self.curdir)
|
||||||
|
|
||||||
|
def _copy_dropbear_deps(self):
|
||||||
|
if not self.dropbear:
|
||||||
|
return
|
||||||
|
|
||||||
|
for dir_ in ('root/.ssh', 'etc/dropbear'):
|
||||||
|
os.makedirs(os.path.join(self.dirname, dir_))
|
||||||
|
|
||||||
|
additional_libs = ['libnss_compat', 'libnss_files']
|
||||||
|
for root, _, fnames in os.walk('/lib64'):
|
||||||
|
for f in fnames:
|
||||||
|
if f.split('.')[0] in additional_libs:
|
||||||
|
shutil.copy(os.path.join(root, f), 'lib64',
|
||||||
|
follow_symlinks=False)
|
||||||
|
|
||||||
|
shutil.copy('/etc/localtime', 'etc')
|
||||||
|
|
||||||
|
# Copy the authorized keys for your regular user you administrate with
|
||||||
|
if self.authorized_keys and os.path.exists(self.authorized_keys):
|
||||||
|
shutil.copy(self.authorized_keys, 'root/.ssh')
|
||||||
|
|
||||||
|
# Copy OpenSSH's host keys to keep both initramfs' and regular ssh
|
||||||
|
# signed the same otherwise openssh clients will see different host
|
||||||
|
# keys and chicken out. Here we only copy the ecdsa host key, because
|
||||||
|
# ecdsa is default with OpenSSH. For RSA and others, copy adequate
|
||||||
|
# keyfile.
|
||||||
|
subprocess.run(['dropbearconvert', 'openssh', 'dropbear',
|
||||||
|
'/etc/ssh/ssh_host_ecdsa_key',
|
||||||
|
'etc/dropbear/dropbear_ecdsa_host_key'])
|
||||||
|
|
||||||
|
# Basic system defaults
|
||||||
|
with open('etc/passwd', 'w') as fobj:
|
||||||
|
fobj.write("root:x:0:0:root:/root:/bin/sh\n")
|
||||||
|
with open('etc/shadow', 'w') as fobj:
|
||||||
|
fobj.write("root:*:::::::\n")
|
||||||
|
with open('etc/group', 'w') as fobj:
|
||||||
|
fobj.write("root:x:0:root\n")
|
||||||
|
with open('etc/shells', 'w') as fobj:
|
||||||
|
fobj.write("/bin/sh\n")
|
||||||
|
os.chmod('etc/shadow', 0b110100000)
|
||||||
|
with open('etc/nsswitch.conf', 'w') as fobj:
|
||||||
|
fobj.write("passwd: files\n"
|
||||||
|
"shadow: files\n"
|
||||||
|
"group: files\n")
|
||||||
|
|
||||||
def _copy_modules(self):
|
def _copy_modules(self):
|
||||||
if not self.modules:
|
if not self.modules:
|
||||||
return
|
return
|
||||||
@@ -367,6 +498,7 @@ class Initramfs(object):
|
|||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
key_path = os.path.abspath(key_path)
|
key_path = os.path.abspath(key_path)
|
||||||
|
self.key = os.path.basename(key_path)
|
||||||
os.chdir(self.dirname)
|
os.chdir(self.dirname)
|
||||||
shutil.copy2(key_path, 'keys')
|
shutil.copy2(key_path, 'keys')
|
||||||
os.chdir(self.curdir)
|
os.chdir(self.curdir)
|
||||||
@@ -389,10 +521,25 @@ class Initramfs(object):
|
|||||||
fobj.write(DECRYPT_KEYDEV)
|
fobj.write(DECRYPT_KEYDEV)
|
||||||
if self.yk:
|
if self.yk:
|
||||||
fobj.write(DECRYPT_YUBICP % {'disk': self._drive})
|
fobj.write(DECRYPT_YUBICP % {'disk': self._drive})
|
||||||
|
if self.dropbear:
|
||||||
|
fobj.write(DROPBEAR % {'ip': self.ip, 'gateway': self.gateway,
|
||||||
|
'netmask': self.netmask})
|
||||||
fobj.write(DECRYPT_PASSWORD)
|
fobj.write(DECRYPT_PASSWORD)
|
||||||
|
if self.dropbear:
|
||||||
|
fobj.write("killall dropbear\n")
|
||||||
fobj.write(SWROOT)
|
fobj.write(SWROOT)
|
||||||
|
|
||||||
os.chmod('init', 0b111101101)
|
os.chmod('init', 0b111101101)
|
||||||
|
|
||||||
|
if self.dropbear:
|
||||||
|
with open('root/decrypt.sh', 'w') as fobj:
|
||||||
|
fobj.write(SHEBANG_ASH)
|
||||||
|
fobj.write(f"UUID='{self.uuid}'\n")
|
||||||
|
if self.key:
|
||||||
|
fobj.write(f"KEY='/keys/{self.key}'\n")
|
||||||
|
fobj.write(DROPBEAR_SCRIPT)
|
||||||
|
os.chmod('root/decrypt.sh', 0b111101101)
|
||||||
|
|
||||||
os.chdir(self.curdir)
|
os.chdir(self.curdir)
|
||||||
|
|
||||||
def _mkcpio_arch(self):
|
def _mkcpio_arch(self):
|
||||||
@@ -513,6 +660,9 @@ def main():
|
|||||||
help='Enable LVM in init.')
|
help='Enable LVM in init.')
|
||||||
parser.add_argument('-y', '--yubikey', action='store_true',
|
parser.add_argument('-y', '--yubikey', action='store_true',
|
||||||
help='Enable Yubikey challenge-response in init.')
|
help='Enable Yubikey challenge-response in init.')
|
||||||
|
parser.add_argument('-b', '--dropbear', action='store_true',
|
||||||
|
help='Enable dropbear ssh server for remotely connect '
|
||||||
|
'to initrd.')
|
||||||
parser.add_argument('drive', choices=disks.keys(), help='Drive name')
|
parser.add_argument('drive', choices=disks.keys(), help='Drive name')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|||||||
Reference in New Issue
Block a user