diff --git a/.yamllint b/.yamllint index 523a0ecef..ee7463e65 100644 --- a/.yamllint +++ b/.yamllint @@ -17,6 +17,10 @@ rules: level: warning comments: min-spaces-from-content: 1 + comments-indentation: false + octal-values: + forbid-implicit-octal: true + forbid-explicit-octal: true braces: max-spaces-inside: 1 truthy: diff --git a/README.md b/README.md index a19ec010a..1199c5b59 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,15 @@ See our [release announcement](https://blog.trailofbits.com/2016/12/12/meet-algo ## Features -* Supports only IKEv2 with strong crypto (AES-GCM, SHA2, and P-256) for iOS, macOS, and Linux +* Supports only IKEv2 with strong crypto (AES-GCM, SHA2, and P-256) for iOS, MacOS, and Linux * Supports [WireGuard](https://www.wireguard.com/) for all of the above, in addition to Android and Windows 11 * Generates .conf files and QR codes for iOS, macOS, Android, and Windows WireGuard clients * Generates Apple profiles to auto-configure iOS and macOS devices for IPsec - no client software required -* Includes a helper script to add and remove users +* Includes helper scripts to add, remove, and manage users * Blocks ads with a local DNS resolver (optional) * Sets up limited SSH users for tunneling traffic (optional) -* Based on current versions of Ubuntu and strongSwan +* Privacy-focused with minimal logging, automatic log rotation, and configurable privacy enhancements +* Based on Ubuntu 22.04 LTS with automatic security updates * Installs to DigitalOcean, Amazon Lightsail, Amazon EC2, Vultr, Microsoft Azure, Google Compute Engine, Scaleway, OpenStack, CloudStack, Hetzner Cloud, Linode, or [your own Ubuntu server (for advanced users)](docs/deploy-to-ubuntu.md) ## Anti-features @@ -175,6 +176,33 @@ To add or remove users, first edit the `users` list in your `config.cfg` file. A After the process completes, new configuration files will be generated in the `configs` directory for any new users. The Algo VPN server will be updated to contain only the users listed in the `config.cfg` file. Removed users will no longer be able to connect, and new users will have fresh certificates and configuration files ready for use. +## Privacy and Logging + +Algo takes a pragmatic approach to privacy. By default, we minimize logging while maintaining enough information for security and troubleshooting. + +What IS logged by default: +* System security events (failed SSH attempts, firewall blocks, system updates) +* Kernel messages and boot diagnostics (with reduced verbosity) +* WireGuard client state (visible via `sudo wg` - shows last endpoint and handshake time) +* Basic service status (service starts/stops/errors) +* All logs automatically rotate and delete after 7 days + +Privacy is controlled by two main settings in `config.cfg`: +* `strongswan_log_level: -1` - Controls StrongSwan connection logging (-1 = disabled, 2 = debug) +* `privacy_enhancements_enabled: true` - Master switch for log rotation, history clearing, log filtering, and cleanup + +To enable full debugging when troubleshooting, set both `strongswan_log_level: 2` and `privacy_enhancements_enabled: false`. This will capture detailed connection logs and disable all privacy features. Remember to revert these changes after debugging. + +After deployment, verify your privacy settings: +```bash +ssh -F configs//ssh_config +sudo /usr/local/bin/privacy-monitor.sh +``` + +Perfect privacy is impossible with any VPN solution. Your cloud provider sees and logs network traffic metadata regardless of your server configuration. And of course, your ISP knows you're connecting to a VPN server, even if they can't see what you're doing through it. + +For the highest level of privacy, treat your Algo servers as disposable. Spin up a new instance when you need it, use it for your specific purpose, then destroy it completely. The ephemeral nature of cloud infrastructure can be a privacy feature if you use it intentionally. + ## Additional Documentation * [FAQ](docs/faq.md) * [Troubleshooting](docs/troubleshooting.md) diff --git a/config.cfg b/config.cfg index 30b142c0f..ddf80f51e 100644 --- a/config.cfg +++ b/config.cfg @@ -12,106 +12,61 @@ users: ### Review these options BEFORE you run Algo, as they are very difficult/impossible to change after the server is deployed. -# Performance optimizations (reduces deployment time) -# Skip reboots unless kernel was updated (saves 0-5 minutes) -performance_skip_optional_reboots: false -# Use parallel key generation for certificates (saves 1-2 minutes) -performance_parallel_crypto: false -# Batch install all packages in one operation (saves 30-60 seconds) -performance_parallel_packages: false -# Pre-install universal packages via cloud-init (saves 30-90 seconds) -performance_preinstall_packages: false -# Configure VPN services in parallel (saves 1-2 minutes) -performance_parallel_services: false - -# Change default SSH port for the cloud roles only -# It doesn't apply if you deploy to your existing Ubuntu Server +# SSH port for cloud deployments (doesn't apply to existing Ubuntu servers) ssh_port: 4160 -# Deploy StrongSwan to enable IPsec support +# VPN protocols to deploy ipsec_enabled: true - -# Deploy WireGuard -# WireGuard will listen on 51820/UDP. You might need to change to another port -# if your network blocks this one. Be aware that 53/UDP (DNS) is blocked on some -# mobile data networks. wireguard_enabled: true -wireguard_port: 51820 +wireguard_port: 51820 # Change if blocked by your network (avoid 53/UDP) -# This feature allows you to configure the Algo server to send outbound traffic -# through a different external IP address than the one you are establishing the VPN connection with. -# More info https://trailofbits.github.io/algo/cloud-alternative-ingress-ip.html -# Available for the following cloud providers: -# - DigitalOcean +# Use different IP for outbound traffic (DigitalOcean only) alternative_ingress_ip: false -# Reduce the MTU of the VPN tunnel -# Some cloud and internet providers use a smaller MTU (Maximum Transmission -# Unit) than the normal value of 1500 and if you don't reduce the MTU of your -# VPN tunnel some network connections will hang. Algo will attempt to set this -# automatically based on your server, but if connections hang you might need to -# adjust this yourself. -# See: https://github.com/trailofbits/algo/blob/master/docs/troubleshooting.md#various-websites-appear-to-be-offline-through-the-vpn +# Reduce MTU if connections hang (0 = auto-detect) +# See: docs/troubleshooting.md#various-websites-appear-to-be-offline-through-the-vpn reduce_mtu: 0 -# Algo will use the following lists to block ads. You can add new block lists -# after deployment by modifying the line starting "BLOCKLIST_URLS=" at: -# /usr/local/sbin/adblock.sh -# If you load very large blocklists, you may also have to modify resource limits: -# /etc/systemd/system/dnsmasq.service.d/100-CustomLimitations.conf +# Ad blocking lists (modify /usr/local/sbin/adblock.sh after deployment to add more) adblock_lists: - - "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" + - "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" -# Enable DNS encryption. -# If 'false', 'dns_servers' should be specified below. -# DNS encryption can not be disabled if DNS adblocking is enabled +# DNS encryption (required if using ad blocking) dns_encryption: true -# Block traffic between connected clients. Change this to false to enable -# connected clients to reach each other, as well as other computers on the -# same LAN as your Algo server (i.e. the "road warrior" setup). In this -# case, you may also want to enable SMB/CIFS and NETBIOS traffic below. +# Client isolation (set false for "road warrior" setup where clients can reach each other) BetweenClients_DROP: true +block_smb: true # Block SMB/CIFS traffic +block_netbios: true # Block NETBIOS traffic -# Block SMB/CIFS traffic -block_smb: true - -# Block NETBIOS traffic -block_netbios: true - -# Your Algo server will automatically install security updates. Some updates -# require a reboot to take effect but your Algo server will not reboot itself -# automatically unless you change 'enabled' below from 'false' to 'true', in -# which case a reboot will take place if necessary at the time specified (as -# HH:MM) in the time zone of your Algo server. The default time zone is UTC. +# Automatic reboot for security updates (time in server's timezone, default UTC) unattended_reboot: enabled: false time: 06:00 +### Privacy Settings ### +# StrongSwan connection logging (-1 = disabled, 2 = debug) +strongswan_log_level: -1 + +# Master switch for privacy enhancements (log rotation, history clearing, etc.) +# Set to false for debugging. For advanced privacy options, see roles/privacy/defaults/main.yml +privacy_enhancements_enabled: true + ### Advanced users only below this line ### -# DNS servers which will be used if 'dns_encryption' is 'true'. Multiple -# providers may be specified, but avoid mixing providers that filter results -# (like Cisco) with those that don't (like Cloudflare) or you could get -# inconsistent results. The list of available public providers can be found -# here: -# https://github.com/DNSCrypt/dnscrypt-resolvers/blob/master/v2/public-resolvers.md +# DNSCrypt providers (see https://github.com/DNSCrypt/dnscrypt-resolvers/blob/master/v2/public-resolvers.md) dnscrypt_servers: ipv4: - cloudflare # - google -# - # E.g., if using NextDNS, this will be something like NextDNS-abc123. - # You must also fill in custom_server_stamps below. You may specify - # multiple custom servers. +# - YourCustomServer # For NextDNS etc., add stamp below ipv6: - cloudflare-ipv6 custom_server_stamps: # YourCustomServer: 'sdns://...' -# DNS servers which will be used if 'dns_encryption' is 'false'. -# Fallback resolvers for systemd-resolved -# The default is to use Cloudflare. +# DNS servers when encryption is disabled dns_servers: ipv4: - 1.1.1.1 @@ -120,37 +75,36 @@ dns_servers: - 2606:4700:4700::1111 - 2606:4700:4700::1001 -# Store the PKI in a ram disk. Enabled only if store_pki (retain the PKI) is set to false -# Supports on MacOS and Linux only (including Windows Subsystem for Linux) +# Store PKI in RAM disk when not retaining (MacOS/Linux only) pki_in_tmpfs: true -# Set this to 'true' when running './algo update-users' if you want ALL users to get new certs, not just new users. +# Regenerate ALL user certs on update-users (not just new users) keys_clean_all: false -# StrongSwan log level -# https://wiki.strongswan.org/projects/strongswan/wiki/LoggerConfiguration -strongswan_log_level: 2 - -# rightsourceip for ipsec -# ipv4 +### VPN Network Configuration ### strongswan_network: 10.48.0.0/16 -# ipv6 strongswan_network_ipv6: '2001:db8:4160::/48' -# If you're behind NAT or a firewall and you want to receive incoming connections long after network traffic has gone silent. -# This option will keep the "connection" open in the eyes of NAT. -# See: https://www.wireguard.com/quickstart/#nat-and-firewall-traversal-persistence -wireguard_PersistentKeepalive: 0 - -# WireGuard network configuration wireguard_network_ipv4: 10.49.0.0/16 wireguard_network_ipv6: 2001:db8:a160::/48 +# Keep NAT connections alive (0 = disabled) +wireguard_PersistentKeepalive: 0 + +### Experimental Performance Options ### +# These are experimental and may cause issues. Enable at your own risk. +# performance_skip_optional_reboots: false # Skip non-kernel reboots +# performance_parallel_crypto: false # Parallel key generation +# performance_parallel_packages: false # Batch package installation +# performance_preinstall_packages: false # Pre-install via cloud-init +# performance_parallel_services: false # Configure VPN services in parallel + # Randomly generated IP address for the local dns resolver local_service_ip: "{{ '172.16.0.1' | ansible.utils.ipmath(1048573 | random(seed=algo_server_name + ansible_fqdn)) }}" local_service_ipv6: "{{ 'fd00::1' | ansible.utils.ipmath(1048573 | random(seed=algo_server_name + ansible_fqdn)) }}" -# Hide sensitive data +# Hide sensitive data in Ansible output during deployment (passwords, keys, etc.) +# This is NOT related to privacy/logging on the VPN server itself algo_no_log: true congrats: @@ -218,11 +172,11 @@ cloud_providers: image: Ubuntu 22.04 Jammy Jellyfish arch: x86_64 hetzner: - server_type: cpx11 + server_type: cpx11 image: ubuntu-22.04 openstack: flavor_ram: ">=512" - image: Ubuntu-22.04 + image: Ubuntu-22.04 cloudstack: size: Micro image: Linux Ubuntu 22.04 LTS 64-bit diff --git a/docs/faq.md b/docs/faq.md index 399256762..0c270d853 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -10,6 +10,7 @@ * [I deployed an Algo server. Can you update it with new features?](#i-deployed-an-algo-server-can-you-update-it-with-new-features) * [Where did the name "Algo" come from?](#where-did-the-name-algo-come-from) * [Can DNS filtering be disabled?](#can-dns-filtering-be-disabled) +* [Does Algo support zero logging?](#does-algo-support-zero-logging) * [Wasn't IPSEC backdoored by the US government?](#wasnt-ipsec-backdoored-by-the-us-government) * [What inbound ports are used?](#what-inbound-ports-are-used) * [How do I monitor user activity?](#how-do-i-monitor-user-activity) @@ -59,6 +60,10 @@ Algo is short for "Al Gore", the **V**ice **P**resident of **N**etworks everywhe You can temporarily disable DNS filtering for all IPsec clients at once with the following workaround: SSH to your Algo server (using the 'shell access' command printed upon a successful deployment), edit `/etc/ipsec.conf`, and change `rightdns=` to `rightdns=8.8.8.8`. Then run `sudo systemctl restart strongswan`. DNS filtering for WireGuard clients has to be disabled on each client device separately by modifying the settings in the app, or by directly modifying the `DNS` setting on the `clientname.conf` file. If all else fails, we recommend deploying a new Algo server without the adblocking feature enabled. +## Does Algo support zero logging? + +Yes, Algo includes privacy enhancements that minimize logging by default. StrongSwan connection logging is disabled, DNSCrypt syslog is turned off, and logs are automatically rotated after 7 days. However, some system-level logging remains for security and troubleshooting purposes. For detailed privacy configuration and limitations, see the [Privacy and Logging](#privacy-and-logging) section in the README. + ## Wasn't IPSEC backdoored by the US government? No. diff --git a/roles/client/tasks/main.yml b/roles/client/tasks/main.yml index 098da9759..85d247d3b 100644 --- a/roles/client/tasks/main.yml +++ b/roles/client/tasks/main.yml @@ -45,11 +45,14 @@ dest: "{{ item.dest }}" line: "{{ item.line }}" create: true + mode: "{{ item.mode }}" with_items: - dest: "{{ configs_prefix }}/ipsec.conf" line: include ipsec.{{ IP_subject_alt_name }}.conf + mode: '0644' - dest: "{{ configs_prefix }}/ipsec.secrets" line: include ipsec.{{ IP_subject_alt_name }}.secrets + mode: '0600' notify: - restart strongswan @@ -59,18 +62,22 @@ dest: "{{ configs_prefix }}/strongswan.d/relax-ca-constraints.conf" owner: root group: root - mode: 0644 + mode: '0644' - name: Setup the certificates and keys template: src: "{{ item.src }}" dest: "{{ item.dest }}" + mode: "{{ item.mode }}" with_items: - src: configs/{{ IP_subject_alt_name }}/ipsec/.pki/certs/{{ vpn_user }}.crt dest: "{{ configs_prefix }}/ipsec.d/certs/{{ vpn_user }}.crt" + mode: '0644' - src: configs/{{ IP_subject_alt_name }}/ipsec/.pki/cacert.pem dest: "{{ configs_prefix }}/ipsec.d/cacerts/{{ IP_subject_alt_name }}.pem" + mode: '0644' - src: configs/{{ IP_subject_alt_name }}/ipsec/.pki/private/{{ vpn_user }}.key dest: "{{ configs_prefix }}/ipsec.d/private/{{ vpn_user }}.key" + mode: '0600' notify: - restart strongswan diff --git a/roles/cloud-azure/tasks/prompts.yml b/roles/cloud-azure/tasks/prompts.yml index 127a4d755..85babf038 100644 --- a/roles/cloud-azure/tasks/prompts.yml +++ b/roles/cloud-azure/tasks/prompts.yml @@ -4,6 +4,7 @@ tenant: "{{ azure_tenant | default(lookup('env', 'AZURE_TENANT'), true) }}" client_id: "{{ azure_client_id | default(lookup('env', 'AZURE_CLIENT_ID'), true) }}" subscription_id: "{{ azure_subscription_id | default(lookup('env', 'AZURE_SUBSCRIPTION_ID'), true) }}" + no_log: true - block: - name: Set the default region diff --git a/roles/cloud-cloudstack/tasks/main.yml b/roles/cloud-cloudstack/tasks/main.yml index ea0593824..744dd62e1 100644 --- a/roles/cloud-cloudstack/tasks/main.yml +++ b/roles/cloud-cloudstack/tasks/main.yml @@ -57,3 +57,4 @@ CLOUDSTACK_KEY: "{{ algo_cs_key }}" CLOUDSTACK_SECRET: "{{ algo_cs_token }}" CLOUDSTACK_ENDPOINT: "{{ algo_cs_url }}" + no_log: true diff --git a/roles/cloud-cloudstack/tasks/prompts.yml b/roles/cloud-cloudstack/tasks/prompts.yml index ae3de1236..ebeb1c5bd 100644 --- a/roles/cloud-cloudstack/tasks/prompts.yml +++ b/roles/cloud-cloudstack/tasks/prompts.yml @@ -8,6 +8,7 @@ when: - cs_key is undefined - lookup('env', 'CLOUDSTACK_KEY')|length <= 0 + no_log: true - pause: prompt: | @@ -17,6 +18,7 @@ when: - cs_secret is undefined - lookup('env', 'CLOUDSTACK_SECRET')|length <= 0 + no_log: true - pause: prompt: | @@ -34,6 +36,7 @@ {{ cs_url | default(_cs_url.user_input|default(None)) | default(lookup('env', 'CLOUDSTACK_ENDPOINT'), true) | default('https://api.exoscale.com/compute', true) }} + no_log: true - name: Get zones on cloud cs_zone_info: @@ -42,6 +45,7 @@ CLOUDSTACK_KEY: "{{ algo_cs_key }}" CLOUDSTACK_SECRET: "{{ algo_cs_token }}" CLOUDSTACK_ENDPOINT: "{{ algo_cs_url }}" + no_log: true - name: Extract zones from output set_fact: diff --git a/roles/cloud-digitalocean/tasks/prompts.yml b/roles/cloud-digitalocean/tasks/prompts.yml index 1ad20409c..f8f8e28dc 100644 --- a/roles/cloud-digitalocean/tasks/prompts.yml +++ b/roles/cloud-digitalocean/tasks/prompts.yml @@ -7,10 +7,12 @@ when: - do_token is undefined - lookup('env', 'DO_API_TOKEN')|length <= 0 + no_log: true - name: Set the token as a fact set_fact: algo_do_token: "{{ do_token | default(_do_token.user_input | default(None)) | default(lookup('env', 'DO_API_TOKEN'), true) }}" + no_log: true - name: Get regions uri: @@ -21,6 +23,7 @@ Content-Type: application/json Authorization: Bearer {{ algo_do_token }} register: _do_regions + no_log: true - name: Set facts about the regions set_fact: diff --git a/roles/cloud-ec2/tasks/cloudformation.yml b/roles/cloud-ec2/tasks/cloudformation.yml index 75dd5f361..407b34870 100644 --- a/roles/cloud-ec2/tasks/cloudformation.yml +++ b/roles/cloud-ec2/tasks/cloudformation.yml @@ -20,3 +20,4 @@ tags: Environment: Algo register: stack + no_log: true diff --git a/roles/cloud-ec2/tasks/main.yml b/roles/cloud-ec2/tasks/main.yml index 5f68c9270..e6e302073 100644 --- a/roles/cloud-ec2/tasks/main.yml +++ b/roles/cloud-ec2/tasks/main.yml @@ -15,6 +15,7 @@ architecture: "{{ cloud_providers.ec2.image.arch }}" name: ubuntu/images/hvm-ssd/{{ cloud_providers.ec2.image.name }}-*64-server-* register: ami_search + no_log: true - name: Set the ami id as a fact set_fact: diff --git a/roles/cloud-ec2/tasks/prompts.yml b/roles/cloud-ec2/tasks/prompts.yml index 5ca5e2460..765fc62bc 100644 --- a/roles/cloud-ec2/tasks/prompts.yml +++ b/roles/cloud-ec2/tasks/prompts.yml @@ -73,6 +73,7 @@ aws_session_token: "{{ session_token if session_token else omit }}" region: us-east-1 register: _aws_regions + no_log: true - name: Set facts about the regions set_fact: @@ -114,6 +115,7 @@ aws_session_token: "{{ session_token if session_token else omit }}" region: "{{ algo_region }}" register: raw_eip_addresses + no_log: true - set_fact: available_eip_addresses: "{{ raw_eip_addresses.addresses | selectattr('association_id', 'undefined') | list }}" diff --git a/roles/cloud-gce/tasks/prompts.yml b/roles/cloud-gce/tasks/prompts.yml index cb2666dcf..a8a32fe9e 100644 --- a/roles/cloud-gce/tasks/prompts.yml +++ b/roles/cloud-gce/tasks/prompts.yml @@ -7,19 +7,23 @@ when: - gce_credentials_file is undefined - lookup('env', 'GCE_CREDENTIALS_FILE_PATH')|length <= 0 + no_log: true - set_fact: credentials_file_path: >- {{ gce_credentials_file | default(_gce_credentials_file.user_input|default(None)) | default(lookup('env', 'GCE_CREDENTIALS_FILE_PATH'), true) }} ssh_public_key_lookup: "{{ lookup('file', '{{ SSH_keys.public }}') }}" + no_log: true - set_fact: credentials_file_lookup: "{{ lookup('file', '{{ credentials_file_path }}') }}" + no_log: true - set_fact: service_account_email: "{{ credentials_file_lookup.client_email | default(lookup('env', 'GCE_EMAIL')) }}" project_id: "{{ credentials_file_lookup.project_id | default(lookup('env', 'GCE_PROJECT')) }}" + no_log: true - block: - name: Get regions diff --git a/roles/cloud-hetzner/tasks/prompts.yml b/roles/cloud-hetzner/tasks/prompts.yml index fd1afcc0a..9b2f3faa6 100644 --- a/roles/cloud-hetzner/tasks/prompts.yml +++ b/roles/cloud-hetzner/tasks/prompts.yml @@ -7,10 +7,12 @@ when: - hcloud_token is undefined - lookup('env', 'HCLOUD_TOKEN')|length <= 0 + no_log: true - name: Set the token as a fact set_fact: algo_hcloud_token: "{{ hcloud_token | default(_hcloud_token.user_input | default(None)) | default(lookup('env', 'HCLOUD_TOKEN'), true) }}" + no_log: true - name: Get regions hetzner.hcloud.datacenter_info: diff --git a/roles/cloud-lightsail/tasks/cloudformation.yml b/roles/cloud-lightsail/tasks/cloudformation.yml index d3798a90d..1a1c37d4e 100644 --- a/roles/cloud-lightsail/tasks/cloudformation.yml +++ b/roles/cloud-lightsail/tasks/cloudformation.yml @@ -17,3 +17,4 @@ Environment: Algo Lightsail: true register: stack + no_log: true diff --git a/roles/cloud-lightsail/tasks/prompts.yml b/roles/cloud-lightsail/tasks/prompts.yml index c48484773..782153d99 100644 --- a/roles/cloud-lightsail/tasks/prompts.yml +++ b/roles/cloud-lightsail/tasks/prompts.yml @@ -8,6 +8,7 @@ when: - aws_access_key is undefined - lookup('env', 'AWS_ACCESS_KEY_ID')|length <= 0 + no_log: true - pause: prompt: | @@ -17,10 +18,12 @@ when: - aws_secret_key is undefined - lookup('env', 'AWS_SECRET_ACCESS_KEY')|length <= 0 + no_log: true - set_fact: access_key: "{{ aws_access_key | default(_aws_access_key.user_input | default(None)) | default(lookup('env', 'AWS_ACCESS_KEY_ID'), true) }}" secret_key: "{{ aws_secret_key | default(_aws_secret_key.user_input | default(None)) | default(lookup('env', 'AWS_SECRET_ACCESS_KEY'), true) }}" + no_log: true - block: - name: Get regions @@ -29,6 +32,7 @@ aws_secret_key: "{{ secret_key }}" region: us-east-1 register: _lightsail_regions + no_log: true - name: Set facts about the regions set_fact: diff --git a/roles/cloud-linode/tasks/main.yml b/roles/cloud-linode/tasks/main.yml index 8cdd47f40..4b316f1b9 100644 --- a/roles/cloud-linode/tasks/main.yml +++ b/roles/cloud-linode/tasks/main.yml @@ -23,6 +23,7 @@ script: | {{ stackscript }} register: _linode_stackscript + no_log: true - name: Update the stackscript uri: @@ -36,6 +37,7 @@ Content-Type: application/json Authorization: Bearer {{ algo_linode_token }} when: (_linode_stackscript.stackscript.script | hash('md5')) != (stackscript | hash('md5')) + no_log: true - name: Creating an instance... linode_v4: @@ -48,6 +50,7 @@ authorized_keys: "{{ public_key }}" stackscript_id: "{{ _linode_stackscript.stackscript.id }}" register: _linode + no_log: true - set_fact: cloud_instance_ip: "{{ _linode.instance.ipv4[0] }}" diff --git a/roles/cloud-linode/tasks/prompts.yml b/roles/cloud-linode/tasks/prompts.yml index 219fe97a4..18fde320e 100644 --- a/roles/cloud-linode/tasks/prompts.yml +++ b/roles/cloud-linode/tasks/prompts.yml @@ -7,10 +7,12 @@ when: - linode_token is undefined - lookup('env', 'LINODE_API_TOKEN')|length <= 0 + no_log: true - name: Set the token as a fact set_fact: algo_linode_token: "{{ linode_token | default(_linode_token.user_input | default(None)) | default(lookup('env', 'LINODE_API_TOKEN'), true) }}" + no_log: true - name: Get regions uri: diff --git a/roles/cloud-scaleway/tasks/prompts.yml b/roles/cloud-scaleway/tasks/prompts.yml index 3551f646b..15a65272b 100644 --- a/roles/cloud-scaleway/tasks/prompts.yml +++ b/roles/cloud-scaleway/tasks/prompts.yml @@ -7,6 +7,7 @@ when: - scaleway_token is undefined - lookup('env', 'SCW_TOKEN')|length <= 0 + no_log: true - pause: prompt: | @@ -27,3 +28,4 @@ {% if region is defined %}{{ region }} {%- elif _algo_region.user_input %}{{ scaleway_regions[_algo_region.user_input | int -1 ]['alias'] }} {%- else %}{{ scaleway_regions.0.alias }}{% endif %} + no_log: true diff --git a/roles/cloud-vultr/tasks/prompts.yml b/roles/cloud-vultr/tasks/prompts.yml index 0569bb4f4..fe2c3b220 100644 --- a/roles/cloud-vultr/tasks/prompts.yml +++ b/roles/cloud-vultr/tasks/prompts.yml @@ -7,10 +7,12 @@ when: - vultr_config is undefined - lookup('env', 'VULTR_API_CONFIG')|length <= 0 + no_log: true - name: Set the token as a fact set_fact: algo_vultr_config: "{{ vultr_config | default(_vultr_config.user_input) | default(lookup('env', 'VULTR_API_CONFIG'), true) }}" + no_log: true - name: Set the Vultr API Key as a fact set_fact: diff --git a/roles/common/tasks/aip/digitalocean.yml b/roles/common/tasks/aip/digitalocean.yml index bc016305b..bedf60d2c 100644 --- a/roles/common/tasks/aip/digitalocean.yml +++ b/roles/common/tasks/aip/digitalocean.yml @@ -16,6 +16,7 @@ template: src: 99-algo-ipv6-egress.yaml.j2 dest: /etc/netplan/99-algo-ipv6-egress.yaml + mode: '0644' when: - ipv6_support - ipv6_subnet_size|int > 1 diff --git a/roles/common/tasks/facts.yml b/roles/common/tasks/facts.yml index 98917326a..6d3d1cc58 100644 --- a/roles/common/tasks/facts.yml +++ b/roles/common/tasks/facts.yml @@ -3,11 +3,13 @@ set_fact: p12_export_password: "{{ p12_password | default(lookup('password', '/dev/null length=9 chars=ascii_letters,digits,_,@')) }}" tags: update-users + no_log: true - name: Set facts set_fact: CA_password: "{{ ca_password | default(lookup('password', '/dev/null length=16 chars=ascii_letters,digits,_,@')) }}" IP_subject_alt_name: "{{ IP_subject_alt_name }}" + no_log: true - name: Set IPv6 support as a fact set_fact: diff --git a/roles/common/tasks/iptables.yml b/roles/common/tasks/iptables.yml index 463dc3814..ddf801c4e 100644 --- a/roles/common/tasks/iptables.yml +++ b/roles/common/tasks/iptables.yml @@ -5,7 +5,7 @@ dest: "{{ item.dest }}" owner: root group: root - mode: 0640 + mode: '0640' with_items: - { src: rules.v4.j2, dest: /etc/iptables/rules.v4 } notify: @@ -17,7 +17,7 @@ dest: "{{ item.dest }}" owner: root group: root - mode: 0640 + mode: '0640' when: ipv6_support with_items: - { src: rules.v6.j2, dest: /etc/iptables/rules.v6 } diff --git a/roles/common/tasks/ubuntu.yml b/roles/common/tasks/ubuntu.yml index b5a919ac7..9f59e33ec 100644 --- a/roles/common/tasks/ubuntu.yml +++ b/roles/common/tasks/ubuntu.yml @@ -77,6 +77,7 @@ section: Resolve option: FallbackDNS value: "{{ dns_servers.ipv4 | join(' ') }}" + mode: '0644' notify: - restart systemd-resolved @@ -84,6 +85,7 @@ template: src: 10-algo-lo100.network.j2 dest: /etc/systemd/network/10-algo-lo100.network + mode: '0644' notify: - restart systemd-networkd @@ -121,6 +123,7 @@ - apparmor-utils - uuid-runtime - coreutils + - iptables - iptables-persistent - cgroup-tools - openssl @@ -178,10 +181,6 @@ with_items: - iptables - ip6tables - - iptables-save - - iptables-restore - - ip6tables-save - - ip6tables-restore when: - ansible_distribution == "Ubuntu" - ansible_distribution_version is version('22.04', '>=') diff --git a/roles/common/tasks/unattended-upgrades.yml b/roles/common/tasks/unattended-upgrades.yml index da7c2fb28..3ffe32cf6 100644 --- a/roles/common/tasks/unattended-upgrades.yml +++ b/roles/common/tasks/unattended-upgrades.yml @@ -10,7 +10,7 @@ dest: /etc/apt/apt.conf.d/50unattended-upgrades owner: root group: root - mode: 0644 + mode: '0644' - name: Periodic upgrades configured template: @@ -18,4 +18,4 @@ dest: /etc/apt/apt.conf.d/10periodic owner: root group: root - mode: 0644 + mode: '0644' diff --git a/roles/dns/tasks/dns_adblocking.yml b/roles/dns/tasks/dns_adblocking.yml index ec2271a55..d0a85ad99 100644 --- a/roles/dns/tasks/dns_adblocking.yml +++ b/roles/dns/tasks/dns_adblocking.yml @@ -5,7 +5,7 @@ dest: /usr/local/sbin/adblock.sh owner: root group: "{{ root_group | default('root') }}" - mode: 0755 + mode: '0755' - name: Adblock script added to cron cron: diff --git a/roles/dns/tasks/main.yml b/roles/dns/tasks/main.yml index f845a382d..724db6790 100644 --- a/roles/dns/tasks/main.yml +++ b/roles/dns/tasks/main.yml @@ -8,6 +8,7 @@ template: src: ip-blacklist.txt.j2 dest: "{{ config_prefix | default('/') }}etc/dnscrypt-proxy/ip-blacklist.txt" + mode: '0644' notify: - restart dnscrypt-proxy @@ -15,6 +16,7 @@ template: src: dnscrypt-proxy.toml.j2 dest: "{{ config_prefix | default('/') }}etc/dnscrypt-proxy/dnscrypt-proxy.toml" + mode: '0644' notify: - restart dnscrypt-proxy diff --git a/roles/dns/tasks/ubuntu.yml b/roles/dns/tasks/ubuntu.yml index f54f643bf..c9c234005 100644 --- a/roles/dns/tasks/ubuntu.yml +++ b/roles/dns/tasks/ubuntu.yml @@ -16,7 +16,7 @@ dest: /etc/apt/apt.conf.d/50-dnscrypt-proxy-unattended-upgrades owner: root group: root - mode: 0644 + mode: '0644' when: ansible_facts['distribution_version'] is version('20.04', '<') - name: Install dnscrypt-proxy (individual) @@ -33,7 +33,7 @@ dest: /etc/apparmor.d/usr.bin.dnscrypt-proxy owner: root group: root - mode: 0600 + mode: '0600' notify: restart dnscrypt-proxy - name: Ubuntu | Enforce the dnscrypt-proxy AppArmor policy @@ -46,13 +46,14 @@ file: path: /etc/systemd/system/dnscrypt-proxy.service.d/ state: directory - mode: 0755 + mode: '0755' owner: root group: root - name: Ubuntu | Add custom requirements to successfully start the unit copy: dest: /etc/systemd/system/dnscrypt-proxy.service.d/99-algo.conf + mode: '0644' content: | [Unit] After=systemd-resolved.service @@ -93,7 +94,7 @@ SystemCallErrorNumber=EPERM owner: root group: root - mode: 0644 + mode: '0644' notify: - daemon-reload - restart dnscrypt-proxy diff --git a/roles/dns/templates/dnscrypt-proxy.toml.j2 b/roles/dns/templates/dnscrypt-proxy.toml.j2 index d0e2e0938..0a07dfccc 100644 --- a/roles/dns/templates/dnscrypt-proxy.toml.j2 +++ b/roles/dns/templates/dnscrypt-proxy.toml.j2 @@ -137,8 +137,11 @@ lb_strategy = 'p2' ## Log level (0-6, default: 2 - 0 is very verbose, 6 only contains fatal errors) +## Privacy enhancement: Set to 4 (warnings and errors only) to reduce information disclosure +## Lower levels may log DNS queries and connection details that could compromise privacy +## Level 4 provides essential error information while minimizing privacy-sensitive logging -log_level = 2 +log_level = 4 ## log file for the application @@ -147,8 +150,11 @@ log_level = 2 ## Use the system logger (syslog on Unix, Event Log on Windows) +## Privacy enhancement: Disabled by default to prevent DNS query logging in system logs +## DNS queries logged to syslog can reveal browsing patterns and compromise privacy +## Enable only if required for debugging purposes -use_syslog = true +use_syslog = false ## Delay, in minutes, after which certificates are reloaded @@ -332,6 +338,9 @@ cache_neg_max_ttl = 600 ############################### ## Log client queries to a file +## Privacy warning: Enabling query logging will record all DNS requests +## which can reveal detailed browsing patterns and compromise user privacy +## Only enable for debugging purposes and disable immediately after use [query_log] @@ -358,6 +367,8 @@ cache_neg_max_ttl = 600 ## Log queries for nonexistent zones ## These queries can reveal the presence of malware, broken/obsolete applications, ## and devices signaling their presence to 3rd parties. +## Privacy warning: This logging can still reveal browsing patterns and queried domains +## Only enable for security monitoring purposes when necessary [nx_log] diff --git a/roles/privacy/README.md b/roles/privacy/README.md new file mode 100644 index 000000000..31461f5b4 --- /dev/null +++ b/roles/privacy/README.md @@ -0,0 +1,205 @@ +# Privacy Enhancements Role + +This Ansible role implements additional privacy enhancements for Algo VPN to minimize server-side traces of VPN usage and reduce log retention. These measures help protect user privacy while maintaining system security. + +## Features + +### 1. Aggressive Log Rotation +- Configures shorter log retention periods (default: 7 days) +- Implements more frequent log rotation +- Compresses rotated logs to save space +- Automatically cleans up old log files + +### 2. History Clearing +- Clears bash/shell history after deployment +- Disables persistent command history for system users +- Clears temporary files and caches +- Sets up automatic history clearing on user logout + +### 3. VPN Log Filtering +- Filters out VPN connection logs from rsyslog +- Excludes WireGuard and StrongSwan messages from persistent storage +- Filters kernel messages related to VPN traffic +- Optional filtering of authentication logs (use with caution) + +### 4. Automatic Cleanup +- Daily/weekly/monthly cleanup of old logs and temporary files +- Package cache cleaning +- Configurable retention policies +- Optional shutdown cleanup for extreme privacy + +### 5. Advanced Privacy Settings +- Reduced kernel log verbosity +- Disabled successful SSH connection logging (optional) +- Volatile systemd journal storage +- Privacy monitoring script + +## Configuration + +All privacy settings are configured in `config.cfg` under the "Privacy Enhancements" section: + +```yaml +# Enable/disable all privacy enhancements +privacy_enhancements_enabled: true + +# Log rotation settings +privacy_log_rotation: + max_age: 7 # Days to keep logs + max_size: 10 # Max size per log file (MB) + rotate_count: 3 # Number of rotated files to keep + compress: true # Compress rotated logs + daily_rotation: true # Force daily rotation + +# History clearing +privacy_history_clearing: + clear_bash_history: true + clear_system_history: true + disable_service_history: true + +# Log filtering +privacy_log_filtering: + exclude_vpn_logs: true + exclude_auth_logs: false # Use with caution + filter_kernel_vpn_logs: true + +# Automatic cleanup +privacy_auto_cleanup: + enabled: true + frequency: "daily" # daily, weekly, monthly + temp_files_max_age: 1 + clean_package_cache: true + +# Advanced settings +privacy_advanced: + disable_ssh_success_logs: false + reduce_kernel_verbosity: true + clear_logs_on_shutdown: false # Extreme measure +``` + +## Security Considerations + +### Safe Settings (Default) +- `exclude_vpn_logs: true` - Safe, only filters VPN-specific messages +- `clear_bash_history: true` - Safe, improves privacy without affecting security +- `reduce_kernel_verbosity: true` - Safe, reduces noise in logs + +### Use With Caution +- `exclude_auth_logs: true` - Reduces security logging, makes incident investigation harder +- `disable_ssh_success_logs: true` - Removes audit trail for successful connections +- `clear_logs_on_shutdown: true` - Extreme measure, makes debugging very difficult + +## Files Created + +### Configuration Files +- `/etc/logrotate.d/99-privacy-enhanced` - Main log rotation config +- `/etc/logrotate.d/99-auth-privacy` - Auth log rotation +- `/etc/logrotate.d/99-kern-privacy` - Kernel log rotation +- `/etc/rsyslog.d/49-privacy-vpn-filter.conf` - VPN log filtering +- `/etc/rsyslog.d/48-privacy-kernel-filter.conf` - Kernel log filtering +- `/etc/rsyslog.d/47-privacy-auth-filter.conf` - Auth log filtering (optional) +- `/etc/rsyslog.d/46-privacy-ssh-filter.conf` - SSH log filtering (optional) +- `/etc/rsyslog.d/45-privacy-minimal.conf` - Minimal logging config + +### Scripts +- `/usr/local/bin/privacy-auto-cleanup.sh` - Automatic cleanup script +- `/usr/local/bin/privacy-log-cleanup.sh` - Initial log cleanup +- `/usr/local/bin/privacy-monitor.sh` - Privacy status monitoring +- `/etc/bash.bash_logout` - History clearing on logout + +### Systemd Services +- `/etc/systemd/system/privacy-shutdown-cleanup.service` - Shutdown cleanup (optional) + +## Usage + +### Enable Privacy Enhancements +Privacy enhancements are enabled by default. To disable them: + +```yaml +privacy_enhancements_enabled: false +``` + +### Run Specific Privacy Tasks +You can run specific privacy components using tags: + +```bash +# Run only log rotation setup +ansible-playbook server.yml --tags privacy-logs + +# Run only history clearing +ansible-playbook server.yml --tags privacy-history + +# Run only log filtering +ansible-playbook server.yml --tags privacy-filtering + +# Run only cleanup tasks +ansible-playbook server.yml --tags privacy-cleanup + +# Run all privacy enhancements +ansible-playbook server.yml --tags privacy +``` + +### Monitor Privacy Status +Check the status of privacy enhancements: + +```bash +sudo /usr/local/bin/privacy-monitor.sh +``` + +### Manual Cleanup +Run manual cleanup: + +```bash +sudo /usr/local/bin/privacy-auto-cleanup.sh +``` + +## Debugging + +If you need to debug VPN issues, temporarily disable privacy enhancements: + +1. Set `privacy_enhancements_enabled: false` in `config.cfg` +2. Re-run the deployment: `./algo` +3. Debug your issues with full logging +4. Re-enable privacy enhancements when done + +Alternatively, disable specific features: +- Set `exclude_vpn_logs: false` to see VPN connection logs +- Set `reduce_kernel_verbosity: false` for full kernel logging +- Check `/var/log/privacy-cleanup.log` for cleanup operation logs + +## Impact on System + +### Positive Effects +- Improved user privacy +- Reduced disk usage from logs +- Faster log searches +- Reduced attack surface + +### Potential Drawbacks +- Limited debugging information +- Shorter audit trail +- May complicate troubleshooting +- Could hide security incidents + +## Compatibility + +- **Ubuntu 22.04**: Fully supported +- **FreeBSD**: Limited support (log rotation and history clearing only) +- **Other distributions**: May require adaptation + +## Best Practices + +1. **Start Conservative**: Use default settings initially +2. **Test Thoroughly**: Verify VPN functionality after enabling privacy features +3. **Monitor Logs**: Check that essential security logs are still being captured +4. **Document Changes**: Keep track of privacy settings for troubleshooting +5. **Regular Reviews**: Periodically review privacy settings and their effectiveness + +## Privacy vs. Security Balance + +This role aims to balance privacy with security by: +- Keeping security-critical logs (authentication failures, system errors) +- Filtering only VPN-specific operational logs +- Providing granular control over what gets filtered +- Maintaining essential audit trails while reducing VPN usage traces + +For maximum privacy, consider running your own log analysis before enabling aggressive filtering options. \ No newline at end of file diff --git a/roles/privacy/defaults/main.yml b/roles/privacy/defaults/main.yml new file mode 100644 index 000000000..b96119c40 --- /dev/null +++ b/roles/privacy/defaults/main.yml @@ -0,0 +1,57 @@ +--- +# Privacy enhancement configuration defaults +# These settings can be overridden in config.cfg + +# Enable privacy enhancements (disabled for debugging when false) +privacy_enhancements_enabled: true + +# Log rotation settings +privacy_log_rotation: + # Maximum age for system logs in days + max_age: 7 + # Maximum size for individual log files in MB + max_size: 10 + # Number of rotated files to keep + rotate_count: 3 + # Compress rotated logs + compress: true + # Force daily rotation regardless of size + daily_rotation: true + +# History clearing settings +privacy_history_clearing: + # Clear bash history after deployment + clear_bash_history: true + # Clear system command history + clear_system_history: true + # Disable bash history persistence for service users + disable_service_history: true + +# Log filtering settings +privacy_log_filtering: + # Exclude VPN connection logs from persistent storage + exclude_vpn_logs: true + # Exclude authentication logs (be careful with this) + exclude_auth_logs: false + # Filter kernel logs related to VPN traffic + filter_kernel_vpn_logs: true + +# Automatic cleanup settings +privacy_auto_cleanup: + # Enable automatic log cleanup + enabled: true + # Cleanup frequency (daily, weekly, monthly) + frequency: "daily" + # Clean up temporary files older than N days + temp_files_max_age: 1 + # Clean up old package cache + clean_package_cache: true + +# Advanced privacy settings +privacy_advanced: + # Disable logging of successful SSH connections + disable_ssh_success_logs: false + # Reduce kernel log verbosity + reduce_kernel_verbosity: true + # Clear logs on shutdown (use with caution) + clear_logs_on_shutdown: false diff --git a/roles/privacy/handlers/main.yml b/roles/privacy/handlers/main.yml new file mode 100644 index 000000000..6de5993db --- /dev/null +++ b/roles/privacy/handlers/main.yml @@ -0,0 +1,29 @@ +--- +# Privacy role handlers +# These handlers are triggered by privacy configuration changes + +- name: restart rsyslog + systemd: + name: rsyslog + state: restarted + daemon_reload: yes + become: yes + +- name: restart systemd-journald + systemd: + name: systemd-journald + state: restarted + daemon_reload: yes + become: yes + +- name: reload systemd + systemd: + daemon_reload: yes + become: yes + +- name: enable privacy shutdown cleanup + systemd: + name: privacy-shutdown-cleanup.service + enabled: yes + daemon_reload: yes + become: yes diff --git a/roles/privacy/tasks/advanced_privacy.yml b/roles/privacy/tasks/advanced_privacy.yml new file mode 100644 index 000000000..9e1f0a72a --- /dev/null +++ b/roles/privacy/tasks/advanced_privacy.yml @@ -0,0 +1,99 @@ +--- +# Advanced privacy settings for enhanced anonymity + +- name: Reduce kernel log verbosity for privacy + sysctl: + name: "{{ item.name }}" + value: "{{ item.value }}" + state: present + reload: yes + loop: + - { name: 'kernel.printk', value: '3 4 1 3' } + - { name: 'kernel.dmesg_restrict', value: '1' } + when: privacy_advanced.reduce_kernel_verbosity | bool + +- name: Disable BPF JIT if available (optional security hardening) + sysctl: + name: net.core.bpf_jit_enable + value: '0' + state: present + reload: yes + when: privacy_advanced.reduce_kernel_verbosity | bool + ignore_errors: yes + +- name: Configure kernel parameters for privacy + lineinfile: + path: /etc/sysctl.d/99-privacy.conf + line: "{{ item }}" + create: yes + mode: '0644' + loop: + - "# Privacy enhancements - reduce kernel logging" + - "kernel.printk = 3 4 1 3" + - "kernel.dmesg_restrict = 1" + - "# Note: net.core.bpf_jit_enable may not be available on all kernels" + when: privacy_advanced.reduce_kernel_verbosity | bool + +- name: Add BPF JIT disable to sysctl config if kernel supports it + lineinfile: + path: /etc/sysctl.d/99-privacy.conf + line: "net.core.bpf_jit_enable = 0 # Disable BPF JIT to reduce attack surface" + create: yes + mode: '0644' + when: privacy_advanced.reduce_kernel_verbosity | bool + ignore_errors: yes + +- name: Configure journal settings for privacy + lineinfile: + path: /etc/systemd/journald.conf + regexp: "^#?{{ item.key }}=" + line: "{{ item.key }}={{ item.value }}" + backup: yes + loop: + - { key: 'MaxRetentionSec', value: '{{ privacy_log_rotation.max_age * 24 * 3600 }}' } + - { key: 'MaxFileSec', value: '1day' } + - { key: 'SystemMaxUse', value: '100M' } + - { key: 'SystemMaxFileSize', value: '{{ privacy_log_rotation.max_size }}M' } + - { key: 'ForwardToSyslog', value: 'no' } + notify: restart systemd-journald + +- name: Disable persistent systemd journal + file: + path: /var/log/journal + state: absent + when: privacy_advanced.reduce_kernel_verbosity | bool + +- name: Create journal configuration for volatile storage only + lineinfile: + path: /etc/systemd/journald.conf + regexp: "^#?Storage=" + line: "Storage=volatile" + backup: yes + notify: restart systemd-journald + +- name: Configure rsyslog for minimal logging + template: + src: privacy-rsyslog.conf.j2 + dest: /etc/rsyslog.d/45-privacy-minimal.conf + mode: '0644' + owner: root + group: root + notify: restart rsyslog + +- name: Set up privacy monitoring script + template: + src: privacy-monitor.sh.j2 + dest: /usr/local/bin/privacy-monitor.sh + mode: '0755' + owner: root + group: root + +- name: Display privacy configuration summary + debug: + msg: + - "Privacy enhancements applied:" + - " - Log retention: {{ privacy_log_rotation.max_age }} days" + - " - VPN log filtering: {{ privacy_log_filtering.exclude_vpn_logs | bool }}" + - " - History clearing: {{ privacy_history_clearing.clear_bash_history | bool }}" + - " - Auto cleanup: {{ privacy_auto_cleanup.enabled | bool }}" + - " - Kernel verbosity reduction: {{ privacy_advanced.reduce_kernel_verbosity | bool }}" diff --git a/roles/privacy/tasks/auto_cleanup.yml b/roles/privacy/tasks/auto_cleanup.yml new file mode 100644 index 000000000..7fa1436a7 --- /dev/null +++ b/roles/privacy/tasks/auto_cleanup.yml @@ -0,0 +1,70 @@ +--- +# Automatic cleanup tasks for enhanced privacy + +- name: Create privacy cleanup script + template: + src: privacy-auto-cleanup.sh.j2 + dest: /usr/local/bin/privacy-auto-cleanup.sh + mode: '0755' + owner: root + group: root + +- name: Set up automatic privacy cleanup cron job + cron: + name: "Privacy auto cleanup" + job: "/usr/local/bin/privacy-auto-cleanup.sh" + minute: "30" + hour: "2" + user: root + state: "{{ 'present' if privacy_auto_cleanup.enabled else 'absent' }}" + when: privacy_auto_cleanup.frequency == 'daily' + +- name: Set up weekly privacy cleanup cron job + cron: + name: "Privacy auto cleanup weekly" + job: "/usr/local/bin/privacy-auto-cleanup.sh" + minute: "30" + hour: "2" + weekday: "0" + user: root + state: "{{ 'present' if privacy_auto_cleanup.enabled else 'absent' }}" + when: privacy_auto_cleanup.frequency == 'weekly' + +- name: Set up monthly privacy cleanup cron job + cron: + name: "Privacy auto cleanup monthly" + job: "/usr/local/bin/privacy-auto-cleanup.sh" + minute: "30" + hour: "2" + day: "1" + user: root + state: "{{ 'present' if privacy_auto_cleanup.enabled else 'absent' }}" + when: privacy_auto_cleanup.frequency == 'monthly' + +- name: Create systemd service for privacy cleanup on shutdown + template: + src: privacy-shutdown-cleanup.service.j2 + dest: /etc/systemd/system/privacy-shutdown-cleanup.service + mode: '0644' + owner: root + group: root + when: privacy_advanced.clear_logs_on_shutdown | bool + notify: + - reload systemd + - enable privacy shutdown cleanup + +- name: Clean up temporary files immediately + shell: | + find /tmp -type f -mtime +{{ privacy_auto_cleanup.temp_files_max_age }} -delete + find /var/tmp -type f -mtime +{{ privacy_auto_cleanup.temp_files_max_age }} -delete + changed_when: false + when: privacy_auto_cleanup.enabled | bool + +- name: Clean package cache immediately + shell: | + apt-get clean + apt-get autoclean + changed_when: false + when: + - privacy_auto_cleanup.enabled | bool + - privacy_auto_cleanup.clean_package_cache | bool diff --git a/roles/privacy/tasks/clear_history.yml b/roles/privacy/tasks/clear_history.yml new file mode 100644 index 000000000..a72604fe6 --- /dev/null +++ b/roles/privacy/tasks/clear_history.yml @@ -0,0 +1,59 @@ +--- +# Clear command history and disable persistent history for privacy + +- name: Clear bash history for all users + shell: | + for user_home in /home/* /root; do + if [ -d "$user_home" ]; then + rm -f "$user_home/.bash_history" + rm -f "$user_home/.zsh_history" + rm -f "$user_home/.sh_history" + fi + done + when: privacy_history_clearing.clear_bash_history | bool + changed_when: false + +- name: Clear system command history logs + file: + path: "{{ item }}" + state: absent + loop: + - /var/log/lastlog + - /var/log/wtmp.1 + - /var/log/btmp.1 + - /tmp/.X* + - /tmp/.font-unix + - /tmp/.ICE-unix + when: privacy_history_clearing.clear_system_history | bool + ignore_errors: true + +- name: Configure bash to not save history for service users + lineinfile: + path: "{{ item }}/.bashrc" + line: "{{ history_disable_line }}" + create: true + mode: '0644' + loop: + - /root + - /home/ubuntu + vars: + history_disable_line: | + # Privacy enhancement: disable bash history + export HISTFILE=/dev/null + export HISTSIZE=0 + export HISTFILESIZE=0 + unset HISTFILE + when: privacy_history_clearing.disable_service_history | bool + ignore_errors: true + +- name: Create history clearing script for logout + template: + src: clear-history-on-logout.sh.j2 + dest: /etc/bash.bash_logout + mode: '0644' + owner: root + group: root + when: privacy_history_clearing.clear_bash_history | bool + +# Note: We don't clear current session history as each Ansible task +# runs in its own shell session, making this operation ineffective diff --git a/roles/privacy/tasks/log_filtering.yml b/roles/privacy/tasks/log_filtering.yml new file mode 100644 index 000000000..8a3f73563 --- /dev/null +++ b/roles/privacy/tasks/log_filtering.yml @@ -0,0 +1,61 @@ +--- +# Configure rsyslog to filter out VPN-related logs for privacy + +- name: Create rsyslog privacy configuration directory + file: + path: /etc/rsyslog.d + state: directory + mode: '0755' + owner: root + group: root + +- name: Configure rsyslog to exclude VPN-related logs + template: + src: 49-privacy-vpn-filter.conf.j2 + dest: /etc/rsyslog.d/49-privacy-vpn-filter.conf + mode: '0644' + owner: root + group: root + notify: restart rsyslog + when: privacy_log_filtering.exclude_vpn_logs | bool + +- name: Configure rsyslog to filter kernel VPN logs + template: + src: 48-privacy-kernel-filter.conf.j2 + dest: /etc/rsyslog.d/48-privacy-kernel-filter.conf + mode: '0644' + owner: root + group: root + notify: restart rsyslog + when: privacy_log_filtering.filter_kernel_vpn_logs | bool + +- name: Configure rsyslog to exclude detailed auth logs (optional) + template: + src: 47-privacy-auth-filter.conf.j2 + dest: /etc/rsyslog.d/47-privacy-auth-filter.conf + mode: '0644' + owner: root + group: root + notify: restart rsyslog + when: privacy_log_filtering.exclude_auth_logs | bool + +- name: Create rsyslog privacy filter for SSH success logs + template: + src: 46-privacy-ssh-filter.conf.j2 + dest: /etc/rsyslog.d/46-privacy-ssh-filter.conf + mode: '0644' + owner: root + group: root + notify: restart rsyslog + when: privacy_advanced.disable_ssh_success_logs | bool + +- name: Test rsyslog configuration + command: rsyslogd -N1 + register: rsyslog_test + changed_when: false + failed_when: rsyslog_test.rc != 0 + +- name: Display rsyslog test results + debug: + msg: "Rsyslog configuration test passed" + when: rsyslog_test.rc == 0 diff --git a/roles/privacy/tasks/log_rotation.yml b/roles/privacy/tasks/log_rotation.yml new file mode 100644 index 000000000..a6c2d5f0f --- /dev/null +++ b/roles/privacy/tasks/log_rotation.yml @@ -0,0 +1,60 @@ +--- +# Aggressive log rotation configuration for privacy +# Reduces log retention time and implements more frequent rotation + +- name: Check if default rsyslog logrotate config exists + stat: + path: /etc/logrotate.d/rsyslog + register: rsyslog_logrotate + +- name: Disable default rsyslog logrotate to prevent conflicts + command: mv /etc/logrotate.d/rsyslog /etc/logrotate.d/rsyslog.disabled + when: rsyslog_logrotate.stat.exists + changed_when: rsyslog_logrotate.stat.exists + +- name: Configure aggressive logrotate for system logs + template: + src: privacy-logrotate.j2 + dest: /etc/logrotate.d/99-privacy-enhanced + mode: '0644' + owner: root + group: root + notify: restart rsyslog + +- name: Configure logrotate for auth logs with shorter retention + template: + src: auth-logrotate.j2 + dest: /etc/logrotate.d/99-auth-privacy + mode: '0644' + owner: root + group: root + notify: restart rsyslog + +- name: Configure logrotate for kern logs with VPN filtering + template: + src: kern-logrotate.j2 + dest: /etc/logrotate.d/99-kern-privacy + mode: '0644' + owner: root + group: root + notify: restart rsyslog + +- name: Set more frequent logrotate execution + cron: + name: "Enhanced privacy log rotation" + job: "/usr/sbin/logrotate /etc/logrotate.conf" + minute: "0" + hour: "*/6" + user: root + state: present + +- name: Create privacy log cleanup script + template: + src: privacy-log-cleanup.sh.j2 + dest: /usr/local/bin/privacy-log-cleanup.sh + mode: '0755' + owner: root + group: root + +# Note: We don't force immediate rotation as it can cause conflicts +# The new settings will apply on the next scheduled rotation diff --git a/roles/privacy/tasks/main.yml b/roles/privacy/tasks/main.yml new file mode 100644 index 000000000..aad22ac88 --- /dev/null +++ b/roles/privacy/tasks/main.yml @@ -0,0 +1,36 @@ +--- +# Privacy enhancements for Algo VPN +# This role implements additional privacy measures to reduce log retention +# and minimize traces of VPN usage on the server + +- name: Display privacy enhancements status + debug: + msg: "Privacy enhancements are {{ 'enabled' if privacy_enhancements_enabled else 'disabled' }}" + +- name: Privacy enhancements block + block: + - name: Include log rotation tasks + include_tasks: log_rotation.yml + tags: privacy-logs + + - name: Include history clearing tasks + include_tasks: clear_history.yml + tags: privacy-history + + - name: Include log filtering tasks + include_tasks: log_filtering.yml + tags: privacy-filtering + + - name: Include automatic cleanup tasks + include_tasks: auto_cleanup.yml + tags: privacy-cleanup + + - name: Include advanced privacy tasks + include_tasks: advanced_privacy.yml + tags: privacy-advanced + + - name: Display privacy enhancements completion + debug: + msg: "Privacy enhancements have been successfully applied" + + when: privacy_enhancements_enabled | bool diff --git a/roles/privacy/templates/46-privacy-ssh-filter.conf.j2 b/roles/privacy/templates/46-privacy-ssh-filter.conf.j2 new file mode 100644 index 000000000..dca38ff66 --- /dev/null +++ b/roles/privacy/templates/46-privacy-ssh-filter.conf.j2 @@ -0,0 +1,15 @@ +# Privacy-enhanced SSH log filtering +# Filters successful SSH connections while keeping failures for security +# Generated by Algo VPN privacy role + +{% if privacy_advanced.disable_ssh_success_logs %} +# Filter successful SSH connections (keep failures for security monitoring) +:msg, contains, "sshd.*Accepted" stop +:msg, contains, "sshd.*session opened" stop +:msg, contains, "sshd.*session closed" stop + +# Filter SSH key-based authentication success +:msg, contains, "sshd.*publickey" stop +{% endif %} + +# Continue processing SSH failure messages and other logs \ No newline at end of file diff --git a/roles/privacy/templates/47-privacy-auth-filter.conf.j2 b/roles/privacy/templates/47-privacy-auth-filter.conf.j2 new file mode 100644 index 000000000..86907c86c --- /dev/null +++ b/roles/privacy/templates/47-privacy-auth-filter.conf.j2 @@ -0,0 +1,19 @@ +# Privacy-enhanced authentication log filtering +# WARNING: Use with caution - this reduces security logging +# Only enable if you understand the security implications +# Generated by Algo VPN privacy role + +{% if privacy_log_filtering.exclude_auth_logs %} +# Filter successful authentication messages (reduces audit trail) +:msg, contains, "authentication success" stop +:msg, contains, "session opened" stop +:msg, contains, "session closed" stop + +# Filter sudo success messages (keep failures for security) +:msg, regex, "sudo.*COMMAND" stop + +# Filter cron messages +:msg, contains, "CRON" stop +{% endif %} + +# Continue processing other auth messages \ No newline at end of file diff --git a/roles/privacy/templates/48-privacy-kernel-filter.conf.j2 b/roles/privacy/templates/48-privacy-kernel-filter.conf.j2 new file mode 100644 index 000000000..cb35fbd75 --- /dev/null +++ b/roles/privacy/templates/48-privacy-kernel-filter.conf.j2 @@ -0,0 +1,21 @@ +# Privacy-enhanced kernel log filtering +# Filters kernel messages that may reveal VPN usage patterns +# Generated by Algo VPN privacy role + +{% if privacy_log_filtering.filter_kernel_vpn_logs %} +# Filter iptables/netfilter messages related to VPN +:msg, contains, "iptables" stop +:msg, contains, "netfilter" stop + +# Filter connection tracking messages +:msg, contains, "nf_conntrack" stop + +# Filter network interface up/down messages for VPN interfaces +:msg, regex, "wg[0-9]+.*link" stop +:msg, regex, "ipsec[0-9]+.*link" stop + +# Filter routing table changes +:msg, contains, "rtnetlink" stop +{% endif %} + +# Continue processing non-filtered messages \ No newline at end of file diff --git a/roles/privacy/templates/49-privacy-vpn-filter.conf.j2 b/roles/privacy/templates/49-privacy-vpn-filter.conf.j2 new file mode 100644 index 000000000..6ff6da70e --- /dev/null +++ b/roles/privacy/templates/49-privacy-vpn-filter.conf.j2 @@ -0,0 +1,34 @@ +# Privacy-enhanced rsyslog configuration +# Filters VPN-related log entries for enhanced privacy +# Generated by Algo VPN privacy role + +# Stop processing VPN-related messages to prevent them from being logged +# This helps maintain user privacy by not storing VPN connection details + +{% if privacy_log_filtering.exclude_vpn_logs %} +# Filter WireGuard messages +:msg, contains, "wireguard" stop + +# Filter StrongSwan/IPsec messages +:msg, contains, "strongswan" stop +:msg, contains, "ipsec" stop +:msg, contains, "charon" stop +:msg, contains, "xl2tpd" stop + +# Filter VPN interface messages +:msg, contains, "wg0" stop +:msg, contains, "ipsec0" stop + +# Filter VPN-related kernel messages +:msg, regex, "IN=wg[0-9]+" stop +:msg, regex, "OUT=wg[0-9]+" stop +{% endif %} + +{% if privacy_log_filtering.filter_kernel_vpn_logs %} +# Filter kernel messages related to VPN traffic +:msg, contains, "netfilter" stop +:msg, regex, "FORWARD.*DPT:(51820|500|4500)" stop +{% endif %} + +# Continue processing other messages +& stop \ No newline at end of file diff --git a/roles/privacy/templates/auth-logrotate.j2 b/roles/privacy/templates/auth-logrotate.j2 new file mode 100644 index 000000000..8de7a094c --- /dev/null +++ b/roles/privacy/templates/auth-logrotate.j2 @@ -0,0 +1,26 @@ +# Privacy-enhanced auth log rotation +# Reduces retention time for authentication logs +# Generated by Algo VPN privacy role + +/var/log/auth.log +{ + # Shorter retention for auth logs (privacy) + rotate 2 + maxage {{ privacy_log_rotation.max_age | int // 2 }} + size {{ privacy_log_rotation.max_size // 2 }}M + + daily + missingok + notifempty + compress + delaycompress + + create 0640 syslog adm + copytruncate + + postrotate + if [ -f /var/run/rsyslogd.pid ]; then + kill -HUP `cat /var/run/rsyslogd.pid` + fi + endscript +} \ No newline at end of file diff --git a/roles/privacy/templates/clear-history-on-logout.sh.j2 b/roles/privacy/templates/clear-history-on-logout.sh.j2 new file mode 100644 index 000000000..0765b78f7 --- /dev/null +++ b/roles/privacy/templates/clear-history-on-logout.sh.j2 @@ -0,0 +1,36 @@ +#!/bin/bash +# Privacy-enhanced history clearing on logout +# This script clears command history when users log out +# Generated by Algo VPN privacy role + +{% if privacy_history_clearing.clear_bash_history %} +# Clear bash history +if [ -f ~/.bash_history ]; then + > ~/.bash_history +fi + +# Clear zsh history +if [ -f ~/.zsh_history ]; then + > ~/.zsh_history +fi + +# Clear current session history +history -c +history -w + +# Clear less history +if [ -f ~/.lesshst ]; then + rm -f ~/.lesshst +fi + +# Clear vim history +if [ -f ~/.viminfo ]; then + rm -f ~/.viminfo +fi +{% endif %} + +{% if privacy_history_clearing.clear_system_history %} +# Clear temporary files in user directory +find ~/tmp -type f -delete 2>/dev/null || true +find ~/.cache -type f -delete 2>/dev/null || true +{% endif %} \ No newline at end of file diff --git a/roles/privacy/templates/kern-logrotate.j2 b/roles/privacy/templates/kern-logrotate.j2 new file mode 100644 index 000000000..a2f44baec --- /dev/null +++ b/roles/privacy/templates/kern-logrotate.j2 @@ -0,0 +1,37 @@ +# Privacy-enhanced kernel log rotation +# Reduces retention time for kernel logs that may contain VPN traces +# Generated by Algo VPN privacy role + +/var/log/kern.log +{ + # Aggressive rotation for kernel logs + rotate {{ privacy_log_rotation.rotate_count }} + maxage {{ privacy_log_rotation.max_age }} + size {{ privacy_log_rotation.max_size }}M + + daily + missingok + notifempty + compress + delaycompress + + create 0640 syslog adm + copytruncate + + # Pre-rotation script to filter VPN-related entries + prerotate + # Create filtered version excluding VPN traces + if [ -f /var/log/kern.log ]; then + grep -v -E "(wireguard|ipsec|strongswan|xl2tpd)" /var/log/kern.log > /tmp/kern.log.filtered || true + if [ -s /tmp/kern.log.filtered ]; then + mv /tmp/kern.log.filtered /var/log/kern.log + fi + fi + endscript + + postrotate + if [ -f /var/run/rsyslogd.pid ]; then + kill -HUP `cat /var/run/rsyslogd.pid` + fi + endscript +} \ No newline at end of file diff --git a/roles/privacy/templates/privacy-auto-cleanup.sh.j2 b/roles/privacy/templates/privacy-auto-cleanup.sh.j2 new file mode 100644 index 000000000..695ce2728 --- /dev/null +++ b/roles/privacy/templates/privacy-auto-cleanup.sh.j2 @@ -0,0 +1,75 @@ +#!/bin/bash +# Privacy auto-cleanup script +# Automatically cleans up logs and temporary files for enhanced privacy +# Generated by Algo VPN privacy role + +set -euo pipefail + +# Configuration +LOG_MAX_AGE={{ privacy_auto_cleanup.temp_files_max_age }} +SCRIPT_LOG="/var/log/privacy-cleanup.log" + +# Logging function +log_message() { + echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$SCRIPT_LOG" +} + +log_message "Starting privacy cleanup" + +{% if privacy_auto_cleanup.enabled %} +# Rotate log files to prevent the cleanup log from growing +if [ -f "$SCRIPT_LOG" ] && [ $(wc -l < "$SCRIPT_LOG") -gt 1000 ]; then + tail -n 500 "$SCRIPT_LOG" > "$SCRIPT_LOG.tmp" + mv "$SCRIPT_LOG.tmp" "$SCRIPT_LOG" +fi + +# Clean temporary files +log_message "Cleaning temporary files older than ${LOG_MAX_AGE} days" +find /tmp -type f -mtime +${LOG_MAX_AGE} -delete 2>/dev/null || true +find /var/tmp -type f -mtime +${LOG_MAX_AGE} -delete 2>/dev/null || true + +# Clean old log files that may have escaped rotation +log_message "Cleaning old rotated logs" +find /var/log -name "*.log.*" -type f -mtime +{{ privacy_log_rotation.max_age }} -delete 2>/dev/null || true +find /var/log -name "*.gz" -type f -mtime +{{ privacy_log_rotation.max_age }} -delete 2>/dev/null || true + +# Clean systemd journal if it exists +if [ -d /var/log/journal ]; then + log_message "Cleaning systemd journal files" + journalctl --vacuum-time={{ privacy_log_rotation.max_age }}d 2>/dev/null || true + journalctl --vacuum-size=50M 2>/dev/null || true +fi + +{% if privacy_auto_cleanup.clean_package_cache %} +# Clean package cache +log_message "Cleaning package cache" +apt-get clean 2>/dev/null || true +apt-get autoclean 2>/dev/null || true +{% endif %} + +# Clean bash history files +log_message "Cleaning bash history files" +for user_home in /home/* /root; do + if [ -d "$user_home" ]; then + rm -f "$user_home/.bash_history" 2>/dev/null || true + rm -f "$user_home/.zsh_history" 2>/dev/null || true + rm -f "$user_home/.lesshst" 2>/dev/null || true + rm -f "$user_home/.viminfo" 2>/dev/null || true + fi +done + +# Clean core dumps +log_message "Cleaning core dumps" +find /var/crash -type f -name "*.crash" -mtime +1 -delete 2>/dev/null || true + +# Force log rotation +log_message "Forcing log rotation" +/usr/sbin/logrotate -f /etc/logrotate.conf 2>/dev/null || true + +log_message "Privacy cleanup completed successfully" +{% else %} +log_message "Privacy cleanup is disabled" +{% endif %} + +# Clean up old privacy cleanup logs +find /var/log -name "privacy-cleanup.log.*" -type f -mtime +7 -delete 2>/dev/null || true \ No newline at end of file diff --git a/roles/privacy/templates/privacy-log-cleanup.sh.j2 b/roles/privacy/templates/privacy-log-cleanup.sh.j2 new file mode 100644 index 000000000..7b6af7007 --- /dev/null +++ b/roles/privacy/templates/privacy-log-cleanup.sh.j2 @@ -0,0 +1,22 @@ +#!/bin/bash +# Privacy log cleanup script +# Immediately cleans up existing logs and applies privacy settings +# Generated by Algo VPN privacy role + +set -euo pipefail + +echo "Starting privacy log cleanup..." + +# Truncate existing log files to apply new rotation settings immediately +find /var/log -type f -name "*.log" -size +{{ privacy_log_rotation.max_size }}M -exec truncate -s {{ privacy_log_rotation.max_size }}M {} \; 2>/dev/null || true + +# Remove old rotated logs that exceed our retention policy +find /var/log -type f \( -name "*.log.*" -o -name "*.gz" \) -mtime +{{ privacy_log_rotation.max_age }} -delete 2>/dev/null || true + +# Clean up systemd journal to respect new settings +if [ -d /var/log/journal ]; then + journalctl --vacuum-time={{ privacy_log_rotation.max_age }}d 2>/dev/null || true + journalctl --vacuum-size={{ privacy_log_rotation.max_size * 10 }}M 2>/dev/null || true +fi + +echo "Privacy log cleanup completed" \ No newline at end of file diff --git a/roles/privacy/templates/privacy-logrotate.j2 b/roles/privacy/templates/privacy-logrotate.j2 new file mode 100644 index 000000000..d74698735 --- /dev/null +++ b/roles/privacy/templates/privacy-logrotate.j2 @@ -0,0 +1,53 @@ +# Privacy-enhanced logrotate configuration +# This configuration enforces aggressive log rotation for privacy +# Generated by Algo VPN privacy role +# Replaces the default rsyslog logrotate configuration + +# Main system logs (may not all exist on every system) +/var/log/syslog +/var/log/messages +/var/log/daemon.log +/var/log/debug +/var/log/user.log +/var/log/mail.log +/var/log/mail.err +/var/log/mail.warn +{ + # Rotate {{ privacy_log_rotation.rotate_count }} times before deletion + rotate {{ privacy_log_rotation.rotate_count }} + + # Maximum age in days + maxage {{ privacy_log_rotation.max_age }} + + # Maximum size per file + size {{ privacy_log_rotation.max_size }}M + + {% if privacy_log_rotation.daily_rotation %} + # Force daily rotation + daily + {% endif %} + + {% if privacy_log_rotation.compress %} + # Compress rotated files + compress + delaycompress + {% endif %} + + # Missing files are ok (not all systems have all logs) + missingok + + # Don't rotate if empty + notifempty + + # Create new files with specific permissions + create 0640 syslog adm + + # Truncate original file after rotation + copytruncate + + # Execute after rotation + postrotate + # Send SIGHUP to rsyslog + /usr/bin/killall -HUP rsyslogd 2>/dev/null || true + endscript +} \ No newline at end of file diff --git a/roles/privacy/templates/privacy-monitor.sh.j2 b/roles/privacy/templates/privacy-monitor.sh.j2 new file mode 100644 index 000000000..dfe71dd7c --- /dev/null +++ b/roles/privacy/templates/privacy-monitor.sh.j2 @@ -0,0 +1,85 @@ +#!/bin/bash +# Privacy monitoring script +# Monitors and reports on privacy settings status +# Generated by Algo VPN privacy role + +set -euo pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +echo -e "${GREEN}Algo VPN Privacy Status Monitor${NC}" +echo "========================================" + +# Check log rotation settings +echo -e "\n${YELLOW}Log Rotation Status:${NC}" +if [ -f /etc/logrotate.d/99-privacy-enhanced ]; then + echo -e " ${GREEN}✓${NC} Privacy log rotation configured" +else + echo -e " ${RED}✗${NC} Privacy log rotation not found" +fi + +# Check rsyslog filtering +echo -e "\n${YELLOW}Log Filtering Status:${NC}" +if [ -f /etc/rsyslog.d/49-privacy-vpn-filter.conf ]; then + echo -e " ${GREEN}✓${NC} VPN log filtering enabled" +else + echo -e " ${RED}✗${NC} VPN log filtering not configured" +fi + +# Check history clearing +echo -e "\n${YELLOW}History Clearing Status:${NC}" +if [ -f /etc/bash.bash_logout ]; then + echo -e " ${GREEN}✓${NC} Logout history clearing configured" +else + echo -e " ${RED}✗${NC} Logout history clearing not configured" +fi + +# Check auto cleanup +echo -e "\n${YELLOW}Auto Cleanup Status:${NC}" +if [ -f /usr/local/bin/privacy-auto-cleanup.sh ]; then + echo -e " ${GREEN}✓${NC} Auto cleanup script installed" + if crontab -l | grep -q "privacy-auto-cleanup"; then + echo -e " ${GREEN}✓${NC} Auto cleanup scheduled" + else + echo -e " ${YELLOW}!${NC} Auto cleanup script exists but not scheduled" + fi +else + echo -e " ${RED}✗${NC} Auto cleanup not configured" +fi + +# Check current log sizes +echo -e "\n${YELLOW}Current Log Status:${NC}" +total_log_size=$(du -sh /var/log 2>/dev/null | cut -f1 || echo "Unknown") +echo " Total log directory size: $total_log_size" + +if [ -f /var/log/auth.log ]; then + auth_size=$(du -h /var/log/auth.log | cut -f1) + echo " Auth log size: $auth_size" +fi + +if [ -f /var/log/syslog ]; then + syslog_size=$(du -h /var/log/syslog | cut -f1) + echo " Syslog size: $syslog_size" +fi + +# Check systemd journal status +echo -e "\n${YELLOW}Journal Status:${NC}" +if [ -d /var/log/journal ]; then + journal_size=$(du -sh /var/log/journal 2>/dev/null | cut -f1 || echo "Unknown") + echo " Journal size: $journal_size" +else + echo -e " ${GREEN}✓${NC} Persistent journal disabled (using volatile storage)" +fi + +# Privacy configuration summary +echo -e "\n${YELLOW}Privacy Configuration Summary:${NC}" +echo " Log retention: {{ privacy_log_rotation.max_age }} days" +echo " Max log size: {{ privacy_log_rotation.max_size }}MB" +echo " VPN log filtering: {{ privacy_log_filtering.exclude_vpn_logs }}" +echo -e " History clearing: {{ privacy_history_clearing.clear_bash_history }}" + +echo -e "\n${GREEN}Privacy monitoring complete${NC}" \ No newline at end of file diff --git a/roles/privacy/templates/privacy-rsyslog.conf.j2 b/roles/privacy/templates/privacy-rsyslog.conf.j2 new file mode 100644 index 000000000..088d5eb5f --- /dev/null +++ b/roles/privacy/templates/privacy-rsyslog.conf.j2 @@ -0,0 +1,32 @@ +# Privacy-enhanced rsyslog configuration +# Minimal logging configuration for enhanced privacy +# Generated by Algo VPN privacy role + +# Global settings for privacy +$ModLoad imuxsock # provides support for local system logging +$ModLoad imklog # provides kernel logging support + +# Reduce logging verbosity +$KLogPermitNonKernelFacility on +$SystemLogSocketName /run/systemd/journal/syslog + +# Privacy-enhanced rules +{% if privacy_advanced.reduce_kernel_verbosity %} +# Reduce kernel message verbosity +kern.info;kern.!debug /var/log/kern.log +{% else %} +kern.* /var/log/kern.log +{% endif %} + +# Essential system messages only +*.emerg :omusrmsg:* +*.alert /var/log/alert.log +*.crit /var/log/critical.log +*.err /var/log/error.log + +# Compress and limit emergency logs +$template PrivacyTemplate,"%timegenerated% %hostname% %syslogtag%%msg%\n" +$ActionFileDefaultTemplate PrivacyTemplate + +# Stop processing after essential logs to prevent detailed logging +& stop \ No newline at end of file diff --git a/roles/privacy/templates/privacy-shutdown-cleanup.service.j2 b/roles/privacy/templates/privacy-shutdown-cleanup.service.j2 new file mode 100644 index 000000000..9d05b34be --- /dev/null +++ b/roles/privacy/templates/privacy-shutdown-cleanup.service.j2 @@ -0,0 +1,44 @@ +# Privacy shutdown cleanup systemd service +# Clears logs and sensitive data on system shutdown +# Generated by Algo VPN privacy role + +[Unit] +Description=Privacy Cleanup on Shutdown +DefaultDependencies=false +Before=shutdown.target reboot.target halt.target +Requires=-.mount + +[Service] +Type=oneshot +RemainAfterExit=true +ExecStart=/bin/true +ExecStop=/bin/bash -c ' + # Clear all logs + find /var/log -type f -name "*.log" -exec truncate -s 0 {} \; 2>/dev/null || true; + + # Clear rotated logs + find /var/log -type f \( -name "*.log.*" -o -name "*.gz" \) -delete 2>/dev/null || true; + + # Clear systemd journal + if [ -d /var/log/journal ]; then + rm -rf /var/log/journal/* 2>/dev/null || true; + fi; + + # Clear bash history + for user_home in /home/* /root; do + if [ -d "$user_home" ]; then + rm -f "$user_home"/.bash_history 2>/dev/null || true; + rm -f "$user_home"/.zsh_history 2>/dev/null || true; + fi; + done; + + # Clear temporary files + rm -rf /tmp/* /var/tmp/* 2>/dev/null || true; + + # Sync to ensure changes are written + sync; +' +TimeoutStopSec=30 + +[Install] +WantedBy=shutdown.target \ No newline at end of file diff --git a/roles/ssh_tunneling/tasks/main.yml b/roles/ssh_tunneling/tasks/main.yml index bb6bf26da..a462346fc 100644 --- a/roles/ssh_tunneling/tasks/main.yml +++ b/roles/ssh_tunneling/tasks/main.yml @@ -23,7 +23,7 @@ file: path: /var/jail/ state: directory - mode: 0755 + mode: '0755' owner: root group: "{{ root_group | default('root') }}" @@ -87,7 +87,7 @@ template: src: ssh_config.j2 dest: "{{ ssh_tunnels_config_path }}/{{ item }}.ssh_config" - mode: 0700 + mode: '0700' with_items: "{{ users }}" delegate_to: localhost become: false diff --git a/roles/strongswan/tasks/client_configs.yml b/roles/strongswan/tasks/client_configs.yml index 08e514299..a596f756d 100644 --- a/roles/strongswan/tasks/client_configs.yml +++ b/roles/strongswan/tasks/client_configs.yml @@ -19,7 +19,7 @@ template: src: mobileconfig.j2 dest: "{{ ipsec_config_path }}/apple/{{ item.0 }}.mobileconfig" - mode: 0600 + mode: '0600' with_together: - "{{ users }}" - "{{ PayloadContent.results }}" @@ -29,7 +29,7 @@ template: src: client_ipsec.conf.j2 dest: "{{ ipsec_config_path }}/manual/{{ item }}.conf" - mode: 0600 + mode: '0600' with_items: - "{{ users }}" @@ -38,7 +38,7 @@ template: src: client_ipsec.secrets.j2 dest: "{{ ipsec_config_path }}/manual/{{ item }}.secrets" - mode: 0600 + mode: '0600' with_items: - "{{ users }}" @@ -46,4 +46,4 @@ file: path: "{{ ipsec_config_path }}" state: directory - mode: 0700 + mode: '0700' diff --git a/roles/strongswan/tasks/openssl.yml b/roles/strongswan/tasks/openssl.yml index 3d13ad8c3..79782b32d 100644 --- a/roles/strongswan/tasks/openssl.yml +++ b/roles/strongswan/tasks/openssl.yml @@ -36,6 +36,7 @@ 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 @@ -91,11 +92,13 @@ 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: @@ -164,6 +167,7 @@ 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: @@ -178,6 +182,7 @@ mode: "0644" with_items: "{{ client_csr_jobs.results }}" register: client_sign_results + no_log: true - name: Generate p12 files community.crypto.openssl_pkcs12: @@ -189,6 +194,7 @@ 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: @@ -202,11 +208,13 @@ 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 }}" @@ -221,6 +229,7 @@ ansible.builtin.lineinfile: path: "{{ ipsec_pki_path }}/all-users" line: "{{ item }}" + mode: '0644' create: true with_items: "{{ users }}" register: users_file @@ -255,6 +264,7 @@ issuer: CN: "{{ IP_subject_alt_name }}" revoked_certificates: "{{ revoked_certificates }}" + no_log: true - name: Set CRL file permissions file: @@ -270,5 +280,6 @@ copy: src: "{{ ipsec_pki_path }}/crl.pem" dest: "{{ config_prefix | default('/') }}etc/ipsec.d/crls/algo.root.pem" + mode: '0644' notify: - rereadcrls diff --git a/roles/strongswan/tasks/ubuntu.yml b/roles/strongswan/tasks/ubuntu.yml index b17b3a0b4..926019050 100644 --- a/roles/strongswan/tasks/ubuntu.yml +++ b/roles/strongswan/tasks/ubuntu.yml @@ -25,7 +25,7 @@ content: " capability setpcap," owner: root group: root - mode: 0644 + mode: '0644' notify: restart strongswan - name: Ubuntu | Enforcing ipsec with apparmor @@ -49,7 +49,7 @@ file: path: /etc/systemd/system/{{ strongswan_service }}.service.d/ state: directory - mode: 0755 + mode: '0755' owner: root group: root @@ -57,6 +57,7 @@ template: src: 100-CustomLimitations.conf.j2 dest: /etc/systemd/system/{{ strongswan_service }}.service.d/100-CustomLimitations.conf + mode: '0644' notify: - daemon-reload - restart strongswan diff --git a/roles/wireguard/files/wireguard.sh b/roles/wireguard/files/wireguard.sh index aefd44c52..a7d971846 100644 --- a/roles/wireguard/files/wireguard.sh +++ b/roles/wireguard/files/wireguard.sh @@ -5,14 +5,19 @@ # BEFORE: securelevel # KEYWORD: shutdown +# shellcheck source=/dev/null . /etc/rc.subr name="wg" +# shellcheck disable=SC2034 rcvar=wg_enable command="/usr/local/bin/wg-quick" +# shellcheck disable=SC2034 start_cmd=wg_up +# shellcheck disable=SC2034 stop_cmd=wg_down +# shellcheck disable=SC2034 status_cmd=wg_status pidfile="/var/run/$name.pid" load_rc_config "$name" diff --git a/roles/wireguard/tasks/keys.yml b/roles/wireguard/tasks/keys.yml index 3c37d8876..74f380e93 100644 --- a/roles/wireguard/tasks/keys.yml +++ b/roles/wireguard/tasks/keys.yml @@ -17,6 +17,7 @@ with_items: - "{{ users }}" - "{{ IP_subject_alt_name }}" + no_log: true - name: Generate raw preshared keys community.crypto.openssl_privatekey: @@ -36,6 +37,7 @@ with_items: - "{{ users }}" - "{{ IP_subject_alt_name }}" + no_log: true - name: Generate public keys x25519_pubkey: @@ -44,3 +46,13 @@ with_items: - "{{ users }}" - "{{ IP_subject_alt_name }}" + no_log: true + +- name: Set permissions for public keys + file: + path: "{{ wireguard_pki_path }}/public/{{ item }}" + mode: '0644' + with_items: + - "{{ users }}" + - "{{ IP_subject_alt_name }}" + no_log: true diff --git a/roles/wireguard/tasks/main.yml b/roles/wireguard/tasks/main.yml index 1cac2ae1d..307dc3578 100644 --- a/roles/wireguard/tasks/main.yml +++ b/roles/wireguard/tasks/main.yml @@ -72,6 +72,7 @@ args: chdir: "{{ wireguard_config_path }}" executable: bash + no_log: true become: false delegate_to: localhost diff --git a/roles/wireguard/tasks/ubuntu.yml b/roles/wireguard/tasks/ubuntu.yml index 63d61d416..4051d1e98 100644 --- a/roles/wireguard/tasks/ubuntu.yml +++ b/roles/wireguard/tasks/ubuntu.yml @@ -15,7 +15,7 @@ file: path: /etc/systemd/system/wg-quick@{{ wireguard_interface }}.service.d/ state: directory - mode: 0755 + mode: '0755' owner: root group: root @@ -48,7 +48,7 @@ SystemCallErrorNumber=EPERM owner: root group: root - mode: 0644 + mode: '0644' notify: - daemon-reload - restart wireguard diff --git a/server.yml b/server.yml index fd8119e2c..83c606149 100644 --- a/server.yml +++ b/server.yml @@ -177,10 +177,16 @@ - algo_ssh_tunneling tags: ssh_tunneling + - import_role: + name: privacy + when: privacy_enhancements_enabled | default(true) + tags: privacy + - block: - name: Dump the configuration copy: dest: configs/{{ IP_subject_alt_name }}/.config.yml + mode: '0644' content: | server: {{ 'localhost' if inventory_hostname == 'localhost' else inventory_hostname }} server_user: {{ ansible_ssh_user }} diff --git a/tests/legacy-lxd/ipsec-client.sh b/tests/legacy-lxd/ipsec-client.sh index b5d49586f..d72cc08c4 100755 --- a/tests/legacy-lxd/ipsec-client.sh +++ b/tests/legacy-lxd/ipsec-client.sh @@ -8,9 +8,12 @@ CA_CONSTRAINTS="$(openssl verify -verbose \ -CAfile ./configs/10.0.8.100/ipsec/.pki/cacert.pem \ ./configs/10.0.8.100/ipsec/.pki/certs/google-algo-test-pair.com.crt 2>&1)" || true -echo "$CA_CONSTRAINTS" | grep "permitted subtree violation" >/dev/null && \ - echo "Name Constraints test passed" || \ - (echo "Name Constraints test failed" && exit 1) +if echo "$CA_CONSTRAINTS" | grep "permitted subtree violation" >/dev/null; then + echo "Name Constraints test passed" +else + echo "Name Constraints test failed" + exit 1 +fi echo "$CA_CONSTRAINTS" diff --git a/tests/legacy-lxd/pre-deploy.sh b/tests/legacy-lxd/pre-deploy.sh index 0b0c87271..fc9d871e5 100755 --- a/tests/legacy-lxd/pre-deploy.sh +++ b/tests/legacy-lxd/pre-deploy.sh @@ -22,7 +22,7 @@ lxc profile set default raw.lxc 'lxc.apparmor.profile = unconfined' lxc profile set default security.privileged true lxc profile show default -lxc init ubuntu:${UBUNTU_VERSION} algo +lxc init ubuntu:"${UBUNTU_VERSION}" algo lxc network attach lxdbr0 algo eth0 eth0 lxc config device set algo eth0 ipv4.address 10.0.8.100 lxc start algo diff --git a/tests/legacy-lxd/ssh-tunnel.sh b/tests/legacy-lxd/ssh-tunnel.sh index 0b491b9de..30064217e 100755 --- a/tests/legacy-lxd/ssh-tunnel.sh +++ b/tests/legacy-lxd/ssh-tunnel.sh @@ -4,12 +4,18 @@ set -euxo pipefail PASS=$(grep ^p12_password: configs/10.0.8.100/.config.yml | awk '{print $2}' | cut -f2 -d\') -ssh-keygen -p -P ${PASS} -N '' -f configs/10.0.8.100/ssh-tunnel/desktop.pem +ssh-keygen -p -P "${PASS}" -N '' -f configs/10.0.8.100/ssh-tunnel/desktop.pem ssh -o StrictHostKeyChecking=no -D 127.0.0.1:1080 -f -q -C -N desktop@10.0.8.100 -i configs/10.0.8.100/ssh-tunnel/desktop.pem -F configs/10.0.8.100/ssh_config git config --global http.proxy 'socks5://127.0.0.1:1080' -for i in {1..10}; do git clone -vv https://github.com/trailofbits/algo /tmp/ssh-tunnel-check && break || sleep 1; done +for _ in {1..10}; do + if git clone -vv https://github.com/trailofbits/algo /tmp/ssh-tunnel-check; then + break + else + sleep 1 + fi +done echo "SSH tunneling tests passed"