Compare commits

...

5 Commits

9 changed files with 292 additions and 61 deletions

5
.dockerignore Normal file
View File

@ -0,0 +1,5 @@
_site
_cache
Dockerfile
.dockerignore

31
Dockerfile Normal file
View File

@ -0,0 +1,31 @@
FROM haskell:9.6-slim AS haskell-builder
WORKDIR /opt/site
RUN cabal update
COPY ./Site.cabal /opt/site/Site.cabal
RUN cabal build --only-dependencies -j
COPY ./site.hs /opt/site/site.hs
RUN cabal install
RUN mv $(readlink -f /root/.local/bin/site) /opt/site/site
FROM debian:bookworm AS site-env
WORKDIR /opt/site
RUN apt update && apt install -y texlive texlive-luatex texlive-latex-extra texlive-lang-italian latexmk curl git
RUN mkdir -p /usr/share/fonts/opentype/alegreya-sans && \
curl -fsSL -o - https://github.com/huertatipografica/Alegreya-Sans/archive/refs/tags/v2.008.tar.gz | \
tar --strip-components 3 -C /usr/share/fonts/opentype/alegreya-sans -xzf - Alegreya-Sans-2.008/fonts/otf/
COPY --from=haskell-builder /opt/site/site /opt/site/site
RUN curl -fsSL https://github.com/sass/dart-sass/releases/download/1.71.0/dart-sass-1.71.0-linux-x64.tar.gz | tar xz -C /opt
ENV PATH="${PATH}:/opt/dart-sass"
ENV LANG=C.utf8
ENV LANGUAGE=C.utf8
ENV LC_ALL=C.utf8
ENTRYPOINT ["/opt/site/site"]
FROM site-env AS site-builder
WORKDIR /opt/site
COPY . /opt/site
RUN ["/opt/site/site", "build"]
FROM scratch AS site
COPY --from=site-builder /opt/site/_site/ /

2
Setup.hs Normal file
View File

@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain

16
Site.cabal Normal file
View File

@ -0,0 +1,16 @@
name: Site
version: 1.0
synopsis: Site builder with Hakyll
author: Tito Sacchi
build-type: Simple
executable site
build-depends: hakyll >= 4.16 && < 5
, base >= 4 && < 5
, mtl >= 2 && < 3
, filepath >= 1.4 && < 2
, process >= 1.6 && < 2
, pandoc >= 3.1 && < 4
main-is: site.hs
hs-source-dirs: .
default-language: Haskell2010

View File

