├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── boot_grub_grub.cfg.tmpl.sh ├── centos7 ├── _etc_default_grub.appendix ├── postinst.d_99-sign-kernel.sh └── postrm.d_99-sign-kernel.sh ├── chain-sign-hook.conf ├── debian9 ├── _etc_default_grub.appendix ├── postinst.d_zzz-sign-kernel └── postrm.d_zzz-sign-kernel ├── dracut_lsbk_sign.conf ├── fedora30 ├── 99-sign-kernel.install └── _etc_default_grub.appendix ├── gpg-batch ├── grub.cfg.tmpl.sh ├── locate-bin.sh ├── locate-cfg.sh ├── probe-grub-modules.sh ├── setup_dkms.sh ├── uuidgen.sh └── zzz-sign-initramfs /.gitignore: -------------------------------------------------------------------------------- 1 | pubkey.gpg 2 | memdisk.tar 3 | grub-verify-unsigned.efi 4 | grub-verify.efi 5 | *.crt 6 | *.key 7 | *.esl 8 | *.auth 9 | *.der 10 | gpg-home 11 | grub.cfg 12 | grub.passwd 13 | grub.passwd.tmp 14 | backup 15 | *.status 16 | *.sig 17 | *.uuid 18 | condmodules.lst 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | EFIFS=/boot/efi 2 | GRUBCFGLINK:=$(shell ./locate-cfg.sh /etc/grub2-efi.cfg /etc/grub2.cfg /boot/grub2/grub.cfg /boot/grub/grub.cfg) 3 | GRUB2MKCFGLIB:=$(shell ./locate-cfg.sh /usr/share/grub2/grub-mkconfig_lib /usr/share/grub/grub-mkconfig_lib) 4 | GPG:=$(shell ./locate-bin.sh gpg2 gpg) 5 | OPENSSL=openssl 6 | TAR=tar 7 | GRUB2MKIMAGE:=$(shell ./locate-bin.sh grub2-mkimage grub-mkimage) 8 | GRUB2MKCONFIG:=$(shell ./locate-bin.sh grub2-mkconfig grub-mkconfig) 9 | GRUB2MKPASSWD:=$(shell ./locate-bin.sh grub2-mkpasswd-pbkdf2 grub-mkpasswd-pbkdf2) 10 | GRUB2MKRELPATH:=$(shell ./locate-bin.sh grub2-mkrelpath grub-mkrelpath) 11 | GRUB2PROBE:=$(shell ./locate-bin.sh grub2-probe grub-probe) 12 | GRUB2MODULES= all_video boot btrfs cat chain configfile echo efifwsetup \ 13 | efinet ext2 fat font gfxmenu gfxterm gzio halt hfsplus iso9660 jpeg \ 14 | loadenv loopback lvm mdraid09 mdraid1x minicmd normal part_apple \ 15 | part_msdos part_gpt password_pbkdf2 png reboot search search_fs_uuid \ 16 | search_fs_file search_label serial sleep syslinuxcfg test tftp video xfs \ 17 | backtrace usb usbserial_common usbserial_pl2303 usbserial_ftdi \ 18 | usbserial_usbdebug linux tar memdisk verify gcry_rsa gcry_dsa gcry_sha256 \ 19 | hashsum 20 | GRUB2CONDMODULES=increment blscfg linuxefi 21 | GRUB2EXTRAMODULES= 22 | RM=rm 23 | MKDIR=mkdir 24 | CP=cp 25 | CAT=cat 26 | SBSIGN=sbsign 27 | GREP=grep 28 | EFIBOOTMGR=efibootmgr 29 | EFIREADVAR=efi-readvar 30 | EFIUPDATEVAR=efi-updatevar 31 | CERTTOEFISIGLIST=cert-to-efi-sig-list 32 | SIGNEFISIGLIST=sign-efi-sig-list 33 | TOUCH=touch 34 | INSTALL=install 35 | RPM=rpm 36 | FIND=find 37 | UPDATEGRUB=update-grub 38 | REALPATH=realpath 39 | SED=sed 40 | 41 | all: image efi-keys pgp-key 42 | 43 | password: grub.passwd 44 | 45 | grub.passwd: 46 | @echo 'Set password for grub "root" user' 47 | $(GRUB2MKPASSWD) --iteration-count=65536 | tee $@.tmp 48 | $(GREP) -Eo 'grub\..+$$' $@.tmp > $@ || { $(RM) -f $@ $@.tmp ; false ; } 49 | $(RM) -f $@.tmp 50 | @echo "Password hash recorded to '$@'" 51 | 52 | grub.cfg: grub.cfg.tmpl.sh grub.passwd 53 | ./$< > $@ 54 | 55 | condmodules.lst: probe-grub-modules.sh 56 | ./$< "$(GRUB2MKIMAGE)" $(GRUB2CONDMODULES) > $@ || \ 57 | { $(RM) -f $@ ; false ; } 58 | 59 | boot/grub/grub.cfg: boot_grub_grub.cfg.tmpl.sh 60 | $(MKDIR) -p boot/grub 61 | ./$< "$(GRUBCFGLINK)" "$(GRUB2PROBE)" "$(GRUB2MKRELPATH)" "$(GRUB2MKCFGLIB)" > $@ 62 | 63 | pgp-key: pubkey.gpg gpg-key-generated.status 64 | 65 | pubkey.gpg: gpg-key-generated.status 66 | GNUPGHOME=gpg-home $(GPG) --quiet --no-permission-warning \ 67 | --output pubkey.gpg --export bootsigner@localhost --yes 68 | 69 | gpg-key-generated.status: gpg-batch 70 | $(MKDIR) gpg-home && \ 71 | GNUPGHOME=gpg-home $(GPG) --quiet --no-permission-warning \ 72 | --batch --gen-key $< 73 | $(TOUCH) $@ 74 | 75 | image: grub-verify.efi 76 | 77 | grub-verify.efi: grub-verify-unsigned.efi db.crt db.key 78 | $(SBSIGN) --key db.key --cert db.crt --output $@ $< || \ 79 | { $(RM) -f $@ ; false ; } 80 | 81 | grub-verify-unsigned.efi: grub.cfg memdisk.tar pubkey.gpg condmodules.lst 82 | $(GRUB2MKIMAGE) --format=x86_64-efi --output=$@ --config=grub.cfg \ 83 | --pubkey=pubkey.gpg --memdisk=memdisk.tar $(GRUB2MODULES) \ 84 | $(GRUB2EXTRAMODULES) $$($(CAT) condmodules.lst) || \ 85 | { $(RM) -f $@ ; false ; } 86 | 87 | memdisk.tar: boot/grub/grub.cfg 88 | $(TAR) cf $@ boot 89 | 90 | clean: 91 | $(RM) -rf grub-verify-unsigned.efi grub-verify.efi memdisk.tar \ 92 | PK.key PK.crt KEK.key KEK.crt db.key db.crt db.der gpg-home pubkey.gpg \ 93 | grub.passwd grub.passwd.tmp grub.cfg PK.esl PK.auth *.status boot \ 94 | PK.crt.uuid condmodules.lst 95 | 96 | efi-keys: PK.crt KEK.crt db.crt PK.key KEK.key db.key PK.esl PK.auth 97 | 98 | PK.key PK.crt: 99 | $(OPENSSL) req -new -x509 -newkey rsa:2048 \ 100 | -subj "/CN=My UEFI Platform Key/" -keyout PK.key -out PK.crt \ 101 | -days 3650 -sha256 -nodes 102 | 103 | KEK.key KEK.crt: 104 | $(OPENSSL) req -new -x509 -newkey rsa:2048 \ 105 | -subj "/CN=My UEFI Key Exchange Key/" -keyout KEK.key -out KEK.crt \ 106 | -days 3650 -sha256 -nodes 107 | 108 | db.key db.crt: 109 | $(OPENSSL) req -new -x509 -newkey rsa:2048 \ 110 | -subj "/CN=My Signing Key/" -keyout db.key -out db.crt -days 3650 \ 111 | -sha256 -nodes 112 | 113 | db.der: db.crt 114 | $(OPENSSL) x509 -in $< -inform p -out $@ -outform d 115 | 116 | PK.crt.uuid: uuidgen.sh PK.crt 117 | ./$< > $@ || { $(RM) -f $@ ; false ; } 118 | 119 | PK.esl: PK.crt PK.crt.uuid 120 | $(CERTTOEFISIGLIST) -g "$$(cat $<.uuid)" $< $@ 121 | 122 | PK.auth: PK.key PK.crt PK.esl 123 | $(SIGNEFISIGLIST) -k PK.key -c PK.crt PK PK.esl PK.auth 124 | 125 | PK.crt: PK.key 126 | KEK.crt: KEK.key 127 | db.crt: db.key 128 | 129 | efi-keys-backup: backup/PK.esl backup/KEK.esl backup/db.esl backup/dbx.esl 130 | 131 | install-gpg-keys: install-gpg-keys.status 132 | 133 | install-gpg-keys.status: gpg-key-generated.status 134 | $(INSTALL) -d -m 755 -o root -g root /var/lib/secureboot 135 | $(RM) -rf /var/lib/secureboot/gpg-home 136 | $(CP) -rvp gpg-home /var/lib/secureboot 137 | $(TOUCH) $@ 138 | 139 | install-image: install-image.status 140 | 141 | install-image.status: grub-verify.efi 142 | $(MKDIR) -p $(EFIFS)/EFI/grub-verify 143 | $(CP) -v $< $(EFIFS)/EFI/grub-verify/$< 144 | $(TOUCH) $@ 145 | 146 | install-boot-entry: install-boot-entry.status 147 | 148 | install-boot-entry.status: install-image.status install-efi-keys.status \ 149 | install-gpg-keys.status 150 | $(EFIBOOTMGR) -c -d $$($(GRUB2PROBE) -t disk $(EFIFS)) -L SignedBoot \ 151 | -l '\EFI\grub-verify\grub-verify.efi' 152 | $(TOUCH) $@ 153 | 154 | install-efi-keys: install-efi-keys.status 155 | 156 | install-efi-keys.status: PK.crt KEK.crt db.crt db.der PK.key KEK.key db.key PK.esl PK.auth 157 | $(INSTALL) -d -m 755 -o root -g root /var/lib/secureboot 158 | $(INSTALL) -d -m 700 -o root -g root /var/lib/secureboot/efi-keys 159 | $(INSTALL) -m 600 -o root -g root -t /var/lib/secureboot/efi-keys \ 160 | PK.crt KEK.crt db.crt db.der PK.key KEK.key db.key PK.esl PK.auth 161 | $(EFIUPDATEVAR) -c KEK.crt KEK 162 | $(EFIUPDATEVAR) -c db.crt db 163 | $(EFIUPDATEVAR) -f PK.auth PK 164 | $(TOUCH) $@ 165 | 166 | install-dkms-hook: install-dkms-hook.status 167 | 168 | install-dkms-hook.status: chain-sign-hook.conf 169 | $(INSTALL) -d -m 755 -o root -g root /var/lib/secureboot 170 | $(INSTALL) -d -m 755 -o root -g root /var/lib/secureboot/dkms 171 | $(INSTALL) -m 644 -o root -g root -t /var/lib/secureboot/dkms $< 172 | $(TOUCH) $@ 173 | 174 | setup-dkms: setup-dkms.status 175 | 176 | setup-dkms.status: setup_dkms.sh install-dkms-hook.status install-efi-keys.status 177 | $(INSTALL) -d -m 755 -o root -g root /var/lib/secureboot/dkms 178 | $(INSTALL) -m 755 -o root -g root -t /var/lib/secureboot/dkms $< 179 | /var/lib/secureboot/dkms/$< 180 | $(TOUCH) $@ 181 | 182 | install: install-efi-keys install-gpg-keys install-image install-boot-entry \ 183 | setup-dkms 184 | 185 | fedora30-install: fedora30-sign.status install 186 | 187 | fedora30-sign.status: fedora30-grub-signer.status fedora30-kernel-signer.status \ 188 | install-gpg-keys.status setup-dkms.status 189 | $(RPM) -q kernel-core | $(GREP) -Po '(?<=kernel-core-)\S+' | \ 190 | while read -r ver ; do \ 191 | /etc/kernel/install.d/99-sign-kernel.install \ 192 | add $$ver "" /boot/vmlinuz-$$ver ; \ 193 | done 194 | $(GRUB2MKCONFIG) -o "$$($(REALPATH) "$(GRUBCFGLINK)")" 195 | $(TOUCH) $@ 196 | 197 | fedora30-grub-signer.status: fedora30/_etc_default_grub.appendix \ 198 | install-gpg-keys.status 199 | $(GREP) -F -q 'bootsigner@localhost' /etc/default/grub || $(CAT) $< >> /etc/default/grub 200 | $(TOUCH) $@ 201 | 202 | fedora30-kernel-signer.status: fedora30/99-sign-kernel.install \ 203 | install-gpg-keys.status dracut_lsbk_sign.conf 204 | $(MKDIR) -p /etc/kernel/install.d 205 | $(INSTALL) -g root -o root -t /etc/kernel/install.d $< 206 | $(INSTALL) -m 644 -g root -o root dracut_lsbk_sign.conf /etc/dracut.conf.d/99-lsbk-sign.conf 207 | $(TOUCH) $@ 208 | 209 | debian9-install: debian9-sign.status install 210 | 211 | debian10-install: debian9-install 212 | 213 | ubuntu-install: debian9-install 214 | 215 | debian9-sign.status: debian9-grub-signer.status debian9-kernel-signer.status \ 216 | install-gpg-keys.status setup-dkms.status 217 | $(FIND) /boot/ -maxdepth 1 -type f -name 'vmlinu[xz]-*' \ 218 | -and -not -name \*.sig | $(GREP) -Po '(?<=^/boot/vmlinu[xz]-)\S+$$' | \ 219 | while read -r ver ; do \ 220 | /etc/kernel/postinst.d/zzz-sign-kernel $$ver ; \ 221 | done 222 | $(UPDATEGRUB) 223 | $(TOUCH) $@ 224 | 225 | debian9-grub-signer.status: debian9/_etc_default_grub.appendix \ 226 | install-gpg-keys.status 227 | $(GREP) -F -q 'bootsigner@localhost' /etc/default/grub || $(CAT) $< >> /etc/default/grub 228 | $(TOUCH) $@ 229 | 230 | debian9-kernel-signer.status: debian9/postinst.d_zzz-sign-kernel \ 231 | debian9/postrm.d_zzz-sign-kernel install-gpg-keys.status zzz-sign-initramfs 232 | $(MKDIR) -p /etc/kernel/postinst.d /etc/kernel/postrm.d 233 | $(INSTALL) -m 755 -g root -o root -T debian9/postinst.d_zzz-sign-kernel \ 234 | /etc/kernel/postinst.d/zzz-sign-kernel 235 | $(INSTALL) -m 755 -g root -o root -T debian9/postrm.d_zzz-sign-kernel \ 236 | /etc/kernel/postrm.d/zzz-sign-kernel 237 | $(INSTALL) -m 755 -o root -g root -d /usr/share/initramfs-tools/conf-hooks.d 238 | $(INSTALL) -m 644 -o root -g root -t /usr/share/initramfs-tools/conf-hooks.d \ 239 | zzz-sign-initramfs 240 | $(TOUCH) $@ 241 | 242 | centos7-install: centos7-sign.status install 243 | 244 | centos7-sign.status: centos7-grub-signer.status centos7-kernel-signer.status \ 245 | install-gpg-keys.status setup-dkms.status 246 | $(RPM) -q kernel | $(GREP) -Po '(?<=kernel-)\S+' | \ 247 | while read -r ver ; do \ 248 | /etc/kernel/postinst.d/99-sign-kernel.sh $$ver ; \ 249 | done 250 | $(GRUB2MKCONFIG) -o "$$($(REALPATH) "$(GRUBCFGLINK)")" 251 | $(TOUCH) $@ 252 | 253 | centos7-grub-signer.status: centos7/_etc_default_grub.appendix \ 254 | install-gpg-keys.status 255 | $(GREP) -F -q 'bootsigner@localhost' /etc/default/grub || $(CAT) $< >> /etc/default/grub 256 | $(TOUCH) $@ 257 | 258 | centos7-kernel-signer.status: centos7/postinst.d_99-sign-kernel.sh \ 259 | centos7/postrm.d_99-sign-kernel.sh install-gpg-keys.status \ 260 | dracut_lsbk_sign.conf 261 | $(MKDIR) -p /etc/kernel/postinst.d /etc/kernel/postrm.d 262 | $(INSTALL) -m 755 -g root -o root -T centos7/postinst.d_99-sign-kernel.sh \ 263 | /etc/kernel/postinst.d/99-sign-kernel.sh 264 | $(INSTALL) -m 755 -g root -o root -T centos7/postrm.d_99-sign-kernel.sh \ 265 | /etc/kernel/postrm.d/99-sign-kernel.sh 266 | $(INSTALL) -m 644 -g root -o root dracut_lsbk_sign.conf /etc/dracut.conf.d/99-lsbk-sign.conf 267 | $(TOUCH) $@ 268 | 269 | backup/%.esl: 270 | [ ! -f install-efi-keys.status ] # disable backup target if keys already installed 271 | [ -d backup ] || $(MKDIR) -p backup 272 | $(EFIREADVAR) -v $* -o $@ 273 | 274 | .PHONY: clean image all pgp-key efi-keys efi-keys-backup install-gpg-keys \ 275 | password install-boot-entry install-image install-efi-keys install-dkms-hook \ 276 | install fedora30-install debian9-install debian10-install ubuntu-install \ 277 | centos7-install setup-dkms 278 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # linux-secureboot-kit 2 | Tool for complete hardening of Linux boot chain with UEFI Secure Boot. Inspired by [Hanno Heinrichs and Florent Hochwelker blog post](https://www.crowdstrike.com/blog/enhancing-secure-boot-chain-on-fedora-29/). 3 | 4 | --- 5 | 6 | :heart: :heart: :heart: 7 | 8 | You can say thanks to the author by donations to these wallets: 9 | 10 | - ETH: `0xB71250010e8beC90C5f9ddF408251eBA9dD7320e` 11 | - BTC: 12 | - Legacy: `1N89PRvG1CSsUk9sxKwBwudN6TjTPQ1N8a` 13 | - Segwit: `bc1qc0hcyxc000qf0ketv4r44ld7dlgmmu73rtlntw` 14 | 15 | --- 16 | 17 | ## Why? 18 | 19 | Even if your hard disk is encrypted with full disk encryption, your bootloader config or initramdrive may be spoofed while you left your computer unattended. And this way your encryption key may be silently extracted when you unlock your system next time. 20 | 21 | ## What does it do? 22 | 23 | This kit establishes following signature verification chain: UEFI Secure Boot -> Custom GRUB2 Image with your embedded verification keys -> Signed kernel, initramrs, grub config. 24 | 25 | ## Features 26 | 27 | * Risk-free deployment. Old bootloader is retained after installation and it is possible to fallback to it at any time. If something went wrong just disable Secure Boot and choose original bootloader in your boot menu. 28 | * No foreign code can be run on such protected machine, including live system images signed with vendor certificates. 29 | * Support for automatic signature of DKMS-built modules. 30 | * No MOK key enrollment required. 31 | 32 | ## How to use it? 33 | 34 | Here is step by step guide: 35 | 36 | ### Step 1. Satisfy requirements 37 | 38 | 1. x64 UEFI-enabled Linux installation with GRUB2 bootloader 39 | 2. GRUB2 config without `blscfg` directives (they will fail boot since all files will have to be signed). Where applicable it is disabled automatically upon installation via `GRUB_ENABLE_BLSCFG="false"` variable in `/etc/default/grub` 40 | 3. GRUB2 tools and modules (`grub2-efi-x64-modules` and `grub2-tools` packages on RPM-based distros, Debian-based provides them by default) 41 | 4. sbsigntools (sbsigntool) 0.6+ (https://git.kernel.org/pub/scm/linux/kernel/git/jejb/sbsigntools.git/). If it is absent in your distro or too old, you have two options: 42 | * Use [static build](https://gist.github.com/Snawoot/a8f0863f362ed328b6bff00a3717f175). HEAD commit of this gist can be verified with [my PGP public key](https://keybase.io/yarmak/pgp_keys.asc). See install instructions in gist comment. 43 | * Build it yourself. You'll need: 44 | 1. @development-tools (build-essential) 45 | 2. openssl-devel (libssl-dev) 46 | 3. libuuid-devel (uuid-dev) 47 | 4. binutils-devel (binutils-dev) 48 | 5. efitools 1.9.2+ (https://git.kernel.org/pub/scm/linux/kernel/git/jejb/efitools.git). If it is absent in your distro or too old, you have two options: 49 | * Use [static build](https://gist.github.com/Snawoot/1937d5bc76d7b0a29f2039aa679c0449). HEAD commit of this gist can be verified with [my PGP public key](https://keybase.io/yarmak/pgp_keys.asc). See install instructions in gist comment. 50 | * Build it yourself. You'll need: 51 | 1. @development-tools (build-essential) 52 | 2. openssl-devel (libssl-dev) 53 | 3. gnu-efi-devel (gnu-efi) 54 | 4. perl-File-Slurp (libfile-slurp-perl) 55 | 5. help2man 56 | 57 | #### Fedora 30 hint 58 | 59 | If you are building efitools on Fedora you'll need this [build script](https://gist.github.com/Snawoot/9cbad8a381b241c5bac5669d00f20620) to workaroud library paths issue. 60 | 61 | ### Step 2. Backup current UEFI keys 62 | 63 | ``` 64 | make backup 65 | ``` 66 | 67 | ### Step 3. Clear your current UEFI keys (putting platform into Setup Mode) 68 | 69 | Usually, it can be done via BIOS Setup Menu. 70 | 71 | When done, verify it. `efi-readvar` output should look like this: 72 | 73 | ``` 74 | # efi-readvar 75 | Variable PK has no entries 76 | Variable KEK has no entries 77 | Variable db has no entries 78 | Variable dbx has no entries 79 | Variable MokList has no entries 80 | ``` 81 | 82 | ### Step 4. Build keys, certificates, signed grub2 image and password hash for grub2 `root` user 83 | 84 | ``` 85 | sudo make 86 | ``` 87 | 88 | Root access is required for proper embedded boot config generation. You will be asked for GRUB password during build process. 89 | 90 | ### Step 5. Install UEFI keys, bootloader and boot GPG signing keys 91 | 92 | ``` 93 | sudo make install 94 | ``` 95 | 96 | ### Step 6. Sign all kernels, ramdrives and boot config 97 | 98 | All new installed kernels, ramdrives and grub config has to be signed on update. Automation of this process may differ on various distros, but basicly all you have to do is generate detached signature with `gpg` like this: 99 | 100 | ```sh 101 | FILE=/boot/vmlinuz-5.0.13-300.fc30.x86_64 102 | gpg2 --quiet --no-permission-warning \ 103 | --homedir /var/lib/secureboot/gpg-home \ 104 | --detach-sign \ 105 | --default-key "bootsigner@localhost" < "$FILE" > "$FILE.sig" 106 | ``` 107 | 108 | For some distros we already have such installable automation. 109 | 110 | #### Fedora 30 111 | 112 | ``` 113 | sudo make fedora30-install 114 | ``` 115 | 116 | #### Debian 9, Debian 10 117 | 118 | ``` 119 | sudo make debian9-install 120 | ``` 121 | 122 | #### Ubuntu 123 | 124 | ``` 125 | sudo make ubuntu-install 126 | ``` 127 | 128 | #### Centos 7 129 | 130 | ``` 131 | sudo make centos7-install 132 | ``` 133 | 134 | Actually, you may just run single command with final target for your system and `make` will figure out which actions are pending. But step-by-step process is more explicit and easier to troubleshoot. 135 | 136 | ### Step 7. Lockdown your system 137 | 138 | Ensure Secure Boot is enabled in your BIOS settings and administrator password is set. Set 'SignedBoot' UEFI boot entry as your first boot option. 139 | 140 | ## Notes 141 | 142 | ### DKMS and custom modules 143 | 144 | Linux kernel in some distrubutions requires all modules to be signed with trusted signature when Secure Boot is enabled. Some distros (like Ubuntu) even offer mechanism for signing DKMS modules after build with enrolled MOK keys. Since we already own all platform keys, we don't need to enroll additional MOK keys into UEFI - we can sign modules with db keys instead. linux-secureboot-kit sets own hooks in order to supress signature with MOK keys and put it's own. Such hook chained after original DKMS source hooks via override file in `/etc/dkms`. Symlinks to override file created for every installed DKMS package upon linux-secureboot-kit setup. If you will install some new DKMS after linux-secureboot-kit setup, you have to create such symlink like this: 145 | 146 | ```bash 147 | ln -s /var/lib/secureboot/dkms/chain-sign-hook.conf /etc/dkms/.conf 148 | ``` 149 | 150 | or just re-run `setup_dkms.sh` script from this source directory. It'll add missing symlinks and initiate rebuild of unsigned modules. 151 | 152 | If you are building modules manually, you may sign them with `/var/lib/secureboot/efi-keys/db.key` and `/var/lib/secureboot/efi-keys/db.der` using tool like `kmodsign` in Ubuntu or `scripts/sign_file` from kernel source directory (see [this issue](https://github.com/Snawoot/linux-secureboot-kit/issues/3) for example). 153 | 154 | ## See Also 155 | 156 | * [mortar](https://github.com/noahbliss/mortar) - all-up solution which intergrates together TPM, SecureBoot (with metered boot) and LUKS 157 | -------------------------------------------------------------------------------- /boot_grub_grub.cfg.tmpl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | GRUBCFGLINK="$1" 4 | GRUB2PROBE="$2" 5 | GRUB2MKRELPATH="$3" 6 | GRUB2MKCFGLIB="$4" 7 | 8 | GRUBCFGPATH="$(realpath "$GRUBCFGLINK")" 9 | GRUBPFXPATH="$(dirname "$GRUBCFGPATH")" 10 | 11 | CFGRELPATH="$("$GRUB2MKRELPATH" "$GRUBCFGPATH")" 12 | PFXRELPATH="$("$GRUB2MKRELPATH" "$GRUBPFXPATH")" 13 | CFGDEV="$("$GRUB2PROBE" -t device "$GRUBCFGPATH")" 14 | 15 | # shellcheck source=/usr/share/grub/grub-mkconfig_lib 16 | . "$GRUB2MKCFGLIB" 17 | 18 | prepare_grub_to_access_device "$CFGDEV" 19 | 20 | echo "set prefix=\"$PFXRELPATH\"" 21 | 22 | cat </dev/null) || \ 8 | GPG=$(command -v gpg 2>/dev/null) 9 | 10 | grub_cfg="${grub_cfg:-/boot/efi/EFI/centos/grub.cfg}" 11 | # /etc/default/grub is sourced by grub2-mkconfig after grub-mkconfig_lib is 12 | # sourced. Overwrite grub_file_is_not_garbage to also ignore signature files. 13 | grub_file_is_not_garbage () 14 | { 15 | if test -f "$1" ; then 16 | case "$1" in 17 | *.dpkg-*) return 1 ;; # debian dpkg 18 | *.rpmsave|*.rpmnew) return 1 ;; 19 | *.sig) return 1 ;; 20 | README*|*/README*) return 1 ;; # documentation 21 | esac 22 | else 23 | return 1 24 | fi 25 | return 0 26 | } 27 | # Unfortunately, this is not sufficient, as /etc/grub.d/10_linux sources 28 | # grub-mkconfig_lib again. It does try to do so from "${pkgdatadir}" though, so 29 | # that we can make it use a patched lib: 30 | TMPDIR="$(mktemp -d)" 31 | sed -e 's,\*\.rpmsave[^)]*),*.rpmsave|*.rpmnew|*.sig),' /usr/share/grub/grub-mkconfig_lib > "${TMPDIR}/grub-mkconfig_lib" 32 | export pkgdatadir="${TMPDIR}" 33 | cleanup() { 34 | rm -rf "${TMPDIR}" 35 | } 36 | sign() { 37 | >&2 echo "About to create signature of '${grub_cfg}' with GPG key '${GPG_SIGN_KEYID}'" 38 | # Don't warn about permissions, we're root and the homedir belongs to a 39 | # different user, so that's okay. 40 | "$GPG" --quiet --no-permission-warning --homedir "${GPG_SIGN_HOMEDIR}" --detach-sign --default-key "${GPG_SIGN_KEYID}" < "${grub_cfg}" > "${grub_cfg}.sig" 41 | >&2 echo "Config signed. Signing bootloader files in /boot/grub" 42 | find /boot/efi/EFI/centos/fonts/ /boot/grub/ -type f -not \( -name \*.sig \ 43 | -or -name grubenv -or -name grub.cfg \) | \ 44 | while read -r i 45 | do 46 | "$GPG" --quiet --no-permission-warning --homedir "${GPG_SIGN_HOMEDIR}" --detach-sign --default-key "${GPG_SIGN_KEYID}" < "${i}" > "${i}.sig" 47 | done 48 | cleanup 49 | } 50 | trap sign EXIT 51 | trap cleanup TERM INT 52 | -------------------------------------------------------------------------------- /centos7/postinst.d_99-sign-kernel.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | PATH="/bin:/usr/bin:/sbin:/usr/sbin" 3 | KERNEL_VERSION="${1}" 4 | # shellcheck disable=SC2034 5 | KERNEL_IMAGE="${2}" 6 | GPG_SIGN_HOMEDIR="/var/lib/secureboot/gpg-home" 7 | GPG_SIGN_KEYID="bootsigner@localhost" 8 | 9 | GPG=$(command -v gpg2 2>/dev/null) || \ 10 | GPG=$(command -v gpg 2>/dev/null) 11 | 12 | sign() { 13 | echo "About to sign file '${1}' with GPG key '${GPG_SIGN_KEYID}'" 14 | # Don't warn about permissions, we're root and the homedir belongs to a 15 | # different user, so that's okay. 16 | "$GPG" --quiet --no-permission-warning --homedir "${GPG_SIGN_HOMEDIR}" --detach-sign --default-key "${GPG_SIGN_KEYID}" < "${1}" > "${1}.sig" 17 | } 18 | 19 | sign "/boot/vmlinuz-${KERNEL_VERSION}" 20 | sign "/boot/initramfs-${KERNEL_VERSION}.img" 21 | -------------------------------------------------------------------------------- /centos7/postrm.d_99-sign-kernel.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | KERNEL_VERSION="${1}" 3 | # shellcheck disable=SC2034 4 | KERNEL_IMAGE="${2}" 5 | 6 | rm -f "/boot/vmlinuz-${KERNEL_VERSION}.sig" "/boot/initramfs-${KERNEL_VERSION}.img.sig" 7 | 8 | exit 0 9 | -------------------------------------------------------------------------------- /chain-sign-hook.conf: -------------------------------------------------------------------------------- 1 | 2 | # shellcheck shell=bash 3 | # shellcheck disable=SC2154 4 | 5 | # determine relative postfix required to discard path prepended by DKMS framework 6 | # script path composed like 7 | # run="$dkms_tree/$module/$module_version/$script_type/$2" 8 | 9 | 10 | root_relative_path="" 11 | 12 | while [[ "$(realpath "$dkms_tree/$root_relative_path")" != '/' ]] ; do 13 | root_relative_path="${root_relative_path}../" 14 | done 15 | root_relative_path="${root_relative_path}../../../" 16 | 17 | # get original post-build script path 18 | orig_post_build="$POST_BUILD" 19 | 20 | # preview some variables inferred by DKMS 21 | array_size=0 22 | for s in ${#BUILT_MODULE_NAME[@]} \ 23 | ${#BUILT_MODULE_LOCATION[@]} \ 24 | ${#DEST_MODULE_NAME[@]} \ 25 | ${#DEST_MODULE_LOCATION[@]}; do 26 | ((s > array_size)) && array_size=$s 27 | done 28 | 29 | for ((index=0; index < array_size; index++)); do 30 | built_module_name[$index]=${BUILT_MODULE_NAME[$index]} 31 | built_module_location[$index]=${BUILT_MODULE_LOCATION[$index]} 32 | dest_module_name[$index]=${DEST_MODULE_NAME[$index]} 33 | dest_module_location[$index]=${DEST_MODULE_LOCATION[$index]} 34 | 35 | [[ ! ${built_module_name[$index]} ]] && \ 36 | ((${#DEST_MODULE_LOCATION[@]} == 1)) && \ 37 | built_module_name[$index]=$module 38 | [[ ! ${dest_module_name[$index]} ]] && \ 39 | dest_module_name[$index]=${built_module_name[$index]} 40 | [[ ${built_module_location[$index]} && \ 41 | ${built_module_location[$index]:(-1)} != / ]] && \ 42 | built_module_location[$index]="${built_module_location[$index]}/" 43 | 44 | dest_module_location[$index]="$(override_dest_module_location ${dest_module_location[$index]})" 45 | done 46 | 47 | # force strip to remove foreign signatures 48 | for ((index=0; index < array_size; index++)); do 49 | STRIP[$index]=Y 50 | done 51 | 52 | # discover module suffix 53 | set_module_suffix "$kernelver" 54 | base_dir="$dkms_tree/$module/$module_version/$kernelver/$arch" 55 | 56 | # make temporary file with script performing required actions and passing 57 | # control to original POST_BUILD script 58 | tmpscript="$(mktemp)" 59 | cat > "$tmpscript" << EOF 60 | #!/bin/bash 61 | 62 | rm -f "$tmpscript" 63 | 64 | orig_post_build="$orig_post_build" 65 | run="$dkms_tree/$module/$module_version/build/$orig_post_build" 66 | 67 | if [[ \$orig_post_build ]] ; then 68 | if [[ -x \${run%% *} ]]; then 69 | \$run 70 | else 71 | >&2 echo "The \$orig_post_build script is not executable." 72 | fi 73 | fi 74 | 75 | EOF 76 | 77 | for ((count=0; count < ${#built_module_name[@]}; count++)); do 78 | if [ "$module_compressed_suffix" = ".gz" ]; then 79 | cat >> "$tmpscript" << EOF 80 | gunzip -f "$dkms_tree/$module/$module_version/build/${built_module_location[$count]}${built_module_name[$count]}$module_suffix" 81 | "$kernel_source_dir/scripts/sign-file" sha256 /var/lib/secureboot/efi-keys/db.key /var/lib/secureboot/efi-keys/db.der "$dkms_tree/$module/$module_version/build/${built_module_location[$count]}${built_module_name[$count]}$module_uncompressed_suffix" 82 | gzip -9f "$dkms_tree/$module/$module_version/build/${built_module_location[$count]}${built_module_name[$count]}$module_uncompressed_suffix" 83 | EOF 84 | elif [ "$module_compressed_suffix" = ".xz" ]; then 85 | cat >> "$tmpscript" << EOF 86 | unxz -f "$dkms_tree/$module/$module_version/build/${built_module_location[$count]}${built_module_name[$count]}$module_suffix" 87 | "$kernel_source_dir/scripts/sign-file" sha256 /var/lib/secureboot/efi-keys/db.key /var/lib/secureboot/efi-keys/db.der "$dkms_tree/$module/$module_version/build/${built_module_location[$count]}${built_module_name[$count]}$module_uncompressed_suffix" 88 | xz -f "$dkms_tree/$module/$module_version/build/${built_module_location[$count]}${built_module_name[$count]}$module_uncompressed_suffix" 89 | EOF 90 | else 91 | cat >> "$tmpscript" << EOF 92 | "$kernel_source_dir/scripts/sign-file" sha256 /var/lib/secureboot/efi-keys/db.key /var/lib/secureboot/efi-keys/db.der "$dkms_tree/$module/$module_version/build/${built_module_location[$count]}${built_module_name[$count]}$module_suffix" 93 | EOF 94 | fi 95 | 96 | # Copy module again, with signature 97 | cat >> "$tmpscript" << EOF 98 | cp -f "$dkms_tree/$module/$module_version/build/${built_module_location[$count]}${built_module_name[$count]}$module_suffix" "$base_dir/module/${dest_module_name[$count]}$module_suffix" >/dev/null 99 | EOF 100 | done 101 | 102 | chmod +x "$tmpscript" 103 | 104 | POST_BUILD="${root_relative_path}${tmpscript}" 105 | 106 | # supress DKMS signature mechanism on Ubuntu. 107 | # $tmpfile is a file which is used by DKMS to source this config safely. 108 | if type sign_build >/dev/null 2>&1 ; then 109 | if [[ "$tmpfile" ]] && [[ "${export_envs+x}" ]] && \ 110 | [[ $_ != $0 ]] && (( $$ != BASHPID )) ; then 111 | # matched safe_source environment 112 | 113 | cat >> "$tmpfile" << EOF 114 | unset sign_build 115 | sign_build () { :; } 116 | EOF 117 | 118 | exec >>"$tmpfile" 119 | for _export_env in "${export_envs[@]}"; do 120 | for _i in $(eval echo \${!$_export_env[@]}); do 121 | eval echo '$_export_env[$_i]=\"${'$_export_env'[$_i]}\"' 122 | done 123 | done 124 | exit 0 125 | else 126 | # safe_source condition doesn't match. probably it's a direct source 127 | unset sign_build 128 | sign_build () { :; } 129 | fi 130 | fi 131 | -------------------------------------------------------------------------------- /debian9/_etc_default_grub.appendix: -------------------------------------------------------------------------------- 1 | 2 | ### Secure Boot signature hooks below 3 | 4 | GPG_SIGN_HOMEDIR="/var/lib/secureboot/gpg-home" 5 | GPG_SIGN_KEYID="bootsigner@localhost" 6 | 7 | GPG=$(command -v gpg2 2>/dev/null) || \ 8 | GPG=$(command -v gpg 2>/dev/null) 9 | 10 | grub_cfg="${grub_cfg:-/boot/grub/grub.cfg}" 11 | # /etc/default/grub is sourced by grub2-mkconfig after grub-mkconfig_lib is 12 | # sourced. Overwrite grub_file_is_not_garbage to also ignore signature files. 13 | grub_file_is_not_garbage () 14 | { 15 | if test -f "$1" ; then 16 | case "$1" in 17 | *.dpkg-*) return 1 ;; # debian dpkg 18 | *.rpmsave|*.rpmnew) return 1 ;; 19 | *.sig) return 1 ;; 20 | README*|*/README*) return 1 ;; # documentation 21 | esac 22 | else 23 | return 1 24 | fi 25 | return 0 26 | } 27 | # Unfortunately, this is not sufficient, as /etc/grub.d/10_linux sources 28 | # grub-mkconfig_lib again. It does try to do so from "${pkgdatadir}" though, so 29 | # that we can make it use a patched lib: 30 | TMPDIR="$(mktemp -d)" 31 | sed -e 's,\*\.rpmsave[^)]*),*.rpmsave|*.rpmnew|*.sig),' /usr/share/grub/grub-mkconfig_lib > "${TMPDIR}/grub-mkconfig_lib" 32 | export pkgdatadir="${TMPDIR}" 33 | cleanup() { 34 | rm -rf "${TMPDIR}" 35 | } 36 | sign() { 37 | >&2 echo "About to create signature of '${grub_cfg}' with GPG key '${GPG_SIGN_KEYID}'" 38 | # Don't warn about permissions, we're root and the homedir belongs to a 39 | # different user, so that's okay. 40 | "$GPG" --quiet --no-permission-warning --homedir "${GPG_SIGN_HOMEDIR}" --detach-sign --default-key "${GPG_SIGN_KEYID}" < "${grub_cfg}" > "${grub_cfg}.sig" 41 | >&2 echo "Config signed. Signing bootloader files in /boot/grub" 42 | find /boot/grub -type f -not \( -name \*.sig -or \ 43 | -name grubenv -or -name grub.cfg \) | \ 44 | while read -r i 45 | do 46 | "$GPG" --quiet --no-permission-warning --homedir "${GPG_SIGN_HOMEDIR}" --detach-sign --default-key "${GPG_SIGN_KEYID}" < "${i}" > "${i}.sig" 47 | done 48 | cleanup 49 | } 50 | trap sign EXIT 51 | trap cleanup TERM INT 52 | -------------------------------------------------------------------------------- /debian9/postinst.d_zzz-sign-kernel: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | version="$1" 4 | location="$2" 5 | 6 | GPG_SIGN_HOMEDIR="/var/lib/secureboot/gpg-home" 7 | GPG_SIGN_KEYID="bootsigner@localhost" 8 | 9 | GPG=$(command -v gpg2 2>/dev/null) || \ 10 | GPG=$(command -v gpg 2>/dev/null) 11 | 12 | sign() { 13 | echo "About to sign file '${1}' with GPG key '${GPG_SIGN_KEYID}'" 14 | # Don't warn about permissions, we're root and the homedir belongs to a 15 | # different user, so that's okay. 16 | "$GPG" --quiet --no-permission-warning --homedir "${GPG_SIGN_HOMEDIR}" --detach-sign --default-key "${GPG_SIGN_KEYID}" < "${1}" > "${2}" 17 | } 18 | 19 | if [ -z "${version}" ]; then 20 | echo >&2 "W: sign-kernel: ${DPKG_MAINTSCRIPT_PACKAGE:-kernel package} did not pass a version number" 21 | exit 2 22 | fi 23 | 24 | if [ -z "$location" ] ; then 25 | location="/boot/vmlinux-$version" 26 | if [ ! -e "$location" ] ; then 27 | location="/boot/vmlinuz-$version" 28 | if [ ! -e "$location" ] ; then 29 | echo >&2 "W: sign-kernel: kernel image not found" 30 | exit 1 31 | fi 32 | fi 33 | fi 34 | 35 | sign "$location" "$location.sig" 36 | 37 | # exit if kernel does not need an initramfs 38 | if [ "$INITRD" = 'No' ]; then 39 | exit 0 40 | fi 41 | 42 | initrd_img="/boot/initrd.img-$version" 43 | 44 | if [ -e "$initrd_img" ] ; then 45 | sign "$initrd_img" "$initrd_img.sig" 46 | else 47 | echo >&2 "W: sign-kernel: initramfs image not found and not signed!" 48 | exit 1 49 | fi 50 | -------------------------------------------------------------------------------- /debian9/postrm.d_zzz-sign-kernel: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | version="$1" 4 | location="$2" 5 | 6 | if [ -z "${version}" ]; then 7 | echo >&2 "W: sign-kernel: ${DPKG_MAINTSCRIPT_PACKAGE:-kernel package} did not pass a version number" 8 | exit 2 9 | fi 10 | 11 | if [ -z "$location" ] ; then 12 | location="/boot/vmlinux-$version" 13 | if [ ! -e "$location" ] ; then 14 | location="/boot/vmlinuz-$version" 15 | if [ ! -e "$location" ] ; then 16 | echo >&2 "W: sign-kernel: kernel image not found" 17 | exit 1 18 | fi 19 | fi 20 | fi 21 | 22 | rm -f "$location.sig" 23 | 24 | # exit if kernel does not need an initramfs 25 | if [ "$INITRD" = 'No' ]; then 26 | exit 0 27 | fi 28 | 29 | initrd_img="/boot/initrd.img-$version" 30 | 31 | rm -f "$initrd_img.sig" 32 | -------------------------------------------------------------------------------- /dracut_lsbk_sign.conf: -------------------------------------------------------------------------------- 1 | 2 | # shellcheck shell=bash 3 | # shellcheck disable=SC2154 4 | 5 | # note: printf is used instead of echo to avoid backslash 6 | # processing and to properly handle values that begin with a '-'. 7 | 8 | lsbk_log() { printf '%s\n' "$*"; } 9 | lsbk_error() { lsbk_log "ERROR: $*" >&2; } 10 | lsbk_fatal() { lsbk_error "$@"; exit 1; } 11 | 12 | # appends a command to a trap 13 | # 14 | # - 1st arg: code to add 15 | # - remaining args: names of traps to modify 16 | # 17 | lsbk_trap_add() { 18 | trap_add_cmd=$1; shift || lsbk_fatal "${FUNCNAME} usage error" 19 | for trap_add_name in "$@"; do 20 | builtin trap -- "$( 21 | # helper fn to get existing trap command from output 22 | # of trap -p 23 | extract_trap_cmd() { printf '%s\n' "$3"; } 24 | # print existing trap command with newline 25 | eval "extract_trap_cmd $(builtin trap -p "${trap_add_name}")" 26 | # print the new trap command 27 | printf '%s\n' "${trap_add_cmd}" 28 | )" "${trap_add_name}" \ 29 | || lsbk_fatal "unable to add to trap ${trap_add_name}" 30 | done 31 | } 32 | 33 | lsbk_sign_ramdrive () { 34 | if [ -r "$outfile" ] ; then 35 | local GPG_SIGN_HOMEDIR="/var/lib/secureboot/gpg-home" 36 | local GPG_SIGN_KEYID="bootsigner@localhost" 37 | 38 | local GPG=$(command -v gpg2 2>/dev/null) || \ 39 | local GPG=$(command -v gpg 2>/dev/null) 40 | if [[ "${outfile: -4}" = '.tmp' ]] && \ 41 | [[ "$(tr '\0' ' ' < /proc/$PPID/cmdline )" =~ 'weak-modules' ]] ; then 42 | # ugly hacks for weak-modules 43 | local realimg="${outfile: 0:-4}.img" 44 | ( 45 | tail --pid=$PPID -f /dev/null 46 | "$GPG" --quiet --no-permission-warning --homedir "${GPG_SIGN_HOMEDIR}" --detach-sign --default-key "${GPG_SIGN_KEYID}" < "${realimg}" > "${realimg}.sig" && \ 47 | { >&2 echo "linux-secureboot-kit: successfully signed ramdrive '$realimg'!" ; } 48 | ) & disown 49 | else 50 | "$GPG" --quiet --no-permission-warning --homedir "${GPG_SIGN_HOMEDIR}" --detach-sign --default-key "${GPG_SIGN_KEYID}" < "${outfile}" > "${outfile}.sig" && \ 51 | { >&2 echo "linux-secureboot-kit: successfully signed ramdrive '$outfile'!" ; } 52 | fi 53 | fi 54 | } 55 | 56 | trap () { lsbk_trap_add "$@" ; } 57 | trap lsbk_sign_ramdrive EXIT 58 | -------------------------------------------------------------------------------- /fedora30/99-sign-kernel.install: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # On Fedora, copy to /etc/kernel/postinst.d to automatically sign new kernels, 3 | # initramfs images and grub configuration on kernel package installations 4 | PATH="/bin:/usr/bin:/sbin:/usr/sbin" 5 | 6 | COMMAND="$1" 7 | KERNEL_VERSION="$2" 8 | # shellcheck disable=SC2034 9 | ENTRY_DIR_ABS="$3" 10 | KERNEL_IMAGE="$4" 11 | # shellcheck disable=SC2034 12 | INITRD_OPTIONS_START="$5" 13 | 14 | GPG_SIGN_HOMEDIR="/var/lib/secureboot/gpg-home" 15 | GPG_SIGN_KEYID="bootsigner@localhost" 16 | 17 | case "$COMMAND" in 18 | add) 19 | #sign 20 | echo "$(date)" "${0}" "${@}" >> /tmp/signkernel.log 21 | sha256sum /boot/efi/EFI/fedora/grub.cfg 22 | sign() { 23 | echo "About to sign file '${1}' with GPG key '${GPG_SIGN_KEYID}'" 24 | # Don't warn about permissions, we're root and the homedir belongs to a 25 | # different user, so that's okay. 26 | gpg2 --quiet --no-permission-warning --homedir "${GPG_SIGN_HOMEDIR}" --detach-sign --default-key "${GPG_SIGN_KEYID}" < "${1}" > "${2}" 27 | } 28 | 29 | sign "${KERNEL_IMAGE}" "/boot/vmlinuz-${KERNEL_VERSION}.sig" 30 | sign "/boot/initramfs-${KERNEL_VERSION}.img" "/boot/initramfs-${KERNEL_VERSION}.img.sig" 31 | ;; 32 | remove) 33 | #remove 34 | rm -vf "/boot/vmlinuz-${KERNEL_VERSION}.sig" 35 | rm -vf "/boot/initramfs-${KERNEL_VERSION}.img.sig" 36 | ;; 37 | esac 38 | -------------------------------------------------------------------------------- /fedora30/_etc_default_grub.appendix: -------------------------------------------------------------------------------- 1 | 2 | ### Secure Boot signature hooks below 3 | 4 | # shellcheck disable=SC2034 5 | GRUB_ENABLE_BLSCFG="false" # BLS config will not work 6 | 7 | GPG_SIGN_HOMEDIR="/var/lib/secureboot/gpg-home" 8 | GPG_SIGN_KEYID="bootsigner@localhost" 9 | 10 | grub_cfg="${grub_cfg:-/boot/efi/EFI/fedora/grub.cfg}" 11 | # /etc/default/grub is sourced by grub2-mkconfig after grub-mkconfig_lib is 12 | # sourced. Overwrite grub_file_is_not_garbage to also ignore signature files. 13 | grub_file_is_not_garbage () 14 | { 15 | if test -f "$1" ; then 16 | case "$1" in 17 | *.dpkg-*) return 1 ;; # debian dpkg 18 | *.rpmsave|*.rpmnew) return 1 ;; 19 | *.sig) return 1 ;; 20 | README*|*/README*) return 1 ;; # documentation 21 | esac 22 | else 23 | return 1 24 | fi 25 | return 0 26 | } 27 | # Unfortunately, this is not sufficient, as /etc/grub.d/10_linux sources 28 | # grub-mkconfig_lib again. It does try to do so from "${pkgdatadir}" though, so 29 | # that we can make it use a patched lib: 30 | TMPDIR="$(mktemp -d)" 31 | sed -e 's,\*\.rpmsave[^)]*),*.rpmsave|*.rpmnew|*.sig),' /usr/share/grub/grub-mkconfig_lib > "${TMPDIR}/grub-mkconfig_lib" 32 | export pkgdatadir="${TMPDIR}" 33 | cleanup() { 34 | rm -rf "${TMPDIR}" 35 | } 36 | sign() { 37 | >&2 echo "About to create signature of '${grub_cfg}' with GPG key '${GPG_SIGN_KEYID}'" 38 | # Don't warn about permissions, we're root and the homedir belongs to a 39 | # different user, so that's okay. 40 | gpg2 --quiet --no-permission-warning --homedir "${GPG_SIGN_HOMEDIR}" --detach-sign --default-key "${GPG_SIGN_KEYID}" < "${grub_cfg}" > "${grub_cfg}.sig" 41 | cleanup 42 | } 43 | trap sign EXIT 44 | trap cleanup TERM INT 45 | -------------------------------------------------------------------------------- /gpg-batch: -------------------------------------------------------------------------------- 1 | %echo Generating a basic OpenPGP key 2 | Key-Type: RSA 3 | Key-Length: 2048 4 | Key-Usage: sign 5 | Name-Real: Boot Signer 6 | Name-Email: bootsigner@localhost 7 | Expire-Date: 0 8 | %no-ask-passphrase 9 | %no-protection 10 | # Do a commit here, so that we can later print "done" :-) 11 | %commit 12 | %echo done 13 | -------------------------------------------------------------------------------- /grub.cfg.tmpl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | GRUB_ROOT_PASSWD="$(cat grub.passwd)" 6 | 7 | cat < /dev/null 2>&1 ; then 7 | echo "$i" 8 | exit 0 9 | fi 10 | done 11 | 12 | echo "false" 13 | exit 1 14 | -------------------------------------------------------------------------------- /locate-cfg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | for i in "$@" ; do 6 | if [ -h "$i" ] || [ -e "$i" ] ; then 7 | echo "$i" 8 | exit 0 9 | fi 10 | done 11 | exit 1 12 | -------------------------------------------------------------------------------- /probe-grub-modules.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | GRUB2MKIMAGE="$1" 6 | shift 7 | 8 | OUTPUT="$(mktemp)" 9 | 10 | # Sanity check 11 | "$GRUB2MKIMAGE" -p /boot/grub --format=x86_64-efi \ 12 | --output="$OUTPUT" >/dev/null || \ 13 | { 14 | echo >&2 "E: grub2 image builder is not usable" 15 | rm "$OUTPUT" 16 | exit 1 17 | } 18 | 19 | for i in "$@" ; do 20 | "$GRUB2MKIMAGE" -p /boot/grub \ 21 | --format=x86_64-efi \ 22 | --output="$OUTPUT" \ 23 | "$i" >/dev/null 2>&1 && \ 24 | echo "$i" 25 | done 26 | 27 | rm "$OUTPUT" 28 | exit 0 29 | -------------------------------------------------------------------------------- /setup_dkms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if ! { [[ -d /etc/dkms ]] && [[ -x "$(command -v dkms)" ]] && \ 4 | [[ -r /etc/dkms/framework.conf ]] ; } ; then 5 | >&2 echo "No working installation of DKMS framework found. Skipping setup." 6 | exit 0 7 | fi 8 | 9 | hook_script="/var/lib/secureboot/dkms/chain-sign-hook.conf" 10 | [[ -e "$hook_script" ]] || { 11 | >&2 echo "Hook script is not installed!" 12 | exit 1 13 | } 14 | 15 | dkms_tree="/var/lib/dkms" 16 | . /etc/dkms/framework.conf 17 | 18 | dkms_status="$(dkms status)" 19 | 20 | # parse dkms status 21 | shopt -s extglob 22 | i=0 23 | while read -r line ; do 24 | IFS=',' read -ra arr <<< "$line" 25 | module[$i]="${arr[0]}" 26 | version[$i]="${arr[1]##+([[:space:]])}" 27 | kernelver[$i]="${arr[2]##+([[:space:]])}" 28 | arch_status="${arr[3]##+([[:space:]])}" 29 | IFS=':' read -ra arch_status_arr <<< "$arch_status" 30 | arch[$i]="${arch_status_arr[0]}" 31 | status[$i]="${arch_status_arr[1]##+([[:space:]])}" 32 | [[ "${module[$i]}" ]] && [[ "${version[$i]}" ]] && [[ "${kernelver[$i]}" ]] \ 33 | && [[ "${arch[$i]}" ]] && [[ "${#arr[@]}" = 4 ]] && \ 34 | [[ "${#arch_status_arr[@]}" = 2 ]] && [[ "${status[$i]}" =~ installed ]] && \ 35 | (( i++ )) 36 | done <<< "$dkms_status" 37 | 38 | mod_count="$i" 39 | 40 | flagdir="$(mktemp -d)" 41 | cleanup () { 42 | rm -rf "$flagdir" 43 | } 44 | trap cleanup EXIT 45 | 46 | # install hooks for modules 47 | for ((i=0; i&2 echo "Module ${module[$i]} already has user override." 57 | >&2 echo "Please do manual merge with $hook_script." 58 | :> "$flagdir/${module[$i]}.merge_notify" 59 | fi 60 | else 61 | ln -s "$hook_script" "$modconf" 62 | :> "$flagdir/${module[$i]}.conf_installed" 63 | fi 64 | fi 65 | done 66 | 67 | # rebuild all modules one by one 68 | for ((i=0; i&2 echo "rebuilding module='${module[$i]}' version='${version[$i]}'" \ 71 | "kernelver='${kernelver[$i]}' arch='${arch[$i]}'" 72 | dkms uninstall -m "${module[$i]}" -v "${version[$i]}" -k "${kernelver[$i]}" -a "${arch[$i]}" 73 | # clean build artifacts to ensure rebuild will take place 74 | rm -rf "${dkms_tree:?}/${module[$i]}/${version[$i]}/${kernelver[$i]}/${arch[$i]}/" 75 | dkms install -m "${module[$i]}" -v "${version[$i]}" -k "${kernelver[$i]}" -a "${arch[$i]}" 76 | done 77 | 78 | # cleanup 79 | exit 0 80 | -------------------------------------------------------------------------------- /uuidgen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | SCRIPT='import uuid;print (str(uuid.uuid4()));' 6 | 7 | UUID=$(uuidgen 2>/dev/null) || \ 8 | UUID=$(python -c "$SCRIPT" 2>/dev/null) || \ 9 | UUID=$(python3 -c "$SCRIPT" 2>/dev/null) || \ 10 | UUID=$(python2 -c "$SCRIPT" 2>/dev/null) 11 | 12 | echo "$UUID" 13 | -------------------------------------------------------------------------------- /zzz-sign-initramfs: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | lsbk_mkinitramfs_ppid=$PPID 4 | case "$(tr '\0' ' ' < /proc/$PPID/cmdline )" in 5 | *update-initramfs*) 6 | lsbk_track_file=yes 7 | ;; 8 | *) 9 | lsbk_track_file=no 10 | ;; 11 | esac 12 | 13 | lsbk_sign_filter () { 14 | ${lsbk_real_compress} 15 | rc=$? 16 | mypid=$(exec sh -c 'echo $PPID') 17 | stdout_path="$(readlink -f /proc/$mypid/fd/1)" 18 | exec > /dev/null 19 | case "${stdout_path}" in 20 | *.sig) 21 | # No need to generate signature for signature 22 | return $rc 23 | ;; 24 | *) 25 | : 26 | ;; 27 | esac 28 | GPG=$(command -v gpg2 2>/dev/null) || \ 29 | GPG=$(command -v gpg 2>/dev/null) 30 | GPG_SIGN_HOMEDIR="/var/lib/secureboot/gpg-home" 31 | GPG_SIGN_KEYID="bootsigner@localhost" 32 | if "$GPG" --quiet --no-permission-warning --homedir "${GPG_SIGN_HOMEDIR}" \ 33 | --detach-sign --default-key "${GPG_SIGN_KEYID}" < "${stdout_path}" \ 34 | > "${stdout_path}.sig" ; then 35 | >&2 echo "linux-secureboot-kit: successfully signed ramdrive '$stdout_path'!" 36 | # also track temporary outputs 37 | case "$stdout_path" in 38 | *.tmp|*.new) 39 | lsbk_track_file=yes 40 | ;; 41 | *) 42 | : 43 | ;; 44 | esac 45 | if [ "$lsbk_track_file" = "yes" ] ; then 46 | # daemonize deferred move action 47 | waiter_source="$(cat <<'EOF' 48 | stdout_path="$0" 49 | lsbk_mkinitramfs_ppid="$1" 50 | mypid=$(exec sh -c 'echo $PPID') 51 | tail --pid=$lsbk_mkinitramfs_ppid -f /dev/null 52 | new_path="$(readlink -f /proc/$mypid/fd/9)" 53 | if [ -r "$new_path" ] ; then 54 | >&2 echo "initramdrive installed to \"$new_path\". moving signature..." 55 | >&2 mv -v "${stdout_path}.sig" "${new_path}.sig" 56 | else 57 | rm -f "${stdout_path}.sig" 58 | fi 59 | EOF 60 | )" 61 | setsid sh -c "$waiter_source" "$stdout_path" "$lsbk_mkinitramfs_ppid" \ 62 | /dev/null 4>/dev/null 5>/dev/null 9<"$stdout_path" & 63 | fi 64 | else 65 | : 66 | fi 67 | return $rc 68 | } 69 | 70 | if [ -z "${compress:-}" ]; then 71 | compress=${COMPRESS} 72 | else 73 | COMPRESS=${compress} 74 | fi 75 | 76 | [ "${compress}" = lzop ] && compress="lzop -9" 77 | [ "${compress}" = xz ] && compress="xz --check=crc32" 78 | 79 | lsbk_real_compress=${compress} 80 | compress=lsbk_sign_filter 81 | --------------------------------------------------------------------------------