2024-01-23 16:24:26 +01:00
{
config ,
lib ,
pkgs ,
. . .
} :
2022-08-14 19:40:31 +03:00
let
2024-01-23 16:24:26 +01:00
inherit ( lib )
2025-01-09 15:42:19 +01:00
attrValues
concatLists
concatStringsSep
escapeShellArgs
filterAttrs
2024-01-23 16:24:26 +01:00
getExe
literalExpression
maintainers
2025-01-09 15:42:19 +01:00
makeBinPath
2024-01-23 16:24:26 +01:00
mapAttrs'
2025-01-09 15:42:19 +01:00
mapAttrsToList
mkAliasOptionModule
2024-01-23 16:24:26 +01:00
mkDefault
mkIf
mkMerge
mkOption
2025-01-09 15:42:19 +01:00
mkOptionDefault
2024-01-23 16:24:26 +01:00
mkPackageOption
nameValuePair
optional
2025-01-09 15:42:19 +01:00
optionalAttrs
optionalString
toShellVars
versionAtLeast
2024-01-23 16:24:26 +01:00
versionOlder
;
inherit ( lib . types )
attrsOf
2025-01-09 15:42:19 +01:00
bool
enum
nullOr
package
path
2024-01-23 16:24:26 +01:00
port
str
submodule
;
2025-01-09 15:42:19 +01:00
inherit ( config . boot ) kernelPackages ;
inherit ( config . boot . kernelPackages ) kernel ;
2024-01-23 16:24:26 +01:00
cfg = config . services . netbird ;
2025-01-09 15:42:19 +01:00
toClientList = fn : map fn ( attrValues cfg . clients ) ;
toClientAttrs = fn : mapAttrs' ( _ : fn ) cfg . clients ;
hardenedClients = filterAttrs ( _ : client : client . hardened ) cfg . clients ;
toHardenedClientList = fn : map fn ( attrValues hardenedClients ) ;
toHardenedClientAttrs = fn : mapAttrs' ( _ : fn ) hardenedClients ;
mkBinName =
client : name :
if client . bin . suffix = = " " || client . bin . suffix = = " n e t b i r d " then
name
else
" ${ name } - ${ client . bin . suffix } " ;
nixosConfig = config ;
2024-01-23 16:24:26 +01:00
in
{
2025-01-09 15:42:19 +01:00
meta . maintainers = with maintainers ; [
nazarewk
] ;
2024-01-23 16:24:26 +01:00
meta . doc = ./netbird.md ;
2022-08-14 19:40:31 +03:00
2025-01-09 15:42:19 +01:00
imports = [
( mkAliasOptionModule [ " s e r v i c e s " " n e t b i r d " " t u n n e l s " ] [ " s e r v i c e s " " n e t b i r d " " c l i e n t s " ] )
] ;
2022-08-14 19:40:31 +03:00
options . services . netbird = {
2025-01-09 15:42:19 +01:00
enable = mkOption {
type = bool ;
default = false ;
description = ''
Enables backwards compatible Netbird client service .
This is strictly equivalent to :
` ` ` nix
services . netbird . clients . default = {
port = 51820 ;
name = " n e t b i r d " ;
systemd . name = " n e t b i r d " ;
interface = " w t 0 " ;
hardened = false ;
} ;
` ` `
'' ;
} ;
2023-11-27 01:19:27 +01:00
package = mkPackageOption pkgs " n e t b i r d " { } ;
2022-08-14 19:40:31 +03:00
2025-01-09 15:42:19 +01:00
ui . enable = mkOption {
type = bool ;
default = config . services . displayManager . sessionPackages != [ ] || config . services . xserver . enable ;
defaultText = literalExpression ''
config . services . displayManager . sessionPackages != [ ] || config . services . xserver . enable
'' ;
description = ''
Controls presence ` netbird-ui ` wrappers , defaults to presence of graphical sessions .
'' ;
} ;
ui . package = mkPackageOption pkgs " n e t b i r d - u i " { } ;
clients = mkOption {
2024-01-23 16:24:26 +01:00
type = attrsOf (
submodule (
{ name , config , . . . }:
2025-01-09 15:42:19 +01:00
let
client = config ;
in
2024-01-23 16:24:26 +01:00
{
options = {
port = mkOption {
type = port ;
2025-01-09 15:42:19 +01:00
example = literalExpression " 5 1 8 2 0 " ;
description = ''
Port the Netbird client listens on .
'' ;
} ;
name = mkOption {
type = str ;
default = name ;
description = ''
Primary name for use ( as a suffix ) in :
- systemd service name ,
- hardened user name and group ,
- [ systemd ` * Directory = ` ] ( https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html #RuntimeDirectory=) names,
- desktop application identification ,
'' ;
} ;
dns-resolver . address = mkOption {
type = nullOr str ;
default = null ;
example = " 1 2 7 . 0 . 0 . 1 2 3 " ;
description = ''
An explicit address that Netbird will serve ` * . netbird . cloud . ` ( usually ) entries on .
Netbird serves DNS on it's own ( dynamic ) client address by default .
'' ;
} ;
dns-resolver . port = mkOption {
type = port ;
default = 53 ;
description = ''
A port to serve DNS entries on when ` dns-resolver . address ` is enabled .
'' ;
} ;
interface = mkOption {
type = str ;
default = " n b - ${ client . name } " ;
2024-01-23 16:24:26 +01:00
description = ''
2025-01-09 15:42:19 +01:00
Name of the network interface managed by this client .
2024-01-23 16:24:26 +01:00
'' ;
2025-01-09 15:42:19 +01:00
apply =
iface :
lib . throwIfNot (
builtins . stringLength iface <= 15
) " N e t w o r k i n t e r f a c e n a m e m u s t b e 1 5 c h a r a c t e r s o r l e s s " iface ;
2024-01-23 16:24:26 +01:00
} ;
2022-08-14 19:40:31 +03:00
2024-01-23 16:24:26 +01:00
environment = mkOption {
type = attrsOf str ;
defaultText = literalExpression ''
{
2025-01-09 15:42:19 +01:00
NB_STATE_DIR = client . dir . state ;
NB_CONFIG = " ' ' ${ client . dir . state } / c o n f i g . j s o n " ;
NB_DAEMON_ADDR = " u n i x : / / ' ' ${ client . dir . runtime } / s o c k " ;
NB_INTERFACE_NAME = client . interface ;
NB_LOG_FILE = mkOptionDefault " c o n s o l e " ;
NB_LOG_LEVEL = client . logLevel ;
NB_SERVICE = client . service . name ;
NB_WIREGUARD_PORT = toString client . port ;
} // optionalAttrs ( client . dns-resolver . address != null ) {
NB_DNS_RESOLVER_ADDRESS = " ' ' ${ client . dns-resolver . address } : ' ' ${ builtins . toString client . dns-resolver . port } " ;
2024-01-23 16:24:26 +01:00
}
'' ;
description = ''
Environment for the netbird service , used to pass configuration options .
'' ;
} ;
2022-08-14 19:40:31 +03:00
2025-01-09 15:42:19 +01:00
autoStart = mkOption {
type = bool ;
default = true ;
description = ''
Start the service with the system .
As of 2 0 2 4 - 0 2 -13 it is not possible to start a Netbird client daemon without immediately
connecting to the network , but it is [ planned for a near future ] ( https://github.com/netbirdio/netbird/projects/2 #card-91718018).
'' ;
} ;
openFirewall = mkOption {
type = bool ;
default = true ;
description = ''
Opens up firewall ` port ` for communication between Netbird peers directly over LAN or public IP ,
without using ( internet-hosted ) TURN servers as intermediaries .
'' ;
} ;
hardened = mkOption {
type = bool ;
default = true ;
description = ''
Hardened service :
- runs as a dedicated user with minimal set of permissions ( see caveats ) ,
- restricts daemon configuration socket access to dedicated user group
( you can grant access to it with ` users . users . " < u s e r > " . extraGroups = [ $ { client . user . group } ] ` ) ,
Even though the local system resources access is restricted :
- ` CAP_NET_RAW ` , ` CAP_NET_ADMIN ` and ` CAP_BPF ` still give unlimited network manipulation possibilites ,
- older kernels don't have ` CAP_BPF ` and use ` CAP_SYS_ADMIN ` instead ,
Known security features that are not ( yet ) integrated into the module :
- 2 0 2 4 - 0 2 -14 : ` rosenpass ` is an experimental feature configurable solely
through ` - - enable-rosenpass ` flag on the ` netbird up ` command ,
see [ the docs ] ( https://docs.netbird.io/how-to/enable-post-quantum-cryptography )
'' ;
} ;
logLevel = mkOption {
type = enum [
# logrus loglevels
" p a n i c "
" f a t a l "
" e r r o r "
" w a r n "
" w a r n i n g "
" i n f o "
" d e b u g "
" t r a c e "
] ;
default = " i n f o " ;
description = " L o g l e v e l o f t h e N e t b i r d d a e m o n . " ;
} ;
ui . enable = mkOption {
type = bool ;
default = nixosConfig . services . netbird . ui . enable ;
defaultText = literalExpression '' c l i e n t . u i . e n a b l e '' ;
description = ''
Controls presence of ` netbird-ui ` wrapper for this Netbird client .
'' ;
} ;
wrapper = mkOption {
type = package ;
internal = true ;
default =
let
makeWrapperArgs = concatLists (
mapAttrsToList ( key : value : [
" - - s e t - d e f a u l t "
key
value
] ) client . environment
) ;
mkBin = mkBinName client ;
in
pkgs . stdenv . mkDerivation {
name = " ${ cfg . package . name } - w r a p p e r - ${ client . name } " ;
meta . mainProgram = mkBin " n e t b i r d " ;
nativeBuildInputs = with pkgs ; [ makeWrapper ] ;
phases = [ " i n s t a l l P h a s e " ] ;
installPhase = concatStringsSep " \n " [
''
mkdir - p " $ o u t / b i n "
makeWrapper $ { lib . getExe cfg . package } " $ o u t / b i n / ${ mkBin " n e t b i r d " } " \
$ { escapeShellArgs makeWrapperArgs }
''
( optionalString cfg . ui . enable ''
# netbird-ui doesn't support envvars
makeWrapper $ { lib . getExe cfg . ui . package } " $ o u t / b i n / ${ mkBin " n e t b i r d - u i " } " \
- - add-flags ' - - daemon-addr = $ { client . environment . NB_DAEMON_ADDR } '
mkdir - p " $ o u t / s h a r e / a p p l i c a t i o n s "
substitute $ { cfg . ui . package } /share/applications/netbird.desktop \
" $ o u t / s h a r e / a p p l i c a t i o n s / ${ mkBin " n e t b i r d " } . d e s k t o p " \
- - replace-fail ' Name = Netbird' " N a m e = N e t b i r d @ ${ client . service . name } " \
- - replace-fail ' $ { lib . getExe cfg . ui . package } ' " $ o u t / b i n / ${ mkBin " n e t b i r d - u i " } "
'' )
] ;
} ;
} ;
# see https://github.com/netbirdio/netbird/blob/88747e3e0191abc64f1e8c7ecc65e5e50a1527fd/client/internal/config.go#L49-L82
config = mkOption {
type = ( pkgs . formats . json { } ) . type ;
defaultText = literalExpression ''
{
DisableAutoConnect = ! client . autoStart ;
WgIface = client . interface ;
WgPort = client . port ;
} // optionalAttrs ( client . dns-resolver . address != null ) {
CustomDNSAddress = " ' ' ${ client . dns-resolver . address } : ' ' ${ builtins . toString client . dns-resolver . port } " ;
}
'' ;
description = ''
Additional configuration that exists before the first start and
later overrides the existing values in ` config . json ` .
It is mostly helpful to manage configuration ignored/not yet implemented
outside of ` netbird up ` invocation .
WARNING : this is not an upstream feature , it could break in the future
( by having lower priority ) after upstream implements an equivalent .
It is implemented as a ` preStart ` script which overrides ` config . json `
with content of ` /etc / $ { client . dir . baseName } /config.d /* . j s o n ` f i l e s .
This option manages specifically ` 5 0 - nixos . json ` file .
Consult [ the source code ] ( https://github.com/netbirdio/netbird/blob/88747e3e0191abc64f1e8c7ecc65e5e50a1527fd/client/internal/config.go #L49-L82)
or inspect existing file for a complete list of available configurations .
'' ;
} ;
suffixedName = mkOption {
type = str ;
default = if client . name != " n e t b i r d " then " n e t b i r d - ${ client . name } " else client . name ;
description = ''
A systemd service name to use ( without ` . service ` suffix ) .
'' ;
} ;
dir . baseName = mkOption {
type = str ;
default = client . suffixedName ;
description = ''
A systemd service name to use ( without ` . service ` suffix ) .
'' ;
} ;
dir . state = mkOption {
type = path ;
default = " / v a r / l i b / ${ client . dir . baseName } " ;
description = ''
A state directory used by Netbird client to store ` config . json ` , ` state . json ` & ` resolv . conf ` .
'' ;
} ;
dir . runtime = mkOption {
type = path ;
default = " / v a r / r u n / ${ client . dir . baseName } " ;
description = ''
A runtime directory used by Netbird client .
'' ;
} ;
service . name = mkOption {
2024-01-23 16:24:26 +01:00
type = str ;
2025-01-09 15:42:19 +01:00
default = client . suffixedName ;
2024-01-23 16:24:26 +01:00
description = ''
2025-01-09 15:42:19 +01:00
A systemd service name to use ( without ` . service ` suffix ) .
'' ;
} ;
user . name = mkOption {
type = str ;
default = client . suffixedName ;
description = ''
A system user name for this client instance .
'' ;
} ;
user . group = mkOption {
type = str ;
default = client . suffixedName ;
description = ''
A system group name for this client instance .
'' ;
} ;
bin . suffix = mkOption {
type = str ;
default = if client . name != " n e t b i r d " then client . name else " " ;
description = ''
A system group name for this client instance .
2024-01-23 16:24:26 +01:00
'' ;
} ;
} ;
2022-08-14 19:40:31 +03:00
2025-01-09 15:42:19 +01:00
config . environment =
{
NB_STATE_DIR = client . dir . state ;
NB_CONFIG = " ${ client . dir . state } / c o n f i g . j s o n " ;
NB_DAEMON_ADDR = " u n i x : / / ${ client . dir . runtime } / s o c k " ;
NB_INTERFACE_NAME = client . interface ;
NB_LOG_FILE = mkOptionDefault " c o n s o l e " ;
NB_LOG_LEVEL = client . logLevel ;
NB_SERVICE = client . service . name ;
NB_WIREGUARD_PORT = toString client . port ;
}
// optionalAttrs ( client . dns-resolver . address != null ) {
NB_DNS_RESOLVER_ADDRESS = " ${ client . dns-resolver . address } : ${ builtins . toString client . dns-resolver . port } " ;
} ;
config . config =
{
DisableAutoConnect = ! client . autoStart ;
WgIface = client . interface ;
WgPort = client . port ;
}
// optionalAttrs ( client . dns-resolver . address != null ) {
CustomDNSAddress = " ${ client . dns-resolver . address } : ${ builtins . toString client . dns-resolver . port } " ;
} ;
2024-01-23 16:24:26 +01:00
}
)
) ;
default = { } ;
description = ''
2025-01-09 15:42:19 +01:00
Attribute set of Netbird client daemons , by default each one will :
1 . be manageable using dedicated tooling :
- ` netbird- <name> ` script ,
- ` Netbird - netbird- <name> ` graphical interface when appropriate ( see ` ui . enable ` ) ,
2 . run as a ` netbird- <name> . service ` ,
3 . listen for incoming remote connections on the port ` 51820 ` ( ` openFirewall ` by default ) ,
4 . manage the ` netbird- <name> ` wireguard interface ,
5 . use the ` /var/lib/netbird- <name> /config.json ` configuration file ,
6 . override ` /var/lib/netbird- <name> /config.json ` with values from ` /etc/netbird- <name> /config.d /* . j s o n ` ,
7 . ( ` hardened ` ) be locally manageable by ` netbird- <name> ` system group ,
With following caveats :
- multiple daemons will interfere with each other's DNS resolution of ` netbird . cloud ` , but
should remain fully operational otherwise .
Setting up custom ( non-conflicting ) DNS zone is currently possible only when self-hosting .
'' ;
example = lib . literalExpression ''
{
services . netbird . clients . wt0 . port = 51820 ;
services . netbird . clients . personal . port = 51821 ;
services . netbird . clients . work1 . port = 51822 ;
}
2024-01-23 16:24:26 +01:00
'' ;
2022-08-14 19:40:31 +03:00
} ;
} ;
2024-01-23 16:24:26 +01:00
config = mkMerge [
( mkIf cfg . enable {
2025-01-09 15:42:19 +01:00
services . netbird . clients . default = {
port = mkDefault 51820 ;
interface = mkDefault " w t 0 " ;
name = mkDefault " n e t b i r d " ;
hardened = mkDefault false ;
} ;
2024-01-23 16:24:26 +01:00
} )
2025-01-09 15:42:19 +01:00
{
boot . extraModulePackages = optional (
cfg . clients != { } && ( versionOlder kernel . version " 5 . 6 " )
) kernelPackages . wireguard ;
2024-01-23 16:24:26 +01:00
2025-01-09 15:42:19 +01:00
environment . systemPackages = toClientList ( client : client . wrapper )
# omitted due to https://github.com/netbirdio/netbird/issues/1562
#++ optional (cfg.clients != { }) cfg.package
# omitted due to https://github.com/netbirdio/netbird/issues/1581
#++ optional (cfg.clients != { } && cfg.ui.enable) cfg.ui.package
;
2024-01-23 16:24:26 +01:00
2025-01-09 15:42:19 +01:00
networking . dhcpcd . denyInterfaces = toClientList ( client : client . interface ) ;
networking . networkmanager . unmanaged = toClientList ( client : " i n t e r f a c e - n a m e : ${ client . interface } " ) ;
2024-01-23 16:24:26 +01:00
2025-01-09 15:42:19 +01:00
networking . firewall . allowedUDPPorts = concatLists (
toClientList ( client : optional client . openFirewall client . port )
) ;
2024-01-23 16:24:26 +01:00
systemd . network . networks = mkIf config . networking . useNetworkd (
2025-01-09 15:42:19 +01:00
toClientAttrs (
client :
nameValuePair " 5 0 - n e t b i r d - ${ client . interface } " {
2024-01-23 16:24:26 +01:00
matchConfig = {
2025-01-09 15:42:19 +01:00
Name = client . interface ;
2024-01-23 16:24:26 +01:00
} ;
linkConfig = {
Unmanaged = true ;
ActivationPolicy = " m a n u a l " ;
} ;
}
2025-01-09 15:42:19 +01:00
)
) ;
environment . etc = toClientAttrs (
client :
nameValuePair " ${ client . dir . baseName } / c o n f i g . d / 5 0 - n i x o s . j s o n " {
text = builtins . toJSON client . config ;
mode = " 0 4 4 4 " ;
}
2024-01-23 16:24:26 +01:00
) ;
2025-01-09 15:42:19 +01:00
systemd . services = toClientAttrs (
client :
nameValuePair client . service . name {
2024-01-23 16:24:26 +01:00
description = " A W i r e G u a r d - b a s e d m e s h n e t w o r k t h a t c o n n e c t s y o u r d e v i c e s i n t o a s i n g l e p r i v a t e n e t w o r k " ;
documentation = [ " h t t p s : / / n e t b i r d . i o / d o c s / " ] ;
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 " ] ;
2025-01-09 15:42:19 +01:00
path = optional ( ! config . services . resolved . enable ) pkgs . openresolv ;
2024-01-23 16:24:26 +01:00
serviceConfig = {
2025-01-09 15:42:19 +01:00
ExecStart = " ${ getExe client . wrapper } s e r v i c e r u n " ;
2024-01-23 16:24:26 +01:00
Restart = " a l w a y s " ;
2025-01-09 15:42:19 +01:00
RuntimeDirectory = client . dir . baseName ;
RuntimeDirectoryMode = mkDefault " 0 7 5 5 " ;
ConfigurationDirectory = client . dir . baseName ;
StateDirectory = client . dir . baseName ;
2024-01-23 16:24:26 +01:00
StateDirectoryMode = " 0 7 0 0 " ;
2025-01-09 15:42:19 +01:00
WorkingDirectory = client . dir . state ;
2024-01-23 16:24:26 +01:00
} ;
unitConfig = {
StartLimitInterval = 5 ;
StartLimitBurst = 10 ;
} ;
stopIfChanged = false ;
}
2025-01-09 15:42:19 +01:00
) ;
}
# Hardening section
( mkIf ( hardenedClients != { } ) {
users . groups = toHardenedClientAttrs ( client : nameValuePair client . user . group { } ) ;
users . users = toHardenedClientAttrs (
client :
nameValuePair client . user . name {
isSystemUser = true ;
home = client . dir . state ;
group = client . user . group ;
}
) ;
systemd . services = toHardenedClientAttrs (
client :
nameValuePair client . service . name (
mkIf client . hardened {
serviceConfig = {
RuntimeDirectoryMode = " 0 7 5 0 " ;
User = client . user . name ;
Group = client . user . group ;
2025-06-02 15:54:57 +02:00
# settings implied by DynamicUser=true, without actually using it,
2025-01-09 15:42:19 +01:00
# see https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#DynamicUser=
RemoveIPC = true ;
PrivateTmp = true ;
ProtectSystem = " s t r i c t " ;
ProtectHome = " y e s " ;
AmbientCapabilities =
[
# see https://man7.org/linux/man-pages/man7/capabilities.7.html
# see https://docs.netbird.io/how-to/installation#running-net-bird-in-docker
#
# seems to work fine without CAP_SYS_ADMIN and CAP_SYS_RESOURCE
# CAP_NET_BIND_SERVICE could be added to allow binding on low ports, but is not required,
# see https://github.com/netbirdio/netbird/pull/1513
# failed creating tunnel interface wt-priv: [operation not permitted
" C A P _ N E T _ A D M I N "
# failed to pull up wgInterface [wt-priv]: failed to create ipv4 raw socket: socket: operation not permitted
" C A P _ N E T _ R A W "
]
# required for eBPF filter, used to be subset of CAP_SYS_ADMIN
++ optional ( versionAtLeast kernel . version " 5 . 8 " ) " C A P _ B P F "
++ optional ( versionOlder kernel . version " 5 . 8 " ) " C A P _ S Y S _ A D M I N "
++ optional (
client . dns-resolver . address != null && client . dns-resolver . port < 1024
) " C A P _ N E T _ B I N D _ S E R V I C E " ;
} ;
}
)
) ;
# see https://github.com/systemd/systemd/blob/17f3e91e8107b2b29fe25755651b230bbc81a514/src/resolve/org.freedesktop.resolve1.policy#L43-L43
# see all actions used at https://github.com/netbirdio/netbird/blob/13e7198046a0d73a9cd91bf8e063fafb3d41885c/client/internal/dns/systemd_linux.go#L29-L32
security . polkit . extraConfig = mkIf config . services . resolved . enable ''
// systemd-resolved access for Netbird clients
polkit . addRule ( function ( action , subject ) {
var actions = [
" o r g . f r e e d e s k t o p . r e s o l v e 1 . r e v e r t " ,
" o r g . f r e e d e s k t o p . r e s o l v e 1 . s e t - d e f a u l t - r o u t e " ,
" o r g . f r e e d e s k t o p . r e s o l v e 1 . s e t - d n s - s e r v e r s " ,
" o r g . f r e e d e s k t o p . r e s o l v e 1 . s e t - d o m a i n s " ,
] ;
var users = $ { builtins . toJSON ( toHardenedClientList ( client : client . user . name ) ) } ;
if ( actions . indexOf ( action . id ) >= 0 && users . indexOf ( subject . user ) >= 0 ) {
return polkit . Result . YES ;
}
} ) ;
'' ;
2024-01-23 16:24:26 +01:00
} )
2025-01-09 15:42:19 +01:00
# migration & temporary fixups section
{
systemd . services = toClientAttrs (
client :
nameValuePair client . service . name {
preStart = ''
set - eEuo pipefail
$ { optionalString ( client . logLevel = = " t r a c e " || client . logLevel = = " d e b u g " ) " s e t - x " }
PATH = " ${
makeBinPath (
with pkgs ;
[
coreutils
jq
diffutils
]
)
} : $ PATH "
export $ { toShellVars client . environment }
# merge /etc/${client.dir.baseName}/config.d' into "$NB_CONFIG"
{
test - e " $ N B _ C O N F I G " || echo - n ' { } ' > " $ N B _ C O N F I G "
# merge config.d with "$NB_CONFIG" into "$NB_CONFIG.new"
jq - sS ' reduce . [ ] as $ i ( { } ; . * $ i ) ' \
" $ N B _ C O N F I G " \
/etc / $ { client . dir . baseName } /config.d /* . j s o n \
> " $ N B _ C O N F I G . n e w "
echo " C o m p a r i n g $ N B _ C O N F I G w i t h $ N B _ C O N F I G . n e w . . . "
if ! diff < ( jq - S < " $ N B _ C O N F I G " ) " $ N B _ C O N F I G . n e w " ; then
echo " U p d a t i n g $ N B _ C O N F I G . . . "
mv " $ N B _ C O N F I G . n e w " " $ N B _ C O N F I G "
else
echo " F i l e s a r e t h e s a m e , n o t d o i n g a n y t h i n g . "
rm " $ N B _ C O N F I G . n e w "
fi
}
'' ;
}
) ;
}
2024-01-23 16:24:26 +01:00
] ;
2022-08-14 19:40:31 +03:00
}