Compare commits

..

1 commit

Author SHA1 Message Date
af1cf7606d
feat(web02): Deploy mattermost on chat.dgnum.eu
All checks were successful
Check meta / check_dns (push) Successful in 15s
Check meta / check_meta (push) Successful in 16s
Check meta / check_dns (pull_request) Successful in 15s
Check meta / check_meta (pull_request) Successful in 15s
Check workflows / check_workflows (pull_request) Successful in 18s
Build all the nodes / ap01 (pull_request) Successful in 39s
Build all the nodes / bridge01 (pull_request) Successful in 45s
Build all the nodes / cof02 (pull_request) Successful in 51s
Build all the nodes / geo01 (pull_request) Successful in 51s
Build all the nodes / build01 (pull_request) Successful in 59s
Build all the nodes / geo02 (pull_request) Successful in 51s
Build all the nodes / netaccess01 (pull_request) Successful in 22s
Build all the nodes / hypervisor01 (pull_request) Successful in 49s
Build all the nodes / hypervisor02 (pull_request) Successful in 48s
Build all the nodes / hypervisor03 (pull_request) Successful in 45s
Build all the nodes / netcore00 (pull_request) Successful in 23s
Build all the nodes / netcore01 (pull_request) Successful in 22s
Build all the nodes / netcore02 (pull_request) Successful in 23s
Build all the nodes / compute01 (pull_request) Successful in 1m18s
Build all the nodes / lab-router01 (pull_request) Successful in 52s
Build all the nodes / iso (pull_request) Successful in 1m0s
Build the shell / build-shell (pull_request) Successful in 22s
Build all the nodes / tower01 (pull_request) Successful in 46s
Build all the nodes / vault01 (pull_request) Successful in 1m0s
Build all the nodes / krz01 (pull_request) Successful in 1m39s
Build all the nodes / rescue01 (pull_request) Successful in 1m11s
Build all the nodes / storage01 (pull_request) Successful in 1m15s
Build all the nodes / web03 (pull_request) Successful in 59s
Build all the nodes / web01 (pull_request) Successful in 1m11s
Build all the nodes / web02 (pull_request) Successful in 3m59s
2025-05-25 16:51:52 +02:00
62 changed files with 1790 additions and 1910 deletions

View file

@ -1,44 +1,6 @@
###
# This file was automatically generated with nix-actions.
jobs:
Jaccess01:
runs-on: nix-infra
steps:
- uses: actions/checkout@v3
- env:
BUILD_NODE: Jaccess01
name: Eval Jaccess01
run: "nix-shell -A eval-nodes --run 'set -o pipefail\nset -o nounset\nset -o
errexit\nDRV=$(instantiate-node)\necho \"DRV=$DRV\" >> $GITHUB_ENV\n'"
- name: Build Jaccess01
run: "STORE_PATH=\"$(nix-store --realise \"$DRV\")\"\necho \"STORE_PATH=$STORE_PATH\"\
\ >> $GITHUB_ENV\n"
- env:
STORE_ENDPOINT: https://snix-store.dgnum.eu/infra.signing/
STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
STORE_USER: admin
name: Cache Jaccess01
run: "nix-shell -A eval-nodes --run 'set -o pipefail\nset -o nounset\nset -o
errexit\npush-to-cache \"$STORE_PATH\"\n'"
Jaccess04:
runs-on: nix-infra
steps:
- uses: actions/checkout@v3
- env:
BUILD_NODE: Jaccess04
name: Eval Jaccess04
run: "nix-shell -A eval-nodes --run 'set -o pipefail\nset -o nounset\nset -o
errexit\nDRV=$(instantiate-node)\necho \"DRV=$DRV\" >> $GITHUB_ENV\n'"
- name: Build Jaccess04
run: "STORE_PATH=\"$(nix-store --realise \"$DRV\")\"\necho \"STORE_PATH=$STORE_PATH\"\
\ >> $GITHUB_ENV\n"
- env:
STORE_ENDPOINT: https://snix-store.dgnum.eu/infra.signing/
STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
STORE_USER: admin
name: Cache Jaccess04
run: "nix-shell -A eval-nodes --run 'set -o pipefail\nset -o nounset\nset -o
errexit\npush-to-cache \"$STORE_PATH\"\n'"
ap01:
runs-on: nix-infra
steps:
@ -286,6 +248,44 @@ jobs:
name: Cache lab-router01
run: "nix-shell -A eval-nodes --run 'set -o pipefail\nset -o nounset\nset -o
errexit\npush-to-cache \"$STORE_PATH\"\n'"
netaccess01:
runs-on: nix-infra
steps:
- uses: actions/checkout@v3
- env:
BUILD_NODE: netaccess01
name: Eval netaccess01
run: "nix-shell -A eval-nodes --run 'set -o pipefail\nset -o nounset\nset -o
errexit\nDRV=$(instantiate-node)\necho \"DRV=$DRV\" >> $GITHUB_ENV\n'"
- name: Build netaccess01
run: "STORE_PATH=\"$(nix-store --realise \"$DRV\")\"\necho \"STORE_PATH=$STORE_PATH\"\
\ >> $GITHUB_ENV\n"
- env:
STORE_ENDPOINT: https://snix-store.dgnum.eu/infra.signing/
STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
STORE_USER: admin
name: Cache netaccess01
run: "nix-shell -A eval-nodes --run 'set -o pipefail\nset -o nounset\nset -o
errexit\npush-to-cache \"$STORE_PATH\"\n'"
netcore00:
runs-on: nix-infra
steps:
- uses: actions/checkout@v3
- env:
BUILD_NODE: netcore00
name: Eval netcore00
run: "nix-shell -A eval-nodes --run 'set -o pipefail\nset -o nounset\nset -o
errexit\nDRV=$(instantiate-node)\necho \"DRV=$DRV\" >> $GITHUB_ENV\n'"
- name: Build netcore00
run: "STORE_PATH=\"$(nix-store --realise \"$DRV\")\"\necho \"STORE_PATH=$STORE_PATH\"\
\ >> $GITHUB_ENV\n"
- env:
STORE_ENDPOINT: https://snix-store.dgnum.eu/infra.signing/
STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
STORE_USER: admin
name: Cache netcore00
run: "nix-shell -A eval-nodes --run 'set -o pipefail\nset -o nounset\nset -o
errexit\npush-to-cache \"$STORE_PATH\"\n'"
netcore01:
runs-on: nix-infra
steps:
@ -457,25 +457,6 @@ jobs:
name: Cache web03
run: "nix-shell -A eval-nodes --run 'set -o pipefail\nset -o nounset\nset -o
errexit\npush-to-cache \"$STORE_PATH\"\n'"
zulip01:
runs-on: nix-infra
steps:
- uses: actions/checkout@v3
- env:
BUILD_NODE: zulip01
name: Eval zulip01
run: "nix-shell -A eval-nodes --run 'set -o pipefail\nset -o nounset\nset -o
errexit\nDRV=$(instantiate-node)\necho \"DRV=$DRV\" >> $GITHUB_ENV\n'"
- name: Build zulip01
run: "STORE_PATH=\"$(nix-store --realise \"$DRV\")\"\necho \"STORE_PATH=$STORE_PATH\"\
\ >> $GITHUB_ENV\n"
- env:
STORE_ENDPOINT: https://snix-store.dgnum.eu/infra.signing/
STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
STORE_USER: admin
name: Cache zulip01
run: "nix-shell -A eval-nodes --run 'set -o pipefail\nset -o nounset\nset -o
errexit\npush-to-cache \"$STORE_PATH\"\n'"
name: Build all the nodes
on:
pull_request:

2
REUSE.toml generated
View file

@ -2,7 +2,7 @@ version = 1
[[annotations]]
SPDX-FileCopyrightText = "NONE"
SPDX-License-Identifier = "CC0-1.0"
path = ["**/.envrc", "**/Cargo.lock", "**/_hardware-configuration.nix", ".gitignore", "REUSE.toml", "shell.nix", "patches/colmena/0001-*", "pkgs/by-name/docuseal/rubyEnv/*", "pkgs/by-name/docuseal/deps.json", "pkgs/by-name/docuseal/yarn.lock"]
path = ["**/.envrc", "**/Cargo.lock", "**/_hardware-configuration.nix", ".gitignore", "REUSE.toml", "shell.nix", "pkgs/by-name/docuseal/rubyEnv/*", "pkgs/by-name/docuseal/deps.json", "pkgs/by-name/docuseal/yarn.lock"]
precedence = "closest"
[[annotations]]

View file

@ -75,9 +75,6 @@ let
"REUSE.toml"
"shell.nix"
# Commit revert
"patches/colmena/0001-*"
# Docuseal
"pkgs/by-name/docuseal/rubyEnv/*"
"pkgs/by-name/docuseal/deps.json"

View file

@ -33,9 +33,6 @@ rec {
# List of keys for the root group
rootKeys = getMemberKeys meta.organization.groups.root;
# List of keys for the root group (for age encryption and decryption)
rootAgeKeys = getAgeKeys meta.organization.groups.root;
# All admins for a node
getNodeAdmins = node: meta.organization.groups.root ++ meta.nodes.${node}.admins;
@ -43,15 +40,15 @@ rec {
getSecretKeys = node: lib.unique (getAgeKeys (getNodeAdmins node) ++ getNodeKeys [ node ]);
# List of keys for all machines wide secrets
machineKeys = rootAgeKeys ++ (getNodeKeys (builtins.attrNames meta.nodes));
machineKeys = rootKeys ++ (getNodeKeys (builtins.attrNames meta.nodes));
mkSecrets = nodes: setDefault { publicKeys = lib.unique (builtins.concatMap getSecretKeys nodes); };
mkRootSecrets = setDefault { publicKeys = lib.unique rootAgeKeys; };
mkRootSecrets = setDefault { publicKeys = lib.unique rootKeys; };
machineKeysBySystem =
system:
rootAgeKeys
rootKeys
++ (getNodeKeys (
builtins.attrNames (lib.filterAttrs (_: v: v.nixpkgs.system == system) meta.nodes)
));

View file

@ -1,141 +0,0 @@
# SPDX-FileCopyrightText: 2025 Lubin Bailly <lubin.bailly@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
{ config, lib, ... }:
let
inherit (lib)
concatImapStringsSep
concatMapAttrsStringSep
concatMapStrings
mkOption
;
inherit (lib.types)
attrsOf
ints
listOf
str
submodule
;
in
{
options = {
access.address-assignment.pool = mkOption {
type = attrsOf (
submodule (
{ name, config, ... }:
{
options = {
family.inet = {
network = mkOption {
type = str;
description = ''
Network where this pool is located.
'';
};
ranges = mkOption {
type = listOf (submodule {
options = {
low = mkOption {
type = str;
description = ''
Lowest IP of this range.
'';
};
high = mkOption {
type = str;
description = ''
Highest IP of this range.
'';
};
};
});
description = ''
IP ranges in this pool.
'';
};
dhcp-attributes = {
maximum-lease-time = mkOption {
type = ints.unsigned;
description = ''
Maximum lease time for leases in this pool.
'';
};
name-server = mkOption {
type = listOf str;
default = [ ];
description = ''
DNS servers to propose.
'';
};
router = mkOption {
type = listOf str;
default = [ ];
description = ''
Router IP for default route.
'';
};
};
};
xml = mkOption {
type = str;
readOnly = true;
visible = false;
};
};
config.xml =
let
inet-cfg = config.family.inet;
in
''
<pool>
<name>${name}</name>
<family>
<inet>
<network>${inet-cfg.network}</network>
${concatImapStringsSep "\n" (
idx:
{ low, high }:
''
<range>
<name>${name}-${toString idx}</name>
<low>${low}</low>
<high>${high}</high>
</range>
''
) inet-cfg.ranges}
<dhcp-attributes>
<maximum-lease-time>${toString inet-cfg.dhcp-attributes.maximum-lease-time}</maximum-lease-time>
${concatMapStrings (
dns: "<name-server><name>${dns}</name></name-server>"
) inet-cfg.dhcp-attributes.name-server}
${concatMapStrings (
router: "<router><name>${router}</name></router>"
) inet-cfg.dhcp-attributes.router}
</dhcp-attributes>
</inet>
</family>
</pool>
'';
}
)
);
default = { };
description = ''
Address pools for DHCP configuration.
'';
};
netconf.xmls.access = mkOption {
type = str;
visible = false;
readOnly = true;
};
};
config.netconf.xmls.access = ''
<access operation="replace">
<address-assignment>
${concatMapAttrsStringSep "\n" (_: pool: pool.xml) config.access.address-assignment.pool}
</address-assignment>
</access>
'';
}

View file

@ -34,14 +34,11 @@ let
in
{
imports = [
./access.nix
./interfaces.nix
./poe.nix
./protocols.nix
./system.nix
./vlans.nix
./routing-options.nix
./snmp.nix
];
options = {
@ -101,9 +98,6 @@ in
${protocols}
${vlans}
${poe}
${access}
${routing-options}
${snmp}
</configuration>
'';
rpc = pkgs.writeText "${name}.rpc" ''

View file

@ -25,7 +25,6 @@ let
interface =
{ name, config, ... }:
let
intf_cfg = config;
unit =
{ name, config, ... }:
{
@ -34,13 +33,6 @@ let
default = true;
example = false;
};
description = mkOption {
type = str;
default = intf_cfg.description + "." + name;
description = ''
Descriptive name of this interface unit.
'';
};
family = {
ethernet-switching = {
enable = mkEnableOption "the ethernet switching on this logical interface";
@ -105,17 +97,17 @@ let
</ethernet-switching>
'';
addr4 = map (addr: "<address><name>${addr}</name></address>") config.family.inet.addresses;
addr4 = map (addr: "<name>${addr}</name>") config.family.inet.addresses;
inet = optionalString config.family.inet.enable ''
<inet>
${builtins.concatStringsSep "" addr4}
<address>${builtins.concatStringsSep "" addr4}</address>
</inet>
'';
addr6 = map (addr: "<address><name>${addr}</name></address>") config.family.inet6.addresses;
addr6 = map (addr: "<name>${addr}</name>") config.family.inet6.addresses;
inet6 = optionalString config.family.inet6.enable ''
<inet6>
${builtins.concatStringsSep "" addr6}
<address>${builtins.concatStringsSep "" addr6}</address>
</inet6>
'';
in
@ -123,7 +115,6 @@ let
<unit>
<name>${name}</name>
${optionalString (!config.enable) "<disable/>"}
${optionalString config.enable "<description>${config.description}</description>"}
<family>
${eth}${inet}${inet6}
</family>
@ -140,13 +131,6 @@ let
Configuration of the logical interfaces on this physical interface.
'';
};
description = mkOption {
type = str;
default = name;
description = ''
Descriptive name of this interface.
'';
};
xml = mkOption {
type = str;
visible = false;
@ -160,7 +144,6 @@ let
''
<interface>
<name>${name}</name>
${optionalString config.enable "<description>${config.description}</description>"}
${optionalString (!config.enable) "<disable/>"}
${builtins.concatStringsSep "" units}
</interface>

View file

@ -1,59 +0,0 @@
# SPDX-FileCopyrightText: 2025 Lubin Bailly <lubin.bailly@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
{ config, lib, ... }:
let
inherit (lib)
concatMapStringsSep
mkOption
;
inherit (lib.types)
str
listOf
submodule
;
in
{
options = {
routing-options.static.route = mkOption {
type = listOf (submodule {
options = {
destination = mkOption {
type = str;
description = ''
Destination network.
'';
};
next-hop = mkOption {
type = str;
description = ''
Gateway for this network.
'';
};
};
});
default = [ ];
description = ''
Static routes.
'';
};
netconf.xmls.routing-options = mkOption {
type = str;
readOnly = true;
visible = false;
};
};
config.netconf.xmls.routing-options = ''
<routing-options operation="replace">
<static>
${concatMapStringsSep "\n" (route: ''
<route>
<name>${route.destination}</name>
<next-hop>${route.next-hop}</next-hop>
</route>
'') config.routing-options.static.route}
</static>
</routing-options>
'';
}

View file

@ -1,80 +0,0 @@
# SPDX-FileCopyrightText: 2025 Lubin Bailly <lubin.bailly@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
{ lib, config, ... }:
let
inherit (lib)
concatMapAttrsStringSep
mkOption
optionalString
;
inherit (lib.types)
attrsOf
bool
enum
str
submodule
;
in
{
options = {
snmp = {
filter-interfaces.all-internal-interfaces = mkOption {
type = bool;
default = false;
description = ''
Whether to filter internal interfaces.
'';
};
community = mkOption {
type = attrsOf (
submodule (
{ name, config, ... }:
{
options = {
authorization = mkOption {
type = enum [
"read-only"
"read-write"
];
description = ''
Authorization type.
'';
};
xml = mkOption {
type = str;
visible = false;
readOnly = true;
};
};
config.xml = ''
<community>
<name>${name}</name>
<authorization>${config.authorization}</authorization>
</community>
'';
}
)
);
default = { };
description = ''
Communities for SNMPv2 access.
'';
};
};
netconf.xmls.snmp = mkOption {
type = str;
visible = false;
readOnly = true;
};
};
config.netconf.xmls.snmp = ''
<snmp operation="replace">
<filter-interfaces>
${optionalString config.snmp.filter-interfaces.all-internal-interfaces "<all-internal-interfaces/>"}
</filter-interfaces>
${concatMapAttrsStringSep "" (_: comm: comm.xml) config.snmp.community}
</snmp>
'';
}

View file

@ -6,25 +6,20 @@
let
inherit (lib)
concatMapAttrsStringSep
concatMapStrings
concatStrings
concatStringsSep
filter
hasPrefix
length
mkOption
optionalString
splitString
;
inherit (lib.types)
attrsOf
enum
listOf
port
str
submodule
;
in
@ -60,20 +55,6 @@ in
description = "Port to use for netconf.";
default = 830;
};
dhcp-local-server.group = mkOption {
type = attrsOf (submodule {
options.interfaces = mkOption {
type = listOf str;
description = ''
Interfaces managed by this group.
'';
};
});
default = { };
description = ''
Groups of configuration for DHCP server.
'';
};
};
};
netconf.xmls.system = mkOption {
@ -94,19 +75,6 @@ in
ed25519 = map (key: "<ssh-ed25519><name>${key}</name></ssh-ed25519>") (
filter (hasPrefix "ssh-ed25519 ") ssh-keys
);
dhcp-local = optionalString (config.system.services.dhcp-local-server.group != { }) ''
<dhcp-local-server>
${concatMapAttrsStringSep "\n" (name: cfg: ''
<group>
<name>${name}</name>
<interface>
${concatMapStrings (intf: "<name>${intf}</name>") cfg.interfaces}
</interface>
</group>
'') config.system.services.dhcp-local-server.group}
</dhcp-local-server>
'';
in
''
<system>
@ -121,7 +89,6 @@ in
<ssh><port>${toString config.system.services.netconf.port}</port></ssh>
<rfc-compliant/><yang-compliant/>
</netconf>
${dhcp-local}
</services>
</system>
'';

View file

@ -1,93 +0,0 @@
# SPDX-FileCopyrightText: 2024 Lubin Bailly <lubin.bailly@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
{ lib, ... }:
let
inherit (lib) mapAttrs mod;
inherit (lib.extra) genFuse;
in
{
dgn-hardware.model = "EX2300-48P";
dgn-isp = {
enable = true;
AP = [
# H1-00
"ge-0/0/0"
"ge-0/0/1"
"ge-0/0/2"
"ge-0/0/3"
"ge-0/0/4"
"ge-0/0/5"
# H1-01
"ge-0/0/6"
"ge-0/0/7"
"ge-0/0/8"
"ge-0/0/9"
"ge-0/0/10"
"ge-0/0/11"
# H1-02
"ge-0/0/12"
"ge-0/0/13"
"ge-0/0/14"
"ge-0/0/15"
"ge-0/0/16"
"ge-0/0/17"
];
admin-ip = "fd26:baf9:d250:8000::1001/64";
};
dgn-interfaces = {
# oob
"ge-0/0/42".ethernet-switching = {
interface-mode = "trunk";
vlans = [ "all" ];
};
# ilo
"ge-0/0/47".ethernet-switching = {
interface-mode = "access";
vlans = [ "admin-core" ];
};
# router
"xe-0/1/0".ethernet-switching = {
interface-mode = "trunk";
vlans = [ "all" ];
};
# netaccess01
"xe-0/1/1".ethernet-switching = {
interface-mode = "trunk";
vlans = [
"users"
"ap-staging"
"admin-ap"
"admin-core"
];
};
# uplink
"ge-0/1/3".ethernet-switching = {
interface-mode = "trunk";
vlans = [ "uplink-cri" ];
};
# debug management
"me0".inet.addresses = [ "192.168.42.6/24" ];
};
interfaces =
{
"irb".unit."0".description = "Admin";
}
// mapAttrs (_: description: { inherit description; }) (
{
"xe-0/1/0" = "netcore01";
"xe-0/1/1" = "Jaccess04";
"ge-0/1/3" = "uplink-cri";
"ge-0/0/42" = "oob";
"ge-0/0/47" = "psu";
}
// genFuse (i: {
"ge-0/0/${toString i}" = "AP_H1_${toString (i / 6)}_${toString (mod i 6 + 1)}";
}) 18
);
snmp.community."public".authorization = "read-only";
}

View file

@ -2,11 +2,6 @@
#
# SPDX-License-Identifier: EUPL-1.2
{ lib, ... }:
let
inherit (lib) mapAttrs mod;
inherit (lib.extra) genFuse;
in
{
dgn-hardware.model = "EX2300-48P";
dgn-isp = {
@ -31,18 +26,4 @@ in
# debug management
"me0".inet.addresses = [ "192.168.42.6/24" ];
};
interfaces =
{
"irb".unit."0".description = "Admin";
}
// mapAttrs (_: description: { inherit description; }) (
{
"xe-0/1/0" = "Jaccess01";
}
// genFuse (i: {
"ge-0/0/${toString i}" = "AP_H2_${toString (i / 2)}_${toString (mod i 2 + 1)}";
}) 6
);
snmp.community."public".authorization = "read-only";
}

