1
0
Fork 0
forked from infra/ansible

draft: add roles for siguldry server and bridge

These roles deploy the Siguldry server and bridge.

There's a few TODOs, and one step that isn't covered at all is the
deployment of the TLS private keys. These need to be encrypted with
systemd-creds, which should be doable via ansible, but the pesign bridge
isn't doing it so I've left it out for now.

Signed-off-by: Jeremy Cline <jeremycline@microsoft.com>
This commit is contained in:
Jeremy Cline 2026-03-31 11:29:19 -04:00
commit 7749af7760
No known key found for this signature in database
12 changed files with 658 additions and 0 deletions

View file

@ -0,0 +1,8 @@
---
- name: Restart siguldry-bridge
ansible.builtin.systemd:
name: siguldry-bridge.service
state: restarted
tags:
- siguldry
- siguldry/bridge

View file

@ -0,0 +1,116 @@
---
- name: Install siguldry package
ansible.builtin.package:
state: present
name: siguldry
tags:
- packages
- siguldry
- siguldry/bridge
# We only need to grant the service explicit access to the TPM on systemd < 258
- name: Get systemd version
ansible.builtin.command: systemctl --version
register: systemd_version_output
changed_when: systemd_version_output.rc != 0
- name: Set whether explicit AllowDevice override is needed
ansible.builtin.set_fact:
explicit_allow_tpm: "{{ (systemd_version_output.stdout_lines[0] | regex_search('\\d+') | int) < 258 }}"
- name: Create siguldry config directory
ansible.builtin.file:
path: /etc/siguldry
state: directory
owner: root
group: root
mode: '0755'
tags:
- config
- siguldry
- siguldry/bridge
- name: Create credstore.encrypted directory
ansible.builtin.file:
path: /etc/credstore.encrypted
state: directory
owner: root
group: root
mode: '0700'
tags:
- config
- siguldry
- siguldry/bridge
- name: Create siguldry-bridge systemd override directory
ansible.builtin.file:
path: /etc/systemd/system/siguldry-bridge.service.d
state: directory
owner: root
group: root
mode: '0755'
tags:
- config
- siguldry
- siguldry/bridge
- name: Setup siguldry-bridge systemd override file
ansible.builtin.template:
src: override.conf.j2
dest: /etc/systemd/system/siguldry-bridge.service.d/override.conf
owner: root
group: root
mode: '0644'
notify: Restart siguldry-bridge
tags:
- config
- siguldry
- siguldry/bridge
- name: Setup siguldry-bridge config file
ansible.builtin.template:
src: bridge.toml.j2
dest: /etc/siguldry/bridge.toml
owner: root
group: root
mode: '0644'
notify: Restart siguldry-bridge
tags:
- config
- siguldry
- siguldry/bridge
- name: Setup bridge certificate
ansible.builtin.copy:
src: "{{ private }}/files/siguldryca/{{ env }}/pki/issued/siguldry-bridge.crt"
dest: /etc/siguldry/bridge.crt
owner: root
group: root
mode: '0644'
tags:
- config
- siguldry
- siguldry/bridge
- name: Setup CA certificate
ansible.builtin.copy:
src: "{{ private }}/files/siguldryca/{{ env }}/pki/ca.crt"
dest: /etc/siguldry/ca.crt
owner: root
group: root
mode: '0644'
tags:
- config
- siguldry
- siguldry/bridge
- name: Enable and start siguldry-bridge service
ansible.builtin.systemd:
name: siguldry-bridge.service
enabled: yes
state: started
daemon_reload: yes
tags:
- services
- siguldry
- siguldry/bridge

View file

@ -0,0 +1,21 @@
# The socket address to listen on for incoming connections from Siguldry servers.
#
# The default is to listen on all interfaces on port 44333.
server_listening_address = "[::]:44333"
# The socket address to listen on for incoming connections from Siguldry clients.
#
# The default is to listen on all interfaces on port 44334.
client_listening_address = "[::]:44334"
# The TLS credentials for the server and client listeners.
#
# Both clients and servers connect to the above addresses and perform mutual TLS.
# Note that the certificate must have `serverAuth` in its extended key usage extension.
#
# Paths that aren't absolute are loaded relative to systemd's credentials directory,
# which are encrypted, see https://systemd.io/CREDENTIALS/
[credentials]
private_key = "siguldry.bridge.private_key.pem"
certificate = "/etc/siguldry/bridge.crt"
ca_certificate = "/etc/siguldry/ca.crt"

