add declarative sso integration for nextcloud
This commit is contained in:
parent
8a5f4e3bf2
commit
1cf6d264e4
7 changed files with 465 additions and 97 deletions
|
@ -6,6 +6,7 @@ let
|
||||||
contracts = pkgs.callPackage ../contracts {};
|
contracts = pkgs.callPackage ../contracts {};
|
||||||
|
|
||||||
fqdn = "${cfg.subdomain}.${cfg.domain}";
|
fqdn = "${cfg.subdomain}.${cfg.domain}";
|
||||||
|
fqdnWithPort = if isNull cfg.port then fqdn else "${fqdn}:${toString cfg.port}";
|
||||||
|
|
||||||
autheliaCfg = config.services.authelia.instances.${fqdn};
|
autheliaCfg = config.services.authelia.instances.${fqdn};
|
||||||
|
|
||||||
|
@ -36,6 +37,12 @@ in
|
||||||
example = "mydomain.com";
|
example = "mydomain.com";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
port = lib.mkOption {
|
||||||
|
description = "If given, adds a port to the `<subdomain>.<domain>` endpoint.";
|
||||||
|
type = lib.types.nullOr lib.types.port;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
|
||||||
ssl = lib.mkOption {
|
ssl = lib.mkOption {
|
||||||
description = "Path to SSL files";
|
description = "Path to SSL files";
|
||||||
type = lib.types.nullOr contracts.ssl.certs;
|
type = lib.types.nullOr contracts.ssl.certs;
|
||||||
|
@ -86,7 +93,11 @@ in
|
||||||
};
|
};
|
||||||
identityProvidersOIDCIssuerPrivateKeyFile = lib.mkOption {
|
identityProvidersOIDCIssuerPrivateKeyFile = lib.mkOption {
|
||||||
type = lib.types.path;
|
type = lib.types.path;
|
||||||
description = "File containing the identity provider OIDC issuer private key.";
|
description = ''
|
||||||
|
File containing the identity provider OIDC issuer private key.
|
||||||
|
|
||||||
|
Generate one with `nix run nixpkgs#openssl -- genrsa -out keypair.pem 2048`
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -99,9 +110,16 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
smtp = lib.mkOption {
|
smtp = lib.mkOption {
|
||||||
description = "SMTP options.";
|
description = ''
|
||||||
default = null;
|
If a string is given, writes notifications to the given path.Otherwise, send notifications
|
||||||
type = lib.types.nullOr (lib.types.submodule {
|
by smtp.
|
||||||
|
|
||||||
|
https://www.authelia.com/configuration/notifications/introduction/
|
||||||
|
'';
|
||||||
|
default = "/tmp/authelia-notifications";
|
||||||
|
type = lib.types.oneOf [
|
||||||
|
lib.types.str
|
||||||
|
(lib.types.nullOr (lib.types.submodule {
|
||||||
options = {
|
options = {
|
||||||
from_address = lib.mkOption {
|
from_address = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
|
@ -131,7 +149,8 @@ in
|
||||||
description = "File containing the password to connect to the SMTP host.";
|
description = "File containing the password to connect to the SMTP host.";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
});
|
}))
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
rules = lib.mkOption {
|
rules = lib.mkOption {
|
||||||
|
@ -175,7 +194,7 @@ in
|
||||||
AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE = toString cfg.secrets.identityProvidersOIDCHMACSecretFile;
|
AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE = toString cfg.secrets.identityProvidersOIDCHMACSecretFile;
|
||||||
AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_PRIVATE_KEY_FILE = toString cfg.secrets.identityProvidersOIDCIssuerPrivateKeyFile;
|
AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_PRIVATE_KEY_FILE = toString cfg.secrets.identityProvidersOIDCIssuerPrivateKeyFile;
|
||||||
|
|
||||||
AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE = lib.mkIf (!(isNull cfg.smtp)) (toString cfg.smtp.passwordFile);
|
AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE = lib.mkIf (!(builtins.isString cfg.smtp)) (toString cfg.smtp.passwordFile);
|
||||||
};
|
};
|
||||||
settings = {
|
settings = {
|
||||||
server.host = "127.0.0.1";
|
server.host = "127.0.0.1";
|
||||||
|
@ -207,7 +226,7 @@ in
|
||||||
};
|
};
|
||||||
totp = {
|
totp = {
|
||||||
disable = "false";
|
disable = "false";
|
||||||
issuer = fqdn;
|
issuer = fqdnWithPort;
|
||||||
algorithm = "sha1";
|
algorithm = "sha1";
|
||||||
digits = "6";
|
digits = "6";
|
||||||
period = "30";
|
period = "30";
|
||||||
|
@ -217,7 +236,7 @@ in
|
||||||
# Inspired from https://www.authelia.com/configuration/session/introduction/ and https://www.authelia.com/configuration/session/redis
|
# Inspired from https://www.authelia.com/configuration/session/introduction/ and https://www.authelia.com/configuration/session/redis
|
||||||
session = {
|
session = {
|
||||||
name = "authelia_session";
|
name = "authelia_session";
|
||||||
domain = cfg.domain;
|
domain = if isNull cfg.port then cfg.domain else "${cfg.domain}:${toString cfg.port}";
|
||||||
same_site = "lax";
|
same_site = "lax";
|
||||||
expiration = "1h";
|
expiration = "1h";
|
||||||
inactivity = "5m";
|
inactivity = "5m";
|
||||||
|
@ -238,10 +257,10 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
notifier = {
|
notifier = {
|
||||||
filesystem = lib.mkIf (isNull cfg.smtp) {
|
filesystem = lib.mkIf (builtins.isString cfg.smtp) {
|
||||||
filename = "/tmp/authelia-notifications";
|
filename = cfg.smtp;
|
||||||
};
|
};
|
||||||
smtp = lib.mkIf (!(isNull cfg.smtp)) {
|
smtp = lib.mkIf (!(builtins.isString cfg.smtp)) {
|
||||||
host = cfg.smtp.host;
|
host = cfg.smtp.host;
|
||||||
port = cfg.smtp.port;
|
port = cfg.smtp.port;
|
||||||
username = cfg.smtp.username;
|
username = cfg.smtp.username;
|
||||||
|
@ -260,7 +279,7 @@ in
|
||||||
];
|
];
|
||||||
rules = [
|
rules = [
|
||||||
{
|
{
|
||||||
domain = fqdn;
|
domain = fqdnWithPort;
|
||||||
policy = "bypass";
|
policy = "bypass";
|
||||||
resources = [
|
resources = [
|
||||||
"^/api/.*"
|
"^/api/.*"
|
||||||
|
|
|
@ -50,9 +50,6 @@ in
|
||||||
let
|
let
|
||||||
commonConfig = {
|
commonConfig = {
|
||||||
services.postgresql.settings = {
|
services.postgresql.settings = {
|
||||||
idle_in_transaction_session_timeout = "30s";
|
|
||||||
idle_session_timeout = "30s";
|
|
||||||
track_io_timing = "true";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -197,6 +197,31 @@ in
|
||||||
shell_command = {
|
shell_command = {
|
||||||
delete_backups = "find ${config.services.home-assistant.configDir}/backups -type f -delete";
|
delete_backups = "find ${config.services.home-assistant.configDir}/backups -type f -delete";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
conversation.intents = {
|
||||||
|
TellJoke = [
|
||||||
|
"Tell [me] (a joke|something funny|a dad joke)"
|
||||||
|
"Raconte [moi] (une blague)"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
sensor = [
|
||||||
|
{
|
||||||
|
name = "random_joke";
|
||||||
|
platform = "rest";
|
||||||
|
json_attributes = ["joke" "id" "status"];
|
||||||
|
value_template = "{{ value_json.joke }}";
|
||||||
|
resource = "https://icanhazdadjoke.com/";
|
||||||
|
scan_interval = "3600";
|
||||||
|
headers.Accept = "application/json";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
intent_script.TellJoke = {
|
||||||
|
speech.text = ''{{ state_attr("sensor.random_joke", "joke") }}'';
|
||||||
|
action = {
|
||||||
|
service = "homeassistant.update_entity";
|
||||||
|
entity_id = "sensor.random_joke";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,10 @@ let
|
||||||
cfg = config.shb.nextcloud;
|
cfg = config.shb.nextcloud;
|
||||||
|
|
||||||
fqdn = "${cfg.subdomain}.${cfg.domain}";
|
fqdn = "${cfg.subdomain}.${cfg.domain}";
|
||||||
|
fqdnWithPort = if isNull cfg.port then fqdn else "${fqdn}:${toString cfg.port}";
|
||||||
|
protocol = if !(isNull cfg.ssl) then "https" else "http";
|
||||||
|
|
||||||
|
ssoFqdnWithPort = if isNull cfg.apps.sso.port then cfg.apps.sso.endpoint else "${cfg.apps.sso.endpoint}:${toString cfg.apps.sso.port}";
|
||||||
|
|
||||||
contracts = pkgs.callPackage ../contracts {};
|
contracts = pkgs.callPackage ../contracts {};
|
||||||
|
|
||||||
|
@ -19,16 +23,40 @@ in
|
||||||
|
|
||||||
subdomain = lib.mkOption {
|
subdomain = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
description = "Subdomain under which Nextcloud will be served.";
|
description = ''
|
||||||
|
Subdomain under which Nextcloud will be served.
|
||||||
|
|
||||||
|
```
|
||||||
|
<subdomain>.<domain>[:<port>]
|
||||||
|
```
|
||||||
|
'';
|
||||||
example = "nextcloud";
|
example = "nextcloud";
|
||||||
};
|
};
|
||||||
|
|
||||||
domain = lib.mkOption {
|
domain = lib.mkOption {
|
||||||
description = "Domain to serve Nextcloud under.";
|
description = ''
|
||||||
|
Domain under which Nextcloud is served.
|
||||||
|
|
||||||
|
```
|
||||||
|
<subdomain>.<domain>[:<port>]
|
||||||
|
```
|
||||||
|
'';
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
example = "domain.com";
|
example = "domain.com";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
port = lib.mkOption {
|
||||||
|
description = ''
|
||||||
|
Port under which Nextcloud will be served. If null is given, then the port is omitted.
|
||||||
|
|
||||||
|
```
|
||||||
|
<subdomain>.<domain>[:<port>]
|
||||||
|
```
|
||||||
|
'';
|
||||||
|
type = lib.types.nullOr lib.types.port;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
|
||||||
ssl = lib.mkOption {
|
ssl = lib.mkOption {
|
||||||
description = "Path to SSL files";
|
description = "Path to SSL files";
|
||||||
type = lib.types.nullOr contracts.ssl.certs;
|
type = lib.types.nullOr contracts.ssl.certs;
|
||||||
|
@ -168,7 +196,11 @@ in
|
||||||
|
|
||||||
jwtSecretFile = lib.mkOption {
|
jwtSecretFile = lib.mkOption {
|
||||||
type = lib.types.nullOr lib.types.path;
|
type = lib.types.nullOr lib.types.path;
|
||||||
description = "File containing the JWT secret. This option is required.";
|
description = ''
|
||||||
|
File containing the JWT secret. This option is required.
|
||||||
|
|
||||||
|
Must be readable by the nextcloud system user.
|
||||||
|
'';
|
||||||
default = null;
|
default = null;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -216,7 +248,7 @@ in
|
||||||
enable = lib.mkEnableOption "LDAP app.";
|
enable = lib.mkEnableOption "LDAP app.";
|
||||||
|
|
||||||
host = lib.mkOption {
|
host = lib.mkOption {
|
||||||
type = lib.types.nullOr lib.types.str;
|
type = lib.types.str;
|
||||||
description = ''
|
description = ''
|
||||||
Host serving the LDAP server.
|
Host serving the LDAP server.
|
||||||
'';
|
'';
|
||||||
|
@ -224,7 +256,7 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
port = lib.mkOption {
|
port = lib.mkOption {
|
||||||
type = lib.types.nullOr lib.types.port;
|
type = lib.types.port;
|
||||||
description = ''
|
description = ''
|
||||||
Port of the service serving the LDAP server.
|
Port of the service serving the LDAP server.
|
||||||
'';
|
'';
|
||||||
|
@ -245,7 +277,11 @@ in
|
||||||
|
|
||||||
adminPasswordFile = lib.mkOption {
|
adminPasswordFile = lib.mkOption {
|
||||||
type = lib.types.path;
|
type = lib.types.path;
|
||||||
description = "File containing the admin password of the LDAP server.";
|
description = ''
|
||||||
|
File containing the admin password of the LDAP server.
|
||||||
|
|
||||||
|
Must be readable by the nextcloud system user.
|
||||||
|
'';
|
||||||
default = "";
|
default = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -257,6 +293,81 @@ in
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sso = lib.mkOption {
|
||||||
|
description = ''
|
||||||
|
SSO Integration App. [Manual](https://docs.nextcloud.com/server/latest/admin_manual/configuration_user/oidc_auth.html)
|
||||||
|
|
||||||
|
Enabling this app will create a new LDAP configuration or update one that exists with
|
||||||
|
the given host.
|
||||||
|
'';
|
||||||
|
default = {};
|
||||||
|
type = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
enable = lib.mkEnableOption "SSO app.";
|
||||||
|
|
||||||
|
endpoint = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "OIDC endpoint for SSO.";
|
||||||
|
example = "https://authelia.example.com";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = lib.mkOption {
|
||||||
|
description = "If given, adds a port to the endpoint.";
|
||||||
|
type = lib.types.nullOr lib.types.port;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
provider = lib.mkOption {
|
||||||
|
type = lib.types.enum [ "Authelia" ];
|
||||||
|
description = "OIDC provider name, used for display.";
|
||||||
|
default = "Authelia";
|
||||||
|
};
|
||||||
|
|
||||||
|
clientID = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Client ID for the OIDC endpoint.";
|
||||||
|
default = "nextcloud";
|
||||||
|
};
|
||||||
|
|
||||||
|
authorization_policy = lib.mkOption {
|
||||||
|
type = lib.types.enum [ "one_factor" "two_factor" ];
|
||||||
|
description = "Require one factor (password) or two factor (device) authentication.";
|
||||||
|
default = "one_factor";
|
||||||
|
};
|
||||||
|
|
||||||
|
secretFile = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = ''
|
||||||
|
File containing the secret for the OIDC endpoint.
|
||||||
|
|
||||||
|
Must be readable by the nextcloud system user.
|
||||||
|
'';
|
||||||
|
default = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
secretFileForAuthelia = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = ''
|
||||||
|
File containing the secret for the OIDC endpoint, must be readable by the Authelia user.
|
||||||
|
|
||||||
|
Must be readable by the authelia system user.
|
||||||
|
'';
|
||||||
|
default = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
fallbackDefaultAuth = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
description = ''
|
||||||
|
Fallback to normal Nextcloud auth if something goes wrong with the SSO app.
|
||||||
|
Usually, you want to enable this to transfer existing users to LDAP and then you
|
||||||
|
can disabled it.
|
||||||
|
'';
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -387,7 +498,7 @@ in
|
||||||
protocol = if !(isNull cfg.ssl) then "https" else "http";
|
protocol = if !(isNull cfg.ssl) then "https" else "http";
|
||||||
in {
|
in {
|
||||||
"overwrite.cli.url" = "${protocol}://${fqdn}";
|
"overwrite.cli.url" = "${protocol}://${fqdn}";
|
||||||
"overwritehost" = if (isNull cfg.externalFqdn) then fqdn else cfg.externalFqdn;
|
"overwritehost" = fqdnWithPort;
|
||||||
# 'trusted_domains' needed otherwise we get this issue https://help.nextcloud.com/t/the-polling-url-does-not-start-with-https-despite-the-login-url-started-with-https/137576/2
|
# 'trusted_domains' needed otherwise we get this issue https://help.nextcloud.com/t/the-polling-url-does-not-start-with-https-despite-the-login-url-started-with-https/137576/2
|
||||||
# TODO: could instead set extraTrustedDomains
|
# TODO: could instead set extraTrustedDomains
|
||||||
"trusted_domains" = [ fqdn ];
|
"trusted_domains" = [ fqdn ];
|
||||||
|
@ -620,5 +731,103 @@ in
|
||||||
'1'
|
'1'
|
||||||
'';
|
'';
|
||||||
})
|
})
|
||||||
|
|
||||||
|
(lib.mkIf cfg.apps.sso.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = cfg.apps.sso.enable -> cfg.apps.ldap.enable;
|
||||||
|
message = "SSO app requires LDAP app to work correctly.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
systemd.services.nextcloud-setup.script =
|
||||||
|
''
|
||||||
|
${occ} app:install oidc_login || :
|
||||||
|
${occ} app:enable oidc_login
|
||||||
|
'';
|
||||||
|
|
||||||
|
systemd.services.nextcloud-setup.preStart =
|
||||||
|
''
|
||||||
|
mkdir -p ${cfg.dataDir}/config
|
||||||
|
cat <<EOF > "${cfg.dataDir}/config/secretFile"
|
||||||
|
{
|
||||||
|
"oidc_login_client_secret": "$(cat ${cfg.apps.sso.secretFile})"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
'';
|
||||||
|
|
||||||
|
services.nextcloud = {
|
||||||
|
secretFile = "${cfg.dataDir}/config/secretFile";
|
||||||
|
|
||||||
|
# See all options at https://github.com/pulsejet/nextcloud-oidc-login
|
||||||
|
extraOptions = {
|
||||||
|
allow_user_to_change_display_name = false;
|
||||||
|
lost_password_link = "disabled";
|
||||||
|
oidc_login_provider_url = ssoFqdnWithPort;
|
||||||
|
oidc_login_client_id = cfg.apps.sso.clientID;
|
||||||
|
|
||||||
|
# Automatically redirect the login page to the provider.
|
||||||
|
oidc_login_auto_redirect = !cfg.apps.sso.fallbackDefaultAuth;
|
||||||
|
# Authelia at least does not support this.
|
||||||
|
oidc_login_end_session_redirect = false;
|
||||||
|
# Redirect to this page after logging out the user
|
||||||
|
oidc_login_logout_url = ssoFqdnWithPort;
|
||||||
|
oidc_login_button_text = "Log in with ${cfg.apps.sso.provider}";
|
||||||
|
oidc_login_hide_password_form = false;
|
||||||
|
oidc_login_use_id_token = true;
|
||||||
|
oidc_login_attributes = {
|
||||||
|
id = "preferred_username";
|
||||||
|
name = "name";
|
||||||
|
mail = "email";
|
||||||
|
groups = "groups";
|
||||||
|
};
|
||||||
|
oidc_login_default_group = "oidc";
|
||||||
|
oidc_login_use_external_storage = false;
|
||||||
|
oidc_login_scope = "openid profile email groups";
|
||||||
|
oidc_login_proxy_ldap = false;
|
||||||
|
# Enable creation of users new to Nextcloud from OIDC login. A user may be known to the
|
||||||
|
# IdP but not (yet) known to Nextcloud. This setting controls what to do in this case.
|
||||||
|
# * 'true' (default): if the user authenticates to the IdP but is not known to Nextcloud,
|
||||||
|
# then they will be returned to the login screen and not allowed entry;
|
||||||
|
# * 'false': if the user authenticates but is not yet known to Nextcloud, then the user
|
||||||
|
# will be automatically created; note that with this setting, you will be allowing (or
|
||||||
|
# relying on) a third-party (the IdP) to create new users
|
||||||
|
oidc_login_disable_registration = false;
|
||||||
|
oidc_login_redir_fallback = cfg.apps.sso.fallbackDefaultAuth;
|
||||||
|
# oidc_login_alt_login_page = "assets/login.php";
|
||||||
|
oidc_login_tls_verify = true;
|
||||||
|
# If you get your groups from the oidc_login_attributes, you might want to create them if
|
||||||
|
# they are not already existing, Default is `false`.
|
||||||
|
oidc_create_groups = true;
|
||||||
|
# Enable use of WebDAV via OIDC bearer token.
|
||||||
|
oidc_login_webdav_enabled = true;
|
||||||
|
oidc_login_password_authentication = false;
|
||||||
|
oidc_login_public_key_caching_time = 86400;
|
||||||
|
oidc_login_min_time_between_jwks_requests = 10;
|
||||||
|
oidc_login_well_known_caching_time = 86400;
|
||||||
|
# If true, nextcloud will download user avatars on login. This may lead to security issues
|
||||||
|
# as the server does not control which URLs will be requested. Use with care.
|
||||||
|
oidc_login_update_avatar = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
shb.authelia.oidcClients = lib.mkIf (cfg.apps.sso.provider == "Authelia") [
|
||||||
|
{
|
||||||
|
id = cfg.apps.sso.clientID;
|
||||||
|
description = "Nextcloud";
|
||||||
|
secretFile = cfg.apps.sso.secretFileForAuthelia;
|
||||||
|
public = "false";
|
||||||
|
authorization_policy = cfg.apps.sso.authorization_policy;
|
||||||
|
redirect_uris = [ "${protocol}://${fqdnWithPort}/apps/oidc_login/oidc" ];
|
||||||
|
scopes = [
|
||||||
|
"openid"
|
||||||
|
"profile"
|
||||||
|
"email"
|
||||||
|
"groups"
|
||||||
|
];
|
||||||
|
userinfo_signing_algorithm = "none";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
})
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ This NixOS module is a service that sets up a [Nextcloud Server](https://nextclo
|
||||||
- Declarative [Apps](#services-nextcloud-server-options-shb.nextcloud.apps) Configuration - no need
|
- Declarative [Apps](#services-nextcloud-server-options-shb.nextcloud.apps) Configuration - no need
|
||||||
to configure those with the UI.
|
to configure those with the UI.
|
||||||
- [LDAP](#services-nextcloud-server-usage-ldap) app: enables app and sets up integration with an existing LDAP server.
|
- [LDAP](#services-nextcloud-server-usage-ldap) app: enables app and sets up integration with an existing LDAP server.
|
||||||
|
- [OIDC](#services-nextcloud-server-usage-oidc) app: enables app and sets up integration with an existing OIDC server.
|
||||||
- [Preview Generator](#services-nextcloud-server-usage-previewgenerator) app: enables app and sets
|
- [Preview Generator](#services-nextcloud-server-usage-previewgenerator) app: enables app and sets
|
||||||
up required cron job.
|
up required cron job.
|
||||||
- [Only Office](#services-nextcloud-server-usage-onlyoffice) app: enables app and sets up Only
|
- [Only Office](#services-nextcloud-server-usage-onlyoffice) app: enables app and sets up Only
|
||||||
|
@ -35,9 +36,36 @@ This NixOS module is a service that sets up a [Nextcloud Server](https://nextclo
|
||||||
|
|
||||||
## Usage {#services-nextcloud-server-usage}
|
## Usage {#services-nextcloud-server-usage}
|
||||||
|
|
||||||
### Basic Configuration {#services-nextcloud-server-usage-basic}
|
### Secrets {#services-nextcloud-server-secrets}
|
||||||
|
|
||||||
This section corresponds to the `basic` target host defined in the [flake.nix](./flake.nix) file.
|
All the secrets should be readable by the nextcloud user.
|
||||||
|
|
||||||
|
Secret should not be stored in the nix store. If you're using
|
||||||
|
[sops-nix](https://github.com/Mic92/sops-nix) and assuming your secrets file is located at
|
||||||
|
`./secrets.yaml`, you can define a secret with:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
sops.secrets."nextcloud/adminpass" = {
|
||||||
|
sopsFile = ./secrets.yaml;
|
||||||
|
mode = "0400";
|
||||||
|
owner = "nextcloud";
|
||||||
|
group = "nextcloud";
|
||||||
|
restartUnits = [ "phpfpm-nextcloud.service" ];
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can use that secret:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
shb.nextcloud.adminPassFile = config.sops.secrets."nextcloud/adminpass".path;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nextcloud through HTTP {#services-nextcloud-server-usage-basic}
|
||||||
|
|
||||||
|
:::: {.note}
|
||||||
|
This section corresponds to the `basic` section of the [Nextcloud
|
||||||
|
demo](demo-nextcloud-server.html#demo-nextcloud-deploy-basic).
|
||||||
|
::::
|
||||||
|
|
||||||
This will set up a Nextcloud service that runs on the NixOS target machine, reachable at
|
This will set up a Nextcloud service that runs on the NixOS target machine, reachable at
|
||||||
`http://nextcloud.example.com`. If the `shb.ssl` block is [enabled](block-ssl.html#usage), the
|
`http://nextcloud.example.com`. If the `shb.ssl` block is [enabled](block-ssl.html#usage), the
|
||||||
|
@ -53,27 +81,19 @@ shb.nextcloud = {
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
The secret should not be stored in the nix store. If you're using
|
After deploying, the Nextcloud server will be reachable at `http://nextcloud.example.com`.
|
||||||
[sops-nix](https://github.com/Mic92/sops-nix) and assuming your secrets file is located at
|
|
||||||
`./secrets.yaml`, you can set the `adminPassFile` option with:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
shb.nextcloud.adminPassFile = config.sops.secrets."nextcloud/adminpass".path;
|
|
||||||
|
|
||||||
sops.secrets."nextcloud/adminpass" = {
|
|
||||||
sopsFile = ./secrets.yaml;
|
|
||||||
mode = "0400";
|
|
||||||
owner = "nextcloud";
|
|
||||||
group = "nextcloud";
|
|
||||||
restartUnits = [ "phpfpm-nextcloud.service" ];
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### With LDAP Support {#services-nextcloud-server-usage-ldap}
|
### With LDAP Support {#services-nextcloud-server-usage-ldap}
|
||||||
|
|
||||||
This section corresponds to the `ldap` target host defined in the [flake.nix](./flake.nix) file. The same information from the [basic](#services-nextcloud-server-usage-basic) section applies, so please read that first.
|
:::: {.note}
|
||||||
|
This section corresponds to the `ldap` section of the [Nextcloud
|
||||||
|
demo](demo-nextcloud-server.html#demo-nextcloud-deploy-ldap).
|
||||||
|
::::
|
||||||
|
|
||||||
This target host uses the LDAP block provided by Self Host Blocks to setup a
|
We will build upon the [Basic Configuration](#services-nextcloud-server-usage-basic) section, so
|
||||||
|
please read that first.
|
||||||
|
|
||||||
|
We will use the LDAP block provided by Self Host Blocks to setup a
|
||||||
[LLDAP](https://github.com/lldap/lldap) service.
|
[LLDAP](https://github.com/lldap/lldap) service.
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
|
@ -84,24 +104,8 @@ shb.ldap = {
|
||||||
ldapPort = 3890;
|
ldapPort = 3890;
|
||||||
webUIListenPort = 17170;
|
webUIListenPort = 17170;
|
||||||
dcdomain = "dc=example,dc=com";
|
dcdomain = "dc=example,dc=com";
|
||||||
ldapUserPasswordFile = config.sops.secrets."lldap/user_password".path;
|
ldapUserPasswordFile = <path/to/ldapUserPasswordSecret>;
|
||||||
jwtSecretFile = config.sops.secrets."lldap/jwt_secret".path;
|
jwtSecretFile = <path/to/ldapJwtSecret>;
|
||||||
};
|
|
||||||
|
|
||||||
sops.secrets."lldap/user_password" = {
|
|
||||||
sopsFile = ./secrets.yaml;
|
|
||||||
mode = "0440";
|
|
||||||
owner = "lldap";
|
|
||||||
group = "lldap";
|
|
||||||
restartUnits = [ "lldap.service" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
sops.secrets."lldap/jwt_secret" = {
|
|
||||||
sopsFile = ./secrets.yaml;
|
|
||||||
mode = "0440";
|
|
||||||
owner = "lldap";
|
|
||||||
group = "lldap";
|
|
||||||
restartUnits = [ "lldap.service" ];
|
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -115,12 +119,110 @@ shb.nextcloud.apps.ldap
|
||||||
port = config.shb.ldap.ldapPort;
|
port = config.shb.ldap.ldapPort;
|
||||||
dcdomain = config.shb.ldap.dcdomain;
|
dcdomain = config.shb.ldap.dcdomain;
|
||||||
adminName = "admin";
|
adminName = "admin";
|
||||||
adminPasswordFile = config.sops.secrets."nextcloud/ldap_admin_password".path;
|
adminPasswordFile = <path/to/ldapUserPasswordSecret>;
|
||||||
userGroup = "nextcloud_user";
|
userGroup = "nextcloud_user";
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
It's nice to be able to reference a options that were defined in the ldap block.
|
The `shb.nextcloud.apps.ldap.adminPasswordFile` must be the same as the
|
||||||
|
`shb.ldap.ldapUserPasswordFile`. The other secret can be randomly generated with `nix run
|
||||||
|
nixpkgs#openssl -- rand -hex 64`.
|
||||||
|
|
||||||
|
And that's it. Now, go to the LDAP server at `http://ldap.example.com`, create the `nextcloud_user`
|
||||||
|
group, create a user and add it to the group. When that's done, go back to the Nextcloud server at
|
||||||
|
`http://nextcloud.example.com` and login with that user.
|
||||||
|
|
||||||
|
Note that we cannot create an admin user from the LDAP server, so you need to create a normal user
|
||||||
|
like above, login with it once so it is known to Nextcloud, then logout, login with the admin
|
||||||
|
Nextcloud user and promote that new user to admin level.
|
||||||
|
|
||||||
|
### With OIDC Support {#services-nextcloud-server-usage-oidc}
|
||||||
|
|
||||||
|
:::: {.note}
|
||||||
|
This section corresponds to the `sso` section of the [Nextcloud
|
||||||
|
demo](demo-nextcloud-server.html#demo-nextcloud-deploy-sso).
|
||||||
|
::::
|
||||||
|
|
||||||
|
We will build upon the [Basic Configuration](#services-nextcloud-server-usage-basic) and [With LDAP
|
||||||
|
Support](#services-nextcloud-server-usage-ldap) sections, so please read those first and setup the
|
||||||
|
LDAP app as described above.
|
||||||
|
|
||||||
|
Here though, we must setup SSL certificates because the SSO provider only works with the https
|
||||||
|
protocol. This is actually quite easy thanks to the [SSL block](blocks-ssl.html). For example, with
|
||||||
|
self-signed certificates:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
shb.certs = {
|
||||||
|
cas.selfsigned.myca = {
|
||||||
|
name = "My CA";
|
||||||
|
};
|
||||||
|
certs.selfsigned = {
|
||||||
|
nextcloud = {
|
||||||
|
ca = config.shb.certs.cas.selfsigned.myca;
|
||||||
|
domain = "nextcloud.example.com";
|
||||||
|
};
|
||||||
|
auth = {
|
||||||
|
ca = config.shb.certs.cas.selfsigned.myca;
|
||||||
|
domain = "auth.example.com";
|
||||||
|
};
|
||||||
|
ldap = {
|
||||||
|
ca = config.shb.certs.cas.selfsigned.myca;
|
||||||
|
domain = "ldap.example.com";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
We need to setup the SSO provider, here Authelia thanks to the corresponding SHB block:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
shb.authelia = {
|
||||||
|
enable = true;
|
||||||
|
domain = "example.com";
|
||||||
|
subdomain = "auth";
|
||||||
|
ssl = config.shb.certs.certs.selfsigned.auth;
|
||||||
|
|
||||||
|
ldapEndpoint = "ldap://127.0.0.1:${builtins.toString config.shb.ldap.ldapPort}";
|
||||||
|
dcdomain = config.shb.ldap.dcdomain;
|
||||||
|
|
||||||
|
secrets = {
|
||||||
|
jwtSecretFile = <path/to/autheliaJwtSecret>;
|
||||||
|
ldapAdminPasswordFile = <path/to/ldapUserPasswordSecret>;
|
||||||
|
sessionSecretFile = <path/to/autheliaSessionSecret>;
|
||||||
|
storageEncryptionKeyFile = <path/to/autheliaStorageEncryptionKeySecret>;
|
||||||
|
identityProvidersOIDCHMACSecretFile = <path/to/providersOIDCHMACSecret>;
|
||||||
|
identityProvidersOIDCIssuerPrivateKeyFile = <path/to/providersOIDCIssuerSecret>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
The `shb.authelia.secrets.ldapAdminPasswordFile` must be the same as the
|
||||||
|
`shb.ldap.ldapUserPasswordFile` defined in the previous section. The secrets can be randomly
|
||||||
|
generated with `nix run nixpkgs#openssl -- rand -hex 64`.
|
||||||
|
|
||||||
|
Now, on the Nextcloud side, you need to add the following options:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
shb.nextcloud.ssl = config.shb.certs.certs.selfsigned.nextcloud;
|
||||||
|
|
||||||
|
shb.nextcloud.apps.sso = {
|
||||||
|
enable = true;
|
||||||
|
endpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}";
|
||||||
|
clientID = "nextcloud";
|
||||||
|
fallbackDefaultAuth = false;
|
||||||
|
|
||||||
|
secretFile = <path/to/oidcNextcloudSharedSecret>;
|
||||||
|
secretFileForAuthelia = <path/to/oidcNextcloudSharedSecret>;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Passing the `ssl` option will auto-configure nginx to force SSL connections with the given
|
||||||
|
certificate.
|
||||||
|
|
||||||
|
The `shb.nextcloud.apps.sso.secretFile` and `shb.nextcloud.apps.sso.secretFileForAuthelia` options
|
||||||
|
must have the same content. The former is a file that must be owned by the `nextcloud` user while
|
||||||
|
the latter must be owned by the `authelia` user. I want to avoid needing to define the same secret
|
||||||
|
twice with a future secrets SHB block.
|
||||||
|
|
||||||
### Tweak PHPFpm Config {#services-nextcloud-server-usage-phpfpm}
|
### Tweak PHPFpm Config {#services-nextcloud-server-usage-phpfpm}
|
||||||
|
|
||||||
|
@ -139,6 +241,8 @@ shb.nextcloud.phpFpmPoolSettings = {
|
||||||
|
|
||||||
### Tweak PostgreSQL Settings {#services-nextcloud-server-usage-postgres}
|
### Tweak PostgreSQL Settings {#services-nextcloud-server-usage-postgres}
|
||||||
|
|
||||||
|
These settings will impact all databases.
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
shb.nextcloud.postgresSettings = {
|
shb.nextcloud.postgresSettings = {
|
||||||
max_connections = "100";
|
max_connections = "100";
|
||||||
|
@ -227,6 +331,19 @@ without LDAP integration on a VM with minimal manual steps.
|
||||||
|
|
||||||
On the command line, the `occ` tool is called `nextcloud-occ`.
|
On the command line, the `occ` tool is called `nextcloud-occ`.
|
||||||
|
|
||||||
|
## Debug {#services-nextcloud-server-debug}
|
||||||
|
|
||||||
|
In case of an issue, check the logs for any systemd service mentioned in this section.
|
||||||
|
|
||||||
|
On startup, the oneshot systemd service `nextcloud-setup.service` starts. After it finishes, the
|
||||||
|
`phpfpm-nextcloud.service` starts to serve Nextcloud. The `nginx.service` is used as the reverse
|
||||||
|
proxy. `postgresql.service` run the database.
|
||||||
|
|
||||||
|
Nextcloud' configuration is found at `${shb.nextcloud.dataDir}/config/config.php`. Nginx'
|
||||||
|
configuration can be found with `systemctl cat nginx | grep -om 1 -e "[^ ]\+conf"`.
|
||||||
|
|
||||||
|
Enable verbose logging by setting the `shb.nextcloud.debug` boolean to `true`.
|
||||||
|
|
||||||
## Options Reference {#services-nextcloud-server-options}
|
## Options Reference {#services-nextcloud-server-options}
|
||||||
|
|
||||||
```{=include=} options
|
```{=include=} options
|
||||||
|
|
|
@ -24,9 +24,9 @@ let
|
||||||
};
|
};
|
||||||
|
|
||||||
commonSettings = {
|
commonSettings = {
|
||||||
idle_in_transaction_session_timeout = "30s";
|
# idle_in_transaction_session_timeout = "30s";
|
||||||
idle_session_timeout = "30s";
|
# idle_session_timeout = "30s";
|
||||||
track_io_timing = "true";
|
# track_io_timing = "true";
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,6 +16,7 @@ in
|
||||||
options = {
|
options = {
|
||||||
shb.ssl.enable = lib.mkEnableOption "ssl";
|
shb.ssl.enable = lib.mkEnableOption "ssl";
|
||||||
shb.backup = lib.mkOption { type = lib.types.anything; };
|
shb.backup = lib.mkOption { type = lib.types.anything; };
|
||||||
|
shb.authelia = lib.mkOption { type = lib.types.anything; };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
# ../../modules/blocks/authelia.nix
|
# ../../modules/blocks/authelia.nix
|
||||||
|
|
Loading…
Reference in a new issue