mirror of
https://github.com/trailofbits/algo.git
synced 2025-08-14 08:43:01 +02:00
Security Hardening and Certificate Authority Constraints (#14811)
* Security hardening and certificate authority constraints This commit addresses Issues #75 and #14804 with defensive security enhancements that provide additional protection layers for edge case scenarios. ## Issue #75: Technically Constrain Root CA - Add pathlen:0 basic constraints preventing subordinate CA creation - Implement name constraints restricting certificate issuance to specific IPs - Add extended key usage restrictions limiting CA scope to VPN certificates - Separate client/server certificate extensions (serverAuth vs clientAuth) - Enhanced CA with critical constraints for defense-in-depth when CA keys saved ## Issue #14804: Comprehensive SystemD Security Hardening - WireGuard: Added systemd hardening as additional defense-in-depth - StrongSwan: Enhanced systemd configuration complementing AppArmor profiles - dnscrypt-proxy: Additional systemd security alongside AppArmor protection - Applied privilege restrictions, filesystem isolation, and system call filtering ## Technical Changes - CA certificate constraints only relevant when users opt to save CA keys - SystemD hardening provides additional isolation layers beyond existing AppArmor - Enhanced client certificate validation for iOS/macOS profiles - Reliable AppArmor profile enforcement for Ubuntu 22.04 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Address PR review feedback and improve code quality ## Fixes Based on Review Feedback: ### Handler Consistency Issues - Fix notification naming: "daemon reload" → "daemon-reload" for consistency - Update deprecated syntax: `daemon_reload: yes` → `daemon_reload: true` ### Enhanced CA Certificate Constraints - Add .mil and .int to excluded DNS domains for completeness - Add .mil and .int to excluded email domains for consistency - Add explanatory comment for openssl_constraint_random_id security purpose ## Technical Improvements: - Ensures proper handler invocation across DNS and WireGuard services - Provides more comprehensive CA name constraints protection - Documents the security rationale for UUID-based CA constraints 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Address PR review feedback - improve documentation and fix duplicate key - Add IPv6 documentation range (2001:db8::/32) to excluded ranges - Add explanatory comment for CA name constraints defense-in-depth purpose - Remove duplicate DisableMOBIKE key from iOS configuration - Add comprehensive comments to iOS/macOS mobileconfig parameters - Explain MOBIKE, redirect disabling, certificate type, and routing settings 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
9e0de205fb
commit
0aaca43019
10 changed files with 154 additions and 5 deletions
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
- name: daemon reload
|
||||
- name: daemon-reload
|
||||
systemd:
|
||||
daemon_reload: true
|
||||
|
||||
|
|
|
@ -62,3 +62,38 @@
|
|||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
notify:
|
||||
- restart dnscrypt-proxy
|
||||
|
||||
- name: Ubuntu | Apply systemd security hardening for dnscrypt-proxy
|
||||
copy:
|
||||
dest: /etc/systemd/system/dnscrypt-proxy.service.d/90-security-hardening.conf
|
||||
content: |
|
||||
# Algo VPN systemd security hardening for dnscrypt-proxy
|
||||
# Additional hardening on top of comprehensive AppArmor
|
||||
[Service]
|
||||
# Privilege restrictions
|
||||
NoNewPrivileges=yes
|
||||
|
||||
# Filesystem isolation (complements AppArmor)
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectControlGroups=yes
|
||||
|
||||
# Network restrictions
|
||||
RestrictAddressFamilies=AF_INET AF_INET6
|
||||
|
||||
# Allow access to dnscrypt-proxy cache (AppArmor also controls this)
|
||||
ReadWritePaths=/var/cache/dnscrypt-proxy
|
||||
|
||||
# System call filtering (complements AppArmor restrictions)
|
||||
SystemCallFilter=@system-service @network-io
|
||||
SystemCallFilter=~@debug @mount @swap @reboot @raw-io
|
||||
SystemCallErrorNumber=EPERM
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
notify:
|
||||
- daemon-reload
|
||||
- restart dnscrypt-proxy
|
||||
|
|
|
@ -11,7 +11,12 @@ algo_ondemand_wifi_exclude: _null
|
|||
algo_dns_adblocking: false
|
||||
ipv6_support: false
|
||||
dns_encryption: true
|
||||
# Random UUID for CA name constraints - prevents certificate reuse across different Algo deployments
|
||||
# This unique identifier ensures each CA can only issue certificates for its specific server instance
|
||||
openssl_constraint_random_id: "{{ IP_subject_alt_name | to_uuid }}.algo"
|
||||
# Subject Alternative Name (SAN) configuration - CRITICAL for client compatibility
|
||||
# Modern clients (especially macOS/iOS) REQUIRE SAN extension in server certificates
|
||||
# Without SAN, IKEv2 connections will fail with certificate validation errors
|
||||
subjectAltName_type: "{{ 'DNS' if IP_subject_alt_name|regex_search('[a-z]') else 'IP' }}"
|
||||
subjectAltName: >-
|
||||
{{ subjectAltName_type }}:{{ IP_subject_alt_name }}
|
||||
|
@ -21,12 +26,16 @@ nameConstraints: >-
|
|||
critical,permitted;{{ subjectAltName_type }}:{{ IP_subject_alt_name }}{{- '/255.255.255.255' if subjectAltName_type == 'IP' else '' -}}
|
||||
{%- if subjectAltName_type == 'IP' -%}
|
||||
,permitted;DNS:{{ openssl_constraint_random_id }}
|
||||
,excluded;DNS:.com,excluded;DNS:.org,excluded;DNS:.net,excluded;DNS:.gov,excluded;DNS:.edu,excluded;DNS:.mil,excluded;DNS:.int
|
||||
,excluded;IP:10.0.0.0/255.0.0.0,excluded;IP:172.16.0.0/255.240.0.0,excluded;IP:192.168.0.0/255.255.0.0
|
||||
{%- else -%}
|
||||
,excluded;IP:0.0.0.0/0.0.0.0
|
||||
{%- endif -%}
|
||||
,permitted;email:{{ openssl_constraint_random_id }}
|
||||
,excluded;email:.com,excluded;email:.org,excluded;email:.net,excluded;email:.gov,excluded;email:.edu,excluded;email:.mil,excluded;email:.int
|
||||
{%- if ipv6_support -%}
|
||||
,permitted;IP:{{ ansible_default_ipv6['address'] }}/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
,excluded;IP:fc00:0:0:0:0:0:0:0/fe00:0:0:0:0:0:0:0,excluded;IP:fe80:0:0:0:0:0:0:0/ffc0:0:0:0:0:0:0:0,excluded;IP:2001:db8:0:0:0:0:0:0/ffff:fff8:0:0:0:0:0:0
|
||||
{%- else -%}
|
||||
,excluded;IP:0:0:0:0:0:0:0:0/0:0:0:0:0:0:0:0
|
||||
{%- endif -%}
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
with_items:
|
||||
- "{{ users }}"
|
||||
|
||||
|
||||
- name: Build the client ipsec secret file
|
||||
template:
|
||||
src: client_ipsec.secrets.j2
|
||||
|
|
|
@ -77,12 +77,17 @@
|
|||
chdir: "{{ ipsec_pki_path }}"
|
||||
creates: serial_generated
|
||||
|
||||
# Generate server certificate with proper Subject Alternative Name (SAN)
|
||||
# CRITICAL: Must use -extensions server_exts to include SAN extension.
|
||||
# The SAN extension is required for modern certificate validation,
|
||||
# especially on macOS/iOS clients connecting via IKEv2.
|
||||
# Without SAN containing the server IP, clients will reject the certificate.
|
||||
- name: Build the server pair
|
||||
shell: >
|
||||
umask 077;
|
||||
{{ openssl_bin }} req -utf8 -new
|
||||
-newkey ec:ecparams/secp384r1.pem
|
||||
-config <(cat openssl.cnf <(printf "[basic_exts]\nsubjectAltName={{ subjectAltName }}"))
|
||||
-config openssl.cnf
|
||||
-keyout private/{{ IP_subject_alt_name }}.key
|
||||
-out reqs/{{ IP_subject_alt_name }}.req -nodes
|
||||
-passin pass:"{{ CA_password }}"
|
||||
|
@ -90,7 +95,8 @@
|
|||
{{ openssl_bin }} ca -utf8
|
||||
-in reqs/{{ IP_subject_alt_name }}.req
|
||||
-out certs/{{ IP_subject_alt_name }}.crt
|
||||
-config <(cat openssl.cnf <(printf "[basic_exts]\nsubjectAltName={{ subjectAltName }}"))
|
||||
-config openssl.cnf
|
||||
-extensions server_exts
|
||||
-days 3650 -batch
|
||||
-passin pass:"{{ CA_password }}"
|
||||
-subj "/CN={{ IP_subject_alt_name }}" &&
|
||||
|
|
|
@ -1,2 +1,24 @@
|
|||
# Algo VPN systemd security hardening for StrongSwan
|
||||
# Enhanced hardening on top of existing AppArmor
|
||||
[Service]
|
||||
MemoryLimit=16777216
|
||||
# Privilege restrictions
|
||||
NoNewPrivileges=yes
|
||||
|
||||
# Filesystem isolation (complements AppArmor)
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectControlGroups=yes
|
||||
|
||||
# Network restrictions - include IPsec kernel communication requirements
|
||||
RestrictAddressFamilies=AF_INET AF_INET6 AF_NETLINK AF_PACKET
|
||||
|
||||
# Allow access to IPsec configuration, state, and kernel interfaces
|
||||
ReadWritePaths=/etc/ipsec.d /var/lib/strongswan
|
||||
ReadOnlyPaths=/proc/net/pfkey
|
||||
|
||||
# System call filtering (complements AppArmor restrictions)
|
||||
# Allow crypto operations, remove cpu-emulation restriction for crypto algorithms
|
||||
SystemCallFilter=@system-service @network-io
|
||||
SystemCallFilter=~@debug @mount @swap @reboot
|
||||
SystemCallErrorNumber=EPERM
|
||||
|
|
|
@ -73,10 +73,13 @@
|
|||
</dict>
|
||||
<key>DeadPeerDetectionRate</key>
|
||||
<string>Medium</string>
|
||||
<!-- MOBIKE allows VPN to survive network changes (WiFi to cellular) -->
|
||||
<key>DisableMOBIKE</key>
|
||||
<integer>0</integer>
|
||||
<!-- Disable IKEv2 redirects for security -->
|
||||
<key>DisableRedirect</key>
|
||||
<integer>1</integer>
|
||||
<!-- Disable CRL checking for performance and reliability -->
|
||||
<key>EnableCertificateRevocationCheck</key>
|
||||
<integer>0</integer>
|
||||
<key>EnablePFS</key>
|
||||
|
@ -96,19 +99,24 @@
|
|||
<string>{{ item.0 }}@{{ openssl_constraint_random_id }}</string>
|
||||
<key>PayloadCertificateUUID</key>
|
||||
<string>{{ pkcs12_PayloadCertificateUUID }}</string>
|
||||
<!-- Use ECDSA P-384 certificates for strong security -->
|
||||
<key>CertificateType</key>
|
||||
<string>ECDSA384</string>
|
||||
<key>ServerCertificateIssuerCommonName</key>
|
||||
<string>{{ IP_subject_alt_name }}</string>
|
||||
<key>ServerCertificateCommonName</key>
|
||||
<string>{{ IP_subject_alt_name }}</string>
|
||||
<key>RemoteAddress</key>
|
||||
<string>{{ IP_subject_alt_name }}</string>
|
||||
<key>RemoteIdentifier</key>
|
||||
<string>{{ IP_subject_alt_name }}</string>
|
||||
<!-- Use server-provided internal IP assignment -->
|
||||
<key>UseConfigurationAttributeInternalIPSubnet</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>IPv4</key>
|
||||
<dict>
|
||||
<!-- Override primary network interface for full VPN routing -->
|
||||
<key>OverridePrimary</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
|
|
|
@ -108,9 +108,27 @@ basicConstraints = CA:FALSE
|
|||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid,issuer:always
|
||||
|
||||
extendedKeyUsage = serverAuth,clientAuth,1.3.6.1.5.5.7.3.17
|
||||
# Client certificates should not have serverAuth
|
||||
extendedKeyUsage = clientAuth,1.3.6.1.5.5.7.3.17
|
||||
keyUsage = digitalSignature, keyEncipherment
|
||||
|
||||
# Server certificate extensions
|
||||
# CRITICAL: The subjectAltName (SAN) extension is REQUIRED for modern clients,
|
||||
# especially macOS/iOS which perform strict certificate validation for IKEv2.
|
||||
# Without SAN, macOS clients will reject the certificate and fail to connect.
|
||||
# The SAN must contain the server's IP address(es) that clients connect to.
|
||||
[ server_exts ]
|
||||
basicConstraints = CA:FALSE
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid,issuer:always
|
||||
|
||||
# Server authentication for IKEv2 VPN connections
|
||||
extendedKeyUsage = serverAuth,1.3.6.1.5.5.7.3.17
|
||||
keyUsage = digitalSignature, keyEncipherment
|
||||
|
||||
# Subject Alternative Name extension
|
||||
subjectAltName = {{ subjectAltName }}
|
||||
|
||||
# The Easy-RSA CA extensions
|
||||
[ easyrsa_ca ]
|
||||
|
||||
|
@ -120,8 +138,12 @@ subjectKeyIdentifier=hash
|
|||
authorityKeyIdentifier=keyid:always,issuer:always
|
||||
|
||||
basicConstraints = critical,CA:true,pathlen:0
|
||||
# Name constraints provide defense-in-depth security by restricting the scope of certificates
|
||||
# this CA can issue, preventing misuse if the CA key is compromised
|
||||
nameConstraints = {{ nameConstraints }}
|
||||
|
||||
# Restrict CA to only sign VPN-related certificates
|
||||
extendedKeyUsage = critical,serverAuth,clientAuth,1.3.6.1.5.5.7.3.17
|
||||
|
||||
# Limit key usage to CA tasks. If you really want to use the generated pair as
|
||||
# a self-signed cert, comment this out.
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
---
|
||||
- name: daemon-reload
|
||||
systemd:
|
||||
daemon_reload: true
|
||||
|
||||
- name: restart wireguard
|
||||
service:
|
||||
name: "{{ service_name }}"
|
||||
|
|
|
@ -10,3 +10,45 @@
|
|||
set_fact:
|
||||
service_name: wg-quick@{{ wireguard_interface }}
|
||||
tags: always
|
||||
|
||||
- name: Ubuntu | Ensure that the WireGuard service directory exists
|
||||
file:
|
||||
path: /etc/systemd/system/wg-quick@{{ wireguard_interface }}.service.d/
|
||||
state: directory
|
||||
mode: 0755
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Ubuntu | Apply systemd security hardening for WireGuard
|
||||
copy:
|
||||
dest: /etc/systemd/system/wg-quick@{{ wireguard_interface }}.service.d/90-security-hardening.conf
|
||||
content: |
|
||||
# Algo VPN systemd security hardening for WireGuard
|
||||
[Service]
|
||||
# Privilege restrictions
|
||||
NoNewPrivileges=yes
|
||||
|
||||
# Filesystem isolation
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectControlGroups=yes
|
||||
|
||||
# Network restrictions - WireGuard needs NETLINK for interface management
|
||||
RestrictAddressFamilies=AF_INET AF_INET6 AF_NETLINK
|
||||
|
||||
# Allow access to WireGuard configuration
|
||||
ReadWritePaths=/etc/wireguard
|
||||
ReadOnlyPaths=/etc/resolv.conf
|
||||
|
||||
# System call filtering - allow network and system service calls
|
||||
SystemCallFilter=@system-service @network-io
|
||||
SystemCallFilter=~@debug @mount @swap @reboot @raw-io
|
||||
SystemCallErrorNumber=EPERM
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
notify:
|
||||
- daemon-reload
|
||||
- restart wireguard
|
||||
|
|
Loading…
Add table
Reference in a new issue