Compare commits

..

3 Commits

Author SHA1 Message Date
d36098c6aa updated admin password in wazuh 2026-09-15 17:53:56 -06:00
9e2e03e6c1 updated admin password 2026-09-15 17:20:29 -06:00
6202985353 added scripts for vpn 2026-09-15 16:33:36 -06:00
9 changed files with 182 additions and 49 deletions

View File

@@ -49,6 +49,7 @@
dest: /etc/modules-load.d/wg-easy.conf
content: |
wireguard
nft_masq
mode: '0644'
- name: Enable IP forwarding on host
@@ -57,7 +58,6 @@
value: '1'
sysctl_set: true
state: present
reload: true
- name: Enable source valid mark on host
ansible.posix.sysctl:
@@ -146,17 +146,18 @@
# - 7359:7359/udp #optional
# - 1900:1900/udp #optional
# WireGuard Easy VPN — deployed on npm_internal behind NPM
# After first deploy, you MUST add NAT hooks in the wg-easy UI:
# 1. Open http://10.10.0.36:51821 in a browser
# 2. Log in with the username/password from vars.yml
# 3. Go to the "Hooks" tab
# 4. PostUp — paste this EXACTLY:
# nft add table inet wg_table; nft add chain inet wg_table prerouting { type nat hook prerouting priority 100 \; }; nft add chain inet wg_table postrouting { type nat hook postrouting priority 100 \; }; nft add rule inet wg_table postrouting ip saddr {{ipv4Cidr}} oifname {{device}} masquerade; nft add rule inet wg_table postrouting ip6 saddr {{ipv6Cidr}} oifname {{device}} masquerade; nft add chain inet wg_table input { type filter hook input priority 0 \; policy accept \; }; nft add rule inet wg_table input udp dport {{port}} accept; nft add rule inet wg_table input tcp dport {{uiPort}} accept; nft add chain inet wg_table forward { type filter hook forward priority 0 \; policy accept \; }; nft add rule inet wg_table forward iifname "wg0" accept; nft add rule inet wg_table forward oifname "wg0" accept;
# 5. PostDown — paste this EXACTLY:
# nft delete table inet wg_table
# 6. Click Save and restart the container
# These hooks enable VPN clients to reach your app containers (10.10.0.0/24)
# WireGuard Easy VPN (v15) — UI at 10.10.0.36:51821 on npm_internal, fronted by NPM
# (vpn.boxswapper.io -> http://10.10.0.36:51821). Login: admin / wireguard_ui_password from vars.yml.
# v15 init is ONE-TIME: the INIT_* envs below are consumed only when /etc/wireguard/wg-easy.db is ABSENT.
# Force a re-init (wipes clients + server keys): podman rm -f wg-easy && rm -rf /opt/apps/wireguard, then re-run.
# NAT from VPN clients to LAN/apps (10.10.1.0/24 -> 10.10.0.0/24) uses NFTABLES hooks —
# wg-easy's iptables-legacy hooks cannot init the nat table inside a podman netns
# ("Table does not exist"), which makes wg-quick up fail and every client API call
# 500 with "Unable to access interface". scripts/patch-wg-hooks.py rewrites the hooks
# to nft rules and the tasks below restart wg-easy to apply them. See the official
# wg-easy "Podman + nftables" tutorial.
# EXTERNAL ACCESS: router MUST forward UDP 51820 -> 192.168.0.98. Test from LTE,
# not your LAN — NAT hairpin breaks public-IP handshakes from inside the network.
- name: Deploy WireGuard Easy (wg-easy)
containers.podman.podman_container:
name: wg-easy
@@ -190,10 +191,37 @@
sysctl:
net.ipv4.ip_forward: "1"
net.ipv4.conf.all.src_valid_mark: "1"
net.ipv6.conf.all.disable_ipv6: "0"
net.ipv6.conf.all.forwarding: "1"
net.ipv6.conf.default.forwarding: "1"
network:
- npm_internal
ip: '{{ wireguard_ip }}'
- name: Copy wg-easy hook repair script
ansible.builtin.copy:
src: ./scripts/patch-wg-hooks.py
dest: /opt/apps/wireguard/patch-wg-hooks.py
owner: root
group: root
mode: '0755'
- name: Replace iptables hooks with nftables (iptables-legacy broken in podman)
ansible.builtin.command: python3 /opt/apps/wireguard/patch-wg-hooks.py
register: wg_hooks_patch
changed_when: "'patched hooks' in wg_hooks_patch.stdout"
- name: Restart wg-easy to apply nft hooks
ansible.builtin.command: podman restart wg-easy
when: "'patched hooks' in wg_hooks_patch.stdout"
- name: Wait for WireGuard Easy web UI
ansible.builtin.wait_for:
host: '{{ wireguard_ip }}'
port: '{{ wireguard_ui_port }}'
delay: 5
timeout: 120
# Wazuh SIEM (single-node: indexer + manager + dashboard) on npm_internal
# Dashboard is NOT exposed on host ports — NPM proxies wazuh.boxswapper.io to https://10.10.0.40:5601
# DNS record must be grey-cloud (Cloudflare 523 lesson from vpn.boxswapper.io), cert via NPM
@@ -207,6 +235,25 @@
mode: preserve
force: true
- name: Check for stale Wazuh certificate residue
ansible.builtin.stat:
path: /opt/apps/wazuh/config/wazuh_indexer_ssl_certs/root-ca.pem
register: wazuh_cert_residue
- name: Remove stale Wazuh certificate directories
ansible.builtin.file:
path: /opt/apps/wazuh/config/wazuh_indexer_ssl_certs
state: absent
when: wazuh_cert_residue.stat.exists and wazuh_cert_residue.stat.isdir
- name: Recreate Wazuh certificates directory
ansible.builtin.file:
path: /opt/apps/wazuh/config/wazuh_indexer_ssl_certs
state: directory
mode: '0755'
owner: root
group: root
- name: Generate Wazuh certificates
ansible.builtin.command: podman run --rm -e CERT_TOOL_VERSION=4.14 -v /opt/apps/wazuh/config/certs.yml:/config/certs.yml:z -v /opt/apps/wazuh/config/wazuh_indexer_ssl_certs/:/certificates:z docker.io/wazuh/wazuh-certs-generator:0.0.3
args:
@@ -263,7 +310,7 @@
env:
INDEXER_URL: "https://wazuh.indexer:9200"
INDEXER_USERNAME: "admin"
INDEXER_PASSWORD: "SecretPassword"
INDEXER_PASSWORD: "*WD*oW9m6iKf4hDovy7BbMmhLxC9D?Tv"
FILEBEAT_SSL_VERIFICATION_MODE: "full"
SSL_CERTIFICATE_AUTHORITIES: "/etc/ssl/root-ca.pem"
SSL_CERTIFICATE: "/etc/ssl/filebeat.pem"
@@ -284,6 +331,8 @@
- "wazuh_logs:/var/ossec/logs"
- "wazuh_queue:/var/ossec/queue"
- "wazuh_var_multigroups:/var/ossec/var/multigroups"
- "wazuh_integrations:/var/ossec/integrations"
- "wazuh_agentless:/var/ossec/agentless"
- "wazuh_active_response:/var/ossec/active-response/bin"
- "wazuh_wodles:/var/ossec/wodles"
- "filebeat_etc:/etc/filebeat"
@@ -306,7 +355,7 @@
state: started
env:
INDEXER_USERNAME: "admin"
INDEXER_PASSWORD: "SecretPassword"
INDEXER_PASSWORD: "*WD*oW9m6iKf4hDovy7BbMmhLxC9D?Tv"
WAZUH_API_URL: "https://wazuh.manager"
DASHBOARD_USERNAME: "kibanaserver"
DASHBOARD_PASSWORD: "kibanaserver"

