Merge branch 'feature/mattermost' into 'main'
Draft: init mattermost on hainich. See merge request hacc/infra/haccfiles!43
This commit is contained in:
commit
68120c7078
3 changed files with 469 additions and 0 deletions
|
@ -19,6 +19,7 @@
|
||||||
./services/syncthing.nix
|
./services/syncthing.nix
|
||||||
./services/monitoring.nix
|
./services/monitoring.nix
|
||||||
./services/workadventure.nix
|
./services/workadventure.nix
|
||||||
|
./services/mattermost.nix
|
||||||
];
|
];
|
||||||
boot.loader.grub.enable = true;
|
boot.loader.grub.enable = true;
|
||||||
boot.loader.grub.version = 2;
|
boot.loader.grub.version = 2;
|
||||||
|
|
211
hosts/hainich/services/mattermost.nix
Normal file
211
hosts/hainich/services/mattermost.nix
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
{config, pkgs, lib, ...}:
|
||||||
|
|
||||||
|
{
|
||||||
|
containers.mattermost = {
|
||||||
|
autoStart = true;
|
||||||
|
privateNetwork = true;
|
||||||
|
hostAddress = "192.168.100.30";
|
||||||
|
localAddress = "192.168.100.31";
|
||||||
|
|
||||||
|
bindMounts."/secrets" = {
|
||||||
|
hostPath = "/var/lib/mattermost/";
|
||||||
|
isReadOnly = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {pkgs, config, ...}: {
|
||||||
|
|
||||||
|
# have to import these here, since container's dont
|
||||||
|
# inherit imports of their environment.
|
||||||
|
imports = [ ../../../modules/mattermost.nix ];
|
||||||
|
|
||||||
|
# couldn't figure out how to actually overwrite modules, so now
|
||||||
|
# there's two mattermost modules ...
|
||||||
|
services.mattermost-patched = {
|
||||||
|
enable = true;
|
||||||
|
siteUrl = "https://mattermost-beta.infra4future.de";
|
||||||
|
siteName = "Mattermost - Blabla for Future";
|
||||||
|
listenAddress = "0.0.0.0:3000";
|
||||||
|
mutableConfig = false;
|
||||||
|
|
||||||
|
secretConfig = "/secrets/secrets.json";
|
||||||
|
|
||||||
|
extraConfig = {
|
||||||
|
ServiceSettings = {
|
||||||
|
TrustedProxyIPHeader = [ "X-Forwarded-For" "X-Real-Ip" ];
|
||||||
|
ReadTimeout = 300;
|
||||||
|
WriteTimeout = 600;
|
||||||
|
IdleTimeout = 60;
|
||||||
|
MaximumLoginAttempts = 10;
|
||||||
|
AllowCorsFrom = "*.infra4future.de/*";
|
||||||
|
WebserverMode = "gzip";
|
||||||
|
EnableCustomEmoji = true;
|
||||||
|
EnableEmojiPicker = true;
|
||||||
|
EnableGifPicker = false;
|
||||||
|
RestrictCustomEmojiCreation = "all";
|
||||||
|
RestrictPostDelete = "all";
|
||||||
|
AllowEditPost = "always";
|
||||||
|
PostEditTimeout = -1;
|
||||||
|
EnableTutorial = false;
|
||||||
|
ExperimentalChannelSidebarOrganization = "default_on";
|
||||||
|
ExperimentalChannelOrganization = true;
|
||||||
|
ExperimentalDataPrefetch = true;
|
||||||
|
EnableEmailInvitations = true;
|
||||||
|
DisableLegacyMFA = true;
|
||||||
|
EnableSVGs = true;
|
||||||
|
EnableLaTeX = true;
|
||||||
|
ThreadAutoFollow = true;
|
||||||
|
};
|
||||||
|
TeamSettings = {
|
||||||
|
EnableTeamCreation = true;
|
||||||
|
EnableUserCreation = true;
|
||||||
|
EnableOpenServer = false;
|
||||||
|
EnableUserDeactivation = true;
|
||||||
|
ExperimentalViewArchivedChannels = true;
|
||||||
|
ExperimentalEnableAutomaticReplies = true;
|
||||||
|
};
|
||||||
|
LogSettings = {
|
||||||
|
EnableConsole = true;
|
||||||
|
ConsoleLevel = "ERROR";
|
||||||
|
};
|
||||||
|
NotificationLogSettings = {
|
||||||
|
EnableConsole = true;
|
||||||
|
ConsoleLevel = "INFO";
|
||||||
|
};
|
||||||
|
PasswordSettings = {
|
||||||
|
MinimumLength = 10;
|
||||||
|
# turn of all the bullshit requirements
|
||||||
|
Lowercase = false;
|
||||||
|
Number = false;
|
||||||
|
Uppercase = false;
|
||||||
|
Symbol = false;
|
||||||
|
};
|
||||||
|
FileSettings = {
|
||||||
|
EnableFileAttachments = true;
|
||||||
|
MaxFileSize = 52428800;
|
||||||
|
DriverName = "local";
|
||||||
|
Directory = "/var/lib/mattermost/uploads-storage";
|
||||||
|
EnablePublicLink = true;
|
||||||
|
PublicLinkSalt = "3k7p3yxdhz6798b3b9openfr9rn3ymwu";
|
||||||
|
};
|
||||||
|
EmailSettings = {
|
||||||
|
EnableSignUpWithEmail = false;
|
||||||
|
EnableSignInWithEmail = false;
|
||||||
|
EnableSignInWithUsername = false;
|
||||||
|
SendEmailNotifications = true;
|
||||||
|
FeedbackName = "mattermost";
|
||||||
|
FeedbackEmail = "mattermost@infra4future.de";
|
||||||
|
ReplyToAddress = "mattermost@infra4future.de";
|
||||||
|
FeedbackOrganization = "∆infra4future.de";
|
||||||
|
EnableSMTPAuth = true;
|
||||||
|
SMTPUsername = "noreply@infra4future.de";
|
||||||
|
SMTPServer = "mail.hacc.space";
|
||||||
|
};
|
||||||
|
RateLimitSettings.Enable = false;
|
||||||
|
PrivacySettings = {
|
||||||
|
ShowEmailAddress = false;
|
||||||
|
ShowFullName = true;
|
||||||
|
};
|
||||||
|
SupportSettings = {
|
||||||
|
TermsOfServiceLink = "https://infra4future.de/nutzungsbedingungen.html";
|
||||||
|
PrivacyPolicyLink = "https://infra4future.de/nutzungsbedingungen.html";
|
||||||
|
AboutLink = "https://infra4future.de";
|
||||||
|
SupportEmail = "info@infra4future.de";
|
||||||
|
CustomTermsOfServiceEnabled = false;
|
||||||
|
EnableAskCommunityLink = true;
|
||||||
|
};
|
||||||
|
AnnouncementSettings.EnableBanner = false;
|
||||||
|
GitLabSettings = {
|
||||||
|
Enable = true;
|
||||||
|
Id = "mattermost-beta";
|
||||||
|
Scope = "";
|
||||||
|
AuthEndpoint = "https://auth.infra4future.de/auth/realms/forfuture/protocol/openid-connect/auth";
|
||||||
|
TokenEndpoint = "https://auth.infra4future.de/auth/realms/forfuture/protocol/openid-connect/token";
|
||||||
|
UserApiEndpoint = "https://auth.infra4future.de/auth/realms/forfuture/protocol/openid-connect/userinfo";
|
||||||
|
};
|
||||||
|
# for some reason, these don't appear to be working; the startup
|
||||||
|
# process complaines and sets these back to en
|
||||||
|
LocalizationSettings = {
|
||||||
|
DefaultServerLocale = "de";
|
||||||
|
DefaultClientLocale = "de";
|
||||||
|
AvailableLocales = "de,en";
|
||||||
|
};
|
||||||
|
MessageExportSettings.EnableExport = false;
|
||||||
|
# plugins appear to have trouble with the read-only filesystem; it may
|
||||||
|
# be necessary to manually change their paths etc.
|
||||||
|
PluginSettings = {
|
||||||
|
Enable = true;
|
||||||
|
EnableUploads = true;
|
||||||
|
Plugins = {
|
||||||
|
bigbluebutton = {
|
||||||
|
adminonly = false;
|
||||||
|
base_url = "https://bbb.infra4future.de/bigbluebutton/api";
|
||||||
|
salt = "zKCsNeaEniC115ynHOsZopgA4iTiJjzgeiPNoCEc";
|
||||||
|
};
|
||||||
|
"com.github.matterpoll.matterpoll" = {
|
||||||
|
experimentalui = true;
|
||||||
|
trigger = "poll";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
PluginStates = {
|
||||||
|
bigbluebutton.Enable = true;
|
||||||
|
"com.github.matterpoll.matterpoll".Enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
ComplianceSettings.Enable = false;
|
||||||
|
ClusterSettings.Enable = false;
|
||||||
|
MetricsSettings.Enable = false;
|
||||||
|
GuestAccountsSettings.Enable = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
# turn of the weirder parts of this module (which insist on passwords
|
||||||
|
# in nix files, instead of just using socket-based authentication)
|
||||||
|
#
|
||||||
|
# It will still attempt to use its default password, but postgres will
|
||||||
|
# just let it in regardless of that.
|
||||||
|
localDatabaseCreate = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.postgresql = {
|
||||||
|
enable = lib.mkForce true; # mattermost sets this to false. wtf.
|
||||||
|
ensureDatabases = [ "mattermost" ];
|
||||||
|
ensureUsers = [ {
|
||||||
|
name = "mattermost";
|
||||||
|
ensurePermissions = { "DATABASE mattermost" = "ALL PRIVILEGES"; };
|
||||||
|
} ];
|
||||||
|
|
||||||
|
authentication = lib.mkForce ''
|
||||||
|
# Generated file; do not edit!
|
||||||
|
local all all trust
|
||||||
|
host mattermost mattermost ::1/128 trust
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = [ 3000 ];
|
||||||
|
|
||||||
|
services.coredns = {
|
||||||
|
enable = true;
|
||||||
|
config = ''
|
||||||
|
.:53 {
|
||||||
|
forward . 1.1.1.1
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.nginx.virtualHosts."mattermost-beta.infra4future.de" = {
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://${config.containers.mattermost.localAddress}:3000";
|
||||||
|
proxyWebsockets = true;
|
||||||
|
};
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.nat = {
|
||||||
|
enable = true;
|
||||||
|
internalInterfaces = [ "ve-mattermost" ];
|
||||||
|
externalInterface = "enp6s0";
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
257
modules/mattermost.nix
Normal file
257
modules/mattermost.nix
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
|
||||||
|
cfg = config.services.mattermost-patched;
|
||||||
|
|
||||||
|
defaultConfig = builtins.fromJSON (builtins.replaceStrings [ "\\u0026" ] [ "&" ]
|
||||||
|
(readFile "${pkgs.mattermost}/config/config.json")
|
||||||
|
);
|
||||||
|
|
||||||
|
database = "postgres://${cfg.localDatabaseUser}:${cfg.localDatabasePassword}@localhost:5432/${cfg.localDatabaseName}?sslmode=disable&connect_timeout=10";
|
||||||
|
|
||||||
|
mattermostConf = foldl recursiveUpdate defaultConfig
|
||||||
|
[ { ServiceSettings.SiteURL = cfg.siteUrl;
|
||||||
|
ServiceSettings.ListenAddress = cfg.listenAddress;
|
||||||
|
TeamSettings.SiteName = cfg.siteName;
|
||||||
|
SqlSettings.DriverName = "postgres";
|
||||||
|
SqlSettings.DataSource = database;
|
||||||
|
}
|
||||||
|
cfg.extraConfig
|
||||||
|
];
|
||||||
|
|
||||||
|
mattermostConfJSON = pkgs.writeText "mattermost-config-raw.json" (builtins.toJSON mattermostConf);
|
||||||
|
|
||||||
|
in
|
||||||
|
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
services.mattermost-patched = {
|
||||||
|
enable = mkEnableOption "Mattermost chat server";
|
||||||
|
|
||||||
|
statePath = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "/var/lib/mattermost";
|
||||||
|
description = "Mattermost working directory";
|
||||||
|
};
|
||||||
|
|
||||||
|
siteUrl = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
example = "https://chat.example.com";
|
||||||
|
description = ''
|
||||||
|
URL this Mattermost instance is reachable under, without trailing slash.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
siteName = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "Mattermost";
|
||||||
|
description = "Name of this Mattermost site.";
|
||||||
|
};
|
||||||
|
|
||||||
|
listenAddress = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = ":8065";
|
||||||
|
example = "[::1]:8065";
|
||||||
|
description = ''
|
||||||
|
Address and port this Mattermost instance listens to.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
mutableConfig = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Whether the Mattermost config.json is writeable by Mattermost.
|
||||||
|
|
||||||
|
Most of the settings can be edited in the system console of
|
||||||
|
Mattermost if this option is enabled. A template config using
|
||||||
|
the options specified in services.mattermost will be generated
|
||||||
|
but won't be overwritten on changes or rebuilds.
|
||||||
|
|
||||||
|
If this option is disabled, changes in the system console won't
|
||||||
|
be possible (default). If an config.json is present, it will be
|
||||||
|
overwritten!
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
extraConfig = mkOption {
|
||||||
|
type = types.attrs;
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Addtional configuration options as Nix attribute set in config.json schema.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
secretConfig = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
Path to a json file containing secret config values, which should
|
||||||
|
not be written into the Nix store. If it is not null (the default)
|
||||||
|
and mutableConfig is set to false, then the mattermost service will
|
||||||
|
join the file at this path into its config.
|
||||||
|
|
||||||
|
Note that this file cannot be used to overwrite values already
|
||||||
|
specified by the other options of this module.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
localDatabaseCreate = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Create a local PostgreSQL database for Mattermost automatically.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
localDatabaseName = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "mattermost";
|
||||||
|
description = ''
|
||||||
|
Local Mattermost database name.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
localDatabaseUser = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "mattermost";
|
||||||
|
description = ''
|
||||||
|
Local Mattermost database username.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
localDatabasePassword = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "mmpgsecret";
|
||||||
|
description = ''
|
||||||
|
Password for local Mattermost database user.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "mattermost";
|
||||||
|
description = ''
|
||||||
|
User which runs the Mattermost service.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
group = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "mattermost";
|
||||||
|
description = ''
|
||||||
|
Group which runs the Mattermost service.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
matterircd = {
|
||||||
|
enable = mkEnableOption "Mattermost IRC bridge";
|
||||||
|
parameters = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [ ];
|
||||||
|
example = [ "-mmserver chat.example.com" "-bind [::]:6667" ];
|
||||||
|
description = ''
|
||||||
|
Set commandline parameters to pass to matterircd. See
|
||||||
|
https://github.com/42wim/matterircd#usage for more information.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkMerge [
|
||||||
|
(mkIf cfg.enable {
|
||||||
|
users.users = optionalAttrs (cfg.user == "mattermost") {
|
||||||
|
mattermost = {
|
||||||
|
group = cfg.group;
|
||||||
|
uid = config.ids.uids.mattermost;
|
||||||
|
home = cfg.statePath;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
users.groups = optionalAttrs (cfg.group == "mattermost") {
|
||||||
|
mattermost.gid = config.ids.gids.mattermost;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.postgresql.enable = cfg.localDatabaseCreate;
|
||||||
|
|
||||||
|
# The systemd service will fail to execute the preStart hook
|
||||||
|
# if the WorkingDirectory does not exist
|
||||||
|
system.activationScripts.mattermost = ''
|
||||||
|
mkdir -p ${cfg.statePath}
|
||||||
|
'';
|
||||||
|
|
||||||
|
systemd.services.mattermost = {
|
||||||
|
description = "Mattermost chat service";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = [ "network.target" "postgresql.service" ];
|
||||||
|
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${cfg.statePath}/{data,config,logs}
|
||||||
|
ln -sf ${pkgs.mattermost}/{bin,fonts,i18n,templates,client} ${cfg.statePath}
|
||||||
|
'' + lib.optionalString (!cfg.mutableConfig) ''
|
||||||
|
rm -f ${cfg.statePath}/config/config.json
|
||||||
|
'' + (if cfg.secretConfig == null
|
||||||
|
then ''
|
||||||
|
cp ${mattermostConfJSON} ${cfg.statePath}/config/config.json
|
||||||
|
''
|
||||||
|
else ''
|
||||||
|
${pkgs.jq}/bin/jq -s ".[1] * .[0]" ${cfg.secretConfig} ${mattermostConfJSON} > ${cfg.statePath}/config/config.json
|
||||||
|
'')
|
||||||
|
+ ''
|
||||||
|
${pkgs.mattermost}/bin/mattermost config migrate ${cfg.statePath}/config/config.json ${database}
|
||||||
|
'' + lib.optionalString cfg.mutableConfig ''
|
||||||
|
if ! test -e "${cfg.statePath}/config/.initial-created"; then
|
||||||
|
rm -f ${cfg.statePath}/config/config.json
|
||||||
|
cp ${mattermostConfJSON} ${cfg.statePath}/config/config.json
|
||||||
|
touch ${cfg.statePath}/config/.initial-created
|
||||||
|
fi
|
||||||
|
'' + lib.optionalString cfg.localDatabaseCreate ''
|
||||||
|
if ! test -e "${cfg.statePath}/.db-created"; then
|
||||||
|
${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \
|
||||||
|
${config.services.postgresql.package}/bin/psql postgres -c \
|
||||||
|
"CREATE ROLE ${cfg.localDatabaseUser} WITH LOGIN NOCREATEDB NOCREATEROLE ENCRYPTED PASSWORD '${cfg.localDatabasePassword}'"
|
||||||
|
${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \
|
||||||
|
${config.services.postgresql.package}/bin/createdb \
|
||||||
|
--owner ${cfg.localDatabaseUser} ${cfg.localDatabaseName}
|
||||||
|
touch ${cfg.statePath}/.db-created
|
||||||
|
fi
|
||||||
|
'' + ''
|
||||||
|
chown ${cfg.user}:${cfg.group} -R ${cfg.statePath}
|
||||||
|
chmod u+rw,g+r,o-rwx -R ${cfg.statePath}
|
||||||
|
'';
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
PermissionsStartOnly = true;
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
ExecStart = "${pkgs.mattermost}/bin/mattermost" +
|
||||||
|
(lib.optionalString (!cfg.mutableConfig) " -c ${database}");
|
||||||
|
WorkingDirectory = "${cfg.statePath}";
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = "10";
|
||||||
|
LimitNOFILE = "49152";
|
||||||
|
};
|
||||||
|
unitConfig.JoinsNamespaceOf = mkIf cfg.localDatabaseCreate "postgresql.service";
|
||||||
|
};
|
||||||
|
})
|
||||||
|
(mkIf cfg.matterircd.enable {
|
||||||
|
systemd.services.matterircd = {
|
||||||
|
description = "Mattermost IRC bridge service";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
User = "nobody";
|
||||||
|
Group = "nogroup";
|
||||||
|
ExecStart = "${pkgs.matterircd}/bin/matterircd ${concatStringsSep " " cfg.matterircd.parameters}";
|
||||||
|
WorkingDirectory = "/tmp";
|
||||||
|
PrivateTmp = true;
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = "5";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
Loading…
Reference in a new issue