Introduction

Welcome to autofirma-nix! This project provides a suite of tools to interact with Spain’s public administration, seamlessly integrating into your NixOS and Home Manager setup. It includes:

  • AutoFirma for digitally signing documents and authenticating on various Spanish administration websites—because ink and paper are so last century.
  • DNIeRemote for using an NFC-based national ID via an Android device—no more digging through drawers for that card reader you haven’t seen since 2010.
  • Configurador FNMT-RCM for securely requesting personal certificates from the Spanish Royal Mint—yes, the mint that makes actual coins.
  • Integration with Mozilla Firefox (provided on both the NixOS and the Home Manager modules) that allows Firefox to communicate with AutoFirma, as required by some sites—now with automatic setup!

Installation

This project is distributed as a Nix flake for easy integration into your NixOS configuration. It provides both a NixOS module for system-wide configurations and a Home Manager module for user-specific setups. Depending on your preference, you can choose one or the other.

Keep in mind that you should enable either the NixOS module or the Home Manager module—not both simultaneously. Think of it like wearing one hat at a time.

Depending on your Nix channel:

  • Use the main branch with nixpkgs-unstable.
  • For stable channels, use the release-XX.YY branch corresponding to your NixOS version.

We officially support current releases. If you encounter issues on an older branch, upgrading is recommended, as it might resolve the problem—and let’s face it, newer is usually better.

Here’s an example configuration to include autofirma-nix as an input:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";

    autofirma-nix = {
      url = "github:nix-community/autofirma-nix";  # For nixpkgs-unstable
      # url = "github:nix-community/autofirma-nix/release-24.11";  # For NixOS 24.11
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  nixConfig = {
    extra-substituters = [
      "https://nix-community.cachix.org"
    ];
    extra-trusted-public-keys = [
      "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
    ];
  };

  # Further configuration details are provided in the sections below.
}

NixOS module

For those who prefer system-wide configurations, autofirma-nix offers a dedicated NixOS module. Below is an example of how to configure the NixOS module. (The inputs section is covered in the previous section.)

{
  outputs = { self, nixpkgs, autofirma-nix, ... }:
    {
      nixosConfigurations.myHostname = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";
        modules = [
          autofirma-nix.nixosModules.default

          {
            programs.autofirma.enable = true;
            programs.autofirma.firefoxIntegration.enable = true;

            programs.dnieremote.enable = true;

            programs.configuradorfnmt.enable = true;
            programs.configuradorfnmt.firefoxIntegration.enable = true;

            programs.firefox.enable = true;
            programs.firefox.policies = {
              SecurityDevices = {
                "OpenSC PKCS#11" = "${pkgs.opensc}/lib/opensc-pkcs11.so";
                "DNIeRemote"     = "${config.programs.dnieremote.finalPackage}/lib/libdnieremotepkcs11.so";
              };
            };
          }
        ];
      };
    };
}

Home Manager module

If you prefer per-user flexibility and customization, the autofirma-nix Home Manager module is the way to go. Here’s how you can set it up in your Home Manager configuration:

{
  outputs = { self, home-manager, autofirma-nix, nixpkgs, ... }:
    {
      homeConfigurations."my-user" = home-manager.lib.homeManagerConfiguration {
        pkgs = nixpkgs.legacyPackages.x86_64-linux;

        modules = [
          autofirma-nix.homeManagerModules.default

          {
            programs.autofirma.enable = true;
            programs.autofirma.firefoxIntegration.profiles = {
              myprofile = {
                enable = true;
              };
            };

            programs.dnieremote.enable = true;

            programs.configuradorfnmt.enable = true;
            programs.configuradorfnmt.firefoxIntegration.profiles = {
              myprofile = {
                enable = true;
              };
            };

            programs.firefox = {
              enable = true;
              policies = {
                SecurityDevices = {
                  "OpenSC PKCS11" = "${pkgs.opensc}/lib/opensc-pkcs11.so";
                  "DNIeRemote" = "${config.programs.dnieremote.finalPackage}/lib/libdnieremotepkcs11.so";
                };
              };
              profiles.myprofile = {
                id = 0;
              };
            };
          }
        ];
      };
    };
}

Security

AutoFirma chats with remote servers in a couple of different ways to handle document signing and authentication. Here’s the lowdown on these scenarios and how certificates fit into the bigger picture.

Browser-based scenario