60
scripts/patch-wg-hooks.py Normal file
View File

@@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""Replace wg-easy iptables hooks with nftables equivalents.
wg-easy images ship iptables-legacy, which cannot initialize the `nat`/`filter`
tables inside a podman container netns on nftables-only kernels (fc42). Every
`wg-quick up wg0` then fails at the PostUp hook, so the wg0 interface is rolled
back and ALL client API calls 500 with "Unable to access interface: No such device".
The official wg-easy podman+nftables tutorial replaces the hooks with nft rules.
This script rewrites hooks_table in /opt/apps/wireguard/wg-easy.db when the hooks
still reference iptables. Run it with the wg-easy container stopped, then restart
the container so wg0.conf is regenerated. Idempotent.
"""
import sqlite3
import sys
DB = "/opt/apps/wireguard/wg-easy.db"
POSTUP = (
"nft add table inet wg_table;"
" nft add chain inet wg_table prerouting { type nat hook prerouting priority 100 \\; };"
" nft add chain inet wg_table postrouting { type nat hook postrouting priority 100 \\; };"
" nft add rule inet wg_table postrouting ip saddr {{ipv4Cidr}} oifname {{device}} masquerade;"
" nft add rule inet wg_table postrouting ip6 saddr {{ipv6Cidr}} oifname {{device}} masquerade;"
" nft add chain inet wg_table input { type filter hook input priority 0 \\; policy accept \\; };"
" nft add rule inet wg_table input udp dport {{port}} accept;"
" nft add rule inet wg_table input tcp dport {{uiPort}} accept;"
" nft add chain inet wg_table forward { type filter hook forward priority 0 \\; policy accept \\; };"
' nft add rule inet wg_table forward iifname "wg0" accept;'
' nft add rule inet wg_table forward oifname "wg0" accept;'
)
POSTDOWN = "nft delete table inet wg_table"
def main() -> int:
con = sqlite3.connect(DB)
try:
rows = con.execute("SELECT id, post_up, post_down FROM hooks_table").fetchall()
changed = 0
for name, post_up, post_down in rows:
post_up = post_up or ""
post_down = post_down or ""
if "iptables" in post_up or "iptables" in post_down:
con.execute(
"UPDATE hooks_table SET post_up=?, post_down=?, updated_at=CURRENT_TIMESTAMP WHERE id=?",
(POSTUP, POSTDOWN, name),
)
print(f"patched hooks for {name!r}")
changed += 1
else:
print(f"hooks ok for {name!r}")
con.commit()
finally:
con.close()
print(f"changed={changed}")
return 0 if changed >= 0 else 1
if __name__ == "__main__":
sys.exit(main())

View File

@@ -46,9 +46,9 @@ POSTGRES_DB: thatoneit
## IMPORTANT: After first run, change wireguard_ui_password to a strong password
## Public IP or DDNS hostname clients connect to — CHANGE THIS before deploying
wireguard_image: ghcr.io/wg-easy/wg-easy:15
wireguard_host: CHANGE_ME
wireguard_host: vpn.boxswapper.io
wireguard_ui_username: admin
wireguard_ui_password: CHANGE_ME
wireguard_ui_password: T3l0penF@5tback
wireguard_port: 51820
wireguard_ui_port: 51821
wireguard_cidr: 10.10.1.0/24

View File

Binary file not shown.

View File

@@ -1,10 +1,24 @@
<ossec_config>
<global>
<agents_disconnection_time>15m</agents_disconnection_time>
<jsonout_output>yes</jsonout_output>
<alerts_log>yes</alerts_log>
<logall>no</logall>
<logall_json>no</logall_json>
<email_notification>no</email_notification>
<smtp_server>smtp.example.wazuh.com</smtp_server>
<email_from>wazuh@example.wazuh.com</email_from>
<email_to>recipient@example.wazuh.com</email_to>
<email_maxperhour>12</email_maxperhour>
<email_log_source>alerts.log</email_log_source>
<agents_disconnection_time>10m</agents_disconnection_time>
<agents_disconnection_alert_time>0</agents_disconnection_alert_time>
<update_check>yes</update_check>
</global>
<alerts>
<log_alert_level>3</log_alert_level>
<email_alert_level>12</email_alert_level>
</alerts>
<!-- Choose between "plain", "json", or "plain,json" for the format of internal logs -->
<logging>
<log_format>plain</log_format>
@@ -20,6 +34,8 @@
<!-- Policy monitoring -->
<rootcheck>
<disabled>no</disabled>
<check_files>yes</check_files>
<check_trojans>yes</check_trojans>
<check_dev>yes</check_dev>
<check_sys>yes</check_sys>
<check_pids>yes</check_pids>
@@ -29,12 +45,31 @@
<!-- Frequency that rootcheck is executed - every 12 hours -->
<frequency>43200</frequency>
<skip_nfs>yes</skip_nfs>
<rootkit_files>etc/rootcheck/rootkit_files.txt</rootkit_files>
<rootkit_trojans>etc/rootcheck/rootkit_trojans.txt</rootkit_trojans>
<ignore>/var/lib/containerd</ignore>
<ignore>/var/lib/docker/overlay2</ignore>
<skip_nfs>yes</skip_nfs>
</rootcheck>
<wodle name="cis-cat">
<disabled>yes</disabled>
<timeout>1800</timeout>
<interval>1d</interval>
<scan-on-start>yes</scan-on-start>
<java_path>wodles/java</java_path>
<ciscat_path>wodles/ciscat</ciscat_path>
</wodle>
<!-- Osquery integration -->
<wodle name="osquery">
<disabled>yes</disabled>
<run_daemon>yes</run_daemon>
<log_path>/var/log/osquery/osqueryd.results.log</log_path>
<config_path>/etc/osquery/osquery.conf</config_path>
<add_labels>yes</add_labels>
</wodle>
<!-- System inventory -->
<wodle name="syscollector">
<disabled>no</disabled>
@@ -46,15 +81,9 @@
<packages>yes</packages>
<ports all="yes">yes</ports>
<processes>yes</processes>
<users>yes</users>
<groups>yes</groups>
<services>yes</services>
<browser_extensions>yes</browser_extensions>
<!-- Database synchronization settings -->
<synchronization>
<enabled>yes</enabled>
<interval>5m</interval>
<max_eps>10</max_eps>
</synchronization>
</wodle>
@@ -63,13 +92,7 @@
<enabled>yes</enabled>
<scan_on_start>yes</scan_on_start>
<interval>12h</interval>
<!-- Database synchronization settings -->
<synchronization>
<enabled>yes</enabled>
<interval>5m</interval>
<max_eps>10</max_eps>
</synchronization>
<skip_nfs>yes</skip_nfs>
</sca>
<vulnerability-detection>
@@ -99,6 +122,8 @@
<!-- Frequency that syscheck is executed default every 12 hours -->
<frequency>43200</frequency>
<scan_on_start>yes</scan_on_start>
<!-- Generate alert when new file detected -->
<alert_new_files>yes</alert_new_files>
@@ -138,12 +163,13 @@
<process_priority>10</process_priority>
<!-- Maximum output throughput -->
<max_eps>50</max_eps>
<max_eps>100</max_eps>
<!-- Database synchronization settings -->
<synchronization>
<enabled>yes</enabled>
<interval>5m</interval>
<max_interval>1h</max_interval>
<max_eps>10</max_eps>
</synchronization>
</syscheck>
@@ -238,6 +264,13 @@
<rule_dir>etc/rules</rule_dir>
</ruleset>
<rule_test>
<enabled>yes</enabled>
<threads>1</threads>
<max_sessions>64</max_sessions>
<session_timeout>15m</session_timeout>
</rule_test>
<!-- Configuration for wazuh-authd -->
<auth>
<disabled>no</disabled>
@@ -270,19 +303,9 @@
</ossec_config>
<ossec_config>
<localfile>
<log_format>journald</log_format>
<location>journald</location>
</localfile>
<localfile>
<log_format>audit</log_format>
<location>/var/log/audit/audit.log</location>
</localfile>
<localfile>
<log_format>syslog</log_format>
<location>/var/ossec/logs/active-responses.log</location>
</localfile>
</ossec_config>
</ossec_config>

View File

@@ -2,7 +2,7 @@ server.host: 0.0.0.0
server.port: 5601
opensearch.hosts: https://wazuh.indexer:9200
opensearch.ssl.verificationMode: certificate
opensearch.requestHeadersAllowlist: ["securitytenant","Authorization"]
opensearch.requestHeadersWhitelist: ["securitytenant","Authorization"]
opensearch_security.multitenancy.enabled: false
opensearch_security.readonly_mode.roles: ["kibana_read_only"]
server.ssl.enabled: true

View File

@@ -11,7 +11,7 @@ _meta:
## Demo users
admin:
hash: "$2y$12$K/SpwjtB.wOHJ/Nc6GVRDuc1h0rM1DfvziFRNPtk27P.c4yDr9njO"
hash: "$2y$12$lMOHMX.dc/xeIAMzqOxo.OBwB2IrhCd2nmhG6GI04hrXxW.KtB9cG"
reserved: true
backend_roles:
- "admin"

View File

@@ -6,6 +6,7 @@ path.logs: /var/log/wazuh-indexer
discovery.type: single-node
http.port: 9200-9299
transport.tcp.port: 9300-9399
compatibility.override_main_response_version: true
plugins.security.ssl.http.pemcert_filepath: /usr/share/wazuh-indexer/config/certs/wazuh.indexer.pem
plugins.security.ssl.http.pemkey_filepath: /usr/share/wazuh-indexer/config/certs/wazuh.indexer.key
plugins.security.ssl.http.pemtrustedcas_filepath: /usr/share/wazuh-indexer/config/certs/root-ca.pem

View File

@@ -19,7 +19,7 @@ services:
environment:
- INDEXER_URL=https://wazuh.indexer:9200
- INDEXER_USERNAME=admin
- INDEXER_PASSWORD=SecretPassword
- INDEXER_PASSWORD=*WD*oW9m6iKf4hDovy7BbMmhLxC9D?Tv
- FILEBEAT_SSL_VERIFICATION_MODE=full
- SSL_CERTIFICATE_AUTHORITIES=/etc/ssl/root-ca.pem
- SSL_CERTIFICATE=/etc/ssl/filebeat.pem