mirror of
https://github.com/tasket/Qubes-VM-hardening.git
synced 2025-04-29 02:46:06 -04:00
Get rescue cli working
This commit is contained in:
parent
15f6570028
commit
69a3cba543
@ -6,7 +6,7 @@ Leverage Qubes template non-persistence to enhance the guest operating system's
|
|||||||
|
|
||||||
## vm-boot-protect.service
|
## vm-boot-protect.service
|
||||||
* Protect /home (user) executable files as immutable
|
* Protect /home (user) executable files as immutable
|
||||||
* Quarrantine /rw (root) configs & scripts, with Whitelisting
|
* Quarantine /rw (root) configs & scripts, with Whitelisting
|
||||||
* SHA256 checksumming guards against unwanted changes
|
* SHA256 checksumming guards against unwanted changes
|
||||||
* Re-deploy custom 'default' files to /rw on each boot
|
* Re-deploy custom 'default' files to /rw on each boot
|
||||||
* Runs at VM start before /rw mounts
|
* Runs at VM start before /rw mounts
|
||||||
@ -35,12 +35,14 @@ Leverage Qubes template non-persistence to enhance the guest operating system's
|
|||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
Operation is automatic, although particular configuration options such as hash checks are possible.
|
Operation is automatic and will result in either a normal process with full access to the private volume at /rw, or a rescue service mode with the private volume quarantined at /mnt/rwtmp.
|
||||||
|
|
||||||
At the `vm-boot-protect` level, certain executable files in /home will be made immutable so PATH and `alias` cannot be used to hijack commands like `su` and `sudo`, nor can impostor apps autostart whenever a VM starts. This prevents normal-privilege attacks from gaining persistence at startup.
|
At the `vm-boot-protect` level, certain executable files in /home will be made immutable so PATH and `alias` cannot be used to hijack commands like `su` and `sudo`, nor can impostor apps autostart whenever a VM starts. This prevents normal-privilege attacks from gaining persistence at startup.
|
||||||
|
|
||||||
At the `vm-boot-protect-root` level, the $privdirs paths will be renamed as backups, effectively removing them from the VM startup. Then whitelisting, hash/checksumming and deployment are done (if configured). This protects VM startup from attacks that had previously achieved privilege escalation.
|
At the `vm-boot-protect-root` level, the $privdirs paths will be renamed as backups, effectively removing them from the VM startup. Then whitelisting, hash/checksumming and deployment are done (if configured). This protects VM startup from attacks that had previously achieved privilege escalation.
|
||||||
|
|
||||||
|
The special `vm-boot-protect-cli` level unconditionally goes to the service shell.
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
Files can be added to /etc/default/vms in the template to enable the following features...
|
Files can be added to /etc/default/vms in the template to enable the following features...
|
||||||
@ -67,6 +69,7 @@ Conversely, attacks which damage/exploit the private filesystem itself or quickl
|
|||||||
* All the user-writable startup files in /home should be protected by the immutable flag; See issue #9 if you notice an omission or other problem. An extra step of disabling the flag using `sudo chattr -i` whenever the user wants to modify these startup files.
|
* All the user-writable startup files in /home should be protected by the immutable flag; See issue #9 if you notice an omission or other problem. An extra step of disabling the flag using `sudo chattr -i` whenever the user wants to modify these startup files.
|
||||||
|
|
||||||
## Releases
|
## Releases
|
||||||
|
- v0.8.1 Rescue service mode on error or request
|
||||||
- v0.8.0 Adds protection to /rw, file SHA checksums, whitelists, deployment
|
- v0.8.0 Adds protection to /rw, file SHA checksums, whitelists, deployment
|
||||||
- v0.2.0 Protects /home/user files and dirs
|
- v0.2.0 Protects /home/user files and dirs
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ RemainAfterExit=no
|
|||||||
# privdirs must begin with /rw
|
# privdirs must begin with /rw
|
||||||
# Environment="privdirs=/rw/config /rw/usrlocal /rw/bind-dirs"
|
# Environment="privdirs=/rw/config /rw/usrlocal /rw/bind-dirs"
|
||||||
ExecStart=/usr/lib/qubes/init/vm-boot-protect.sh
|
ExecStart=/usr/lib/qubes/init/vm-boot-protect.sh
|
||||||
|
Restart=no
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=sysinit.target
|
WantedBy=sysinit.target
|
||||||
|
@ -13,8 +13,10 @@ chfiles=".bashrc .bash_profile .bash_login .bash_logout .profile \
|
|||||||
.xprofile .xinitrc .xserverrc .xsession"
|
.xprofile .xinitrc .xserverrc .xsession"
|
||||||
chdirs="bin .local/bin .config/autostart .config/plasma-workspace/env \
|
chdirs="bin .local/bin .config/autostart .config/plasma-workspace/env \
|
||||||
.config/plasma-workspace/shutdown .config/autostart-scripts"
|
.config/plasma-workspace/shutdown .config/autostart-scripts"
|
||||||
|
|
||||||
vmname=`qubesdb-read /name`
|
vmname=`qubesdb-read /name`
|
||||||
rw=/mnt/rwtmp
|
rw=/mnt/rwtmp
|
||||||
|
errlog=/var/run/vm-protect-error
|
||||||
|
|
||||||
# Function: Make user scripts immutable.
|
# Function: Make user scripts immutable.
|
||||||
make_immutable() {
|
make_immutable() {
|
||||||
@ -27,26 +29,56 @@ make_immutable() {
|
|||||||
#touch $rw/home/user/FIXED #debug
|
#touch $rw/home/user/FIXED #debug
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Start rescue shell then exit/fail
|
||||||
|
abort_startup() {
|
||||||
|
echo "$1" >>$errlog
|
||||||
|
cat $errlog
|
||||||
|
|
||||||
|
umount /dev/xvdb
|
||||||
|
mv -f /dev/xvdb /dev/badxvdb
|
||||||
|
mount -o ro /dev/badxvdb $rw
|
||||||
|
truncate --size=500M /root/dev-xvdb
|
||||||
|
loop=`losetup --find --show /root/dev-xvdb`
|
||||||
|
mv -f $loop /dev/xvdb
|
||||||
|
|
||||||
|
cat /etc/bashrc /etc/bash.bashrc >/etc/bashrc-insert
|
||||||
|
echo "echo '** VM-BOOT-PROTECT SERVICE SHELL'" >/etc/bashrc
|
||||||
|
echo "echo '** Private volume is located at' $rw" >>/etc/bashrc
|
||||||
|
echo "cat $errlog" >>/etc/bashrc
|
||||||
|
echo ". /etc/bashrc-insert" >>/etc/bashrc
|
||||||
|
ln -f /etc/bashrc /etc/bash.bashrc
|
||||||
|
echo '/usr/bin/nohup /usr/bin/xterm /bin/bash 0<&- &>/dev/null &' \
|
||||||
|
>/etc/X11/Xsession.d/98rescue
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Don't bother with root protections in template or standalone
|
||||||
|
if ! is_rwonly_persistent; then
|
||||||
|
### make_immutable
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo >$errlog # Clear
|
||||||
|
|
||||||
|
if qsvc vm-boot-protect-cli; then
|
||||||
|
abort_startup "CLI requested."
|
||||||
|
fi
|
||||||
|
|
||||||
# Mount private volume in temp location
|
# Mount private volume in temp location
|
||||||
mkdir -p $rw
|
mkdir -p $rw
|
||||||
if [ -e /dev/xvdb ] && mount /dev/xvdb $rw ; then
|
if [ -e /dev/xvdb ] && mount -o ro /dev/xvdb $rw ; then
|
||||||
echo Good rw mount.
|
echo "Good read-only mount."
|
||||||
else
|
else
|
||||||
echo Mount failed!
|
abort_startup "Mount failed!"
|
||||||
xterm -hold -display :0 -title "VM PROTECTION: MOUNT FAILED!" \
|
|
||||||
-e "bash -i"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if qsvc vm-boot-protect-cli; then
|
|
||||||
xterm -hold -display :0 -title "VM PROTECTION: SERVICE PROMPT" \
|
|
||||||
-e "echo Private volume is mounted at $rw; bash -i"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Protection measures for /rw dirs:
|
# Protection measures for /rw dirs:
|
||||||
# Activated by presence of vm-boot-protect-root Qubes service.
|
# Activated by presence of vm-boot-protect-root Qubes service.
|
||||||
# * Hashes in vms/vms.all.SHA and vms/$vmname.SHA files will be checked.
|
# * Hashes in vms/vms.all.SHA and vms/$vmname.SHA files will be checked.
|
||||||
# * Remove /rw root startup files - except whitelist.
|
# * Remove /rw root startup files (config, usrlocal, bind-dirs).
|
||||||
# * Contents of vms/vms.all and vms/$vmname folders will be copied.
|
# * Contents of vms/vms.all and vms/$vmname folders will be copied.
|
||||||
defdir="/etc/default/vms"
|
defdir="/etc/default/vms"
|
||||||
privdirs=${privdirs:-"$rw/config $rw/usrlocal $rw/bind-dirs"}
|
privdirs=${privdirs:-"$rw/config $rw/usrlocal $rw/bind-dirs"}
|
||||||
@ -55,21 +87,24 @@ if qsvc vm-boot-protect-root && is_rwonly_persistent; then
|
|||||||
|
|
||||||
# Check hashes
|
# Check hashes
|
||||||
checkcode=0
|
checkcode=0
|
||||||
echo >/tmp/vm-protect-sum-error
|
|
||||||
echo "File hash checks:" >/tmp/vm-protect-sum-error
|
echo "File hash checks:" >/tmp/vm-protect-sum-error
|
||||||
for vmset in vms.all $vmname; do
|
for vmset in vms.all $vmname; do
|
||||||
if [ -f $defdir/$vmset.SHA ]; then
|
if [ -f $defdir/$vmset.SHA ]; then
|
||||||
sha256sum --strict -c $defdir/$vmset.SHA >>/tmp/vm-protect-sum-error 2>&1
|
sha256sum --strict -c $defdir/$vmset.SHA >>$errlog 2>&1
|
||||||
checkcode=$((checkcode+$?))
|
checkcode=$((checkcode+$?))
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
cat /tmp/vm-protect-sum-error # For logging
|
|
||||||
|
|
||||||
# Stop system startup on checksum mismatch:
|
# Stop system startup on checksum mismatch:
|
||||||
if [ $checkcode != 0 ]; then
|
if [ $checkcode != 0 ]; then
|
||||||
xterm -hold -display :0 -title "VM PROTECTION: CHECKSUM MISMATCH!" \
|
abort_startup "Hash check failed!"
|
||||||
-e "cat /tmp/vm-protect-sum-error; echo Private volume is mounted at $rw; bash -i"
|
fi
|
||||||
exit 1
|
|
||||||
|
# Begin write operations
|
||||||
|
if [ -e /dev/xvdb ] && mount -o remount,rw /dev/xvdb $rw ; then
|
||||||
|
echo Good rw remount.
|
||||||
|
else
|
||||||
|
abort_startup "Remount failed!"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Files mutable for del/copy operations
|
# Files mutable for del/copy operations
|
||||||
@ -77,11 +112,13 @@ if qsvc vm-boot-protect-root && is_rwonly_persistent; then
|
|||||||
chattr -R -f -i $chfiles $chdirs $privdirs
|
chattr -R -f -i $chfiles $chdirs $privdirs
|
||||||
cd /root
|
cd /root
|
||||||
|
|
||||||
|
|
||||||
# Deactivate private.img config dirs
|
# Deactivate private.img config dirs
|
||||||
mkdir -p $rw/vm-boot-protect
|
mkdir -p $rw/vm-boot-protect
|
||||||
for dir in $privdirs; do
|
for dir in $privdirs; do
|
||||||
bakdir=$rw/vm-boot-protect/BAK-`basename $dir`
|
echo "Deactivate $dir"
|
||||||
origdir=$rw/vm-boot-protect/ORIG-`basename $dir`
|
bakdir=`dirname $dir`/vm-boot-protect/BAK-`basename $dir`
|
||||||
|
origdir=`dirname $dir`/vm-boot-protect/ORIG-`basename $dir`
|
||||||
if [ -d $bakdir ] && [ ! -d $origdir ]; then
|
if [ -d $bakdir ] && [ ! -d $origdir ]; then
|
||||||
mv $bakdir $origdir
|
mv $bakdir $origdir
|
||||||
fi
|
fi
|
||||||
@ -97,11 +134,12 @@ if qsvc vm-boot-protect-root && is_rwonly_persistent; then
|
|||||||
| while read wlfile; do
|
| while read wlfile; do
|
||||||
# Must begin with '/rw/'
|
# Must begin with '/rw/'
|
||||||
if echo $wlfile |grep -q "^\/rw\/"; then #Was [ $wlfile =~ ^\/rw\/ ];
|
if echo $wlfile |grep -q "^\/rw\/"; then #Was [ $wlfile =~ ^\/rw\/ ];
|
||||||
srcfile="`echo $wlfile |sed -r \"s|^/rw/(.+)$|$rw/vm-boot-protect/BAK-\1|\"`"
|
srcfile="`echo $wlfile |sed -r \"s|^/rw/(.+)$|$rw/BAK-\1|\"`"
|
||||||
dstfile="`echo $wlfile |sed -r \"s|^/rw/(.+)$|$rw/\1|\"`"
|
dstfile="`echo $wlfile |sed -r \"s|^/rw/(.+)$|$rw/\1|\"`"
|
||||||
dstdir="`dirname \"$dstfile\"`"
|
dstdir="`dirname \"$dstfile\"`"
|
||||||
if [ ! -e "$srcfile" ]; then
|
if [ ! -e "$srcfile" ]; then
|
||||||
echo "Whitelist entry not present in filesystem."
|
echo "Whitelist entry not present in filesystem:"
|
||||||
|
echo "$srcfile"
|
||||||
continue
|
continue
|
||||||
# For very large dirs: mv whole dir when entry ends with '/'
|
# For very large dirs: mv whole dir when entry ends with '/'
|
||||||
elif echo $wlfile |grep -q "\/$"; then
|
elif echo $wlfile |grep -q "\/$"; then
|
||||||
@ -113,12 +151,13 @@ if qsvc vm-boot-protect-root && is_rwonly_persistent; then
|
|||||||
cp -a --link "$srcfile" "$dstdir"
|
cp -a --link "$srcfile" "$dstdir"
|
||||||
fi
|
fi
|
||||||
elif [ -n "$wlfile" ]; then
|
elif [ -n "$wlfile" ]; then
|
||||||
echo "Whitelist path must begin with /rw/."
|
echo "Whitelist path must begin with /rw/. Skipped."
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Copy default files...
|
# Copy default files...
|
||||||
if [ -d $defdir/$vmset/rw ]; then
|
if [ -d $defdir/$vmset/rw ]; then
|
||||||
|
echo "Copy files from $defdir/$vmset/rw"
|
||||||
cp -af $defdir/$vmset/rw/* $rw
|
cp -af $defdir/$vmset/rw/* $rw
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user