#!/usr/bin/bash if [ $(/usr/bin/id -u) != 0 ]; then echo "only root can do that"; exit 2; fi # # Copyright 2015-2025 Senderek Web Security, Ireland. All rights reserved. # ############################################################################## # This file is part of the CRYPTO BONE # File : init.d/cryptoboned # Version : 2.0 (ALL-IN-ONE) # License : BSD-3-Clause # Date : 24 May 2025 # Contact : Please send enquiries and bug-reports to innovation@senderek.ie # # Copyright (c) 2015-2025 # Ralf Senderek, Ireland. All rights reserved. (https://senderek.ie) # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. 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. # 3. All advertising materials mentioning features or use of this software # must display the following acknowledgement: # This product includes software developed by Ralf Senderek. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. ############################################################################## # Author: Ralf Senderek # license: BSD-3-Clause # description: Activates the cryptobone daemon during the boot process # processname: cryptoboned # config: none # date: 24 May 2025 # This script makes sure that the cryptobone daemon is started during the boot process # of the machine working in ALL-IN-ONE mode. The cryptobone daemon cannot be started when the # machine has finished the boot process, because the secret information that is used to # decrypt the database of secrets is only visible in a fraction of a second during the # boot process. # To ensure that this script is started exclusively via the systemd service cryptoboned.service at # boot time a bootswitch is used to indicate boot time. The bootswitch is protected from deletion # using the chattr command to prevent modification. # # If the link /usr/lib/cryptobone/masterkey does not exist, the script /usr/lib/cryptobone/createmasterkey # initialises the necessary files that don't exist when the cryptobone is started for the first time. # Once the files boot.fs and permanent.fs exist, the encrypted boot.fs is mounted on the directory # /usr/lib/cryptobone/keys while cryptsetup has access to the key $KEYS/$DIR/$FILE. # Once the boot.fs is decrypted and successfully mounted and the encrypted database exists, # the essential cryptobone daemon is being started. The daemon now has access to the masterkey # that decrypts the database so that all secrets can be accessed in the daemon's memory. # # Secrets that reside in the daemon's memory can now be read and modified via the UNIX-socket (as root). # # In order to make the information stored in the decrypted boot file system invisible as soon as possible, # a permanent file system is mounted on the $KEYS directory after the boot file system is unmounted and # destroyed via cryptsetup. # This permanent file system remains in the $KEYS directory and holds only non-sensitive information # like hash values of the master key and a local key indentifying the machine that uses the daemon via # the UNIX socket. # If this machine is running in the ALL-IN-ONE mode the daemon has beem established and all is done. # If this machine uses an EXTERNAL device to store secrets in the memory of a cryptoboneexternd # on the external device, the external device must be contacted and queried for the secrets that are # stored in the external daemon. # # In order to secure the information on the external device, the daemon on the external device # does not possess the master key to decrypt the external database. This master key must be transferred # to the exernal device in a secure process. # # When the external device pings back under the IP address that is stored in the client, a ssh tunnel # is used to upload the master key the external daemon is waiting for. # In this case the master key (real.key) and the ssh private key (cbb) has been read during the # short time the boot file system was visible, so that the master key can be transported securely # to the external device with this script. This machine authenticates itself by opening the ssh tunnel # with the correct ssh private key (cbb) and by sending its local key to the external device # for verification there. # Once the external daemon has got the master key through the ssh tunnel, it is working and can # respond to queries using the same ssh tunnel by sending the external device's current status. # If this status is "active" this fact is recorded on this machine by creating the file UPLOADED. # If the external device is not working, the master key can only be sent again by rebooting this # machine. # # To examine the sequence of events, debugging can be enabled by creating (and subsequently destroying) # a file /root/.cryptobone.debug, that shows events and timestamps (to the root user) when the boot # process has finished. KEYS="/usr/lib/cryptobone/keys" REAL="none" SOCK="/usr/lib/cryptobone/secrets.sock" #-----------------------------------------------------# log_debug() { # make sure, non-sensitive information is stored for debugging purposes # if a file /root/.cryptobone.debug exists with permissions 0600 for the # root user DEB=/root/.cryptobone.debug if /bin/ls $DEB 2> /dev/null > /dev/null ; then PERM=$(/bin/ls -l $DEB | cut -c2-10) TIME=$(date '+%T %N') if [[ $PERM = "rw-------" ]] ; then if [[ x$1 = xclean ]] ; then echo -n > $DEB fi echo "$TIME $1 $2" >> $DEB fi fi } #-----------------------------------------------------# start() { if ! df | grep /usr/lib/cryptobone/keys 2> /dev/null > /dev/null then # we're booting # the package rng-tools should be installed and the rngd enabled by default # starting rngd here will slow down the shutdown / stop job for cryptoboned #/usr/sbin/rngd -b -r /dev/hwrnd 2> /dev/null # load the SELinux policy for the daemon log_debug "clean" if selinuxenabled ; then log_debug "start selinux module" semodule -i /usr/lib/cryptobone/selinux/cryptobone.pp semodule -e cryptobone log_debug "end selinux module" fi SWITCH=/usr/lib/cryptobone/bootswitch if [ ! -f $SWITCH ] then touch $SWITCH fi chattr -i $SWITCH chmod 600 $SWITCH chmod 700 $KEYS if [ ! -L /usr/lib/cryptobone/masterkey ] then # initialize database and create a master key and local key /usr/lib/cryptobone/createmasterkey # now there is $KEYS/random and both file systems (boot.fs and permanent.fs) fi /usr/bin/rm /usr/lib/cryptobone/UPLOADED 2> /dev/null # record evidence that we're really booting typeset -i BOOTTIME=$(cat /proc/stat | grep btime | cut -f2 -d" ") typeset -i NOW=$(date +%s) typeset -i DIFF=$NOW-$BOOTTIME echo $DIFF > $SWITCH log_debug "booting" DIR=$(echo -n $(cat $KEYS/random) $(stat -c "directory %i" $KEYS) | sha256sum | cut -c-64) FILE=$(echo -n $DIR $(stat -c "file %i" $KEYS/random) | sha256sum | cut -c-64) # establish the boot file system L=$(losetup -f) losetup $L $KEYS/boot.fs cat $KEYS/$DIR/$FILE | cryptsetup create cryptobone $L if mount /dev/mapper/cryptobone $KEYS then # start the daemon unless there is a non-empty database if [ ! -s /usr/lib/cryptobone/database ] ; then /bin/rm /usr/lib/cryptobone/database log_debug "performing database initialisation" systemctl start cryptobone-dbinit /bin/sleep 1 typeset -i x=0 while [ $x -lt 8 ] do if [ ! -s /usr/lib/cryptobone/database ] ; then log_debug "restarting database initialisation" systemctl stop cryptobone-dbinit /bin/rm /usr/lib/cryptobone/database systemctl start cryptobone-dbinit /bin/sleep 1 x=$x+1 else x=10 fi done fi rm -f /usr/lib/cryptobone/ssh.sock rm -f /usr/lib/cryptobone/secrets.sock log_debug "starting daemon ... " /usr/lib/cryptobone/cryptoboned log_debug "daemon started" ssh-agent -s -a /usr/lib/cryptobone/ssh.sock export SSH_AUTH_SOCK=/usr/lib/cryptobone/ssh.sock 2>/dev/null > /dev/null OVERWRITE=$(echo "get-element EXTERN.OVERWRITE" | socat - UNIX-connect:$SOCK 2> /dev/null) if [ x$OVERWRITE = "xyes" ] then rm -f $KEYS/real.key $KEYS/cbb fi if [ ! -r $KEYS/real.key ] then # check if external keys are stored in the encrypted database REALKEY=$(echo "get-element EXTERN.real.key" | socat - UNIX-connect:$SOCK 2> /dev/null) if [ x$REALKEY != "x" ] then echo $REALKEY > $KEYS/real.key chmod 600 $KEYS/real.key log_debug "overwriting master key" REALKEY="000000000000000000000000000000000000000000" unset REALKEY fi fi if [ ! -r $KEYS/cbb ] then SSHKEY=$(echo "get-element EXTERN.cbb" | socat - UNIX-connect:$SOCK 2> /dev/null) if [ x$SSHKEY != "x" ] then echo "$SSHKEY" | base64 -d > $KEYS/cbb chmod 600 $KEYS/cbb SSHKEY=$(/bin/dd if=/dev/zero bs=1K count=4 2>/dev/null) unset SSHKEY fi fi echo -n "replace EXTERN.OVERWRITE no" | socat - UNIX-connect:$SOCK 2> /dev/null if [ -f $KEYS/cbb ] then ssh-add $KEYS/cbb REAL=$(cat /usr/lib/cryptobone/keys/real.key) 2> /dev/null fi fi # remove the boot file system umount /dev/mapper/cryptobone cryptsetup remove cryptobone losetup -d $L # mount the permanent file system L=$(losetup -f) losetup $L $KEYS/permanent.fs mount $L $KEYS chmod 700 $KEYS echo 9999 > $SWITCH chattr +i $SWITCH log_debug "secrets become invisible" if [ ! -L /usr/lib/cryptobone/ALLINONE ] then # we're using a real EXTERNAL Crypto Bone log_debug "Trying to contact the external device" if [ -f /usr/lib/cryptobone/cbb.config ] then chmod 600 /usr/lib/cryptobone/cbb.config . /usr/lib/cryptobone/cbb.config log_debug "reading the config file cbb.config" # wait for BONEIP to come up typeset -i count=0 while [ $count -lt 20 ] do log_debug "waiting for the external device to ping" /bin/sleep 2 count=$count+1 /bin/ping -w1 -c1 ${BONEIP} 2>/dev/null > /dev/null if [ $? -eq 0 ] then count=21 fi done /bin/ping -w1 -c1 ${BONEIP} 2>/dev/null > /dev/null if [ $? -eq 0 ] then # upload the master key to the real crypto bone log_debug "external device is reachable" echo $(/usr/lib/cryptobone/getlocalsecret) SYSTEM UPLOAD $(echo $REAL) | /usr/bin/ssh -l cryptobone ${BONEIP} /cbb/cbcontrol log_debug "master key sent" count=0 while [ $count -lt 8 ] do /bin/sleep 2 count=$count+1 /bin/ping -w1 -c1 ${BONEIP} 2>/dev/null > /dev/null RES=$(echo $(/usr/lib/cryptobone/getlocalsecret) STATUS | /usr/bin/ssh -l cryptobone ${BONEIP} /cbb/cbcontrol) if [ "${RES}x" = "activex" ] then log_debug "device becomes active" echo "EXTERNAL is active now." touch /usr/lib/cryptobone/UPLOADED # now remove the master key from the database echo "remove-element EXTERN.real.key" | socat - UNIX-connect:$SOCK 2> /dev/null RES=$(echo "get-element EXTERN.real.key" | socat - UNIX-connect:$SOCK 2> /dev/null) echo "reading the master key :$RES master key is not stored in the database" count=11 RES=$(echo $(/usr/lib/cryptobone/getlocalsecret) SYSTEM RESTART | /usr/bin/ssh -l cryptobone ${BONEIP} /cbb/cbcontrol) else log_debug "device inactive [$RES]" RES=$(echo $(/usr/lib/cryptobone/getlocalsecret) SYSTEM UPLOAD $(echo $REAL) | /usr/bin/ssh -l cryptobone ${BONEIP} /cbb/cbcontrol) log_debug "uploading master key again [$RES]" fi log_debug "$count" "sending master key" done echo "EXTERNAL finished." REAL="000000000000000000000000000000000000000000" unset REAL fi fi else echo "ALL-IN-ONE finished." fi REAL="000000000000000000000000000000000000000000" unset REAL fi } #-----------------------------------------------------# stop() { if [ -d /dev/shm/RAM ] then /usr/lib/cryptobone/bin/savemessages fi killall cryptoboned } #-----------------------------------------------------# case "$1" in start) start ;; stop) stop ;; restart) echo "deliberately not implemented" ;; status) echo "deliberately not implemented" ;; force-reload) echo "deliberately not implemented" ;; *) echo "Usage: /usr/lib/cryptobone/init.d/cryptoboned {start|stop}" exit 1 esac exit 0