In a pretty good working state
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
{ pkgs, lib, config, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
cfg = config.fudo.chat;
|
||||
|
||||
in {
|
||||
options.fudo.chat = {
|
||||
enable = mkEnableOption "Enable chat server";
|
||||
|
||||
hostname = mkOption {
|
||||
type = types.str;
|
||||
description = "Hostname at which this chat server is accessible.";
|
||||
example = "chat.mydomain.com";
|
||||
};
|
||||
|
||||
site-name = mkOption {
|
||||
type = types.str;
|
||||
description = "The name of this chat server.";
|
||||
example = "My Fancy Chat Site";
|
||||
};
|
||||
|
||||
smtp-server = mkOption {
|
||||
type = types.str;
|
||||
description = "SMTP server to use for sending notification emails.";
|
||||
example = "mail.my-site.com";
|
||||
};
|
||||
|
||||
smtp-user = mkOption {
|
||||
type = types.str;
|
||||
description = "Username with which to connect to the SMTP server.";
|
||||
};
|
||||
|
||||
smtp-password-file = mkOption {
|
||||
type = types.path;
|
||||
description = "Path to a file containing the password to use while connecting to the SMTP server.";
|
||||
};
|
||||
|
||||
state-directory = mkOption {
|
||||
type = types.str;
|
||||
description = "Path at which to store server state data.";
|
||||
default = "/var/lib/mattermost";
|
||||
};
|
||||
|
||||
database = mkOption {
|
||||
type = (types.submodule {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
description = "Database name.";
|
||||
};
|
||||
|
||||
hostname = mkOption {
|
||||
type = types.str;
|
||||
description = "Database host.";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
description = "Database user.";
|
||||
};
|
||||
|
||||
password-file = mkOption {
|
||||
type = types.path;
|
||||
description = "Path to file containing database password.";
|
||||
};
|
||||
};
|
||||
});
|
||||
description = "Database configuration.";
|
||||
example = {
|
||||
name = "my_database";
|
||||
hostname = "my.database.com";
|
||||
user = "db_user";
|
||||
password-file = /path/to/some/file.pw;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable (let
|
||||
pkg = pkgs.mattermost;
|
||||
default-config = builtins.fromJSON (readFile "${pkg}/config/config.json");
|
||||
modified-config = recursiveUpdate default-config {
|
||||
ServiceSettings.SiteURL = "https://${cfg.hostname}";
|
||||
ServiceSettings.ListenAddress = "127.0.0.1:8065";
|
||||
TeamSettings.SiteName = cfg.site-name;
|
||||
EmailSettings = {
|
||||
RequireEmailVerification = true;
|
||||
SMTPServer = cfg.smtp-server;
|
||||
SMTPPort = 587;
|
||||
EnableSMTPAuth = true;
|
||||
SMTPUsername = cfg.smtp-user;
|
||||
SMTPPassword = (fileContents cfg.smtp-password-file);
|
||||
SendEmailNotifications = true;
|
||||
ConnectionSecurity = "STARTTLS";
|
||||
FeedbackEmail = "chat@fudo.org";
|
||||
FeedbackName = "Admin";
|
||||
};
|
||||
EnableEmailInvitations = true;
|
||||
SqlSettings.DriverName = "postgres";
|
||||
SqlSettings.DataSource =
|
||||
"postgres://${cfg.database.user}:${fileContents cfg.database.password-file}@${cfg.database.hostname}:5432/${cfg.database.name}";
|
||||
};
|
||||
mattermost-config-file = pkgs.writeText "mattermost-config.json" (builtins.toJSON modified-config);
|
||||
mattermost-user = "mattermost";
|
||||
mattermost-group = "mattermost";
|
||||
|
||||
in {
|
||||
users = {
|
||||
users = {
|
||||
${mattermost-user} = {
|
||||
isSystemUser = true;
|
||||
group = mattermost-group;
|
||||
};
|
||||
};
|
||||
|
||||
groups = {
|
||||
${mattermost-group} = {
|
||||
members = [ mattermost-user ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
system.activationScripts.mattermost = ''
|
||||
mkdir -p ${cfg.state-directory}
|
||||
'';
|
||||
|
||||
systemd.services.mattermost = {
|
||||
description = "Mattermost Chat Server";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
|
||||
preStart = ''
|
||||
mkdir -p ${cfg.state-directory}/config
|
||||
cp ${mattermost-config-file} ${cfg.state-directory}/config/config.json
|
||||
ln -sf ${pkg}/bin ${cfg.state-directory}
|
||||
ln -sf ${pkg}/fonts ${cfg.state-directory}
|
||||
ln -sf ${pkg}/i18n ${cfg.state-directory}
|
||||
ln -sf ${pkg}/templates ${cfg.state-directory}
|
||||
cp -uRL ${pkg}/client ${cfg.state-directory}
|
||||
chown -R ${mattermost-user}:${mattermost-group} ${cfg.state-directory}
|
||||
chmod u+w -R ${cfg.state-directory}/client
|
||||
chmod o-rwx -R ${cfg.state-directory}
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
PermissionsStartOnly = true;
|
||||
ExecStart = "${pkg}/bin/mattermost";
|
||||
WorkingDirectory = cfg.state-directory;
|
||||
Restart = "always";
|
||||
RestartSec = "10";
|
||||
LimitNOFILE = "49152";
|
||||
User = mattermost-user;
|
||||
Group = mattermost-group;
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
|
||||
appendHttpConfig = ''
|
||||
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=mattermost_cache:10m max_size=3g inactive=120m use_temp_path=off;
|
||||
'';
|
||||
|
||||
virtualHosts = {
|
||||
"${cfg.hostname}" = {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:8065";
|
||||
|
||||
extraConfig = ''
|
||||
client_max_body_size 50M;
|
||||
proxy_set_header Connection "";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-By $server_addr:$server_port;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Frame-Options SAMEORIGIN;
|
||||
proxy_buffers 256 16k;
|
||||
proxy_buffer_size 16k;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_cache mattermost_cache;
|
||||
proxy_cache_revalidate on;
|
||||
proxy_cache_min_uses 2;
|
||||
proxy_cache_use_stale timeout;
|
||||
proxy_cache_lock on;
|
||||
proxy_http_version 1.1;
|
||||
'';
|
||||
};
|
||||
|
||||
locations."~ /api/v[0-9]+/(users/)?websocket$" = {
|
||||
proxyPass = "http://127.0.0.1:8065";
|
||||
|
||||
extraConfig = ''
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
client_max_body_size 50M;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-By $server_addr:$server_port;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Frame-Options SAMEORIGIN;
|
||||
proxy_buffers 256 16k;
|
||||
proxy_buffer_size 16k;
|
||||
client_body_timeout 60;
|
||||
send_timeout 300;
|
||||
lingering_timeout 5;
|
||||
proxy_connect_timeout 90;
|
||||
proxy_send_timeout 300;
|
||||
proxy_read_timeout 90s;
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -12,5 +12,39 @@ with lib;
|
||||
'';
|
||||
default = [];
|
||||
};
|
||||
|
||||
domain = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
Domain of the
|
||||
'';
|
||||
};
|
||||
|
||||
profile = mkOption {
|
||||
type = with types; nullOr str;
|
||||
example = "desktop";
|
||||
description = ''
|
||||
The profile to use for this host. This will do some profile-dependent
|
||||
configuration, for example removing X-libs from servers and adding UI
|
||||
packages to desktops.
|
||||
'';
|
||||
default = null;
|
||||
};
|
||||
|
||||
site = mkOption {
|
||||
type = with types; nullOr str;
|
||||
example = "seattle";
|
||||
description = ''
|
||||
The site at which this host is located. This will do some site-dependent
|
||||
configuration.
|
||||
'';
|
||||
default = null;
|
||||
};
|
||||
|
||||
www-root = mkOption {
|
||||
type = types.path;
|
||||
description = "Path at which to store www files for serving.";
|
||||
example = /var/www;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+29
-16
@@ -9,6 +9,27 @@ let
|
||||
database-name = "grafana";
|
||||
database-user = "grafana";
|
||||
|
||||
databaseOpts = { ... }: {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
description = "Database name.";
|
||||
};
|
||||
hostname = mkOption {
|
||||
type = types.str;
|
||||
description = "Hostname of the database server.";
|
||||
};
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
description = "Database username.";
|
||||
};
|
||||
password-file = mkOption {
|
||||
type = types.path;
|
||||
description = "File containing the database user's password.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
in {
|
||||
|
||||
options.fudo.grafana = {
|
||||
@@ -30,9 +51,9 @@ in {
|
||||
description = "Path to a file containing the email user's password.";
|
||||
};
|
||||
|
||||
database-password-file = mkOption {
|
||||
type = types.path;
|
||||
description = "Path to a file containing the database user's password.";
|
||||
database = mkOption {
|
||||
type = (types.submodule databaseOpts);
|
||||
description = "Grafana database configuration.";
|
||||
};
|
||||
|
||||
admin-password-file = mkOption {
|
||||
@@ -52,15 +73,6 @@ in {
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
fudo.postgresql = {
|
||||
databases.${database-name} = {};
|
||||
|
||||
local-users.${database-user} = {
|
||||
password = (fileContents cfg.database-password-file);
|
||||
databases = ["${database-name}"];
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
|
||||
@@ -89,6 +101,7 @@ in {
|
||||
|
||||
addr = "127.0.0.1";
|
||||
protocol = "http";
|
||||
port = 3000;
|
||||
domain = "${cfg.hostname}";
|
||||
rootUrl = "https://${cfg.hostname}/";
|
||||
|
||||
@@ -106,10 +119,10 @@ in {
|
||||
};
|
||||
|
||||
database = {
|
||||
host = "localhost";
|
||||
name = database-name;
|
||||
user = database-user;
|
||||
passwordFile = cfg.database-password-file;
|
||||
host = cfg.database.hostname;
|
||||
name = cfg.database.name;
|
||||
user = cfg.database.user;
|
||||
passwordFile = cfg.database.password-file;
|
||||
type = "postgres";
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
lib: site: config: version:
|
||||
with lib;
|
||||
let
|
||||
db-config = if (config.database != null) then
|
||||
''
|
||||
type = "${config.database.type}"
|
||||
pdo_dsn = "${config.database.type}:host=${config.database.hostname};port=${toString config.database.port};dbname=${config.database.name}"
|
||||
pdo_user = "${config.database.user}"
|
||||
pdo_password = "${fileContents config.database.password-file}"
|
||||
''
|
||||
else "";
|
||||
in ''
|
||||
[webmail]
|
||||
title = "${config.title}"
|
||||
loading_description = "${config.title}"
|
||||
favicon_url = "https://${site}/favicon.ico"
|
||||
theme = "${config.theme}"
|
||||
allow_themes = On
|
||||
allow_user_background = Off
|
||||
language = "en"
|
||||
language_admin = "en"
|
||||
allow_languages_on_settings = On
|
||||
allow_additional_accounts = On
|
||||
allow_additional_identities = On
|
||||
messages_per_page = ${toString config.messages-per-page}
|
||||
attachment_size_limit = ${toString config.max-upload-size}
|
||||
|
||||
[interface]
|
||||
show_attachment_thumbnail = On
|
||||
new_move_to_folder_button = On
|
||||
|
||||
[branding]
|
||||
|
||||
[contacts]
|
||||
enable = On
|
||||
allow_sync = On
|
||||
sync_interval = 20
|
||||
suggestions_limit = 10
|
||||
${db-config}
|
||||
|
||||
[security]
|
||||
csrf_protection = On
|
||||
custom_server_signature = "RainLoop"
|
||||
x_frame_options_header = ""
|
||||
openpgp = On
|
||||
|
||||
admin_login = "admin"
|
||||
admin_password = ""
|
||||
allow_admin_panel = Off
|
||||
allow_two_factor_auth = On
|
||||
force_two_factor_auth = Off
|
||||
hide_x_mailer_header = Off
|
||||
admin_panel_host = ""
|
||||
admin_panel_key = "admin"
|
||||
content_security_policy = ""
|
||||
core_install_access_domain = ""
|
||||
|
||||
[login]
|
||||
default_domain = "${config.domain}"
|
||||
allow_languages_on_login = On
|
||||
determine_user_language = On
|
||||
determine_user_domain = Off
|
||||
welcome_page = Off
|
||||
hide_submit_button = On
|
||||
|
||||
[plugins]
|
||||
enable = Off
|
||||
|
||||
[defaults]
|
||||
view_editor_type = "${config.edit-mode}"
|
||||
view_layout = ${if (config.layout-mode == "bottom") then "2" else "1"}
|
||||
contacts_autosave = On
|
||||
mail_use_threads = ${if config.enable-threading then "On" else "Off"}
|
||||
allow_draft_autosave = On
|
||||
mail_reply_same_folder = Off
|
||||
show_images = On
|
||||
|
||||
[logs]
|
||||
enable = ${if config.debug then "On" else "Off"}
|
||||
|
||||
[debug]
|
||||
enable = ${if config.debug then "On" else "Off"}
|
||||
hide_passwords = On
|
||||
filename = "log-{date:Y-m-d}.txt"
|
||||
|
||||
[social]
|
||||
google_enable = Off
|
||||
fb_enable = Off
|
||||
twitter_enable = Off
|
||||
dropbox_enable = Off
|
||||
|
||||
[cache]
|
||||
enable = On
|
||||
index = "v1"
|
||||
fast_cache_driver = "files"
|
||||
fast_cache_index = "v1"
|
||||
http = On
|
||||
http_expires = 3600
|
||||
server_uids = On
|
||||
|
||||
[labs]
|
||||
allow_mobile_version = ${if config.enable-mobile then "On" else "Off"}
|
||||
check_new_password_strength = On
|
||||
allow_gravatar = On
|
||||
allow_prefetch = On
|
||||
allow_smart_html_links = On
|
||||
cache_system_data = On
|
||||
date_from_headers = On
|
||||
autocreate_system_folders = On
|
||||
allow_ctrl_enter_on_compose = On
|
||||
favicon_status = On
|
||||
use_local_proxy_for_external_images = On
|
||||
detect_image_exif_orientation = On
|
||||
|
||||
[version]
|
||||
current = "${version}"
|
||||
''
|
||||
+3
-1
@@ -69,7 +69,9 @@ in {
|
||||
acl_file = ${cfg.acl-file}
|
||||
}
|
||||
addresses = ${stringJoin " " cfg.bind-addresses}
|
||||
enable-http = true
|
||||
|
||||
# Binds to port 80!
|
||||
enable-http = false
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ let
|
||||
container-mail-user = "mailer";
|
||||
container-mail-user-id = 542;
|
||||
container-mail-group = "mailer";
|
||||
trusted-networks = config.fudo.prometheus.trusted-networks;
|
||||
trusted-networks = config.fudo.common.local-networks;
|
||||
|
||||
in rec {
|
||||
options.fudo.mail-server.container = {
|
||||
@@ -80,11 +80,11 @@ in rec {
|
||||
proxy-headers = ''
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
''
|
||||
;
|
||||
'';
|
||||
trusted-network-string = optionalString ((length trusted-networks) > 0)
|
||||
(concatStringsSep "\n"
|
||||
(map (network: "allow ${network};") trusted-networks)) + "\ndeny all;";
|
||||
|
||||
in {
|
||||
"${cfg.hostname}" = {
|
||||
enableACME = true;
|
||||
|
||||
@@ -51,13 +51,13 @@ let
|
||||
# A list of domains for which we accept mail
|
||||
virtual-mailbox-map-file = builtins.toFile "virtual_mailbox_map"
|
||||
(concatStringsSep "\n"
|
||||
(map (domain: "@${domain} OK") cfg.local-domains));
|
||||
(map (domain: "@${domain} OK") (cfg.local-domains ++ [cfg.domain])));
|
||||
|
||||
sender-login-map-file = let
|
||||
escapeDot = (str: replaceStrings ["."] ["\\."] str);
|
||||
in builtins.toFile "sender_login_maps"
|
||||
(concatStringsSep "\n"
|
||||
(map (domain: "/^(.*)@${escapeDot domain}$/ \${1}") cfg.local-domains));
|
||||
(map (domain: "/^(.*)@${escapeDot domain}$/ \${1}") (cfg.local-domains ++ [cfg.domain])));
|
||||
|
||||
mapped-file = name: "hash:/var/lib/postfix/conf/${name}";
|
||||
|
||||
@@ -95,6 +95,9 @@ in {
|
||||
services.prometheus.exporters.postfix = mkIf cfg.monitoring {
|
||||
enable = true;
|
||||
systemd.enable = true;
|
||||
showqPath = "/var/lib/postfix/queue/public/showq";
|
||||
user = config.services.postfix.user;
|
||||
group = config.services.postfix.group;
|
||||
};
|
||||
|
||||
services.postfix = {
|
||||
@@ -103,7 +106,8 @@ in {
|
||||
origin = cfg.domain;
|
||||
hostname = cfg.hostname;
|
||||
destination = ["localhost" "localhost.localdomain"] ++
|
||||
(map (domain: "localhost.${domain}") cfg.local-domains);
|
||||
(map (domain: "localhost.${domain}") cfg.local-domains) ++
|
||||
cfg.local-domains;
|
||||
|
||||
enableHeaderChecks = true;
|
||||
enableSmtp = true;
|
||||
@@ -153,6 +157,10 @@ in {
|
||||
smtpd_sasl_type = "dovecot";
|
||||
smtpd_sasl_path = "/run/dovecot2/auth";
|
||||
smtpd_sasl_auth_enable = "yes";
|
||||
smtpd_sasl_local_domain = "fudo.org";
|
||||
|
||||
smtpd_sasl_security_options = "noanonymous";
|
||||
smtpd_sasl_tls_security_options = "noanonymous";
|
||||
|
||||
smtpd_sender_login_maps = (pcre-file "sender_login_map");
|
||||
|
||||
@@ -184,6 +192,9 @@ in {
|
||||
|
||||
smtpd_sender_restrictions = [
|
||||
"check_sender_access ${mapped-file "reject_senders"}"
|
||||
"permit_mynetworks"
|
||||
"permit_sasl_authenticated"
|
||||
"reject_unknown_sender_domain"
|
||||
];
|
||||
|
||||
smtpd_recipient_restrictions = [
|
||||
@@ -200,6 +211,12 @@ in {
|
||||
"reject_non_fqdn_recipient"
|
||||
];
|
||||
|
||||
smtpd_helo_restrictions = [
|
||||
"permit_mynetworks"
|
||||
"reject_invalid_hostname"
|
||||
"permit"
|
||||
];
|
||||
|
||||
# Handled by submission
|
||||
smtpd_tls_security_level = "may";
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ in {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
|
||||
location."/metrics/node" = {
|
||||
locations."/metrics/node" = {
|
||||
extraConfig = ''
|
||||
${concatStringsSep "\n" (map allow-network fudo-cfg.local-networks)}
|
||||
allow 127.0.0.0/16;
|
||||
|
||||
+32
-20
@@ -8,14 +8,18 @@ let
|
||||
userOpts = { username, ... }: {
|
||||
options = {
|
||||
password = mkOption {
|
||||
type = types.str;
|
||||
type = with types; nullOr str;
|
||||
description = "The user's (plaintext) password.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
databases = mkOption {
|
||||
type = with types; listOf str;
|
||||
description = "Databases to which this user has access.";
|
||||
default = [];
|
||||
type = with types; loaOf str;
|
||||
description = "Map of databases to which this user has access, to the required perms.";
|
||||
default = {};
|
||||
example = {
|
||||
my_database = "ALL PRIVILEGES";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -24,19 +28,16 @@ let
|
||||
options = {
|
||||
users = mkOption {
|
||||
type = with types; listOf str;
|
||||
description = "A list of users who should have access to this database.";
|
||||
description = "A list of users who should have full access to this database.";
|
||||
default = [];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
userDatabaseAccess = user: databases:
|
||||
listToAttrs (map (database-name:
|
||||
{
|
||||
name = "DATABASE ${database-name}";
|
||||
value = "ALL PRIVILEGES";
|
||||
})
|
||||
databases);
|
||||
mapAttrs' (database: perms:
|
||||
nameValuePair "DATABASE ${database}" perms)
|
||||
databases;
|
||||
|
||||
stringJoin = joiner: els:
|
||||
if (length els) == 0 then
|
||||
@@ -54,9 +55,9 @@ let
|
||||
"ALTER USER ${username} ENCRYPTED PASSWORD '${attrs.password}';";
|
||||
|
||||
setPasswordsSql = users:
|
||||
stringJoin "\n"
|
||||
(stringJoin "\n"
|
||||
(mapAttrsToList (username: attrs: setPasswordSql username attrs)
|
||||
users);
|
||||
(filterAttrs (user: attrs: attrs.password != null) users))) + "\n";
|
||||
|
||||
makeLocalUserPasswordEntries = users:
|
||||
stringJoin "\n"
|
||||
@@ -66,7 +67,7 @@ let
|
||||
(map (db: ''
|
||||
host ${username} ${db} 127.0.0.1/16 md5
|
||||
host ${username} ${db} ::1/128 md5
|
||||
'') attrs.databases))
|
||||
'') (attrNames attrs.databases)))
|
||||
users);
|
||||
|
||||
|
||||
@@ -99,7 +100,7 @@ in {
|
||||
default = [];
|
||||
};
|
||||
|
||||
local-users = mkOption {
|
||||
users = mkOption {
|
||||
type = with types; loaOf (submodule userOpts);
|
||||
description = "A map of users to user attributes.";
|
||||
example = {
|
||||
@@ -146,6 +147,13 @@ in {
|
||||
group = "postgres";
|
||||
source = cfg.keytab;
|
||||
};
|
||||
|
||||
"postgresql/private/user-script.sql" = {
|
||||
mode = "0400";
|
||||
user = "postgres";
|
||||
group = "postgres";
|
||||
text = setPasswordsSql cfg.users;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -162,7 +170,7 @@ in {
|
||||
#{ "DATABASE ${username}" = "ALL PRIVILEGES"; };
|
||||
(userDatabaseAccess username attrs.databases);
|
||||
})
|
||||
cfg.local-users;
|
||||
cfg.users;
|
||||
|
||||
extraConfig =
|
||||
''
|
||||
@@ -179,7 +187,7 @@ in {
|
||||
''
|
||||
local all all ident
|
||||
|
||||
${makeLocalUserPasswordEntries cfg.local-users}
|
||||
${makeLocalUserPasswordEntries cfg.users}
|
||||
|
||||
# host-local
|
||||
host all all 127.0.0.1/32 gss include_realm=0 krb_realm=FUDO.ORG
|
||||
@@ -189,9 +197,13 @@ in {
|
||||
${makeNetworksEntry cfg.local-networks}
|
||||
'';
|
||||
|
||||
initialScript = pkgs.writeText "database-init.sql" ''
|
||||
${setPasswordsSql cfg.local-users}
|
||||
'';
|
||||
# initialScript = pkgs.writeText "database-init.sql" ''
|
||||
# ${setPasswordsSql cfg.users}
|
||||
# '';
|
||||
};
|
||||
|
||||
systemd.services.postgresql.postStart = ''
|
||||
${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${pkgs.postgresql}/bin/psql --port ${toString config.services.postgresql.port} -f /etc/postgresql/private/user-script.sql -d postgres
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
# Switch between different basic profiles
|
||||
{ lib, config, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
profile = config.fudo.profile;
|
||||
|
||||
profiles = {
|
||||
desktop = ./profiles/desktop.nix {
|
||||
pkgs = pkgs;
|
||||
config = config;
|
||||
};
|
||||
server = ./profiles/server.nix {
|
||||
pkgs = pkgs;
|
||||
config = config;
|
||||
};
|
||||
};
|
||||
|
||||
in {
|
||||
options.fudo.profile = {
|
||||
type = types.enum (attrNames profiles);
|
||||
example = "desktop";
|
||||
description = ''
|
||||
The profile to use for this host. This will do some profile-dependent
|
||||
configuration, for example removing X-libs from servers and adding UI
|
||||
packages to desktops.
|
||||
'';
|
||||
default = "server";
|
||||
};
|
||||
|
||||
config = optionalAttrs (profiles ? profile) profiles.${profile};
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
environment.systemPackages = with pkgs; [
|
||||
cool-retro-term
|
||||
chrome-gnome-shell
|
||||
chromium
|
||||
ffmpeg-full
|
||||
firefox
|
||||
gimp
|
||||
glxinfo
|
||||
gnome3.gnome-shell
|
||||
gnome3.gnome-session
|
||||
google-chrome
|
||||
gtk2
|
||||
gtk2-x11
|
||||
gtk3
|
||||
gtkimageview
|
||||
i3lock
|
||||
libfixposix
|
||||
minecraft
|
||||
mplayer
|
||||
nomacs
|
||||
openssl_1_1
|
||||
redshift
|
||||
rhythmbox
|
||||
shotwell
|
||||
spotify
|
||||
sqlite
|
||||
steam
|
||||
system-config-printer
|
||||
virtmanager
|
||||
xorg.xev
|
||||
xzgv
|
||||
virtmanager-qt
|
||||
];
|
||||
|
||||
# Splash screen
|
||||
boot.plymouth.enable = true;
|
||||
|
||||
services.avahi = {
|
||||
enable = true;
|
||||
browseDomains = [config.fudo.domain;];
|
||||
domainName = config.fudo.domain;
|
||||
};
|
||||
|
||||
boot.tmpOnTmpfs = true;
|
||||
|
||||
services.xserver = {
|
||||
enable = true;
|
||||
|
||||
layout = "us";
|
||||
xkbVariant = "dvp";
|
||||
xkbOptions = "ctrl:nocaps";
|
||||
|
||||
desktopManager.gnome3.enable = true;
|
||||
desktopManager.default = "gnome3";
|
||||
|
||||
displayManager.gdm.enable = true;
|
||||
|
||||
windowManager.session = pkgs.lib.singleton {
|
||||
name = "stumpwm";
|
||||
start = ''
|
||||
${pkgs.lispPackages.stumpwm}/bin/stumpwm &
|
||||
waidPID=$!
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
services.printing = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
services.gnome3 = {
|
||||
evolution-data-server.enable = pkgs.lib.mkForce false;
|
||||
gnome-user-share.enable = pkgs.lib.mkForce false;
|
||||
};
|
||||
|
||||
services.dbus.socketActivated = true;
|
||||
|
||||
services.openssh.forwardX11 = true;
|
||||
|
||||
programs.ssh.forwardX11 = true;
|
||||
|
||||
sound.enable = true;
|
||||
|
||||
hardware.pulseaudio.enable = true;
|
||||
|
||||
fonts = {
|
||||
enableCoreFonts = true;
|
||||
enableFontDir = true;
|
||||
enableGhostscriptFonts = false;
|
||||
fontconfig.ultimate.enable = true;
|
||||
|
||||
fonts = with pkgs; [
|
||||
cantarell_fonts
|
||||
dejavu_fonts
|
||||
dina-font
|
||||
dosemu_fonts
|
||||
fira-code
|
||||
fira-code-symbols
|
||||
freefont_ttf
|
||||
liberation_ttf
|
||||
mplus-outline-fonts
|
||||
nerdfonts
|
||||
noto-fonts
|
||||
noto-fonts-cjk
|
||||
noto-fonts-emoji
|
||||
proggyfonts
|
||||
terminus_font
|
||||
ubuntu_font_family
|
||||
ucsFonts
|
||||
unifont
|
||||
vistafonts
|
||||
xlibs.fontadobe100dpi
|
||||
xlibs.fontadobe75dpi
|
||||
xlibs.fontadobeutopia100dpi
|
||||
xlibs.fontadobeutopia75dpi
|
||||
xlibs.fontadobeutopiatype1
|
||||
xlibs.fontarabicmisc
|
||||
xlibs.fontbh100dpi
|
||||
xlibs.fontbh75dpi
|
||||
xlibs.fontbhlucidatypewriter100dpi
|
||||
xlibs.fontbhlucidatypewriter75dpi
|
||||
xlibs.fontbhttf
|
||||
xlibs.fontbhtype1
|
||||
xlibs.fontbitstream100dpi
|
||||
xlibs.fontbitstream75dpi
|
||||
xlibs.fontbitstreamtype1
|
||||
xlibs.fontcronyxcyrillic
|
||||
xlibs.fontcursormisc
|
||||
xlibs.fontdaewoomisc
|
||||
xlibs.fontdecmisc
|
||||
xlibs.fontibmtype1
|
||||
xlibs.fontisasmisc
|
||||
xlibs.fontjismisc
|
||||
xlibs.fontmicromisc
|
||||
xlibs.fontmisccyrillic
|
||||
xlibs.fontmiscethiopic
|
||||
xlibs.fontmiscmeltho
|
||||
xlibs.fontmiscmisc
|
||||
xlibs.fontmuttmisc
|
||||
xlibs.fontschumachermisc
|
||||
xlibs.fontscreencyrillic
|
||||
xlibs.fontsonymisc
|
||||
xlibs.fontsunmisc
|
||||
xlibs.fontwinitzkicyrillic
|
||||
xlibs.fontxfree86type1
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
environment = {
|
||||
systemPackages = with pkgs; [
|
||||
];
|
||||
|
||||
noXlibs = true;
|
||||
};
|
||||
|
||||
security = {
|
||||
hideProcessInformation = true;
|
||||
};
|
||||
|
||||
boot.tmpOnTmpfs = true;
|
||||
|
||||
services.xserver.enable = false;
|
||||
|
||||
programs = {
|
||||
gnupg.agent = {
|
||||
enable = true;
|
||||
enableSSHSupport = true;
|
||||
};
|
||||
|
||||
ssh.startAgent = true;
|
||||
};
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
site = config.fudo.site;
|
||||
|
||||
hostname = config.networking.hostName;
|
||||
|
||||
winnipeg-networks = [
|
||||
"208.81.1.128/28"
|
||||
"208.81.3.112/28"
|
||||
"192.168.11.1/24"
|
||||
];
|
||||
|
||||
site-configs = {
|
||||
global-config = {
|
||||
};
|
||||
|
||||
winnipeg = global-config // {
|
||||
time.timeZone = "America/Winnipeg";
|
||||
|
||||
fudo.common.local-networks = winnipeg-networks;
|
||||
|
||||
services.cron = {
|
||||
mailto = "admin@fudo.org";
|
||||
};
|
||||
|
||||
networking = {
|
||||
domain = "fudo.org";
|
||||
search = ["fudo.org"];
|
||||
firewall.enable = false;
|
||||
networkmanager.enable = pkgs.lib.mkForce false;
|
||||
nameservers = [ "1.1.1.1" "208.81.7.14" "2606:4700:4700::1111" ];
|
||||
};
|
||||
|
||||
security.acme.certs."${hostname}" = {
|
||||
email = "admin@fudo.org";
|
||||
|
||||
plugins = [
|
||||
"fullchain.pem"
|
||||
"full.pem"
|
||||
"key.pem"
|
||||
"chain.pem"
|
||||
"cert.pem"
|
||||
];
|
||||
};
|
||||
|
||||
fudo.node-exporter = {
|
||||
enable = true;
|
||||
hostname = hostname;
|
||||
};
|
||||
|
||||
nginx = {
|
||||
enable = true;
|
||||
|
||||
recommendedGzipSettings = true;
|
||||
recommendedOptimisation = true;
|
||||
recommendedTlsSettings = true;
|
||||
};
|
||||
};
|
||||
|
||||
nutty-club = winnipeg // {
|
||||
defaultGateway = "208.81.3.113";
|
||||
};
|
||||
};
|
||||
|
||||
in {
|
||||
options.fudo.site = mkOption {
|
||||
type = types.enum (attrNames site-configs);
|
||||
example = "nutty-club";
|
||||
description = ''
|
||||
The site at which this host is located. This will do some site-dependent
|
||||
configuration.
|
||||
'';
|
||||
default = "";
|
||||
};
|
||||
|
||||
config = optionalAttrs (site-configs ? site) site-configs.${site};
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
cfg = config.fudo.webmail;
|
||||
|
||||
inherit (lib.strings) concatStringsSep;
|
||||
|
||||
webmail-user = "webmail-php";
|
||||
webmail-group = "webmail-php";
|
||||
|
||||
base-data-path = "/var/rainloop";
|
||||
|
||||
fastcgi-conf = builtins.toFile "fastcgi.conf" ''
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param QUERY_STRING $query_string;
|
||||
fastcgi_param REQUEST_METHOD $request_method;
|
||||
fastcgi_param CONTENT_TYPE $content_type;
|
||||
fastcgi_param CONTENT_LENGTH $content_length;
|
||||
|
||||
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
|
||||
fastcgi_param REQUEST_URI $request_uri;
|
||||
fastcgi_param DOCUMENT_URI $document_uri;
|
||||
fastcgi_param DOCUMENT_ROOT $document_root;
|
||||
fastcgi_param SERVER_PROTOCOL $server_protocol;
|
||||
fastcgi_param REQUEST_SCHEME $scheme;
|
||||
fastcgi_param HTTPS $https if_not_empty;
|
||||
|
||||
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
|
||||
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
|
||||
|
||||
fastcgi_param REMOTE_ADDR $remote_addr;
|
||||
fastcgi_param REMOTE_PORT $remote_port;
|
||||
fastcgi_param SERVER_ADDR $server_addr;
|
||||
fastcgi_param SERVER_PORT $server_port;
|
||||
fastcgi_param SERVER_NAME $server_name;
|
||||
|
||||
# PHP only, required if PHP was built with --enable-force-cgi-redirect
|
||||
fastcgi_param REDIRECT_STATUS 200;
|
||||
'';
|
||||
|
||||
site-packages = mapAttrs (site: site-cfg:
|
||||
pkgs.rainloop-community.overrideAttrs (oldAttrs: {
|
||||
# Not sure how to correctly specify this arg...
|
||||
#dataPath = "${base-data-path}/${site}";
|
||||
|
||||
# Overwriting, to correctly create data dir
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
cp -r rainloop/* $out
|
||||
rm -rf $out/data
|
||||
ln -s ${base-data-path}/${site} $out/data
|
||||
ln -s ${site-cfg.favicon} $out/favicon.ico
|
||||
'';
|
||||
}))
|
||||
cfg.sites;
|
||||
|
||||
siteOpts = { site-host, ... }: {
|
||||
options = {
|
||||
title = mkOption {
|
||||
type = types.str;
|
||||
description = "Webmail site title";
|
||||
example = "My Webmail";
|
||||
};
|
||||
|
||||
debug = mkOption {
|
||||
type = types.bool;
|
||||
description = "Turn debug logs on.";
|
||||
default = false;
|
||||
};
|
||||
|
||||
mail-server = mkOption {
|
||||
type = types.str;
|
||||
description = "Mail server from which to send & recieve email.";
|
||||
default = "mail.fudo.org";
|
||||
};
|
||||
|
||||
favicon = mkOption {
|
||||
type = types.str;
|
||||
description = "URL of the site favicon";
|
||||
example = "https://www.somepage.com/fav.ico";
|
||||
};
|
||||
|
||||
messages-per-page = mkOption {
|
||||
type = types.int;
|
||||
description = "Default number of messages to show per page";
|
||||
default = 30;
|
||||
};
|
||||
|
||||
max-upload-size = mkOption {
|
||||
type = types.int;
|
||||
description = "Size limit in MB for uploaded files";
|
||||
default = 30;
|
||||
};
|
||||
|
||||
theme = mkOption {
|
||||
type = types.str;
|
||||
description = "Default theme to use for this webmail site.";
|
||||
default = "Default";
|
||||
};
|
||||
|
||||
# Ideally, don't even allow admin logins, since they'll just add state that can be clobbered
|
||||
# admin-password = mkOption {
|
||||
# type = types.str;
|
||||
# description = "Password to use for the admin user";
|
||||
# };
|
||||
|
||||
domain = mkOption {
|
||||
type = types.str;
|
||||
description = "Domain for which the server acts as webmail server";
|
||||
};
|
||||
|
||||
edit-mode = mkOption {
|
||||
type = types.enum ["Plain" "Html" "PlainForced" "HtmlForced"];
|
||||
description = "Default text editing mode for email";
|
||||
default = "Html";
|
||||
};
|
||||
|
||||
layout-mode = mkOption {
|
||||
type = types.enum ["side" "bottom"];
|
||||
description = "Layout mode to use for email preview.";
|
||||
default = "side";
|
||||
};
|
||||
|
||||
enable-threading = mkOption {
|
||||
type = types.bool;
|
||||
description = "Whether to enable threading for email.";
|
||||
default = true;
|
||||
};
|
||||
|
||||
enable-mobile = mkOption {
|
||||
type = types.bool;
|
||||
description = "Whether to enable a mobile site view.";
|
||||
default = true;
|
||||
};
|
||||
|
||||
database = mkOption {
|
||||
type = with types; nullOr (submodule databaseOpts);
|
||||
description = "Database configuration for storing contact data.";
|
||||
example = {
|
||||
name = "my_db";
|
||||
host = "db.domain.com";
|
||||
user = "my_user";
|
||||
password-file = /path/to/some/file.pw;
|
||||
};
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
databaseOpts = { ... }: {
|
||||
options = {
|
||||
type = mkOption {
|
||||
type = types.enum ["pgsql" "mysql"];
|
||||
description = "Driver to use when connecting to the database.";
|
||||
default = "pgsql";
|
||||
};
|
||||
|
||||
hostname = mkOption {
|
||||
type = types.str;
|
||||
description = "Name of host running the database.";
|
||||
example = "my-db.domain.com";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.int;
|
||||
description = "Port on which the database server is listening.";
|
||||
default = 5432;
|
||||
};
|
||||
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
description = "Name of the database containing contact info. <user> must have access.";
|
||||
default = "rainloop_contacts";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
description = "User as which to connect to the database.";
|
||||
};
|
||||
|
||||
password-file = mkOption {
|
||||
type = types.path;
|
||||
description = "Password to use when connecting to the database.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
in {
|
||||
options.fudo.webmail = {
|
||||
enable = mkEnableOption "Enable a RainLoop webmail server.";
|
||||
|
||||
sites = mkOption {
|
||||
type = with types; (loaOf (submodule siteOpts));
|
||||
description = "A map of webmail sites to site configurations.";
|
||||
example = {
|
||||
"webmail.domain.com" = {
|
||||
title = "My Awesome Webmail";
|
||||
layout-mode = "side";
|
||||
favicon = "/path/to/favicon.ico";
|
||||
admin-password = "shh-don't-tell";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
users = {
|
||||
users = {
|
||||
${webmail-user} = {
|
||||
isSystemUser = true;
|
||||
description = "Webmail PHP FPM user";
|
||||
group = webmail-group;
|
||||
};
|
||||
};
|
||||
groups = {
|
||||
${webmail-group} = {
|
||||
members = [ webmail-user config.services.nginx.user ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services = {
|
||||
phpfpm = {
|
||||
pools.webmail = {
|
||||
settings = {
|
||||
"pm" = "dynamic";
|
||||
"pm.max_children" = 50;
|
||||
"pm.start_servers" = 5;
|
||||
"pm.min_spare_servers" = 1;
|
||||
"pm.max_spare_servers" = 8;
|
||||
};
|
||||
|
||||
# Not working....see chmod below
|
||||
user = webmail-user;
|
||||
group = webmail-group;
|
||||
};
|
||||
};
|
||||
|
||||
nginx = {
|
||||
enable = true;
|
||||
|
||||
virtualHosts = mapAttrs (site: site-cfg: {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
|
||||
root = "${site-packages.${site}}";
|
||||
|
||||
locations = {
|
||||
"/" = {
|
||||
index = "index.php";
|
||||
};
|
||||
|
||||
"/data" = {
|
||||
extraConfig = ''
|
||||
deny all;
|
||||
return 403;
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
extraConfig = ''
|
||||
location ~ \.php$ {
|
||||
expires -1;
|
||||
|
||||
include ${fastcgi-conf};
|
||||
fastcgi_index index.php;
|
||||
fastcgi_pass unix:${config.services.phpfpm.pools.webmail.socket};
|
||||
}
|
||||
'';
|
||||
})
|
||||
cfg.sites;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.nginx.preStart = let
|
||||
link-configs = concatStringsSep "\n" (mapAttrsToList (site: site-cfg: let
|
||||
cfg-file = builtins.toFile "${site}-rainloop.cfg" (import ./include/rainloop.nix lib site site-cfg site-packages.${site}.version);
|
||||
domain-cfg = builtins.toFile "${site}-domain.cfg" ''
|
||||
imap_host = "${site-cfg.mail-server}"
|
||||
imap_port = 143
|
||||
imap_secure = "TLS"
|
||||
imap_short_login = On
|
||||
sieve_use = Off
|
||||
sieve_allow_raw = Off
|
||||
sieve_host = ""
|
||||
sieve_port = 4190
|
||||
sieve_secure = "None"
|
||||
smtp_host = "${site-cfg.mail-server}"
|
||||
smtp_port = 587
|
||||
smtp_secure = "TLS"
|
||||
smtp_short_login = On
|
||||
smtp_auth = On
|
||||
smtp_php_mail = Off
|
||||
white_list = ""
|
||||
'';
|
||||
|
||||
in ''
|
||||
mkdir -p ${base-data-path}/${site}/_data_/_default_/configs
|
||||
cp ${cfg-file} ${base-data-path}/${site}/_data_/_default_/configs/application.ini
|
||||
|
||||
mkdir -p ${base-data-path}/${site}/_data_/_default_/domains/
|
||||
cp ${domain-cfg} ${base-data-path}/${site}/_data_/_default_/domains/${site-cfg.domain}.ini
|
||||
|
||||
'') cfg.sites);
|
||||
|
||||
in ''
|
||||
${link-configs}
|
||||
|
||||
chown -R ${webmail-user}:${webmail-group} ${base-data-path}
|
||||
chmod -R ug+w ${base-data-path}
|
||||
'';
|
||||
|
||||
systemd.services.phpfpm-webmail.postStart = ''
|
||||
chown ${webmail-user}:${webmail-group} ${config.services.phpfpm.pools.webmail.socket}
|
||||
'';
|
||||
};
|
||||
}
|
||||
+5
-2
@@ -5,6 +5,7 @@ with lib;
|
||||
imports = [
|
||||
./fudo/acme-for-hostname.nix
|
||||
./fudo/authentication.nix
|
||||
./fudo/chat.nix
|
||||
./fudo/common.nix
|
||||
./fudo/grafana.nix
|
||||
./fudo/kdc.nix
|
||||
@@ -14,8 +15,10 @@ with lib;
|
||||
./fudo/minecraft-server.nix
|
||||
./fudo/node-exporter.nix
|
||||
./fudo/postgres.nix
|
||||
./fudo/profiles.nix
|
||||
./fudo/prometheus.nix
|
||||
./fudo/sites.nix
|
||||
./fudo/webmail.nix
|
||||
|
||||
../fudo/profiles
|
||||
../fudo/sites
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user