Skip to content

Commit

Permalink
added basic receiving & processing
Browse files Browse the repository at this point in the history
  • Loading branch information
superstes committed Sep 23, 2023
1 parent c376ccd commit 61352f3
Show file tree
Hide file tree
Showing 44 changed files with 8,237 additions and 6 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ jobs:

- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
args: '--config=.golangci.yml'

- name: Running tests
run: go test -v ./...
shell: bash

- name: Build binary
run: |
Expand Down
12 changes: 12 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---

run:
go: '1.21'
skip-dirs: []
skip-files:
# stdlib
- 'sort.go'

issues:
exclude: []
exclude-rules: []
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -638,4 +638,4 @@ copy of the Program in return for a fee.
along with this program. If not, see <http://www.gnu.org/licenses/>.

E-Mail: [email protected]
Web: https://github.com/shield-wall-net
Web: https://github.com/superstes
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,28 @@ Its focus is set on security filtering for HTTPS.

**Features**:
* certificate verification
* mode to enforce TLS (*deny any unencrypted connections*)
* detect plain HTTP and respond with generic HTTPS-redirect
* support for [proxy-protocol](https://github.com/pires/go-proxyproto)


## Why?

Forward proxies are very useful to enforce a security-baseline in networks and a must-have for Zero-Trust environments.

Many enterprises and individuals will use proxies integrated with vendor network-firewalls or cloud-services to handle this filtering.

But some of us might like to keep control over that system.

The usage of go-based applications is easy (_single binary_) and can perform well.

### Why not use Squid?

**Squid has some limitations** that make its usage more complicated than it should be.

**Per example**:

* intercept/transparent mode - [DNAT not supported](http://www.squid-cache.org/Advisories/SQUID-2011_1.txt)
* intercept/transparent mode - no native solution for [the DNAT restrictions](http://www.squid-cache.org/Advisories/SQUID-2011_1.txt)

Related errors:

Expand All @@ -37,6 +49,10 @@ Its focus is set on security filtering for HTTPS.
Related error: `Host header forgery detected`


Squid is a good and stable software. But I get the feeling it needed to grow into more than it was designed for initially. Some behavior is incomsistent between modes and not optimized for todays IT-world.

I would much preferr a keep-it-simple approach. Even if that means that some nice-to-have features are not implemented.


## How?

Expand Down Expand Up @@ -69,6 +85,14 @@ Its focus is set on security filtering for HTTPS.

- [ ] YAML-based configuration

- [ ] Parsing

- [x] TCP
- [ ] TLS
- [ ] HTTP
- [ ] UDP
- [ ] DNS

- [ ] Filtering

- [ ] TCP
Expand All @@ -77,7 +101,8 @@ Its focus is set on security filtering for HTTPS.
- [ ] UDP

- [ ] Certificate validation
- [ ] ACLs
- [ ] Matches

- [ ] Config
- [ ] Matching
- [ ] Additional checks
1 change: 1 addition & 0 deletions cnf/acl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package cnf
43 changes: 43 additions & 0 deletions cnf/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package cnf

import (
"time"
)

const (
VERSION string = "1.0"
LOG_TIME_FORMAT = "2006-01-02 15:04:05"
UDP_TTL = 30 * time.Second
UDP_BUFFER_SIZE = 4096
)

var DEBUG bool = false
var LOG_TIME bool = true
var CONFIG = Config{}

type Config struct {
Timeout ConfigTimeout `yaml:"timeout"`
Listen ConfigListen `yaml:"listen"`
Output ConfigOutput `yaml:"output"`
}

type ConfigListen struct {
Port int `yaml:"port,default=4128"`
IP4 []string `ip4:"ip4"`
IP6 []string `ip6:"ip6"`
Tcp bool `yaml:"tcp,default=true"`
Udp bool `yaml:"udp,default=false"`
Transparent bool `yaml:"transparent,default=false"`
}

type ConfigTimeout struct {
Connection int `yaml:"connection,default=5"`
Handshake int `yaml:"handshake,default=5"`
Dial int `yaml:"dial,default=5"`
Intercept int `yaml:"intercept,default=5"`
}

type ConfigOutput struct {
FwMark int `yaml:"fwmark,default=0"`
Interface int `yaml:"interface"`
}
44 changes: 44 additions & 0 deletions cnf/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---

config:
listen:
port: 4128
ip4:
- '127.0.0.1'
ip6:
- '::1'
tcp: true
udp: false
transparent: false

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

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]

filters:
- match:
dest: '192.168.100.0/24'
action: 'drop'

- match:
src: '$net_private'
dest: '$net_private'
port: '$svc_http'
protoL4: 'tcp'
action: 'accept'

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

import (
"fmt"
"net"
"strings"

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

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 {
switch strings.ToLower(configProto) {
case "ip4", "ipv4":
return meta.ProtoL3IP4
case "ip6", "ipv6":
return meta.ProtoL3IP6
default:
panic(fmt.Errorf("protoL3 '%v' not found", configProto))
}
}

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

func matchProtoL5(configProto string) meta.Proto {
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.Errorf("protoL5 '%v' not found or not yet supported", configProto))
}
}

/*
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 matchIPs(configIPs []string) (networks []*net.IPNet) {
// todo: allow users to provide single ip or list
for i := range configIPs {
ip := configIPs[i]
if strings.Contains(ip, "/") {
_, netip, err := net.ParseCIDR(ip)
if err != nil {
panic(fmt.Errorf("IP-network '%v' could not be parsed (must be valid CIDR-notation)", ip))
}
networks = append(
networks,
netip,
)

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

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

return
}
Loading

0 comments on commit 61352f3

Please sign in to comment.