Skip to content

Commit

Permalink
loading of config-file, parsing of rules
Browse files Browse the repository at this point in the history
  • Loading branch information
superstes committed Sep 24, 2023
1 parent 3ed1122 commit 46eb1b1
Show file tree
Hide file tree
Showing 28 changed files with 578 additions and 262 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lib/main/main
1 change: 0 additions & 1 deletion lib/cnf/acl.go

This file was deleted.

16 changes: 9 additions & 7 deletions lib/cnf/config.yml → lib/cnf/cnf_file/config.yml
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
---

config:
service:
listen:
port: 4128
ip4:
- '127.0.0.1'
ip6:
- '::1'
tcp: true
udp: false
udp: false # not yet implemented
transparent: false

timeout:
connection: 5
handshake: 5
dial: 5
intercept: 5
intercept: 2

output:
fwmark: 0
interface: ''

vars:
net_private: ['192.168.0.0/16', '172.16.0.0/12', '10.0.0.0/8']
svc_http: [80, 443]
- name: 'net_private'
value: ['192.168.0.0/16', '172.16.0.0/12', '10.0.0.0/8']
- name: 'svc_http'
value: [80, 443]

filters:
rules:
- match:
dest: '192.168.100.0/24'
action: 'drop'
Expand All @@ -38,7 +40,7 @@ filters:
action: 'accept'

- match:
dest: '!= $net_private'
dest: '!$net_private'
port: 443
protoL4: 'tcp'
action: 'accept'
51 changes: 51 additions & 0 deletions lib/cnf/cnf_file/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package cnf_file

import (
"fmt"
"os"

"github.com/superstes/calamary/cnf"
"github.com/superstes/calamary/log"
"gopkg.in/yaml.v2"
)

func readConfigFile(file string) (config []byte, err error) {
_, err = os.Stat(file)
if !os.IsNotExist(err) {
config, err = os.ReadFile(file)
if err != nil {
// log.ErrorS("config", "Unable to read config-file '%s'", file)
return nil, err
}
}
return
}

func readConfig() (config []byte) {
cwd, err := os.Getwd()
cwdConfig := "./calamary.yml"
if err == nil {
cwdConfig = cwd + "/calamary.yml"
config, err := readConfigFile(cwdConfig)
if err == nil && config != nil {
return config
}
}
config, err = readConfigFile(cnf.CONFIG_FILE_ABS)
if err == nil && config != nil {
return config
}
log.ErrorS("config", fmt.Sprintf(
"Neither config file could be read: (%s, %s)", cwdConfig, cnf.CONFIG_FILE_ABS,
))
panic(fmt.Errorf("no valid config file found! (%s, %s)", cwdConfig, cnf.CONFIG_FILE_ABS))
}

func Load() {
err := yaml.Unmarshal(readConfig(), &cnf.C)
if err != nil {
log.ErrorS("config", "Failed to parse config! Check if it is valid!")
panic(fmt.Errorf("failed to parse config"))
}
cnf.RULES = ParseRules(cnf.C.Rules)
}
246 changes: 246 additions & 0 deletions lib/cnf/cnf_file/rules_parse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
package cnf_file

import (
"fmt"
"net"
"strconv"
"strings"

"github.com/superstes/calamary/cnf"
"github.com/superstes/calamary/proc/meta"
"github.com/superstes/calamary/u"
)

