nsetup 是 Nihility 的 Linux 主机与 Docker Compose 管理工具。它以 systemd daemon
持有系统权限,CLI 通过本机 Unix socket 上的 gRPC 接口调用它,因此日常操作无需
sudo、无需复制 root token,也不会默认开放网络端口。
构建或下载单个静态链接二进制后初始化:
chmod +x ./nsetup
sudo ./nsetup init \
--domain example.com \
--stacks-root /var/lib/nsetup/stacks \
--data-root /srv/data
sudo usermod -aG nihility "$USER"重新登录后检查 daemon 与 Docker:
nsetup statusinit --force 可更新现有安装,未重新指定的配置项保持不变。
TOML 是唯一的声明式配置格式。先输出带注释的配置骨架:
nsetup template app > app.toml
nsetup template authelia > authelia.toml
nsetup template traefik > traefik.toml
nsetup template static > static.tomlapp 骨架逐字段说明当前支持的配置;仅保留常用的最小路由为有效配置,端口发布、
挂载、固定容器名、重启策略、健康检查、日志和高级路由等可选项均以注释示例展示。
普通容器应用支持一个项目内的多个服务:
format = 1
name = "media"
[services.web]
image = "ghcr.io/example/media"
version = "1.0"
port = 8080
publish = ["12780:8080/tcp"]
volumes = ["/srv/data/media:/data"]
environment = { LOG_LEVEL = "info" }
[services.web.traefik]
hosts = ["media"]
middlewares = ["gzip", "internal-only"]
[services.worker]
image = "ghcr.io/example/worker"
version = "1.2"同一容器暴露多个子路由时使用具名表,让名称、域名和端口保持显式绑定;路由名也会 进入 Traefik router/backend 名称,不依赖声明顺序:
[services.web.traefik.routes.api]
hosts = ["s3"]
port = 9000
[services.web.traefik.routes.console]
hosts = ["s3-console"]
port = 9001旧的 [[services.*.traefik.routes]] 顺序数组不再支持。
应用配置并启动:
nsetup up -f app.toml --start镜像必须拆分为 image 与明确的 version,省略版本或使用 latest 会被拒绝。
短主机名会拼接全局主域名;例如 media 生成 media.example.com。
基础设施与静态站点使用相同入口。先部署 Traefik,再部署 Authelia:
chmod 600 traefik.toml authelia.toml
nsetup up -f traefik.toml --start
nsetup up -f authelia.toml --start
nsetup up -f static.toml --assets ./dist --startAuthelia 模板使用文件用户库、文件型密钥和 SQLite,运行状态持久化到第一个
data_root 下的 authelia/。先交互生成密码哈希,再替换模板中的占位值:
模板将 TOTP 设为默认且唯一的二次验证方式,并禁用 WebAuthn;未配置 Duo,因此
不会提供移动推送。default_policy 决定普通 ForwardAuth 路由是否必须进行二次验证,
OIDC 客户端则使用各自的 authorization_policy。
docker run --rm --pull=never -it authelia/authelia:4.39.20 \
authelia crypto hash generate argon2
openssl rand -hex 32 # 三个密钥分别运行一次storage_encryption_key 用于加密 SQLite 中的敏感字段。数据库首次初始化后必须保留
原值;普通 up --force 不得重新生成它。需要轮换时,应停止 Authelia,先用旧密钥
执行 authelia storage encryption change-key,再把 TOML 更新为新密钥。丢失旧密钥
后无法解密已有的 TOTP、WebAuthn 和 OIDC 状态。
重点:登录用户名由
[users.<用户名>]的表名决定。 模板中的[users.admin]表示登录用户名是admin;display_name只是显示名称,nihilityer,应把表名改为[users.nihilityer], 不是只修改display_name。
修改用户名或密码哈希后重新应用并重启;文件用户库关闭了自动监视,当前模板也关闭 网页改密与“忘记密码”流程:
nsetup up -f authelia.toml --force
nsetup restart authelia可选 OIDC provider 是 Authelia 的全局能力,只在 authelia.toml 中配置 HMAC 和
RS256 私钥。每个 OIDC 客户端则由实际使用它的应用 TOML 拥有,具名表键就是
client_id。先生成 provider HMAC、RS256 私钥、客户端 ID 与客户端密钥:
openssl rand -hex 64
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out oidc-rs256.pem
docker run --rm --pull=never authelia/authelia:4.39.20 \
authelia crypto rand --length 72 --charset rfc3986
docker run --rm --pull=never authelia/authelia:4.39.20 \
authelia crypto hash generate pbkdf2 --variant sha512 --random \
--random.length 72 --random.charset rfc3986# authelia.toml
[oidc]
hmac_secret = "...openssl rand 的输出..."
jwk_private_key = """
-----BEGIN PRIVATE KEY-----
...oidc-rs256.pem 的完整内容...
-----END PRIVATE KEY-----
"""# gitea.toml(与 name、services 同级)
[authelia.oidc_clients.gitea]
client_name = "Gitea"
client_secret_hash = '$pbkdf2-sha512$...'
authorization_policy = "two_factor"
redirect_uris = ["https://git.example.com/user/oauth2/authelia/callback"]
scopes = ["openid", "profile", "email", "groups"]
grant_types = ["authorization_code", "refresh_token"]
require_pkce = false
token_endpoint_auth_method = "client_secret_basic"PBKDF2 命令输出的 Random Password 配置到客户端应用,Digest 才写入
client_secret_hash;回调 URI 区分大小写且必须精确一致。公共 SPA/CLI 客户端省略
client_secret_hash,并配置 public = true、require_pkce = true 和
token_endpoint_auth_method = "none"。应用使用的 discovery 地址为
https://<认证门户域名>/.well-known/openid-configuration。OIDC 的
authorization_policy 独立于全局 default_policy。
nsetup up -f gitea.toml --force 会把该应用拥有的客户端同步到
stacks_root/authelia/config/oidc-clients/gitea.yml;从应用 TOML 中删除声明或删除
整个应用项目时,对应片段也会删除。客户端变更后需要执行
nsetup restart authelia;全部应用中的 client_id 必须全局唯一。
需要认证的应用只需在路由中加入 authelia;认证门户自身不会套用认证中间件:
[services.web.traefik]
hosts = ["admin"]
middlewares = ["authelia", "tls"]原生 OIDC 登录与 Traefik ForwardAuth 是两种独立集成方式;只使用应用自身的 OIDC
登录时,通常不需要再给该路由添加 authelia 中间件。
整体更新已有项目需要 --force。静态站点再次上传 --assets 时会整体替换站点文件;
Traefik 的 acme.json 与 Authelia 的 SQLite 状态会在更新时保留。导出的基础设施
TOML 含密钥,-o 创建的文件权限为 0600。
可导入架构支持字段子集内的 Compose 文件:
nsetup import media -f compose.yaml --env-file .env --start
nsetup edit media --service web --version 1.1 --start
nsetup export media -o media.toml导入遇到未知 Compose 字段、命名卷、相对 bind mount 或未固定版本镜像时直接报错。
export 总是从当前 compose.yaml + .env 反解 TOML,不依赖另存的声明副本;输出文件
已存在时不会覆盖。复杂编辑参数请查看 nsetup edit --help。
nsetup list
nsetup show media
nsetup start media
nsetup stop media
nsetup restart media
nsetup pull media
nsetup build media
nsetup logs media --tail 300
nsetup logs media --follow
nsetup rm mediapull 在交互式终端中原地刷新单行进度条;重定向或管道中仍输出稳定的制表符分隔事件。
up、import、edit、start、stop、restart、build、rm 会立即显示排队和
执行阶段,最终结果仍单独写入 stdout。logs --follow 不占用变更锁,退出客户端后
daemon 会终止对应的 Compose 日志进程。
删除项目不会删除 bind mount 指向的数据。rm --force 仅跳过交互确认。
daemon 配置位于 /etc/nsetup/config.toml,使用扁平键:
domain = "example.com"
stacks_root = "/var/lib/nsetup/stacks"
data_roots = ["/srv/data"]
listen = "unix:///run/nsetup/nsetup.sock"
docker_socket = "/var/run/docker.sock"bind mount 源路径只能位于 data_roots 或 stacks_root,Docker socket 只允许精确
匹配。所有路径会先归一化并解析已有符号链接。nihility 组能够间接控制 Docker,
应只授予可信管理员。
需要远程管理时,将 daemon 的 listen 设为 TCP 地址,并为任意命令同时指定:
nsetup --endpoint http://192.168.1.10:50051 \
--token-file ./nsetup.auth.token statusTCP token 存放于 /etc/nsetup/auth.token(0600)。跨主机流量还应置于 VPN 或
TLS HTTP/2 代理之后。
详细设计与约束见 架构文档,gRPC 接口见 proto/nsetup.proto。提交前运行:
cargo fmt --check
cargo test --quiet
cargo clippy --all-targets --quiet -- -D warnings
git diff --check