diff --git a/ansible_collections/nullified/infrastructure/extensions/molecule/default/converge.yml b/ansible_collections/nullified/infrastructure/extensions/molecule/default/converge.yml index 5c6730d..76998a3 100644 --- a/ansible_collections/nullified/infrastructure/extensions/molecule/default/converge.yml +++ b/ansible_collections/nullified/infrastructure/extensions/molecule/default/converge.yml @@ -82,3 +82,9 @@ ansible.builtin.include_role: name: nullified.infrastructure.deluge tasks_from: main.yml + - name: Testing valkey role + ansible.builtin.include_role: + name: nullified.infrastructure.valkey + tasks_from: main.yml + + - meta: flush_handlers diff --git a/ansible_collections/nullified/infrastructure/extensions/molecule/default/platform_vars.yml b/ansible_collections/nullified/infrastructure/extensions/molecule/default/platform_vars.yml index 92b6b17..a07a3df 100644 --- a/ansible_collections/nullified/infrastructure/extensions/molecule/default/platform_vars.yml +++ b/ansible_collections/nullified/infrastructure/extensions/molecule/default/platform_vars.yml @@ -10,3 +10,5 @@ external_provisioner_source_ips: - '192.0.0.0/24' - '192.168.0.0/16' - '198.18.0.0/15' +security_sysctl_configuration: + 'vm.overcommit_memory': 1 diff --git a/ansible_collections/nullified/infrastructure/roles/valkey/README.md b/ansible_collections/nullified/infrastructure/roles/valkey/README.md new file mode 100644 index 0000000..edb3eca --- /dev/null +++ b/ansible_collections/nullified/infrastructure/roles/valkey/README.md @@ -0,0 +1,29 @@ +Role Name +========= + +@todo + +Requirements +------------ + +@todo + +Role Variables +-------------- + +@todo + +Dependencies +------------ + +@todo + +Example Playbook +---------------- + +@todo + +License +------- + +[MIT](https://opensource.org/license/mit) diff --git a/ansible_collections/nullified/infrastructure/roles/valkey/defaults/main.yml b/ansible_collections/nullified/infrastructure/roles/valkey/defaults/main.yml new file mode 100644 index 0000000..ade04eb --- /dev/null +++ b/ansible_collections/nullified/infrastructure/roles/valkey/defaults/main.yml @@ -0,0 +1,41 @@ +--- +valkey_github_token: "{{ custom_github_token | default('') }}" +valkey_binary_filepath: /usr/local/bin/valkey-server +valkey_config_dir: /etc/valkey +valkey_user: valkey +valkey_group: valkey +valkey_install_dir: /var/lib/valkey +valkey_version: latest +valkey_use_tls: true +valkey_generate_cert: true # if set to false you must override valkey_tls_{certfile, keyfile} +valkey_enable_unixsocket: false + +# TLS configuration +valkey_tls_certfile: ~ +valkey_tls_keyfile: ~ +valkey_tls_keyfile_pass: ~ + +# Connection configuration +valkey_server_bind_addresses: ['127.0.0.1', '-::1'] +valkey_server_port: 0 # default is 0 because we enable TLS +valkey_server_tls_port: 6379 +valkey_server_tcp_backlog: 511 +valkey_server_unixsocket: /run/valkey.sock +valkey_server_unixsocketgroup: wheel +valkey_server_unixsocketperm: 700 +valkey_server_timeout: 0 +valkey_server_tcp_keepalive: 300 + +# Various +valkey_server_loglevel: notice +valkey_server_log_format: logfmt +valkey_server_log_timestamp_format: iso8601 +valkey_server_databases: 16 +valkey_server_sanitize_dump_payload: clients +valkey_server_maxclients: 10000 +valkey_server_maxmemory: ~ # in bytes +valkey_server_maxmemory_policy: allkeys-lfu +valkey_server_maxmemory_samples: 5 +valkey_server_active_expire_effort: 1 +valkey_server_lua_time_limit: 5000 +valkey_server_busy_reply_threshold: 5000 diff --git a/ansible_collections/nullified/infrastructure/roles/valkey/handlers/main.yml b/ansible_collections/nullified/infrastructure/roles/valkey/handlers/main.yml new file mode 100644 index 0000000..14b4910 --- /dev/null +++ b/ansible_collections/nullified/infrastructure/roles/valkey/handlers/main.yml @@ -0,0 +1,15 @@ +--- +- name: restart service + become: true + ansible.builtin.systemd: + name: valkey.service + enabled: true + state: restarted + daemon_reload: true + +- name: restart firewall + become: true + ansible.builtin.systemd: + name: nftables.service + enabled: true + state: restarted diff --git a/ansible_collections/nullified/infrastructure/roles/valkey/meta/main.yml b/ansible_collections/nullified/infrastructure/roles/valkey/meta/main.yml new file mode 100644 index 0000000..b87b2a1 --- /dev/null +++ b/ansible_collections/nullified/infrastructure/roles/valkey/meta/main.yml @@ -0,0 +1,13 @@ +--- +galaxy_info: + author: Florian L. + namespace: nullified + description: + license: MIT + min_ansible_version: 2.15 + platforms: + - name: Debian + versions: + - bookworm + galaxy_tags: [] +dependencies: [] diff --git a/ansible_collections/nullified/infrastructure/roles/valkey/tasks/gather_facts.yml b/ansible_collections/nullified/infrastructure/roles/valkey/tasks/gather_facts.yml new file mode 100644 index 0000000..265d0b3 --- /dev/null +++ b/ansible_collections/nullified/infrastructure/roles/valkey/tasks/gather_facts.yml @@ -0,0 +1,16 @@ +--- +- name: find systemd unit directory + become: true + ansible.builtin.command: pkg-config systemd --variable=systemdsystemunitdir + changed_when: false + register: systemd_unit_directory_cmd +- name: find systemd version + become: true + ansible.builtin.shell: > + systemctl --version | awk '{if($1=="systemd" && $2~"^[0-9]+$"){print $2}}' + changed_when: false + register: systemd_version_cmd +- name: set facts + ansible.builtin.set_fact: + systemd_unit_directory: "{{ systemd_unit_directory_cmd.stdout }}" + systemd_version: "{{ systemd_version_cmd.stdout | int }}" diff --git a/ansible_collections/nullified/infrastructure/roles/valkey/tasks/main.yml b/ansible_collections/nullified/infrastructure/roles/valkey/tasks/main.yml new file mode 100644 index 0000000..5011422 --- /dev/null +++ b/ansible_collections/nullified/infrastructure/roles/valkey/tasks/main.yml @@ -0,0 +1,102 @@ +--- +- name: install dependencies + become: true + ansible.builtin.apt: + update_cache: true + force_apt_get: true + state: present + cache_valid_time: 3600 + pkg: + - build-essential + - git + - libjemalloc2 + - libjemalloc-dev + - libssl-dev + - libsystemd-dev + - python3-cryptography + - ssl-cert +- name: gather facts + ansible.builtin.include_tasks: gather_facts.yml +- name: create service group + become: true + ansible.builtin.group: + name: "{{ valkey_group }}" + system: true + state: present +- name: create service user + become: true + ansible.builtin.user: + name: "{{ valkey_user }}" + group: "{{ valkey_group }}" + shell: "/usr/sbin/nologin" + home: "{{ valkey_install_dir }}" + create_home: true + system: true + state: present +- name: fetch Valkey {{ valkey_version }} source code archive + become: true + nullified.infrastructure.github_artifact: + asset_type: tag + version: "{{ valkey_version | default('latest') }}" + github_token: "{{ valkey_github_token }}" + repository: "valkey-io/valkey" + creates: "{{ valkey_binary_filepath | default('/usr/local/bin/valkey-server') }}" + cmds: + - tar -zxf {asset_dirname}/{asset_filename} + - |- + cd $(find . -maxdepth 1 -name valkey-io-valkey\* -type d); + make -j $(nproc) BUILD_TLS=yes USE_SYSTEMD=yes && + make install +- name: create directories + become: true + ansible.builtin.file: + path: '{{ item }}' + owner: '{{ valkey_user }}' + group: '{{ valkey_group }}' + mode: 'u=rwX,g=rX,o=' + state: directory + loop: + - '{{ valkey_config_dir }}' + - '{{ valkey_config_dir }}/conf.d' + - '{{ valkey_install_dir }}' +- name: create basic configuration + become: true + ansible.builtin.template: + src: '../templates/valkey.conf.j2' + dest: '{{ valkey_config_dir }}/valkey.conf' + owner: '{{ valkey_user }}' + group: '{{ valkey_group }}' + mode: 'u=rw,g=r,o=' +- name: create self-signed certificate + become: true + block: + - name: create private key + community.crypto.openssl_privatekey: + path: '{{ valkey_config_dir }}/valkey.key' + mode: '0600' + owner: '{{ valkey_user }}' + group: '{{ valkey_group }}' + curve: secp521r1 + - name: create self-signed certificate + community.crypto.x509_certificate: + path: '{{ valkey_config_dir }}/valkey.crt' + privatekey_path: '{{ valkey_config_dir }}/valkey.key' + provider: selfsigned + mode: '0644' + owner: '{{ valkey_user }}' + group: '{{ valkey_group }}' + when: valkey_generate_cert is truthy +- name: install Valkey service file + become: true + ansible.builtin.template: + src: ../templates/systemd/valkey.service.j2 + dest: '{{ systemd_unit_directory }}/valkey.service' + owner: root + group: root + mode: 'u=rwX,g=rX,o=' + notify: + - 'valkey : restart service' +- meta: flush_handlers +- name: test server + ansible.builtin.command: valkey-cli --tls --insecure info + changed_when: false diff --git a/ansible_collections/nullified/infrastructure/roles/valkey/templates/systemd/valkey.service.j2 b/ansible_collections/nullified/infrastructure/roles/valkey/templates/systemd/valkey.service.j2 new file mode 100644 index 0000000..9f4de2c --- /dev/null +++ b/ansible_collections/nullified/infrastructure/roles/valkey/templates/systemd/valkey.service.j2 @@ -0,0 +1,58 @@ +[Unit] +Description=Valey service +Documentation=https://github.com/valkey-io/valkey-doc +Wants=network-online.target +After=network-online.target + +[Service] +User={{ valkey_user }} +Group={{ valkey_group }} +Type=notify +UMask=0077 +ExecStart={{ valkey_binary_filepath }} {{ valkey_config_dir }}/valkey.conf +Restart=on-failure +RestartSec=3 +TimeoutStartSec=infinity +TimeoutStopSec=infinity +WorkingDirectory=~ +LimitNOFILE=10032 + +# Security Hardening +PrivateTmp=true +CapabilityBoundingSet=CAP_SYS_RESOURCE +{% if systemd_version | int >= 187 %} +NoNewPrivileges=true +SystemCallFilter=@system-service +{% endif %} +{%+ if systemd_version | int >= 209 %}SystemCallArchitectures=native{%- endif +%} +{% if systemd_version | int >= 214 %} +ProtectHome=true +ProtectSystem=true +{% endif %} +{% if systemd_version | int >= 231 %} +ReadOnlyPaths=/ +ReadWritePaths={{ valkey_config_dir }} {{ valkey_install_dir }} +RestrictRealtime=true +{% endif %} +{% if systemd_version | int >= 232 %} +ProtectControlGroups=true +ProtectKernelModules=true +ProtectKernelTunables=true +RemoveIPC=true +{% endif %} +{% if systemd_version | int >= 233 %} +MountAPIVFS=true +RestrictNamespaces=ipc net mnt pid +{% endif %} +{%+ if systemd_version | int >= 235 %}LockPersonality=true{%- endif +%} +{% if systemd_version | int >= 242 %} +ProtectHostname=true +RestrictSUIDSGID=true +{% endif %} +{%+ if systemd_version | int >= 243 %}OOMScoreAdjust=-900{%- endif +%} +{%+ if systemd_version | int >= 244 %}ProtectKernelLogs=true{%- endif +%} +{%+ if systemd_version | int >= 245 %}ProtectClock=true{%- endif +%} +{%+ if systemd_version | int >= 247 %}ProtectProc=invisible{%- endif +%} + +[Install] +WantedBy=multi-user.target diff --git a/ansible_collections/nullified/infrastructure/roles/valkey/templates/valkey.conf.j2 b/ansible_collections/nullified/infrastructure/roles/valkey/templates/valkey.conf.j2 new file mode 100644 index 0000000..d86dd5e --- /dev/null +++ b/ansible_collections/nullified/infrastructure/roles/valkey/templates/valkey.conf.j2 @@ -0,0 +1,72 @@ +daemonize no +supervised systemd +bind "{{ valkey_server_bind_addresses | default(['127.0.0.1', '-::1'], True) | join(' ') }}" +protected-mode yes +enable-protected-configs {{ valkey_enable_protected_configs | default('no', True) }} +enable-debug-command {{ valkey_enable_debug_command | default('no', True) }} +enable-module-command {{ valkey_enable_module_command | default('no', True) }} +# default for port is 0 because we enable TLS +port {{ valkey_server_port | default(0 if valkey_use_tls else 6379, True) }} +{% if valkey_use_tls is truthy %}tls-port {{ valkey_server_tls_port | default(6379, True) }}{% endif +%} +tcp-backlog {{ valkey_server_tcp_backlog | default(511, True) }} +{% if valkey_enable_unixsocket is truthy %} +unixsocket {{ valkey_server_unixsocket | default('/run/valkey.sock', True) }} +unixsocketgroup {{ valkey_server_unixsocketgroup | default('wheel', True) }} +unixsocketperm {{ valkey_server_unixsocketperm | default(700, True) }} +{% endif %} +timeout {{ valkey_server_timeout | default(0, True) }} +tcp-keepalive {{ valkey_server_tcp_keepalive | default(300, True) }} +{% if valkey_use_tls is truthy %} +tls-cert-file "{{ valkey_tls_certfile | default(valkey_config_dir ~ '/valkey.crt', True) | quote }}" +tls-key-file "{{ valkey_tls_keyfile | default(valkey_config_dir ~ '/valkey.key', True) | quote }}" +{% if valkey_tls_keyfile_pass is truthy %}tls-key-file-pass "{{ valkey_tls_keyfile_pass | quote }}"{% endif %} +{% endif %} +tls-ca-cert-dir "/etc/ssl/certs" +tls-auth-clients optional +tls-replication yes +tls-cluster yes +tls-protocols "TLSv1.2 TLSv1.3" +tls-ciphersuites TLS_CHACHA20_POLY1305_SHA256 +tls-prefer-server-ciphers yes +tls-session-cache-size 5000 +tls-session-cache-timeout 60 +loglevel {{ valkey_server_loglevel | default('notice', True) }} +log-format {{ valkey_server_log_format | default('logfmt', True) }} +log-timestamp-format {{ valkey_server_log_timestamp_format | default('iso8601', True) }} +syslog-enabled yes +syslog-ident valkey +databases {{ valkey_server_databases | default(16, True) }} +always-show-logo no +hide-user-data-from-log yes +set-proc-title no +locale-collate {{ valkey_server_locale_collate | default('', True) | quote }} +stop-writes-on-bgsave-error yes +rdbcompression yes +rdbchecksum yes +rdb-version-check strict +sanitize-dump-payload {{ valkey_server_sanitize_dump_payload | default('clients', True) | quote }} +dir ./ +maxclients {{ valkey_server_maxclients | default(10000, True) }} +{% if valkey_server_maxmemory is truthy %}maxmemory {{ valkey_server_maxmemory }}{% endif %} +maxmemory-policy {{ valkey_server_maxmemory_policy | default('allkeys-lfu', True) }} +maxmemory-samples {{ valkey_server_maxmemory_samples | default(5, True) }} +active-expire-effort {{ valkey_server_active_expire_effort | default(1, True) }} +lazyfree-lazy-eviction yes +lazyfree-lazy-expire yes +lazyfree-lazy-server-del yes +replica-lazy-flush yes +lazyfree-lazy-user-del yes +lazyfree-lazy-user-flush yes +disable-thp yes +shutdown-on-sigint default +shutdown-on-sigterm default +lua-time-limit {{ valkey_server_lua_time_limit | default(5000, True) }} +busy-reply-threshold {{ valkey_server_busy_reply_threshold | default(5000, True) }} +commandlog-execution-slower-than 10000 +commandlog-slow-execution-max-len 128 +commandlog-request-larger-than 1048576 +commandlog-large-request-max-len 128 +commandlog-reply-larger-than 1048576 +commandlog-large-reply-max-len 128 + +include {{ valkey_config_dir }}/conf.d/*.conf diff --git a/ansible_collections/nullified/infrastructure/roles/valkey/tests/inventory b/ansible_collections/nullified/infrastructure/roles/valkey/tests/inventory new file mode 100644 index 0000000..878877b --- /dev/null +++ b/ansible_collections/nullified/infrastructure/roles/valkey/tests/inventory @@ -0,0 +1,2 @@ +localhost + diff --git a/ansible_collections/nullified/infrastructure/roles/valkey/tests/test.yml b/ansible_collections/nullified/infrastructure/roles/valkey/tests/test.yml new file mode 100644 index 0000000..ec10bb2 --- /dev/null +++ b/ansible_collections/nullified/infrastructure/roles/valkey/tests/test.yml @@ -0,0 +1,5 @@ +--- +- hosts: localhost + remote_user: root + roles: + - test diff --git a/ansible_collections/nullified/infrastructure/roles/valkey/vars/main.yml b/ansible_collections/nullified/infrastructure/roles/valkey/vars/main.yml new file mode 100644 index 0000000..6f684a0 --- /dev/null +++ b/ansible_collections/nullified/infrastructure/roles/valkey/vars/main.yml @@ -0,0 +1,4 @@ +--- +valkey_enable_protected_configs: no +valkey_enable_debug_command: no +valkey_enable_module_command: no diff --git a/ansible_galaxy-requirements.yml b/ansible_galaxy-requirements.yml index ba0d453..bf6a890 100644 --- a/ansible_galaxy-requirements.yml +++ b/ansible_galaxy-requirements.yml @@ -2,6 +2,8 @@ collections: - name: kubernetes.core version: 5.1.0 + - name: community.crypto + version: 2.26.0 - name: community.general version: 10.3.0 - name: ansible.utils diff --git a/inventory/host_vars/lithium/vars.yml b/inventory/host_vars/lithium/vars.yml index fc0f50d..20dcf6a 100644 --- a/inventory/host_vars/lithium/vars.yml +++ b/inventory/host_vars/lithium/vars.yml @@ -10,3 +10,5 @@ deluge_allow_remote_control: '{{ vault_deluge_allow_remote_control }}' deluge_allowed_remotes: '{{ vault_deluge_allowed_remotes }}' global_dns_udp_dns4: "{{ vault_global_dns_udp_dns4 }}" global_dns_udp_dns6: "{{ vault_global_dns_udp_dns6 }}" +security_sysctl_configuration: + 'vm.overcommit_memory': 1 diff --git a/playbooks/external.yml b/playbooks/external.yml index 07103a0..6a685ac 100644 --- a/playbooks/external.yml +++ b/playbooks/external.yml @@ -57,3 +57,13 @@ apply: tags: [deluge] tags: [deluge] + +- name: setup valkey instances + hosts: external:&valkey + tasks: + - name: include valkey role + ansible.builtin.include_role: + name: nullified.infrastructure.valkey + apply: + tags: [valkey] + tags: [valkey] diff --git a/requirements.txt b/requirements.txt index 348d466..6e7f62f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ ansible-core==2.18.3 +cryptography==44.0.2 docker==7.1.0 Jinja2==3.1.6 jsonschema==4.23.0