mirror of https://github.com/CIRCL/Circlean
chg: Use udisksctl to mount source keys, support more FS types
parent
4c77d41d69
commit
6f5d09d374
|
@ -21,7 +21,7 @@ Dependencies
|
|||
* Timidity for playing midi files
|
||||
* Git for installing some Python dependencies
|
||||
* 7Zip for unpacking archives
|
||||
* Pmount and ntfs-3g for mounting usb key partitions
|
||||
* ntfs-3g, exfat-fuse for mounting usb key partitions
|
||||
* Python 3 and pip for installing and running Python dependencies
|
||||
* Python3-lxml for handling ooxml and other Office files in filecheck.py
|
||||
* libjpeg-dev, libtiff-dev, libwebp-dev, liblcms2-dev, tcl-dev, tk-dev, and python-tk for various image formats (dependencies for pillow)
|
||||
|
|
34
README.md
34
README.md
|
@ -24,9 +24,9 @@ This is a work in progress - contributions are welcome.
|
|||
FAQ
|
||||
===
|
||||
|
||||
**Question**: I can't login, what is the password?
|
||||
**Question**: I can't login, what is the password?
|
||||
|
||||
**Answer**: For security reasons, it is **not possible** to login on the default image runinng CIRCLean/KittenGroomer (an attacker could exploit that functionality).
|
||||
**Answer**: For security reasons, it is **not possible** to login on the default image runinng CIRCLean/KittenGroomer (an attacker could exploit that functionality).
|
||||
|
||||
The only thing the default image does is booting, processing the content of the source key, copying over the files to the destination key, and finally shutting down.
|
||||
|
||||
|
@ -42,31 +42,31 @@ your computer directly. The official project page can be found at [https://www.c
|
|||
The Raspberry Pi Foundation has a [blog post](https://www.raspberrypi.org/blog/kittengroomercirclean-data-security-for-journalists-and-activists/) with more information
|
||||
about an older version of the project and details of the inspiration behind it.
|
||||
|
||||
CIRCLean is currently tested to work with USB keys that have FAT32, NTFS, or
|
||||
ext2/3/4 filesystems (ext* filesystems can only be used as source keys, not destination
|
||||
keys). Currently, exFAT is not supported due to lack of support for this format in pmount.
|
||||
The vast majority of USB keys will be FAT32 or NTFS.
|
||||
CIRCLean is currently tested to work with USB keys that have FAT32, NTFS, exFAT or
|
||||
ext2/3/4 filesystems (ext\* filesystems can only be used as source keys, not destination
|
||||
keys).
|
||||
The vast majority of USB keys will be FAT32, NTFS, and exFAT.
|
||||
|
||||
The content of the untrusted key will be copied or/and converted to the second
|
||||
(blank) key following these rules (based on the mime type as determined by libmagic):
|
||||
- Direct copy of:
|
||||
- Plain text files (mime type: text/*)
|
||||
- Audio files (mime type: audio/*)
|
||||
- Video files (mime type: video/*)
|
||||
- Example files (mime type: example/*)
|
||||
- Multipart files (mime type: multipart/*)
|
||||
- Plain text files (mime type: text/\*)
|
||||
- Audio files (mime type: audio/\*)
|
||||
- Video files (mime type: video/\*)
|
||||
- Example files (mime type: example/\*)
|
||||
- Multipart files (mime type: multipart/\*)
|
||||
- xml files, after being converted to text files
|
||||
- Octet-stream files
|
||||
- Copied after verification:
|
||||
- Image files after verifying that they are not compression bombs (mime type: image/*)
|
||||
- Image files after verifying that they are not compression bombs (mime type: image/\*)
|
||||
- PDF files, after marking as dangerous if they contain malicious content
|
||||
- msword|vnd.openxmlformats-officedocument.*|vnd.ms-*|vnd.oasis.opendocument*, after
|
||||
- msword|vnd.openxmlformats-officedocument.\*|vnd.ms-\*|vnd.oasis.opendocument\*, after
|
||||
parsing with oletools/olefile and marking as dangerous if the parsing fails.
|
||||
- Copied but marked as dangerous (DANGEROUS_filename_DANGEROUS)
|
||||
- Message files (mime type: message/*)
|
||||
- Model files (mime type: model/*)
|
||||
- Copied but marked as dangerous (DANGEROUS\_filename\_DANGEROUS)
|
||||
- Message files (mime type: message/\*)
|
||||
- Model files (mime type: model/\*)
|
||||
- x-dosexec (executable)
|
||||
- Compressed files (zip|x-rar|x-bzip2|x-lzip|x-lzma|x-lzop|x-xz|x-compress|x-gzip|x-tar|*compressed):
|
||||
- Compressed files (zip|x-rar|x-bzip2|x-lzip|x-lzma|x-lzop|x-xz|x-compress|x-gzip|x-tar|\*compressed):
|
||||
- Archives are unpacked, with the unpacking process stopped after 2 levels of archives
|
||||
to prevent archive bombs.
|
||||
- The above rules are applied recursively to the unpacked files.
|
||||
|
|
|
@ -4,3 +4,4 @@ proc /proc proc defaults 0 0
|
|||
tmpfs /tmp tmpfs rw,size=64M,noexec,nodev,nosuid,mode=1777 0 0
|
||||
tmpfs /media tmpfs rw,size=64M,noexec,nodev,nosuid,mode=1777 0 0
|
||||
# a swapfile is not a swap partition, so no using swapon|off from here on, use dphys-swapfile swap[on|off] for that
|
||||
/dev/dest_key1 /media/kitten/dest_key auto rw,user,noauto,uid=1001 0 2
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
# /etc/pmount.allow
|
||||
# pmount will allow users to additionally mount all devices that are
|
||||
# listed here.
|
||||
/dev/sdb*
|
||||
/dev/sda*
|
|
@ -0,0 +1,14 @@
|
|||
[udisks1]
|
||||
Identity=unix-group:plugdev
|
||||
Action=org.freedesktop.udisks.filesystem-mount;org.freedesktop.udisks.luks-unlock;org.freedesktop.udisks.drive-eject;org.freedesktop.udisks.drive-detach
|
||||
ResultAny=yes
|
||||
|
||||
[udisks2]
|
||||
Identity=unix-group:plugdev
|
||||
Action=org.freedesktop.udisks2.filesystem-mount;org.freedesktop.udisks2.encrypted-unlock;org.freedesktop.udisks2.eject-media;org.freedesktop.udisks2.power-off-drive
|
||||
ResultAny=yes
|
||||
|
||||
[udisks2-other-seat]
|
||||
Identity=unix-group:plugdev
|
||||
Action=org.freedesktop.udisks2.filesystem-mount-other-seat;org.freedesktop.udisks2.filesystem-unmount-others;org.freedesktop.udisks2.encrypted-unlock-other-seat;org.freedesktop.udisks2.eject-media-other-seat;org.freedesktop.udisks2.power-off-drive-other-seat
|
||||
ResultAny=yes
|
|
@ -0,0 +1,25 @@
|
|||
polkit.addRule(function(action, subject) {
|
||||
var YES = polkit.Result.YES;
|
||||
// NOTE: there must be a comma at the end of each line except for the last:
|
||||
var permission = {
|
||||
// required for udisks1:
|
||||
"org.freedesktop.udisks.filesystem-mount": YES,
|
||||
"org.freedesktop.udisks.luks-unlock": YES,
|
||||
"org.freedesktop.udisks.drive-eject": YES,
|
||||
"org.freedesktop.udisks.drive-detach": YES,
|
||||
// required for udisks2:
|
||||
"org.freedesktop.udisks2.filesystem-mount": YES,
|
||||
"org.freedesktop.udisks2.encrypted-unlock": YES,
|
||||
"org.freedesktop.udisks2.eject-media": YES,
|
||||
"org.freedesktop.udisks2.power-off-drive": YES,
|
||||
// required for udisks2 if using udiskie from another seat (e.g. systemd):
|
||||
"org.freedesktop.udisks2.filesystem-mount-other-seat": YES,
|
||||
"org.freedesktop.udisks2.filesystem-unmount-others": YES,
|
||||
"org.freedesktop.udisks2.encrypted-unlock-other-seat": YES,
|
||||
"org.freedesktop.udisks2.eject-media-other-seat": YES,
|
||||
"org.freedesktop.udisks2.power-off-drive-other-seat": YES
|
||||
};
|
||||
if (subject.isInGroup("plugdev")) {
|
||||
return permission[action.id];
|
||||
}
|
||||
});
|
|
@ -1,3 +1,3 @@
|
|||
# The purpose of this rules file is to ensure that the top left usb port and its partitions have a symlink to /dev/source_key[num], and the other ports to /dev/dest_key[num]
|
||||
KERNEL=="sd*", KERNELS=="1-1.2", SUBSYSTEMS=="usb", SYMLINK+="source_key%n"
|
||||
KERNEL=="sd*", KERNELS=="1-1.[3-5]", SUBSYSTEMS=="usb", SYMLINK+="dest_key%n"
|
||||
KERNEL=="sd*", KERNELS=="1-1.2", SUBSYSTEMS=="usb", SYMLINK+="source_key%n" GROUP="kitten" OWNER="kitten" MODE="0666"
|
||||
KERNEL=="sd*", KERNELS=="1-1.[3-5]", SUBSYSTEMS=="usb", SYMLINK+="dest_key%n" GROUP="kitten" OWNER="kitten" MODE="0666"
|
||||
|
|
|
@ -7,10 +7,9 @@ readonly ID=$(/usr/bin/id -u)
|
|||
|
||||
# Paths used in multiple scripts
|
||||
readonly SRC_DEV="/dev/source_key"
|
||||
readonly SRC_MNT="/media/src"
|
||||
|
||||
readonly DST_DEV="/dev/dest_key"
|
||||
readonly DST_MNT="/media/dst"
|
||||
readonly DST_MNT="/media/kitten/dest_key"
|
||||
|
||||
readonly TEMP="${DST_MNT}/temp"
|
||||
readonly LOGS_DIR="${DST_MNT}/logs"
|
||||
|
@ -20,9 +19,8 @@ readonly MUSIC_DIR="/opt/midi/"
|
|||
# Commands
|
||||
readonly SYNC="/bin/sync"
|
||||
readonly TIMIDITY="/usr/bin/timidity"
|
||||
readonly MOUNT="/bin/mount"
|
||||
readonly PMOUNT="/usr/bin/pmount -A -s"
|
||||
readonly PUMOUNT="/usr/bin/pumount"
|
||||
readonly MOUNT="udisksctl mount"
|
||||
readonly UMOUNT="udisksctl unmount"
|
||||
|
||||
# Config flags
|
||||
readonly DEBUG=false
|
||||
|
|
|
@ -27,13 +27,6 @@ check_has_partitions () {
|
|||
fi
|
||||
}
|
||||
|
||||
unmount_source_partition() {
|
||||
# Unmount anything that is mounted on /media/src
|
||||
if [ "$(${MOUNT} | grep -c "${SRC_MNT}")" -ne 0 ]; then
|
||||
${PUMOUNT} "${SRC_MNT}"
|
||||
fi
|
||||
}
|
||||
|
||||
run_groomer() {
|
||||
local dev_partitions
|
||||
# Find the partition names on the device
|
||||
|
@ -44,15 +37,18 @@ run_groomer() {
|
|||
for partition in ${dev_partitions}
|
||||
do
|
||||
echo "GROOMER: Processing partition ${partition}"
|
||||
unmount_source_partition
|
||||
# Mount the current partition in write mode
|
||||
${PMOUNT} -w ${partition} "${SRC_MNT}"
|
||||
SRC_MNT=`${MOUNT} -o rw -b ${partition}| sed -ne 's/Mounted \(.*\) at \(\/media\/kitten\/.*\).$/\2/p'`
|
||||
if [ -z "$SRC_MNT" ]; then
|
||||
echo "Unable to mount source partition (${partition})."
|
||||
continue
|
||||
fi
|
||||
# Mark any autorun.inf files as dangerous on the source device to be extra careful
|
||||
ls "${SRC_MNT}" | grep -i autorun.inf | xargs -I {} mv "${SRC_MNT}"/{} "${SRC_MNT}"/DANGEROUS_{}_DANGEROUS || true
|
||||
# Unmount and remount the current partition in read-only mode
|
||||
${PUMOUNT} "${SRC_MNT}"
|
||||
${UMOUNT} -b "${partition}"
|
||||
|
||||
if ${PMOUNT} -r "${partition}" "${SRC_MNT}"; then
|
||||
if ${MOUNT} -o ro -b "${partition}"; then
|
||||
echo "GROOMER: ${partition} mounted at ${SRC_MNT}"
|
||||
|
||||
# Create a directory on ${DST_MNT} named PARTION_$PARTCOUNT
|
||||
|
@ -66,6 +62,7 @@ run_groomer() {
|
|||
if [ "${DEBUG}" = true ]; then
|
||||
ls -lR "${target_dir}"
|
||||
fi
|
||||
${UMOUNT} -b "${partition}"
|
||||
else
|
||||
# Previous command (mounting current partition) failed
|
||||
echo "GROOMER: Unable to mount ${partition} on ${SRC_MNT}"
|
||||
|
|
|
@ -9,9 +9,8 @@ clean(){
|
|||
echo "GROOMER: Cleaning up in mount_keys.sh."
|
||||
rm -rf "${DST_MNT}/IN_PROGRESS.txt"*
|
||||
${SYNC} # Write anything in memory to disk
|
||||
# Unmount source and destination
|
||||
pumount "${SRC_MNT}"
|
||||
pumount "${DST_MNT}"
|
||||
# Unmount destination
|
||||
${UMOUNT} -b "${DST_DEV}"
|
||||
exit
|
||||
}
|
||||
|
||||
|
@ -37,13 +36,13 @@ check_dest_exists() {
|
|||
}
|
||||
|
||||
unmount_dest_if_mounted() {
|
||||
if ${MOUNT}|grep "${DST_MNT}"; then
|
||||
${PUMOUNT} "${DST_MNT}" || true
|
||||
if /bin/mount|grep "${DST_MNT}"; then
|
||||
${UMOUNT} -b "${DST_DEV}" || true
|
||||
fi
|
||||
}
|
||||
|
||||
mount_dest_partition() {
|
||||
if ${PMOUNT} -w "${DST_DEV}1" "${DST_MNT}"; then # pmount automatically mounts on /media/ (at /media/dst in this case).
|
||||
if ${MOUNT} -o rw -b "${DST_DEV}1"; then # mount automatically mounts on /media/ (at /media/dst in this case).
|
||||
echo "GROOMER: Destination USB device (${DST_DEV}1) mounted at ${DST_MNT}"
|
||||
else
|
||||
echo "GROOMER: Unable to mount ${DST_DEV}1 on ${DST_MNT}"
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
- timidity
|
||||
- git
|
||||
- p7zip-full
|
||||
- pmount ntfs-3g
|
||||
- ntfs-3g exfat-fuse exfat-utils
|
||||
- python3 python3-pip
|
||||
- python3-lxml
|
||||
- libjpeg-dev libtiff-dev libwebp-dev liblcms2-dev tcl-dev tk-dev python3-tk
|
||||
|
|
|
@ -41,11 +41,11 @@ Download the Raspbian image
|
|||
```
|
||||
* Verify the hash of the downloaded file and compare it to the hash on the server:
|
||||
```
|
||||
shasum XXXX-XX-XX-raspbian-jessie-lite.zip
|
||||
shasum XXXX-XX-XX-raspbian-buster-lite.zip
|
||||
```
|
||||
* Unpack it:
|
||||
```
|
||||
unzip XXXX-XX-XX-raspbian-jessie-lite.zip
|
||||
unzip XXXX-XX-XX-raspbian-buster-lite.zip
|
||||
```
|
||||
|
||||
Add space to the image
|
||||
|
@ -96,6 +96,8 @@ sudo shell_utils/basic_mount_image.sh
|
|||
|
||||
* Resize the filesystem
|
||||
|
||||
Find the loop device of the root filesystem by running `losetup`, and it is the biggest one related to the image you mounted
|
||||
|
||||
```
|
||||
sudo resize2fs /dev/loop<ID of the loop FS mounted as /mnt/rpi-root>
|
||||
```
|
||||
|
@ -138,9 +140,9 @@ raspbian-sys-mods related installs may fail - you can ignore them:
|
|||
from qemu about "Unsupported syscall: 384", you can ignore them. `getrandom(2)` was implemented in
|
||||
kernel 3.17 and apt will use /dev/urandom when it fails:
|
||||
```
|
||||
apt-get install timidity git p7zip-full python3 python3-pip pmount ntfs-3g libjpeg-dev libtiff-dev \
|
||||
apt-get install timidity git p7zip-full python3 python3-pip ntfs-3g libjpeg-dev libtiff-dev \
|
||||
libwebp-dev tk-dev python3-tk liblcms2-dev tcl-dev libopenjp2-7 libxml2-dev \
|
||||
libssl-dev libffi-dev libxslt1-dev
|
||||
libssl-dev libffi-dev libxslt1-dev exfat-fuse exfat-utils udisks2
|
||||
```
|
||||
* Compile p7zip-rar from source. First, uncomment out the second line in /etc/apt/sources.list. Then:
|
||||
```
|
||||
|
@ -161,7 +163,7 @@ verify that these dependencies are current by checking in the PyCIRCLean git rep
|
|||
cd /home/pi
|
||||
git clone https://github.com/CIRCL/PyCIRCLean.git
|
||||
cd PyCIRCLean
|
||||
pip install -r requirements.txt
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
* Create a new user named "kitten":
|
||||
```
|
||||
|
|
|
@ -17,7 +17,7 @@ set -x
|
|||
|
||||
# Double check the path and offsets as noted above!
|
||||
# Path to the image
|
||||
IMAGE='2019-07-10-raspbian-buster-lite.img'
|
||||
IMAGE='2019-09-26-raspbian-buster-lite.img'
|
||||
# Start sector of boot (first) partition
|
||||
BOOT_START=`sfdisk -J ${IMAGE} | grep img1 | sed -n 's/.*"start":*\([[:digit:]]*\).*/\1/p'`
|
||||
# Amount of sectors of boot (first) partition
|
||||
|
|
Loading…
Reference in New Issue