Compare commits
5 Commits
Author | SHA1 | Date |
---|---|---|
Inex Code | 6594967570 | |
Inex Code | c60440a865 | |
Inex Code | 33cec16c61 | |
Inex Code | 5dde39956a | |
Inex Code | 82578e3531 |
|
@ -10,12 +10,14 @@ in
|
|||
./variables.nix
|
||||
./files.nix
|
||||
./volumes.nix
|
||||
./ldap/ldap-module.nix
|
||||
./users.nix
|
||||
./mailserver/system/mailserver.nix
|
||||
./vpn/ocserv.nix
|
||||
./api/api.nix
|
||||
./api/api-module.nix
|
||||
./social/pleroma.nix
|
||||
./social/mastodon.nix
|
||||
./letsencrypt/acme.nix
|
||||
./letsencrypt/resolve.nix
|
||||
./backup/restic.nix
|
||||
|
|
|
@ -13,10 +13,10 @@ in
|
|||
gitea = {
|
||||
enable = cfg.gitea.enable;
|
||||
stateDir = "/var/lib/gitea";
|
||||
# log = {
|
||||
# rootPath = "/var/lib/gitea/log";
|
||||
# level = "Warn";
|
||||
# };
|
||||
# log = {
|
||||
# rootPath = "/var/lib/gitea/log";
|
||||
# level = "Warn";
|
||||
# };
|
||||
user = "gitea";
|
||||
database = {
|
||||
type = "sqlite3";
|
||||
|
@ -40,7 +40,7 @@ in
|
|||
rootUrl = "https://git.${cfg.domain}/";
|
||||
httpAddress = "0.0.0.0";
|
||||
httpPort = 3000;
|
||||
# cookieSecure = true;
|
||||
# cookieSecure = true;
|
||||
settings = {
|
||||
mailer = {
|
||||
ENABLED = false;
|
||||
|
|
|
@ -0,0 +1,363 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
let
|
||||
cfg = config.selfprivacy.ldap;
|
||||
domain = lib.concatMapStringsSep "," (x: "dc=${x}") (lib.splitString "." cfg.domain);
|
||||
openssh-ldap-publickey = pkgs.fetchFromGitHub {
|
||||
owner = "AndriiGrytsenko";
|
||||
repo = "openssh-ldap-publickey";
|
||||
rev = "v1.0.2";
|
||||
hash = "sha256-Citukp6dQrmFUGFTRSXAhoUpjKUlEvkAOffx2/P5Gag=";
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
selfprivacy.ldap = {
|
||||
enable = lib.mkEnableOption (lib.mdDoc "LDAP integration");
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "example.com";
|
||||
description = ''
|
||||
LDAP domain.
|
||||
'';
|
||||
};
|
||||
rootUser = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "root";
|
||||
description = lib.mdDoc ''
|
||||
LDAP root user.
|
||||
'';
|
||||
};
|
||||
rootHashedPassword = lib.mkOption {
|
||||
type = lib.types.passwdEntry lib.types.str;
|
||||
description = lib.mdDoc ''
|
||||
LDAP root user hashed password.
|
||||
'';
|
||||
};
|
||||
users = lib.mkOption {
|
||||
type = lib.types.listOf (lib.types.submodule {
|
||||
options = {
|
||||
username = lib.mkOption {
|
||||
type = lib.types.passwdEntry lib.types.str;
|
||||
example = "john";
|
||||
description = lib.mdDoc ''
|
||||
User's username.
|
||||
'';
|
||||
};
|
||||
hashedPassword = lib.mkOption {
|
||||
type = lib.types.passwdEntry lib.types.str;
|
||||
description = lib.mdDoc ''
|
||||
Specifies the hashed password for the user.
|
||||
'';
|
||||
};
|
||||
sshKeys = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.singleLineStr;
|
||||
default = [ ];
|
||||
description = lib.mdDoc ''
|
||||
A list of user's OpenSSH public keys.
|
||||
'';
|
||||
};
|
||||
email = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "john@example.com";
|
||||
description = lib.mdDoc ''
|
||||
User email for LDAP.
|
||||
'';
|
||||
};
|
||||
displayName = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "John Doe";
|
||||
default = "";
|
||||
description = lib.mdDoc ''
|
||||
Display name for LDAP.
|
||||
'';
|
||||
};
|
||||
firstName = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "John";
|
||||
default = "";
|
||||
description = lib.mdDoc ''
|
||||
User's first name for LDAP.
|
||||
'';
|
||||
};
|
||||
lastName = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "Doe";
|
||||
default = "";
|
||||
description = lib.mdDoc ''
|
||||
User's last name for LDAP.
|
||||
'';
|
||||
};
|
||||
jpegPhoto = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.singleLineStr;
|
||||
default = null;
|
||||
description = lib.mdDoc ''
|
||||
A jpegPhoto attribute for LDAP, base64-encoded.
|
||||
'';
|
||||
};
|
||||
groups = lib.mkOption {
|
||||
type = lib.types.listOf (lib.types.enum [
|
||||
"admin"
|
||||
"gitea"
|
||||
"nextcloud"
|
||||
"pleroma"
|
||||
"mastodon"
|
||||
]);
|
||||
example = [ "gitea" ];
|
||||
default = [ ];
|
||||
description = lib.mdDoc ''
|
||||
Which services the user is allowed to use.
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
default = [ ];
|
||||
description = lib.mdDoc ''
|
||||
List of LDAP users.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
config = lib.mkMerge [
|
||||
(lib.mkIf cfg.enable {
|
||||
services.openldap =
|
||||
let
|
||||
filterUsers = group: users: lib.filter
|
||||
(user: builtins.elem group user.groups)
|
||||
users;
|
||||
mkUser = usersNamespace: user: lib.concatStringsSep "\n" ([
|
||||
"dn: uid=${user.username},ou=${usersNamespace},${domain}"
|
||||
"objectClass: inetOrgPerson"
|
||||
"objectClass: shadowAccount"
|
||||
] ++ lib.optionals (user.sshKeys != [ ]) [
|
||||
"objectClass: ldapPublicKey"
|
||||
] ++ lib.optionals (user.jpegPhoto != null) [
|
||||
"jpegPhoto:: ${user.jpegPhoto}"
|
||||
] ++ (map (key: "sshPublicKey: ${key}") user.sshKeys)
|
||||
++ [
|
||||
"mail: ${user.email}"
|
||||
"displayName: ${user.displayName}"
|
||||
"cn: ${user.firstName}"
|
||||
"sn: ${user.lastName}"
|
||||
"userPassword: {crypt}${user.hashedPassword}"
|
||||
]);
|
||||
mkGroup = usersNamespace: users: group:
|
||||
let
|
||||
groupUsers = (filterUsers group users);
|
||||
in
|
||||
lib.optionalString (groupUsers != [ ]) ''
|
||||
dn: cn=${group},ou=groups,${domain}
|
||||
objectClass: groupOfNames
|
||||
${lib.concatMapStringsSep
|
||||
"\n"
|
||||
(user: "member: uid=${user.username},ou=${usersNamespace},${domain}")
|
||||
groupUsers
|
||||
}
|
||||
'';
|
||||
mkUsersNamespace = usersNamespace: users: ''
|
||||
dn: ou=${usersNamespace},${domain}
|
||||
objectClass: organizationalUnit
|
||||
|
||||
${lib.concatMapStringsSep "\n\n" (mkUser usersNamespace) users}
|
||||
'';
|
||||
mkGroupsNamespace = usersNamespace: users: groupsNamespace: groups: ''
|
||||
dn: ou=${groupsNamespace},${domain}
|
||||
objectClass: organizationalUnit
|
||||
|
||||
${lib.concatMapStringsSep "\n\n" (mkGroup usersNamespace users) groups}
|
||||
'';
|
||||
in
|
||||
{
|
||||
enable = true;
|
||||
urlList = [ "ldap://localhost:389" ];
|
||||
declarativeContents."${domain}" = ''
|
||||
dn: ${domain}
|
||||
objectClass: domain
|
||||
|
||||
${mkUsersNamespace "users" cfg.users}
|
||||
|
||||
# Make a root user for some services to bind
|
||||
dn: uid=root,ou=users,${domain}
|
||||
objectClass: inetOrgPerson
|
||||
cn: root
|
||||
sn: root
|
||||
mail: root@${domain}
|
||||
# Password is "root"
|
||||
userPassword: {crypt}$6$teiD8ySLE58taSvY$veZS9QRSmfBcox2JfgYH/AWv24cpHD4P7IUzFv8WgxUaio.j7Y4aqMcC4a17v3PvOdCu8vgkKAtu/jhhKjVQm0
|
||||
|
||||
|
||||
${mkGroupsNamespace "users" cfg.users "groups" [
|
||||
"admin"
|
||||
"gitea"
|
||||
"nextcloud"
|
||||
"mastodon"
|
||||
]}
|
||||
|
||||
# pleroma has no support for ldap filters
|
||||
# so we just put pleroma users under separate namespace
|
||||
# https://git.pleroma.social/pleroma/pleroma/-/issues/1645
|
||||
${mkUsersNamespace "pleroma" (filterUsers "pleroma" cfg.users)}
|
||||
'';
|
||||
settings = {
|
||||
children = {
|
||||
"cn=schema".includes = [
|
||||
"${pkgs.openldap}/etc/schema/core.ldif"
|
||||
"${pkgs.openldap}/etc/schema/cosine.ldif"
|
||||
"${pkgs.openldap}/etc/schema/dyngroup.ldif"
|
||||
"${pkgs.openldap}/etc/schema/inetorgperson.ldif"
|
||||
"${pkgs.openldap}/etc/schema/nis.ldif"
|
||||
"${openssh-ldap-publickey}/misc/openssh-lpk-openldap.ldif"
|
||||
];
|
||||
"cn=modules" = {
|
||||
attrs = {
|
||||
objectClass = [ "olcModuleList" ];
|
||||
olcModuleLoad = "dynlist";
|
||||
};
|
||||
};
|
||||
"olcDatabase={0}config" = {
|
||||
attrs = {
|
||||
objectClass = [ "olcDatabaseConfig" ];
|
||||
olcDatabase = "{0}config";
|
||||
olcRootDN = "cn=${cfg.rootUser},cn=config";
|
||||
olcRootPW = "{crypt}${cfg.rootHashedPassword}";
|
||||
};
|
||||
};
|
||||
"olcDatabase={1}mdb" = {
|
||||
attrs = {
|
||||
objectClass = [ "olcDatabaseConfig" "olcMdbConfig" ];
|
||||
olcDatabase = "{1}mdb";
|
||||
olcDbDirectory = "/var/lib/openldap/db";
|
||||
olcSuffix = "${domain}";
|
||||
olcRootDN = "cn=${cfg.rootUser},${domain}";
|
||||
olcRootPW = "{crypt}${cfg.rootHashedPassword}";
|
||||
};
|
||||
};
|
||||
"olcOverlay=dynlist,olcDatabase={1}mdb" = {
|
||||
attrs = {
|
||||
objectClass = [ "olcOverlayConfig" "olcDynListConfig" ];
|
||||
olcDynListAttrSet = "groupOfURLs memberURL member+memberOf@groupOfNames";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
})
|
||||
(lib.mkIf config.services.gitea.enable {
|
||||
systemd.services.gitea.preStart = lib.mkAfter ''
|
||||
ldap_id=$(${config.services.gitea.package}/bin/gitea admin auth list | grep nixos-ldap | ${pkgs.gawk}/bin/awk '{ print $1 }' || true)
|
||||
|
||||
${lib.optionalString (!cfg.enable) ''
|
||||
if [ ! -z "$ldap_id" ]; then
|
||||
${config.services.gitea.package}/bin/gitea admin auth update-ldap \
|
||||
--id $ldap_id \
|
||||
--not-active
|
||||
fi
|
||||
''}
|
||||
|
||||
${lib.optionalString cfg.enable ''
|
||||
if [ -z "$ldap_id" ]; then
|
||||
auth_command="add-ldap"
|
||||
else
|
||||
auth_command="update-ldap --id $ldap_id"
|
||||
fi
|
||||
|
||||
# https://docs.gitea.io/en-us/command-line/#admin
|
||||
${config.services.gitea.package}/bin/gitea admin auth $auth_command \
|
||||
--name nixos-ldap \
|
||||
--security-protocol unencrypted \
|
||||
--host 127.0.0.1 \
|
||||
--port 389 \
|
||||
--bind-dn "${domain}" \
|
||||
--user-search-base "ou=users,${domain}" \
|
||||
--user-filter "(&(objectClass=shadowAccount)(memberOf=cn=gitea,ou=groups,${domain})(uid=%s))" \
|
||||
--admin-filter "(&(objectClass=shadowAccount)(memberOf=cn=admin,ou=groups,${domain}))" \
|
||||
--username-attribute uid \
|
||||
--email-attribute mail \
|
||||
--firstname-attribute cn \
|
||||
--surname-attribute sn \
|
||||
--avatar-attribute jpegPhoto \
|
||||
--public-ssh-key-attribute sshPublicKey \
|
||||
--synchronize-users
|
||||
''}
|
||||
'';
|
||||
})
|
||||
(lib.mkIf config.services.nextcloud.enable {
|
||||
# No support for admins via LDAP yet:
|
||||
# https://github.com/nextcloud/server/issues/6428
|
||||
systemd.services.nextcloud-setup.script =
|
||||
let
|
||||
# https://docs.nextcloud.com/server/25/admin_manual/configuration_server/occ_command.html#ldap-commands
|
||||
# https://docs.nextcloud.com/server/25/admin_manual/configuration_user/user_auth_ldap_api.html#configuration-keys
|
||||
occAction = action: "nextcloud-occ --no-interaction ${action}";
|
||||
ldapAction = action: occAction "ldap:${action}";
|
||||
ldapConfigIdFile = lib.escapeShellArg "${config.services.nextcloud.datadir}/config/.ldap-nixos-config-id";
|
||||
ldapConfigAction = action: "${ldapAction action} $(<${ldapConfigIdFile})";
|
||||
ldapSetConfig = ldapConfigAction "set-config";
|
||||
ldapTestConfig = ldapConfigAction "test-config";
|
||||
in
|
||||
lib.mkAfter ''
|
||||
${lib.optionalString (!cfg.enable) ''
|
||||
if [ -f ${ldapConfigIdFile} ]; then
|
||||
${ldapSetConfig} ldapConfigurationActive 0
|
||||
fi
|
||||
''}
|
||||
|
||||
${lib.optionalString cfg.enable ''
|
||||
if [ ! -f ${ldapConfigIdFile} ]; then
|
||||
${occAction "app:enable"} user_ldap
|
||||
if ! ${ldapAction "create-empty-config"} --only-print-prefix > ${ldapConfigIdFile}; then
|
||||
rm ${ldapConfigIdFile}
|
||||
echo "Failed to create LDAP configuration"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
${ldapSetConfig} ldapHost 127.0.0.1
|
||||
${ldapSetConfig} ldapPort 389
|
||||
${ldapSetConfig} ldapBase "${domain}"
|
||||
${ldapSetConfig} ldapBaseUsers "ou=users,${domain}"
|
||||
${ldapSetConfig} ldapUserFilter "(&(objectClass=shadowAccount)(memberOf=cn=nextcloud,ou=groups,${domain}))"
|
||||
${ldapSetConfig} ldapLoginFilter "(&(objectClass=shadowAccount)(memberOf=cn=nextcloud,ou=groups,${domain})(uid=%uid))"
|
||||
${ldapSetConfig} ldapExpertUsernameAttr uid
|
||||
${ldapSetConfig} ldapEmailAttribute mail
|
||||
${ldapSetConfig} ldapUserDisplayName displayName
|
||||
${ldapSetConfig} ldapUserAvatarRule "data:jpegPhoto"
|
||||
|
||||
if ${ldapTestConfig} | grep -q 'configuration is valid and the connection could be established'; then
|
||||
${ldapSetConfig} ldapConfigurationActive 1
|
||||
else
|
||||
echo "LDAP configuration is invalid, disabling"
|
||||
${ldapSetConfig} ldapConfigurationActive 0
|
||||
fi
|
||||
''}
|
||||
'';
|
||||
})
|
||||
(lib.mkIf (config.services.mastodon.enable && cfg.enable) {
|
||||
services.mastodon.extraConfig = {
|
||||
LDAP_ENABLED = true;
|
||||
LDAP_HOST = "127.0.0.1";
|
||||
LDAP_PORT = 389;
|
||||
LDAP_BASE = "ou=users,${domain}";
|
||||
LDAP_BIND_DN = "uid=root,ou=users,${domain}";
|
||||
LDAP_BIND_PASSWORD = "root";
|
||||
LDAP_UID = "uid";
|
||||
LDAP_MAIL = "mail";
|
||||
LDAP_SEARCH_FILTER = "(&(objectClass=shadowAccount)(memberOf=cn=mastodon,ou=groups,${domain})(uid=%{username}))";
|
||||
};
|
||||
})
|
||||
(lib.mkIf (config.services.pleroma.enable && cfg.enable) {
|
||||
services.pleroma.configs = [
|
||||
''
|
||||
import Config
|
||||
config :pleroma, Pleroma.Web.Auth.Authenticator, Pleroma.Web.Auth.LDAPAuthenticator
|
||||
config :pleroma, :ldap,
|
||||
enabled: true,
|
||||
host: "localhost",
|
||||
port: 389,
|
||||
ssl: false,
|
||||
base: "ou=pleroma,${domain}",
|
||||
uid: "uid"
|
||||
''
|
||||
];
|
||||
})
|
||||
];
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
{ pkgs, lib, config, ... }:
|
||||
let
|
||||
cfg = config.services.userdata;
|
||||
in
|
||||
{
|
||||
services.mastodon = {
|
||||
enable = cfg.mastodon.enable;
|
||||
localDomain = "mastodon.${cfg.domain}";
|
||||
configureNginx = true;
|
||||
smtp.fromAddress = "noreply@${cfg.domain}";
|
||||
};
|
||||
}
|
15
users.nix
15
users.nix
|
@ -22,4 +22,19 @@ in
|
|||
})
|
||||
cfg.users);
|
||||
};
|
||||
selfprivacy.ldap = {
|
||||
enable = true;
|
||||
domain = "${cfg.domain}";
|
||||
rootUser = "${cfg.username}";
|
||||
rootHashedPassword = cfg.hashedMasterPassword;
|
||||
users =
|
||||
(builtins.map
|
||||
(user: {
|
||||
username = "${user.username}";
|
||||
email = "${user.username}@${cfg.domain}";
|
||||
hashedPassword = user.hashedPassword;
|
||||
groups = [ "gitea" "nextcloud" "pleroma" "mastodon" ];
|
||||
})
|
||||
cfg.users);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -175,6 +175,16 @@ in
|
|||
type = types.nullOr types.bool;
|
||||
};
|
||||
};
|
||||
mastodon = {
|
||||
enable = mkOption {
|
||||
default = false;
|
||||
type = types.nullOr types.bool;
|
||||
};
|
||||
location = mkOption {
|
||||
default = "sda1";
|
||||
type = types.nullOr types.str;
|
||||
};
|
||||
};
|
||||
#########
|
||||
# SSH #
|
||||
#########
|
||||
|
|
|
@ -51,6 +51,10 @@ in
|
|||
ocserv = {
|
||||
enable = lib.attrsets.attrByPath [ "ocserv" "enable" ] false jsonData;
|
||||
};
|
||||
mastodon = {
|
||||
enable = lib.attrsets.attrByPath [ "mastodon" "enable" ] false jsonData;
|
||||
location = lib.attrsets.attrByPath [ "mastodon" "location" ] "sda1" jsonData;
|
||||
};
|
||||
ssh = {
|
||||
enable = lib.attrsets.attrByPath [ "ssh" "enable" ] true jsonData;
|
||||
rootKeys = lib.attrsets.attrByPath [ "ssh" "rootKeys" ] [ "" ] jsonData;
|
||||
|
|
|
@ -130,6 +130,10 @@ in
|
|||
};
|
||||
};
|
||||
};
|
||||
"mastodon.${domain}" = {
|
||||
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
|
||||
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";
|
||||
};
|
||||
"social.${domain}" = {
|
||||
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
|
||||
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";
|
||||
|
|
Loading…
Reference in New Issue