Skip to content

Commit

Permalink
add tcp proxy; add http log; add --rules docopt
Browse files Browse the repository at this point in the history
  • Loading branch information
kung-foo committed Jan 18, 2017
1 parent 0cd22e2 commit 601514b
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 79 deletions.
84 changes: 28 additions & 56 deletions app/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"os"
"os/signal"
"sync"
"time"

docopt "github.com/docopt/docopt-go"
"github.com/kung-foo/freki"
Expand All @@ -16,15 +15,22 @@ var VERSION = "0.0.0"

var usage = `
Usage:
freki [options] [-v ...] -i=<interface>
freki [options] [-v ...] -i=<interface> -r=<rules>
freki -h | --help | --version
Options:
-i --interface=<iface> Bind to this interface.
-r --rules=<rules> Rules file.
-h --help Show this screen.
--version Show version.
-v Enable verbose logging (-vv for very verbose)
`

const (
tcpLoggerPort = 6000
httpLoggerPort = 6001
tcpProxyPort = 6002
)

func main() {
mainEx(os.Args[1:])
}
Expand All @@ -49,11 +55,17 @@ func mainEx(argv []string) {
args, err := docopt.Parse(usage, argv, true, VERSION, true)
onErrorExit(err)

// log.SetLevel(log.DebugLevel)
logger := log.New()
logger.Level = log.DebugLevel

rulesFile, err := os.Open("app/rules.yaml")
if args["-v"].(int) > 0 {
logger.Level = log.DebugLevel
}

if args["-v"].(int) > 1 {
// TODO: trace level
}

rulesFile, err := os.Open(args["--rules"].(string))
onErrorExit(err)

rules, err := freki.ReadRulesFromFile(rulesFile)
Expand All @@ -62,7 +74,9 @@ func mainEx(argv []string) {
processor, err := freki.New(args["--interface"].(string), rules, logger)
onErrorExit(err)

processor.AddServer(freki.NewTCPLogger(6000, 1024))
processor.AddServer(freki.NewTCPLogger(tcpLoggerPort, 1024))
processor.AddServer(freki.NewHTTPLogger(httpLoggerPort))
processor.AddServer(freki.NewTCPProxy(tcpProxyPort))

err = processor.Init()
onErrorExit(err)
Expand All @@ -78,61 +92,19 @@ func mainEx(argv []string) {
defer exit()
onInterruptSignal(exit)

go func() {
pp := uint64(0)
for range time.NewTicker(time.Second * 5).C {
t := processor.PacketsProcessed()
pps := (t - pp) / uint64(5)
logger.Debugf("PPS: %d", pps)
pp = t
}
}()

/*
// TODO: move
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
host, port, _ := net.SplitHostPort(r.RemoteAddr)
log.Infof("http %s %s", host, port)
ck := freki.NewConnKeyByString(host, port)
md := processor.Connections.GetByFlow(ck)
//fmt.Fprintf(w, "Hello on port %d\n", md.TargetPort)
log.Infof("%s -> %s", host, md.TargetPort.String())
fmt.Fprintf(w, "OK")
})
go http.ListenAndServe(":8080", nil)
*/

/*
go func() {
ln, err := net.Listen("tcp", ":6000")
onErrorExit(err)
for {
conn, err := ln.Accept()
onErrorExit(err)
go func(conn net.Conn) {
conn.SetReadDeadline(time.Now().Add(time.Second * 5))
host, port, _ := net.SplitHostPort(conn.RemoteAddr().String())
ck := freki.NewConnKeyByString(host, port)
md := processor.Connections.GetByFlow(ck)
log.Infof("%s -> %s", host, md.TargetPort)
buffer := make([]byte, 1024)
n, _ := conn.Read(buffer)
hex.Dumper(log.StandardLogger().Writer()).Write(buffer[0:n])
err := conn.Close()
if err != nil {
log.Error(err)
}
}(conn)
pp := uint64(0)
for range time.NewTicker(time.Second * 5).C {
t := processor.PacketsProcessed()
pps := (t - pp) / uint64(5)
if pps > 0 {
logger.Debugf("PPS: %d", pps)
}
pp = t
}
}()
*/

// TODO: pass in stop channel
processor.Start()

// processor.Stop()
}
74 changes: 51 additions & 23 deletions freki.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,36 @@ const table = "raw"

var chains = []string{"PREROUTING", "OUTPUT"}

// TODO: add "-i iface" to PREROUTING
func genRule(protocol, queuespec string) []string {
return strings.Split(fmt.Sprintf("-p,%s,-j,NFQUEUE,--queue-num,%s", protocol, queuespec), ",")
func genRuleSpec(chain, iface, protocol, queuespec string) []string {
spec := "-p,%s,-j,NFQUEUE,--queue-num,%s"
if chain == "PREROUTING" {
spec = "-i,%s," + spec
}
if chain == "OUTPUT" {
spec = "-o,%s," + spec
}
return strings.Split(fmt.Sprintf(spec, iface, protocol, queuespec), ",")
}

type iptrule struct {
table string
chain string
rulespec []string
}

func (r *iptrule) Append(ipt *iptables.IPTables) error {
return ipt.AppendUnique(r.table, r.chain, r.rulespec...)
}

func (r *iptrule) Delete(ipt *iptables.IPTables) error {
return ipt.Delete(r.table, r.chain, r.rulespec...)
}

type Processor struct {
log Logger
rules []*Rule
ipt *iptables.IPTables
iptRules [][]string
iptRules []iptrule
nfq *netfilter.Queue
cleanupOnce sync.Once
Connections *connTable
Expand Down Expand Up @@ -64,7 +84,7 @@ func New(ifaceName string, rules []*Rule, logger Logger) (*Processor, error) {
processor := &Processor{
rules: rules,
log: logger,
iptRules: make([][]string, 0),
iptRules: make([]iptrule, 0),
Connections: newConnTable(logger),
shutdown: make(chan struct{}),
publicAddrs: nonLoopbackAddrs,
Expand All @@ -73,11 +93,15 @@ func New(ifaceName string, rules []*Rule, logger Logger) (*Processor, error) {
}

// TODO: customize protocols
processor.iptRules = append(processor.iptRules,
genRule("tcp", "0"),
// genRule("udp", "0"),
// genRule("icmp", "0"),
)

for _, chain := range chains {
r := iptrule{
table: table,
chain: chain,
rulespec: genRuleSpec(chain, ifaceName, "tcp", "0"),
}
processor.iptRules = append(processor.iptRules, r)
}

return processor, nil
}
Expand All @@ -88,23 +112,19 @@ func (p *Processor) AddServer(s Server) {

func (p *Processor) initIPTables() (err error) {
for _, rule := range p.iptRules {
for _, chain := range chains {
err = p.ipt.AppendUnique(table, chain, rule...)
if err != nil {
return
}
err = rule.Append(p.ipt)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("%+v", rule))
}
}
return
}

func (p *Processor) resetIPTables() (err error) {
for _, rule := range p.iptRules {
for _, chain := range chains {
err = p.ipt.Delete(table, chain, rule...)
if err != nil {
p.log.Errorf("[freki ] error deleting \"%s %s\": %v", table, chain, err)
}
err = rule.Delete(p.ipt)
if err != nil {
p.log.Errorf("[freki ] error deleting: %+v", rule)
}
}
return
Expand All @@ -116,7 +136,8 @@ func (p *Processor) Init() (err error) {
return
}

_, err = p.ipt.List("filter", "INPUT")
// quick permissions test
_, err = p.ipt.List(table, chains[0])
if err != nil {
return
}
Expand Down Expand Up @@ -261,7 +282,7 @@ func (p *Processor) mangle(
}

switch md.Rule.ruleType {
case Rewrite, LogTCP:
case Rewrite, LogTCP, LogHTTP, ProxyTCP:
tcp.SrcPort = layers.TCPPort(md.TargetPort)
goto modified
case Drop:
Expand All @@ -288,6 +309,13 @@ func (p *Processor) mangle(
// TODO: optimize?
tcp.DstPort = layers.TCPPort(p.servers["log.tcp"].Port())
goto modified
case LogHTTP:
// TODO: optimize?
tcp.DstPort = layers.TCPPort(p.servers["log.http"].Port())
goto modified
case ProxyTCP:
tcp.DstPort = layers.TCPPort(p.servers["proxy.tcp"].Port())
goto modified
case Drop:
goto drop
case PassThrough:
Expand Down Expand Up @@ -383,7 +411,7 @@ func (p *Processor) onPacket(rawPacket *netfilter.RawPacket) (err error) {
rule, err = p.applyRules(packet)

if err != nil {
p.log.Errorf("[freki ] ", err)
p.log.Errorf("[freki ] %v", err)
goto accept
}

Expand Down
66 changes: 66 additions & 0 deletions log_http.go
Original file line number Diff line number Diff line change
@@ -1 +1,67 @@
package freki

import (
"encoding/hex"
"fmt"
"io/ioutil"
"net"
"net/http"
)

type HTTPLogger struct {
port uint
//maxReadSize uint
processor *Processor
log Logger
}

func NewHTTPLogger(port uint) *HTTPLogger {
return &HTTPLogger{
port: port,
}
}

func (h *HTTPLogger) Port() uint {
return h.port
}

func (h *HTTPLogger) Type() string {
return "log.http"
}

func (h *HTTPLogger) Start(p *Processor) error {
h.processor = p
h.log = h.processor.log

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
host, port, _ := net.SplitHostPort(r.RemoteAddr)
ck := NewConnKeyByString(host, port)
md := h.processor.Connections.GetByFlow(ck)
h.log.Infof("[log.http] %s -> %s\n%s %s\n%v",
host,
md.TargetPort.String(),
r.Method, r.URL,
r.Header)

if r.Body != nil {
defer r.Body.Close()
body, _ := ioutil.ReadAll(r.Body)
if len(body) > 0 {
h.log.Infof("[log.http] %s -> %s\n%s",
host,
md.TargetPort.String(),
hex.Dump(body),
)
}
}

fmt.Fprintf(w, "OK\n")
})

return http.ListenAndServe(fmt.Sprintf(":%d", h.port), nil)
}

func (h *HTTPLogger) Shutdown() error {
// TODO: go1.8 add server shutdown
return nil
}
Loading

0 comments on commit 601514b

Please sign in to comment.