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:
- Check out the repository
- Install Nix (Sécurix upstream is developed and tested using https://lix.systems/).
- Configure cache access (substituters + credentials)
- Run
nix-build,nix develop, ornix build - 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.conffornixpkgsinputs - 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 endpointregion: object storage regioncompression: artifact compression format. We recommendzstdoverxz:zstdoffers much faster compression and decompression with a reasonable size trade-off. Preferxzonly if storage or bandwidth is the primary constraint.parallel-compression: speed optimization forxzorzstd
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®ion=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-hookto 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/LixandNixOSecosystem - Manuals
Nixos, and particularly the module development sectionNix/Lixfor commands and some implementation detailsnixpkgsregarding writing expressions for building packages
- the official wiki has some interesting tips in niche topics, but of very mixed quality and up-to-date-ness