View file

@ -0,0 +1,12 @@
[Service]
# Tweak for lower/higher log verbosity.
# Refer to https://docs.rs/tracing-subscriber/0.3.19/tracing_subscriber/filter/struct.EnvFilter.html#directives
Environment=SIGULDRY_BRIDGE_LOG="WARN,siguldry=INFO"
{% if explicit_allow_tpm %}
# Credentials require the TPM to decrypt.
#
# For systemd 257 and older you'll need to explicitly allow access to the TPM
# Refer to https://github.com/systemd/systemd/issues/35959.
DeviceAllow=/dev/tpmrm0
{% endif %}

View file

@ -0,0 +1,7 @@
---
siguldry_kryoptic_conf: "/etc/siguldry/kryoptic.toml"
siguldry_kryoptic_db: "/var/lib/siguldry/kryoptic.sqlite"
siguldry_kryoptic_so_pin: "12345678"
siguldry_kryoptic_user_pin: "123456"

View file

@ -0,0 +1,8 @@
---
- name: Restart siguldry-server
ansible.builtin.systemd:
name: siguldry-server.service
state: restarted
tags:
- siguldry
- siguldry/server

View file

@ -0,0 +1,151 @@
---
# For staging, use Kryoptic as a software HSM for binding
- name: Install kryoptic and PKCS#11 tools (staging only)
ansible.builtin.package:
state: present
name:
- kryoptic
- opensc
- openssl
- pkcs11-provider
tags:
- packages
- siguldry
- siguldry/server
- name: Setup kryoptic configuration
ansible.builtin.template:
src: kryoptic.toml.j2
dest: "{{ siguldry_kryoptic_conf }}"
owner: root
group: root
mode: '0644'
tags:
- config
- siguldry
- siguldry/server
- name: Check if kryoptic token is initialized
ansible.builtin.stat:
path: "{{ siguldry_kryoptic_db }}"
register: kryoptic_db
tags:
- config
- siguldry
- siguldry/server
- name: Initialize kryoptic PKCS#11 token
ansible.builtin.command:
cmd: >-
pkcs11-tool
--module /usr/lib64/pkcs11/libkryoptic_pkcs11.so
--init-token
--label siguldry-stg-token
--so-pin env:KRYOPTIC_SO_PIN
environment:
KRYOPTIC_CONF: "{{ siguldry_kryoptic_conf }}"
KRYOPTIC_SO_PIN: "{{ siguldry_kryoptic_so_pin }}"
when: not kryoptic_db.stat.exists
tags:
- config
- siguldry
- siguldry/server
- name: Initialize kryoptic user PIN
ansible.builtin.command:
cmd: >-
pkcs11-tool
--module /usr/lib64/pkcs11/libkryoptic_pkcs11.so
--login
--so-pin env:KRYOPTIC_SO_PIN
--init-pin
--new-pin env:KRYOPTIC_USER_PIN
environment:
KRYOPTIC_CONF: "{{ siguldry_kryoptic_conf }}"
KRYOPTIC_SO_PIN: "{{ siguldry_kryoptic_so_pin }}"
KRYOPTIC_USER_PIN: "{{ siguldry_kryoptic_user_pin }}"
when: not kryoptic_db.stat.exists
tags:
- config
- siguldry
- siguldry/server
- name: Check for existing binding key
ansible.builtin.command:
cmd: >-
pkcs11-tool
--module /usr/lib64/pkcs11/libkryoptic_pkcs11.so
--login
--pin env:KRYOPTIC_USER_PIN
--list-objects
--type privkey
environment:
KRYOPTIC_CONF: "{{ siguldry_kryoptic_conf }}"
KRYOPTIC_USER_PIN: "{{ siguldry_kryoptic_user_pin }}"
register: kryoptic_objects
changed_when: false
tags:
- config
- siguldry
- siguldry/server
- name: Generate RSA binding key pair
ansible.builtin.command:
cmd: >-
pkcs11-tool
--module /usr/lib64/pkcs11/libkryoptic_pkcs11.so
--login
--pin env:KRYOPTIC_USER_PIN
--keypairgen
--key-type rsa:4096
--usage-sign
--usage-decrypt
--id 1
--label siguldry-binding-key
environment:
KRYOPTIC_CONF: "{{ siguldry_kryoptic_conf }}"
KRYOPTIC_USER_PIN: "{{ siguldry_kryoptic_user_pin }}"
when: "'siguldry-binding-key' not in kryoptic_objects.stdout"
tags:
- config
- siguldry
- siguldry/server
- name: Create self-signed x509 binding certificate
ansible.builtin.command:
cmd: >-
openssl req -x509
-provider pkcs11
-subj /CN=siguldry-binding-key
-passin pass:{{ siguldry_kryoptic_user_pin }}
-key pkcs11:token=siguldry-stg-token;type=private
-out /etc/siguldry/kryoptic-binding.crt
creates: /etc/siguldry/kryoptic-binding.crt
environment:
KRYOPTIC_CONF: "{{ siguldry_kryoptic_conf }}"
tags:
- config
- siguldry
- siguldry/server
- name: Import x509 certificate to token
ansible.builtin.command:
cmd: >-
pkcs11-tool
--module /usr/lib64/pkcs11/libkryoptic_pkcs11.so
--login
--pin env:KRYOPTIC_USER_PIN
--keypairgen
--key-type rsa:4096
--usage-sign
--usage-decrypt
--id 1
--label siguldry-binding-key
environment:
KRYOPTIC_CONF: "{{ siguldry_kryoptic_conf }}"
KRYOPTIC_USER_PIN: "{{ siguldry_kryoptic_user_pin }}"
when: "'siguldry-binding-key' not in kryoptic_objects.stdout"
tags:
- config
- siguldry
- siguldry/server

