Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

What is Sécurix?

An operating system developed at DINUM, primarily for internal use, for building workstation environments that are declarative, reproducible and secure by default. Based on NixOS, it allows to write configuration as code for defining users, programs, services, configurations, and more.

Note

This project is alpha, no support is provided at this time.

Goals

SécurixOS is a NixOS distribution developed by DINUM to equip secure computers for system administration, office and development to process Unclassified information initially and potentially Restricted information thereafter.

It is a secure PC model designed to allow access to production and other critical uses while guaranteeing a variable security level depending on the configuration used.

Thanks to NixOS, this model is re-instantiable for various use cases: multi-agent workstation, multi-level workstation, intranet-only workstation, etc. with different teams and VPN strains.

Built according to ANSSI recommendations: https://cyber.gouv.fr/publications/recommandations-relatives-ladministration-securisee-des-si.

Licenses

Sécurix is mainly distributed under the MIT license. See the LICENSES folder for more details. The project makes use of REUSE to annotate the licenses. See also How to contribute.

Initialization

Context

The SécurixOS project is a workstation building kit, whether for administration or for office use and everything in between in terms of workstation nuances, e.g. specialized use-case workstation, hardened nomad workstation, etc.

The project builds on the NixOS project, which itself is a Linux distribution building kit inheriting Nix concepts [1] and specializes it for the purpose of building organizationally managed workstations.

The SécurixOS project does not address workstation needs in BYOD (“Bring Your Own Device”) environments; it only addresses managed workstations under the control of a centralized entity within an organization. It is of course possible to manage several SécurixOS strains under different branches within the same organization, if necessary.

Since SécurixOS is a building kit, it does not provide “off-the-shelf” installation artifacts like classic Linux distributions, so it is not possible to download a SécurixOS ISO to try it out as you would need to fabricate a fake organization and have a demonstration workstation infrastructure.

Nevertheless, the SécurixOS project offers example code repositories to see one way to integrate the SécurixOS kit to obtain all kinds of artifacts: ISOs, systems to copy to disk, virtual machine images, etc.

The SécurixOS project assumes that those who take it on already have an understanding of the Nix or NixOS ecosystem; it is not advisable to use SécurixOS without Nix skills within your organization.

Distinction between securix and securix-$org or bureautix-$org

In the SécurixOS project, we recommend keeping a reference to the open source project and pooling efforts in the digital commons when your need could benefit everyone. When your need is very specific to your organization or experimental, it is preferable to keep it to yourself and mature it. In addition, the SécurixOS project offers many mechanisms to control the policies applied to generate your organization’s workstations. This information must be stored in a code repository, we call them securix-$org repositories (if you use an admin workstation variant) or bureautix-$org (if you use an office variant), where you find:

  • your organization-specific customizations
  • your experiments
  • your static inventories
  • your VPN configuration items
  • your proxy configuration items
graph TD
    A[securix<br/>open-source<br/>cloud-gouv/securix] --> B[securix-$org<br/>org repo<br/>custom + inventory + VPN/proxy]
    A --> C[bureautix-$org<br/>org office repo]
    B -.-> D[securix-acme<br/>e.g. acme]
    C -.-> E[bureautix-acme<br/>e.g. acme]
    D & E --> F[deployed workstations]

For example, if your organization acme decides to deploy an admin workstation and an office workstation (two populations, sometimes overlapping) under the management of two different divisions, you can then build securix-acme and bureautix-acme and they will both refer to the open source project https://github.com/cloud-gouv/securix. It is of course possible to mirror this project on your forge and depend on the mirrored version.

Thus, for upgrades, there are two major components to manage:

  • NixOS & nixpkgs which is updated every 6 months if you follow stable versions or every week if you follow rolling versions
  • SécurixOS which is developed continuously and maintains security components that are not in nixpkgs, e.g. lanzaboote for Secure Boot or disko for disk partitioning.

Prepare the securix-$org or bureautix-$org repository

We will take the example of the organization acme, to prepare the securix-acme repository, there are two possibilities:

  • Create your hierarchy and structure from scratch if you understand the APIs provided by the SécurixOS project for instantiation, it is also quite possible to consume the useful modules directly and ignore all the project’s abstractions
  • Follow an example template like bureautix-example

We propose to explain how to work with the latter for the rest of this guide.

Fork bureautix-example

