diff --git a/.github/labeler.yaml b/.github/labeler.yaml index 38eaa325..b49ed384 100644 --- a/.github/labeler.yaml +++ b/.github/labeler.yaml @@ -51,7 +51,9 @@ area/scripts: area/extensions: - changed-files: - - any-glob-to-any-file: "extensions/**/*" + - any-glob-to-any-file: "ansible/playbooks/templates/extensions/**/*" + - any-glob-to-any-file: "ansible/playbooks/files/extensions/**/*" + - any-glob-to-any-file: "providers/**/*" area/python: - changed-files: diff --git a/.github/workflows/goreleaser.yaml b/.github/workflows/goreleaser.yaml new file mode 100644 index 00000000..9b6e32c8 --- /dev/null +++ b/.github/workflows/goreleaser.yaml @@ -0,0 +1,40 @@ +--- +name: goreleaser +on: + push: + tags: + - "*" + +env: + GO_VERSION: 1.26.1 + +jobs: + goreleaser: + name: Run GoReleaser + runs-on: ubuntu-latest + steps: + - name: Set up git repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - name: Fetch all tags + run: git fetch --force --tags + + - name: Set up Go + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + with: + go-version: "${{ env.GO_VERSION }}" + cache-dependency-path: "**/*.sum" + + - name: Fix GOPATH + run: echo "PATH=$(go env GOPATH)/bin:$PATH" >> "$GITHUB_ENV" + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7 + with: + distribution: goreleaser + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 00000000..f48be191 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,40 @@ +--- +version: 2 + +snapshot: + version_template: "{{ .Version }}-next" + +builds: + - id: "dreadgoad" + + binary: dreadgoad + + dir: ./cli + + main: . + + ldflags: + - -s -w + - -X main.version={{.Summary}} + - -X main.commit={{.ShortCommit}} + - -X main.date={{.Date}} + + goos: + - linux + - darwin + + goarch: + - amd64 + - arm + - arm64 + + goarm: + - "6" + - "7" + + goamd64: + - v2 + - v3 + + hooks: + pre: go mod tidy diff --git a/.hooks/linters/ansible-lint.yaml b/.hooks/linters/ansible-lint.yaml index 2e3abc84..f4a2ab0e 100644 --- a/.hooks/linters/ansible-lint.yaml +++ b/.hooks/linters/ansible-lint.yaml @@ -14,8 +14,12 @@ exclude_paths: - ansible/requirements_311.yml - template/provider/ludus/ - playbooks.yml - - ansible/extensions/exchange/ansible/install.yml - - ansible/extensions/ws01/ansible/install.yml + # Extension playbooks reference roles/deps not resolvable in the linter sandbox + - ansible/playbooks/ext-exchange.yml + - ansible/playbooks/ext-guacamole.yml + - ansible/playbooks/ext-lx01.yml + - ansible/playbooks/ext-wazuh.yml + - ansible/playbooks/network_setup.yml - ansible/roles/onlyusers/ skip_list: diff --git a/.hooks/linters/markdownlint.json b/.hooks/linters/markdownlint.json index fce036cd..703952b1 100644 --- a/.hooks/linters/markdownlint.json +++ b/.hooks/linters/markdownlint.json @@ -11,6 +11,7 @@ "MD041": false, "MD055": false, "MD056": false, + "MD011": false, "MD057": false, "MD060": false, "line-length": false, diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0190a715..4f68dfa3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,9 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - id: check-added-large-files + exclude: 'docs/img/.*\.png$' - id: detect-private-key + exclude: 'ad/.*/files/.*' - id: check-shebang-scripts-are-executable - repo: https://github.com/rhysd/actionlint diff --git a/ad/DRACARYS/README.md b/ad/DRACARYS/README.md new file mode 100644 index 00000000..cbfcfc68 --- /dev/null +++ b/ad/DRACARYS/README.md @@ -0,0 +1,65 @@ +# DRACARYS + +![DRACARYS](../../docs/img/dracarys_logo.png) + +- DRACARYS is written as a training challenge where GOAD was written as a lab with a maximum of vulns. +- You should find your way in to get domain admin on the domain dracarys.lab +- Using vagrant user is prohibited of course ^^ +- Starting point is on lx01 : `.12` +- Obviously do not cheat by looking at the passwords and flags in the recipe files, the lab must start without user to full compromise. +- If you use goad previously your ansible requirements may not be up to date. Be sure to do this before the install: + +```bash +source ~/.goad/.venv/bin/activate +cd ~/GOAD/ansible + +# if you python is >=3.11 +ansible-galaxy install -r requirements_311.yml +# if you got a python <3.10 +ansible-galaxy install -r requirements.yml +``` + +- Install : + +```bash +./goad.sh -t install -l DRACARYS -p virtualbox +``` + +or + +```bash +./goad.sh +> set_lab DRACARYS +> set_provider +> set_iprange 192.168.56 # select the one you want and you can skip this with ludus +> install +``` + +- Once install finish disable vagrant user to avoid using it : + +```bash +./goad.sh +> load +> disable_vagrant +``` + +- Now do a reboot of all the machine to avoid unintended secrets stored : + +```bash +> stop +> start +``` + +And you are ready to play ! :) + +- If you need to re-enable vagrant + +```bash +> load +> enable_vagrant +``` + +- If you want to create a write up of the chall, no problem, have fun. Please ping me on X (@M4yFly) or Discord, i will be happy to read it :) + +!!! tip + Be sure to get your arsenal up to date diff --git a/ad/DRACARYS/data/config.json b/ad/DRACARYS/data/config.json new file mode 100644 index 00000000..eeb53f6e --- /dev/null +++ b/ad/DRACARYS/data/config.json @@ -0,0 +1,170 @@ +{ +"lab" : { + "hosts" : { + "dc01" : { + "hostname" : "balerion", + "type" : "dc", + "local_admin_password": "8dCsfT-DJjgS3xdcp", + "domain" : "dracarys.lab", + "path" : "DC=dracarys,DC=lab", + "local_groups" : { + "Administrators" : [ + "dracarys\\Drogon" + ] + }, + "scripts" : ["set_spn.ps1","wsman_kerb.ps1"], + "security": ["directory", "files", "ldaps"], + "security_vars": { + "directory": { + "setup": "c:\\setup" + }, + "files" : { + "certificate" : { + "src" : "dc01/ldaps.pfx", + "dest" : "c:\\setup\\ldaps.pfx" + } + }, + "ldaps": { + "certificate" : { + "pfx" : "c:\\setup\\ldaps.pfx", + "cert_password" : "MyStr@ngeCertP@ssword123" + } + } + }, + "vulns" : ["files", "enable_credssp_client", "schedule"], + "vulns_vars" : { + "files" : { + "bot_keepass" : { + "src" : "dc01/keepass_bot.ps1", + "dest" : "c:\\keepass_bot.ps1" + } + }, + "schedule": { + "bot": { + "name": "keepass_bot", + "cmd" : "powershell c:\\keepass_bot.ps1", + "interval" : "PT1M", + "multiple_instances" : "3" + } + } + } + }, + "srv01" : { + "hostname" : "vhagar", + "type" : "server", + "local_admin_password": "NgtkgtIAs75cKV+Pu", + "domain" : "dracarys.lab", + "path" : "DC=dracarys,DC=lab", + "use_laps": false, + "local_groups" : { + "Administrators" : [ + "dracarys\\Rhaegal" + ], + "Remote Desktop Users" : [ + "dracarys\\Rhaegal" + ] + }, + "scripts" : [], + "vulns" : ["files", "enable_credssp_server", "schedule"], + "vulns_vars" : { + "files" : { + "vault" : { + "src" : "srv01/vault.kdbx", + "dest" : "c:\\vault.kdbx" + }, + "bot_ssh" : { + "src" : "srv01/bot_ssh.ps1", + "dest" : "c:\\bot_ssh.ps1" + } + }, + "schedule": { + "bot": { + "name": "bot_ssh", + "cmd" : "powershell c:\\bot_ssh.ps1", + "interval" : "PT1M" + } + } + } + }, + "lx01" : { + "hostname" : "syrax", + "type" : "server", + "os": "linux", + "local_admin_password": "HGLXaxQSP@ssw_rd$", + "domain" : "dracarys.lab", + "path" : "DC=dracarys,DC=lab", + "local_groups" : { + "sudoers" : ["LinuxAdmins"], + "ssh" : ["LinuxAdmins", "LinuxUsers"] + }, + "security": [], + "security_vars": {}, + "scripts" : [] + } + }, + "domains" : { + "dracarys.lab" : { + "dc": "dc01", + "domain_password" : "8dCsfT-DJjgS3xdcp", + "netbios_name": "DRACARYS", + "organisation_units" : { + }, + "groups" : { + "universal" : {}, + "global" : { + "LinuxAdmins" : { + "managed_by" : "drogon", + "path" : "CN=Users,DC=dracarys,DC=lab" + }, + "LinuxUsers" : { + "managed_by" : "drogon", + "path" : "CN=Users,DC=dracarys,DC=lab" + } + }, + "domainlocal" : {} + }, + "multi_domain_groups_member" : {}, + "acls" : { + "WriteSPN_viserion_vhagar": {"for": "viserion", "to": "vhagar$", "right": "Ext-Write-SPN", "inheritance": "None"} + }, + "users" : { + "drogon" : { + "firstname" : "drogon", + "surname" : "-", + "password" : "sUIjHxs1i0yxZsGBreh0", + "city" : "-", + "description" : "Domain admin", + "groups" : ["LinuxAdmins", "Domain Admins"], + "path" : "CN=Users,DC=dracarys,DC=lab" + }, + "rhaegal" : { + "firstname" : "rhaegal", + "surname" : "-", + "password" : "ufsmcvDaFz1uEqzAtaiL", + "city" : "-", + "description" : "Rhaegal", + "groups" : ["LinuxAdmins"], + "path" : "CN=Users,DC=dracarys,DC=lab" + }, + "viserion" : { + "firstname" : "viserion", + "surname" : "-", + "password" : "aLHtz1WvIVmeV4Zh4CDE", + "city" : "-", + "description" : "viserion", + "groups" : ["LinuxUsers"], + "path" : "CN=Users,DC=dracarys,DC=lab" + }, + "sunfyre" : { + "firstname" : "sunfyre", + "surname" : "-", + "password" : "BSno5DP4tjJ4jIu8is3B", + "city" : "-", + "description" : "glpi service account", + "groups" : ["LinuxUsers"], + "path" : "CN=Users,DC=dracarys,DC=lab" + } + } + } + } +}} diff --git a/ad/DRACARYS/data/inventory b/ad/DRACARYS/data/inventory new file mode 100644 index 00000000..e35fe315 --- /dev/null +++ b/ad/DRACARYS/data/inventory @@ -0,0 +1,108 @@ +; GLOBAL CONFIG +[all:vars] +; domain_name : folder inside ad/ +domain_name=DRACARYS + +; administrator user +admin_user=administrator + +; global settings inventory default value +keyboard_layouts=["en-US", "da-DK", "fr-FR"] + +; modify this to add a default route +add_route=no +route_gateway=192.168.56.1 +route_network=10.0.0.0/8 + +; modify this to enable http proxy +enable_http_proxy=no +ad_http_proxy=http://x.x.x.x:xxxx +ad_https_proxy=http://x.x.x.x:xxxx + +;force_dns_server +force_dns_server=no +dns_server=1.1.1.1 + +;dns server forwarder +dns_server_forwarder=1.1.1.1 + +; winrm connection (windows) +ansible_user=vagrant +ansible_password=vagrant +ansible_connection=winrm +ansible_winrm_server_cert_validation=ignore +ansible_winrm_operation_timeout_sec=400 +ansible_winrm_read_timeout_sec=500 +# ansible_winrm_transport=basic +# ansible_port=5985 + +; proxy settings (the lab need internet for some install, if you are behind a proxy you should set the proxy here) +enable_http_proxy=no +ad_http_proxy=http://x.x.x.x:xxxx +ad_https_proxy=http://x.x.x.x:xxxx + +; LAB SCENARIO CONFIGURATION ----------------------------- + +; computers inside domain (mandatory) +; usage : build.yml, ad-relations.yml, ad-servers.yml, vulnerabilities.yml +[domain] +dc01 +srv01 + +[linux_domain] +lx01 + +; domain controller (mandatory) +; usage : ad-acl.yml, ad-data.yml, ad-relations.yml, laps.yml +[dc] +dc01 + +; domain server to enroll (mandatory if you want servers) +; usage : ad-data.yml, ad-servers.yml, laps.yml +[server] +srv01 + +; workstation to enroll (mandatory if you want workstation) +; usage : ad-servers.yml, laps.yml +[workstation] + +; parent domain controller (mandatory) +; usage : ad-servers.yml +[parent_dc] +dc01 + +; child domain controller (need a fqdn child_name.parent_name) +; usage : ad-servers.yml +[child_dc] + +; allow computer update +; usage : update.yml +[update] +dc01 + +; disable update +; usage : update.yml +[no_update] +srv01 + +; allow defender +; usage : security.yml +[defender_on] +dc01 +srv01 + +; disable defender +; usage : security.yml +[defender_off] + +[glpi] +lx01 + +[klink] +srv01 + +[keepass] +srv01 + +;stay empty until override +[extensions] diff --git a/ad/DRACARYS/data/inventory_disable_vagrant b/ad/DRACARYS/data/inventory_disable_vagrant new file mode 100644 index 00000000..a153a3b8 --- /dev/null +++ b/ad/DRACARYS/data/inventory_disable_vagrant @@ -0,0 +1,28 @@ +[default] +; ------------------------------------------------ +; dracarys.lab +; ------------------------------------------------ +dc01 ansible_host={{ip_range}}.10 dns_domain=dc01 dict_key=dc01 ansible_user=drogon@dracarys.lab ansible_password=sUIjHxs1i0yxZsGBreh0 +srv01 ansible_host={{ip_range}}.11 dns_domain=dc01 dict_key=srv01 ansible_user=drogon@dracarys.lab ansible_password=sUIjHxs1i0yxZsGBreh0 +lx01 ansible_host={{ip_range}}.12 dict_key=lx01 ansible_connection=ssh ansible_ssh_common_args='-o StrictHostKeyChecking=no' ansible_user=drogon ansible_password=sUIjHxs1i0yxZsGBreh0 + +[all:vars] +; domain_name : folder inside ad/ +domain_name=DRACARYS + +; winrm connection (windows) +ansible_winrm_transport=ntlm +ansible_user=notused +ansible_password=notused +ansible_connection=winrm +ansible_winrm_server_cert_validation=ignore +ansible_winrm_operation_timeout_sec=400 +ansible_winrm_read_timeout_sec=500 + +; LAB SCENARIO CONFIGURATION ----------------------------- +[domain] +dc01 +srv01 + +[linux_domain] +lx01 diff --git a/ad/DRACARYS/files/dc01/cert.md b/ad/DRACARYS/files/dc01/cert.md new file mode 100644 index 00000000..f5d2b0a9 --- /dev/null +++ b/ad/DRACARYS/files/dc01/cert.md @@ -0,0 +1,3 @@ +openssl genrsa -out ldaps.key 2048 +openssl req -new -x509 -key ldaps.key -out ldaps.crt -days 3650 -subj "/CN=balerion.dracarys.lab" -addext "subjectAltName = DNS:balerion.dracarys.lab,DNS:balerion" +openssl pkcs12 -export -out ldaps.pfx -inkey ldaps.key -in ldaps.crt -passout pass:MyStr@ngeCertP@ssword123 diff --git a/ad/DRACARYS/files/dc01/keepass_bot.ps1 b/ad/DRACARYS/files/dc01/keepass_bot.ps1 new file mode 100644 index 00000000..153b2071 --- /dev/null +++ b/ad/DRACARYS/files/dc01/keepass_bot.ps1 @@ -0,0 +1,26 @@ +$pass = ConvertTo-SecureString 'ufsmcvDaFz1uEqzAtaiL' -AsPlainText -Force +$creds = New-Object System.Management.Automation.PSCredential ( + 'dracarys.lab\rhaegal', + $pass +) + +Invoke-Command ` + -ComputerName vhagar.dracarys.lab ` + -Authentication Credssp ` + -Credential $creds ` + -ScriptBlock { + $kpPath = '"C:\Program Files\KeePass Password Safe 2\KeePass.exe"' + $dbPath = 'C:\vault.kdbx' + $masterPassword = 'lj-endlmkfQSLDKPDFNZLEK' + $openTime = 30 + + Write-Host "[*] Start KeePass via cmd pipe" + + cmd /c "echo $masterPassword | $kpPath $dbPath -pw-stdin" + + Write-Host "[+] KeePass started" + Start-Sleep -Seconds $openTime + + Write-Host "[-] Closing KeePass" + Get-Process KeePass -ErrorAction SilentlyContinue | Stop-Process -Force + } diff --git a/ad/DRACARYS/files/dc01/ldaps.crt b/ad/DRACARYS/files/dc01/ldaps.crt new file mode 100644 index 00000000..0394a34e --- /dev/null +++ b/ad/DRACARYS/files/dc01/ldaps.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDTTCCAjWgAwIBAgIUU2yqCIa/Vzxgktgu/1gcLG6OjMkwDQYJKoZIhvcNAQEL +BQAwIDEeMBwGA1UEAwwVYmFsZXJpb24uZHJhY2FyeXMubGFiMB4XDTI1MTIwODA4 +NTk0OVoXDTM1MTIwNjA4NTk0OVowIDEeMBwGA1UEAwwVYmFsZXJpb24uZHJhY2Fy +eXMubGFiMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl+jBdQImgoxK +d52vvYnmtgLqF7M5v6620aBAMOs316P1SBX8GVrdpWo2uC+pYF78TK5/MfP8QLs5 +QMM3Qb46Y8rtA6Gueg9fQcTJAI9rsLg+JCA3YjWlvSN3yEjdHuZ1vQO6Ttjb1S29 +ORl3MBEiqW2JTkS2xiFwhRNFV900TSoHKF1ZCqp1jvqS73f8kjaGckJ3Xq2IU3WU +FQvpWGlKWvJxKN2MDAhB48mtscDvi9kfkTbXDn5w8ChWtWeTdDi5fQaRezTNdOzu +gfZoMZKrNVdvR4DhHYPrk8YXsVJf7LuVcOhRHykzCcMqEF7eH2AKpB9EitycJIEV +LGA8dryqgQIDAQABo38wfTAdBgNVHQ4EFgQU/OzihMQWP+/mO+HcNdR8Dn7QWpYw +HwYDVR0jBBgwFoAU/OzihMQWP+/mO+HcNdR8Dn7QWpYwDwYDVR0TAQH/BAUwAwEB +/zAqBgNVHREEIzAhghViYWxlcmlvbi5kcmFjYXJ5cy5sYWKCCGJhbGVyaW9uMA0G +CSqGSIb3DQEBCwUAA4IBAQAZhdFOXz/WMPFewlHnDIMuVdkJPIHjMHyUCIdX7CtV +wiEw0u2U6If6hXgsMw7WbYFhQRDWvR+zKLfL4xODxYivtAhr/P4T0jGqznTk6y5T +YU59K4NSGj7iJzkF0euRbPWJWdK9i+YG0TyGB4xanl6jOqjLZPt7UxL+IFM73Qxf +Mnhn8YxqXg2vTPdknoaqytu3oD4st3bdSH6+ssthBnExm6PpCCnp3a7hosO6I4JZ +ae5zg5Wy3jcXghOHA3h+MKgk4CTNLjQBNioVBY7gfoW4GzcjxkfiBufxlfvtH7pd +/WUoRKKmKUfOEMATBvCFxoAm+8cW3HlSVCjgZWBwbT6X +-----END CERTIFICATE----- diff --git a/ad/DRACARYS/files/dc01/ldaps.key b/ad/DRACARYS/files/dc01/ldaps.key new file mode 100644 index 00000000..0fb4c986 --- /dev/null +++ b/ad/DRACARYS/files/dc01/ldaps.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCX6MF1AiaCjEp3 +na+9iea2AuoXszm/rrbRoEAw6zfXo/VIFfwZWt2laja4L6lgXvxMrn8x8/xAuzlA +wzdBvjpjyu0Doa56D19BxMkAj2uwuD4kIDdiNaW9I3fISN0e5nW9A7pO2NvVLb05 +GXcwESKpbYlORLbGIXCFE0VX3TRNKgcoXVkKqnWO+pLvd/ySNoZyQnderYhTdZQV +C+lYaUpa8nEo3YwMCEHjya2xwO+L2R+RNtcOfnDwKFa1Z5N0OLl9BpF7NM107O6B +9mgxkqs1V29HgOEdg+uTxhexUl/su5Vw6FEfKTMJwyoQXt4fYAqkH0SK3JwkgRUs +YDx2vKqBAgMBAAECggEANgAerTqLeALpAeaDL4yTAAa/MpeaosI36QLfbsRfAIAf +VHXEPTso6YF9XDJNMp3xcEzjmF1UQaqMarI6tVsrJIhhgtX50Rgf06Bhl5pkPNjx +9iOhrH7HoRm3nfIQ8MgZ+IwXsamzU+/DgUXFMcgVm0b99V3F317JwfScxOQ0kG0g +5uD9QDfxRRAJ0DbLYovFYG2Xg8+BAldb9S7gD3JTVLRDEmSixruSkK0hHQA2GNGP +bxvEJpgS153Qv6NbHCwJAWEY3HWhPBBzd5ObC0DVDPs56AzFsTdbjaIKbCFYfyPY +wRw+r66aIw3cx9143GZEXlgM29lvM3lfpx0f1eBvRQKBgQC7kivCqUQQjAse2QbR +uMnZuuXS0gPgD4Dk+48rbsvE6g8FG84FQmoB9hiAi4jT5nEQKgHg2I+NQRnu4spz +MpwBhhZC/12mtBW6kTRSzuoGdJhivZHarTuvH6+nnkw7uwzDnB7BrnhHhbosgpb7 +fhsVritbA3f+s6VCXIJOisJ4hwKBgQDPVAWcIAyMAn4QaGjuLYdxkBE1AukCGTTS +IAQsRFUb9i8PA8zvIXTrJjACJlNTVfDuPk4qp68eO/GwQRHFqTXlmz/Lhvdn9m9p +CWpmL4fLpLYjdTZ8iFXlvjW155PuPCmE0vD3qtQKlYo03LVtcxgX3VhHgQQ2RT0l +x/UmEr3utwKBgHtB8JO4m0usW1poDz0dizcSxBenfnhsd60BSfGmmyzJChm3TcjS +/cpQJ4XBK6bjlYSjthxE8wBFuX7rdVIB/dZagKKCIM59JJI2/QU1hz+6urCYFhJ3 +J/NKhSlGsp3FqvuXyfZIai3FyLObFRAqrC2xCDiErQOolX5oQBDQyj5dAoGAWDL+ +T/SyYb2Ns503RlvICt+m8k5SobnnZpyIKezH6CVKz7BmNjSdcIvGUKPNPt6IqFGJ +H0xGiy4lGz4TOWtKqmrpMMQx6+BCdQS0ZtRBiLiBY4QxsbiuEhZg8wmZPPgLEZ5L +NJFPs1D6gpKB/BXCYiSfsYuJJy09Xh06hP/kHPECgYBMUh9ZD8UJTLXdWqn2sLxS +U+ippoaBR1PaD6Id/31FpR+5wuv78Kzz4e8wejMlp0oPMpxLrp78q0F0RGbVsZOy +E78Sr9qrQWHDyjWImtkzcWAPcbsrUeBCAmsjCov17yCwBsCuODIT8oImuGBjDRrB +JGj0b+OHm2nmez8vAmb0jg== +-----END PRIVATE KEY----- diff --git a/ad/DRACARYS/files/dc01/ldaps.pfx b/ad/DRACARYS/files/dc01/ldaps.pfx new file mode 100644 index 00000000..80651730 Binary files /dev/null and b/ad/DRACARYS/files/dc01/ldaps.pfx differ diff --git a/ad/DRACARYS/files/srv01/bot_ssh.ps1 b/ad/DRACARYS/files/srv01/bot_ssh.ps1 new file mode 100644 index 00000000..5e79bd3a --- /dev/null +++ b/ad/DRACARYS/files/srv01/bot_ssh.ps1 @@ -0,0 +1,5 @@ +$User = "viserion" +$SSHHost = "syrax" +$Password = "aLHtz1WvIVmeV4Zh4CDE" + +& "C:\Program Files\PuTTY\klink.exe" -auto_store_sshkey $SSHHost -l "$User" -pw $Password "sleep 45" diff --git a/ad/DRACARYS/files/srv01/vault.kdbx b/ad/DRACARYS/files/srv01/vault.kdbx new file mode 100644 index 00000000..f7df523e Binary files /dev/null and b/ad/DRACARYS/files/srv01/vault.kdbx differ diff --git a/ad/DRACARYS/providers/aws/inventory b/ad/DRACARYS/providers/aws/inventory new file mode 100644 index 00000000..f37bfbd5 --- /dev/null +++ b/ad/DRACARYS/providers/aws/inventory @@ -0,0 +1,10 @@ +[default] +; ------------------------------------------------ +; dracarys.lab +; ------------------------------------------------ +dc01 ansible_host={{ip_range}}.10 dns_domain=dc01 dict_key=dc01 ansible_user=ansible ansible_password=8dCsfT-DJjgS3xdcp +srv01 ansible_host={{ip_range}}.11 dns_domain=dc01 dict_key=srv01 ansible_user=ansible ansible_password=NgtkgtIAs75cKV+Pu +lx01 ansible_host={{ip_range}}.12 dict_key=lx01 ansible_connection=ssh ansible_ssh_common_args='-o StrictHostKeyChecking=no' ansible_user=goadmin ansible_password=HGLXaxQSP@ssw_rd$ + +[all:vars] +admin_user=goadmin diff --git a/ad/DRACARYS/providers/aws/linux.tf b/ad/DRACARYS/providers/aws/linux.tf new file mode 100644 index 00000000..45739841 --- /dev/null +++ b/ad/DRACARYS/providers/aws/linux.tf @@ -0,0 +1,9 @@ +"lx01" = { + name = "lx01" + linux_sku = "24_04-lts-gen2" + linux_version = "latest" + ami = "ami-0bb8b77ad97138af1" + private_ip_address = "{{ip_range}}.12" + password = "HGLXaxQSP@ssw_rd$" + instance_type = "t3.medium" +} diff --git a/ad/DRACARYS/providers/aws/windows.tf b/ad/DRACARYS/providers/aws/windows.tf new file mode 100644 index 00000000..e1da7885 --- /dev/null +++ b/ad/DRACARYS/providers/aws/windows.tf @@ -0,0 +1,21 @@ +# Standard_B2s : 2 CPU / 4GB +# Standard_B2ms : 2CPU / 8GB +# Standard_B4ms : 4 cpu / 16 GB +"dc01" = { + name = "dc01" + domain = "dracarys.lab" + windows_sku = "2025-Datacenter" + ami = "ami-0979a3709cb073ec3" + instance_type = "t3.medium" + private_ip_address = "{{ip_range}}.10" + password = "8dCsfT-DJjgS3xdcp" +} +"srv01" = { + name = "srv01" + domain = "dracarys.lab" + windows_sku = "2025-Datacenter" + ami = "ami-0979a3709cb073ec3" + instance_type = "t3.medium" + private_ip_address = "{{ip_range}}.11" + password = "NgtkgtIAs75cKV+Pu" +} diff --git a/ad/DRACARYS/providers/azure/inventory b/ad/DRACARYS/providers/azure/inventory new file mode 100644 index 00000000..f37bfbd5 --- /dev/null +++ b/ad/DRACARYS/providers/azure/inventory @@ -0,0 +1,10 @@ +[default] +; ------------------------------------------------ +; dracarys.lab +; ------------------------------------------------ +dc01 ansible_host={{ip_range}}.10 dns_domain=dc01 dict_key=dc01 ansible_user=ansible ansible_password=8dCsfT-DJjgS3xdcp +srv01 ansible_host={{ip_range}}.11 dns_domain=dc01 dict_key=srv01 ansible_user=ansible ansible_password=NgtkgtIAs75cKV+Pu +lx01 ansible_host={{ip_range}}.12 dict_key=lx01 ansible_connection=ssh ansible_ssh_common_args='-o StrictHostKeyChecking=no' ansible_user=goadmin ansible_password=HGLXaxQSP@ssw_rd$ + +[all:vars] +admin_user=goadmin diff --git a/ad/DRACARYS/providers/azure/linux.tf b/ad/DRACARYS/providers/azure/linux.tf new file mode 100644 index 00000000..f0756cfb --- /dev/null +++ b/ad/DRACARYS/providers/azure/linux.tf @@ -0,0 +1,9 @@ +"lx01" = { + name = "lx01" + linux_offer = "ubuntu-24_04-lts" + linux_sku = "server" + linux_version = "latest" + private_ip_address = "{{ip_range}}.12" + password = "HGLXaxQSP@ssw_rd$" + size = "Standard_B2s" +} diff --git a/ad/DRACARYS/providers/azure/windows.tf b/ad/DRACARYS/providers/azure/windows.tf new file mode 100644 index 00000000..7940621a --- /dev/null +++ b/ad/DRACARYS/providers/azure/windows.tf @@ -0,0 +1,23 @@ +# Standard_B2s : 2 CPU / 4GB +# Standard_B2ms : 2CPU / 8GB +# Standard_B4ms : 4 cpu / 16 GB +"dc01" = { + name = "dc01" + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + windows_sku = "2025-Datacenter" + windows_version = "latest" + private_ip_address = "{{ip_range}}.10" + password = "8dCsfT-DJjgS3xdcp" + size = "Standard_B2s" +} +"srv01" = { + name = "srv01" + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + windows_sku = "2025-Datacenter" + windows_version = "latest" + private_ip_address = "{{ip_range}}.11" + password = "NgtkgtIAs75cKV+Pu" + size = "Standard_B2s" +} diff --git a/ad/DRACARYS/providers/ludus/config.yml b/ad/DRACARYS/providers/ludus/config.yml new file mode 100644 index 00000000..9c150d5f --- /dev/null +++ b/ad/DRACARYS/providers/ludus/config.yml @@ -0,0 +1,27 @@ +ludus: + - vm_name: "{{ range_id }}-DC01" + hostname: "{{ range_id }}-DC01" + template: win2025-server-x64-tpm-template + vlan: 10 + ip_last_octet: 10 + ram_gb: 4 + cpus: 2 + windows: + sysprep: true + - vm_name: "{{ range_id }}-SRV01" + hostname: "{{ range_id }}-SRV01" + template: win2025-server-x64-tpm-template + vlan: 10 + ip_last_octet: 11 + ram_gb: 4 + cpus: 2 + windows: + sysprep: true + - vm_name: "{{ range_id }}-LX01" + hostname: "{{ range_id }}-LX01" + template: ubuntu-24.04-x64-server-template + vlan: 10 + ip_last_octet: 12 + ram_gb: 4 + cpus: 2 + linux: true diff --git a/ad/DRACARYS/providers/ludus/inventory b/ad/DRACARYS/providers/ludus/inventory new file mode 100644 index 00000000..a7a260cd --- /dev/null +++ b/ad/DRACARYS/providers/ludus/inventory @@ -0,0 +1,16 @@ +[default] +; ------------------------------------------------ +; dracarys.lab +; ------------------------------------------------ +dc01 ansible_host={{ip_range}}.10 dns_domain=dc01 dict_key=dc01 +srv01 ansible_host={{ip_range}}.11 dns_domain=dc01 dict_key=srv01 +lx01 ansible_host={{ip_range}}.12 dict_key=lx01 ansible_connection=ssh ansible_ssh_common_args='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' + +[all:vars] +force_dns_server=no +dns_server={{ip_range}}.254 + +dns_server_forwarder={{ip_range}}.254 + +ansible_user=localuser +ansible_password=password diff --git a/ad/DRACARYS/providers/proxmox/inventory b/ad/DRACARYS/providers/proxmox/inventory new file mode 100644 index 00000000..f25754a9 --- /dev/null +++ b/ad/DRACARYS/providers/proxmox/inventory @@ -0,0 +1,11 @@ +[default] +; ------------------------------------------------ +; dracarys.lab +; ------------------------------------------------ +dc01 ansible_host={{ip_range}}.10 dns_domain=dc01 dict_key=dc01 +srv01 ansible_host={{ip_range}}.11 dns_domain=dc01 dict_key=srv01 +lx01 ansible_host={{ip_range}}.12 dict_key=lx01 ansible_connection=ssh ansible_ssh_common_args='-o StrictHostKeyChecking=no' + +[all:vars] +force_dns_server=yes +dns_server={{ip_range}}.1 diff --git a/ad/DRACARYS/providers/proxmox/linux.tf b/ad/DRACARYS/providers/proxmox/linux.tf new file mode 100644 index 00000000..d9671f46 --- /dev/null +++ b/ad/DRACARYS/providers/proxmox/linux.tf @@ -0,0 +1,10 @@ +"lx01" = { + name = "LX01" + desc = "LX01 - DRACARYS" + cores = 2 + memory = 4096 + clone = "Ubuntu_2404_x64" + dns = "{{ip_range}}.1" + ip = "{{ip_range}}.12/24" + gateway = "{{ip_range}}.1" +} diff --git a/ad/DRACARYS/providers/proxmox/windows.tf b/ad/DRACARYS/providers/proxmox/windows.tf new file mode 100644 index 00000000..58fcc4ad --- /dev/null +++ b/ad/DRACARYS/providers/proxmox/windows.tf @@ -0,0 +1,22 @@ +"dc01" = { + name = "DC01" + desc = "DC01 - DRACARYS - windows server 2025 - {{ip_range}}.10" + cores = 2 + memory = 4096 + clone = "WinServer2025_x64" + dns = "{{ip_range}}.1" + ip = "{{ip_range}}.10/24" + gateway = "{{ip_range}}.1" + os = "win11" +} +"srv01" = { + name = "SRV01" + desc = "SRV01 - DRACARYS- windows server 2025 - {{ip_range}}.11" + cores = 2 + memory = 4096 + clone = "WinServer2025_x64" + dns = "{{ip_range}}.1" + ip = "{{ip_range}}.11/24" + gateway = "{{ip_range}}.1" + os = "win11" +} diff --git a/ad/DRACARYS/providers/virtualbox/Vagrantfile b/ad/DRACARYS/providers/virtualbox/Vagrantfile new file mode 100644 index 00000000..fe1322e2 --- /dev/null +++ b/ad/DRACARYS/providers/virtualbox/Vagrantfile @@ -0,0 +1,28 @@ +boxes = +[ + { :name => "DRACARYS-DC01", + :ip => "{{ip_range}}.10", + :box => "GOAD/WindowsServer2025", + :box_version => "2026.02.09", + :os => "windows", + :cpus => 2, + :mem => 3000 + }, + { :name => "DRACARYS-SRV01", + :ip => "{{ip_range}}.11", + :box => "GOAD/WindowsServer2025", + :box_version => "2026.02.09", + :os => "windows", + :cpus => 2, + :mem => 3000 + }, + { :name => "DRACARYS-LX01", + :ip => "{{ip_range}}.12", + :box => "bento/ubuntu-24.04", + :box_version => "202510.26.0", + :os => "linux", + :cpus => 2, + :mem => 3000, + :forwarded_port => [ {:guest => 22, :host => 2210, :id => "ssh"} ] + } +] diff --git a/ad/DRACARYS/providers/virtualbox/inventory b/ad/DRACARYS/providers/virtualbox/inventory new file mode 100644 index 00000000..30865fd4 --- /dev/null +++ b/ad/DRACARYS/providers/virtualbox/inventory @@ -0,0 +1,7 @@ +[default] +; ------------------------------------------------ +; dracarys.lab +; ------------------------------------------------ +dc01 ansible_host={{ip_range}}.10 dns_domain=dc01 dict_key=dc01 +srv01 ansible_host={{ip_range}}.11 dns_domain=dc01 dict_key=srv01 +lx01 ansible_host={{ip_range}}.12 dict_key=lx01 ansible_connection=ssh ansible_ssh_common_args='-o StrictHostKeyChecking=no' diff --git a/ad/DRACARYS/providers/vmware/Vagrantfile b/ad/DRACARYS/providers/vmware/Vagrantfile new file mode 100644 index 00000000..fe1322e2 --- /dev/null +++ b/ad/DRACARYS/providers/vmware/Vagrantfile @@ -0,0 +1,28 @@ +boxes = +[ + { :name => "DRACARYS-DC01", + :ip => "{{ip_range}}.10", + :box => "GOAD/WindowsServer2025", + :box_version => "2026.02.09", + :os => "windows", + :cpus => 2, + :mem => 3000 + }, + { :name => "DRACARYS-SRV01", + :ip => "{{ip_range}}.11", + :box => "GOAD/WindowsServer2025", + :box_version => "2026.02.09", + :os => "windows", + :cpus => 2, + :mem => 3000 + }, + { :name => "DRACARYS-LX01", + :ip => "{{ip_range}}.12", + :box => "bento/ubuntu-24.04", + :box_version => "202510.26.0", + :os => "linux", + :cpus => 2, + :mem => 3000, + :forwarded_port => [ {:guest => 22, :host => 2210, :id => "ssh"} ] + } +] diff --git a/ad/DRACARYS/providers/vmware/inventory b/ad/DRACARYS/providers/vmware/inventory new file mode 100644 index 00000000..30865fd4 --- /dev/null +++ b/ad/DRACARYS/providers/vmware/inventory @@ -0,0 +1,7 @@ +[default] +; ------------------------------------------------ +; dracarys.lab +; ------------------------------------------------ +dc01 ansible_host={{ip_range}}.10 dns_domain=dc01 dict_key=dc01 +srv01 ansible_host={{ip_range}}.11 dns_domain=dc01 dict_key=srv01 +lx01 ansible_host={{ip_range}}.12 dict_key=lx01 ansible_connection=ssh ansible_ssh_common_args='-o StrictHostKeyChecking=no' diff --git a/ad/DRACARYS/scripts/set_spn.ps1 b/ad/DRACARYS/scripts/set_spn.ps1 new file mode 100644 index 00000000..ec84c38c --- /dev/null +++ b/ad/DRACARYS/scripts/set_spn.ps1 @@ -0,0 +1 @@ +Set-ADComputer -Identity "syrax$" -Add @{'msDS-AllowedToDelegateTo'=@('HTTP/arrax.dracarys.lab','HTTP/arrax')} diff --git a/ad/DRACARYS/scripts/wsman_kerb.ps1 b/ad/DRACARYS/scripts/wsman_kerb.ps1 new file mode 100644 index 00000000..a1586936 --- /dev/null +++ b/ad/DRACARYS/scripts/wsman_kerb.ps1 @@ -0,0 +1,2 @@ +Set-ADComputer -Identity "vhagar$" -ServicePrincipalNames @{Add='WSMAN/vhagar.dracarys.lab'} +Set-ADComputer -Identity "vhagar$" -Add @{'msDS-AllowedToDelegateTo'=@('WSMAN/vhagar.dracarys.lab')} diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg index 4368ce86..1b1ac9d6 100644 --- a/ansible/ansible.cfg +++ b/ansible/ansible.cfg @@ -36,6 +36,9 @@ any_errors_fatal = False timeout = 600 command_timeout = 600 +# Vars plugins +vars_plugins_enabled = host_group_vars,dreadnode.goad.lab_config + # Fact caching configuration # Note: fact_caching_connection is set via ANSIBLE_CACHE_PLUGIN_CONNECTION env var in Taskfile gathering = smart diff --git a/ansible/extensions/elk/README.md b/ansible/extensions/elk/README.md deleted file mode 100644 index 1ece9245..00000000 --- a/ansible/extensions/elk/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# ELK extension - -- Extension Name: elk -- Description: Add an ELK to the current lab -- Machine name : {{lab_name}}-ELK -- Compatible with labs : * - -## prerequisites - -On ludus prepare template : - -```bash -ludus templates add -d ubuntu-22.04-x64-server -ludus templates build -``` - -## Install - -```bash -instance_id> install_extension elk -``` - -- machine: {{lab_name}}-ELK -- filebeat agent domain computer machines - - -## Uninstall - -- Not implemented yet diff --git a/ansible/extensions/elk/ansible/install.yml b/ansible/extensions/elk/ansible/install.yml deleted file mode 100644 index 73b8a4ac..00000000 --- a/ansible/extensions/elk/ansible/install.yml +++ /dev/null @@ -1,11 +0,0 @@ -# LOGS and Monitoring ========================================================================================== -- name: Install ELK - hosts: elk_server - become: yes - roles: - - { role: 'elk', tags: 'elk' } - -- name: Install log agent on windows vms - hosts: elk_log - roles: - - { role: 'logs_windows', tags: 'agent' } diff --git a/ansible/extensions/elk/ansible/roles/elk/defaults/main.yml b/ansible/extensions/elk/ansible/roles/elk/defaults/main.yml deleted file mode 100644 index 830562d5..00000000 --- a/ansible/extensions/elk/ansible/roles/elk/defaults/main.yml +++ /dev/null @@ -1,2 +0,0 @@ -elasticsearch_version: '7.x' -es_cluster_name: elasticsearch diff --git a/ansible/extensions/elk/ansible/roles/elk/files/elasticsearch.yml b/ansible/extensions/elk/ansible/roles/elk/files/elasticsearch.yml deleted file mode 100644 index 59eb635b..00000000 --- a/ansible/extensions/elk/ansible/roles/elk/files/elasticsearch.yml +++ /dev/null @@ -1,89 +0,0 @@ -# ======================== Elasticsearch Configuration ========================= -# -# NOTE: Elasticsearch comes with reasonable defaults for most settings. -# Before you set out to tweak and tune the configuration, make sure you -# understand what are you trying to accomplish and the consequences. -# -# The primary way of configuring a node is via this file. This template lists -# the most important settings you may want to configure for a production cluster. -# -# Please consult the documentation for further information on configuration options: -# https://www.elastic.co/guide/en/elasticsearch/reference/index.html -# -# ---------------------------------- Cluster ----------------------------------- -# -# Use a descriptive name for your cluster: -# -#cluster.name: my-application -# -# ------------------------------------ Node ------------------------------------ -# -# Use a descriptive name for the node: -# -#node.name: node-1 -# -# Add custom attributes to the node: -# -#node.attr.rack: r1 -# -# ----------------------------------- Paths ------------------------------------ -# -# Path to directory where to store the data (separate multiple locations by comma): -# -path.data: /var/lib/elasticsearch -# -# Path to log files: -# -path.logs: /var/log/elasticsearch -# -# ----------------------------------- Memory ----------------------------------- -# -# Lock the memory on startup: -# -#bootstrap.memory_lock: true -# -# Make sure that the heap size is set to about half the memory available -# on the system and that the owner of the process is allowed to use this -# limit. -# -# Elasticsearch performs poorly when the system is swapping the memory. -# -# ---------------------------------- Network ----------------------------------- -# -# Set the bind address to a specific IP (IPv4 or IPv6): -# -network.host: 0.0.0.0 -# -# Set a custom port for HTTP: -# -http.port: 9200 -# -# For more information, consult the network module documentation. -# -# --------------------------------- Discovery ---------------------------------- -# -# Pass an initial list of hosts to perform discovery when this node is started: -# The default list of hosts is ["127.0.0.1", "[::1]"] -# -#discovery.seed_hosts: ["host1", "host2"] -discovery.type: single-node -# -# Bootstrap the cluster using an initial set of master-eligible nodes: -# -#cluster.initial_master_nodes: ["node-1", "node-2"] -# -# For more information, consult the discovery and cluster formation module documentation. -# -# ---------------------------------- Gateway ----------------------------------- -# -# Block initial recovery after a full cluster restart until N nodes are started: -# -#gateway.recover_after_nodes: 3 -# -# For more information, consult the gateway module documentation. -# -# ---------------------------------- Various ----------------------------------- -# -# Require explicit names when deleting indices: -# -#action.destructive_requires_name: true diff --git a/ansible/extensions/elk/ansible/roles/elk/files/kibana.yml b/ansible/extensions/elk/ansible/roles/elk/files/kibana.yml deleted file mode 100644 index 8e3dbc7b..00000000 --- a/ansible/extensions/elk/ansible/roles/elk/files/kibana.yml +++ /dev/null @@ -1,111 +0,0 @@ -# Kibana is served by a back end server. This setting specifies the port to use. -server.port: 5601 - -# Specifies the address to which the Kibana server will bind. IP addresses and host names are both valid values. -# The default is 'localhost', which usually means remote machines will not be able to connect. -# To allow connections from remote users, set this parameter to a non-loopback address. -server.host: "0.0.0.0" - -# Enables you to specify a path to mount Kibana at if you are running behind a proxy. -# Use the `server.rewriteBasePath` setting to tell Kibana if it should remove the basePath -# from requests it receives, and to prevent a deprecation warning at startup. -# This setting cannot end in a slash. -#server.basePath: "" - -# Specifies whether Kibana should rewrite requests that are prefixed with -# `server.basePath` or require that they are rewritten by your reverse proxy. -# This setting was effectively always `false` before Kibana 6.3 and will -# default to `true` starting in Kibana 7.0. -#server.rewriteBasePath: false - -# Specifies the public URL at which Kibana is available for end users. If -# `server.basePath` is configured this URL should end with the same basePath. -#server.publicBaseUrl: "" - -# The maximum payload size in bytes for incoming server requests. -#server.maxPayloadBytes: 1048576 - -# The Kibana server's name. This is used for display purposes. -#server.name: "your-hostname" - -# The URLs of the Elasticsearch instances to use for all your queries. -#elasticsearch.hosts: ["http://localhost:9200"] - -# Kibana uses an index in Elasticsearch to store saved searches, visualizations and -# dashboards. Kibana creates a new index if the index doesn't already exist. -#kibana.index: ".kibana" - -# The default application to load. -#kibana.defaultAppId: "home" - -# If your Elasticsearch is protected with basic authentication, these settings provide -# the username and password that the Kibana server uses to perform maintenance on the Kibana -# index at startup. Your Kibana users still need to authenticate with Elasticsearch, which -# is proxied through the Kibana server. -#elasticsearch.username: "kibana_system" -#elasticsearch.password: "pass" - -# Enables SSL and paths to the PEM-format SSL certificate and SSL key files, respectively. -# These settings enable SSL for outgoing requests from the Kibana server to the browser. -#server.ssl.enabled: false -#server.ssl.certificate: /path/to/your/server.crt -#server.ssl.key: /path/to/your/server.key - -# Optional settings that provide the paths to the PEM-format SSL certificate and key files. -# These files are used to verify the identity of Kibana to Elasticsearch and are required when -# xpack.security.http.ssl.client_authentication in Elasticsearch is set to required. -#elasticsearch.ssl.certificate: /path/to/your/client.crt -#elasticsearch.ssl.key: /path/to/your/client.key - -# Optional setting that enables you to specify a path to the PEM file for the certificate -# authority for your Elasticsearch instance. -#elasticsearch.ssl.certificateAuthorities: [ "/path/to/your/CA.pem" ] - -# To disregard the validity of SSL certificates, change this setting's value to 'none'. -#elasticsearch.ssl.verificationMode: full - -# Time in milliseconds to wait for Elasticsearch to respond to pings. Defaults to the value of -# the elasticsearch.requestTimeout setting. -#elasticsearch.pingTimeout: 1500 - -# Time in milliseconds to wait for responses from the back end or Elasticsearch. This value -# must be a positive integer. -#elasticsearch.requestTimeout: 30000 - -# List of Kibana client-side headers to send to Elasticsearch. To send *no* client-side -# headers, set this value to [] (an empty list). -#elasticsearch.requestHeadersWhitelist: [ authorization ] - -# Header names and values that are sent to Elasticsearch. Any custom headers cannot be overwritten -# by client-side headers, regardless of the elasticsearch.requestHeadersWhitelist configuration. -#elasticsearch.customHeaders: {} - -# Time in milliseconds for Elasticsearch to wait for responses from shards. Set to 0 to disable. -#elasticsearch.shardTimeout: 30000 - -# Logs queries sent to Elasticsearch. Requires logging.verbose set to true. -#elasticsearch.logQueries: false - -# Specifies the path where Kibana creates the process ID file. -#pid.file: /run/kibana/kibana.pid - -# Enables you to specify a file where Kibana stores log output. -#logging.dest: stdout - -# Set the value of this setting to true to suppress all logging output. -#logging.silent: false - -# Set the value of this setting to true to suppress all logging output other than error messages. -#logging.quiet: false - -# Set the value of this setting to true to log all events, including system usage information -# and all requests. -#logging.verbose: false - -# Set the interval in milliseconds to sample system and process performance -# metrics. Minimum is 100ms. Defaults to 5000. -#ops.interval: 5000 - -# Specifies locale to be used for all localizable strings, dates and number formats. -# Supported languages are the following: English - en , by default , Chinese - zh-CN . -#i18n.locale: "en" diff --git a/ansible/extensions/elk/ansible/roles/elk/tasks/main.yml b/ansible/extensions/elk/ansible/roles/elk/tasks/main.yml deleted file mode 100644 index 4cd07819..00000000 --- a/ansible/extensions/elk/ansible/roles/elk/tasks/main.yml +++ /dev/null @@ -1,96 +0,0 @@ ---- -- name: Update cache - ansible.builtin.apt: - update_cache: true - cache_valid_time: 86400 - -- name: Add required dependencies - ansible.builtin.apt: - name: - - apt-transport-https - - gnupg2 - state: present - update_cache: yes - -- name: Add Elasticsearch apt key - ansible.builtin.apt_key: - url: https://artifacts.elastic.co/GPG-KEY-elasticsearch - state: present - -- name: Add Elasticsearch repository - ansible.builtin.apt_repository: - repo: 'deb https://artifacts.elastic.co/packages/{{ elasticsearch_version }}/apt stable main' - state: present - update_cache: true - -- name: Install logstash - ansible.builtin.apt: - name: logstash - state: present - -- name: Install java - ansible.builtin.apt: - name: openjdk-11-jre - state: present - -- name: Install elasticsearch - ansible.builtin.apt: - name: elasticsearch - state: present - -- name: Install kibana - ansible.builtin.apt: - name: kibana - state: present - -- name: Copy kibana config - ansible.builtin.copy: - src: kibana.yml - dest: /etc/kibana/kibana.yml - owner: "root" - group: "kibana" - mode: 0660 - -- name: Elasticsearch change start timeout to 3min - ansible.builtin.lineinfile: - destfile: /usr/lib/systemd/system/elasticsearch.service - regexp: 'TimeoutStartSec=' - line: 'TimeoutStartSec=180' - -- name: Copy elasticsearch config - ansible.builtin.copy: - src: elasticsearch.yml - dest: /etc/elasticsearch/elasticsearch.yml - owner: "root" - group: "elasticsearch" - mode: 0660 - -- name: Enable logstash - ansible.builtin.service: - name: logstash - enabled: yes - -- name: Enable elasticsearch - ansible.builtin.service: - name: elasticsearch - enabled: yes - -- name: Enable kibana - ansible.builtin.service: - name: kibana - enabled: yes - -- name: Start logstash - ansible.builtin.service: - name: logstash - state: started - -- name: Start elasticsearch - ansible.builtin.service: - name: elasticsearch - state: started - -- name: Start kibana - ansible.builtin.service: - name: kibana - state: started diff --git a/ansible/extensions/elk/ansible/roles/logs_windows/defaults/main.yml b/ansible/extensions/elk/ansible/roles/logs_windows/defaults/main.yml deleted file mode 100644 index c1f16ce7..00000000 --- a/ansible/extensions/elk/ansible/roles/logs_windows/defaults/main.yml +++ /dev/null @@ -1,12 +0,0 @@ ---- -sysmon_download_url_base: "https://download.sysinternals.com/files" -sysmon_install_location: "C:\\sysmon" -sysmon_download_file: Sysmon -file_ext: .zip -sysmon_config_url: "https://raw.githubusercontent.com/SwiftOnSecurity/sysmon-config/master/sysmonconfig-export.xml" - -winlogbeat_service: - install_path_64: "C:\\Program Files\\Elastic\\winlogbeat" - install_path_32: "C:\\Program Files (x86)\\Elastic\\winlogbeat" - version: "7.17.6" - download: true diff --git a/ansible/extensions/elk/ansible/roles/logs_windows/files/Sysmon.zip b/ansible/extensions/elk/ansible/roles/logs_windows/files/Sysmon.zip deleted file mode 100644 index b9e9c516..00000000 Binary files a/ansible/extensions/elk/ansible/roles/logs_windows/files/Sysmon.zip and /dev/null differ diff --git a/ansible/extensions/elk/ansible/roles/logs_windows/files/sysmonconfig-export.xml b/ansible/extensions/elk/ansible/roles/logs_windows/files/sysmonconfig-export.xml deleted file mode 100644 index 9cea79f2..00000000 --- a/ansible/extensions/elk/ansible/roles/logs_windows/files/sysmonconfig-export.xml +++ /dev/null @@ -1,1160 +0,0 @@ - - - - - md5,sha256,IMPHASH - - - - - - - - - - - - - - - - - "C:\Windows\system32\wermgr.exe" "-queuereporting_svc" - C:\Windows\system32\DllHost.exe /Processid - C:\Windows\system32\wbem\wmiprvse.exe -Embedding - C:\Windows\system32\wbem\wmiprvse.exe -secured -Embedding - C:\Windows\system32\wermgr.exe -upload - C:\Windows\system32\SearchIndexer.exe /Embedding - C:\windows\system32\wermgr.exe -queuereporting - \??\C:\Windows\system32\autochk.exe * - \SystemRoot\System32\smss.exe - C:\Windows\System32\RuntimeBroker.exe -Embedding - C:\Program Files (x86)\Common Files\microsoft shared\ink\TabTip32.exe - C:\Windows\System32\TokenBrokerCookies.exe - C:\Windows\System32\plasrv.exe - C:\Windows\System32\wifitask.exe - C:\Windows\system32\CompatTelRunner.exe - C:\Windows\system32\PrintIsolationHost.exe - C:\Windows\system32\SppExtComObj.Exe - C:\Windows\system32\audiodg.exe - C:\Windows\system32\conhost.exe - C:\Windows\system32\mobsync.exe - C:\Windows\system32\musNotification.exe - C:\Windows\system32\musNotificationUx.exe - C:\Windows\system32\powercfg.exe - C:\Windows\system32\sndVol.exe - C:\Windows\system32\sppsvc.exe - C:\Windows\system32\wbem\WmiApSrv.exe - AppContainer - %%SystemRoot%%\system32\csrss.exe ObjectDirectory=\Windows - C:\windows\system32\wermgr.exe -queuereporting - C:\WINDOWS\system32\devicecensus.exe UserCxt - C:\Windows\System32\usocoreworker.exe -Embedding - C:\Windows\system32\SearchIndexer.exe - - C:\Windows\system32\svchost.exe -k appmodel -s StateRepository - C:\Windows\system32\svchost.exe -k appmodel -p -s camsvc - C:\Windows\system32\svchost.exe -k appmodel - C:\Windows\system32\svchost.exe -k appmodel -p -s tiledatamodelsvc - C:\Windows\system32\svchost.exe -k camera -s FrameServer - C:\Windows\system32\svchost.exe -k dcomlaunch -s LSM - C:\Windows\system32\svchost.exe -k dcomlaunch -s PlugPlay - C:\Windows\system32\svchost.exe -k defragsvc - C:\Windows\system32\svchost.exe -k devicesflow -s DevicesFlowUserSvc - C:\Windows\system32\svchost.exe -k imgsvc - C:\Windows\system32\svchost.exe -k localService -s EventSystem - C:\Windows\system32\svchost.exe -k localService -s bthserv - C:\Windows\system32\svchost.exe -k LocalService -p -s BthAvctpSvc - C:\Windows\system32\svchost.exe -k localService -s nsi - C:\Windows\system32\svchost.exe -k localService -s w32Time - C:\Windows\system32\svchost.exe -k localServiceAndNoImpersonation - C:\Windows\system32\svchost.exe -k localServiceNetworkRestricted -s Dhcp - C:\Windows\system32\svchost.exe -k localServiceNetworkRestricted -s EventLog - C:\Windows\system32\svchost.exe -k localServiceNetworkRestricted -s TimeBrokerSvc - C:\Windows\system32\svchost.exe -k localServiceNetworkRestricted -s WFDSConMgrSvc - C:\Windows\system32\svchost.exe -k LocalServiceNetworkRestricted -s BTAGService - C:\Windows\System32\svchost.exe -k LocalSystemNetworkRestricted -p -s NcbService - C:\Windows\system32\svchost.exe -k localServiceNetworkRestricted - C:\Windows\system32\svchost.exe -k localServiceAndNoImpersonation -s SensrSvc - C:\Windows\system32\svchost.exe -k localServiceAndNoImpersonation -p -s SSDPSRV - C:\Windows\system32\svchost.exe -k localServiceNoNetwork - C:\Windows\system32\svchost.exe -k localSystemNetworkRestricted -p -s WPDBusEnum - C:\Windows\system32\svchost.exe -k localSystemNetworkRestricted -p -s fhsvc - C:\Windows\system32\svchost.exe -k localSystemNetworkRestricted -s DeviceAssociationService - C:\Windows\system32\svchost.exe -k localSystemNetworkRestricted -s NcbService - C:\Windows\system32\svchost.exe -k localSystemNetworkRestricted -s SensorService - C:\Windows\system32\svchost.exe -k localSystemNetworkRestricted -s TabletInputService - C:\Windows\system32\svchost.exe -k localSystemNetworkRestricted -s UmRdpService - C:\Windows\system32\svchost.exe -k localSystemNetworkRestricted -s WPDBusEnum - C:\Windows\system32\svchost.exe -k localSystemNetworkRestricted -p -s NgcSvc - C:\Windows\system32\svchost.exe -k localServiceNetworkRestricted -p -s NgcCtnrSvc - C:\Windows\system32\svchost.exe -k localServiceAndNoImpersonation -s SCardSvr - C:\Windows\system32\svchost.exe -k netsvcs -p -s wuauserv - C:\Windows\System32\svchost.exe -k netsvcs -p -s SessionEnv - C:\Windows\system32\svchost.exe -k localSystemNetworkRestricted -s WdiSystemHost - C:\Windows\System32\svchost.exe -k localSystemNetworkRestricted -p -s WdiSystemHost - C:\Windows\system32\svchost.exe -k localSystemNetworkRestricted - C:\Windows\system32\svchost.exe -k netsvcs -p -s wlidsvc - C:\Windows\system32\svchost.exe -k netsvcs -p -s ncaSvc - C:\Windows\system32\svchost.exe -k netsvcs -s BDESVC - C:\Windows\System32\svchost.exe -k netsvcs -p -s BDESVC - C:\Windows\system32\svchost.exe -k netsvcs -p -s BITS - C:\Windows\system32\svchost.exe -k netsvcs -s BITS - C:\Windows\system32\svchost.exe -k netsvcs -s CertPropSvc - C:\Windows\system32\svchost.exe -k netsvcs -s DsmSvc - C:\Windows\system32\svchost.exe -k netsvcs -p -s Appinfo - C:\Windows\system32\svchost.exe -k netsvcs -s Gpsvc - C:\Windows\system32\svchost.exe -k netsvcs -s ProfSvc - C:\Windows\system32\svchost.exe -k netsvcs -s SENS - C:\Windows\system32\svchost.exe -k netsvcs -s SessionEnv - C:\Windows\system32\svchost.exe -k netsvcs -s Themes - C:\Windows\system32\svchost.exe -k netsvcs -s Winmgmt - C:\Windows\system32\svchost.exe -k netsvcs - C:\Windows\system32\svchost.exe -k networkService -p -s DoSvc - C:\Windows\system32\svchost.exe -k networkService -s Dnscache - C:\Windows\system32\svchost.exe -k networkService -s LanmanWorkstation - C:\Windows\system32\svchost.exe -k networkService -s NlaSvc - C:\Windows\system32\svchost.exe -k networkService -s TermService - C:\Windows\system32\svchost.exe -k networkService - C:\Windows\system32\svchost.exe -k networkServiceNetworkRestricted - C:\Windows\system32\svchost.exe -k rPCSS - C:\Windows\system32\svchost.exe -k secsvcs - C:\Windows\system32\svchost.exe -k swprv - C:\Windows\system32\svchost.exe -k unistackSvcGroup - C:\Windows\system32\svchost.exe -k utcsvc - C:\Windows\system32\svchost.exe -k wbioSvcGroup - C:\Windows\system32\svchost.exe -k werSvcGroup - C:\Windows\system32\svchost.exe -k wusvcs -p -s WaaSMedicSvc - C:\Windows\System32\svchost.exe -k wsappx -p -s ClipSVC - C:\Windows\system32\svchost.exe -k wsappx -p -s AppXSvc - C:\Windows\system32\svchost.exe -k wsappx -s ClipSVC - C:\Windows\system32\svchost.exe -k wsappx - C:\Windows\system32\svchost.exe -k netsvcs - C:\Windows\system32\svchost.exe -k localSystemNetworkRestricted - C:\Windows\system32\deviceenroller.exe /c /AutoEnrollMDM - - "C:\Program Files (x86)\Microsoft\Edge Dev\Application\msedge.exe" --type= - - C:\Windows\Microsoft.NET\Framework\v4.0.30319\ngen.exe - C:\WINDOWS\Microsoft.NET\Framework64\v4.0.30319\Ngen.exe - C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscorsvw.exe - C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorsvw.exe - C:\Windows\Microsoft.Net\Framework64\v3.0\WPF\PresentationFontCache.exe - C:\Windows\Microsoft.NET\Framework64\v4.0.30319\ngentask.exe - C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscorsvw.exe - C:\Windows\Microsoft.NET\Framework64\v4.0.30319\ngentask.exe - C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorsvw.exe - C:\Windows\Microsoft.NET\Framework\v4.0.30319\ngentask.exe - - C:\Program Files\Microsoft Office\Office16\MSOSYNC.EXE - C:\Program Files (x86)\Microsoft Office\Office16\MSOSYNC.EXE - C:\Program Files\Common Files\Microsoft Shared\OfficeSoftwareProtectionPlatform\OSPPSVC.EXE - C:\Program Files\Microsoft Office\Office16\msoia.exe - C:\Program Files (x86)\Microsoft Office\root\Office16\officebackgroundtaskhandler.exe - - C:\Program Files\Common Files\Microsoft Shared\ClickToRun\OfficeC2RClient.exe - C:\Program Files\Common Files\Microsoft Shared\ClickToRun\OfficeClickToRun.exe - C:\Program Files\Common Files\Microsoft Shared\ClickToRun\OfficeC2RClient.exe - - C:\Program Files\Windows Media Player\wmpnscfg.exe - - "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --type= - "C:\Program Files\Google\Chrome\Application\chrome.exe" --type= - - - - - - - - - - C:\Users - .exe - \Device\HarddiskVolumeShadowCopy - - - - - - OneDrive.exe - C:\Windows\system32\backgroundTaskHost.exe - setup - install - Update\ - redist.exe - msiexec.exe - TrustedInstaller.exe - \NVIDIA\NvBackend\ApplicationOntology\ - - - - - - - - - - - - - - - - - C:\Users - C:\Recycle - C:\ProgramData - C:\Windows\Temp - \ - C:\perflogs - C:\intel - C:\Windows\fonts - C:\Windows\system32\config - - at.exe - certutil.exe - cmd.exe - cmstp.exe - cscript.exe - driverquery.exe - dsquery.exe - hh.exe - infDefaultInstall.exe - java.exe - javaw.exe - javaws.exe - mmc.exe - msbuild.exe - mshta.exe - msiexec.exe - nbtstat.exe - net.exe - net1.exe - notepad.exe - nslookup.exe - powershell.exe - qprocess.exe - qwinsta.exe - qwinsta.exe - reg.exe - regsvcs.exe - regsvr32.exe - rundll32.exe - rwinsta.exe - sc.exe - schtasks.exe - taskkill.exe - tasklist.exe - wmic.exe - wscript.exe - - nc.exe - ncat.exe - psexec.exe - psexesvc.exe - tor.exe - vnc.exe - vncservice.exe - vncviewer.exe - winexesvc.exe - nmap.exe - psinfo.exe - - 22 - 23 - 25 - 143 - 3389 - 5800 - 5900 - 444 - - 1080 - 3128 - 8080 - - 1723 - 9001 - 9030 - - - - - - - C:\ProgramData\Microsoft\Windows Defender\Platform\ - AppData\Local\Microsoft\Teams\current\Teams.exe - .microsoft.com - microsoft.com.akadns.net - microsoft.com.nsatc.net - - 23.4.43.27 - 72.21.91.29 - - 127.0.0.1 - fe80:0:0:0 - - - - - - - - - - - - - - - C:\Users - \ - - - - - - - - - - - - - - - - microsoft - windows - Intel - - - - - - - - - - - - - - - - - - - - - - C:\Windows\system32\wbem\WmiPrvSE.exe - C:\Windows\system32\svchost.exe - C:\Windows\system32\wininit.exe - C:\Windows\system32\csrss.exe - C:\Windows\system32\services.exe - C:\Windows\system32\winlogon.exe - C:\Windows\system32\audiodg.exe - C:\Windows\system32\kernel32.dll - C:\Program Files (x86)\Google\Chrome\Application\chrome.exe - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \Start Menu - \Startup\ - \Content.Outlook\ - \Downloads\ - .application - .appref-ms - .bat - .chm - .cmd - .cmdline - .crx - .dmp - .docm - .dll - .exe - .exe.log - .jar - .jnlp - .jse - .hta - .job - .pptm - .ps1 - .sys - .scr - .vbe - .vbs - .xlsm - .ocx - proj - .sln - .xls - C:\Users\Default - C:\Windows\system32\Drivers - C:\Windows\SysWOW64\Drivers - C:\Windows\system32\GroupPolicy\Machine\Scripts - C:\Windows\system32\GroupPolicy\User\Scripts - C:\Windows\system32\Wbem - C:\Windows\SysWOW64\Wbem - C:\Windows\system32\WindowsPowerShell - C:\Windows\SysWOW64\WindowsPowerShell - C:\Windows\Tasks\ - C:\Windows\system32\Tasks - C:\Windows\SysWOW64\Tasks - \Device\HarddiskVolumeShadowCopy - - C:\Windows\AppPatch\Custom - VirtualStore - - .xls - .ppt - .rtf - - - - - - - C:\Program Files (x86)\EMET 5.5\EMET_Service.exe - - C:\Program Files\Common Files\Microsoft Shared\ClickToRun\OfficeC2RClient.exe - - C:\Windows\system32\smss.exe - C:\Windows\system32\CompatTelRunner.exe - \\?\C:\Windows\system32\wbem\WMIADAP.EXE - C:\Windows\system32\mobsync.exe - C:\Windows\system32\DriverStore\Temp\ - C:\Windows\system32\wbem\Performance\ - C:\Windows\Installer\ - - C:\$WINDOWS.~BT\Sources\ - C:\Windows\winsxs\amd64_microsoft-windows - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CurrentVersion\Run - Policies\Explorer\Run - Group Policy\Scripts - Windows\System\Scripts - CurrentVersion\Windows\Load - CurrentVersion\Windows\Run - CurrentVersion\Winlogon\Shell - CurrentVersion\Winlogon\System - HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify - HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell - HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit - HKLM\Software\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Drivers32 - HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\BootExecute - HKLM\Software\Microsoft\Windows NT\CurrentVersion\AeDebug - UserInitMprLogonScript - user shell folders\startup - - \ServiceDll - \ServiceManifest - \ImagePath - \Start - - Control\Terminal Server\WinStations\RDP-Tcp\PortNumber - Control\Terminal Server\fSingleSessionPerUser - fDenyTSConnections - LastLoggedOnUser - RDP-tcp\PortNumber - Services\PortProxy\v4tov4 - - \command\ - \ddeexec\ - {86C86720-42A0-1069-A2E8-08002B30309D} - exefile - - \InprocServer32\(Default) - - \Hidden - \ShowSuperHidden - \HideFileExt - - Classes\*\ - Classes\AllFilesystemObjects\ - Classes\Directory\ - Classes\Drive\ - Classes\Folder\ - Classes\PROTOCOLS\ - ContextMenuHandlers\ - CurrentVersion\Shell - HKLM\Software\Microsoft\Windows\CurrentVersion\explorer\ShellExecuteHooks - HKLM\Software\Microsoft\Windows\CurrentVersion\explorer\ShellServiceObjectDelayLoad - HKLM\Software\Microsoft\Windows\CurrentVersion\explorer\ShellIconOverlayIdentifiers - - HKLM\Software\Microsoft\Windows\CurrentVersion\App Paths\ - - HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp\InitialProgram - - HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\GPExtensions\ - - HKLM\SYSTEM\CurrentControlSet\Services\WinSock - \ProxyServer - - HKLM\Software\Microsoft\Windows\CurrentVersion\Authentication\Credential Provider - HKLM\SYSTEM\CurrentControlSet\Control\Lsa\ - HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SecurityProviders - HKLM\Software\Microsoft\Netsh - Software\Microsoft\Windows\CurrentVersion\Internet Settings\ProxyEnable - - HKLM\SYSTEM\CurrentControlSet\Control\NetworkProvider\Order\ - HKLM\Software\Microsoft\Windows NT\CurrentVersion\NetworkList\Profiles - \EnableFirewall - \DoNotAllowExceptions - HKLM\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\StandardProfile\AuthorizedApplications\List - HKLM\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\DomainProfile\AuthorizedApplications\List - - HKLM\Software\Microsoft\Windows NT\CurrentVersion\Windows\Appinit_Dlls\ - HKLM\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows\Appinit_Dlls\ - HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDlls\ - - Microsoft\Office\Outlook\Addins\ - Office Test\ - Security\Trusted Documents\TrustRecords - \EnableBHO - - Internet Explorer\Toolbar\ - Internet Explorer\Extensions\ - Browser Helper Objects\ - \DisableSecuritySettingsCheck - \3\1206 - \3\2500 - \3\1809 - - HKLM\Software\Classes\CLSID\{AB8902B4-09CA-4BB6-B78D-A8F59079A8D5}\ - HKLM\Software\Classes\WOW6432Node\CLSID\{AB8902B4-09CA-4BB6-B78D-A8F59079A8D5}\ - HKLM\Software\Classes\CLSID\{083863F1-70DE-11d0-BD40-00A0C911CE86}\ - HKLM\Software\Classes\WOW6432Node\CLSID\{083863F1-70DE-11d0-BD40-00A0C911CE86}\ - - \UrlUpdateInfo - \InstallSource - \EulaAccepted - - \DisableAntiSpyware - \DisableAntiVirus - \SpynetReporting - DisableRealtimeMonitoring - \SubmitSamplesConsent - - HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA - HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\LocalAccountTokenFilterPolicy - - HKLM\Software\Microsoft\Security Center\ - SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\HideSCAHealth - - HKLM\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Custom - HKLM\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\InstalledSDB - VirtualStore - - HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\ - HKLM\Software\Microsoft\Windows\CurrentVersion\WINEVT\ - HKLM\SYSTEM\CurrentControlSet\Control\Safeboot\ - HKLM\SYSTEM\CurrentControlSet\Control\Winlogon\ - \FriendlyName - HKLM\Software\Microsoft\Windows\CurrentVersion\Installer\InProgress\(Default) - HKLM\Software\Microsoft\Tracing\RASAPI32 - HKLM\Software\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\ - - \LowerCaseLongPath - \Publisher - \BinProductVersion - \DriverVersion - \DriverVerVersion - \LinkDate - Compatibility Assistant\Store\ - - regedit.exe - \ - - - - - - - - \{CAFEEFAC- - CreateKey - HKLM\COMPONENTS - - HKLM\Software\Microsoft\Windows\CurrentVersion\AppModel\StateRepository\Cache - - Toolbar\WebBrowser - Browser\ITBar7Height - Browser\ITBar7Layout - Internet Explorer\Toolbar\Locked - Toolbar\WebBrowser\{47833539-D0C5-4125-9FA8-0819E2EAAC93} - }\PreviousPolicyAreas - \Control\WMI\Autologger\ - HKLM\SYSTEM\CurrentControlSet\Services\UsoSvc\Start - \Lsa\OfflineJoin\CurrentValue - HKLM\Software\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\ - _Classes\AppX - HKLM\Software\Microsoft\Windows\CurrentVersion\WINEVT\Publishers\ - - HKLM\SYSTEM\CurrentControlSet\Control\Lsa\LsaPid - HKLM\SYSTEM\CurrentControlSet\Control\Lsa\SspiCache - HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Domains - - \Services\BITS\Start - \services\clr_optimization_v2.0.50727_32\Start - \services\clr_optimization_v2.0.50727_64\Start - \services\clr_optimization_v4.0.30319_32\Start - \services\clr_optimization_v4.0.30319_64\Start - \services\deviceAssociationService\Start - \services\fhsvc\Start - \services\nal\Start - \services\trustedInstaller\Start - \services\tunnel\Start - \services\usoSvc\Start - - \UserChoice\ProgId - \UserChoice\Hash - \OpenWithList\MRUList - Shell Extentions\Cached - - HKLM\System\CurrentControlSet\Control\Lsa\Audit\SpecialGroups - SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\0\PSScriptOrder - SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\0\SOM-ID - SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\0\GPO-ID - SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\0\0\IsPowershell - SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\0\0\ExecTime - SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown\0\PSScriptOrder - SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown\0\SOM-ID - SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown\0\GPO-ID - SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown\0\0\IsPowershell - SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown\0\0\ExecTime - \safer\codeidentifiers\0\HASHES\{ - - VirtualStore\MACHINE\SOFTWARE\Microsoft\Office\ClickToRun\ - HKLM\SOFTWARE\Microsoft\Office\ClickToRun\ - - C:\Program Files\WIDCOMM\Bluetooth Software\btwdins.exe - HKCR\VLC. - HKCR\iTunes. - - HKLM\Software\Microsoft\Windows\CurrentVersion\WINEVT\Publishers\{945a8954-c147-4acd-923f-40c45405a658} - - - - - - - - - - - Downloads - Temp\7z - Startup - .bat - .cmd - .doc - .hta - .lnk - .ppt - .ps1 - .ps2 - .reg - .jse - .vb - .vbe - .vbs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .arpa. - .arpa - .msftncsi.com - ..localmachine - localhost - - -pushp.svc.ms - .b-msedge.net - .bing.com - .hotmail.com - .live.com - .live.net - .s-microsoft.com - .microsoft.com - .microsoftonline.com - .microsoftstore.com - .ms-acdc.office.com - .msedge.net - .msn.com - .msocdn.com - .skype.com - .skype.net - .windows.com - .windows.net.nsatc.net - .windowsupdate.com - .xboxlive.com - login.windows.net - C:\ProgramData\Microsoft\Windows Defender\Platform\ - - .activedirectory.windowsazure.com - .aria.microsoft.com - .msauth.net - .msftauth.net - .office.net - .opinsights.azure.com - .res.office365.com - acdc-direct.office.com - atm-fp-direct.office.com - loki.delve.office.com - management.azure.com - messaging.office.com - outlook.office365.com - portal.azure.com - protection.outlook.com - substrate.office.com - .measure.office.com - - .adobe.com - .adobe.io - .mozaws.net - .mozilla.com - .mozilla.net - .mozilla.org - .spotify.com - .spotify.map.fastly.net - .wbx2.com - .webex.com - clients1.google.com - clients2.google.com - clients3.google.com - clients4.google.com - clients5.google.com - clients6.google.com - safebrowsing.googleapis.com - - .akadns.net - .netflix.com - aspnetcdn.com - ajax.googleapis.com - cdnjs.cloudflare.com - fonts.googleapis.com - .typekit.net - cdnjs.cloudflare.com - .stackassets.com - .steamcontent.com - play.google.com - content-autofill.googleapis.com - - .disqus.com - .fontawesome.com - disqus.com - - .1rx.io - .2mdn.net - .3lift.com - .adadvisor.net - .adap.tv - .addthis.com - .adform.net - .adnxs.com - .adroll.com - .adrta.com - .adsafeprotected.com - .adsrvr.org - .adsymptotic.com - .advertising.com - .agkn.com - .amazon-adsystem.com - .amazon-adsystem.com - .analytics.yahoo.com - .aol.com - .betrad.com - .bidswitch.net - .casalemedia.com - .chartbeat.net - .cnn.com - .convertro.com - .criteo.com - .criteo.net - .crwdcntrl.net - .demdex.net - .domdex.com - .dotomi.com - .doubleclick.net - .doubleverify.com - .emxdgt.com - .everesttech.net - .exelator.com - .google-analytics.com - .googleadservices.com - .googlesyndication.com - .googletagmanager.com - .googlevideo.com - .gstatic.com - .gvt1.com - .gvt2.com - .ib-ibi.com - .jivox.com - .krxd.net - .lijit.com - .mathtag.com - .moatads.com - .moatpixel.com - .mookie1.com - .myvisualiq.net - .netmng.com - .nexac.com - .openx.net - .optimizely.com - .outbrain.com - .pardot.com - .phx.gbl - .pinterest.com - .pubmatic.com - .quantcount.com - .quantserve.com - .revsci.net - .rfihub.net - .rlcdn.com - .rubiconproject.com - .scdn.co - .scorecardresearch.com - .serving-sys.com - .sharethrough.com - .simpli.fi - .sitescout.com - .smartadserver.com - .snapads.com - .spotxchange.com - .taboola.com - .taboola.map.fastly.net - .tapad.com - .tidaltv.com - .trafficmanager.net - .tremorhub.com - .tribalfusion.com - .turn.com - .twimg.com - .tynt.com - .w55c.net - .ytimg.com - .zorosrv.com - 1rx.io - adservice.google.com - ampcid.google.com - clientservices.googleapis.com - googleadapis.l.google.com - imasdk.googleapis.com - l.google.com - ml314.com - mtalk.google.com - update.googleapis.com - www.googletagservices.com - - .pscp.tv - - .amazontrust.com - .digicert.com - .globalsign.com - .globalsign.net - .intel.com - .symcb.com - .symcd.com - .thawte.com - .usertrust.com - .verisign.com - ocsp.identrust.com - pki.goog - msocsp.com - ocsp.comodoca.com - ocsp.entrust.net - ocsp.godaddy.com - ocsp.int-x3.letsencrypt.org - ocsp.msocsp.com - pki.goog - ocsp.godaddy.com - amazontrust.com - ocsp.sectigo.com - pki-goog.l.google.com - .usertrust.com - ocsp.comodoca.com - ocsp.verisign.com - ocsp.entrust.net - ocsp.identrust.com - status.rapidssl.com - status.thawte.com - ocsp.int-x3.letsencrypt.org - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ansible/extensions/elk/ansible/roles/logs_windows/files/uninstall-service-winlogbeat.ps1 b/ansible/extensions/elk/ansible/roles/logs_windows/files/uninstall-service-winlogbeat.ps1 deleted file mode 100644 index c89a26e6..00000000 --- a/ansible/extensions/elk/ansible/roles/logs_windows/files/uninstall-service-winlogbeat.ps1 +++ /dev/null @@ -1,7 +0,0 @@ -# Delete and stop the service if it already exists. -if (Get-Service winlogbeat -ErrorAction SilentlyContinue) { - $service = Get-WmiObject -Class Win32_Service -Filter "name='winlogbeat'" - $service.StopService() - Start-Sleep -s 1 - $service.delete() -} diff --git a/ansible/extensions/elk/ansible/roles/logs_windows/handlers/main.yml b/ansible/extensions/elk/ansible/roles/logs_windows/handlers/main.yml deleted file mode 100644 index ba425ccc..00000000 --- a/ansible/extensions/elk/ansible/roles/logs_windows/handlers/main.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -- name: Restart-winlogbeat - ansible.windows.win_shell: Restart-Service winlogbeat diff --git a/ansible/extensions/elk/ansible/roles/logs_windows/tasks/main.yml b/ansible/extensions/elk/ansible/roles/logs_windows/tasks/main.yml deleted file mode 100644 index e07ae98a..00000000 --- a/ansible/extensions/elk/ansible/roles/logs_windows/tasks/main.yml +++ /dev/null @@ -1,72 +0,0 @@ ---- -- name: Install winlogbeat - ansible.builtin.import_tasks: winlogbeat.yml - -- name: Set winlogbeat config file - ansible.windows.win_copy: - src: winlogbeat.yml - dest: C:\ProgramData\chocolatey\lib\winlogbeat\tools\ - -# sysmon install (https://github.com/NVISOsecurity/ansible-sysmon) -- name: Create directory - ansible.windows.win_file: - path: "{{ sysmon_install_location }}" - state: directory - register: result - -- name: Get sysmon zip - ansible.windows.win_copy: - src: "{{ sysmon_download_file }}{{ file_ext }}" - dest: "{{ sysmon_install_location }}/{{ sysmon_download_file }}{{ file_ext }}" - -- name: Unzip sysmon - community.windows.win_unzip: - src: "{{ sysmon_install_location }}/{{ sysmon_download_file }}{{ file_ext }}" - dest: "{{ sysmon_install_location }}" - -- name: Copy sysmon config - ansible.windows.win_copy: - src: sysmonconfig-export.xml - dest: c:\sysmon\sysmonconfig-export.xml - -# RUN sysmon -- name: Check sysmon service - ansible.windows.win_service: - name: sysmon64 - register: result - failed_when: result is not defined - ignore_errors: true - -- name: Run sysmon - ansible.windows.win_command: "{{ sysmon_install_location }}\\sysmon64.exe -accepteula -i {{ sysmon_install_location }}\\sysmonconfig-export.xml" - args: - chdir: "{{ sysmon_install_location }}" - when: result.state is not defined or result.name is not defined - -# RUN winlogbeat -- name: Check winlogbeat service - ansible.windows.win_service: - name: winlogbeat - register: resultwlb - failed_when: resultwlb is not defined - ignore_errors: true - -- name: Reboot before launch setup - ansible.windows.win_reboot: - reboot_timeout: 600 - post_reboot_delay: 100 - when: resultwlb.state is defined and resultwlb.state != 'running' - -- name: Run winlogbeat setup - ansible.windows.win_command: "winlogbeat setup -e" - args: - chdir: "{{ winlogbeat_service.install_path_64 }}\\winlogbeat-{{ winlogbeat_service.version }}-windows-x86_64\\" - when: resultwlb.state is defined and resultwlb.state != 'running' - -# RUN winlogbeat -- name: Check winlogbeat service - ansible.windows.win_service: - name: winlogbeat - start_mode: auto - state: started - when: resultwlb.state is defined and resultwlb.state != 'running' diff --git a/ansible/extensions/elk/ansible/roles/logs_windows/tasks/winlogbeat.yml b/ansible/extensions/elk/ansible/roles/logs_windows/tasks/winlogbeat.yml deleted file mode 100644 index 65844613..00000000 --- a/ansible/extensions/elk/ansible/roles/logs_windows/tasks/winlogbeat.yml +++ /dev/null @@ -1,67 +0,0 @@ ---- -# from https://github.com/j91321/ansible-role-winlogbeat -- name: Create 64-bit install directory - ansible.windows.win_file: - path: "{{ winlogbeat_service.install_path_64 }}" - state: directory - -- name: Check if winlogbeat service is installed - ansible.windows.win_service: - name: winlogbeat - register: winlogbeat_installed - -- name: Check if winlogbeat is using current version - ansible.windows.win_stat: - path: "{{ winlogbeat_service.install_path_64 }}\\winlogbeat-{{ winlogbeat_service.version }}-windows-x86_64" - register: winlogbeat_folder - -- name: Copy winlogbeat uninstall script - ansible.windows.win_copy: - src: files/uninstall-service-winlogbeat.ps1 - dest: "{{ winlogbeat_service.install_path_64 }}\\uninstall-service-winlogbeat.ps1" - force: yes - when: winlogbeat_installed.exists and not winlogbeat_folder.stat.exists - -- name: Uninstall winlogbeat - ansible.windows.win_shell: .\uninstall-service-winlogbeat.ps1 - args: - chdir: "{{ winlogbeat_service.install_path_64 }}" - when: winlogbeat_installed.exists and not winlogbeat_folder.stat.exists - -- name: Download winlogbeat - ansible.windows.win_get_url: - url: "https://artifacts.elastic.co/downloads/beats/winlogbeat/winlogbeat-{{ winlogbeat_service.version }}-windows-x86_64.zip" - dest: "{{ winlogbeat_service.install_path_64 }}\\winlogbeat.zip" - when: winlogbeat_service.download and not winlogbeat_folder.stat.exists - -- name: Copy winlogbeat - ansible.windows.win_copy: - src: "files/winlogbeat-{{ winlogbeat_service.version }}-windows-x86_64.zip" - dest: "{{ winlogbeat_service.install_path_64 }}\\winlogbeat.zip" - when: not winlogbeat_service.download and not winlogbeat_folder.stat.exists - -- name: Unzip winlogbeat - community.windows.win_unzip: - src: "{{ winlogbeat_service.install_path_64 }}\\winlogbeat.zip" - dest: "{{ winlogbeat_service.install_path_64 }}\\" - delete_archive: yes - when: not winlogbeat_folder.stat.exists - -- name: Configure winlogbeat - ansible.windows.win_template: - src: winlogbeat.yml.j2 - dest: "{{ winlogbeat_service.install_path_64 }}\\winlogbeat-{{ winlogbeat_service.version }}-windows-x86_64\\winlogbeat.yml" - notify: restart-winlogbeat - -- name: Install winlogbeat - ansible.windows.win_shell: .\install-service-winlogbeat.ps1 - args: - chdir: "{{ winlogbeat_service.install_path_64 }}\\winlogbeat-{{ winlogbeat_service.version }}-windows-x86_64\\" - when: not winlogbeat_folder.stat.exists - notify: restart-winlogbeat - -- name: Remove other winlogbeat installations - ansible.windows.win_shell: | - $version="{{ winlogbeat_service.version }}" - Get-ChildItem -Path "{{ winlogbeat_service.install_path_64 }}" | Where-Object {$_.Name -CNotMatch $version} | Remove-Item -Recurse - when: not winlogbeat_folder.stat.exists diff --git a/ansible/extensions/elk/ansible/roles/logs_windows/templates/winlogbeat.yml.j2 b/ansible/extensions/elk/ansible/roles/logs_windows/templates/winlogbeat.yml.j2 deleted file mode 100644 index bd01487b..00000000 --- a/ansible/extensions/elk/ansible/roles/logs_windows/templates/winlogbeat.yml.j2 +++ /dev/null @@ -1,245 +0,0 @@ -###################### Winlogbeat Configuration Example ######################## - -# This file is an example configuration file highlighting only the most common -# options. The winlogbeat.reference.yml file from the same directory contains -# all the supported options with more comments. You can use it as a reference. -# -# You can find the full configuration reference here: -# https://www.elastic.co/guide/en/beats/winlogbeat/index.html - -# ======================== Winlogbeat specific options ========================= - -# event_logs specifies a list of event logs to monitor as well as any -# accompanying options. The YAML data type of event_logs is a list of -# dictionaries. -# -# The supported keys are name (required), tags, fields, fields_under_root, -# forwarded, ignore_older, level, event_id, provider, and include_xml. Please -# visit the documentation for the complete details of each option. -# https://go.es.io/WinlogbeatConfig - -winlogbeat.event_logs: - - name: Application - ignore_older: 30m - - - name: System - - - name: Security - processors: - - script: - lang: javascript - id: security - file: ${path.home}/module/security/config/winlogbeat-security.js - - - name: Microsoft-Windows-Sysmon/Operational - processors: - - script: - lang: javascript - id: sysmon - file: ${path.home}/module/sysmon/config/winlogbeat-sysmon.js - - - name: Windows PowerShell - event_id: 400, 403, 600, 800 - processors: - - script: - lang: javascript - id: powershell - file: ${path.home}/module/powershell/config/winlogbeat-powershell.js - - - name: Microsoft-Windows-PowerShell/Operational - event_id: 4103, 4104, 4105, 4106 - processors: - - script: - lang: javascript - id: powershell - file: ${path.home}/module/powershell/config/winlogbeat-powershell.js - - - name: ForwardedEvents - tags: [forwarded] - processors: - - script: - when.equals.winlog.channel: Security - lang: javascript - id: security - file: ${path.home}/module/security/config/winlogbeat-security.js - - script: - when.equals.winlog.channel: Microsoft-Windows-Sysmon/Operational - lang: javascript - id: sysmon - file: ${path.home}/module/sysmon/config/winlogbeat-sysmon.js - - script: - when.equals.winlog.channel: Windows PowerShell - lang: javascript - id: powershell - file: ${path.home}/module/powershell/config/winlogbeat-powershell.js - - script: - when.equals.winlog.channel: Microsoft-Windows-PowerShell/Operational - lang: javascript - id: powershell - file: ${path.home}/module/powershell/config/winlogbeat-powershell.js - - name: Microsoft-Windows-WMI-Activity/Operational - event_id: 5857,5858,5859,5860,5861 -# ====================== Elasticsearch template settings ======================= - -setup.template.settings: - index.number_of_shards: 1 - #index.codec: best_compression - #_source.enabled: false - - -# ================================== General =================================== - -# The name of the shipper that publishes the network data. It can be used to group -# all the transactions sent by a single shipper in the web interface. -#name: - -# The tags of the shipper are included in their own field with each -# transaction published. -#tags: ["service-X", "web-tier"] - -# Optional fields that you can specify to add additional information to the -# output. -#fields: -# env: staging - -# ================================= Dashboards ================================= -# These settings control loading the sample dashboards to the Kibana index. Loading -# the dashboards is disabled by default and can be enabled either by setting the -# options here or by using the `setup` command. -#setup.dashboards.enabled: false - -# The URL from where to download the dashboards archive. By default this URL -# has a value which is computed based on the Beat name and version. For released -# versions, this URL points to the dashboard archive on the artifacts.elastic.co -# website. -#setup.dashboards.url: - -# =================================== Kibana =================================== - -# Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. -# This requires a Kibana endpoint configuration. -setup.kibana: - host: "{{ hostvars['elk'].ansible_host }}" - # Kibana Host - # Scheme and port can be left out and will be set to the default (http and 5601) - # In case you specify and additional path, the scheme is required: http://localhost:5601/path - # IPv6 addresses should always be defined as: https://[2001:db8::1]:5601 - #host: "localhost:5601" - - # Kibana Space ID - # ID of the Kibana Space into which the dashboards should be loaded. By default, - # the Default Space will be used. - #space.id: - -# =============================== Elastic Cloud ================================ - -# These settings simplify using Winlogbeat with the Elastic Cloud (https://cloud.elastic.co/). - -# The cloud.id setting overwrites the `output.elasticsearch.hosts` and -# `setup.kibana.host` options. -# You can find the `cloud.id` in the Elastic Cloud web UI. -#cloud.id: - -# The cloud.auth setting overwrites the `output.elasticsearch.username` and -# `output.elasticsearch.password` settings. The format is `:`. -#cloud.auth: - -# ================================== Outputs =================================== - -# Configure what output to use when sending the data collected by the beat. - -# ---------------------------- Elasticsearch Output ---------------------------- -output.elasticsearch: - # Array of hosts to connect to. - hosts: ["{{ hostvars['elk'].ansible_host }}:9200"] - username: "elastic" - password: "changeme" - - # Protocol - either `http` (default) or `https`. - #protocol: "https" - - # Authentication credentials - either API key or username/password. - #api_key: "id:api_key" - #username: "elastic" - #password: "changeme" - -# ------------------------------ Logstash Output ------------------------------- -#output.logstash: - # The Logstash hosts - #hosts: ["localhost:5044"] - - # Optional SSL. By default is off. - # List of root certificates for HTTPS server verifications - #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] - - # Certificate for SSL client authentication - #ssl.certificate: "/etc/pki/client/cert.pem" - - # Client Certificate Key - #ssl.key: "/etc/pki/client/cert.key" - -# ================================= Processors ================================= -processors: - - add_host_metadata: - when.not.contains.tags: forwarded - - add_cloud_metadata: ~ - -# ================================== Logging =================================== - -# Sets log level. The default log level is info. -# Available log levels are: error, warning, info, debug -#logging.level: debug - -# At debug level, you can selectively enable logging only for some components. -# To enable all selectors use ["*"]. Examples of other selectors are "beat", -# "publisher", "service". -#logging.selectors: ["*"] - -# ============================= X-Pack Monitoring ============================== -# Winlogbeat can export internal metrics to a central Elasticsearch monitoring -# cluster. This requires xpack monitoring to be enabled in Elasticsearch. The -# reporting is disabled by default. - -# Set to true to enable the monitoring reporter. -#monitoring.enabled: false - -# Sets the UUID of the Elasticsearch cluster under which monitoring data for this -# Winlogbeat instance will appear in the Stack Monitoring UI. If output.elasticsearch -# is enabled, the UUID is derived from the Elasticsearch cluster referenced by output.elasticsearch. -#monitoring.cluster_uuid: - -# Uncomment to send the metrics to Elasticsearch. Most settings from the -# Elasticsearch output are accepted here as well. -# Note that the settings should point to your Elasticsearch *monitoring* cluster. -# Any setting that is not set is automatically inherited from the Elasticsearch -# output configuration, so if you have the Elasticsearch output configured such -# that it is pointing to your Elasticsearch monitoring cluster, you can simply -# uncomment the following line. -#monitoring.elasticsearch: - -# ============================== Instrumentation =============================== - -# Instrumentation support for the winlogbeat. -#instrumentation: - # Set to true to enable instrumentation of winlogbeat. - #enabled: false - - # Environment in which winlogbeat is running on (eg: staging, production, etc.) - #environment: "" - - # APM Server hosts to report instrumentation results to. - #hosts: - # - http://localhost:8200 - - # API Key for the APM Server(s). - # If api_key is set then secret_token will be ignored. - #api_key: - - # Secret token for the APM Server(s). - #secret_token: - - -# ================================= Migration ================================== - -# This allows to enable 6.7 migration aliases -#migration.6_to_7.enabled: true diff --git a/ansible/extensions/elk/extension.json b/ansible/extensions/elk/extension.json deleted file mode 100644 index 57db01a9..00000000 --- a/ansible/extensions/elk/extension.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "elk", - "description": "Add an ELK to the current lab", - "machines": [ - "elk" - ], - "compatibility": [ - "*" - ], - "impact": "add a linux machine and add a logbeat agent on all windows machine" -} diff --git a/ansible/extensions/exchange/README.md b/ansible/extensions/exchange/README.md deleted file mode 100644 index 409663a6..00000000 --- a/ansible/extensions/exchange/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# exchange Extension - -- name: exchange - - -role used : https://github.com/aleemladha/ludus_exchange diff --git a/ansible/extensions/exchange/ansible/ansible.cfg b/ansible/extensions/exchange/ansible/ansible.cfg deleted file mode 100644 index 1e672cea..00000000 --- a/ansible/extensions/exchange/ansible/ansible.cfg +++ /dev/null @@ -1,13 +0,0 @@ -[defaults] -host_key_checking = false -display_skipped_hosts = false -show_per_host_start = True -deprecation_warning = false -;stdout_callback = yaml -# Logging configuration -log_path = ~/.ansible/logs/dreadgoad_exchange.log -no_log = False -callback_whitelist = profile_tasks - -; add default roles folder into roles_path -roles_path = ./roles:../../../ansible/roles diff --git a/ansible/extensions/exchange/ansible/iso/.gitkeep b/ansible/extensions/exchange/ansible/iso/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/ansible/extensions/exchange/ansible/roles/ludus_exchange/README.md b/ansible/extensions/exchange/ansible/roles/ludus_exchange/README.md deleted file mode 100644 index 4d551c03..00000000 --- a/ansible/extensions/exchange/ansible/roles/ludus_exchange/README.md +++ /dev/null @@ -1,83 +0,0 @@ -# Ansible Role: Exchange 2019 (Ludus) - -An Ansible Role that installs [Microsoft Exchange Server 2019](https://learn.microsoft.com/en-us/exchange/exchange-server?view=exchserver-2019). - -- Turns the VM into Microsoft Exchange Server -- Users are can Test various CVEs including ProxyShell and ProxyLogon in a safe environment - -## Requirements - -None. - -## Ludus install the exchange ansible role - -```bash -# Add the role to your ludus host -ludus ansible roles add aleemladha.ludus_exchange - -``` - - -## Role Variables - -Available variables are listed below, along with default values (see `defaults/main.yml`): - -```yaml -# This pulls the netbios_name out of the domain assigned to this machine in the ludus range config -ludus_exchange_domain: "{{ (ludus | selectattr('vm_name', 'match', inventory_hostname))[0].domain.fqdn.split('.')[0] }}" -# This pulls the vm_name of the primary-dc for the domain assigned to this machine in the ludus range config -ludus_exchange_dc: "{{ (ludus | selectattr('domain', 'defined') | selectattr('domain.fqdn', 'match', ludus_exchange_domain) | selectattr('domain.role', 'match', 'primary-dc'))[0].hostname }}" -# This pulls the hostname from the ludus config for this host -ludus_exchange_host: "{{ (ludus | selectattr('vm_name', 'match', inventory_hostname))[0].hostname }}" -ludus_exchange_domain_username: "{{ ludus_exchange_domain }}\\{{ defaults.ad_domain_admin }}" -ludus_exchange_domain_password: "{{ defaults.ad_domain_admin_password }}" -``` - -## Dependencies - -None. - -## Example Ludus config.yml file to deploy the range for various Exchange Attacks - - -```yaml -ludus: - - vm_name: "{{ range_id }}-EXC-DC01" - hostname: "{{ range_id }}-DC01" - template: win2019-server-x64-template - vlan: 20 - ip_last_octet: 2 - ram_gb: 8 - cpus: 4 - windows: - sysprep: true - domain: - fqdn: ludus.domain - role: primary-dc - roles: - - aleemladha.ludus_exchange -``` - -## Ludus setup - -```bash - -# Get your config into a file so you can assign to a VM -ludus range config get > config.yml - -# Edit config to add the role to the VMs you wish to make an wazuh siem server -ludus range config set -f config.yml - -# Deploy the range and access the kali machine to start attacking -ludus range deploy - - -``` - -## License - -GPLv3 - -## Author Information - -This role was created in 2024 by [Aleem ladha](https://twitter.com/LadhaAleem). diff --git a/ansible/extensions/exchange/extension.json b/ansible/extensions/exchange/extension.json deleted file mode 100644 index 9c355671..00000000 --- a/ansible/extensions/exchange/extension.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "exchange", - "description": "Add an exchange to goad lab", - "machines": [ - "srv01 (the-eyrie.sevenkingdoms.local)" - ], - "compatibility": [ - "GOAD", - "GOAD-Light", - "GOAD-Mini" - ], - "impact": "Modify the ad schema and add a computer (warning the exchange machine is really heavy)" -} diff --git a/ansible/extensions/wazuh/README.md b/ansible/extensions/wazuh/README.md deleted file mode 100644 index a085ce80..00000000 --- a/ansible/extensions/wazuh/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# WAZUH extension - -- Extension Name: wazuh -- Description: Add wazuh free EDR server and agent on all the domain computers + soc fortress rules (https://github.com/socfortress/Wazuh-Rules) -- Machine name : {{lab_name}}-WAZUH -- Compatible with labs : * - -## prerequisites - -On ludus prepare template : - -```bash -ludus templates add -d ubuntu-22.04-x64-server -ludus templates build -``` - -## Install - -```bash -instance_id> install_extension wazuh -``` - - -## credits - -- https://github.com/aleemladha (https://github.com/Orange-Cyberdefense/GOAD/pull/215) diff --git a/ansible/extensions/wazuh/ansible/install.yml b/ansible/extensions/wazuh/ansible/install.yml deleted file mode 100644 index 439e1032..00000000 --- a/ansible/extensions/wazuh/ansible/install.yml +++ /dev/null @@ -1,14 +0,0 @@ -#Aleem Ladha @LadhaAleem -#Credits to SOCFortress and Mayfly277 -- name: Install and configure Wazuh Manager - hosts: wazuh_server - become: yes - roles: - - { role: 'wazuh_manager', tags: 'wazuh_manager' } - -- name: Install Wazuh Agent - hosts: wazuh_agents - roles: - - { role: 'wazuh_agent', tags: 'wazuh_agent' } - vars: - wazuh_manager_host: "{{ hostvars['wazuh']['ansible_host'] }}" diff --git a/ansible/extensions/wazuh/extension.json b/ansible/extensions/wazuh/extension.json deleted file mode 100644 index 1acd8e54..00000000 --- a/ansible/extensions/wazuh/extension.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "wazuh", - "description": "Add the wazuh EDR into the lab", - "machines": [ - "wazuh" - ], - "compatibility": [ - "*" - ], - "impact": "add a wazuh machine and a wazuh agent on all windows machine" -} diff --git a/ansible/extensions/ws01/README.md b/ansible/extensions/ws01/README.md deleted file mode 100644 index d5eff1c9..00000000 --- a/ansible/extensions/ws01/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# WS01 extension (Workstation 01) - -- Extension Name: ws01 -- Description: Add a Windows 10 workstation to the lab GOAD or GOAD Light in the domain sevenkingdoms.local -- Machine name : {{lab_name}}-WS01 -- Compatible with labs : - - GOAD - - GOAD-Light - -- Lab infos: - - hostname: casterlyrock - - Users: - - Administrators : - - tywin.lannister - - jaime.lannister - - RDP Users: - - Lannister group - -- Features : - - run_as_ppl - - powershell restricted - - asr rules : - - block lsass stealing - - block PSExec and WMI - -- Providers: - - aws doesn't provide windows10 ami. you can still install ws01 but a windows server 2019 will be used instead - -## prerequisites - -On ludus prepare template : - -```bash -ludus templates add -d win10-21h1-x64-enterprise -ludus templates build -``` - -## Install - -```bash -instance_id> install_extension ws01 -``` - -## Uninstall - -- Not implemented yet - -## credits - -- asr rules implementation : https://github.com/zuesdevil (https://github.com/Orange-Cyberdefense/GOAD/pull/172) diff --git a/ansible/extensions/ws01/ansible/ansible.cfg b/ansible/extensions/ws01/ansible/ansible.cfg deleted file mode 100644 index 5092728d..00000000 --- a/ansible/extensions/ws01/ansible/ansible.cfg +++ /dev/null @@ -1,13 +0,0 @@ -[defaults] -host_key_checking = false -display_skipped_hosts = false -show_per_host_start = True -deprecation_warning = false -;stdout_callback = yaml -# Logging configuration -log_path = ~/.ansible/logs/dreadgoad_ws01.log -no_log = False -callback_whitelist = profile_tasks - -; add default roles folder into roles_path -roles_path = ./roles:../../../ansible/roles diff --git a/ansible/extensions/ws01/ansible/install.yml b/ansible/extensions/ws01/ansible/install.yml deleted file mode 100644 index 71c50dde..00000000 --- a/ansible/extensions/ws01/ansible/install.yml +++ /dev/null @@ -1,57 +0,0 @@ ---- -# read global configuration file and set up adapters -- ansible.builtin.import_playbook: "../../../ansible/playbooks/data.yml" - tags: 'data' - -- name: Read local config file - hosts: domain:extensions - connection: local - vars_files: - - "../data/config.json" - tasks: - - name: merge lab variable with local config - set_fact: - lab: "{{ lab|combine(lab_extension, recursive=True) }}" - -- name: Set execution policy unrestricted (in cas of re-run) - hosts: ws01 - tasks: - - name: Change execution policy before running - win_shell: | - Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Force - -- name: Prepare workstation - hosts: ws01 - roles: - # build.yml - - { role: 'common', tags: 'common', http_proxy: "{{enable_http_proxy}}" } - - { role: 'settings/keyboard', tags: 'keyboard', layouts: "{{keyboard_layouts}}" } - # ad-servers.yml - - { role: 'settings/admin_password', tags: 'admin_password' } - - { role: 'settings/hostname', tags: 'hostname' } - # ad-members.yml : enroll ws01 - - { role: 'commonwkstn', tags: 'workstation' } - # ad-relations.yml : domain group and users local permissions - - { role: "settings/adjust_rights", tags: 'adjust_rights' } - - { role: "settings/user_rights", tags: 'adjust_rights' } - vars: - local_admin_password: "{{lab.hosts[dict_key].local_admin_password}}" - hostname: "{{lab.hosts[dict_key].hostname}}" - member_domain: "{{lab.hosts[dict_key].domain}}" - domain_username: "{{member_domain}}\\{{admin_user}}" - domain_password: "{{lab.domains[member_domain].domain_password}}" - local_groups: "{{lab.hosts[dict_key].local_groups | default({}) }}" - -- name: Setup security with tasks - hosts: ws01 - tasks: - - include_role: - name: "security/{{secu}}" - vars: - security_vars: "{{ lab.hosts[dict_key].security_vars[secu] | default({}) }}" - domain: "{{lab.hosts[dict_key].domain}}" - domain_username: "{{member_domain}}\\{{admin_user}}" - domain_password: "{{lab.domains[member_domain].domain_password}}" - loop: "{{lab.hosts[dict_key].security | default([]) }}" - loop_control: - loop_var: secu diff --git a/ansible/extensions/ws01/extension.json b/ansible/extensions/ws01/extension.json deleted file mode 100644 index 148969f2..00000000 --- a/ansible/extensions/ws01/extension.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "ws01", - "description": "Add an hardened workstation into the lab", - "machines": [ - "ws01 (casterlyrock.sevenkingdoms.local)" - ], - "compatibility": [ - "GOAD", - "GOAD-Light", - "GOAD-Mini" - ], - "impact": "aws doesn't provide windows10 ami. you can still install ws01 with aws but a windows server 2019 will be used instead" -} diff --git a/ansible/playbooks/ad-acl.yml b/ansible/playbooks/ad-acl.yml index a82ca555..dc04a10b 100644 --- a/ansible/playbooks/ad-acl.yml +++ b/ansible/playbooks/ad-acl.yml @@ -1,8 +1,4 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: data # set AD data ================================================================================================== diff --git a/ansible/playbooks/ad-child_domain.yml b/ansible/playbooks/ad-child_domain.yml index 33b6c217..43aa7dae 100644 --- a/ansible/playbooks/ad-child_domain.yml +++ b/ansible/playbooks/ad-child_domain.yml @@ -1,8 +1,4 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: data # set AD data ================================================================================================== # child diff --git a/ansible/playbooks/ad-data.yml b/ansible/playbooks/ad-data.yml index d9e48c26..49466db9 100644 --- a/ansible/playbooks/ad-data.yml +++ b/ansible/playbooks/ad-data.yml @@ -1,8 +1,4 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: data # set AD data ================================================================================================== - name: DCs AD data configuration diff --git a/ansible/playbooks/ad-gmsa.yml b/ansible/playbooks/ad-gmsa.yml index 816d844f..d826d501 100644 --- a/ansible/playbooks/ad-gmsa.yml +++ b/ansible/playbooks/ad-gmsa.yml @@ -1,8 +1,4 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: data # set AD data ================================================================================================== diff --git a/ansible/playbooks/ad-members.yml b/ansible/playbooks/ad-members.yml index 1bc66c16..a73b22ad 100644 --- a/ansible/playbooks/ad-members.yml +++ b/ansible/playbooks/ad-members.yml @@ -1,8 +1,4 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: data # set AD data ================================================================================================== - name: Servers AD configuration diff --git a/ansible/playbooks/ad-parent_domain.yml b/ansible/playbooks/ad-parent_domain.yml index 134a09b6..653649cb 100644 --- a/ansible/playbooks/ad-parent_domain.yml +++ b/ansible/playbooks/ad-parent_domain.yml @@ -1,8 +1,4 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: data # set AD data ================================================================================================== diff --git a/ansible/playbooks/ad-relations.yml b/ansible/playbooks/ad-relations.yml index 38decfa0..41bb586a 100644 --- a/ansible/playbooks/ad-relations.yml +++ b/ansible/playbooks/ad-relations.yml @@ -1,8 +1,4 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: data # set AD data ================================================================================================== diff --git a/ansible/playbooks/ad-servers.yml b/ansible/playbooks/ad-servers.yml index 368b91ea..08b736a2 100644 --- a/ansible/playbooks/ad-servers.yml +++ b/ansible/playbooks/ad-servers.yml @@ -1,8 +1,4 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: data # set AD data ================================================================================================== diff --git a/ansible/playbooks/ad-trusts.yml b/ansible/playbooks/ad-trusts.yml index e5f4f3ef..5cbf0e6e 100644 --- a/ansible/playbooks/ad-trusts.yml +++ b/ansible/playbooks/ad-trusts.yml @@ -1,8 +1,4 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: data # set AD trusts ================================================================================================== - name: Trusts configuration prepare diff --git a/ansible/playbooks/adcs.yml b/ansible/playbooks/adcs.yml index da682ad7..2ed028cf 100644 --- a/ansible/playbooks/adcs.yml +++ b/ansible/playbooks/adcs.yml @@ -1,8 +1,5 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: data + # set AD data ================================================================================================== - name: ADCS diff --git a/ansible/playbooks/build.yml b/ansible/playbooks/build.yml index df3f5c69..b99a87d2 100644 --- a/ansible/playbooks/build.yml +++ b/ansible/playbooks/build.yml @@ -1,8 +1,4 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: data - name: Build all hosts: domain diff --git a/ansible/playbooks/data.yml b/ansible/playbooks/data.yml deleted file mode 100644 index 51c7fd7c..00000000 --- a/ansible/playbooks/data.yml +++ /dev/null @@ -1,254 +0,0 @@ ---- -- name: Read data files - hosts: domain:extensions - gather_facts: true - serial: 5 - tasks: - - name: Load JSON config as a variable - ansible.builtin.include_vars: - file: "{{ data_path }}/{{ env }}-config.json" - run_once: true - - - name: Save the JSON data to a variable as a fact - ansible.builtin.set_fact: - lab: "{{ lab }}" - cacheable: true - run_once: true - - - name: Load AWS instance_to_ip mapping from file for SSM connections - ansible.builtin.include_vars: - file: "/tmp/aws_instance_mapping_{{ env }}.json" - name: aws_mapping - run_once: true - when: ansible_connection is search('aws_ssm') - ignore_errors: true - - - name: Display AWS instance to IP mappings - ansible.builtin.debug: - msg: "Instance {{ item.key }} -> IP {{ item.value }}" - loop: "{{ aws_mapping.instance_to_ip | dict2items }}" - run_once: true - when: - - ansible_connection is search('aws_ssm') - - aws_mapping is defined - - - name: Set instance_to_ip mapping for all hosts - ansible.builtin.set_fact: - instance_to_ip: "{{ aws_mapping.instance_to_ip | default({}) }}" - cacheable: true - run_once: true - delegate_facts: true - delegate_to: "{{ item }}" - loop: "{{ groups['all'] }}" - loop_control: - label: "{{ item }} ({{ hostvars[item].ansible_host | default('no instance_id') }})" - when: - - ansible_connection is search('aws_ssm') - - aws_mapping is defined - - - name: Set host_ipv4 from AWS instance mapping for SSM connections - ansible.builtin.set_fact: - host_ipv4: "{{ instance_to_ip[ansible_host] | default('') }}" - cacheable: true - when: - - ansible_connection is search('aws_ssm') - - instance_to_ip is defined - - ansible_host in instance_to_ip - - - name: Display host IP assignment from AWS mapping - ansible.builtin.debug: - msg: "Host {{ inventory_hostname }} ({{ ansible_host }}) assigned IP: {{ host_ipv4 }}" - when: - - ansible_connection is search('aws_ssm') - - host_ipv4 is defined - - host_ipv4 != '' - - - name: Check if network facts are already cached - ansible.builtin.set_fact: - skip_ip_detection: "{{ host_ipv4 is defined and host_ipv4 != '' }}" - skip_adapter_detection: "{{ domain_adapter is defined and domain_adapter != '' }}" - - - name: Get adapter names (always needed on first run) - ansible.windows.win_powershell: - script: | - $ProgressPreference = 'SilentlyContinue' - $ErrorActionPreference = 'Stop' - - try { - $adapters = @(Get-NetAdapter -ErrorAction Stop | Where-Object {$_.Status -eq "Up"}) - if ($adapters.Count -eq 0) { - # Fallback to WMI - $wmiAdapters = @(Get-WmiObject Win32_NetworkAdapter | Where-Object {$_.NetEnabled -eq $true}) - Write-Output "DOMAIN_ADAPTER:$($wmiAdapters[0].NetConnectionID)" - if ($wmiAdapters.Count -gt 1) { - Write-Output "NAT_ADAPTER:$($wmiAdapters[1].NetConnectionID)" - } else { - Write-Output "NAT_ADAPTER:$($wmiAdapters[0].NetConnectionID)" - } - Write-Output "ADAPTER_COUNT:$($wmiAdapters.Count)" - } else { - Write-Output "DOMAIN_ADAPTER:$($adapters[0].Name)" - if ($adapters.Count -gt 1) { - Write-Output "NAT_ADAPTER:$($adapters[1].Name)" - } else { - Write-Output "NAT_ADAPTER:$($adapters[0].Name)" - } - Write-Output "ADAPTER_COUNT:$($adapters.Count)" - } - $Ansible.Changed = $false - } catch { - Write-Output "DOMAIN_ADAPTER:Ethernet" - Write-Output "NAT_ADAPTER:Ethernet" - Write-Output "ADAPTER_COUNT:1" - $Ansible.Changed = $false - } - register: adapter_info - when: not skip_adapter_detection | default(false) - - - name: Get all network information - ansible.windows.win_powershell: - script: | - $ProgressPreference = 'SilentlyContinue' - $ErrorActionPreference = 'Stop' - - # Set a timeout for the entire script - $timeout = New-TimeSpan -Seconds 30 - $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() - - try { - $primaryAdapter = $null - $adapters = @() - - try { - $adapters = @(Get-NetAdapter -ErrorAction Stop | Where-Object {$_.Status -eq "Up"}) - $primaryAdapter = $adapters | Select-Object -First 1 - } catch { - # Fallback to WMI if Get-NetAdapter fails - $adapters = @(Get-WmiObject Win32_NetworkAdapter | Where-Object {$_.NetEnabled -eq $true}) - if ($adapters.Count -gt 0) { - $primaryAdapter = @{Name = $adapters[0].NetConnectionID} - } - } - - # Get IP quickly using WMI - $primaryIP = "" - try { - $networkConfigs = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.IPEnabled -eq $true -and $_.IPAddress[0] -notmatch '^127\.' -and $_.IPAddress[0] -notmatch '^169\.254\.'} - if ($networkConfigs) { - $primaryIP = $networkConfigs[0].IPAddress[0] - } - } catch { - $primaryIP = "" - } - - Write-Output "DOMAIN_ADAPTER:$(if ($primaryAdapter) { $primaryAdapter.Name } else { 'Ethernet' })" - Write-Output "NAT_ADAPTER:$(if ($adapters.Count -gt 1) { $adapters[1].Name } else { if ($primaryAdapter) { $primaryAdapter.Name } else { 'Ethernet' } })" - Write-Output "ADAPTER_COUNT:$($adapters.Count)" - Write-Output "PRIMARY_IP:$primaryIP" - $Ansible.Changed = $false - } catch { - # Quick fallback - Write-Output "DOMAIN_ADAPTER:Ethernet" - Write-Output "NAT_ADAPTER:Ethernet" - Write-Output "ADAPTER_COUNT:1" - Write-Output "PRIMARY_IP:" - $Ansible.Changed = $false - } - async: 60 # Maximum 60 seconds - poll: 5 # Check every 5 second - register: network_info - ignore_errors: true - when: not skip_ip_detection | default(false) - - - name: Set adapter facts from adapter detection - ansible.builtin.set_fact: - domain_adapter: "{{ adapter_info.output | select('match', '^DOMAIN_ADAPTER:') | first | regex_replace('^DOMAIN_ADAPTER:', '') | default('Ethernet') }}" - nat_adapter: "{{ adapter_info.output | select('match', '^NAT_ADAPTER:') | first | regex_replace('^NAT_ADAPTER:', '') | default('Ethernet') }}" - number_of_interfaces: "{{ adapter_info.output | select('match', '^ADAPTER_COUNT:') | first | regex_replace('^ADAPTER_COUNT:', '') | default('1') }}" - two_adapters: "{{ (adapter_info.output | select('match', '^ADAPTER_COUNT:') | first | regex_replace('^ADAPTER_COUNT:', '') | default('1') | int) > 1 }}" - cacheable: true - when: - - adapter_info is defined - - adapter_info.output is defined - - - name: Set all network facts from full detection - ansible.builtin.set_fact: - domain_adapter: "{{ network_info.output | select('match', '^DOMAIN_ADAPTER:') | first | regex_replace('^DOMAIN_ADAPTER:', '') | default('Ethernet') }}" - nat_adapter: "{{ network_info.output | select('match', '^NAT_ADAPTER:') | first | regex_replace('^NAT_ADAPTER:', '') | default('Ethernet') }}" - number_of_interfaces: "{{ network_info.output | select('match', '^ADAPTER_COUNT:') | first | regex_replace('^ADAPTER_COUNT:', '') | default('1') }}" - two_adapters: "{{ (network_info.output | select('match', '^ADAPTER_COUNT:') | first | regex_replace('^ADAPTER_COUNT:', '') | default('1') | int) > 1 }}" - host_ipv4: "{{ network_info.output | select('match', '^PRIMARY_IP:') | first | regex_replace('^PRIMARY_IP:', '') | default(ansible_default_ipv4.address | default('')) }}" - cacheable: true - when: - - network_info is defined - - network_info.output is defined - - - name: Set fallback network facts - ansible.builtin.set_fact: - domain_adapter: "{{ domain_adapter | default('Ethernet') }}" - nat_adapter: "{{ nat_adapter | default(domain_adapter | default('Ethernet')) }}" - number_of_interfaces: "{{ number_of_interfaces | default('1') }}" - two_adapters: "{{ two_adapters | default(false) }}" - host_ipv4: "{{ host_ipv4 | default(ansible_default_ipv4.address | default('')) }}" - cacheable: true - when: domain_adapter is not defined - - - name: Store IP as host fact for DCs - ansible.builtin.set_fact: - dc_ipv4: "{{ host_ipv4 }}" - cacheable: true - when: - - host_ipv4 is defined - - host_ipv4 != '' - - inventory_hostname in groups['dc'] - - - name: Show host network configuration - ansible.builtin.debug: - msg: - - "hostname: {{ inventory_hostname }}" - - "instance_id: {{ ansible_host }}" - - "resolved_ip: {{ host_ipv4 | default('NOT RESOLVED') }}" - - "domain interface: {{ domain_adapter }}" - - "number_of_interfaces: {{ number_of_interfaces }}" - - "two_adapters: {{ two_adapters }}" - - "nat interface: {{ nat_adapter }}" - verbosity: 1 # Only shows with -v flag - -# Create mappings on all hosts -- name: Create global mappings - hosts: all - gather_facts: false - serial: 10 - tasks: - - name: Build all mappings and distribute to all hosts - ansible.builtin.set_fact: - dc_hostname_to_ip: >- - {%- set mapping = {} -%} - {%- for host in groups['dc'] -%} - {%- if hostvars[host]['dc_ipv4'] is defined and hostvars[host]['dc_ipv4'] != '' -%} - {%- set _ = mapping.update({host: hostvars[host]['dc_ipv4']}) -%} - {%- endif -%} - {%- endfor -%} - {{ mapping }} - instance_to_ip: >- - {%- set mapping = {} -%} - {%- for host in groups['all'] -%} - {%- if hostvars[host]['host_ipv4'] is defined and hostvars[host]['ansible_host'] is defined and hostvars[host]['host_ipv4'] != '' -%} - {%- set _ = mapping.update({hostvars[host]['ansible_host']: hostvars[host]['host_ipv4']}) -%} - {%- endif -%} - {%- endfor -%} - {{ mapping }} - cacheable: true - run_once: true - delegate_facts: true - delegate_to: "{{ item }}" - loop: "{{ groups['all'] }}" - throttle: 5 - - - name: Debug the mappings - ansible.builtin.debug: - msg: - - "DC Hostname to IP mapping: {{ dc_hostname_to_ip | default({}) }}" - - "Instance to IP mapping: {{ instance_to_ip | default({}) }}" - verbosity: 2 - run_once: true diff --git a/ansible/playbooks/dhcp.yml b/ansible/playbooks/dhcp.yml index 7d7d9074..92b1f570 100644 --- a/ansible/playbooks/dhcp.yml +++ b/ansible/playbooks/dhcp.yml @@ -1,7 +1,4 @@ --- -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: 'data' - name: Setup Prerequisites hosts: dc diff --git a/ansible/playbooks/diagnose-dc01.yml b/ansible/playbooks/diagnose-dc01.yml index 16453a92..c9a279f7 100644 --- a/ansible/playbooks/diagnose-dc01.yml +++ b/ansible/playbooks/diagnose-dc01.yml @@ -4,7 +4,7 @@ # # Usage: # # First, ensure the AWS mapping file exists: -# ansible-playbook -i dev-inventory ansible/data.yml --limit dc03 +# ansible-playbook -i dev-inventory ansible/playbooks/network_setup.yml --limit dc03 # # # Then run diagnostics: # ansible-playbook -i dev-inventory ansible/diagnose-dc01.yml --limit dc03 @@ -52,8 +52,8 @@ The instance_to_ip mapping doesn't have an entry for {{ target_dc }}. Options: - 1. Run data.yml first to populate the mapping: - ansible-playbook -i dev-inventory ansible/data.yml --limit dc03 + 1. Run network_setup.yml first to populate the mapping: + ansible-playbook -i dev-inventory ansible/playbooks/network_setup.yml --limit dc03 2. Pass the IP directly: ansible-playbook -i dev-inventory ansible/diagnose-dc01.yml --limit dc03 -e dc01_ip_override=10.x.x.x diff --git a/ansible/playbooks/elk.yml b/ansible/playbooks/ext-elk.yml similarity index 100% rename from ansible/playbooks/elk.yml rename to ansible/playbooks/ext-elk.yml diff --git a/ansible/extensions/exchange/ansible/install.yml b/ansible/playbooks/ext-exchange.yml similarity index 66% rename from ansible/extensions/exchange/ansible/install.yml rename to ansible/playbooks/ext-exchange.yml index e50e91af..15a81eeb 100644 --- a/ansible/extensions/exchange/ansible/install.yml +++ b/ansible/playbooks/ext-exchange.yml @@ -1,22 +1,20 @@ --- -# read global configuration file and set up adapters -- ansible.builtin.import_playbook: "../../../ansible/playbooks/data.yml" - tags: 'data' +# Extension: exchange — Add Exchange Server to GOAD lab -- name: Read local config file +- name: Read extension config file hosts: domain:extensions connection: local vars_files: - - "../data/config.json" + - "{{ extension_data_dir }}/config.json" tasks: - - name: merge lab variable with local config + - name: merge lab variable with extension config set_fact: lab: "{{ lab|combine(lab_extension, recursive=True) }}" - name: Update AD data hosts: dc01 roles: - - { role: 'ad', tags: 'ad_domain_data' } + - { role: 'dreadnode.goad.ad', tags: 'ad_domain_data' } vars: hostname: "{{lab.hosts[dict_key].hostname}}" domain: "{{lab.hosts[dict_key].domain}}" @@ -30,18 +28,14 @@ - name: Install new server hosts: srv01 roles: - # build.yml - - { role: 'common', tags: 'common', http_proxy: "{{enable_http_proxy}}" } - - { role: 'settings/keyboard', tags: 'keyboard', layouts: "{{keyboard_layouts}}" } - - { role: 'settings/no_updates', tags: 'no_updates' } - # ad-servers.yml - - { role: 'settings/admin_password', tags: 'admin_password' } - - { role: 'settings/hostname', tags: 'hostname' } - # ad-members.yml : enroll srv01 - - { role: 'member_server', tags: 'server' } - # ad-relations.yml : domain group and users local permissions - - { role: "settings/adjust_rights", tags: 'adjust_rights' } - - { role: "settings/user_rights", tags: 'adjust_rights' } + - { role: 'dreadnode.goad.common', tags: 'common', http_proxy: "{{enable_http_proxy}}" } + - { role: 'dreadnode.goad.settings_keyboard', tags: 'keyboard', layouts: "{{keyboard_layouts}}" } + - { role: 'dreadnode.goad.settings_no_updates', tags: 'no_updates' } + - { role: 'dreadnode.goad.settings_admin_password', tags: 'admin_password' } + - { role: 'dreadnode.goad.settings_hostname', tags: 'hostname' } + - { role: 'dreadnode.goad.member_server', tags: 'server' } + - { role: 'dreadnode.goad.settings_adjust_rights', tags: 'adjust_rights' } + - { role: 'dreadnode.goad.settings_user_rights', tags: 'adjust_rights' } vars: local_admin_password: "{{lab.hosts[dict_key].local_admin_password}}" hostname: "{{lab.hosts[dict_key].hostname}}" @@ -53,7 +47,7 @@ - name: Synchronize all domains hosts: dc roles: - - { role: 'sync_domains', tags: 'sync' } + - { role: 'dreadnode.goad.sync_domains', tags: 'sync' } vars: domain: "{{lab.hosts[dict_key].domain}}" domain_username: "{{domain}}\\{{admin_user}}" @@ -62,7 +56,7 @@ - name: Install exchange hosts: srv01 roles: - - { role: 'ludus_exchange', tags: 'exchange' } + - { role: 'dreadnode.goad.ludus_exchange', tags: 'exchange' } vars: domain: "{{lab.hosts[dict_key].domain}}" ludus_exchange_domain: "{{domain.split('.')[0]}}" @@ -79,4 +73,4 @@ - name: Add exchange mail reader bot hosts: srv01 roles: - - { role: 'exchange_bot', tags: 'exchange_bot' } + - { role: 'dreadnode.goad.exchange_bot', tags: 'exchange_bot' } diff --git a/ansible/playbooks/ext-guacamole.yml b/ansible/playbooks/ext-guacamole.yml new file mode 100644 index 00000000..c04014f6 --- /dev/null +++ b/ansible/playbooks/ext-guacamole.yml @@ -0,0 +1,65 @@ +--- +# Extension: guacamole — Apache Guacamole remote access gateway +- name: Read lab data files + hosts: guacamole + connection: local + vars_files: + - "{{ lab_data_file }}" + tasks: + - name: register lab var + set_fact: + lab: "{{lab}}" + cacheable: yes + run_once: true + +- name: Read extension config files and merge + hosts: guacamole + connection: local + tasks: + - name: Include and merge config file + vars: + ext_config: {} + ansible.builtin.include_vars: + file: "{{ item }}" + name: ext_config + loop: "{{ extension_data_files | default([]) }}" + register: included_vars + + - name: Merge all included configs into lab + set_fact: + lab: "{{ lab | combine(item.ansible_facts.ext_config.lab_extension, recursive=True) }}" + loop: "{{ included_vars.results }}" + when: item.ansible_facts.ext_config.lab_extension is defined + +- name: Install Guacamole + hosts: guacamole + become: yes + vars_files: + - "{{ guacamole_vars_file }}" + roles: + - { role: 'geerlingguy.mysql', tags: 'mysql' } + - { role: 'dreadnode.goad.linux_tomcat', tags: 'tomcat' } + - { role: 'dreadnode.goad.linux_guacamole', tags: 'guacamole' } + vars: + mysql_databases: + - name: "{{guacamole_db}}" + encoding: utf8 + collation: utf8_general_ci + mysql_users: + - name: "{{guacamole_db_username}}" + host: "%" + password: "{{guacamole_db_password}}" + priv: "{{guacamole_db}}.*:ALL" + salt: "{{99999999999999 | random | to_uuid | hash('sha256')}}" + +- name: Create guacamole connections + hosts: guacamole + become: yes + vars_files: + - "{{ guacamole_vars_file }}" + roles: + - { role: 'dreadnode.goad.linux_guacamole_create_connections', tags: 'guacamole_connections' } + vars: + guacadmin_username: "guacadmin" + hosts: "{{lab.hosts}}" + domains: "{{lab.domains}}" diff --git a/ansible/playbooks/ext-lx01.yml b/ansible/playbooks/ext-lx01.yml new file mode 100644 index 00000000..18b3f431 --- /dev/null +++ b/ansible/playbooks/ext-lx01.yml @@ -0,0 +1,39 @@ +--- +# Extension: lx01 — Enroll Linux machine to domain + +- name: Read extension config file + hosts: domain:extensions + connection: local + vars_files: + - "{{ extension_data_dir }}/config.json" + tasks: + - name: merge lab variable with extension config + set_fact: + lab: "{{ lab|combine(lab_extension, recursive=True) }}" + +- name: Enroll lx01 machine to domain + hosts: lx01 + become: yes + roles: + - { role: 'dreadnode.goad.linux_add_linux_to_domain', tags: 'linux_domain' } + vars: + hostname: "{{lab.hosts[dict_key].hostname}}" + domain: "{{lab.hosts[dict_key].domain}}" + dc_key: "{{lab.domains[domain].dc}}" + dc_name: "{{lab.hosts[dc_key].hostname}}" + dc_fqdn: "{{dc_name}}.{{domain}}" + dc_ip: "{{hostvars[dc_key].ansible_host}}" + domain_username: "{{admin_user}}" + domain_password: "{{lab.domains[domain].domain_password}}" + sudoers_group: "{{lab.hosts[dict_key].local_groups.sudoers | default([])}}" + ssh_group: "{{lab.hosts[dict_key].local_groups.ssh | default([])}}" + +- name: Setup lx01 machine dns on domain controller + hosts: dc01 + roles: + - { role: 'dreadnode.goad.add_dns_record', tags: 'add_dns_record' } + vars: + domain: "{{lab.hosts[dict_key].domain}}" + record_name: "{{lab.hosts['lx01'].hostname}}" + record_type: "A" + record_value: "{{hostvars['lx01'].ansible_host}}" diff --git a/ansible/playbooks/ext-wazuh.yml b/ansible/playbooks/ext-wazuh.yml new file mode 100644 index 00000000..baa84f07 --- /dev/null +++ b/ansible/playbooks/ext-wazuh.yml @@ -0,0 +1,23 @@ +--- +# Extension: wazuh — Install Wazuh manager and agents +#Credits to SOCFortress and Mayfly277 +- name: Install and configure Wazuh Manager + hosts: wazuh_server + become: yes + roles: + - { role: 'dreadnode.goad.wazuh_manager', tags: 'wazuh_manager' } + +- name: Install Wazuh Agent + hosts: wazuh_agents + roles: + - { role: 'dreadnode.goad.wazuh_agent', tags: 'wazuh_agent' } + vars: + wazuh_manager_host: "{{ hostvars['wazuh']['ansible_host'] }}" + +- name: Install Wazuh Agent on Linux + hosts: wazuh_agents_linux + become: yes + roles: + - { role: 'dreadnode.goad.wazuh_agent_linux', tags: 'wazuh_agent_linux' } + vars: + wazuh_manager_host: "{{ hostvars['wazuh']['ansible_host'] }}" diff --git a/ansible/playbooks/ext-ws01.yml b/ansible/playbooks/ext-ws01.yml new file mode 100644 index 00000000..df8631bd --- /dev/null +++ b/ansible/playbooks/ext-ws01.yml @@ -0,0 +1,52 @@ +--- +# Extension: ws01 — Add hardened workstation + +- name: Read extension config file + hosts: domain:extensions + connection: local + vars_files: + - "{{ extension_data_dir }}/config.json" + tasks: + - name: Merge lab variable with extension config + ansible.builtin.set_fact: + lab: "{{ lab | combine(lab_extension, recursive=True) }}" + +- name: Set execution policy unrestricted (in case of re-run) + hosts: ws01 + tasks: + - name: Change execution policy before running + ansible.windows.win_shell: | + Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Force + +- name: Prepare workstation + hosts: ws01 + roles: + - { role: 'dreadnode.goad.common', tags: 'common', http_proxy: "{{enable_http_proxy}}" } + - { role: 'dreadnode.goad.settings_keyboard', tags: 'keyboard', layouts: "{{keyboard_layouts}}" } + - { role: 'dreadnode.goad.settings_admin_password', tags: 'admin_password' } + - { role: 'dreadnode.goad.settings_hostname', tags: 'hostname' } + - { role: 'dreadnode.goad.commonwkstn', tags: 'workstation' } + - { role: 'dreadnode.goad.settings_adjust_rights', tags: 'adjust_rights' } + - { role: 'dreadnode.goad.settings_user_rights', tags: 'adjust_rights' } + vars: + local_admin_password: "{{lab.hosts[dict_key].local_admin_password}}" + hostname: "{{lab.hosts[dict_key].hostname}}" + member_domain: "{{lab.hosts[dict_key].domain}}" + domain_username: "{{member_domain}}\\{{admin_user}}" + domain_password: "{{lab.domains[member_domain].domain_password}}" + local_groups: "{{lab.hosts[dict_key].local_groups | default({}) }}" + +- name: Setup security with tasks + hosts: ws01 + tasks: + - name: Include security role + ansible.builtin.include_role: + name: "dreadnode.goad.security_{{ secu }}" + vars: + security_vars: "{{ lab.hosts[dict_key].security_vars[secu] | default({}) }}" + domain: "{{ lab.hosts[dict_key].domain }}" + domain_username: "{{ member_domain }}\\{{ admin_user }}" + domain_password: "{{ lab.domains[member_domain].domain_password }}" + loop: "{{ lab.hosts[dict_key].security | default([]) }}" + loop_control: + loop_var: secu diff --git a/ansible/extensions/exchange/data/config.json b/ansible/playbooks/files/extensions/exchange/config.json similarity index 100% rename from ansible/extensions/exchange/data/config.json rename to ansible/playbooks/files/extensions/exchange/config.json diff --git a/ansible/playbooks/files/extensions/guacamole/guacamole.yml b/ansible/playbooks/files/extensions/guacamole/guacamole.yml new file mode 100644 index 00000000..64ffb99f --- /dev/null +++ b/ansible/playbooks/files/extensions/guacamole/guacamole.yml @@ -0,0 +1,13 @@ +--- +# GUACAMOLE MYSQL +mysql_root_password: "ohmygoadchangeme" +mysql_root_password_update: true +guacamole_db: guacamole +guacamole_db_username: guacamole +guacamole_db_password: ohmygoadchangeme +# GUACAMOLE +layout: "fr-fr-azerty" +guacadmin_password: "ohmygoadchangeme" +guacamole_users: + - name: "user" + pass: "ohmygoadchangeme" diff --git a/ansible/playbooks/files/extensions/lx01/config.json b/ansible/playbooks/files/extensions/lx01/config.json new file mode 100644 index 00000000..e8989656 --- /dev/null +++ b/ansible/playbooks/files/extensions/lx01/config.json @@ -0,0 +1,20 @@ +{ + "lab_extension" : { + "hosts" : { + "lx01" : { + "hostname" : "dragonstone", + "type" : "server", + "os": "linux", + "local_admin_password": "HGLXP@ssw_rd$", + "domain" : "sevenkingdoms.local", + "path" : "DC=sevenkingdoms,DC=local", + "local_groups" : { + "sudoers" : ["Baratheon"], + "ssh" : ["Lannister", "Baratheon"] + }, + "security": [""], + "security_vars": {} + } + } + } + } diff --git a/ansible/extensions/ws01/data/config.json b/ansible/playbooks/files/extensions/ws01/config.json similarity index 100% rename from ansible/extensions/ws01/data/config.json rename to ansible/playbooks/files/extensions/ws01/config.json diff --git a/ansible/playbooks/fix_dns.yml b/ansible/playbooks/fix_dns.yml index d4f12396..d2b1c9e0 100644 --- a/ansible/playbooks/fix_dns.yml +++ b/ansible/playbooks/fix_dns.yml @@ -1,8 +1,4 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: 'data' - name: Setup dns again on all domain computers hosts: domain diff --git a/ansible/playbooks/fix_trust.yml b/ansible/playbooks/fix_trust.yml index a62c0fac..f1b59f81 100644 --- a/ansible/playbooks/fix_trust.yml +++ b/ansible/playbooks/fix_trust.yml @@ -1,6 +1,4 @@ --- -- name: Import Data - ansible.builtin.import_playbook: data.yml # use this to change machine password if the trust between the dc and the computer is broken - name: Fix trust relationship between this workstation and the primary domain failed diff --git a/ansible/playbooks/interfaces.yml b/ansible/playbooks/interfaces.yml index 355e869b..0598ccf9 100644 --- a/ansible/playbooks/interfaces.yml +++ b/ansible/playbooks/interfaces.yml @@ -1,7 +1,4 @@ --- -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: 'data' #- name: show variables # hosts: domain diff --git a/ansible/playbooks/laps.yml b/ansible/playbooks/laps.yml index 2faed72d..9870d703 100644 --- a/ansible/playbooks/laps.yml +++ b/ansible/playbooks/laps.yml @@ -1,8 +1,4 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: data - name: Configure laps on DCs hosts: laps_dc diff --git a/ansible/playbooks/localusers.yml b/ansible/playbooks/localusers.yml index ac1d9dbc..c576dd04 100644 --- a/ansible/playbooks/localusers.yml +++ b/ansible/playbooks/localusers.yml @@ -1,8 +1,4 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: 'data' # set local users ================================================================================================== - name: Local Users diff --git a/ansible/playbooks/main.yml b/ansible/playbooks/main.yml index 4a82d45f..68434397 100644 --- a/ansible/playbooks/main.yml +++ b/ansible/playbooks/main.yml @@ -1,9 +1,9 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: 'data' +# Network discovery (lab config loaded automatically by vars plugin) +- name: Import Network Setup + ansible.builtin.import_playbook: network_setup.yml + tags: 'network_setup' # Prepare servers - name: Import Build diff --git a/ansible/playbooks/network_setup.yml b/ansible/playbooks/network_setup.yml new file mode 100644 index 00000000..ade4303f --- /dev/null +++ b/ansible/playbooks/network_setup.yml @@ -0,0 +1,50 @@ +--- +# Network discovery — run once before provisioning playbooks. +# The lab config (the `lab` variable) is loaded automatically by the +# dreadnode.goad.lab_config vars plugin. This playbook only handles +# runtime network detection that requires connecting to Windows hosts. + +- name: Discover network configuration + hosts: domain:extensions + gather_facts: true + serial: 5 + roles: + - { role: 'dreadnode.goad.network_discovery', tags: 'network_discovery' } + +- name: Create global network mappings + hosts: all + gather_facts: false + serial: 10 + tasks: + - name: Build and distribute DC and instance IP mappings + ansible.builtin.set_fact: + dc_hostname_to_ip: >- + {%- set mapping = {} -%} + {%- for host in groups['dc'] -%} + {%- if hostvars[host]['dc_ipv4'] is defined and hostvars[host]['dc_ipv4'] != '' -%} + {%- set _ = mapping.update({host: hostvars[host]['dc_ipv4']}) -%} + {%- endif -%} + {%- endfor -%} + {{ mapping }} + instance_to_ip: >- + {%- set mapping = {} -%} + {%- for host in groups['all'] -%} + {%- if hostvars[host]['host_ipv4'] is defined and hostvars[host]['ansible_host'] is defined and hostvars[host]['host_ipv4'] != '' -%} + {%- set _ = mapping.update({hostvars[host]['ansible_host']: hostvars[host]['host_ipv4']}) -%} + {%- endif -%} + {%- endfor -%} + {{ mapping }} + cacheable: true + run_once: true + delegate_facts: true + delegate_to: "{{ item }}" + loop: "{{ groups['all'] }}" + throttle: 5 + + - name: Debug the mappings + ansible.builtin.debug: + msg: + - "DC Hostname to IP mapping: {{ dc_hostname_to_ip | default({}) }}" + - "Instance to IP mapping: {{ instance_to_ip | default({}) }}" + verbosity: 2 + run_once: true diff --git a/ansible/playbooks/onlyusers.yml b/ansible/playbooks/onlyusers.yml index ba091bbc..26896a81 100644 --- a/ansible/playbooks/onlyusers.yml +++ b/ansible/playbooks/onlyusers.yml @@ -1,8 +1,4 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: 'data' # set AD data ================================================================================================== - name: DCs AD data configuration diff --git a/ansible/playbooks/sccm-client.yml b/ansible/playbooks/sccm-client.yml index ce447104..44017349 100644 --- a/ansible/playbooks/sccm-client.yml +++ b/ansible/playbooks/sccm-client.yml @@ -1,7 +1,4 @@ --- -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: 'data' - name: Client install hosts: sccm diff --git a/ansible/playbooks/sccm-config.yml b/ansible/playbooks/sccm-config.yml index cd1317b5..7d35933a 100644 --- a/ansible/playbooks/sccm-config.yml +++ b/ansible/playbooks/sccm-config.yml @@ -1,7 +1,4 @@ --- -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: 'data' - name: Config SCCM hosts: sccm diff --git a/ansible/playbooks/sccm-install.yml b/ansible/playbooks/sccm-install.yml index 5d79643a..d1a35ada 100644 --- a/ansible/playbooks/sccm-install.yml +++ b/ansible/playbooks/sccm-install.yml @@ -1,7 +1,4 @@ --- -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: 'data' - name: "Setup Prerequisites" hosts: dc diff --git a/ansible/playbooks/sccm-pxe.yml b/ansible/playbooks/sccm-pxe.yml index c2fa304c..0563bfba 100644 --- a/ansible/playbooks/sccm-pxe.yml +++ b/ansible/playbooks/sccm-pxe.yml @@ -1,7 +1,4 @@ --- -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: 'data' - name: Setup SCCM PXE hosts: sccm diff --git a/ansible/playbooks/security.yml b/ansible/playbooks/security.yml index 6ef45aac..f2312805 100644 --- a/ansible/playbooks/security.yml +++ b/ansible/playbooks/security.yml @@ -1,8 +1,4 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: data - name: Setup enable defender hosts: defender_on diff --git a/ansible/playbooks/servers.yml b/ansible/playbooks/servers.yml index 75811ddd..14de882d 100644 --- a/ansible/playbooks/servers.yml +++ b/ansible/playbooks/servers.yml @@ -1,8 +1,4 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: data - name: Install IIS hosts: iis diff --git a/ansible/extensions/elk/inventory b/ansible/playbooks/templates/extensions/elk/inventory.j2 similarity index 100% rename from ansible/extensions/elk/inventory rename to ansible/playbooks/templates/extensions/elk/inventory.j2 diff --git a/ansible/extensions/exchange/inventory b/ansible/playbooks/templates/extensions/exchange/inventory.j2 similarity index 100% rename from ansible/extensions/exchange/inventory rename to ansible/playbooks/templates/extensions/exchange/inventory.j2 diff --git a/ansible/playbooks/templates/extensions/guacamole/inventory.j2 b/ansible/playbooks/templates/extensions/guacamole/inventory.j2 new file mode 100644 index 00000000..b47b4697 --- /dev/null +++ b/ansible/playbooks/templates/extensions/guacamole/inventory.j2 @@ -0,0 +1,5 @@ +[default] +guacamole ansible_host={{ip_range}}.52 ansible_connection=ssh ansible_ssh_common_args='-o StrictHostKeyChecking=no' + +[extensions] +guacamole diff --git a/ansible/playbooks/templates/extensions/lx01/inventory.j2 b/ansible/playbooks/templates/extensions/lx01/inventory.j2 new file mode 100644 index 00000000..d57ef001 --- /dev/null +++ b/ansible/playbooks/templates/extensions/lx01/inventory.j2 @@ -0,0 +1,8 @@ +[default] +lx01 ansible_host={{ip_range}}.32 dict_key=lx01 ansible_connection=ssh ansible_ssh_common_args='-o StrictHostKeyChecking=no' + +[extensions] +lx01 + +[linux_domain] +lx01 diff --git a/ansible/extensions/wazuh/inventory b/ansible/playbooks/templates/extensions/wazuh/inventory.j2 similarity index 100% rename from ansible/extensions/wazuh/inventory rename to ansible/playbooks/templates/extensions/wazuh/inventory.j2 diff --git a/ansible/extensions/ws01/inventory b/ansible/playbooks/templates/extensions/ws01/inventory.j2 similarity index 100% rename from ansible/extensions/ws01/inventory rename to ansible/playbooks/templates/extensions/ws01/inventory.j2 diff --git a/ansible/playbooks/vulnerabilities.yml b/ansible/playbooks/vulnerabilities.yml index 014ca45b..5102b332 100644 --- a/ansible/playbooks/vulnerabilities.yml +++ b/ansible/playbooks/vulnerabilities.yml @@ -1,8 +1,4 @@ --- -# Load data -- name: Import Data - ansible.builtin.import_playbook: data.yml - tags: data - name: Setup vulnerabilities with tasks hosts: domain diff --git a/ansible/plugins/vars/lab_config.py b/ansible/plugins/vars/lab_config.py new file mode 100644 index 00000000..ee0bf042 --- /dev/null +++ b/ansible/plugins/vars/lab_config.py @@ -0,0 +1,151 @@ +"""Vars plugin that auto-loads the lab JSON config into host variables. + +Reads ``data_path`` and ``env`` from the inventory ``[all:vars]`` section, +loads ``{data_path}/{env}-config.json``, and injects the ``lab`` key as a +variable available to all hosts. This replaces the old pattern of every +playbook importing ``data.yml`` just to call ``include_vars`` + ``set_fact``. +""" + +from __future__ import annotations + +import json +import os + +from ansible.errors import AnsibleParserError +from ansible.inventory.host import Host +from ansible.inventory.group import Group +from ansible.plugins.vars import BaseVarsPlugin +from ansible.utils.display import Display + +DOCUMENTATION = """ + name: lab_config + version_added: "1.0.0" + short_description: Auto-load lab JSON config from inventory vars + description: + - Reads C(data_path) and C(env) from inventory variables and loads + the corresponding JSON config file. The top-level C(lab) key from + the JSON is injected as a variable for all hosts. + options: {} +""" + +display = Display() + +# Module-level cache so we only read/parse the JSON once per process, +# regardless of how many hosts or plays trigger get_vars(). +_cache: dict | None = None +_cache_path: str | None = None + + +class VarsModule(BaseVarsPlugin): + """Inject ``lab`` variable from the environment JSON config file.""" + + REQUIRES_ENABLED = True + + def get_vars(self, loader, path, entities, cache=True): + global _cache, _cache_path # noqa: PLW0603 + + if not entities: + return {} + + # Resolve data_path and env from inventory variables. + # We inspect the first entity (host or group) to get access to + # inventory-level variables. + data_path = None + env = None + + for entity in entities: + if isinstance(entity, Host): + all_vars = entity.get_vars() + # Host vars may contain group vars via inheritance + data_path = all_vars.get("data_path") + env = all_vars.get("env") + if data_path and env: + break + # Also check the 'all' group + for group in entity.get_groups(): + if group.name == "all": + gvars = group.get_vars() + data_path = data_path or gvars.get("data_path") + env = env or gvars.get("env") + break + elif isinstance(entity, Group): + gvars = entity.get_vars() + data_path = gvars.get("data_path") + env = env or gvars.get("env") + + if data_path and env: + break + + if not data_path or not env: + return {} + + # data_path may contain {{ playbook_dir }} which we can't resolve + # here (no playbook context). Fall back to resolving relative to + # the inventory file path. + if "{{" in str(data_path): + # The inventory sets data_path relative to playbook_dir, e.g.: + # data_path="{{ playbook_dir }}/../../ad/GOAD/data" + # We can't evaluate Jinja here, but we know the project layout: + # the inventory file sits at the project root, and the path + # relative to the project root is ad//data. + # Extract the meaningful suffix after the Jinja expression. + raw = str(data_path) + # Strip the Jinja template prefix to get the relative tail + # e.g. "{{ playbook_dir }}/../../ad/GOAD/data" → "../../ad/GOAD/data" + parts = raw.split("}}") + if len(parts) > 1: + tail = parts[-1].lstrip("/") + else: + return {} + + # Resolve from the inventory source path (project root) + # path is the inventory directory passed by Ansible + if path: + base = path if os.path.isdir(path) else os.path.dirname(path) + else: + base = os.getcwd() + + # The playbook_dir points to ansible/playbooks/, so ../../ goes + # to the project root. Since we're resolving from the project + # root (where the inventory lives), we need to adjust. + # "../../ad/GOAD/data" from ansible/playbooks/ == "ad/GOAD/data" from root + # Normalise by stripping leading "../" segments + while tail.startswith("../"): + tail = tail[3:] + data_path = os.path.join(base, tail) + + config_file = os.path.join(str(data_path), f"{env}-config.json") + + if not os.path.isfile(config_file): + # Try without env prefix (some labs use config.json directly) + config_file_alt = os.path.join(str(data_path), "config.json") + if os.path.isfile(config_file_alt): + config_file = config_file_alt + else: + display.vvv(f"lab_config: no config file found at {config_file}") + return {} + + # Return cached result if we already parsed this file + if cache and _cache is not None and _cache_path == config_file: + return _cache + + try: + with open(config_file, "r") as f: + data = json.load(f) + except (OSError, json.JSONDecodeError) as exc: + raise AnsibleParserError( + f"lab_config: failed to load {config_file}: {exc}" + ) from exc + + result = {} + if "lab" in data: + result["lab"] = data["lab"] + else: + display.warning(f"lab_config: no 'lab' key in {config_file}") + + if cache: + _cache = result + _cache_path = config_file + + display.vvv(f"lab_config: loaded lab config from {config_file}") + return result diff --git a/ansible/requirements.yml b/ansible/requirements.yml index 81131957..fecf0f6f 100644 --- a/ansible/requirements.yml +++ b/ansible/requirements.yml @@ -11,3 +11,6 @@ collections: version: 1.10.0 - name: amazon.aws version: 11.2.0 + +roles: + - name: geerlingguy.mysql diff --git a/ansible/roles/add_dns_record/README.md b/ansible/roles/add_dns_record/README.md new file mode 100644 index 00000000..ee91e294 --- /dev/null +++ b/ansible/roles/add_dns_record/README.md @@ -0,0 +1,36 @@ + +# add_dns_record + +## Description + +Stub role — needs implementation + +## Requirements + +- Ansible >= 2.15 + +## Role Variables + +## Tasks + +### main.yml + +- **TODO: implement {{ role_name }}** (ansible.builtin.debug) + +## Example Playbook + +```yaml +- hosts: servers + roles: + - add_dns_record +``` + +## Author Information + +- **Author**: dreadnode +- **Company**: +- **License**: GPL-3.0-or-later + +## Platforms + + diff --git a/ansible/roles/add_dns_record/meta/main.yml b/ansible/roles/add_dns_record/meta/main.yml new file mode 100644 index 00000000..751b3694 --- /dev/null +++ b/ansible/roles/add_dns_record/meta/main.yml @@ -0,0 +1,7 @@ +--- +galaxy_info: + author: dreadnode + description: Stub role — needs implementation + min_ansible_version: "2.15" + license: GPL-3.0-or-later +dependencies: [] diff --git a/ansible/roles/add_dns_record/tasks/main.yml b/ansible/roles/add_dns_record/tasks/main.yml new file mode 100644 index 00000000..67a5ac2c --- /dev/null +++ b/ansible/roles/add_dns_record/tasks/main.yml @@ -0,0 +1,4 @@ +--- +- name: "TODO: implement {{ role_name }}" + ansible.builtin.debug: + msg: "Role {{ role_name }} is a stub — implementation required" diff --git a/ansible/roles/elk/tasks/main.yml b/ansible/roles/elk/tasks/main.yml index d7da52bd..aed39716 100644 --- a/ansible/roles/elk/tasks/main.yml +++ b/ansible/roles/elk/tasks/main.yml @@ -24,22 +24,22 @@ - name: Install logstash ansible.builtin.apt: - name: Logstash + name: logstash state: present - name: Install java ansible.builtin.apt: - name: Openjdk-11-jre + name: openjdk-11-jre state: present - name: Install elasticsearch ansible.builtin.apt: - name: Elasticsearch + name: elasticsearch state: present - name: Install kibana ansible.builtin.apt: - name: Kibana + name: kibana state: present - name: Copy kibana config @@ -66,30 +66,30 @@ - name: Enable logstash ansible.builtin.service: - name: Logstash + name: logstash enabled: yes - name: Enable elasticsearch ansible.builtin.service: - name: Elasticsearch + name: elasticsearch enabled: yes - name: Enable kibana ansible.builtin.service: - name: Kibana + name: kibana enabled: yes - name: Start logstash ansible.builtin.service: - name: Logstash + name: logstash state: started - name: Start elasticsearch ansible.builtin.service: - name: Elasticsearch + name: elasticsearch state: started - name: Start kibana ansible.builtin.service: - name: Kibana + name: kibana state: started diff --git a/ansible/roles/exchange_bot/README.md b/ansible/roles/exchange_bot/README.md new file mode 100644 index 00000000..8713fd3d --- /dev/null +++ b/ansible/roles/exchange_bot/README.md @@ -0,0 +1,38 @@ + +# exchange_bot + +## Description + +Deploy Exchange mailbox bot for automated mail reading + +## Requirements + +- Ansible >= 2.15 + +## Role Variables + +## Tasks + +### main.yml + +- **Create setup folder** (ansible.windows.win_file) +- **Copy scripts** (ansible.windows.win_copy) +- **Create schedule task bot_scheduler** (ansible.windows.win_shell) + +## Example Playbook + +```yaml +- hosts: servers + roles: + - exchange_bot +``` + +## Author Information + +- **Author**: Dreadnode +- **Company**: +- **License**: MIT + +## Platforms + + diff --git a/ansible/extensions/exchange/ansible/roles/exchange_bot/files/botScheduler.ps1 b/ansible/roles/exchange_bot/files/botScheduler.ps1 similarity index 100% rename from ansible/extensions/exchange/ansible/roles/exchange_bot/files/botScheduler.ps1 rename to ansible/roles/exchange_bot/files/botScheduler.ps1 diff --git a/ansible/extensions/exchange/ansible/roles/exchange_bot/files/readMail.ps1 b/ansible/roles/exchange_bot/files/readMail.ps1 similarity index 100% rename from ansible/extensions/exchange/ansible/roles/exchange_bot/files/readMail.ps1 rename to ansible/roles/exchange_bot/files/readMail.ps1 diff --git a/ansible/roles/exchange_bot/meta/main.yml b/ansible/roles/exchange_bot/meta/main.yml new file mode 100644 index 00000000..e23096b3 --- /dev/null +++ b/ansible/roles/exchange_bot/meta/main.yml @@ -0,0 +1,8 @@ +--- +galaxy_info: + role_name: exchange_bot + author: Dreadnode + description: Deploy Exchange mailbox bot for automated mail reading + min_ansible_version: "2.15" + license: MIT +dependencies: [] diff --git a/ansible/extensions/exchange/ansible/roles/exchange_bot/tasks/main.yml b/ansible/roles/exchange_bot/tasks/main.yml similarity index 100% rename from ansible/extensions/exchange/ansible/roles/exchange_bot/tasks/main.yml rename to ansible/roles/exchange_bot/tasks/main.yml diff --git a/ansible/roles/linux_add_linux_to_domain/README.md b/ansible/roles/linux_add_linux_to_domain/README.md new file mode 100644 index 00000000..e33a1d16 --- /dev/null +++ b/ansible/roles/linux_add_linux_to_domain/README.md @@ -0,0 +1,36 @@ + +# linux_add_linux_to_domain + +## Description + +Stub role — needs implementation + +## Requirements + +- Ansible >= 2.15 + +## Role Variables + +## Tasks + +### main.yml + +- **TODO: implement {{ role_name }}** (ansible.builtin.debug) + +## Example Playbook + +```yaml +- hosts: servers + roles: + - linux_add_linux_to_domain +``` + +## Author Information + +- **Author**: dreadnode +- **Company**: +- **License**: GPL-3.0-or-later + +## Platforms + + diff --git a/ansible/roles/linux_add_linux_to_domain/meta/main.yml b/ansible/roles/linux_add_linux_to_domain/meta/main.yml new file mode 100644 index 00000000..751b3694 --- /dev/null +++ b/ansible/roles/linux_add_linux_to_domain/meta/main.yml @@ -0,0 +1,7 @@ +--- +galaxy_info: + author: dreadnode + description: Stub role — needs implementation + min_ansible_version: "2.15" + license: GPL-3.0-or-later +dependencies: [] diff --git a/ansible/roles/linux_add_linux_to_domain/tasks/main.yml b/ansible/roles/linux_add_linux_to_domain/tasks/main.yml new file mode 100644 index 00000000..67a5ac2c --- /dev/null +++ b/ansible/roles/linux_add_linux_to_domain/tasks/main.yml @@ -0,0 +1,4 @@ +--- +- name: "TODO: implement {{ role_name }}" + ansible.builtin.debug: + msg: "Role {{ role_name }} is a stub — implementation required" diff --git a/ansible/roles/linux_guacamole/README.md b/ansible/roles/linux_guacamole/README.md new file mode 100644 index 00000000..185a5c87 --- /dev/null +++ b/ansible/roles/linux_guacamole/README.md @@ -0,0 +1,36 @@ + +# linux_guacamole + +## Description + +Stub role — needs implementation + +## Requirements + +- Ansible >= 2.15 + +## Role Variables + +## Tasks + +### main.yml + +- **TODO: implement {{ role_name }}** (ansible.builtin.debug) + +## Example Playbook + +```yaml +- hosts: servers + roles: + - linux_guacamole +``` + +## Author Information + +- **Author**: dreadnode +- **Company**: +- **License**: GPL-3.0-or-later + +## Platforms + + diff --git a/ansible/roles/linux_guacamole/meta/main.yml b/ansible/roles/linux_guacamole/meta/main.yml new file mode 100644 index 00000000..751b3694 --- /dev/null +++ b/ansible/roles/linux_guacamole/meta/main.yml @@ -0,0 +1,7 @@ +--- +galaxy_info: + author: dreadnode + description: Stub role — needs implementation + min_ansible_version: "2.15" + license: GPL-3.0-or-later +dependencies: [] diff --git a/ansible/roles/linux_guacamole/tasks/main.yml b/ansible/roles/linux_guacamole/tasks/main.yml new file mode 100644 index 00000000..67a5ac2c --- /dev/null +++ b/ansible/roles/linux_guacamole/tasks/main.yml @@ -0,0 +1,4 @@ +--- +- name: "TODO: implement {{ role_name }}" + ansible.builtin.debug: + msg: "Role {{ role_name }} is a stub — implementation required" diff --git a/ansible/roles/linux_guacamole_create_connections/README.md b/ansible/roles/linux_guacamole_create_connections/README.md new file mode 100644 index 00000000..d53a4b62 --- /dev/null +++ b/ansible/roles/linux_guacamole_create_connections/README.md @@ -0,0 +1,36 @@ + +# linux_guacamole_create_connections + +## Description + +Stub role — needs implementation + +## Requirements + +- Ansible >= 2.15 + +## Role Variables + +## Tasks + +### main.yml + +- **TODO: implement {{ role_name }}** (ansible.builtin.debug) + +## Example Playbook + +```yaml +- hosts: servers + roles: + - linux_guacamole_create_connections +``` + +## Author Information + +- **Author**: dreadnode +- **Company**: +- **License**: GPL-3.0-or-later + +## Platforms + + diff --git a/ansible/roles/linux_guacamole_create_connections/meta/main.yml b/ansible/roles/linux_guacamole_create_connections/meta/main.yml new file mode 100644 index 00000000..751b3694 --- /dev/null +++ b/ansible/roles/linux_guacamole_create_connections/meta/main.yml @@ -0,0 +1,7 @@ +--- +galaxy_info: + author: dreadnode + description: Stub role — needs implementation + min_ansible_version: "2.15" + license: GPL-3.0-or-later +dependencies: [] diff --git a/ansible/roles/linux_guacamole_create_connections/tasks/main.yml b/ansible/roles/linux_guacamole_create_connections/tasks/main.yml new file mode 100644 index 00000000..67a5ac2c --- /dev/null +++ b/ansible/roles/linux_guacamole_create_connections/tasks/main.yml @@ -0,0 +1,4 @@ +--- +- name: "TODO: implement {{ role_name }}" + ansible.builtin.debug: + msg: "Role {{ role_name }} is a stub — implementation required" diff --git a/ansible/roles/linux_tomcat/README.md b/ansible/roles/linux_tomcat/README.md new file mode 100644 index 00000000..ff303425 --- /dev/null +++ b/ansible/roles/linux_tomcat/README.md @@ -0,0 +1,36 @@ + +# linux_tomcat + +## Description + +Stub role — needs implementation + +## Requirements + +- Ansible >= 2.15 + +## Role Variables + +## Tasks + +### main.yml + +- **TODO: implement {{ role_name }}** (ansible.builtin.debug) + +## Example Playbook + +```yaml +- hosts: servers + roles: + - linux_tomcat +``` + +## Author Information + +- **Author**: dreadnode +- **Company**: +- **License**: GPL-3.0-or-later + +## Platforms + + diff --git a/ansible/roles/linux_tomcat/meta/main.yml b/ansible/roles/linux_tomcat/meta/main.yml new file mode 100644 index 00000000..751b3694 --- /dev/null +++ b/ansible/roles/linux_tomcat/meta/main.yml @@ -0,0 +1,7 @@ +--- +galaxy_info: + author: dreadnode + description: Stub role — needs implementation + min_ansible_version: "2.15" + license: GPL-3.0-or-later +dependencies: [] diff --git a/ansible/roles/linux_tomcat/tasks/main.yml b/ansible/roles/linux_tomcat/tasks/main.yml new file mode 100644 index 00000000..67a5ac2c --- /dev/null +++ b/ansible/roles/linux_tomcat/tasks/main.yml @@ -0,0 +1,4 @@ +--- +- name: "TODO: implement {{ role_name }}" + ansible.builtin.debug: + msg: "Role {{ role_name }} is a stub — implementation required" diff --git a/ansible/roles/ludus_exchange/README.md b/ansible/roles/ludus_exchange/README.md new file mode 100644 index 00000000..b59a6091 --- /dev/null +++ b/ansible/roles/ludus_exchange/README.md @@ -0,0 +1,140 @@ + +# ludus_exchange + +## Description + +Install Exchange 2019 or Exchange 2016 to a Windows server + +## Requirements + +- Ansible >= 2.10 + +## Role Variables + +### Default Variables (main.yml) + +| Variable | Type | Default | Description | +| -------- | ---- | ------- | ----------- | +| `ludus_install_directory` | str | `/opt/ludus` | No description | +| `exchange_dotnet_install_path` | str | `https://download.visualstudio.microsoft.com/download/pr/2d6bb6b2-226a-4baa-bdec-798822606ff1/8494001c276a4b96804cde7829c04d7f/ndp48-x86-x64-allos-enu.exe` | No description | +| `vcredist2013_install_path` | str | `https://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x64.exe` | No description | +| `rewrite_module_path` | str | `https://download.microsoft.com/download/1/2/8/128E2E22-C1B9-44A4-BE2A-5859ED1D4592/rewrite_amd64_en-US.msi` | No description | +| `ucma_runtime_path` | str | `https://download.microsoft.com/download/2/C/4/2C47A5C1-A1F3-4843-B9FE-84C0032C61EC/UcmaRuntimeSetup.exe` | No description | +| `ludus_exchange_iso_url` | str | `https://download.microsoft.com/download/d/7/b/d7bcf78a-00d2-4a46-a3d2-7d506116bcd2/ExchangeServer2019-x64-CU9.ISO` | No description | +| `ludus_exchange2016_iso_url` | str | `https://download.microsoft.com/download/2/5/8/258D30CF-CA4C-433A-A618-FB7E6BCC4EEE/ExchangeServer2016-x64-cu12.iso` | No description | +| `ludus_exchange_domain` | str | `{{ (ludus | selectattr('vm_name', 'match', inventory_hostname))[0].domain.fqdn.split('.')[0] }}` | No description | +| `ludus_exchange_dc` | str | `{{ (ludus | selectattr('domain', 'defined') | selectattr('domain.fqdn', 'match', ludus_exchange_domain) | selectattr('domain.role', 'match', 'primary-dc'))[0].hostname }}` | No description | +| `ludus_exchange_host` | str | `{{ (ludus | selectattr('vm_name', 'match', inventory_hostname))[0].hostname }}` | No description | +| `ludus_exchange_domain_username` | str | `{{ ludus_exchange_domain }}\{{ defaults.ad_domain_admin }}` | No description | +| `ludus_exchange_domain_password` | str | `{{ defaults.ad_domain_admin_password }}` | No description | +| `send_connector_name` | str | `` | No description | +| `send_connector_smtpserver` | str | `` | No description | +| `send_connector_address_spaces` | list | `[]` | No description | +| `send_connector_address_spaces.0` | str | `SMTP:*;1` | No description | +| `send_connector_source_transport_servers` | str | `{{ (ludus | selectattr('vm_name', 'match', inventory_hostname))[0].hostname }}` | No description | + +## Tasks + +### ludus-create-mailbox.yml + +- **Enable Mailbox for all AD users** (ansible.windows.win_shell) +- **Disable mailbox splash screen** (ansible.windows.win_shell) + +### ludus-download-exchange-2016.yml + +- **Get Exchange 2016 ISO if needed** (block) +- **Create resources ISO directory if it does not exist** (ansible.builtin.file) +- **Check if Exchange 2016 ISO exists** (ansible.builtin.stat) +- **Downloading EXCHANGE 2016 ISO - This will take a while** (ansible.builtin.get_url) - Conditional +- **Create EXCHANGE folder** (ansible.windows.win_file) +- **Check if EXCHANGE 2016 ISO exists on host** (ansible.windows.win_stat) +- **Copy Exchange ISO to windows host** (ansible.windows.win_copy) - Conditional + +### ludus-download-exchange-2019.yml + +- **Get Exchange ISO if needed** (block) +- **Create resources ISO directory if it does not exist** (ansible.builtin.file) +- **Check if Exchange ISO exists** (ansible.builtin.stat) +- **Downloading EXCHANGE ISO - This will take a while** (ansible.builtin.get_url) - Conditional +- **Create EXCHANGE folder** (ansible.windows.win_file) +- **Check if EXCHANGE ISO exists on host** (ansible.windows.win_stat) +- **Copy Exchange ISO to windows host** (ansible.windows.win_copy) - Conditional + +### ludus-exchange-2016-install.yml + +- **Mount Exchange 2016 ISO** (community.windows.win_disk_image) +- **Prepare Schema** (ansible.windows.win_shell) +- **Prepare Active Directory** (ansible.windows.win_shell) +- **Install Exchange 2016** (ansible.windows.win_shell) +- **Unmount ISO** (community.windows.win_disk_image) +- **Remove ISO file** (ansible.windows.win_file) + +### ludus-exchange-2019-install.yml + +- **Mount Exchange ISO** (community.windows.win_disk_image) +- **Prepare Schema** (ansible.windows.win_shell) +- **Prepare Active Directory** (ansible.windows.win_shell) +- **Install Exchange** (ansible.windows.win_shell) +- **Unmount ISO** (community.windows.win_disk_image) +- **Remove ISO file** (ansible.windows.win_file) + +### ludus-exchange-dns.yml + +- **Configure InternalDNSAdapter** (ansible.windows.win_powershell) - Conditional +- **Restart the Exchange Transport service** (ansible.windows.win_service) - Conditional + +### ludus-exchange-pre.yml + +- **Check if pre-req installation is completed** (ansible.windows.win_stat) +- **Install Exchange prerequisites** (block) - Conditional +- **Install IIS 6 Compatibility Features** (ansible.windows.win_feature) +- **Enable Windows optional features** (ansible.windows.win_optional_feature) +- **Install .NET Framework** (ansible.windows.win_package) +- **Reboot after installing .NET Framework 4.8** (ansible.windows.win_reboot) - Conditional +- **Wait for the machine to be up after reboot** (ansible.builtin.wait_for_connection) +- **Install Visual C++ Redistributable for Visual Studio 2013** (ansible.windows.win_package) +- **Reboot after installing Visual C++ Redistributable for Visual Studio 2013** (ansible.windows.win_reboot) - Conditional +- **The IIS URL Rewrite Module is required with Exchange Server 2016 CU22 and Exchange Server 2019 CU11 or later** (ansible.windows.win_package) +- **Install Unified Communications Managed API 4.0 Runtime.** (ansible.windows.win_package) +- **Reboot after installing Unified Communications Managed API 4.0 Runtime** (ansible.windows.win_reboot) - Conditional +- **Wait for the machine to be up after reboot** (ansible.builtin.wait_for_connection) +- **Install Windows features ADLDS Exchange Transport Hub** (ansible.windows.win_feature) +- **Create file marker for pre-req installation completion** (ansible.windows.win_shell) - Conditional +- **Reboot the system** (ansible.windows.win_reboot) +- **Wait for the machine to be up after reboot** (ansible.builtin.wait_for_connection) +- **Check if pre-req installation is already completed** (ansible.builtin.debug) - Conditional + +### ludus_sendconnector.yml + +- **Alert if send_connector_smtpserver is not set or empty** (ansible.builtin.debug) - Conditional +- **Configure Send Connector** (ansible.windows.win_shell) - Conditional + +### main.yml + +- **Check if Exchange is installed** (ansible.windows.win_service) +- **Download Exchange ISO for Windows Server 2016** (ansible.builtin.include_tasks) - Conditional +- **Download Exchange ISO for Windows Server 2019** (ansible.builtin.include_tasks) - Conditional +- **Ludus Exchange Server features to be installed** (ansible.builtin.include_tasks) - Conditional +- **Install Exchange Server for Windows Server 2016** (ansible.builtin.include_tasks) - Conditional +- **Install Exchange Server for Windows Server 2019** (ansible.builtin.include_tasks) - Conditional +- **Create ad users mailbox** (ansible.builtin.include_tasks) +- **Setup internal dns adapter** (ansible.builtin.include_tasks) + +## Example Playbook + +```yaml +- hosts: servers + roles: + - ludus_exchange +``` + +## Author Information + +- **Author**: aleemladha +- **Company**: aleemladha +- **License**: GPLv3 + +## Platforms + +- Windows: 2012R2, 2019, 2022 + diff --git a/ansible/extensions/exchange/ansible/roles/ludus_exchange/defaults/main.yml b/ansible/roles/ludus_exchange/defaults/main.yml similarity index 100% rename from ansible/extensions/exchange/ansible/roles/ludus_exchange/defaults/main.yml rename to ansible/roles/ludus_exchange/defaults/main.yml diff --git a/ansible/extensions/exchange/ansible/roles/ludus_exchange/meta/main.yml b/ansible/roles/ludus_exchange/meta/main.yml similarity index 100% rename from ansible/extensions/exchange/ansible/roles/ludus_exchange/meta/main.yml rename to ansible/roles/ludus_exchange/meta/main.yml diff --git a/ansible/extensions/exchange/ansible/roles/ludus_exchange/tasks/ludus-create-mailbox.yml b/ansible/roles/ludus_exchange/tasks/ludus-create-mailbox.yml similarity index 100% rename from ansible/extensions/exchange/ansible/roles/ludus_exchange/tasks/ludus-create-mailbox.yml rename to ansible/roles/ludus_exchange/tasks/ludus-create-mailbox.yml diff --git a/ansible/extensions/exchange/ansible/roles/ludus_exchange/tasks/ludus-download-exchange-2016.yml b/ansible/roles/ludus_exchange/tasks/ludus-download-exchange-2016.yml similarity index 100% rename from ansible/extensions/exchange/ansible/roles/ludus_exchange/tasks/ludus-download-exchange-2016.yml rename to ansible/roles/ludus_exchange/tasks/ludus-download-exchange-2016.yml diff --git a/ansible/extensions/exchange/ansible/roles/ludus_exchange/tasks/ludus-download-exchange-2019.yml b/ansible/roles/ludus_exchange/tasks/ludus-download-exchange-2019.yml similarity index 100% rename from ansible/extensions/exchange/ansible/roles/ludus_exchange/tasks/ludus-download-exchange-2019.yml rename to ansible/roles/ludus_exchange/tasks/ludus-download-exchange-2019.yml diff --git a/ansible/extensions/exchange/ansible/roles/ludus_exchange/tasks/ludus-exchange-2016-install.yml b/ansible/roles/ludus_exchange/tasks/ludus-exchange-2016-install.yml similarity index 100% rename from ansible/extensions/exchange/ansible/roles/ludus_exchange/tasks/ludus-exchange-2016-install.yml rename to ansible/roles/ludus_exchange/tasks/ludus-exchange-2016-install.yml diff --git a/ansible/extensions/exchange/ansible/roles/ludus_exchange/tasks/ludus-exchange-2019-install.yml b/ansible/roles/ludus_exchange/tasks/ludus-exchange-2019-install.yml similarity index 100% rename from ansible/extensions/exchange/ansible/roles/ludus_exchange/tasks/ludus-exchange-2019-install.yml rename to ansible/roles/ludus_exchange/tasks/ludus-exchange-2019-install.yml diff --git a/ansible/extensions/exchange/ansible/roles/ludus_exchange/tasks/ludus-exchange-dns.yml b/ansible/roles/ludus_exchange/tasks/ludus-exchange-dns.yml similarity index 100% rename from ansible/extensions/exchange/ansible/roles/ludus_exchange/tasks/ludus-exchange-dns.yml rename to ansible/roles/ludus_exchange/tasks/ludus-exchange-dns.yml diff --git a/ansible/extensions/exchange/ansible/roles/ludus_exchange/tasks/ludus-exchange-pre.yml b/ansible/roles/ludus_exchange/tasks/ludus-exchange-pre.yml similarity index 100% rename from ansible/extensions/exchange/ansible/roles/ludus_exchange/tasks/ludus-exchange-pre.yml rename to ansible/roles/ludus_exchange/tasks/ludus-exchange-pre.yml diff --git a/ansible/extensions/exchange/ansible/roles/ludus_exchange/tasks/ludus_sendconnector.yml b/ansible/roles/ludus_exchange/tasks/ludus_sendconnector.yml similarity index 100% rename from ansible/extensions/exchange/ansible/roles/ludus_exchange/tasks/ludus_sendconnector.yml rename to ansible/roles/ludus_exchange/tasks/ludus_sendconnector.yml diff --git a/ansible/extensions/exchange/ansible/roles/ludus_exchange/tasks/main.yml b/ansible/roles/ludus_exchange/tasks/main.yml similarity index 100% rename from ansible/extensions/exchange/ansible/roles/ludus_exchange/tasks/main.yml rename to ansible/roles/ludus_exchange/tasks/main.yml diff --git a/ansible/roles/network_discovery/README.md b/ansible/roles/network_discovery/README.md new file mode 100644 index 00000000..829beefd --- /dev/null +++ b/ansible/roles/network_discovery/README.md @@ -0,0 +1,76 @@ + +# network_discovery + +## Description + +Discover network adapters, IPs, and AWS instance mappings on lab hosts + +## Requirements + +- Ansible >= 2.15 + +## Role Variables + +### Default Variables (main.yml) + +| Variable | Type | Default | Description | +| -------- | ---- | ------- | ----------- | +| `skip_ip_detection` | bool | `False` | No description | +| `skip_adapter_detection` | bool | `False` | No description | + +## Tasks + +### adapters.yml + +- **Get adapter names** (ansible.windows.win_powershell) +- **Set adapter facts from adapter detection** (ansible.builtin.set_fact) - Conditional + +### aws_mapping.yml + +- **Load AWS instance_to_ip mapping from file for SSM connections** (ansible.builtin.include_vars) - Conditional +- **Display AWS instance to IP mappings** (ansible.builtin.debug) - Conditional +- **Set instance_to_ip mapping for all hosts** (ansible.builtin.set_fact) - Conditional +- **Set host_ipv4 from AWS instance mapping for SSM connections** (ansible.builtin.set_fact) - Conditional +- **Display host IP assignment from AWS mapping** (ansible.builtin.debug) - Conditional + +### dc_facts.yml + +- **Store IP as host fact for DCs** (ansible.builtin.set_fact) - Conditional + +### fallbacks.yml + +- **Set fallback network facts** (ansible.builtin.set_fact) - Conditional +- **Show host network configuration** (ansible.builtin.debug) + +### ip_detection.yml + +- **Get all network information** (ansible.windows.win_powershell) +- **Set all network facts from full detection** (ansible.builtin.set_fact) - Conditional + +### main.yml + +- **Include AWS instance mapping tasks** (ansible.builtin.include_tasks) +- **Check if network facts are already cached** (ansible.builtin.set_fact) +- **Include adapter detection tasks** (ansible.builtin.include_tasks) - Conditional +- **Include IP detection tasks** (ansible.builtin.include_tasks) - Conditional +- **Include fallback facts** (ansible.builtin.include_tasks) +- **Include DC fact storage** (ansible.builtin.include_tasks) + +## Example Playbook + +```yaml +- hosts: servers + roles: + - network_discovery +``` + +## Author Information + +- **Author**: Dreadnode +- **Company**: Dreadnode +- **License**: MIT + +## Platforms + +- Windows: all + diff --git a/ansible/roles/network_discovery/defaults/main.yml b/ansible/roles/network_discovery/defaults/main.yml new file mode 100644 index 00000000..88c6302a --- /dev/null +++ b/ansible/roles/network_discovery/defaults/main.yml @@ -0,0 +1,4 @@ +--- +# Set to true to skip network detection (e.g., when facts are already cached) +skip_ip_detection: false +skip_adapter_detection: false diff --git a/ansible/roles/network_discovery/meta/main.yml b/ansible/roles/network_discovery/meta/main.yml new file mode 100644 index 00000000..a7f73e60 --- /dev/null +++ b/ansible/roles/network_discovery/meta/main.yml @@ -0,0 +1,12 @@ +--- +galaxy_info: + role_name: network_discovery + author: Dreadnode + description: Discover network adapters, IPs, and AWS instance mappings on lab hosts + company: Dreadnode + min_ansible_version: "2.15" + license: MIT + platforms: + - name: Windows + versions: [all] +dependencies: [] diff --git a/ansible/roles/network_discovery/tasks/adapters.yml b/ansible/roles/network_discovery/tasks/adapters.yml new file mode 100644 index 00000000..3d646870 --- /dev/null +++ b/ansible/roles/network_discovery/tasks/adapters.yml @@ -0,0 +1,47 @@ +--- +- name: Get adapter names + ansible.windows.win_powershell: + script: | + $ProgressPreference = 'SilentlyContinue' + $ErrorActionPreference = 'Stop' + + try { + $adapters = @(Get-NetAdapter -ErrorAction Stop | Where-Object {$_.Status -eq "Up"}) + if ($adapters.Count -eq 0) { + # Fallback to WMI + $wmiAdapters = @(Get-WmiObject Win32_NetworkAdapter | Where-Object {$_.NetEnabled -eq $true}) + Write-Output "DOMAIN_ADAPTER:$($wmiAdapters[0].NetConnectionID)" + if ($wmiAdapters.Count -gt 1) { + Write-Output "NAT_ADAPTER:$($wmiAdapters[1].NetConnectionID)" + } else { + Write-Output "NAT_ADAPTER:$($wmiAdapters[0].NetConnectionID)" + } + Write-Output "ADAPTER_COUNT:$($wmiAdapters.Count)" + } else { + Write-Output "DOMAIN_ADAPTER:$($adapters[0].Name)" + if ($adapters.Count -gt 1) { + Write-Output "NAT_ADAPTER:$($adapters[1].Name)" + } else { + Write-Output "NAT_ADAPTER:$($adapters[0].Name)" + } + Write-Output "ADAPTER_COUNT:$($adapters.Count)" + } + $Ansible.Changed = $false + } catch { + Write-Output "DOMAIN_ADAPTER:Ethernet" + Write-Output "NAT_ADAPTER:Ethernet" + Write-Output "ADAPTER_COUNT:1" + $Ansible.Changed = $false + } + register: adapter_info + +- name: Set adapter facts from adapter detection + ansible.builtin.set_fact: + domain_adapter: "{{ adapter_info.output | select('match', '^DOMAIN_ADAPTER:') | first | regex_replace('^DOMAIN_ADAPTER:', '') | default('Ethernet') }}" + nat_adapter: "{{ adapter_info.output | select('match', '^NAT_ADAPTER:') | first | regex_replace('^NAT_ADAPTER:', '') | default('Ethernet') }}" + number_of_interfaces: "{{ adapter_info.output | select('match', '^ADAPTER_COUNT:') | first | regex_replace('^ADAPTER_COUNT:', '') | default('1') }}" + two_adapters: "{{ (adapter_info.output | select('match', '^ADAPTER_COUNT:') | first | regex_replace('^ADAPTER_COUNT:', '') | default('1') | int) > 1 }}" + cacheable: true + when: + - adapter_info is defined + - adapter_info.output is defined diff --git a/ansible/roles/network_discovery/tasks/aws_mapping.yml b/ansible/roles/network_discovery/tasks/aws_mapping.yml new file mode 100644 index 00000000..10215259 --- /dev/null +++ b/ansible/roles/network_discovery/tasks/aws_mapping.yml @@ -0,0 +1,48 @@ +--- +- name: Load AWS instance_to_ip mapping from file for SSM connections + ansible.builtin.include_vars: + file: "/tmp/aws_instance_mapping_{{ env }}.json" + name: aws_mapping + run_once: true + when: ansible_connection is search('aws_ssm') + ignore_errors: true + +- name: Display AWS instance to IP mappings + ansible.builtin.debug: + msg: "Instance {{ item.key }} -> IP {{ item.value }}" + loop: "{{ aws_mapping.instance_to_ip | dict2items }}" + run_once: true + when: + - ansible_connection is search('aws_ssm') + - aws_mapping is defined + +- name: Set instance_to_ip mapping for all hosts + ansible.builtin.set_fact: + instance_to_ip: "{{ aws_mapping.instance_to_ip | default({}) }}" + cacheable: true + run_once: true + delegate_facts: true + delegate_to: "{{ item }}" + loop: "{{ groups['all'] }}" + loop_control: + label: "{{ item }} ({{ hostvars[item].ansible_host | default('no instance_id') }})" + when: + - ansible_connection is search('aws_ssm') + - aws_mapping is defined + +- name: Set host_ipv4 from AWS instance mapping for SSM connections + ansible.builtin.set_fact: + host_ipv4: "{{ instance_to_ip[ansible_host] | default('') }}" + cacheable: true + when: + - ansible_connection is search('aws_ssm') + - instance_to_ip is defined + - ansible_host in instance_to_ip + +- name: Display host IP assignment from AWS mapping + ansible.builtin.debug: + msg: "Host {{ inventory_hostname }} ({{ ansible_host }}) assigned IP: {{ host_ipv4 }}" + when: + - ansible_connection is search('aws_ssm') + - host_ipv4 is defined + - host_ipv4 != '' diff --git a/ansible/roles/network_discovery/tasks/dc_facts.yml b/ansible/roles/network_discovery/tasks/dc_facts.yml new file mode 100644 index 00000000..2807a14b --- /dev/null +++ b/ansible/roles/network_discovery/tasks/dc_facts.yml @@ -0,0 +1,9 @@ +--- +- name: Store IP as host fact for DCs + ansible.builtin.set_fact: + dc_ipv4: "{{ host_ipv4 }}" + cacheable: true + when: + - host_ipv4 is defined + - host_ipv4 != '' + - inventory_hostname in groups['dc'] diff --git a/ansible/roles/network_discovery/tasks/fallbacks.yml b/ansible/roles/network_discovery/tasks/fallbacks.yml new file mode 100644 index 00000000..d6cee240 --- /dev/null +++ b/ansible/roles/network_discovery/tasks/fallbacks.yml @@ -0,0 +1,22 @@ +--- +- name: Set fallback network facts + ansible.builtin.set_fact: + domain_adapter: "{{ domain_adapter | default('Ethernet') }}" + nat_adapter: "{{ nat_adapter | default(domain_adapter | default('Ethernet')) }}" + number_of_interfaces: "{{ number_of_interfaces | default('1') }}" + two_adapters: "{{ two_adapters | default(false) }}" + host_ipv4: "{{ host_ipv4 | default(ansible_default_ipv4.address | default('')) }}" + cacheable: true + when: domain_adapter is not defined + +- name: Show host network configuration + ansible.builtin.debug: + msg: + - "hostname: {{ inventory_hostname }}" + - "instance_id: {{ ansible_host }}" + - "resolved_ip: {{ host_ipv4 | default('NOT RESOLVED') }}" + - "domain interface: {{ domain_adapter }}" + - "number_of_interfaces: {{ number_of_interfaces }}" + - "two_adapters: {{ two_adapters }}" + - "nat interface: {{ nat_adapter }}" + verbosity: 1 diff --git a/ansible/roles/network_discovery/tasks/ip_detection.yml b/ansible/roles/network_discovery/tasks/ip_detection.yml new file mode 100644 index 00000000..898c12d2 --- /dev/null +++ b/ansible/roles/network_discovery/tasks/ip_detection.yml @@ -0,0 +1,66 @@ +--- +- name: Get all network information + ansible.windows.win_powershell: + script: | + $ProgressPreference = 'SilentlyContinue' + $ErrorActionPreference = 'Stop' + + # Set a timeout for the entire script + $timeout = New-TimeSpan -Seconds 30 + $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + + try { + $primaryAdapter = $null + $adapters = @() + + try { + $adapters = @(Get-NetAdapter -ErrorAction Stop | Where-Object {$_.Status -eq "Up"}) + $primaryAdapter = $adapters | Select-Object -First 1 + } catch { + # Fallback to WMI if Get-NetAdapter fails + $adapters = @(Get-WmiObject Win32_NetworkAdapter | Where-Object {$_.NetEnabled -eq $true}) + if ($adapters.Count -gt 0) { + $primaryAdapter = @{Name = $adapters[0].NetConnectionID} + } + } + + # Get IP quickly using WMI + $primaryIP = "" + try { + $networkConfigs = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.IPEnabled -eq $true -and $_.IPAddress[0] -notmatch '^127\.' -and $_.IPAddress[0] -notmatch '^169\.254\.'} + if ($networkConfigs) { + $primaryIP = $networkConfigs[0].IPAddress[0] + } + } catch { + $primaryIP = "" + } + + Write-Output "DOMAIN_ADAPTER:$(if ($primaryAdapter) { $primaryAdapter.Name } else { 'Ethernet' })" + Write-Output "NAT_ADAPTER:$(if ($adapters.Count -gt 1) { $adapters[1].Name } else { if ($primaryAdapter) { $primaryAdapter.Name } else { 'Ethernet' } })" + Write-Output "ADAPTER_COUNT:$($adapters.Count)" + Write-Output "PRIMARY_IP:$primaryIP" + $Ansible.Changed = $false + } catch { + # Quick fallback + Write-Output "DOMAIN_ADAPTER:Ethernet" + Write-Output "NAT_ADAPTER:Ethernet" + Write-Output "ADAPTER_COUNT:1" + Write-Output "PRIMARY_IP:" + $Ansible.Changed = $false + } + async: 60 + poll: 5 + register: network_info + ignore_errors: true + +- name: Set all network facts from full detection + ansible.builtin.set_fact: + domain_adapter: "{{ network_info.output | select('match', '^DOMAIN_ADAPTER:') | first | regex_replace('^DOMAIN_ADAPTER:', '') | default('Ethernet') }}" + nat_adapter: "{{ network_info.output | select('match', '^NAT_ADAPTER:') | first | regex_replace('^NAT_ADAPTER:', '') | default('Ethernet') }}" + number_of_interfaces: "{{ network_info.output | select('match', '^ADAPTER_COUNT:') | first | regex_replace('^ADAPTER_COUNT:', '') | default('1') }}" + two_adapters: "{{ (network_info.output | select('match', '^ADAPTER_COUNT:') | first | regex_replace('^ADAPTER_COUNT:', '') | default('1') | int) > 1 }}" + host_ipv4: "{{ network_info.output | select('match', '^PRIMARY_IP:') | first | regex_replace('^PRIMARY_IP:', '') | default(ansible_default_ipv4.address | default('')) }}" + cacheable: true + when: + - network_info is defined + - network_info.output is defined diff --git a/ansible/roles/network_discovery/tasks/main.yml b/ansible/roles/network_discovery/tasks/main.yml new file mode 100644 index 00000000..29374122 --- /dev/null +++ b/ansible/roles/network_discovery/tasks/main.yml @@ -0,0 +1,22 @@ +--- +- name: Include AWS instance mapping tasks + ansible.builtin.include_tasks: aws_mapping.yml + +- name: Check if network facts are already cached + ansible.builtin.set_fact: + skip_ip_detection: "{{ host_ipv4 is defined and host_ipv4 != '' }}" + skip_adapter_detection: "{{ domain_adapter is defined and domain_adapter != '' }}" + +- name: Include adapter detection tasks + ansible.builtin.include_tasks: adapters.yml + when: not skip_adapter_detection | default(false) + +- name: Include IP detection tasks + ansible.builtin.include_tasks: ip_detection.yml + when: not skip_ip_detection | default(false) + +- name: Include fallback facts + ansible.builtin.include_tasks: fallbacks.yml + +- name: Include DC fact storage + ansible.builtin.include_tasks: dc_facts.yml diff --git a/ansible/roles/wazuh_agent/README.md b/ansible/roles/wazuh_agent/README.md new file mode 100644 index 00000000..eb8c187d --- /dev/null +++ b/ansible/roles/wazuh_agent/README.md @@ -0,0 +1,47 @@ + +# wazuh_agent + +## Description + +Install Wazuh agent on Windows hosts + +## Requirements + +- Ansible >= 2.15 + +## Role Variables + +### Default Variables (main.yml) + +| Variable | Type | Default | Description | +| -------- | ---- | ------- | ----------- | +| `wazuh_agent_install_package` | str | `https://packages.wazuh.com/4.x/windows/wazuh-agent-4.8.2-1.msi` | No description | +| `wazuh_install_location` | str | `C:\tmp` | No description | + +## Tasks + +### main.yml + +- **Check if Wazuh Agent service is installed** (ansible.windows.win_service) +- **Create wazuh_install_location folder if not exist** (ansible.windows.win_file) +- **Download Wazuh Agent MSI package** (ansible.windows.win_get_url) - Conditional +- **Install Wazuh Agent** (ansible.windows.win_command) - Conditional +- **Start Wazuh Agent service** (ansible.windows.win_service) - Conditional + +## Example Playbook + +```yaml +- hosts: servers + roles: + - wazuh_agent +``` + +## Author Information + +- **Author**: Dreadnode +- **Company**: +- **License**: MIT + +## Platforms + + diff --git a/ansible/extensions/wazuh/ansible/roles/wazuh_agent/defaults/main.yml b/ansible/roles/wazuh_agent/defaults/main.yml similarity index 100% rename from ansible/extensions/wazuh/ansible/roles/wazuh_agent/defaults/main.yml rename to ansible/roles/wazuh_agent/defaults/main.yml diff --git a/ansible/roles/wazuh_agent/meta/main.yml b/ansible/roles/wazuh_agent/meta/main.yml new file mode 100644 index 00000000..5ee9e060 --- /dev/null +++ b/ansible/roles/wazuh_agent/meta/main.yml @@ -0,0 +1,8 @@ +--- +galaxy_info: + role_name: wazuh_agent + author: Dreadnode + description: Install Wazuh agent on Windows hosts + min_ansible_version: "2.15" + license: MIT +dependencies: [] diff --git a/ansible/extensions/wazuh/ansible/roles/wazuh_agent/tasks/main.yml b/ansible/roles/wazuh_agent/tasks/main.yml similarity index 100% rename from ansible/extensions/wazuh/ansible/roles/wazuh_agent/tasks/main.yml rename to ansible/roles/wazuh_agent/tasks/main.yml diff --git a/ansible/roles/wazuh_agent_linux/README.md b/ansible/roles/wazuh_agent_linux/README.md new file mode 100644 index 00000000..1c85b078 --- /dev/null +++ b/ansible/roles/wazuh_agent_linux/README.md @@ -0,0 +1,32 @@ + +# wazuh_agent_linux + +## Description + +Install Wazuh agent on Linux hosts + +## Requirements + +- Ansible >= 2.15 + +## Role Variables + +## Tasks + +## Example Playbook + +```yaml +- hosts: servers + roles: + - wazuh_agent_linux +``` + +## Author Information + +- **Author**: Dreadnode +- **Company**: +- **License**: MIT + +## Platforms + + diff --git a/ansible/roles/wazuh_agent_linux/meta/main.yml b/ansible/roles/wazuh_agent_linux/meta/main.yml new file mode 100644 index 00000000..66c355db --- /dev/null +++ b/ansible/roles/wazuh_agent_linux/meta/main.yml @@ -0,0 +1,8 @@ +--- +galaxy_info: + role_name: wazuh_agent_linux + author: Dreadnode + description: Install Wazuh agent on Linux hosts + min_ansible_version: "2.15" + license: MIT +dependencies: [] diff --git a/ansible/roles/wazuh_manager/README.md b/ansible/roles/wazuh_manager/README.md new file mode 100644 index 00000000..f487439b --- /dev/null +++ b/ansible/roles/wazuh_manager/README.md @@ -0,0 +1,53 @@ + +# wazuh_manager + +## Description + +Deploy and configure Wazuh manager server + +## Requirements + +- Ansible >= 2.15 + +## Role Variables + +### Default Variables (main.yml) + +| Variable | Type | Default | Description | +| -------- | ---- | ------- | ----------- | +| `wazuh_install_script_url` | str | `https://packages.wazuh.com/4.8/wazuh-install.sh` | No description | +| `socfortress_rules_script_url` | str | `https://raw.githubusercontent.com/socfortress/Wazuh-Rules/main/wazuh_socfortress_rules.sh` | No description | + +## Tasks + +### main.yml + +- **Create /opt/wazuh directory if it does not exist** (ansible.builtin.file) +- **Check services facts** (ansible.builtin.service_facts) +- **Download Wazuh installation script** (ansible.builtin.get_url) - Conditional +- **Run Wazuh installation script** (ansible.builtin.shell) - Conditional +- **Fix rootkit trojan detection due to issue** (ansible.builtin.lineinfile) +- **Start Wazuh Manager service** (ansible.builtin.service) +- **Get stats of ossec directory** (ansible.builtin.stat) +- **Download SOCFORTRESS Wazuh rules script** (ansible.builtin.copy) - Conditional +- **Run SOCFORTRESS Wazuh rules script** (ansible.builtin.shell) - Conditional +- **Extract username and password** (ansible.builtin.shell) +- **Display username and password** (ansible.builtin.debug) + +## Example Playbook + +```yaml +- hosts: servers + roles: + - wazuh_manager +``` + +## Author Information + +- **Author**: Dreadnode +- **Company**: +- **License**: MIT + +## Platforms + + diff --git a/ansible/extensions/wazuh/ansible/roles/wazuh_manager/defaults/main.yml b/ansible/roles/wazuh_manager/defaults/main.yml similarity index 100% rename from ansible/extensions/wazuh/ansible/roles/wazuh_manager/defaults/main.yml rename to ansible/roles/wazuh_manager/defaults/main.yml diff --git a/ansible/extensions/wazuh/ansible/roles/wazuh_manager/files/wazuh_socfortress_rules.sh b/ansible/roles/wazuh_manager/files/wazuh_socfortress_rules.sh similarity index 100% rename from ansible/extensions/wazuh/ansible/roles/wazuh_manager/files/wazuh_socfortress_rules.sh rename to ansible/roles/wazuh_manager/files/wazuh_socfortress_rules.sh diff --git a/ansible/roles/wazuh_manager/meta/main.yml b/ansible/roles/wazuh_manager/meta/main.yml new file mode 100644 index 00000000..7b35144f --- /dev/null +++ b/ansible/roles/wazuh_manager/meta/main.yml @@ -0,0 +1,8 @@ +--- +galaxy_info: + role_name: wazuh_manager + author: Dreadnode + description: Deploy and configure Wazuh manager server + min_ansible_version: "2.15" + license: MIT +dependencies: [] diff --git a/ansible/extensions/wazuh/ansible/roles/wazuh_manager/tasks/main.yml b/ansible/roles/wazuh_manager/tasks/main.yml similarity index 100% rename from ansible/extensions/wazuh/ansible/roles/wazuh_manager/tasks/main.yml rename to ansible/roles/wazuh_manager/tasks/main.yml diff --git a/cli/cmd/config_cmd.go b/cli/cmd/config_cmd.go index 324a435d..ac4b5042 100644 --- a/cli/cmd/config_cmd.go +++ b/cli/cmd/config_cmd.go @@ -20,7 +20,10 @@ var configShowCmd = &cobra.Command{ Use: "show", Short: "Display current effective configuration", RunE: func(cmd *cobra.Command, args []string) error { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + return err + } fmt.Printf("Environment: %s\n", cfg.Env) fmt.Printf("Region: %s\n", valueOrDefault(cfg.Region, "(from inventory)")) fmt.Printf("Debug: %v\n", cfg.Debug) @@ -65,9 +68,14 @@ var configInitCmd = &cobra.Command{ Use: "init", Short: "Create default configuration file", RunE: func(cmd *cobra.Command, args []string) error { - home, _ := os.UserHomeDir() + home, err := os.UserHomeDir() + if err != nil { + return fmt.Errorf("resolving home directory: %w", err) + } dir := filepath.Join(home, ".config", "dreadgoad") - _ = os.MkdirAll(dir, 0o755) + if err := os.MkdirAll(dir, 0o755); err != nil { + return fmt.Errorf("creating config directory: %w", err) + } cfgPath := filepath.Join(dir, "dreadgoad.yaml") if _, err := os.Stat(cfgPath); err == nil { diff --git a/cli/cmd/diagnose.go b/cli/cmd/diagnose.go index 2125ff94..8492212f 100644 --- a/cli/cmd/diagnose.go +++ b/cli/cmd/diagnose.go @@ -33,7 +33,10 @@ func init() { } func runDiagnose(cmd *cobra.Command, args []string) error { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + return err + } ctx := context.Background() dc01IP, _ := cmd.Flags().GetString("dc01-ip") diff --git a/cli/cmd/doctor.go b/cli/cmd/doctor.go index fa0f6645..40170968 100644 --- a/cli/cmd/doctor.go +++ b/cli/cmd/doctor.go @@ -12,7 +12,10 @@ var doctorCmd = &cobra.Command{ Long: `Verifies that all required tools and configurations are in place: ansible-core version, AWS CLI, Python, Ansible collections, credentials, and inventory.`, RunE: func(cmd *cobra.Command, args []string) error { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + return err + } results := doctor.RunChecks(cfg.InventoryPath(), cfg.ProjectRoot) doctor.PrintResults(results) diff --git a/cli/cmd/extension.go b/cli/cmd/extension.go new file mode 100644 index 00000000..221713a1 --- /dev/null +++ b/cli/cmd/extension.go @@ -0,0 +1,219 @@ +package cmd + +import ( + "context" + "fmt" + "os" + "path/filepath" + "sort" + "strings" + "time" + + "github.com/dreadnode/dreadgoad/internal/ansible" + "github.com/dreadnode/dreadgoad/internal/config" + "github.com/spf13/cobra" +) + +var extensionCmd = &cobra.Command{ + Use: "extension", + Aliases: []string{"ext"}, + Short: "Manage lab extensions", + Long: `List, inspect, and provision lab extensions such as ELK, Exchange, Guacamole, and more.`, +} + +var extensionListCmd = &cobra.Command{ + Use: "list", + Short: "List available extensions", + RunE: runExtensionList, +} + +var extensionProvisionCmd = &cobra.Command{ + Use: "provision ", + Short: "Provision a specific extension", + Args: cobra.ExactArgs(1), + RunE: runExtensionProvision, +} + +var extensionProvisionAllCmd = &cobra.Command{ + Use: "provision-all", + Short: "Provision all enabled extensions for the active environment", + RunE: runExtensionProvisionAll, +} + +func init() { + rootCmd.AddCommand(extensionCmd) + extensionCmd.AddCommand(extensionListCmd) + extensionCmd.AddCommand(extensionProvisionCmd) + extensionCmd.AddCommand(extensionProvisionAllCmd) + + extensionListCmd.Flags().String("lab", "", "Filter by lab compatibility (e.g. GOAD, GOAD-Light)") + + extensionProvisionCmd.Flags().String("limit", "", "Limit execution to specific hosts") + extensionProvisionCmd.Flags().Int("max-retries", 0, "Max retry attempts (default: from config)") + extensionProvisionCmd.Flags().Int("retry-delay", 0, "Delay between retries in seconds") +} + +func runExtensionList(cmd *cobra.Command, args []string) error { + cfg, err := config.Get() + if err != nil { + return err + } + labFilter, _ := cmd.Flags().GetString("lab") + + // Sort extension names for stable output + names := make([]string, 0, len(cfg.Extensions)) + for name := range cfg.Extensions { + names = append(names, name) + } + sort.Strings(names) + + enabled := cfg.EnabledExtensionsForEnv() + enabledSet := make(map[string]bool, len(enabled)) + for _, e := range enabled { + enabledSet[e] = true + } + + fmt.Printf("Available extensions (env: %s):\n\n", cfg.Env) + for _, name := range names { + ext := cfg.Extensions[name] + + if labFilter != "" && !cfg.IsExtensionCompatible(name, labFilter) { + continue + } + + status := " " + if enabledSet[name] { + status = "*" + } + + compat := strings.Join(ext.Compatibility, ", ") + fmt.Printf(" [%s] %-12s %s\n", status, name, ext.Description) + fmt.Printf(" machines: %s | compatible: %s\n", strings.Join(ext.Machines, ", "), compat) + if ext.Impact != "" && ext.Impact != "none" { + fmt.Printf(" impact: %s\n", ext.Impact) + } + fmt.Println() + } + + fmt.Println(" [*] = enabled for current environment") + return nil +} + +func runExtensionProvision(cmd *cobra.Command, args []string) error { + cfg, err := config.Get() + if err != nil { + return err + } + name := args[0] + + ext, ok := cfg.Extensions[name] + if !ok { + return fmt.Errorf("unknown extension %q; run 'dreadgoad extension list' to see available extensions", name) + } + + limit, _ := cmd.Flags().GetString("limit") + maxRetries, _ := cmd.Flags().GetInt("max-retries") + retryDelay, _ := cmd.Flags().GetInt("retry-delay") + + return provisionExtension(cfg, name, ext, limit, maxRetries, retryDelay) +} + +func runExtensionProvisionAll(cmd *cobra.Command, args []string) error { + cfg, err := config.Get() + if err != nil { + return err + } + enabled := cfg.EnabledExtensionsForEnv() + + if len(enabled) == 0 { + fmt.Printf("No extensions enabled for environment %q.\n", cfg.Env) + fmt.Println("Enable extensions in your config under environments..enabled_extensions") + return nil + } + + fmt.Printf("Provisioning %d extension(s) for environment %q:\n", len(enabled), cfg.Env) + for _, name := range enabled { + fmt.Printf(" - %s\n", name) + } + fmt.Println() + + for _, name := range enabled { + ext, ok := cfg.Extensions[name] + if !ok { + return fmt.Errorf("enabled extension %q not found in config", name) + } + if err := provisionExtension(cfg, name, ext, "", 0, 0); err != nil { + return fmt.Errorf("extension %s failed: %w", name, err) + } + } + + fmt.Println("All extensions provisioned successfully.") + return nil +} + +func provisionExtension(cfg *config.Config, name string, ext config.ExtensionConfig, limit string, maxRetries, retryDelay int) error { + ctx := context.Background() + + // Ensure log directory + _ = os.MkdirAll(cfg.LogDir, 0o755) + logFile := filepath.Join(cfg.LogDir, fmt.Sprintf("%s-ext-%s-%s.log", + cfg.Env, name, time.Now().Format("20060102_150405"))) + + // Build extension inventory path + extInvPath := cfg.ExtensionInventoryTemplate(name) + + // Build extra vars for the extension + extraVars := make(map[string]string) + if ext.DataDir != "" { + extraVars["extension_data_dir"] = cfg.ExtensionDataDir(name) + } + + // Guacamole needs special vars + if name == "guacamole" { + extraVars["lab_data_file"] = filepath.Join(cfg.ProjectRoot, "ad", "GOAD", "data", "inventory.yml") + extraVars["guacamole_vars_file"] = filepath.Join(cfg.ExtensionDataDir(name), "guacamole.yml") + + // Collect extension data files for merging + var dataFiles []string + for extName, extCfg := range cfg.Extensions { + if extCfg.DataDir != "" { + dataFile := filepath.Join(cfg.ExtensionDataDir(extName), "config.json") + if _, err := os.Stat(dataFile); err == nil { + dataFiles = append(dataFiles, dataFile) + } + } + } + if len(dataFiles) > 0 { + extraVars["extension_data_files"] = "[" + strings.Join(dataFiles, ",") + "]" + } + } + + fmt.Println("===============================================") + fmt.Printf("Provisioning extension: %s\n", name) + fmt.Printf("Playbook: ansible/playbooks/%s\n", ext.Playbook) + fmt.Printf("Log file: %s\n", logFile) + fmt.Println("===============================================") + + opts := ansible.RetryOptions{ + Playbook: ext.Playbook, + Env: cfg.Env, + Inventories: []string{extInvPath}, + ExtraVars: extraVars, + Limit: limit, + Debug: cfg.Debug, + LogFile: logFile, + } + if maxRetries > 0 { + opts.MaxRetries = maxRetries + } + if retryDelay > 0 { + opts.RetryDelay = time.Duration(retryDelay) * time.Second + } + + if err := ansible.RunPlaybookWithRetry(ctx, opts); err != nil { + return err + } + + fmt.Printf("Extension %s provisioned successfully.\n", name) + return nil +} diff --git a/cli/cmd/infra.go b/cli/cmd/infra.go index 6bc8bc22..cbd921d3 100644 --- a/cli/cmd/infra.go +++ b/cli/cmd/infra.go @@ -24,7 +24,10 @@ type infraContext struct { // requireInfra validates that AWS credentials work, GOAD instances are discoverable, // and SSM agents are online. Returns the ready-to-use infrastructure context. func requireInfra(ctx context.Context) (*infraContext, error) { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + return nil, err + } region := cfg.Region if region == "" { diff --git a/cli/cmd/inventory.go b/cli/cmd/inventory.go index ce8461dd..dcb8fcb7 100644 --- a/cli/cmd/inventory.go +++ b/cli/cmd/inventory.go @@ -56,7 +56,10 @@ type instanceInfo struct { } func runInventorySync(cmd *cobra.Command, args []string) error { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + return err + } invPath := cfg.InventoryPath() if _, err := os.Stat(invPath); os.IsNotExist(err) { @@ -182,7 +185,10 @@ func applyInstanceUpdates(invPath string, instances []instanceInfo) error { } func runInventoryShow(cmd *cobra.Command, args []string) error { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + return err + } parsed, err := inv.Parse(cfg.InventoryPath()) if err != nil { @@ -202,7 +208,10 @@ func runInventoryShow(cmd *cobra.Command, args []string) error { } func runInventoryMapping(cmd *cobra.Command, args []string) error { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + return err + } ctx := context.Background() parsed, err := inv.Parse(cfg.InventoryPath()) diff --git a/cli/cmd/lab.go b/cli/cmd/lab.go index 8e4a13bd..1197a9e1 100644 --- a/cli/cmd/lab.go +++ b/cli/cmd/lab.go @@ -41,7 +41,10 @@ func init() { } func runLabStatus(cmd *cobra.Command, args []string) error { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + return err + } ctx := context.Background() region := cfg.Region @@ -77,7 +80,10 @@ func runLabStatus(cmd *cobra.Command, args []string) error { func runLabAction(action string) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, args []string) error { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + return err + } ctx := context.Background() region := cfg.Region diff --git a/cli/cmd/provision.go b/cli/cmd/provision.go index 8c80e5c3..aa7fc64e 100644 --- a/cli/cmd/provision.go +++ b/cli/cmd/provision.go @@ -60,7 +60,10 @@ func init() { } func runProvision(cmd *cobra.Command, args []string) error { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + return err + } ctx := context.Background() // Determine playbooks diff --git a/cli/cmd/root.go b/cli/cmd/root.go index ae221f9d..be3d5f92 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -20,7 +20,13 @@ It manages the full lifecycle: infrastructure provisioning via Terraform, configuration via Ansible, validation of vulnerability configurations, and operational tasks like SSM session management.`, PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - cfg := config.Get() + if err := config.Init(); err != nil { + return err + } + cfg, err := config.Get() + if err != nil { + return err + } logging.Init(cfg.Debug, cfg.LogDir, cfg.Env) return nil }, @@ -28,6 +34,11 @@ and operational tasks like SSM session management.`, SilenceErrors: true, } +// SetVersionInfo sets the root command version from build-time ldflags. +func SetVersionInfo(version, commit, date string) { + rootCmd.Version = fmt.Sprintf("%s (commit: %s, built: %s)", version, commit, date) +} + func Execute() error { if err := rootCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, err) @@ -37,14 +48,22 @@ func Execute() error { } func init() { - cobra.OnInitialize(config.Init) - rootCmd.PersistentFlags().StringP("env", "e", "staging", "Target environment (dev, staging, prod)") rootCmd.PersistentFlags().String("region", "", "AWS region (default: from inventory)") rootCmd.PersistentFlags().Bool("debug", false, "Enable debug/verbose output") rootCmd.PersistentFlags().String("config", "", "Config file path") - _ = viper.BindPFlag("env", rootCmd.PersistentFlags().Lookup("env")) - _ = viper.BindPFlag("region", rootCmd.PersistentFlags().Lookup("region")) - _ = viper.BindPFlag("debug", rootCmd.PersistentFlags().Lookup("debug")) + for _, bind := range []struct { + key string + flag string + }{ + {"env", "env"}, + {"region", "region"}, + {"debug", "debug"}, + {"config", "config"}, + } { + if err := viper.BindPFlag(bind.key, rootCmd.PersistentFlags().Lookup(bind.flag)); err != nil { + panic(fmt.Sprintf("failed to bind flag %q: %v", bind.flag, err)) + } + } } diff --git a/cli/cmd/ssm.go b/cli/cmd/ssm.go index 3ac14b4d..27b9044a 100644 --- a/cli/cmd/ssm.go +++ b/cli/cmd/ssm.go @@ -61,7 +61,10 @@ func init() { } func runSSMStatus(cmd *cobra.Command, args []string) error { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + return err + } ctx := context.Background() inv, err := inventory.Parse(cfg.InventoryPath()) @@ -100,7 +103,10 @@ func runSSMStatus(cmd *cobra.Command, args []string) error { } func runSSMCleanup(cmd *cobra.Command, args []string) error { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + return err + } ctx := context.Background() maxAge, _ := cmd.Flags().GetInt("max-age") @@ -133,7 +139,10 @@ func runSSMCleanup(cmd *cobra.Command, args []string) error { } func runSSMConnect(cmd *cobra.Command, args []string) error { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + return err + } inv, err := inventory.Parse(cfg.InventoryPath()) if err != nil { @@ -159,7 +168,10 @@ func runSSMConnect(cmd *cobra.Command, args []string) error { } func runSSMRun(cmd *cobra.Command, args []string) error { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + return err + } ctx := context.Background() hostsFlag, _ := cmd.Flags().GetString("hosts") diff --git a/cli/cmd/variant.go b/cli/cmd/variant.go index cd3f5397..0f95fb55 100644 --- a/cli/cmd/variant.go +++ b/cli/cmd/variant.go @@ -38,7 +38,10 @@ func init() { } func runVariantGenerate(cmd *cobra.Command, args []string) error { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + return err + } envCfg := cfg.ActiveEnvironment() source, _ := cmd.Flags().GetString("source") diff --git a/cli/internal/ansible/retry.go b/cli/internal/ansible/retry.go index dbc5c3ba..bd0410d9 100644 --- a/cli/internal/ansible/retry.go +++ b/cli/internal/ansible/retry.go @@ -16,14 +16,16 @@ import ( // RetryOptions configures the retry behavior for playbook execution. type RetryOptions struct { - Playbook string - Env string - Limit string - Debug bool - MaxRetries int - RetryDelay time.Duration - LogFile string - Log *slog.Logger // optional; falls back to slog.Default() + Playbook string + Env string + Inventories []string // additional inventory paths + ExtraVars map[string]string // extra variables passed to ansible-playbook + Limit string + Debug bool + MaxRetries int + RetryDelay time.Duration + LogFile string + Log *slog.Logger // optional; falls back to slog.Default() } func (o *RetryOptions) logger() *slog.Logger { @@ -35,7 +37,10 @@ func (o *RetryOptions) logger() *slog.Logger { // RunPlaybookWithRetry runs a playbook with error-specific retry logic. func RunPlaybookWithRetry(ctx context.Context, opts RetryOptions) error { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + return err + } log := opts.logger() if opts.MaxRetries == 0 { @@ -60,11 +65,13 @@ func RunPlaybookWithRetry(ctx context.Context, opts RetryOptions) error { log.Info("starting playbook", "playbook", opts.Playbook, "attempt", attempt+1, "max", opts.MaxRetries) result := RunPlaybook(ctx, RunOptions{ - Playbook: opts.Playbook, - Env: opts.Env, - Limit: opts.Limit, - Debug: opts.Debug, - LogFile: opts.LogFile, + Playbook: opts.Playbook, + Env: opts.Env, + Inventories: opts.Inventories, + Limit: opts.Limit, + Debug: opts.Debug, + LogFile: opts.LogFile, + ExtraVars: opts.ExtraVars, }) if result.TimedOut { @@ -97,11 +104,13 @@ func retryWithErrorStrategy(ctx context.Context, opts RetryOptions, failResult * limit := buildRetryLimit(opts.Limit, failedHostsStr) baseOpts := RunOptions{ - Playbook: opts.Playbook, - Env: opts.Env, - Limit: limit, - Debug: opts.Debug, - LogFile: opts.LogFile, + Playbook: opts.Playbook, + Env: opts.Env, + Inventories: opts.Inventories, + Limit: limit, + Debug: opts.Debug, + LogFile: opts.LogFile, + ExtraVars: opts.ExtraVars, } switch failResult.ErrorType { @@ -216,7 +225,11 @@ func buildRetryLimit(userLimit, failedHosts string) string { } func cleanupSSMSessions(ctx context.Context, env string, log *slog.Logger) { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + log.Warn("could not get config for SSM cleanup", "error", err) + return + } inv, err := inventory.Parse(cfg.InventoryPath()) if err != nil { log.Warn("could not parse inventory for SSM cleanup", "error", err) @@ -244,7 +257,11 @@ func fixSSMUsers(ctx context.Context, env string, failedHosts []string, log *slo return } - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + log.Warn("could not get config for ssm-user fix", "error", err) + return + } inv, err := inventory.Parse(cfg.InventoryPath()) if err != nil { log.Warn("could not parse inventory for ssm-user fix", "error", err) @@ -276,7 +293,11 @@ func fixSSMUsers(ctx context.Context, env string, failedHosts []string, log *slo } func rebootFailedHosts(ctx context.Context, opts RetryOptions, log *slog.Logger) { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + log.Warn("could not get config for reboot", "error", err) + return + } for _, host := range strings.Split(opts.Limit, ",") { if host == "" { continue @@ -289,7 +310,12 @@ func rebootFailedHosts(ctx context.Context, opts RetryOptions, log *slog.Logger) } rebootCmd := execCommand(ctx, "ansible", args...) rebootCmd.Dir = cfg.ProjectRoot - rebootCmd.Env = buildEnv(RunOptions{Env: opts.Env}, cfg) + env, envErr := buildEnv(RunOptions{Env: opts.Env}, cfg) + if envErr != nil { + log.Warn("could not build env for reboot", "host", host, "error", envErr) + continue + } + rebootCmd.Env = env if output, err := rebootCmd.CombinedOutput(); err != nil { log.Warn("reboot failed", "host", host, "error", err, "output", string(output)) } diff --git a/cli/internal/ansible/runner.go b/cli/internal/ansible/runner.go index 27dae0b8..2dda0cda 100644 --- a/cli/internal/ansible/runner.go +++ b/cli/internal/ansible/runner.go @@ -23,6 +23,7 @@ import ( type RunOptions struct { Playbook string Env string + Inventories []string // additional inventory paths (appended after the default env inventory) Limit string Forks int ExtraVars map[string]string @@ -45,11 +46,19 @@ type RunResult struct { // RunPlaybook executes ansible-playbook with idle timeout monitoring. func RunPlaybook(ctx context.Context, opts RunOptions) *RunResult { - cfg := config.Get() + cfg, err := config.Get() + if err != nil { + return &RunResult{ExitCode: 1, Output: fmt.Sprintf("config error: %v", err)} + } result := &RunResult{} args := buildArgs(opts, cfg) - env := buildEnv(opts, cfg) + env, err := buildEnv(opts, cfg) + if err != nil { + result.ErrorType = ErrUnclassified + result.ErrorDetail = err.Error() + return result + } slog.Info("running playbook", "playbook", opts.Playbook, "args", strings.Join(args, " ")) @@ -179,10 +188,18 @@ func buildArgs(opts RunOptions, cfg *config.Config) []string { args := []string{ "-i", inventoryPath, - "-e", "ansible_facts_gathering_timeout=60", - playbookPath, } + // Append additional inventories (e.g. extension inventories) + for _, inv := range opts.Inventories { + if !filepath.IsAbs(inv) { + inv = filepath.Join(cfg.ProjectRoot, inv) + } + args = append(args, "-i", inv) + } + + args = append(args, "-e", "ansible_facts_gathering_timeout=60", playbookPath) + if opts.Debug { args = append([]string{"-vvv"}, args...) } @@ -202,10 +219,15 @@ func buildArgs(opts RunOptions, cfg *config.Config) []string { return args } -func buildEnv(opts RunOptions, cfg *config.Config) []string { +func buildEnv(opts RunOptions, cfg *config.Config) ([]string, error) { env := os.Environ() - for k, v := range cfg.AnsibleEnv() { + ansibleEnv, err := cfg.AnsibleEnv() + if err != nil { + return nil, fmt.Errorf("ansible env: %w", err) + } + + for k, v := range ansibleEnv { env = append(env, k+"="+v) } @@ -213,7 +235,7 @@ func buildEnv(opts RunOptions, cfg *config.Config) []string { env = append(env, k+"="+v) } - return env + return env, nil } func killProcessGroup(pid int) { diff --git a/cli/internal/config/config.go b/cli/internal/config/config.go index 42da3e04..82e88dbb 100644 --- a/cli/internal/config/config.go +++ b/cli/internal/config/config.go @@ -1,6 +1,8 @@ package config import ( + "errors" + "fmt" "os" "path/filepath" "sync" @@ -8,12 +10,23 @@ import ( "github.com/spf13/viper" ) +// ExtensionConfig holds metadata for a lab extension. +type ExtensionConfig struct { + Description string `mapstructure:"description"` + Machines []string `mapstructure:"machines"` + Compatibility []string `mapstructure:"compatibility"` + Impact string `mapstructure:"impact"` + Playbook string `mapstructure:"playbook"` + DataDir string `mapstructure:"data_dir"` +} + // EnvironmentConfig holds per-environment settings. type EnvironmentConfig struct { - Variant bool `mapstructure:"variant"` - VariantSource string `mapstructure:"variant_source"` - VariantTarget string `mapstructure:"variant_target"` - VariantName string `mapstructure:"variant_name"` + Variant bool `mapstructure:"variant"` + VariantSource string `mapstructure:"variant_source"` + VariantTarget string `mapstructure:"variant_target"` + VariantName string `mapstructure:"variant_name"` + EnabledExtensions []string `mapstructure:"enabled_extensions"` } // Config holds all CLI configuration. @@ -28,6 +41,7 @@ type Config struct { Playbooks []string `mapstructure:"playbooks"` ProjectRoot string `mapstructure:"project_root"` Environments map[string]EnvironmentConfig `mapstructure:"environments"` + Extensions map[string]ExtensionConfig `mapstructure:"extensions"` } var ( @@ -35,12 +49,15 @@ var ( once sync.Once ) -// Init initializes Viper configuration. Called by cobra.OnInitialize. -func Init() { +// Init initializes Viper configuration. Called from PersistentPreRunE. +func Init() error { if cfgFile := viper.GetString("config"); cfgFile != "" { viper.SetConfigFile(cfgFile) } else { - home, _ := os.UserHomeDir() + home, err := os.UserHomeDir() + if err != nil { + return fmt.Errorf("resolving home directory: %w", err) + } viper.AddConfigPath(filepath.Join(home, ".config", "dreadgoad")) viper.AddConfigPath(".") viper.SetConfigName("dreadgoad") @@ -52,28 +69,44 @@ func Init() { setDefaults() - // Config file is optional - _ = viper.ReadInConfig() + if err := viper.ReadInConfig(); err != nil { + var notFound viper.ConfigFileNotFoundError + if !errors.As(err, ¬Found) { + return fmt.Errorf("reading config: %w", err) + } + } + return nil } // Get returns the current configuration, loading it once. -func Get() *Config { +func Get() (*Config, error) { + var initErr error once.Do(func() { cfg = &Config{} - _ = viper.Unmarshal(cfg) + if err := viper.Unmarshal(cfg); err != nil { + initErr = fmt.Errorf("unmarshaling config: %w", err) + return + } - // Resolve project root (directory containing ansible/) if cfg.ProjectRoot == "" { - cfg.ProjectRoot = findProjectRoot() + root, err := findProjectRoot() + if err != nil { + initErr = fmt.Errorf("finding project root: %w", err) + return + } + cfg.ProjectRoot = root } - // Expand log dir if cfg.LogDir == "" { - home, _ := os.UserHomeDir() + home, err := os.UserHomeDir() + if err != nil { + initErr = fmt.Errorf("resolving home directory: %w", err) + return + } cfg.LogDir = filepath.Join(home, ".ansible", "logs", "goad") } }) - return cfg + return cfg, initErr } // Reset clears the cached config (for testing). @@ -93,15 +126,18 @@ func (c *Config) AnsibleCfgPath() string { } // AnsibleEnv returns environment variables needed for ansible-playbook execution. -func (c *Config) AnsibleEnv() map[string]string { - home, _ := os.UserHomeDir() +func (c *Config) AnsibleEnv() (map[string]string, error) { + home, err := os.UserHomeDir() + if err != nil { + return nil, fmt.Errorf("resolving home directory: %w", err) + } return map[string]string{ "ANSIBLE_CONFIG": c.AnsibleCfgPath(), "ANSIBLE_CACHE_PLUGIN_CONNECTION": filepath.Join(home, ".ansible", "cache", c.Env+"_dreadgoad_facts"), "ANSIBLE_HOST_KEY_CHECKING": "False", "ANSIBLE_RETRY_FILES_ENABLED": "True", "ANSIBLE_GATHER_TIMEOUT": "60", - } + }, nil } // ActiveEnvironment returns the EnvironmentConfig for the currently selected env. @@ -137,12 +173,53 @@ func (c *Config) ResolvedVariantPaths() (source, target string) { return src, tgt } -func findProjectRoot() string { +// ExtensionInventoryTemplate returns the path to an extension's inventory template +// within the Ansible collection (ansible/playbooks/templates/extensions//). +func (c *Config) ExtensionInventoryTemplate(name string) string { + return filepath.Join(c.ProjectRoot, "ansible", "playbooks", "templates", "extensions", name, "inventory.j2") +} + +// ExtensionDataDir returns the path to an extension's data directory +// within the Ansible collection (ansible/playbooks/files/extensions//). +func (c *Config) ExtensionDataDir(name string) string { + return filepath.Join(c.ProjectRoot, "ansible", "playbooks", "files", "extensions", name) +} + +// ExtensionProviderPath returns the path to an extension's provider-specific config +// at the repository root (providers///). +func (c *Config) ExtensionProviderPath(name, provider string) string { + return filepath.Join(c.ProjectRoot, "providers", name, provider) +} + +// IsExtensionCompatible checks if an extension is compatible with the given lab. +func (c *Config) IsExtensionCompatible(name, lab string) bool { + ext, ok := c.Extensions[name] + if !ok { + return false + } + for _, compat := range ext.Compatibility { + if compat == "*" || compat == lab { + return true + } + } + return false +} + +// EnabledExtensionsForEnv returns the enabled extensions for the active environment. +func (c *Config) EnabledExtensionsForEnv() []string { + return c.ActiveEnvironment().EnabledExtensions +} + +func findProjectRoot() (string, error) { + cwd, err := os.Getwd() + if err != nil { + return "", fmt.Errorf("getting working directory: %w", err) + } // Walk up from cwd looking for ansible/ directory - dir, _ := os.Getwd() + dir := cwd for { if _, err := os.Stat(filepath.Join(dir, "ansible")); err == nil { - return dir + return dir, nil } parent := filepath.Dir(dir) if parent == dir { @@ -151,6 +228,5 @@ func findProjectRoot() string { dir = parent } // Fallback to cwd - cwd, _ := os.Getwd() - return cwd + return cwd, nil } diff --git a/cli/internal/config/config_test.go b/cli/internal/config/config_test.go index 57b2c884..eb561840 100644 --- a/cli/internal/config/config_test.go +++ b/cli/internal/config/config_test.go @@ -28,7 +28,10 @@ func TestConfigAnsibleCfgPath(t *testing.T) { func TestConfigAnsibleEnv(t *testing.T) { c := &Config{ProjectRoot: "/opt/goad", Env: "staging"} - env := c.AnsibleEnv() + env, err := c.AnsibleEnv() + if err != nil { + t.Fatalf("AnsibleEnv() returned unexpected error: %v", err) + } if env["ANSIBLE_CONFIG"] != c.AnsibleCfgPath() { t.Errorf("ANSIBLE_CONFIG = %q, want %q", env["ANSIBLE_CONFIG"], c.AnsibleCfgPath()) @@ -75,9 +78,9 @@ func TestDefaultPlaybooks(t *testing.T) { t.Fatal("DefaultPlaybooks is empty") } - // First playbook should be build.yml - if DefaultPlaybooks[0] != "build.yml" { - t.Errorf("first playbook = %q, want %q", DefaultPlaybooks[0], "build.yml") + // First playbook should be network_setup.yml + if DefaultPlaybooks[0] != "network_setup.yml" { + t.Errorf("first playbook = %q, want %q", DefaultPlaybooks[0], "network_setup.yml") } // Last playbook should be vulnerabilities.yml @@ -142,7 +145,10 @@ func TestFindProjectRoot(t *testing.T) { } t.Cleanup(func() { _ = os.Chdir(origDir) }) - got := findProjectRoot() + got, err := findProjectRoot() + if err != nil { + t.Fatalf("findProjectRoot() returned unexpected error: %v", err) + } if got != dir { t.Errorf("findProjectRoot() = %q, want %q", got, dir) } @@ -156,7 +162,10 @@ func TestFindProjectRoot(t *testing.T) { } t.Cleanup(func() { _ = os.Chdir(origDir) }) - got := findProjectRoot() + got, err := findProjectRoot() + if err != nil { + t.Fatalf("findProjectRoot() returned unexpected error: %v", err) + } if got != dir { t.Errorf("findProjectRoot() = %q, want %q", got, dir) } diff --git a/cli/internal/config/defaults.go b/cli/internal/config/defaults.go index 4361ff02..22a5988a 100644 --- a/cli/internal/config/defaults.go +++ b/cli/internal/config/defaults.go @@ -4,6 +4,7 @@ import "github.com/spf13/viper" // DefaultPlaybooks is the ordered list of all GOAD playbooks. var DefaultPlaybooks = []string{ + "network_setup.yml", "build.yml", "ad-servers.yml", "ad-parent_domain.yml", @@ -38,6 +39,46 @@ func setDefaults() { viper.SetDefault("idle_timeout", 1200) viper.SetDefault("log_dir", "") viper.SetDefault("playbooks", DefaultPlaybooks) + // Extension defaults + viper.SetDefault("extensions.elk.description", "Add an ELK stack to the current lab") + viper.SetDefault("extensions.elk.machines", []string{"elk"}) + viper.SetDefault("extensions.elk.compatibility", []string{"*"}) + viper.SetDefault("extensions.elk.impact", "add a linux machine and add a logbeat agent on all windows machines") + viper.SetDefault("extensions.elk.playbook", "ext-elk.yml") + + viper.SetDefault("extensions.exchange.description", "Add an Exchange server to the GOAD lab") + viper.SetDefault("extensions.exchange.machines", []string{"srv01"}) + viper.SetDefault("extensions.exchange.compatibility", []string{"GOAD", "GOAD-Light", "GOAD-Mini"}) + viper.SetDefault("extensions.exchange.impact", "modifies AD schema and adds a server (heavy)") + viper.SetDefault("extensions.exchange.playbook", "ext-exchange.yml") + viper.SetDefault("extensions.exchange.data_dir", "exchange/data") + + viper.SetDefault("extensions.guacamole.description", "Add Apache Guacamole for remote access") + viper.SetDefault("extensions.guacamole.machines", []string{"guacamole"}) + viper.SetDefault("extensions.guacamole.compatibility", []string{"*"}) + viper.SetDefault("extensions.guacamole.impact", "none") + viper.SetDefault("extensions.guacamole.playbook", "ext-guacamole.yml") + + viper.SetDefault("extensions.lx01.description", "Add a Linux machine enrolled to the domain") + viper.SetDefault("extensions.lx01.machines", []string{"lx01"}) + viper.SetDefault("extensions.lx01.compatibility", []string{"GOAD", "GOAD-Light", "GOAD-Mini"}) + viper.SetDefault("extensions.lx01.impact", "none") + viper.SetDefault("extensions.lx01.playbook", "ext-lx01.yml") + viper.SetDefault("extensions.lx01.data_dir", "lx01/data") + + viper.SetDefault("extensions.wazuh.description", "Add the Wazuh EDR into the lab") + viper.SetDefault("extensions.wazuh.machines", []string{"wazuh"}) + viper.SetDefault("extensions.wazuh.compatibility", []string{"*"}) + viper.SetDefault("extensions.wazuh.impact", "add a wazuh machine and agent on all windows machines") + viper.SetDefault("extensions.wazuh.playbook", "ext-wazuh.yml") + + viper.SetDefault("extensions.ws01.description", "Add a hardened workstation into the lab") + viper.SetDefault("extensions.ws01.machines", []string{"ws01"}) + viper.SetDefault("extensions.ws01.compatibility", []string{"GOAD", "GOAD-Light", "GOAD-Mini"}) + viper.SetDefault("extensions.ws01.impact", "AWS uses Windows Server 2019 instead of Windows 10") + viper.SetDefault("extensions.ws01.playbook", "ext-ws01.yml") + viper.SetDefault("extensions.ws01.data_dir", "ws01/data") + viper.SetDefault("environments", map[string]interface{}{ "dev": map[string]interface{}{ "variant": true, diff --git a/cli/main.go b/cli/main.go index 03778f8a..7a26a094 100644 --- a/cli/main.go +++ b/cli/main.go @@ -6,7 +6,15 @@ import ( "github.com/dreadnode/dreadgoad/cmd" ) +// Set via ldflags at build time. +var ( + version = "dev" + commit = "none" + date = "unknown" +) + func main() { + cmd.SetVersionInfo(version, commit, date) if err := cmd.Execute(); err != nil { os.Exit(1) } diff --git a/docs/FQDNs.md b/docs/FQDNs.md new file mode 100644 index 00000000..379c9788 --- /dev/null +++ b/docs/FQDNs.md @@ -0,0 +1,9 @@ +# GOAD Staging FQDNs + +| FQDN | Private IP | Role | Domain | +|---|---|---|---| +| `kingslanding.sevenkingdoms.local` | 10.1.2.238 | DC | sevenkingdoms.local | +| `winterfell.north.sevenkingdoms.local` | 10.1.2.121 | DC | north.sevenkingdoms.local | +| `meereen.essos.local` | 10.1.2.211 | DC | essos.local | +| `castelblack.north.sevenkingdoms.local` | 10.1.2.17 | Server | north.sevenkingdoms.local | +| `braavos.essos.local` | 10.1.2.210 | Server | essos.local | diff --git a/docs/architecture.mmd b/docs/architecture.mmd index 7f4f5c9d..5306505e 100644 --- a/docs/architecture.mmd +++ b/docs/architecture.mmd @@ -7,7 +7,7 @@ graph LR Collection --> Security["Security
8 roles"] Collection --> Settings["Settings
13 roles"] Collection --> ActiveDirectory["Active Directory
21 roles"] - Collection --> ServerRoles["Server Roles
15 roles"] + Collection --> ServerRoles["Server Roles
26 roles"] LAPS -.- LAPS_detail["dc • permissions • server
verify"] SCCM -.- SCCM_detail["config_accounts • config_boundary • config_client_install
config_client_push • config_discovery • config_naa
config_pxe • config_users • install_adk
install_iis • install_mecm • install_prerequisites
install_wsus • pxe"] @@ -15,10 +15,10 @@ graph LR Security -.- Security_detail["dc_audit_sacl • ldap_diagnostic_logging • account_is_sensitive
asr • audit_policy • enable_run_as_ppl
ensure_kb_not_installed • powershell_restrict"] Settings -.- Settings_detail["adjust_rights • admin_password • copy_files
disable_nat_adapter • enable_nat_adapter • gpmc
gpo_remove • hostname • keyboard
no_updates • updates • user_rights
windows_defender"] ActiveDirectory -.- ActiveDirectory_detail["acl • ad • adcs
adcs_templates • child_domain • dc_dns_conditional_forwarder
disable_user • dns_conditional_forwarder • domain_controller
domain_controller_slave • enable_user • gmsa
gmsa_hosts • groups_domains • member_server
move_to_ou • onlyusers • parent_child_dns
password_policy • sync_domains • trusts"] - ServerRoles -.- ServerRoles_detail["common • commonwkstn • dhcp
elk • fix_dns • iis
localusers • logs_windows • mssql
audit • link • reporting
ssms • ps • webdav"] + ServerRoles -.- ServerRoles_detail["common • commonwkstn • dhcp
elk • fix_dns • iis
localusers • logs_windows • mssql
audit • link • reporting
ssms • ps • webdav
add_dns_record • exchange_bot • linux_add_linux_to_domain
linux_guacamole • linux_guacamole_create_connections • linux_tomcat
ludus_exchange • network_discovery • wazuh_agent
wazuh_agent_linux • wazuh_manager"] - Collection --> Playbooks["Playbooks
35 playbooks"] - Playbooks -.- Playbooks_detail["ad • ad-acl • ad-child_domain
ad-data • ad-gmsa • ad-members
ad-parent_domain • ad-relations • ad-servers
ad-trusts • adcs • build
data • dhcp • diagnose-dc01
disable_vagrant • elk • enable_vagrant
fix_dns • fix_trust • interfaces
laps • localusers • main
onlyusers • reboot • sccm-client
sccm-config • sccm-install • sccm-pxe
security • security_logging • servers
vulnerabilities • wait5m"] + Collection --> Playbooks["Playbooks
40 playbooks"] + Playbooks -.- Playbooks_detail["ad • ad-acl • ad-child_domain
ad-data • ad-gmsa • ad-members
ad-parent_domain • ad-relations • ad-servers
ad-trusts • adcs • build
dhcp • diagnose-dc01 • disable_vagrant
enable_vagrant • ext-elk • ext-exchange
ext-guacamole • ext-lx01 • ext-wazuh
ext-ws01 • fix_dns • fix_trust
interfaces • laps • localusers
main • network_setup • onlyusers
reboot • sccm-client • sccm-config
sccm-install • sccm-pxe • security
security_logging • servers • vulnerabilities
wait5m"] style Collection fill:#4a9eff,stroke:#2d7cd4,color:#fff,font-weight:bold style Playbooks fill:#7f8c8d,stroke:#6c7a7d,color:#fff diff --git a/docs/architecture.svg b/docs/architecture.svg index bcb3ed2f..fc43e144 100644 --- a/docs/architecture.svg +++ b/docs/architecture.svg @@ -1 +1 @@ -

