@@ -0,0 +1,269 @@
{ config , lib , pkgs , options , . . . }:
with lib ;
let
cfg = config . services . biboumi ;
inherit ( config . environment ) etc ;
rootDir = " / r u n / b i b o u m i / m n t - r o o t " ;
stateDir = " / v a r / l i b / b i b o u m i " ;
settingsFile = pkgs . writeText " b i b o u m i . c f g " (
generators . toKeyValue {
mkKeyValue = k : v :
if v = = null then " "
else generators . mkKeyValueDefault { } " = " k v ;
} cfg . settings ) ;
need_CAP_NET_BIND_SERVICE = cfg . settings . identd_port != 0 && cfg . settings . identd_port < 1024 ;
in
{
options = {
services . biboumi = {
enable = mkEnableOption " t h e B i b o u m i X M P P g a t e w a y t o I R C " ;
settings = mkOption {
description = ''
S e e < l i n k x l i n k : h r e f = " h t t p s : / / l a b . l o u i z . o r g / l o u i z / b i b o u m i / b l o b / 8 . 5 / d o c / b i b o u m i . 1 . r s t " > b i b o u m i 8 . 5 < / l i n k >
f o r d o c u m e n t a t i o n .
'' ;
default = { } ;
type = types . submodule {
freeformType = with types ;
( attrsOf ( nullOr ( oneOf [ str int bool ] ) ) ) // {
description = " s e t t i n g s o p t i o n " ;
} ;
options . admin = mkOption {
type = with types ; listOf str ;
default = [ ] ;
example = [ " a d m i n @ e x a m p l e . o r g " ] ;
apply = concatStringsSep " : " ;
description = ''
T h e b a r e J I D o f t h e g a t e w a y a d m i n i s t r a t o r . T h i s J I D w i l l h a v e m o r e
p r i v i l e g e s t h a n o t h e r s t a n d a r d u s e r s , f o r e x a m p l e s o m e a d m i n i s t r a t i o n
a d - h o c c o m m a n d s w i l l o n l y b e a v a i l a b l e t o t h a t J I D .
'' ;
} ;
options . ca_file = mkOption {
type = types . path ;
default = " / e t c / s s l / c e r t s / c a - c e r t i f i c a t e s . c r t " ;
description = ''
S p e c i f i e s w h i c h f i l e s h o u l d b e u s e d a s t h e l i s t o f t r u s t e d C A
w h e n n e g o c i a t i n g a T L S s e s s i o n .
'' ;
} ;
options . db_name = mkOption {
type = with types ; either path str ;
default = " ${ stateDir } / b i b o u m i . s q l i t e " ;
description = ''
T h e n a m e o f t h e d a t a b a s e t o u s e .
'' ;
example = " p o s t g r e s q l : / / u s e r : s e c r e t @ l o c a l h o s t " ;
} ;
options . hostname = mkOption {
type = types . str ;
example = " b i b o u m i . e x a m p l e . o r g " ;
description = ''
T h e h o s t n a m e s e r v e d b y t h e X M P P g a t e w a y .
T h i s d o m a i n m u s t b e c o n f i g u r e d i n t h e X M P P s e r v e r
a s a n e x t e r n a l c o m p o n e n t .
'' ;
} ;
options . identd_port = mkOption {
type = types . port ;
default = 113 ;
example = 0 ;
description = ''
T h e T C P p o r t o n w h i c h t o l i s t e n f o r i d e n t d q u e r i e s .
'' ;
} ;
options . log_level = mkOption {
type = types . ints . between 0 3 ;
default = 1 ;
description = ''
I n d i c a t e w h a t t y p e o f l o g m e s s a g e s t o w r i t e i n t h e l o g s .
0 i s d e b u g , 1 i s i n f o , 2 i s w a r n i n g , 3 i s e r r o r .
'' ;
} ;
options . password = mkOption {
type = with types ; nullOr str ;
description = ''
T h e p a s s w o r d u s e d t o a u t h e n t i c a t e t h e X M P P c o m p o n e n t t o y o u r X M P P s e r v e r .
T h i s p a s s w o r d m u s t b e c o n f i g u r e d i n t h e X M P P s e r v e r ,
a s s o c i a t e d w i t h t h e e x t e r n a l c o m p o n e n t o n
< l i n k l i n k e n d = " o p t - s e r v i c e s . b i b o u m i . s e t t i n g s . h o s t n a m e " > h o s t n a m e < / l i n k > .
S e t i t t o n u l l a n d u s e < l i n k l i n k e n d = " o p t - s e r v i c e s . b i b o u m i . c r e d e n t i a l s F i l e " > c r e d e n t i a l s F i l e < / l i n k >
i f y o u d o n o t w a n t t h i s p a s s w o r d t o g o i n t o t h e N i x s t o r e .
'' ;
} ;
options . persistent_by_default = mkOption {
type = types . bool ;
default = false ;
description = ''
W h e t h e r a l l r o o m s w i l l b e p e r s i s t e n t b y d e f a u l t :
t h e v a l u e o f t h e “ p e r s i s t e n t ” o p t i o n i n t h e g l o b a l c o n f i g u r a t i o n o f e a c h
u s e r w i l l b e “ t r u e ” , b u t t h e v a l u e o f e a c h i n d i v i d u a l r o o m w i l l s t i l l
d e f a u l t t o f a l s e . T h i s m e a n s t h a t a u s e r j u s t n e e d s t o c h a n g e t h e g l o b a l
“ p e r s i s t e n t ” c o n f i g u r a t i o n o p t i o n t o f a l s e i n o r d e r t o o v e r r i d e t h i s .
'' ;
} ;
options . policy_directory = mkOption {
type = types . path ;
default = " ${ pkgs . biboumi } / e t c / b i b o u m i " ;
description = ''
A d i r e c t o r y t h a t s h o u l d c o n t a i n t h e p o l i c y f i l e s ,
u s e d t o c u s t o m i z e B o t a n ’ s b e h a v i o u r
w h e n n e g o c i a t i n g t h e T L S c o n n e c t i o n s w i t h t h e I R C s e r v e r s .
'' ;
} ;
options . port = mkOption {
type = types . port ;
default = 5347 ;
description = ''
T h e T C P p o r t t o u s e t o c o n n e c t t o t h e l o c a l X M P P c o m p o n e n t .
'' ;
} ;
options . realname_customization = mkOption {
type = types . bool ;
default = true ;
description = ''
W h e t h e r t h e u s e r s w i l l b e a b l e t o u s e
t h e a d - h o c c o m m a n d s t h a t l e t s t h e m c o n f i g u r e
t h e i r r e a l n a m e a n d u s e r n a m e .
'' ;
} ;
options . realname_from_jid = mkOption {
type = types . bool ;
default = false ;
description = ''
W h e t h e r t h e r e a l n a m e a n d u s e r n a m e o f e a c h b i b o u m i
u s e r w i l l b e e x t r a c t e d f r o m t h e i r J I D .
O t h e r w i s e t h e y w i l l b e s e t t o t h e n i c k
t h e y u s e d t o c o n n e c t t o t h e I R C s e r v e r .
'' ;
} ;
options . xmpp_server_ip = mkOption {
type = types . str ;
default = " 1 2 7 . 0 . 0 . 1 " ;
description = ''
T h e I P a d d r e s s t o c o n n e c t t o t h e X M P P s e r v e r o n .
T h e c o n n e c t i o n t o t h e X M P P s e r v e r i s u n e n c r y p t e d ,
s o t h e b i b o u m i i n s t a n c e a n d t h e s e r v e r s h o u l d
n o r m a l l y b e o n t h e s a m e h o s t .
'' ;
} ;
} ;
} ;
credentialsFile = mkOption {
type = types . path ;
description = ''
P a t h t o a c o n f i g u r a t i o n f i l e t o b e m e r g e d w i t h t h e s e t t i n g s .
B e w a r e n o t t o s u r r o u n d " = " w i t h s p a c e s w h e n s e t t i n g b i b o u m i ' s o p t i o n s i n t h i s f i l e .
U s e f u l t o m e r g e a f i l e w h i c h i s b e t t e r k e p t o u t o f t h e N i x s t o r e
b e c a u s e i t c o n t a i n s s e n s i b l e d a t a l i k e
< l i n k l i n k e n d = " o p t - s e r v i c e s . b i b o u m i . s e t t i n g s . p a s s w o r d " > p a s s w o r d < / l i n k > .
'' ;
default = " / d e v / n u l l " ;
example = " / r u n / k e y s / b i b o u m i . c f g " ;
} ;
openFirewall = mkEnableOption " o p e n i n g o f t h e i d e n t d p o r t i n t h e f i r e w a l l " ;
} ;
} ;
config = mkIf cfg . enable {
networking . firewall = mkIf ( cfg . openFirewall && cfg . settings . identd_port != 0 )
{ allowedTCPPorts = [ cfg . settings . identd_port ] ; } ;
systemd . services . biboumi = {
description = " B i b o u m i , X M P P t o I R C g a t e w a y " ;
after = [ " n e t w o r k . t a r g e t " ] ;
wantedBy = [ " m u l t i - u s e r . t a r g e t " ] ;
serviceConfig = {
Type = " n o t i f y " ;
# Biboumi supports systemd's watchdog.
WatchdogSec = 20 ;
Restart = " a l w a y s " ;
# Use "+" because credentialsFile may not be accessible to User= or Group=.
ExecStartPre = [ ( " + " + pkgs . writeShellScript " b i b o u m i - p r e s t a r t " ''
s e t - e u x
c a t ${ settingsFile } ' ${ cfg . credentialsFile } ' |
i n s t a l l - m 6 4 4 / d e v / s t d i n / r u n / b i b o u m i / b i b o u m i . c f g
'' ) ] ;
ExecStart = " ${ pkgs . biboumi } / b i n / b i b o u m i / r u n / b i b o u m i / b i b o u m i . c f g " ;
ExecReload = " ${ pkgs . coreutils } / b i n / k i l l - U S R 1 $ M A I N P I D " ;
# Firewalls needing opening for output connections can still do that
# selectively for biboumi with:
# users.users.biboumi.isSystemUser = true;
# and, for example:
# networking.nftables.ruleset = ''
# add rule inet filter output meta skuid biboumi tcp accept
# '';
DynamicUser = true ;
RootDirectory = rootDir ;
RootDirectoryStartOnly = true ;
InaccessiblePaths = [ " - + ${ rootDir } " ] ;
RuntimeDirectory = [ " b i b o u m i " ( removePrefix " / r u n / " rootDir ) ] ;
RuntimeDirectoryMode = " 7 0 0 " ;
StateDirectory = " b i b o u m i " ;
StateDirectoryMode = " 7 0 0 " ;
MountAPIVFS = true ;
UMask = " 0 0 6 6 " ;
BindPaths = [
stateDir
# This is for Type="notify"
# See https://github.com/systemd/systemd/issues/3544
" / r u n / s y s t e m d / n o t i f y "
" / r u n / s y s t e m d / j o u r n a l / s o c k e t "
] ;
BindReadOnlyPaths = [
builtins . storeDir
" / e t c "
] ;
# The following options are only for optimizing:
# systemd-analyze security biboumi
AmbientCapabilities = [ ( optionalString need_CAP_NET_BIND_SERVICE " C A P _ N E T _ B I N D _ S E R V I C E " ) ] ;
CapabilityBoundingSet = [ ( optionalString need_CAP_NET_BIND_SERVICE " C A P _ N E T _ B I N D _ S E R V I C E " ) ] ;
# ProtectClock= adds DeviceAllow=char-rtc r
DeviceAllow = " " ;
LockPersonality = true ;
MemoryDenyWriteExecute = true ;
NoNewPrivileges = true ;
PrivateDevices = true ;
PrivateMounts = true ;
PrivateNetwork = mkDefault false ;
PrivateTmp = true ;
# PrivateUsers=true breaks AmbientCapabilities=CAP_NET_BIND_SERVICE
# See https://bugs.archlinux.org/task/65921
PrivateUsers = ! need_CAP_NET_BIND_SERVICE ;
ProtectClock = true ;
ProtectControlGroups = true ;
ProtectHome = true ;
ProtectHostname = true ;
ProtectKernelLogs = true ;
ProtectKernelModules = true ;
ProtectKernelTunables = true ;
ProtectSystem = " s t r i c t " ;
RemoveIPC = true ;
# AF_UNIX is for /run/systemd/notify
RestrictAddressFamilies = [ " A F _ U N I X " " 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 "
# Groups in @system-service which do not contain a syscall
# listed by perf stat -e 'syscalls:sys_enter_*' biboumi biboumi.cfg
# in tests, and seem likely not necessary for biboumi.
# To run such a perf in ExecStart=, you have to:
# - AmbientCapabilities="CAP_SYS_ADMIN"
# - mount -o remount,mode=755 /sys/kernel/debug/{,tracing}
" ~ @ a i o " " ~ @ c h o w n " " ~ @ i p c " " ~ @ k e y r i n g " " ~ @ r e s o u r c e s " " ~ @ s e t u i d " " ~ @ t i m e r "
] ;
SystemCallArchitectures = " n a t i v e " ;
SystemCallErrorNumber = " E P E R M " ;
} ;
} ;
} ;
meta . maintainers = with maintainers ; [ julm ] ;
}