All checks were successful
🛡️ Shell Script Linting / 🛡️ Shell Script Linting (push) Successful in 1m10s
Signed-off-by: Marc S. Weidner <msw@coresecret.dev>
630 lines
39 KiB
Markdown
630 lines
39 KiB
Markdown
---
|
||
gitea: none
|
||
include_toc: true
|
||
---
|
||
[](https://git.coresecret.dev/msw/CISS.debian.live.builder)
|
||
|
||
[](https://eupl.eu/1.2/en/)
|
||
[](https://opensource.org/license/eupl-1-2)
|
||
[](https://www.gnu.org/software/bash/)
|
||
[](https://shellcheck.net/)
|
||
[](https://github.com/mvdan/sh)
|
||
[](https://google.github.io/styleguide/shellguide.html)
|
||
|
||
[](https://docs.gitea.com/)
|
||
[](https://docs.gitea.com/)
|
||
[](https://www.jetbrains.com/store/?section=personal&billing=yearly)
|
||
[](https://keepassxc.org/)
|
||
[](https://www.netcup.com/de)
|
||
[](https://coresecret.eu/)
|
||
[](https://x.com/coresecret_eu)
|
||
[](https://coresecret.eu/spenden/#sepa)
|
||
[](https://coresecret.eu/spenden/#bitcoin)
|
||
[](https://coresecret.eu/contact/#simplex)
|
||
|
||
# 1. CISS.debian.live.builder
|
||
|
||
**Centurion Intelligence Consulting Agency Information Security Standard**<br>
|
||
*Debian Live Build Generator for hardened live environment and CISS Debian Installer*<br>
|
||
**Master Version**: 8.13<br>
|
||
**Build**: V8.13.536.2025.12.04<br>
|
||
|
||
**CISS.debian.live.builder — First of its own.**<br>
|
||
**World-class CIA designed, handcrafted and powered by Centurion Intelligence Consulting Agency.**
|
||
|
||
Developed and maintained as a one-man, security-driven engineering effort since 2024, **CISS.debian.live.builder** is designed
|
||
to serve as a reference implementation for hardened, image-based Debian deployments.
|
||
|
||
This shell wrapper automates the creation of a Debian Trixie live ISO hardened according to the latest best practices in server
|
||
and service security. It integrates into your build pipeline to deliver an isolated, robust environment suitable for cloud
|
||
deployment or unattended installations via the forthcoming `CISS.debian.installer`. Additionally, automated CI workflows based
|
||
on Gitea Actions are provided, enabling reproducible ISO generation. A generic ISO is automatically built upon significant
|
||
changes and made publicly available for download. The latest generic ISO is available at:
|
||
**[PUBLIC CISS.debian.live.ISO](/docs/DL_PUB_ISO.md)**
|
||
|
||
Beyond a conventional live system, **CISS.debian.live.builder** assembles a **fully encrypted, integrity-protected live medium**
|
||
in a single, deterministic build step: a LUKS2 container backed by `dm-integrity` hosting the SquashFS root filesystem, combined
|
||
with a hardened initramfs chain including a dedicated Dropbear build pipeline for remote LUKS unlock. The resulting ISO ships
|
||
with a hardened kernel configuration, strict sysctl and network tuning, pre-configured SSH hardening and fail2ban, and a
|
||
customised `verify-checksums` path providing both ISO-edge verification and runtime attestation of the live root. All components
|
||
are aligned with the `CISS.debian.installer` baseline, ensuring a unified cryptographic and security posture from first boot to
|
||
an installed system. For an overview of the entire build process, see:
|
||
**[MAN_CISS_ISO_BOOT_CHAIN.md](docs/MAN_CISS_ISO_BOOT_CHAIN.md)**
|
||
|
||
When built with the ``--dhcp-centurion`` profile, the live system ships with a strict network and resolver policy:
|
||
``systemd-networkd`` and ``systemd-resolved`` are pre-configured to use ``DNS-over-TLS (DoT)`` exclusively against the
|
||
**CenturionDNS** resolver infrastructure; plain DNS is not used and connectivity failures are treated as hard errors. DNSSEC
|
||
validation is enforced in a fail-closed manner: zones with invalid or broken signatures result in ``SERVFAIL`` and are not
|
||
silently downgraded. Multicast name resolution via ``mDNS`` and ``LLMNR`` is disabled globally to avoid unintended name leakage
|
||
and spoofing surfaces.
|
||
|
||
Check out more leading world-class services powered by Centurion Intelligence Consulting Agency:
|
||
* [CenturionDNS Resolver](https://eddns.eu/)
|
||
* [CenturionDNS Blocklist](https://dns.eddns.eu/blocklists/centurion_titanium_ultimate.txt)
|
||
* [CenturionMeet](https://talk.e2ee.li/)
|
||
* [CenturionNet Services](https://coresecret.eu/cnet/)
|
||
* [CenturionNet Status](https://uptime.coresecret.eu/)
|
||
|
||
**Contact the author:**
|
||
* [Contact the author](https://coresecret.eu/contact/)
|
||
|
||
**Legal Disclaimer:**
|
||
* This project is not affiliated with, authorized, maintained, sponsored, or endorsed by the [Debian Project](https://www.debian.org/)
|
||
* [Centurion Imprint & Legal Notice](https://coresecret.eu/imprint/)
|
||
* [Centurion Privacy Policy](https://coresecret.eu/privacy/)
|
||
|
||
## 1.1. Preliminary Remarks
|
||
|
||
### 1.1.1. HSM
|
||
|
||
Please note that all my signing keys are stored in an HSM and that the signing environment is air-gapped. The next step is to
|
||
move to a room-gapped environment. ^^
|
||
|
||
### 1.1.2. DNSSEC, HSTS, TLS
|
||
|
||
Please note that `coresecret.dev` is included in the [(HSTS Preload List)](https://hstspreload.org/) and always serves the headers:
|
||
````nginx configuration pro
|
||
add_header Expect-CT "max-age=86400, enforce" always;
|
||
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
|
||
````
|
||
|
||
* The zones behind this project are dual-signed with **DNSSEC**. The current validation state is documented in the **[DNSSEC Audit Report](/docs/AUDIT_DNSSEC.md)**
|
||
* The TLS surface of **``git.coresecret.dev``** is independently audited, and the findings are held in the **[TLS Audit Report](/docs/AUDIT_TLS.md)**
|
||
* The topology of the underlying **`CISS.debian.live.builder`** building infrastructure is described in **[Centurion Net](/docs/CNET.md)**
|
||
|
||
### 1.1.3. Gitea Action Runner Hardening
|
||
|
||
The CI runners live on a host in a separate autonomous system, and that host has exactly one purpose: run Gitea Actions runners.
|
||
Each runner receives its own service account without a login shell, is bound to a separate directory tree, and inherits a
|
||
hardened systemd unit with ``DynamicUser``, reduced capabilities, and restrictive sandboxing. A ``systemd-analyze security`` score
|
||
of around **``2.6``** is the baseline, not an aspiration. Traffic from those runners traverses both a software firewall (UFW)
|
||
and dedicated hardware firewall appliances. Docker, where used, runs unprivileged.
|
||
|
||
## 1.2. Match Host and Target Versions
|
||
|
||
I always build a Debian Trixie live image on a Debian Trixie host. The toolchain and all boot components that matter to
|
||
reproducibility are release-specific: ``live-build``, ``live-boot``, ``live-config``, ``debootstrap``, ``mksquashfs``, ``grub``,
|
||
the ``kernel``, ``initramfs`` tooling, and even ``dpkg`` and ``apt`` defaults evolve from one release to the next. Mixing
|
||
generations produces fragile or outright broken ISOs, sometimes subtly, sometimes catastrophically. Keeping host and target in
|
||
lockstep avoids those mismatches and gives me predictable artifacts across builds.
|
||
|
||
## 1.3. Immutable Source-of-Truth System and Encrypted Live Root
|
||
|
||
The live ISO acts as a sealed, immutable execution environment. All relevant configuration, all installation logic, and all
|
||
security decisions are rendered into the image at build time and treated as read-only at runtime. On top of that logical
|
||
immutability, I now layer cryptographic protection of the live root file system itself. The live image contains a LUKS2 container
|
||
file with dm-integrity that wraps the SquashFS payload. The initramfs knows how to locate this container, unlock it, verify its
|
||
integrity, and then present the decrypted SquashFS as the root component of an OverlayFS stack. The detailed boot and
|
||
verification chain is documented separately in **[CISS ISO Boot Chain](docs/MAN_CISS_ISO_BOOT_CHAIN.md)**<br>
|
||
|
||
In compact form, my expectations for the system are:<br>
|
||
|
||
* Every bit that matters for boot and provisioning is covered by checksums that I control and that are signed with keys under my solely authoritative HSM.
|
||
* The live root runs out of a LUKS2 dm-integrity container so that a tampered or bit-rotted SquashFS never becomes a trusted root.
|
||
* Verification steps are not advisory. Any anomaly causes a hard abort during boot.
|
||
* After the live environment has reached a stable, verified state, it can hand off to ``CISS.debian.installer``. The installer operates from the same image, does not pull random payloads from the internet, and keeps the target system behind a hardened firewall until the entire provisioning process has completed.
|
||
* For unattended, headless scenarios I also support builds where the target system is installed without ever exposing a shell over the console. After installation and reboot, the machine waits for a decryption passphrase via an embedded Dropbear SSH instance in the initramfs, limited to public key authentication and guarded by strict cryptographic policies. In such variants even ``/boot`` can be encrypted, with GRUB taking care of unlocking the boot partition.
|
||
|
||
These combinations give me a provisioning chain that is auditable, reproducible, and robust against both casual and targeted tampering.<br>
|
||
|
||
Once the system is up, I can trigger a set of audits from within the live environment:
|
||
|
||
* **Lynis Audit Report**: Outputs a detailed security score and recommendations, confirming a 93%+ hardening baseline.
|
||
Type `lsadt` at the prompt. See example report: **[Lynis Audit Report](/docs/AUDIT_LYNIS.md)**
|
||
* **SSH Audit Report**: Verifies SSH daemon configuration against the latest best-practice cipher, KEX, and MAC recommendations.
|
||
Type `ssh-audit <IP>:<PORT>`. See example report: **[SSH Audit Report](/docs/AUDIT_SSH.md)**
|
||
|
||
## 1.4. Preview
|
||
|
||

|
||
|
||
## 1.5. Caution. Debian Installer and Security Context
|
||
**The Debian Installer (d-i) will ALWAYS boot a new system.**<br>
|
||
|
||
The classical Debian Installer (d-i) always boots its own kernel and its own initramfs. That effect is independent of the way it
|
||
is launched:
|
||
|
||
* from a GRUB entry on the live medium,
|
||
* from within a running live session via a graphical shortcut,
|
||
* through kexec,
|
||
* or via helper packages such as debian-installer-launcher.
|
||
|
||
In all of these cases the running live system is discarded. The memory contents of the hardened live environment vanish, the
|
||
firewall disappears, the hardened SSH daemon is terminated, and the hardened kernel is replaced by the installer kernel. The
|
||
installer brings its own minimal root file system, usually BusyBox plus a limited set of udeb packages, and it does not
|
||
implement my firewall, my AppArmor profiles, my logging configuration, or my remote access policies, unless I explicitly
|
||
reintroduce those elements via preseed.
|
||
|
||
In that phase the security properties are therefore those of d-i, not those of CISS.debian.live.builder. This is not a defect in
|
||
Debian, it is a property of how any installer that boots its own kernel behaves. It is important to keep this distinction in
|
||
mind when deciding whether a workflow must stay inside the hardened live context or may trade that environment for the standard
|
||
installer toolchain.
|
||
|
||
## 1.6. Versioning Schema
|
||
|
||
This project adheres strictly to a structured versioning scheme following the pattern x.y.z-Date.
|
||
|
||
Example: `V8.13.536.2025.12.04`
|
||
|
||
`x.y.z` represents major (x), minor (y), and patch (z) version increments.
|
||
|
||
Date (YYYY.MM.DD) denotes the build or release date, facilitating clear tracking of incremental changes and ensuring
|
||
reproducibility and traceability.
|
||
|
||
## 1.7. Keywords
|
||
|
||
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED",
|
||
"MAY", and "OPTIONAL" in this Repo are to be interpreted as described in [[BCP 14](https://www.rfc-editor.org/info/bcp14)],
|
||
[[RFC2119](https://datatracker.ietf.org/doc/html/rfc2119)], [[RFC8174](https://datatracker.ietf.org/doc/html/rfc8174)] when,
|
||
and only when, they appear in all capitals, as shown here.
|
||
|
||
# 2. Features & Rationale
|
||
|
||
Below I walk through the major hardening components, with a focus on why I implemented them the way I did and how they interact.
|
||
I treat this builder as a reference implementation for my own infrastructure; **it is not a toy**.
|
||
|
||
## 2.1. Kernel Hardening
|
||
|
||
### 2.1.1. Unified Hardened Boot Parameters
|
||
|
||
Both the ``CISS.debian.live.builder`` LIVE ISO and the ``CISS.debian.installer`` rely on the same kernel command line. I consider
|
||
a diverging kernel baseline between installer and live system operationally dangerous, because it leads to two distinct sets of
|
||
expectations about mitigations and attack surface. The boot parameters I apply are:
|
||
|
||
````bash
|
||
apparmor=1 security=apparmor audit_backlog_limit=262144 audit=1 debugfs=off \
|
||
efi=disable_early_pci_dma hardened_usercopy=1 ia32_emulation=0 \
|
||
init_on_alloc=1 init_on_free=1 \
|
||
iommu.passthrough=0 iommu.strict=1 iommu=force \
|
||
kfence.sample_interval=100 kvm.nx_huge_pages=force \
|
||
l1d_flush=on lockdown=integrity loglevel=0 \
|
||
mitigations=auto,nosmt mmio_stale_data=full,force nosmt=force \
|
||
oops=panic page_alloc.shuffle=1 page_poison=1 panic=0 pti=on \
|
||
random.trust_bootloader=off random.trust_cpu=off randomize_kstack_offset=on \
|
||
retbleed=auto,nosmt rodata=on slab_nomerge vdso32=0 vsyscall=none
|
||
````
|
||
|
||
The parameters fall into several categories.
|
||
|
||
* The AppArmor-related flags ``apparmor=1``, ``security=apparmor`` guarantee that AppArmor is not an afterthought but an integral part of the security architecture from the first instruction. I do not accept a boot sequence that comes up without LSM enforcement and then attempts to enable it later.
|
||
* The audit subsystem is configured to be always on ``audit=1`` and to tolerate heavy bursts without dropping events ``audit_backlog_limit=262144``. I treat the audit trail as an evidentiary artifact; truncation because of backlog limits is not acceptable in that model.
|
||
* The debug surface of the kernel is reduced aggressively. ``debugfs=off`` avoids a traditional footgun that exposes kernel internals in a way that is friendly to attackers and rarely necessary in production.
|
||
* Memory is hardened on several levels at allocation time and at free time. ``init_on_alloc=1`` and ``init_on_free=1`` provide deterministic zeroing, ``page_poison=1`` fills freed pages with a poison pattern, and ``page_alloc.shuffle=1`` shuffles the allocator so that a process can no longer rely on stable physical patterns. Together these measures raise the cost of use-after-free exploitation and other memory corruption attacks.
|
||
* The IOMMU is not optional. I force it on ``iommu=force``, disable passthrough ``iommu.passthrough=0`` and require strict behavior ``iommu.strict=``1. Any environment that contains devices capable of DMA must have a correctly configured IOMMU, otherwise the trust model for the CPU and for the memory hierarchy collapses as soon as a hostile device is introduced.
|
||
* ``kfence.sample_interval=100`` activates KFENCE with a sampling interval that is still usable in production but sensitive enough to catch a meaningful subset of memory safety bugs under real workloads.
|
||
* Virtualization-specific knobs include ``kvm.nx_huge_pages=force``, to keep huge pages non-executable, and ``l1d_flush=on`` so that context switches flush the L1 data cache where needed.
|
||
* ``lockdown=integrity`` places the kernel into lockdown mode with an emphasis on integrity. In this project I consider the integrity of the system more critical than the ability to introspect a running kernel from userspace.
|
||
* Speculative execution and microarchitectural issues are covered by ``mitigations=auto,nosmt``,`` mmio_stale_data=full,force``, and ``retbleed=auto,nosmt``. I combine the automatic mitigation set provided by the kernel with a forced Single Thread mode where it is required because simultaneous multithreading is simply not worth the residual risk profile in many server contexts.
|
||
* ``nosmt=force`` acts as a guardrail here. It prevents a misconfiguration from quietly re-enabling SMT while the system operator assumes it is disabled.
|
||
* Fault handling is configured through ``oops=panic`` and ``panic=0``. An oops triggers a panic so that I do not continue to run a kernel in an undefined state. At the same time I instruct the system not to reboot automatically on panic, to preserve the state for post-mortem analysis rather than cutting the ground away under a debugging session.
|
||
* ``pti=on``, ``rodata=on``, and ``slab_nomerge`` are classical hardening parameters that I still consider essential. Page-table isolation, read-only data segments, and prohibiting slab merging collectively prevent a wide range of exploits, especially under pressure from speculative execution attacks.
|
||
* To avoid brittle side assumptions, I remove legacy or obsolete interfaces: ``vdso32=0`` and ``vsyscall=none`` shut down the remaining vestiges of 32-bit vDSO and vsyscall support on 64-bit systems. ``ia32_emulation=0`` it again narrows the attack surface by disabling full 32-bit compatibility on 64-bit kernels.
|
||
* Finally, I do not trust entropy claims either from the bootloader or the CPU itself. I opt out of both with ``random.trust_bootloader=off`` and ``random.trust_cpu=off`` and rely on my own entropy strategy described later.
|
||
|
||
All of these parameters are applied in exactly the same way for the live ISO and for the installer environment. That is a
|
||
deliberate design decision.
|
||
|
||
### 2.1.2. CPU Vulnerability Mitigations
|
||
|
||
I build the kernels with the relevant mitigations for Spectre, Meltdown, L1TF, MDS, TAA, Retbleed, and related families activated.
|
||
The ``mitigations=auto,nosmt`` flag ensures that new mitigations integrated into the mainline kernel become effective as they
|
||
are added, instead of requiring that I micromanage every single toggle. The residual performance cost is acceptable in the
|
||
context I am targeting; stale mitigations can be revisited, but missing mitigations will not be.
|
||
|
||
### 2.1.3. Kernel Self-Protection
|
||
|
||
I enable the standard set of self-protection options, such as strict module page permissions, read-only data enforcement, and
|
||
restrictions around kprobes and BPF. The builder is not a kernel configuration tool, but it carries the expectation that the
|
||
kernels it runs with are compiled according to this hardening profile. I treat deviations from that profile as unsupported.
|
||
|
||
### 2.1.4. Local Kernel Hardening
|
||
|
||
* **Description**: The wrapper `sysp()`provides a function to apply and audit local kernel hardening rules from `/etc/sysctl.d/99_local.hardened`:
|
||
````bash
|
||
###########################################################################################
|
||
# Globals: Wrapper for loading CISS.2025 hardened Kernel Parameters
|
||
# Arguments:
|
||
# none
|
||
###########################################################################################
|
||
# shellcheck disable=SC2317
|
||
sysp() {
|
||
sysctl -p /etc/sysctl.d/99_local.hardened
|
||
# sleep 1
|
||
sysctl -a | grep -E 'kernel|vm|net' > /var/log/sysctl_check"$(date +"%Y-%m-%d_%H:%M:%S")".log
|
||
}
|
||
````
|
||
* **Key measures loaded by this file include:**
|
||
* Disabling module loading `kernel.modules_disabled=1`
|
||
* Restricting kernel pointers & logs `kernel.kptr_restrict=2`, `kernel.dmesg_restrict=1`, `kernel.printk=3 3 3 3`
|
||
* Disabling unprivileged BPF and userfaultfd
|
||
* Disabling kexec and unprivileged user namespaces
|
||
* Locking down ptrace scope `kernel.yama.ptrace_scope=2`
|
||
* Protecting filesystem links and FIFOs `fs.protected_*`
|
||
|
||
**Warning**
|
||
Once applied, some hardening settings cannot be undone via `sysctl` without a reboot, and dynamic module loading remains disabled
|
||
until the next boot. Automatic enforcement at startup is therefore omitted by design—run `sysp()` manually and plan a reboot to
|
||
apply or revert these controls.
|
||
|
||
## 2.2. Module Blacklisting
|
||
|
||
* **Description**: Disables and blacklists non-essential or insecure kernel modules.
|
||
* **Rationale**: Minimizes attack surface by preventing loads of drivers or modules not required by the live environment.
|
||
|
||
## 2.3. Network Hardening
|
||
|
||
* **Description**: Applies `sysctl` settings (e.g., `net.ipv4.conf.all.rp_filter=1`, `arp_ignore`, `arp_announce`) to restrict
|
||
inbound/outbound traffic behaviors.
|
||
* **Rationale**: Mitigates ARP spoofing, IP spoofing, and reduces the risk of man-in-the-middle on internal networks.
|
||
|
||
## 2.4. Core Dump & Kernel Hardening
|
||
|
||
* **Description**: Limits core dump generation paths, enforces `Yama` restrictions, and configures `kernel.kptr_restrict`.
|
||
* **Rationale**: Prevents leakage of sensitive memory contents and reduces information disclosure from unintentional crash
|
||
dumps.
|
||
|
||
## 2.5. Entropy Collection Improvements
|
||
|
||
* **Description**: Installs and configures `haveged`, seeds `/dev/random` early.
|
||
* **Rationale**: Cloud instances frequently suffer low entropy at the start; improving randomness ensures strong cryptographic key
|
||
generation for SSH and other services.
|
||
|
||
## 2.6. Permissions & Authentication
|
||
|
||
* **Description**: Sets strict directory and file permissions, integrates with PAM modules (e.g., `pam_faillock`).
|
||
* **Rationale**: Enforces the principle of least privilege at file-system level and strengthens authentication policies.
|
||
|
||
## 2.7. High-Security Baseline (Lynis Audit)
|
||
|
||
* **Description**: Run a baseline audit via [Lynis](https://cisofy.com/lynis/) after build completion.
|
||
The generated live environment consistently achieves a 91%+ score in Lynis security audits.
|
||
* **Rationale**: Provides independent verification of security posture and flags any configuration drifts or missing
|
||
hardening steps.
|
||
|
||
## 2.8. SSH Tunnel & Access Security
|
||
|
||
* **Description**: The SSH tunnel and access are secured through multiple layers of defense:
|
||
* **Firewall Restriction**: ufw allows connections only from defined jump host or VPN exit node IPs.
|
||
* **TCP Wrappers**: `/etc/hosts.allow` and `/etc/hosts.deny` enforce an `ALL: ALL` deny policy, permitting only specified hosts.
|
||
* **One-Hit Ban**: A custom Fail2Ban rule `/etc/fail2ban/jail.d/ciss-default.conf` immediately bans any host
|
||
that touches closed ports.
|
||
* Additionally, the `fail2ban` service is hardened as well according to:
|
||
[Arch Linux Wiki Fail2ban Hardening](https://wiki.archlinux.org/title/fail2ban#Service_hardening)
|
||
* **SSH Ultra-Hardening**: The `/etc/sshd_config` enforces strict cryptographic and connection controls with respect to
|
||
[SSH Audit Guide Debian 12](https://www.ssh-audit.com/hardening_guides.html#debian_12):
|
||
* `RekeyLimit 1G 1h`
|
||
* `HostKey /etc/ssh/ssh_host_ed25519_key`
|
||
* `HostKey /etc/ssh/ssh_host_rsa_key (8192-bit RSA)`
|
||
* `PubkeyAuthentication yes`
|
||
* `PermitRootLogin prohibit-password`
|
||
* `PasswordAuthentication no`
|
||
* `PermitEmptyPasswords no`
|
||
* `LoginGraceTime 2m`
|
||
* `MaxAuthTries 3`
|
||
* `MaxSessions 2`
|
||
* `MaxStartups 08:64:16`
|
||
* `PerSourceMaxStartups 4`
|
||
* `RequiredRSASize 4096`
|
||
* `Ciphers aes256-gcm@openssh.com`
|
||
* `KexAlgorithms sntrup761x25519-sha512@openssh.com,sntrup761x25519-sha512,gss-curve25519-sha256-`
|
||
* `MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com`
|
||
* **Rationale**: These measures ensure that only authorized hosts can establish SSH tunnels, with strict cryptographic and usage
|
||
policies enforced. Minimizes brute force, passive sniffing, and reduces credentials' exposure by limiting protocol features to
|
||
vetted algorithms.
|
||
|
||
## 2.9. UFW Hardening
|
||
|
||
* **Description**: Defaults to `deny incoming` and (optionally) `deny outgoing`; automatically opens only whitelisted ports.
|
||
* **Rationale**: Implements a default-deny firewall, reducing lateral movement and data exfiltration risks immediately after
|
||
deployment.
|
||
|
||
## 2.10. Fail2Ban Enhancements
|
||
|
||
* **Description**:
|
||
* Bans any connection to a closed port for 24 hours
|
||
* Automatically ignores designated bastion/jump host subnets
|
||
* Hardened via `systemd` policy override to limit privileges of the Fail2Ban service itself
|
||
* **Rationale**: Provides proactive defense against port scans and brute-force attacks, while isolating the ban daemon in a
|
||
minimal-privilege context.
|
||
|
||
## 2.11. NTPsec & Chrony
|
||
|
||
* **Description**: Installs `chrony`, selects PTB NTPsec servers by default.
|
||
* **Rationale**: Ensures tamper-resistant time synchronization, which is essential for log integrity, certificate validation,
|
||
and forensic accuracy.
|
||
|
||
# 3. Script Features & Rationale
|
||
|
||
## 3.1. Input Validation & Security
|
||
|
||
* **Description**: All script arguments are validated using a robust input sanitizer.
|
||
* **Rationale**: Prevents injection attacks and ensures only expected data types and values are processed.
|
||
|
||
## 3.2. Debug Mode with Detailed Logging
|
||
|
||
* **Description**: A built-in debug mode outputs clear, timestamped logs including:
|
||
|
||
* Script Name and Path of called Function,
|
||
* Line Number,
|
||
* Function Name,
|
||
* Exit Code of the previous Command,
|
||
* Executed Command.
|
||
* **Rationale**: Simplifies troubleshooting and provides precise error tracing.
|
||
|
||
## 3.3. Secure Debug Logging
|
||
|
||
* **Description**: No hardcoded plaintext password fragments or sensitive artifacts appear in debug logs.
|
||
* **Rationale**: Prevents accidental exposure of credentials during troubleshooting.
|
||
|
||
## 3.4. Secure Password Handling
|
||
|
||
* **Description**: Password files, if provided, are shredded immediately after being hashed.
|
||
* **Rationale**: Prevents password recovery from temporary files.
|
||
|
||
## 3.5. Variable Declaration & Validation
|
||
|
||
* **Description**: All variables are declared and validated before use.
|
||
* **Rationale**: Avoids unintended behavior from unset or improperly set variables.
|
||
|
||
## 3.6. Pure Bash Implementation
|
||
|
||
* **Description**: The entire wrapper and all its functions are written in pure Bash, without external dependencies.
|
||
* **Rationale**: Ensures maximum portability and compatibility with standard Debian environments.
|
||
|
||
## 3.7. Bash Error Handling
|
||
|
||
* **Description**: The implemented xtrace wrapper `set -x` enforces comprehensive Bash error handling to ensure
|
||
* robust,
|
||
* predictable execution,
|
||
* and early detection of failures.
|
||
|
||
and delivers full information, which command failed to execute:
|
||
* Script Name and Path of called Function,
|
||
* Line Number,
|
||
* Function Name,
|
||
* Exit Code of the previous Command,
|
||
* Executed Command,
|
||
* Environment Settings,
|
||
* Argument Counter passed to Script,
|
||
* Argument String passed to Script.
|
||
|
||
* The following `set` options are applied at the beginning of the script (see
|
||
[Bash Manual, The Set Builtin](https://www.gnu.org/software/bash/manual/bash.html#The-Set-BuiltinGNU)):
|
||
```bash
|
||
set -o errexit # Exit script when a command exits with non-zero status (same as "set -e").
|
||
set -o errtrace # Inherit ERR traps in subshells (same as "set -E").
|
||
set -o functrace # Inherit DEBUG and RETURN traps in subshells (same as "set -T").
|
||
set -o ignoreeof # An interactive shell will not exit upon reading EOF.
|
||
set -o nounset # Exit script on use of an undefined variable (same as "set -u").
|
||
set -o pipefail # Return the exit status of the last failed command in a pipeline.
|
||
set -o noclobber # Prevent overwriting files via redirection (same as "set -C").
|
||
```
|
||
|
||
* The following `shopt` options are applied at the beginning of the script (see
|
||
[Bash Manual, The Shopt Builtin](https://www.gnu.org/software/bash/manual/bash.html#The-Shopt-Builtin)):
|
||
````bash
|
||
shopt -s failglob # If set, patterns that fail to match filenames during filename expansion result in an expansion error.
|
||
shopt -s inherit_errexit # If set, command substitution inherits the value of the errexit option instead of unsetting it in the
|
||
# subshell environment.
|
||
shopt -s lastpipe # If set, and job control is not active, the shell runs the last command of a pipeline not executed in
|
||
# the background in the current shell environment.
|
||
shopt -u expand_aliases # If set, aliases are expanded as described. This option is enabled by default for interactive shells.
|
||
shopt -u dotglob # If set, Bash includes filenames beginning with a '.' in the results of filename expansion.
|
||
shopt -u extglob # If set, enable the extended pattern matching features.
|
||
shopt -u nullglob # If set, filename expansion patterns that match no files expand to nothing and are removed.
|
||
````
|
||
|
||
* **Rationale**: These options enforce strict error checking and handling, reducing silent failures and ensuring
|
||
predictable script behavior.
|
||
|
||
# 4. Prerequisites
|
||
|
||
To use **``CISS.debian.live.builder``** as intended, the following baseline is expected:<br>
|
||
|
||
* The build host runs Debian 13 Trixie, fully updated. Building a Trixie image on an older or newer release is technically possible but explicitly not supported.
|
||
* The host has the standard live-build stack installed ``live-build``, ``live-boot``, ``live-config``, ``debootstrap`` and the cryptographic tooling required for ``LUKS2``, ``dm-integrity``, ``cryptsetup``, ``gpg``.
|
||
* Disk space must be sufficient to hold the chroot, the temporary build artifacts, and the final ISO with encrypted root. For comfortable work I assume around 30–40 gigabytes of free space.
|
||
* The user running the builder has root privileges and understands that the script is capable of creating, mounting, and manipulating block devices.
|
||
|
||
# 5. Installation & Usage
|
||
|
||
## 5.1. Interactive CLI / Dialog Wrapper
|
||
|
||
1. Clone the repository:
|
||
```bash
|
||
git clone https://git.coresecret.dev/msw/CISS.debian.live.builder.git
|
||
cd CISS.debian.live.builder
|
||
```
|
||
|
||
2. Preparation:
|
||
1. Ensure you are root.
|
||
2. Create the build directory `mkdir /opt/cdlb` and the tmpfs secrets directory `mkdir /dev/shm/cdlb_secrets`.
|
||
3. Place your desired SSH public key in the `authorized_keys` file, for example, in the `/dev/shm/cdlb_secrets` directory.
|
||
4. Place your desired Password in the `password.txt` file, for example, in the `/dev/shm/cdlb_secrets` directory.
|
||
5. Make any other changes you need to.
|
||
|
||
3. Run the config builder script `./ciss_live_builder.sh` and the integrated `lb build` command (example):
|
||
|
||
````bash
|
||
chmod 0700 ./ciss_live_builder.sh
|
||
timestamp=$(date -u +%Y-%m-%dT%H:%M:%S%z)
|
||
./ciss_live_builder.sh \
|
||
--architecture amd64 \
|
||
--autobuild=6.16.3+deb13-amd64 \
|
||
--build-directory /opt/cdlb \
|
||
--cdi \
|
||
--change-splash hexagon \
|
||
--control "${timestamp}" \
|
||
--debug \
|
||
--dhcp-centurion \
|
||
--jump-host 10.0.0.128 [c0de:4711:0815:4242::1] [2abc:4711:0815:4242::1]/64 \
|
||
--key_age=keys.txt \
|
||
--key_luks=luks.txt \
|
||
--provider-netcup-ipv6 [c0de:4711:0815:4242::ffff] \
|
||
--reionice-priority 1 2 \
|
||
--renice-priority "-19" \
|
||
--root-password-file /dev/shm/cdlb_secrets/password.txt \
|
||
--signing_key_fpr=98089A472CCF4601CD51D7C7095D36535296EA14B8DE92198723C4DC606E8F76 \
|
||
--signing_key_pass=signing_key_pass.txt \
|
||
--signing_key=signing_key.asc \
|
||
--ssh-port 4242 \
|
||
--ssh-pubkey /dev/shm/cdlb_secrets \
|
||
--sshfp \
|
||
--trixie
|
||
````
|
||
|
||
4. Locate your ISO in the `--build-directory`.
|
||
5. Boot from the ISO and login to the live image via the console, or the multi-layer secured **coresecret** SSH tunnel.
|
||
6. Type `sysp` for the final kernel hardening features.
|
||
7. Check the boot log with `jboot` and via `ssf` that all services are up.
|
||
8. Finally, audit your environment with `lsadt` for a comprehensive Lynis audit.
|
||
9. Type `celp` for some shortcuts.
|
||
|
||
## 5.2. Make Wrapper, Quick Usage
|
||
|
||
This repo ships a thin make wrapper around ``./ciss_live_builder.sh``, so you can compose a correctly quoted command and either
|
||
preview it or run it.
|
||
|
||
1. Clone the repository:
|
||
|
||
```bash
|
||
git clone https://git.coresecret.dev/msw/CISS.debian.live.builder.git
|
||
cd CISS.debian.live.builder
|
||
```
|
||
|
||
2. Preparation:
|
||
1. Ensure you are root.
|
||
2. Create the build directory `mkdir /opt/cdlb` and the tmpfs secrets directory `mkdir /dev/shm/cdlb_secrets`.
|
||
3. Place your desired SSH public key in the `authorized_keys` file, for example, in the `/dev/shm/cdlb_secrets` directory.
|
||
4. Place your desired Password in the `password.txt` file, for example, in the `/dev/shm/cdlb_secrets` directory.
|
||
5. Copy and edit the sample and set your options (no spaces around commas in lists):
|
||
|
||
````bash
|
||
cp config.mk.sample config.mk
|
||
````
|
||
|
||
````bash
|
||
BUILD_DIR=/opt/cdlb
|
||
ROOT_PASSWORD_FILE=/dev/shm/cdlb_secrets/password.txt
|
||
SSH_PORT=4242
|
||
SSH_PUBKEY=/dev/shm/cdlb_secrets
|
||
|
||
# Optional
|
||
PROVIDER_NETCUP_IPV6=2001:cdb::1
|
||
# comma-separated; IPv6 in [] is fine
|
||
JUMP_HOSTS=[2001:db8::1],[2001:db8::2]
|
||
````
|
||
|
||
3. Dry-run first (prints the exact command): ````make dry-run````
|
||
|
||
4. Execute the build: ````make live````
|
||
|
||
## 5.3. CI/CD Gitea Runner Workflow Example
|
||
|
||
1. Clone the repository:
|
||
|
||
```bash
|
||
git clone https://git.coresecret.dev/msw/CISS.debian.live.builder.git
|
||
cd CISS.debian.live.builder
|
||
```
|
||
2. Edit the `.gitea/workflows/generate-iso.yaml` file according to your requirements. Ensure that the trigger file
|
||
`.gitea/trigger/t_generate.iso.yaml` and the counter are updated. Change all the necessary `{{ secrets.VAR }}`.
|
||
Push your commits to trigger the workflow. Then download your final ISO from the specified Location.
|
||
|
||
```yaml
|
||
#...
|
||
steps:
|
||
- name: Preparing SSH Setup, SSH Deploy Key, Known Hosts, .config.
|
||
run: |
|
||
rm -rf ~/.ssh && mkdir -m700 ~/.ssh
|
||
|
||
### Private Key
|
||
echo "${{ secrets.CHANGE_ME }}" >| ~/.ssh/id_ed25519
|
||
chmod 0600 ~/.ssh/id_ed25519
|
||
#...
|
||
### https://github.com/actions/checkout/issues/1843
|
||
- name: Using manual clone via SSH to circumvent Gitea SHA-256 object issues.
|
||
run: |
|
||
git clone --branch "${GITHUB_REF_NAME}" ssh://git@CHANGE_ME .
|
||
#...
|
||
- name: Importing the 'CI PGP DEPLOY ONLY' key.
|
||
run: |
|
||
### GPG-Home relative to the Runner Workspace to avoid changing global files.
|
||
export GNUPGHOME="$(pwd)/.gnupg"
|
||
mkdir -m700 "${GNUPGHOME}"
|
||
echo "${{ secrets.CHANGE_ME }}" >| ci-bot.sec.asc
|
||
#...
|
||
- name: Configuring Git for signed CI/DEPLOY commits.
|
||
run: |
|
||
export GNUPGHOME="$(pwd)/.gnupg"
|
||
git config user.name "CHANGE_ME"
|
||
git config user.email "CHANGE_ME"
|
||
#...
|
||
- name: Preparing the build environment.
|
||
run: |
|
||
mkdir -p /opt/config
|
||
mkdir -p /opt/livebuild
|
||
echo "${{ secrets.CHANGE_ME }}" >| /opt/config/password.txt
|
||
echo "${{ secrets.CHANGE_ME }}" >| /opt/config/authorized_keys
|
||
#...
|
||
- name: Starting CISS.debian.live.builder. This may take a while ...
|
||
run: |
|
||
chmod 0700 ciss_live_builder.sh && chown root:root ciss_live_builder.sh
|
||
timestamp=$(date -u +"%Y_%m_%d_%H_%M_Z")
|
||
### Change "--autobuild=" to the specific kernel version you need: '6.12.22+bpo-amd64'.
|
||
./ciss_live_builder.sh \
|
||
--autobuild=CHANGE_ME \
|
||
--architecture CHANGE_ME \
|
||
--build-directory /opt/livebuild \
|
||
--control "${timestamp}" \
|
||
--jump-host "${{ secrets.CHANGE_ME }}" \
|
||
--root-password-file /opt/config/password.txt \
|
||
--ssh-port CHANGE_ME \
|
||
--ssh-pubkey /opt/config
|
||
#...
|
||
### SKIP OR CHANGE ALL REMAINING STEPS
|
||
```
|
||
|
||
# 6. Licensing & Compliance
|
||
|
||
Unless stated otherwise in individual files via SPDX headers, this project is licensed under the European Union Public License (EUPL 1.2).
|
||
That license is OSI-approved and compatible with internal use in both public sector and private environments. Several files carry
|
||
dual or multi-license statements, for example **``LicenseRef-CNCL-1.1``** and / or **``LicenseRef-CCLA-1.1``**, where I offer a
|
||
non-commercial license for community use and a commercial license for professional integration. The SPDX headers in each file
|
||
are authoritative. If you plan to integrate **``CISS.debian.live.builder``** into a commercial product or a managed service
|
||
offering, you should treat these license markers as binding and reach out for a proper agreement where required.
|
||
|
||
# 7. Disclaimer
|
||
|
||
This repository is designed for well-experienced administrators and security professionals who are comfortable with low-level
|
||
Linux tooling, cryptography, and automation. It can and will create, format, and encrypt devices. It is entirely possible to
|
||
destroy data if you use it carelessly. I publish this work in good faith and with a strong focus on correctness and robustness.
|
||
Nevertheless, there is no warranty of any kind. You are responsible for understanding what you are doing, for validating your
|
||
own threat model, and for ensuring that this tool fits your regulatory and operational environment. If you treat the builder, and
|
||
the resulting images with the same discipline with which they were created, you will obtain a hardened, reproducible, and
|
||
auditable base for serious systems. If you treat them casually, they will not save you from yourself.
|
||
|
||
---
|
||
**[no tracking | no logging | no advertising | no profiling | no bullshit](https://coresecret.eu/)**
|
||
<!-- vim: set number et ts=2 sw=2 sts=2 ai tw=128 ft=markdown -->
|