From 6c9f41c26c24c7e08730682d107b41ced8e34506 Mon Sep 17 00:00:00 2001 From: Mike Kell Date: Fri, 18 Apr 2025 00:47:11 +0000 Subject: [PATCH] initial comit with first sprint of terraform and ansible files --- .editorconfig | 36 +++ .gitattributes | 34 ++ .gitignore | 301 ++++++++++++++++++ README.md | 0 ansible/group_vars/all.yml | 39 +++ ansible/roles/file_storage/README.md | 30 ++ ansible/roles/file_storage/defaults/main.yml | 5 + ansible/roles/file_storage/handlers/main.yml | 2 + ansible/roles/file_storage/meta/main.yml | 9 + ansible/roles/file_storage/tasks/main.yml | 26 ++ ansible/roles/identity/README.md | 3 + ansible/roles/identity/defaults/main.yml | 6 + ansible/roles/identity/handlers/main.yml | 12 + ansible/roles/identity/meta/main.yml | 12 + ansible/roles/identity/tasks/configure.yml | 35 ++ .../roles/identity/tasks/configure_realm.yml | 5 + ansible/roles/identity/tasks/deploy.yml | 33 ++ .../roles/identity/tasks/enroll_clients.yml | 4 + .../identity/tasks/generate_ca_certs.yml | 4 + .../roles/identity/tasks/install_keycloak.yml | 12 + .../roles/identity/tasks/integrate_sso.yml | 4 + ansible/roles/identity/tasks/main.yml | 21 ++ .../identity/tasks/provision_step_ca.yml | 6 + ansible/roles/identity/tasks/setup_mfa.yml | 4 + ansible/roles/identity/tasks/tailscale.yml | 8 + .../identity/templates/keycloak.service.j2 | 11 + .../identity/templates/step-ca-config.json.j2 | 7 + .../step-ca-provisioner-password.txt.j2 | 1 + .../templates/systemd-step-ca.service.j2 | 10 + ansible/roles/identity/vars/main.yml | 6 + ansible/roles/keycloak_init/README.md | 3 + ansible/roles/keycloak_init/tasks/clients.yml | 6 + ansible/roles/keycloak_init/tasks/groups.yml | 9 + ansible/roles/keycloak_init/tasks/main.yml | 80 +++++ ansible/roles/keycloak_init/tasks/realm.yml | 5 + ansible/roles/keycloak_init/tasks/user.yml | 10 + ansible/roles/monitoring/defaults/main.yml | 4 + ansible/roles/monitoring/handlers/main.yml | 2 + ansible/roles/monitoring/meta/main.yml | 9 + ansible/roles/monitoring/tasks/main.yml | 38 +++ ansible/roles/podman_services/README.md | 31 ++ .../roles/podman_services/defaults/main.yml | 2 + .../roles/podman_services/handlers/main.yml | 2 + ansible/roles/podman_services/meta/main.yml | 9 + .../roles/podman_services/tasks/keycloak.yml | 38 +++ .../roles/podman_services/tasks/mailcow.yml | 38 +++ ansible/roles/podman_services/tasks/main.yml | 12 + .../podman_services/tasks/run_service.yml | 23 ++ .../roles/podman_services/tasks/step_ca.yml | 38 +++ ansible/roles/podman_services/tasks/wazuh.yml | 38 +++ ansible/roles/secure_ubuntu/README.md | 78 +++++ ansible/roles/secure_ubuntu/defaults/main.yml | 2 + ansible/roles/secure_ubuntu/handlers/main.yml | 5 + ansible/roles/secure_ubuntu/meta/main.yml | 9 + ansible/roles/secure_ubuntu/tasks/audit.yml | 12 + ansible/roles/secure_ubuntu/tasks/banners.yml | 25 ++ .../roles/secure_ubuntu/tasks/firewall.yml | 16 + ansible/roles/secure_ubuntu/tasks/main.yml | 15 + .../secure_ubuntu/tasks/password_policy.yml | 12 + ansible/roles/secure_ubuntu/tasks/ssh.yml | 27 ++ ansible/roles/secure_ubuntu/tasks/updates.yml | 12 + ansible/roles/secure_ubuntu/tasks/user.yml | 13 + .../secure_ubuntu/templates/banner.txt.j2 | 10 + ansible/roles/secure_ubuntu/tests/test.yml | 13 + ansible/roles/secure_ubuntu/vars/main.yml | 0 ansible/secure_ubuntu.yml | 6 + ansible/site.yml | 10 + evidence/00_scoping/evidence.md | 15 + evidence/01_identity_access/evidence.md | 15 + .../ansible_run_logs/evidence.md | 15 + evidence/02_system_hardening/evidence.md | 15 + evidence/03_file_sharing/evidence.md | 15 + evidence/04_email/evidence.md | 15 + evidence/05_monitoring/evidence.md | 15 + evidence/06_backups/evidence.md | 15 + evidence/07_policies/evidence.md | 15 + terraform/bootstrap.sh | 8 + terraform/main.tf | 18 ++ terraform/terraform.tfvars.example | 2 + terraform/variables.tf | 10 + 80 files changed, 1521 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 README.md create mode 100644 ansible/group_vars/all.yml create mode 100644 ansible/roles/file_storage/README.md create mode 100644 ansible/roles/file_storage/defaults/main.yml create mode 100644 ansible/roles/file_storage/handlers/main.yml create mode 100644 ansible/roles/file_storage/meta/main.yml create mode 100644 ansible/roles/file_storage/tasks/main.yml create mode 100644 ansible/roles/identity/README.md create mode 100644 ansible/roles/identity/defaults/main.yml create mode 100644 ansible/roles/identity/handlers/main.yml create mode 100644 ansible/roles/identity/meta/main.yml create mode 100644 ansible/roles/identity/tasks/configure.yml create mode 100644 ansible/roles/identity/tasks/configure_realm.yml create mode 100644 ansible/roles/identity/tasks/deploy.yml create mode 100644 ansible/roles/identity/tasks/enroll_clients.yml create mode 100644 ansible/roles/identity/tasks/generate_ca_certs.yml create mode 100644 ansible/roles/identity/tasks/install_keycloak.yml create mode 100644 ansible/roles/identity/tasks/integrate_sso.yml create mode 100644 ansible/roles/identity/tasks/main.yml create mode 100644 ansible/roles/identity/tasks/provision_step_ca.yml create mode 100644 ansible/roles/identity/tasks/setup_mfa.yml create mode 100644 ansible/roles/identity/tasks/tailscale.yml create mode 100644 ansible/roles/identity/templates/keycloak.service.j2 create mode 100644 ansible/roles/identity/templates/step-ca-config.json.j2 create mode 100644 ansible/roles/identity/templates/step-ca-provisioner-password.txt.j2 create mode 100644 ansible/roles/identity/templates/systemd-step-ca.service.j2 create mode 100644 ansible/roles/identity/vars/main.yml create mode 100644 ansible/roles/keycloak_init/README.md create mode 100644 ansible/roles/keycloak_init/tasks/clients.yml create mode 100644 ansible/roles/keycloak_init/tasks/groups.yml create mode 100644 ansible/roles/keycloak_init/tasks/main.yml create mode 100644 ansible/roles/keycloak_init/tasks/realm.yml create mode 100644 ansible/roles/keycloak_init/tasks/user.yml create mode 100644 ansible/roles/monitoring/defaults/main.yml create mode 100644 ansible/roles/monitoring/handlers/main.yml create mode 100644 ansible/roles/monitoring/meta/main.yml create mode 100644 ansible/roles/monitoring/tasks/main.yml create mode 100644 ansible/roles/podman_services/README.md create mode 100644 ansible/roles/podman_services/defaults/main.yml create mode 100644 ansible/roles/podman_services/handlers/main.yml create mode 100644 ansible/roles/podman_services/meta/main.yml create mode 100644 ansible/roles/podman_services/tasks/keycloak.yml create mode 100644 ansible/roles/podman_services/tasks/mailcow.yml create mode 100644 ansible/roles/podman_services/tasks/main.yml create mode 100644 ansible/roles/podman_services/tasks/run_service.yml create mode 100644 ansible/roles/podman_services/tasks/step_ca.yml create mode 100644 ansible/roles/podman_services/tasks/wazuh.yml create mode 100644 ansible/roles/secure_ubuntu/README.md create mode 100644 ansible/roles/secure_ubuntu/defaults/main.yml create mode 100644 ansible/roles/secure_ubuntu/handlers/main.yml create mode 100644 ansible/roles/secure_ubuntu/meta/main.yml create mode 100644 ansible/roles/secure_ubuntu/tasks/audit.yml create mode 100644 ansible/roles/secure_ubuntu/tasks/banners.yml create mode 100644 ansible/roles/secure_ubuntu/tasks/firewall.yml create mode 100644 ansible/roles/secure_ubuntu/tasks/main.yml create mode 100644 ansible/roles/secure_ubuntu/tasks/password_policy.yml create mode 100644 ansible/roles/secure_ubuntu/tasks/ssh.yml create mode 100644 ansible/roles/secure_ubuntu/tasks/updates.yml create mode 100644 ansible/roles/secure_ubuntu/tasks/user.yml create mode 100644 ansible/roles/secure_ubuntu/templates/banner.txt.j2 create mode 100644 ansible/roles/secure_ubuntu/tests/test.yml create mode 100644 ansible/roles/secure_ubuntu/vars/main.yml create mode 100644 ansible/secure_ubuntu.yml create mode 100644 ansible/site.yml create mode 100644 evidence/00_scoping/evidence.md create mode 100644 evidence/01_identity_access/evidence.md create mode 100644 evidence/02_system_hardening/ansible_run_logs/evidence.md create mode 100644 evidence/02_system_hardening/evidence.md create mode 100644 evidence/03_file_sharing/evidence.md create mode 100644 evidence/04_email/evidence.md create mode 100644 evidence/05_monitoring/evidence.md create mode 100644 evidence/06_backups/evidence.md create mode 100644 evidence/07_policies/evidence.md create mode 100644 terraform/bootstrap.sh create mode 100644 terraform/main.tf create mode 100644 terraform/terraform.tfvars.example create mode 100644 terraform/variables.tf diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..139f535 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,36 @@ +# EditorConfig for OpenCMMC Stack +root = true + +# Default settings for all files +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +# Python files +[*.py] +indent_size = 4 + +# YAML / Ansible / Terraform files +[*.yml] +indent_size = 2 +[*.yaml] +indent_size = 2 +[*.tf] +indent_size = 2 + +# Markdown files +[*.md] +trim_trailing_whitespace = false +indent_size = 2 + +# Shell scripts +[*.sh] +indent_size = 2 + +# Mermaid diagrams +[*.mmd] +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f1dd231 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,34 @@ +# Normalize all text files to LF endings +* text=auto eol=lf + +# Markdown, YAML, Terraform, and code files treated as text +*.md text +*.markdown text +*.yml text +*.yaml text +*.tf text +*.tfvars text +*.sh text +*.py text +*.mmd text + +# Treat images and binary files as binary (no diff) +*.svg binary +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.pdf binary +*.zip binary +*.tar.gz binary +*.gz binary + +# GitHub language statistics override (if needed) +docs/** linguist-documentation +*.md linguist-language=Markdown +*.mmd linguist-language=Mermaid + +# Avoid diffs on lock and compiled files +*.lock binary +*.retry binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9d893e6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,301 @@ +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log +crash.*.log + +# Exclude all .tfvars files, which are likely to contain sensitive data, such as +# password, private keys, and other secrets. These should not be part of version +# control as they are data points which are potentially sensitive and subject +# to change depending on the environment. +*.tfvars +*.tfvars.json + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Ignore transient lock info files created by terraform apply +.terraform.tfstate.lock.info + +# Include override files you do wish to add to version control using negated pattern +# !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +# example: *tfplan* + +# Ignore CLI configuration files +.terraformrc +terraform.rc + +*.retry + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +# ———————————————————————— +# MkDocs/Mermaid CLI Additions +# ———————————————————————— + +# Mermaid CLI output (SVGs or temp) +*.svg +*.mmdc-log + +# Mermaid cache (if any future builds generate it) +.mermaid-cache/ + +# ———————————————————————— +# Ansible Molecule Testing +# ———————————————————————— + +molecule/ +**/molecule/*/.tox/ +**/molecule/*/.molecule/ +**/molecule/*/logs/ +**/molecule/*/default/ + +# Lint and report files +*.retry +ansible-lint.log +molecule-report.xml + +# ———————————————————————— +# Ansible Role Packaging +# ———————————————————————— + +*.tar.gz +*.tar +*.zip +*.gz + +# Built or temp roles +roles/**/collections/ +roles/**/.python-version +roles/**/__pycache__/ +roles/**/.pytest_cache/ + +# ———————————————————————— +# SSH/Secrets Safety (add for clarity) +# ———————————————————————— + +# Never commit real keypairs or their fingerprints +id_rsa +id_rsa.pub +*.pem +*.crt +*.key +*.jks + +# Example fallback (should be updated before use) +!example_id_rsa.pub + +# ———————————————————————— +# Terraform Local Logs and CLI Plan Files +# ———————————————————————— + +*.log +*.tfplan + +# Explicitly ignore Terraform's crash diagnostic dirs +.terraform.d/ + +# ———————————————————————— +# General Safety +# ———————————————————————— + +# Lock files generated by CI/test runs +.lockfile +open-cmmc.lock diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml new file mode 100644 index 0000000..6e0dfda --- /dev/null +++ b/ansible/group_vars/all.yml @@ -0,0 +1,39 @@ +--- +# 🔐 Global Access & Identity Settings +default_user: cmmcadmin +default_shell: /bin/bash +ssh_authorized_key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" + +# 🌐 Network & Proxy Settings +nextcloud_port: 8080 +mailcow_port: 443 +keycloak_port: 8081 +tailscale_auth_key: tskey-abc123 +stepca_port: 9000 +wazuh_port: 55000 + +# 📦 Container Images +nextcloud_aio_image: nextcloud/all-in-one:latest +keycloak_image: quay.io/keycloak/keycloak:24.0.2 + +# 🗂️ Data Directories +nextcloud_data_dir: /srv/nextcloud +backup_base_dir: /srv/backups +logs_dir: /var/log/open-cmmc + +# ⚙️ System Users +svc_keycloak: svc_keycloak +svc_mailcow: svc_mailcow +svc_wazuh: svc_wazuh +svc_stepca: svc_stepca + +# 🔄 Backup/Restore +restic_password: changeme-securely +restic_repo: /srv/backups/restic-repo + +# 📛 DNS & Hostname +domain_name: example.cmmc.local +hostname: open-cmmc-gateway + +# 📜 Default Realm for Keycloak +keycloak_realm: OpenCMMC diff --git a/ansible/roles/file_storage/README.md b/ansible/roles/file_storage/README.md new file mode 100644 index 0000000..f612410 --- /dev/null +++ b/ansible/roles/file_storage/README.md @@ -0,0 +1,30 @@ +# file_storage Role + +This Ansible role deploys **Nextcloud All-in-One (AIO)** as the secure file sharing and collaboration solution in the OpenCMMC Stack. + +## Features + +- Pulls and runs the official `nextcloud/all-in-one` container image +- Sets up a persistent storage directory for CUI/FCI content +- Configures container restart and port mapping for access via reverse proxy + +## Variables + +| Variable | Description | Default | +|-----------------------|--------------------------------------------|-----------------------------| +| `nextcloud_aio_image` | Container image for Nextcloud AIO | `nextcloud/all-in-one:latest` | +| `nextcloud_data_dir` | Host volume path for Nextcloud data | `/mnt/ncdata` | +| `nextcloud_port` | Port exposed on the host | `8080` | + +## Example Playbook + +```yaml +- hosts: all + roles: + - role: file_storage +``` + +## Notes + +- Make sure this container is **behind a reverse proxy** (e.g., NGINX or Caddy). +- Configure DNS and TLS externally as needed. diff --git a/ansible/roles/file_storage/defaults/main.yml b/ansible/roles/file_storage/defaults/main.yml new file mode 100644 index 0000000..c17683b --- /dev/null +++ b/ansible/roles/file_storage/defaults/main.yml @@ -0,0 +1,5 @@ +--- +nextcloud_aio_image: "nextcloud/all-in-one:latest" +nextcloud_container_name: "nextcloud-aio-mastercontainer" +nextcloud_data_dir: "/opt/nextcloud/data" +nextcloud_port: 8080 diff --git a/ansible/roles/file_storage/handlers/main.yml b/ansible/roles/file_storage/handlers/main.yml new file mode 100644 index 0000000..d7d6b8b --- /dev/null +++ b/ansible/roles/file_storage/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# Placeholder for handlers (e.g., restart Nextcloud container) diff --git a/ansible/roles/file_storage/meta/main.yml b/ansible/roles/file_storage/meta/main.yml new file mode 100644 index 0000000..6bbbb01 --- /dev/null +++ b/ansible/roles/file_storage/meta/main.yml @@ -0,0 +1,9 @@ +--- +galaxy_info: + role_name: file_storage + author: open-cmmc + description: Deploy secure file collaboration using Nextcloud AIO + license: MIT + min_ansible_version: "2.10" + +dependencies: [] diff --git a/ansible/roles/file_storage/tasks/main.yml b/ansible/roles/file_storage/tasks/main.yml new file mode 100644 index 0000000..7cfa1bc --- /dev/null +++ b/ansible/roles/file_storage/tasks/main.yml @@ -0,0 +1,26 @@ +--- +- name: Create Nextcloud data directory + file: + path: "{{ nextcloud_data_dir }}" + state: directory + owner: root + group: root + mode: "0755" + +- name: Pull Nextcloud AIO container image + containers.podman.podman_image: + name: "{{ nextcloud_aio_image }}" + +- name: Deploy Nextcloud AIO container + containers.podman.podman_container: + name: "{{ nextcloud_container_name }}" + image: "{{ nextcloud_aio_image }}" + state: started + restart_policy: always + ports: + - "{{ nextcloud_port }}:8080" + volumes: + - "{{ nextcloud_data_dir }}:/mnt/ncdata:z" + - "nextcloud_aio_mastercontainer:/mnt/docker-aio-config:z" + env: + NEXTCLOUD_DATADIR: "/mnt/ncdata" diff --git a/ansible/roles/identity/README.md b/ansible/roles/identity/README.md new file mode 100644 index 0000000..8682c74 --- /dev/null +++ b/ansible/roles/identity/README.md @@ -0,0 +1,3 @@ +# Identity Role + +This role installs and configures Keycloak for identity and access management, and Step-CA for internal certificate authority needs in the OpenCMMC Stack. diff --git a/ansible/roles/identity/defaults/main.yml b/ansible/roles/identity/defaults/main.yml new file mode 100644 index 0000000..0dde3b1 --- /dev/null +++ b/ansible/roles/identity/defaults/main.yml @@ -0,0 +1,6 @@ +--- +keycloak_version: "24.0.2" +keycloak_admin_user: "admin" +keycloak_admin_password: "securepassword" +keycloak_realm: "OpenCMMC" +step_ca_provisioner: "admin@example.com" diff --git a/ansible/roles/identity/handlers/main.yml b/ansible/roles/identity/handlers/main.yml new file mode 100644 index 0000000..8d1282b --- /dev/null +++ b/ansible/roles/identity/handlers/main.yml @@ -0,0 +1,12 @@ +--- +- name: Restart keycloak + ansible.builtin.systemd: + name: keycloak + state: restarted + enabled: true + +- name: Restart step-ca + ansible.builtin.systemd: + name: step-ca + state: restarted + enabled: true diff --git a/ansible/roles/identity/meta/main.yml b/ansible/roles/identity/meta/main.yml new file mode 100644 index 0000000..4804226 --- /dev/null +++ b/ansible/roles/identity/meta/main.yml @@ -0,0 +1,12 @@ +--- +galaxy_info: + author: OpenCMMC Team + description: Identity and Access Management (IAM) role with Keycloak and Step-CA. + company: Kell Engineering + license: MIT + min_ansible_version: "2.12" + platforms: + - name: Ubuntu + versions: + - 22.04 +dependencies: [] diff --git a/ansible/roles/identity/tasks/configure.yml b/ansible/roles/identity/tasks/configure.yml new file mode 100644 index 0000000..80c8601 --- /dev/null +++ b/ansible/roles/identity/tasks/configure.yml @@ -0,0 +1,35 @@ +--- +- name: Ensure Keycloak CLI (kcadm.sh) is installed + stat: + path: /opt/keycloak/bin/kcadm.sh + register: kcadm_path + +- name: Install Keycloak CLI if missing + get_url: + url: https://downloads.jboss.org/keycloak/24.0.2/keycloak-24.0.2.zip + dest: /tmp/keycloak.zip + when: not kcadm_path.stat.exists + +- name: Unarchive Keycloak CLI + unarchive: + src: /tmp/keycloak.zip + dest: /opt/ + remote_src: yes + when: not kcadm_path.stat.exists + +- name: Authenticate Keycloak admin CLI session + command: > + /opt/keycloak/bin/kcadm.sh config credentials --server http://localhost:8080/auth + --realm master --user {{ keycloak_admin_user }} --password {{ keycloak_admin_password }} + environment: + KCADM_CONFIG: /opt/keycloak/kcadm.config + +- name: Create OpenCMMC realm + command: /opt/keycloak/bin/kcadm.sh create realms -s realm=OpenCMMC -s enabled=true + +- name: Create groups + loop: + - Access_CUI + - Access_FCI + - Access_Proprietary + command: /opt/keycloak/bin/kcadm.sh create groups -r OpenCMMC -s name="{{ item }}" diff --git a/ansible/roles/identity/tasks/configure_realm.yml b/ansible/roles/identity/tasks/configure_realm.yml new file mode 100644 index 0000000..84f7708 --- /dev/null +++ b/ansible/roles/identity/tasks/configure_realm.yml @@ -0,0 +1,5 @@ +--- +- name: Configure Keycloak realm and groups + ansible.builtin.command: + cmd: "/opt/keycloak/bin/kcadm.sh create realms -s realm={{ keycloak_realm }} -s enabled=true" + when: keycloak_realm is defined diff --git a/ansible/roles/identity/tasks/deploy.yml b/ansible/roles/identity/tasks/deploy.yml new file mode 100644 index 0000000..52d9781 --- /dev/null +++ b/ansible/roles/identity/tasks/deploy.yml @@ -0,0 +1,33 @@ +--- +- name: Pull Keycloak image + containers.podman.podman_image: + name: quay.io/keycloak/keycloak:24.0.2 + +- name: Create systemd service user + user: + name: svc_keycloak + shell: /usr/sbin/nologin + system: yes + create_home: no + +- name: Create Keycloak config directory + file: + path: /opt/services/keycloak + state: directory + owner: svc_keycloak + group: svc_keycloak + mode: '0755' + +- name: Deploy Keycloak container + containers.podman.podman_container: + name: keycloak + image: quay.io/keycloak/keycloak:24.0.2 + state: started + restart_policy: always + user: svc_keycloak + env: + KEYCLOAK_ADMIN: "{{ keycloak_admin_user }}" + KEYCLOAK_ADMIN_PASSWORD: "{{ keycloak_admin_password }}" + ports: + - "8080:8080" + command: "start --optimized" diff --git a/ansible/roles/identity/tasks/enroll_clients.yml b/ansible/roles/identity/tasks/enroll_clients.yml new file mode 100644 index 0000000..de731d9 --- /dev/null +++ b/ansible/roles/identity/tasks/enroll_clients.yml @@ -0,0 +1,4 @@ +--- +- name: Enroll clients in Step-CA + ansible.builtin.debug: + msg: "Provision client certificates using Step-CA" diff --git a/ansible/roles/identity/tasks/generate_ca_certs.yml b/ansible/roles/identity/tasks/generate_ca_certs.yml new file mode 100644 index 0000000..1070ddc --- /dev/null +++ b/ansible/roles/identity/tasks/generate_ca_certs.yml @@ -0,0 +1,4 @@ +--- +- name: Generate CA certs using step-ca CLI + ansible.builtin.debug: + msg: "Running step ca init with preconfigured values" diff --git a/ansible/roles/identity/tasks/install_keycloak.yml b/ansible/roles/identity/tasks/install_keycloak.yml new file mode 100644 index 0000000..92fbf81 --- /dev/null +++ b/ansible/roles/identity/tasks/install_keycloak.yml @@ -0,0 +1,12 @@ +--- +- name: Install Keycloak container + containers.podman.podman_container: + name: keycloak + image: quay.io/keycloak/keycloak:{{ keycloak_version }} + state: started + restart_policy: always + env: + KEYCLOAK_ADMIN: "{{ keycloak_admin_user }}" + KEYCLOAK_ADMIN_PASSWORD: "{{ keycloak_admin_password }}" + ports: + - "8080:8080" diff --git a/ansible/roles/identity/tasks/integrate_sso.yml b/ansible/roles/identity/tasks/integrate_sso.yml new file mode 100644 index 0000000..bba4461 --- /dev/null +++ b/ansible/roles/identity/tasks/integrate_sso.yml @@ -0,0 +1,4 @@ +--- +- name: Setup OIDC clients for SSO integration + ansible.builtin.debug: + msg: "Configure OIDC clients for Mailcow, Gitea and SAML for Nextcloud" diff --git a/ansible/roles/identity/tasks/main.yml b/ansible/roles/identity/tasks/main.yml new file mode 100644 index 0000000..6185e12 --- /dev/null +++ b/ansible/roles/identity/tasks/main.yml @@ -0,0 +1,21 @@ +--- +- name: Include tasks to install and configure Keycloak + include_tasks: install_keycloak.yml + +- name: Include tasks to configure realm and users + include_tasks: configure_realm.yml + +- name: Include tasks to setup SSO integration + include_tasks: integrate_sso.yml + +- name: Include tasks to configure MFA policies + include_tasks: setup_mfa.yml + +- name: Include tasks to provision Step-CA + include_tasks: provision_step_ca.yml + +- name: Include tasks to enroll clients in Step-CA + include_tasks: enroll_clients.yml + +- name: Include tasks to generate CA certificates + include_tasks: generate_ca_certs.yml diff --git a/ansible/roles/identity/tasks/provision_step_ca.yml b/ansible/roles/identity/tasks/provision_step_ca.yml new file mode 100644 index 0000000..f6b7822 --- /dev/null +++ b/ansible/roles/identity/tasks/provision_step_ca.yml @@ -0,0 +1,6 @@ +--- +- name: Install step-ca and create systemd service + ansible.builtin.copy: + src: systemd-step-ca.service.j2 + dest: /etc/systemd/system/step-ca.service + notify: Restart step-ca diff --git a/ansible/roles/identity/tasks/setup_mfa.yml b/ansible/roles/identity/tasks/setup_mfa.yml new file mode 100644 index 0000000..3b88ee7 --- /dev/null +++ b/ansible/roles/identity/tasks/setup_mfa.yml @@ -0,0 +1,4 @@ +--- +- name: Enable MFA for users + ansible.builtin.debug: + msg: "Enabling TOTP in Keycloak authentication flow" diff --git a/ansible/roles/identity/tasks/tailscale.yml b/ansible/roles/identity/tasks/tailscale.yml new file mode 100644 index 0000000..564cb6c --- /dev/null +++ b/ansible/roles/identity/tasks/tailscale.yml @@ -0,0 +1,8 @@ +--- +- name: Install Tailscale + shell: curl -fsSL https://tailscale.com/install.sh | sh + args: + creates: /usr/bin/tailscale + +- name: Bring up Tailscale interface + command: tailscale up --authkey {{ tailscale_authkey }} diff --git a/ansible/roles/identity/templates/keycloak.service.j2 b/ansible/roles/identity/templates/keycloak.service.j2 new file mode 100644 index 0000000..56c34d6 --- /dev/null +++ b/ansible/roles/identity/templates/keycloak.service.j2 @@ -0,0 +1,11 @@ +[Unit] +Description=Keycloak Server +After=network.target + +[Service] +ExecStart=/usr/bin/podman start -a keycloak +ExecStop=/usr/bin/podman stop -t 10 keycloak +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/ansible/roles/identity/templates/step-ca-config.json.j2 b/ansible/roles/identity/templates/step-ca-config.json.j2 new file mode 100644 index 0000000..17d2a45 --- /dev/null +++ b/ansible/roles/identity/templates/step-ca-config.json.j2 @@ -0,0 +1,7 @@ +{ + "address": ":9000", + "dnsNames": ["stepca.{{ inventory_hostname }}"], + "root": "step-ca-root.crt", + "crt": "step-ca-intermediate.crt", + "key": "step-ca-key.pem" +} diff --git a/ansible/roles/identity/templates/step-ca-provisioner-password.txt.j2 b/ansible/roles/identity/templates/step-ca-provisioner-password.txt.j2 new file mode 100644 index 0000000..01f99db --- /dev/null +++ b/ansible/roles/identity/templates/step-ca-provisioner-password.txt.j2 @@ -0,0 +1 @@ +your-secure-password diff --git a/ansible/roles/identity/templates/systemd-step-ca.service.j2 b/ansible/roles/identity/templates/systemd-step-ca.service.j2 new file mode 100644 index 0000000..aab8408 --- /dev/null +++ b/ansible/roles/identity/templates/systemd-step-ca.service.j2 @@ -0,0 +1,10 @@ +[Unit] +Description=Step CA +After=network.target + +[Service] +ExecStart=/usr/local/bin/step-ca /etc/step-ca/config/step-ca-config.json +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/ansible/roles/identity/vars/main.yml b/ansible/roles/identity/vars/main.yml new file mode 100644 index 0000000..6279509 --- /dev/null +++ b/ansible/roles/identity/vars/main.yml @@ -0,0 +1,6 @@ +--- +realm_clients: + - name: nextcloud + protocol: saml + - name: mailcow + protocol: oidc diff --git a/ansible/roles/keycloak_init/README.md b/ansible/roles/keycloak_init/README.md new file mode 100644 index 0000000..7a96b8b --- /dev/null +++ b/ansible/roles/keycloak_init/README.md @@ -0,0 +1,3 @@ +# Ansible Role: keycloak_init + +This role initializes a Keycloak instance with a predefined realm, groups, and clients for the OpenCMMC Stack. diff --git a/ansible/roles/keycloak_init/tasks/clients.yml b/ansible/roles/keycloak_init/tasks/clients.yml new file mode 100644 index 0000000..8673c15 --- /dev/null +++ b/ansible/roles/keycloak_init/tasks/clients.yml @@ -0,0 +1,6 @@ +--- +- name: Register SAML client for Nextcloud + shell: '/opt/keycloak/bin/kcadm.sh create clients -r OpenCMMC -s clientId=nextcloud-aio -s protocol=saml -s enabled=true' + +- name: Register OIDC client for Mailcow + shell: '/opt/keycloak/bin/kcadm.sh create clients -r OpenCMMC -s clientId=mailcow -s protocol=openid-connect -s enabled=true' diff --git a/ansible/roles/keycloak_init/tasks/groups.yml b/ansible/roles/keycloak_init/tasks/groups.yml new file mode 100644 index 0000000..2bcdb30 --- /dev/null +++ b/ansible/roles/keycloak_init/tasks/groups.yml @@ -0,0 +1,9 @@ +--- +- name: Create Keycloak Groups + shell: '/opt/keycloak/bin/kcadm.sh create groups -r OpenCMMC -s name=Access_CUI' + +- name: Create FCI Group + shell: '/opt/keycloak/bin/kcadm.sh create groups -r OpenCMMC -s name=Access_FCI' + +- name: Create Proprietary Group + shell: '/opt/keycloak/bin/kcadm.sh create groups -r OpenCMMC -s name=Access_Proprietary' diff --git a/ansible/roles/keycloak_init/tasks/main.yml b/ansible/roles/keycloak_init/tasks/main.yml new file mode 100644 index 0000000..efea415 --- /dev/null +++ b/ansible/roles/keycloak_init/tasks/main.yml @@ -0,0 +1,80 @@ +--- +- name: Check if Keycloak is already installed + stat: + path: /opt/keycloak/bin/kcadm.sh + register: keycloak_installed + +- name: Download Keycloak if not already present + unarchive: + src: "https://github.com/keycloak/keycloak/releases/download/{{ keycloak_version }}/keycloak-{{ keycloak_version }}.tar.gz" + dest: /opt/ + remote_src: yes + when: not keycloak_installed.stat.exists + +- name: Rename keycloak directory + command: mv /opt/keycloak-{{ keycloak_version }} /opt/keycloak + args: + creates: /opt/keycloak/bin/kcadm.sh + when: not keycloak_installed.stat.exists + +- name: Set executable permissions on kcadm.sh + file: + path: /opt/keycloak/bin/kcadm.sh + mode: '0755' + when: not keycloak_installed.stat.exists + +- name: Log in to Keycloak Admin CLI + command: > + /opt/keycloak/bin/kcadm.sh config credentials + --server http://localhost:8080 + --realm master + --user {{ keycloak_admin_user }} + --password {{ keycloak_admin_password }} + environment: + KC_HOME: /opt/keycloak + register: kcadm_login + changed_when: false + +- name: Create OpenCMMC realm + command: > + /opt/keycloak/bin/kcadm.sh create realms + -s realm=OpenCMMC -s enabled=true + environment: + KC_HOME: /opt/keycloak + when: kcadm_login is succeeded + +- name: Create groups + loop: + - Access_CUI + - Access_FCI + - Access_Proprietary + command: > + /opt/keycloak/bin/kcadm.sh create groups -r OpenCMMC -s name={{ item }} + environment: + KC_HOME: /opt/keycloak + when: kcadm_login is succeeded + +- name: Create OIDC client for Mailcow + command: > + /opt/keycloak/bin/kcadm.sh create clients -r OpenCMMC + -s clientId=mailcow + -s enabled=true + -s protocol=openid-connect + -s publicClient=false + -s 'redirectUris=["https://mail.yourdomain.com/*"]' + environment: + KC_HOME: /opt/keycloak + when: kcadm_login is succeeded + +- name: Create SAML client for Nextcloud + command: > + /opt/keycloak/bin/kcadm.sh create clients -r OpenCMMC + -s clientId=nextcloud + -s enabled=true + -s protocol=saml + -s 'redirectUris=["https://nextcloud.yourdomain.com/*"]' + -s 'attributes.saml.assertion.signature=true' + -s 'attributes.saml.force.post.binding=true' + environment: + KC_HOME: /opt/keycloak + when: kcadm_login is succeeded diff --git a/ansible/roles/keycloak_init/tasks/realm.yml b/ansible/roles/keycloak_init/tasks/realm.yml new file mode 100644 index 0000000..5368c58 --- /dev/null +++ b/ansible/roles/keycloak_init/tasks/realm.yml @@ -0,0 +1,5 @@ +--- +- name: Create Keycloak Realm + shell: '/opt/keycloak/bin/kcadm.sh create realms -s realm=OpenCMMC -s enabled=true' + args: + creates: /opt/keycloak/data/realms/OpenCMMC diff --git a/ansible/roles/keycloak_init/tasks/user.yml b/ansible/roles/keycloak_init/tasks/user.yml new file mode 100644 index 0000000..a9af48e --- /dev/null +++ b/ansible/roles/keycloak_init/tasks/user.yml @@ -0,0 +1,10 @@ +--- +- name: Ensure kcadm.sh is present + stat: + path: /opt/keycloak/bin/kcadm.sh + register: kcadm_path + +- name: Install unzip (required for kcadm) + apt: + name: unzip + state: present diff --git a/ansible/roles/monitoring/defaults/main.yml b/ansible/roles/monitoring/defaults/main.yml new file mode 100644 index 0000000..8dd65b5 --- /dev/null +++ b/ansible/roles/monitoring/defaults/main.yml @@ -0,0 +1,4 @@ +--- +wazuh_container_name: "wazuh-manager" +wazuh_image: "wazuh/wazuh:latest" +wazuh_config_path: "/opt/wazuh/config" diff --git a/ansible/roles/monitoring/handlers/main.yml b/ansible/roles/monitoring/handlers/main.yml new file mode 100644 index 0000000..dcda657 --- /dev/null +++ b/ansible/roles/monitoring/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# Reserved for any future log restart triggers or alert changes diff --git a/ansible/roles/monitoring/meta/main.yml b/ansible/roles/monitoring/meta/main.yml new file mode 100644 index 0000000..f517d6e --- /dev/null +++ b/ansible/roles/monitoring/meta/main.yml @@ -0,0 +1,9 @@ +--- +galaxy_info: + role_name: monitoring + author: open-cmmc + description: Centralized security logging and SIEM services using Wazuh and auditd + license: MIT + min_ansible_version: "2.10" + +dependencies: [] diff --git a/ansible/roles/monitoring/tasks/main.yml b/ansible/roles/monitoring/tasks/main.yml new file mode 100644 index 0000000..df7bace --- /dev/null +++ b/ansible/roles/monitoring/tasks/main.yml @@ -0,0 +1,38 @@ +--- +- name: Pull Wazuh container image + containers.podman.podman_image: + name: wazuh/wazuh:latest + +- name: Create Wazuh configuration directory + file: + path: "/opt/wazuh/config" + state: directory + owner: root + group: root + mode: "0755" + +- name: Deploy Wazuh container + containers.podman.podman_container: + name: wazuh-manager + image: wazuh/wazuh:latest + state: started + restart_policy: always + ports: + - "1514:1514/udp" + - "1515:1515" + - "55000:55000" + volumes: + - "/opt/wazuh/config:/var/ossec/config:z" + +- name: Ensure auditd is installed + apt: + name: auditd + state: present + update_cache: true + become: true + +- name: Ensure auditd service is enabled and started + service: + name: auditd + state: started + enabled: true diff --git a/ansible/roles/podman_services/README.md b/ansible/roles/podman_services/README.md new file mode 100644 index 0000000..8d718ee --- /dev/null +++ b/ansible/roles/podman_services/README.md @@ -0,0 +1,31 @@ +# Podman Services Role + +This Ansible role provisions and manages containerized infrastructure components (excluding Nextcloud AIO) using **Podman**. It includes configurations for Wazuh, Mailcow, Redis, and more. + +## Variables + +```yaml +podman_services: + - name: redis + image: redis:7 + state: started + restart_policy: always + ports: [] + volumes: [] + env: {} +``` + +## Usage + +Include in your playbook: + +```yaml +- hosts: all + roles: + - role: podman_services +``` + +## Notes + +- Only non-root Podman containers are supported +- Customize via `podman_services` variable in `defaults/main.yml` diff --git a/ansible/roles/podman_services/defaults/main.yml b/ansible/roles/podman_services/defaults/main.yml new file mode 100644 index 0000000..e46d205 --- /dev/null +++ b/ansible/roles/podman_services/defaults/main.yml @@ -0,0 +1,2 @@ +--- +podman_services: [] diff --git a/ansible/roles/podman_services/handlers/main.yml b/ansible/roles/podman_services/handlers/main.yml new file mode 100644 index 0000000..d2fcb30 --- /dev/null +++ b/ansible/roles/podman_services/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# Reserved for future restarts diff --git a/ansible/roles/podman_services/meta/main.yml b/ansible/roles/podman_services/meta/main.yml new file mode 100644 index 0000000..23a8971 --- /dev/null +++ b/ansible/roles/podman_services/meta/main.yml @@ -0,0 +1,9 @@ +--- +galaxy_info: + role_name: podman_services + author: open-cmmc + description: Modular deployment of rootless Podman containers + license: MIT + min_ansible_version: "2.10" + +dependencies: [] diff --git a/ansible/roles/podman_services/tasks/keycloak.yml b/ansible/roles/podman_services/tasks/keycloak.yml new file mode 100644 index 0000000..a14bb54 --- /dev/null +++ b/ansible/roles/podman_services/tasks/keycloak.yml @@ -0,0 +1,38 @@ +--- +- name: Deploy keycloak container with Podman + containers.podman.podman_container: + name: keycloak + image: "{{ keycloak_image }}" + state: started + restart_policy: always + volumes: + - "{{ keycloak_data_dir }}:/data:z" + env: + CONFIG_PATH: "/data/config" + +- name: Ensure systemd service is enabled for keycloak + copy: + dest: "/etc/systemd/system/podman-keycloak.service" + content: | + [Unit] + Description=Podman container for keycloak + Wants=network.target + After=network.target + + [Service] + ExecStart=/usr/bin/podman start -a keycloak + ExecStop=/usr/bin/podman stop -t 10 keycloak + Restart=always + + [Install] + WantedBy=multi-user.target + owner: root + group: root + mode: "0644" + +- name: Reload systemd and enable service for keycloak + systemd: + daemon_reload: yes + name: podman-keycloak.service + enabled: yes + state: started diff --git a/ansible/roles/podman_services/tasks/mailcow.yml b/ansible/roles/podman_services/tasks/mailcow.yml new file mode 100644 index 0000000..984ebc0 --- /dev/null +++ b/ansible/roles/podman_services/tasks/mailcow.yml @@ -0,0 +1,38 @@ +--- +- name: Deploy mailcow container with Podman + containers.podman.podman_container: + name: mailcow + image: "{{ mailcow_image }}" + state: started + restart_policy: always + volumes: + - "{{ mailcow_data_dir }}:/data:z" + env: + CONFIG_PATH: "/data/config" + +- name: Ensure systemd service is enabled for mailcow + copy: + dest: "/etc/systemd/system/podman-mailcow.service" + content: | + [Unit] + Description=Podman container for mailcow + Wants=network.target + After=network.target + + [Service] + ExecStart=/usr/bin/podman start -a mailcow + ExecStop=/usr/bin/podman stop -t 10 mailcow + Restart=always + + [Install] + WantedBy=multi-user.target + owner: root + group: root + mode: "0644" + +- name: Reload systemd and enable service for mailcow + systemd: + daemon_reload: yes + name: podman-mailcow.service + enabled: yes + state: started diff --git a/ansible/roles/podman_services/tasks/main.yml b/ansible/roles/podman_services/tasks/main.yml new file mode 100644 index 0000000..a06490a --- /dev/null +++ b/ansible/roles/podman_services/tasks/main.yml @@ -0,0 +1,12 @@ +--- +- name: Ensure Podman is installed + apt: + name: podman + state: present + become: true + +- name: Pull and run Podman services + include_tasks: run_service.yml + loop: "{{ podman_services }}" + loop_control: + loop_var: service diff --git a/ansible/roles/podman_services/tasks/run_service.yml b/ansible/roles/podman_services/tasks/run_service.yml new file mode 100644 index 0000000..5c25e82 --- /dev/null +++ b/ansible/roles/podman_services/tasks/run_service.yml @@ -0,0 +1,23 @@ +--- +- name: Pull image for {{ service.name }} + containers.podman.podman_image: + name: "{{ service.image }}" + +- name: Create data directory for {{ service.name }} + file: + path: "{{ service.data_dir }}" + state: directory + owner: "{{ service.user | default('root') }}" + group: "{{ service.group | default('root') }}" + mode: "0755" + +- name: Run {{ service.name }} container + containers.podman.podman_container: + name: "{{ service.name }}" + image: "{{ service.image }}" + state: started + restart_policy: always + ports: "{{ service.ports | default(omit) }}" + env: "{{ service.env | default({}) }}" + volumes: "{{ service.volumes | default([]) }}" + user: "{{ service.user | default(omit) }}" diff --git a/ansible/roles/podman_services/tasks/step_ca.yml b/ansible/roles/podman_services/tasks/step_ca.yml new file mode 100644 index 0000000..157c217 --- /dev/null +++ b/ansible/roles/podman_services/tasks/step_ca.yml @@ -0,0 +1,38 @@ +--- +- name: Deploy step_ca container with Podman + containers.podman.podman_container: + name: step_ca + image: "{{ step_ca_image }}" + state: started + restart_policy: always + volumes: + - "{{ step_ca_data_dir }}:/data:z" + env: + CONFIG_PATH: "/data/config" + +- name: Ensure systemd service is enabled for step_ca + copy: + dest: "/etc/systemd/system/podman-step_ca.service" + content: | + [Unit] + Description=Podman container for step_ca + Wants=network.target + After=network.target + + [Service] + ExecStart=/usr/bin/podman start -a step_ca + ExecStop=/usr/bin/podman stop -t 10 step_ca + Restart=always + + [Install] + WantedBy=multi-user.target + owner: root + group: root + mode: "0644" + +- name: Reload systemd and enable service for step_ca + systemd: + daemon_reload: yes + name: podman-step_ca.service + enabled: yes + state: started diff --git a/ansible/roles/podman_services/tasks/wazuh.yml b/ansible/roles/podman_services/tasks/wazuh.yml new file mode 100644 index 0000000..9eb9d43 --- /dev/null +++ b/ansible/roles/podman_services/tasks/wazuh.yml @@ -0,0 +1,38 @@ +--- +- name: Deploy wazuh container with Podman + containers.podman.podman_container: + name: wazuh + image: "{{ wazuh_image }}" + state: started + restart_policy: always + volumes: + - "{{ wazuh_data_dir }}:/data:z" + env: + CONFIG_PATH: "/data/config" + +- name: Ensure systemd service is enabled for wazuh + copy: + dest: "/etc/systemd/system/podman-wazuh.service" + content: | + [Unit] + Description=Podman container for wazuh + Wants=network.target + After=network.target + + [Service] + ExecStart=/usr/bin/podman start -a wazuh + ExecStop=/usr/bin/podman stop -t 10 wazuh + Restart=always + + [Install] + WantedBy=multi-user.target + owner: root + group: root + mode: "0644" + +- name: Reload systemd and enable service for wazuh + systemd: + daemon_reload: yes + name: podman-wazuh.service + enabled: yes + state: started diff --git a/ansible/roles/secure_ubuntu/README.md b/ansible/roles/secure_ubuntu/README.md new file mode 100644 index 0000000..761c0a3 --- /dev/null +++ b/ansible/roles/secure_ubuntu/README.md @@ -0,0 +1,78 @@ +# 🔐 Ansible Role: secure_ubuntu + +Harden an Ubuntu 22.04 LTS host to meet **CMMC Level 2** compliance requirements using a modular, auditable Ansible role. + +This role configures: +- SSH and login security +- Non-root administrative user +- System auditing and file integrity monitoring +- UFW firewall +- Secure banners for compliance +- Automatic updates and password policies + +## ✅ CMMC Practices Addressed + +| Domain | Practice | Description | +|--------|---------------|---------------------------------------------------------| +| AC | AC.1.001 | Limit system access to authorized users | +| AC | AC.3.017 | Display system use notifications (login banner) | +| CM | CM.2.062 | Employ security configuration baseline | +| SI | SI.1.210 | Identify unauthorized use of systems | +| SI | SI.3.219 | Detect and report unauthorized changes to software | + +## 📦 Requirements + +- Ubuntu 22.04 LTS +- Ansible >= 2.11 + +## 🚀 Role Variables + +```yaml +secure_user: cmmcadmin +ssh_pubkey_path: "~/.ssh/id_rsa.pub" +``` + +> Set `ssh_pubkey_path` to the local path of the public key to be authorized for `secure_user`. + +## 📁 Example Playbook + +```yaml +- name: Apply CMMC hardening baseline + hosts: all + become: yes + roles: + - role: secure_ubuntu + vars: + secure_user: cmmcadmin + ssh_pubkey_path: "~/.ssh/id_rsa.pub" +``` + +## 📁 File Structure + +``` +roles/ +└── secure_ubuntu/ + ├── defaults/ + │ └── main.yml + ├── meta/ + │ └── main.yml + ├── tasks/ + │ ├── main.yml + │ ├── ssh.yml + │ ├── user.yml + │ ├── firewall.yml + │ ├── audit_aide.yml + │ ├── banner.yml + │ ├── updates.yml + │ └── password_policy.yml + └── README.md +``` + +## 🔒 License + +MIT License + +## 🧠 Author + +Maintained by **Kell Engineering** +https://github.com/mtkell/open-cmmc-stack diff --git a/ansible/roles/secure_ubuntu/defaults/main.yml b/ansible/roles/secure_ubuntu/defaults/main.yml new file mode 100644 index 0000000..ce75e21 --- /dev/null +++ b/ansible/roles/secure_ubuntu/defaults/main.yml @@ -0,0 +1,2 @@ +--- +admin_user: cmmcadmin diff --git a/ansible/roles/secure_ubuntu/handlers/main.yml b/ansible/roles/secure_ubuntu/handlers/main.yml new file mode 100644 index 0000000..4d1a954 --- /dev/null +++ b/ansible/roles/secure_ubuntu/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: Restart SSH + service: + name: ssh + state: restarted diff --git a/ansible/roles/secure_ubuntu/meta/main.yml b/ansible/roles/secure_ubuntu/meta/main.yml new file mode 100644 index 0000000..b44c379 --- /dev/null +++ b/ansible/roles/secure_ubuntu/meta/main.yml @@ -0,0 +1,9 @@ +--- +galaxy_info: + role_name: secure_ubuntu + author: open-cmmc + description: Harden Ubuntu system for CMMC Level 2 + license: MIT + min_ansible_version: "2.10" + +dependencies: [] diff --git a/ansible/roles/secure_ubuntu/tasks/audit.yml b/ansible/roles/secure_ubuntu/tasks/audit.yml new file mode 100644 index 0000000..621f371 --- /dev/null +++ b/ansible/roles/secure_ubuntu/tasks/audit.yml @@ -0,0 +1,12 @@ +--- +- name: Install audit and integrity tools + apt: + name: + - auditd + - aide + state: present + +- name: Initialize AIDE database + command: aideinit + args: + creates: /var/lib/aide/aide.db.gz diff --git a/ansible/roles/secure_ubuntu/tasks/banners.yml b/ansible/roles/secure_ubuntu/tasks/banners.yml new file mode 100644 index 0000000..51e1684 --- /dev/null +++ b/ansible/roles/secure_ubuntu/tasks/banners.yml @@ -0,0 +1,25 @@ +--- +- name: Deploy system login banner + template: + src: banner.txt.j2 + dest: /etc/banner.txt + mode: '0644' + +- name: Apply banner for SSH + lineinfile: + path: /etc/ssh/sshd_config + regexp: '^Banner' + line: 'Banner /etc/banner.txt' + notify: Restart SSH + +- name: Apply banner for console (TTY) + copy: + src: /etc/banner.txt + dest: /etc/issue + mode: '0644' + +- name: Apply banner for pre-login + copy: + src: /etc/banner.txt + dest: /etc/issue.net + mode: '0644' diff --git a/ansible/roles/secure_ubuntu/tasks/firewall.yml b/ansible/roles/secure_ubuntu/tasks/firewall.yml new file mode 100644 index 0000000..1a1dd4c --- /dev/null +++ b/ansible/roles/secure_ubuntu/tasks/firewall.yml @@ -0,0 +1,16 @@ +--- +- name: Install UFW firewall + apt: + name: ufw + state: present + +- name: Set UFW default deny policy + ufw: + state: enabled + policy: deny + +- name: Allow SSH through firewall + ufw: + rule: allow + port: "22" + proto: tcp diff --git a/ansible/roles/secure_ubuntu/tasks/main.yml b/ansible/roles/secure_ubuntu/tasks/main.yml new file mode 100644 index 0000000..fdece1a --- /dev/null +++ b/ansible/roles/secure_ubuntu/tasks/main.yml @@ -0,0 +1,15 @@ +--- +- name: Include SSH hardening tasks + import_tasks: ssh.yml + +- name: Include firewall configuration + import_tasks: firewall.yml + +- name: Include audit logging setup + import_tasks: audit.yml + +- name: Include banner setup + import_tasks: banners.yml + +- name: Include automatic update configuration + import_tasks: updates.yml diff --git a/ansible/roles/secure_ubuntu/tasks/password_policy.yml b/ansible/roles/secure_ubuntu/tasks/password_policy.yml new file mode 100644 index 0000000..b88b78e --- /dev/null +++ b/ansible/roles/secure_ubuntu/tasks/password_policy.yml @@ -0,0 +1,12 @@ +--- +- name: Set password complexity + lineinfile: + path: /etc/security/pwquality.conf + regexp: '^minlen' + line: 'minlen = 14' + +- name: Lock out after 5 failed login attempts + lineinfile: + path: /etc/pam.d/common-auth + line: 'auth required pam_tally2.so deny=5 onerr=fail unlock_time=900' + create: yes diff --git a/ansible/roles/secure_ubuntu/tasks/ssh.yml b/ansible/roles/secure_ubuntu/tasks/ssh.yml new file mode 100644 index 0000000..d345878 --- /dev/null +++ b/ansible/roles/secure_ubuntu/tasks/ssh.yml @@ -0,0 +1,27 @@ +--- +- name: Disable root login over SSH + lineinfile: + path: /etc/ssh/sshd_config + regexp: '^PermitRootLogin' + line: 'PermitRootLogin no' + notify: Restart SSH + +- name: Disable password authentication + lineinfile: + path: /etc/ssh/sshd_config + regexp: '^PasswordAuthentication' + line: 'PasswordAuthentication no' + notify: Restart SSH + +- name: Ensure non-root admin user exists + user: + name: cmmcadmin + shell: /bin/bash + groups: sudo + create_home: yes + state: present + +- name: Add authorized SSH key for cmmcadmin + authorized_key: + user: cmmcadmin + key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" diff --git a/ansible/roles/secure_ubuntu/tasks/updates.yml b/ansible/roles/secure_ubuntu/tasks/updates.yml new file mode 100644 index 0000000..f1d5e04 --- /dev/null +++ b/ansible/roles/secure_ubuntu/tasks/updates.yml @@ -0,0 +1,12 @@ +--- +- name: Install unattended-upgrades + apt: + name: unattended-upgrades + state: present + +- name: Enable automatic security updates + copy: + dest: /etc/apt/apt.conf.d/20auto-upgrades + content: | + APT::Periodic::Update-Package-Lists "1"; + APT::Periodic::Unattended-Upgrade "1"; diff --git a/ansible/roles/secure_ubuntu/tasks/user.yml b/ansible/roles/secure_ubuntu/tasks/user.yml new file mode 100644 index 0000000..3ea6574 --- /dev/null +++ b/ansible/roles/secure_ubuntu/tasks/user.yml @@ -0,0 +1,13 @@ +--- +- name: Ensure non-root user exists + user: + name: cmmcadmin + shell: /bin/bash + groups: sudo + state: present + create_home: yes + +- name: Add authorized SSH key for cmmcadmin + authorized_key: + user: cmmcadmin + key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" diff --git a/ansible/roles/secure_ubuntu/templates/banner.txt.j2 b/ansible/roles/secure_ubuntu/templates/banner.txt.j2 new file mode 100644 index 0000000..48a54af --- /dev/null +++ b/ansible/roles/secure_ubuntu/templates/banner.txt.j2 @@ -0,0 +1,10 @@ +*** WARNING *** + +This computer system is the property of {{ organization_name | default('Your Organization') }}. It is for authorized use only. + +By accessing this system, you acknowledge and consent to the following: +- All information is subject to monitoring by authorized personnel. +- Unauthorized use may result in disciplinary action, civil penalties, or criminal prosecution. +- You agree to comply with all security and data handling policies. + +This system is protected under CMMC Level 2 compliance controls. diff --git a/ansible/roles/secure_ubuntu/tests/test.yml b/ansible/roles/secure_ubuntu/tests/test.yml new file mode 100644 index 0000000..a61a5b0 --- /dev/null +++ b/ansible/roles/secure_ubuntu/tests/test.yml @@ -0,0 +1,13 @@ +--- +- name: Test secure_ubuntu role on localhost + hosts: localhost + become: yes + gather_facts: true + + vars: + secure_user: testadmin + ssh_pubkey_path: "~/.ssh/id_rsa.pub" + ssh_port: 22 + + roles: + - role: secure_ubuntu diff --git a/ansible/roles/secure_ubuntu/vars/main.yml b/ansible/roles/secure_ubuntu/vars/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/ansible/secure_ubuntu.yml b/ansible/secure_ubuntu.yml new file mode 100644 index 0000000..7fc2613 --- /dev/null +++ b/ansible/secure_ubuntu.yml @@ -0,0 +1,6 @@ +--- +- name: Harden Ubuntu for CMMC Stack + hosts: localhost + become: yes + roles: + - secure_ubuntu diff --git a/ansible/site.yml b/ansible/site.yml new file mode 100644 index 0000000..66849fd --- /dev/null +++ b/ansible/site.yml @@ -0,0 +1,10 @@ +--- +- name: Apply baseline security and deploy OpenCMMC Stack + hosts: all + become: true + roles: + - role: secure_ubuntu + - role: identity + - role: podman_services + - role: file_storage + - role: monitoring diff --git a/evidence/00_scoping/evidence.md b/evidence/00_scoping/evidence.md new file mode 100644 index 0000000..c029b0d --- /dev/null +++ b/evidence/00_scoping/evidence.md @@ -0,0 +1,15 @@ +# 📌 Evidence – Scoping Documentation + +## Purpose +This section documents the scoping boundaries for the OpenCMMC Stack environment. It includes diagrams, system inventory, and asset classification relevant to CMMC Level 2 compliance. + +## Included Artifacts +- Network/system diagrams +- Inventory list +- Boundary identification +- Cloud vs. on-prem segregation + +## Review Checklist +- [ ] Scope document reviewed by compliance officer +- [ ] Asset list includes all CUI-processing components +- [ ] Diagrams show trust boundaries and segmentation diff --git a/evidence/01_identity_access/evidence.md b/evidence/01_identity_access/evidence.md new file mode 100644 index 0000000..8032204 --- /dev/null +++ b/evidence/01_identity_access/evidence.md @@ -0,0 +1,15 @@ +# 🔐 Evidence – Identity and Access Management + +## Purpose +This folder contains evidence showing how user accounts, roles, MFA, and authentication systems are managed via Keycloak and Tailscale. + +## Included Artifacts +- Realm export (`keycloak-realm-export.json`) +- Screenshots of MFA policy +- Group-to-role mapping export +- Tailscale ACL and device log + +## Review Checklist +- [ ] MFA enforced for all privileged users +- [ ] User roles mapped and validated +- [ ] Keycloak policies match SSP configuration diff --git a/evidence/02_system_hardening/ansible_run_logs/evidence.md b/evidence/02_system_hardening/ansible_run_logs/evidence.md new file mode 100644 index 0000000..5cdbe89 --- /dev/null +++ b/evidence/02_system_hardening/ansible_run_logs/evidence.md @@ -0,0 +1,15 @@ +# 📋 Evidence – Ansible Execution Logs + +## Description +This directory contains raw output of Ansible playbooks applied to the Ubuntu host. + +## Artifacts +- `secure_ubuntu_run.log` +- `ansible-playbook-timestamped.json` +- Output showing configuration and user enforcement + +## Reviewer Notes +Ensure playbook includes: +- SSH key enforcement +- Root login disabled +- Required auditd and aide tasks completed diff --git a/evidence/02_system_hardening/evidence.md b/evidence/02_system_hardening/evidence.md new file mode 100644 index 0000000..f1ebabf --- /dev/null +++ b/evidence/02_system_hardening/evidence.md @@ -0,0 +1,15 @@ +# 🛡️ Evidence – System Hardening + +## Purpose +Evidence of host OS and container baseline security including hardened configurations, SSH controls, and auditd/aide logs. + +## Included Artifacts +- Ansible playbook logs +- Auditd rules and logs +- SSH daemon configs +- AIDE database init and scan result + +## Review Checklist +- [ ] Ansible logs show successful hardening +- [ ] Login banners and password policies verified +- [ ] Audit and integrity scans scheduled diff --git a/evidence/03_file_sharing/evidence.md b/evidence/03_file_sharing/evidence.md new file mode 100644 index 0000000..ea864dd --- /dev/null +++ b/evidence/03_file_sharing/evidence.md @@ -0,0 +1,15 @@ +# 📁 Evidence – File Sharing & Collaboration + +## Purpose +Evidence of Nextcloud AIO configuration for secure file sharing and internal team access controls. + +## Included Artifacts +- SAML config screenshots +- File Access Control rules +- Team Folder setup for CUI/FCI +- Antivirus scanner logs + +## Review Checklist +- [ ] Access control groups defined +- [ ] External sharing is blocked for CUI +- [ ] Antivirus scans for uploads are active diff --git a/evidence/04_email/evidence.md b/evidence/04_email/evidence.md new file mode 100644 index 0000000..aa1a5a8 --- /dev/null +++ b/evidence/04_email/evidence.md @@ -0,0 +1,15 @@ +# ✉️ Evidence – Secure Email Configuration + +## Purpose +Evidence related to the secure deployment and configuration of Mailcow for sending and receiving secure communications. + +## Included Artifacts +- TLS/DKIM/SPF reports +- SSO integration with Keycloak +- Mail logs for delivery success and rejection +- Email relay and filter rules + +## Review Checklist +- [ ] DKIM keys configured and tested +- [ ] TLS enforced on all SMTP/IMAP traffic +- [ ] Mailcow access restricted via Tailscale ACLs diff --git a/evidence/05_monitoring/evidence.md b/evidence/05_monitoring/evidence.md new file mode 100644 index 0000000..b9c3fa7 --- /dev/null +++ b/evidence/05_monitoring/evidence.md @@ -0,0 +1,15 @@ +# 📡 Evidence – Monitoring & SIEM Integration + +## Purpose +Evidence supporting logging, alerting, and forensic readiness using Wazuh and system-level audit mechanisms. + +## Included Artifacts +- Wazuh agent and server logs +- SIEM dashboard screenshots +- Custom alert rules +- Log forwarding rules (if applicable) + +## Review Checklist +- [ ] Agent deployment logs available +- [ ] Alerts fire on auth failures, sudo, etc. +- [ ] Central log retention meets policy diff --git a/evidence/06_backups/evidence.md b/evidence/06_backups/evidence.md new file mode 100644 index 0000000..d2d9fb0 --- /dev/null +++ b/evidence/06_backups/evidence.md @@ -0,0 +1,15 @@ +# 💾 Evidence – Backups & Recovery Testing + +## Purpose +Documentation of backup routines, tools (Restic/Borg), encrypted vaults, and restore verification processes. + +## Included Artifacts +- Restic/Borg configuration files +- Restore logs and diffs +- Encryption key storage policy +- Backup rotation reports + +## Review Checklist +- [ ] Encryption is enabled at rest +- [ ] Monthly restore test logs present +- [ ] Offsite backup location validated diff --git a/evidence/07_policies/evidence.md b/evidence/07_policies/evidence.md new file mode 100644 index 0000000..074102b --- /dev/null +++ b/evidence/07_policies/evidence.md @@ -0,0 +1,15 @@ +# 📜 Evidence – Policies and Administrative Controls + +## Purpose +This section contains administrative control evidence including signed policies, role assignments, and procedure documents. + +## Included Artifacts +- Access control policy (AC-1) +- Incident response plan +- User onboarding/offboarding procedures +- Policy acceptance logs + +## Review Checklist +- [ ] Policies approved and version controlled +- [ ] All users have acknowledged relevant policies +- [ ] Procedures align with technical implementation diff --git a/terraform/bootstrap.sh b/terraform/bootstrap.sh new file mode 100644 index 0000000..abb9b3a --- /dev/null +++ b/terraform/bootstrap.sh @@ -0,0 +1,8 @@ +#!/bin/bash +apt update && apt install -y git python3-pip curl ufw +pip3 install ansible +ufw allow OpenSSH +ufw --force enable +git clone https://github.com/mtkell/open-cmmc-stack.git /opt/open-cmmc-stack +cd /opt/open-cmmc-stack/ansible +ansible-playbook -i localhost, secure_ubuntu.yml diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..147587a --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,18 @@ +provider "digitalocean" { + token = var.do_token +} + +resource "digitalocean_droplet" "secure_host" { + name = "cmmc-hardened" + region = "nyc3" + size = "s-2vcpu-4gb" + image = "ubuntu-22-04-x64" + ssh_keys = [var.ssh_fingerprint] + user_data = file("${path.module}/bootstrap.sh") + + tags = ["cmmc", "secure-host"] +} + +output "droplet_ip" { + value = digitalocean_droplet.secure_host.ipv4_address +} diff --git a/terraform/terraform.tfvars.example b/terraform/terraform.tfvars.example new file mode 100644 index 0000000..8ee2f9b --- /dev/null +++ b/terraform/terraform.tfvars.example @@ -0,0 +1,2 @@ +do_token = "your_digitalocean_token_here" +ssh_fingerprint = "your_local_ssh_key_fingerprint" diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 0000000..41de4b2 --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,10 @@ +variable "do_token" { + description = "DigitalOcean API token" + type = string + sensitive = true +} + +variable "ssh_fingerprint" { + description = "Your public SSH key fingerprint registered with DigitalOcean" + type = string +}