Compare commits
5 Commits
17b685b93c
...
805977c4c5
Author | SHA1 | Date |
---|---|---|
Tito Sacchi | 805977c4c5 | |
Tito Sacchi | ce2e5ffb14 | |
Tito Sacchi | ca9df83ec9 | |
Tito Sacchi | 1ffd39d441 | |
Tito Sacchi | e2d4f66ee9 |
|
@ -0,0 +1,5 @@
|
|||
_site
|
||||
_cache
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
|
|
@ -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/ /
|
|
@ -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
|
|
@ -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.
|
||||
|
|
61
about_me.md
61
about_me.md
|
@ -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
|
||||
|
|
10
index.html
10
index.html
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
```
|
3
site.hs
3
site.hs
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue