AcidDemon/ansible-nextcloud-debian

GitHub: AcidDemon/ansible-nextcloud-debian

一套通过 Ansible 在 Proxmox VE 的 Debian 13 虚拟机上自动化部署安全隔离架构 Nextcloud 私有云的 playbook。

Stars: 0 | Forks: 0

# ansible-nextcloud-debian ![Ansible](https://img.shields.io/badge/Ansible-2.16%2B-EE0000?logo=ansible&logoColor=white) ![Debian](https://img.shields.io/badge/Debian-13%20trixie-A81D33?logo=debian&logoColor=white) ![Nextcloud](https://img.shields.io/badge/Nextcloud-0082C9?logo=nextcloud&logoColor=white) ![Podman](https://img.shields.io/badge/rootless-Podman%20%2B%20Quadlet-892CA0?logo=podman&logoColor=white) ![gVisor + Coraza WAF](https://img.shields.io/badge/edge-gVisor%20%2B%20Coraza%20WAF-4B275F) ![Proxmox VE](https://img.shields.io/badge/Proxmox-VE-E57000?logo=proxmox&logoColor=white) ![Tested with Vagrant](https://img.shields.io/badge/tested%20with-Vagrant-1868F2?logo=vagrant&logoColor=white) [![License: MIT](https://img.shields.io/github/license/AcidDemon/ansible-nextcloud-debian?color=blue)](LICENSE) [![最后提交](https://img.shields.io/github/last-commit/AcidDemon/ansible-nextcloud-debian)](https://github.com/AcidDemon/ansible-nextcloud-debian/commits/main) [![仓库大小](https://img.shields.io/github/repo-size/AcidDemon/ansible-nextcloud-debian)](https://github.com/AcidDemon/ansible-nextcloud-debian) [![GitLab 镜像](https://img.shields.io/badge/mirror-GitLab-FC6D26?logo=gitlab&logoColor=white)](https://git.inviziblenet.work/AcidDemon/ansible-nextcloud-debian) 在运行于 Proxmox VE 的 Debian 13 虚拟机上部署 **Nextcloud** 的 Ansible playbook。 **应用层采用 rootless Podman + Quadlet**(systemd *用户* 单元),以 lingering `ncstack` 用户身份运行:Postgres (`nc-db`)、Valkey (`nc-redis`)、Nextcloud php-fpm (`nc-app`)、内部 mTLS nginx (`nc-web`),以及可选的 Collabora Online (rootless)、ClamAV、监控 exporter 和 restic 备份。 **边缘层是基于 rootful 且由 gVisor 沙箱保护的 Caddy + Coraza WAF**(`edge`,一个 在 `runsc` 下运行的 *系统* Quadlet),在 80/443 端口终止 TLS。它必须以 rootful 运行: gVisor 需要 rootful cgroups,而且 rootless `runsc` 在 cgroup v2 上无法正常工作(它会写入 根 cgroup 的 subtree_control / 驱动系统 systemd 管理器 → 触发 polkit)。边缘层 运行在 podman bridge 上 —— **而不是** `--network host`,因为 `runsc` 在 host networking 下没有可用的 loopback —— 并且通过主机网关(`host.containers.internal`)跨越 podman 作用域访问 rootless 应用层。 所有跨层流量都经过内部 CA (mTLS);应用层通过名称在 `nc-internal` Quadlet 网络上解析对等节点。 ## 前置条件 - **Proxmox VE** 及 Debian 13 cloud-init 模板(由兄弟仓库 `proxmox-debian13-template` 构建,VMID 为 `9999`);将 `pve_template_vmid` 设置为该值。 - **部署 SSH 密钥对**位于 `files/ssh/nextcloud-deploy`(私钥,已添加至 git-ignore)和 `files/ssh/nextcloud-deploy.pub`(公钥,已提交)。使用以下命令生成: `ssh-keygen -t ed25519 -f files/ssh/nextcloud-deploy`。 - **direnv**(执行 `direnv allow`)或手动执行 `export ANSIBLE_CONFIG=$PWD/ansible.cfg`。 `.envrc` 还会取消设置 `ANSIBLE_COLLECTIONS_PATH`、`ANSIBLE_COLLECTIONS_PATHS` 和 `ANSIBLE_ROLES_PATH` —— 如果在你的 shell profile 中设置了这些变量,它们会覆盖 项目配置并导致 collection/role 解析失败。 - **Collections:** `ansible-galaxy collection install -r requirements.yml` - **VM 配置:**至少 **8 GB 内存和 4 个 vCPU**(`pve_vm_memory: 8192`, `pve_vm_cores: 4`)。请参阅下方的配置说明 —— 此架构无法在 4 GB 内存中运行。 ## 机密信息 编辑 `inventory/group_vars/all/vault.yml` —— 它以**纯文本存根**的形式提供,包含 `change-me-*` 值。为每个字段设置真实值,然后加密: ``` ansible-vault encrypt inventory/group_vars/all/vault.yml ``` `nextcloud` role 中的预检 `assert.yml` 会拒绝运行,前提是仍然存在任何 `change-me-*` 占位符。Vagrant 实验环境(lab)是例外,因为 `inventory/vagrant.yml` 将所有 `vault_*` 键覆盖为真实的实验环境密码作为 host vars(其优先级高于 `group_vars/all`),因此在实验环境中没有 `change-me-*` 占位符 会到达预检关卡。 加密后,**每一次**运行都需要 `--ask-vault-pass` —— 包括 Vagrant 实验环境的 运行 —— 因为 `group_vars/all/vault.yml` 会为所有 inventory 加载。实验环境仍然 使用来自 `inventory/vagrant.yml` 的临时值。配置中**没有 `.vault_pass` 文件和 `vault_password_file`**;密码仅在运行时提供。(为了避免在开发期间反复输入,可以保持存根为纯文本,仅在投入生产前加密, 或者自行传递 `--vault-password-file`。) | Vault 键 | 用途 | |---|---| | `vault_pve_api_token_secret` | Proxmox API token secret(仅用于置备) | | `vault_nc_db_password` | Postgres 密码(Podman secret `nc-db-password`) | | `vault_nc_redis_password` | Valkey 密码(Podman secret `nc-redis-password`) | | `vault_nc_admin_password` | Nextcloud 管理员密码(Podman secret `nc-admin-password`) | | `vault_oidc_client_secret` | OIDC client secret(Podman secret `oidc-client-secret`) | | `vault_ldap_bind_password` | LDAP bind 密码(Podman secret `ldap-bind-password`) | | `vault_tls_cert` / `vault_tls_key` | 自带 TLS 材料(仅限 `tls_mode: byo`) | ## 运行矩阵 ### 本地 Vagrant (libvirt) ``` vagrant up ansible-playbook -i inventory/vagrant.yml site.yml --ask-vault-pass ``` ### 远程测试 VM ``` ansible-playbook -i inventory/test.yml site.yml --ask-vault-pass ``` ### 生产环境(包括在 Proxmox 上创建 VM) ``` ansible-playbook -i inventory/hosts.yml site.yml \ -e provision_vm_enabled=true --ask-vault-pass ``` ### 按角色测试 请参阅 `tests/README.md` 获取完整列表以及每个测试断言的内容。示例: ``` ansible-playbook -i inventory/vagrant.yml tests/test-podman_host.yml --ask-vault-pass ansible-playbook -i inventory/vagrant.yml tests/test-nextcloud.yml --ask-vault-pass ansible-playbook -i inventory/vagrant.yml tests/test-collabora.yml --ask-vault-pass ansible-playbook -i inventory/vagrant.yml tests/test-euro_office.yml --ask-vault-pass ``` 按依赖顺序运行所有测试: ``` for t in os_base podman_host internal_pki caddy_edge \ nextcloud collabora clamav fail2ban monitoring nextcloud_backup \ firewall_coexistence; do ansible-playbook -i inventory/vagrant.yml tests/test-$t.yml --ask-vault-pass || break done ``` ## 关键变量 所有变量都位于 `inventory/group_vars/all/main.yml`(单一事实来源); role 内部的可调参数位于 `roles//defaults/main.yml`。 | 变量 | 默认值 | 说明 | |---|---|---| | `nc_domain` | `cloud.example.com` | 公共 FQDN;用于证书、边缘 vhost、Nextcloud trusted domains | | `collabora_domain` | `office.example.com` | Collabora 公共 FQDN(边缘 vhost) | | `tls_mode` | `acme` | `acme` = Let's Encrypt(公共 FQDN + 80/443 可达);`byo` = 通过 vault 提供的运维人员证书/密钥;`selfsigned` = 实验环境/Vagrant | | `edge_tls_profile` | `intermediate` | `intermediate`(TLS 1.2+)或 `modern`(仅限 1.3;需要 `edge_tls_modern_attested: true`) | | `waf_mode` | `detection` | Coraza/CRS 模式;**在 `enforce` 之前先在 `detection` 中观察** | | `gvisor_platform` | `systrap` | gVisor 平台 —— **绝不要使用 `kvm`**(克隆的 VM 上没有嵌套虚拟化) | | `ldap_enabled` / `oidc_enabled` | `true` / `true` | LDAP(s) 身份验证和 OIDC SSO | | `sse_master_key_enabled` | `true` | 主密钥服务端加密(与 SSO 登录配合使用) | | `collabora_enabled` / `collabora_mode` | `true` / `rootless` | 强制使用 rootless;`rootful` 仅限明确主动开启 | | `clamav_enabled` | `false` | ClamAV 杀毒;`clamav_max_scan_bytes` 必须等于 NC 最大上传大小且 <= `php_memory_limit` | | `fail2ban_enabled` | `true` | `jail.d/` 中的防暴力破解 jail(与 base-debian 的 `inet base_filter` 共存,见下文) | | `monitoring_enabled` / `monitoring_local_dashboard` | `false` / `false` | loopback 上的 exporter;可选的本机 Prometheus+Grafana | | `nextcloud_backup_enabled` | `false` | 通过 restic 备份到 `nextcloud_backup_repo` | | `management_cidrs` | `["10.0.0.0/8"]` | 允许访问 SSH (22) 的 CIDR;也是 fail2ban 的 `ignoreip`。由 base-debian 的防火墙使用 | | `firewall_allow_tcp` | `[80, 443]` | base-debian 为边缘层开放的公共 TCP 端口 | | `firewall_trust_iifnames` | `["podman*"]` | base-debian 在 INPUT 上信任的接口(podman bridge) | | `firewall_forward_policy` | `accept` | base-debian 的 FORWARD 策略(容器出站流量) | | `provision_vm_enabled` | `false` | 从 Proxmox 模板克隆并配置新 VM | | `pve_vm_cores` / `pve_vm_memory` / `pve_vm_disk_size` | `4` / `8192` / `100G` | VM 配置 —— 见说明 | | `pve_validate_certs` | `false` | 容忍 Proxmox 自签名的 API 证书;对于 CA 签名/远程 PVE 设置为 `true` | ### VM 配置 (>=8 GB) 默认的 `pve_vm_memory: 8192`(8 GB)和 `pve_vm_cores: 4` 是**下限,而不是 建议**。并发运行 Postgres + Valkey + Nextcloud php-fpm + nginx + 受 gVisor 沙箱保护的 Caddy/Coraza 边缘层无法容纳在 4 GB 内存中。如果启用了 ClamAV, 请额外预留约 2.5 GB 的峰值内存(clamd 会保存完整的特征库以及大小设为 `clamav_max_scan_bytes` 的扫描缓冲区);如果开启了 `monitoring_local_dashboard`, 请为本机 Prometheus + Grafana 预留足够空间。不要将内存缩减到 8 GB 以下。 **磁盘:**仅容器镜像就总计达数 GB —— Collabora CODE 约 1.5 GB, Nextcloud-fpm 约 300 MB,Postgres/Valkey/nginx 约 250 MB,加上边缘镜像构建 (xcaddy Go 工具链 + 模块缓存需要约 1 GB *瞬时空间*)。在计算用户数据之前,至少为 OS 磁盘预留 **20 GB** 空间(`pve_vm_disk_size: 100G` 是默认值,专为存放数据而设定)。较小的根磁盘(例如 3 GB)甚至无法构建 边缘镜像 —— `podman build` 会因 `no space left on device` 而失败。 ## 操作系统加固(外部)—— 关键集成限制 完整的操作系统加固由兄弟仓库 [`ansible-hardening-debian`](https://github.com/acidnetworks/ansible-hardening-debian) 负责,并作为一个**独立的编排层**运行。此仓库仅提供 浅层的 `os_base`。按以下顺序运行: 1. `provision-vm.yml`(或使用现有的 Debian 13 主机)。 2. `ansible-hardening-debian`:先执行 `bootstrap.yml`,再执行其 `site.yml`。 3. 此仓库的 `site.yml`,通过密钥以加固仓库的 deploy user 身份进行连接。 `playbooks/harden.yml.disabled` 包含了一个被注释掉的 `import_playbook` 钩子; 将其重命名为 `harden.yml`,并在加固仓库公开稳定的 `harden.yml` 后将其接入。 ## 隔离与 rootless 说明 - **应用层(rootless):**`nc-db`、`nc-redis`、`nc-app`、`nc-web`、Colla、 `euro-office` 以及 exporter 以 lingering 的 `ncstack` 用户(UID 1500)身份运行。 它们的 Quadlet 位于 `/home/ncstack/.config/containers/systemd/`;请以 `ncstack` 身份使用 `systemctl --user` 进行管理。`podman-auto-update.timer` 在用户作用域内运行。 - **边缘层(rootful):**`edge` 单元是 `/etc/containers/systemd/` 中的**系统** Quadlet;请以 root 身份使用 `systemctl` 进行管理。它直接绑定 80/443 端口(rootful —— 无需 unprivileged-port sysctl),并在 `runsc` 下运行。 - gVisor `runsc` 安装在 `/usr/local/bin/runsc` 并注册为名为 `runsc` 的 Podman OCI runtime,适用于 root podman(边缘层)**以及** rootless ncstack 层(euro_office Document Server)。平台为 `systrap`,绝不要使用 `kvm`。crun 仍然是 rootless 的默认选项。 - **边缘层 ↔ 应用网络:**rootful 边缘层无法共享 rootless 应用层的 podman 网络,因此它通过主机网关跨作用域访问应用层。因此 `nc-web` 和 Collabora 发布在 `0.0.0.0`(`:8443` / `:9980`)上,而不是 loopback —— 但是 base-debian 的 `base_filter` INPUT 链**会丢弃来自 公共接口的这些端口**(只有内部的 podman bridge,即 `iifname "podman*"`,可以 通过 `firewall_trust_iifnames` 访问它们),并且边缘层→`nc-web` 的跳转是 mTLS。 **DB 和 Redis 绝不会被发布。** - base-debian 在 INPUT(`firewall_trust_iifnames: ["podman*"]`)中信任 podman bridge, 以便 rootful 容器可以通过 aardvark-dns(监听在 bridge 网关上)解析名称,并且边缘层可以访问应用层。公共接口绝不能是 podman bridge,因此仅暴露 80/443/22 的限制仍然有效。 - 容器间的名称解析使用 netavark + aardvark-dns(明确安装了 —— 在 Debian 上 netavark 只是一个 `Recommends` 依赖)。 - `occ` 以 `podman exec -u www-data nc-app php occ ` (作为 `become_user: ncstack`)的方式运行。
标签:Ansible, gVisor, Nextcloud, Podman, Proxmox, 测试用例, 特权提升, 系统提示词, 系统运维, 自动化部署