Skip to content

Repository files navigation

ansible-owncloud-debian

Ansible Debian ownCloud Infinite Scale Podman gVisor + Coraza WAF Proxmox VE Tested with Vagrant

Ansible playbooks to deploy ownCloud Infinite Scale (oCIS) on a hardened Debian 13 VM on Proxmox VE.

Why oCIS over ownCloud 10? oCIS is a ground-up Go rewrite: single binary, no PHP, no database, no Redis — dramatically fewer moving parts. Its decomposedfs storage replaces the Nextcloud/ownCloud 10 POSIX+DB model. Built-in IDP, JWT-based microservice mesh, and a first-class REST/Graph API make it the forward path for ownCloud deployments. Memory footprint for a small team is roughly 300–500 MB idle, well below a comparable ownCloud 10 LAMP stack.


Architecture

Internet
   │ 80/443
   ▼
┌──────────────────────────────────────────────────┐
│  edge (rootful system Quadlet, runsc/gVisor)     │
│  Caddy 2 + xcaddy-coraza WAF (OWASP CRS v4)     │
│  TLS termination (acme | byo | selfsigned)       │
└──────────────────┬───────────────────────────────┘
                   │ server-TLS (internal CA, CN=ocis)
                   │ https://host.containers.internal:9200
                   ▼
┌──────────────────────────────────────────────────┐
│  ocis (rootless user Quadlet, ocisstack uid 1500)│
│  owncloud/ocis:7.1.0, network ocis-internal      │
│  proxy  :9200   ← HTTPS (decomposedfs on disk)  │
│  debug  :9205   ← Prometheus scrape target      │
│  IDM    :9235   ← built-in LDAP                 │
│  IDP    :9130   ← built-in OIDC (default)       │
└──────────────────────────────────────────────────┘
   │ clamd.sock (shared volume, when clamav_enabled)
   ▼
┌──────────────────────────────────────────────────┐
│  ocis-clamav (rootless, clamav/clamav image)     │
└──────────────────────────────────────────────────┘

   collaboration (rootless, collabora/code image)  ← when collabora_enabled
   node_exporter + prometheus + grafana             ← when monitoring_enabled
   restic backup timer                              ← when ocis_backup_enabled
   fail2ban (system)                                ← when fail2ban_enabled

Edge layer: A single rootful Caddy container runs under the runsc gVisor OCI runtime (systrap platform) with the Coraza WAF module and OWASP Core Rule Set v4. It terminates public TLS on 80/443 and reverse-proxies to the oCIS proxy using server-TLS verified against the internal CA (the oCIS server cert has CN/SAN ocis). This is one-way TLS — oCIS proxy cannot verify client certificates — not mutual TLS. A mTLS sidecar is a documented follow-up.

App layer: A single rootless owncloud/ocis container runs in user scope under the lingering ocisstack (UID 1500) account as a Podman Quadlet systemd user unit. All secrets are injected as environment variables via podman secrets (not a templated ocis.yaml). The container is attached to the ocis-internal Podman network.

Secrets model: Ansible generates no secrets of its own. The operator runs scripts/gen-ocis-secrets.sh once, pastes the output into inventory/group_vars/all/vault.yml, and encrypts with ansible-vault. At deploy time, the ocis role creates podman secrets from those vault values and passes them as OCIS_* / IDM_* environment variables into the container.


Roles

Role Purpose
os_base Locale, timezone, SSH hardening, firewall contract vars
podman_host Podman, netavark/aardvark-dns, gVisor runsc, ocis-internal network, ocisstack linger
internal_pki Self-signed CA + server cert (CN ocis) for the edge→oCIS hop
caddy_edge Build xcaddy+coraza image; rootful edge container (runsc)
ocis Vault secrets → podman secrets; oCIS container Quadlet; env-var config
collabora Collabora CODE + WOPI collaboration container (gated)
clamav ClamAV + clamd socket shared into oCIS antivirus service (gated)
fail2ban fail2ban jail reading oCIS proxy log (gated)
monitoring node_exporter + Prometheus (scrapes :9205) + Grafana (gated)
ocis_backup restic backup systemd timer for ocis-data + ocis-config volumes (gated)
proxmox_vm Clone Proxmox cloud-init template → new VM (gated by provision_vm_enabled)

Prerequisites

  • Proxmox VE with a Debian 13 cloud-init template (built by the sibling proxmox-debian13-template repo, VMID 9999). Set pve_template_vmid in inventory/group_vars/all/main.yml to match.
  • Deploy SSH keypair at files/ssh/ocis-deploy (private, git-ignored) and files/ssh/ocis-deploy.pub (committed). Generate with:
    ssh-keygen -t ed25519 -f files/ssh/ocis-deploy
  • direnv (direnv allow) or manually:
    export ANSIBLE_CONFIG=$PWD/ansible.cfg
    unset ANSIBLE_ROLES_PATH
    unset ANSIBLE_COLLECTIONS_PATH
    unset ANSIBLE_COLLECTIONS_PATHS
    The .envrc unsets those three variables. If they are set in your shell profile they override the project ansible.cfg and break collection/role resolution. If you run ansible-lint or ansible-playbook without direnv, use the explicit unset form:
    env -u ANSIBLE_COLLECTIONS_PATHS -u ANSIBLE_COLLECTIONS_PATH ansible-lint
    ANSIBLE_ROLES_PATH=roles ansible-playbook --syntax-check site.yml
  • Collections:
    ansible-galaxy collection install -r requirements.yml
  • VM sizing: oCIS is a single Go binary — 2 vCPU / 4 GB RAM is sufficient for a small team without ClamAV. Enable ClamAV or the local monitoring dashboard and budget an extra 1–2 GB. The Vagrantfile reflects these minimums.

Secrets workflow

  1. Run the generator (requires openssl and uuidgen):
    scripts/gen-ocis-secrets.sh
  2. Paste the entire output into inventory/group_vars/all/vault.yml, replacing every change-me-* placeholder.
  3. Encrypt:
    ansible-vault encrypt inventory/group_vars/all/vault.yml
  4. Every subsequent ansible-playbook run requires --ask-vault-pass (or --vault-password-file).

The ocis role translates the vault keys into podman secrets and wires them as Secret= entries in the Quadlet .container unit. The container never reads a file-based ocis.yaml; all configuration is environment-variable-driven.

Vault key Podman secret / env var
vault_ocis_jwt_secret ocis-jwt-secret / OCIS_JWT_SECRET
vault_ocis_transfer_secret ocis-transfer-secret / OCIS_TRANSFER_SECRET
vault_ocis_machine_auth_api_key ocis-machine-auth-api-key / OCIS_MACHINE_AUTH_API_KEY
vault_ocis_system_user_api_key ocis-system-user-api-key / OCIS_SYSTEM_USER_API_KEY
vault_ocis_storage_system_jwt_secret ocis-storage-system-jwt-secret / STORAGE_SYSTEM_JWT_SECRET
vault_ocis_service_account_secret ocis-service-account-secret / OCIS_SERVICE_ACCOUNT_SECRET
vault_ocis_admin_password ocis-idm-admin-password / IDM_ADMIN_PASSWORD
vault_ocis_idm_svc_password ocis-idm-svc-password / IDM_SVC_PASSWORD
vault_ocis_idm_revasvc_password ocis-idm-revasvc-password / IDM_REVASVC_PASSWORD
vault_ocis_idm_idpsvc_password ocis-idm-idpsvc-password / IDM_IDPSVC_PASSWORD
vault_ocis_collaboration_wopi_secret ocis-collaboration-wopi-secret / COLLABORATION_WOPI_SECRET
vault_ocis_system_user_id injected as OCIS_SYSTEM_USER_ID
vault_ocis_service_account_id injected as OCIS_SERVICE_ACCOUNT_ID
vault_ocis_backup_password used by the ocis_backup restic role

Identity: built-in IDP vs external OIDC

By default oCIS runs with its built-in IDP (Keycloak-compatible OIDC issuer). No extra infrastructure is needed.

To switch to an external OIDC provider (e.g. Keycloak), set:

# inventory/group_vars/all/main.yml
ocis_oidc_external_enabled: true
ocis_oidc_issuer: "https://keycloak.example.net/realms/ocis"
ocis_oidc_web_client_id: web

When ocis_oidc_external_enabled: true, the ocis role appends idp to OCIS_EXCLUDE_RUN_SERVICES, removing the bundled IDP from the run set. The OCIS_OIDC_ISSUER environment variable is set to ocis_oidc_issuer. Your Keycloak realm must pre-exist; the role does not manage it.


Optional integrations

All integrations are gated by a boolean variable. Disable any by setting the corresponding flag to false in inventory/group_vars/all/main.yml.

Integration Variable What it does
Collabora Online collabora_enabled Deploys a second rootless collabora/code container + oCIS WOPI collaboration sidecar. Registers as the WOPI app in oCIS.
ClamAV antivirus clamav_enabled Deploys clamav/clamav; shares a clamd.sock unix socket volume into oCIS. Activates antivirus in OCIS_ADD_RUN_SERVICES.
Monitoring monitoring_enabled node_exporter + Prometheus (scrapes oCIS debug endpoint :9205) + optional on-box Grafana (monitoring_local_dashboard).
fail2ban fail2ban_enabled Jail reading the oCIS proxy log for brute-force attempts.
Restic backups ocis_backup_enabled Nightly restic snapshot of ocis-data and ocis-config volumes at 02:30.
Proxmox VM create provision_vm_enabled Clone the Proxmox template into a new VM before provisioning. Off by default.

