2025-09-11 17:30:40 +03:00
|
|
|
{
|
|
|
|
config,
|
|
|
|
lib,
|
|
|
|
pkgs,
|
|
|
|
...
|
|
|
|
}:
|
|
|
|
let
|
|
|
|
sp = config.selfprivacy;
|
|
|
|
cfg = sp.modules.mastodon;
|
|
|
|
oauthClientID = "mastodon";
|
|
|
|
auth-passthru = config.selfprivacy.passthru.auth;
|
2025-09-12 15:26:28 +03:00
|
|
|
oauthDiscoveryURL = auth-passthru.oauth2-discovery-url oauthClientID;
|
2025-09-11 17:30:40 +03:00
|
|
|
issuer = lib.strings.removeSuffix "/.well-known/openid-configuration" oauthDiscoveryURL;
|
|
|
|
|
|
|
|
usersGroup = "sp.mastodon.users";
|
2025-09-11 18:08:18 +03:00
|
|
|
adminsGroup = "sp.mastodon.admins";
|
2025-09-11 17:30:40 +03:00
|
|
|
|
|
|
|
oauthClientSecretFP = auth-passthru.mkOAuth2ClientSecretFP oauthClientID;
|
|
|
|
oauthRedirectURL = "https://${cfg.subdomain}.${sp.domain}/auth/auth/openid_connect/callback";
|
2025-09-11 22:38:06 +03:00
|
|
|
|
|
|
|
# emailPassword = pkgs.runCommand "genpassword" {} "echo `head -c 32 /dev/urandom | base64 | sed 's/[+=\\/A-Z]//g'` > $out";
|
|
|
|
# emailPasswordHash = pkgs.runCommand "genpassword" {} "echo `head -c 32 /dev/urandom | base64 | sed 's/[+=\\/A-Z]//g'` > $out";
|
2025-09-11 17:30:40 +03:00
|
|
|
in
|
|
|
|
{
|
|
|
|
options.selfprivacy.modules.mastodon = {
|
|
|
|
enable =
|
|
|
|
(lib.mkOption {
|
|
|
|
default = false;
|
|
|
|
type = lib.types.bool;
|
|
|
|
description = "Enable Mastodon";
|
|
|
|
})
|
|
|
|
// {
|
|
|
|
meta = {
|
|
|
|
type = "enable";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
location =
|
|
|
|
(lib.mkOption {
|
|
|
|
type = lib.types.str;
|
|
|
|
description = "Mastodon location";
|
|
|
|
})
|
|
|
|
// {
|
|
|
|
meta = {
|
|
|
|
type = "location";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
subdomain =
|
|
|
|
(lib.mkOption {
|
|
|
|
default = "mastodon";
|
|
|
|
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;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
|
|
fileSystems = lib.mkIf sp.useBinds {
|
|
|
|
"/var/lib/mastodon" = {
|
|
|
|
device = "/volumes/${cfg.location}/mastodon";
|
|
|
|
options = [ "bind" ];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
# services.postgresql = {
|
|
|
|
# ensureDatabases = [ "mastodon" ];
|
|
|
|
# ensureUsers = [
|
|
|
|
# {
|
|
|
|
# name = "mastodon";
|
|
|
|
# ensureDBOwnership = true;
|
|
|
|
# }
|
|
|
|
# ];
|
|
|
|
# };
|
|
|
|
|
|
|
|
services.mastodon = {
|
|
|
|
enable = true;
|
|
|
|
localDomain = "${cfg.subdomain}.${sp.domain}";
|
|
|
|
enableUnixSocket = false;
|
|
|
|
configureNginx = true;
|
|
|
|
database.createLocally = true;
|
2025-09-11 18:20:50 +03:00
|
|
|
streamingProcesses = 3;
|
2025-09-11 22:38:06 +03:00
|
|
|
|
|
|
|
smtp = {
|
|
|
|
createLocally = false;
|
|
|
|
user = "noreply.mastodon@${sp.domain}";
|
2025-09-11 22:41:09 +03:00
|
|
|
fromAddress = "noreply.mastodon@${sp.domain}";
|
2025-09-12 14:45:39 +03:00
|
|
|
passwordFile = "/run/keys/mastodon/email_password";
|
2025-09-11 22:38:06 +03:00
|
|
|
authenticate = true;
|
|
|
|
};
|
2025-09-11 17:30:40 +03:00
|
|
|
};
|
|
|
|
|
2025-09-12 15:26:28 +03:00
|
|
|
# mailserver.loginAccounts."noreply.mastodon@${sp.domain}" = {
|
|
|
|
# hashedPasswordFile = "/run/keys/mastodon/email_password";
|
|
|
|
# sendOnly = true;
|
|
|
|
# };
|
2025-09-11 22:38:06 +03:00
|
|
|
|
|
|
|
services.postfix.config.virtual_mailbox_maps = [ "hash:/run/postfix/mastodon.cf" ];
|
2025-09-11 17:30:40 +03:00
|
|
|
|
|
|
|
systemd = {
|
2025-09-11 22:38:06 +03:00
|
|
|
services.mastodon-email-password-setup = {
|
|
|
|
enable = true;
|
|
|
|
wantedBy = [ "multi-user.target" "mastodon-web.service" "postfix.service" ];
|
|
|
|
serviceConfig = {
|
|
|
|
Type = "oneshot";
|
|
|
|
ExecStart = pkgs.writeShellScript "gen-mastodon-email-password" ''
|
|
|
|
export password=$(head -c 32 /dev/urandom | base64 | sed 's/[+=\\/A-Z]//g')
|
|
|
|
|
|
|
|
rm -f /run/keys/mastodon/email_password || true
|
|
|
|
mkdir /run/keys/mastodon/ || true # Create /run/keys/mastodon if it doesn't exist
|
|
|
|
echo $password > /run/keys/mastodon/email_password
|
|
|
|
chmod 400 /run/keys/mastodon/email_password
|
|
|
|
chown ${config.services.mastodon.user}:${config.services.mastodon.group} /run/keys/mastodon/email_password
|
|
|
|
|
|
|
|
rm -f /run/postfix/mastodon.cf || true
|
|
|
|
mkdir /run/postfix/ || true # Create /run/postfix if it doesn't exist
|
|
|
|
export hashedPassword=$(mkpasswd -sm bcrypt "$password")
|
|
|
|
echo "noreply.mastodon@${sp.domain}: $hashedPassword" > /run/postfix/mastodon.cf
|
|
|
|
chmod 440 /run/postfix/mastodon.cf
|
|
|
|
chown ${config.services.postfix.user}:${config.services.postfix.group} /run/postfix/mastodon.cf
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2025-09-11 18:51:52 +03:00
|
|
|
services.mastodon-web = {
|
2025-09-11 17:30:40 +03:00
|
|
|
unitConfig.RequiresMountsFor = lib.mkIf sp.useBinds "/volumes/${cfg.location}/mastodon";
|
|
|
|
serviceConfig = {
|
2025-09-12 16:46:24 +03:00
|
|
|
LoadCredential = ["client-secret:${oauthClientSecretFP}"];
|
2025-09-12 15:05:42 +03:00
|
|
|
ExecStart = lib.mkForce (pkgs.writeShellScript "run-mastodon-with-client-secret" ''
|
2025-09-12 15:03:34 +03:00
|
|
|
export CLIENT_SECRET=$(cat $CREDENTIALS_DIRECTORY/client-secret)
|
2025-09-12 15:08:34 +03:00
|
|
|
${config.services.mastodon.package}/bin/puma -C config/puma.rb
|
2025-09-12 15:05:42 +03:00
|
|
|
'');
|
2025-09-11 17:30:40 +03:00
|
|
|
};
|
|
|
|
environment = {
|
2025-09-12 14:42:50 +03:00
|
|
|
OIDC_ENABLED = "true";
|
2025-09-11 17:30:40 +03:00
|
|
|
OIDC_DISPLAY_NAME= "Kanidm";
|
2025-09-12 18:05:17 +03:00
|
|
|
# OIDC_ISSUER = issuer;
|
|
|
|
OIDC_ISSUER = "https://auth.hollowness.top";
|
2025-09-12 14:42:50 +03:00
|
|
|
OIDC_DISCOVERY = "true";
|
2025-09-11 17:30:40 +03:00
|
|
|
OIDC_SCOPE = "openid,profile";
|
|
|
|
OIDC_UID_FIELD = "sub";
|
|
|
|
OIDC_CLIENT_ID = oauthClientID;
|
|
|
|
OIDC_REDIRECT_URI = oauthRedirectURL;
|
2025-09-12 17:52:24 +03:00
|
|
|
OIDC_SECURITY_ASSUME_EMAIL_IS_VERIFIED = "true";
|
2025-09-11 17:30:40 +03:00
|
|
|
};
|
|
|
|
};
|
|
|
|
slices.mastodon = {
|
|
|
|
description = "Mastodon service slice";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
selfprivacy.auth.clients.${oauthClientID} = {
|
|
|
|
inherit usersGroup;
|
2025-09-11 18:08:18 +03:00
|
|
|
inherit adminsGroup;
|
2025-09-11 17:30:40 +03:00
|
|
|
subdomain = cfg.subdomain;
|
|
|
|
originLanding = "https://${cfg.subdomain}.${sp.domain}/";
|
|
|
|
originUrl = oauthRedirectURL;
|
|
|
|
clientSystemdUnits = [ "mastodon.service" ];
|
2025-09-12 15:40:59 +03:00
|
|
|
enablePkce = false;
|
2025-09-11 17:30:40 +03:00
|
|
|
linuxUserOfClient = "mastodon";
|
|
|
|
linuxGroupOfClient = "mastodon";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|