Skip to content

Mortar

https://github.com/noahbliss/mortar

  • Mortar는 Linux의 물리적 보안 구성 요소들을 연결하는 프레임워크다.
  • Mortar 모델을 통해 디스크에서 사용되는 모든 것이 암호화되거나, 서명되거나, 해시된다.
    • 평문 시크릿이 저장되는 유일한 위치는 TPM 모듈이다.
  • TPM은 특정 부팅 상태를 효과적으로 화이트리스트하는 데 사용되며, Mortar는 변조되지 않은 시스템이 감지될 때만 키를 해제하도록 구성한다.
    • 이 검증 및 잠금 해제 프로세스가 완전히 자동화되어 있어서, 정상적인 시스템은 사용자 개입 없이 완전히 재시작된다.
    • 덕분에 전체 디스크 암호화가 최종 사용자에게 훨씬 편리해지고, 서버에서도 실용적으로 사용할 수 있게 된다.
image

기본 디스크에서는 2개의 파티션만 사용된다: UEFI ESP와 암호화된 LUKS 파티션이다. (암호화되지 않은 부팅 파티션을 그대로 둬도 되지만, 커널과 initram 파일시스템이 LUKS 파티션에 암호화되어 저장될 수 있도록 자동 마운트를 제거하거나 비활성화할 것을 강력히 권장한다.)

자체 Secureboot 키를 생성한다. 서명한 efi 파일만이 BIOS 수정 없이도 성공적으로 부팅되며 (PCR1 검증도 깨뜨리지 않는다).

Mortar에는 env 파일(/etc/mortar/mortar.env)과 cmdline 파일(/etc/mortar/cmdline.conf)이 있다. 그리고 /etc/mortar에서 작업한다. env 파일의 기본값은 여기에서 확인할 수 있다.

mortar를 설치할 때, 운영체제 식별 데이터를 포함하는 os-release가 먼저 실행된다.

Terminal window
# Figure out our distribuition.
source /etc/os-release
# Install prerequisite packages.
if [ -f "res/$ID/prereqs.sh" ]; then
source "res/$ID/prereqs.sh";
else
echo "Could not find a prerequisite installer for $ID. Please only press enter if you want to continue at your own risk."
read -p "Press enter to continue." asdf
fi

그 다음 필수 패키지들을 설치하는 전제 조건 스크립트를 실행한다.

Terminal window
apt-get update
apt-get install \
binutils \
efitools \
uuid-runtime

랜덤 UUID 값을 가진 KEY_UUID 키가 env 파일에 추가된다.

Terminal window
# Install the env file with a random key_uuid if it doesn't exist.
if ! (command -v uuidgen >/dev/null); then echo "Cannot find uuidgen tool."; exit 1; fi
if ! [ -f "$ENVFILE" ]; then echo "Generating new KEY_UUID and installing mortar.env to $WORKING_DIR"; KEY_UUID=$(uuidgen --random); sed -e "/^KEY_UUID=.*/{s//KEY_UUID=$KEY_UUID/;:a" -e '$!N;$!ba' -e '}' mortar.env > "$ENVFILE"; else echo "mortar.env already installed in $WORKING_DIR"; fi

cmdline.conf 설치

Terminal window
if ! [ -f "$CMDLINEFILE" ]; then echo "No CMDLINE options file found. Using currently running cmdline options from /proc/cmdline"; cat /proc/cmdline > "$CMDLINEFILE"; else echo "cmdline.conf already installed in $WORKING_DIR"; fi
echo "Make sure to update the installed mortar.env with your TPM version and ensure all the paths are correct."
if grep " splash" "$CMDLINEFILE" >/dev/null; then echo "WARNING - \"splash\" detected in "$CMDLINEFILE" this this may hide boot-time mortar output!"; fi
if grep " quiet" "$CMDLINEFILE" >/dev/null; then echo "WARNING - \"quiet\" detected in "$CMDLINEFILE" this this may hide boot-time mortar output!"; fi
if grep " rhgb" "$CMDLINEFILE" >/dev/null; then echo "WARNING - \"rhgb\" detected in "$CMDLINEFILE" this this may hide boot-time mortar output!"; fi

efi 서명 스크립트를 설치한다.