In most cases, your friendly web browser takes care of the heavy lifting for server authentication: it connects to the remote server and confirms the server’s identity with its own certificate store. After that, the browser opens a WebSocket to AutoFirma, relaying commands back and forth. For this communication to work, a SSL certificate is created and added to Firefox; depending on the installation method you chose is located either in /etc/AutoFirma or in $HOME/.afirma/AutoFirma.

Direct connection scenario

Sometimes, the browser tells AutoFirma to talk directly to the remote server. In that case, AutoFirma itself must determine which Certificate Authorities (CAs) are valid. This is where certificate management in AutoFirma becomes important.

Managing certificates in autofirma-nix

AutoFirma trusts a certificate only if it meets two conditions:

  1. Official Provider
    It must come from one of the providers published in the Spanish Government’s authorized list.

  2. System CA Store
    It must also appear in your system’s ca-bundle (or cacerts) on NixOS. If your NixOS configuration blocks or adds a certificate, AutoFirma respects that setting.

If a certificate is missing from the system CA store or explicitly blocked, AutoFirma will ignore it—even if it shows up on the official list.

Relevant NixOS options

  • security.pki.certificateFiles
    Adds extra certificates to the global truststore. If a certificate is on the official list, and you include it here, AutoFirma will trust it.

  • security.pki.caCertificateBlacklist
    Blocks specific certificates. Even if one is on the official list, AutoFirma ignores it if it appears here.

Minimal example

{
  security.pki = {
    certificateFiles = [
      ./my-certificate.crt
    ];
    caCertificateBlacklist = [
      "Izenpe.com"
    ];
  };
  programs.autofirma.enable = true;
}

In this snippet, if ./my-certificate.crt is on the official list, AutoFirma will trust it, while any certificate from Izenpe.com is blacklisted, no matter what.

Troubleshooting

Encountering issues? Here are some tips to get you back on track:

Security devices do not seem to update or do not appear

If you have installed AutoFirma and enabled Firefox integration, but Firefox does not detect the security devices, you may need to remove the pkcs11.txt file from the Firefox profile folder. For instance, if you enabled the Home Manager module and the profile is named myprofile, the file is located in ~/.mozilla/firefox/myprofile/pkcs11.txt.

Removing it and restarting Firefox should solve the issue:

$ rm ~/.mozilla/firefox/myprofile/pkcs11.txt
$ firefox

Missing certificates even though the DNIe PIN was requested

If OpenSC PKCS#11 prompts you for a password but no certificates are available for signing, you might see something like the following in the Autofirma logs (when running it from a terminal):

$ autofirma
...
INFO: El almacen externo 'OpenSC PKCS#11' ha podido inicializarse, se anadiran sus entradas y se detiene la carga del resto de almacenes
...
INFO: Se ocultara el certificado por no estar vigente: java.security.cert.CertificateExpiredException: NotAfter: Sat Oct 26 15:03:27 GMT 2024
...
INFO: Se ocultara el certificado por no estar vigente: java.security.cert.CertificateExpiredException: NotAfter: Sat Oct 26 15:03:27 GMT 2024
...
SEVERE: Se genero un error en el dialogo de seleccion de certificados: java.lang.reflect.InvocationTargetException
....
SEVERE: El almacen no contiene ningun certificado que se pueda usar para firmar: es.gob.afirma.keystores.AOCertificatesNotFoundException: No se han encontrado certificados validos en el almacen

This occurs because your certificates have expired, as indicated by the “NotAfter:” date.

If the certificates are not expired because you recently renewed them, but you used AutoFirma before this renewal, it is possible that OpenSC has cached your old certificates. To fix this, you need to delete the OpenSC cache. By default, it is located at $HOME/.cache/opensc.

$ rm -rf $HOME/.cache/opensc

NixOS options

The following options can only be set in a NixOS configuration.

programs.autofirma.enable

Whether to enable AutoFirma.

Type: boolean

Default: false

Example: true

programs.autofirma.package

The autofirma package to use.

Type: package

Default: pkgs.autofirma

programs.autofirma.finalPackage

The AutoFirma package after applying configuration.

Type: package (read only)

Default: `programs.autofirma.package` with applied configuration

programs.autofirma.firefoxIntegration.enable

Whether to enable Firefox integration.

Type: boolean

Default: false

Example: true

programs.autofirma.fixJavaCerts

Whether to enable Fix Java certificates.

Type: boolean

Default: false

Example: true

programs.autofirma.truststore.package

The autofirma-truststore package to use.

Type: package

