diff --git a/readme.md b/README.md similarity index 98% rename from readme.md rename to README.md index 138bebe..acf97ba 100644 --- a/readme.md +++ b/README.md @@ -97,6 +97,8 @@ type peer = { ipAddresses?: str[]; extraArgs?: any; }; + publicKey: string; + privateKeyFile: string; groups?: str[]; endpoints?: endpoint[]; extraArgs?: any; diff --git a/examples/fullMesh/acl.nix b/examples/fullMesh/acl.nix new file mode 100644 index 0000000..d547f60 --- /dev/null +++ b/examples/fullMesh/acl.nix @@ -0,0 +1,74 @@ +{ + version = "v1"; + subnets = [ + { + name = "myMesh"; + endpoints = [ + { + # No match mean match any + port = 51820; + } + ]; + } + ]; + groups = [ + # groups field is expected, but can be empty + ]; + peers = [ + { + name = "peer1"; + subnets = { + myMesh = { + listenPort = 51820; + # empty ipAddresses will auto generate an IPv6 address + }; + }; + publicKey = "t2kmvyJbDle433HCUlP48HxGlLdd8HyiRi2y8aYvTV8="; + privateKeyFile = "/path/to/private/key"; # path is relative to the machine + endpoints = [ + { + # no match can be any + ip = "192.168.1.11"; + } + ]; + } + { + name = "peer2"; + subnets = { + myMesh = { + listenPort = 51820; + }; + }; + publicKey = "xTiftEK/lzqxJYtbH8tt8yINEnSq7tTEN46hXTr9X2M="; + privateKeyFile = "/path/to/private/key"; + endpoints = [ + { + # no match can be any + ip = "192.168.1.12"; + } + ]; + } + { + name = "peer3"; + subnets = { + myMesh = { + listenPort = 51820; + }; + }; + endpoints = [ + { + # no match can be any + ip = "192.168.1.13"; + } + ]; + publicKey = "jSp9uutNwpNHn1XjeQb9ixCixBSjvsRi2uYIiM2LORE="; + privateKeyFile = "/path/to/private/key"; + } + ]; + connections = [ + { + a = [{type= "subnet"; rule = "is"; value = "myMesh";}]; + b = [{type= "subnet"; rule = "is"; value = "myMesh";}]; + } + ]; +} \ No newline at end of file diff --git a/examples/ring/acl.nix b/examples/ring/acl.nix new file mode 100644 index 0000000..805643e --- /dev/null +++ b/examples/ring/acl.nix @@ -0,0 +1,106 @@ +{ + version = "v1"; + subnets = [ + { + name = "myRing"; + endpoints = [ + { + # No match means match any + port = 51820; + } + ]; + } + ]; + groups = [ + # groups field is expected, but can be empty + ]; + peers = [ + { + name = "peer1"; + subnets = { + myRing = { + listenPort = 51820; + # empty ipAddresses will auto generate an IPv6 address + }; + }; + publicKey = "2CMNhw0CU2iFwKZvKT3dnqviBTKbJtuq5SNZvQpXCDY="; + privateKeyFile = "/path/to/private/key"; # path is relative to the machine + endpoints = [ + { + # no match can be any + ip = "192.168.1.11"; + } + ]; + } + { + name = "peer2"; + subnets = { + myRing = { + listenPort = 51820; + }; + }; + publicKey = "nU97PJin1r+g/jk4Jm9+xd0ibZKErESNeav4VjOQKlc="; + privateKeyFile = "/path/to/private/key"; + endpoints = [ + { + # no match can be any + ip = "192.168.1.12"; + } + ]; + } + { + name = "peer3"; + subnets = { + myRing = { + listenPort = 51820; + }; + }; + endpoints = [ + { + # no match can be any + ip = "192.168.1.13"; + } + ]; + publicKey = "FNdLup5glVp5dlynS4ker75i+fIIJPs4ri5vQ+9sGnE="; + privateKeyFile = "/path/to/private/key"; + } + { + name = "peer4"; + subnets = { + myRing = { + listenPort = 51820; + }; + }; + publicKey = "Xa+AhcRbAdOufv5mEwR8V5GDkJ+AiRMxTjtAwmWBCVI="; + privateKeyFile = "/path/to/private/key"; + endpoints = [ + { + # no match can be any + ip = "192.168.1.14"; + } + ]; + } + ]; + connections = [ + { + # Connections go both ways, no need to do a = peer2 and b = peer1 + a = [{type= "peer"; rule = "is"; value = "peer1";}]; + b = [{type= "peer"; rule = "is"; value = "peer2";}]; + } + { + # Connections go both ways, no need to do a = peer2 and b = peer1 + a = [{type= "peer"; rule = "is"; value = "peer2";}]; + b = [{type= "peer"; rule = "is"; value = "peer3";}]; + } + { + # Connections go both ways, no need to do a = peer2 and b = peer1 + a = [{type= "peer"; rule = "is"; value = "peer3";}]; + b = [{type= "peer"; rule = "is"; value = "peer4";}]; + } + { + # Connections go both ways, no need to do a = peer2 and b = peer1 + a = [{type= "peer"; rule = "is"; value = "peer4";}]; + b = [{type= "peer"; rule = "is"; value = "peer1";}]; + } + ]; +} \ No newline at end of file diff --git a/examples/spoke/acl.nix b/examples/spoke/acl.nix new file mode 100644 index 0000000..45c0a4c --- /dev/null +++ b/examples/spoke/acl.nix @@ -0,0 +1,90 @@ +{ + version = "v1"; + subnets = [ + { + name = "mySpoke"; + endpoints = [ + { + # No match means match any + port = 51820; + } + ]; + } + ]; + groups = [ + # groups field is expected, but can be empty + ]; + peers = [ + { + name = "central"; + subnets = { + mySpoke = { + listenPort = 51820; + # empty ipAddresses will auto generate an IPv6 address + }; + }; + publicKey = "whST3RRJziiQTIizeQW6z8fCtoiBfHK559WCY2RS4GQ="; + privateKeyFile = "/path/to/private/key"; # path is relative to the machine + endpoints = [ + { + # no match can be any + ip = "192.168.1.11"; + } + ]; + } + { + name = "leaf1"; + subnets = { + mySpoke = { + listenPort = 51820; + }; + }; + publicKey = "AgsXp89XUz545KRZiNzCYw1Jr6WC5zIfKZnq5M+UUkM="; + privateKeyFile = "/path/to/private/key"; + endpoints = [ + { + # no match can be any + ip = "192.168.1.12"; + } + ]; + } + { + name = "leaf2"; + subnets = { + mySpoke = { + listenPort = 51820; + }; + }; + endpoints = [ + { + # no match can be any + ip = "192.168.1.13"; + } + ]; + publicKey = "1fn/kD11tWPc5VFAZn30PDM4nPoZoGPBEoVFnQdx62o="; + privateKeyFile = "/path/to/private/key"; + } + { + name = "leaf3"; + subnets = { + mySpoke = { + listenPort = 51820; + }; + }; + publicKey = "z5IfD9joAexurf2TyoKd49LrRFRN4JyCCPBAOJXqlGc="; + privateKeyFile = "/path/to/private/key"; + endpoints = [ + { + # no match can be any + ip = "192.168.1.14"; + } + ]; + } + ]; + connections = [ + { + a = [{type= "subnet"; rule = "is"; value = "mySpoke";}]; + b = [{type= "peer"; rule = "is"; value = "central";}]; + } + ]; +} \ No newline at end of file diff --git a/flake.nix b/flake.nix index 9a787b0..60b81c7 100644 --- a/flake.nix +++ b/flake.nix @@ -3,7 +3,7 @@ outputs = { self, ... }: { - lib = import ./lib.nix; - nixosModules.myModule = import ./wire.nix; + wirenix.lib = import ./lib.nix; + nixosModules.default = import ./wire.nix; }; } diff --git a/lib.nix b/lib.nix index 0f6ca05..7a76ed3 100644 --- a/lib.nix +++ b/lib.nix @@ -3,6 +3,17 @@ with builtins; ACL independent functions that can be used in parsers. */ rec { + /** Builtin Parsers */ + defaultParsers = { + v1 = import ./parsers/v1.nix; + }; + /** Builtin configurers */ + defaultConfigurers = rec { + auto = static; # TODO: make smart + static = import ./configurers/static.nix; + networkd = import ./configurers/networkd.nix; + networkmanager = import ./configurers/networkmanager.nix; + }; /** listOfSetsToSetByKey :: string -> list -> attrSet * Example: * listOfSetsToSetByKey "primary" [ {primary = "foo"; secondary = 1; tertiary = "one"} {primary = "bar"; secondary = 2; tertiary = "two"} ] @@ -15,7 +26,7 @@ rec { value = removeAttrs item [ key ]; }) list ); - /** */ + /** Like listOfSetsToSetByKey, but also performs a map before dropping the key */ mapListOfSetsToSetByKey = key: function: list: mapAttrs (name: value: removeAttrs (function value) [key]) ( listToAttrs ( diff --git a/wire.nix b/wire.nix index e9d2134..a5c347a 100644 --- a/wire.nix +++ b/wire.nix @@ -1,5 +1,6 @@ { config, lib, ... }@inputs: with lib; +with import ./lib.nix; { options = { wirenix = { @@ -58,15 +59,8 @@ with lib; config = let - configurers = rec { - auto = static; - static = import ./configurers/static.nix; - networkd = import ./configurers/networkd.nix; - networkmanager = import ./configurers/networkmanager.nix; - } // config.modules.wirenix.additionalConfigurers; - parsers = { - v1 = import ./parsers/v1.nix; - } // config.modules.wirenix.additionalParsers; + configurers = defaultConfigurers // config.modules.wirenix.additionalConfigurers; + parsers = defaultParsers // config.modules.wirenix.additionalParsers; acl = config.modules.wirenix.aclConfig; parser = parsers."${acl.version}" inputs; configurer = configurers."${config.modules.wirenix.configurer}" inputs;