@ -109,14 +109,25 @@
\end{tabularx}
\section{Presentazione}
Frequento il liceo scientifico e nutro un forte interesse verso l'informatica e
la matematica.
Sono interessato principalmente alla matematica e all'informatica.
Mi piace particolarmente ciò che si trova agli estremi: la sicurezza
informatica (low level) e la programmazione funzionale (high level e astratta a
tal punto da diventare matematica).
\section{Istruzione e formazione}
\begin{tabularx}{\textwidth}{p{0.2\textwidth}|l}
\hfill\textsf{09/2023 -- Oggi} &
\makecell[Xt]{\hphantom{}%
{\usekomafont{subsection}Corso di Laurea triennale in Matematica}\\
\textit{Università di Pavia}\\
Allievo della \href{https://www.iusspavia.it}{Scuola Universitaria Superiore IUSS}\\
Allievo del \href{https://www.ghislieri.it/collegio}{Collegio Ghislieri}\hfill
\vspace{5pt}\break
{\url{https://matematica.unipv.it}}\\
Via A. Ferrata, 5, 27100~Pavia~(Italia)\\
\vspace{0.8\baselineskip}
} \\
\hfill\textsf{09/2018 -- 06/2023} &
\makecell[Xt]{\hphantom{}%
{\usekomafont{subsection}Diploma di maturità scientifica}\\
@ -208,7 +219,7 @@ Wolfram Mathematica per ease-of-use da parte degli altri autori.
\section{Progetti}
\subsection{TeamItaly (2022)}
Dal 2022 sono tra i 20 membri di \href{https://teamitaly.eu}{TeamItaly}, la
Nel 2022 sono stato parte di \href{https://teamitaly.eu}{TeamItaly}, la
squadra nazionale italiana di ethical hacking e Capture The Flag. Con TeamItaly
ho partecipato ad \href{https://www.ecsc2022.eu}{ECSC 2022}, la competizione
europea di sicurezza informatica supportata da ENISA, svoltasi a Vienna
@ -320,17 +331,6 @@ community di contributor e mentor che circonda il progetto. Mi è sempre piaciut
esplorare il funzionamento interno di ogni linguaggio, sistema operativo e
software che uso e mi sto dedicando al red teaming.
\subsection{Musica}
Suono il pianoforte da oltre dieci anni e di recente ho iniziato a studiare la
chitarra elettrica, per ampliare il mio background di musicista classico.
Tra il repertorio classico ho una particolare preferenza per il periodo
romantico.
\subsection{Sport}
Ho praticato nuoto agonistico per quattro stagioni (2015-2019) e sono salito sul
podio di svariate competizioni regionali con la mia squadra; ora pratico canoa a
livello amatoriale presso il Centro Universitario Sportivo di Pavia.
\subsection{Fotografia}
Negli ultimi anni ho sviluppato un particolare interesse per la fotografia, sia
per lo scatto sul campo, sia per la post-produzione e l'editing digitale.

View File

@ -5,33 +5,20 @@ showtitle: true
---
I'm Tito, alias "tauroh". I'm a high school student living in Pavia, Italy. I
was born in 2004. I have many more interests than I'm able to pursue and I still
have to decide what to do after high school. Unless I undergo a sudden change in
personality and hobbies, it will be something science-related. I'm quite a geek;
I'm into maths and pretty much everything that has to do with computers. I used
to play the piano regularly until last year but I don't have enough free time to
study music seriously right now.
I'm Tito, alias "tauroh". I'm studying for a bachelor's degree in Maths at the
University of Pavia, in Italy. I'm into maths and pretty much everything that
has to do with computers, except front-end development.
## Maths
I have been fascinated by maths since I was a child, and my parents taught me
where the elegance that some people (like them, and like me) see in mathematics
really resides. Abstraction and formal logic are in my opinion the most advanced
capabilities of the human mind -- the latest that evolution gave us from a
biological perspective. When you get to really appreciate maths, you discover a
brand new world. Unfortunately, that's something that school is not able to
teach -- that's why many people still think that maths is made of formulas,
exercises and grades.
When you get to really appreciate maths, you discover a brand new world.
Unfortunately, that's something that school is not able to teach -- that's why
many people still think that maths is made of formulas, exercises and grades.
As you might have understood, I like pure mathematics, and specifically the most
As you might have understood, I like pure mathematics, and specifically its most
abstract and foundational aspects, often related to logic and philosophy:
[category theory][ct], [abstract algebra][abstract-algebra], [type
theory][type-theory], [model structures][simplicial-sets]... Problem is, you're
supposed to study a lot of undergraduate mathematics before proceeding with this
areas. I haven't done that (yet); my mathematical background is fragile and
therefore my knowledge is quite fragmented. I would like to study maths with a
more consistent approach. Hopefully, that's what I will do after high school.
theory][type-theory], [model structures][simplicial-sets]...
I spent some time studying [Categorical Quantum Mechanics][cqm], a mathematical
setting for quantum physics in dagger-compact categories (such as the one of
@ -43,22 +30,17 @@ two things together! I wrote a partial formalization of these categorical
structures in [Coq][coq]. The sources can be found [on my GitHub][titos-catqm]
(it uses @jwiegley's [category theory library][coq-ct]).
I have taken part in the Italian [Mathematical Olympiad][olimate] with my school
since my first year here. However, training and competitions are fun only with a
team -- I perform much better in the team olympiad, and I don't really put the
required effort and training in the individual competitions.
While attending high school, I took part in the Italian [Mathematical Olympiad][olimate].
This exciting experience led me to study maths.
The [nLab][nLab] is a nice play to get lost in during cold winter nights with a
cup of tea.
## Computer science
Mathematics is hard: it's something I still haven't been able to get around. CS
and programming are easier and that's why I spend a considerable amount of hours
a day playing with my computer. Before examining my interests, I have to stress
that I really happen to hate frontend development and weakly typed programming
languages. This website doesn't depend on 50KB-sized DOM-diffing JavaScript
frameworks and it never will.
I have to stress that I really happen to hate frontend development and weakly
typed programming languages. This website doesn't depend on 50KB-sized
DOM-diffing JavaScript frameworks and it never will.
My approach to computer science is dual. Just like in mathematics, I like
abstract and theoretical areas of CS: functional programming,
@ -71,27 +53,24 @@ operating systems internals.
[Haskell][hs] is an awesome language and [GHC][ghc] is an astonishingly
well-engineered piece of software. Functional programming in general has some
inherently interesting properties related to pure mathematics and
interesting properties related to pure mathematics and
logics[^curry-howard-lambek]. Apart from theoretical and academic topics, I'm
interested in the implementation of call-by-need referentially transparent
functional languages and I'm currently studying the Haskell RTS and the STG
machine (mostly during boring school classes). The GHC codebase is quite hard to
read for a newcomer, and I'm still looking for a mentor! Sometimes I hang out on
`#haskell-it` on `libera.chat`, the IRC channel of [Haskell-ITA][haskell-ita].
functional languages and I've spent some months studying the Haskell RTS and the STG
machine (mostly during boring high school classes).
#### Hacking and cybersecurity
My low-level geek soul sometimes needs to take a break from lambda-calculi and
theoretical CS and gets involved into hacking competitions called [CTFs][ctf]. I
am part of [Tower of Hanoi][toh], the CTF and hacking team from Politecnico di
I like playing hacking competitions called [CTFs][ctf]. I
am part of [Tower of Hanoi][toh], the CTF team from Politecnico di
Milano (although I'm not a student there). Staying up all night looking at
disassemblies and memory dumps has some kind of inherently mystical meaning, and
it's also good fun.
People from ToH were the first to introduce me to offensive cybersecurity during
the [CyberChallenge.IT 2021][cyberchallenge] project. I also take part in the
the [CyberChallenge.IT 2021][cyberchallenge] project. I also took part in the
Italian [Cybersecurity Olympiad][olicyber], which targets high-school students.
I got the second place at the finals in 2021.
[I won the finals in 2022][olicyber-classifica22].
Apart from my technical interests, hacking history is an interesting topic on
its own, and the underground scene that started to fade away a few years before

View File

@ -1,11 +1,11 @@
<h1>~tito</h1>
<p>
Hi! I'm Tito Sacchi, alias "tauroh", an Italian high school student trying to
have fun while deciding which of my interests I'm willing to spend more time
on in the forecoming years. I like maths, computers, classical piano,
swimming, electric guitars, hiking, nature photography and a lot of other
things that I'd be glad to share with someone else.
Hi! I'm Tito Sacchi, alias "tauroh". I'm currently studying maths at the
<a href="https://unipv.it">University of Pavia</a>, and I'm staying at
<a href="https://www.ghislieri.it/collegio/">Collegio Ghislieri</a>.
I like computers, classical piano, swimming, Pallas cats and a lot of
other things.
<a href="about_me.html">Read more about me.</a>
</p>
<p>

View File

@ -0,0 +1,197 @@
---
title: Unlocking ZFS datasets on boot with a YubiKey
summary: 'Custom initramfs scripts to unlock ZFS datasets with a YubiKey.'
tags: sysadmin, linux, zfs
---
My home server runs Rocky Linux 9 on ZFS. Its main root dataset is encrypted, and until a few weeks
ago I had to manually enter the dataset password on the boot console (fortunately the iDRAC allows
me to do that remotely). With a modern server, I would use the TPM2 chip to provide the decryption
key; however, my home server is a PowerEdge R330 and only has the obsolete TPM1.2 chip. I had a
spare YubiKey 5 I bought when Cloudflare offered them at $10 each, so I decided to put that YubiKey
into the internal USB port and use it to unlock datasets without user input. You could argue that
it's as useful as having no encryption, because the YubiKey has no way to detect whether the
boot was from a trusted source or not. Still, I decided to encrypt my root ZFS pool mostly to be
capable of sending encrypted raw sends for backup purposes.
I'm sharing the custom dracut module I built to serve this purpose. It simply loads a symmetrically
encrypted GPG file stored in the initramfs and decrypts it with a passphrase generated by the
YubiKey HMAC feature. The decrypted contents of the GPG file provide the decryption key to ZFS.
## How it works
* During boot, dracut calls our custom hook before mounting ZFS datasets and checks whether
the rootfs dataset has the `zfs_yubikey:keylocation` and `zfs_yubikey:slot` custom attributes set.
The former specifies where the encrypted GPG file containing the ZFS key resides in the initramfs;
the latter is optional and suggests a particular YubiKey HMAC slot to use (defaults to 1).
* If the rootfs requires YubiKey decryption, a HMAC challenge will be generated from the SHA256 sum
of the string `YUBIKEY_ZFS_V1;<machine_id>;<pool_guid>;<dataset_objsetid>`.
* The `ykchalresp` binary sends the aforementioned 256-bit challenge to the YubiKey on the specified
slot and waits for a response.
* The HMAC response is used to decrypt the GPG file and the resulting plaintext is sent to `zfs
load-key -L prompt <dataset>`.
## Setup
The code for the dracut module is provided below. To setup your dataset for automatic unlock,
first setup your YubiKey for HMAC challenge-response on one of the available slots.
Then open a shell prompt and source `zfs-yubikey-lib.sh`; run
`get_response <dataset> [<yubikey_slot>]` to ask the YubiKey the generate the HMAC response and
use it to encrypt the ZFS key with GnuPG. Save the resulting encrypted file in `/etc/zfs/yubikey/`
and set the `zfs_yubikey:keylocation` property to the path of the file you just saved.
Regenerate the initramfs and you're done.
Shell examples:
```sh
source zfs-yubikey-lib.sh
# Set DATASET to your ZFS dataset
DATASET=pool/various/elements/to/dataset
ykinfo -H || echo 'YubiKey not found!'
# Write your ZFS key to the stdin of the following command
gpg --symmetric --pinentry-mode loopback --passphrase-fd 3 --armor \
--output "/etc/zfs/yubikey/${DATASET##*/}.gpg" 3< <(get_response "${DATASET}")
zfs set zfs_yubikey:keylocation="/etc/zfs/yubikey/${DATASET##*/}.gpg" "${DATASET}"
dracut --regenerate-all --force
```
## Code
A dracut module is composed of a `module-setup.sh` (executed on initramfs generation) and an
arbitrary number of hooks and files installed by the module. The directory structure of our module
is the following:
```
zfs-yubikey
├── module-setup.sh (executable)
├── zfs-yubikey-lib.sh (executable)
└── zfs-yubikey-load-key.sh (executable)
```
This directory should be copied to `/usr/lib/dracut/modules.d/91zfs-yubikey` and dracut should be
configured to include this module (see `man 5 dracut.conf`). Code follows.
<br/>
* `module-setup.sh`
```sh
#!/usr/bin/bash
check() {
require_binaries sha256sum gpg ykchalresp ykinfo || return 1
return 0
}
depends() {
echo zfs
return 0
}
install() {
inst_multiple gpg gpg-agent gpg-connect-agent ykchalresp ykinfo sha256sum ||
{ dfatal "Failed to install essential binaries"; exit 1; }
inst_hook pre-mount 85 "${moddir}/zfs-yubikey-load-key.sh"
inst_script "${moddir}/zfs-yubikey-lib.sh" "/lib/dracut-zfs-yubikey-lib.sh"
inst_multiple -o -H /etc/zfs/yubikey/*
}
```
<br/>
* `zfs-yubikey-lib.sh`
```sh
#!/usr/bin/sh
command -v ykchalresp &>/dev/null || return 127
command -v ykinfo &>/dev/null || return 127
command -v zpool &>/dev/null || return 127
command -v zfs &>/dev/null || return 127
command -v gpg &>/dev/null || return 127
generate_challenge () {
local dataset="${1}"
local pool="${dataset%%/*}"
local machine_id=''
if [ -n "$ZFS_YUBI_USE_MACHINE_ID" ]; then
machine_id="$(< /etc/machine-id)"
fi
local pool_guid="$(zpool get -Ho value guid "$pool")"
local dataset_objsetid="$(zfs get -Ho value objsetid "$dataset")"
local key="$(printf 'YUBIKEY_ZFS_V1;%s;%s;%s' "$machine_id" "$pool_guid" "$dataset_objsetid")"
sha256sum < <(printf %s "$key") | cut -f1 -d' '
}
get_response () {
if [ -z "$1" ]; then return 1; fi
local dataset="${1}"
local slot="${2:-1}"
if [ "$slot" != 1 -a "$slot" != 2 ]; then
echo "Invalid slot number!" >&2; return 1
fi
local challenge="$(generate_challenge "$dataset")"
ykchalresp -"$slot" -x "$challenge"
}
```
<br/>
* `zfs-yubikey-load-key.sh`
```sh
#!/usr/bin/sh
. /lib/dracut-zfs-lib.sh
. /lib/dracut-zfs-yubikey-lib.sh
# decode_root_args || return 0
decode_root_args
# There is a race between the zpool import and the pre-mount hooks, so we wait for a pool to be imported
while ! systemctl is-active --quiet zfs-import.target; do
systemctl is-failed --quiet zfs-import-cache.service zfs-import-scan.service && return 1
sleep 0.1s
done
BOOTFS="$root"
if [ "$BOOTFS" = "zfs:AUTO" ]; then
BOOTFS="$(zpool get -Ho value bootfs | grep -m1 -vFx -)"
fi
[ "$(zpool get -Ho value feature@encryption "${BOOTFS%%/*}")" = 'active' ] || return 0
_load_key_yubi_cb() {
ENCRYPTIONROOT="$(zfs get -Ho value encryptionroot "${1}")"
[ "${ENCRYPTIONROOT}" = "-" ] && return 0
[ "$(zfs get -Ho value keystatus "${ENCRYPTIONROOT}")" = "unavailable" ] || return 0
local yubi_keylocation="$(zfs get -Ho value zfs_yubikey:keylocation "${ENCRYPTIONROOT}")"
[ "${yubi_keylocation}" = "-" ] && return 0
[ -r "${yubi_keylocation}" ] || return 0
local yubi_slot="$(zfs get -Ho value zfs_yubikey:slot "${ENCRYPTIONROOT}")"
[ "${yubi_slot}" = "-" ] && yubi_slot=1
udevadm settle
info "ZFS-YubiKey: Checking for YubiKey..."
ykinfo -v &>/dev/null && break
gpg --passphrase-file <(get_response "${ENCRYPTIONROOT}" "${yubi_slot}") --pinentry-mode loopback \
--decrypt "${yubi_keylocation}" | zfs load-key -L prompt "${ENCRYPTIONROOT}"
}
_load_key_yubi_cb "$BOOTFS"
for_relevant_root_children "$BOOTFS" _load_key_yubi_cb
```

View File

@ -1,6 +1,7 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad.State
import Control.Monad
import Hakyll
import System.FilePath
import System.Process
@ -92,7 +93,7 @@ config :: Configuration
config = defaultConfiguration {
deployCommand = "rsync -avP --delete \
\ --exclude blog --exclude cgi-bin --exclude .DS_Store \
\ _site/ tito@tilde.team:~/public_html"
\ --exclude .well-known _site/ tito@tilde.team:~/public_html"
}
postCtx :: Tags -> Context String