View file

@ -0,0 +1,28 @@
# SPDX-FileCopyrightText: 2025 Lubin Bailly <lubin.bailly@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
{
dgn-hardware = {
model = "EX4400-24X";
extensions = [ "EX4400-EM-4Y" ];
};
dgn-isp = {
enable = true;
admin-ip = "fd26:baf9:d250:8000::1010/64";
};
dgn-interfaces = {
"xe-0/2/0".ethernet-switching = {
interface-mode = "trunk";
vlans = [ "all" ];
};
"xe-0/0/23".ethernet-switching = {
interface-mode = "trunk";
vlans = [ "all" ];
};
# debug management
"me0".inet.addresses = [ "192.168.2.3/24" ];
};
}

View file

@ -2,69 +2,51 @@
#
# SPDX-License-Identifier: EUPL-1.2
{ lib, ... }:
let
inherit (lib) mapAttrs;
in
{
dgn-hardware = {
model = "EX4400-24X";
extensions = [ "EX4400-EM-4Y" ];
};
dgn-hardware.model = "EX4100-F-48P";
dgn-isp = {
enable = true;
admin-ip = "fd26:baf9:d250:8000::1010/64";
core-links = [
"xe-0/0/0"
"xe-0/0/3"
"xe-0/0/22"
"xe-0/0/21"
];
admin-ip = "fd26:baf9:d250:8000::100f/64";
};
dgn-profiles = {
"hypervisor" = {
interfaces = [
"ge-0/0/1"
"ge-0/0/3"
"ge-0/0/5"
"ge-0/0/7"
"ge-0/0/9"
];
configuration.ethernet-switching = {
interface-mode = "access";
vlans = [ "hypervisor" ];
};
};
"idrac" = {
interfaces = [
"ge-0/0/0"
"ge-0/0/2"
"ge-0/0/4"
"ge-0/0/6"
"ge-0/0/8"
# PDU and PSU
"ge-0/0/46"
"ge-0/0/47"
];
configuration.ethernet-switching = {
interface-mode = "access";
vlans = [ "admin-core" ];
};
};
};
dgn-interfaces = {
"ge-0/0/23".ethernet-switching = {
"xe-0/2/0".ethernet-switching = {
interface-mode = "trunk";
vlans = [ "uplink-cri" ];
vlans = [ "all" ];
};
"xe-0/0/0".ethernet-switching.vlans = [ "uplink-cri" ];
"xe-0/0/21".ethernet-switching.vlans = [ "all" ];
"xe-0/0/22".ethernet-switching.vlans = [ "all" ];
# debug management
"me0".inet.addresses = [ "192.168.2.3/24" ];
"me0".inet.addresses = [ "192.168.2.2/24" ];
};
dgn-profiles."hypervisor" = {
interfaces = [
"xe-0/0/4"
"xe-0/0/5"
"xe-0/0/6"
"xe-0/0/7"
"xe-0/0/8"
"xe-0/0/9"
];
configuration.ethernet-switching = {
interface-mode = "access";
vlans = [ "hypervisor" ];
};
};
interfaces =
{
"irb".unit."0".description = "Admin";
}
// mapAttrs (_: description: { inherit description; }) {
"xe-0/0/0" = "Jaccess01";
"xe-0/0/3" = "Jaccess04";
"xe-0/0/21" = "vault01";
"xe-0/0/22" = "netcore02";
"ge-0/0/23" = "uplink-cri";
"xe-0/0/4" = "random02";
"xe-0/0/5" = "random03";
"xe-0/0/6" = "hypervisor01";
"xe-0/0/7" = "hypervisor02";
"xe-0/0/8" = "hypervisor03";
"xe-0/0/9" = "build01";
};
snmp.community."public".authorization = "read-only";
}

View file

@ -1,87 +1,77 @@
# SPDX-FileCopyrightText: 2025 Lubin Bailly <lubin.bailly@dgnum.eu>
# SPDX-FileCopyrightText: 2024 Lubin Bailly <lubin.bailly@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
{ lib, ... }:
let
inherit (lib) mapAttrs;
in
{
dgn-hardware.model = "EX4100-F-48P";
dgn-hardware.model = "EX2300-48P";
dgn-isp = {
enable = true;
admin-ip = "fd26:baf9:d250:8000::100f/64";
};
dgn-profiles = {
"hypervisor" = {
interfaces = [
"ge-0/0/1"
"ge-0/0/3"
"ge-0/0/5"
"ge-0/0/7"
"ge-0/0/9"
];
configuration.ethernet-switching = {
interface-mode = "access";
vlans = [ "hypervisor" ];
};
};
"idrac" = {
interfaces = [
"ge-0/0/0"
"ge-0/0/2"
"ge-0/0/4"
"ge-0/0/6"
"ge-0/0/8"
"ge-0/0/10"
"ge-0/0/12"
"ge-0/0/14"
# PDU and PSU
"ge-0/0/45"
"ge-0/0/46"
"ge-0/0/47"
];
configuration.ethernet-switching = {
interface-mode = "access";
vlans = [ "admin-core" ];
};
};
AP = [
# H1-00
"ge-0/0/0"
"ge-0/0/1"
"ge-0/0/2"
"ge-0/0/3"
"ge-0/0/4"
"ge-0/0/5"
# H1-01
"ge-0/0/6"
"ge-0/0/7"
"ge-0/0/8"
"ge-0/0/9"
"ge-0/0/10"
"ge-0/0/11"
# H1-02
"ge-0/0/12"
"ge-0/0/13"
"ge-0/0/14"
"ge-0/0/15"
"ge-0/0/16"
"ge-0/0/17"
];
admin-ip = "fd26:baf9:d250:8000::1001/64";
};
dgn-interfaces = {
"xe-0/2/0".ethernet-switching = {
# oob
"ge-0/0/42".ethernet-switching = {
interface-mode = "trunk";
vlans = [ "all" ];
};
# ilo
"ge-0/0/47".ethernet-switching = {
interface-mode = "access";
vlans = [ "admin-core" ];
};
# router
"xe-0/1/0".ethernet-switching = {
interface-mode = "trunk";
vlans = [ "all" ];
};
# netaccess01
"xe-0/1/1".ethernet-switching = {
interface-mode = "trunk";
vlans = [
"users"
"ap-staging"
"admin-ap"
"admin-core"
];
};
# netcore01 (Potos)
"xe-0/1/2".ethernet-switching = {
interface-mode = "trunk";
vlans = [
"all"
];
};
# uplink
"ge-0/1/3".ethernet-switching = {
interface-mode = "trunk";
vlans = [ "uplink-cri" ];
};
# debug management
"me0".inet.addresses = [ "192.168.2.2/24" ];
"me0".inet.addresses = [ "192.168.42.6/24" ];
};
interfaces =
{
"irb".unit."0".description = "Admin";
}
// mapAttrs (_: description: { inherit description; }) {
"xe-0/2/0" = "netcore01";
"ge-0/0/0" = "hypervisor01_idrac";
"ge-0/0/2" = "hypervisor02_idrac";
"ge-0/0/4" = "hypervisor03_idrac";
"ge-0/0/6" = "build01_idrac";
"ge-0/0/8" = "random01_idrac";
"ge-0/0/10" = "random02_idrac";
"ge-0/0/12" = "random03_idrac";
"ge-0/0/14" = "vault01_idrac";
"ge-0/0/1" = "hypervisor01";
"ge-0/0/3" = "hypervisor02";
"ge-0/0/5" = "hypervisor03";
"ge-0/0/7" = "build01";
"ge-0/0/9" = "random03";
"ge-0/0/47" = "psu";
"ge-0/0/46" = "psu_pdu";
"ge-0/0/45" = "pdu_32A";
};
snmp.community."public".authorization = "read-only";
}

View file

@ -16,10 +16,4 @@ builtins.map pkgs.grafanaPlugins.grafanaPlugin [
version = "0.13.1";
zipHash = "sha256-n1LskeOzp32LZS3PcsRh8FwQVBFVlzczfO2aGbEClSo=";
}
{
pname = "knightss27-weathermap-panel";
version = "0.4.3";
zipHash = "sha256-N0jhFKYEgU8dZCJ1txcYg0rr17+FkGJjXjwyq2TSa74=";
}
]

View file

@ -203,21 +203,6 @@ in
"email"
];
};
dgn_zulip = {
displayName = "Zulip [Chat]";
originUrl = "https://zulip.dgnum.eu/complete/oidc/";
originLanding = "https://zulip.dgnum.eu";
preferShortUsername = true;
allowInsecureClientDisablePkce = true;
enableLegacyCrypto = true;
scopeMaps.grp_active = [
"openid"
"profile"
"email"
];
};
};
};
};

View file

@ -23,19 +23,7 @@ in
hostname = host;
settings = {
auth.socialite = {
configs.kanidm = {
listener = "\\SocialiteProviders\\Kanidm\\KanidmExtendSocialite";
client_id = "$KANIDM_CLIENT_ID";
client_secret = "$KANIDM_CLIENT_SECRET";
redirect = "$KANIDM_REDIRECT_URI";
base_url = "$KANIDM_BASE_URL";
};
default_role = "normal";
register = true;
};
};
settings = { };
database = {
createLocally = true;

View file

@ -80,11 +80,3 @@ index 3d89a1530..a00c5f307 100644
{
"name": "socialiteproviders/manager",
"version": "v4.6.0",
index 3d89a1530..a00c5f307 100644
--- a/app/Providers/EventServiceProvider.php
+++ b/app/Providers/EventServiceProvider.php
@@ -33,3 +33,4 @@
\SocialiteProviders\Manager\SocialiteWasCalled::class => [
+ \SocialiteProviders\Kanidm\KanidmExtendSocialite::class.'@handle',
\App\Listeners\SocialiteWasCalledListener::class,
],

View file

@ -12,7 +12,6 @@
# Becareful, jool will not translate ips. Prefer ipv6 proxy target
redirects = {
"kfet.lab.dgnum.eu" = "v6.labcore01.pav01.infra.lab.dgnum.eu:443";
"zulip.dgnum.eu" = "v6.zulip01.pav01.infra.lab.dgnum.eu:443";
};
};
};

View file

@ -33,8 +33,6 @@ in
};
dashboard = {
package = nixpkgs.nixos.unstable.netbird-dashboard;
settings = {
AUTH_AUTHORITY = "https://sso.dgnum.eu/oauth2/openid/dgn_netbird";
AUTH_AUDIENCE = "dgn_netbird";

View file

@ -15,7 +15,6 @@ in
flags = {
retentionPeriod = "4w";
httpListenAddr = "${meta.network.${name}.netbirdIp}:${builtins.toString port}";
maxConcurrentInserts = "200";
};
};

View file

@ -14,7 +14,7 @@ lib.extra.mkConfig {
"k-radius"
"monitoring"
"networking"
# "ups"
"ups"
"ulogd"
];

View file

@ -5,6 +5,5 @@
{
imports = [
./victorialogs.nix
./snmp.nix
];
}

View file

@ -1,68 +0,0 @@
# SPDX-FileCopyrightText: 2025 Lubin Bailly <lubin@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
{
pkgs,
lib,
meta,
...
}:
let
inherit (lib) elem filterAttrs mapAttrsToList;
port = 9004;
in
{
dgn-monitoring = {
exporters.ports.snmp = port;
scrapeConfigs = {
"switches" = {
static_configs =
mapAttrsToList
(hostname: cfg: {
targets = [ cfg.deployment.targetHost ];
labels = { inherit hostname; };
})
(
filterAttrs (
_: cfg:
cfg.nixpkgs.system == "netconf"
&& elem cfg.site [
"pot01"
"hyp01"
"hyp02"
]
) meta.nodes
);
scrape_timeout = "30s";
metrics_path = "/snmp";
params = {
auth = [ "public_v2" ];
module = [ "if_mib" ];
};
relabel_configs = [
{
target_label = "__param_target";
source_labels = [ "__address__" ];
}
{
target_label = "instance";
source_labels = [ "__param_target" ];
}
{
target_label = "__address__";
replacement = "localhost:${toString port}";
}
];
};
};
};
services.prometheus.exporters.snmp = {
enable = true;
enableConfigCheck = false;
configurationPath = pkgs.prometheus-snmp-exporter.src + "/snmp.yml";
};
}

View file

@ -12,12 +12,7 @@
}:
let
inherit (lib)
genList
mapAttrs'
mkOption
nameValuePair
;
inherit (lib) mapAttrs' mkOption nameValuePair;
inherit (lib.types) listOf attrs;
uplink = {
@ -353,7 +348,6 @@ in
chain postrouting {
type nat hook postrouting priority 100;
ip saddr 10.0.0.0/16 ip daddr != 10.0.0.0/16 snat ip to 129.199.195.130-129.199.195.157
ip saddr 192.168.0.0/17 ip daddr != 192.168.0.0/17 snat ip to 129.199.195.130-129.199.195.157
}
'';
};
@ -389,7 +383,7 @@ in
ip saddr 10.0.255.0/24 jump forward_reject;
# We only forward for ISP clients and our stuff
ip saddr != 10.0.0.0/16 ip saddr != 192.168.0.0/17 jump forward_reject;
ip saddr != 10.0.0.0/16 jump forward_reject;
# Can talk to us
ip daddr 10.0.0.0/27 accept;

