Update README and deployment playbooks

This commit is contained in:
Gonzalo Acuña 2022-02-21 09:41:22 -03:00
parent 78ff9920aa
commit f531fa2728
No known key found for this signature in database
GPG Key ID: 646BA79A313A2270
4 changed files with 157 additions and 120 deletions

195
README.md
View File

@ -5,7 +5,7 @@
[![Documentation](https://img.shields.io/badge/docs-view-green.svg)](https://documentation.wazuh.com) [![Documentation](https://img.shields.io/badge/docs-view-green.svg)](https://documentation.wazuh.com)
[![Documentation](https://img.shields.io/badge/web-view-green.svg)](https://wazuh.com) [![Documentation](https://img.shields.io/badge/web-view-green.svg)](https://wazuh.com)
These playbooks install and configure Wazuh agent, manager and Elastic Stack. These playbooks install and configure Wazuh agent, manager and indexer and dashboard.
## Branches ## Branches
* `master` branch contains the latest code, be aware of possible bugs on this branch. * `master` branch contains the latest code, be aware of possible bugs on this branch.
@ -15,7 +15,7 @@ These playbooks install and configure Wazuh agent, manager and Elastic Stack.
| Wazuh version | Elastic | ODFE | | Wazuh version | Elastic | ODFE |
|---------------|---------|--------| |---------------|---------|--------|
| v4.3.0 | 7.10.2 | 1.13.2 | | v4.3.0 | | 1.13.2 |
| v4.2.5 | 7.10.2 | 1.13.2 | | v4.2.5 | 7.10.2 | 1.13.2 |
| v4.2.4 | 7.10.2 | 1.13.2 | | v4.2.4 | 7.10.2 | 1.13.2 |
| v4.2.3 | 7.10.2 | 1.13.2 | | v4.2.3 | 7.10.2 | 1.13.2 |
@ -41,9 +41,9 @@ These playbooks install and configure Wazuh agent, manager and Elastic Stack.
│ │ │ ├── ansible-elasticsearch │ │ │ ├── ansible-elasticsearch
│ │ │ ├── ansible-kibana │ │ │ ├── ansible-kibana
│ │ │ │
│ │ ├── opendistro │ │ ├── opensearch
│ │ │ ├── opendistro-elasticsearch │ │ │ ├── wazuh-dashboard
│ │ │ ├── opendistro-kibana │ │ │ ├── wazuh-indexer
│ │ │ │
│ │ ├── wazuh │ │ ├── wazuh
│ │ │ ├── ansible-filebeat │ │ │ ├── ansible-filebeat
@ -60,10 +60,12 @@ These playbooks install and configure Wazuh agent, manager and Elastic Stack.
│ │ ├── wazuh-elastic_stack-distributed.yml │ │ ├── wazuh-elastic_stack-distributed.yml
│ │ ├── wazuh-elastic_stack-single.yml │ │ ├── wazuh-elastic_stack-single.yml
│ │ ├── wazuh-kibana.yml │ │ ├── wazuh-kibana.yml
│ │ ├── wazuh-manager.yml
│ │ ├── wazuh-manager-oss.yml │ │ ├── wazuh-manager-oss.yml
│ │ ├── wazuh-opendistro.yml │ │ ├── wazuh-manager.yml
│ │ ├── wazuh-opendistro-kibana.yml │ │ ├── wazuh-opensearch-dashboard.yml
| | ├── wazuh-opensearch-production-ready
│ │ ├── wazuh-opensearch-single.yml
│ │ ├── wazuh-opensearch.yml
│ ├── README.md │ ├── README.md
│ ├── VERSION │ ├── VERSION
@ -78,82 +80,97 @@ The hereunder example playbook uses the `wazuh-ansible` role to provision a prod
```yaml ```yaml
--- ---
# Certificates generation # Certificates generation
- hosts: es1 - hosts: wi1
roles: roles:
- role: ../roles/opendistro/opendistro-elasticsearch - role: ../roles/opensearch/wazuh-indexer
elasticsearch_network_host: "{{ private_ip }}" indexer_network_host: "{{ private_ip }}"
elasticsearch_cluster_nodes: indexer_cluster_nodes:
- "{{ hostvars.es1.private_ip }}" - "{{ hostvars.wi1.private_ip }}"
- "{{ hostvars.es2.private_ip }}" - "{{ hostvars.wi2.private_ip }}"
- "{{ hostvars.es3.private_ip }}" - "{{ hostvars.wi3.private_ip }}"
elasticsearch_discovery_nodes: indexer_discovery_nodes:
- "{{ hostvars.es1.private_ip }}" - "{{ hostvars.wi1.private_ip }}"
- "{{ hostvars.es2.private_ip }}" - "{{ hostvars.wi2.private_ip }}"
- "{{ hostvars.es3.private_ip }}" - "{{ hostvars.wi3.private_ip }}"
perform_installation: false perform_installation: false
become: yes become: no
become_user: root
vars: vars:
elasticsearch_node_master: true indexer_node_master: true
instances: instances:
node1: node1:
name: node-1 # Important: must be equal to elasticsearch_node_name. name: node-1 # Important: must be equal to indexer_node_name.
ip: "{{ hostvars.es1.private_ip }}" # When unzipping, the node will search for its node name folder to get the cert. ip: "{{ hostvars.wi1.private_ip }}" # When unzipping, the node will search for its node name folder to get the cert.
role: indexer
node2: node2:
name: node-2 name: node-2
ip: "{{ hostvars.es2.private_ip }}" ip: "{{ hostvars.wi2.private_ip }}"
role: indexer
node3: node3:
name: node-3 name: node-3
ip: "{{ hostvars.es3.private_ip }}" ip: "{{ hostvars.wi3.private_ip }}"
role: indexer
node4: node4:
name: node-4 name: node-4
ip: "{{ hostvars.manager.private_ip }}" ip: "{{ hostvars.manager.private_ip }}"
role: wazuh
node_type: master
node5: node5:
name: node-5 name: node-5
ip: "{{ hostvars.worker.private_ip }}" ip: "{{ hostvars.worker.private_ip }}"
role: wazuh
node_type: worker
node6: node6:
name: node-6 name: node-6
ip: "{{ hostvars.kibana.private_ip }}" ip: "{{ hostvars.dashboard.private_ip }}"
role: dashboard
tags: tags:
- generate-certs - generate-certs
#ODFE Cluster #Wazuh Indexer Cluster
- hosts: odfe_cluster - hosts: wi_cluster
strategy: free strategy: free
roles: roles:
- role: ../roles/opendistro/opendistro-elasticsearch - role: ../roles/opensearch/wazuh-indexer
elasticsearch_network_host: "{{ private_ip }}" indexer_network_host: "{{ private_ip }}"
become: yes become: yes
become_user: root become_user: root
vars: vars:
elasticsearch_cluster_nodes: indexer_cluster_nodes:
- "{{ hostvars.es1.private_ip }}" - "{{ hostvars.wi1.private_ip }}"
- "{{ hostvars.es2.private_ip }}" - "{{ hostvars.wi2.private_ip }}"
- "{{ hostvars.es3.private_ip }}" - "{{ hostvars.wi3.private_ip }}"
elasticsearch_discovery_nodes: indexer_discovery_nodes:
- "{{ hostvars.es1.private_ip }}" - "{{ hostvars.wi1.private_ip }}"
- "{{ hostvars.es2.private_ip }}" - "{{ hostvars.wi2.private_ip }}"
- "{{ hostvars.es3.private_ip }}" - "{{ hostvars.wi3.private_ip }}"
elasticsearch_node_master: true indexer_node_master: true
instances: instances:
node1: node1:
name: node-1 # Important: must be equal to elasticsearch_node_name. name: node-1 # Important: must be equal to indexer_node_name.
ip: "{{ hostvars.es1.private_ip }}" # When unzipping, the node will search for its node name folder to get the cert. ip: "{{ hostvars.wi1.private_ip }}" # When unzipping, the node will search for its node name folder to get the cert.
role: indexer
node2: node2:
name: node-2 name: node-2
ip: "{{ hostvars.es2.private_ip }}" ip: "{{ hostvars.wi2.private_ip }}"
role: indexer
node3: node3:
name: node-3 name: node-3
ip: "{{ hostvars.es3.private_ip }}" ip: "{{ hostvars.wi3.private_ip }}"
role: indexer
node4: node4:
name: node-4 name: node-4
ip: "{{ hostvars.manager.private_ip }}" ip: "{{ hostvars.manager.private_ip }}"
role: wazuh
node_type: master
node5: node5:
name: node-5 name: node-5
ip: "{{ hostvars.worker.private_ip }}" ip: "{{ hostvars.worker.private_ip }}"
role: wazuh
node_type: worker
node6: node6:
name: node-6 name: node-6
ip: "{{ hostvars.kibana.private_ip }}" ip: "{{ hostvars.dashboard.private_ip }}"
role: dashboard
#Wazuh cluster #Wazuh cluster
- hosts: manager - hosts: manager
@ -180,10 +197,13 @@ The hereunder example playbook uses the `wazuh-ansible` role to provision a prod
nodes: nodes:
- "{{ hostvars.manager.private_ip }}" - "{{ hostvars.manager.private_ip }}"
hidden: 'no' hidden: 'no'
wazuh_api_users:
- username: custom-user
password: .S3cur3Pa55w0rd*-
filebeat_output_indexer_hosts: filebeat_output_indexer_hosts:
- "{{ hostvars.es1.private_ip }}" - "{{ hostvars.wi1.private_ip }}"
- "{{ hostvars.es2.private_ip }}" - "{{ hostvars.wi2.private_ip }}"
- "{{ hostvars.es3.private_ip }}" - "{{ hostvars.wi3.private_ip }}"
- hosts: worker - hosts: worker
roles: roles:
@ -210,57 +230,66 @@ The hereunder example playbook uses the `wazuh-ansible` role to provision a prod
- "{{ hostvars.manager.private_ip }}" - "{{ hostvars.manager.private_ip }}"
hidden: 'no' hidden: 'no'
filebeat_output_indexer_hosts: filebeat_output_indexer_hosts:
- "{{ hostvars.es1.private_ip }}" - "{{ hostvars.wi1.private_ip }}"
- "{{ hostvars.es2.private_ip }}" - "{{ hostvars.wi2.private_ip }}"
- "{{ hostvars.es3.private_ip }}" - "{{ hostvars.wi3.private_ip }}"
#ODFE+Kibana node #Indexer+Dashboard node
- hosts: kibana - hosts: dashboard
roles: roles:
- role: "../roles/opendistro/opendistro-elasticsearch" - role: "../roles/opensearch/wazuh-indexer"
- role: "../roles/opendistro/opendistro-kibana" - role: "../roles/opensearch/wazuh-dashboard"
become: yes become: yes
become_user: root become_user: root
vars: vars:
elasticsearch_network_host: "{{ hostvars.kibana.private_ip }}" indexer_network_host: "{{ hostvars.dashboard.private_ip }}"
elasticsearch_node_name: node-6 indexer_node_name: node-6
elasticsearch_node_master: false indexer_node_master: false
elasticsearch_node_ingest: false indexer_node_ingest: false
elasticsearch_node_data: false indexer_node_data: false
elasticsearch_cluster_nodes: indexer_cluster_nodes:
- "{{ hostvars.es1.private_ip }}" - "{{ hostvars.wi1.private_ip }}"
- "{{ hostvars.es2.private_ip }}" - "{{ hostvars.wi2.private_ip }}"
- "{{ hostvars.es3.private_ip }}" - "{{ hostvars.wi3.private_ip }}"
elasticsearch_discovery_nodes: indexer_discovery_nodes:
- "{{ hostvars.es1.private_ip }}" - "{{ hostvars.wi1.private_ip }}"
- "{{ hostvars.es2.private_ip }}" - "{{ hostvars.wi2.private_ip }}"
- "{{ hostvars.es3.private_ip }}" - "{{ hostvars.wi3.private_ip }}"
kibana_node_name: node-6 dashboard_node_name: node-6
wazuh_api_credentials: wazuh_api_credentials:
- id: default - id: default
url: https://{{ hostvars.manager.private_ip }} url: https://{{ hostvars.manager.private_ip }}
port: 55000 port: 55000
user: foo username: custom-user
password: bar password: .S3cur3Pa55w0rd*-
instances: instances:
node1: node1:
name: node-1 # Important: must be equal to elasticsearch_node_name. name: node-1 # Important: must be equal to indexer_node_name.
ip: "{{ hostvars.es1.private_ip }}" # When unzipping, the node will search for its node name folder to get the cert. ip: "{{ hostvars.wi1.private_ip }}" # When unzipping, the node will search for its node name folder to get the cert.
role: indexer
node2: node2:
name: node-2 name: node-2
ip: "{{ hostvars.es2.private_ip }}" ip: "{{ hostvars.wi2.private_ip }}"
role: indexer
node3: node3:
name: node-3 name: node-3
ip: "{{ hostvars.es3.private_ip }}" ip: "{{ hostvars.wi3.private_ip }}"
role: indexer
node4: node4:
name: node-4 name: node-4
ip: "{{ hostvars.manager.private_ip }}" ip: "{{ hostvars.manager.private_ip }}"
role: wazuh
node_type: master
node5: node5:
name: node-5 name: node-5
ip: "{{ hostvars.worker.private_ip }}" ip: "{{ hostvars.worker.private_ip }}"
role: wazuh
node_type: worker
node6: node6:
name: node-6 name: node-6
ip: "{{ hostvars.kibana.private_ip }}" ip: "{{ hostvars.dashboard.private_ip }}"
role: dashboard
ansible_shell_allow_world_readable_temp: true
``` ```
### Inventory file ### Inventory file
@ -271,17 +300,17 @@ The hereunder example playbook uses the `wazuh-ansible` role to provision a prod
- The ssh credentials used by Ansible during the provision can be specified in this file too. Another option is including them directly on the playbook. - The ssh credentials used by Ansible during the provision can be specified in this file too. Another option is including them directly on the playbook.
```ini ```ini
es1 ansible_host=<es1_ec2_public_ip> private_ip=<es1_ec2_private_ip> elasticsearch_node_name=node-1 wi1 ansible_host=<wi1_ec2_public_ip> private_ip=<wi1_ec2_private_ip> elasticsearch_node_name=node-1
es2 ansible_host=<es2_ec2_public_ip> private_ip=<es2_ec2_private_ip> elasticsearch_node_name=node-2 wi2 ansible_host=<wi2_ec2_public_ip> private_ip=<wi2_ec2_private_ip> elasticsearch_node_name=node-2
es3 ansible_host=<es3_ec2_public_ip> private_ip=<es3_ec2_private_ip> elasticsearch_node_name=node-3 wi3 ansible_host=<wi3_ec2_public_ip> private_ip=<wi3_ec2_private_ip> elasticsearch_node_name=node-3
kibana ansible_host=<kibana_node_public_ip> private_ip=<kibana_ec2_private_ip> kibana ansible_host=<kibana_node_public_ip> private_ip=<kibana_ec2_private_ip>
manager ansible_host=<manager_node_public_ip> private_ip=<manager_ec2_private_ip> manager ansible_host=<manager_node_public_ip> private_ip=<manager_ec2_private_ip>
worker ansible_host=<worker_node_public_ip> private_ip=<worker_ec2_private_ip> worker ansible_host=<worker_node_public_ip> private_ip=<worker_ec2_private_ip>
[odfe_cluster] [wi_cluster]
es1 wi1
es2 wi2
es3 wi3
[all:vars] [all:vars]
ansible_ssh_user=vagrant ansible_ssh_user=vagrant

View File

@ -13,8 +13,7 @@
- "{{ hostvars.wi2.private_ip }}" - "{{ hostvars.wi2.private_ip }}"
- "{{ hostvars.wi3.private_ip }}" - "{{ hostvars.wi3.private_ip }}"
perform_installation: false perform_installation: false
become: yes become: no
become_user: root
vars: vars:
indexer_node_master: true indexer_node_master: true
instances: instances:
@ -47,7 +46,7 @@
tags: tags:
- generate-certs - generate-certs
#ODFE Cluster #Wazuh Indexer Cluster
- hosts: wi_cluster - hosts: wi_cluster
strategy: free strategy: free
roles: roles:

View File

@ -3,13 +3,17 @@ import sys
import json import json
import random import random
import string import string
import argparse
import os import os
# Set framework path # Set framework path
sys.path.append("/var/ossec/framework") sys.path.append(os.path.dirname(sys.argv[0]) + "/../framework")
USER_FILE_PATH = "/var/ossec/api/configuration/admin.json"
SPECIAL_CHARS = "@$!%*?&-_"
try: try:
from wazuh.rbac.orm import create_rbac_db
from wazuh.security import ( from wazuh.security import (
create_user, create_user,
get_users, get_users,
@ -22,6 +26,12 @@ except Exception as e:
sys.exit(1) sys.exit(1)
def read_user_file(path=USER_FILE_PATH):
with open(path) as user_file:
data = json.load(user_file)
return data["username"], data["password"]
def db_users(): def db_users():
users_result = get_users() users_result = get_users()
return {user["username"]: user["id"] for user in users_result.affected_items} return {user["username"]: user["id"] for user in users_result.affected_items}
@ -31,15 +41,35 @@ def db_roles():
roles_result = get_roles() roles_result = get_roles()
return {role["name"]: role["id"] for role in roles_result.affected_items} return {role["name"]: role["id"] for role in roles_result.affected_items}
def disable_user(uid):
random_pass = "".join(
random.choices(
string.ascii_uppercase
+ string.ascii_lowercase
+ string.digits
+ SPECIAL_CHARS,
k=8,
)
)
# assure there must be at least one character from each group
random_pass = random_pass + ''.join([random.choice(chars) for chars in [string.ascii_lowercase, string.digits, string.ascii_uppercase, SPECIAL_CHARS]])
random_pass = ''.join(random.sample(random_pass,len(random_pass)))
update_user(
user_id=[
str(uid),
],
password=random_pass,
)
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser(description='add_user script') if not os.path.exists(USER_FILE_PATH):
parser.add_argument('--username', action="store", dest="username") # abort if no user file detected
parser.add_argument('--password', action="store", dest="password") sys.exit(0)
results = parser.parse_args() username, password = read_user_file()
username = results.username # create RBAC database
password = results.password create_rbac_db()
initial_users = db_users() initial_users = db_users()
if username not in initial_users: if username not in initial_users:
@ -66,28 +96,7 @@ if __name__ == "__main__":
], ],
password=password, password=password,
) )
# set a random password for all other users # disable unused default users
for name, id in initial_users.items(): for def_user in ['wazuh', 'wazuh-wui']:
if name != username: if def_user != username:
specials = "@$!%*?&-_" disable_user(initial_users[def_user])
random_pass = "".join(
[
random.choice(string.ascii_uppercase),
random.choice(string.ascii_lowercase),
random.choice(string.digits),
random.choice(specials),
] +
random.choices(
string.ascii_uppercase
+ string.ascii_lowercase
+ string.digits
+ specials,
k=14,
)
)
update_user(
user_id=[
str(id),
],
password=random_pass,
)

View File

@ -16,7 +16,7 @@
become: true become: true
shell: | shell: |
set -o pipefail set -o pipefail
curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | apt-key add - curl -s https://packages-dev.wazuh.com/key/GPG-KEY-WAZUH | apt-key add -
args: args:
warn: false warn: false
executable: /bin/bash executable: /bin/bash