Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NOMERGE] Add buendia-security-monitor package. #252

Open
wants to merge 18 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
91c2378
Generate system key if not present when buendia-utils is installed.
schuyler Feb 6, 2020
e2b89bd
Add encrypt/decrypt_file functions to utils.sh.
schuyler Feb 6, 2020
32632c7
Integration test to ensure OpenMRS tables are encrypted.
schuyler Feb 6, 2020
021d974
Change buendia-mysql dependency to mariadb-server-10.1.
schuyler Feb 5, 2020
98dc9ef
Rename /etc/mysql/conf.d/buendia-mysql to .cnf just to confirm to Mar…
schuyler Feb 6, 2020
81d4197
Create MariaDB encryption keys in buendia-mysql postinst.
schuyler Feb 5, 2020
9efc87f
Add MariaDB configuration to enable InnoDB table encryption.
schuyler Feb 4, 2020
d8d43df
Basic approach to encrypting backups.
schuyler Feb 7, 2020
d7bcffb
Don't fail simultaneous backup test if database is empty.
schuyler Feb 7, 2020
ad48920
Add a note about the MariaDB table encryption test.
schuyler Feb 10, 2020
60664e3
buendia-backup copies system.key to external backup
schuyler Feb 10, 2020
39cd7ca
buendia-restore restores a system key from external storage
schuyler Feb 10, 2020
850a9db
buendia-restore should copy encrypted files to temporary local storage
schuyler Feb 11, 2020
2545c9e
Add buendia-security-monitor package.
schuyler Feb 14, 2020
e4c6a08
buendia-wifi-watchdog should record wifi state in /run/buendia-networ…
schuyler Feb 14, 2020
38f1dff
Move buendia-db-is-empty to buendia-db package.
schuyler Feb 14, 2020
4083050
Add buendia-security-monitor script.
schuyler Feb 14, 2020
946c991
Add systemd simple service to start buendia-security-monitor in
schuyler Mar 10, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions packages/buendia-backup/data/usr/bin/buendia-backup
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,9 @@ echo "Saving Buendia configuration..."
# about any which might be missing.
config_paths=$(ls -d /usr/share/buendia/{counts,distilled,openmrs,profiles,site} || true)
tar cfz "$new_dir/buendia.tar.gz" --exclude '*.omod' $config_paths
ls -l "$new_dir/buendia.tar.gz"
encrypt_file "$new_dir/buendia.tar.gz"
rm "$new_dir/buendia.tar.gz"
ls -l "$new_dir/buendia.tar.gz.enc"

# ---- Back up the MySQL database.

Expand All @@ -148,7 +150,9 @@ if ! buendia-mysql-dump openmrs "$new_dir/openmrs.zip" >"$tmp/out" 2>&1; then
echo "buendia-mysql-dump failed!"
exit 1
fi
ls -l "$new_dir/openmrs.zip"
encrypt_file "$new_dir/openmrs.zip"
rm "$new_dir/openmrs.zip"
ls -l "$new_dir/openmrs.zip.enc"

# ---- Back up the package list and non-base packages.

Expand Down Expand Up @@ -216,6 +220,17 @@ sync
mv "$new_dir" "$final_dir"
sync

# If we're backing up to a block device, make sure that the system key is
# copied there, now that the most recent backup has been committed.
#
# This may overwrite the key used to create earlier backups, which is why
# we waited until this point to copy the key.
if [ -n "$mnt_dir" ]; then
echo "Backing up system key to external storage."
cp /usr/share/buendia/system.key "$mnt_dir"
sync
fi

# ---- Sweep all the packages in completed backups into a common directory.

echo
Expand Down
24 changes: 22 additions & 2 deletions packages/buendia-backup/data/usr/bin/buendia-restore
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,27 @@ if [ $progress_state -le $PROGRESS_SETTINGS_NEXT ]; then
buendia-pkgserver-index-debs /usr/share/buendia/packages $suite
fi

if [ -n "$mnt_dir" ]; then
echo
if [ ! -r "$mnt_dir/system.key" ]; then
echo "ERROR: Cannot find system key on external storage!"
exit 1
fi
echo "Restoring system key from external storage..."
cp "$mnt_dir/system.key" /usr/share/buendia/system.key
chown root:mysql /usr/share/buendia/system.key
chmod 640 /usr/share/buendia/system.key
fi