func ParseRules(rawRules []cnf.RuleRaw) (rules []cnf.Rule) {
for i := range rawRules {
ruleRaw := rawRules[i]
rule := cnf.Rule{
Action: filterAction(ruleRaw.Action),
}

if len(ruleRaw.Match.SrcNet) > 0 {
rule.Match.SrcNet = []*net.IPNet{}
rule.Match.SrcNetN = []*net.IPNet{}
}
for i2 := range ruleRaw.Match.SrcNet {
if ruleRaw.Match.SrcNet[i2][0] == '!' {
rule.Match.SrcNetN = append(rule.Match.SrcNetN, matchNet(ruleRaw.Match.SrcNet[i2]))
} else {
rule.Match.SrcNet = append(rule.Match.SrcNet, matchNet(ruleRaw.Match.SrcNet[i2]))
}
}

if len(ruleRaw.Match.DestNet) > 0 {
rule.Match.DestNet = []*net.IPNet{}
rule.Match.DestNetN = []*net.IPNet{}
}
for i2 := range ruleRaw.Match.DestNet {
if ruleRaw.Match.DestNet[i2][0] == '!' {
rule.Match.DestNetN = append(rule.Match.DestNetN, matchNet(ruleRaw.Match.DestNet[i2]))
} else {
rule.Match.DestNet = append(rule.Match.DestNet, matchNet(ruleRaw.Match.DestNet[i2]))
}
}

if len(ruleRaw.Match.SrcPort) > 0 {
rule.Match.SrcPort = []uint16{}
rule.Match.SrcPortN = []uint16{}
}
for i2 := range ruleRaw.Match.SrcPort {
rule.Match.SrcPort = []uint16{}
rule.Match.SrcPortN = []uint16{}
if ruleRaw.Match.SrcPort[i2][0] == '!' {
rule.Match.SrcPortN = append(rule.Match.SrcPortN, matchPort(ruleRaw.Match.SrcPort[i2]))
} else {
rule.Match.SrcPort = append(rule.Match.SrcPort, matchPort(ruleRaw.Match.SrcPort[i2]))
}
}

if len(ruleRaw.Match.DestPort) > 0 {
rule.Match.DestPort = []uint16{}
rule.Match.DestPortN = []uint16{}
}
for i2 := range ruleRaw.Match.DestPort {
if ruleRaw.Match.DestPort[i2][0] == '!' {
rule.Match.DestPortN = append(rule.Match.DestPortN, matchPort(ruleRaw.Match.DestPort[i2]))
} else {
rule.Match.DestPort = append(rule.Match.DestPort, matchPort(ruleRaw.Match.DestPort[i2]))
}
}

if len(ruleRaw.Match.ProtoL3) > 0 {
rule.Match.ProtoL3 = []meta.Proto{}
rule.Match.ProtoL3N = []meta.Proto{}
}
for i2 := range ruleRaw.Match.ProtoL3 {
if ruleRaw.Match.ProtoL3[i2][0] == '!' {
rule.Match.ProtoL3N = append(rule.Match.ProtoL3N, matchProtoL3(ruleRaw.Match.ProtoL3[i2]))
} else {
rule.Match.ProtoL3 = append(rule.Match.ProtoL3, matchProtoL3(ruleRaw.Match.ProtoL3[i2]))
}
}

if len(ruleRaw.Match.ProtoL4) > 0 {
rule.Match.ProtoL4 = []meta.Proto{}
rule.Match.ProtoL4N = []meta.Proto{}
}
for i2 := range ruleRaw.Match.ProtoL4 {
if ruleRaw.Match.ProtoL4[i2][0] == '!' {
rule.Match.ProtoL4N = append(rule.Match.ProtoL4N, matchProtoL4(ruleRaw.Match.ProtoL4[i2]))
} else {
rule.Match.ProtoL4 = append(rule.Match.ProtoL4, matchProtoL4(ruleRaw.Match.ProtoL4[i2]))
}
}

if len(ruleRaw.Match.ProtoL5) > 0 {
rule.Match.ProtoL5 = []meta.Proto{}
rule.Match.ProtoL5N = []meta.Proto{}
}
for i2 := range ruleRaw.Match.ProtoL5 {
if ruleRaw.Match.ProtoL5[i2][0] == '!' {
rule.Match.ProtoL5N = append(rule.Match.ProtoL5N, matchProtoL5(ruleRaw.Match.ProtoL5[i2]))
} else {
rule.Match.ProtoL5 = append(rule.Match.ProtoL5, matchProtoL5(ruleRaw.Match.ProtoL5[i2]))
}
}

if len(ruleRaw.Match.Domains) > 0 {
rule.Match.Domains = []string{}
}
for i2 := range ruleRaw.Match.Domains {
if ruleRaw.Match.Domains[i2][0] == '!' {
rule.Match.Domains = append(rule.Match.Domains, matchDomain(ruleRaw.Match.Domains[i2]))
} else {
rule.Match.Domains = append(rule.Match.Domains, matchDomain(ruleRaw.Match.Domains[i2]))
}
}

rules = append(rules, rule)
}
return
}