View file

@ -0,0 +1,202 @@
---
- name: Install siguldry package
ansible.builtin.package:
state: present
name: siguldry
tags:
- packages
- siguldry
- siguldry/server
# We only need to grant the service explicit access to the TPM on systemd < 258
- name: Get systemd version
ansible.builtin.command: systemctl --version
register: systemd_version_output
changed_when: systemd_version_output.rc != 0
- name: Set whether explicit AllowDevice override is needed
ansible.builtin.set_fact:
explicit_allow_tpm: "{{ (systemd_version_output.stdout_lines[0] | regex_search('\\d+') | int) < 258 }}"
- name: Create siguldry config directory
ansible.builtin.file:
path: /etc/siguldry
state: directory
owner: root
group: root
mode: '0755'
tags:
- config
- siguldry
- siguldry/server
- name: Create credstore.encrypted directory
ansible.builtin.file:
path: /etc/credstore.encrypted
state: directory
owner: root
group: root
mode: '0700'
tags:
- config
- siguldry
- siguldry/server
- name: Ensure the siguldry state directory exists
ansible.builtin.file:
path: /var/lib/siguldry
state: directory
owner: siguldry
group: siguldry
mode: '0750'
tags:
- siguldry
- siguldry/server
- name: Create siguldry-signer systemd override directory
ansible.builtin.file:
path: /etc/systemd/system/siguldry-signer@.service.d
state: directory
owner: root
group: root
mode: '0755'
tags:
- config
- siguldry
- siguldry/server
- name: Setup siguldry-signer systemd override file
ansible.builtin.template:
src: signer-override.conf.j2
dest: /etc/systemd/system/siguldry-signer@.service.d/override.conf
owner: root
group: root
mode: '0644'
notify: Restart siguldry-server
tags:
- config
- siguldry
- siguldry/server
- name: Create siguldry-server systemd override directory
ansible.builtin.file:
path: /etc/systemd/system/siguldry-server.service.d
state: directory
owner: root
group: root
mode: '0755'
tags:
- config
- siguldry
- siguldry/server
- name: Setup siguldry-server systemd override file
ansible.builtin.template:
src: server-override.conf.j2
dest: /etc/systemd/system/siguldry-server.service.d/override.conf
owner: root
group: root
mode: '0644'
notify: Restart siguldry-server
tags:
- config
- siguldry
- siguldry/server
- name: Setup siguldry-server config file
ansible.builtin.template:
src: server.toml.j2
dest: /etc/siguldry/server.toml
owner: root
group: root
mode: '0644'
notify: Restart siguldry-server
tags:
- config
- siguldry
- siguldry/server
- name: Setup server certificate
ansible.builtin.copy:
src: "{{ private }}/files/siguldryca/{{ env }}/pki/issued/siguldry-server.crt"
dest: /etc/siguldry/server.crt
owner: root
group: root
mode: '0644'
tags:
- config
- siguldry
- siguldry/server
- name: Setup CA certificate
ansible.builtin.copy:
src: "{{ private }}/files/siguldryca/{{ env }}/pki/ca.crt"
dest: /etc/siguldry/ca.crt
owner: root
group: root
mode: '0644'
tags:
- config
- siguldry
- siguldry/server
- name: Setup binding certificates
ansible.builtin.copy:
src: "{{ private }}/files/sigul/{{ item }}"
dest: "/etc/siguldry/{{ item }}"
owner: root
group: root
mode: '0644'
with_items:
- yubikey_sv03.pem
- yubikey_sv04.pem
- yubikey_sv05.pem
- yubikey_sv06.pem
when: env == "production"
tags:
- config
- siguldry
- siguldry/server
- name: Setup software HSM for binding certificate in staging
ansible.builtin.include_tasks: kryoptic.yml
when: env != "production"
tags:
- siguldry
- siguldry/server
- name: Check if siguldry database is initialized
ansible.builtin.stat:
path: /var/lib/siguldry/siguldry.sqlite
register: siguldry_db
tags:
- config
- siguldry
- siguldry/server
- name: Initialize Siguldry database
ansible.builtin.command:
cmd: >-
systemd-run --pty --wait --collect
--working-directory=/var/lib/siguldry
--setenv=SIGULDRY_SERVER_CONFIG=/etc/siguldry/server.toml
--property=UMask=017
--uid=siguldry
--gid=siguldry
siguldry-server manage migrate
when: not siguldry_db.stat.exists
tags:
- config
- siguldry
- siguldry/server
- name: Enable and start siguldry-server service
ansible.builtin.systemd:
name: siguldry-server.service
enabled: yes
state: started
daemon_reload: yes
tags:
- services
- siguldry
- siguldry/server