View file

@ -68,7 +68,7 @@ in
};
plugins = {
inherit (wp4nix.plugins) pages-with-category-and-tag user-role-editor;
inherit (wp4nix.plugins) user-role-editor;
};
languages = [ pkgs.wordpressPackages.languages.fr_FR ];

View file

@ -15,6 +15,7 @@ lib.extra.mkConfig {
"cas-eleves"
# "kadenios"
"django-apps"
"mattermost"
];
extraConfig = {

View file

@ -0,0 +1,348 @@
From ec1ffa5fc38cdd6d0cc5e8dea07d9a4730fc5a32 Mon Sep 17 00:00:00 2001
From: hbarral <hbarral@clipper.ens.fr>
Date: Fri, 25 Oct 2024 06:50:47 +0200
Subject: [PATCH 1/2] Login page: ENS theme This commit should be cherry-picked
on top of of new Mattermost releases
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Théophile Bastian <contact@tobast.fr>
---
.../channels/src/components/login/login.scss | 87 +++++++++++++++++-
.../channels/src/components/login/login.tsx | 78 +++++++++++++---
webapp/channels/src/images/emailLogo.png | Bin 0 -> 605 bytes
webapp/channels/src/images/gitlabLogo.png | Bin 0 -> 1655 bytes
webapp/channels/src/images/noaccountLogo.png | Bin 0 -> 693 bytes
5 files changed, 153 insertions(+), 12 deletions(-)
create mode 100644 webapp/channels/src/images/emailLogo.png
create mode 100644 webapp/channels/src/images/gitlabLogo.png
create mode 100644 webapp/channels/src/images/noaccountLogo.png
diff --git a/webapp/channels/src/components/login/login.scss b/webapp/channels/src/components/login/login.scss
index e64aebccb4..ec7dafdafb 100644
--- a/webapp/channels/src/components/login/login.scss
+++ b/webapp/channels/src/components/login/login.scss
@@ -3,6 +3,10 @@
@use 'utils/mixins';
+$clipper_color: #548;
+$email_color: #4d8844;
+$exte_color: #886344;
+
.login-body {
display: flex;
flex-direction: column;
@@ -130,6 +134,88 @@
margin: 20px 0 10px;
}
+ .collapsed {
+ display: none;
+ }
+
+ .login-btn {
+ @include mixins.primary-button;
+ @include mixins.button-large;
+
+ display: flex;
+ height: 64px;
+ flex-direction: row;
+ align-items: center;
+ justify-content: flex-start;
+ background-color: red;
+ text-decoration: none;
+
+ .text {
+ display: flex;
+ width: 80%;
+ flex-direction: column;
+ align-items: center;
+ justify-content: space-evenly;
+
+ & > span {
+ display: inline-block;
+
+ &:nth-child(2) {
+ font-size: 0.8em;
+ font-style: italic;
+ font-weight: normal;
+ }
+ }
+ }
+
+ .icon {
+ position: relative;
+ display: inline-block;
+ width: 42px;
+ height: 42px;
+ margin-right: 12px;
+ background-size: 100% 100%;
+ text-align: center;
+ }
+ }
+
+ .login-body-card-auth-clipper {
+ background-color: $clipper_color;
+
+ &:hover {
+ background-color: darken($clipper_color, 10%);
+ }
+
+ .icon {
+ background-image: url('../../images/gitlabLogo.png');
+ }
+ }
+
+ .login-body-card-auth-mail-toggle {
+ background-color: $email_color;
+
+ &:hover {
+ background-color: darken($email_color, 10%);
+ }
+
+ .icon {
+ background-image: url('../../images/emailLogo.png');
+ }
+ }
+
+ .login-body-card-auth-exte {
+ background-color: $exte_color;
+ margin-top: 10px;
+
+ &:hover {
+ background-color: darken($exte_color, 10%);
+ }
+
+ .icon {
+ background-image: url('../../images/noaccountLogo.png');
+ }
+ }
+
.login-body-card-form {
.login-body-card-form-input {
margin-top: 22px;
@@ -161,7 +246,7 @@
display: flex;
height: 1px;
justify-content: center;
- margin: 40px 0;
+ margin: 20px 0;
background: rgba(var(--center-channel-color-rgb), 0.08);
text-align: center;
diff --git a/webapp/channels/src/components/login/login.tsx b/webapp/channels/src/components/login/login.tsx
index 5668c029a7..d5e2794584 100644
--- a/webapp/channels/src/components/login/login.tsx
+++ b/webapp/channels/src/components/login/login.tsx
@@ -121,6 +121,8 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
const [alertBanner, setAlertBanner] = useState<AlertBannerProps | null>(null);
const [hasError, setHasError] = useState(false);
const [isMobileView, setIsMobileView] = useState(false);
+ const [showEmailForm, setShowEmailForm] = useState(false);
+
const enableCustomBrand = EnableCustomBrand === 'true';
const enableLdap = EnableLdap === 'true';
@@ -460,6 +462,10 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
};
}, []);
+ function toggleEmailForm() {
+ setShowEmailForm(!showEmailForm);
+ }
+
if (initializing) {
return (<LoadingScreen/>);
}
@@ -780,6 +786,8 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
);
}
+ const doShowEmailForm = (showEmailForm) ? '' : ' collapsed';
+
if (!enableBaseLogin && !enableExternalSignup) {
return (
<ColumnLayout
@@ -848,13 +856,50 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
onDismiss={alertBanner.onDismiss ?? dismissAlert}
/>
)}
+ {enableExternalSignup && (
+ <a
+ className='login-body-card-auth-clipper login-btn'
+ id='gitlab'
+ href={Client4.getOAuthRoute() + '/gitlab/login'}
+ >
+ <span className='icon'/>
+ <span className='text'>
+ <span>
+ {formatMessage({id: 'login.clipper', defaultMessage: 'Clipper'})}
+ </span>
+ <span>
+ {formatMessage({id: 'login.clipper_help', defaultMessage: '("Gitlab" sur mobile)'})}
+ </span>
+ </span>
+ </a>
+ )}
+ {enableBaseLogin && enableExternalSignup && (
+ <div className='login-body-card-form-divider'>
+ <span className='login-body-card-form-divider-label'>
+ {formatMessage({id: 'login.or', defaultMessage: 'or log in with'})}
+ </span>
+ </div>
+ )}
+ {enableBaseLogin && (
+ <div
+ className='login-body-card-auth-mail-toggle login-btn'
+ onClick={toggleEmailForm}
+ >
+ <span className='icon'/>
+ <span className='text'>
+ <span>
+ {formatMessage({id: 'login.email', defaultMessage: 'Adresse email'})}
+ </span>
+ </span>
+ </div>
+ )}
{enableBaseLogin && (
<form
onSubmit={(event: FormEvent<HTMLFormElement>) => {
preSubmit(event as unknown as React.MouseEvent);
}}
>
- <div className='login-body-card-form'>
+ <div className={'login-body-card-form' + doShowEmailForm}>
<Input
ref={loginIdInput}
name='loginId'
@@ -895,16 +940,27 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
</span>
</div>
)}
- {enableExternalSignup && (
- <div className={classNames('login-body-card-form-login-options', {column: !enableBaseLogin})}>
- {getExternalLoginOptions().map((option) => (
- <ExternalLoginButton
- key={option.id}
- direction={enableBaseLogin ? undefined : 'column'}
- {...option}
- />
- ))}
- </div>
+ {enableBaseLogin && (
+ <a
+ className='login-body-card-auth-exte login-btn'
+ href='@exte_login@'
+ >
+ <span className='icon'/>
+ <span className='text'>
+ <span>
+ {formatMessage({
+ id: 'login.noClipperAccount',
+ defaultMessage: 'Pas de compte Clipper ?',
+ })}
+ </span>
+ <span>
+ {formatMessage({
+ id: 'login.createExte',
+ defaultMessage: 'Demander un compte extérieur',
+ })}
+ </span>
+ </span>
+ </a>
)}
</div>
</div>
diff --git a/webapp/channels/src/images/emailLogo.png b/webapp/channels/src/images/emailLogo.png
new file mode 100644
index 0000000000000000000000000000000000000000..098922ea6e827eca14dad9810cb953079a1a623d
GIT binary patch
literal 605
zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o4i*Lm28M*4p$rTROiAAEE)2Od`|PeWFfecy
zctjR6Fz6|RFk{71`!WUw2KEw9Usv|WENsGR>R+M-zcDZ{zVmc(49U3nc80EpbD%`q
z{)Ie(EuBqGY~4qV1X>SXnswG}_QkD|z0oGSN;Yar#caOmqc+)<O{}@8$x)z9da`ZX
zYuW2>|Gk@6z5YSvZQ18<Z0+xt&nvbQ)CGaH@_`2yxVG=Oy0FzT@rK=TUC&p2mb?F2
z&h{ysHRCVq<tLUK-|#+|94q+kXV=|5{&RV(w=f>QQ|;~Uv*Bp<ns4$slNY-Ef4)26
z-2F?UmSH>l&;DyPP1~{MZxV;@e6?3kO!iLeQJJ^X<Lg1Cl|hFKo2?&7sF@l~$<#JB
zci!_#<k6!`OHZ0q%1ymtU#YsYLPk{kOtI|Ee{HPO?)F=LeDTYz)00p6ru^ikCstQ8
ze|&TAu|tdPuH>2DSN!@ut)aMrGdF)-{NeR8S#BM5o#u2%tANFG!TxGK&eh!yEL9d7
zS#qp-vmwx;VZjEew%$vOKMrT{O!7E!<J%^Q?xhdeH#lAt4C7VP*dX_wVT~o<;Zse%
zXQC7Movhcq+0d!W;ePpoRaRSK^0LIpTq!H&uf0N%?l(-X%DKFAcpJ3v5r=fE`$qkM
z#Ru>3AHV8hVb{_2xWS>9?X$|dlz9fzUpVx#6@SnE=@)eU(-nEOmG)<Y>gO!`YvaGt
zAy7_4!^OqIdxy*AKcR~R1ryD#DqWS=xSR<Bf0JvOmpne>k^WoC8<cE3UHx3vIVCg!
E0QZ>*HUIzs
literal 0
HcmV?d00001
diff --git a/webapp/channels/src/images/gitlabLogo.png b/webapp/channels/src/images/gitlabLogo.png
new file mode 100644
index 0000000000000000000000000000000000000000..bdc4422783b804cbaf45f13219354f38e080b654
GIT binary patch
literal 1655
zcmeAS@N?(olHy`uVBq!ia0y~yV6bFhU~u4IVPIh3vJq!zU|?V>cJd72;0S%=1`^~f
z@Q5sCU|=~8!i*D3zrJH&V2~_vjVKAuPb(=;EJ|f4FE7{2%*!rLPAo{(%P&fw{mw>;
zfr0g#r;B4q#NoF$t9wGiWf>kYYl}Py^jdjp^;9v<++Y^ND|Rf=8`&o0K2_YJ<a*(5
z(#9pc8x)sKZO`tVVG_}+cP*s1UnDwbmD>uT0#<I1iS7yuH+{O`#FaBqG(xQG`{(|>
z*2VANoO`3wtfDsW^Z7f)=jXn^cYjWGoUqgGN#B3%;fj_m^;?^~Ei!f6_tyVzmrj~Y
zJr|X|vGSD8#yd&=5oz63Q<uH2sXXY(cjI;7_ungpSA|-oE^l7(t*LWbW%SgQRxxtx
z+NX!iPTtXL@osimMDl+5h?dr@53>ZPt=#Ii{?NhfH`|UYsJ=2;n*L!EoBxkF_bboX
z72L~F{&Ux^mW$c?#)BD*&ocb3%3Xh;`DVj8NA5TNbG1vG7ynz6toTGk=R;n7sOV|-
zoRHN0TA^Bhbwl@s%Nu{Z>2zY^xu``;WGnYAW)l;;^-=5AO#2v)^7i0k71s`nJ!{o9
zI+WfMkbGt7q~eb|s%D3tQqSFXMo>7gYULA#(qkH?2ON8Hl&@Q+Gzcx8d;R|<=iR4t
z*n1hT@4g>W9c^=)QPu9{->kxZt<AOjC&U+f^vJZ%l{370PGtLOk)v_PV-1T66jNFy
zIfcq*Pg9cr<n~3=_ngif&Hb}JPARZ*oR{Nx?%S880m5rH$N1m9o40FU<z!}Gb02e8
zufMmpf9BiBZrWu0t2Lp2_h+^DTeMpQ{+MamzFhU^{_$lc{v2<4b2yG2yH&jX&xwaC
zxz&!IxIO2j*EK0Q<#^xiFKU=?Uk#Qzz$P1<Vjt|Y;nF^qpUbYM{oe9#<uhZK+LCPt
zKWuY9{%+-Y`R=;&oSg^eeeH_mEZTK7%P1<Rc-M_*Gm<98=J6@Aa4TI<-K+9)%QD?h
zS>CcA7>{o3=2A94n%42u?y6Pvs$2Pb(l;}TIJ$H~R$g47a`iIvvR^CoRte25OH_G2
z#ohV-?3s7JKfF>qcmJ9cE7@<SZ$xLsTuc1^X`+y@;nK>9;<-lmj|)G^`26=?tIbKn
z%%WVmCs(^{pBHp5U$f~_%JpZi_ipfr{roP<^=3oK<HFVx$;V75%I{24Sw5TniMIPS
zvx(ZbUFUgCIDXr6UgoPv{b#<{B3@p*p=+1EL3Pz_$9dd}GbU{InkDn-blxTv(NoV1
z)|*^OZhPi=jnz0Ly`(Vmgg}YWts7yVGUhs+m|UZwVR&MVOsJcd*%7lnm)-Kdv?+Gg
zYN+2_{PV2N`KVN*ebc`iy|A7!>FXc0&x=L3oeWDq%lzf)<x4WAmnwJ8`}J!2;$zGY
z|G%s^shqwruiW>vu<Ew-vrhx(st2oPKb~Ayo^r+CcV*1$-F~|l_%-t$oAvnk%hUIg
zCzU>sUwOTHQRsu`fpZ)8JqfsSV}Ad{yFJ^zbhFkL#D9La@U?76Zr3?QiB#cNx#xKf
zM!xZ%qU$^P;-SRw7Qgcymz`!k7M%J=@N$rQY))os>!Y<|DHCN2jaXEru0Gr{+h|+%
z3>oWx3P-jJ+-=>PJ4sO@zwS}R8aDr?wN^(@u|K?b<0FrU-_+a_f4=bq=@&<5Hh#~%
zpg8O7dJi4lBjsBcsUGV(vh{PwQ?+i{ytzD%^8a0ut9ln&yB&LVcLihbwAYigUgtXT
z1x>V1QFk_ovh90)-Ab}ad~w^iZR=BGKUH$8Ptgp{Gv0Y(h3RI;K(S5jy(<Nm+*|$b
zjN3o&R<8X;JA5}S{+dwuI)%gPms67GSK-)V)@`fj?mQ{*rJPCp?p-U%jgQwBDmq<H
zf8(BRGT;4{lCwdaa^H)&y*;nKesyZGo6)`J;Q3oh&L4CtR=RetEr>og^}P96j@xgz
zoG#p+R$@`(l)Q-hMUHjE1V_0~YkS_m<loq5%l=c;RPy%k&i%7_!kTyS6zyO9^6u0X
zZ&dc(n`AR7aP9QRAC2NwiuI2lK3jY+;^w<E3cI{)JHu6_JYNM(-nZ<Md}8^VA5YKZ
z>9h#^*}%G!mpAR0m)uePOYPfdz25pF?z=|#GM}0GF8hO1ITUxCb6W3Sx-II#t|K#c
z>No$HTpse+_3@oYao%&ct9^@`HRE%AW93CDlgO&#N0#kByQ7ni`_Eo7d*?Olb@vR{
fFZuoWKf}xkStZ|&E;z}+z`)??>gTe~DWM4fVgxXO
literal 0
HcmV?d00001
diff --git a/webapp/channels/src/images/noaccountLogo.png b/webapp/channels/src/images/noaccountLogo.png
new file mode 100644
index 0000000000000000000000000000000000000000..b51eda513122fe8da10f2fe896b78f380811cc61
GIT binary patch
literal 693
zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o4i*Lm28M*4p$rTROiAAEE)2Od`|PeWFfecy
zctjR6Fz6|RFk{71`!WUw2KEw9Usv|WENmijEG}<O{bFEXa`SX?49U3ncE*0c!v+G!
zlvCHSBrWxJ7vK&ycbVU4+97^IXyc|5SJ9w*4Qp(i1!phU;W(z3w$|a*n^(t!LNDCf
zDy#10xHIpn<@cX8-|x=Xc(r7*pVddPUn)<0W^PP4If>PH*%Os7v(0K<+89(=HI>=E
zyf92~@oHpYk#zUHYwd2Be(1sq<CH@(u5r8dcQ`A0cCMXes3~+bw)WV0!2>t){;+o#
zDp;&PT+_73WOwcH;wc)F0zOLy?6_Zhn4fh*>#X@Ku?a#9jVf3ENI3=Rei3oib&Oe{
z)uP<=>G|!=zar1)z7z~r`ty0-{fY6i4kCsu`f&^TlXCuV&~DI3>iLx5Ui2jGRbjJB
zQL^potIBSZc2Ada-Su@ni$j#Z^cRo7zSORspJrcKm^)@#8!<Ue|C{K)A&KRoSi=wF
z{mG>V6a_rr<gMdzv?@@lwEdI*z@=Kv;O@DGTMSEl-g+6k^)gBrZr0Y7FkwqLaA@6@
zCD$by<WAc&EcmKkz~OXm$J0*peE}N#Djs)6pL@mW<SkXydqAb>^#_wXjxKXP@Ep+o
z9#eRV(a?~6PSH`@^$a$}LT)Z@TF;vN9~3^%_{LFFB<OUsE$N7&{GQ@W+xnB%@-kt1
zZpVuL9O!<xIckNzO^Dv{V{J>G+_1dcr|B#uRO|gLbWzFkxzR?o!BQTMcD^h3>GHEY
zxIXPQ+vEpnXUY>dTFzqKdB5J|cJsq6Uq9CHi-}ZkSah>l^{ST4?U>7JD;Mr{eL73_
ucFfgvr$eewzlz_NksW*AQ+@6q@1JbVm+nZK2==9blB1`qpUXO@geCxL=_bDb
literal 0
HcmV?d00001
--
2.49.0

View file

@ -0,0 +1,3 @@
SPDX-FileCopyrightText: 2025 hbarral <hbarral@clipper.ens.fr>
SPDX-License-Identifier: MIT

View file

@ -0,0 +1,25 @@
From 4b0ad3a8398511843393ea92cc12f0666465a13f Mon Sep 17 00:00:00 2001
From: hbarral <hbarral@clipper.ens.fr>
Date: Sat, 7 Dec 2024 12:24:09 +0100
Subject: [PATCH 2/2] Remove annoying "FREE EDITION" badge from webapp UI This
commit should be cherry-picked on top of new Mattermost releases.
---
.../product_branding_team_edition.tsx | 1 -
1 file changed, 1 deletion(-)
diff --git a/webapp/channels/src/components/global_header/left_controls/product_menu/product_branding_team_edition/product_branding_team_edition.tsx b/webapp/channels/src/components/global_header/left_controls/product_menu/product_branding_team_edition/product_branding_team_edition.tsx
index 2773ba5184..018454c880 100644
--- a/webapp/channels/src/components/global_header/left_controls/product_menu/product_branding_team_edition/product_branding_team_edition.tsx
+++ b/webapp/channels/src/components/global_header/left_controls/product_menu/product_branding_team_edition/product_branding_team_edition.tsx
@@ -43,7 +43,6 @@ const ProductBrandingTeamEdition = (): JSX.Element => {
width={116}
height={20}
/>
- <Badge>{'FREE EDITION'}</Badge>
</ProductBrandingTeamEditionContainer>
);
};
--
2.49.0

View file

@ -0,0 +1,3 @@
SPDX-FileCopyrightText: 2025 hbarral <hbarral@clipper.ens.fr>
SPDX-License-Identifier: MIT

View file

@ -0,0 +1,50 @@
# SPDX-FileCopyrightText: 2024 Tom Hubrecht <tom.hubrecht@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
{ pkgs, ... }:
let
host = "chat.dgnum.eu";
port = 8065;
exte_login = "https://demarches.dgnum.eu/commencer/demande-compte-merle";
in
{
imports = [ ./module.nix ];
services.mattermost = {
enable = true;
inherit port;
package = pkgs.mattermostLatest.overrideAttrs (old: {
webapp = old.webapp.overrideAttrs (wOld: {
nativeBuildInputs = wOld.nativeBuildInputs ++ [
pkgs.git
pkgs.gnused
];
postPatch =
(wOld.postPatch or "")
+ ''
sed -i 's/GitLab/Clipper/g' channels/src/i18n/*.json
git apply -p2 < ${pkgs.replaceVars ./0001-Login-page-ENS-theme.patch { inherit exte_login; }}
git apply -p2 < ${./0002-Remove-annoying-FREE-EDITION-badge-from-webapp-UI.patch}
'';
});
});
siteName = "Merle [Discussions ENS]";
siteUrl = "https://${host}";
mutableConfig = true;
database.peerAuth = true;
};
dgn-web.simpleProxies.mattermost = {
inherit host port;
};
}

View file

@ -0,0 +1,996 @@
# SPDX-FileCopyrightText: The nixpkgs contributors
#
# SPDX-License-Identifier: MIT
{
config,
pkgs,
lib,
...
}:
let
inherit (lib.strings)
hasInfix
hasSuffix
escapeURL
concatStringsSep
escapeShellArg
escapeShellArgs
versionAtLeast
optionalString
;
inherit (lib.meta) getExe;
inherit (lib.lists) singleton;
inherit (lib.attrsets) mapAttrsToList recursiveUpdate optionalAttrs;
inherit (lib.options) mkOption mkPackageOption mkEnableOption;
inherit (lib.modules)
mkRenamedOptionModule
mkMerge
mkIf
mkDefault
;
inherit (lib.trivial) warnIf throwIf;
inherit (lib) types;
cfg = config.services.mattermost;
# The directory to store mutable data within dataDir.
mutableDataDir = "${cfg.dataDir}/data";
# The plugin directory. Note that this is the *pre-unpack* plugin directory,
# since Mattermost looks in mutableDataDir for a directory called "plugins".
# If Mattermost is installed with plugins defined in a Nix configuration, the plugins
# are symlinked here. Otherwise, this is a real directory and the tarballs are uploaded here.
pluginTarballDir = "${mutableDataDir}/plugins";
# We need a different unpack directory for Mattermost to sync things to at launch,
# since the above may be a symlink to the store.
pluginUnpackDir = "${mutableDataDir}/.plugins";
# Mattermost uses this as a staging directory to unpack plugins, among possibly other things.
# Ensure that it's inside mutableDataDir since it can get rather large.
tempDir = "${mutableDataDir}/tmp";
# Creates a database URI.
mkDatabaseUri =
{
scheme,
user ? null,
password ? null,
escapeUserAndPassword ? true,
host ? null,
escapeHost ? true,
port ? null,
path ? null,
query ? { },
}:
let
nullToEmpty = val: if val == null then "" else toString val;
# Converts a list of URI attrs to a query string.
toQuery = mapAttrsToList (
name: value: if value == null then null else (escapeURL name) + "=" + (escapeURL (toString value))
);
schemePart = if scheme == null then "" else "${escapeURL scheme}://";
userPart =
let
realUser = if escapeUserAndPassword then escapeURL user else user;
realPassword = if escapeUserAndPassword then escapeURL password else password;
in
if user == null && password == null then
""
else if user != null && password != null then
"${realUser}:${realPassword}"
else if user != null then
realUser
else
throw "Either user or username and password must be provided";
hostPart =
let
realHost = if escapeHost then escapeURL (nullToEmpty host) else nullToEmpty host;
in
if userPart == "" then realHost else "@" + realHost;
portPart = if port == null then "" else ":" + (toString port);
pathPart = if path == null then "" else "/" + path;
queryPart = if query == { } then "" else "?" + concatStringsSep "&" (toQuery query);
in
schemePart + userPart + hostPart + portPart + pathPart + queryPart;
database =
let
hostIsPath = hasInfix "/" cfg.database.host;
in
if cfg.database.driver == "postgres" then
if cfg.database.peerAuth then
mkDatabaseUri {
scheme = cfg.database.driver;
inherit (cfg.database) user;
path = escapeURL cfg.database.name;
query = {
host = cfg.database.socketPath;
} // cfg.database.extraConnectionOptions;
}
else
mkDatabaseUri {
scheme = cfg.database.driver;
inherit (cfg.database) user password;
host = if hostIsPath then null else cfg.database.host;
port = if hostIsPath then null else cfg.database.port;
path = escapeURL cfg.database.name;
query =
optionalAttrs hostIsPath { inherit (cfg.database) host; } // cfg.database.extraConnectionOptions;
}
else if cfg.database.driver == "mysql" then
if cfg.database.peerAuth then
mkDatabaseUri {
scheme = null;
inherit (cfg.database) user;
escapeUserAndPassword = false;
host = "unix(${cfg.database.socketPath})";
escapeHost = false;
path = escapeURL cfg.database.name;
query = cfg.database.extraConnectionOptions;
}
else
mkDatabaseUri {
scheme = null;
inherit (cfg.database) user password;
escapeUserAndPassword = false;
host =
if hostIsPath then
"unix(${cfg.database.host})"
else
"tcp(${cfg.database.host}:${toString cfg.database.port})";
escapeHost = false;
path = escapeURL cfg.database.name;
query = cfg.database.extraConnectionOptions;
}
else
throw "Invalid database driver: ${cfg.database.driver}";
mattermostPluginDerivations = map (
plugin:
pkgs.stdenvNoCC.mkDerivation {
name = "${cfg.package.name}-plugin";
installPhase = ''
runHook preInstall
mkdir -p $out/share
ln -sf ${plugin} $out/share/plugin.tar.gz
runHook postInstall
'';
dontUnpack = true;
dontPatch = true;
dontConfigure = true;
dontBuild = true;
preferLocalBuild = true;
}
) cfg.plugins;
mattermostPlugins =
if mattermostPluginDerivations == [ ] then
null
else
pkgs.stdenvNoCC.mkDerivation {
name = "${cfg.package.name}-plugins";
nativeBuildInputs = [ pkgs.autoPatchelfHook ] ++ mattermostPluginDerivations;
buildInputs = [ cfg.package ];
installPhase = ''
runHook preInstall
mkdir -p $out
plugins=(${
escapeShellArgs (map (plugin: "${plugin}/share/plugin.tar.gz") mattermostPluginDerivations)
})
for plugin in "''${plugins[@]}"; do
hash="$(sha256sum "$plugin" | awk '{print $1}')"
mkdir -p "$hash"
tar -C "$hash" -xzf "$plugin"
autoPatchelf "$hash"
GZIP_OPT=-9 tar -C "$hash" -cvzf "$out/$hash.tar.gz" .
rm -rf "$hash"
done
runHook postInstall
'';
dontUnpack = true;
dontPatch = true;
dontConfigure = true;
dontBuild = true;
preferLocalBuild = true;
};
mattermostConfWithoutPlugins = recursiveUpdate {
ServiceSettings = {
SiteURL = cfg.siteUrl;
ListenAddress = "${cfg.host}:${toString cfg.port}";
LocalModeSocketLocation = cfg.socket.path;
EnableLocalMode = cfg.socket.enable;
EnableSecurityFixAlert = cfg.telemetry.enableSecurityAlerts;
};
TeamSettings.SiteName = cfg.siteName;
SqlSettings.DriverName = cfg.database.driver;
SqlSettings.DataSource =
if cfg.database.fromEnvironment then
null
else
warnIf (!cfg.database.peerAuth && cfg.database.password != null) ''
Database password is set in Mattermost config! This password will end up in the Nix store.
You may be able to simply set the following, if the database is on the same host
and peer authentication is enabled:
services.mattermost.database.peerAuth = true;
Note that this is the default if you set system.stateVersion to 25.05 or later
and the database host is localhost.
Alternatively, you can write the following to ${
if cfg.environmentFile == null then "your environment file" else cfg.environmentFile
}:
MM_SQLSETTINGS_DATASOURCE=${database}
Then set the following options:
services.mattermost.environmentFile = "<your environment file>";
services.mattermost.database.fromEnvironment = true;
'' database;
# Note that the plugin tarball directory is not configurable, and is expected to be in FileSettings.Directory/plugins.
FileSettings.Directory = mutableDataDir;
PluginSettings.Directory = "${pluginUnpackDir}/server";
PluginSettings.ClientDirectory = "${pluginUnpackDir}/client";
LogSettings = {
FileLocation = cfg.logDir;
# Reaches out to Mattermost's servers for telemetry; disable it by default.
# https://docs.mattermost.com/configure/environment-configuration-settings.html#enable-diagnostics-and-error-reporting
EnableDiagnostics = cfg.telemetry.enableDiagnostics;
};
} cfg.settings;
mattermostConf = recursiveUpdate mattermostConfWithoutPlugins (
if mattermostPlugins == null then
{ }
else
{
PluginSettings = {
Enable = true;
};
}
);
format = pkgs.formats.json { };
finalConfig = format.generate "mattermost-config.json" mattermostConf;
in
{
disabledModules = [ "services/web-apps/mattermost.nix" ];
imports = [
(mkRenamedOptionModule
[
"services"
"mattermost"
"listenAddress"
]
[
"services"
"mattermost"
"host"
]
)
(mkRenamedOptionModule
[
"services"
"mattermost"
"localDatabaseCreate"
]
[
"services"
"mattermost"
"database"
"create"
]
)
(mkRenamedOptionModule
[
"services"
"mattermost"
"localDatabasePassword"
]
[
"services"
"mattermost"
"database"
"password"
]
)
(mkRenamedOptionModule
[
"services"
"mattermost"
"localDatabaseUser"
]
[
"services"
"mattermost"
"database"
"user"
]
)
(mkRenamedOptionModule
[
"services"
"mattermost"
"localDatabaseName"
]
[
"services"
"mattermost"
"database"
"name"
]
)
(mkRenamedOptionModule
[
"services"
"mattermost"
"extraConfig"
]
[
"services"
"mattermost"
"settings"
]
)
(mkRenamedOptionModule
[
"services"
"mattermost"
"statePath"
]
[
"services"
"mattermost"
"dataDir"
]
)
];
options = {
services.mattermost = {
enable = mkEnableOption "Mattermost chat server";
package = mkPackageOption pkgs "mattermost" { };
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.";
};
host = mkOption {
type = types.str;
default = "127.0.0.1";
example = "0.0.0.0";
description = ''
Host or address that this Mattermost instance listens on.
'';
};
port = mkOption {
type = types.port;
default = 8065;
description = ''
Port for Mattermost server to listen on.
'';
};
dataDir = mkOption {
type = types.path;
default = "/var/lib/mattermost";
description = ''
Mattermost working directory.
'';
};
socket = {
enable = mkEnableOption "Mattermost control socket";
path = mkOption {
type = types.path;
default = "${cfg.dataDir}/mattermost.sock";
defaultText = ''''${config.mattermost.dataDir}/mattermost.sock'';
description = ''
Default location for the Mattermost control socket used by `mmctl`.
'';
};
export = mkEnableOption "Export socket control to system environment variables";
};
logDir = mkOption {
type = types.path;
default =
if versionAtLeast config.system.stateVersion "25.05" then
"/var/log/mattermost"
else
"${cfg.dataDir}/logs";
defaultText = ''
if versionAtLeast config.system.stateVersion "25.05" then "/var/log/mattermost"
else "''${config.services.mattermost.dataDir}/logs";
'';
description = ''
Mattermost log directory.
'';
};
configDir = mkOption {
type = types.path;
default =
if versionAtLeast config.system.stateVersion "25.05" then
"/etc/mattermost"
else
"${cfg.dataDir}/config";
defaultText = ''
if versionAtLeast config.system.stateVersion "25.05" then
"/etc/mattermost"
else
"''${config.services.mattermost.dataDir}/config";
'';
description = ''
Mattermost config directory.
'';
};
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, persistent changes in the system
console won't be possible (the default). If a config.json is
present, it will be overwritten at service start!
'';
};
preferNixConfig = mkOption {
type = types.bool;
default = versionAtLeast config.system.stateVersion "25.05";
defaultText = ''
versionAtLeast config.system.stateVersion "25.05";
'';
description = ''
If both mutableConfig and this option are set, the Nix configuration
will take precedence over any settings configured in the server
console.
'';
};
plugins = mkOption {
type = with types; listOf (either path package);
default = [ ];
example = "[ ./com.github.moussetc.mattermost.plugin.giphy-2.0.0.tar.gz ]";
description = ''
Plugins to add to the configuration. Overrides any installed if non-null.
This is a list of paths to .tar.gz files or derivations evaluating to
.tar.gz files. You can use `mattermost.buildPlugin` to build plugins;
see the NixOS documentation for more details.
'';
};
pluginsBundle = mkOption {
type = with types; nullOr package;
default = mattermostPlugins;
defaultText = ''
All entries in {config}`services.mattermost.plugins`, repacked
'';
description = ''
Derivation building to a directory of plugin tarballs.
This overrides {option}`services.mattermost.plugins` if provided.
'';
};
telemetry = {
enableSecurityAlerts = mkOption {
type = types.bool;
default = true;
description = ''
True if we should enable security update checking. This reaches out to Mattermost's servers:
https://docs.mattermost.com/manage/telemetry.html#security-update-check-feature
'';
};
enableDiagnostics = mkOption {
type = types.bool;
default = false;
description = ''
True if we should enable sending diagnostic data. This reaches out to Mattermost's servers:
https://docs.mattermost.com/manage/telemetry.html#error-and-diagnostics-reporting-feature
'';
};
};
environment = mkOption {
type = with types; attrsOf (either int str);
default = { };
description = ''
Extra environment variables to export to the Mattermost process
from the systemd unit configuration.
'';
example = {
MM_SERVICESETTINGS_SITEURL = "http://example.com";
};
};
environmentFile = mkOption {
type = with types; nullOr path;
default = null;
description = ''
Environment file (see {manpage}`systemd.exec(5)`
"EnvironmentFile=" section for the syntax) which sets config options
for mattermost (see [the Mattermost documentation](https://docs.mattermost.com/configure/configuration-settings.html#environment-variables)).
Settings defined in the environment file will overwrite settings
set via Nix or via the {option}`services.mattermost.extraConfig`
option.
Useful for setting config options without their value ending up in the
(world-readable) Nix store, e.g. for a database password.
'';
};
database = {
driver = mkOption {
type = types.enum [
"postgres"
"mysql"
];
default = "postgres";
description = ''
The database driver to use (Postgres or MySQL).
'';
};
create = mkOption {
type = types.bool;
default = true;
description = ''
Create a local PostgreSQL or MySQL database for Mattermost automatically.
'';
};
peerAuth = mkOption {
type = types.bool;
default = versionAtLeast config.system.stateVersion "25.05" && cfg.database.host == "localhost";
defaultText = ''
versionAtLeast config.system.stateVersion "25.05" && config.services.mattermost.database.host == "localhost"
'';
description = ''
If set, will use peer auth instead of connecting to a Postgres server.
Use services.mattermost.database.socketPath to configure the socket path.
'';
};
socketPath = mkOption {
type = types.path;
default =
if cfg.database.driver == "postgres" then "/run/postgresql" else "/run/mysqld/mysqld.sock";
defaultText = ''
if config.services.mattermost.database.driver == "postgres" then "/run/postgresql" else "/run/mysqld/mysqld.sock";
'';
description = ''
The database (Postgres or MySQL) socket path.
'';
};
fromEnvironment = mkOption {
type = types.bool;
default = false;
description = ''
Use services.mattermost.environmentFile to configure the database instead of writing the database URI
to the Nix store. Useful if you use password authentication with peerAuth set to false.
'';
};
name = mkOption {
type = types.str;
default = "mattermost";
description = ''
Local Mattermost database name.
'';
};
host = mkOption {
type = types.str;
default = "localhost";
example = "127.0.0.1";
description = ''
Host to use for the database. Can also be set to a path if you'd like to connect
to a socket using a username and password.
'';
};
port = mkOption {
type = types.port;
default = if cfg.database.driver == "postgres" then 5432 else 3306;
defaultText = ''
if config.services.mattermost.database.type == "postgres" then 5432 else 3306
'';
example = 3306;
description = ''
Port to use for the database.
'';
};
user = mkOption {
type = types.str;
default = "mattermost";
description = ''
Local Mattermost database username.
'';
};
password = mkOption {
type = types.str;
default = "mmpgsecret";
description = ''
Password for local Mattermost database user. If set and peerAuth is not true,
will cause a warning nagging you to use environmentFile instead since it will
end up in the Nix store.
'';
};
extraConnectionOptions = mkOption {
type = with types; attrsOf (either int str);
default =
if cfg.database.driver == "postgres" then
{
sslmode = "disable";
connect_timeout = 60;
}
else if cfg.database.driver == "mysql" then
{
charset = "utf8mb4,utf8";
writeTimeout = "60s";
readTimeout = "60s";
}
else
throw "Invalid database driver ${cfg.database.driver}";
defaultText = ''
if config.mattermost.database.driver == "postgres" then
{
sslmode = "disable";
connect_timeout = 60;
}
else if config.mattermost.database.driver == "mysql" then
{
charset = "utf8mb4,utf8";
writeTimeout = "60s";
readTimeout = "60s";
}
else
throw "Invalid database driver";
'';
description = ''
Extra options that are placed in the connection URI's query parameters.
'';
};
};
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.
'';
};
settings = mkOption {
inherit (format) type;
default = { };
description = ''
Additional configuration options as Nix attribute set in config.json schema.
'';
};
matterircd = {
enable = mkEnableOption "Mattermost IRC bridge";
package = mkPackageOption pkgs "matterircd" { };
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 = {
${cfg.user} = {
inherit (cfg) group;
uid = mkIf (cfg.user == "mattermost") config.ids.uids.mattermost;
home = cfg.dataDir;
isSystemUser = true;
packages = [ cfg.package ];
};
};
users.groups = {
${cfg.group} = {
gid = mkIf (cfg.group == "mattermost") config.ids.gids.mattermost;
};
};
services.postgresql = mkIf (cfg.database.driver == "postgres" && cfg.database.create) {
enable = true;
ensureDatabases = singleton cfg.database.name;
ensureUsers = singleton {
name =
throwIf
(cfg.database.peerAuth && (cfg.database.user != cfg.user || cfg.database.name != cfg.database.user))
''
Mattermost database peer auth is enabled and the user, database user, or database name mismatch.
Peer authentication will not work.
''
cfg.database.user;
ensureDBOwnership = true;
};
};
services.mysql = mkIf (cfg.database.driver == "mysql" && cfg.database.create) {
enable = true;
package = mkDefault pkgs.mariadb;
ensureDatabases = singleton cfg.database.name;
ensureUsers = singleton {
name = cfg.database.user;
ensurePermissions = {
"${cfg.database.name}.*" = "ALL PRIVILEGES";
};
};
settings = rec {
mysqld = {
collation-server = mkDefault "utf8mb4_general_ci";
init-connect = mkDefault "SET NAMES utf8mb4";
character-set-server = mkDefault "utf8mb4";
};
mysqld_safe = mysqld;
};
};
environment = {
variables = mkIf cfg.socket.export {
MMCTL_LOCAL = "true";
MMCTL_LOCAL_SOCKET_PATH = cfg.socket.path;
};
};
systemd.tmpfiles.rules =
[
"d ${cfg.dataDir} 0750 ${cfg.user} ${cfg.group} - -"
"d ${cfg.logDir} 0750 ${cfg.user} ${cfg.group} - -"
"d ${cfg.configDir} 0750 ${cfg.user} ${cfg.group} - -"
"d ${mutableDataDir} 0750 ${cfg.user} ${cfg.group} - -"
# Make sure tempDir exists and is not a symlink.
"R- ${tempDir} - - - - -"
"d= ${tempDir} 0750 ${cfg.user} ${cfg.group} - -"
# Ensure that pluginUnpackDir is a directory.
# Don't remove or clean it out since it should be persistent, as this is where plugins are unpacked.
"d= ${pluginUnpackDir} 0750 ${cfg.user} ${cfg.group} - -"
# Ensure that the plugin directories exist.
"d= ${mattermostConf.PluginSettings.Directory} 0750 ${cfg.user} ${cfg.group} - -"
"d= ${mattermostConf.PluginSettings.ClientDirectory} 0750 ${cfg.user} ${cfg.group} - -"
# Link in some of the immutable data directories.
"L+ ${cfg.dataDir}/bin - - - - ${cfg.package}/bin"
"L+ ${cfg.dataDir}/fonts - - - - ${cfg.package}/fonts"
"L+ ${cfg.dataDir}/i18n - - - - ${cfg.package}/i18n"
"L+ ${cfg.dataDir}/templates - - - - ${cfg.package}/templates"
"L+ ${cfg.dataDir}/client - - - - ${cfg.package}/client"
]
++ (
if cfg.pluginsBundle == null then
# Create the plugin tarball directory to allow plugin uploads.
[
"d= ${pluginTarballDir} 0750 ${cfg.user} ${cfg.group} - -"
]
else
# Symlink the plugin tarball directory, removing anything existing, since it's managed by Nix.
[ "L+ ${pluginTarballDir} - - - - ${cfg.pluginsBundle}" ]
);
systemd.services.mattermost = rec {
description = "Mattermost chat service";
wantedBy = [ "multi-user.target" ];
after = mkMerge [
[ "network.target" ]
(mkIf (cfg.database.driver == "postgres" && cfg.database.create) [ "postgresql.service" ])
(mkIf (cfg.database.driver == "mysql" && cfg.database.create) [ "mysql.service" ])
];
requires = after;
environment = mkMerge [
{
# Use tempDir as this can get rather large, especially if Mattermost unpacks a large number of plugins.
TMPDIR = tempDir;
}
cfg.environment
];
preStart =
''
dataDir=${escapeShellArg cfg.dataDir}
configDir=${escapeShellArg cfg.configDir}
logDir=${escapeShellArg cfg.logDir}
package=${escapeShellArg cfg.package}
nixConfig=${escapeShellArg finalConfig}
''
+ optionalString (versionAtLeast config.system.stateVersion "25.05") ''
# Migrate configs in the pre-25.05 directory structure.
oldConfig="$dataDir/config/config.json"
newConfig="$configDir/config.json"
if [ "$oldConfig" != "$newConfig" ] && [ -f "$oldConfig" ] && [ ! -f "$newConfig" ]; then
# Migrate the legacy config location to the new config location
echo "Moving legacy config at $oldConfig to $newConfig" >&2
mkdir -p "$configDir"
mv "$oldConfig" "$newConfig"
touch "$configDir/.initial-created"
fi
# Logs too.
oldLogs="$dataDir/logs"
newLogs="$logDir"
if [ "$oldLogs" != "$newLogs" ] && [ -d "$oldLogs" ] && [ ! -f "$newLogs/.initial-created" ]; then
# Migrate the legacy log location to the new log location.
# Allow this to fail if there aren't any logs to move.
echo "Moving legacy logs at $oldLogs to $newLogs" >&2
mkdir -p "$newLogs"
mv "$oldLogs"/* "$newLogs" || true
touch "$newLogs/.initial-created"
fi
''
+ optionalString (!cfg.mutableConfig) ''
${getExe pkgs.jq} -s '.[0] * .[1]' "$package/config/config.json" "$nixConfig" > "$configDir/config.json"
''
+ optionalString cfg.mutableConfig ''
if [ ! -e "$configDir/.initial-created" ]; then
${getExe pkgs.jq} -s '.[0] * .[1]' "$package/config/config.json" "$nixConfig" > "$configDir/config.json"
touch "$configDir/.initial-created"
fi
''
+ optionalString (cfg.mutableConfig && cfg.preferNixConfig) ''
echo "$(${getExe pkgs.jq} -s '.[0] * .[1]' "$configDir/config.json" "$nixConfig")" > "$configDir/config.json"
'';
serviceConfig = mkMerge [
{
User = cfg.user;
Group = cfg.group;
ExecStart = "${getExe cfg.package} --config ${cfg.configDir}/config.json";
ReadWritePaths = [
cfg.dataDir
cfg.logDir
cfg.configDir
];
UMask = "0027";
Restart = "always";
RestartSec = 10;
LimitNOFILE = 49152;
LockPersonality = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateTmp = true;
PrivateUsers = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProtectSystem = "strict";
RestrictNamespaces = true;
RestrictSUIDSGID = true;
EnvironmentFile = cfg.environmentFile;
WorkingDirectory = cfg.dataDir;
}
(mkIf (cfg.dataDir == "/var/lib/mattermost") {
StateDirectory = baseNameOf cfg.dataDir;
StateDirectoryMode = "0750";
})
(mkIf (cfg.logDir == "/var/log/mattermost") {
LogsDirectory = baseNameOf cfg.logDir;
LogsDirectoryMode = "0750";
})
(mkIf (cfg.configDir == "/etc/mattermost") {
ConfigurationDirectory = baseNameOf cfg.configDir;
ConfigurationDirectoryMode = "0750";
})
];
unitConfig.JoinsNamespaceOf = mkMerge [
(mkIf (cfg.database.driver == "postgres" && cfg.database.create) [ "postgresql.service" ])
(mkIf (cfg.database.driver == "mysql" && cfg.database.create) [ "mysql.service" ])
];
};
assertions = [
{
# Make sure the URL doesn't have a trailing slash
assertion = !(hasSuffix "/" cfg.siteUrl);
message = ''
services.mattermost.siteUrl should not have a trailing "/".
'';
}
{
# Make sure this isn't a host/port pair
assertion = !(hasInfix ":" cfg.host && !(hasInfix "[" cfg.host) && !(hasInfix "]" cfg.host));
message = ''
services.mattermost.host should not include a port. Use services.mattermost.host for the address
or hostname, and services.mattermost.port to specify the port separately.
'';
}
];
})
(mkIf cfg.matterircd.enable {
systemd.services.matterircd = {
description = "Mattermost IRC bridge service";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
User = "nobody";
Group = "nogroup";
ExecStart = "${getExe cfg.matterircd.package} ${escapeShellArgs cfg.matterircd.parameters}";
WorkingDirectory = "/tmp";
PrivateTmp = true;
Restart = "always";
RestartSec = "5";
};
};
})
];
meta.maintainers = with lib.maintainers; [ numinit ];
}

View file

@ -11,7 +11,6 @@
./gestiocof.nix
./gestiojeux.nix
./interludes.nix
./vector.nix
./wikiens.nix
];

View file

@ -1,48 +0,0 @@
# SPDX-FileCopyrightText: 2025 Tom Hubrecht <tom.hubrecht@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
{ config, ... }:
{
services.django-apps.sites.vector = {
source = "https://git.dgnum.eu/DGNum/vector";
branch = "main";
domain = "photos.dgnum.eu";
nginx = {
enableACME = true;
forceSSL = true;
};
webHookSecret = config.age.secrets."webhook-vector_token".path;
overlays.nix-pkgs = [
# Required packages
"authens"
"django-browser-reload"
"django-bulma-forms"
"django-unfold"
"loadcredential"
# Dependencies
"python-cas"
];
dependencies = ps: [
ps.authens
ps.django
ps.django-browser-reload
ps.django-bulma-forms
ps.django-import-export
ps.django-sesame
ps.django-unfold
ps.loadcredential
ps.pillow
];
credentials = {
SECRET_KEY = config.age.secrets."dj_vector-secret_key_file".path;
};
};
}

View file

@ -1,34 +0,0 @@
age-encryption.org/v1
-> ssh-ed25519 ZIo4kw U9Awj0lxgjMUgaAzQL2WzYiMgfD+86sKsvNnbd7g1lY
ss6ZQIANobWXxHjTykQE3nZqXJ+i+x3x6f7kAzWNbh0
-> ssh-ed25519 9/PCvA Yzdb3ff9BW1AAL5JqprCiyAByqsaSmyyUt5DVFq1IBw
UxTfF4o9wSk3+XKKTNuWGSLMOPYrgA+ipumR5AzlNgk
-> ssh-ed25519 prNEsA VkEG8GR5wWU+V3Dne9UtNK8lOPnMy7lbh9ea+kiTLik
gY8l0Uj3ayMkPIVuMHG+BGvm+BYCR1n3HOEcAZWVLWs
-> ssh-ed25519 jIXfPA mzhgIxTRN7ZtlRuZTkz0P/Zdl/QVetQHylH6KgL5fQE
6dJo93aqlNqC6Kno8958r6+c552CPCW0sPD+sf9x678
-> ssh-ed25519 QlRB9Q 8GZjCubDXSt/uNfchgoY4nC+nmq7h0mHH5PzaVwS8Gw
WfCIpHHSL/ZzCrCGonXzov5aiEG8nEabRYc61Sot6W0
-> ssh-ed25519 r+nK/Q dxq/i0WDDxRd2o2RapDZUTmink7pQpWbEp+6TByjYSY
c/Z2Y2jXeAYZJ3ltI3XEb2qU2dM9WuBCfn4DmdLWuoc
-> ssh-rsa krWCLQ
DyMp39BeLJYmgfgyeVqcAUKWVSmqXj7Z24bu4JJ1TSjO9jWB61gp7K3rR/ascaZ3
E+TkzK4DOUs81anTI1A6nUr3a5/PONU8y1M78OBRhaUkgA0EawUYWCvk7ITTri9N
5U9m7yYXVjFImbPDmTuS496KEqqrYKF9hMU54MzAVjl7Glz3mw2sJmD3oyWX3lx+
XgzS4O/tmL7WxsaHqzAnojr32aJH52Es0VNZRplCff4sgZl+1U+RPpi4kLk8E1+p
QnXDyUEYpkJ4OlslT7+WrURumZhEfBhvDtJUIkILc650lJcxeO5kSgw9s46RmEwW
tFtkMsG+M+sPqcgiqdpp1A
-> ssh-ed25519 /vwQcQ C8NLKCeZRYT1hFAX7QWjaPXnlN8iiMXeSQEmdPMWhx0
gHagG9v72kxa9LIs4aIV5CZsq60i62s4HkDwh6ostew
-> ssh-ed25519 0R97PA /9uUwdq1jdah4pJsFBm8Lubc3bkpBtqDh2VCDZegJDo
WD17gOJkv4INdNUIX0erAjNOeeWQZCQ1rXdc68KFLbw
-> ssh-ed25519 JGx7Ng GpCSAEFl4Y/0pNYWXX5+iMfRqPIbJ2cTS6YPjNLiJW0
KZNU1H+MVNUb5aJMkEurBPDd9h9b023CqK0TLTrd4y4
-> ssh-ed25519 bUjjig GIZfUJbejtNHD7ezMM/KsNetxwLBYGHJ3ql5tn6sYwY
U9jkfmRTM1WXvExskdponMb9/kTwzeZYA3CBUBq5PI8
-> ssh-ed25519 VQSaNw YUZzVKlzWxTgDUZyKToDR3fvMSGDPCQL5hOD/fyW8lI
S5IJ5E325zHj3VBq6jcQmZmhXgLDAceGMBrRUP0po5g
-> ,`:sK>-grease
1Jkp/UVsxUfBFXRGT1ADI97FkgaK/jTSj+Gu/mv3Kjhk3kCHCwrN2emrILvK
--- 6tPIZn536nfhB/4+hiKitxMpYKdj42N+Xxw7gz+cCuQ
¾Ú‡u™ŒhŠhŠ%Å$<24>¯^þrÒ3Üëi¯ð8§qK§½9‡ˆd®Ø&±<>X4ŠØ'lœ%ršCƒ™„€“R”Ÿß=®ëƒ_ïì>XÙilO

View file

@ -27,7 +27,6 @@
"dj_gestiojeux-secret_key_file"
"dj_interludes-email_host_password_file"
"dj_interludes-secret_key_file"
"dj_vector-secret_key_file"
"dj_wikiens-secret_key_file"
"webhook-annuaire_token"
"webhook-bocal_token"
@ -36,6 +35,5 @@
"webhook-gestiocof_token"
"webhook-gestiojeux_token"
"webhook-interludes_token"
"webhook-vector_token"
"webhook-wikiens_token"
]

View file

@ -1,36 +0,0 @@
age-encryption.org/v1
-> ssh-ed25519 ZIo4kw nixtliB448Qelkkhdy4Njr4V5gGJtlzR5z2mdjxENhs
529gFsnjUhI/yFnphje0jhoszNrRoc1OmV1tc8QgTVk
-> ssh-ed25519 9/PCvA eFSvkx9G++qeJWsAusXUz+V5IyVoW3kRuhL8M6LITSs
Die/10zkmScdj5wmWKlVHtbIZ3n7Tw5O9xvmLrNittM
-> ssh-ed25519 prNEsA +GGlW+gSP7WEvvjBNpNhzG8FSV3cVLu6A/2IbD51Wzo
wTNAoE5Wk/ZzhuKPLuBbE40rHpuXqOFuTE2fn1Nw1kQ
-> ssh-ed25519 jIXfPA qROvOffeaatJSsqQ+bNgXcZc0tlZDhNawcUsybu0YQo
GgsBWLQuFbL4CghDOxbyaJ4dMJtuWV5IJWxr/aNDE5I
-> ssh-ed25519 QlRB9Q K07kQAeVxZlkp4RFRPgAbX8ny5bqB9klZEGUyLaUtzg
Oe7IAxrU+4onN8V6M178yTbXPPqkh++JSB+WKEXocQ8
-> ssh-ed25519 r+nK/Q RFCuFtv3kdHjFHT4PcbVFfLUdD3kUBoO1XSfqB4ae2Q
39RVcTVbte9KNt3vEHE0aEcSLJyhASqlNp+/+AFkrB0
-> ssh-rsa krWCLQ
LEFifhwKhjyaqSRtNpwVv9PTKmMmkz0tQLccZLTMQ4suKo3+YzLpBNrpIoi3xvuZ
75Xv/MtcemPoVX5qBgJwkxZyV5roAtOpvRo+iTBiQ7awgFpiWcn+cILOyr3a/LER
Xu92Wiotzv9WFJMsZd1DfeOvNl1YkxD2QqRdd6jF13vt8NwuogEY2GzgS/PerKi1
YJ0ntGhecsy7n13kapA9VsTt8/e9dywZ/ZZIkmDG7ma0tqYnn1VzQTbZXjPMTl04
kZ7Rg+u1C0Q1mCzSnJJtpbmDj0M8OyqHfEjg1DoWR3uBizH3BOjQjbnLPhnp0mL3
cInvK02DW7JGazJDsuJblw
-> ssh-ed25519 /vwQcQ bRu/3avtfAEqsAzlOdM/fjVoUaOPoJuD2lbvovojoz8
7CO/CcO1kDXTrSjzy1z4KhcVnLwhOZEuMN+LySUaeyI
-> ssh-ed25519 0R97PA Hb/RO8mCBX445wyyAXSnilOzE6HMhTGuw0Urz2Y6YVc
BdZ98IodA3Tp5k0Tau+Iv8qrVf/CJRfYseicHyP1fCs
-> ssh-ed25519 JGx7Ng gdEebhCXIuHzA3bDWltgnRo2XvGHszVScTfNXGBVX3A
X5ZLnUcX9cjIrvpqw2jEveLcljE/idPD0BfX5V7QVwE
-> ssh-ed25519 bUjjig f/pw/bL37yroIaiv4y9EqJqAVwh06vC9VcaMQPW40Qw
LScPAIupupgpR1OL4oqG87pRYKSctQip3rHnnjBrCGg
-> ssh-ed25519 VQSaNw BxWJoLzSaZwbO+XU0Hd61t2JaukDmcAzQphYKaAtWBI
4RoGyHkU926BomYSLj6KquJ74OwMeVYQeBAV84wu57c
-> R?\kyN-grease gO`2zD 7eWdp ,_h
ZkiJSlbbGvaHYu0mUfMyW+hOZNDTZ4Fvql+0hsSgaJ9h5sGLMijNzinflWaUg2JE
NTcZv5Xy77Y+j1Ch
--- TrMQG6BDUx1tuCujCZM2u/g5DhfpSv6MVKtR+zC1S+w
ÝÔ¾TVs]“VBYZ)iÁ®Ù‰HˆeþRîêòÂ.(&²
N'3¡¢œD-Íè‰_V¯î

View file

@ -1,26 +0,0 @@
# SPDX-FileCopyrightText: 2024 Tom Hubrecht <tom.hubrecht@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
{ lib, ... }:
lib.extra.mkConfig {
enabledModules = [
# List of modules to enable
"dgn-web"
];
enabledServices = [
# List of services to enable
];
extraConfig = {
# Disable monitoring
dgn-monitoring.enable = false;
dgn-records.enable = false;
dgn-notify.enable = false;
dgn-backups.jobs = lib.mkForce { };
};
root = ./.;
}

View file

@ -1,33 +0,0 @@
# SPDX-FileCopyrightText: 2024 Tom Hubrecht <tom.hubrecht@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ modulesPath, sources, ... }:
{
imports = [
(modulesPath + "/profiles/qemu-guest.nix")
(sources.disko + "/module.nix")
./disko.nix
];
boot = {
initrd = {
availableKernelModules = [
"ata_piix"
"uhci_hcd"
"ehci_pci"
"virtio_pci"
"sr_mod"
"virtio_blk"
];
kernelModules = [ ];
};
kernelModules = [ "kvm-intel" ];
extraModulePackages = [ ];
};
}

View file

@ -1,37 +0,0 @@
# SPDX-FileCopyrightText: 2024 Maurice Debray <maurice.debray@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
_: {
disko.devices = {
disk = {
main = {
device = "/dev/sda";
type = "disk";
content = {
type = "gpt";
partitions = {
ESP = {
type = "EF00";
size = "1G";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}

View file

@ -1,5 +0,0 @@
# SPDX-FileCopyrightText: 2024 La Délégation Générale Numérique <contact@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
(import ../../../../keys.nix).mkSecrets [ "zulip01" ] [ ]

View file

@ -12,7 +12,6 @@ let
mapAttrs'
nameValuePair
optional
optionalAttrs
;
inherit (lib.extra) fuseAttrs mapSingleFuse;
@ -29,7 +28,6 @@ let
dual ? [ ],
v4 ? [ ],
v6 ? [ ],
proxied ? [ ],
}:
let
base = "${server}.${meta.nodes.${server}.site}.infra";
@ -39,7 +37,6 @@ let
(mkHost base dual)
(mkHost "v4.${base}" v4)
(mkHost "v6.${base}" v6)
(mkHost "proxied.${base}" proxied)
];
cnames = builtins.mapAttrs (_: to: { CNAME = [ to ]; }) {
@ -170,17 +167,10 @@ let
];
web03.dual = [
"photos" # Vector
# Django Apps
"*.webapps"
"apps-webhook"
];
zulip01.proxied = [
"zulip"
"z"
];
}
)
);
@ -259,21 +249,23 @@ in
{ site, ... }:
let
net = meta.network.${host};
inherit (net.addresses) A AAAA;
f =
x:
if x == [ ] then
[ ]
else if net.singleIpRecord then
[ (builtins.head x) ]
else
x;
inherit (net.addresses) ipv4 ipv6;
in
nameValuePair "${host}.${site}" {
inherit A AAAA;
A = f ipv4;
AAAA = f ipv6;
subdomains = {
v4 = { inherit A; };
v6 = { inherit AAAA; };
v4.A = f ipv4;
v6.AAAA = f ipv6;
private.A = optional (net.netbirdIp != null) net.netbirdIp;
proxied = optionalAttrs (net.proxy != null) {
# NOTE: We assume that we want to proxy ipv4 to an ipv6-only node
# This might change in the future but is not planned yet.
inherit (meta.network.${net.proxy}.addresses) A;
inherit AAAA;
};
};
}
) (filterAttrs (_: { nixpkgs, ... }: nixpkgs.system == "nixos") meta.nodes);

View file

@ -18,12 +18,12 @@
hostId = "f57f3ba0";
interfaces = { };
netbirdIp = "100.80.9.42";
netbirdIp = null;
};
build01 = {
interfaces = {
enp35s0f1np1 = {
enp35s0f0np0 = {
ipv4 = [
{
address = "10.0.254.21";
@ -31,19 +31,6 @@
}
];
gateways = [ "10.0.254.1" ];
enableDefaultDNS = true;
};
enp66s0f1np1 = {
ipv4 = [
{
address = "10.0.254.21";
prefixLength = 24;
}
];
metric = 500;
gateways = [ "10.0.254.1" ];
enableDefaultDNS = true;
};
@ -154,20 +141,6 @@
gateways = [ "10.0.254.1" ];
enableDefaultDNS = true;
};
eno1 = {
ipv4 = [
{
address = "10.0.254.11";
prefixLength = 24;
}
];
metric = 500;
gateways = [ "10.0.254.1" ];
enableDefaultDNS = true;
};
};
hostId = "4dbbd76a";
@ -187,20 +160,6 @@
gateways = [ "10.0.254.1" ];
enableDefaultDNS = true;
};
eno1 = {
ipv4 = [
{
address = "10.0.254.12";
prefixLength = 24;
}
];
metric = 500;
gateways = [ "10.0.254.1" ];
enableDefaultDNS = true;
};
};
hostId = "d0b48483";
@ -220,20 +179,6 @@
gateways = [ "10.0.254.1" ];
enableDefaultDNS = true;
};
enp66s0f0 = {
ipv4 = [
{
address = "10.0.254.13";
prefixLength = 24;
}
];
metric = 500;
gateways = [ "10.0.254.1" ];
enableDefaultDNS = true;
};
};
hostId = "1c407ea8";
@ -425,27 +370,5 @@
netbirdIp = "100.80.157.46";
singleIpRecord = true;
};
zulip01 = {
interfaces = {
ens18 = {
ipv6 = [
{
address = "2a0e:e701:1120:1000::dead:beef";
prefixLength = 64;
}
];
gateways = [ "2a0e:e701:1120:1000::1" ];
dns = [ "2a0e:e701:1120:1000::f:1" ];
};
};
hostId = "b551861d";
netbirdIp = null; # zulip01 is not to be connected on the VPN for now
# This node does not have ipv4 connectivity
proxy = "lab-router01";
};
};
}

View file

@ -7,6 +7,7 @@
versions = [
# Supported nixpkgs versions
"unstable"
"22.11"
"23.11"
"24.05"
"24.11"

View file

@ -1,61 +1,106 @@
# SPDX-FileCopyrightText: 2024 Lubin Bailly <lubin.bailly@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
{ lib, ... }:
let
inherit (lib) mapAttrs;
{
nodes = {
netcore00 = {
site = "pot01";
hashedPassword = "$2b$05$3h26h8rJ5yks1vJYXZG.fuHagYBwrVMlGR7iFmsV1FTJlMUj97fl2";
mkSwitch =
{
site,
ip,
entry,
}:
{
inherit site hashedPassword;
hashedPassword = "$6$BKetIIfT$JVyE0B7F4O.fJwQFu5jVrVExAZROrEMLW5HkDkhjMShJ9cRIgxSm2VM9OThDowsnLmAewqDN7eAY.EQt4UR4U0";
stateVersion = null;
adminGroups = [ "fai" ];
deployment = {
targetHost = ip;
targetHost = "fd26:baf9:d250:8000::1010";
sshOptions = [
"-J"
entry
"root@vault01.hyp01.infra.dgnum.eu"
"-p"
"830"
];
};
nixpkgs = {
version = "unstable";
version = "24.05"; # FIXME: meaningless
system = "netconf";
};
};
in
{
nodes = mapAttrs (_: mkSwitch) {
netcore01 = {
site = "pot01";
ip = "fd26:baf9:d250:8000::1010";
entry = "root@vault01.hyp01.infra.dgnum.eu";
hashedPassword = "$6$BKetIIfT$JVyE0B7F4O.fJwQFu5jVrVExAZROrEMLW5HkDkhjMShJ9cRIgxSm2VM9OThDowsnLmAewqDN7eAY.EQt4UR4U0";
stateVersion = null;
adminGroups = [ "fai" ];
deployment = {
targetHost = "fd26:baf9:d250:8000::100f";
sshOptions = [
"-J"
"root@vault01.hyp01.infra.dgnum.eu"
"-p"
"830"
];
};
nixpkgs = {
version = "24.05"; # FIXME: meaningless
system = "netconf";
};
};
netcore02 = {
site = "pot01";
ip = "fd26:baf9:d250:8000::100f";
entry = "root@vault01.hyp01.infra.dgnum.eu";
};
Jaccess01 = {
site = "hyp01";
ip = "fd26:baf9:d250:8000::1001";
entry = "root@vault01.hyp01.infra.dgnum.eu";
hashedPassword = "$6$BKetIIfT$JVyE0B7F4O.fJwQFu5jVrVExAZROrEMLW5HkDkhjMShJ9cRIgxSm2VM9OThDowsnLmAewqDN7eAY.EQt4UR4U0";
stateVersion = null;
adminGroups = [ "fai" ];
deployment = {
targetHost = "fd26:baf9:d250:8000::1001";
sshOptions = [
"-J"
"root@vault01.hyp01.infra.dgnum.eu"
"-p"
"830"
];
};
nixpkgs = {
version = "24.05"; # FIXME: meaningless
system = "netconf";
};
};
Jaccess04 = {
netaccess01 = {
site = "hyp02";
ip = "fd26:baf9:d250:8000::2001";
entry = "root@vault01.hyp01.infra.dgnum.eu";
hashedPassword = "$6$BKetIIfT$JVyE0B7F4O.fJwQFu5jVrVExAZROrEMLW5HkDkhjMShJ9cRIgxSm2VM9OThDowsnLmAewqDN7eAY.EQt4UR4U0";
stateVersion = null;
adminGroups = [ "fai" ];
deployment = {
targetHost = "fd26:baf9:d250:8000::2001";
sshOptions = [
"-J"
"root@vault01.hyp01.infra.dgnum.eu"
"-p"
"830"
];
};
nixpkgs = {
version = "24.05"; # FIXME: meaningless
system = "netconf";
};
};
};
}

View file

@ -58,10 +58,16 @@
adminGroups = [ "fai" ];
deployment.targetHost = "bridge01.dgnum";
deployment = {
targetHost = "fd26:baf9:d250:8000::ffff";
sshOptions = [
"-J"
"root@vault01.hyp01.infra.dgnum.eu"
];
};
nixpkgs = {
version = "25.05";
version = "24.11";
system = "nixos";
};
};
@ -331,7 +337,7 @@
stateVersion = "23.11";
nixpkgs = {
version = "25.05";
version = "24.11";
system = "nixos";
};
@ -389,20 +395,5 @@
admins = [ "catvayor" ];
};
zulip01 = {
site = "pav01";
hashedPassword = "$y$j9T$7NuClEAftCG0O7AA0KLK10$/ZLXV73tiZVMXFdgKfa4yVeYk.Qdea6uIgQTrtWHIbA";
sshKeys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDnYjoGAfJGeMAds7RoqtqRJlfm2f6aBbb9SIPNizIof" ];
stateVersion = "24.11";
nixpkgs = {
version = "unstable";
system = "nixos";
};
};
};
}

View file

@ -21,7 +21,6 @@ let
attrs
attrsOf
bool
int
enum
ints
listOf
@ -375,14 +374,6 @@ in
'';
};
metric = mkOption {
type = int;
default = 1000;
description = ''
Metric of routes created by this interface.
'';
};
DHCP = mkOption {
type = nullOr str;
default = null;
@ -412,50 +403,22 @@ in
);
};
addresses =
let
select = b: x: if (b && x != [ ]) then [ (builtins.head x) ] else x;
in
{
ipv4 = mkOption {
type = listOf str;
default = [ ];
description = ''
List of public ipv4 addresses of the node.
'';
};
ipv6 = mkOption {
type = listOf str;
default = [ ];
description = ''
List of public ipv6 addresses of the node.
'';
};
A = mkOption {
type = listOf str;
default = select config.singleIpRecord config.addresses.ipv4;
description = ''
List of ipv4 addresses used for the A record.
'';
};
AAAA = mkOption {
type = listOf str;
default = select config.singleIpRecord config.addresses.ipv6;
description = ''
List of ipv6 addresses used for the AAAA record.
'';
};
addresses = {
ipv4 = mkOption {
type = listOf str;
default = [ ];
description = ''
List of public ipv4 addresses of the node.
'';
};
proxy = mkOption {
type = nullOr str;
default = null;
description = ''
If not `null`, then a SNI proxy will be created to passthrough ipv4 traffic to this node via ipv6.
'';
ipv6 = mkOption {
type = listOf str;
default = [ ];
description = ''
List of public ipv6 addresses of the node.
'';
};
};
hostId = mkOption {

View file

@ -23,14 +23,6 @@ in
options.dgn-isp = {
enable = mkEnableOption "Common isp configuration";
core-links = mkOption {
type = listOf str;
default = [ ];
description = ''
Interfaces of link between switches
'';
};
AP = mkOption {
type = listOf str;
default = [ ];
@ -77,18 +69,6 @@ in
dgn-interfaces."irb".inet6.addresses = [ cfg.admin-ip ];
dgn-profiles = {
core = {
interfaces = cfg.core-links;
configuration.ethernet-switching = {
interface-mode = "trunk";
vlans = [
"users"
"ap-staging"
"admin-ap"
"admin-core"
];
};
};
AP = {
interfaces = cfg.AP;
configuration = {

View file

@ -1,39 +1,62 @@
age-encryption.org/v1
-> ssh-ed25519 ZIo4kw 4Dvbj86lTQ6xC73sI8eEU6GyGo/Qzv3Htq3jZCzaWSA
5sbbvnKk6Ef6zacjMV2EK9optOKVUIqfk/IAuFd3M5o
-> ssh-ed25519 9/PCvA b5eEu4H7gu2Jxta0J9pVUJjCBqy3sa9fl4/Glwxatwk
JAcfuZyUGyEHsTrOpfcYKGlmoc6WHLQdJCm6mVc7Bq4
-> ssh-ed25519 prNEsA DC648/P8gUL0kTjuXdM34wT+lo4wkTwtk0CghxWumjU
SZaiXJXPjL+gYkPn5mVlw7vQ4otKVSKX5VIdkgsgURA
-> ssh-ed25519 jIXfPA an1P3n+ZiKaUOYMZCcWJXwkmL8ZzehP2of1DunzlugE
w0tyvbo/Dkl9ovbi2jbveuu/9fQ6JYox3711Ke+PL7c
-> ssh-ed25519 QlRB9Q iyFxHQBFLBVKMv2Uv97p2cQCizTOSrQDYMctdoafmXk
nMemf/k6qdrY91waHLxG7L4AuZCZWexPsC0pUI/dKdc
-> ssh-ed25519 r+nK/Q NRJzo86V7hd9M9mLkZS/SFv+3wyzQUIAxSop1t0NIk4
kB+sIcPPx80o276zRVwOJ1kZlrmZRagAS+J2K9pIs54
-> ssh-ed25519 jIXfPA IBDWE9Xn6wliGbftDIGWXHa7HKUMNefpFqPbyM1hGyE
g3RFQAd1fr04JDJWNOiA5Az+Th5y4KQsMoZtDUTChII
-> ssh-ed25519 QlRB9Q wUwww3D9NqymfeRaiTlPqSQs+d4jgeU5et7t5Bh16G0
vQMO+HmuvKUouppp1fPSVzF88WWnWtTfHm+c6WJeJNs
-> ssh-ed25519 r+nK/Q CyIT2nwckqhLB5WojCJOzKw+62qejILHOyUI91IJMyc
pStkO3bVXBwsoadaJ+FyjwY/SxNneaLjGreqlGXy7MI
-> ssh-rsa krWCLQ
Vu4hzL2lpYaPlT0M5Dq7i2sYZQiHtc0wx/+Ol7yX6imT+36ZYGWOr5etTWNsG5H1
r4POK1efijLJDcDXdG9H0aIRKYk0LIWu8Lc8mXQicS4sI/DAuHcwd4yEHoiqBroB
HrIEtpctjszOUwcORPPnVeuxrc7qV7dZHQOTFb37m5wB1mOWQPf9+aEZ8Pa9N3HB
MYyCiZdkjnB+GV2CRdpg3Nrn5MBYzTk6qKc/4Dt97fSjnZp7mEAUu3MwJN6xwZvt
edoxH7qH/pyjn6L4F3BOzoXi0OGp7UhZrWdSm9rpP9bQoSDl2xPlfyQltAx6F5L/
rWCxDh1tMzKTkjfLdA5yYg
-> ssh-ed25519 /vwQcQ ed+mwY1AiYksn4D74JVHIHCXdujK+T4M5fqRertiXDE
dZ27Pab3C8OesdLbHREAkQs76fk2+HQFsC5vrqHenU8
-> ssh-ed25519 0R97PA /SJIhnQqyJ30Igm3lD58ASvHDDVVHvGOVBCQPHXDyn4
N4Q7XsCTpgVDEl9BC5D+W559VZGzRBmZT7zdHH2TxVw
-> ssh-ed25519 JGx7Ng kOpYTaE79ToAfe21Y4Rj6OzarTviMcZrr/XPeORY/Gs
pTJv9SEeCj/c7WoXsS9/C7Ur0GObvNXQN82P2Vpv/ks
-> ssh-ed25519 bUjjig R+nYaUJc2bnGlu7MfwH2l4UZPCyp//JA6XIcTPQ76Vw
GkXas4s/L+cPmw9/gFi/vlfEGDvOJ4bHxpNmHx1lIXE
-> ssh-ed25519 rHotTw vR1ALL0WuY0iD3XlIbgTXDw+BlHaeqWvo0xLRfg4PA8
SzI7LrCzE14oHrnVfgUWr01iB6HFDEHDqYw4rnGu8Hs
-> ssh-ed25519 oRtTqQ ZsVQ29JKayWapAJ8Ni3p5XTtHM6V13kPI+9Y+QHPBxs
mAKmtdqpq+VhI2mCIpW8JIC4k2R8MI4ZH73yFuluWAg
-> ssh-ed25519 IxxZqA gIpd8G6cBK+KjQXbVgDWbtHdtcQhmZbruODpFyQajzk
Gi1rQGYakrkDDTlGobnv4bQhrJdWb1jF54W2jBcNPY8
-> oa~]37NU-grease &> o~hy` 5>|
Brm0NbNjpmLUrPn/REprmi00Gdsu+1VX5Oqt7jrguA4ushkDL9KbhhmLfaE1Bih4
wwtPwaAnWoYGhLEFQ/Z4IMhZ
--- KFSjNpmZ/nktxjdYS+2Cg/RztxprVrb5VRpOol5vHtM
dH<EFBFBD> -üä¯mÒ¾<C392>§ÂË <09>B'Û9š ²³Y*™1J&:§Ï¦Ü ­,g~ëiÍEÜŠ <0B>¶² àM¡zôR[)Ifûóˆ²ñu—
xSBrLm6xfBZC2NSVjWOlw1PdAfV4SsrUr6FuLeMa291otjMyz5o/RhxVeU6uEh2F
B6lk7WdEsYWl6lp98ha1bz4udabPJZdMHO1dj0uOx2Qnt5F5P+Dk/dOsZlbylX7n
oKLm7w+P0xKMZyy4VoK0UWuwAwNZ+Rvh0aiqRxWQK+fh6W9pii/MDssCn/cwqsP3
0ehUupmTP1GPWzXufVdvdV3EUuD6wczYMit3XhVpHWs3rh8nCY8WEw2kaF/YJ3kq
QAzT1MGn2Bd9qJxPmnThJPr669cdX0FxRT8tirskBwGD50/AGzkGHmrDxhJ+lA+a
oaAOAH2JuzxVU8eZTNzBaA
-> ssh-ed25519 /vwQcQ wnnNXgdxTHBfy1m9Q85dE6qiQ1SGk40NSXEKFhJhoUg
VRpUiwOot1uuTyp9zoBdoHVH/Sk/ZQvXZ/OlJrFLJNw
-> ssh-ed25519 0R97PA 3NnOUAVFDA6jGIImHHaY5neOksXHY5W3SRvQjSMHIhE
0fpC7PKc2Ih/ZaUamsiXMya+cRHFo47pu+/+VPPh1mY
-> ssh-ed25519 JGx7Ng wjLJcV9WaZzaXL9G4+gWLrJwlQHpu2wNauNGCKHlcjE
KXtlcgvCx4IjWKZYcdrc/+ptN89TYdztcKx3D8SOGJQ
-> ssh-ed25519 bUjjig 1anXdq+bgCUIgtwdb3r107avA454AmvzCZ/9Gg6KJRY
jZ+57yUb5S2o4jYIfle2Cosnr244lyT6OovC6pki3aM
-> ssh-ed25519 DqHxWQ IRL5LMBRyHEuerrJLXwmFLn60w9kPLFfdwJ+B98B4ys
+WLwLTWwswxa6q/okI+h8lMjAk8qEiOT/eODA8K6o4Q
-> ssh-ed25519 IxxZqA susvyhJg3BFg25/w0u8HlbyuuUVBUkKt4OYdDyrs3SY
kShLE/m+WjpHFe9rp1u1myVVT92wTSX9/LbBCBBEDGM
-> ssh-ed25519 /fsvPA 8v8ltxt9mHugEPxI6+oIrVhV5z63QExUl2FF81hvoDY
3UyQnrt9ZUQgoFWa+sOhUyfW8ayE1hT6iJktRUqsPCs
-> ssh-ed25519 tDqJRg 0TyPeokIZ2VEJIefa771p/mgPKXNn9N1gFfJZNzJ5g4
9c+48/+JkSNjIpbdLS1gMqCJsVR++aTd2fAtq+Vf5mU
-> ssh-ed25519 9pVK7Q zGCbrGgD9+ZJWRjOmxXG+NTK3sXaRtVa5nFUd16dkgY
5KplC/lMmYROpQXez04Fek7csTAKhCDTFMvi1gTkZLg
-> ssh-ed25519 /BRpBQ BaEwBZBU4V86HQJL//Y7yZgNiIsVe14NrgHqXNdVIjI
o23RVqBoMazA5FcvvrxTsO/8YY6v/Fi2fe0vHCuMXP8
-> ssh-ed25519 t0vvHQ 3ry3IKDCwvHlya5gRl1WvWWg9eyvzoscLFIkSS+0W0M
ARJQYM8UOxL4MKYeVTm4iuqRmKKt5WVA7vyHxBeSbFE
-> ssh-ed25519 E6cGqw l8mjNJC/Rv8H8RAJJ6YPFh1giAELtq6YwOg9NGfpPEI
3LZG3xs9yWKzDiIfXK5WuT5DBoKRRZXvMW4wrvD0Yz4
-> ssh-ed25519 EEPmeQ vST8LKLp2Z5KqAVyGRedZ0UYrvTeBOh6VktNovlh+BE
h67yS6SCGgDAbaVZFGPJWSTZjyKihR4lU2WI0ddtPS0
-> ssh-ed25519 /x+F2Q MGL5T94LGLWkGMDHWgsYSklh9bNNS5wQ4UsHd2DhIAw
wN9w+g0v4Ai9/LLBp0AVFPxGlYb9TFvFMherfQ1HAII
-> ssh-ed25519 +MNHsw MmeQYG3LMRYshE1iW8c0aK+4ClW4cdmL0IRNOG4b0xE
H172Nrg6evAabW0tiPkbmtqM0pJd2V+AXqbcrqizO50
-> ssh-ed25519 rHotTw jAf0aGFc8O4Q6kFT/bvDvMgsng+P7McydHsf3XGXOU4
hVU16P3uqnBXN0VM1k0D/yUB/4qXPJHlucZYaEeotu0
-> ssh-ed25519 NaIdrw RDQ5Pt+Q3pDI7jNEFuOJj6FhyQ+rOs7tjW7aZ6juvU8
l84vMl7TqapmfSpM0n7OqSGPzpxckppENDVsH1UfUOk
-> ssh-ed25519 +mFdtQ TCPW7Vv185tMyBUD1kKUYiLCjs2BODC6/yk1bnCNLAw
uCRPJ7/vD5IsGiclvSDUccqghHpJafw6bOnuCd1hSTU
-> ssh-ed25519 0IVRbA zQ1t9aOeOZsoyG5PYe/qmQhz5zTwRaZOH1w747gcOV8
/6kyxLF7RhGo5csn1FRzutUur5dEe95L8OJ1xuUGbp0
-> ssh-ed25519 IY5FSQ fg14TlFknPaqzzhvOBj1xUPK+SXdVwE1kVrqsXguQhQ
JM2jK91iWXF4NoOimeFgYBRr5Y51C1qKGW/bQh1I2wk
-> ssh-ed25519 VQSaNw KRupcX4ff19rhTEE7T7OYbX5R7N8FN/6dKZOw1BfRCc
v5K8+gyqx8v5qp48hFCblLWdmm1dTjqr8Tnjz/PsLgs
-> )-grease >`MLFR
AzmLKWjXTSMRbDzD9U0wWgH8Llfigdh3Ks2EAHWp3Rrbb1lyfsjnTBkPGTz3oaKx
Ucpzmu+rcVhNfb7S9RGnJXAm1bcdFGj+fipRUsLqcfxtG8+7Gy9WyZkgxKdJqRcn
--- BcXdoymLtYCqMH5BALUywcLlG1MVXoAmxH4/p4BBxgw
|êamxòD[¾±OЦ*;%`³å®uŒ4eƹjÜü¸g•$Vi´LGï×~½µY®¬@žÊiƒÚ¬GÈã§Äy®äòSAÇm·<6D>)

View file

@ -1,8 +1,5 @@
# SPDX-FileCopyrightText: 2024 La Délégation Générale Numérique <contact@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
(import ../../../keys.nix).mkSecrets
[ "storage01" "build01" ]
[
"forgejo_runners-token_file"
]
{ forgejo_runners-token_file.publicKeys = (import ../../../keys.nix).machineKeysBySystem "nixos"; }

View file

@ -17,8 +17,8 @@ let
net' = meta.network.${name};
mkAddress = { address, prefixLength, ... }: "${address}/${builtins.toString prefixLength}";
mkRoute = Metric: Gateway: {
inherit Gateway Metric;
mkRoute = Gateway: {
inherit Gateway;
GatewayOnLink = true;
};
@ -27,7 +27,7 @@ let
value = {
name = interface;
address = builtins.map mkAddress (net.ipv4 ++ net.ipv6);
routes = builtins.map (mkRoute net.metric) net.gateways;
routes = builtins.map mkRoute net.gateways;
inherit (net) DHCP dns;
};

View file

@ -74,9 +74,9 @@
},
"branch": "main",
"submodules": false,
"revision": "fbf6385e65400802a3f9f75f7cd91d5c01373d1b",
"revision": "904eb7058b9a61250fbfcb0b0bfa71e214bf1067",
"url": null,
"hash": "sha256-aOUI69wbMm9+KVWwcMw5TgVnk3DfjOzE4OEyYTD8XPU="
"hash": "sha256-tDqte7MuOn0Gcj0a94m//wcjJvssnEZprtt5o/IpXlU="
},
"disko": {
"type": "GitRelease",
@ -143,9 +143,9 @@
},
"branch": "master",
"submodules": false,
"revision": "ee58919c0a34a478820dda72e756f5db45a068f4",
"revision": "2df4e901590ebd139364d1df140a6ccb2cd0a5a7",
"url": null,
"hash": "sha256-9mnQk8mcRlV3wOcJkXU/fIUSLUSj9Jg8HrqzD8/hx+w="
"hash": "sha256-W4pGBZs1+iPnuios88kbqb2ITdLalYq65G8IWGm+EKY="
},
"liminix": {
"type": "Git",
@ -290,8 +290,8 @@
"nixos-25.05": {
"type": "Channel",
"name": "nixos-25.05",
"url": "https://releases.nixos.org/nixos/25.05/nixos-25.05.803579.70c74b02eac4/nixexprs.tar.xz",
"hash": "sha256-0RxtgAd4gHYPFFwICal8k8hvJBOkCeTjFkh4HsqYDbE="
"url": "https://releases.nixos.org/nixos/25.05/nixos-25.05beta801800.ca49c4304acf/nixexprs.tar.xz",
"hash": "sha256-O+9sQ6QEoKcM/lJXDumDdUZbuxs2TMuBf7xi3ivOXCo="
},
"nixos-unstable": {
"type": "Channel",

View file

@ -1,639 +0,0 @@
From f49797f5a589b88e6ad938038e53570fdaa37fa8 Mon Sep 17 00:00:00 2001
From: Tom Hubrecht <tom.hubrecht@dgnum.eu>
Date: Mon, 26 May 2025 13:47:07 +0200
Subject: [PATCH] Revert "add aliases, refactor eval logic"
This reverts commit 3ef5ba8658f68564ed268828ccee39a363200188.
---
src/error.rs | 14 +-
src/nix/hive/mod.rs | 94 +++----------
src/nix/hive/options.nix | 9 --
src/nix/mod.rs | 2 -
src/nix/node_filter.rs | 290 ++++++++++++++-------------------------
5 files changed, 122 insertions(+), 287 deletions(-)
diff --git a/src/error.rs b/src/error.rs
index cbc41ba..f0fbe11 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -6,7 +6,7 @@ use std::process::ExitStatus;
use snafu::{Backtrace, Snafu};
use validator::ValidationErrors;
-use crate::nix::{key, NodeName, Profile, StorePath};
+use crate::nix::{key, Profile, StorePath};
pub type ColmenaResult<T> = Result<T, ColmenaError>;
@@ -76,18 +76,6 @@ pub enum ColmenaError {
#[snafu(display("Filter rule cannot be empty"))]
EmptyFilterRule,
- #[snafu(display(
- "Alias \"{}\" is already taken by {} \"{}\"",
- what.as_str(),
- if *is_node_name { "node name" } else { "alias" },
- with.as_str(),
- ))]
- DuplicateAlias {
- what: NodeName,
- is_node_name: bool,
- with: NodeName,
- },
-
#[snafu(display("Deployment already executed"))]
DeploymentAlreadyExecuted,
diff --git a/src/nix/hive/mod.rs b/src/nix/hive/mod.rs
index 8ebf6de..63ae08e 100644
--- a/src/nix/hive/mod.rs
+++ b/src/nix/hive/mod.rs
@@ -3,7 +3,7 @@ mod assets;
#[cfg(test)]
mod tests;
-use std::collections::{HashMap, HashSet};
+use std::collections::HashMap;
use std::convert::AsRef;
use std::path::{Path, PathBuf};
use std::str::FromStr;
@@ -14,7 +14,6 @@ use tokio::sync::OnceCell;
use validator::Validate;
use super::deployment::TargetNode;
-use super::node_filter::{NeedsEval, PartialNodeConfig};
use super::{
Flake, MetaConfig, NixExpression, NixFlags, NodeConfig, NodeFilter, NodeName,
ProfileDerivation, RegistryConfig, SerializedNixExpression, StorePath,
@@ -262,66 +261,39 @@ impl Hive {
ssh_config: Option<PathBuf>,
ssh_only: bool,
) -> ColmenaResult<HashMap<NodeName, TargetNode>> {
+ let mut node_configs = None;
+
log::info!("Enumerating systems...");
let registry = self.get_registry_config().await?;
log::info!("Enumerating nodes...");
+
let all_nodes = self.node_names().await?;
-
- // try to quickly evaluate the filter without any data to see if it's trivial to evaluate
- let filter_trivial = filter.as_ref().and_then(|filter| filter.try_eval_trivial());
-
let selected_nodes = match filter {
- Some(filter) if filter_trivial.is_none() => {
- log::debug!("Retrieving deployment info for all nodes...");
+ Some(filter) => {
+ if filter.has_node_config_rules() {
+ log::debug!("Retrieving deployment info for all nodes...");
- let needs_eval = filter.needs_eval();
+ let all_node_configs = self.deployment_info().await?;
+ let filtered = filter
+ .filter_node_configs(all_node_configs.iter())
+ .into_iter()
+ .collect();
- let all_node_configs = self.deployment_info_partial(needs_eval).await?;
+ node_configs = Some(all_node_configs);
- // Check for collisions between node names and aliases
- // Returns error if:
- // - A node has an alias matching another node's name
- // - A node has an alias matching its own name
- // - A node has an alias already used by another node
- if needs_eval.aliases {
- let mut taken_aliases = HashSet::new();
- for (name, config) in all_node_configs.iter() {
- for alias in config.aliases.as_ref().unwrap().iter() {
- let overlaps_this = alias == name;
- let overlaps_names = all_node_configs.contains_key(alias);
- let overlaps_aliases = !taken_aliases.insert(alias.clone());
- if overlaps_this || overlaps_names || overlaps_aliases {
- return Err(ColmenaError::DuplicateAlias {
- what: alias.clone(),
- is_node_name: overlaps_this || overlaps_names,
- with: name.clone(),
- });
- }
- }
- }
+ filtered
+ } else {
+ filter.filter_node_names(&all_nodes)?.into_iter().collect()
}
-
- let filtered = filter
- .filter_nodes(all_node_configs.iter())
- .into_iter()
- .collect();
-
- filtered
}
- _ => match filter_trivial {
- // Filter is known to always evaluate to no nodes
- Some(false) => vec![],
- _ => all_nodes.clone(),
- },
+ None => all_nodes.clone(),
};
let n_selected = selected_nodes.len();
- log::debug!("Filtered {n_selected} node names for deployment");
- let mut node_configs = if n_selected == all_nodes.len() {
- log::debug!("Retrieving deployment info for all nodes...");
- self.deployment_info().await?
+ let mut node_configs = if let Some(configs) = node_configs {
+ configs.into_iter().filter(|(name, _)| selected_nodes.contains(name)).collect()
} else {
log::debug!("Retrieving deployment info for selected nodes...");
self.deployment_info_selected(&selected_nodes).await?
@@ -425,34 +397,6 @@ impl Hive {
Ok(configs)
}
- pub async fn deployment_info_partial(
- &self,
- needs_eval: NeedsEval,
- ) -> ColmenaResult<HashMap<NodeName, PartialNodeConfig>> {
- if !needs_eval.any() {
- // Need just the un-aliased names
- return Ok(self
- .node_names()
- .await?
- .into_iter()
- .map(|name| (name, PartialNodeConfig::default()))
- .collect());
- }
-
- let expr = format!(
- "(mapAttrs (name: attrs: {{ inherit (attrs) {} {}; }}) hive.deploymentConfig)",
- needs_eval.aliases.then_some("aliases").unwrap_or_default(),
- needs_eval.tags.then_some("tags").unwrap_or_default(),
- );
- let configs: HashMap<NodeName, PartialNodeConfig> = self
- .nix_instantiate(&expr)
- .eval_with_builders()
- .await?
- .capture_json()
- .await?;
- Ok(configs)
- }
-
/// Retrieve deployment info for a single node.
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
pub async fn deployment_info_single(
diff --git a/src/nix/hive/options.nix b/src/nix/hive/options.nix
index 0d13642..0db53c5 100644
--- a/src/nix/hive/options.nix
+++ b/src/nix/hive/options.nix
@@ -179,15 +179,6 @@ with builtins; rec {
type = types.listOf types.str;
default = [];
};
- aliases = lib.mkOption {
- description = ''
- A list of aliases for the node.
-
- Can be used to select a node with another name.
- '';
- type = types.listOf types.str;
- default = [];
- };
keys = lib.mkOption {
description = ''
A set of secrets to be deployed to the node.
diff --git a/src/nix/mod.rs b/src/nix/mod.rs
index 6728270..4823f74 100644
--- a/src/nix/mod.rs
+++ b/src/nix/mod.rs
@@ -75,8 +75,6 @@ pub struct NodeConfig {
tags: Vec<String>,
- aliases: Vec<NodeName>,
-
#[serde(rename = "replaceUnknownProfiles")]
replace_unknown_profiles: bool,
diff --git a/src/nix/node_filter.rs b/src/nix/node_filter.rs
index 434fb95..886ad50 100644
--- a/src/nix/node_filter.rs
+++ b/src/nix/node_filter.rs
@@ -7,7 +7,6 @@ use std::str::FromStr;
use clap::Args;
use glob::Pattern as GlobPattern;
-use serde::Deserialize;
use super::{ColmenaError, ColmenaResult, NodeConfig, NodeName};
@@ -29,53 +28,6 @@ The list is comma-separated and globs are supported. To match tags, prepend the
pub on: Option<NodeFilter>,
}
-/// Which fields need to be evaluated
-/// in order to execute the node filter.
-#[derive(Clone, Copy, Debug, Default)]
-pub struct NeedsEval {
- /// Need to evaluate deployment.aliases of all nodes.
- pub aliases: bool,
- /// Need to evaluate deployment.tags of all nodes.
- pub tags: bool,
-}
-
-impl NeedsEval {
- pub fn any(&self) -> bool {
- self.aliases || self.tags
- }
-}
-
-impl std::ops::BitOr for NeedsEval {
- type Output = Self;
- fn bitor(self, rhs: Self) -> Self::Output {
- Self {
- aliases: self.aliases || rhs.aliases,
- tags: self.tags || rhs.tags,
- }
- }
-}
-
-impl std::ops::BitOrAssign for NeedsEval {
- fn bitor_assign(&mut self, rhs: Self) {
- *self = *self | rhs;
- }
-}
-
-#[derive(Debug, Default, Deserialize)]
-pub struct PartialNodeConfig {
- pub aliases: Option<Vec<NodeName>>,
- pub tags: Option<Vec<String>>,
-}
-
-impl From<NodeConfig> for PartialNodeConfig {
- fn from(node_config: NodeConfig) -> Self {
- Self {
- aliases: Some(node_config.aliases),
- tags: Some(node_config.tags),
- }
- }
-}
-
/// A filter rule.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum NodeFilter {
@@ -288,47 +240,30 @@ impl NodeFilter {
}
}
- /// Returns which NodeConfig information is needed to evaluate the filter.
+ /// Returns whether the filter has any rule matching NodeConfig information.
///
/// Evaluating `config.deployment` can potentially be very expensive,
/// especially when its values (e.g., tags) depend on other parts of
/// the configuration.
- pub fn needs_eval(&self) -> NeedsEval {
- // XXX: is the hashset overkill?
+ pub fn has_node_config_rules(&self) -> bool {
match self {
- Self::MatchName(_) => NeedsEval {
- aliases: true,
- ..Default::default()
- },
- Self::MatchTag(_) => NeedsEval {
- tags: true,
- ..Default::default()
- },
- Self::Union(v) | Self::Inter(v) => v
- .iter()
- .fold(NeedsEval::default(), |acc, e| acc | e.needs_eval()),
- Self::Not(e) => e.needs_eval(),
- Self::Empty => NeedsEval::default(),
+ Self::MatchName(_) => false,
+ Self::MatchTag(_) => true,
+ Self::Union(v) => v.iter().any(|e| e.has_node_config_rules()),
+ Self::Inter(v) => v.iter().any(|e| e.has_node_config_rules()),
+ Self::Not(e) => e.has_node_config_rules(),
+ Self::Empty => false,
}
}
/// Decides whether a node is accepted by the filter or not.
- /// panic if the filter depends on tags or aliases and they're None
- fn is_accepted(&self, name: &NodeName, config: &PartialNodeConfig) -> bool {
+ /// panic if the filter depends on tags and config is None
+ fn is_accepted(&self, name: &NodeName, config: Option<&NodeConfig>) -> bool {
match self {
- Self::MatchName(pat) => {
- pat.matches(name.as_str())
- || config
- .aliases
- .as_ref()
- .expect("aliases missing")
- .iter()
- .any(|alias| pat.matches(&alias.0))
- }
+ Self::MatchName(pat) => pat.matches(name.as_str()),
Self::MatchTag(pat) => config
- .tags
- .as_ref()
- .expect("tags missing")
+ .unwrap()
+ .tags()
.iter()
.any(|tag| pat.matches(tag.as_str())),
Self::Union(v) => v.iter().any(|e| e.is_accepted(name, config)),
@@ -339,17 +274,17 @@ impl NodeFilter {
}
/// Runs the filter against a set of NodeConfigs and returns the matched ones.
- pub fn filter_nodes<'a, I>(&self, nodes: I) -> HashSet<NodeName>
+ pub fn filter_node_configs<'a, I>(&self, nodes: I) -> HashSet<NodeName>
where
- I: Iterator<Item = (&'a NodeName, &'a PartialNodeConfig)>,
+ I: Iterator<Item = (&'a NodeName, &'a NodeConfig)>,
{
if self == &Self::Empty {
return HashSet::new();
}
nodes
- .filter_map(|(name, config)| {
- if self.is_accepted(name, config) {
+ .filter_map(|(name, node)| {
+ if self.is_accepted(name, Some(node)) {
Some(name)
} else {
None
@@ -359,34 +294,26 @@ impl NodeFilter {
.collect()
}
- /// In case of trivial filters which dont actually use any node info
- /// Try to eval them immediately
- pub fn try_eval_trivial(&self) -> Option<bool> {
- match self {
- Self::MatchName(_) => None,
- Self::MatchTag(_) => None,
- Self::Union(fs) => {
- for f in fs {
- match f.try_eval_trivial() {
- None => return None,
- Some(true) => return Some(true),
- Some(false) => continue,
+ /// Runs the filter against a set of node names and returns the matched ones.
+ pub fn filter_node_names(&self, nodes: &[NodeName]) -> ColmenaResult<HashSet<NodeName>> {
+ if self.has_node_config_rules() {
+ Err(ColmenaError::Unknown {
+ message: format!(
+ "Not enough information to run rule {:?} - We only have node names",
+ self
+ ),
+ })
+ } else {
+ Ok(nodes
+ .iter()
+ .filter_map(|name| {
+ if self.is_accepted(name, None) {
+ Some(name.clone())
+ } else {
+ None
}
- }
- Some(false)
- }
- Self::Inter(fs) => {
- for f in fs {
- match f.try_eval_trivial() {
- None => return None,
- Some(true) => continue,
- Some(false) => return Some(false),
- }
- }
- Some(true)
- }
- Self::Not(f) => f.try_eval_trivial().map(|b| !b),
- Self::Empty => Some(true),
+ })
+ .collect())
}
}
}
@@ -395,36 +322,6 @@ impl NodeFilter {
mod tests {
use super::*;
- impl PartialNodeConfig {
- fn known_empty() -> Self {
- Self {
- aliases: Some(vec![]),
- tags: Some(vec![]),
- }
- }
-
- pub fn known_aliases_tags(
- aliases: Option<Vec<NodeName>>,
- tags: Option<Vec<String>>,
- ) -> Self {
- Self { aliases, tags }
- }
-
- fn known_tags(tags: Vec<String>) -> Self {
- Self {
- aliases: Some(vec![]),
- tags: Some(tags),
- }
- }
-
- fn known_aliases(aliases: Vec<NodeName>) -> Self {
- Self {
- aliases: Some(aliases),
- tags: Some(vec![]),
- }
- }
- }
-
use std::collections::{HashMap, HashSet};
macro_rules! node {
@@ -527,109 +424,126 @@ mod tests {
}
#[test]
- fn test_filter_nodes_names_only() {
- let nodes = vec![
- (node!("lax-alpha"), PartialNodeConfig::known_empty()),
- (node!("lax-beta"), PartialNodeConfig::known_empty()),
- (node!("sfo-gamma"), PartialNodeConfig::known_empty()),
- ];
+ fn test_filter_node_names() {
+ let nodes = vec![node!("lax-alpha"), node!("lax-beta"), node!("sfo-gamma")];
assert_eq!(
&HashSet::from_iter([node!("lax-alpha")]),
&NodeFilter::new("lax-alpha")
.unwrap()
- .filter_nodes(nodes.iter().map(|x| (&x.0, &x.1))),
+ .filter_node_names(&nodes)
+ .unwrap(),
);
assert_eq!(
&HashSet::from_iter([node!("lax-alpha"), node!("lax-beta")]),
&NodeFilter::new("lax-*")
.unwrap()
- .filter_nodes(nodes.iter().map(|x| (&x.0, &x.1))),
+ .filter_node_names(&nodes)
+ .unwrap(),
);
}
#[test]
- fn test_filter_nodes() {
- let nodes: HashMap<NodeName, PartialNodeConfig> = HashMap::from([
- (
- node!("alpha"),
- PartialNodeConfig::known_tags(vec!["web".to_string(), "infra-lax".to_string()]),
- ),
- (
- node!("beta"),
- PartialNodeConfig::known_tags(vec!["router".to_string(), "infra-sfo".to_string()]),
- ),
- (
- node!("gamma-a"),
- PartialNodeConfig::known_tags(vec!["controller".to_string()]),
- ),
- (
- node!("gamma-b"),
- PartialNodeConfig::known_tags(vec!["ewaste".to_string()]),
- ),
- (
- node!("aliases-test"),
- PartialNodeConfig::known_aliases_tags(
- Some(vec![node!("whatever-alias1"), node!("whatever-alias2")]),
- Some(vec!["testing".into()]),
- ),
- ),
- ]);
- assert_eq!(5, nodes.len());
+ fn test_filter_node_configs() {
+ // TODO: Better way to mock
+ let template = NodeConfig {
+ tags: vec![],
+ target_host: None,
+ target_user: None,
+ target_port: None,
+ allow_local_deployment: false,
+ build_on_target: false,
+ replace_unknown_profiles: false,
+ privilege_escalation_command: vec![],
+ extra_ssh_options: vec![],
+ keys: HashMap::new(),
+ system_type: None,
+ };
+
+ let mut nodes = HashMap::new();
+
+ nodes.insert(
+ node!("alpha"),
+ NodeConfig {
+ tags: vec!["web".to_string(), "infra-lax".to_string()],
+ ..template.clone()
+ },
+ );
+
+ nodes.insert(
+ node!("beta"),
+ NodeConfig {
+ tags: vec!["router".to_string(), "infra-sfo".to_string()],
+ ..template.clone()
+ },
+ );
+
+ nodes.insert(
+ node!("gamma-a"),
+ NodeConfig {
+ tags: vec!["controller".to_string()],
+ ..template.clone()
+ },
+ );
+
+ nodes.insert(
+ node!("gamma-b"),
+ NodeConfig {
+ tags: vec!["ewaste".to_string()],
+ ..template
+ },
+ );
+
+ assert_eq!(4, nodes.len());
assert_eq!(
&HashSet::from_iter([node!("alpha")]),
- &NodeFilter::new("@web").unwrap().filter_nodes(nodes.iter()),
+ &NodeFilter::new("@web")
+ .unwrap()
+ .filter_node_configs(nodes.iter()),
);
assert_eq!(
&HashSet::from_iter([node!("alpha"), node!("beta")]),
&NodeFilter::new("@infra-*")
.unwrap()
- .filter_nodes(nodes.iter()),
+ .filter_node_configs(nodes.iter()),
);
assert_eq!(
&HashSet::from_iter([node!("beta"), node!("gamma-a")]),
&NodeFilter::new("@router,@controller")
.unwrap()
- .filter_nodes(nodes.iter()),
+ .filter_node_configs(nodes.iter()),
);
assert_eq!(
&HashSet::from_iter([node!("beta"), node!("gamma-a"), node!("gamma-b")]),
&NodeFilter::new("@router,gamma-*")
.unwrap()
- .filter_nodes(nodes.iter()),
+ .filter_node_configs(nodes.iter()),
);
assert_eq!(
&HashSet::from_iter([]),
&NodeFilter::new("@router&@controller")
.unwrap()
- .filter_nodes(nodes.iter()),
+ .filter_node_configs(nodes.iter()),
);
assert_eq!(
&HashSet::from_iter([node!("beta")]),
&NodeFilter::new("@router&@infra-*")
.unwrap()
- .filter_nodes(nodes.iter()),
+ .filter_node_configs(nodes.iter()),
);
assert_eq!(
&HashSet::from_iter([node!("alpha")]),
&NodeFilter::new("!@router&@infra-*")
.unwrap()
- .filter_nodes(nodes.iter()),
- );
-
- assert_eq!(
- &HashSet::from_iter([node!("aliases-test")]),
- &NodeFilter::new("whatever-alias1")
- .unwrap()
- .filter_nodes(nodes.iter()),
+ .filter_node_configs(nodes.iter()),
);
}
}
--
2.49.0

View file

@ -12,10 +12,6 @@ with {
};
{
colmena = [
(local ./colmena/0001-Revert-add-aliases-refactor-eval-logic.patch)
];
lix = [
(local ./lix/01-disable-installChecks.patch)
(local ./lix/02-fetchGit-locked.patch)
@ -48,9 +44,6 @@ with {
"nixos-unstable" = [
# Build netbird-relay
(local ./nixpkgs/05-netbird-relay.patch)
# netbird-dashboard: 2.9.0 -> 2.12.0
(npr 403844 "sha256-oQUc/UEvWOdZ5IyemnZeFS5dVysblqdV9fm6t790Kms=")
];
"agenix" = [