# Restore backed up site settings
echo
echo "Restoring settings..."
mv /usr/share/buendia/site /usr/share/buendia/site.$$
if tar -xzf $root/buendia.tar.gz -C / usr/share/buendia/site; then
# Copy the file off the backup device to ensure we have enough space to
# decrypt it
cp "$root/buendia.tar.gz.enc" "$tmp"
decrypt_file "$tmp/buendia.tar.gz"
if tar -xzf $tmp/buendia.tar.gz -C / usr/share/buendia/site; then
rm -rf /usr/share/buendia/site.$$
else
rm -rf /usr/share/buendia/site
Expand Down Expand Up @@ -171,8 +187,12 @@ if [ $progress_state -le $PROGRESS_MYSQL_NEXT ]; then
# ---- Restore the MySQL database.
echo
echo "Restoring MySQL database..."
# Copy the file off the backup device to ensure we have enough space to
# decrypt it
cp "$root/openmrs.zip.enc" "$tmp"
decrypt_file "$tmp/openmrs.zip"
service tomcat7 stop
if ! buendia-mysql-load -f openmrs "$root/openmrs.zip" >"$tmp/out" 2>&1; then
if ! buendia-mysql-load -f openmrs "$tmp/openmrs.zip" >"$tmp/out" 2>&1; then
if [ -e $tmp/out ]; then
cat "$tmp/out"
fi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,36 @@ BUENDIA_TEST_BACKUP_TARGET=/var/backups/buendia/backup.$(date +%Y-%m-%d)

# When the backup cron job runs
test_10_run_backup_cron () {
rm -rf $BUENDIA_TEST_BACK_TARGET
rm -rf $BUENDIA_TEST_BACKUP_TARGET
execute_cron_right_now backup
}

