diff options
-rw-r--r-- | acme.yaml | 4 | ||||
-rw-r--r-- | out/phase-1.yaml | 49 | ||||
-rw-r--r-- | out/vpn0.yaml | 41 | ||||
-rw-r--r-- | pom.xml | 13 | ||||
-rw-r--r-- | src/main/java/io/trygvis/rules/acme/IpCalc.java | 67 | ||||
-rw-r--r-- | src/main/java/io/trygvis/rules/engine/Main.java | 11 | ||||
-rw-r--r-- | src/main/java/io/trygvis/rules/network/Ipv4Address.java | 35 | ||||
-rw-r--r-- | src/main/java/io/trygvis/rules/network/Ipv4Cidr.java | 40 | ||||
-rw-r--r-- | src/main/resources/io/trygvis/rules/acme/vpn.drl | 23 | ||||
-rw-r--r-- | src/test/java/io/trygvis/rules/acme/IpCalcTest.java | 27 |
10 files changed, 299 insertions, 11 deletions
@@ -34,8 +34,10 @@ type: io.trygvis.rules.machine.Machine data: name: ws-2 ---- +--- # Wireguard VPN network type: io.trygvis.rules.acme.WgNet data: name: vpn0 domain: vpn.acme.com + linkCidr: 192.168.10.0/29 + networkCidr: 10.55.55.0/24 diff --git a/out/phase-1.yaml b/out/phase-1.yaml index 0ec42e0..8c34f1a 100644 --- a/out/phase-1.yaml +++ b/out/phase-1.yaml @@ -74,6 +74,8 @@ type: "io.trygvis.rules.acme.WgNet" data: name: "vpn0" domain: "vpn.acme.com" + linkCidr: "192.168.10.0/29" + networkCidr: "10.55.55.0/24" --- type: "io.trygvis.rules.dba.Cluster" data: @@ -158,7 +160,7 @@ data: type: "io.trygvis.rules.dba.Container" data: cluster: - name: "acme-ci" + name: "acme-production" name: "db" machineRole: "mdb" image: "mongodb" @@ -167,7 +169,7 @@ data: type: "io.trygvis.rules.dba.Container" data: cluster: - name: "acme-production" + name: "acme-ci" name: "db" machineRole: "mdb" image: "mongodb" @@ -176,7 +178,7 @@ data: type: "io.trygvis.rules.dba.Container" data: cluster: - name: "acme-ci" + name: "acme-production" name: "db" machineRole: "pdb" image: "postgresql" @@ -185,7 +187,7 @@ data: type: "io.trygvis.rules.dba.Container" data: cluster: - name: "acme-production" + name: "acme-ci" name: "db" machineRole: "pdb" image: "postgresql" @@ -285,6 +287,45 @@ data: name: "ws-2" fqdn: null --- +type: "io.trygvis.rules.network.Ipv4Address" +data: + value: "192.168.10.7" +--- +type: "io.trygvis.rules.network.Ipv4Address" +data: + value: "192.168.10.6" +--- +type: "io.trygvis.rules.network.Ipv4Address" +data: + value: "192.168.10.5" +--- +type: "io.trygvis.rules.network.Ipv4Address" +data: + value: "192.168.10.4" +--- +type: "io.trygvis.rules.network.Ipv4Address" +data: + value: "192.168.10.3" +--- +type: "io.trygvis.rules.network.Ipv4Address" +data: + value: "192.168.10.2" +--- +type: "io.trygvis.rules.network.Ipv4Address" +data: + value: "192.168.10.1" +--- +type: "io.trygvis.rules.network.Ipv4Address" +data: + value: "192.168.10.0" +--- +type: "io.trygvis.rules.network.Ipv4Cidr" +data: + network: -1062729216 + netmask: -8 + size: 8 + bits: 29 +--- type: "io.trygvis.rules.terraform.ScalewayMachine" data: machine: diff --git a/out/vpn0.yaml b/out/vpn0.yaml index c4a798f..1c88e80 100644 --- a/out/vpn0.yaml +++ b/out/vpn0.yaml @@ -43,6 +43,8 @@ type: "io.trygvis.rules.acme.WgNet" data: name: "vpn0" domain: "vpn.acme.com" + linkCidr: "192.168.10.0/29" + networkCidr: "10.55.55.0/24" --- type: "io.trygvis.rules.dns.DnsEntry" data: @@ -108,3 +110,42 @@ type: "io.trygvis.rules.machine.Machine" data: name: "ws-2" fqdn: null +--- +type: "io.trygvis.rules.network.Ipv4Address" +data: + value: "192.168.10.7" +--- +type: "io.trygvis.rules.network.Ipv4Address" +data: + value: "192.168.10.6" +--- +type: "io.trygvis.rules.network.Ipv4Address" +data: + value: "192.168.10.5" +--- +type: "io.trygvis.rules.network.Ipv4Address" +data: + value: "192.168.10.4" +--- +type: "io.trygvis.rules.network.Ipv4Address" +data: + value: "192.168.10.3" +--- +type: "io.trygvis.rules.network.Ipv4Address" +data: + value: "192.168.10.2" +--- +type: "io.trygvis.rules.network.Ipv4Address" +data: + value: "192.168.10.1" +--- +type: "io.trygvis.rules.network.Ipv4Address" +data: + value: "192.168.10.0" +--- +type: "io.trygvis.rules.network.Ipv4Cidr" +data: + network: -1062729216 + netmask: -8 + size: 8 + bits: 29 @@ -115,6 +115,19 @@ <version>2.8.0</version> </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <version>5.7.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <version>5.7.0</version> + <scope>test</scope> + </dependency> + </dependencies> <build> diff --git a/src/main/java/io/trygvis/rules/acme/IpCalc.java b/src/main/java/io/trygvis/rules/acme/IpCalc.java new file mode 100644 index 0000000..5369d62 --- /dev/null +++ b/src/main/java/io/trygvis/rules/acme/IpCalc.java @@ -0,0 +1,67 @@ +package io.trygvis.rules.acme; + +import io.trygvis.rules.network.Ipv4Cidr; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +public class IpCalc { + private static final Pattern pattern = Pattern.compile("([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})/([0-9]{1,3})"); + + public static class FirstLast { + public final int first; + public final int last; + + public FirstLast(int first, int last) { + this.first = first; + this.last = last; + } + } + + public static Ipv4Cidr cidr(String cidr) { + var matcher = pattern.matcher(cidr); + if (!matcher.matches()) { + throw new IllegalArgumentException("Not a CIDR: " + cidr); + } + + var b1 = matcher.group(1); + var b2 = matcher.group(2); + var b3 = matcher.group(3); + var b4 = matcher.group(4); + + int network = parse(b1) << 24 | + parse(b2) << 16 | + parse(b3) << 8 | + parse(b4); + +// System.out.printf("network = %x%n", network); + + var l = matcher.group(5); + var bits = Integer.parseInt(l); + var hostBits = 32 - bits; + int size = 1 << hostBits; + + int netmask = (-1 >> hostBits) << hostBits; +// System.out.printf("netmask = %08x%n", netmask); + + int x = network & ~netmask; + + if (x != 0) { + throw new IllegalArgumentException("Not a CIDR: " + cidr); + } + + return new Ipv4Cidr(network, netmask, size, bits); + } + + private static int parse(String s) { + var i = Integer.parseInt(s); + if (i > 255) { + throw new IllegalArgumentException("Not a CIDR"); + } + + return i; + } +} diff --git a/src/main/java/io/trygvis/rules/engine/Main.java b/src/main/java/io/trygvis/rules/engine/Main.java index 5556db7..6f04a98 100644 --- a/src/main/java/io/trygvis/rules/engine/Main.java +++ b/src/main/java/io/trygvis/rules/engine/Main.java @@ -3,6 +3,8 @@ package io.trygvis.rules.engine; import io.trygvis.rules.acme.AcmeIo; import io.trygvis.rules.dns.DnsEntry; import io.trygvis.rules.machine.Machine; +import io.trygvis.rules.network.Ipv4Address; +import io.trygvis.rules.network.Ipv4Cidr; import org.drools.core.audit.WorkingMemoryConsoleLogger; import org.kie.api.KieServices; import org.kie.api.event.rule.AgendaEventListener; @@ -38,9 +40,12 @@ public class Main { io.dump("phase-1", session.getFactHandles()); - io.dump("vpn0", session.getFactHandles(), (Object o) -> { - return o.getClass().getName().contains("Wg") || o instanceof Machine || o instanceof DnsEntry; - }); + io.dump("vpn0", session.getFactHandles(), (Object o) -> + o.getClass().getName().contains("Wg") || + o instanceof Machine || + o instanceof DnsEntry || + o instanceof Ipv4Cidr || + o instanceof Ipv4Address); session.dispose(); } diff --git a/src/main/java/io/trygvis/rules/network/Ipv4Address.java b/src/main/java/io/trygvis/rules/network/Ipv4Address.java new file mode 100644 index 0000000..071bca9 --- /dev/null +++ b/src/main/java/io/trygvis/rules/network/Ipv4Address.java @@ -0,0 +1,35 @@ +package io.trygvis.rules.network; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import java.io.IOException; + +@JsonSerialize(using = Ipv4Address.Serializer.class) +public class Ipv4Address { + public final int address; + + public Ipv4Address(int address) { + this.address = address; + } + + @Override + public String toString() { + return "%d.%d.%d.%d".formatted( + address >> 24 & 0xff, + address >> 16 & 0xff, + address >> 8 & 0xff, + address & 0xff); + } + + public static class Serializer extends JsonSerializer<Ipv4Address> { + @Override + public void serialize(Ipv4Address value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeStartObject(); + gen.writeObjectField("value", value.toString()); + gen.writeEndObject(); + } + } +} diff --git a/src/main/java/io/trygvis/rules/network/Ipv4Cidr.java b/src/main/java/io/trygvis/rules/network/Ipv4Cidr.java new file mode 100644 index 0000000..34b3a0b --- /dev/null +++ b/src/main/java/io/trygvis/rules/network/Ipv4Cidr.java @@ -0,0 +1,40 @@ +package io.trygvis.rules.network; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collection; + +public class Ipv4Cidr { + public final int network; + public final int netmask; + public final int size; + public final int bits; + + public Ipv4Cidr(int network, int netmask, int size, int bits) { + this.network = network; + this.netmask = netmask; + this.size = size; + this.bits = bits; + } + + @Override + public String toString() { + return "%d.%d.%d.%d/%d".formatted( + network >> 24 & 0xff, + network >> 16 & 0xff, + network >> 8 & 0xff, + network & 0xff, + bits); + } + + public Collection<Ipv4Address> addresses() { + var end = network + size; + var addresses = new ArrayList<Ipv4Address>(size); + for (int address = network; address < end; address++) { + addresses.add(new Ipv4Address(address)); + } + + return addresses; + } +} diff --git a/src/main/resources/io/trygvis/rules/acme/vpn.drl b/src/main/resources/io/trygvis/rules/acme/vpn.drl index 90cdce2..0f0b3c8 100644 --- a/src/main/resources/io/trygvis/rules/acme/vpn.drl +++ b/src/main/resources/io/trygvis/rules/acme/vpn.drl @@ -4,13 +4,30 @@ import java.util.ArrayList import io.trygvis.rules.machine.Machine; import io.trygvis.rules.dns.DnsEntry; import io.trygvis.rules.acme.AcmeServer -import io.trygvis.rules.acme.WgHost; +import io.trygvis.rules.network.Ipv4Address +import io.trygvis.rules.network.Ipv4Cidr dialect "mvel" declare WgNet - name : String - domain : String + name : String + domain : String + linkCidr : String + networkCidr : String +end + +rule "Create link network" when + $net : WgNet() + not(Ipv4Cidr(network == IpCalc.cidr($net.linkCidr).network)) +then + insert(IpCalc.cidr($net.linkCidr)) +end + +rule "Create link network addresses" when + $cidr : Ipv4Cidr() + $addresses : Ipv4Address() from $cidr.addresses +then + insert($addresses) end declare WgHost diff --git a/src/test/java/io/trygvis/rules/acme/IpCalcTest.java b/src/test/java/io/trygvis/rules/acme/IpCalcTest.java new file mode 100644 index 0000000..8b1e2c6 --- /dev/null +++ b/src/test/java/io/trygvis/rules/acme/IpCalcTest.java @@ -0,0 +1,27 @@ +package io.trygvis.rules.acme; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.junit.jupiter.api.Assertions.*; + +class IpCalcTest { + + @Test + public void basic() { + Assertions.assertThrows(IllegalArgumentException.class, () -> IpCalc.cidr("192.168.1.1/24").addresses()); + assertEquals(256, IpCalc.cidr("192.168.1.0/24").addresses().size()); + assertEquals(128, IpCalc.cidr("192.168.1.128/25").addresses().size()); + } + + @ParameterizedTest + @ValueSource(strings = { + "192.168.1.0/24", + "192.168.1.128/25", + }) + public void testParsing(String s) { + assertEquals(s, IpCalc.cidr(s).toString()); + } +} |