Key variables

All top-level variables live in inventory/group_vars/all/main.yml; role tunables live in roles/<role>/defaults/main.yml.

Variable Default Notes
ocis_domain cloud.example.net Public FQDN; used in certs, edge vhost, oCIS trusted domains
collabora_domain office.example.net Collabora public FQDN
grafana_domain grafana.example.net Grafana dashboard FQDN
ocis_version 7.1.0 oCIS image tag — pin deliberately, bump with care
tls_mode selfsigned acme (Let's Encrypt), byo (vault cert/key), selfsigned (lab)
edge_tls_profile intermediate intermediate (TLS 1.2+) or modern (TLS 1.3 only)
waf_mode enforce Coraza/CRS mode — soak in detection before switching to enforce
gvisor_platform systrap Never kvm — no nested virt on a cloned VM
ocis_oidc_external_enabled false Switch to external OIDC; excludes bundled idp service
collabora_enabled true Deploy Collabora Online
clamav_enabled true Deploy ClamAV antivirus
fail2ban_enabled true Deploy fail2ban
monitoring_enabled true Deploy node_exporter + Prometheus
monitoring_local_dashboard false On-box Grafana
ocis_backup_enabled true Restic backups
provision_vm_enabled false Clone Proxmox template
pve_vm_cores 2 VM vCPUs
pve_vm_memory 4096 VM RAM in MB
management_cidrs [] CIDRs allowed SSH (22)
firewall_allow_tcp [80, 443] Public TCP ports

OS hardening (external) — integration notes

Full OS hardening is owned by the sibling ansible-hardening-debian repo and runs as a separate orchestration layer. Run in this order:

  1. provision-vm.yml (or use an existing Debian 13 host).
  2. ansible-hardening-debian: bootstrap.yml then its site.yml.
  3. This repo's site.yml, connecting as the hardening repo's deploy user.

CRITICAL: Rootless Podman and gVisor systrap both require unprivileged user namespaces. An over-aggressive hardening profile that sets kernel.unprivileged_userns_clone=0 will break the entire stack. Confirm kernel.unprivileged_userns_clone=1 and user.max_user_namespaces>0 before and after hardening.


Run matrix

Local Vagrant (libvirt)

Rootless systemd-user linger, gVisor systrap, and rootless Collabora all require a real VM with unprivileged user namespaces enabled.

vagrant up            # boots the VM and runs site.yml via the Ansible provisioner
vagrant provision     # re-run the playbook after changes

vagrant up is self-contained: before provisioning it auto-generates an unencrypted dev vault at inventory/group_vars/all/vault.yml (git-ignored) from scripts/gen-ocis-secrets.sh, so no vault password is needed for the local box. Delete that file to regenerate fresh secrets. This dev convenience is Vagrant-only — for remote/production runs you create and ansible-vault encrypt the vault yourself (below) and pass --ask-vault-pass.

Remote test VM

ansible-playbook -i inventory/test.yml site.yml --ask-vault-pass

Production (including VM creation on Proxmox)

ansible-playbook -i inventory/hosts.yml site.yml \
  -e provision_vm_enabled=true --ask-vault-pass

Per-role tests

ansible-playbook -i inventory/vagrant.yml tests/test-ocis.yml --ask-vault-pass

Run all tests in dependency order:

for t in os_base podman_host internal_pki caddy_edge \
         ocis collabora clamav fail2ban monitoring ocis_backup; do
  ansible-playbook -i inventory/vagrant.yml tests/test-$t.yml --ask-vault-pass || break
done

Known follow-ups

  • Internal mTLS sidecar: The edge currently uses one-way server-TLS to reach oCIS (the edge verifies the internal CA cert; oCIS proxy cannot verify client certs). A mTLS sidecar that terminates client auth before handing off to oCIS is documented but not yet implemented.
  • clamd cross-container socket verification: The clamd unix socket is shared via a Podman volume from the ocis-clamav container into the ocis container. Live round-trip verification under concurrent upload load has not been run yet.
  • Collabora WOPI nats reachability: The collaboration service registers with oCIS via a nats endpoint (:9233). End-to-end collaborative editing under the ocis-internal Podman network has not been verified in a live lab.
  • Vagrant end-to-end smoke test: vagrant up + tests/test-ocis.yml was not run (no KVM available in the dev environment). Verify manually before production use.

About

Ansible: self-hosted ownCloud Infinite Scale (oCIS) on Debian 13 - rootless Podman/Quadlet, Caddy edge, optional Collabora + ClamAV, adopts the acidnetworks base/hardening collections.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages