package io.trygvis.rules.wireguard; import java.util.ArrayList 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 io.trygvis.rules.network.IpCalc import java.util.Map global io.trygvis.rules.engine.TemplateEngine te; dialect "mvel" declare WgNet name : String domain : String linkCidr : String networkCidr : String networkBits : int end declare WgIpPool net : WgNet role : String cidr : Ipv4Cidr end declare WgHost machine : Machine net : WgNet publicName : String ip : String // This host's IP networkCidr : String end declare WgConnection name : String host : WgHost to : WgHost end declare WgIpAllocation host : WgHost role : String ip : Ipv4Address end rule "Create IP pools" when $net : WgNet() // not(Ipv4Cidr(network == Ipv4Cidr.parseCidr($net.linkCidr).network)) then System.out.println("Creating main IP pools"); insert(new WgIpPool($net, "link", Ipv4Cidr.parseCidr($net.linkCidr))) insert(new WgIpPool($net, "networks", Ipv4Cidr.parseCidr($net.networkCidr))) end rule "WgHost VPN machines" when $machine : Machine() $wgNet : WgNet(name == "vpn0") not(WgHost(machine == $machine)) then var wgHost = new WgHost(); wgHost.machine = $machine; wgHost.net = $wgNet; wgHost.publicName = $machine.fqdn; insert(wgHost) end rule "Set public name of WgHost" when $host : WgHost(publicName == null) $m : Machine(this == $host.machine, fqdn != null) then modify($host) { publicName = $m.fqdn } end rule "Make DNS entries for all VPN hosts" when $h : WgHost() not(DnsEntry(fqdn == "%s.%s".formatted($h.machine.name, $h.net.domain), type == "A")) then var fqdn = "%s.%s".formatted($h.machine.name, $h.net.domain); insert(DnsEntry.a(fqdn)) end rule "Connect VPN nodes" salience -1 when $h : WgHost() $other : WgHost(publicName != null, this != $h) then System.out.printf("VPN connection from %s to %s%n", $h.machine.name, $other.machine.name); var c = new WgConnection(); c.host = $h; c.to = $other; insert(c) end rule "Name connections" when $c : WgConnection(name == null, host != null, to != null) then String n = $c.host.machine.name + "_x_" + $c.to.machine.name; modify($c) { name = n } end // This and the next rule needs to use .toString(), the specific objects might be generated multiple times, // but Drools use identityHashCode() to find equal objects, not equals(). rule "Assign IP" when $pool : WgIpPool(role == "link") $ip : Ipv4Address() from $pool.cidr.addresses() not(WgHost(net == $pool.net, ip == $ip.toString())) $host : WgHost(net == $pool.net, ip == null) then System.out.printf("IP: net=%s, pool.role=%s, host=%s, ip=%s%n", $pool.net.name, $pool.role, $host.machine.name, $ip); modify($host) { ip = $ip.toString() } end rule "Assign network CIDR" when $net : WgNet() $network : Ipv4Cidr() from Ipv4Cidr.parseCidr($net.networkCidr).partition($net.networkBits) $host : WgHost(net == $net, networkCidr == null) not(WgHost(net == $net, networkCidr == $network.toString())) then System.out.printf("Network CIDR: net=%s, host=%s, network=%s%n", $net.name, $host.machine.name, $network); modify($host) { networkCidr = $network.toString() } end rule "Generate per-net files" agenda-group "generate" salience 10 when $net : WgNet() $hosts : ArrayList() from collect(WgHost(net == $net)) then te.template("wireguard/ansible", "wireguard-" + $net.name + ".yml", Map.of( "net", $net )); var machines = new ArrayList(); for (Object o : $hosts) { WgHost m = (WgHost) o; machines.add(m.machine); } te.template("wireguard/inventory", "inventory.yml", Map.of( "hosts", machines )); end rule "Generate per-net, per-host files" agenda-group "generate" salience 10 when $net : WgNet() $host : WgHost(net == $net) $peers : ArrayList() from accumulate(WgConnection(host == $host, $to: to), collectList($to.machine)) then System.out.printf("Generating per-host files: net=%s, host=%s%n", $net.name, $host.machine.name); String output = "host_vars/%s/wireguard.yml".formatted($host.machine.name); te.template("wireguard/ansible-host", output, Map.of( "net", $net, "host", $host, "peers", $peers )); end