Introduction
NIST finalized three post-quantum cryptography (PQC) standards in 2024: ML-KEM (key encapsulation), ML-DSA (digital signatures), and SLH-DSA (stateless hash signatures). Organizations must migrate before quantum computers can break RSA and ECC. Ansible automates this migration across thousands of systems — updating crypto libraries, rotating certificates, reconfiguring SSH, and validating compliance.
PQC Standards Quick Reference
| Standard | NIST Name | Purpose | Replaces |
|---|---|---|---|
| ML-KEM (CRYSTALS-Kyber) | FIPS 203 | Key encapsulation | RSA key exchange, ECDH |
| ML-DSA (CRYSTALS-Dilithium) | FIPS 204 | Digital signatures | RSA signatures, ECDSA |
| SLH-DSA (SPHINCS+) | FIPS 205 | Hash-based signatures | RSA, ECDSA (conservative) |
Crypto Inventory Audit
First, discover what crypto is deployed:
---
- name: PQC readiness audit
hosts: all
become: true
tasks:
- name: Check OpenSSL version
ansible.builtin.command: openssl version
register: openssl_ver
changed_when: false
- name: Check SSH key types
ansible.builtin.shell: |
for key in /etc/ssh/ssh_host_*_key.pub; do
echo "$(ssh-keygen -lf $key) $key"
done
register: ssh_keys
changed_when: false
- name: Find all TLS certificates
ansible.builtin.find:
paths:
- /etc/ssl/certs
- /etc/pki/tls/certs
- /etc/nginx/ssl
- /etc/letsencrypt/live
patterns: "*.pem,*.crt"
recurse: true
register: certs_found
- name: Check certificate algorithms
ansible.builtin.shell: >
openssl x509 -in {{ item.path }} -noout -text 2>/dev/null |
grep -E "Signature Algorithm|Public Key Algorithm" | head -2
loop: "{{ certs_found.files }}"
register: cert_algos
changed_when: false
- name: Generate crypto inventory report
ansible.builtin.template:
src: crypto-audit.j2
dest: /tmp/crypto-audit-{{ inventory_hostname }}.txt
delegate_to: localhost
- name: Flag vulnerable algorithms
ansible.builtin.debug:
msg: >
⚠️ {{ inventory_hostname }}: Found {{ item.stdout_lines | select('search', 'RSA|ECDSA|sha256WithRSA') | list | length }}
certificates using pre-quantum algorithms
loop: "{{ cert_algos.results }}"
when: item.stdout is search('RSA|ECDSA')
Update Crypto Libraries
- name: Deploy PQC-ready crypto libraries
hosts: all
become: true
vars:
oqs_openssl_version: "3.2.0"
tasks:
- name: Update OpenSSL to PQC-capable version
ansible.builtin.package:
name: "{{ openssl_package }}"
state: latest
vars:
openssl_package: "{{ 'openssl' if ansible_os_family == 'Debian' else 'openssl' }}"
- name: Install liboqs (Open Quantum Safe)
block:
- name: Install build dependencies
ansible.builtin.package:
name:
- cmake
- gcc
- ninja-build
- libssl-dev
state: present
- name: Clone liboqs
ansible.builtin.git:
repo: https://github.com/open-quantum-safe/liboqs.git
dest: /opt/liboqs
version: "main"
- name: Build liboqs
ansible.builtin.shell: |
cd /opt/liboqs
mkdir -p build && cd build
cmake -GNinja -DBUILD_SHARED_LIBS=ON ..
ninja
ninja install
args:
creates: /usr/local/lib/liboqs.so
- name: Install OQS provider for OpenSSL
ansible.builtin.git:
repo: https://github.com/open-quantum-safe/oqs-provider.git
dest: /opt/oqs-provider
version: "main"
- name: Build OQS provider
ansible.builtin.shell: |
cd /opt/oqs-provider
mkdir -p build && cd build
cmake -DOPENSSL_ROOT_DIR=/usr -DCMAKE_PREFIX_PATH=/usr/local ..
make
make install
args:
creates: /usr/local/lib/ossl-modules/oqsprovider.so
- name: Enable OQS provider in OpenSSL config
ansible.builtin.blockinfile:
path: /etc/ssl/openssl.cnf
marker: "# {mark} ANSIBLE MANAGED - OQS PROVIDER"
insertafter: "\\[openssl_init\\]"
block: |
providers = provider_sect
[provider_sect]
default = default_sect
oqsprovider = oqs_sect
[default_sect]
activate = 1
[oqs_sect]
activate = 1
module = /usr/local/lib/ossl-modules/oqsprovider.so
Migrate SSH to Hybrid PQC
- name: Configure SSH for post-quantum
hosts: all
become: true
tasks:
- name: Generate ML-KEM hybrid SSH host key
ansible.builtin.command: >
ssh-keygen -t ml-kem-768-x25519
-f /etc/ssh/ssh_host_mlkem_key -N ""
args:
creates: /etc/ssh/ssh_host_mlkem_key
- name: Configure sshd for hybrid key exchange
ansible.builtin.blockinfile:
path: /etc/ssh/sshd_config
marker: "# {mark} ANSIBLE MANAGED - PQC"
block: |
# Post-quantum hybrid key exchange
KexAlgorithms mlkem768x25519-sha256,curve25519-sha256,curve25519-sha256@libssh.org
# Host key algorithms (PQC + classical fallback)
HostKeyAlgorithms ssh-ml-dsa-65,ssh-ed25519,rsa-sha2-512
# Accept PQC and classical keys
PubkeyAcceptedAlgorithms ssh-ml-dsa-65,ssh-ed25519,rsa-sha2-512
notify: Restart sshd
- name: Configure SSH client for PQC
ansible.builtin.blockinfile:
path: /etc/ssh/ssh_config
marker: "# {mark} ANSIBLE MANAGED - PQC"
block: |
Host *
KexAlgorithms mlkem768x25519-sha256,curve25519-sha256
handlers:
- name: Restart sshd
ansible.builtin.service:
name: sshd
state: restarted
Deploy PQC TLS Certificates
- name: Generate hybrid PQC TLS certificates
hosts: webservers
become: true
vars:
pqc_cert_dir: /etc/ssl/pqc
tasks:
- name: Create PQC certificate directory
ansible.builtin.file:
path: "{{ pqc_cert_dir }}"
state: directory
mode: "0700"
- name: Generate ML-DSA CA key and certificate
ansible.builtin.shell: |
openssl genpkey -algorithm ml-dsa-65 -out {{ pqc_cert_dir }}/ca-key.pem
openssl req -new -x509 -key {{ pqc_cert_dir }}/ca-key.pem \
-out {{ pqc_cert_dir }}/ca-cert.pem \
-days 3650 -subj "/CN=PQC Internal CA"
args:
creates: "{{ pqc_cert_dir }}/ca-cert.pem"
- name: Generate server hybrid certificate
ansible.builtin.shell: |
# Generate hybrid key (ML-DSA + ECDSA)
openssl genpkey -algorithm ml-dsa-65 -out {{ pqc_cert_dir }}/server-key.pem
# Create CSR
openssl req -new -key {{ pqc_cert_dir }}/server-key.pem \
-out {{ pqc_cert_dir }}/server.csr \
-subj "/CN={{ inventory_hostname }}"
# Sign with PQC CA
openssl x509 -req -in {{ pqc_cert_dir }}/server.csr \
-CA {{ pqc_cert_dir }}/ca-cert.pem \
-CAkey {{ pqc_cert_dir }}/ca-key.pem \
-CAcreateserial \
-out {{ pqc_cert_dir }}/server-cert.pem \
-days 365
args:
creates: "{{ pqc_cert_dir }}/server-cert.pem"
- name: Configure Nginx with hybrid TLS
ansible.builtin.template:
src: nginx-pqc.conf.j2
dest: /etc/nginx/conf.d/pqc.conf
notify: Reload Nginx
handlers:
- name: Reload Nginx
ansible.builtin.service:
name: nginx
state: reloaded
PQC Compliance Validation
- name: Validate PQC compliance
hosts: all
become: true
tasks:
- name: Verify SSH uses PQC key exchange
ansible.builtin.shell: >
ssh -vv localhost 2>&1 | grep -i "kex:" | head -1
register: ssh_kex
changed_when: false
failed_when: "'mlkem' not in ssh_kex.stderr"
- name: Check TLS supports PQC
ansible.builtin.shell: >
openssl s_client -connect localhost:443 2>/dev/null |
grep "Server Temp Key"
register: tls_key
changed_when: false
when: "'webservers' in group_names"
- name: Verify no RSA-only certificates remain
ansible.builtin.shell: |
find /etc/ssl /etc/pki -name "*.pem" -exec \
openssl x509 -in {} -noout -text 2>/dev/null \; |
grep "Public Key Algorithm" | sort | uniq -c
register: algo_report
changed_when: false
- name: Generate compliance report
ansible.builtin.template:
src: pqc-compliance.j2
dest: "/tmp/pqc-compliance-{{ inventory_hostname }}.json"
delegate_to: localhost
Migration Roadmap Playbook
---
# Phase 1: Audit (run immediately)
- import_playbook: pqc-audit.yml
tags: [audit]
# Phase 2: Libraries (deploy PQC-capable crypto)
- import_playbook: pqc-libraries.yml
tags: [libraries]
# Phase 3: SSH (hybrid PQC key exchange)
- import_playbook: pqc-ssh.yml
tags: [ssh]
# Phase 4: TLS (hybrid certificates)
- import_playbook: pqc-tls.yml
tags: [tls]
# Phase 5: Validate (compliance check)
- import_playbook: pqc-validate.yml
tags: [validate]
# Run audit first
ansible-playbook site.yml --tags audit
# Deploy libraries
ansible-playbook site.yml --tags libraries
# Full migration
ansible-playbook site.yml
Related Articles
Conclusion
Post-quantum cryptography migration is a fleet-wide operation — exactly what Ansible excels at. Start with a crypto audit to inventory all RSA/ECC usage. Deploy PQC-capable libraries (liboqs, OQS provider). Migrate SSH to hybrid ML-KEM key exchange. Deploy hybrid TLS certificates. Validate compliance across the fleet. The key strategy for 2026: use hybrid mode (PQC + classical) during transition so nothing breaks, while becoming quantum-resistant. Ansible makes this repeatable across thousands of hosts.