Compare commits
5 commits
358f2296ce
...
f2501365c4
| Author | SHA1 | Date | |
|---|---|---|---|
| f2501365c4 | |||
| 8ceba4bcfd | |||
| 28d00db42d | |||
| 3fae855aec | |||
| 12b77a3960 |
8 changed files with 302 additions and 86 deletions
|
|
@ -109,6 +109,8 @@ in
|
||||||
matrix-authentication-service # mas-cli for migration
|
matrix-authentication-service # mas-cli for migration
|
||||||
];
|
];
|
||||||
|
|
||||||
|
security.sudo.enable = true;
|
||||||
|
|
||||||
networking.hostName = hostname;
|
networking.hostName = hostname;
|
||||||
|
|
||||||
services.openssh = {
|
services.openssh = {
|
||||||
|
|
|
||||||
|
|
@ -571,6 +571,36 @@ in {
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Provision an OpenClaw bot user in MAS and issue a long-lived compatibility
|
||||||
|
# access token. Runs once; the token is persisted in /var/lib/mas/.
|
||||||
|
systemd.services.matrix-bot-provision = {
|
||||||
|
description = "Provision Matrix bot user for OpenClaw";
|
||||||
|
after = [ "matrix-authentication-service.service" ];
|
||||||
|
before = [ "podman-openclaw.service" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
};
|
||||||
|
path = [ "/run/wrappers" ];
|
||||||
|
script = ''
|
||||||
|
TOKEN_FILE="/persist/openclaw-token"
|
||||||
|
if [ ! -f "$TOKEN_FILE" ]; then
|
||||||
|
sudo -u mas ${masPackage}/bin/mas-cli manage register-user \
|
||||||
|
--config /run/mas/config.yaml \
|
||||||
|
--username openclaw \
|
||||||
|
--display-name "OpenClaw Bot" \
|
||||||
|
--yes 2>/dev/null || true
|
||||||
|
TOKEN=$(sudo -u mas ${masPackage}/bin/mas-cli manage issue-compatibility-token \
|
||||||
|
--config /run/mas/config.yaml \
|
||||||
|
openclaw OPENCLAW 2>&1 \
|
||||||
|
| ${pkgs.gnugrep}/bin/grep -oP 'token issued: \K\S+')
|
||||||
|
echo "$TOKEN" > "$TOKEN_FILE"
|
||||||
|
chmod 0444 "$TOKEN_FILE"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
# Mattermost bridge (bridgev2 — attrs replace entirely, so include all needed fields)
|
# Mattermost bridge (bridgev2 — attrs replace entirely, so include all needed fields)
|
||||||
services.mautrix-mattermost = {
|
services.mautrix-mattermost = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ in
|
||||||
environment.persistence."/nix/persist/system" = {
|
environment.persistence."/nix/persist/system" = {
|
||||||
hideMounts = true;
|
hideMounts = true;
|
||||||
directories = [
|
directories = [
|
||||||
|
"/var/lib/downloads"
|
||||||
"/var/lib/pyload"
|
"/var/lib/pyload"
|
||||||
"/var/lib/jellyfin"
|
"/var/lib/jellyfin"
|
||||||
"/var/lib/audiobookshelf"
|
"/var/lib/audiobookshelf"
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ let
|
||||||
# to a non-local peer means a download is in flight.
|
# to a non-local peer means a download is in flight.
|
||||||
pyload_conns=$(${pkgs.iproute2}/bin/ss -H -t -n -p \
|
pyload_conns=$(${pkgs.iproute2}/bin/ss -H -t -n -p \
|
||||||
state established state syn-sent state syn-recv 2>/dev/null \
|
state established state syn-sent state syn-recv 2>/dev/null \
|
||||||
| grep -F '"pyload"' \
|
| grep -F 'pyload' \
|
||||||
| awk '{print $5}' \
|
| awk '{print $5}' \
|
||||||
| grep -Ev '${localPeerRegex}' || true)
|
| grep -Ev '${localPeerRegex}' || true)
|
||||||
if [[ -n "$pyload_conns" ]]; then
|
if [[ -n "$pyload_conns" ]]; then
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
./modules/bitwarden
|
./modules/bitwarden
|
||||||
./modules/authelia.nix
|
./modules/authelia.nix
|
||||||
./modules/collabora.nix
|
./modules/collabora.nix
|
||||||
|
./modules/ocis.nix
|
||||||
./modules/nextcloud
|
./modules/nextcloud
|
||||||
./modules/rustdesk.nix
|
./modules/rustdesk.nix
|
||||||
./modules/postgresql.nix
|
./modules/postgresql.nix
|
||||||
|
|
@ -54,6 +55,11 @@
|
||||||
"openssl-1.1.1w"
|
"openssl-1.1.1w"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# oCIS (ownCloud Infinite Scale) has an unfree license
|
||||||
|
nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
|
||||||
|
"ocis_5-bin"
|
||||||
|
];
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
vim
|
vim
|
||||||
davfs2
|
davfs2
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,7 @@ in {
|
||||||
};
|
};
|
||||||
settings = {
|
settings = {
|
||||||
theme = "dark";
|
theme = "dark";
|
||||||
default_redirection_url = "https://cloonar.com";
|
|
||||||
|
|
||||||
# log = {
|
# log = {
|
||||||
# level = "debug";
|
# level = "debug";
|
||||||
# format = "text";
|
# format = "text";
|
||||||
|
|
@ -117,27 +116,24 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
session = {
|
session = {
|
||||||
name = "authelia_session";
|
|
||||||
expiration = "12h";
|
|
||||||
inactivity = "45m";
|
|
||||||
remember_me_duration = "1M";
|
|
||||||
domain = "cloonar.com";
|
|
||||||
redis = {
|
redis = {
|
||||||
host = "/run/redis-authelia/redis.sock";
|
host = "/run/redis-authelia/redis.sock";
|
||||||
};
|
};
|
||||||
# todo: enable with 4.38
|
# Authelia 4.38+ replaced top-level session.domain with per-cookie
|
||||||
# cookies = [
|
# entries. Each entry needs an authelia_url, so only cloonar.com is
|
||||||
# {
|
# configured here — adding cloonar.dev / gbv-aktuell.at would require
|
||||||
# domain = "cloonar.com";
|
# separate Authelia endpoints on those domains.
|
||||||
# }
|
cookies = [
|
||||||
# {
|
{
|
||||||
# domain = "cloonar.dev";
|
name = "authelia_session";
|
||||||
# }
|
domain = "cloonar.com";
|
||||||
# {
|
authelia_url = "https://auth.cloonar.com";
|
||||||
# domain = "gbv-aktuell.at";
|
default_redirection_url = "https://cloonar.com";
|
||||||
# same_site = "strict";
|
expiration = "12h";
|
||||||
# }
|
inactivity = "45m";
|
||||||
# ];
|
remember_me = "1M";
|
||||||
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
regulation = {
|
regulation = {
|
||||||
|
|
@ -173,6 +169,23 @@ in {
|
||||||
oidc = {
|
oidc = {
|
||||||
## The other portions of the mandatory OpenID Connect 1.0 configuration go here.
|
## The other portions of the mandatory OpenID Connect 1.0 configuration go here.
|
||||||
## See: https://www.authelia.com/c/oidc
|
## See: https://www.authelia.com/c/oidc
|
||||||
|
lifespans = {
|
||||||
|
custom = {
|
||||||
|
ocis = {
|
||||||
|
access_token = "2 days";
|
||||||
|
refresh_token = "3 days";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
cors = {
|
||||||
|
endpoints = [
|
||||||
|
"authorization"
|
||||||
|
"token"
|
||||||
|
"revocation"
|
||||||
|
"introspection"
|
||||||
|
"userinfo"
|
||||||
|
];
|
||||||
|
};
|
||||||
authorization_policies = {
|
authorization_policies = {
|
||||||
"admin-only" = {
|
"admin-only" = {
|
||||||
default_policy = "deny";
|
default_policy = "deny";
|
||||||
|
|
@ -284,6 +297,79 @@ in {
|
||||||
];
|
];
|
||||||
userinfo_signing_algorithm = "none";
|
userinfo_signing_algorithm = "none";
|
||||||
}
|
}
|
||||||
|
# oCIS (ownCloud Infinite Scale) - web client (public, PKCE)
|
||||||
|
{
|
||||||
|
id = "ocis";
|
||||||
|
description = "ownCloud Infinite Scale";
|
||||||
|
lifespan = "ocis";
|
||||||
|
public = true;
|
||||||
|
authorization_policy = "internal";
|
||||||
|
require_pkce = true;
|
||||||
|
pkce_challenge_method = "S256";
|
||||||
|
redirect_uris = [
|
||||||
|
"https://files.cloonar.com/"
|
||||||
|
"https://files.cloonar.com/oidc-callback.html"
|
||||||
|
"https://files.cloonar.com/oidc-silent-redirect.html"
|
||||||
|
"https://files.cloonar.com/apps/openidconnect/redirect"
|
||||||
|
];
|
||||||
|
scopes = [ "openid" "offline_access" "groups" "profile" "email" ];
|
||||||
|
response_types = [ "code" ];
|
||||||
|
grant_types = [ "authorization_code" "refresh_token" ];
|
||||||
|
access_token_signed_response_alg = "none";
|
||||||
|
userinfo_signing_algorithm = "none";
|
||||||
|
token_endpoint_auth_method = "none";
|
||||||
|
}
|
||||||
|
# oCIS Desktop - static credentials hardcoded in the oCIS desktop app
|
||||||
|
{
|
||||||
|
id = "xdXOt13JKxym1B1QcEncf2XDkLAexMBFwiT9j6EfhhHFJhs2KM9jbjTmf8JBXE69";
|
||||||
|
description = "ownCloud Infinite Scale (Desktop)";
|
||||||
|
secret = "$pbkdf2-sha512$310000$NR4tztBecptj1ZiITK/Ktw$GkFNBfq1B3T1lDTKMci1aO8iulQFNlEtfydLwTrNTKIfrQFjM7EiOBaHGOBC7ohPaNfYCRAYYzcP2fDQf5XRGQ";
|
||||||
|
public = false;
|
||||||
|
authorization_policy = "internal";
|
||||||
|
require_pkce = true;
|
||||||
|
pkce_challenge_method = "S256";
|
||||||
|
redirect_uris = [ "http://127.0.0.1" "http://localhost" ];
|
||||||
|
scopes = [ "openid" "offline_access" "groups" "profile" "email" ];
|
||||||
|
response_types = [ "code" ];
|
||||||
|
grant_types = [ "authorization_code" "refresh_token" ];
|
||||||
|
access_token_signed_response_alg = "none";
|
||||||
|
userinfo_signing_algorithm = "none";
|
||||||
|
token_endpoint_auth_method = "client_secret_basic";
|
||||||
|
}
|
||||||
|
# oCIS Android - static credentials hardcoded in the oCIS Android app
|
||||||
|
{
|
||||||
|
id = "e4rAsNUSIUs0lF4nbv9FmCeUkTlV9GdgTLDH1b5uie7syb90SzEVrbN7HIpmWJeD";
|
||||||
|
description = "ownCloud Infinite Scale (Android)";
|
||||||
|
secret = "$pbkdf2-sha512$310000$NjEumkph77Gql.CH0Oq3zg$I9ubOZ3VRCXPbHpW1U4bQmvLgP5DdiFeGgple2nIjtUJsFgkdiV/hcCt1h6adr1uvJSJAtHDRnMhYf3Zp2BpcQ";
|
||||||
|
public = false;
|
||||||
|
authorization_policy = "internal";
|
||||||
|
require_pkce = true;
|
||||||
|
pkce_challenge_method = "S256";
|
||||||
|
redirect_uris = [ "oc://android.owncloud.com" ];
|
||||||
|
scopes = [ "openid" "offline_access" "groups" "profile" "email" ];
|
||||||
|
response_types = [ "code" ];
|
||||||
|
grant_types = [ "authorization_code" "refresh_token" ];
|
||||||
|
access_token_signed_response_alg = "none";
|
||||||
|
userinfo_signing_algorithm = "none";
|
||||||
|
token_endpoint_auth_method = "client_secret_basic";
|
||||||
|
}
|
||||||
|
# oCIS iOS - static credentials hardcoded in the oCIS iOS app
|
||||||
|
{
|
||||||
|
id = "mxd5OQDk6es5LzOzRvidJNfXLUZS2oN3oUFeXPP8LpPrhx3UroJFduGEYIBOxkY1";
|
||||||
|
description = "ownCloud Infinite Scale (iOS)";
|
||||||
|
secret = "$pbkdf2-sha512$310000$.nIk0IUua7n8VAUoR85yyA$6UhT/gi7spH/0PRqTa6clz7QMRSmP/FZ0BDIumJupM4V2Ai6MgGKdzlEaNTc2IDqpGL3NxF626g4zAHFRgD7Zg";
|
||||||
|
public = false;
|
||||||
|
authorization_policy = "internal";
|
||||||
|
require_pkce = true;
|
||||||
|
pkce_challenge_method = "S256";
|
||||||
|
redirect_uris = [ "oc://ios.owncloud.com" "oc.ios://ios.owncloud.com" ];
|
||||||
|
scopes = [ "openid" "offline_access" "groups" "profile" "email" ];
|
||||||
|
response_types = [ "code" ];
|
||||||
|
grant_types = [ "authorization_code" "refresh_token" ];
|
||||||
|
access_token_signed_response_alg = "none";
|
||||||
|
userinfo_signing_algorithm = "none";
|
||||||
|
token_endpoint_auth_method = "client_secret_basic";
|
||||||
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -323,16 +409,13 @@ in {
|
||||||
proxy_connect_timeout 360;
|
proxy_connect_timeout 360;
|
||||||
|
|
||||||
# Basic Proxy Config
|
# Basic Proxy Config
|
||||||
proxy_set_header Host $host;
|
# Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host
|
||||||
|
# and Connection are already set by recommendedProxySettings; redefining
|
||||||
|
# them here caused duplicate headers and fasthttp "too many Host headers" 400s.
|
||||||
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
|
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
proxy_set_header X-Forwarded-Host $http_host;
|
|
||||||
proxy_set_header X-Forwarded-Uri $request_uri;
|
proxy_set_header X-Forwarded-Uri $request_uri;
|
||||||
proxy_set_header X-Forwarded-Ssl on;
|
proxy_set_header X-Forwarded-Ssl on;
|
||||||
proxy_redirect http:// $scheme://;
|
proxy_redirect http:// $scheme://;
|
||||||
proxy_set_header Connection "";
|
|
||||||
proxy_cache_bypass $cookie_session;
|
proxy_cache_bypass $cookie_session;
|
||||||
proxy_no_cache $cookie_session;
|
proxy_no_cache $cookie_session;
|
||||||
proxy_buffers 64 256k;
|
proxy_buffers 64 256k;
|
||||||
|
|
|
||||||
93
hosts/web-arm/modules/ocis.nix
Normal file
93
hosts/web-arm/modules/ocis.nix
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
sops.secrets.ocis-admin-password = {
|
||||||
|
owner = "ocis";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Upstream services.ocis module adds ReadOnlyPaths = [ configDir ] to the
|
||||||
|
# systemd unit, which makes systemd fail the namespace setup if the path
|
||||||
|
# does not exist, and it never runs `ocis init` to populate ocis.yaml with
|
||||||
|
# the service's internal secrets. Run init in a separate oneshot so the
|
||||||
|
# sandbox restrictions of ocis.service don't block writes to configDir.
|
||||||
|
systemd.services.ocis-init = {
|
||||||
|
description = "Initialize oCIS config (one-shot)";
|
||||||
|
before = [ "ocis.service" ];
|
||||||
|
requiredBy = [ "ocis.service" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
User = "ocis";
|
||||||
|
Group = "ocis";
|
||||||
|
StateDirectory = "ocis";
|
||||||
|
LoadCredential = "admin-password:${config.sops.secrets.ocis-admin-password.path}";
|
||||||
|
};
|
||||||
|
|
||||||
|
script = ''
|
||||||
|
install -d -m 0700 /var/lib/ocis/config
|
||||||
|
if [ ! -f /var/lib/ocis/config/ocis.yaml ]; then
|
||||||
|
${lib.getExe pkgs.ocis_5-bin} init \
|
||||||
|
--config-path /var/lib/ocis/config \
|
||||||
|
--admin-password "$(cat "$CREDENTIALS_DIRECTORY/admin-password")" \
|
||||||
|
--insecure true
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
services.ocis = {
|
||||||
|
enable = true;
|
||||||
|
url = "https://files.cloonar.com";
|
||||||
|
address = "127.0.0.1";
|
||||||
|
port = 9200;
|
||||||
|
stateDir = "/var/lib/ocis";
|
||||||
|
configDir = "/var/lib/ocis/config";
|
||||||
|
environment = {
|
||||||
|
# Proxy - SSL terminated at nginx
|
||||||
|
PROXY_TLS = "false";
|
||||||
|
OCIS_INSECURE = "false";
|
||||||
|
|
||||||
|
# OIDC - Authelia
|
||||||
|
PROXY_OIDC_ISSUER = "https://auth.cloonar.com";
|
||||||
|
PROXY_OIDC_REWRITE_WELLKNOWN = "true";
|
||||||
|
PROXY_OIDC_ACCESS_TOKEN_VERIFY_METHOD = "none";
|
||||||
|
PROXY_OIDC_SKIP_USER_INFO = "false";
|
||||||
|
WEB_OIDC_CLIENT_ID = "ocis";
|
||||||
|
|
||||||
|
# Auto-provision user accounts from OIDC claims
|
||||||
|
PROXY_AUTOPROVISION_ACCOUNTS = "true";
|
||||||
|
PROXY_AUTOPROVISION_CLAIM_USERNAME = "preferred_username";
|
||||||
|
PROXY_AUTOPROVISION_CLAIM_EMAIL = "email";
|
||||||
|
PROXY_AUTOPROVISION_CLAIM_DISPLAYNAME = "name";
|
||||||
|
PROXY_AUTOPROVISION_CLAIM_GROUPS = "groups";
|
||||||
|
|
||||||
|
# Disable demo users
|
||||||
|
IDM_CREATE_DEMO_USERS = "false";
|
||||||
|
|
||||||
|
# Move internal services off their defaults where Prometheus exporters
|
||||||
|
# already bind on this host:
|
||||||
|
# - node-exporter owns 9100 (oCIS web default)
|
||||||
|
# - blackbox-exporter owns 9115 (oCIS webdav default)
|
||||||
|
WEB_HTTP_ADDR = "127.0.0.1:19100";
|
||||||
|
WEBDAV_HTTP_ADDR = "127.0.0.1:19115";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Nginx reverse proxy
|
||||||
|
services.nginx.virtualHosts."files.cloonar.com" = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
acmeRoot = null;
|
||||||
|
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://127.0.0.1:9200";
|
||||||
|
proxyWebsockets = true;
|
||||||
|
extraConfig = ''
|
||||||
|
client_max_body_size 10G;
|
||||||
|
proxy_read_timeout 600s;
|
||||||
|
proxy_send_timeout 600s;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue