2021-08-10 21:28:14 +08:00
|
|
|
# based on https://gist.github.com/ajs124/ff04ab14435908d914cf5cedbc56a52e
|
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
|
|
|
|
with lib;
|
|
|
|
let
|
|
|
|
cfg = config.services.rt;
|
|
|
|
|
|
|
|
configFile = pkgs.writeTextFile {
|
|
|
|
name = "RT_SiteConfig.pm";
|
|
|
|
text = ''
|
|
|
|
use utf8;
|
|
|
|
|
|
|
|
# System (Base configuration)
|
|
|
|
Set($rtname, '${cfg.rtName}'); # Changing this will break responses to existing tickets
|
|
|
|
Set($Organization, '${cfg.organization}'); # Changing this will break all existing tickets
|
|
|
|
Set($CorrespondAddress, '${cfg.correspondAddress}');
|
|
|
|
Set($CommentAddress, '${cfg.commentAddress}');
|
|
|
|
Set($WebDomain, '${cfg.domain}');
|
|
|
|
Set($Timezone, '${cfg.timeZone}');
|
|
|
|
|
|
|
|
Set($DatabaseType, 'Pg');
|
|
|
|
Set($DatabaseHost, 'localhost');
|
|
|
|
Set($DatabaseUser, 'rt_user');
|
|
|
|
Set($DatabaseName, 'rt5');
|
|
|
|
# Read database password from file
|
|
|
|
open my $fh, '<', '${cfg.dbPasswordFile}' or die 'Can\'t open file $!';
|
|
|
|
my $dbpw = do { local $/; <$fh> };
|
|
|
|
$dbpw =~ s/^\s+|\s+$//g;
|
|
|
|
Set($DatabasePassword, $dbpw);
|
|
|
|
|
|
|
|
# System (Logging)
|
|
|
|
Set($LogToSTDERR, undef); # Don't log twice
|
|
|
|
|
|
|
|
# System (Incoming mail gateway)
|
|
|
|
Set($OwnerEmail, '${cfg.ownerEmail}');
|
|
|
|
Set($MaxAttachmentSize, 15360000);
|
|
|
|
Set($CheckMoreMSMailHeaders, 1);
|
2021-08-11 10:54:24 +08:00
|
|
|
Set($RTAddressRegexp, '^(helpdesk|sales)\@(m-labs.hk)$');
|
2021-08-10 21:28:14 +08:00
|
|
|
Set($LoopsToRTOwner, 0);
|
|
|
|
|
|
|
|
# System (Outgoing mail)
|
2021-08-11 10:54:24 +08:00
|
|
|
Set($SetOutgoingMailFrom, 'helpdesk@m-labs.hk');
|
2021-08-10 21:28:14 +08:00
|
|
|
|
|
|
|
# System (Sendmail configuration)
|
|
|
|
Set($SendmailPath, '${cfg.sendmailPath}');
|
|
|
|
Set($SendmailArguments, '${concatStringsSep " " cfg.sendmailArguments}');
|
|
|
|
|
|
|
|
# System (Application logic)
|
|
|
|
Set($ParseNewMessageForTicketCcs, 1);
|
|
|
|
|
|
|
|
# System (Extra Security)
|
|
|
|
Set($RestrictLoginReferrer, 1);
|
|
|
|
|
|
|
|
# System (Date and time handling)
|
|
|
|
Set($DefaultTimeUnitsToHours, 1);
|
|
|
|
Set($TimeInICal, 1);
|
|
|
|
Set($DateTimeFormat, 'RFC2822');
|
|
|
|
|
|
|
|
# System (Authorization and user configuration)
|
|
|
|
Set($AutoLogoff, 262800); # 6 months
|
|
|
|
Set($WebSecureCookies, 1);
|
|
|
|
|
|
|
|
# Web Interface (Base configuration)
|
|
|
|
Set($CanonicalizeRedirectURLs, 1);
|
|
|
|
Set($CanonicalizeURLsInFeeds, 1);
|
|
|
|
Set($WebBaseURL, '${cfg.baseUrl}');
|
|
|
|
Set($LogoLinkURL, '${cfg.baseUrl}');
|
|
|
|
|
|
|
|
# Web Interface (Home page)
|
|
|
|
Set($DefaultSummaryRows, 50);
|
|
|
|
|
|
|
|
# Web Interface (Ticket search)
|
|
|
|
Set($DefaultSearchResultOrder, 'DESC'); # Display newer tickets first
|
|
|
|
Set($SearchResultsAutoRedirect, 1); # Don't show result list when there is only one match
|
|
|
|
Set(%FullTextSearch,
|
|
|
|
Enable => 1,
|
|
|
|
Indexed => 1,
|
|
|
|
Column => 'ContentIndex',
|
|
|
|
Table => 'AttachmentsIndex',
|
|
|
|
);
|
|
|
|
|
|
|
|
# Web Interface (Ticket options)
|
|
|
|
Set($ShowMoreAboutPrivilegedUsers, 1);
|
|
|
|
Set($MoreAboutRequestorGroupsLimit, undef);
|
|
|
|
Set($HideUnsetFieldsOnDisplay, 1);
|
|
|
|
|
|
|
|
# Web Interface (Articles)
|
|
|
|
Set($ArticleOnTicketCreate, 0);
|
|
|
|
|
|
|
|
# Web Interface (Message box properties)
|
|
|
|
Set($MessageBoxRichText, 0);
|
|
|
|
Set($MessageBoxIncludeSignatureOnComment, 0);
|
|
|
|
|
|
|
|
# Web Interface (Transaction display)
|
|
|
|
Set($MaxInlineBody, 0);
|
2021-10-27 12:20:08 +08:00
|
|
|
Set($SuppressInlineTextFiles, 1);
|
2021-08-10 21:28:14 +08:00
|
|
|
|
|
|
|
# Web Interface (Administrative interface)
|
|
|
|
Set($ShowRTPortal, 0);
|
|
|
|
Set($ShowEditSsytemConfig, 0);
|
|
|
|
|
|
|
|
# Features (External storage)
|
|
|
|
Set(%ExternalStorage,
|
|
|
|
Type => 'Disk',
|
|
|
|
Path => '/var/lib/rt/attachments',
|
|
|
|
);
|
|
|
|
Set($ExternalStorageCutoffSize, 0);
|
|
|
|
|
|
|
|
# Features (Cryptography)
|
|
|
|
Set(%Crypt, RejectOnMissingPrivateKey => 0, RejectOnBadData => 0, AllowEncryptDataInDB => 0);
|
|
|
|
Set(%SMIME, Enable => 1, Keyring => '${pkgs.cacert}/etc/ssl/certs/');
|
|
|
|
Set(%GnuPG, Enable => 1);
|
|
|
|
Set(%GnuPGOptions,
|
|
|
|
'keyserver' => 'hkp://keys.openpgp.org',
|
|
|
|
'always-trust' => undef,
|
|
|
|
'auto-key-locate' => 'keyserver',
|
|
|
|
'keyserver-options' => 'auto-key-retrieve'
|
|
|
|
);
|
|
|
|
|
|
|
|
${cfg.extraConfig}
|
|
|
|
|
|
|
|
1;
|
|
|
|
'';
|
|
|
|
checkPhase = ''
|
|
|
|
${pkgs.perl}/bin/perl -c $out
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
in {
|
|
|
|
options.services.rt = with types; {
|
|
|
|
|
|
|
|
enable = mkEnableOption "rt system";
|
|
|
|
|
|
|
|
package = mkOption {
|
|
|
|
description = "Package to use";
|
|
|
|
default = pkgs.rt;
|
|
|
|
defaultText = "pkgs.rt";
|
|
|
|
type = package;
|
|
|
|
};
|
|
|
|
|
|
|
|
baseUrl = mkOption {
|
|
|
|
description = "Base URL for web interface";
|
|
|
|
default = "https://${cfg.domain}";
|
|
|
|
defaultText = "https://\${cfg.domain}";
|
|
|
|
type = str;
|
|
|
|
};
|
|
|
|
|
|
|
|
commentAddress = mkOption {
|
|
|
|
description = "Default address from/to which comments are sent";
|
|
|
|
type = str;
|
|
|
|
};
|
|
|
|
|
|
|
|
correspondAddress = mkOption {
|
|
|
|
description = "Default address from/to which correspondences are sent";
|
|
|
|
type = str;
|
|
|
|
};
|
|
|
|
|
|
|
|
dbPasswordFile = mkOption {
|
|
|
|
description = "File containing the database password";
|
|
|
|
type = str;
|
|
|
|
default = "/etc/nixos/secret/rtpasswd";
|
|
|
|
internal = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
domain = mkOption {
|
|
|
|
description = "Which domain RT is running on";
|
|
|
|
type = str;
|
|
|
|
};
|
|
|
|
|
|
|
|
ownerEmail = mkOption {
|
|
|
|
description = "Address of a human who manages RT. RT will send errors generated by the mail gateway to this address; it will also be displayed as the contact person on the RT's login page.";
|
|
|
|
type = str;
|
|
|
|
};
|
|
|
|
|
|
|
|
port = mkOption {
|
|
|
|
description = "Which port rt-server should listen on";
|
|
|
|
type = port;
|
|
|
|
default = 4201;
|
|
|
|
};
|
|
|
|
|
|
|
|
sendmailPath = mkOption {
|
|
|
|
description = "Sendmail binary used to send... mail";
|
|
|
|
default = "${pkgs.msmtp}/bin/sendmail";
|
|
|
|
defaultText = "\${pkgs.msmtp}/bin/sendmail";
|
|
|
|
type = str;
|
|
|
|
};
|
|
|
|
|
|
|
|
sendmailArguments = mkOption {
|
|
|
|
description = "Arguments to call sendmailPath with";
|
|
|
|
default = [ ];
|
|
|
|
type = listOf (oneOf [ str path ]);
|
|
|
|
};
|
|
|
|
|
|
|
|
timeZone = mkOption {
|
|
|
|
description = "Used to convert times entered by users into GMT, as they are stored in the database, and back again; users can override this";
|
|
|
|
type = str;
|
|
|
|
default = config.time.timeZone;
|
|
|
|
defaultText = "[time.timeZone]";
|
|
|
|
};
|
|
|
|
|
|
|
|
rtName = mkOption {
|
|
|
|
description = "Name of this RT instance";
|
|
|
|
type = str;
|
|
|
|
};
|
|
|
|
|
|
|
|
organization = mkOption {
|
|
|
|
description = "Name of the organization of this instance";
|
|
|
|
type = str;
|
|
|
|
};
|
|
|
|
|
|
|
|
extraConfig = mkOption {
|
|
|
|
description = "Verbatim config to append to generated on";
|
|
|
|
type = lines;
|
|
|
|
default = "";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
config = let
|
|
|
|
components = [
|
|
|
|
"rt-clean-sessions"
|
|
|
|
"rt-email-dashboards"
|
|
|
|
"rt-email-digest-daily"
|
|
|
|
"rt-email-digest-weekly"
|
|
|
|
"rt-externalize-attachments"
|
|
|
|
"rt-fulltext-indexer"
|
|
|
|
"rt-validator"
|
|
|
|
];
|
|
|
|
|
|
|
|
mkTimer = name: {
|
|
|
|
"${name}" = {
|
|
|
|
wantedBy = [ "timers.target" ];
|
|
|
|
timerConfig.Unit = [ "${name}.service" ];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
mkService = name: extraArgs: {
|
|
|
|
"${name}" = {
|
|
|
|
stopIfChanged = false;
|
|
|
|
|
|
|
|
serviceConfig = {
|
|
|
|
ExecStart = if extraArgs == ""
|
|
|
|
then "${cfg.package}/bin/${name}"
|
|
|
|
else mkForce "${cfg.package}/bin/${name} ${extraArgs}";
|
|
|
|
User = "rt";
|
|
|
|
Group = "rt";
|
|
|
|
|
|
|
|
PrivateNetwork = false;
|
|
|
|
MemoryDenyWriteExecute = false;
|
|
|
|
|
|
|
|
ReadOnlyPaths = [ cfg.dbPasswordFile ];
|
|
|
|
};
|
|
|
|
|
|
|
|
environment = {
|
|
|
|
RT_SITE_CONFIG = configFile;
|
|
|
|
};
|
|
|
|
|
|
|
|
path = with pkgs; [
|
|
|
|
w3m
|
|
|
|
];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
in (mkIf cfg.enable {
|
|
|
|
systemd.services = mkMerge ((map (c: mkService c "") components) ++ [
|
|
|
|
(mkService "rt-server" "--port ${toString cfg.port} --server Starman")
|
|
|
|
(mkService "rt-clean-sessions" "--skip-user")
|
|
|
|
(mkService "rt-fulltext-indexer" "--limit 500000")
|
|
|
|
(mkService "rt-validator" "--check")
|
|
|
|
{
|
|
|
|
rt-server = {
|
|
|
|
serviceConfig = {
|
|
|
|
StateDirectory = [ "rt/" "rt/attachments/" "rt/shredder/" "rt/smime/" ];
|
|
|
|
RuntimeDirectory = [ "rt/" "rt/mason_data/" ];
|
|
|
|
LogsDirectory = "rt/";
|
|
|
|
};
|
|
|
|
|
2021-11-22 12:18:06 +08:00
|
|
|
after = [ "postgresql.service" ];
|
2021-08-10 21:28:14 +08:00
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
};
|
|
|
|
}
|
|
|
|
{
|
|
|
|
rt-externalize-attachments = {
|
|
|
|
serviceConfig.StateDirectory = "rt/attachments/";
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
{ rt-email-digest-daily.serviceConfig.ExecStart = mkForce "${cfg.package}/bin/rt-email-digest -m daily"; }
|
|
|
|
{ rt-email-digest-weekly.serviceConfig.ExecStart = mkForce "${cfg.package}/bin/rt-email-digest -m weekly"; }
|
|
|
|
]);
|
|
|
|
|
|
|
|
systemd.timers = mkMerge ((map mkTimer components) ++ [
|
|
|
|
{
|
|
|
|
rt-clean-sessions.timerConfig.OnCalendar = "daily";
|
|
|
|
rt-email-dashboards.timerConfig.OnCalendar = "hourly";
|
|
|
|
rt-email-digest-daily.timerConfig.OnCalendar = "daily";
|
|
|
|
rt-email-digest-weekly.timerConfig.OnCalendar = "weekly";
|
|
|
|
rt-externalize-attachments.timerConfig.OnCalendar = "01:00";
|
|
|
|
rt-fulltext-indexer.timerConfig.OnCalendar = "02:00";
|
|
|
|
rt-validator.timerConfig.OnCalendar = "*-*-01 03:00:00";
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
|
|
|
|
users.users.rt = {
|
|
|
|
isSystemUser = true;
|
2021-11-23 13:52:00 +08:00
|
|
|
group = "rt";
|
2021-08-10 21:28:14 +08:00
|
|
|
};
|
|
|
|
users.groups.rt = {};
|
|
|
|
|
|
|
|
systemd.tmpfiles.rules = [
|
|
|
|
"d /var/lib/secrets/rt 0500 rt rt -"
|
|
|
|
"d /var/lib/rt/gpg 0700 rt rt -"
|
|
|
|
];
|
|
|
|
});
|
|
|
|
}
|