Files
algo/roles/strongswan/tasks/openssl.yml
T
454faa96b1 fix: Prevent sensitive information from being logged (#14779)
* fix: Add no_log to tasks handling sensitive information

- Add no_log: true to OpenSSL commands that contain passwords/passphrases
- Add no_log: true to WireGuard key generation commands
- Add no_log: true to password/CA password generation tasks
- Add no_log: true to AWS credential handling tasks
- Add no_log: true to QR code generation that contains full configs

This prevents sensitive information like passwords, private keys, and
WireGuard configurations from being logged to syslog/journald.

Fixes #1617

* feat: Comprehensive privacy enhancements

- Add no_log directives to all cloud provider credential handling
- Set privacy-focused defaults (StrongSwan logging disabled, DNSCrypt syslog off)
- Implement privacy role with log rotation, history clearing, and log filtering
- Add Privacy Considerations section to README
- Make all privacy features configurable and enabled by default

This update significantly reduces Algo's logging footprint to enhance user privacy
while maintaining the ability to enable logging for debugging when needed.

* docs: Move privacy documentation from README to FAQ

- Remove Privacy Considerations section from README
- Add expanded 'Does Algo support zero logging?' question to FAQ
- Better placement alongside existing logging/monitoring questions
- More detailed explanation of privacy features and limitations

* fix: Remove invalid 'bool' filter from Jinja2 template

The privacy-monitor.sh.j2 template was using '| bool' which is not a valid
Jinja2 filter. The 'bool' is a built-in Python function, not a Jinja2 filter.

Fixed by removing the '| bool' filter and directly outputting the boolean
variables as they will be rendered correctly by Jinja2.

This resolves the template syntax error that was causing CI tests to fail:
"No filter named 'bool'" error in privacy monitoring script template.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix YAML linting issues in privacy role

* Fix linting warnings: shellcheck and ansible-lint issues

- Fixed all shellcheck warnings in test scripts:
  - Quoted variables to prevent word splitting
  - Replaced A && B || C constructs with proper if-then-else
  - Changed unused loop variable to _
  - Added shellcheck directives for FreeBSD rc.d script

- Fixed ansible-lint risky-file-permissions warnings:
  - Added explicit file permissions for sensitive files (mode 0600)
  - Added permissions for config files and certificates (mode 0644)
  - Set proper permissions for directories (mode 0755)

- Fixed yamllint compatibility with ansible-lint:
  - Added required octal-values configuration
  - Quoted all octal mode values to prevent YAML misinterpretation
  - Added comments-indentation: false as required

All tests pass and functionality remains unchanged.

* Remove algo.egg-info from version control

This directory is generated by Python package tools (pip/setuptools) and
should not be tracked in git. It's already listed in .gitignore but was
accidentally committed. The directory contains build metadata that is
regenerated when the package is installed.

* Restructure privacy documentation for clarity

- Simplified FAQ entry to be concise with link to README for details
- Added comprehensive Privacy and Logging section to README
- Clarified what IS logged by default vs what is not
- Explained two separate privacy settings (strongswan_log_level and privacy_enhancements_enabled)
- Added clear debugging instructions (need to change both settings)
- Removed confusing language about "enabling additional features"
- Made documentation more natural and less AI-generated sounding

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix Ubuntu 22.04 iptables deployment issues and simplify config.cfg

Issues fixed:
1. Added base 'iptables' package to batch installation list (was missing, only iptables-persistent was included)
2. Fixed alternatives configuration for Ubuntu 22.04+ - only configure main iptables/ip6tables alternatives, not save/restore (they're handled as slaves)

Config.cfg improvements:
- Reduced from 308 to 198 lines (35% reduction)
- Moved privacy settings above "Advanced users only" line for better accessibility
- Clarified algo_no_log is for Ansible output, not server privacy
- Simplified verbose comments throughout
- Moved experimental performance options to commented section at end
- Better organized into logical sections

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Add privacy features to README and improve feature descriptions

- Added privacy-focused feature bullet highlighting minimal logging and privacy enhancements
- Simplified IKEv2 bullet (removed redundant platform list)
- Updated helper scripts description to be more comprehensive
- Specified Ubuntu 22.04 LTS and automatic security updates
- Made feature list more concise and accurate

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix logrotate duplicate entries error in privacy role

The privacy role was creating logrotate configs that duplicated the default
Ubuntu rsyslog logrotate rules, causing deployment failures with errors like
'duplicate log entry for /var/log/syslog'.

Changes:
- Disable default rsyslog logrotate config before applying privacy configs
- Consolidate system log rotation into single config file
- Add missingok flag to handle logs that may not exist on all systems
- Remove forced immediate rotation that was triggering the error

This ensures privacy-enhanced log rotation works without conflicts.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix 'history: not found' error in privacy role

The 'history -c' command was failing because history is a bash built-in
that doesn't exist in /bin/sh (Ubuntu's default shell for scripts).

Changes:
- Removed the 'Clear current session history' task since it's ineffective
  in Ansible context (each task runs in a new shell)
- History files are already cleared by the existing file removal tasks
- Added explanatory comment about why session history clearing is omitted

This fixes the deployment failure while maintaining all effective history
clearing functionality.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix BPF JIT sysctl error in privacy role

The net.core.bpf_jit_enable sysctl parameter was failing on some systems
because BPF JIT support is not available in all kernel configurations.

Changes:
- Separated BPF JIT setting into its own task with ignore_errors
- Made BPF JIT disabling optional since it's not critical for privacy
- Added explanatory comments about kernel support variability
- Both runtime sysctl and persistent config now handle missing parameter

This allows deployments to succeed on systems without BPF JIT support
while still applying the setting where available.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-08-17 15:58:19 -04:00

286 lines
11 KiB
YAML

---
- block:
- debug: var=subjectAltName
- name: Ensure the pki directory does not exist
file:
dest: "{{ ipsec_pki_path }}"
state: absent
when: keys_clean_all|bool
- name: Ensure the pki directories exist
file:
dest: "{{ ipsec_pki_path }}/{{ item }}"
state: directory
recurse: true
mode: "0700"
with_items:
- certs
- private
- public
- name: Ensure the config directories exist
file:
dest: "{{ ipsec_config_path }}/{{ item }}"
state: directory
recurse: true
mode: "0700"
with_items:
- apple
- manual
- name: Create private key with password protection
community.crypto.openssl_privatekey:
path: "{{ ipsec_pki_path }}/private/cakey.pem"
passphrase: "{{ CA_password }}"
type: ECC
curve: secp384r1
mode: "0600"
no_log: true
# CA certificate with name constraints to prevent certificate misuse (Issue #75)
- name: Create certificate signing request (CSR) for CA certificate with security constraints
community.crypto.openssl_csr_pipe:
privatekey_path: "{{ ipsec_pki_path }}/private/cakey.pem"
privatekey_passphrase: "{{ CA_password }}"
common_name: "{{ IP_subject_alt_name }}"
use_common_name_for_san: true
# Generate Subject Key Identifier for proper Authority Key Identifier creation
create_subject_key_identifier: true
basic_constraints:
- 'CA:TRUE'
- 'pathlen:0' # Prevents sub-CA creation - limits certificate chain depth if CA key compromised
basic_constraints_critical: true
key_usage:
- keyCertSign
- cRLSign
key_usage_critical: true
# CA restricted to VPN certificate issuance only
extended_key_usage:
- '1.3.6.1.5.5.7.3.17' # IPsec End Entity OID - VPN-specific usage
extended_key_usage_critical: true
# Name Constraints: Defense-in-depth security restricting certificate scope to prevent misuse
# Limits CA to only issue certificates for this specific VPN deployment's resources
# Per-deployment UUID prevents cross-deployment reuse, unique email domain isolates certificate scope
name_constraints_permitted: >-
{{ [
subjectAltName_type + ':' + IP_subject_alt_name + ('/255.255.255.255' if subjectAltName_type == 'IP' else ''),
'DNS:' + openssl_constraint_random_id,
'email:' + openssl_constraint_random_id
] + (
['IP:' + ansible_default_ipv6['address'] + '/128'] if ipv6_support else []
) }}
# Block public domains/networks to prevent certificate abuse for impersonation attacks
# Public TLD exclusion, Email domain exclusion, RFC 1918: prevents lateral movement
# IPv6: ULA/link-local/doc ranges or all
name_constraints_excluded: >-
{{ [
'DNS:.com', 'DNS:.org', 'DNS:.net', 'DNS:.gov', 'DNS:.edu', 'DNS:.mil', 'DNS:.int',
'email:.com', 'email:.org', 'email:.net', 'email:.gov', 'email:.edu', 'email:.mil', 'email:.int',
'IP:10.0.0.0/255.0.0.0', 'IP:172.16.0.0/255.240.0.0', 'IP:192.168.0.0/255.255.0.0'
] + (
['IP:fc00::/7', 'IP:fe80::/10', 'IP:2001:db8::/32'] if ipv6_support else ['IP:::/0']
) }}
name_constraints_critical: true
register: ca_csr
- name: Create self-signed CA certificate from CSR
community.crypto.x509_certificate:
path: "{{ ipsec_pki_path }}/cacert.pem"
csr_content: "{{ ca_csr.csr }}"
privatekey_path: "{{ ipsec_pki_path }}/private/cakey.pem"
privatekey_passphrase: "{{ CA_password }}"
provider: selfsigned
mode: "0644"
no_log: true
- name: Copy the CA certificate
copy:
src: "{{ ipsec_pki_path }}/cacert.pem"
dest: "{{ ipsec_config_path }}/manual/cacert.pem"
mode: '0644'
- name: Create private keys for users and server
community.crypto.openssl_privatekey:
path: "{{ ipsec_pki_path }}/private/{{ item }}.key"
type: ECC
curve: secp384r1
mode: "0600"
with_items:
- "{{ users }}"
- "{{ IP_subject_alt_name }}"
register: client_key_jobs
# Server certificate with SAN extension - required for modern Apple devices
- name: Create CSRs for server certificate with SAN
community.crypto.openssl_csr_pipe:
privatekey_path: "{{ ipsec_pki_path }}/private/{{ IP_subject_alt_name }}.key"
subject_alt_name: "{{ subjectAltName.split(',') }}"
common_name: "{{ IP_subject_alt_name }}"
# Add Basic Constraints to prevent certificate chain validation errors
basic_constraints:
- 'CA:FALSE'
basic_constraints_critical: false
key_usage:
- digitalSignature
- keyEncipherment
key_usage_critical: false
# Server auth EKU required for IKEv2 server certificates (Issue #75)
# NOTE: clientAuth deliberately excluded to prevent role confusion attacks
extended_key_usage:
- serverAuth # Server Authentication (RFC 5280)
- '1.3.6.1.5.5.7.3.17' # IPsec End Entity (RFC 4945)
extended_key_usage_critical: false
register: server_csr
- name: Create CSRs for client certificates
community.crypto.openssl_csr_pipe:
privatekey_path: "{{ ipsec_pki_path }}/private/{{ item }}.key"
subject_alt_name:
- "email:{{ item }}@{{ openssl_constraint_random_id }}" # UUID domain prevents certificate reuse across deployments
common_name: "{{ item }}"
# Add Basic Constraints to client certificates for proper PKI validation
basic_constraints:
- 'CA:FALSE'
basic_constraints_critical: false
key_usage:
- digitalSignature
- keyEncipherment
key_usage_critical: false
# Client certs restricted to clientAuth only - prevents clients from impersonating the VPN server
# NOTE: serverAuth deliberately excluded to prevent server impersonation attacks
extended_key_usage:
- clientAuth # Client Authentication (RFC 5280)
- '1.3.6.1.5.5.7.3.17' # IPsec End Entity (RFC 4945)
extended_key_usage_critical: false
with_items: "{{ users }}"
register: client_csr_jobs
- name: Sign server certificate with CA
community.crypto.x509_certificate:
csr_content: "{{ server_csr.csr }}"
path: "{{ ipsec_pki_path }}/certs/{{ IP_subject_alt_name }}.crt"
provider: ownca
ownca_path: "{{ ipsec_pki_path }}/cacert.pem"
ownca_privatekey_path: "{{ ipsec_pki_path }}/private/cakey.pem"
ownca_privatekey_passphrase: "{{ CA_password }}"
ownca_not_after: "+{{ certificate_validity_days }}d"
ownca_not_before: "-1d"
mode: "0644"
no_log: true
- name: Sign client certificates with CA
community.crypto.x509_certificate:
csr_content: "{{ item.csr }}"
path: "{{ ipsec_pki_path }}/certs/{{ item.item }}.crt"
provider: ownca
ownca_path: "{{ ipsec_pki_path }}/cacert.pem"
ownca_privatekey_path: "{{ ipsec_pki_path }}/private/cakey.pem"
ownca_privatekey_passphrase: "{{ CA_password }}"
ownca_not_after: "+{{ certificate_validity_days }}d"
ownca_not_before: "-1d"
mode: "0644"
with_items: "{{ client_csr_jobs.results }}"
register: client_sign_results
no_log: true
- name: Generate p12 files
community.crypto.openssl_pkcs12:
path: "{{ ipsec_pki_path }}/private/{{ item }}.p12"
friendly_name: "{{ item }}"
privatekey_path: "{{ ipsec_pki_path }}/private/{{ item }}.key"
certificate_path: "{{ ipsec_pki_path }}/certs/{{ item }}.crt"
passphrase: "{{ p12_export_password }}"
mode: "0600"
encryption_level: "compatibility2022" # Apple device compatibility
with_items: "{{ users }}"
no_log: true
- name: Generate p12 files with CA certificate included
community.crypto.openssl_pkcs12:
path: "{{ ipsec_pki_path }}/private/{{ item }}_ca.p12"
friendly_name: "{{ item }}"
privatekey_path: "{{ ipsec_pki_path }}/private/{{ item }}.key"
certificate_path: "{{ ipsec_pki_path }}/certs/{{ item }}.crt"
other_certificates:
- "{{ ipsec_pki_path }}/cacert.pem"
passphrase: "{{ p12_export_password }}"
mode: "0600"
encryption_level: "compatibility2022" # Apple device compatibility
with_items: "{{ users }}"
no_log: true
- name: Copy the p12 certificates
copy:
src: "{{ ipsec_pki_path }}/private/{{ item }}.p12"
dest: "{{ ipsec_config_path }}/manual/{{ item }}.p12"
mode: '0600'
with_items:
- "{{ users }}"
- name: Build openssh public keys
community.crypto.openssl_publickey:
path: "{{ ipsec_pki_path }}/public/{{ item }}.pub"
privatekey_path: "{{ ipsec_pki_path }}/private/{{ item }}.key"
format: OpenSSH
with_items: "{{ users }}"
- name: Add all users to the file
ansible.builtin.lineinfile:
path: "{{ ipsec_pki_path }}/all-users"
line: "{{ item }}"
mode: '0644'
create: true
with_items: "{{ users }}"
register: users_file
- name: Set all users as a fact
set_fact:
all_users: "{{ lookup('file', ipsec_pki_path + '/all-users').splitlines() }}"
# Certificate Revocation List (CRL) for removed users
- name: Calculate current timestamp for CRL
set_fact:
crl_timestamp: "{{ '%Y%m%d%H%M%SZ' | strftime(ansible_date_time.epoch | int) }}"
- name: Identify users whose certificates need revocation
set_fact:
users_to_revoke: "{{ all_users | difference(users) }}"
- name: Build revoked certificates list
set_fact:
revoked_certificates: >-
{{ users_to_revoke | map('regex_replace', '^(.*)$',
'{"path": "' + ipsec_pki_path + '/certs/\1.crt", "revocation_date": "' + crl_timestamp + '"}') | list }}
- name: Generate a CRL
community.crypto.x509_crl:
path: "{{ ipsec_pki_path }}/crl.pem"
privatekey_path: "{{ ipsec_pki_path }}/private/cakey.pem"
privatekey_passphrase: "{{ CA_password }}"
last_update: "{{ '%Y%m%d%H%M%SZ' | strftime(ansible_date_time.epoch | int) }}"
next_update: "{{ '%Y%m%d%H%M%SZ' | strftime((ansible_date_time.epoch | int) + (10 * 365 * 24 * 60 * 60)) }}"
crl_mode: generate
issuer:
CN: "{{ IP_subject_alt_name }}"
revoked_certificates: "{{ revoked_certificates }}"
no_log: true
- name: Set CRL file permissions
file:
path: "{{ ipsec_pki_path }}/crl.pem"
mode: "0644"
delegate_to: localhost
become: false
vars:
ansible_python_interpreter: "{{ ansible_playbook_python }}"
certificate_validity_days: 3650 # 10 years - configurable certificate lifespan
- name: Copy the CRL to the vpn server
copy:
src: "{{ ipsec_pki_path }}/crl.pem"
dest: "{{ config_prefix | default('/') }}etc/ipsec.d/crls/algo.root.pem"
mode: '0644'
notify:
- rereadcrls