302 lines
8.8 KiB
Nix
302 lines
8.8 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
shb,
|
|
...
|
|
}:
|
|
let
|
|
cfg = config.shb.open-webui;
|
|
|
|
roleClaim = "openwebui_groups";
|
|
oauthScopes = [
|
|
"openid"
|
|
"email"
|
|
"profile"
|
|
"groups"
|
|
"${roleClaim}"
|
|
];
|
|
in
|
|
{
|
|
imports = [
|
|
../../lib/module.nix
|
|
../blocks/nginx.nix
|
|
];
|
|
|
|
options.shb.open-webui = {
|
|
enable = lib.mkEnableOption "the Open-WebUI service";
|
|
|
|
subdomain = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "Subdomain under which Open-WebUI will be served.";
|
|
default = "open-webui";
|
|
};
|
|
|
|
domain = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "domain under which Open-WebUI will be served.";
|
|
example = "mydomain.com";
|
|
};
|
|
|
|
ssl = lib.mkOption {
|
|
description = "Path to SSL files";
|
|
type = lib.types.nullOr shb.contracts.ssl.certs;
|
|
default = null;
|
|
};
|
|
|
|
port = lib.mkOption {
|
|
type = lib.types.port;
|
|
description = "Port Open-WebUI listens to incoming requests.";
|
|
default = 12444;
|
|
};
|
|
|
|
environment = lib.mkOption {
|
|
type = lib.types.attrsOf lib.types.str;
|
|
description = "Extra environment variables. See https://docs.openwebui.com/getting-started/env-configuration";
|
|
default = { };
|
|
example = ''
|
|
{
|
|
WEBUI_NAME = "SelfHostBlocks";
|
|
|
|
OLLAMA_BASE_URL = "http://127.0.0.1:''${toString config.services.ollama.port}";
|
|
RAG_EMBEDDING_MODEL = "nomic-embed-text:v1.5";
|
|
|
|
ENABLE_OPENAI_API = "True";
|
|
OPENAI_API_BASE_URL = "http://127.0.0.1:''${toString config.services.llama-cpp.port}";
|
|
ENABLE_WEB_SEARCH = "True";
|
|
RAG_EMBEDDING_ENGINE = "openai";
|
|
}
|
|
'';
|
|
};
|
|
|
|
ldap = lib.mkOption {
|
|
description = ''
|
|
Setup LDAP integration.
|
|
'';
|
|
default = { };
|
|
type = lib.types.submodule {
|
|
options = {
|
|
userGroup = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "Group users must belong to to be able to login.";
|
|
default = "open-webui_user";
|
|
};
|
|
|
|
adminGroup = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "Group users must belong to to have administrator privileges.";
|
|
default = "open-webui_admin";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
sso = lib.mkOption {
|
|
description = ''
|
|
Setup SSO integration.
|
|
'';
|
|
default = { };
|
|
type = lib.types.submodule {
|
|
options = {
|
|
enable = lib.mkEnableOption "SSO integration.";
|
|
|
|
authEndpoint = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "Endpoint to the SSO provider.";
|
|
example = "https://authelia.example.com";
|
|
};
|
|
|
|
clientID = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "Client ID for the OIDC endpoint.";
|
|
default = "open-webui";
|
|
};
|
|
|
|
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";
|
|
};
|
|
|
|
sharedSecret = lib.mkOption {
|
|
description = "OIDC shared secret for Open-WebUI.";
|
|
type = lib.types.submodule {
|
|
options = shb.contracts.secret.mkRequester {
|
|
owner = "open-webui";
|
|
restartUnits = [ "open-webui.service" ];
|
|
};
|
|
};
|
|
};
|
|
|
|
sharedSecretForAuthelia = lib.mkOption {
|
|
description = "OIDC shared secret for Authelia. Must be the same as `sharedSecret`";
|
|
type = lib.types.submodule {
|
|
options = shb.contracts.secret.mkRequester {
|
|
mode = "0400";
|
|
ownerText = "config.shb.authelia.autheliaUser";
|
|
owner = config.shb.authelia.autheliaUser;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
backup = lib.mkOption {
|
|
description = ''
|
|
Backup state directory.
|
|
'';
|
|
default = { };
|
|
type = lib.types.submodule {
|
|
options = shb.contracts.backup.mkRequester {
|
|
user = "open-webui";
|
|
sourceDirectories = [
|
|
config.services.open-webui.stateDir
|
|
];
|
|
sourceDirectoriesText = "[ config.services.open-webui.stateDir ]";
|
|
};
|
|
};
|
|
};
|
|
|
|
dashboard = lib.mkOption {
|
|
description = ''
|
|
Dashboard contract consumer
|
|
'';
|
|
default = { };
|
|
type = lib.types.submodule {
|
|
options = shb.contracts.dashboard.mkRequester {
|
|
externalUrl = "https://${cfg.subdomain}.${cfg.domain}";
|
|
externalUrlText = "https://\${config.shb.open-webui.subdomain}.\${config.shb.open-webui.domain}";
|
|
internalUrl = "http://127.0.0.1:${toString cfg.port}";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
config = (
|
|
lib.mkMerge [
|
|
(lib.mkIf cfg.enable {
|
|
users.users.open-webui = {
|
|
isSystemUser = true;
|
|
group = "open-webui";
|
|
};
|
|
users.groups.open-webui = { };
|
|
|
|
services.open-webui = {
|
|
enable = true;
|
|
|
|
host = "127.0.0.1";
|
|
inherit (cfg) port;
|
|
|
|
environment = {
|
|
WEBUI_URL = "https://${cfg.subdomain}.${cfg.domain}";
|
|
|
|
ENABLE_PERSISTENT_CONFIG = "False";
|
|
|
|
ANONYMIZED_TELEMETRY = "False";
|
|
DO_NOT_TRACK = "True";
|
|
SCARF_NO_ANALYTICS = "True";
|
|
|
|
ENABLE_VERSION_UPDATE_CHECK = "False";
|
|
}
|
|
// cfg.environment;
|
|
};
|
|
|
|
systemd.services.open-webui.path = [
|
|
pkgs.ffmpeg-headless
|
|
];
|
|
|
|
shb.nginx.vhosts = [
|
|
{
|
|
inherit (cfg) subdomain domain ssl;
|
|
upstream = "http://127.0.0.1:${toString cfg.port}/";
|
|
extraConfig = ''
|
|
proxy_read_timeout 300s;
|
|
proxy_send_timeout 300s;
|
|
'';
|
|
}
|
|
];
|
|
})
|
|
(lib.mkIf (cfg.enable && cfg.sso.enable) {
|
|
shb.lldap.ensureGroups = {
|
|
${cfg.ldap.userGroup} = { };
|
|
${cfg.ldap.adminGroup} = { };
|
|
};
|
|
|
|
services.open-webui = {
|
|
package = pkgs.open-webui.overrideAttrs (finalAttrs: {
|
|
patches = [
|
|
../../patches/0001-selfhostblocks-never-onboard.patch
|
|
../../patches/0002-selfhostblocks-do-not-allow-unauthorized-roles.patch
|
|
];
|
|
});
|
|
environment = {
|
|
ENABLE_SIGNUP = "False";
|
|
WEBUI_AUTH = "True";
|
|
ENABLE_FORWARD_USER_INFO_HEADERS = "True";
|
|
ENABLE_OAUTH_SIGNUP = "True";
|
|
OAUTH_UPDATE_PICTURE_ON_LOGIN = "True";
|
|
OAUTH_CLIENT_ID = cfg.sso.clientID;
|
|
OPENID_PROVIDER_URL = "${cfg.sso.authEndpoint}/.well-known/openid-configuration";
|
|
OAUTH_PROVIDER_NAME = "Single Sign-On";
|
|
OAUTH_USERNAME_CLAIM = "preferred_username";
|
|
ENABLE_OAUTH_ROLE_MANAGEMENT = "True";
|
|
OAUTH_ALLOWED_ROLES = "user,admin";
|
|
OAUTH_ADMIN_ROLES = "admin";
|
|
OAUTH_ROLES_CLAIM = roleClaim;
|
|
OAUTH_SCOPES = lib.concatStringsSep " " oauthScopes;
|
|
};
|
|
};
|
|
|
|
shb.authelia.extraDefinitions = {
|
|
user_attributes.${roleClaim}.expression =
|
|
''"${cfg.ldap.adminGroup}" in groups ? ["admin"] : ("${cfg.ldap.userGroup}" in groups ? ["user"] : [""])'';
|
|
};
|
|
shb.authelia.extraOidcClaimsPolicies.${roleClaim} = {
|
|
custom_claims = {
|
|
"${roleClaim}" = { };
|
|
};
|
|
};
|
|
shb.authelia.extraOidcScopes."${roleClaim}" = {
|
|
claims = [ "${roleClaim}" ];
|
|
};
|
|
|
|
shb.authelia.oidcClients = [
|
|
{
|
|
client_id = cfg.sso.clientID;
|
|
client_name = "Open WebUI";
|
|
client_secret.source = cfg.sso.sharedSecretForAuthelia.result.path;
|
|
claims_policy = "${roleClaim}";
|
|
public = false;
|
|
authorization_policy = cfg.sso.authorization_policy;
|
|
redirect_uris = [
|
|
"https://${cfg.subdomain}.${cfg.domain}/oauth/oidc/callback"
|
|
];
|
|
scopes = oauthScopes;
|
|
}
|
|
];
|
|
|
|
systemd.services.open-webui.serviceConfig.EnvironmentFile = "/run/open-webui/secrets.env";
|
|
systemd.tmpfiles.rules = [
|
|
"d '/run/open-webui' 0750 root root - -"
|
|
];
|
|
systemd.services.open-webui-pre = {
|
|
script = shb.replaceSecrets {
|
|
userConfig = {
|
|
OAUTH_CLIENT_SECRET.source = cfg.sso.sharedSecret.result.path;
|
|
};
|
|
resultPath = "/run/open-webui/secrets.env";
|
|
generator = shb.toEnvVar;
|
|
};
|
|
serviceConfig.Type = "oneshot";
|
|
wantedBy = [ "multi-user.target" ];
|
|
before = [ "open-webui.service" ];
|
|
requiredBy = [ "open-webui.service" ];
|
|
};
|
|
})
|
|
]
|
|
);
|
|
}
|