dreadnode.goad
Ansible Collection

LAPS
4 roles

SCCM
14 roles

Vulnerabilities
20 roles

Security
8 roles

Settings
13 roles

Active Directory
21 roles

Server Roles
15 roles

dc • permissions • server
verify

config_accounts • config_boundary • config_client_install
config_client_push • config_discovery • config_naa
config_pxe • config_users • install_adk
install_iis • install_mecm • install_prerequisites
install_wsus • pxe

acls • adcs_templates • administrator_folder
anonymous_enum • autologon • credentials
directory • disable_firewall • enable_credssp_client
enable_credssp_server • enable_llmnr • enable_nbt_ns
files • mssql • ntlmdowngrade
openshares • permissions • schedule
shares • smbv1

dc_audit_sacl • ldap_diagnostic_logging • account_is_sensitive
asr • audit_policy • enable_run_as_ppl
ensure_kb_not_installed • powershell_restrict

adjust_rights • admin_password • copy_files
disable_nat_adapter • enable_nat_adapter • gpmc
gpo_remove • hostname • keyboard
no_updates • updates • user_rights
windows_defender

acl • ad • adcs
adcs_templates • child_domain • dc_dns_conditional_forwarder
disable_user • dns_conditional_forwarder • domain_controller
domain_controller_slave • enable_user • gmsa
gmsa_hosts • groups_domains • member_server
move_to_ou • onlyusers • parent_child_dns
password_policy • sync_domains • trusts

common • commonwkstn • dhcp
elk • fix_dns • iis
localusers • logs_windows • mssql
audit • link • reporting
ssms • ps • webdav

Playbooks
35 playbooks

ad • ad-acl • ad-child_domain
ad-data • ad-gmsa • ad-members
ad-parent_domain • ad-relations • ad-servers
ad-trusts • adcs • build
data • dhcp • diagnose-dc01
disable_vagrant • elk • enable_vagrant
fix_dns • fix_trust • interfaces
laps • localusers • main
onlyusers • reboot • sccm-client
sccm-config • sccm-install • sccm-pxe
security • security_logging • servers
vulnerabilities • wait5m

+

dreadnode.goad
Ansible Collection

LAPS
4 roles

SCCM
14 roles

Vulnerabilities
20 roles

Security
8 roles

Settings
13 roles

Active Directory
21 roles

Server Roles
26 roles

dc • permissions • server
verify

config_accounts • config_boundary • config_client_install
config_client_push • config_discovery • config_naa
config_pxe • config_users • install_adk
install_iis • install_mecm • install_prerequisites
install_wsus • pxe

acls • adcs_templates • administrator_folder
anonymous_enum • autologon • credentials
directory • disable_firewall • enable_credssp_client
enable_credssp_server • enable_llmnr • enable_nbt_ns
files • mssql • ntlmdowngrade
openshares • permissions • schedule
shares • smbv1

