2023-05-11 05:33:08 +07:00
{ lib
, pkgs
, config
, . . . } :
let
cfg = config . server ;
efiPart = " / d e v / d i s k / b y - u u i d / 3 E 2 A - A 5 C B " ;
rootUuid = " 6 a a c e 2 3 7 - 9 b 4 8 - 4 2 9 4 - 8 e 9 6 - 1 9 6 7 5 9 a 5 3 0 5 b " ;
rootPart = " / d e v / d i s k / b y - u u i d / ${ rootUuid } " ;
hosted-domains =
map
( prefix : if prefix == null then cfg . domainName else " ${ prefix } . ${ cfg . domainName } " )
[
null
" d n s "
" m u m b l e "
" m a i l "
" m u s i c "
" w w w "
" m a t r i x "
" s e a r c h "
" g i t "
" c l o u d "
" n s 1 "
" n s 2 "
] ;
in {
imports = [
./options.nix
./matrix.nix
./fdroid.nix
./mumble.nix
] ;
system . stateVersion = " 2 2 . 1 1 " ;
boot = {
initrd = {
availableKernelModules = [ " x h c i _ p c i " " e h c i _ p c i " " a h c i " " u s b _ s t o r a g e " " s d _ m o d " " s r _ m o d " " r t s x _ p c i _ s d m m c " ] ;
} ;
kernelModules = [ " k v m - i n t e l " ] ;
kernelParams = [
" c o n s o l e b l a n k = 6 0 "
] ;
loader = {
grub = {
enable = true ;
device = " n o d e v " ;
version = 2 ;
efiSupport = true ;
efiInstallAsRemovable = true ;
gfxmodeEfi = " 1 9 2 0 x 1 0 8 0 " ;
gfxmodeBios = " 1 9 2 0 x 1 0 8 0 " ;
} ;
efi . efiSysMountPoint = " / b o o t / e f i " ;
} ;
} ;
hardware . enableRedistributableFirmware = true ;
fileSystems = {
" / " = { device = " n o n e " ; fsType = " t m p f s " ; neededForBoot = true ;
options = [ " d e f a u l t s " " s i z e = 2 G " " m o d e = 7 5 5 " ] ; } ;
" / p e r s i s t " =
{ device = rootPart ; fsType = " b t r f s " ; neededForBoot = true ;
options = [ " c o m p r e s s = z s t d : 1 5 " ] ; } ;
" / b o o t " =
{ device = rootPart ; fsType = " b t r f s " ; neededForBoot = true ;
options = [ " c o m p r e s s = z s t d : 1 5 " " s u b v o l = b o o t " ] ; } ;
" / b o o t / e f i " =
{ device = efiPart ; fsType = " v f a t " ; } ;
} ;
zramSwap . enable = true ;
swapDevices = [ ] ;
impermanence = {
enable = true ;
path = /persist ;
directories = [
{ directory = /var/www / ${ cfg . domainName } ; }
{ directory = /var/lib/maubot ; }
{ directory = /var/lib/fdroid ; }
{ directory = config . mailserver . dkimKeyDirectory ; }
{ directory = config . mailserver . mailDirectory ; }
{ directory = /home/user ; }
{ directory = /root ; }
{ directory = /nix ; }
] ;
} ;
services . beesd = {
filesystems . root = {
spec = " U U I D = ${ rootUuid } " ;
hashTableSizeMB = 128 ;
extraOptions = [ " - - l o a d a v g - t a r g e t " " 8 . 0 " ] ;
} ;
} ;
i18n . defaultLocale = lib . mkDefault " e n _ U S . U T F - 8 " ;
i18n . supportedLocales = lib . mkDefault [
" C . U T F - 8 / U T F - 8 "
" e n _ U S . U T F - 8 / U T F - 8 "
" e n _ D K . U T F - 8 / U T F - 8 "
] ;
# ISO-8601
i18n . extraLocaleSettings . LC_TIME = " e n _ D K . U T F - 8 " ;
console . font = " ${ pkgs . terminus_font } / s h a r e / c o n s o l e f o n t s / t e r - v 2 4 n . p s f . g z " ;
networking . useDHCP = true ;
networking . resolvconf . extraConfig = ''
name_servers = " 1 2 7 . 0 . 0 . 1 : : 1 "
'' ;
networking . firewall = {
enable = true ;
allowedTCPPorts = [
# ssh
22
# dns
53 853
# http/s
80 443
] ;
allowedUDPPorts = [
# dns
53 853
# wireguard
# 5553
] ;
} ;
# UNBOUND
services . unbound = {
enable = true ;
package = pkgs . unbound-with-systemd . override {
withPythonModule = true ;
python = pkgs . python3 . withPackages ( pkgs : with pkgs ; [ pydbus dnspython ] ) ;
} ;
localControlSocketPath = " / r u n / u n b o u n d / u n b o u n d . c t l " ;
resolveLocalQueries = false ;
settings = {
server = {
interface = [ " 0 . 0 . 0 . 0 " " : : " ] ;
access-control = [ " ${ cfg . lanCidrV4 } a l l o w " " ${ cfg . lanCidrV6 } a l l o w " ] ;
aggressive-nsec = true ;
do-ip6 = true ;
module-config = '' " v a l i d a t o r i t e r a t o r " '' ;
local-zone = [
'' " l o c a l . " s t a t i c ''
] ++ ( lib . optionals ( cfg . localIpV4 != null || cfg . localIpV6 != null ) [
'' " ${ cfg . domainName } . " t y p e t r a n s p a r e n t ''
] ) ;
local-data = builtins . concatLists ( map ( domain :
lib . optionals ( cfg . localIpV4 != null ) [
'' " ${ domain } . A ${ cfg . localIpV4 } " ''
] ++ ( lib . optionals ( cfg . localIpV6 != null ) [
'' " ${ domain } . A ${ cfg . localIpV6 } " ''
] ) ) hosted-domains ) ;
} ;
python . python-script = toString ( pkgs . fetchurl {
url = " h t t p s : / / r a w . g i t h u b u s e r c o n t e n t . c o m / N L n e t L a b s / u n b o u n d / a 9 1 2 7 8 6 c a 9 e 7 2 d c 1 c c d e 9 8 d 5 a f 7 d 2 3 5 9 5 6 4 0 0 4 3 b / p y t h o n m o d / e x a m p l e s / a v a h i - r e s o l v e r . p y " ;
sha256 = " 0 r 1 i q j f 0 8 w r k p z v j 6 p q l 1 j q a 8 8 4 h b b f y 9 i x 5 g x d r k r v a 0 9 m s i q g i " ;
} ) ;
remote-control . control-enable = true ;
} ;
} ;
systemd . services . unbound . environment . MDNS_ACCEPT_NAMES = " ^ . * \\ . l o c a l \\ . $ " ;
# just in case
networking . hosts . " 1 2 7 . 0 . 0 . 1 " = [ " l o c a l h o s t " ] ++ hosted-domains ;
# CUPS
services . printing = {
enable = true ;
allowFrom = [ cfg . lanCidrV4 cfg . lanCidrV6 ] ;
browsing = true ;
clientConf = ''
ServerName $ { cfg . domainName }
'' ;
defaultShared = true ;
drivers = [ pkgs . hplip ] ;
startWhenNeeded = false ;
} ;
programs . fish . enable = true ;
users . defaultUserShell = pkgs . fish ;
users . users . user = {
isNormalUser = true ;
extraGroups = [ " w h e e l " config . services . unbound . group ] ;
} ;
environment . systemPackages = with pkgs ; [
comma
git
vim
wget
# rxvt-unicode-unwrapped.terminfo
kitty . terminfo
tmux
] ;
services . postgresql . enable = true ;
services . postgresql . package = pkgs . postgresql_13 ;
nix = {
settings = {
allowed-users = [ " u s e r " ] ;
auto-optimise-store = true ;
} ;
gc = {
automatic = true ;
dates = " w e e k l y " ;
options = " - - d e l e t e - o l d e r - t h a n 3 0 d " ;
} ;
package = pkgs . nixFlakes ;
extraOptions = ''
experimental-features = nix-command flakes
'' ;
} ;
systemd . services . nix-daemon . serviceConfig . LimitSTACKSoft = " i n f i n i t y " ;
# SSH
services . openssh = {
enable = true ;
2023-05-11 17:28:47 +07:00
# settings.PermitRootLogin = "no";
2023-05-11 05:33:08 +07:00
settings . PasswordAuthentication = false ;
listenAddresses = [ {
addr = " 0 . 0 . 0 . 0 " ;
} {
addr = " : : " ;
} ] ;
} ;
services . fail2ban . enable = true ;
# SEARXNG
services . searx . enable = true ;
services . searx . package = pkgs . searxng . overrideAttrs ( _ : {
src = pkgs . fetchFromGitHub {
owner = " s e a r x n g " ;
repo = " s e a r x n g " ;
rev = " c b 1 c 3 7 4 1 d 7 d e 1 3 5 4 b 5 2 4 5 8 9 1 1 4 6 1 7 f 1 8 3 0 0 9 f 6 a 8 " ;
sha256 = " s h a 2 5 6 - 7 e r Y 5 B d 1 Z o T p A I D b h I u p u 6 4 X d 1 P Q s p a W 6 v B q u 7 k n z N I = " ;
} ;
} ) ;
services . searx . runInUwsgi = true ;
services . searx . uwsgiConfig = let inherit ( config . services . searx ) settings ; in {
2023-05-11 17:28:47 +07:00
socket = " ${ lib . quotePotentialIpV6 settings . server . bind_address } : ${ toString settings . server . port } " ;
2023-05-11 05:33:08 +07:00
} ;
users . groups . searx . members = [ " n g i n x " ] ;
services . searx . environmentFile = " / e t c / n i x o s / p r i v a t e / s e a r x . e n v " ;
services . searx . settings = {
use_default_settings = true ;
search = {
safe_search = 0 ; # Filter results. 0: None, 1: Moderate, 2: Strict
autocomplete = " d u c k d u c k g o " ; # Existing autocomplete backends: "dbpedia", "duckduckgo", "google", "startpage", "swisscows", "qwant", "wikipedia" - leave blank to turn it off by default
default_lang = " " ; # Default search language - leave blank to detect from browser information or use codes from 'languages.py'
} ;
server = {
port = 8888 ;
bind_address = " : : 1 " ;
secret_key = " @ S E A R X _ S E C R E T _ K E Y @ " ;
base_url = " h t t p s : / / s e a r c h . ${ cfg . domainName } / " ;
image_proxy = true ;
default_http_headers = {
X-Content-Type-Options = " n o s n i f f " ;
X-XSS-Protection = " 1 ; m o d e = b l o c k " ;
X-Download-Options = " n o o p e n " ;
X-Robots-Tag = " n o i n d e x , n o f o l l o w " ;
Referrer-Policy = " n o - r e f e r r e r " ;
} ;
} ;
outgoing = {
request_timeout = 5 .0 ; # default timeout in seconds, can be override by engine
max_request_timeout = 15 .0 ; # the maximum timeout in seconds
pool_connections = 100 ; # Maximum number of allowable connections, or null
pool_maxsize = 10 ; # Number of allowable keep-alive connections, or null
enable_http2 = true ; # See https://www.python-httpx.org/http2/
} ;
/* = {
name = " s o u n d c l o u d " ;
disabled = true ;
} ; * /
} ;
services . nginx . virtualHosts . " s e a r c h . ${ cfg . domainName } " = let inherit ( config . services . searx ) settings ; in {
enableACME = true ;
forceSSL = true ;
2023-05-11 17:28:47 +07:00
# locations."/".proxyPass = "http://${lib.quotePotentialIpV6 settings.server.bind_address}:${toString settings.server.port}";
2023-05-11 05:33:08 +07:00
locations . " / " . extraConfig = ''
2023-05-11 17:28:47 +07:00
uwsgi_pass " ${ lib . quotePotentialIpV6 settings . server . bind_address } : ${ toString settings . server . port } " ;
2023-05-11 05:33:08 +07:00
include $ { config . services . nginx . package } /conf/uwsgi_params ;
'' ;
} ;
# NGINX
services . nginx . enable = true ;
services . nginx . streamConfig =
let
cert = config . security . acme . certs . " ${ cfg . domainName } " . directory + " / f u l l c h a i n . p e m " ;
certKey = config . security . acme . certs . " ${ cfg . domainName } " . directory + " / k e y . p e m " ;
trustedCert = config . security . acme . certs . " ${ cfg . domainName } " . directory + " / c h a i n . p e m " ;
in ''
upstream dns {
zone dns 6 4 k ;
server 127 .0 .0 .1 : 53 ;
}
server {
listen 853 ssl ;
ssl_certificate $ { cert } ;
ssl_certificate_key $ { certKey } ;
ssl_trusted_certificate $ { trustedCert } ;
proxy_pass dns ;
}
'' ;
services . nginx . commonHttpConfig = " l o g _ f o r m a t p o s t d a t a ' { \" i p \" : \" $ r e m o t e _ a d d r \" , \" t i m e \" : \" $ t i m e _ i s o 8 6 0 1 \" , \" r e f e r e r \" : \" $ h t t p _ r e f e r e r \" , \" b o d y \" : \" $ r e q u e s t _ b o d y \" , \" u a \" : \" $ h t t p _ u s e r _ a g e n t \" } ' ; " ;
services . nginx . recommendedTlsSettings = true ;
services . nginx . recommendedOptimisation = true ;
services . nginx . recommendedGzipSettings = true ;
services . nginx . recommendedProxySettings = true ;
# BLOG
services . nginx . virtualHosts . " ${ cfg . domainName } " = {
enableACME = true ;
forceSSL = true ;
extraConfig = " a u t o i n d e x o n ; " ;
locations . " / " . root = " / v a r / w w w / ${ cfg . domainName } / " ;
locations . " / s r c " . root = " / v a r / w w w / ${ cfg . domainName } / " ;
locations . " / s r c " . extraConfig = " i n d e x f o r c e _ d i r l i s t i n g ; " ;
locations . " / s u b m i t _ c o m m e n t " . extraConfig = ''
access_log /var/log/nginx/comments.log postdata ;
proxy_pass https://$ { cfg . domainName } /submit.htm ;
break ;
'' ;
locations . " / s u b m i t . h t m " = {
extraConfig = ''
return 200 ' < ! doctype html > <html> <head> < base href = " / " / > < link rel = " p r e l o a d " href = " s t y l e . c s s " as = " s t y l e " > <title> Success ! < /title > < meta charset = " u t f - 8 " / > < meta name = " v i e w p o r t " content = " w i d t h = d e v i c e - w i d t h , i n i t i a l - s c a l e = 1 " / > < link rel = " i c o n " type = " i m a g e / j p e g " href = " p f p . j p g " > < link rel = " a l t e r n a t e " type = " a p p l i c a t i o n / r s s + x m l " title = " R S S " href = " h t t p s : / / ${ cfg . domainName } / b l o g / i n d e x . x m l " > < link href = " s t y l e . c s s " rel = " s t y l e s h e e t " / > < script src = " m a i n . j s " > < /script > < meta http-equiv = " r e f r e s h " content = " 1 0 ; u r l = $ h t t p _ r e f e r e r " / > < /head > < body onload = " d o c u m e n t L o a d e d ( ) " > < hr / > < div class = " m a i n - b o d y " > <p> Success ! It may take a while for your comment to get moderated . < /p > <p> Please wait for 10 seconds until you get redirected back . . . < /p > <p> Or just go there < a href = " $ h t t p _ r e f e r e r " > manually < /a > . < /p > < /div > < hr / > < /body > < /html > ' ;
'' ;
} ;
} ;
services . nginx . virtualHosts . " w w w . ${ cfg . domainName } " = {
enableACME = true ;
globalRedirect = cfg . domainName ;
} ;
# MAILSERVER
# roundcube
services . nginx . virtualHosts . " m a i l . ${ cfg . domainName } " = {
enableACME = true ;
} ;
services . roundcube = {
enable = true ;
package = pkgs . roundcube . withPlugins ( plugins : [ plugins . persistent_login ] ) ;
dicts = with pkgs . aspellDicts ; [ en ru ] ;
hostName = " m a i l . ${ cfg . domainName } " ;
maxAttachmentSize = 100 ;
plugins = [ " p e r s i s t e n t _ l o g i n " ] ;
} ;
mailserver = {
enable = true ;
fqdn = " m a i l . ${ cfg . domainName } " ;
domains = [ cfg . domainName ] ;
certificateScheme = 1 ;
certificateFile = config . security . acme . certs . " m a i l . ${ cfg . domainName } " . directory + " / f u l l c h a i n . p e m " ;
keyFile = config . security . acme . certs . " m a i l . ${ cfg . domainName } " . directory + " / k e y . p e m " ;
localDnsResolver = false ;
recipientDelimiter = " - " ;
lmtpSaveToDetailMailbox = " n o " ;
hierarchySeparator = " / " ;
} ;
# Only allow local connections to noreply account
mailserver . loginAccounts . " n o r e p l y @ ${ cfg . domainName } " = {
# password is set in private.nix
hashedPassword = cfg . hashedNoreplyPassword ;
sendOnly = true ;
} ;
services . dovecot2 . extraConfig =
let passwd = builtins . toFile " d o v e c o t 2 - l o c a l - p a s s w d " ''
noreply @ $ { cfg . domainName }: { plain } $ { cfg . unhashedNoreplyPassword }: : : : : : allow_nets = local , 127.0.0.0/8 , : : 1
'' ;
in ''
passdb {
driver = passwd-file
args = $ { passwd }
}
'' ;
# GITEA
services . nginx . virtualHosts . " g i t . ${ cfg . domainName } " = let inherit ( config . services . gitea ) settings ; in {
enableACME = true ;
forceSSL = true ;
2023-05-11 17:28:47 +07:00
locations . " / " . proxyPass = " h t t p : / / ${ lib . quotePotentialIpV6 settings . server . HTTP_ADDR } : ${ toString settings . server . HTTP_PORT } " ;
2023-05-11 05:33:08 +07:00
} ;
services . gitea = {
enable = true ;
database = {
createDatabase = false ;
passwordFile = " / v a r / l i b / g i t e a / d b _ p a s s w o r d " ;
type = " p o s t g r e s " ;
} ;
settings = {
mailer = {
ENABLED = true ;
FROM = " G i t e a < n o r e p l y @ ${ cfg . domainName } > " ;
MAILER_TYPE = " s m t p " ;
HOST = " m a i l . ${ cfg . domainName } : 5 8 7 " ;
USER = " n o r e p l y @ ${ cfg . domainName } " ;
PASSWD = cfg . unhashedNoreplyPassword ;
SKIP_VERIFY = true ;
} ;
session = {
COOKIE_SECURE = true ;
} ;
server = {
ROOT_URL = " h t t p s : / / g i t . ${ cfg . domainName } " ;
HTTP_ADDR = " : : 1 " ;
HTTP_PORT = 3310 ;
DOMAIN = " g i t . ${ cfg . domainName } " ;
# START_SSH_SERVER = true;
# SSH_PORT = 2222;
} ;
service = {
DISABLE_REGISTRATION = true ;
REGISTER_EMAIL_CONFIRM = true ;
} ;
} ;
} ;
# NEXTCLOUD
services . nginx . virtualHosts . " c l o u d . ${ cfg . domainName } " = {
enableACME = true ;
forceSSL = true ;
} ;
services . nextcloud = {
enable = true ;
enableBrokenCiphersForSSE = false ;
package = pkgs . nextcloud26 ;
autoUpdateApps . enable = true ;
# TODO: use socket auth and remove the next line
database . createLocally = false ;
config = {
adminpassFile = " / v a r / l i b / n e x t c l o u d / a d m i n _ p a s s w o r d " ;
dbpassFile = " / v a r / l i b / n e x t c l o u d / d b _ p a s s w o r d " ;
dbtype = " p g s q l " ;
dbhost = " / r u n / p o s t g r e s q l " ;
overwriteProtocol = " h t t p s " ;
} ;
hostName = " c l o u d . ${ cfg . domainName } " ;
https = true ;
} ;
2023-05-11 17:28:47 +07:00
services . pleroma = {
enable = true ;
secretConfigFile = " / v a r / l i b / p l e r o m a / s e c r e t s . e x s " ;
configs = [ ''
import Config
'' ] ;
} ;
2023-05-11 05:33:08 +07:00
systemd . services . pleroma . path = [ pkgs . exiftool pkgs . gawk ] ;
services . nginx . virtualHosts . " p l e r o m a . ${ cfg . domainName } " = {
enableACME = true ;
forceSSL = true ;
locations . " / " . proxyPass = " h t t p : / / 1 2 7 . 0 . 0 . 1 : 9 9 7 0 " ;
} ;
/* l o c a t i o n s . " / d n s - q u e r y " . e x t r a C o n f i g = ' '
grpc_pass grpc://127.0.0.1:53453 ;
'' ; * /
# TODO: firefox sync?
}