From eb124a1645c3154a1e444145c6647c392b7e0dc2 Mon Sep 17 00:00:00 2001 From: James Rouzier Date: Tue, 16 Feb 2021 21:28:35 -0500 Subject: [PATCH 01/20] Add start of rules --- filter/rule.go | 57 ++++++++++++++++++++++++ filter/rule_test.go | 104 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 filter/rule.go create mode 100644 filter/rule_test.go diff --git a/filter/rule.go b/filter/rule.go new file mode 100644 index 0000000..91f7400 --- /dev/null +++ b/filter/rule.go @@ -0,0 +1,57 @@ +package filter + +type RuleCmd int + +const ( + Skip RuleCmd = iota + Deny = iota + Permit = iota +) + +type RuleFunc func([]byte) RuleCmd + +func RulePermit([]byte) RuleCmd { + return Permit +} + +func RuleDeny([]byte) RuleCmd { + return Deny +} + +func PermitAllRule() RuleFunc { + return RuleFunc(RulePermit) +} + +func DenyAllRule() RuleFunc { + return RuleFunc(RuleDeny) +} + +func PermitAllTcpRule() RuleFunc { + return PermitAllProto(tcpProtocol) +} + +func PermitAllUdpRule() RuleFunc { + return PermitAllProto(udpProtocol) +} + +func PermitAllIcmpRule() RuleFunc { + return PermitAllProto(icmpProtocol) +} + +func PermitAllProto(proto byte) RuleFunc { + return RuleFunc(func(p []byte) RuleCmd { + if p[9] == proto { + return Permit + } + + return Skip + }) +} + +type Ipv4NetworkMask struct { + Network, Mask uint32 +} + +func (nm Ipv4NetworkMask) Match(ip uint32) bool { + return (ip & nm.Mask) == nm.Network +} diff --git a/filter/rule_test.go b/filter/rule_test.go new file mode 100644 index 0000000..3822f74 --- /dev/null +++ b/filter/rule_test.go @@ -0,0 +1,104 @@ +package filter + +import ( + "testing" +) + +var rulePackets = [][]byte{ + []byte{69, 0, 0, 60, 203, 131, 64, 0, 64, 6, 99, 226, 192, 168, 69, 3, 192, 168, 69, 2, 153, 222, 17, 92, 31, 232, 147, 213, 0, 0, 0, 0, 160, 2, 107, 208, 25, 66, 0, 0, 2, 4, 5, 100, 4, 2, 8, 10, 0, 153, 88, 86, 0, 0, 0, 0, 1, 3, 3, 7}, + []byte{69, 0, 0, 52, 203, 132, 64, 0, 64, 6, 99, 233, 192, 168, 69, 3, 192, 168, 69, 2, 153, 222, 17, 92, 31, 232, 147, 214, 181, 110, 17, 81, 128, 16, 0, 216, 252, 127, 0, 0, 1, 1, 8, 10, 0, 153, 88, 88, 0, 151, 238, 205}, + []byte{69, 0, 0, 58, 203, 133, 64, 0, 64, 6, 99, 226, 192, 168, 69, 3, 192, 168, 69, 2, 153, 222, 17, 92, 31, 232, 147, 214, 181, 110, 17, 81, 128, 24, 0, 216, 184, 149, 0, 0, 1, 1, 8, 10, 0, 153, 88, 88, 0, 151, 238, 205, 104, 101, 108, 108, 111, 10}, + []byte{69, 0, 0, 52, 203, 134, 64, 0, 64, 6, 99, 231, 192, 168, 69, 3, 192, 168, 69, 2, 153, 222, 17, 92, 31, 232, 147, 220, 181, 110, 17, 81, 128, 17, 0, 216, 252, 120, 0, 0, 1, 1, 8, 10, 0, 153, 88, 88, 0, 151, 238, 205}, + []byte{69, 0, 0, 52, 203, 135, 64, 0, 64, 6, 99, 230, 192, 168, 69, 3, 192, 168, 69, 2, 153, 222, 17, 92, 31, 232, 147, 221, 181, 110, 17, 82, 128, 16, 0, 216, 252, 110, 0, 0, 1, 1, 8, 10, 0, 153, 88, 93, 0, 151, 238, 209}, +} + +var udpRulePacket = []byte{69, 0, 0, 34, 51, 79, 64, 0, 64, 17, 252, 37, 192, 168, 69, 3, 192, 168, 69, 2, 141, 66, 17, 92, 0, 14, 18, 1, 104, 101, 108, 108, 111, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +var icmpRulePacket = []byte{0x45, 0x00, 0x00, 0x54, 0x26, 0xef, 0x00, 0x00, 0x40, 0x01, 0x57, 0xf9, 0xc0, 0xa8, 0x2b, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0xbb, 0xb3, 0xd7, 0x3b, 0x00, 0x00, 0x51, 0xa7, 0xd6, 0x7d, 0x00, 0x04, 0x51, 0xe4, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37} + +func TestPermitAllRule(t *testing.T) { + rule := PermitAllRule() + if rule(rulePackets[0]) != Permit { + t.Error("The PermitAllRule failed tcp") + } + + if rule(udpRulePacket) != Permit { + t.Error("The PermitAllRule failed udp") + } + + if rule(icmpRulePacket) != Permit { + t.Error("The PermitAllRule failed icmp") + } + +} + +func TestPermitAllProtoRule(t *testing.T) { + rule := PermitAllTcpRule() + if rule(rulePackets[0]) != Permit { + t.Error("The PermitAllRule failed tcp") + } + + if rule(udpRulePacket) != Skip { + t.Error("The PermitAllRule did't skip udp") + } + + if rule(icmpRulePacket) != Skip { + t.Error("The PermitAllTcpRule did't skip icmp") + } + + rule = PermitAllUdpRule() + if rule(rulePackets[0]) != Skip { + t.Error("The PermitAllUdpRule did't skip tcp") + } + + if rule(udpRulePacket) != Permit { + t.Error("The PermitAllUdpRule failed udp") + } + + if rule(icmpRulePacket) != Skip { + t.Error("The PermitAllTcpRule did't skip icmp") + } + + rule = PermitAllIcmpRule() + if rule(rulePackets[0]) != Skip { + t.Error("The PermitAllUdpRule did't skip tcp") + } + + if rule(udpRulePacket) != Skip { + t.Error("The PermitAllUdpRule did't skip udp") + } + + if rule(icmpRulePacket) != Permit { + t.Error("The PermitAllTcpRule failed icmp") + } + +} + +func TestDenyAllRule(t *testing.T) { + rule := DenyAllRule() + if rule(rulePackets[0]) != Deny { + t.Error("The DenyAllRule failed tcp") + } + + if rule(udpRulePacket) != Deny { + t.Error("The DenyAllRule failed udp") + } + + if rule(icmpRulePacket) != Deny { + t.Error("The DenyAllRule failed icmp") + } +} + +func toIpv4(a, b, c, d uint8) uint32 { + return (uint32(a) << 24) | (uint32(b) << 16) | (uint32(c) << 8) | uint32(d) +} + +func TestIpv4NetworkMask(t *testing.T) { + nm := Ipv4NetworkMask{toIpv4(192, 168, 2, 0), toIpv4(255, 255, 255, 0)} + if !nm.Match(toIpv4(192, 168, 2, 1)) { + t.Error("192,168,2,1 failed to match 192,168,2,0/255,255,255,0") + } + + if nm.Match(toIpv4(192, 168, 3, 1)) { + t.Error("192,168,3,1 match 192,168,2,0/255,255,255,0") + } +} From 5f7f84a56c4add321eea17315452e2adbda76494 Mon Sep 17 00:00:00 2001 From: James Rouzier Date: Wed, 17 Feb 2021 14:49:15 -0500 Subject: [PATCH 02/20] Add the simple Src/Dst rule --- filter/rule.go | 46 +++++++++++++++++++++++++++++++++++++++++++++ filter/rule_test.go | 34 ++++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/filter/rule.go b/filter/rule.go index 91f7400..4ac1ad2 100644 --- a/filter/rule.go +++ b/filter/rule.go @@ -48,6 +48,52 @@ func PermitAllProto(proto byte) RuleFunc { }) } +func toIpv4(a, b, c, d uint8) uint32 { + return (uint32(a) << 24) | (uint32(b) << 16) | (uint32(c) << 8) | uint32(d) +} + +func PermitSrcIpRule(network, mask uint32) RuleFunc { + return RuleFunc(func(p []byte) RuleCmd { + srcIpv4NetworkMask := Ipv4NetworkMask{network, mask} + if srcIpv4NetworkMask.Match(toIpv4(p[12], p[13], p[14], p[15])) { + return Permit + } + return Skip + }) +} + +func DenySrcIpRule(network, mask uint32) RuleFunc { + return RuleFunc(func(p []byte) RuleCmd { + srcIpv4NetworkMask := Ipv4NetworkMask{network, mask} + if srcIpv4NetworkMask.Match(toIpv4(p[12], p[13], p[14], p[15])) { + return Deny + } + + return Skip + }) +} + +func PermitDstIpRule(network, mask uint32) RuleFunc { + return RuleFunc(func(p []byte) RuleCmd { + srcIpv4NetworkMask := Ipv4NetworkMask{network, mask} + if srcIpv4NetworkMask.Match(toIpv4(p[16], p[17], p[18], p[19])) { + return Permit + } + return Skip + }) +} + +func DenyDstIpRule(network, mask uint32) RuleFunc { + return RuleFunc(func(p []byte) RuleCmd { + srcIpv4NetworkMask := Ipv4NetworkMask{network, mask} + if srcIpv4NetworkMask.Match(toIpv4(p[16], p[17], p[18], p[19])) { + return Deny + } + + return Skip + }) +} + type Ipv4NetworkMask struct { Network, Mask uint32 } diff --git a/filter/rule_test.go b/filter/rule_test.go index 3822f74..73148a8 100644 --- a/filter/rule_test.go +++ b/filter/rule_test.go @@ -5,7 +5,7 @@ import ( ) var rulePackets = [][]byte{ - []byte{69, 0, 0, 60, 203, 131, 64, 0, 64, 6, 99, 226, 192, 168, 69, 3, 192, 168, 69, 2, 153, 222, 17, 92, 31, 232, 147, 213, 0, 0, 0, 0, 160, 2, 107, 208, 25, 66, 0, 0, 2, 4, 5, 100, 4, 2, 8, 10, 0, 153, 88, 86, 0, 0, 0, 0, 1, 3, 3, 7}, + []byte{69, 0, 0, 60, 203, 131, 64, 0, 64, 6, 99, 226, 192, 168, 69, 3, 192, 168, 68, 2, 153, 222, 17, 92, 31, 232, 147, 213, 0, 0, 0, 0, 160, 2, 107, 208, 25, 66, 0, 0, 2, 4, 5, 100, 4, 2, 8, 10, 0, 153, 88, 86, 0, 0, 0, 0, 1, 3, 3, 7}, []byte{69, 0, 0, 52, 203, 132, 64, 0, 64, 6, 99, 233, 192, 168, 69, 3, 192, 168, 69, 2, 153, 222, 17, 92, 31, 232, 147, 214, 181, 110, 17, 81, 128, 16, 0, 216, 252, 127, 0, 0, 1, 1, 8, 10, 0, 153, 88, 88, 0, 151, 238, 205}, []byte{69, 0, 0, 58, 203, 133, 64, 0, 64, 6, 99, 226, 192, 168, 69, 3, 192, 168, 69, 2, 153, 222, 17, 92, 31, 232, 147, 214, 181, 110, 17, 81, 128, 24, 0, 216, 184, 149, 0, 0, 1, 1, 8, 10, 0, 153, 88, 88, 0, 151, 238, 205, 104, 101, 108, 108, 111, 10}, []byte{69, 0, 0, 52, 203, 134, 64, 0, 64, 6, 99, 231, 192, 168, 69, 3, 192, 168, 69, 2, 153, 222, 17, 92, 31, 232, 147, 220, 181, 110, 17, 81, 128, 17, 0, 216, 252, 120, 0, 0, 1, 1, 8, 10, 0, 153, 88, 88, 0, 151, 238, 205}, @@ -88,10 +88,6 @@ func TestDenyAllRule(t *testing.T) { } } -func toIpv4(a, b, c, d uint8) uint32 { - return (uint32(a) << 24) | (uint32(b) << 16) | (uint32(c) << 8) | uint32(d) -} - func TestIpv4NetworkMask(t *testing.T) { nm := Ipv4NetworkMask{toIpv4(192, 168, 2, 0), toIpv4(255, 255, 255, 0)} if !nm.Match(toIpv4(192, 168, 2, 1)) { @@ -102,3 +98,31 @@ func TestIpv4NetworkMask(t *testing.T) { t.Error("192,168,3,1 match 192,168,2,0/255,255,255,0") } } + +func TestPermitSrcIpRule(t *testing.T) { + rule := PermitSrcIpRule(toIpv4(192, 168, 69, 0), toIpv4(255, 255, 255, 0)) + if rule(rulePackets[0]) != Permit { + t.Error("Packet was not allowed") + } +} + +func TestDenySrcIpRule(t *testing.T) { + rule := DenySrcIpRule(toIpv4(192, 168, 69, 0), toIpv4(255, 255, 255, 0)) + if rule(rulePackets[0]) != Deny { + t.Error("Packet was not denied") + } +} + +func TestDenyDstIpRule(t *testing.T) { + rule := DenyDstIpRule(toIpv4(192, 168, 68, 0), toIpv4(255, 255, 255, 0)) + if rule(rulePackets[0]) != Deny { + t.Error("Packet was not denied") + } +} + +func TestPermitDstIpRule(t *testing.T) { + rule := PermitDstIpRule(toIpv4(192, 168, 68, 0), toIpv4(255, 255, 255, 0)) + if rule(rulePackets[0]) != Permit { + t.Error("Packet was not denied") + } +} From a9c57d1d40b5b1b65be3d49b37fbcd3101d88e19 Mon Sep 17 00:00:00 2001 From: James Rouzier Date: Wed, 24 Feb 2021 06:47:43 -0500 Subject: [PATCH 03/20] Parse ACLs to rules --- filter/acls_parser.go | 469 ++++++++++++++++++++++++++++++++++++ filter/acls_parser_test.go | 160 ++++++++++++ filter/errors.go | 7 + filter/icmp_packets_test.go | 97 ++++++++ filter/rule.go | 296 +++++++++++++++++++++-- filter/rule_test.go | 115 ++++++++- 6 files changed, 1127 insertions(+), 17 deletions(-) create mode 100644 filter/acls_parser.go create mode 100644 filter/acls_parser_test.go create mode 100644 filter/errors.go create mode 100644 filter/icmp_packets_test.go diff --git a/filter/acls_parser.go b/filter/acls_parser.go new file mode 100644 index 0000000..d0a0d4f --- /dev/null +++ b/filter/acls_parser.go @@ -0,0 +1,469 @@ +package filter + +import ( + "strconv" + "strings" +) + +func AclsToRules(acls ...string) Rules { + rules := []RuleFunc{} + for _, acl := range acls { + rule := AclToRule(acl) + if rule != nil { + rules = append(rules, rule) + } + } + + return rules +} + +func AclsToRulesFilter(acls []string, pre, post RuleFunc) func([]byte) error { + rules := Rules([]RuleFunc{}) + if pre != nil { + rules = append(rules, pre) + } + rules = append(rules, AclsToRules(acls...)...) + if post != nil { + rules = append(rules, pre) + } + + return rules.Filter +} + +const SRC_IP_OFFSET = 12 +const DST_IP_OFFSET = 16 + +const invalidIcmpTypeCode = int16(-1) + +type icmpRule struct { + iType int16 + code int16 +} + +func (r icmpRule) AnyCode() bool { + return r.code == invalidIcmpTypeCode +} + +func (r icmpRule) AnyType() bool { + return r.iType == invalidIcmpTypeCode +} + +func (r icmpRule) Any() bool { + return r.AnyType() && r.AnyCode() +} + +type Ipv4RuleData struct { + src Ipv4NetworkMask + dst Ipv4NetworkMask + srcPort portOp + dstPort portOp + cmd RuleCmd + protocol int16 + icmpRule icmpRule +} + +func NewIpv4RuleData() Ipv4RuleData { + return Ipv4RuleData{ + icmpRule: icmpRule{invalidIcmpTypeCode, invalidIcmpTypeCode}, + srcPort: portOp{op: portOpTrue}, + dstPort: portOp{op: portOpTrue}, + } +} + +func (r *Ipv4RuleData) AnySrcIP() bool { + return r.src.Network == 0 && r.src.Mask == 0 +} + +func (r *Ipv4RuleData) AnySrcPort() bool { + return r.srcPort.op == portOpTrue +} + +func (r *Ipv4RuleData) AnyDstIP() bool { + return r.dst.Network == 0 && r.dst.Mask == 0 +} + +func (r *Ipv4RuleData) AnyDstPort() bool { + return r.dstPort.op == portOpTrue +} + +func (r *Ipv4RuleData) AnyIp() bool { + return r.AnySrcIP() && r.AnyDstIP() +} + +func (r *Ipv4RuleData) AnyPort() bool { + return r.AnyDstPort() && r.AnySrcPort() +} + +func (r *Ipv4RuleData) AnyProto() bool { + return r.protocol == allProtocols +} + +func AclToRule(acl string) RuleFunc { + tokens := strings.Fields(acl) + if len(tokens) < 2 { + return nil + } + + if len(tokens) == 2 { + return twoPartAcl(tokens) + } + + rule := NewIpv4RuleData() + + token, tokens, ok := nextToken(tokens) + rule.cmd = Skip + switch token { + case "permit": + rule.cmd = Permit + case "deny": + rule.cmd = Deny + default: + return nil + } + + rule.src.Network, rule.src.Mask, tokens, ok = getSource(tokens) + if ok { + return singleIpRule(rule.src, rule.cmd, SRC_IP_OFFSET) + } + + rule.protocol, tokens, ok = getProtocol(tokens) + if !ok { + return nil + } + + rule.src.Network, rule.src.Mask, tokens, ok = getSource(tokens) + if !ok { + return nil + } + + if rule.protocol == 6 || rule.protocol == 17 { + rule.srcPort.op, rule.srcPort.start, rule.srcPort.end, tokens, ok = getPort(rule.protocol, tokens) + } + + rule.dst.Network, rule.dst.Mask, tokens, ok = getSource(tokens) + if !ok { + return nil + } + + switch rule.protocol { + case 6, 17: + rule.dstPort.op, rule.dstPort.start, rule.dstPort.end, tokens, ok = getPort(rule.protocol, tokens) + case 1: + rule.icmpRule, tokens, ok = getIcmpRule(tokens) + } + + if rule.AnyIp() { + if rule.AnyProto() { + if rule.cmd == Permit { + return PermitAllRule() + } + + return DenyAllRule() + } + + if rule.protocol == 1 { + return icmpRuleRule(rule.icmpRule, rule.cmd) + } + + if rule.AnyPort() { + return protoRule(byte(rule.protocol), rule.cmd) + } + + if rule.AnySrcPort() && !rule.AnyDstPort() { + return portProtoRule(rule.dstPort, byte(rule.protocol), rule.cmd, 2) + } + + if !rule.AnySrcPort() && rule.AnyDstPort() { + return portProtoRule(rule.srcPort, byte(rule.protocol), rule.cmd, 0) + } + + } + + if rule.AnyProto() { + if rule.AnySrcIP() && !rule.AnyDstIP() { + return singleIpRule(rule.dst, rule.cmd, DST_IP_OFFSET) + } + + if !rule.AnySrcIP() && rule.AnyDstIP() { + return singleIpRule(rule.src, rule.cmd, SRC_IP_OFFSET) + } + + return srcDstRule(rule.src, rule.dst, rule.cmd) + } + + if rule.AnyPort() { + return srcDstProtoRule(rule.src, rule.dst, byte(rule.protocol), rule.cmd) + } + + return srcDstProtoSrcPortDstPort(rule.src, rule.dst, byte(rule.protocol), rule.srcPort, rule.dstPort, rule.cmd) +} + +func getProtocol(oTokens []string) (int16, []string, bool) { + token, tokens, ok := nextToken(oTokens) + if !ok { + return invalidProtocol, oTokens, false + } + + if proto, found := protocolLookup[token]; found { + return proto, tokens, true + } + + proto, err := strconv.ParseUint(token, 10, 8) + if err == nil { + return int16(proto), tokens, true + } + + return invalidProtocol, oTokens, false +} + +const fullMask = uint32(1<<32 - 1) +const allProtocols = int16(256) +const invalidProtocol = -1 + +var protocolLookup = map[string]int16{ + // "ahp": , + "eigrp": 88, + "esp": 50, + "gre": 47, + "icmp": 1, + "igmp": 2, + "igrp": 9, + "ip": allProtocols, + "ipinip": 94, + // "nos": , + "ospf": 89, + // "pcp": , + "pim": 103, + "tcp": 6, + "udp": 17, +} + +const ( + invalidOp = iota + portOpEq = iota + portOpNeq = iota + portOpGt = iota + portOpLt = iota + portOpRange = iota + portOpTrue = iota +) + +var validOps = map[string]int{ + "eq": portOpEq, + "neq": portOpNeq, + "gt": portOpGt, + "lt": portOpLt, + "range": portOpRange, +} + +type portOp struct { + op int + start uint16 + end uint16 +} + +func (po portOp) Match(data []byte, offset int) bool { + switch po.op { + default: + return false + case portOpEq: + return toPort(data[offset], data[offset+1]) == po.start + case portOpNeq: + return toPort(data[offset], data[offset+1]) != po.start + case portOpGt: + return toPort(data[offset], data[offset+1]) > po.start + case portOpLt: + return toPort(data[offset], data[offset+1]) < po.start + case portOpRange: + port := toPort(data[offset], data[offset+1]) + return po.start <= port && port <= po.end + case portOpTrue: + return true + } +} + +func getSource(oToken []string) (uint32, uint32, []string, bool) { + token, tokens, ok := nextToken(oToken) + if !ok { + return 0, 0, oToken, false + } + + switch token { + case "any": + return 0, 0, tokens, true + case "host": + token, tokens, ok = nextToken(tokens) + if !ok { + return 0, 0, oToken, false + } + + hostIp, valid := parseIpv4(token) + if valid { + return hostIp, fullMask, tokens, true + } + + return 0, 0, oToken, false + } + + hostIp, valid := parseIpv4(token) + if !valid { + return 0, 0, oToken, false + } + + token, tokens, ok = nextToken(tokens) + if !ok { + return 0, 0, oToken, false + } + + hostMask, valid := parseIpv4(token) + if valid { + return hostIp, fullMask - hostMask, tokens, true + } + + return 0, 0, oToken, false +} + +func nextToken(tokens []string) (string, []string, bool) { + if len(tokens) == 0 { + return "", nil, false + } + + return tokens[0], tokens[1:], true +} + +func getPort(protocol int16, oTokens []string) (int, uint16, uint16, []string, bool) { + token, tokens, ok := nextToken(oTokens) + if !ok { + return invalidOp, 0, 0, oTokens, false + } + + op, found := validOps[token] + if !found { + return invalidOp, 0, 0, oTokens, false + } + + port, tokens, ok := nextToken(tokens) + if !ok { + return invalidOp, 0, 0, oTokens, false + } + + startPort, ok := lookupPort(protocol, port) + endPort := startPort + if !ok { + return invalidOp, 0, 0, oTokens, false + } + + if op == portOpRange { + port, tokens, ok = nextToken(tokens) + if !ok { + return invalidOp, 0, 0, oTokens, false + } + + endPort, ok = lookupPort(protocol, port) + if !ok { + return invalidOp, 0, 0, oTokens, false + } + } + + return op, startPort, endPort, tokens, true +} + +func getIcmpRule(oTokens []string) (icmpRule, []string, bool) { + rule := icmpRule{invalidIcmpTypeCode, invalidIcmpTypeCode} + token, tokens, ok := nextToken(oTokens) + if !ok { + return rule, oTokens, false + } + + if icmpType, found := icmpTypes[token]; found { + rule.iType = int16(icmpType) + } else { + num, err := strconv.ParseUint(token, 10, 8) + if err != nil { + return rule, oTokens, false + } + + rule.iType = int16(num) + } + + oTokens = tokens + token, tokens, ok = nextToken(tokens) + if !ok { + return rule, oTokens, true + } + + num, err := strconv.ParseUint(token, 10, 8) + if err != nil { + return rule, oTokens, true + } + + rule.code = int16(num) + return rule, tokens, true +} + +func lookupPort(protocol int16, port string) (uint16, bool) { + num, err := strconv.ParseUint(port, 10, 16) + if err != nil { + return 0, false + } + + return uint16(num), true +} + +func parseIpv4(ip string) (uint32, bool) { + a, i, ok := dtoi(ip) + if !ok { + return 0, false + } + + ip = ip[i+1:] + b, i, ok := dtoi(ip) + if !ok { + return 0, false + } + + ip = ip[i+1:] + c, i, ok := dtoi(ip) + if !ok { + return 0, false + } + + ip = ip[i+1:] + d, i, ok := dtoi(ip) + if !ok { + return 0, false + } + + return toIpv4(byte(a), byte(b), byte(c), byte(d)), true +} + +// Decimal to integer. +// Returns number, characters consumed, success. +func dtoi(s string) (n int, i int, ok bool) { + n = 0 + for i = 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ { + n = n*10 + int(s[i]-'0') + if n >= 256 { + return 256, i, false + } + } + + if i == 0 { + return 0, 0, false + } + + return n, i, true +} + +func twoPartAcl(tokens []string) RuleFunc { + if tokens[1] == "any" { + switch tokens[0] { + case "permit": + return RulePermit + case "deny": + return RuleDeny + } + } + + return nil +} diff --git a/filter/acls_parser_test.go b/filter/acls_parser_test.go new file mode 100644 index 0000000..9739595 --- /dev/null +++ b/filter/acls_parser_test.go @@ -0,0 +1,160 @@ +package filter + +import ( + "strings" + "testing" +) + +func TestParseIpv4(t *testing.T) { + ip, valid := parseIpv4("1.2.3.4") + if !valid { + t.Error("parseIpv4 says 1.2.3.4 is invalid") + } else { + if ip != toIpv4(1, 2, 3, 4) { + t.Error("parseIpv4 did not parse 1.2.3.4 properly") + } + } + + ip, valid = parseIpv4("0.0.0.0") + if !valid { + t.Error("parseIpv4 says 0.0.0.0 is invalid") + } else { + if ip != toIpv4(0, 0, 0, 0) { + t.Error("parseIpv4 did not parse 0.0.0.0 properly") + } + } + +} + +func TestGetSource(t *testing.T) { + net, mask, tokens, ok := getSource([]string{"host", "192.168.69.3"}) + if !ok { + t.Fatal("getSource failed") + } + + if net != toIpv4(192, 168, 69, 3) { + t.Fatal("getSource network is invalid") + } + + if mask != toIpv4(255, 255, 255, 255) { + t.Fatal("getSource mask is invalid") + } + + if len(tokens) != 0 { + t.Fatal("getSource tokens is invalid") + } + +} + +func TestGetProtocol(t *testing.T) { + protocol, tokens, ok := getProtocol([]string{"ip", "any", "any"}) + if !ok { + t.Fatal("getProtocol failed") + } + + if protocol != allProtocols { + t.Fatal("getProtocol did not parse protocol") + } + + if len(tokens) != 2 { + t.Fatal("getProtocol did not parse tokens") + } + + protocol, tokens, ok = getProtocol([]string{"54", "any", "any"}) + if !ok { + t.Fatal("getProtocol failed") + } + + if protocol != 54 { + t.Fatal("getProtocol did not parse protocol") + } + + if len(tokens) != 2 { + t.Fatal("getProtocol did not parse tokens") + } + +} + +type getPortExpected struct { + op int + tokens []string + startPort uint16 + endPort uint16 + ok bool +} + +func TestGetPort(t *testing.T) { + tests := []struct { + protocol int16 + input string + expected getPortExpected + }{ + {6, "eq 123 any", getPortExpected{portOpEq, []string{"any"}, 123, 123, true}}, + {6, "neq 123 any", getPortExpected{portOpNeq, []string{"any"}, 123, 123, true}}, + {6, "gt 123 any", getPortExpected{portOpGt, []string{"any"}, 123, 123, true}}, + {6, "lt 123 any", getPortExpected{portOpLt, []string{"any"}, 123, 123, true}}, + {6, "range 123 125 any", getPortExpected{portOpRange, []string{"any"}, 123, 125, true}}, + {6, "range 123 any", getPortExpected{invalidOp, []string{"range", "123", "any"}, 0, 0, false}}, + {6, "eq any", getPortExpected{invalidOp, []string{"eq", "any"}, 0, 0, false}}, + } + + for _, test := range tests { + op, startPort, endPort, tokens, ok := getPort(test.protocol, strings.Fields(test.input)) + e := test.expected + + if ok != e.ok { + t.Fatalf("Expected value for ok failed for '%s'", test.input) + } + + if op != e.op { + t.Fatalf("Expected value for op failed for '%s' expected '%d' got '%d'", test.input, e.op, op) + } + + if startPort != e.startPort { + t.Fatalf("Expected value for startPort failed for '%s' expected '%d' got '%d'", test.input, e.startPort, startPort) + } + + if endPort != e.endPort { + t.Fatalf("Expected value for endPort failed for '%s' expected '%d' got '%d'", test.input, e.endPort, endPort) + } + + if len(tokens) != len(e.tokens) { + t.Fatalf("Expected value for tokens failed for '%s' expected '%d' got '%d'", test.input, len(e.tokens), len(tokens)) + } + + } + +} + +func TestGetIcmpRule(t *testing.T) { + tests := []struct { + acl string + rule icmpRule + tokens []string + ok bool + }{ + {"echo", icmpRule{8, -1}, []string{}, true}, + {"echo 5", icmpRule{8, 5}, []string{}, true}, + {"bob", icmpRule{-1, -1}, []string{"bob"}, false}, + {"echo bob", icmpRule{8, -1}, []string{"bob"}, true}, + } + for _, test := range tests { + acl := test.acl + icmpRule, tokens, ok := getIcmpRule(strings.Fields(acl)) + if ok != test.ok { + t.Fatalf("Expected value for ok failed for '%s'", acl) + } + + if icmpRule.iType != test.rule.iType { + t.Fatalf("Expected value for iType failed for '%s' expected '%d' got '%d'", acl, test.rule.iType, icmpRule.iType) + } + + if icmpRule.code != test.rule.code { + t.Fatalf("Expected value for code failed for '%s' expected '%d' got '%d'", acl, test.rule.code, icmpRule.code) + } + + if len(tokens) != len(test.tokens) { + t.Fatalf("Expected value for len tokens failed for '%s' expected '%d' got '%d'", acl, len(test.tokens), len(tokens)) + } + } +} diff --git a/filter/errors.go b/filter/errors.go new file mode 100644 index 0000000..7266d2b --- /dev/null +++ b/filter/errors.go @@ -0,0 +1,7 @@ +package filter + +import ( + "errors" +) + +var ErrDenyAll = errors.New("Deny All") diff --git a/filter/icmp_packets_test.go b/filter/icmp_packets_test.go new file mode 100644 index 0000000..232bf34 --- /dev/null +++ b/filter/icmp_packets_test.go @@ -0,0 +1,97 @@ +package filter + +var icmpPacket1 = []byte{ + 0x45, 0x00, + 0x00, 0x3c, 0xd7, 0x43, 0x00, 0x00, 0x80, 0x01, + 0x2b, 0x73, 0xc0, 0xa8, 0x9e, 0x8b, 0xae, 0x89, + 0x2a, 0x4d, 0x08, 0x00, 0x2a, 0x5c, 0x02, 0x00, + 0x21, 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, + 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, +} + +var icmpPacket2 = []byte{ + 0x45, 0x00, + 0x00, 0x3c, 0x76, 0xe1, 0x00, 0x00, 0x80, 0x01, + 0x8b, 0xd5, 0xae, 0x89, 0x2a, 0x4d, 0xc0, 0xa8, + 0x9e, 0x8b, 0x00, 0x00, 0x32, 0x5c, 0x02, 0x00, + 0x21, 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, + 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, +} + +var icmpPacket3 = []byte{ + 0x45, 0x00, + 0x00, 0x3c, 0xd7, 0x46, 0x00, 0x00, 0x80, 0x01, + 0x2b, 0x70, 0xc0, 0xa8, 0x9e, 0x8b, 0xae, 0x89, + 0x2a, 0x4d, 0x08, 0x00, 0x29, 0x5c, 0x02, 0x00, + 0x22, 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, + 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, +} + +var icmpPacket4 = []byte{ + 0x45, 0x00, + 0x00, 0x3c, 0x76, 0xe4, 0x00, 0x00, 0x80, 0x01, + 0x8b, 0xd2, 0xae, 0x89, 0x2a, 0x4d, 0xc0, 0xa8, + 0x9e, 0x8b, 0x00, 0x00, 0x31, 0x5c, 0x02, 0x00, + 0x22, 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, + 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, +} + +var icmpPacket5 = []byte{ + 0x45, 0x00, + 0x00, 0x3c, 0xd7, 0x49, 0x00, 0x00, 0x80, 0x01, + 0x2b, 0x6d, 0xc0, 0xa8, 0x9e, 0x8b, 0xae, 0x89, + 0x2a, 0x4d, 0x08, 0x00, 0x28, 0x5c, 0x02, 0x00, + 0x23, 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, + 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, +} + +var icmpPacket6 = []byte{ + 0x45, 0x00, + 0x00, 0x3c, 0x76, 0xf0, 0x00, 0x00, 0x80, 0x01, + 0x8b, 0xc6, 0xae, 0x89, 0x2a, 0x4d, 0xc0, 0xa8, + 0x9e, 0x8b, 0x00, 0x00, 0x30, 0x5c, 0x02, 0x00, + 0x23, 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, + 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, +} + +var icmpPacket7 = []byte{ + 0x45, 0x00, + 0x00, 0x3c, 0xd7, 0x4e, 0x00, 0x00, 0x80, 0x01, + 0x2b, 0x68, 0xc0, 0xa8, 0x9e, 0x8b, 0xae, 0x89, + 0x2a, 0x4d, 0x08, 0x00, 0x27, 0x5c, 0x02, 0x00, + 0x24, 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, + 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, +} + +var icmpPacket8 = []byte{ + 0x45, 0x00, + 0x00, 0x3c, 0x76, 0xf5, 0x00, 0x00, 0x80, 0x01, + 0x8b, 0xc1, 0xae, 0x89, 0x2a, 0x4d, 0xc0, 0xa8, + 0x9e, 0x8b, 0x00, 0x00, 0x2f, 0x5c, 0x02, 0x00, + 0x24, 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, + 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, +} diff --git a/filter/rule.go b/filter/rule.go index 4ac1ad2..70aa2ad 100644 --- a/filter/rule.go +++ b/filter/rule.go @@ -1,5 +1,7 @@ package filter +import () + type RuleCmd int const ( @@ -8,6 +10,46 @@ const ( Permit = iota ) +type Rules []RuleFunc + +func (rules Rules) PassDefaultDeny(p []byte) bool { + if len(rules) == 0 { + return true + } + + for _, rule := range rules { + cmd := rule(p) + if cmd != Skip { + return cmd == Permit + } + } + + return false +} + +func (rules Rules) PassDefaultPermit(p []byte) bool { + if len(rules) == 0 { + return true + } + + for _, rule := range rules { + cmd := rule(p) + if cmd != Skip { + return cmd == Permit + } + } + + return true +} + +func (rules Rules) Filter(p []byte) error { + if rules.PassDefaultDeny(p) { + return nil + } + + return ErrDenyAll +} + type RuleFunc func([]byte) RuleCmd func RulePermit([]byte) RuleCmd { @@ -48,56 +90,280 @@ func PermitAllProto(proto byte) RuleFunc { }) } +func protoRule(proto byte, cmd RuleCmd) RuleFunc { + return RuleFunc(func(p []byte) RuleCmd { + if p[9] == proto { + return cmd + } + + return Skip + }) +} + func toIpv4(a, b, c, d uint8) uint32 { return (uint32(a) << 24) | (uint32(b) << 16) | (uint32(c) << 8) | uint32(d) } func PermitSrcIpRule(network, mask uint32) RuleFunc { + return singleIpRule(Ipv4NetworkMask{network, mask}, Permit, 12) +} + +func DenySrcIpRule(network, mask uint32) RuleFunc { + return singleIpRule(Ipv4NetworkMask{network, mask}, Deny, 12) +} + +func PermitDstIpRule(network, mask uint32) RuleFunc { + return singleIpRule(Ipv4NetworkMask{network, mask}, Permit, 16) +} + +func DenyDstIpRule(network, mask uint32) RuleFunc { + return singleIpRule(Ipv4NetworkMask{network, mask}, Deny, 16) +} + +func portProtoRule(portRule portOp, proto byte, cmd RuleCmd, offset int) RuleFunc { return RuleFunc(func(p []byte) RuleCmd { - srcIpv4NetworkMask := Ipv4NetworkMask{network, mask} - if srcIpv4NetworkMask.Match(toIpv4(p[12], p[13], p[14], p[15])) { - return Permit + if proto == p[9] { + hlength := (p[0] & 0x0F) << 2 + data := p[hlength:] + if portRule.Match(data, offset) { + return cmd + } } + return Skip }) } -func DenySrcIpRule(network, mask uint32) RuleFunc { +func singleIpRule(networkMask Ipv4NetworkMask, cmd RuleCmd, offset int) RuleFunc { return RuleFunc(func(p []byte) RuleCmd { - srcIpv4NetworkMask := Ipv4NetworkMask{network, mask} - if srcIpv4NetworkMask.Match(toIpv4(p[12], p[13], p[14], p[15])) { - return Deny + if networkMask.MatchBytes(p, offset) { + return cmd } return Skip }) } -func PermitDstIpRule(network, mask uint32) RuleFunc { +func netmaskProtoRule(nm Ipv4NetworkMask, proto byte, cmd RuleCmd, offset int) RuleFunc { return RuleFunc(func(p []byte) RuleCmd { - srcIpv4NetworkMask := Ipv4NetworkMask{network, mask} - if srcIpv4NetworkMask.Match(toIpv4(p[16], p[17], p[18], p[19])) { - return Permit + if p[9] == proto && nm.MatchBytes(p, offset) { + return cmd } + return Skip }) } -func DenyDstIpRule(network, mask uint32) RuleFunc { +func srcDstProtoRule(src, dst Ipv4NetworkMask, proto byte, cmd RuleCmd) RuleFunc { + return RuleFunc(func(p []byte) RuleCmd { + if proto == p[9] && src.MatchSrc(p) && dst.MatchDst(p) { + return cmd + } + + return Skip + }) +} + +func toPort(a, b byte) uint16 { + return (uint16(a) << 8) | uint16(b) +} + +func srcDstProtoPortRule(src, dst Ipv4NetworkMask, proto byte, port uint16, cmd RuleCmd, offset int) RuleFunc { + return RuleFunc(func(p []byte) RuleCmd { + if proto == p[9] { + hlength := (p[0] & 0x0F) << 2 + data := p[hlength:] + if src.MatchSrc(p) && dst.MatchDst(p) && toPort(data[offset], data[offset+1]) == port { + return cmd + } + } + + return Skip + }) +} + +func srcDstProtoSrcPortDstPort(src, dst Ipv4NetworkMask, proto byte, srcPort, dstPort portOp, cmd RuleCmd) RuleFunc { return RuleFunc(func(p []byte) RuleCmd { - srcIpv4NetworkMask := Ipv4NetworkMask{network, mask} - if srcIpv4NetworkMask.Match(toIpv4(p[16], p[17], p[18], p[19])) { - return Deny + if proto == p[9] { + hlength := (p[0] & 0x0F) << 2 + data := p[hlength:] + if src.MatchSrc(p) && dst.MatchDst(p) && srcPort.Match(data, 0) && dstPort.Match(data, 2) { + return cmd + } } return Skip }) } +func srcDstRule(src, dst Ipv4NetworkMask, cmd RuleCmd) RuleFunc { + return RuleFunc(func(p []byte) RuleCmd { + if src.MatchSrc(p) && dst.MatchDst(p) { + return cmd + } + + return Skip + }) +} + +func icmpRuleRule(rule icmpRule, cmd RuleCmd) RuleFunc { + switch { + default: + return protoRule(1, cmd) + case rule.iType != invalidIcmpTypeCode && rule.code != invalidIcmpTypeCode: + return RuleFunc(func(p []byte) RuleCmd { + if p[9] == 1 { + hlength := (p[0] & 0x0F) << 2 + data := p[hlength:] + if data[0] == byte(rule.iType) && data[1] == byte(rule.code) { + return cmd + } + } + + return Skip + }) + case rule.iType != invalidIcmpTypeCode && rule.code == invalidIcmpTypeCode: + return RuleFunc(func(p []byte) RuleCmd { + if p[9] == 1 { + hlength := (p[0] & 0x0F) << 2 + data := p[hlength:] + if data[0] == byte(rule.iType) { + return cmd + } + } + + return Skip + }) + case rule.iType == invalidIcmpTypeCode && rule.code != invalidIcmpTypeCode: + return RuleFunc(func(p []byte) RuleCmd { + if p[9] == 1 { + hlength := (p[0] & 0x0F) << 2 + data := p[hlength:] + if data[1] == byte(rule.code) { + return cmd + } + } + + return Skip + }) + } +} + +func netMaskIcmpRuleRule(nm Ipv4NetworkMask, rule icmpRule, cmd RuleCmd, offset int) RuleFunc { + switch { + default: + return netmaskProtoRule(nm, 1, cmd, offset) + case rule.iType != invalidIcmpTypeCode && rule.code != invalidIcmpTypeCode: + return RuleFunc(func(p []byte) RuleCmd { + if p[9] == 1 { + if nm.MatchBytes(p, offset) { + hlength := (p[0] & 0x0F) << 2 + data := p[hlength:] + if data[0] == byte(rule.iType) && data[1] == byte(rule.code) { + return cmd + } + } + } + + return Skip + }) + case rule.iType != invalidIcmpTypeCode && rule.code == invalidIcmpTypeCode: + return RuleFunc(func(p []byte) RuleCmd { + if p[9] == 1 { + if nm.MatchBytes(p, offset) { + hlength := (p[0] & 0x0F) << 2 + data := p[hlength:] + if data[0] == byte(rule.iType) { + return cmd + } + } + } + + return Skip + }) + case rule.iType == invalidIcmpTypeCode && rule.code != invalidIcmpTypeCode: + return RuleFunc(func(p []byte) RuleCmd { + if p[9] == 1 { + if nm.MatchBytes(p, offset) { + hlength := (p[0] & 0x0F) << 2 + data := p[hlength:] + if data[1] == byte(rule.code) { + return cmd + } + } + } + + return Skip + }) + } +} + +func srcDstIcmpRuleRule(src, dst Ipv4NetworkMask, rule icmpRule, cmd RuleCmd, offset int) RuleFunc { + switch { + default: + return srcDstProtoRule(src, dst, 1, cmd) + case rule.iType != invalidIcmpTypeCode && rule.code != invalidIcmpTypeCode: + return RuleFunc(func(p []byte) RuleCmd { + if p[9] == 1 { + if src.MatchSrc(p) && dst.MatchDst(p) { + hlength := (p[0] & 0x0F) << 2 + data := p[hlength:] + if data[0] == byte(rule.iType) && data[1] == byte(rule.code) { + return cmd + } + } + } + + return Skip + }) + case rule.iType != invalidIcmpTypeCode && rule.code == invalidIcmpTypeCode: + return RuleFunc(func(p []byte) RuleCmd { + if p[9] == 1 { + if src.MatchSrc(p) && dst.MatchDst(p) { + hlength := (p[0] & 0x0F) << 2 + data := p[hlength:] + if data[0] == byte(rule.iType) { + return cmd + } + } + } + + return Skip + }) + case rule.iType == invalidIcmpTypeCode && rule.code != invalidIcmpTypeCode: + return RuleFunc(func(p []byte) RuleCmd { + if p[9] == 1 { + if src.MatchSrc(p) && dst.MatchDst(p) { + hlength := (p[0] & 0x0F) << 2 + data := p[hlength:] + if data[1] == byte(rule.code) { + return cmd + } + } + } + + return Skip + }) + } +} + type Ipv4NetworkMask struct { Network, Mask uint32 } +func (nm Ipv4NetworkMask) MatchBytes(p []byte, offset int) bool { + return nm.Match(toIpv4(p[offset], p[offset+1], p[offset+2], p[offset+3])) +} + +func (nm Ipv4NetworkMask) MatchSrc(p []byte) bool { + return nm.MatchBytes(p, SRC_IP_OFFSET) +} + +func (nm Ipv4NetworkMask) MatchDst(p []byte) bool { + return nm.MatchBytes(p, DST_IP_OFFSET) +} + func (nm Ipv4NetworkMask) Match(ip uint32) bool { return (ip & nm.Mask) == nm.Network } + diff --git a/filter/rule_test.go b/filter/rule_test.go index 73148a8..1f050ea 100644 --- a/filter/rule_test.go +++ b/filter/rule_test.go @@ -102,7 +102,7 @@ func TestIpv4NetworkMask(t *testing.T) { func TestPermitSrcIpRule(t *testing.T) { rule := PermitSrcIpRule(toIpv4(192, 168, 69, 0), toIpv4(255, 255, 255, 0)) if rule(rulePackets[0]) != Permit { - t.Error("Packet was not allowed") + t.Error("Packet was not permited") } } @@ -123,6 +123,117 @@ func TestDenyDstIpRule(t *testing.T) { func TestPermitDstIpRule(t *testing.T) { rule := PermitDstIpRule(toIpv4(192, 168, 68, 0), toIpv4(255, 255, 255, 0)) if rule(rulePackets[0]) != Permit { - t.Error("Packet was not denied") + t.Error("Packet was not permited") + } +} + +func TestPermitAny(t *testing.T) { + rules := AclsToRules("permit any") + if !rules.PassDefaultDeny(rulePackets[0]) { + t.Error("permit any failed") + } + + rules = AclsToRules("permit 0.0.0.0 255.255.255.255") + if !rules.PassDefaultDeny(rulePackets[0]) { + t.Error("permit any failed") + } +} + +func TestPermitHost(t *testing.T) { + rules := AclsToRules("permit host 192.168.69.3") + if !rules.PassDefaultDeny(rulePackets[0]) { + t.Error("permit host 192.169.68.3 failed") + } +} + +func TestDenyAny(t *testing.T) { + rules := AclsToRules("deny any") + if rules.PassDefaultPermit(rulePackets[0]) { + t.Error("deny any failed") + } +} + +func TestPermitSrcDstPortProto(t *testing.T) { + rules := AclsToRules("permit tcp any any eq 80") + if rules.PassDefaultDeny(rulePackets[0]) { + t.Error("permit tcp any any eq 80 failed") + } +} + +func TestSimpleHost(t *testing.T) { + acls := []string{ + "permit host 192.168.69.3", + "permit 192.168.69.3 0.0.0.0", + "permit ip 192.168.69.3 0.0.0.0 any", + } + + for _, acl := range acls { + rules := AclsToRules(acl) + if !rules.PassDefaultDeny(rulePackets[0]) { + t.Errorf("acl '%s' failed", acl) + } + } +} + +func TestIcmpPermitAny(t *testing.T) { + acls := []string{ + "permit icmp any any", + } + + for _, acl := range acls { + rules := AclsToRules(acl) + if !rules.PassDefaultDeny(icmpPacket1) { + t.Errorf("acl '%s' failed", acl) + } + + if rules.PassDefaultDeny(rulePackets[0]) { + t.Errorf("acl '%s' passed", acl) + } + } +} + +func TestIcmpPermit(t *testing.T) { + acls := []string{ + "permit icmp any any echo", + "permit icmp any any echo 0", + } + + for _, acl := range acls { + rules := AclsToRules(acl) + if !rules.PassDefaultDeny(icmpPacket1) { + t.Errorf("acl '%s' failed", acl) + } + + } + + for _, acl := range acls { + rules := AclsToRules(acl) + if rules.PassDefaultDeny(icmpPacket2) { + t.Errorf("acl '%s' passed", acl) + } + + } +} + +func TestIcmpDeny(t *testing.T) { + acls := []string{ + "deny icmp any any echo", + "deny icmp any any echo 0", + } + + for _, acl := range acls { + rules := AclsToRules(acl) + if rules.PassDefaultDeny(icmpPacket1) { + t.Errorf("acl '%s' passed", acl) + } + + } + + for _, acl := range acls { + rules := AclsToRules(acl) + if !rules.PassDefaultPermit(icmpPacket2) { + t.Errorf("acl '%s' deny", acl) + } + } } From d723343c5dce69d0fe52882c0a3066e62758caf4 Mon Sep 17 00:00:00 2001 From: James Rouzier Date: Wed, 24 Feb 2021 06:48:56 -0500 Subject: [PATCH 04/20] use rules instead PortFilter --- main_shared.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main_shared.go b/main_shared.go index 9a8cbec..1aee21a 100644 --- a/main_shared.go +++ b/main_shared.go @@ -150,7 +150,7 @@ func startInverse(interfaceName string, device *device.Device) { go networkConnection.Start() - filter := filter.NewFilterFromAcls(profile.ACLs) + filter := filter.AclsToRulesFilter(profile.ACLs, nil, nil) device.SetReceiveFilter(filter) for _, peerID := range profile.AllowedPeers { From 5c1327ca0547b3946c1c4000ebf8f338318820b0 Mon Sep 17 00:00:00 2001 From: James Rouzier Date: Fri, 16 Apr 2021 13:59:56 -0400 Subject: [PATCH 05/20] Keepalive packets 'empty' are always allowed --- filter/rule.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/filter/rule.go b/filter/rule.go index 70aa2ad..b07ff40 100644 --- a/filter/rule.go +++ b/filter/rule.go @@ -43,6 +43,10 @@ func (rules Rules) PassDefaultPermit(p []byte) bool { } func (rules Rules) Filter(p []byte) error { + if len(p) == 0 { + return nil + } + if rules.PassDefaultDeny(p) { return nil } From 748856820e8a1e7915273d72b59ba76a2bd1483e Mon Sep 17 00:00:00 2001 From: Julien Semaan Date: Fri, 16 Apr 2021 14:14:16 -0400 Subject: [PATCH 06/20] started work on RBAC filter --- filter/rbac_filter.go | 24 ++++++++++++++++++++++++ go.mod | 2 +- main_shared.go | 4 +++- 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 filter/rbac_filter.go diff --git a/filter/rbac_filter.go b/filter/rbac_filter.go new file mode 100644 index 0000000..dd5a23c --- /dev/null +++ b/filter/rbac_filter.go @@ -0,0 +1,24 @@ +package filter + +import ( + "fmt" + + "github.com/davecgh/go-spew/spew" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/inverse-inc/packetfence/go/unifiedapiclient" +) + +func BuildRBACFilter(apiClient *unifiedapiclient.Client) RuleFunc { + return func(data []byte) RuleCmd { + spew.Dump(data) + packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.Default) + if ip4Layer := packet.Layer(layers.LayerTypeIPv4); ip4Layer != nil { + ip4 := ip4Layer.(*layers.IPv4) + spew.Dump(ip4.SrcIP, ip4.DstIP) + } else { + fmt.Println("Not an ipv4 packet") + } + return Permit + } +} diff --git a/go.mod b/go.mod index 1614e52..3f2ade8 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/getlantern/systray v1.0.4 github.com/gin-gonic/gin v1.6.2 github.com/golang/protobuf v1.4.3 - github.com/google/gopacket v1.1.19 // indirect + github.com/google/gopacket v1.1.19 github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 github.com/infobloxopen/go-trees v0.0.0-20190313150506-2af4e13f9062 github.com/inverse-inc/coredns-caddy v1.1.1-0.20201218123235-18d2d15a1452 diff --git a/main_shared.go b/main_shared.go index 1aee21a..a6b6c11 100644 --- a/main_shared.go +++ b/main_shared.go @@ -16,6 +16,7 @@ import ( "syscall" "time" + "github.com/davecgh/go-spew/spew" "github.com/inverse-inc/packetfence/go/remoteclients" "github.com/inverse-inc/packetfence/go/sharedutils" "github.com/inverse-inc/wireguard-go/binutils" @@ -150,7 +151,8 @@ func startInverse(interfaceName string, device *device.Device) { go networkConnection.Start() - filter := filter.AclsToRulesFilter(profile.ACLs, nil, nil) + filter := filter.AclsToRulesFilter(profile.ACLs, filter.BuildRBACFilter(ztn.APIClient), nil) + spew.Dump(filter) device.SetReceiveFilter(filter) for _, peerID := range profile.AllowedPeers { From 511d866ef625c62d7ec21bc64bd94be9edb153c4 Mon Sep 17 00:00:00 2001 From: Julien Semaan Date: Mon, 19 Apr 2021 12:06:48 -0400 Subject: [PATCH 07/20] implement RBAC filter with API call --- filter/rbac_filter.go | 27 ++++++++++++++++++++------- go.mod | 4 ++-- go.sum | 26 ++++++++++++++++++++++++++ main_shared.go | 9 ++++++--- 4 files changed, 54 insertions(+), 12 deletions(-) diff --git a/filter/rbac_filter.go b/filter/rbac_filter.go index dd5a23c..c597dc8 100644 --- a/filter/rbac_filter.go +++ b/filter/rbac_filter.go @@ -1,23 +1,36 @@ package filter import ( + "context" "fmt" + "sync/atomic" - "github.com/davecgh/go-spew/spew" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/inverse-inc/packetfence/go/unifiedapiclient" ) -func BuildRBACFilter(apiClient *unifiedapiclient.Client) RuleFunc { +func BuildRBACFilter(apiClientCtx context.Context, apiClient *unifiedapiclient.Client, mode *uint32) RuleFunc { return func(data []byte) RuleCmd { - spew.Dump(data) - packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.Default) + if atomic.LoadUint32(mode) == 0 { + return Permit + } + packet := gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.Default) if ip4Layer := packet.Layer(layers.LayerTypeIPv4); ip4Layer != nil { ip4 := ip4Layer.(*layers.IPv4) - spew.Dump(ip4.SrcIP, ip4.DstIP) - } else { - fmt.Println("Not an ipv4 packet") + fmt.Println("from", ip4.SrcIP, "to", ip4.DstIP) + res := struct { + Permit bool `json:"permit"` + }{} + err := apiClient.Call(apiClientCtx, "GET", fmt.Sprintf("/api/v1/remote_clients/allowed_ip_communication?src_ip=%s&dst_ip=%s", ip4.SrcIP, ip4.DstIP), &res) + if err != nil { + //TODO: add debounced DEBUG logging for that src+dst IP couple + return Deny + } + if !res.Permit { + //TODO: add debounced INFO logging for that src+dst IP couple + return Deny + } } return Permit } diff --git a/go.mod b/go.mod index 3f2ade8..3ec5c25 100644 --- a/go.mod +++ b/go.mod @@ -19,8 +19,8 @@ require ( github.com/infobloxopen/go-trees v0.0.0-20190313150506-2af4e13f9062 github.com/inverse-inc/coredns-caddy v1.1.1-0.20201218123235-18d2d15a1452 github.com/inverse-inc/go-dnschange v0.0.0-20210426140614-f23e42334797 - github.com/inverse-inc/packetfence v10.2.1-0.20210121141640-49103b1c822f+incompatible - github.com/inverse-inc/packetfence/go v0.0.0-20210121141640-49103b1c822f + github.com/inverse-inc/packetfence v10.2.1-0.20210419153453-d96def7abcc5+incompatible + github.com/inverse-inc/packetfence/go v0.0.0-20210419153453-d96def7abcc5 github.com/inverse-inc/upnp v0.0.0-20201222212413-bcc73e2a6122 github.com/jackpal/gateway v1.0.6 github.com/jackpal/go-nat-pmp v1.0.2 diff --git a/go.sum b/go.sum index f1d3bfe..f7bc437 100644 --- a/go.sum +++ b/go.sum @@ -92,6 +92,7 @@ github.com/DataDog/datadog-go v3.5.0+incompatible h1:AShr9cqkF+taHjyQgcBcQUt/ZNK github.com/DataDog/datadog-go v3.5.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/datadog-go v4.2.0+incompatible h1:Q73jzyKHwyA04Gf4SSukRF+KR4wJEimU6tAuU0B8Y4Y= github.com/DataDog/datadog-go v4.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I= @@ -101,9 +102,11 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.7/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= +github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Sereal/Sereal v0.0.0-20161214074320-36f2c9f6f409/go.mod h1:D0JMgToj/WdxCgd30Kc1UcA9E+WdZoJqeVOuYW7iTBM= +github.com/Sereal/Sereal v0.0.0-20200729022450-08708a3c86f3/go.mod h1:D0JMgToj/WdxCgd30Kc1UcA9E+WdZoJqeVOuYW7iTBM= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/sarama v1.24.0/go.mod h1:fGP8eQ6PugKEI0iUETYYtnP6d1pH/bdDMTel1X5ajsU= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= @@ -123,6 +126,7 @@ github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/apache/thrift v0.12.0 h1:pODnxUFNcjP9UTLZGTdeh+j16A8lJbRvD3rOtrk/7bs= github.com/apache/thrift v0.12.0 h1:pODnxUFNcjP9UTLZGTdeh+j16A8lJbRvD3rOtrk/7bs= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -156,6 +160,7 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bifurcation/mint v0.0.0-20180715133206-93c51c6ce115/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw= github.com/caddyserver/caddy v1.0.3/go.mod h1:G+ouvOY32gENkJC+jhgl62TyhvqEsFaDiZ4uw0RzP1E= github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMSc6E5ydlp5NIonxObaeu/Iub/X03EKPVYo= @@ -222,6 +227,7 @@ github.com/decker502/dnspod-go v0.2.0/go.mod h1:qsurYu1FgxcDwfSwXJdLt4kRsBLZeosE github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgfR0jcVVXuTsYv/erY2NnEDqwRojbxR1rBYA= github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgfR0jcVVXuTsYv/erY2NnEDqwRojbxR1rBYA= github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= +github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-csnappy v0.0.0-20150721071530-9d18391da197/go.mod h1:pfVCdwZVFFTOLP28KMboesC928sunsjfJ5xnLQojTsM= @@ -326,6 +332,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200625191551-73d3c3675aa3 h1:q521PfSp5/ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200625191551-73d3c3675aa3/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-ini/ini v1.44.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -358,6 +365,8 @@ github.com/go-redis/redis v0.0.0-20190325112110-a679e614427a/go.mod h1:NAIEuMOZ/ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.5/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= @@ -373,6 +382,7 @@ github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptG github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8= github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw= github.com/golang-collections/go-datastructures v0.0.0-20150211160725-59788d5eb259/go.mod h1:9Qcha0gTWLw//0VNka1Cbnjvg3pNKGFdAm7E9sBabxE= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -445,7 +455,9 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= github.com/goreleaser/goreleaser v0.134.0/go.mod h1:ZT6Y2rSYa6NxQzIsdfWWNWAlYGXGbreo66NmE+3X3WQ= github.com/goreleaser/nfpm v1.2.1/go.mod h1:TtWrABZozuLOttX2uDlYyECfQX7x5XYkVxhjYcR6G9w= +github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.4.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/rpc v0.0.0-20160927134711-22c016f3df3f/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= @@ -454,6 +466,7 @@ github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY github.com/gorilla/websocket v1.0.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd5rUMdNogn35MWXBX1UiBigrU8eTj8DoAC2c= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= @@ -657,6 +670,8 @@ github.com/inverse-inc/packetfence v10.2.1-0.20201204184054-13570c183c25+incompa github.com/inverse-inc/packetfence v10.2.1-0.20201204184054-13570c183c25+incompatible/go.mod h1:RWR1ck6w1QYjFQuI9JeWJ5sTGAeg9hBBMDFX2/TGeL0= github.com/inverse-inc/packetfence v10.2.1-0.20210121141640-49103b1c822f+incompatible h1:1XI2vtslL4kmBsHvkmR7z3eJI3NRtrW98GRamTN74M0= github.com/inverse-inc/packetfence v10.2.1-0.20210121141640-49103b1c822f+incompatible/go.mod h1:RWR1ck6w1QYjFQuI9JeWJ5sTGAeg9hBBMDFX2/TGeL0= +github.com/inverse-inc/packetfence v10.2.1-0.20210419153453-d96def7abcc5+incompatible h1:VEA24pi6tx67iz9INHWBYdK8QFbgJSqamuVRPW5xVSg= +github.com/inverse-inc/packetfence v10.2.1-0.20210419153453-d96def7abcc5+incompatible/go.mod h1:RWR1ck6w1QYjFQuI9JeWJ5sTGAeg9hBBMDFX2/TGeL0= github.com/inverse-inc/packetfence/go v0.0.0-20200724130522-9906e3b0ef36 h1:6mQM09YcCuLCzLr/hGLmldoT5jHHtM+a7G3v5NNYcY0= github.com/inverse-inc/packetfence/go v0.0.0-20200724130522-9906e3b0ef36/go.mod h1:WlVGQDgT+HJRzbbMUMJB3tC5VZJnA1WO1GKXgq9lymc= github.com/inverse-inc/packetfence/go v0.0.0-20200729120005-d8f9c1cc13a4 h1:3T4SNX5VJeQyjC3YqFY9DefZnbIO3uN9jBADKBDfvPI= @@ -733,6 +748,10 @@ github.com/inverse-inc/packetfence/go v0.0.0-20201214182518-cf77f7346ad3 h1:x1PR github.com/inverse-inc/packetfence/go v0.0.0-20201214182518-cf77f7346ad3/go.mod h1:Ka7Ebm6cKPsibHZID3n8wdHWm6B+5nlwUCtIZeyOT9U= github.com/inverse-inc/packetfence/go v0.0.0-20210121141640-49103b1c822f h1:0MA33jQkoY1KxsdXylBxdC5cTaSGWCLeXGEp/GbtswU= github.com/inverse-inc/packetfence/go v0.0.0-20210121141640-49103b1c822f/go.mod h1:Ka7Ebm6cKPsibHZID3n8wdHWm6B+5nlwUCtIZeyOT9U= +github.com/inverse-inc/packetfence/go v0.0.0-20210419153453-d96def7abcc5 h1:hmS/bCZ+ATqH8WgqSWPgVLR1nJzDtjcYGF5YKJrJTJU= +github.com/inverse-inc/packetfence/go v0.0.0-20210419153453-d96def7abcc5/go.mod h1:xWtaMyTQldwhira3BT1efiak8NlTDp1EDS6hgTuknfM= +github.com/inverse-inc/pkcs7 v0.0.0-20210120195334-f839fdbb1f84/go.mod h1:adBgAyadl5D9Mz9rhkQyzZpwcUK9eXdKslXH4oImbB0= +github.com/inverse-inc/scep v0.0.0-20210120201805-eb98f5654ee4/go.mod h1:bcHlFB5I8JqSU5Lqt+NatfA9y64T2fFt05QSFOEFJGI= github.com/inverse-inc/upnp v0.0.0-20161226025956-82caf20da2dd h1:j+hJYhZwl4Ftq7Mrhsa+SNBPxCHwk7okY5Ojq8Vi6CM= github.com/inverse-inc/upnp v0.0.0-20161226025956-82caf20da2dd h1:j+hJYhZwl4Ftq7Mrhsa+SNBPxCHwk7okY5Ojq8Vi6CM= github.com/inverse-inc/upnp v0.0.0-20161226025956-82caf20da2dd/go.mod h1:T3Dbxbxjg8EHW72Z8oegSpfY9CbiK3bMx0o1lTbJGCU= @@ -769,6 +788,8 @@ github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSn github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8= github.com/jinzhu/gorm v1.9.11 h1:gaHGvE+UnWGlbWG4Y3FUwY1EcZ5n6S9WtqBA/uySMLE= github.com/jinzhu/gorm v1.9.11/go.mod h1:bu/pK8szGZ2puuErfU0RwyeNdsf3e6nCX/noXaVxkfw= +github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= +github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= @@ -875,6 +896,7 @@ github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -1211,6 +1233,7 @@ golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88= golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1249,6 +1272,8 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20170726083632-f5079bd7f6f7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1310,6 +1335,7 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170728174421-0f826bdd13b5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/main_shared.go b/main_shared.go index a6b6c11..34fb70f 100644 --- a/main_shared.go +++ b/main_shared.go @@ -16,7 +16,6 @@ import ( "syscall" "time" - "github.com/davecgh/go-spew/spew" "github.com/inverse-inc/packetfence/go/remoteclients" "github.com/inverse-inc/packetfence/go/sharedutils" "github.com/inverse-inc/wireguard-go/binutils" @@ -151,8 +150,12 @@ func startInverse(interfaceName string, device *device.Device) { go networkConnection.Start() - filter := filter.AclsToRulesFilter(profile.ACLs, filter.BuildRBACFilter(ztn.APIClient), nil) - spew.Dump(filter) + var enableRBACFilter uint32 + if profile.RBACIPFiltering { + enableRBACFilter = 1 + } + + filter := filter.AclsToRulesFilter(profile.ACLs, filter.BuildRBACFilter(ztn.APIClientCtx, ztn.APIClient, &enableRBACFilter), nil) device.SetReceiveFilter(filter) for _, peerID := range profile.AllowedPeers { From f18e51d4e96b787b5af5fe01ce3ffc1f5afe0b75 Mon Sep 17 00:00:00 2001 From: Julien Semaan Date: Mon, 19 Apr 2021 13:21:32 -0400 Subject: [PATCH 08/20] cache the RBAC IP filtering result --- filter/rbac_filter.go | 64 +++++++++++++++++++++++++++++++------------ go.mod | 1 + go.sum | 1 + main_shared.go | 2 +- ztn/env_constants.go | 2 ++ 5 files changed, 51 insertions(+), 19 deletions(-) diff --git a/filter/rbac_filter.go b/filter/rbac_filter.go index c597dc8..ead9fb4 100644 --- a/filter/rbac_filter.go +++ b/filter/rbac_filter.go @@ -3,35 +3,63 @@ package filter import ( "context" "fmt" - "sync/atomic" + "time" "github.com/google/gopacket" "github.com/google/gopacket/layers" + "github.com/inverse-inc/packetfence/go/sharedutils" "github.com/inverse-inc/packetfence/go/unifiedapiclient" + "github.com/inverse-inc/wireguard-go/device" + "github.com/inverse-inc/wireguard-go/ztn" + "github.com/patrickmn/go-cache" ) -func BuildRBACFilter(apiClientCtx context.Context, apiClient *unifiedapiclient.Client, mode *uint32) RuleFunc { +var rbacAllowCache *cache.Cache + +func init() { + var cacheTime time.Duration + defaultCacheTime := 30 * time.Minute + cacheTimeEnv := sharedutils.EnvOrDefault(ztn.EnvRBACIPFilteringCacheTime, defaultCacheTime.String()) + if cacheTimeParsed, err := time.ParseDuration(cacheTimeEnv); err == nil { + cacheTime = cacheTimeParsed + } else { + cacheTime = defaultCacheTime + } + rbacAllowCache = cache.New(cacheTime, 1*time.Minute) +} + +func BuildRBACFilter(apiClientCtx context.Context, apiClient *unifiedapiclient.Client, logger *device.Logger, mode *uint32) RuleFunc { return func(data []byte) RuleCmd { - if atomic.LoadUint32(mode) == 0 { - return Permit - } + //if atomic.LoadUint32(mode) == 0 { + // return Permit + //} packet := gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.Default) if ip4Layer := packet.Layer(layers.LayerTypeIPv4); ip4Layer != nil { ip4 := ip4Layer.(*layers.IPv4) - fmt.Println("from", ip4.SrcIP, "to", ip4.DstIP) - res := struct { - Permit bool `json:"permit"` - }{} - err := apiClient.Call(apiClientCtx, "GET", fmt.Sprintf("/api/v1/remote_clients/allowed_ip_communication?src_ip=%s&dst_ip=%s", ip4.SrcIP, ip4.DstIP), &res) - if err != nil { - //TODO: add debounced DEBUG logging for that src+dst IP couple - return Deny - } - if !res.Permit { - //TODO: add debounced INFO logging for that src+dst IP couple - return Deny + k := fmt.Sprintf("%s->%s", ip4.SrcIP, ip4.DstIP) + if cacheRes, found := rbacAllowCache.Get(k); found { + return cacheRes.(RuleCmd) + } else { + apiRes := struct { + Permit bool `json:"permit"` + }{} + err := apiClient.Call(apiClientCtx, "GET", fmt.Sprintf("/api/v1/remote_clients/allowed_ip_communication?src_ip=%s&dst_ip=%s", ip4.SrcIP, ip4.DstIP), &apiRes) + var res RuleCmd + if err != nil { + logger.Info.Println("(API ERR) Denying access from", ip4.SrcIP, "to", ip4.DstIP) + res = Deny + } else if !apiRes.Permit { + logger.Info.Println("(Access Denied) Denying access from", ip4.SrcIP, "to", ip4.DstIP) + res = Deny + } else { + logger.Info.Println("Allowing access from", ip4.SrcIP, "to", ip4.DstIP) + res = Permit + } + rbacAllowCache.SetDefault(k, res) + return res } + } else { + return Permit } - return Permit } } diff --git a/go.mod b/go.mod index 3ec5c25..a0cd33e 100644 --- a/go.mod +++ b/go.mod @@ -36,6 +36,7 @@ require ( github.com/opentracing/opentracing-go v1.2.0 github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 github.com/openzipkin/zipkin-go v0.2.2 + github.com/patrickmn/go-cache v2.1.0+incompatible github.com/philhofer/fwd v1.1.0 // indirect github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.8.0 diff --git a/go.sum b/go.sum index f7bc437..e8cb8e8 100644 --- a/go.sum +++ b/go.sum @@ -1014,6 +1014,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/patrickmn/go-cache v0.0.0-20170722040110-a3647f8e31d7 h1:TDCdfoLETbr1D5m3VZp/7ky1vG0/2D68UO0/opGRj9I= github.com/patrickmn/go-cache v0.0.0-20170722040110-a3647f8e31d7/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/patrickmn/go-cache v0.0.0-20170722040110-a3647f8e31d7/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= diff --git a/main_shared.go b/main_shared.go index 34fb70f..7378ac1 100644 --- a/main_shared.go +++ b/main_shared.go @@ -155,7 +155,7 @@ func startInverse(interfaceName string, device *device.Device) { enableRBACFilter = 1 } - filter := filter.AclsToRulesFilter(profile.ACLs, filter.BuildRBACFilter(ztn.APIClientCtx, ztn.APIClient, &enableRBACFilter), nil) + filter := filter.AclsToRulesFilter(profile.ACLs, filter.BuildRBACFilter(ztn.APIClientCtx, ztn.APIClient, logger, &enableRBACFilter), nil) device.SetReceiveFilter(filter) for _, peerID := range profile.AllowedPeers { diff --git a/ztn/env_constants.go b/ztn/env_constants.go index 6cc486c..33a0f3c 100644 --- a/ztn/env_constants.go +++ b/ztn/env_constants.go @@ -26,4 +26,6 @@ const ( EnvGUIPID = "WG_GUI_PID" EnvSetupDNS = "WG_SETUP_DNS" + + EnvRBACIPFilteringCacheTime = "WG_RBAC_IP_FILTERING_CACHE_TIME" ) From 74af1c9e4d1953ef12a056e15eba59587a667fce Mon Sep 17 00:00:00 2001 From: Julien Semaan Date: Mon, 19 Apr 2021 13:26:08 -0400 Subject: [PATCH 09/20] re-enable rbac mode checking --- filter/rbac_filter.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/filter/rbac_filter.go b/filter/rbac_filter.go index ead9fb4..f6c00ad 100644 --- a/filter/rbac_filter.go +++ b/filter/rbac_filter.go @@ -3,6 +3,7 @@ package filter import ( "context" "fmt" + "sync/atomic" "time" "github.com/google/gopacket" @@ -30,9 +31,9 @@ func init() { func BuildRBACFilter(apiClientCtx context.Context, apiClient *unifiedapiclient.Client, logger *device.Logger, mode *uint32) RuleFunc { return func(data []byte) RuleCmd { - //if atomic.LoadUint32(mode) == 0 { - // return Permit - //} + if atomic.LoadUint32(mode) == 0 { + return Permit + } packet := gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.Default) if ip4Layer := packet.Layer(layers.LayerTypeIPv4); ip4Layer != nil { ip4 := ip4Layer.(*layers.IPv4) From 11fef5314572435835248f3d522f57542bfe47b7 Mon Sep 17 00:00:00 2001 From: Julien Semaan Date: Mon, 19 Apr 2021 15:36:22 -0400 Subject: [PATCH 10/20] better output and less cache --- filter/rbac_filter.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/filter/rbac_filter.go b/filter/rbac_filter.go index f6c00ad..2d371b4 100644 --- a/filter/rbac_filter.go +++ b/filter/rbac_filter.go @@ -19,11 +19,12 @@ var rbacAllowCache *cache.Cache func init() { var cacheTime time.Duration - defaultCacheTime := 30 * time.Minute + defaultCacheTime := 30 * time.Second cacheTimeEnv := sharedutils.EnvOrDefault(ztn.EnvRBACIPFilteringCacheTime, defaultCacheTime.String()) if cacheTimeParsed, err := time.ParseDuration(cacheTimeEnv); err == nil { cacheTime = cacheTimeParsed } else { + fmt.Println("Unable to parse", ztn.EnvRBACIPFilteringCacheTime, err) cacheTime = defaultCacheTime } rbacAllowCache = cache.New(cacheTime, 1*time.Minute) @@ -42,7 +43,8 @@ func BuildRBACFilter(apiClientCtx context.Context, apiClient *unifiedapiclient.C return cacheRes.(RuleCmd) } else { apiRes := struct { - Permit bool `json:"permit"` + Permit bool `json:"permit"` + Reason string `json:"reason"` }{} err := apiClient.Call(apiClientCtx, "GET", fmt.Sprintf("/api/v1/remote_clients/allowed_ip_communication?src_ip=%s&dst_ip=%s", ip4.SrcIP, ip4.DstIP), &apiRes) var res RuleCmd @@ -50,7 +52,7 @@ func BuildRBACFilter(apiClientCtx context.Context, apiClient *unifiedapiclient.C logger.Info.Println("(API ERR) Denying access from", ip4.SrcIP, "to", ip4.DstIP) res = Deny } else if !apiRes.Permit { - logger.Info.Println("(Access Denied) Denying access from", ip4.SrcIP, "to", ip4.DstIP) + logger.Info.Println("(Access Denied) Denying access from", ip4.SrcIP, "to", ip4.DstIP, apiRes.Reason) res = Deny } else { logger.Info.Println("Allowing access from", ip4.SrcIP, "to", ip4.DstIP) From c45646448cac87ba7f307a3ec055354d3afa7b88 Mon Sep 17 00:00:00 2001 From: Julien Semaan Date: Fri, 23 Apr 2021 08:29:18 -0400 Subject: [PATCH 11/20] install fix installing pre when its a post --- filter/acls_parser.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/filter/acls_parser.go b/filter/acls_parser.go index d0a0d4f..f1411d4 100644 --- a/filter/acls_parser.go +++ b/filter/acls_parser.go @@ -18,16 +18,16 @@ func AclsToRules(acls ...string) Rules { } func AclsToRulesFilter(acls []string, pre, post RuleFunc) func([]byte) error { - rules := Rules([]RuleFunc{}) - if pre != nil { - rules = append(rules, pre) - } - rules = append(rules, AclsToRules(acls...)...) - if post != nil { - rules = append(rules, pre) - } - - return rules.Filter + rules := Rules([]RuleFunc{}) + if pre != nil { + rules = append(rules, pre) + } + rules = append(rules, AclsToRules(acls...)...) + if post != nil { + rules = append(rules, post) + } + + return rules.Filter } const SRC_IP_OFFSET = 12 From 353645775d3607b721beb9c553233df5724a5a8a Mon Sep 17 00:00:00 2001 From: Julien Semaan Date: Fri, 23 Apr 2021 09:33:04 -0400 Subject: [PATCH 12/20] go fmt on filter/rule.go --- filter/rule.go | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/filter/rule.go b/filter/rule.go index b07ff40..8bf646d 100644 --- a/filter/rule.go +++ b/filter/rule.go @@ -1,7 +1,5 @@ package filter -import () - type RuleCmd int const ( @@ -13,9 +11,9 @@ const ( type Rules []RuleFunc func (rules Rules) PassDefaultDeny(p []byte) bool { - if len(rules) == 0 { - return true - } + if len(rules) == 0 { + return true + } for _, rule := range rules { cmd := rule(p) @@ -28,9 +26,9 @@ func (rules Rules) PassDefaultDeny(p []byte) bool { } func (rules Rules) PassDefaultPermit(p []byte) bool { - if len(rules) == 0 { - return true - } + if len(rules) == 0 { + return true + } for _, rule := range rules { cmd := rule(p) @@ -43,15 +41,15 @@ func (rules Rules) PassDefaultPermit(p []byte) bool { } func (rules Rules) Filter(p []byte) error { - if len(p) == 0 { - return nil - } + if len(p) == 0 { + return nil + } - if rules.PassDefaultDeny(p) { - return nil - } + if rules.PassDefaultDeny(p) { + return nil + } - return ErrDenyAll + return ErrDenyAll } type RuleFunc func([]byte) RuleCmd @@ -370,4 +368,3 @@ func (nm Ipv4NetworkMask) MatchDst(p []byte) bool { func (nm Ipv4NetworkMask) Match(ip uint32) bool { return (ip & nm.Mask) == nm.Network } - From c988342e41dac87ab358b9f92cc97961a039ea8d Mon Sep 17 00:00:00 2001 From: Julien Semaan Date: Fri, 23 Apr 2021 09:34:19 -0400 Subject: [PATCH 13/20] apply goimport on filter/filter.go --- filter/filter.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/filter/filter.go b/filter/filter.go index e0ea1cb..e49e1a6 100644 --- a/filter/filter.go +++ b/filter/filter.go @@ -2,9 +2,10 @@ package filter import ( "errors" - "github.com/inverse-inc/wireguard-go/services" "strconv" "strings" + + "github.com/inverse-inc/wireguard-go/services" ) const ( From 89039c1ea1528150d2e4b0693421a4362ae5eb4e Mon Sep 17 00:00:00 2001 From: James Rouzier Date: Fri, 23 Apr 2021 09:39:34 -0400 Subject: [PATCH 14/20] If there are no ACLs default to a Permit --- filter/acls_parser.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/filter/acls_parser.go b/filter/acls_parser.go index f1411d4..de7dd28 100644 --- a/filter/acls_parser.go +++ b/filter/acls_parser.go @@ -27,6 +27,10 @@ func AclsToRulesFilter(acls []string, pre, post RuleFunc) func([]byte) error { rules = append(rules, post) } + if len(acls) == 0 { + rules = append(rules, RulePermit) + } + return rules.Filter } From 60d6fddb33955f5a8e1ba20700603411e3570f69 Mon Sep 17 00:00:00 2001 From: James Rouzier Date: Fri, 23 Apr 2021 10:05:51 -0400 Subject: [PATCH 15/20] Check against the returned rules --- filter/acls_parser.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/filter/acls_parser.go b/filter/acls_parser.go index de7dd28..b01e641 100644 --- a/filter/acls_parser.go +++ b/filter/acls_parser.go @@ -22,12 +22,14 @@ func AclsToRulesFilter(acls []string, pre, post RuleFunc) func([]byte) error { if pre != nil { rules = append(rules, pre) } - rules = append(rules, AclsToRules(acls...)...) + + aclRules := AclsToRules(acls...) + rules = append(rules, aclRules...) if post != nil { rules = append(rules, post) } - if len(acls) == 0 { + if len(aclRules) == 0 { rules = append(rules, RulePermit) } From af4122545d10107c1b97dcf4ddbc52836de953c6 Mon Sep 17 00:00:00 2001 From: James Rouzier Date: Fri, 23 Apr 2021 10:55:32 -0400 Subject: [PATCH 16/20] Add RuleSkip --- filter/rule.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/filter/rule.go b/filter/rule.go index 8bf646d..2affbb8 100644 --- a/filter/rule.go +++ b/filter/rule.go @@ -58,6 +58,10 @@ func RulePermit([]byte) RuleCmd { return Permit } +func RuleSkip([]byte) RuleCmd { + return Skip +} + func RuleDeny([]byte) RuleCmd { return Deny } From 4b9e5dc619c8c8acb47b5e12f5466c7bc55b4e6a Mon Sep 17 00:00:00 2001 From: Julien Semaan Date: Fri, 23 Apr 2021 10:57:17 -0400 Subject: [PATCH 17/20] don't even load pre filter if RBAC filter is disabled --- filter/rbac_filter.go | 6 +----- main_shared.go | 6 +++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/filter/rbac_filter.go b/filter/rbac_filter.go index 2d371b4..0ec53bb 100644 --- a/filter/rbac_filter.go +++ b/filter/rbac_filter.go @@ -3,7 +3,6 @@ package filter import ( "context" "fmt" - "sync/atomic" "time" "github.com/google/gopacket" @@ -30,11 +29,8 @@ func init() { rbacAllowCache = cache.New(cacheTime, 1*time.Minute) } -func BuildRBACFilter(apiClientCtx context.Context, apiClient *unifiedapiclient.Client, logger *device.Logger, mode *uint32) RuleFunc { +func BuildRBACFilter(apiClientCtx context.Context, apiClient *unifiedapiclient.Client, logger *device.Logger) RuleFunc { return func(data []byte) RuleCmd { - if atomic.LoadUint32(mode) == 0 { - return Permit - } packet := gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.Default) if ip4Layer := packet.Layer(layers.LayerTypeIPv4); ip4Layer != nil { ip4 := ip4Layer.(*layers.IPv4) diff --git a/main_shared.go b/main_shared.go index 7378ac1..93807bc 100644 --- a/main_shared.go +++ b/main_shared.go @@ -150,12 +150,12 @@ func startInverse(interfaceName string, device *device.Device) { go networkConnection.Start() - var enableRBACFilter uint32 + var preFilter filter.RuleFunc = nil if profile.RBACIPFiltering { - enableRBACFilter = 1 + preFilter = filter.BuildRBACFilter(ztn.APIClientCtx, ztn.APIClient, logger) } - filter := filter.AclsToRulesFilter(profile.ACLs, filter.BuildRBACFilter(ztn.APIClientCtx, ztn.APIClient, logger, &enableRBACFilter), nil) + filter := filter.AclsToRulesFilter(profile.ACLs, preFilter, nil) device.SetReceiveFilter(filter) for _, peerID := range profile.AllowedPeers { From 30116ae7f71fd248217b0333369e3319d7a8a472 Mon Sep 17 00:00:00 2001 From: Julien Semaan Date: Fri, 23 Apr 2021 14:19:18 -0400 Subject: [PATCH 18/20] some bug fixing --- main_dns.go | 14 +++++++------- ztn/profile.go | 12 +++++++++++- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/main_dns.go b/main_dns.go index f7e9898..65a347d 100644 --- a/main_dns.go +++ b/main_dns.go @@ -87,28 +87,28 @@ func GenerateCoreDNSConfig(myDNSInfo *godnschange.DNSInfo, profile ztn.Profile, bind 127.0.0.69 reload #debug + +dnsredir {{$.InternalDomain}} { + to ietf-doh://{{ $.API }}:{{$.Port}}/dns-ztn-query +} {{ range .Domains }}{{ if ne . "" }}{{$domain := .}} dnsredir {{.}} { to ietf-doh://{{ $.API }}:{{$.Port}}/dns-query } {{ end }}{{ end }} -dnsredir {{$.InternalDomain}} { - to ietf-doh://{{ $.API }}:{{$.Port}}/dns-ztn-query -} - {{ range .ZTNPeers }}{{ if ne . "" }}{{$ztnpeer := .}} {{ range $.SearchDomain }}{{ if ne . "" }} dnsredir {{$ztnpeer}}.{{.}} { - to ietf-doh://{{ $.API }}:{{$.Port}}/dns-ztn-query + to ietf-doh://{{ $.API }}:{{$.Port}}/dns-ztn-query } {{ end }}{{ end }}{{ end }}{{ end }} {{ if .ZTNServer }} forward {{ .API }} {{ .Nameservers }} { - prefer_udp + prefer_udp } {{ end }} forward . {{ .Nameservers }} { - prefer_udp + prefer_udp } }`) diff --git a/ztn/profile.go b/ztn/profile.go index 01b5974..71d31d7 100644 --- a/ztn/profile.go +++ b/ztn/profile.go @@ -180,6 +180,8 @@ func (p *Profile) findClientMAC() (net.HardwareAddr, error) { return net.HardwareAddr{}, err } + var firstMAC net.HardwareAddr + var foundValidMAC bool for _, i := range ifaces { addrs, err := i.Addrs() if err != nil { @@ -192,12 +194,20 @@ func (p *Profile) findClientMAC() (net.HardwareAddr, error) { if ipnet.Contains(gwIP) { p.logger.Info.Println("Found MAC address", i.HardwareAddr, "on interface", ipnet, "("+i.Name+")") return i.HardwareAddr, nil + } else if i.HardwareAddr.String() != "00:00:00:00:00:00" { + firstMAC = i.HardwareAddr + foundValidMAC = true } } } } - return net.HardwareAddr{}, errors.New("Unable to find MAC address") + if foundValidMAC { + p.logger.Info.Println("Failed to find the MAC address that talks to the default gateway but found MAC address", firstMAC) + return firstMAC, nil + } else { + return net.HardwareAddr{}, errors.New("Unable to find MAC address") + } } type RouteInfo struct { From 5ac8ee856627ff4096ac4f20c51d5917be47d3e5 Mon Sep 17 00:00:00 2001 From: James Rouzier Date: Fri, 23 Apr 2021 14:20:23 -0400 Subject: [PATCH 19/20] Logger error when parsing ACLs --- filter/acls_parser.go | 68 ++++++++++++++++++++++++------------------- filter/errors.go | 2 +- filter/rule_test.go | 28 +++++++++++------- main_shared.go | 2 +- 4 files changed, 57 insertions(+), 43 deletions(-) diff --git a/filter/acls_parser.go b/filter/acls_parser.go index b01e641..1de6002 100644 --- a/filter/acls_parser.go +++ b/filter/acls_parser.go @@ -1,14 +1,20 @@ package filter import ( + "fmt" + "github.com/inverse-inc/wireguard-go/device" "strconv" "strings" ) -func AclsToRules(acls ...string) Rules { +func AclsToRules(logger *device.Logger, acls ...string) Rules { rules := []RuleFunc{} for _, acl := range acls { - rule := AclToRule(acl) + rule, err := AclToRule(acl) + if err != nil { + logger.Error.Println(err) + } + if rule != nil { rules = append(rules, rule) } @@ -17,19 +23,17 @@ func AclsToRules(acls ...string) Rules { return rules } -func AclsToRulesFilter(acls []string, pre, post RuleFunc) func([]byte) error { +func AclsToRulesFilter(logger *device.Logger, acls []string, pre, post RuleFunc) func([]byte) error { rules := Rules([]RuleFunc{}) if pre != nil { rules = append(rules, pre) } - aclRules := AclsToRules(acls...) + aclRules := AclsToRules(logger, acls...) rules = append(rules, aclRules...) if post != nil { rules = append(rules, post) - } - - if len(aclRules) == 0 { + } else if len(aclRules) == 0 { rules = append(rules, RulePermit) } @@ -104,14 +108,14 @@ func (r *Ipv4RuleData) AnyProto() bool { return r.protocol == allProtocols } -func AclToRule(acl string) RuleFunc { +func AclToRule(acl string) (RuleFunc, error) { tokens := strings.Fields(acl) if len(tokens) < 2 { - return nil + return nil, fmt.Errorf("Acl to short: '%s'", acl) } if len(tokens) == 2 { - return twoPartAcl(tokens) + return twoPartAcl(acl, tokens) } rule := NewIpv4RuleData() @@ -124,22 +128,22 @@ func AclToRule(acl string) RuleFunc { case "deny": rule.cmd = Deny default: - return nil + return nil, invalidAcl(acl) } rule.src.Network, rule.src.Mask, tokens, ok = getSource(tokens) if ok { - return singleIpRule(rule.src, rule.cmd, SRC_IP_OFFSET) + return singleIpRule(rule.src, rule.cmd, SRC_IP_OFFSET), nil } rule.protocol, tokens, ok = getProtocol(tokens) if !ok { - return nil + return nil, invalidAcl(acl) } rule.src.Network, rule.src.Mask, tokens, ok = getSource(tokens) if !ok { - return nil + return nil, invalidAcl(acl) } if rule.protocol == 6 || rule.protocol == 17 { @@ -148,7 +152,7 @@ func AclToRule(acl string) RuleFunc { rule.dst.Network, rule.dst.Mask, tokens, ok = getSource(tokens) if !ok { - return nil + return nil, invalidAcl(acl) } switch rule.protocol { @@ -161,47 +165,47 @@ func AclToRule(acl string) RuleFunc { if rule.AnyIp() { if rule.AnyProto() { if rule.cmd == Permit { - return PermitAllRule() + return PermitAllRule(), nil } - return DenyAllRule() + return DenyAllRule(), nil } if rule.protocol == 1 { - return icmpRuleRule(rule.icmpRule, rule.cmd) + return icmpRuleRule(rule.icmpRule, rule.cmd), nil } if rule.AnyPort() { - return protoRule(byte(rule.protocol), rule.cmd) + return protoRule(byte(rule.protocol), rule.cmd), nil } if rule.AnySrcPort() && !rule.AnyDstPort() { - return portProtoRule(rule.dstPort, byte(rule.protocol), rule.cmd, 2) + return portProtoRule(rule.dstPort, byte(rule.protocol), rule.cmd, 2), nil } if !rule.AnySrcPort() && rule.AnyDstPort() { - return portProtoRule(rule.srcPort, byte(rule.protocol), rule.cmd, 0) + return portProtoRule(rule.srcPort, byte(rule.protocol), rule.cmd, 0), nil } } if rule.AnyProto() { if rule.AnySrcIP() && !rule.AnyDstIP() { - return singleIpRule(rule.dst, rule.cmd, DST_IP_OFFSET) + return singleIpRule(rule.dst, rule.cmd, DST_IP_OFFSET), nil } if !rule.AnySrcIP() && rule.AnyDstIP() { - return singleIpRule(rule.src, rule.cmd, SRC_IP_OFFSET) + return singleIpRule(rule.src, rule.cmd, SRC_IP_OFFSET), nil } - return srcDstRule(rule.src, rule.dst, rule.cmd) + return srcDstRule(rule.src, rule.dst, rule.cmd), nil } if rule.AnyPort() { - return srcDstProtoRule(rule.src, rule.dst, byte(rule.protocol), rule.cmd) + return srcDstProtoRule(rule.src, rule.dst, byte(rule.protocol), rule.cmd), nil } - return srcDstProtoSrcPortDstPort(rule.src, rule.dst, byte(rule.protocol), rule.srcPort, rule.dstPort, rule.cmd) + return srcDstProtoSrcPortDstPort(rule.src, rule.dst, byte(rule.protocol), rule.srcPort, rule.dstPort, rule.cmd), nil } func getProtocol(oTokens []string) (int16, []string, bool) { @@ -461,15 +465,19 @@ func dtoi(s string) (n int, i int, ok bool) { return n, i, true } -func twoPartAcl(tokens []string) RuleFunc { +func invalidAcl(acl string) error { + return fmt.Errorf("Invalid Acl: '%s'", acl) +} + +func twoPartAcl(acl string, tokens []string) (RuleFunc, error) { if tokens[1] == "any" { switch tokens[0] { case "permit": - return RulePermit + return RulePermit, nil case "deny": - return RuleDeny + return RuleDeny, nil } } - return nil + return nil, invalidAcl(acl) } diff --git a/filter/errors.go b/filter/errors.go index 7266d2b..b67b929 100644 --- a/filter/errors.go +++ b/filter/errors.go @@ -1,7 +1,7 @@ package filter import ( - "errors" + "errors" ) var ErrDenyAll = errors.New("Deny All") diff --git a/filter/rule_test.go b/filter/rule_test.go index 1f050ea..174131d 100644 --- a/filter/rule_test.go +++ b/filter/rule_test.go @@ -1,9 +1,15 @@ package filter import ( + "github.com/inverse-inc/wireguard-go/device" "testing" ) +var logger = device.NewLogger( + device.LogLevelSilent, + "(Testing)", +) + var rulePackets = [][]byte{ []byte{69, 0, 0, 60, 203, 131, 64, 0, 64, 6, 99, 226, 192, 168, 69, 3, 192, 168, 68, 2, 153, 222, 17, 92, 31, 232, 147, 213, 0, 0, 0, 0, 160, 2, 107, 208, 25, 66, 0, 0, 2, 4, 5, 100, 4, 2, 8, 10, 0, 153, 88, 86, 0, 0, 0, 0, 1, 3, 3, 7}, []byte{69, 0, 0, 52, 203, 132, 64, 0, 64, 6, 99, 233, 192, 168, 69, 3, 192, 168, 69, 2, 153, 222, 17, 92, 31, 232, 147, 214, 181, 110, 17, 81, 128, 16, 0, 216, 252, 127, 0, 0, 1, 1, 8, 10, 0, 153, 88, 88, 0, 151, 238, 205}, @@ -128,33 +134,33 @@ func TestPermitDstIpRule(t *testing.T) { } func TestPermitAny(t *testing.T) { - rules := AclsToRules("permit any") + rules := AclsToRules(logger, "permit any") if !rules.PassDefaultDeny(rulePackets[0]) { t.Error("permit any failed") } - rules = AclsToRules("permit 0.0.0.0 255.255.255.255") + rules = AclsToRules(logger, "permit 0.0.0.0 255.255.255.255") if !rules.PassDefaultDeny(rulePackets[0]) { t.Error("permit any failed") } } func TestPermitHost(t *testing.T) { - rules := AclsToRules("permit host 192.168.69.3") + rules := AclsToRules(logger, "permit host 192.168.69.3") if !rules.PassDefaultDeny(rulePackets[0]) { t.Error("permit host 192.169.68.3 failed") } } func TestDenyAny(t *testing.T) { - rules := AclsToRules("deny any") + rules := AclsToRules(logger, "deny any") if rules.PassDefaultPermit(rulePackets[0]) { t.Error("deny any failed") } } func TestPermitSrcDstPortProto(t *testing.T) { - rules := AclsToRules("permit tcp any any eq 80") + rules := AclsToRules(logger, "permit tcp any any eq 80") if rules.PassDefaultDeny(rulePackets[0]) { t.Error("permit tcp any any eq 80 failed") } @@ -168,7 +174,7 @@ func TestSimpleHost(t *testing.T) { } for _, acl := range acls { - rules := AclsToRules(acl) + rules := AclsToRules(logger, acl) if !rules.PassDefaultDeny(rulePackets[0]) { t.Errorf("acl '%s' failed", acl) } @@ -181,7 +187,7 @@ func TestIcmpPermitAny(t *testing.T) { } for _, acl := range acls { - rules := AclsToRules(acl) + rules := AclsToRules(logger, acl) if !rules.PassDefaultDeny(icmpPacket1) { t.Errorf("acl '%s' failed", acl) } @@ -199,7 +205,7 @@ func TestIcmpPermit(t *testing.T) { } for _, acl := range acls { - rules := AclsToRules(acl) + rules := AclsToRules(logger, acl) if !rules.PassDefaultDeny(icmpPacket1) { t.Errorf("acl '%s' failed", acl) } @@ -207,7 +213,7 @@ func TestIcmpPermit(t *testing.T) { } for _, acl := range acls { - rules := AclsToRules(acl) + rules := AclsToRules(logger, acl) if rules.PassDefaultDeny(icmpPacket2) { t.Errorf("acl '%s' passed", acl) } @@ -222,7 +228,7 @@ func TestIcmpDeny(t *testing.T) { } for _, acl := range acls { - rules := AclsToRules(acl) + rules := AclsToRules(logger, acl) if rules.PassDefaultDeny(icmpPacket1) { t.Errorf("acl '%s' passed", acl) } @@ -230,7 +236,7 @@ func TestIcmpDeny(t *testing.T) { } for _, acl := range acls { - rules := AclsToRules(acl) + rules := AclsToRules(logger, acl) if !rules.PassDefaultPermit(icmpPacket2) { t.Errorf("acl '%s' deny", acl) } diff --git a/main_shared.go b/main_shared.go index 93807bc..7235a4c 100644 --- a/main_shared.go +++ b/main_shared.go @@ -155,7 +155,7 @@ func startInverse(interfaceName string, device *device.Device) { preFilter = filter.BuildRBACFilter(ztn.APIClientCtx, ztn.APIClient, logger) } - filter := filter.AclsToRulesFilter(profile.ACLs, preFilter, nil) + filter := filter.AclsToRulesFilter(logger, profile.ACLs, preFilter, nil) device.SetReceiveFilter(filter) for _, peerID := range profile.AllowedPeers { From 65f60d7f1444118615a8410362991b0240163345 Mon Sep 17 00:00:00 2001 From: Julien Semaan Date: Mon, 26 Apr 2021 14:53:13 -0400 Subject: [PATCH 20/20] routes can have a priority --- routes/routes_darwin.go | 2 +- routes/routes_linux.go | 2 +- routes/routes_windows.go | 8 ++++++-- ztn/profile.go | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/routes/routes_darwin.go b/routes/routes_darwin.go index 852c41a..405216a 100644 --- a/routes/routes_darwin.go +++ b/routes/routes_darwin.go @@ -6,7 +6,7 @@ import ( "os/exec" ) -func Add(ipnet *net.IPNet, gw net.IP) error { +func Add(ipnet *net.IPNet, gw net.IP, priority int) error { res, err := exec.Command("route", "-n", "add", "-net", ipnet.String(), gw.String()).Output() if err != nil { fmt.Println(string(res)) diff --git a/routes/routes_linux.go b/routes/routes_linux.go index 0fd4fb7..bc96555 100644 --- a/routes/routes_linux.go +++ b/routes/routes_linux.go @@ -6,7 +6,7 @@ import ( "os/exec" ) -func Add(ipnet *net.IPNet, gw net.IP) error { +func Add(ipnet *net.IPNet, gw net.IP, priority int) error { res, err := exec.Command("ip", "route", "add", ipnet.String(), "via", gw.String()).Output() if err != nil { fmt.Println(string(res)) diff --git a/routes/routes_windows.go b/routes/routes_windows.go index 0295937..dcd27b2 100644 --- a/routes/routes_windows.go +++ b/routes/routes_windows.go @@ -6,8 +6,12 @@ import ( "os/exec" ) -func Add(ipnet *net.IPNet, gw net.IP) error { - res, err := exec.Command("route", "add", ipnet.IP.String(), "mask", net.IPv4(ipnet.Mask[0], ipnet.Mask[1], ipnet.Mask[2], ipnet.Mask[3]).String(), gw.String()).Output() +func Add(ipnet *net.IPNet, gw net.IP, priority int) error { + res, err := exec.Command( + "route", "add", + ipnet.IP.String(), "mask", net.IPv4(ipnet.Mask[0], ipnet.Mask[1], ipnet.Mask[2], ipnet.Mask[3]).String(), gw.String(), + "metric", fmt.Sprintf("%d", priority), + ).Output() if err != nil { fmt.Println(string(res)) } diff --git a/ztn/profile.go b/ztn/profile.go index 71d31d7..e55f845 100644 --- a/ztn/profile.go +++ b/ztn/profile.go @@ -243,12 +243,12 @@ func (p *Profile) ParseRoutes() []RouteInfo { func (p *Profile) SetupRoutes() error { if sharedutils.EnvOrDefault(EnvHonorRoutes, "true") == "true" { - for _, r := range p.ParseRoutes() { + for i, r := range p.ParseRoutes() { p.logger.Info.Println("Installing route to", r.Network, "via", r.Gateway) go func(r RouteInfo) { // Sleep to give time to the WG interface to get up time.Sleep(5 * time.Second) - err := routes.Add(r.Network, r.Gateway) + err := routes.Add(r.Network, r.Gateway, i+1) if err != nil { p.logger.Error.Println("Error while nstalling route to", r.Network, "via", r.Gateway, ":", err) }