func cleanRaw(configRaw string) (configClean string) {
configClean = strings.ReplaceAll(configRaw, " ", "")
configClean = strings.ReplaceAll(configClean, "!", "")
return
}

func filterAction(configAction string) meta.Action {
switch strings.ToLower(configAction) {
case "accept", "allow":
return meta.ActionAccept
default:
return meta.ActionDeny
}
}

func matchProtoL3(configProto string) meta.Proto {
configProto = cleanRaw(configProto)
switch strings.ToLower(configProto) {
case "ip4", "ipv4":
return meta.ProtoL3IP4
case "ip6", "ipv6":
return meta.ProtoL3IP6
default:
panic(fmt.Sprintf("protoL3 '%v' not found", configProto))
}
return 0
}

func matchProtoL4(configProto string) meta.Proto {
configProto = cleanRaw(configProto)
switch strings.ToLower(configProto) {
case "tcp":
return meta.ProtoL4Tcp
case "udp":
return meta.ProtoL4Udp
default:
panic(fmt.Sprintf("protoL4 '%v' not found or not yet supported", configProto))
}
return 0
}

func matchProtoL5(configProto string) meta.Proto {
configProto = cleanRaw(configProto)
switch strings.ToLower(configProto) {
case "tls":
return meta.ProtoL5Tls
case "http":
return meta.ProtoL5Http
/*
case "dns":
return meta.ProtoL5Dns
case "ntp":
return meta.ProtoL5Ntp
*/
default:
panic(fmt.Sprintf("protoL5 '%v' not found or not yet supported", configProto))
}
return 0
}

/*
func matchIPsRaw(configIPs interface{}) (networks []*net.IPNet) {
switch reflect.TypeOf(configIPs).Kind() {
case reflect.String:
return matchIPs(strings.Split(fmt.Sprintf("%v", configIPs), ","))
case reflect.Slice, reflect.Array:
return matchIPs(configIPs)
default:
panic(fmt.Errorf("IP-match '%v' neither of type string nor array/slice", configIPs))
}
}
*/

func matchNet(ip string) *net.IPNet {
ip = cleanRaw(ip)
// todo: allow users to provide single ip or list
if strings.Contains(ip, "/") {
_, netip, err := net.ParseCIDR(ip)
if err != nil {
panic(fmt.Sprintf("IP-network '%s' could not be parsed (must be valid CIDR-notation)", ip))
}
return netip

} else if strings.Contains(ip, ".") {
netip := net.ParseIP(ip)
if netip == nil {
panic(fmt.Sprintf("IPv4 '%s' could not be parsed", ip))
}
return &net.IPNet{
IP: netip,
Mask: net.CIDRMask(32, 32),
}

} else {
netip := net.ParseIP(ip)
if netip == nil {
panic(fmt.Sprintf("IPv6 '%s' could not be parsed", ip))
}
return &net.IPNet{
IP: netip,
Mask: net.CIDRMask(128, 128),
}
}
}

func matchPort(configPort string) uint16 {
configPort = cleanRaw(configPort)
port, err := strconv.ParseUint(configPort, 10, 0)
if err != nil {
panic(fmt.Sprintf("Port '%s' could not be parsed", configPort))
}
if port > 65535 {
panic(fmt.Sprintf("Port '%s' outside of valid range", configPort))
}
return uint16(port)
}

func matchDomain(configDomain string) string {
configDomain = cleanRaw(configDomain)
if !u.IsDomainName(configDomain) {
panic(fmt.Sprintf("Domain '%s' is not valid", configDomain))
}
return configDomain
}
Loading

0 comments on commit 46eb1b1

Please sign in to comment.