https://github.com/cloud-gouv/bureautix-example is a dummy example of applying the SécurixOS kit.

To take it over, you can fork it directly on GitHub then make it private (but be careful, it leaves traces in GitHub’s database), or clone it then push it to a new independent repository that is private from the start.

The repository structure is as follows:

bureautix-example/
├── common/                         # Shared configurations (all machines)
│   ├── admins.nix                  # System administrators via FIDO2
│   ├── filesystems.nix             # Partition model choice
│   ├── pam_u2f.nix                 # YubiKey/U2F authentication
│   ├── printing.nix                # Printing drivers
│   └── tools.nix                   # Common tools (curl, vim, htop...)
│
├── defaults/                       # Hardware profiles
│   └── x280.nix                    # ThinkPad X280 (reference model)
│
├── developer/                      # Developer profile
│   └── virtualisation.nix          # Docker, KVM, libvirtd
│
├── inventory/                      # Static inventory
│   ├── machines/
│   │   └── PC140V35.nix            # Specific machine configuration
│   └── users/
│       ├── alice.nix               # User accounts (examples)
│       ├── bob.nix
│       └── heloise.nix
│
├── netboot/                        # PXE network boot
│   ├── Caddyfile                   # Caddy web server
│   └── shell.nix                   # netboot environment
│
├── npins/                          # Dependency pinning
│   └── sources.json                # Pinned source versions
│
├── pkgs/                           # Nix packages specific to Sécurix
│   └── nixos-installer/
│       └── installer.py            # NixOS installer script
│
├── workflows/                      # CI/CD for GitHub
│   ├── build-toplevels.nix         # Building configurations
│   └── pre-commit.nix              # Quality checks
│
├── default.nix                     # Main Nix entry point
├── shell.nix                       # Development environment
├── treefmt.toml                    # Automatic formatting
├── README.md                       # Documentation in English
└── README.fr.md                    # Documentation in French

Manage static inventory: additions and removals

The SécurixOS project officially only supports static inventories for now, which are suitable for environments up to 300 users. Beyond that, it is possible to use static inventories but you should implement scale management techniques, i.e. generate the static inventory from an ITSM database, split inventory directories by distinguishing prefixes to avoid too many files in the same directory and so on.

The SécurixOS project plans to support dynamic inventories based on LDAP or OIDC, which would eliminate the need to pre-declare users in Git.

How to add a new machine?

To add a new machine, simply add a new .nix file with its identifier (e.g. serial number, asset tag, internal inventory number) with the following content:

{
  securix.self.mainDisk = "/dev/nvme0n1"; # If it's an NVMe disk, otherwise /dev/sda if it's a SATA disk.
  securix.self.machine = {
    hardwareSKU = "x280"; # SKU for the hardware profile, here: X280.
    serialNumber = "PC140V35"; # Serial number, asset tag or internal inventory number, it is used to build the machine's hostname.

    users = [
      "heloise" # Users that will be provisioned on this machine. They must exist beforehand.
    ];
  };
}

How to add a new person?

To add a new person, simply add a new .nix file with their user identifier (first name, first then last name, etc.):

{ pkgs, ... }:
{
  securix.self.user = {
    email = "heloise@example.com"; # Email address
    username = "heloise"; # Username
    # Universal 2 Factor keys (U2F), e.g. FIDO2 keys like Yubikey.
    # Generated via pamu2fcfg.
    u2f_keys = [
       "..."
    ];
    defaultLoginShell = pkgs.zsh; # Optionally, shell for the user
  };
}

⚠️ Note, these Nix files do not live in a NixOS module for developers familiar with NixOS, this is a limitation documented by https://github.com/cloud-gouv/securix/issues/196 that we want to lift.

All configurable options for a user are available at https://github.com/cloud-gouv/securix/blob/main/modules/self.nix#L74-L140.

Declare tools and programs

Declaring tools or programs is done using classic NixOS options, e.g. environment.systemPackages.

Declare a tool for all users of the workstation

In the common/tools.nix file, you can add a package for everyone.

Declare a tool for a subset of workstation users

In the common/tools.nix file, you can add a package conditionally depending on the workstation or user by inspecting config.securix.self.user or config.securix.self.machine.

Declare a tool for a person

In the inventory file, you can add environment.systemPackages on a machine or on a person.