View file

@ -0,0 +1,4 @@
[[slots]]
slot = 1
dbtype = "sqlite"
dbargs = "{{ siguldry_kryoptic_db }}"

View file

@ -0,0 +1,17 @@
[Service]
# Tweak for lower/higher log verbosity.
# Refer to https://docs.rs/tracing-subscriber/0.3.19/tracing_subscriber/filter/struct.EnvFilter.html#directives
Environment=SIGULDRY_SERVER_LOG="WARN,siguldry=INFO"
{% if env != "production" %}
# Kryoptic software HSM configuration for staging
Environment=KRYOPTIC_CONF="{{ siguldry_kryoptic_conf }}"
{% endif %}
{% if explicit_allow_tpm %}
# Credentials require the TPM to decrypt.
#
# For systemd 257 and older you'll need to explicitly allow access to the TPM
# Refer to https://github.com/systemd/systemd/issues/35959.
DeviceAllow=/dev/tpmrm0
{% endif %}

View file

@ -0,0 +1,101 @@
# The location where the server should store its state.
#
# To back up the service, back up this directory.
state_directory = "/var/lib/siguldry/"
# The hostname of the Siguldry bridge.
{% if env == "production" %}
bridge_hostname = "sign-bridge.{{datacenter}}.fedoraproject.org"
{% else %}
bridge_hostname = "sign-bridge.stg.{{datacenter}}.fedoraproject.org"
{% endif %}
# The port to use when connecting to the Siguldry bridge.
bridge_port = 44333
# The number of ready connections to maintain with the bridge. This decreases the latency of
# responses when multiple client connections are established, at the expense of some idle
# connections. Be aware that the bridge has its own limits on the allowable number of idle
# server connections. If you use multiple servers with a single bridge, be sure that the
# bridge allows enough idle connections to cover each server's pool size. The default is 32.
connection_pool_size = 32
# The minimum length for user's access password, in *bytes*. For example, the multi-byte
# UTF-8 character "🪿" counts as 4 bytes.
user_password_length = 64
# The user ID to use when creating OpenPGP keys.
#
# This is typically an email like "Signing Key <signing@example.com>".
openpgp_user_id = "Fedora <fedora-openpgp@fedoraproject.org>"
# The set of certificates to encrypt passwords with.
#
# At least one entry should include a PKCS#11 URI for a private key. Signing keys are encrypted
# using each certificate, so providing more than one binding means *any* of the private keys
# associated with the certificates will allow you to access the signing key, assuming you have
# the user-set password for the key as well.
#
# When binding is used, the admin needs to unlock the token by entering the PIN using
# "siguldry-server enter-pin".
#
# If no bindings are configured, the keys are protected using only the user-provided
# password.
#
# An example binding entry:
#
# [[pkcs11_bindings]]
# certificate = "/path/to/cert.pem"
# private_key = "pkcs11:token=some-token;type=private"
#
# [[pkcs11_bindings]]
# certificate = "/path/to/a/second/cert.pem"
{% if env == "production" %}
[[pkcs11_bindings]]
certificate = "/etc/sigul/yubikey_sv03.pem"
{% if inventory_hostname.startswith('sign-vault03') %}
private_key = "pkcs11:serial=8f2a341e00d7a665;id=%03;type=private"
{% endif %}
[[pkcs11_bindings]]
certificate = "/etc/sigul/yubikey_sv04.pem"
{% if inventory_hostname.startswith('sign-vault04') %}
private_key = "pkcs11:serial=b38ee13e56b3b987;id=%03;type=private"
{% endif %}
[[pkcs11_bindings]]
certificate = "/etc/siguldry/yubikey_sv05.pem"
{% if inventory_hostname.startswith('sign-vault05') %}
private_key = "pkcs11:serial=e8dae68ae187ff13;id=%03;type=private"
{% endif %}
[[pkcs11_bindings]]
certificate = "/etc/siguldry/yubikey_sv06.pem"
{% if inventory_hostname.startswith('sign-vault06') or inventory_hostname == "sign-vault01.rdu3.fedoraproject.org" %}
private_key = "pkcs11:serial=e3c4804a6631dd5f;id=%03;type=private"
{% endif %}
{% else %}
# In staging, use Kryoptic as a software HSM for binding
[[pkcs11_bindings]]
certificate = "/etc/siguldry/kryoptic-binding.crt"
private_key = "pkcs11:token=siguldry-stg-token;type=private"
{% endif %}
# The credentials to use when connecting to the bridge and when accepting client connections
# tunneled through the bridge. Note that the certificate must have both `clientAuth` and
# `serverAuth` in its extended key usage extension.
[credentials]
private_key = "siguldry.server.private_key.pem"
certificate = "/etc/siguldry/server.crt"
ca_certificate = "/etc/siguldry/ca.crt"
# Certificates created by Siguldry allow the user to specify the subject's common name.
#
# The rest of the certificate's subject is specified here.
[certificate_subject]
country = "US"
state_or_province = "North Carolina"
locality = "Raleigh"
organization = "Red Hat, Inc."
organizational_unit = "The Fedora Project"

View file

@ -0,0 +1,11 @@
[Service]
# Tweak for lower/higher log verbosity.
# Refer to https://docs.rs/tracing-subscriber/0.3.19/tracing_subscriber/filter/struct.EnvFilter.html#directives
Environment=SIGULDRY_SERVER_LOG="WARN,siguldry=INFO"
# Unbinding keys requires access to a Yubikey
# I'm not sure there's a good way to allow _only_ the Yubikey device.
#
# TODO: also need to add access to whatever device has the Secure Boot keys.
# Also TODO: is this even the right way to expose USB devices?
DeviceAllow=/dev/bus/usb