Default: pkgs.autofirma-truststore

programs.autofirma.truststore.finalPackage

The AutoFirma truststore package after applying configuration.

Type: package (read only)

Default: `programs.autofirma.truststore.package` with applied configuration

programs.configuradorfnmt.enable

Whether to enable configuradorfnmt.

Type: boolean

Default: false

Example: true

programs.configuradorfnmt.package

The configuradorfnmt package to use.

Type: package

Default: pkgs.configuradorfnmt

programs.configuradorfnmt.finalPackage

The configuradorfnmt package after applying configuration.

Type: package (read only)

Default: `programs.configuradorfnmt.package` with applied configuration

programs.configuradorfnmt.firefoxIntegration.enable

Whether to enable Firefox integration.

Type: boolean

Default: false

Example: true

programs.dnieremote.enable

Whether to enable DNIeRemote.

Type: boolean

Default: false

Example: true

programs.dnieremote.package

The dnieremote package to use.

Type: package

Default: pkgs.dnieremote

programs.dnieremote.finalPackage

The DNIeRemote package after applying configuration.

Type: package (read only)

Default: `programs.dnieremote.package` with applied configuration

programs.dnieremote.jumpIntro

Skip the intro and jump to a specific screen.

Type: one of “usb”, “wifi”, “no”

Default: "no"

programs.dnieremote.openFirewall

Open the firewall for the selected port.

Type: boolean

Default: false

programs.dnieremote.usbPort

The port to use for the USB connection.

Type: signed integer

Default: 9501

programs.dnieremote.wifiPort

The port to use for the Wi-Fi connection.

Type: signed integer

Default: 9501

Home Manager options

The following options can only be set in a Home Manager configuration.

programs.autofirma.enable

Whether to enable AutoFirma.

Type: boolean

Default: false

Example: true

programs.autofirma.package

The autofirma package to use.

Type: package

Default: pkgs.autofirma

programs.autofirma.finalPackage

The AutoFirma package after applying configuration.

Type: package (read only)

Default: `programs.autofirma.package` with applied configuration

programs.autofirma.firefoxIntegration.profiles

Firefox profiles to integrate AutoFirma with.

Type: attribute set of (submodule)

programs.autofirma.firefoxIntegration.profiles.<name>.enable

Whether to enable Enable AutoFirma in this firefox profile…

Type: boolean

Default: false

Example: true

programs.autofirma.firefoxIntegration.profiles.<name>.name

Profile name.

Type: string

Default: "‹name›"

programs.autofirma.truststore.package

The autofirma-truststore package to use.

Type: package

Default: pkgs.autofirma-truststore

programs.autofirma.truststore.finalPackage

The AutoFirma truststore package after applying configuration.

Type: package (read only)

Default: `programs.autofirma.truststore.package` with applied configuration

programs.configuradorfnmt.enable

Whether to enable configuradorfnmt.

Type: boolean

Default: false

Example: true

programs.configuradorfnmt.package

The configuradorfnmt package to use.

Type: package

Default: pkgs.configuradorfnmt

programs.configuradorfnmt.finalPackage

The configuradorfnmt package after applying configuration.

Type: package (read only)

Default: `programs.configuradorfnmt.package` with applied configuration

programs.configuradorfnmt.firefoxIntegration.profiles

Firefox profiles to integrate configuradorfnmt with.

Type: attribute set of (submodule)

programs.configuradorfnmt.firefoxIntegration.profiles.<name>.enable

Whether to enable Enable configuradorfnmt in this firefox profile…

Type: boolean

Default: false

Example: true

programs.configuradorfnmt.firefoxIntegration.profiles.<name>.name

Profile name.

Type: string

Default: "‹name›"

programs.dnieremote.enable

Whether to enable DNIeRemote.

Type: boolean

Default: false

Example: true

programs.dnieremote.package

The dnieremote package to use.

Type: package

Default: pkgs.dnieremote

programs.dnieremote.finalPackage

The DNIeRemote package after applying configuration.

Type: package (read only)

Default: `programs.dnieremote.package` with applied configuration

programs.dnieremote.jumpIntro

Skip the intro and jump to a specific screen.

Type: one of “usb”, “wifi”, “no”

Default: "no"

programs.dnieremote.usbPort

The port to use for the USB connection.

Type: signed integer

Default: 9501

programs.dnieremote.wifiPort

The port to use for the Wi-Fi connection.

Type: signed integer

Default: 9501