dc_audit_sacl • ldap_diagnostic_logging • account_is_sensitive
asr • audit_policy • enable_run_as_ppl
ensure_kb_not_installed • powershell_restrict

adjust_rights • admin_password • copy_files
disable_nat_adapter • enable_nat_adapter • gpmc
gpo_remove • hostname • keyboard
no_updates • updates • user_rights
windows_defender

acl • ad • adcs
adcs_templates • child_domain • dc_dns_conditional_forwarder
disable_user • dns_conditional_forwarder • domain_controller
domain_controller_slave • enable_user • gmsa
gmsa_hosts • groups_domains • member_server
move_to_ou • onlyusers • parent_child_dns
password_policy • sync_domains • trusts

common • commonwkstn • dhcp
elk • fix_dns • iis
localusers • logs_windows • mssql
audit • link • reporting
ssms • ps • webdav
add_dns_record • exchange_bot • linux_add_linux_to_domain
linux_guacamole • linux_guacamole_create_connections • linux_tomcat
ludus_exchange • network_discovery • wazuh_agent
wazuh_agent_linux • wazuh_manager

Playbooks
40 playbooks

ad • ad-acl • ad-child_domain
ad-data • ad-gmsa • ad-members
ad-parent_domain • ad-relations • ad-servers
ad-trusts • adcs • build
dhcp • diagnose-dc01 • disable_vagrant
enable_vagrant • ext-elk • ext-exchange
ext-guacamole • ext-lx01 • ext-wazuh
ext-ws01 • fix_dns • fix_trust
interfaces • laps • localusers
main • network_setup • onlyusers
reboot • sccm-client • sccm-config
sccm-install • sccm-pxe • security
security_logging • servers • vulnerabilities
wait5m

