style: format tree
This commit is contained in:
@@ -1,35 +1,38 @@
|
||||
{
|
||||
description = "PoC SP module for Gitea forge service";
|
||||
|
||||
outputs = { self }: {
|
||||
nixosModules.default = import ./module.nix;
|
||||
configPathsNeeded =
|
||||
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta = { lib, ... }: {
|
||||
spModuleSchemaVersion = 1;
|
||||
id = "gitea";
|
||||
name = "Forgejo";
|
||||
description = "Forgejo is a Git forge.";
|
||||
svgIcon = builtins.readFile ./icon.svg;
|
||||
isMovable = true;
|
||||
isRequired = false;
|
||||
backupDescription = "Git repositories, database and user data.";
|
||||
systemdServices = [
|
||||
"forgejo.service"
|
||||
];
|
||||
folders = [
|
||||
"/var/lib/gitea"
|
||||
];
|
||||
license = [
|
||||
lib.licenses.gpl3Plus
|
||||
];
|
||||
homepage = "https://forgejo.org";
|
||||
sourcePage = "https://codeberg.org/forgejo/forgejo";
|
||||
supportLevel = "normal";
|
||||
sso = {
|
||||
userGroup = "sp.gitea.users";
|
||||
adminGroup = "sp.gitea.admins";
|
||||
};
|
||||
outputs =
|
||||
{ self }:
|
||||
{
|
||||
nixosModules.default = import ./module.nix;
|
||||
configPathsNeeded = builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
|
||||
meta =
|
||||
{ lib, ... }:
|
||||
{
|
||||
spModuleSchemaVersion = 1;
|
||||
id = "gitea";
|
||||
name = "Forgejo";
|
||||
description = "Forgejo is a Git forge.";
|
||||
svgIcon = builtins.readFile ./icon.svg;
|
||||
isMovable = true;
|
||||
isRequired = false;
|
||||
backupDescription = "Git repositories, database and user data.";
|
||||
systemdServices = [
|
||||
"forgejo.service"
|
||||
];
|
||||
folders = [
|
||||
"/var/lib/gitea"
|
||||
];
|
||||
license = [
|
||||
lib.licenses.gpl3Plus
|
||||
];
|
||||
homepage = "https://forgejo.org";
|
||||
sourcePage = "https://codeberg.org/forgejo/forgejo";
|
||||
supportLevel = "normal";
|
||||
sso = {
|
||||
userGroup = "sp.gitea.users";
|
||||
adminGroup = "sp.gitea.admins";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@@ -1,10 +1,12 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
sp = config.selfprivacy;
|
||||
stateDir =
|
||||
if sp.useBinds
|
||||
then "/volumes/${cfg.location}/gitea"
|
||||
else "/var/lib/gitea";
|
||||
stateDir = if sp.useBinds then "/volumes/${cfg.location}/gitea" else "/var/lib/gitea";
|
||||
cfg = sp.modules.gitea;
|
||||
themes = [
|
||||
"forgejo-auto"
|
||||
@@ -18,8 +20,7 @@ let
|
||||
oauthClientID = "forgejo";
|
||||
auth-passthru = config.selfprivacy.passthru.auth;
|
||||
oauth2-provider-name = auth-passthru.oauth2-provider-name;
|
||||
redirect-uri =
|
||||
"https://${cfg.subdomain}.${sp.domain}/user/oauth2/${oauth2-provider-name}/callback";
|
||||
redirect-uri = "https://${cfg.subdomain}.${sp.domain}/user/oauth2/${oauth2-provider-name}/callback";
|
||||
oauthDiscoveryURL = auth-passthru.oauth2-discovery-url oauthClientID;
|
||||
|
||||
# SelfPrivacy uses SP Module ID to identify the group!
|
||||
@@ -30,381 +31,401 @@ let
|
||||
linuxGroupOfService = "gitea";
|
||||
forgejoPackage = pkgs.forgejo;
|
||||
|
||||
serviceAccountTokenFP =
|
||||
auth-passthru.mkServiceAccountTokenFP linuxGroupOfService;
|
||||
oauthClientSecretFP =
|
||||
auth-passthru.mkOAuth2ClientSecretFP linuxGroupOfService;
|
||||
serviceAccountTokenFP = auth-passthru.mkServiceAccountTokenFP linuxGroupOfService;
|
||||
oauthClientSecretFP = auth-passthru.mkOAuth2ClientSecretFP linuxGroupOfService;
|
||||
in
|
||||
{
|
||||
options.selfprivacy.modules.gitea = {
|
||||
enable = (lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable Forgejo";
|
||||
}) // {
|
||||
meta = {
|
||||
type = "enable";
|
||||
enable =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable Forgejo";
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "enable";
|
||||
};
|
||||
};
|
||||
};
|
||||
location = (lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Forgejo location";
|
||||
}) // {
|
||||
meta = {
|
||||
type = "location";
|
||||
location =
|
||||
(lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Forgejo location";
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "location";
|
||||
};
|
||||
};
|
||||
};
|
||||
subdomain = (lib.mkOption {
|
||||
default = "git";
|
||||
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||
description = "Subdomain";
|
||||
}) // {
|
||||
meta = {
|
||||
widget = "subdomain";
|
||||
type = "string";
|
||||
regex = "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||
weight = 0;
|
||||
subdomain =
|
||||
(lib.mkOption {
|
||||
default = "git";
|
||||
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||
description = "Subdomain";
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
widget = "subdomain";
|
||||
type = "string";
|
||||
regex = "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
|
||||
weight = 0;
|
||||
};
|
||||
};
|
||||
};
|
||||
appName = (lib.mkOption {
|
||||
default = "SelfPrivacy git Service";
|
||||
type = lib.types.str;
|
||||
description = "The name displayed in the web interface";
|
||||
}) // {
|
||||
meta = {
|
||||
type = "string";
|
||||
weight = 1;
|
||||
appName =
|
||||
(lib.mkOption {
|
||||
default = "SelfPrivacy git Service";
|
||||
type = lib.types.str;
|
||||
description = "The name displayed in the web interface";
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "string";
|
||||
weight = 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
enableLfs = (lib.mkOption {
|
||||
default = true;
|
||||
type = lib.types.bool;
|
||||
description = "Enable Git LFS";
|
||||
}) // {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 2;
|
||||
enableLfs =
|
||||
(lib.mkOption {
|
||||
default = true;
|
||||
type = lib.types.bool;
|
||||
description = "Enable Git LFS";
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 2;
|
||||
};
|
||||
};
|
||||
};
|
||||
forcePrivate = (lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Force all new repositories to be private";
|
||||
}) // {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 3;
|
||||
forcePrivate =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Force all new repositories to be private";
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 3;
|
||||
};
|
||||
};
|
||||
};
|
||||
disableRegistration = (lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Disable registration of new users";
|
||||
}) // {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 4;
|
||||
disableRegistration =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Disable registration of new users";
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 4;
|
||||
};
|
||||
};
|
||||
};
|
||||
requireSigninView = (lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Force users to log in to view any page";
|
||||
}) // {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 5;
|
||||
requireSigninView =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Force users to log in to view any page";
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 5;
|
||||
};
|
||||
};
|
||||
};
|
||||
defaultTheme = (lib.mkOption {
|
||||
default = "forgejo-auto";
|
||||
type = lib.types.enum themes;
|
||||
description = "Default theme";
|
||||
}) // {
|
||||
meta = {
|
||||
type = "enum";
|
||||
options = themes;
|
||||
weight = 6;
|
||||
defaultTheme =
|
||||
(lib.mkOption {
|
||||
default = "forgejo-auto";
|
||||
type = lib.types.enum themes;
|
||||
description = "Default theme";
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "enum";
|
||||
options = themes;
|
||||
weight = 6;
|
||||
};
|
||||
};
|
||||
};
|
||||
enableSso = (lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable Single Sign-On";
|
||||
}) // {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 7;
|
||||
enableSso =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable Single Sign-On";
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 7;
|
||||
};
|
||||
};
|
||||
};
|
||||
debug = (lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable debug logging";
|
||||
}) // {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 8;
|
||||
debug =
|
||||
(lib.mkOption {
|
||||
default = false;
|
||||
type = lib.types.bool;
|
||||
description = "Enable debug logging";
|
||||
})
|
||||
// {
|
||||
meta = {
|
||||
type = "bool";
|
||||
weight = 8;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable (lib.mkMerge [
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.enableSso -> sp.sso.enable;
|
||||
message =
|
||||
"SSO cannot be enabled for Forgejo when SSO is disabled globally.";
|
||||
}
|
||||
];
|
||||
fileSystems = lib.mkIf sp.useBinds {
|
||||
"/var/lib/gitea" = {
|
||||
device = "/volumes/${cfg.location}/gitea";
|
||||
options = [ "bind" ];
|
||||
config = lib.mkIf cfg.enable (
|
||||
lib.mkMerge [
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.enableSso -> sp.sso.enable;
|
||||
message = "SSO cannot be enabled for Forgejo when SSO is disabled globally.";
|
||||
}
|
||||
];
|
||||
fileSystems = lib.mkIf sp.useBinds {
|
||||
"/var/lib/gitea" = {
|
||||
device = "/volumes/${cfg.location}/gitea";
|
||||
options = [ "bind" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
services.gitea.enable = false;
|
||||
services.forgejo = {
|
||||
enable = true;
|
||||
package = forgejoPackage;
|
||||
inherit stateDir;
|
||||
user = linuxUserOfService;
|
||||
group = linuxGroupOfService;
|
||||
database = {
|
||||
type = "sqlite3";
|
||||
host = "127.0.0.1";
|
||||
name = "gitea";
|
||||
services.gitea.enable = false;
|
||||
services.forgejo = {
|
||||
enable = true;
|
||||
package = forgejoPackage;
|
||||
inherit stateDir;
|
||||
user = linuxUserOfService;
|
||||
path = "${stateDir}/data/gitea.db";
|
||||
createDatabase = true;
|
||||
group = linuxGroupOfService;
|
||||
database = {
|
||||
type = "sqlite3";
|
||||
host = "127.0.0.1";
|
||||
name = "gitea";
|
||||
user = linuxUserOfService;
|
||||
path = "${stateDir}/data/gitea.db";
|
||||
createDatabase = true;
|
||||
};
|
||||
# ssh = {
|
||||
# enable = true;
|
||||
# clonePort = 22;
|
||||
# };
|
||||
lfs = {
|
||||
enable = cfg.enableLfs;
|
||||
contentDir = "${stateDir}/lfs";
|
||||
};
|
||||
repositoryRoot = "${stateDir}/repositories";
|
||||
# cookieSecure = true;
|
||||
settings = {
|
||||
DEFAULT = {
|
||||
APP_NAME = "${cfg.appName}";
|
||||
};
|
||||
server = {
|
||||
DOMAIN = "${cfg.subdomain}.${sp.domain}";
|
||||
ROOT_URL = "https://${cfg.subdomain}.${sp.domain}/";
|
||||
HTTP_ADDR = "0.0.0.0";
|
||||
HTTP_PORT = 3000;
|
||||
};
|
||||
mailer = {
|
||||
ENABLED = false;
|
||||
};
|
||||
ui = {
|
||||
DEFAULT_THEME = cfg.defaultTheme;
|
||||
SHOW_USER_EMAIL = false;
|
||||
};
|
||||
picture = {
|
||||
DISABLE_GRAVATAR = true;
|
||||
};
|
||||
admin = {
|
||||
ENABLE_KANBAN_BOARD = true;
|
||||
};
|
||||
repository = {
|
||||
FORCE_PRIVATE = cfg.forcePrivate;
|
||||
};
|
||||
session = {
|
||||
COOKIE_SECURE = true;
|
||||
};
|
||||
log = {
|
||||
ROOT_PATH = "${stateDir}/log";
|
||||
LEVEL = if cfg.debug then "Warn" else "Trace";
|
||||
};
|
||||
service = {
|
||||
DISABLE_REGISTRATION = cfg.disableRegistration;
|
||||
REQUIRE_SIGNIN_VIEW = cfg.requireSigninView;
|
||||
};
|
||||
};
|
||||
};
|
||||
# ssh = {
|
||||
# enable = true;
|
||||
# clonePort = 22;
|
||||
# };
|
||||
lfs = {
|
||||
enable = cfg.enableLfs;
|
||||
contentDir = "${stateDir}/lfs";
|
||||
|
||||
users.users.gitea = {
|
||||
home = "${stateDir}";
|
||||
useDefaultShell = true;
|
||||
group = linuxGroupOfService;
|
||||
isSystemUser = true;
|
||||
};
|
||||
repositoryRoot = "${stateDir}/repositories";
|
||||
# cookieSecure = true;
|
||||
settings = {
|
||||
DEFAULT = {
|
||||
APP_NAME = "${cfg.appName}";
|
||||
users.groups.${linuxGroupOfService} = { };
|
||||
services.nginx.virtualHosts."${cfg.subdomain}.${sp.domain}" = {
|
||||
useACMEHost = sp.domain;
|
||||
forceSSL = true;
|
||||
extraConfig = ''
|
||||
add_header Strict-Transport-Security $hsts_header;
|
||||
#add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always;
|
||||
add_header 'Referrer-Policy' 'origin-when-cross-origin';
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
|
||||
'';
|
||||
locations = {
|
||||
"/" = {
|
||||
proxyPass = "http://127.0.0.1:3000";
|
||||
};
|
||||
};
|
||||
server = {
|
||||
DOMAIN = "${cfg.subdomain}.${sp.domain}";
|
||||
ROOT_URL = "https://${cfg.subdomain}.${sp.domain}/";
|
||||
HTTP_ADDR = "0.0.0.0";
|
||||
HTTP_PORT = 3000;
|
||||
};
|
||||
systemd = {
|
||||
services.forgejo = {
|
||||
unitConfig.RequiresMountsFor = lib.mkIf sp.useBinds "/volumes/${cfg.location}/gitea";
|
||||
serviceConfig = {
|
||||
Slice = "gitea.slice";
|
||||
};
|
||||
};
|
||||
mailer = {
|
||||
ENABLED = false;
|
||||
};
|
||||
ui = {
|
||||
DEFAULT_THEME = cfg.defaultTheme;
|
||||
SHOW_USER_EMAIL = false;
|
||||
};
|
||||
picture = {
|
||||
DISABLE_GRAVATAR = true;
|
||||
};
|
||||
admin = {
|
||||
ENABLE_KANBAN_BOARD = true;
|
||||
};
|
||||
repository = {
|
||||
FORCE_PRIVATE = cfg.forcePrivate;
|
||||
};
|
||||
session = {
|
||||
COOKIE_SECURE = true;
|
||||
};
|
||||
log = {
|
||||
ROOT_PATH = "${stateDir}/log";
|
||||
LEVEL = if cfg.debug then "Warn" else "Trace";
|
||||
slices.gitea = {
|
||||
description = "Forgejo service slice";
|
||||
};
|
||||
};
|
||||
}
|
||||
# the following part is active only when enableSso = true
|
||||
(lib.mkIf is-auth-enabled {
|
||||
services.forgejo.settings = {
|
||||
auth.DISABLE_LOGIN_FORM = true;
|
||||
service = {
|
||||
DISABLE_REGISTRATION = cfg.disableRegistration;
|
||||
REQUIRE_SIGNIN_VIEW = cfg.requireSigninView;
|
||||
ALLOW_ONLY_EXTERNAL_REGISTRATION = true;
|
||||
SHOW_REGISTRATION_BUTTON = false;
|
||||
ENABLE_BASIC_AUTHENTICATION = false;
|
||||
};
|
||||
|
||||
# disallow explore page and access to private repositories, but allow public
|
||||
"service.explore".REQUIRE_SIGNIN_VIEW = true;
|
||||
|
||||
# TODO control via selfprivacy parameter
|
||||
# "service.explore".DISABLE_USERS_PAGE = true;
|
||||
|
||||
oauth2_client = {
|
||||
REDIRECT_URI = redirect-uri;
|
||||
ACCOUNT_LINKING = "auto";
|
||||
ENABLE_AUTO_REGISTRATION = true;
|
||||
OPENID_CONNECT_SCOPES = "email openid profile";
|
||||
};
|
||||
# doesn't work if LDAP auth source is not active!
|
||||
"cron.sync_external_users" = {
|
||||
ENABLED = true;
|
||||
RUN_AT_START = true;
|
||||
NOTICE_ON_SUCCESS = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
systemd.services.forgejo = {
|
||||
preStart =
|
||||
let
|
||||
waitForURL = url: maxRetries: delaySec: ''
|
||||
for ((i=1; i<=${toString maxRetries}; i++))
|
||||
do
|
||||
if ${lib.getExe pkgs.curl} -X GET --silent --fail "${url}" > /dev/null
|
||||
then
|
||||
echo "${url} responds to GET HTTP request (attempt #$i)"
|
||||
break
|
||||
else
|
||||
echo "${url} does not respond to GET HTTP request (attempt #$i)"
|
||||
echo sleeping for ${toString delaySec} seconds
|
||||
fi
|
||||
sleep ${toString delaySec}
|
||||
done
|
||||
if [[ "$i" -gt "${toString maxRetries}" ]]
|
||||
then
|
||||
echo "error, max attempts to access "${url}" have been used unsuccessfully!"
|
||||
exit 124
|
||||
fi
|
||||
'';
|
||||
|
||||
users.users.gitea = {
|
||||
home = "${stateDir}";
|
||||
useDefaultShell = true;
|
||||
group = linuxGroupOfService;
|
||||
isSystemUser = true;
|
||||
};
|
||||
users.groups.${linuxGroupOfService} = { };
|
||||
services.nginx.virtualHosts."${cfg.subdomain}.${sp.domain}" = {
|
||||
useACMEHost = sp.domain;
|
||||
forceSSL = true;
|
||||
extraConfig = ''
|
||||
add_header Strict-Transport-Security $hsts_header;
|
||||
#add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always;
|
||||
add_header 'Referrer-Policy' 'origin-when-cross-origin';
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
|
||||
'';
|
||||
locations = {
|
||||
"/" = {
|
||||
proxyPass = "http://127.0.0.1:3000";
|
||||
exe = lib.getExe config.services.forgejo.package;
|
||||
# FIXME skip-tls-verify, bind-password
|
||||
ldapConfigArgs = ''
|
||||
--name LDAP \
|
||||
--active \
|
||||
--security-protocol LDAPS \
|
||||
--skip-tls-verify \
|
||||
--host '${auth-passthru.ldap-host}' \
|
||||
--port '${toString auth-passthru.ldap-port}' \
|
||||
--user-search-base '${auth-passthru.ldap-base-dn}' \
|
||||
--user-filter '(&(class=person)(memberof=${usersGroup})(name=%s))' \
|
||||
--admin-filter '(&(class=person)(memberof=${adminsGroup})' \
|
||||
--username-attribute name \
|
||||
--firstname-attribute name \
|
||||
--surname-attribute displayname \
|
||||
--email-attribute mail \
|
||||
--public-ssh-key-attribute sshPublicKey \
|
||||
--bind-dn 'dn=token' \
|
||||
--bind-password "$(< ${serviceAccountTokenFP})" \
|
||||
--synchronize-users
|
||||
'';
|
||||
oauthConfigArgs = ''
|
||||
--name "${oauth2-provider-name}" \
|
||||
--provider openidConnect \
|
||||
--key forgejo \
|
||||
--secret "$(< ${oauthClientSecretFP})" \
|
||||
--group-claim-name groups \
|
||||
--admin-group admins \
|
||||
--auto-discover-url '${oauthDiscoveryURL}'
|
||||
'';
|
||||
in
|
||||
lib.mkMerge [
|
||||
(waitForURL oauthDiscoveryURL 10 10)
|
||||
(lib.mkAfter ''
|
||||
set -o xtrace
|
||||
|
||||
# Check if LDAP is already configured
|
||||
ldap_line="$(${exe} admin auth list | grep LDAP | head -n 1)"
|
||||
|
||||
if [[ -n "$ldap_line" ]]; then
|
||||
# update ldap config
|
||||
id="$(echo "$ldap_line" | ${pkgs.gawk}/bin/awk '{print $1}')"
|
||||
${exe} admin auth update-ldap --id "$id" ${ldapConfigArgs}
|
||||
else
|
||||
# initially configure ldap
|
||||
${exe} admin auth add-ldap ${ldapConfigArgs}
|
||||
fi
|
||||
|
||||
oauth_line="$(${exe} admin auth list | grep "${oauth2-provider-name}" | head -n 1)"
|
||||
if [[ -n "$oauth_line" ]]; then
|
||||
id="$(echo "$oauth_line" | ${pkgs.gawk}/bin/awk '{print $1}')"
|
||||
${exe} admin auth update-oauth --id "$id" ${oauthConfigArgs}
|
||||
else
|
||||
${exe} admin auth add-oauth ${oauthConfigArgs}
|
||||
fi
|
||||
'')
|
||||
];
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts."${cfg.subdomain}.${sp.domain}" = {
|
||||
extraConfig = lib.mkAfter ''
|
||||
rewrite ^/user/login$ /user/oauth2/${oauth2-provider-name} last;
|
||||
# FIXME is it needed?
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
'';
|
||||
};
|
||||
|
||||
selfprivacy.auth.clients."${oauthClientID}" = {
|
||||
inherit adminsGroup usersGroup;
|
||||
imageFile = ./icon.svg;
|
||||
displayName = "Forgejo";
|
||||
subdomain = cfg.subdomain;
|
||||
isTokenNeeded = true;
|
||||
originLanding = "https://${cfg.subdomain}.${sp.domain}/user/login?redirect_to=%2f";
|
||||
originUrl = redirect-uri;
|
||||
clientSystemdUnits = [ "forgejo.service" ];
|
||||
enablePkce = lib.versionAtLeast forgejoPackage.version "8.0";
|
||||
linuxUserOfClient = linuxUserOfService;
|
||||
linuxGroupOfClient = linuxGroupOfService;
|
||||
claimMaps.groups = {
|
||||
joinType = "array";
|
||||
valuesByGroup.${adminsGroup} = [ "admins" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
systemd = {
|
||||
services.forgejo = {
|
||||
unitConfig.RequiresMountsFor = lib.mkIf sp.useBinds "/volumes/${cfg.location}/gitea";
|
||||
serviceConfig = {
|
||||
Slice = "gitea.slice";
|
||||
};
|
||||
};
|
||||
slices.gitea = {
|
||||
description = "Forgejo service slice";
|
||||
};
|
||||
};
|
||||
}
|
||||
# the following part is active only when enableSso = true
|
||||
(lib.mkIf is-auth-enabled {
|
||||
services.forgejo.settings = {
|
||||
auth.DISABLE_LOGIN_FORM = true;
|
||||
service = {
|
||||
DISABLE_REGISTRATION = cfg.disableRegistration;
|
||||
REQUIRE_SIGNIN_VIEW = cfg.requireSigninView;
|
||||
ALLOW_ONLY_EXTERNAL_REGISTRATION = true;
|
||||
SHOW_REGISTRATION_BUTTON = false;
|
||||
ENABLE_BASIC_AUTHENTICATION = false;
|
||||
};
|
||||
|
||||
# disallow explore page and access to private repositories, but allow public
|
||||
"service.explore".REQUIRE_SIGNIN_VIEW = true;
|
||||
|
||||
# TODO control via selfprivacy parameter
|
||||
# "service.explore".DISABLE_USERS_PAGE = true;
|
||||
|
||||
oauth2_client = {
|
||||
REDIRECT_URI = redirect-uri;
|
||||
ACCOUNT_LINKING = "auto";
|
||||
ENABLE_AUTO_REGISTRATION = true;
|
||||
OPENID_CONNECT_SCOPES = "email openid profile";
|
||||
};
|
||||
# doesn't work if LDAP auth source is not active!
|
||||
"cron.sync_external_users" = {
|
||||
ENABLED = true;
|
||||
RUN_AT_START = true;
|
||||
NOTICE_ON_SUCCESS = true;
|
||||
};
|
||||
};
|
||||
systemd.services.forgejo = {
|
||||
preStart =
|
||||
let
|
||||
waitForURL = url: maxRetries: delaySec: ''
|
||||
for ((i=1; i<=${toString maxRetries}; i++))
|
||||
do
|
||||
if ${lib.getExe pkgs.curl} -X GET --silent --fail "${url}" > /dev/null
|
||||
then
|
||||
echo "${url} responds to GET HTTP request (attempt #$i)"
|
||||
break
|
||||
else
|
||||
echo "${url} does not respond to GET HTTP request (attempt #$i)"
|
||||
echo sleeping for ${toString delaySec} seconds
|
||||
fi
|
||||
sleep ${toString delaySec}
|
||||
done
|
||||
if [[ "$i" -gt "${toString maxRetries}" ]]
|
||||
then
|
||||
echo "error, max attempts to access "${url}" have been used unsuccessfully!"
|
||||
exit 124
|
||||
fi
|
||||
'';
|
||||
|
||||
exe = lib.getExe config.services.forgejo.package;
|
||||
# FIXME skip-tls-verify, bind-password
|
||||
ldapConfigArgs = ''
|
||||
--name LDAP \
|
||||
--active \
|
||||
--security-protocol LDAPS \
|
||||
--skip-tls-verify \
|
||||
--host '${auth-passthru.ldap-host}' \
|
||||
--port '${toString auth-passthru.ldap-port}' \
|
||||
--user-search-base '${auth-passthru.ldap-base-dn}' \
|
||||
--user-filter '(&(class=person)(memberof=${usersGroup})(name=%s))' \
|
||||
--admin-filter '(&(class=person)(memberof=${adminsGroup})' \
|
||||
--username-attribute name \
|
||||
--firstname-attribute name \
|
||||
--surname-attribute displayname \
|
||||
--email-attribute mail \
|
||||
--public-ssh-key-attribute sshPublicKey \
|
||||
--bind-dn 'dn=token' \
|
||||
--bind-password "$(< ${serviceAccountTokenFP})" \
|
||||
--synchronize-users
|
||||
'';
|
||||
oauthConfigArgs = ''
|
||||
--name "${oauth2-provider-name}" \
|
||||
--provider openidConnect \
|
||||
--key forgejo \
|
||||
--secret "$(< ${oauthClientSecretFP})" \
|
||||
--group-claim-name groups \
|
||||
--admin-group admins \
|
||||
--auto-discover-url '${oauthDiscoveryURL}'
|
||||
'';
|
||||
in
|
||||
lib.mkMerge [
|
||||
(waitForURL oauthDiscoveryURL 10 10)
|
||||
(lib.mkAfter ''
|
||||
set -o xtrace
|
||||
|
||||
# Check if LDAP is already configured
|
||||
ldap_line="$(${exe} admin auth list | grep LDAP | head -n 1)"
|
||||
|
||||
if [[ -n "$ldap_line" ]]; then
|
||||
# update ldap config
|
||||
id="$(echo "$ldap_line" | ${pkgs.gawk}/bin/awk '{print $1}')"
|
||||
${exe} admin auth update-ldap --id "$id" ${ldapConfigArgs}
|
||||
else
|
||||
# initially configure ldap
|
||||
${exe} admin auth add-ldap ${ldapConfigArgs}
|
||||
fi
|
||||
|
||||
oauth_line="$(${exe} admin auth list | grep "${oauth2-provider-name}" | head -n 1)"
|
||||
if [[ -n "$oauth_line" ]]; then
|
||||
id="$(echo "$oauth_line" | ${pkgs.gawk}/bin/awk '{print $1}')"
|
||||
${exe} admin auth update-oauth --id "$id" ${oauthConfigArgs}
|
||||
else
|
||||
${exe} admin auth add-oauth ${oauthConfigArgs}
|
||||
fi
|
||||
'')
|
||||
];
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts."${cfg.subdomain}.${sp.domain}" = {
|
||||
extraConfig = lib.mkAfter ''
|
||||
rewrite ^/user/login$ /user/oauth2/${oauth2-provider-name} last;
|
||||
# FIXME is it needed?
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
'';
|
||||
};
|
||||
|
||||
selfprivacy.auth.clients."${oauthClientID}" = {
|
||||
inherit adminsGroup usersGroup;
|
||||
imageFile = ./icon.svg;
|
||||
displayName = "Forgejo";
|
||||
subdomain = cfg.subdomain;
|
||||
isTokenNeeded = true;
|
||||
originLanding =
|
||||
"https://${cfg.subdomain}.${sp.domain}/user/login?redirect_to=%2f";
|
||||
originUrl = redirect-uri;
|
||||
clientSystemdUnits = [ "forgejo.service" ];
|
||||
enablePkce = lib.versionAtLeast forgejoPackage.version "8.0";
|
||||
linuxUserOfClient = linuxUserOfService;
|
||||
linuxGroupOfClient = linuxGroupOfService;
|
||||
claimMaps.groups = {
|
||||
joinType = "array";
|
||||
valuesByGroup.${adminsGroup} = [ "admins" ];
|
||||
};
|
||||
};
|
||||
})
|
||||
]);
|
||||
})
|
||||
]
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user