Terminal window
cp bin/mortar-compilesigninstall /usr/local/sbin/mortar-compilesigninstall
if ! command -v mortar-compilesigninstall >/dev/null; then
echo "Installed mortar-compilesigninstall to /usr/local/sbin but couldn't find it in PATH. Please update your PATH to include /usr/local/sbin"
fi

openssl x509으로 Secureboot 키를 생성한다.

Terminal window
set -e
source mortar.env
echo "Generating secureboot keys..."
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=PK$SECUREBOOT_MODIFIER/" -keyout "$SECUREBOOT_PK_KEY" -out "$SECUREBOOT_PK_CRT" -days 7300 -nodes -sha256
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=KEK$SECUREBOOT_MODIFIER/" -keyout "$SECUREBOOT_KEK_KEY" -out "$SECUREBOOT_KEK_CRT" -days 7300 -nodes -sha256
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=db$SECUREBOOT_MODIFIER/" -keyout "$SECUREBOOT_DB_KEY" -out "$SECUREBOOT_DB_CRT" -days 7300 -nodes -sha256
# Adding der versions of keys to private dir.
openssl x509 -in "$SECUREBOOT_PK_CRT" -outform der -out "$SECUREBOOT_PK_CRT".der
openssl x509 -in "$SECUREBOOT_KEK_CRT" -outform der -out "$SECUREBOOT_KEK_CRT".der
openssl x509 -in "$SECUREBOOT_DB_CRT" -outform der -out "$SECUREBOOT_DB_CRT".der

그리고 cert-to-efi-sig-list 명령어로 해당 키들을 efi sig로 만든다.

Terminal window
# Generate secureboot specific file variants.
cert-to-efi-sig-list -g "$KEY_UUID" "$SECUREBOOT_PK_CRT" "$SECUREBOOT_PK_ESL"
sign-efi-sig-list -g "$KEY_UUID" -k "$SECUREBOOT_PK_KEY" -c "$SECUREBOOT_PK_CRT" PK "$SECUREBOOT_PK_ESL" "$SECUREBOOT_PK_AUTH"
cert-to-efi-sig-list -g "$KEY_UUID" "$SECUREBOOT_KEK_CRT" "$SECUREBOOT_KEK_ESL"
sign-efi-sig-list -g "$KEY_UUID" -k "$SECUREBOOT_PK_KEY" -c "$SECUREBOOT_PK_CRT" KEK "$SECUREBOOT_KEK_ESL" "$SECUREBOOT_KEK_AUTH"
cert-to-efi-sig-list -g "$KEY_UUID" "$SECUREBOOT_DB_CRT" "$SECUREBOOT_DB_ESL"
sign-efi-sig-list -g "$KEY_UUID" -k "$SECUREBOOT_KEK_KEY" -c "$SECUREBOOT_KEK_CRT" db "$SECUREBOOT_DB_ESL" "$SECUREBOOT_DB_AUTH"
echo "You now need to generate/install a signed efi file Before installing the keys and enabling secureboot!"
echo "Run bin/mortar-compilesigninstall FULLPATHTOKERNELIMAGE FULLPATHTOINITRDIMAGE"

efi-updatevar 명령어로 설치한다.

Terminal window
chattr -i /sys/firmware/efi/efivars/{PK,KEK,db,dbx}-* 2>/dev/null
if (efi-updatevar -f "$SECUREBOOT_DB_AUTH" db); then thing="db"; installed $thing; else failed $thing; exit 1; fi
if (efi-updatevar -f "$SECUREBOOT_KEK_AUTH" KEK); then thing="KEK"; installed $thing; else failed $thing; exit 1; fi
if (efi-updatevar -f "$SECUREBOOT_PK_AUTH" PK); then thing="PK"; installed $thing; else failed $thing; exit 1; fi

시큐어 부트가 켜져 있고 작동하는지 테스트한다.

Terminal window
MORTAR_FILE="/etc/mortar/mortar.env"
OLD_DIR="$PWD"
source "$MORTAR_FILE"
od --address-radix=n --format=u1 /sys/firmware/efi/efivars/SecureBoot-*
read -p "ENTER to continue only if the last number is a \"1\" and you are sure the TPM registers are as you want them." asdf

실패한 실행에서 남은 tmpramfs가 있다면 제거한다.

Terminal window
if [ -d tmpramfs ]; then
echo "Removing existing tmpfs..."
umount tmpramfs
rm -rf tmpramfs
fi

