flakificaion #5
44
README.md
44
README.md
|
@ -1,7 +1,37 @@
|
||||||
|
# Setup
|
||||||
|
|
||||||
|
Nix with flakes support is required. Run this in a shell…
|
||||||
|
```
|
||||||
|
# Enter a temporary shell with flakes support:
|
||||||
|
nix-shell --packages nixFlakes
|
||||||
|
|
||||||
|
# Set some configuration (do this only once):
|
||||||
|
echo 'experimental-features = nix-command flakes' >> ~/.config/nix/nix.conf
|
||||||
|
|
||||||
|
# Add this repository to your local flake registry:
|
||||||
|
nix registry add c3d2 git+https://gitea.c3d2.de/C3D2/nix-config
|
||||||
|
```
|
||||||
|
|
||||||
|
…or set this to your NixOS configuration:
|
||||||
|
```
|
||||||
|
{ pkgs, ... }: {
|
||||||
|
nix = {
|
||||||
|
package = pkgs.nixFlakes;
|
||||||
|
extraOptions = "experimental-features = nix-command flakes";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
# Deployment
|
# Deployment
|
||||||
|
|
||||||
Beide failen bei Activation des neuen Profils. (TODO)
|
Beide failen bei Activation des neuen Profils. (TODO)
|
||||||
|
|
||||||
|
## Mit flakes
|
||||||
|
|
||||||
|
Use `nix run` with one of the deploy scripts exported by the flake,
|
||||||
|
for example: `nix run c3d2#deploy-glotzbert switch`. Use `nix flake show c3d2`
|
||||||
|
to show what is available. Note that the deploy scripts only work if
|
||||||
|
the target machines already has flakes enabled.
|
||||||
|
|
||||||
## Mit NixOps
|
## Mit NixOps
|
||||||
|
|
||||||
|
@ -55,10 +85,10 @@ This is necessary, so you can login to any machine with your gpg key.
|
||||||
|
|
||||||
# Laptops / Desktops
|
# Laptops / Desktops
|
||||||
|
|
||||||
This repository contains a NixOS module that can be used with personal machines
|
This repository contains a NixOS module that can be used with personal machines
|
||||||
as well. This module appends `/etc/ssh/ssh_known_hosts` with the host keys of
|
as well. This module appends `/etc/ssh/ssh_known_hosts` with the host keys of
|
||||||
registered HQ hosts, and optionally appends `/etc/hosts` with static IPv6
|
registered HQ hosts, and optionally appends `/etc/hosts` with static IPv6
|
||||||
addresses local to HQ. Simply import the `lib` directory to use the module. As
|
addresses local to HQ. Simply import the `lib` directory to use the module. As
|
||||||
an example:
|
an example:
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
|
@ -83,3 +113,9 @@ in {
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Custom packages
|
||||||
|
|
||||||
|
Additional packages can be added to [./nixpkgs-overlay]. These packages are available
|
||||||
|
during NixOS configuration as well as from the flake via `nix shell c3d2#…` (see above
|
||||||
|
for adding this repository to your local nix registry).
|
||||||
|
|
91
flake.lock
91
flake.lock
|
@ -1,77 +1,6 @@
|
||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"hydra": {
|
|
||||||
"inputs": {
|
|
||||||
"nix": "nix",
|
|
||||||
"nixpkgs": [
|
|
||||||
"hydra",
|
|
||||||
"nix",
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1603366072,
|
|
||||||
"narHash": "sha256-9dK7Mx9BZHZTeJ/oolS7nMakVnCdXQlsA2ePWNPhQks=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "hydra",
|
|
||||||
"rev": "be709d450b98a384374228db51c14dc958a3a72a",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"id": "hydra",
|
|
||||||
"type": "indirect"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lowdown-src": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1598695561,
|
|
||||||
"narHash": "sha256-gyH/5j+h/nWw0W8AcR2WKvNBUsiQ7QuxqSJNXAwV+8E=",
|
|
||||||
"owner": "kristapsdz",
|
|
||||||
"repo": "lowdown",
|
|
||||||
"rev": "1705b4a26fbf065d9574dce47a94e8c7c79e052f",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "kristapsdz",
|
|
||||||
"repo": "lowdown",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nix": {
|
|
||||||
"inputs": {
|
|
||||||
"lowdown-src": "lowdown-src",
|
|
||||||
"nixpkgs": "nixpkgs"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1603189103,
|
|
||||||
"narHash": "sha256-KVS/Z6FzMBOl5XCyOLwfiVoX7G2LQRa9HMGNnJRPCoo=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nix",
|
|
||||||
"rev": "e0ca98c2071b815578470e280df8fdb750c7e23b",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"id": "nix",
|
|
||||||
"type": "indirect"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
|
||||||
"lastModified": 1602702596,
|
|
||||||
"narHash": "sha256-fqJ4UgOb4ZUnCDIapDb4gCrtAah5Rnr2/At3IzMitig=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "ad0d20345219790533ebe06571f82ed6b034db31",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"id": "nixpkgs",
|
|
||||||
"ref": "nixos-20.09-small",
|
|
||||||
"type": "indirect"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs_2": {
|
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1603722914,
|
"lastModified": 1603722914,
|
||||||
"narHash": "sha256-V3cst4osjvfsrR5Qpk8CYWRFQiGm1Rm4lanjMWooH2o=",
|
"narHash": "sha256-V3cst4osjvfsrR5Qpk8CYWRFQiGm1Rm4lanjMWooH2o=",
|
||||||
|
@ -89,8 +18,24 @@
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"hydra": "hydra",
|
"nixpkgs": "nixpkgs",
|
||||||
"nixpkgs": "nixpkgs_2"
|
"secrets": "secrets"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"secrets": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1607473285,
|
||||||
|
"narHash": "sha256-cnilic++Xa2RB8krfNe0/ndZ6jFo2FQWIq8YrZ1pWrE=",
|
||||||
|
"ref": "master",
|
||||||
|
"rev": "0efb7df81d358c033a72fcc0c65016ff86f54858",
|
||||||
|
"revCount": 76,
|
||||||
|
"type": "git",
|
||||||
|
"url": "ssh://git@gitea.c3d2.de:2222/c3d2-admins/secrets.git"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "ssh://git@gitea.c3d2.de:2222/c3d2-admins/secrets.git"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
123
flake.nix
123
flake.nix
|
@ -3,36 +3,111 @@
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/release-20.09";
|
nixpkgs.url = "github:nixos/nixpkgs/release-20.09";
|
||||||
# secrets.url = "git+file:///etc/nixos/secrets";
|
secrets = {
|
||||||
|
url = "git+ssh://git@gitea.c3d2.de:2222/c3d2-admins/secrets.git";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, hydra }: {
|
outputs = { self, nixpkgs, secrets }:
|
||||||
|
let
|
||||||
|
forAllSystems = f:
|
||||||
|
nixpkgs.lib.genAttrs [ "aarch64-linux" "x86_64-linux" ]
|
||||||
|
(system: f system);
|
||||||
|
in {
|
||||||
|
|
||||||
nixosConfigurations = {
|
overlay = import ./nixpkgs-overlay;
|
||||||
|
|
||||||
|
legacyPackages = forAllSystems (system:
|
||||||
|
import nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
overlays = [ self.overlay ];
|
||||||
|
});
|
||||||
|
|
||||||
|
packages = forAllSystems (system:
|
||||||
|
let
|
||||||
|
pkgs = self.legacyPackages.${system};
|
||||||
|
|
||||||
|
mkDeploy =
|
||||||
|
# Generate a small script for copying this flake to the
|
||||||
|
# remote machine and bulding and switching there.
|
||||||
|
# Can be run with nix run c3d2#deploy-…
|
||||||
|
name: host:
|
||||||
|
let target = "root@${host}";
|
||||||
|
in pkgs.writeScriptBin "deploy-${name}" ''
|
||||||
|
#!${pkgs.runtimeShell}
|
||||||
|
set -ev
|
||||||
|
nix-copy-closure --to ${target} ${self}
|
||||||
|
exec ssh -t ${target} \
|
||||||
|
nix shell \
|
||||||
|
${self}#nixosConfigurations.${name}.config.system.build.toplevel \
|
||||||
|
--command switch-to-configuration $@
|
||||||
|
'';
|
||||||
|
in {
|
||||||
|
inherit (pkgs) bmxd;
|
||||||
|
inherit (pkgs.pile) ledball;
|
||||||
|
|
||||||
|
deploy-freifunk = mkDeploy "freifunk" "172.20.72.40";
|
||||||
|
deploy-glotzbert = mkDeploy "glotzbert" "glotzbert.hq.c3d2.de";
|
||||||
|
deploy-kibana = mkDeploy "kibana" "172.20.73.44";
|
||||||
|
deploy-ledstripes = mkDeploy "ledstripes" "172.22.99.168";
|
||||||
|
deploy-scrape = mkDeploy "scrape" "172.20.73.32";
|
||||||
|
});
|
||||||
|
|
||||||
|
nixosConfigurations = let
|
||||||
|
|
||||||
|
nixosSystem' =
|
||||||
|
# Our custom NixOS builder
|
||||||
|
{ modules, system ? "x86_64-linux", ... }@args:
|
||||||
|
nixpkgs.lib.nixosSystem (args // {
|
||||||
|
inherit system;
|
||||||
|
modules = modules ++ [
|
||||||
|
self.nixosModules.c3d2
|
||||||
|
({ pkgs, ... }: {
|
||||||
|
nix = {
|
||||||
|
package = pkgs.nixFlakes;
|
||||||
|
extraOptions = "experimental-features = nix-command flakes";
|
||||||
|
};
|
||||||
|
nixpkgs.overlays = [ self.overlay ];
|
||||||
|
})
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
in {
|
||||||
|
|
||||||
|
freifunk = nixosSystem' {
|
||||||
|
modules = [
|
||||||
|
(import ./hosts/containers/freifunk/configuration.nix {
|
||||||
|
inherit secrets;
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
glotzbert =
|
||||||
|
nixosSystem' { modules = [ ./hosts/glotzbert/configuration.nix ]; };
|
||||||
|
|
||||||
|
kibana = nixosSystem' {
|
||||||
|
modules = [ ./hosts/containers/kibana/configuration.nix ];
|
||||||
|
};
|
||||||
|
|
||||||
|
ledstripes = nixosSystem' {
|
||||||
|
modules = [ ./hosts/containers/ledstripes/configuration.nix ];
|
||||||
|
};
|
||||||
|
|
||||||
|
pulsebert =
|
||||||
|
nixosSystem' { modules = [ ./hosts/pulsebert/configuration.nix ]; };
|
||||||
|
|
||||||
|
scrape = nixosSystem' {
|
||||||
|
modules = [
|
||||||
|
(import ./hosts/containers/scrape/configuration.nix {
|
||||||
|
inherit secrets;
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
glotzbert = nixpkgs.lib.nixosSystem {
|
|
||||||
modules = [ ./hosts/glotzbert/configuration.nix ];
|
|
||||||
system = "x86_64-linux";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
hydra = nixpkgs.lib.nixosSystem {
|
nixosModules.c3d2 = import ./lib;
|
||||||
modules = [ ./hosts/hydra/configuration.nix ];
|
|
||||||
system = "x86_64-linux";
|
|
||||||
};
|
|
||||||
|
|
||||||
kibana = nixpkgs.lib.nixosSystem {
|
|
||||||
modules = [ ./hosts/containers/kibana/configuration.nix ];
|
|
||||||
system = "x86_64-linux";
|
|
||||||
};
|
|
||||||
|
|
||||||
pulsebert = nixpkgs.lib.nixosSystem {
|
|
||||||
modules = [ ./hosts/pulsebert/configuration.nix ];
|
|
||||||
system = "aarch64-linux";
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
nixosModules.c3d2 = import ./lib;
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{ config, pkgs, lib, ... }:
|
{ secrets }:
|
||||||
|
{ config, pkgs, lib, modulesPath, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
coreAddress = "172.20.72.40";
|
coreAddress = "172.20.72.40";
|
||||||
|
@ -6,21 +7,17 @@ let
|
||||||
meshInterface = "bmx";
|
meshInterface = "bmx";
|
||||||
meshLoopback = "bmx_prime";
|
meshLoopback = "bmx_prime";
|
||||||
ddmeshRegisterUrl = "https://register.freifunk-dresden.de/bot.php";
|
ddmeshRegisterUrl = "https://register.freifunk-dresden.de/bot.php";
|
||||||
secrets = import <secrets/hosts/freifunk>;
|
secrets' = import "${secrets}/hosts/freifunk";
|
||||||
ddmeshRegisterKey = secrets.ddmeshRegisterKey;
|
ddmeshRegisterKey = secrets'.ddmeshRegisterKey;
|
||||||
ddmeshNode = 51073;
|
ddmeshNode = 51073;
|
||||||
ddmeshAddrPart = "200.74";
|
ddmeshAddrPart = "200.74";
|
||||||
rt_table = 7;
|
rt_table = 7;
|
||||||
bmxd = import (toString <lib/pkgs/bmxd.nix>) { inherit pkgs; };
|
sysinfo-json = import ./sysinfo-json.nix { inherit pkgs ddmeshNode; };
|
||||||
sysinfo-json = import <this-host/sysinfo-json.nix> {
|
|
||||||
inherit pkgs bmxd ddmeshNode;
|
|
||||||
};
|
|
||||||
in {
|
in {
|
||||||
imports = [
|
imports = [
|
||||||
<nixpkgs/nixos/modules/profiles/minimal.nix>
|
"${modulesPath}/profiles/minimal.nix"
|
||||||
<lib>
|
../../../lib/lxc-container.nix
|
||||||
<lib/lxc-container.nix>
|
../../../lib/shared.nix
|
||||||
<lib/shared.nix>
|
|
||||||
];
|
];
|
||||||
|
|
||||||
boot.tmpOnTmpfs = true;
|
boot.tmpOnTmpfs = true;
|
||||||
|
@ -78,23 +75,23 @@ in {
|
||||||
"10-bmx" = {
|
"10-bmx" = {
|
||||||
enable = true;
|
enable = true;
|
||||||
matchConfig = { Name = meshInterface; };
|
matchConfig = { Name = meshInterface; };
|
||||||
addresses = [ {
|
addresses = [{
|
||||||
addressConfig = {
|
addressConfig = {
|
||||||
Address = "10.201.${ddmeshAddrPart}/16";
|
Address = "10.201.${ddmeshAddrPart}/16";
|
||||||
Broadcast = "10.255.255.255";
|
Broadcast = "10.255.255.255";
|
||||||
};
|
};
|
||||||
} ];
|
}];
|
||||||
};
|
};
|
||||||
# Dummy interface for primary (10.200) address
|
# Dummy interface for primary (10.200) address
|
||||||
"11-bmx-loopback" = {
|
"11-bmx-loopback" = {
|
||||||
enable = true;
|
enable = true;
|
||||||
matchConfig = { Name = meshLoopback; };
|
matchConfig = { Name = meshLoopback; };
|
||||||
addresses = [ {
|
addresses = [{
|
||||||
addressConfig = {
|
addressConfig = {
|
||||||
Address = "10.200.${ddmeshAddrPart}/32";
|
Address = "10.200.${ddmeshAddrPart}/32";
|
||||||
Broadcast = "10.255.255.255";
|
Broadcast = "10.255.255.255";
|
||||||
};
|
};
|
||||||
} ];
|
}];
|
||||||
};
|
};
|
||||||
# ZW
|
# ZW
|
||||||
"20-core" = {
|
"20-core" = {
|
||||||
|
@ -120,7 +117,7 @@ in {
|
||||||
wantedBy = [ "network.target" ];
|
wantedBy = [ "network.target" ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
ExecStart = ''
|
ExecStart = ''
|
||||||
${bmxd}/sbin/bmxd \
|
${pkgs.bmxd}/sbin/bmxd \
|
||||||
--rt_table_offset=${toString rt_table} \
|
--rt_table_offset=${toString rt_table} \
|
||||||
--no_fork 1 \
|
--no_fork 1 \
|
||||||
--throw-rules 0 \
|
--throw-rules 0 \
|
||||||
|
@ -131,7 +128,7 @@ in {
|
||||||
-g 500000/50000 \
|
-g 500000/50000 \
|
||||||
dev=bmx_prime /linklayer 0 \
|
dev=bmx_prime /linklayer 0 \
|
||||||
dev=${meshInterface} /linklayer 1
|
dev=${meshInterface} /linklayer 1
|
||||||
'';
|
'';
|
||||||
Restart = "always";
|
Restart = "always";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -141,7 +138,9 @@ in {
|
||||||
script = ''
|
script = ''
|
||||||
${pkgs.curl}/bin/curl \
|
${pkgs.curl}/bin/curl \
|
||||||
-o /tmp/ddmesh-registration.json \
|
-o /tmp/ddmesh-registration.json \
|
||||||
'${ddmeshRegisterUrl}?registerkey=${ddmeshRegisterKey}&node=${toString ddmeshNode}'
|
'${ddmeshRegisterUrl}?registerkey=${ddmeshRegisterKey}&node=${
|
||||||
|
toString ddmeshNode
|
||||||
|
}'
|
||||||
'';
|
'';
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
User = "nobody";
|
User = "nobody";
|
||||||
|
@ -149,8 +148,8 @@ in {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
systemd.timers.ddmesh-register-node = {
|
systemd.timers.ddmesh-register-node = {
|
||||||
partOf = [ "ddmesh-register-node.service" ];
|
partOf = [ "ddmesh-register-node.service" ];
|
||||||
wantedBy = [ "timers.target" ];
|
wantedBy = [ "timers.target" ];
|
||||||
timerConfig.OnCalendar = "daily";
|
timerConfig.OnCalendar = "daily";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -162,8 +161,8 @@ in {
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
systemd.timers.sysinfo-json = {
|
systemd.timers.sysinfo-json = {
|
||||||
partOf = [ "sysinfo-json.service" ];
|
partOf = [ "sysinfo-json.service" ];
|
||||||
wantedBy = [ "timers.target" ];
|
wantedBy = [ "timers.target" ];
|
||||||
timerConfig.OnCalendar = "minutely";
|
timerConfig.OnCalendar = "minutely";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -171,49 +170,53 @@ in {
|
||||||
services.bird2 = {
|
services.bird2 = {
|
||||||
enable = true;
|
enable = true;
|
||||||
config = ''
|
config = ''
|
||||||
protocol kernel K4 {
|
protocol kernel K4 {
|
||||||
ipv4 {
|
ipv4 {
|
||||||
export all;
|
export all;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
protocol kernel K6 {
|
protocol kernel K6 {
|
||||||
ipv6 {
|
ipv6 {
|
||||||
export all;
|
export all;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
protocol device {
|
protocol device {
|
||||||
scan time 10;
|
scan time 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol ospf v2 ZW4 {
|
protocol ospf v2 ZW4 {
|
||||||
area 0 {
|
area 0 {
|
||||||
networks {
|
networks {
|
||||||
172.20.72.0/21;
|
172.20.72.0/21;
|
||||||
};
|
|
||||||
stubnet 10.200.0.0/15;
|
|
||||||
interface "core" {
|
|
||||||
authentication cryptographic;
|
|
||||||
password "${import <secrets/shared/ospf/message-digest-key.nix>}";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
stubnet 10.200.0.0/15;
|
||||||
|
interface "core" {
|
||||||
protocol ospf v3 ZW6 {
|
authentication cryptographic;
|
||||||
area 0 {
|
password "${
|
||||||
networks {
|
import "${secrets}/shared/ospf/message-digest-key.nix"
|
||||||
fd23:42:c3d2:500::/56;
|
}";
|
||||||
2a02:8106:208:5200::/56;
|
|
||||||
2a02:8106:211:e900::/56;
|
|
||||||
};
|
|
||||||
interface "core" {
|
|
||||||
#authentication cryptographic;
|
|
||||||
#password "${import <secrets/shared/ospf/message-digest-key.nix>}";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
router id ${coreAddress};
|
protocol ospf v3 ZW6 {
|
||||||
'';
|
area 0 {
|
||||||
|
networks {
|
||||||
|
fd23:42:c3d2:500::/56;
|
||||||
|
2a02:8106:208:5200::/56;
|
||||||
|
2a02:8106:211:e900::/56;
|
||||||
|
};
|
||||||
|
interface "core" {
|
||||||
|
#authentication cryptographic;
|
||||||
|
#password "${
|
||||||
|
import "${secrets}/shared/ospf/message-digest-key.nix"
|
||||||
|
}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
router id ${coreAddress};
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
# HTTP Reverse Proxy to provide services into Freifunk
|
# HTTP Reverse Proxy to provide services into Freifunk
|
||||||
|
@ -228,35 +231,36 @@ in {
|
||||||
virtualHosts = {
|
virtualHosts = {
|
||||||
"c3d2.ffdd" = {
|
"c3d2.ffdd" = {
|
||||||
default = true;
|
default = true;
|
||||||
root = <this-host/assets>;
|
root = ./assets;
|
||||||
locations =
|
locations = let
|
||||||
let
|
sysinfo-json = {
|
||||||
sysinfo-json = {
|
alias = "/run/nginx/sysinfo.json";
|
||||||
alias = "/run/nginx/sysinfo.json";
|
extraConfig = ''
|
||||||
extraConfig = ''
|
add_header Content-Type "application/json;charset=UTF-8";
|
||||||
add_header Content-Type "application/json;charset=UTF-8";
|
'';
|
||||||
'';
|
|
||||||
};
|
|
||||||
in {
|
|
||||||
"/" = {
|
|
||||||
index = "index.html";
|
|
||||||
extraConfig = ''
|
|
||||||
etag off;
|
|
||||||
add_header etag "\"${builtins.substring 11 32 (<this-host> + "/assets")}\"";
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
"=/sysinfo-json.cgi" = sysinfo-json;
|
|
||||||
"=/sysinfo.json" = sysinfo-json;
|
|
||||||
};
|
};
|
||||||
|
in {
|
||||||
|
"/" = {
|
||||||
|
index = "index.html";
|
||||||
|
extraConfig = ''
|
||||||
|
etag off;
|
||||||
|
add_header etag "\"${builtins.substring 11 32 (./assets)}\"";
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"=/sysinfo-json.cgi" = sysinfo-json;
|
||||||
|
"=/sysinfo.json" = sysinfo-json;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
"storage.hq.c3d2.ffdd".locations."/".proxyPass = "http://storage.hq.c3d2.de/";
|
"storage.hq.c3d2.ffdd".locations."/".proxyPass =
|
||||||
|
"http://storage.hq.c3d2.de/";
|
||||||
"grafana.hq.c3d2.ffdd".locations."/" = {
|
"grafana.hq.c3d2.ffdd".locations."/" = {
|
||||||
proxyPass = "https://grafana.hq.c3d2.de/";
|
proxyPass = "https://grafana.hq.c3d2.de/";
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
proxy_ssl_server_name on;
|
proxy_ssl_server_name on;
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
"influxdb.hq.c3d2.ffdd".locations."/".proxyPass = "http://grafana.hq.c3d2.de:8086/";
|
"influxdb.hq.c3d2.ffdd".locations."/".proxyPass =
|
||||||
|
"http://grafana.hq.c3d2.de:8086/";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
{ pkgs ? import <nixpkgs> {},
|
{ pkgs ? import <nixpkgs> { }, ffdd-server ? pkgs.fetchgit {
|
||||||
ffdd-server ? builtins.fetchGit "https://github.com/Freifunk-Dresden/ffdd-server.git",
|
url = "https://github.com/Freifunk-Dresden/ffdd-server.git";
|
||||||
bmxd,
|
sha256 = "15iijpywfp0zd785na5ry0g8z41x3zg238piih5rp8khc5xis09c";
|
||||||
ddmeshNode,
|
}, ddmeshNode, ... }:
|
||||||
... }:
|
|
||||||
|
|
||||||
with pkgs;
|
with pkgs;
|
||||||
let
|
let
|
||||||
|
@ -18,8 +17,7 @@ let
|
||||||
gps_longitude = "13.7285866";
|
gps_longitude = "13.7285866";
|
||||||
gps_altitude = "100";
|
gps_altitude = "100";
|
||||||
};
|
};
|
||||||
in
|
in stdenv.mkDerivation {
|
||||||
stdenv.mkDerivation {
|
|
||||||
name = "sysinfo-json";
|
name = "sysinfo-json";
|
||||||
src = "${ffdd-server}/salt/freifunk/base/ddmesh/";
|
src = "${ffdd-server}/salt/freifunk/base/ddmesh/";
|
||||||
buildPhase = ''
|
buildPhase = ''
|
||||||
|
@ -50,14 +48,21 @@ stdenv.mkDerivation {
|
||||||
--replace '"node_type":"server"' '"node_type":"node"' \
|
--replace '"node_type":"server"' '"node_type":"node"' \
|
||||||
--replace ddmesh-ipcalc.sh $out/bin/ddmesh-ipcalc.sh \
|
--replace ddmesh-ipcalc.sh $out/bin/ddmesh-ipcalc.sh \
|
||||||
--replace lsb_release $out/bin/lsb_release \
|
--replace lsb_release $out/bin/lsb_release \
|
||||||
--replace ${lib.strings.escapeShellArg "$(sudo /sbin/iptables -w -xvn -L stat_from_ovpn | awk '/RETURN/{print $2}')"} 0 \
|
--replace ${
|
||||||
--replace ${lib.strings.escapeShellArg "$(sudo /sbin/iptables -w -xvn -L stat_to_ovpn | awk '/RETURN/{print $2}')"} 0 \
|
lib.strings.escapeShellArg
|
||||||
|
"$(sudo /sbin/iptables -w -xvn -L stat_from_ovpn | awk '/RETURN/{print $2}')"
|
||||||
|
} 0 \
|
||||||
|
--replace ${
|
||||||
|
lib.strings.escapeShellArg
|
||||||
|
"$(sudo /sbin/iptables -w -xvn -L stat_to_ovpn | awk '/RETURN/{print $2}')"
|
||||||
|
} 0 \
|
||||||
--replace 'nettype_lookup[$2]' '"lan"' \
|
--replace 'nettype_lookup[$2]' '"lan"' \
|
||||||
--replace awk ${gawk}/bin/awk
|
--replace awk ${gawk}/bin/awk
|
||||||
'' +
|
'' + lib.strings.concatStrings (lib.attrsets.mapAttrsToList (var: value: ''
|
||||||
lib.strings.concatStrings (lib.attrsets.mapAttrsToList (
|
substituteInPlace sysinfo-json.cgi --replace ${
|
||||||
var: value: "substituteInPlace sysinfo-json.cgi --replace ${lib.strings.escapeShellArg "$(uci -qX get ffdd.sys.${var})"} '${value}'\n"
|
lib.strings.escapeShellArg "$(uci -qX get ffdd.sys.${var})"
|
||||||
) nvram);
|
} '${value}'
|
||||||
|
'') nvram);
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
pwd
|
pwd
|
||||||
mkdir -p $out/bin
|
mkdir -p $out/bin
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
(modulesPath + "/profiles/minimal.nix")
|
(modulesPath + "/profiles/minimal.nix")
|
||||||
../../../lib
|
|
||||||
../../../lib/lxc-container.nix
|
../../../lib/lxc-container.nix
|
||||||
../../../lib/shared.nix
|
../../../lib/shared.nix
|
||||||
];
|
];
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
(modulesPath + "/profiles/minimal.nix")
|
(modulesPath + "/profiles/minimal.nix")
|
||||||
../../../lib
|
|
||||||
../../../lib/lxc-container.nix
|
../../../lib/lxc-container.nix
|
||||||
../../../lib/shared.nix
|
../../../lib/shared.nix
|
||||||
];
|
];
|
||||||
|
@ -21,16 +20,14 @@
|
||||||
services.openssh.enable = true;
|
services.openssh.enable = true;
|
||||||
environment.systemPackages = [ pkgs.git ];
|
environment.systemPackages = [ pkgs.git ];
|
||||||
|
|
||||||
systemd.services.ledball =
|
systemd.services.ledball = {
|
||||||
let pile = import ../../../lib/pkgs/pile.nix { inherit pkgs; };
|
after = [ "network-online.target" ];
|
||||||
in {
|
wantedBy = [ "multi-user.target" ];
|
||||||
after = [ "network-online.target" ];
|
serviceConfig = {
|
||||||
wantedBy = [ "multi-user.target" ];
|
ExecStart = "${pkgs.pile.ledball}/bin/rows";
|
||||||
serviceConfig = {
|
Restart = "always";
|
||||||
ExecStart = "${pile.ledball}/bin/rows";
|
|
||||||
Restart = "always";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# This value determines the NixOS release with which your system is to be
|
# This value determines the NixOS release with which your system is to be
|
||||||
# compatible, in order to avoid breaking some software such as database
|
# compatible, in order to avoid breaking some software such as database
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{ secrets }:
|
||||||
{ config, pkgs, lib, modulesPath, ... }:
|
{ config, pkgs, lib, modulesPath, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
|
@ -13,7 +14,6 @@ let
|
||||||
in {
|
in {
|
||||||
imports = [
|
imports = [
|
||||||
(modulesPath + "/profiles/minimal.nix")
|
(modulesPath + "/profiles/minimal.nix")
|
||||||
../../../lib
|
|
||||||
../../../lib/lxc-container.nix
|
../../../lib/lxc-container.nix
|
||||||
../../../lib/shared.nix
|
../../../lib/shared.nix
|
||||||
];
|
];
|
||||||
|
@ -24,77 +24,78 @@ in {
|
||||||
enableHail = false;
|
enableHail = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
networking.hostName = "scrape";
|
networking.hostName = "scrape";
|
||||||
networking.interfaces.eth0.ipv4.addresses = [ { address = "172.20.73.32"; prefixLength = 26; } ];
|
networking.interfaces.eth0.ipv4.addresses = [{
|
||||||
|
address = "172.20.73.32";
|
||||||
|
prefixLength = 26;
|
||||||
|
}];
|
||||||
networking.defaultGateway = "172.20.73.1";
|
networking.defaultGateway = "172.20.73.1";
|
||||||
|
|
||||||
# Required for krops
|
# Required for krops
|
||||||
services.openssh.enable = true;
|
services.openssh.enable = true;
|
||||||
environment.systemPackages = [ pkgs.git ];
|
environment.systemPackages = [ pkgs.git ];
|
||||||
|
|
||||||
systemd.services =
|
systemd.services = let
|
||||||
let
|
scrapers = import (pkgs.fetchgit {
|
||||||
scrapers = import (
|
url = "https://gitea.c3d2.de/astro/scrapers.git";
|
||||||
builtins.fetchGit { url = "https://gitea.c3d2.de/astro/scrapers.git"; }
|
sha256 = "0fnq58gz7lgn615jn7fqkk5wmn7mv7nkk5zayifhwyybagi9nvlr";
|
||||||
) {
|
}) { inherit pkgs; };
|
||||||
inherit pkgs;
|
makeService = { script, host, user ? "", password ? "" }: {
|
||||||
|
script =
|
||||||
|
"${scrapers.${script}}/bin/${script} ${host} ${user} ${password}";
|
||||||
|
};
|
||||||
|
xeriLogin = import "${secrets}/hosts/scrape/xeri.nix";
|
||||||
|
fhemLogin = import "${secrets}/hosts/scrape/fhem.nix";
|
||||||
|
matematLogin = import "${secrets}/hosts/scrape/matemat.nix";
|
||||||
|
makeNodeScraper = nodeId: {
|
||||||
|
name = "scrape-node${nodeId}";
|
||||||
|
value = makeService {
|
||||||
|
script = "freifunk_node";
|
||||||
|
host = freifunkNodes.${nodeId};
|
||||||
};
|
};
|
||||||
makeService = { script, host, user ? "", password ? "" }: {
|
};
|
||||||
script = "${scrapers.${script}}/bin/${script} ${host} ${user} ${password}";
|
in {
|
||||||
};
|
scrape-xeri = makeService {
|
||||||
xeriLogin = import <secrets/hosts/scrape/xeri.nix>;
|
script = "xerox";
|
||||||
fhemLogin = import <secrets/hosts/scrape/fhem.nix>;
|
host = "xeri.hq.c3d2.de";
|
||||||
matematLogin = import <secrets/hosts/scrape/matemat.nix>;
|
inherit (xeriLogin) user password;
|
||||||
makeNodeScraper = nodeId: {
|
};
|
||||||
name = "scrape-node${nodeId}";
|
scrape-roxi = makeService {
|
||||||
value = makeService {
|
script = "xerox";
|
||||||
script = "freifunk_node";
|
host = "roxi.hq.c3d2.de";
|
||||||
host = freifunkNodes.${nodeId};
|
};
|
||||||
};
|
scrape-fhem = makeService {
|
||||||
};
|
script = "fhem";
|
||||||
in {
|
host = "fhem.hq.c3d2.de";
|
||||||
scrape-xeri = makeService {
|
inherit (fhemLogin) user password;
|
||||||
script = "xerox";
|
};
|
||||||
host = "xeri.hq.c3d2.de";
|
scrape-matemat = makeService {
|
||||||
inherit (xeriLogin) user password;
|
script = "matemat";
|
||||||
};
|
host = "matemat.hq.c3d2.de";
|
||||||
scrape-roxi = makeService {
|
inherit (matematLogin) user password;
|
||||||
script = "xerox";
|
};
|
||||||
host = "roxi.hq.c3d2.de";
|
} // builtins.listToAttrs
|
||||||
};
|
(map makeNodeScraper (builtins.attrNames freifunkNodes));
|
||||||
scrape-fhem = makeService {
|
|
||||||
script = "fhem";
|
|
||||||
host = "fhem.hq.c3d2.de";
|
|
||||||
inherit (fhemLogin) user password;
|
|
||||||
};
|
|
||||||
scrape-matemat = makeService {
|
|
||||||
script = "matemat";
|
|
||||||
host = "matemat.hq.c3d2.de";
|
|
||||||
inherit (matematLogin) user password;
|
|
||||||
};
|
|
||||||
} // builtins.listToAttrs (map makeNodeScraper (builtins.attrNames freifunkNodes));
|
|
||||||
|
|
||||||
systemd.timers =
|
systemd.timers = let
|
||||||
let
|
makeTimer = service: interval: {
|
||||||
makeTimer = service: interval: {
|
partOf = [ "${service}.service" ];
|
||||||
partOf = [ "${service}.service" ];
|
wantedBy = [ "timers.target" ];
|
||||||
wantedBy = [ "timers.target" ];
|
timerConfig.OnCalendar = interval;
|
||||||
timerConfig.OnCalendar = interval;
|
};
|
||||||
|
makeNodeScraperTimer = nodeId:
|
||||||
|
let name = "scrape-node${nodeId}";
|
||||||
|
in {
|
||||||
|
inherit name;
|
||||||
|
value = makeTimer name "minutely";
|
||||||
};
|
};
|
||||||
makeNodeScraperTimer = nodeId:
|
in {
|
||||||
let
|
scrape-xeri = makeTimer "scrape-xeri.service" "minutely";
|
||||||
name = "scrape-node${nodeId}";
|
scrape-roxi = makeTimer "scrape-roxi.service" "minutely";
|
||||||
in {
|
scrape-fhem = makeTimer "scrape-fhem.service" "minutely";
|
||||||
inherit name;
|
scrape-matemat = makeTimer "scrape-matemat.service" "minutely";
|
||||||
value = makeTimer name "minutely";
|
} // builtins.listToAttrs
|
||||||
};
|
(map makeNodeScraperTimer (builtins.attrNames freifunkNodes));
|
||||||
in {
|
|
||||||
scrape-xeri = makeTimer "scrape-xeri.service" "minutely";
|
|
||||||
scrape-roxi = makeTimer "scrape-roxi.service" "minutely";
|
|
||||||
scrape-fhem = makeTimer "scrape-fhem.service" "minutely";
|
|
||||||
scrape-matemat = makeTimer "scrape-matemat.service" "minutely";
|
|
||||||
} // builtins.listToAttrs (map makeNodeScraperTimer (builtins.attrNames freifunkNodes));
|
|
||||||
|
|
||||||
# This value determines the NixOS release with which your system is to be
|
# This value determines the NixOS release with which your system is to be
|
||||||
# compatible, in order to avoid breaking some software such as database
|
# compatible, in order to avoid breaking some software such as database
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{ config, pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
imports = [ <this-host/hardware-configuration.nix> <lib> ];
|
imports = [ ./hardware-configuration.nix ];
|
||||||
|
|
||||||
c3d2 = {
|
c3d2 = {
|
||||||
users.k-ot = true;
|
users.k-ot = true;
|
||||||
|
|
|
@ -10,7 +10,7 @@ let
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [ # Include the results of the hardware scan.
|
imports = [ # Include the results of the hardware scan.
|
||||||
<this-host/hardware-configuration.nix>
|
./hardware-configuration.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
boot.loader.grub.enable = false;
|
boot.loader.grub.enable = false;
|
||||||
|
@ -194,7 +194,7 @@ in
|
||||||
|
|
||||||
systemd.services.mjpeg-stream =
|
systemd.services.mjpeg-stream =
|
||||||
let
|
let
|
||||||
mjpeg-proxy = pkgs.callPackage <lib/pkgs/mjpeg-proxy.nix> {};
|
mjpeg-proxy = pkgs.callPackage ../../lib/pkgs/mjpeg-proxy.nix {};
|
||||||
in {
|
in {
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
after = [ "network.target" ];
|
after = [ "network.target" ];
|
||||||
|
|
|
@ -8,7 +8,6 @@ let eth0 = "ens18";
|
||||||
in {
|
in {
|
||||||
imports = [ # Include the results of the hardware scan.
|
imports = [ # Include the results of the hardware scan.
|
||||||
./hardware-configuration.nix
|
./hardware-configuration.nix
|
||||||
../../lib
|
|
||||||
# ../../lib/hq.nix
|
# ../../lib/hq.nix
|
||||||
../../lib/shared.nix
|
../../lib/shared.nix
|
||||||
../../lib/default-gateway.nix
|
../../lib/default-gateway.nix
|
||||||
|
|
|
@ -162,12 +162,10 @@ in {
|
||||||
host.ip6
|
host.ip6
|
||||||
else
|
else
|
||||||
toHqPrivateAddress hostName;
|
toHqPrivateAddress hostName;
|
||||||
in [
|
in [{
|
||||||
{
|
name = ip6;
|
||||||
name = ip6;
|
value = [ "${hostName}.hq" hostName ];
|
||||||
value = [ "${hostName}.hq" hostName ];
|
}] ++ lib.optional (hasAttr "ip4" host) {
|
||||||
}
|
|
||||||
] ++ lib.optional (hasAttr "ip4" host) {
|
|
||||||
name = host.ip4;
|
name = host.ip4;
|
||||||
value = [ "${hostName}.hq" hostName ];
|
value = [ "${hostName}.hq" hostName ];
|
||||||
};
|
};
|
||||||
|
@ -191,6 +189,7 @@ in {
|
||||||
{ }
|
{ }
|
||||||
else {
|
else {
|
||||||
"${cfg.hq.interface}" = {
|
"${cfg.hq.interface}" = {
|
||||||
|
tempAddress = lib.mkDefault "disabled";
|
||||||
ipv6.addresses = [{
|
ipv6.addresses = [{
|
||||||
address = toHqPrivateAddress config.networking.hostName;
|
address = toHqPrivateAddress config.networking.hostName;
|
||||||
prefixLength = 64;
|
prefixLength = 64;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
networking.interfaces.eth0 = {
|
networking.interfaces.eth0 = {
|
||||||
useDHCP = false;
|
useDHCP = false;
|
||||||
preferTempAddress = false;
|
tempAddress = "disabled";
|
||||||
};
|
};
|
||||||
systemd.network.networks."40-eth0" = {
|
systemd.network.networks."40-eth0" = {
|
||||||
networkConfig = {
|
networkConfig = {
|
||||||
|
|
|
@ -1,19 +1,24 @@
|
||||||
{ pkgs ? import <nixpkgs> {},
|
{ stdenv, fetchgit, fetchpatch, }:
|
||||||
src ? builtins.fetchGit "https://gitlab.freifunk-dresden.de/firmware-developer/firmware.git",
|
|
||||||
}:
|
|
||||||
|
|
||||||
with pkgs;
|
|
||||||
let
|
let
|
||||||
|
src = fetchgit {
|
||||||
|
url = "https://gitlab.freifunk-dresden.de/firmware-developer/firmware.git";
|
||||||
|
sha256 = "sha256-3sV59uqFp+TZKrDf7kmksLvz+5ZKriwFyXZMBH2Sdws=";
|
||||||
|
};
|
||||||
path = "feeds/19.07/feeds-own/bmxd";
|
path = "feeds/19.07/feeds-own/bmxd";
|
||||||
makefile = builtins.readFile "${src}/${path}/Makefile";
|
makefile = builtins.readFile "${src}/${path}/Makefile";
|
||||||
makeDef = name:
|
makeDef = name:
|
||||||
builtins.elemAt (builtins.match ".*?${name}:=([^\n]+).*?" makefile) 0;
|
builtins.elemAt (builtins.match ''
|
||||||
|
.*?${name}:=([^
|
||||||
|
]+).*?'' makefile) 0;
|
||||||
name = makeDef "PKG_NAME";
|
name = makeDef "PKG_NAME";
|
||||||
version = makeDef "PKG_VERSION";
|
version = makeDef "PKG_VERSION";
|
||||||
release = makeDef "PKG_RELEASE";
|
release = makeDef "PKG_RELEASE";
|
||||||
patch = fetchurl {
|
patch = fetchpatch {
|
||||||
url = "https://gitlab.freifunk-dresden.de/firmware-developer/firmware/merge_requests/36.patch";
|
name = "timercpy.patch";
|
||||||
sha256 = "10gm1fqg2s8c261i0j1py3sfyyzr0h5b6wwdsgg3icn7lfjd6k75";
|
url =
|
||||||
|
"https://gitlab.freifunk-dresden.de/firmware-developer/firmware/merge_requests/36.patch";
|
||||||
|
sha256 = "sha256-40BbcCZ10cQzvkfsAi8ApCgmC4hGMh2J8xU6gjD3cng=";
|
||||||
};
|
};
|
||||||
in stdenv.mkDerivation {
|
in stdenv.mkDerivation {
|
||||||
name = "${name}-${version}-${release}";
|
name = "${name}-${version}-${release}";
|
|
@ -0,0 +1,9 @@
|
||||||
|
final: prev:
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
bmxd = prev.callPackage ./bmdx.nix { };
|
||||||
|
|
||||||
|
pile = prev.callPackage ./pile.nix { };
|
||||||
|
|
||||||
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
{ pkgs ? import <nixpkgs> {} }:
|
{ fetchgit, rustPlatform }:
|
||||||
|
|
||||||
with pkgs;
|
|
||||||
let
|
let
|
||||||
pile = builtins.fetchGit "https://github.com/astro/pile.git";
|
pile = fetchgit {
|
||||||
|
url = "https://github.com/astro/pile.git";
|
||||||
|
sha256 = "sha256-z4xNUGmP35ZBZUpgozQHANZniADfmwEoclnEwNlvAC4=";
|
||||||
|
};
|
||||||
in {
|
in {
|
||||||
ledball = rustPlatform.buildRustPackage {
|
ledball = rustPlatform.buildRustPackage {
|
||||||
name = "ledball";
|
name = "ledball";
|
Loading…
Reference in New Issue