From 3be22b72497b8c314f37dbf5d953554328de3dbf Mon Sep 17 00:00:00 2001 From: stuebinm Date: Sat, 13 Mar 2021 16:49:22 +0100 Subject: [PATCH 1/5] init mattermost on hainich. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an initial test config for mattermost on NixOS; the intention is to perhaps deploy this as soon as it looks reasonable, then have it running as a "beta instance" in parallel with the current instance on libocedrus for a while to see if any issues come up before we can make a permament switch here. The mattermost module has a somewhat weird approach to database configuration (per default, it generates an entire postgres config, and if told not to do that, it generates a /disabled/ postgrs config ...), which I have for the most part worked around. Mattermost provides extensive configuration options, which are usually changed using its web UI. I have instead set the more important ones using Nix, and made the config immutable --- however, the config of our current instance is rather long and full of default values; it may well be that I missed some important settings. Open questions which we may want to answer before deploying this: - is there a reason why we use mysql for our current instance? At least during my tests, mattermost appears to work just fine with postgres - to access the noreply@infra4future.de mail address, mattermost needs a password, which --- as it looks right now --- must be set in the nix store. Can we work around that or should we fork / override the module? - plugins are apparently broken right now - locales are broken as well, for whatever reason — the german locale is definitively present, but setting it as the default will break and then reverted by mattermost on startup - for now, I have set `mutableConfig` to `false`, i.e. any changes done in the mattermost web UI will be overwritten on next startup. This is great for reproducability, but less so for ease of use (and perhaps for secrets as well) --- do we want to keep it this way? - as it is right now, using this instead of our current instance would represent a version DOWNGRADE (from 5.30.6. to 5.25.3); this may break the database schema. We may have to package a more recent version of mattermost and use that instead. Things I was unable to test locally (in a nixos container): - authentication using "gitlab" / keycloak - mail notifications (including coredns forwarding) - more advanced stuff like notifications, anything to do with "true" multi-user interaction --- hosts/hainich/configuration.nix | 1 + hosts/hainich/services/mattermost.nix | 190 ++++++++++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 hosts/hainich/services/mattermost.nix diff --git a/hosts/hainich/configuration.nix b/hosts/hainich/configuration.nix index 5cc94e0..3805211 100644 --- a/hosts/hainich/configuration.nix +++ b/hosts/hainich/configuration.nix @@ -19,6 +19,7 @@ ./services/syncthing.nix ./services/monitoring.nix ./services/workadventure.nix + ./services/mattermost.nix ]; boot.loader.grub.enable = true; boot.loader.grub.version = 2; diff --git a/hosts/hainich/services/mattermost.nix b/hosts/hainich/services/mattermost.nix new file mode 100644 index 0000000..76e4914 --- /dev/null +++ b/hosts/hainich/services/mattermost.nix @@ -0,0 +1,190 @@ +{config, pkgs, lib, ...}: + +{ + containers.mattermost = { + autoStart = true; + privateNetwork = true; + hostAddress6 = "fd00::42:24"; + localAddress6 = "fd00::42:25"; + + config = {pkgs, config, ...}: { + services.mattermost = { + enable = true; + siteUrl = "https://mattermost.infra4future.de"; + siteName = "Mattermost - Blabla for Future"; + listenAddress = "[::]:3000"; + mutableConfig = false; + + extraConfig = { + ServiceSettings = { + TrustedProxyIPHeader = [ "X-Forwarded-For" "X-Real-Ip" ]; + ReadTimeout = 300; + WriteTimeout = 600; + IdleTimeout = 60; + MaximumLoginAttempts = 10; + AllowCorsFrom = "*.infra4future.de/*"; + WebserverMode = "gzip"; + EnableCustomEmoji = true; + EnableEmojiPicker = true; + EnableGifPicker = false; + RestrictCustomEmojiCreation = "all"; + RestrictPostDelete = "all"; + AllowEditPost = "always"; + PostEditTimeout = -1; + EnableTutorial = false; + ExperimentalChannelSidebarOrganization = "default_on"; + ExperimentalChannelOrganization = true; + ExperimentalDataPrefetch = true; + EnableEmailInvitations = true; + DisableLegacyMFA = true; + EnableSVGs = true; + EnableLaTeX = true; + ThreadAutoFollow = true; + }; + TeamSettings = { + EnableTeamCreation = true; + EnableUserCreation = true; + EnableOpenServer = false; + EnableUserDeactivation = true; + ExperimentalViewArchivedChannels = true; + ExperimentalEnableAutomaticReplies = true; + }; + LogSettings = { + EnableConsole = true; + ConsoleLevel = "ERROR"; + }; + NotificationLogSettings = { + EnableConsole = true; + ConsoleLevel = "INFO"; + }; + PasswordSettings = { + MinimumLength = 10; + # turn of all the bullshit requirements + Lowercase = false; + Number = false; + Uppercase = false; + Symbol = false; + }; + FileSettings = { + EnableFileAttachments = true; + MaxFileSize = 52428800; + DriverName = "local"; + Directory = "/mnt/storage"; + EnablePublicLink = true; + PublicLinkSalt = "3k7p3yxdhz6798b3b9openfr9rn3ymwu"; + }; + EmailSettings = { + EnableSignUpWithEmail = false; + EnableSignInWithEmail = false; + EnableSignInWithUsername = false; + SendEmailNotifications = true; + FeedbackName = "mattermost"; + FeedbackEmail = "mattermost@infra4future.de"; + ReplyToAddress = "mattermost@infra4future.de"; + FeedbackOrganization = "∆infra4future.de"; + EnableSMTPAuth = true; + SMTPUsername = "noreply@infra4future.de"; + SMTPPassword = ""; # TODO: how to best read in these? + SMTPServer = "mail.hacc.space"; + }; + RateLimitSettings.Enable = false; + PrivacySettings = { + ShowEmailAddress = false; + ShowFullName = true; + }; + SupportSettings = { + TermsOfServiceLink = "https://infra4future.de/nutzungsbedingungen.html"; + PrivacyPolicyLink = "https://infra4future.de/nutzungsbedingungen.html"; + AboutLink = "https://infra4future.de"; + SupportEmail = "info@infra4future.de"; + CustomTermsOfServiceEnabled = false; + EnableAskCommunityLink = true; + }; + AnnouncementSettings.EnableBanner = false; + GitLabSettings = { + Enable = true; + Secret = ""; # TODO: how to do secrets? + Id = "mattermost"; + Scope = ""; + AuthEndpoint = "https://auth.infra4future.de/auth/realms/forfuture/protocol/openid-connect/auth"; + TokenEndpoint = "https://auth.infra4future.de/auth/realms/forfuture/protocol/openid-connect/token"; + UserApiEndpoint = "https://auth.infra4future.de/auth/realms/forfuture/protocol/openid-connect/userinfo"; + }; + # for some reason, these don't appear to be working; the startup + # process complaines and sets these back to en + LocalizationSettings = { + DefaultServerLocale = "de"; + DefaultClientLocale = "de"; + AvailableLocales = "de,en"; + }; + MessageExportSettings.EnableExport = false; + # plugins appear to have trouble with the read-only filesystem; it may + # be necessary to manually change their paths etc. + PluginSettings = { + Enable = true; + EnableUploads = true; + Plugins = { + bigbluebutton = { + adminonly = false; + base_url = "https://bbb.infra4future.de/bigbluebutton/api"; + salt = "zKCsNeaEniC115ynHOsZopgA4iTiJjzgeiPNoCEc"; + }; + "com.github.matterpoll.matterpoll" = { + experimentalui = true; + trigger = "poll"; + }; + }; + PluginStates = { + bigbluebutton.Enable = true; + "com.github.matterpoll.matterpoll".Enable = true; + }; + }; + ComplianceSettings.Enable = false; + ClusterSettings.Enable = false; + MetricsSettings.Enable = false; + GuestAccountsSettings.Enable = false; + }; + + # turn of the weirder parts of this module (which insist on passwords + # in nix files, instead of just using socket-based authentication) + # + # It will still attempt to use its default password, but postgres will + # just let it in regardless of that. + localDatabaseCreate = false; + }; + + services.postgresql = { + enable = lib.mkForce true; # mattermost sets this to false. wtf. + ensureDatabases = [ "mattermost" ]; + ensureUsers = [ { + name = "mattermost"; + ensurePermissions = { "DATABASE mattermost" = "ALL PRIVILEGES"; }; + } ]; + + authentication = lib.mkForce '' + # Generated file; do not edit! + local all all trust + host mattermost mattermost ::1/128 trust + ''; + }; + + networking.firewall.allowedTCPPorts = [ 3000 ]; + + services.coredns = { + enable = true; + config = '' + .:53 { + forward . 64:ff9b::1.1.1.1 + } + ''; + }; + }; + }; + + services.nginx.virtualHosts."mattermost.infra4future.de" = { + locations."/".proxyPass = "http://[${config.containers.mattermost.localAddress6}]:3000"; + forceSSL = true; + enableACME = true; + }; + +} From a3a4264b3811803a948fb2f26a6a9693046598ab Mon Sep 17 00:00:00 2001 From: stuebinm Date: Mon, 15 Mar 2021 17:04:27 +0100 Subject: [PATCH 2/5] Patch mattermost module to allow secrets outside the nix store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a custom mattermost module (`services.mattermost-patched`) which is identical to the one in nixpkgs except that it also has an option `secretConfig`, which should point to a file containing all secret parts of the mattermost config (e.g. mailserver password), and which is merged with the config genereated from the module at startup time. This allows us to have a (almost) immutable config without having secrets in the nix store. Before deploying this, add a secrets file at /var/lib/mattermost/screts.json (on the host — there is a bind mount in place so we won't have to enter the container each time to change something). --- hosts/hainich/services/mattermost.nix | 18 +- modules/mattermost.nix | 257 ++++++++++++++++++++++++++ 2 files changed, 272 insertions(+), 3 deletions(-) create mode 100644 modules/mattermost.nix diff --git a/hosts/hainich/services/mattermost.nix b/hosts/hainich/services/mattermost.nix index 76e4914..eb3fb88 100644 --- a/hosts/hainich/services/mattermost.nix +++ b/hosts/hainich/services/mattermost.nix @@ -7,14 +7,28 @@ hostAddress6 = "fd00::42:24"; localAddress6 = "fd00::42:25"; + bindMounts."/secrets" = { + hostPath = "/var/lib/mattermost/"; + isReadOnly = true; + }; + config = {pkgs, config, ...}: { - services.mattermost = { + + # have to import these here, since container's dont + # inherit imports of their environment. + imports = [ ../../../modules/mattermost.nix ]; + + # couldn't figure out how to actually overwrite modules, so now + # there's two mattermost modules ... + services.mattermost-patched = { enable = true; siteUrl = "https://mattermost.infra4future.de"; siteName = "Mattermost - Blabla for Future"; listenAddress = "[::]:3000"; mutableConfig = false; + secretConfig = "/secrets/secrets.json"; + extraConfig = { ServiceSettings = { TrustedProxyIPHeader = [ "X-Forwarded-For" "X-Real-Ip" ]; @@ -84,7 +98,6 @@ FeedbackOrganization = "∆infra4future.de"; EnableSMTPAuth = true; SMTPUsername = "noreply@infra4future.de"; - SMTPPassword = ""; # TODO: how to best read in these? SMTPServer = "mail.hacc.space"; }; RateLimitSettings.Enable = false; @@ -103,7 +116,6 @@ AnnouncementSettings.EnableBanner = false; GitLabSettings = { Enable = true; - Secret = ""; # TODO: how to do secrets? Id = "mattermost"; Scope = ""; AuthEndpoint = "https://auth.infra4future.de/auth/realms/forfuture/protocol/openid-connect/auth"; diff --git a/modules/mattermost.nix b/modules/mattermost.nix new file mode 100644 index 0000000..0d35f08 --- /dev/null +++ b/modules/mattermost.nix @@ -0,0 +1,257 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + + cfg = config.services.mattermost-patched; + + defaultConfig = builtins.fromJSON (builtins.replaceStrings [ "\\u0026" ] [ "&" ] + (readFile "${pkgs.mattermost}/config/config.json") + ); + + database = "postgres://${cfg.localDatabaseUser}:${cfg.localDatabasePassword}@localhost:5432/${cfg.localDatabaseName}?sslmode=disable&connect_timeout=10"; + + mattermostConf = foldl recursiveUpdate defaultConfig + [ { ServiceSettings.SiteURL = cfg.siteUrl; + ServiceSettings.ListenAddress = cfg.listenAddress; + TeamSettings.SiteName = cfg.siteName; + SqlSettings.DriverName = "postgres"; + SqlSettings.DataSource = database; + } + cfg.extraConfig + ]; + + mattermostConfJSON = pkgs.writeText "mattermost-config-raw.json" (builtins.toJSON mattermostConf); + +in + +{ + options = { + services.mattermost-patched = { + enable = mkEnableOption "Mattermost chat server"; + + statePath = mkOption { + type = types.str; + default = "/var/lib/mattermost"; + description = "Mattermost working directory"; + }; + + siteUrl = mkOption { + type = types.str; + example = "https://chat.example.com"; + description = '' + URL this Mattermost instance is reachable under, without trailing slash. + ''; + }; + + siteName = mkOption { + type = types.str; + default = "Mattermost"; + description = "Name of this Mattermost site."; + }; + + listenAddress = mkOption { + type = types.str; + default = ":8065"; + example = "[::1]:8065"; + description = '' + Address and port this Mattermost instance listens to. + ''; + }; + + mutableConfig = mkOption { + type = types.bool; + default = false; + description = '' + Whether the Mattermost config.json is writeable by Mattermost. + + Most of the settings can be edited in the system console of + Mattermost if this option is enabled. A template config using + the options specified in services.mattermost will be generated + but won't be overwritten on changes or rebuilds. + + If this option is disabled, changes in the system console won't + be possible (default). If an config.json is present, it will be + overwritten! + ''; + }; + + extraConfig = mkOption { + type = types.attrs; + default = { }; + description = '' + Addtional configuration options as Nix attribute set in config.json schema. + ''; + }; + + secretConfig = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Path to a json file containing secret config values, which should + not be written into the Nix store. If it is not null (the default) + and mutableConfig is set to false, then the mattermost service will + join the file at this path into its config. + + Note that this file cannot be used to overwrite values already + specified by the other options of this module. + ''; + }; + + localDatabaseCreate = mkOption { + type = types.bool; + default = true; + description = '' + Create a local PostgreSQL database for Mattermost automatically. + ''; + }; + + localDatabaseName = mkOption { + type = types.str; + default = "mattermost"; + description = '' + Local Mattermost database name. + ''; + }; + + localDatabaseUser = mkOption { + type = types.str; + default = "mattermost"; + description = '' + Local Mattermost database username. + ''; + }; + + localDatabasePassword = mkOption { + type = types.str; + default = "mmpgsecret"; + description = '' + Password for local Mattermost database user. + ''; + }; + + user = mkOption { + type = types.str; + default = "mattermost"; + description = '' + User which runs the Mattermost service. + ''; + }; + + group = mkOption { + type = types.str; + default = "mattermost"; + description = '' + Group which runs the Mattermost service. + ''; + }; + + matterircd = { + enable = mkEnableOption "Mattermost IRC bridge"; + parameters = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "-mmserver chat.example.com" "-bind [::]:6667" ]; + description = '' + Set commandline parameters to pass to matterircd. See + https://github.com/42wim/matterircd#usage for more information. + ''; + }; + }; + }; + }; + + config = mkMerge [ + (mkIf cfg.enable { + users.users = optionalAttrs (cfg.user == "mattermost") { + mattermost = { + group = cfg.group; + uid = config.ids.uids.mattermost; + home = cfg.statePath; + }; + }; + + users.groups = optionalAttrs (cfg.group == "mattermost") { + mattermost.gid = config.ids.gids.mattermost; + }; + + services.postgresql.enable = cfg.localDatabaseCreate; + + # The systemd service will fail to execute the preStart hook + # if the WorkingDirectory does not exist + system.activationScripts.mattermost = '' + mkdir -p ${cfg.statePath} + ''; + + systemd.services.mattermost = { + description = "Mattermost chat service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" "postgresql.service" ]; + + preStart = '' + mkdir -p ${cfg.statePath}/{data,config,logs} + ln -sf ${pkgs.mattermost}/{bin,fonts,i18n,templates,client} ${cfg.statePath} + '' + lib.optionalString (!cfg.mutableConfig) '' + rm -f ${cfg.statePath}/config/config.json + '' + (if cfg.secretConfig == null + then '' + cp ${mattermostConfJSON} ${cfg.statePath}/config/config.json + '' + else '' + ${pkgs.jq}/bin/jq -s ".[1] * .[0]" ${cfg.secretConfig} ${mattermostConfJSON} > ${cfg.statePath}/config/config.json + '') + + '' + ${pkgs.mattermost}/bin/mattermost config migrate ${cfg.statePath}/config/config.json ${database} + '' + lib.optionalString cfg.mutableConfig '' + if ! test -e "${cfg.statePath}/config/.initial-created"; then + rm -f ${cfg.statePath}/config/config.json + cp ${mattermostConfJSON} ${cfg.statePath}/config/config.json + touch ${cfg.statePath}/config/.initial-created + fi + '' + lib.optionalString cfg.localDatabaseCreate '' + if ! test -e "${cfg.statePath}/.db-created"; then + ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \ + ${config.services.postgresql.package}/bin/psql postgres -c \ + "CREATE ROLE ${cfg.localDatabaseUser} WITH LOGIN NOCREATEDB NOCREATEROLE ENCRYPTED PASSWORD '${cfg.localDatabasePassword}'" + ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} \ + ${config.services.postgresql.package}/bin/createdb \ + --owner ${cfg.localDatabaseUser} ${cfg.localDatabaseName} + touch ${cfg.statePath}/.db-created + fi + '' + '' + chown ${cfg.user}:${cfg.group} -R ${cfg.statePath} + chmod u+rw,g+r,o-rwx -R ${cfg.statePath} + ''; + + serviceConfig = { + PermissionsStartOnly = true; + User = cfg.user; + Group = cfg.group; + ExecStart = "${pkgs.mattermost}/bin/mattermost" + + (lib.optionalString (!cfg.mutableConfig) " -c ${database}"); + WorkingDirectory = "${cfg.statePath}"; + Restart = "always"; + RestartSec = "10"; + LimitNOFILE = "49152"; + }; + unitConfig.JoinsNamespaceOf = mkIf cfg.localDatabaseCreate "postgresql.service"; + }; + }) + (mkIf cfg.matterircd.enable { + systemd.services.matterircd = { + description = "Mattermost IRC bridge service"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = "nobody"; + Group = "nogroup"; + ExecStart = "${pkgs.matterircd}/bin/matterircd ${concatStringsSep " " cfg.matterircd.parameters}"; + WorkingDirectory = "/tmp"; + PrivateTmp = true; + Restart = "always"; + RestartSec = "5"; + }; + }; + }) + ]; +} From f24da2510df6dec99032267173b74cbf5237b806 Mon Sep 17 00:00:00 2001 From: stuebinm Date: Mon, 15 Mar 2021 17:11:38 +0100 Subject: [PATCH 3/5] mattermost: change url to mattermost-beta.infra4future.de I propose we deploy this sometime and see how it goes (as soon as someone sets up DNS for this). --- hosts/hainich/services/mattermost.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hosts/hainich/services/mattermost.nix b/hosts/hainich/services/mattermost.nix index eb3fb88..19b172e 100644 --- a/hosts/hainich/services/mattermost.nix +++ b/hosts/hainich/services/mattermost.nix @@ -22,7 +22,7 @@ # there's two mattermost modules ... services.mattermost-patched = { enable = true; - siteUrl = "https://mattermost.infra4future.de"; + siteUrl = "https://mattermost-beta.infra4future.de"; siteName = "Mattermost - Blabla for Future"; listenAddress = "[::]:3000"; mutableConfig = false; @@ -193,7 +193,7 @@ }; }; - services.nginx.virtualHosts."mattermost.infra4future.de" = { + services.nginx.virtualHosts."mattermost-beta.infra4future.de" = { locations."/".proxyPass = "http://[${config.containers.mattermost.localAddress6}]:3000"; forceSSL = true; enableACME = true; From b07e4d08fa608fa653adee02e00abe12318a0d68 Mon Sep 17 00:00:00 2001 From: stuebinm Date: Tue, 16 Mar 2021 23:59:32 +0100 Subject: [PATCH 4/5] Mattermost: Enable networking for the container. Otherwise, the login via keycloak will fail, as mattermost cannot verify the associated token. Since networking.nat only appears to work for ipv4 and I have no idea how to do it with ipv6, this unfortunately implies downgrading the container's networking to ipv4 only ... --- hosts/hainich/services/mattermost.nix | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/hosts/hainich/services/mattermost.nix b/hosts/hainich/services/mattermost.nix index 19b172e..e06e1f3 100644 --- a/hosts/hainich/services/mattermost.nix +++ b/hosts/hainich/services/mattermost.nix @@ -4,8 +4,8 @@ containers.mattermost = { autoStart = true; privateNetwork = true; - hostAddress6 = "fd00::42:24"; - localAddress6 = "fd00::42:25"; + hostAddress = "192.168.100.30"; + localAddress = "192.168.100.31"; bindMounts."/secrets" = { hostPath = "/var/lib/mattermost/"; @@ -24,7 +24,7 @@ enable = true; siteUrl = "https://mattermost-beta.infra4future.de"; siteName = "Mattermost - Blabla for Future"; - listenAddress = "[::]:3000"; + listenAddress = "0.0.0.0:3000"; mutableConfig = false; secretConfig = "/secrets/secrets.json"; @@ -83,7 +83,7 @@ EnableFileAttachments = true; MaxFileSize = 52428800; DriverName = "local"; - Directory = "/mnt/storage"; + Directory = "/var/lib/mattermost/uploads-storage"; EnablePublicLink = true; PublicLinkSalt = "3k7p3yxdhz6798b3b9openfr9rn3ymwu"; }; @@ -116,7 +116,7 @@ AnnouncementSettings.EnableBanner = false; GitLabSettings = { Enable = true; - Id = "mattermost"; + Id = "mattermost-beta"; Scope = ""; AuthEndpoint = "https://auth.infra4future.de/auth/realms/forfuture/protocol/openid-connect/auth"; TokenEndpoint = "https://auth.infra4future.de/auth/realms/forfuture/protocol/openid-connect/token"; @@ -186,7 +186,7 @@ enable = true; config = '' .:53 { - forward . 64:ff9b::1.1.1.1 + forward . 1.1.1.1 } ''; }; @@ -194,9 +194,18 @@ }; services.nginx.virtualHosts."mattermost-beta.infra4future.de" = { - locations."/".proxyPass = "http://[${config.containers.mattermost.localAddress6}]:3000"; + locations."/" = { + proxyPass = "http://${config.containers.mattermost.localAddress}:3000"; + proxyWebsockets = true; + }; forceSSL = true; enableACME = true; }; + networking.nat = { + enable = true; + internalInterfaces = [ "ve-mattermost" ]; + externalInterface = "enp6s0"; + }; + } From c6df2c2a9df04bb7e9a8fbd1df803945a0e9efeb Mon Sep 17 00:00:00 2001 From: schweby Date: Wed, 17 Mar 2021 23:21:23 +0100 Subject: [PATCH 5/5] hainich/mattermost: disable external diagnostics --- hosts/hainich/services/mattermost.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hosts/hainich/services/mattermost.nix b/hosts/hainich/services/mattermost.nix index e06e1f3..51ccbec 100644 --- a/hosts/hainich/services/mattermost.nix +++ b/hosts/hainich/services/mattermost.nix @@ -66,6 +66,8 @@ LogSettings = { EnableConsole = true; ConsoleLevel = "ERROR"; + EnableDiagnostics = true; + EnableWebhookDebugging = true; }; NotificationLogSettings = { EnableConsole = true;