diff --git a/docs/img/dracarys_logo.png b/docs/img/dracarys_logo.png new file mode 100644 index 00000000..177144d4 Binary files /dev/null and b/docs/img/dracarys_logo.png differ diff --git a/dreadgoad.yaml b/dreadgoad.yaml new file mode 100644 index 00000000..88547e4d --- /dev/null +++ b/dreadgoad.yaml @@ -0,0 +1,19 @@ +# DreadGOAD CLI Configuration +env: staging +# region: us-west-2 # Override AWS region (default: from inventory) +debug: false +max_retries: 3 +retry_delay: 30 +idle_timeout: 1200 +# log_dir: ~/.ansible/logs/goad +# project_root: /path/to/DreadGOAD # Auto-detected if omitted + +# Per-environment settings +environments: + dev: + variant: true + variant_source: ad/GOAD + variant_target: ad/GOAD-variant-1 + variant_name: variant-1 + staging: + variant: false diff --git a/goad/goadpath.py b/goad/goadpath.py index 25c8b6dd..8234330e 100644 --- a/goad/goadpath.py +++ b/goad/goadpath.py @@ -127,42 +127,32 @@ def get_instance_path(instance_id): def get_instance_provider_path(instance_id): return GoadPath.get_instance_path(instance_id) + sep + 'provider' - # EXTENSIONS + # EXTENSIONS — Ansible content lives in ansible/playbooks/{templates,files}/extensions/, + # provider configs live in providers/ at project root. @staticmethod - def get_extensions_path(): - return project_path + os.path.sep + 'extensions' + def get_extension_inventory_template(extension_name): + return project_path + sep + 'ansible' + sep + 'playbooks' + sep + 'templates' + sep + 'extensions' + sep + extension_name + sep + 'inventory.j2' @staticmethod - def get_extension_path(extension_name): - """ - :return: /extensions// - """ - return GoadPath.get_extensions_path() + os.path.sep + extension_name + os.path.sep + def get_extension_data_dir(extension_name): + return project_path + sep + 'ansible' + sep + 'playbooks' + sep + 'files' + sep + 'extensions' + sep + extension_name @staticmethod def get_extension_config_file(extension_name): - """ - :return: /extensions// - """ - return GoadPath.get_extensions_path() + os.path.sep + extension_name + os.path.sep + 'extension.json' + return GoadPath.get_extension_data_dir(extension_name) + sep + 'extension.json' + + @staticmethod + def get_extensions_ansible_path(): + return project_path + sep + 'ansible' + sep + 'playbooks' + sep + 'files' + sep + 'extensions' @staticmethod def get_extension_providers_path(extension_name): - """ - :return: /extensions//provider - """ - return GoadPath.get_extension_path(extension_name) + 'providers' + return project_path + sep + 'providers' + sep + extension_name @staticmethod def get_extension_providers_provider_path(extension_name, provider_name): - """ - :return: /extensions//providers// - """ - return GoadPath.get_extension_providers_path(extension_name) + os.path.sep + provider_name + os.path.sep + return GoadPath.get_extension_providers_path(extension_name) + sep + provider_name + sep @staticmethod def get_extension_ansible_path(extension_name): - """ - :return /extensions//ansible - """ - return GoadPath.get_extension_path(extension_name) + 'ansible' + return GoadPath.get_extension_data_dir(extension_name) diff --git a/goad/instance.py b/goad/instance.py index 63ab32ab..64229df4 100644 --- a/goad/instance.py +++ b/goad/instance.py @@ -330,9 +330,9 @@ def _create_extensions_inventory(self): Log.info('Create instance extensions inventory files') for extension in self.extensions: - extension_folder = GoadPath.get_extension_path(extension) - extension_environment = Environment(loader=FileSystemLoader(extension_folder)) - instance_extension_inventory_template = extension_environment.get_template("inventory") + extension_template_dir = os.path.dirname(GoadPath.get_extension_inventory_template(extension)) + extension_environment = Environment(loader=FileSystemLoader(extension_template_dir)) + instance_extension_inventory_template = extension_environment.get_template("inventory.j2") instance_extension_inventory_content = instance_extension_inventory_template.render( lab_name=self.lab_name, ip_range=self.ip_range, diff --git a/goad/labs.py b/goad/labs.py index 13268814..789cbd6c 100644 --- a/goad/labs.py +++ b/goad/labs.py @@ -50,7 +50,7 @@ def _load_providers(self, lab_name, config): self.providers[provider_name] = provider def _load_extensions(self, lab_name): - for extension_name in Utils.list_folders(GoadPath.get_extensions_path()): + for extension_name in Utils.list_folders(GoadPath.get_extensions_ansible_path()): extension = Extension(extension_name) if extension.is_available(lab_name): self.extensions[extension_name] = extension diff --git a/ansible/extensions/elk/providers/aws/linux.tf b/providers/elk/aws/linux.tf similarity index 100% rename from ansible/extensions/elk/providers/aws/linux.tf rename to providers/elk/aws/linux.tf diff --git a/ansible/extensions/elk/providers/azure/linux.tf b/providers/elk/azure/linux.tf similarity index 100% rename from ansible/extensions/elk/providers/azure/linux.tf rename to providers/elk/azure/linux.tf diff --git a/ansible/extensions/elk/providers/ludus/config.yml b/providers/elk/ludus/config.yml similarity index 100% rename from ansible/extensions/elk/providers/ludus/config.yml rename to providers/elk/ludus/config.yml diff --git a/ansible/extensions/elk/providers/virtualbox/Vagrantfile b/providers/elk/virtualbox/Vagrantfile similarity index 100% rename from ansible/extensions/elk/providers/virtualbox/Vagrantfile rename to providers/elk/virtualbox/Vagrantfile diff --git a/ansible/extensions/elk/providers/vmware/Vagrantfile b/providers/elk/vmware/Vagrantfile similarity index 100% rename from ansible/extensions/elk/providers/vmware/Vagrantfile rename to providers/elk/vmware/Vagrantfile diff --git a/ansible/extensions/exchange/providers/aws/windows.tf b/providers/exchange/aws/windows.tf similarity index 100% rename from ansible/extensions/exchange/providers/aws/windows.tf rename to providers/exchange/aws/windows.tf diff --git a/ansible/extensions/exchange/providers/azure/windows.tf b/providers/exchange/azure/windows.tf similarity index 100% rename from ansible/extensions/exchange/providers/azure/windows.tf rename to providers/exchange/azure/windows.tf diff --git a/ansible/extensions/exchange/providers/ludus/config.yml b/providers/exchange/ludus/config.yml similarity index 100% rename from ansible/extensions/exchange/providers/ludus/config.yml rename to providers/exchange/ludus/config.yml diff --git a/ansible/extensions/exchange/providers/proxmox/windows.tf b/providers/exchange/proxmox/windows.tf similarity index 100% rename from ansible/extensions/exchange/providers/proxmox/windows.tf rename to providers/exchange/proxmox/windows.tf diff --git a/ansible/extensions/exchange/providers/virtualbox/Vagrantfile b/providers/exchange/virtualbox/Vagrantfile similarity index 100% rename from ansible/extensions/exchange/providers/virtualbox/Vagrantfile rename to providers/exchange/virtualbox/Vagrantfile diff --git a/ansible/extensions/exchange/providers/vmware/Vagrantfile b/providers/exchange/vmware/Vagrantfile similarity index 100% rename from ansible/extensions/exchange/providers/vmware/Vagrantfile rename to providers/exchange/vmware/Vagrantfile diff --git a/providers/guacamole/aws/linux.tf b/providers/guacamole/aws/linux.tf new file mode 100644 index 00000000..5b52191f --- /dev/null +++ b/providers/guacamole/aws/linux.tf @@ -0,0 +1,9 @@ +"guacamole" = { + name = "guacamole" + linux_sku = "22_04-lts-gen2" + linux_version = "latest" + ami = "ami-04c332520bd9cedb4" + private_ip_address = "{{ip_range}}.52" + password = "sgHvnkThdsXlsd" + size = "t2.medium" # 2cpu / 4GB +} diff --git a/providers/guacamole/azure/linux.tf b/providers/guacamole/azure/linux.tf new file mode 100644 index 00000000..3d6d44de --- /dev/null +++ b/providers/guacamole/azure/linux.tf @@ -0,0 +1,9 @@ +"guacamole" = { + name = "guacamole" + linux_offer = "0001-com-ubuntu-server-jammy" + linux_sku = "22_04-lts-gen2" + linux_version = "latest" + private_ip_address = "{{ip_range}}.52" + password = "sgHvnkThdsXlsd" + size = "Standard_B2s" # 2cpu/4G +} diff --git a/providers/guacamole/ludus/config.yml b/providers/guacamole/ludus/config.yml new file mode 100644 index 00000000..507ea0eb --- /dev/null +++ b/providers/guacamole/ludus/config.yml @@ -0,0 +1,8 @@ + - vm_name: "{{ range_id }}-GUACAMOLE" + hostname: "{{ range_id }}-GUACAMOLE" + template: ubuntu-22.04-x64-server-template + vlan: 10 + ip_last_octet: 52 + ram_gb: 4 + cpus: 2 + linux: true diff --git a/providers/guacamole/proxmox/linux.tf b/providers/guacamole/proxmox/linux.tf new file mode 100644 index 00000000..15b343d0 --- /dev/null +++ b/providers/guacamole/proxmox/linux.tf @@ -0,0 +1,10 @@ +"guacamole" = { + name = "guacamole" + desc = "guacamole GOAD" + cores = 2 + memory = 4096 + clone = "Ubuntu_2204_x64" + dns = "{{ip_range}}.1" + ip = "{{ip_range}}.52/24" + gateway = "{{ip_range}}.1" +} diff --git a/providers/guacamole/virtualbox/Vagrantfile b/providers/guacamole/virtualbox/Vagrantfile new file mode 100644 index 00000000..1273df22 --- /dev/null +++ b/providers/guacamole/virtualbox/Vagrantfile @@ -0,0 +1,10 @@ +boxes.append( + { :name => "{{lab_name}}-GUACAMOLE", + :ip => "{{ip_range}}.52", + :box => "bento/ubuntu-22.04", + :os => "linux", + :cpus => 2, + :mem => 3000, + :forwarded_port => [ {:guest => 22, :host => 2210, :id => "ssh"} ] + } +) diff --git a/providers/guacamole/vmware/Vagrantfile b/providers/guacamole/vmware/Vagrantfile new file mode 100644 index 00000000..1273df22 --- /dev/null +++ b/providers/guacamole/vmware/Vagrantfile @@ -0,0 +1,10 @@ +boxes.append( + { :name => "{{lab_name}}-GUACAMOLE", + :ip => "{{ip_range}}.52", + :box => "bento/ubuntu-22.04", + :os => "linux", + :cpus => 2, + :mem => 3000, + :forwarded_port => [ {:guest => 22, :host => 2210, :id => "ssh"} ] + } +) diff --git a/ansible/extensions/wazuh/providers/aws/linux.tf b/providers/wazuh/aws/linux.tf similarity index 100% rename from ansible/extensions/wazuh/providers/aws/linux.tf rename to providers/wazuh/aws/linux.tf diff --git a/ansible/extensions/wazuh/providers/azure/linux.tf b/providers/wazuh/azure/linux.tf similarity index 100% rename from ansible/extensions/wazuh/providers/azure/linux.tf rename to providers/wazuh/azure/linux.tf diff --git a/ansible/extensions/wazuh/providers/ludus/config.yml b/providers/wazuh/ludus/config.yml similarity index 100% rename from ansible/extensions/wazuh/providers/ludus/config.yml rename to providers/wazuh/ludus/config.yml diff --git a/ansible/extensions/wazuh/providers/virtualbox/Vagrantfile b/providers/wazuh/virtualbox/Vagrantfile similarity index 100% rename from ansible/extensions/wazuh/providers/virtualbox/Vagrantfile rename to providers/wazuh/virtualbox/Vagrantfile diff --git a/ansible/extensions/wazuh/providers/vmware/Vagrantfile b/providers/wazuh/vmware/Vagrantfile similarity index 100% rename from ansible/extensions/wazuh/providers/vmware/Vagrantfile rename to providers/wazuh/vmware/Vagrantfile diff --git a/ansible/extensions/ws01/providers/aws/windows.tf b/providers/ws01/aws/windows.tf similarity index 100% rename from ansible/extensions/ws01/providers/aws/windows.tf rename to providers/ws01/aws/windows.tf diff --git a/ansible/extensions/ws01/providers/azure/windows.tf b/providers/ws01/azure/windows.tf similarity index 100% rename from ansible/extensions/ws01/providers/azure/windows.tf rename to providers/ws01/azure/windows.tf diff --git a/ansible/extensions/ws01/providers/ludus/config.yml b/providers/ws01/ludus/config.yml similarity index 100% rename from ansible/extensions/ws01/providers/ludus/config.yml rename to providers/ws01/ludus/config.yml diff --git a/ansible/extensions/ws01/providers/proxmox/windows.tf b/providers/ws01/proxmox/windows.tf similarity index 100% rename from ansible/extensions/ws01/providers/proxmox/windows.tf rename to providers/ws01/proxmox/windows.tf diff --git a/ansible/extensions/ws01/providers/proxmox/ws01.tf b/providers/ws01/proxmox/ws01.tf similarity index 100% rename from ansible/extensions/ws01/providers/proxmox/ws01.tf rename to providers/ws01/proxmox/ws01.tf diff --git a/ansible/extensions/ws01/providers/virtualbox/Vagrantfile b/providers/ws01/virtualbox/Vagrantfile similarity index 100% rename from ansible/extensions/ws01/providers/virtualbox/Vagrantfile rename to providers/ws01/virtualbox/Vagrantfile diff --git a/ansible/extensions/ws01/providers/vmware/Vagrantfile b/providers/ws01/vmware/Vagrantfile similarity index 100% rename from ansible/extensions/ws01/providers/vmware/Vagrantfile rename to providers/ws01/vmware/Vagrantfile