생성된 mortar 키를 위한 tmpramfs를 생성하고 사용자 luks 비밀번호를 파일로 읽어들인다.

Terminal window
if mkdir tmpramfs && mount tmpfs -t tmpfs -o size=1M,noexec,nosuid tmpramfs; then
echo "Created tmpramfs for storing the key."
trap "if [ -f tmpramfs/user.key ]; then rm -f tmpramfs/user.key; fi" EXIT
echo -n "Enter luks password: "; read -s PASSWORD; echo
echo -n "$PASSWORD" > tmpramfs/user.key
unset PASSWORD
else
echo "Failed to create tmpramfs for storing the key."
exit 1
fi
if command -v luksmeta >/dev/null; then
echo "Wiping any existing metadata in the luks keyslot."
luksmeta wipe -d "$CRYPTDEV" -s "$SLOT"
fi

키슬롯에 있는 기존 luks 키를 삭제한다.

Terminal window
cryptsetup luksKillSlot --key-file tmpramfs/user.key "$CRYPTDEV" "$SLOT"
read -p "If this is the first time running, do you want to attempt taking ownership of the tpm? (y/N): " takeowner
case "$takeowner" in
[yY]*) tpm_takeownership -z ;;
esac

키를 생성한다.

Terminal window
dd bs=1 count=512 if=/dev/urandom of=tmpramfs/mortar.key
chmod 700 tmpramfs/mortar.key
cryptsetup luksAddKey "$CRYPTDEV" --key-slot "$SLOT" tmpramfs/mortar.key --key-file tmpramfs/user.key

TPM에 키를 봉인한다. NVRAM 영역에 데이터를 쓰는 tpm_nvwrite를 사용한다.

NVRAM (non-volatile random-access memory) refers to computer memory that can hold data even when power to the memory chips has been turned off.

Terminal window
if [ -z "$TPMINDEX" ]; then echo "TPMINDEX not set."; exit 1; fi
PERMISSIONS="OWNERWRITE|READ_STCLEAR"
read -s -r -p "Owner password: " OWNERPW
# Wipe index if it is populated.
if tpm_nvinfo | grep \($TPMINDEX\) > /dev/null; then tpm_nvrelease -i "$TPMINDEX" -o"$OWNERPW"; fi
# Convert PCR format...
PCRS=`echo "-r""$BINDPCR" | sed 's/,/ -r/g'`
# Create new index sealed to PCRS.
if tpm_nvdefine -i "$TPMINDEX" -s `wc -c tmpramfs/mortar.key` -p "$PERMISSIONS" -o "$OWNERPW" -z $PCRS; then
# Write key into the index...
tpm_nvwrite -i "$TPMINDEX" -f tmpramfs/mortar.key -z --password="$OWNERPW"
fi

ramdisk에 있는 키를 제거한다.

Terminal window
echo "Cleaning up luks keys and tmpfs..."
rm tmpramfs/mortar.key
rm tmpramfs/user.key
umount -l tmpramfs
rm -rf tmpramfs

luks 헤더의 새로운 sha256 값을 mortar env 파일에 추가한다.

Terminal window
if [ -f "$HEADERFILE" ]; then rm "$HEADERFILE"; fi
cryptsetup luksHeaderBackup "$CRYPTDEV" --header-backup-file "$HEADERFILE"
HEADERSHA256=`sha256sum "$HEADERFILE" | cut -f1 -d' '`
sed -i -e "/^HEADERSHA256=.*/{s//HEADERSHA256=$HEADERSHA256/;:a" -e '$!N;$!b' -e '}' "$MORTAR_FILE"
if [ -f "$HEADERFILE" ]; then rm "$HEADERFILE"; fi

tpm 버전과 배포판별 설치 스크립트로 위임한다.

Terminal window
source /etc/os-release
tpmverdir='tpm1.2'
if [ -d "$OLD_DIR/""res/""$ID/""$tpmverdir/" ]; then
cd "$OLD_DIR/""res/""$ID/""$tpmverdir/"
echo "Distribution: $ID"
echo "Installing kernel update and initramfs build scripts with mortar.env values..."
bash install.sh # Start in new process so we don't get dropped to another directory.
else
echo "Distribution: $ID"
echo "Could not find scripts for your distribution."
fi

추천