Deployment Options

Most of these methods are architectural ideas, the default implementation is the first one.

Bureautix implements most of the next ones.

1. USB Installer for Each System

This deployment method involves creating a separate USB installer for each individual system. Each USB drive contains the necessary installation files tailored to the specific system configuration.

When the user boots from this USB, the installer will directly install a specific NixOS closure after autoinstall-terminal is invoked.

This method is practical when you have few systems and no infrastructure, it’s the default method.

2. Generic “Mass” USB Installer

The “Mass” USB installer is designed to simplify the deployment of multiple systems. It contains a list of pre-configured systems, each indexed by its serial number. When you boot a machine using this USB installer, the system checks its serial number and automatically selects the appropriate system configuration to deploy. This way, you can have a single USB installer for multiple systems, each one automatically configured according to its serial number.

This method is practical when you mass install multiple systems based on serial in serial. It requires a large USB stick if all the multiple systems have high amount of unique customization leading to large closures.

This method is implemented as an example in Bureautix.

3. Generic Online USB Installer

The Generic Online USB installer works similarly to the Mass USB installer, but with a key difference: it connects to an external website during the installation process. Upon booting from the USB, the system sends its serial number to the website, which redirects to a NixOS closure tailored for that specific serial number.

The target website must act as a Nix cache, once the redirection is performed, Nix will copy the toplevel closure in memory.

This method is practical when configurations are all built from CI and pushed to a cache that can be reached in the installer environment. The USB stick can be burned once and stays relatively up-to-date as long as disk layouts or special boot features do not change.

This method can be implemented using an example in Bureautix and adding more system closures.

4. Netboot Installation

The Netboot installation method is a lightweight solution where the installer is sent via PXE (Preboot Execution Environment) instead of a physical USB drive. When a system is booted, it retrieves the installer over the network (typically via HTTP or TFTP) and downloads the rest of the necessary installation files. This enables you to perform installations without needing physical media on hand.

This method is practical for physical device testing development cycles, mass deployment on-site and even online deployments if your OEM supports HTTP boot and you have a mechanism to authenticate the originating system.

This method is implemented as an example in Bureautix.

A cache for your (internal) CI/CD pipelines

This document describes how to configure a Nix binary cache in a CI/CD pipeline so that build results can be reused across runs and across machines. Using a cache dramatically reduces build times and load on your CI infrastructure when building Sécurix-related artifacts (ISOs, system configurations, etc.)

The examples are intentionally CI-agnostic. Where GitHub Actions–specific tooling is referenced, we explain how to replace it in other CI systems.

High-level pipeline flow