# Then it stores a tarball of site configs locally
test_20_cron_saved_site_config () {
tar tfz $BUENDIA_TEST_BACKUP_TARGET/buendia.tar.gz
# Then it encrypts the backup
test_20_no_unencrypted_backup_files () {
! [ -f $BUENDIA_TEST_BACKUP_TARGET/buendia.tar.gz \
-o -f $BUENDIA_TEST_BACKUP_TARGET/openmrs.zip ]
}

# And it stores a tarball of site configs locally
test_30_cron_saved_site_config () {
cp $BUENDIA_TEST_BACKUP_TARGET/buendia.tar.gz.enc .
decrypt_file buendia.tar.gz
tar tfz buendia.tar.gz
}

# And it stores a database dump locally
test_20_cron_saved_database_dump () {
unzip -t $BUENDIA_TEST_BACKUP_TARGET/openmrs.zip
test_30_cron_saved_database_dump () {
cp $BUENDIA_TEST_BACKUP_TARGET/openmrs.zip.enc .
decrypt_file openmrs.zip
unzip -t openmrs.zip
}

test_20_cron_saved_package_list () {
# And it stores a list of installed Buendia packages
test_40_cron_saved_package_list () {
[ -s $BUENDIA_TEST_BACKUP_TARGET/buendia.list ]
}

# But it doesn't back up the system key locally
test_50_no_local_system_key_backup () {
[ ! -f $BUENDIA_TEST_BACKUP_TARGET/system.key ]
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
test_10_prevent_simultaneous_backup () {
mount_loopback
buendia-backup /dev/loop0 &
buendia-backup /dev/loop0 | tee backup.log &
sleep 0.1
if grep skip backup.log; then
echo "Backup was skipped; test can't be evaluated"
return 0
fi
if buendia-backup /dev/loop0; then
echo "Simultaneous backup should be prevented"
return 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,24 @@ test_10_run_backup_cron () {

# Then it stores a tarball of site configs on the external device
test_20_cron_saved_site_config () {
tar tfz $BUENDIA_TEST_BACKUP_TARGET/buendia.tar.gz
cp $BUENDIA_TEST_BACKUP_TARGET/buendia.tar.gz.enc .
decrypt_file buendia.tar.gz
tar tfz buendia.tar.gz
}

# And it stores a database dump on the external device
test_20_cron_saved_database_dump () {
unzip -t $BUENDIA_TEST_BACKUP_TARGET/openmrs.zip
cp $BUENDIA_TEST_BACKUP_TARGET/openmrs.zip.enc .
decrypt_file openmrs.zip
unzip -t openmrs.zip
}

# And it stores a package listing on the external device
test_20_cron_saved_package_list () {
[ -s $BUENDIA_TEST_BACKUP_TARGET/buendia.list ]
}

# And it stores a copy of the system key
test_30_cron_saved_system_key () {
[ "$(cat /usr/share/buendia/system.key)" = "$(cat loop/system.key)" ]
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ test_50_confirm_changed_patient_list () {
}

test_60_restore_from_backup () {
# Replace the current system key, to ensure that the restore process
# correctly pulls in the one we just backed up.
openssl rand -hex 128 > /usr/share/buendia/system.key

# Now try restoring!
execute_cron_right_now backup
}

Expand Down
2 changes: 1 addition & 1 deletion packages/buendia-mysql/control/control.template
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ Package: ${PACKAGE_NAME}
Version: ${PACKAGE_VERSION}
Architecture: all
Pre-Depends: buendia-utils
Depends: buendia-monitoring, cron-daemon, mysql-server, sysvinit-utils, unzip, zip
Depends: buendia-monitoring, cron-daemon, mariadb-server-10.1 (>= 10.1.4), sysvinit-utils, unzip, zip
Description: MySQL server configured for Buendia
Maintainer: projectbuendia.org
22 changes: 22 additions & 0 deletions packages/buendia-mysql/control/postinst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,28 @@ set -e; . /usr/share/buendia/utils.sh

case $1 in
configure)
if [ ! -r /etc/mysql/keyfile.enc ]; then
# Key #1 is required by MariaDB for encrypting system data. It will be used
# for other purposes as well, if no other keys are defined.
# https://mariadb.com/kb/en/encryption-key-management/#using-multiple-encryption-keys
( echo -n '1;'; openssl rand -hex 32 ) > /etc/mysql/keyfile

# Encrypt the keyfile using the Buendia system key and remove the plaintext version
openssl enc -aes-256-cbc -md sha1 -in /etc/mysql/keyfile -out /etc/mysql/keyfile.enc \
-pass file:/usr/share/buendia/system.key
rm /etc/mysql/keyfile
fi

# Ensure that MariaDB can read the encrypted key.
chown root:mysql /etc/mysql/keyfile.enc
chmod 640 /etc/mysql/keyfile.enc

# Ensure that MariaDB can read the system key.
chown root:mysql /usr/share/buendia/system.key
chmod 640 /usr/share/buendia/system.key

service mysql restart

buendia-reconfigure mysql
service cron start
;;
Expand Down
5 changes: 4 additions & 1 deletion packages/buendia-mysql/control/preinst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
set -e; . /usr/share/buendia/utils.sh

case $1 in
install|upgrade) service_if_exists cron stop ;;
install|upgrade)
service_if_exists cron stop
service_if_exists mysql stop
;;
*) exit 1
esac
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[mariadb]
plugin_load_add = file_key_management

# The table encryption secret gets *itself* encrypted at rest.
# See https://mariadb.com/kb/en/file-key-management-encryption-plugin/#creating-the-key-file
file_key_management_filename = /etc/mysql/keyfile.enc
file_key_management_filekey = FILE:/usr/share/buendia/system.key

# AES_CBC is used by default. AES_CTR is preferred, but not supported in older MariaDB builds.
# https://mariadb.com/kb/en/file-key-management-encryption-plugin/#choosing-an-encryption-algorithm
# file_key_management_encryption_algorithm = AES_CTR

# InnoDB/XtraDB Encryption
# https://mariadb.com/kb/en/innodb-encryption-overview/#basic-configuration
# The 'FORCE' option requires all InnoDB tables to be encrypted.
innodb_encrypt_tables = FORCE
innodb_encrypt_log = ON
innodb_encryption_threads = 4
innodb_encryption_rotate_key_age = 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
test_10_openmrs_tables_are_encrypted () {
# MariaDB starts encrypting tables on a background thread when encryption
# is enabled. As a result, there's no obvious way to know when the job is
# done. This test makes the assumption that if at least one of the OpenMRS
# tables is encrypted, then the server background thread is doing its work
# and all OpenMRS tables will be encrypted eventually.

sudo mysql -s openmrs >table_count <<End
SELECT COUNT(*) FROM information_schema.INNODB_TABLESPACES_ENCRYPTION
WHERE name LIKE "openmrs/%"
AND encryption_scheme = 1;
End
[ "$(cat table_count)" -gt 0 ]
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,8 @@ if ! grep -q wpa_state=COMPLETED $tmpfile; then
# "-f" means to force redoing network setup even though the network
# configuration settings haven't changed.
buendia-reconfigure -f networking
else
# Update the current wifi watchdog state for the benefit of the security
# monitor, if installed.
mv $tmpfile /run/buendia-networking.state
fi
1 change: 1 addition & 0 deletions packages/buendia-security-monitor/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../Makefile.inc
7 changes: 7 additions & 0 deletions packages/buendia-security-monitor/control/control.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Package: ${PACKAGE_NAME}
Version: ${PACKAGE_VERSION}
Architecture: all
Pre-Depends: buendia-utils
Depends: cron-daemon, buendia-networking (> 1.0), buendia-db
Description: Data theft prevention monitor for Buendia server.
Maintainer: projectbuendia.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[Unit]
Description=Buendia security monitor
Wants=buendia-reconfigure.service
After=buendia-reconfigure.service

[Service]
Type=simple
ExecStart=/usr/bin/buendia-log buendia-security-monitor

[Install]
WantedBy=multi-user.target
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/bin/bash
# Copyright 2020 The Project Buendia Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at: http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distrib-
# uted under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
# OR CONDITIONS OF ANY KIND, either express or implied. See the License for
# specific language governing permissions and limitations under the License.

# buendia-security-monitor is designed to remove the Buendia system keys in the
# event that a Buendia system with user records that was configured to talk to
# a particular wifi network cannot connect to that network within
# $SECURITY_MONITOR_TIMEOUT_MINS minutes of booting.

set -e; . /usr/share/buendia/utils.sh

if [ "$(id -u)" != 0 ]; then
echo "ERROR: This script must be run as root!"
exit 1
fi

if [ -z "$SECURITY_MONITOR_TIMEOUT_MINS" ]; then
echo "skipping: no security monitor timeout set"
exit 0
fi

if [ -z "$NETWORKING_WIFI_INTERFACE" ]; then
echo "skipping: no wifi interface configured"
exit 0
fi

# According to man proc(5): /proc/uptime contains two numbers (values in
# seconds): the uptime of the system (including time spent in suspend) and the
# amount of time spent in the idle process.
current_uptime_secs=$(cut -d" " -f1 /proc/uptime)
monitor_timeout_secs=$(( $SECURITY_MONITOR_TIMEOUT_MINS * 60 ))
if [ $current_uptime_secs -le $monitor_timeout_secs ]; then
echo "deferring: system has been up for ${current_uptime_secs}s; check will be at ${monitor_timeout_secs}s"
sleep $(( $monitor_timeout_secs - $current_uptime_secs ))
fi

if [ ! -f /etc/buendia-db-init-installed ] || buendia-db-is-empty; then
echo "skipping: Buendia database is empty"
exit 0
fi

# This file should be created by buendia-wifi-watchdog
if [ -r /run/buendia-networking.state ]; then
echo "skipping: configured wifi network is active"
exit 0
fi

echo "ERROR: system has been up for ${SECURITY_MONITOR_TIMEOUT_MINS}m without a wireless network connection!"
echo "Stopping database server."
systemctl mysql stop || true

echo "Marking local database as uninitialized."
rm -f /etc/buendia-db-init-installed

echo "Deleting local system key."
rm -f /usr/share/buendia/system.key

echo "Checking external devices for system keys."
tmpdir=$(mktemp -d)
trap "rmdir $tmpdir || true" EXIT
for device in external_file_systems; do
if mount $device $tmpdir; then
trap "umount $tmpdir || true; rmdir $tmpdir" EXIT
if [ -f $tmpdir/system.key ]; then
echo "Deleting system key from $device."
rm -f $tmpdir/system.key
fi
umount $tmpdir || true
fi
done

echo "$0 finished."
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SECURITY_MONITOR_TIMEOUT_MINS=10
2 changes: 1 addition & 1 deletion packages/buendia-utils/control/control.template
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: ${PACKAGE_NAME}
Version: ${PACKAGE_VERSION}
Architecture: all
Depends: coreutils, cron-daemon, curl, perl
Depends: coreutils, cron-daemon, curl, perl, openssl
Description: Utility scripts for Buendia
Maintainer: projectbuendia.org
5 changes: 5 additions & 0 deletions packages/buendia-utils/control/postinst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ set -e; . /usr/share/buendia/utils.sh

case $1 in
configure)
if [ ! -f /usr/share/buendia/system.key ]; then
openssl rand -hex 128 > /usr/share/buendia/system.key
chmod 600 /usr/share/buendia/system.key
fi

if [ -d /usr/share/buendia/systemd ]; then
cp /usr/share/buendia/systemd/* /lib/systemd/system
systemctl enable reboot-check.timer
Expand Down
Loading