From d54eff344fc50ee5a0668aa003f5d8cb29fdbf0a Mon Sep 17 00:00:00 2001 From: CroneKorkN Date: Mon, 30 Jun 2025 09:56:08 +0200 Subject: [PATCH 1/4] routeros wip --- bundles/routeros/README.md | 8 ++ bundles/routeros/items.py | 117 ++++++++++++++++++ bundles/routeros/metadata.py | 26 ++++ groups/os/routeros.py | 31 +++++ ...-switch-10g.py => home.switch-rack-10g.py} | 0 nodes/home.switch-vorratsraum-poe.py | 17 +++ nodes/home.switch-wohnzimmer-10g.py | 30 +++++ nodes/home.wohnzimmer-switch-10g.py | 10 -- 8 files changed, 229 insertions(+), 10 deletions(-) create mode 100644 bundles/routeros/README.md create mode 100644 bundles/routeros/metadata.py rename nodes/{home.rack-switch-10g.py => home.switch-rack-10g.py} (100%) create mode 100644 nodes/home.switch-vorratsraum-poe.py create mode 100644 nodes/home.switch-wohnzimmer-10g.py delete mode 100644 nodes/home.wohnzimmer-switch-10g.py diff --git a/bundles/routeros/README.md b/bundles/routeros/README.md new file mode 100644 index 0000000..8119b90 --- /dev/null +++ b/bundles/routeros/README.md @@ -0,0 +1,8 @@ +- reset (hold reset for 5-10 seconds, until user light starts flashing) +- open webinterface under 192.168.88.1 +- set password +- vlans need to be configured and an additional ip needs to be assined to a vlan which es later accessible preferably through an untagged port +- for example add 10.0.0.62/24 to "home" vlan +- this happens on the first apply +- when vlan filering gets enabled, the apply freezes and the switch is no longer available under the old ip +- now that filtering is active, the switch is available under its new ip, because now you dont speak to the bridge anymore, where the old ip was residing, but to the vlan interface, where the new ip is residing \ No newline at end of file diff --git a/bundles/routeros/items.py b/bundles/routeros/items.py index 57bd4bf..b9c3ef1 100644 --- a/bundles/routeros/items.py +++ b/bundles/routeros/items.py @@ -1,3 +1,120 @@ +routeros['/ip/dns'] = { + 'servers': '8.8.8.8', +} + routeros['/system/identity'] = { 'name': node.name, } + +# for service in ( +# 'api-ssl', # slow :( +# 'ftp', # we can download files via HTTP +# 'telnet', +# 'www-ssl', # slow :( +# 'winbox', +# ): +# routeros[f'/ip/service?name={service}'] = { +# 'disabled': True, +# } + +# LOGGING_TOPICS = ( +# 'critical', +# 'error', +# 'info', +# 'stp', +# 'warning', +# ) +# for topic in LOGGING_TOPICS: +# routeros[f'/system/logging?action=memory&topics={topic}'] = {} + +# routeros['/snmp'] = { +# 'enabled': True, +# } +# routeros['/snmp/community?name=public'] = { +# 'addresses': '0.0.0.0/0', +# 'disabled': False, +# 'read-access': True, +# 'write-access': False, +# } + +# routeros['/system/clock'] = { +# 'time-zone-autodetect': False, +# 'time-zone-name': 'UTC', +# } + +# routeros['/ip/neighbor/discovery-settings'] = { +# 'protocol': 'cdp,lldp,mndp', +# } + +# routeros['/ip/route?dst-address=0.0.0.0/0'] = { +# 'gateway': node.metadata.get('routeros/gateway'), +# } + +for vlan_name, vlan_id in node.metadata.get('routeros/vlans').items(): + routeros[f'/interface/vlan?name={vlan_name}'] = { + 'vlan-id': vlan_id, + 'interface': 'bridge', + 'tags': { + 'routeros-vlan', + }, + 'needs': { + #'routeros:/interface/bridge?name=bridge', + }, + } + + routeros[f"/interface/bridge/vlan?vlan-ids={vlan_id}"] = { + 'bridge': 'bridge', + 'untagged': sorted(node.metadata.get(f'routeros/vlan_ports/{vlan_name}/untagged')), + 'tagged': sorted(node.metadata.get(f'routeros/vlan_ports/{vlan_name}/tagged')), + '_comment': vlan_name, + 'tags': {'routeros-bridge-vlan'}, + 'needs': { + #'routeros:/interface/bridge?name=bridge', + 'tag:routeros-vlan', + }, + } + +# create IPs +for ip, ip_conf in node.metadata.get('routeros/ips').items(): + routeros[f'/ip/address?address={ip}'] = { + 'interface': ip_conf['interface'], + 'tags': { + 'routeros-ip', + }, + 'needs': { + 'tag:routeros-vlan', + }, + } + +routeros['/interface/bridge?name=bridge'] = { + 'vlan-filtering': True, # ENABLE AFTER PORT VLANS ARE SET UP + 'igmp-snooping': False, + 'priority': node.metadata.get('routeros/bridge_priority'), + 'protocol-mode': 'rstp', + 'needs': { + 'tag:routeros-vlan', + 'tag:routeros-ip', + }, +} + +# purge unused vlans +routeros['/interface/vlan'] = { + 'purge': { + 'id-by': 'name', + }, + 'needed_by': { + 'tag:routeros-vlan', + } +} + +routeros['/interface/bridge/vlan'] = { + 'purge': { + 'id-by': 'vlan-ids', + 'keep': { + 'dynamic': True, + }, + }, + 'needed_by': { + 'tag:routeros-vlan', + } +} diff --git a/bundles/routeros/metadata.py b/bundles/routeros/metadata.py new file mode 100644 index 0000000..8b91d2f --- /dev/null +++ b/bundles/routeros/metadata.py @@ -0,0 +1,26 @@ +defaults = {} + + +@metadata_reactor.provides( + 'routeros/vlan_ports', +) +def routeros__(metadata): + return { + 'routeros': { + 'vlan_ports': { + vlan_name: { + 'untagged': { + port_name + for port_name, port_conf in metadata.get('routeros/ports').items() + if vlan_name == metadata.get(f'routeros/vlan_groups/{port_conf["vlan_group"]}/untagged') + }, + 'tagged': { + port_name + for port_name, port_conf in metadata.get('routeros/ports').items() + if vlan_name in metadata.get(f'routeros/vlan_groups/{port_conf["vlan_group"]}/tagged') + }, + } + for vlan_name in metadata.get('routeros/vlans').keys() + }, + }, + } diff --git a/groups/os/routeros.py b/groups/os/routeros.py index 1dc5448..c037f73 100644 --- a/groups/os/routeros.py +++ b/groups/os/routeros.py @@ -9,6 +9,37 @@ 'routeros', ], 'metadata': { + 'routeros': { + 'gateway': '10.0.0.1', + 'bridge_priority': '0x8000', + 'ports': {}, + 'vlans': { + 'home': '1', + 'iot': '2', + 'internet': '3', + 'proxmox': '4', + 'gast': '9', + 'rolf': '51', + }, + 'vlan_groups': { + 'infra': { + 'untagged': 'home', + 'tagged': { + 'iot', + 'internet', + 'proxmox', + 'gast', + 'rolf', + }, + }, + }, + 'vlan_ports': {}, + 'ips': { + '10.0.0.62/24': { + 'interface': 'home', + }, + }, + }, }, 'os': 'routeros', } diff --git a/nodes/home.rack-switch-10g.py b/nodes/home.switch-rack-10g.py similarity index 100% rename from nodes/home.rack-switch-10g.py rename to nodes/home.switch-rack-10g.py diff --git a/nodes/home.switch-vorratsraum-poe.py b/nodes/home.switch-vorratsraum-poe.py new file mode 100644 index 0000000..6eb1efe --- /dev/null +++ b/nodes/home.switch-vorratsraum-poe.py @@ -0,0 +1,17 @@ +{ + 'hostname': '10.0.0.60', + 'password': '!decrypt:encrypt$gAAAAABoYVzxzO0R_bnW3S3Ggiq2LCCAGaKtXToviGZjgIlH2NpL9ojO8aNlSPPcGTKbn5z5RxSxjOlL161U0Ctdf6Rns2e5I5p5TIcsQ7c9qnAiaV-Hhuw=', + 'groups': [ + 'routeros', + ], + 'metadata': { + 'id': 'e6a24df7-eed1-404e-af78-15ebcbcc02a2', + 'routeros': { + 'ports': { + 'ether1': { + 'vlan_group': 'infra', + }, + }, + }, + }, +} diff --git a/nodes/home.switch-wohnzimmer-10g.py b/nodes/home.switch-wohnzimmer-10g.py new file mode 100644 index 0000000..3da9d95 --- /dev/null +++ b/nodes/home.switch-wohnzimmer-10g.py @@ -0,0 +1,30 @@ +{ + #'hostname': '192.168.88.1', + 'hostname': '10.0.0.62', + 'password': '!decrypt:encrypt$gAAAAABoYFSyt2JAsdePXiHim1RdQwbarJedhAOE3XpS2rGMBx-F5eCWRCIyLU2g2ocUDUIDfgH3nBipUCkdcd0Bv4vbK-yqKmGSeSH7YXLYwq3ZWuCDsLM=', + 'groups': [ + 'routeros', + ], + 'metadata': { + 'id': 'e6a24df7-eed1-404e-af78-15ebcbcc02a2', + 'routeros': { + 'ports': { + 'ether1': { + 'vlan_group': 'infra', + }, + 'ether2': { + 'vlan_group': 'infra', + }, + 'ether3': { + 'vlan_group': 'infra', + }, + 'ether4': { + 'vlan_group': 'infra', + }, + 'ether5': { + 'vlan_group': 'infra', + }, + }, + }, + }, +} diff --git a/nodes/home.wohnzimmer-switch-10g.py b/nodes/home.wohnzimmer-switch-10g.py deleted file mode 100644 index f008a78..0000000 --- a/nodes/home.wohnzimmer-switch-10g.py +++ /dev/null @@ -1,10 +0,0 @@ -{ - 'hostname': '10.0.0.62', - 'password': '!decrypt:encrypt$gAAAAABoYFSyt2JAsdePXiHim1RdQwbarJedhAOE3XpS2rGMBx-F5eCWRCIyLU2g2ocUDUIDfgH3nBipUCkdcd0Bv4vbK-yqKmGSeSH7YXLYwq3ZWuCDsLM=', - 'groups': [ - 'routeros', - ], - 'metadata': { - 'id': 'e6a24df7-eed1-404e-af78-15ebcbcc02a2', - }, -} -- 2.39.5 From 0e6a705d3f09f5782af17172680051e178a6f998 Mon Sep 17 00:00:00 2001 From: CroneKorkN Date: Tue, 1 Jul 2025 07:41:04 +0200 Subject: [PATCH 2/4] routeros switches ok --- bundles/proxmox-ve/items.py | 2 +- bundles/routeros/items.py | 5 +- groups/os/routeros.py | 5 -- nodes/home.server.py | 7 ++- nodes/home.switch-rack-10g.py | 36 ++++++++++++ nodes/home.switch-vorratsraum-poe.py | 86 ++++++++++++++++++++++++++++ nodes/home.switch-wohnzimmer-10g.py | 6 +- 7 files changed, 138 insertions(+), 9 deletions(-) diff --git a/bundles/proxmox-ve/items.py b/bundles/proxmox-ve/items.py index d00540f..7be208c 100644 --- a/bundles/proxmox-ve/items.py +++ b/bundles/proxmox-ve/items.py @@ -9,7 +9,7 @@ files = { }, '/etc/apt/apt.conf.d/76pveproxy': { 'content_type': 'any', - 'mode': '0444', + 'mode': '0644', }, '/etc/network/interfaces': { 'content_type': 'any', diff --git a/bundles/routeros/items.py b/bundles/routeros/items.py index b9c3ef1..eb2e279 100644 --- a/bundles/routeros/items.py +++ b/bundles/routeros/items.py @@ -67,7 +67,9 @@ for vlan_name, vlan_id in node.metadata.get('routeros/vlans').items(): 'untagged': sorted(node.metadata.get(f'routeros/vlan_ports/{vlan_name}/untagged')), 'tagged': sorted(node.metadata.get(f'routeros/vlan_ports/{vlan_name}/tagged')), '_comment': vlan_name, - 'tags': {'routeros-bridge-vlan'}, + 'tags': { + 'routeros-vlan-ports', + }, 'needs': { #'routeros:/interface/bridge?name=bridge', 'tag:routeros-vlan', @@ -93,6 +95,7 @@ routeros['/interface/bridge?name=bridge'] = { 'protocol-mode': 'rstp', 'needs': { 'tag:routeros-vlan', + 'tag:routeros-vlan-ports', 'tag:routeros-ip', }, } diff --git a/groups/os/routeros.py b/groups/os/routeros.py index c037f73..87b7035 100644 --- a/groups/os/routeros.py +++ b/groups/os/routeros.py @@ -34,11 +34,6 @@ }, }, 'vlan_ports': {}, - 'ips': { - '10.0.0.62/24': { - 'interface': 'home', - }, - }, }, }, 'os': 'routeros', diff --git a/nodes/home.server.py b/nodes/home.server.py index 4b30c61..23076ee 100644 --- a/nodes/home.server.py +++ b/nodes/home.server.py @@ -38,10 +38,15 @@ 'id': 'af96709e-b13f-4965-a588-ef2cd476437a', 'network': { 'internal': { - 'interface': 'enp42s0', + 'interface': 'enp43s0', 'ipv4': '10.0.0.2/24', 'gateway4': '10.0.0.1', }, + 'old-internal': { + 'interface': 'enp42s0', + 'ipv4': '10.0.0.82/24', + 'gateway4': '10.0.0.1', + }, }, 'apt': { 'packages': { diff --git a/nodes/home.switch-rack-10g.py b/nodes/home.switch-rack-10g.py index a5e7cdb..968b62a 100644 --- a/nodes/home.switch-rack-10g.py +++ b/nodes/home.switch-rack-10g.py @@ -6,5 +6,41 @@ ], 'metadata': { 'id': '26eca3f1-975e-426f-bd7d-e2a1ef36519e', + 'routeros': { + 'ips': { + '10.0.0.63/24': { + 'interface': 'home', + }, + }, + 'ports': { + 'sfp-sfpplus1': { + 'vlan_group': 'infra', + }, + 'sfp-sfpplus2': { + 'vlan_group': 'infra', + }, + 'sfp-sfpplus3': { + 'vlan_group': 'infra', + }, + 'sfp-sfpplus4': { + 'vlan_group': 'infra', + }, + 'sfp-sfpplus5': { + 'vlan_group': 'infra', + }, + 'sfp-sfpplus6': { + 'vlan_group': 'infra', + }, + 'sfp-sfpplus7': { + 'vlan_group': 'infra', + }, + 'sfp-sfpplus8': { + 'vlan_group': 'infra', + }, + 'ether1': { + 'vlan_group': 'infra', + }, + }, + }, }, } diff --git a/nodes/home.switch-vorratsraum-poe.py b/nodes/home.switch-vorratsraum-poe.py index 6eb1efe..346de17 100644 --- a/nodes/home.switch-vorratsraum-poe.py +++ b/nodes/home.switch-vorratsraum-poe.py @@ -7,10 +7,96 @@ 'metadata': { 'id': 'e6a24df7-eed1-404e-af78-15ebcbcc02a2', 'routeros': { + 'ips': { + '10.0.0.60/24': { + 'interface': 'home', + }, + }, 'ports': { + 'sfp-sfpplus1': { + 'vlan_group': 'infra', + }, + 'sfp-sfpplus2': { + 'vlan_group': 'infra', + }, + 'sfp-sfpplus3': { + 'vlan_group': 'infra', + }, + 'sfp-sfpplus4': { + 'vlan_group': 'infra', + }, 'ether1': { 'vlan_group': 'infra', }, + 'ether2': { + 'vlan_group': 'infra', + }, + 'ether3': { + 'vlan_group': 'infra', + }, + 'ether4': { + 'vlan_group': 'infra', + }, + 'ether5': { + 'vlan_group': 'infra', + }, + 'ether6': { + 'vlan_group': 'infra', + }, + 'ether7': { + 'vlan_group': 'infra', + }, + 'ether8': { + 'vlan_group': 'infra', + }, + 'ether9': { + 'vlan_group': 'infra', + }, + 'ether10': { + 'vlan_group': 'infra', + }, + 'ether11': { + 'vlan_group': 'infra', + }, + 'ether12': { + 'vlan_group': 'infra', + }, + 'ether13': { + 'vlan_group': 'infra', + }, + 'ether14': { + 'vlan_group': 'infra', + }, + 'ether15': { + 'vlan_group': 'infra', + }, + 'ether16': { + 'vlan_group': 'infra', + }, + 'ether17': { + 'vlan_group': 'infra', + }, + 'ether18': { + 'vlan_group': 'infra', + }, + 'ether19': { + 'vlan_group': 'infra', + }, + 'ether20': { + 'vlan_group': 'infra', + }, + 'ether21': { + 'vlan_group': 'infra', + }, + 'ether22': { + 'vlan_group': 'infra', + }, + 'ether23': { + 'vlan_group': 'infra', + }, + 'ether24': { + 'vlan_group': 'infra', + }, }, }, }, diff --git a/nodes/home.switch-wohnzimmer-10g.py b/nodes/home.switch-wohnzimmer-10g.py index 3da9d95..e90b213 100644 --- a/nodes/home.switch-wohnzimmer-10g.py +++ b/nodes/home.switch-wohnzimmer-10g.py @@ -1,5 +1,4 @@ { - #'hostname': '192.168.88.1', 'hostname': '10.0.0.62', 'password': '!decrypt:encrypt$gAAAAABoYFSyt2JAsdePXiHim1RdQwbarJedhAOE3XpS2rGMBx-F5eCWRCIyLU2g2ocUDUIDfgH3nBipUCkdcd0Bv4vbK-yqKmGSeSH7YXLYwq3ZWuCDsLM=', 'groups': [ @@ -8,6 +7,11 @@ 'metadata': { 'id': 'e6a24df7-eed1-404e-af78-15ebcbcc02a2', 'routeros': { + 'ips': { + '10.0.0.62/24': { + 'interface': 'home', + }, + }, 'ports': { 'ether1': { 'vlan_group': 'infra', -- 2.39.5 From 460f8094037b769aa36e084eeafddeb6b2059818 Mon Sep 17 00:00:00 2001 From: CroneKorkN Date: Tue, 1 Jul 2025 09:59:47 +0200 Subject: [PATCH 3/4] more routeros --- bundles/routeros/items.py | 3 +-- groups/os/routeros.py | 4 ++++ nodes/home.router.py | 13 ++++--------- nodes/home.switch-vorratsraum-poe.py | 2 +- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/bundles/routeros/items.py b/bundles/routeros/items.py index eb2e279..8c9b0ee 100644 --- a/bundles/routeros/items.py +++ b/bundles/routeros/items.py @@ -62,7 +62,7 @@ for vlan_name, vlan_id in node.metadata.get('routeros/vlans').items(): }, } - routeros[f"/interface/bridge/vlan?vlan-ids={vlan_id}"] = { + routeros[f"/interface/bridge/vlan?vlan-ids={vlan_id}&dynamic=false"] = { 'bridge': 'bridge', 'untagged': sorted(node.metadata.get(f'routeros/vlan_ports/{vlan_name}/untagged')), 'tagged': sorted(node.metadata.get(f'routeros/vlan_ports/{vlan_name}/tagged')), @@ -71,7 +71,6 @@ for vlan_name, vlan_id in node.metadata.get('routeros/vlans').items(): 'routeros-vlan-ports', }, 'needs': { - #'routeros:/interface/bridge?name=bridge', 'tag:routeros-vlan', }, } diff --git a/groups/os/routeros.py b/groups/os/routeros.py index 87b7035..26811c4 100644 --- a/groups/os/routeros.py +++ b/groups/os/routeros.py @@ -32,6 +32,10 @@ 'rolf', }, }, + 'internet': { + 'untagged': 'internet', + 'tagged': set(), + }, }, 'vlan_ports': {}, }, diff --git a/nodes/home.router.py b/nodes/home.router.py index bb9a888..f83ae67 100644 --- a/nodes/home.router.py +++ b/nodes/home.router.py @@ -14,16 +14,10 @@ 'metadata': { 'id': '1d6a43e5-858c-42f9-9c40-ab63d61c787c', 'network': { - 'external': { - 'interface': 'enp2s0', - 'ipv4': '10.0.99.126/24', - 'gateway4': '10.0.99.1', - 'vlans': {'iot', 'internet', 'guest', 'rolf', 'internal', 'proxmox'}, - }, 'internal': { - 'type': 'vlan', - 'id': 1, + 'interface': 'enp1s0f0', 'ipv4': '10.0.0.1/24', + 'vlans': {'iot', 'internet', 'guest', 'rolf', 'proxmox'}, 'dhcp_server': True, }, 'iot': { @@ -35,7 +29,8 @@ 'internet': { 'type': 'vlan', 'id': 3, - 'ipv4': '10.0.3.1/24', + 'ipv4': '10.0.99.126/24', + 'gateway4': '10.0.99.1', }, 'proxmox': { 'type': 'vlan', diff --git a/nodes/home.switch-vorratsraum-poe.py b/nodes/home.switch-vorratsraum-poe.py index 346de17..7e10908 100644 --- a/nodes/home.switch-vorratsraum-poe.py +++ b/nodes/home.switch-vorratsraum-poe.py @@ -35,7 +35,7 @@ 'vlan_group': 'infra', }, 'ether4': { - 'vlan_group': 'infra', + 'vlan_group': 'internet', }, 'ether5': { 'vlan_group': 'infra', -- 2.39.5 From d17f6da77aa1f19cd36a05118af32101b12565b8 Mon Sep 17 00:00:00 2001 From: CroneKorkN Date: Tue, 1 Jul 2025 11:29:44 +0200 Subject: [PATCH 4/4] tidy up and try home dns server --- bundles/bind/files/named.conf.options | 4 ++-- nodes/home.router.py | 16 +++++----------- nodes/home.server.py | 7 +------ 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/bundles/bind/files/named.conf.options b/bundles/bind/files/named.conf.options index a17f44b..64fd17c 100644 --- a/bundles/bind/files/named.conf.options +++ b/bundles/bind/files/named.conf.options @@ -10,7 +10,7 @@ options { % if type == 'master': notify yes; - also-notify { ${' '.join([f'{ip};' for ip in slave_ips])} }; - allow-transfer { ${' '.join([f'{ip};' for ip in slave_ips])} }; + also-notify { ${' '.join(sorted(f'{ip};' for ip in slave_ips))} }; + allow-transfer { ${' '.join(sorted(f'{ip};' for ip in slave_ips))} }; % endif }; diff --git a/nodes/home.router.py b/nodes/home.router.py index f83ae67..94b94af 100644 --- a/nodes/home.router.py +++ b/nodes/home.router.py @@ -6,6 +6,7 @@ 'hardware', 'home', 'monitored', + #'dnsserver', ], 'bundles': [ 'kea-dhcpd', @@ -26,7 +27,7 @@ 'ipv4': '10.0.2.1/24', 'dhcp_server': True, }, - 'internet': { + 'external': { 'type': 'vlan', 'id': 3, 'ipv4': '10.0.99.126/24', @@ -51,16 +52,9 @@ 'dhcp_server': True, }, }, - # 'nftables': { - # 'forward': { - # # Drop DHCP client requests (UDP port 68) - # 'udp sport 68 drop', - # 'udp dport 68 drop', - - # # Drop DHCP server responses (UDP port 67) - # 'udp sport 67 drop', - # 'udp dport 67 drop', - # }, + # 'bind': { + # 'master_node': 'htz.mails', + # 'hostname': 'home.resolver.name', # }, 'sysctl': { 'net': { diff --git a/nodes/home.server.py b/nodes/home.server.py index 23076ee..16a8c6f 100644 --- a/nodes/home.server.py +++ b/nodes/home.server.py @@ -42,11 +42,6 @@ 'ipv4': '10.0.0.2/24', 'gateway4': '10.0.0.1', }, - 'old-internal': { - 'interface': 'enp42s0', - 'ipv4': '10.0.0.82/24', - 'gateway4': '10.0.0.1', - }, }, 'apt': { 'packages': { @@ -113,7 +108,7 @@ }, 'nextcloud': { 'hostname': 'cloud.sublimity.de', - 'version': '29.0.7', + 'version': '29.0.16', 'config': { 'instanceid': 'oci6dw1woodz', 'secret': '!decrypt:encrypt$gAAAAABj96CFynVtEgsje7173zjQAcY7xQG3uyf5cxE-sJAvhyPh_KUykTKdwnExc8NTDJ8RIGUmVfgC6or5crnYaggARPIEg5-Cb0xVdEPPZ3oZ01ImLmynLu3qXT9O8kVM-H21--OKeztMRn7bySsbXdWEGtETFQ==', -- 2.39.5