@ -48,78 +48,98 @@ let
let proto = removeSuffix " d " srv ;
needNetwork = builtins . hasAttr proto cfg && cfg . ${ proto } . port == null ;
in {
# Enable JIT-compiled C (via Inline::C)
Environment = [ " P E R L _ I N L I N E _ D I R E C T O R Y = / r u n / p u b l i c - i n b o x - ${ srv } / p e r l - i n l i n e " ] ;
# NonBlocking is REQUIRED to avoid a race condition
# if running simultaneous services.
NonBlocking = true ;
#LimitNOFILE = 30000;
User = config . users . users . " p u b l i c - i n b o x " . name ;
Group = config . users . groups . " p u b l i c - i n b o x " . name ;
RuntimeDirectory = [
" p u b l i c - i n b o x - ${ srv } / p e r l - i n l i n e "
# Create RootDirectory= in the host's mount namespace.
" p u b l i c - i n b o x - ${ srv } / r o o t "
] ;
RuntimeDirectoryMode = " 7 0 0 " ;
# Avoid mounting RootDirectory= in the own RootDirectory= of ExecStart='s mount namespace.
InaccessiblePaths = [ " - + / r u n / p u b l i c - i n b o x - ${ srv } / r o o t " ] ;
# This is for BindPaths= and BindReadOnlyPaths=
# to allow traversal of directories they create in RootDirectory=.
UMask = " 0 0 6 6 " ;
RootDirectory = " / r u n / p u b l i c - i n b o x - ${ srv } / r o o t " ;
RootDirectoryStartOnly = true ;
WorkingDirectory = stateDir ;
MountAPIVFS = true ;
BindReadOnlyPaths = [
builtins . storeDir
" / e t c "
" / r u n "
# For Inline::C
" / b i n / s h "
] ;
BindPaths = [
stateDir
] ;
# The following options are only for optimizing:
# systemd-analyze security public-inbox-'*'
AmbientCapabilities = " " ;
CapabilityBoundingSet = " " ;
# ProtectClock= adds DeviceAllow=char-rtc r
DeviceAllow = " " ;
LockPersonality = true ;
MemoryDenyWriteExecute = true ;
NoNewPrivileges = true ;
PrivateDevices = true ;
PrivateMounts = true ;
PrivateNetwork = mkDefault ( ! needNetwork ) ;
PrivateTmp = true ;
PrivateUsers = true ;
ProcSubset = " p i d " ;
ProtectClock = true ;
ProtectControlGroups = true ;
ProtectHome = mkDefault true ;
ProtectHostname = true ;
ProtectKernelLogs = true ;
ProtectKernelModules = true ;
ProtectKernelTunables = true ;
ProtectProc = " i n v i s i b l e " ;
ProtectSystem = " s t r i c t " ;
RemoveIPC = true ;
RestrictAddressFamilies = [ " A F _ U N I X " ]
++ optionals needNetwork [ " A F _ I N E T " " A F _ I N E T 6 " ] ;
RestrictNamespaces = true ;
RestrictRealtime = true ;
RestrictSUIDSGID = true ;
SystemCallFilter = [
" @ s y s t e m - s e r v i c e "
" ~ @ a i o " " ~ @ c h o w n " " ~ @ k e y r i n g " " ~ @ m e m l o c k " " ~ @ r e s o u r c e s "
# Not removing @setuid and @privileged
# because Inline::C needs them.
# Not removing @timer
# because git upload-pack needs it.
] ;
SystemCallArchitectures = " n a t i v e " ;
serviceConfig = {
# Enable JIT-compiled C (via Inline::C)
Environment = [ " P E R L _ I N L I N E _ D I R E C T O R Y = / r u n / p u b l i c - i n b o x - ${ srv } / p e r l - i n l i n e " ] ;
# NonBlocking is REQUIRED to avoid a race condition
# if running simultaneous services.
NonBlocking = true ;
#LimitNOFILE = 30000;
User = config . users . users . " p u b l i c - i n b o x " . name ;
Group = config . users . groups . " p u b l i c - i n b o x " . name ;
RuntimeDirectory = [
" p u b l i c - i n b o x - ${ srv } / p e r l - i n l i n e "
] ;
RuntimeDirectoryMode = " 7 0 0 " ;
# This is for BindPaths= and BindReadOnlyPaths=
# to allow traversal of directories they create inside RootDirectory=
UMask = " 0 0 6 6 " ;
StateDirectory = [ " p u b l i c - i n b o x " ] ;
StateDirectoryMode = " 0 7 5 0 " ;
WorkingDirectory = stateDir ;
BindReadOnlyPaths = [
" / e t c "
" / r u n / s y s t e m d "
" ${ config . i18n . glibcLocales } "
] ++
mapAttrsToList ( name : inbox : inbox . description ) cfg . inboxes ++
# Without confinement the whole Nix store
# is made available to the service
optionals ( ! config . systemd . services . " p u b l i c - i n b o x - ${ srv } " . confinement . enable ) [
" ${ pkgs . dash } / b i n / d a s h : / b i n / s h "
builtins . storeDir
] ;
# The following options are only for optimizing:
# systemd-analyze security public-inbox-'*'
AmbientCapabilities = " " ;
CapabilityBoundingSet = " " ;
# ProtectClock= adds DeviceAllow=char-rtc r
DeviceAllow = " " ;
LockPersonality = true ;
MemoryDenyWriteExecute = true ;
NoNewPrivileges = true ;
PrivateNetwork = mkDefault ( ! needNetwork ) ;
ProcSubset = " p i d " ;
ProtectClock = true ;
ProtectHome = mkDefault true ;
ProtectHostname = true ;
ProtectKernelLogs = true ;
ProtectProc = " i n v i s i b l e " ;
#ProtectSystem = "strict";
RemoveIPC = true ;
RestrictAddressFamilies = [ " A F _ U N I X " ] ++
optionals needNetwork [ " A F _ I N E T " " A F _ I N E T 6 " ] ;
RestrictNamespaces = true ;
RestrictRealtime = true ;
RestrictSUIDSGID = true ;
SystemCallFilter = [
" @ s y s t e m - s e r v i c e "
" ~ @ a i o " " ~ @ c h o w n " " ~ @ k e y r i n g " " ~ @ m e m l o c k " " ~ @ r e s o u r c e s "
# Not removing @setuid and @privileged because Inline::C needs them.
# Not removing @timer because git upload-pack needs it.
] ;
SystemCallArchitectures = " n a t i v e " ;
# The following options are redundant when confinement is enabled
RootDirectory = " / v a r / e m p t y " ;
TemporaryFileSystem = " / " ;
PrivateMounts = true ;
MountAPIVFS = true ;
PrivateDevices = true ;
PrivateTmp = true ;
PrivateUsers = true ;
ProtectControlGroups = true ;
ProtectKernelModules = true ;
ProtectKernelTunables = true ;
} ;
confinement = {
# Until we agree upon doing it directly here in NixOS
# https://github.com/NixOS/nixpkgs/pull/104457#issuecomment-1115768447
# let the user choose to enable the confinement with:
# systemd.services.public-inbox-httpd.confinement.enable = true;
# systemd.services.public-inbox-imapd.confinement.enable = true;
# systemd.services.public-inbox-init.confinement.enable = true;
# systemd.services.public-inbox-nntpd.confinement.enable = true;
#enable = true;
mode = " f u l l - a p i v f s " ;
# Inline::C needs a /bin/sh, and dash is enough
binSh = " ${ pkgs . dash } / b i n / d a s h " ;
packages = [
pkgs . iana-etc
( getLib pkgs . nss )
pkgs . tzdata
] ;
} ;
} ;
in
@ -168,6 +188,7 @@ in
type = types . str ;
example = " u s e r / d e v d i s c u s s i o n o f p u b l i c - i n b o x i t s e l f " ;
description = " U s e r - v i s i b l e d e s c r i p t i o n f o r t h e r e p o s i t o r y . " ;
apply = pkgs . writeText " p u b l i c - i n b o x - d e s c r i p t i o n - ${ name } " ;
} ;
options . newsgroup = mkOption {
type = with types ; nullOr str ;
@ -409,24 +430,24 @@ in
) [ " i m a p " " h t t p " " n n t p " ] ) ;
systemd . services = mkMerge [
( mkIf cfg . imap . enable
{ public-inbox-imapd = {
{ public-inbox-imapd = mkMerge [ ( serviceConfig " i m a p d " ) {
after = [ " p u b l i c - i n b o x - i n i t . s e r v i c e " " p u b l i c - i n b o x - w a t c h . s e r v i c e " ] ;
requires = [ " p u b l i c - i n b o x - i n i t . s e r v i c e " ] ;
serviceConfig = mkMerge [ ( serviceConfig " i m a p d " ) {
serviceConfig = {
ExecStart = escapeShellArgs (
[ " ${ cfg . package } / b i n / p u b l i c - i n b o x - i m a p d " ] ++
cfg . imap . args ++
optionals ( cfg . imap . cert != null ) [ " - - c e r t " cfg . imap . cert ] ++
optionals ( cfg . imap . key != null ) [ " - - k e y " cfg . imap . key ]
) ;
} ] ;
} ;
} ;
} ] ;
} )
( mkIf cfg . http . enable
{ public-inbox-httpd = {
{ public-inbox-httpd = mkMerge [ ( serviceConfig " h t t p d " ) {
after = [ " p u b l i c - i n b o x - i n i t . s e r v i c e " " p u b l i c - i n b o x - w a t c h . s e r v i c e " ] ;
requires = [ " p u b l i c - i n b o x - i n i t . s e r v i c e " ] ;
serviceConfig = mkMerge [ ( serviceConfig " h t t p d " ) {
serviceConfig = {
ExecStart = escapeShellArgs (
[ " ${ cfg . package } / b i n / p u b l i c - i n b o x - h t t p d " ] ++
cfg . http . args ++
@ -458,41 +479,41 @@ in
}
'' ) ]
) ;
} ] ;
} ;
} ;
} ] ;
} )
( mkIf cfg . nntp . enable
{ public-inbox-nntpd = {
{ public-inbox-nntpd = mkMerge [ ( serviceConfig " n n t p d " ) {
after = [ " p u b l i c - i n b o x - i n i t . s e r v i c e " " p u b l i c - i n b o x - w a t c h . s e r v i c e " ] ;
requires = [ " p u b l i c - i n b o x - i n i t . s e r v i c e " ] ;
serviceConfig = mkMerge [ ( serviceConfig " n n t p d " ) {
serviceConfig = {
ExecStart = escapeShellArgs (
[ " ${ cfg . package } / b i n / p u b l i c - i n b o x - n n t p d " ] ++
cfg . nntp . args ++
optionals ( cfg . nntp . cert != null ) [ " - - c e r t " cfg . nntp . cert ] ++
optionals ( cfg . nntp . key != null ) [ " - - k e y " cfg . nntp . key ]
) ;
} ] ;
} ;
} ;
} ] ;
} )
( mkIf ( any ( inbox : inbox . watch != [ ] ) ( attrValues cfg . inboxes )
|| cfg . settings . publicinboxwatch . watchspam != null )
{ public-inbox-watch = {
{ public-inbox-watch = mkMerge [ ( serviceConfig " w a t c h " ) {
inherit ( cfg ) path ;
wants = [ " p u b l i c - i n b o x - i n i t . s e r v i c e " ] ;
requires = [ " p u b l i c - i n b o x - i n i t . s e r v i c e " ] ++
optional ( cfg . settings . publicinboxwatch . spamcheck == " s p a m c " ) " s p a m a s s a s s i n . s e r v i c e " ;
wantedBy = [ " m u l t i - u s e r . t a r g e t " ] ;
serviceConfig = mkMerge [ ( serviceConfig " w a t c h " ) {
serviceConfig = {
ExecStart = " ${ cfg . package } / b i n / p u b l i c - i n b o x - w a t c h " ;
ExecReload = " ${ pkgs . coreutils } / b i n / k i l l - H U P $ M A I N P I D " ;
} ] ;
} ;
} ;
} ] ;
} )
( { public-inbox-init = let
PI_CONFIG = gitIni . generate " p u b l i c - i n b o x . i n i "
( filterAttrsRecursive ( n : v : v != null ) cfg . settings ) ;
in {
in mkMerge [ ( serviceConfig " i n i t " ) {
wantedBy = [ " m u l t i - u s e r . t a r g e t " ] ;
restartIfChanged = true ;
restartTriggers = [ PI_CONFIG ] ;
@ -520,7 +541,7 @@ in
rm - rf $ conf_dir
fi
ln - sf $ { pkgs . writeText " d e s c r i p t i o n " inbox . description } \
ln - sf $ { inbox . description } \
$ { stateDir } /inboxes / $ { escapeShellArg name } /description
export GIT_DIR = $ { stateDir } /inboxes / $ { escapeShellArg name } /all.git
@ -540,18 +561,16 @@ in
$ { cfg . package } /bin/public-inbox-index " $ i n b o x "
done
'' ;
serviceConfig = mkMerge [ ( serviceConfig " i n i t " ) {
serviceConfig = {
Type = " o n e s h o t " ;
RemainAfterExit = true ;
StateDirectory = [
" p u b l i c - i n b o x "
" p u b l i c - i n b o x / . p u b l i c - i n b o x "
" p u b l i c - i n b o x / . p u b l i c - i n b o x / e m e r g e n c y "
" p u b l i c - i n b o x / i n b o x e s "
] ;
StateDirectoryMode = " 0 7 5 0 " ;
} ] ;
} ;
} ;
} ] ;
} )
] ;
environment . systemPackages = with pkgs ; [ cfg . package ] ;