A CI pipeline using a Nix cache usually follows these steps:

  1. Check out the repository
  2. Install Nix (Sécurix upstream is developed and tested using https://lix.systems/).
  3. Configure cache access (substituters + credentials)
  4. Run nix-build, nix develop, or nix build
  5. Upload build results to the cache

Each of these steps is described below.

Note: Garbage collection is not covered in this document. After a while, your S3 cache will accumulate useless Nix store paths. You can run a scheduled pipeline to expire objects based on date or liveness using on-the-shelf S3 tooling.

Step 1: Check out the repository

Your CI system must fetch the source code before running Nix commands.

No Nix-specific configuration is required here.

Step 2: Install Nix

A Nix interpreter must be installed on the CI runner.

Requirements

  • Linux or macOS
  • Root or sudo access (unless using user-mode Nix which is not recommended)

Generic installation options

You can install Lix using (Linux example):

curl -L https://install.lix.systems/lix/lix-installer-x86_64-linux | sh

After installation, ensure that:

nix --version

works in subsequent CI steps.

Remarks

Many CI systems offer reusable workflows or templates to install Nix. These often:

  • Preconfigure nix.conf for nixpkgs inputs
  • Inject forge tokens (GitHub, GitLab, etc.) to avoid API rate limits
  • Enable optional features such as KVM acceleration

If you are using GitHub Actions, we recommend https://github.com/samueldr/lix-gha-installer-action, which is fast, slim, and easy to audit. Its logic can be ported to other CI systems if needed.

Step 3: Configure the binary cache

This is the most important, and often the most complex, step, depending on your needs.

Configure substituters

A substituter tells Nix where to download cached artifacts from.

Example substituter configuration:

s3://oss-securix

With additional parameters:

  • endpoint: custom S3-compatible endpoint
  • region: object storage region
  • compression: artifact compression format. We recommend zstd over xz: zstd offers much faster compression and decompression with a reasonable size trade-off. Prefer xz only if storage or bandwidth is the primary constraint.
  • parallel-compression: speed optimization for xz or zstd

These settings can be applied via:

  • nix.conf
  • Environment variables
  • CLI flags

Example nix.conf snippet:

substituters = https://cache.nixos.org s3://oss-securix?endpoint=https://s3.gra.io.cloud.ovh.net&region=gra
trusted-public-keys = oss-securix-1:PUBLIC_KEY_HERE

Step 4: Configure secrets

To upload artifacts to a cache, Nix must sign them and have write access to the S3 object store.

Required secrets

Store the following as CI secrets:

  • Nix signing private key: can be generated via nix key generate-secret
  • Object storage access key: your S3 provider should give you that information.
  • Object storage secret key: your S3 provider should give you that information.

If your project accepts untrusted pull requests, it is strongly recommended to separate caches:

  • Untrusted CI cache: builds triggered by pull requests or forks
  • Trusted CD cache: builds triggered after merge or approval

This prevents unreviewed code from polluting trusted binary caches.

Setting secrets in the CI system

Most CI systems support masked secrets, prefer using that mechanism to a mechanism that can accidentally show the actual credentials.

The signing key is typically referenced in nix.conf:

secret-key-files = /path/to/signing-key

Step 5: Enable cache uploads

To ensure build results are uploaded:

  • The cache must be listed as a substituter
  • The signing key must be available
  • The CI job must have write access to the object store
  • Some part of your workflow must upload the paths you care about

For the last item, there’s many solutions:

  • Use a post-build-hook to upload built paths immediately. This ensures that all store paths touched by the workflow are uploaded, including paths copied from https://cache.nixos.org.
  • use a manual upload step using nix copy $built_path s3://your-bucket?endpoint=... at the end
  • run a daemon at the same time and watch for filesystem events and copy store path as you build them

Step 6: Run the build

Once everything is configured, run your build as usual:

nix-build -A tests

Security considerations

  • Never commit private signing keys
  • Scope object storage credentials to the minimum required permissions (read and write here)
  • Use separate caches for trusted vs untrusted builds if necessary
  • Restrict who can upload to the cache
  • Removing direct access to the signing key and S3 credentials is possible by introducing an intermediate service between CI and the cache, such as Attic.

Hardware profiles

The SecurixOS team maintains a set of hardwares profiles in the hardware directory. These hardware profiles have been manually tested and are used in real-world environments.

Warning

Currently, the team doesn’t accept hardware contributions from the community we cannot test and validate continuously.

However, if your hardware is not supported, you could still use it in SecurixOS by creating and importing your own module in your configuration, as supported by the NixOS extensible option types feature. A SecurixOS hardware module file your-hardware.nix looks like:

{ pkgs, lib, config, ...}:
{
  options.securix.self.machine.hardwareSKU = mkOption {
    type = lib.types.enum ["your-hardware-name"];
  }
  config = lib.mkIf (config.securix.self.machine.hardwareSKU == "your-hardware-name") {
    # The NixOS configuration of your hardware which can be generated by nixos-generate-config
  }
}

In your SecurixOS configuration, you can then import this file and enable your hardware configuration:

{
  imports = [ ./your-hardware.nix ];
  config = {
    securix.self.machine.hardwareSKU = "your-hardware-name";
  }
}

Contributing

Contributing to the project

Contributions are welcome, NixOS expertise is recommended to ease integration. Check open issues and the contribution guide to participate. You can open tickets to propose features and discuss architecture. AI-generated PRs without review and testing will be closed, further contributions from the same author may be blocked.

This README is in French in the source repository but code, issues and PRs are in English.

Building the documentation

To build and render the documentation:

nix-build -A docs.all
xdg-open ./result/index.html
# or
xdg-open ./result/en/index.html  # English version
xdg-open ./result/fr/index.html  # French version

How to test my changes in a virtual machine?

How to test my changes on a real machine?

Resources

  • nix.dev a good starting point into the Nix/Lix and NixOS ecosystem
  • Manuals
  • the official wiki has some interesting tips in niche topics, but of very mixed quality and up-to-date-ness