goul

package module
v0.0.0-...-1f56359 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 20, 2022 License: GPL-3.0 Imports: 10 Imported by: 0

README

Goul, Virtual Port Mirror over Internet (for Cloud)

Goul Test DeepSource codecov Coverage Status Code Climate Go Report Card Go Reference GitHub license

Goul(거울; Mirror in English) is a tool for virtual network port mirroring over the Internet for network traffic analyze and/or security monitoring. May useful especially for cloud computing environment.

On legacy infrastructure, with many physical switches, we can use a port mirroring (SPAN; Switched Port Analyzer in Cisco's term) on the switch for monitoring and analyzing of traffic, and connecting a security appliances. But in cloud computing environment, it is not easy as legacy and in some cases, it is completely impossible.

This tool is for someone like me who want to mirror some port of the virtual instances or virtual network appliances.

Goul is now on development using Go language with gopacket and pcap library but not yet stable enough.

Features

  • Mirror one network device(port) from virtual instance to remote system.
  • Selection of Rx, Tx or Both direction of traffic. (Plan)
  • Packet filtering based on pcap library's rule.
  • Pipelining for filtering, buffering, compression, deduplication, and more.
  • Use TCP/IP for transmission over the Internet.
  • Support adaptive mode to reduce the impact of production traffic. (Plan)

Architecture

Goul is a set of library and its default executable. The executable goul is a program written with goul library ofcause.

Goul Library
Goul Executable

Goul consists of Controller, Processing Router, Read/Write Adapters, and Pipes. In conceptual structure, The Read Adapter is positioned on one edge of the frame and Write Adapter is located on the other side. Between them, the Processing Router is located and it contains some Pipes. For controlling of them, the Controller is positioned on top of them. It is easier to understand this with diagram:

Goul Client Block

Goul can be executed as both server mode and client mode. The diagram above is for client mode of Goul. The Read Adapter for client is Device Adapter and it is located on right bottom of the frame (red box). By the Read Adapter, Forward Pipeline, the virtual component of the Router is positioned with Router (green boxes) and it has three Pipes from #1 to #3 (yellow boxes). On left bottom, Network Adapter, the Write Adapter for client is located (blue box). All of them are covered with Controller finally. With this straight pipeline formation, Device Adapter works as a source of the pipeline and Network Adapter will be a sync of the pipeline.

Goul Server Block

In server mode, next diagram, basic structure is same as client but there are two differences. The first one is the Adapters. Since Goul works as receiver and packet injector in server mode, the Read Adapter is configured with Network Adapter and the Write Adapter is configured with Device Adapter. Second is the order of Pipes. As you can see on the label of pipeline, we call it Reverse Pipeline in contrast to Forward Pipeline of client, the order of Pipes is reversed in server mode. #3, #2 and #1

Data Flow

Client Mode

When the Goul started, it setup Router with Adapters and Pipes. In client mode, Read Adapter initializes network interface device for packet capture and Write Adapter makes a connection to the server while setup. When Goul executes Router, Router executes Adapter's reader function and writer function, and Pipe functions internally then all these functions will start their own loop as goroutines.

When the reader loop read packets from the network interface device, the packets are passed into the Processing Router via channel. Bundled executable uses the default Pipeline Router as a Router so the packets are pushed into the Forward Pipeline (Actually the input channel of the first Pipe). Pipeline Router is a chain of the Pipes. Each Pipe has its own loop for the processing of incoming data such as compression, counting, deduplication, and so on. The output of the Read Adapter is always a packet data but since some of Pipes are used for modifying the packet data, for example compress it with zlib or gzip, the data type on the pipeline can not be specified as single format (See Router Details). Another end of the pipeline is connected to Write Adapter's writer function and it receives data from the pipeline then just send it to the connected server.

Diagram below diagram shows this client flow:

Data Flow of Client

Server Mode

As you can imagine, the data flow of the server mode also as same as client since it just a mode, with same structure. When Goul runs as server mode, it makes a listner for the connections from clients, and initialize network device for packet injection. If a client was connected, It invoke separated goroutine for each connection but using same pipeline and writer for all of clients.

As already said, second difference of server mode is the order of the Pipes. With same reason, the data type from reader to the end of the pipeline can not be specified but the input data to the writer function is packet data. Note that, the order of the Pipes are very important for Pipeline Router. This Router is just a simple ORDERED stack of the functions. Even though I write the Pipes carefully with exception handling for the data type, it can be broken if you use custome Pipe without this consideration.

Flow of the server side is shown in this diagram:

Data Flow of Server

Yes, Quite simple!

All Together Now

In Data Flow section, we just show the data flow inside of the Goul program, for client mode and server mode. Now "Calling All Engines" and say "All Together Now!"

Let say there is a virtual network appliance on the cloud environment and we have network analyzer on the on-premises data center. In below configuration, the right side is the cloud environment with running instance of Goul in client mode and the left side is the on-premises with network analyzer and running Goul in server mode.

Direct Configuration

The packets on the cloud network (thick blue line on right bottom) should be readable by the NIC on the client for this works. (Yes, sometimes we need Promiscuos mode or the box contains the client must be a router or similar phase.) After captured and processed, the packet on the blue line will be sent via the Internet and it will be received by server. Server will process it back and inject the packets into the network interface that connected directly to the network analyzer. As a result, even though the network analyzer cannot see the packets on the blue line directly, all of the packets can be analyzed by the analyzer.

Sometime, No. In most cases, the network analyzer is not dedicated device for this target network. In this case, we can configure our environment like below:

Aggregated Configuration

In this case, we use additional physical switch to make it as hub of the analyzing. If the switch was configured well as mirror or dummy, the packets from the top port which is connected to the server can be found on the bottom port which is connected to the network analyzer.

Simple too!

Router Details

Processing Router is a set of processing units which controls data processing such as compression, deduplication, and other required processing. In perspective of abstraction, the Router has only two channels. The input channel and output channel. Input channel is the output channel of the Read Adapter and output channel is the input channel for the Write Adapter. With this abstracted interface rules, the internal of the Router can be free.

The default Router for the Goul is Pipeline Router. This requires right order of the Pipes for forward pipeline of client and reverse pipeline of server. Original design of the Router is that it handles MIME type of the data passed between Pipes and automatically find right Pipe for the data type with MIME but it is not easy to implement (since we need to know not only the input type of the Pipe but also the output type, and type of the final goal too.) and the effect of the real routing is not big as I expected at the first time.

Anyway, user can implement their own Router with the interfaces.

Pipe Details

Writing a Pipe also very easy. The interface for the pipe is very few and simple. Currently gzip and zlib compression/decompression, packet counting and printing for debug are included in the library.

Prefered candidates are packet counting with packet types for statistics, discard some protocol on air, deduplication of the packet, and so on. Note that protocol or port based filtering is supported by gopacket library itself and it can be passed to Goul as command line arguments.

Adapter Details

Adapter is most important part of the Goul. Everything is started from Adapter and is finished with Adapter. There is two types of Adapters and they must be provided to Router before run it.

Read Adapter is a Adapter for reading data from its own input source. For example, the Read Adapter of client is Device Adapter as I already mentioned. It reads packet data from configured network interface and push it into the pipeline. Like this, Read Adapter is used as source of the pipeline.

Write Adapter is a Adapter for writing given data to its destination. For example, the Write Adapter for the server also Device Adapter and it write(inject) given packet into the target device. Yes, Write Adapter is used as sync of the pipeline, ofcause.

Controller Details

Controller for the Goul was planed but currently not implemented. Currently, just a simple interrupt handler is used as controller for terminate Goul gracefully. If Goul receives interrupt signal, the signal number 2 in linux and similar, the controller catch it and send a termination message to the router via control channel (In fact, it just close the channel now but the fine controll can be implemented).

Diagram below shows the propagation of the termination message:

Client Mode

Control Flow of Client

Server Mode

Control Flow of Server

Installation

Installation of Goul is same as any Go programs. Just get it. But while compiling it, it needs libpcap development packet so you need to install it before get. Below is the installation process for Ubuntu Linux. (Or other Debian based Linux distributions)

$ sudo apt-get install libpcap-dev gcc
<...>
$ go get github.com/hyeoncheon/goul/cmd/goul
$
$ ls $GOPATH/pkg/linux_amd64/github.com/hyeoncheon
goul  goul.a
$
$ ls $GOPATH/bin
goul
$

Running

Goul was wrote as can support 2x2 mode. It means, the server can be both a capturer and injector, the client also can be a capturer and injector. with this, we can configure our environment eaily even if there is a very complex firewalls in front of the receiver(injector). So called reverse connection mode: server do capture on the interface of its device and wait for connection then send it to the connected client which can act as receiver/injector.

Currently, I removed those codes for 2x2 mode because it makes the code is not readable sometime and the chance to use this reverse connection mode is rare. Most important reason for this decision is, it is harder to implement multi-session receiver for multiple sources.

For the complex firewall environment, I have a plan of proxy mode. To be continued...

Anyway,

Command line options for version 0.2 are shown below:

$ ./goul --help
goul 0.2-head

goul is a packet capture program for cloud environment.

If it runs as capturer mode, it captures all packets on local network
interface and sends them to remote receiver over internet.
The other side, while it runs as receiver mode, it receives packets from
remote capturer and inject them into the interface on the system.

Usage: goul [-DhlsTv] [-a value] [-d value] [-p value] filters ...
 -a, --addr=value  address to connect (for client)
 -D, --debug       debugging mode (print log messages)
 -d, --dev=value   network interface to read/write
 -h, --help        help
 -l, --list        list network devices
 -p, --port=value  tcp port number (default is 6001)
 -s, --server      run as receiver
 -T, --test        test mode (no injection)
 -v, --version     show version of goul
$

Command for server mode is:

$ sudo ./goul --server
<...>

Command for client mode is:

$ sudo ./goul --addr 10.0.0.1
<...>

10.0.0.1 is a IP address of the server. Note that, for both server and client, you need super user permission to handle network interface devices. use sudo for it on command line.

If you cannot use default port number 6001 with any reason, simply pass -p # or --port=# for assigning user defined port. # is the number of the port to listen or connect.

Default device to capture or injection is eth0. but I know in most cases, it need to be overrided. Use -d dev or --device dev option for your device configuration. If you don't know which devices are exists or configured on the system, use -l or --listfor listing all devices. Root privilege is not requires for this.

$ goul -l

Devices:
* eth0
  - IP address:  10.0.0.2
  - IP address:  fe80::465:0000:0000:40000
* eth1
  - IP address:  192.168.0.2
  - IP address:  fe80::4a8:0000:0000:0000
* any
* lo
  - IP address:  127.0.0.1
  - IP address:  ::1
$

The Goul is currently on development and it can be unstable. So if you just want to test it without real packet injection, use -t flag. It allows receiver runs in testing mode then it does not inject but just display the number of packets it received.

Along with -t flag, -D flag is useful for testing. It turns Goul in verbose mode and it print out a lots of messages while running.

Please note that, The Goul's default capturing filter is ip. so it just ignore all non-IP protocols including L2 level protocols. If you want to set more specific filter, you can put it in the end of the command line. (filters ...) The filter rule is same as other pcap based application like tcpdump. So you can set port 80 and port 443 as filter for getting HTTP and HTTPS traffic.

Have fun with packets! and funnier with the Goul!

Current Status

  • Support simple capturing and injection of packet over the Internet.
  • Currently all compression features are disabled by default.
    • I found that it consumes CPU but the compression ratio is not effective.
  • Currently Pipeline configuration from command line is not supported.

Caution!!!

THIS PROGRAM IS WORKING WITH LOW LEVEL NETWORKING FEATURES. DO NOT USE THIS PROGRAM IF YOU DO NOT UNDERSTAND WHAT IT DOES.

ESPECIALLY, DO NOT FORWARD L2 MANAGEMENT PROTOCOL LIKE STP INTO THE NORMALLY CONFIGURED PORT. IT CAN BREAK YOUR ENTIRE NETWORK.

Author

Yonghwan SO https://github.com/sio4, http://www.sauru.so

Copyright 2016 Yonghwan SO

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

Documentation

Index

Constants

View Source
const (
	ErrAdapterReadNotImplemented  = "AdapterReadNotImplemented"
	ErrAdapterWriteNotImplemented = "AdapterWriteNotImplemented"
)

constants.

View Source
const (
	ItemTypeUnknown   = "unknown"
	ItemTypeRawPacket = "rawpacket"
)

constants...

View Source
const (
	LogLevelDebug = "debug"
	LogLevelInfo  = "info"
	LogLevelWarn  = "wan"
	LogLevelErr   = "error"
)

constants

View Source
const (
	ModeConverter = true
	ModeReverter  = false

	ErrPipeConvertNotImplemented = "convert method of pipe not implemented"
	ErrPipeRevertNotImplemented  = "revert method of pipe not implemented"
	ErrPipeInputClosed           = "input channel closed"
)

constants.

View Source
const (
	ErrRouterPipelineNotSupported = "RouterPipelineNotSupported"
	ErrRouterNoReaderOrWriter     = "RouterNoReaderOrWriter"
)

error messages of router subsystem.

View Source
const ChannelSize = 10

ChannelSize is a default size of pipeline channels.

Variables

View Source
var Messages = map[string]*ItemGeneric{
	"closed": {
		Meta: "message",
		DATA: []byte("channel closed. done"),
	},
}

Messages is a map for system messages.

Functions

func Error

func Error(logger Logger, module, fmt string, args ...interface{})

Error ...

func GoID

func GoID() uint64

GoID returns goroutine ID.

https://blog.sgmansfield.com/2015/12/goroutine-ids/
https://groups.google.com/d/msg/golang-nuts/Nt0hVV_nqHE/bwndAYvxAAAJ
https://play.golang.org/p/OeEmT_CXyO

func Launch

func Launch(fn PipeFunction, in chan Item, message Message) (chan Item, error)

Launch is a helper function that make goroutine execution simple.

func Log

func Log(logger Logger, module, format string, args ...interface{})

Log ...

func PrintDevices

func PrintDevices() error

PrintDevices print out all interfaces on the system.

Types

type Adapter

type Adapter interface {
	CommonMixin
	Write(in chan Item, message Message) (done chan Item, err error)
	Read(ctrl chan Item, message Message) (out chan Item, err error)
	Close() error
}

Adapter is an interface for in/out adapters for pipeline.

type BaseAdapter

type BaseAdapter struct {
	BaseCommon
	ID string
}

BaseAdapter is a base implementation for the Adapter interface

func (*BaseAdapter) Close

func (a *BaseAdapter) Close() error

Close implements Adapter interface

func (*BaseAdapter) Read

func (a *BaseAdapter) Read(ctrl chan Item, message Message) (chan Item, error)

func (*BaseAdapter) Write

func (a *BaseAdapter) Write(in chan Item, message Message) (chan Item, error)

type BaseCommon

type BaseCommon struct {
	// contains filtered or unexported fields
}

BaseCommon is a base implementation for the CommonMixin

func (*BaseCommon) GetError

func (c *BaseCommon) GetError() error

GetError implements CommonMixin

func (*BaseCommon) GetLogger

func (c *BaseCommon) GetLogger() Logger

GetLogger implements CommonMixin

func (*BaseCommon) SetError

func (c *BaseCommon) SetError(err error)

SetError implements CommonMixin

func (*BaseCommon) SetLogger

func (c *BaseCommon) SetLogger(logger Logger) error

SetLogger implements CommonMixin

type BasePipe

type BasePipe struct {
	BaseCommon
	Mode bool
}

BasePipe is a base implementation for the Pipe interface

func (*BasePipe) Convert

func (p *BasePipe) Convert(in chan Item, message Message) (chan Item, error)

Convert implements interface Converter

func (*BasePipe) IsConverter

func (p *BasePipe) IsConverter() bool

IsConverter implements Pipe

func (*BasePipe) Revert

func (p *BasePipe) Revert(in chan Item, message Message) (chan Item, error)

Revert implements interface Revert

type BaseRouter

type BaseRouter struct {
	// contains filtered or unexported fields
}

BaseRouter is a simple/sample router that just support in/out adapters. You can use it as a base of your own router implementation so you can just implement and override interesting parts of your implementation. In this case, you can use following statement:

router := &MyRouter{Router: &BaseRouter{}}

Please refer to goul.Pipeline and related test cases for more detail. (router_pipeline.go)

func (*BaseRouter) AddPipe

func (r *BaseRouter) AddPipe(pipe Pipe) error

AddPipe implements Router: but BaseRouter does not support pipelining.

func (*BaseRouter) GetPipes

func (r *BaseRouter) GetPipes() []Pipe

GetPipes implements Router: placeholder

func (*BaseRouter) Run

func (r *BaseRouter) Run() (chan Item, chan Item, error)

Run implements Router. This implementation just invoke reader and writer without pipes.

func (*BaseRouter) SetLogger

func (r *BaseRouter) SetLogger(logger Logger) error

SetLogger implements Router: working

func (*BaseRouter) SetReader

func (r *BaseRouter) SetReader(reader Adapter) error

SetReader implements Router: working

func (*BaseRouter) SetWriter

func (r *BaseRouter) SetWriter(writer Adapter) error

SetWriter implements Router: working

type CommonMixin

type CommonMixin interface {
	SetLogger(logger Logger) error
	GetLogger() Logger
	SetError(err error)
	GetError() error
}

CommonMixin is an interface for common functions

type Item

type Item interface {
	String() string
	Data() []byte
}

Item is an interface for passing data between pipes.

type ItemGeneric

type ItemGeneric struct {
	Meta string
	DATA []byte
}

ItemGeneric is a structure for the generic byte slice data.

func (*ItemGeneric) Data

func (c *ItemGeneric) Data() []byte

Data implements goul.Item

func (*ItemGeneric) String

func (c *ItemGeneric) String() string

String implements goul.Item

type Logger

type Logger interface {
	Debug(args ...interface{})
	Debugf(format string, args ...interface{})
	Debugln(args ...interface{})
	Info(args ...interface{})
	Infof(format string, args ...interface{})
	Infoln(args ...interface{})
	Warn(args ...interface{})
	Warnf(format string, args ...interface{})
	Warnln(args ...interface{})
	Error(args ...interface{})
	Errorf(format string, args ...interface{})
	Errorln(args ...interface{})
	Printf(format string, args ...interface{})
	Println(args ...interface{})
}

Logger is an interface of the logging facility

func NewLogger

func NewLogger(level string) Logger

NewLogger returns new logger. currently an instance of logrus.

type Message

type Message interface {
}

Message ...

type Pipe

type Pipe interface {
	CommonMixin
	Convert(in chan Item, message Message) (out chan Item, err error)
	Revert(in chan Item, message Message) (out chan Item, err error)
	IsConverter() bool
}

Pipe is an interface for pipeline handlers.

type PipeFunction

type PipeFunction func(in, out chan Item, message Message)

PipeFunction ...

type Pipeline

type Pipeline struct {
	Router
	// contains filtered or unexported fields
}

Pipeline is a structure of pipeline router

func (*Pipeline) AddPipe

func (r *Pipeline) AddPipe(pipe Pipe) (err error)

AddPipe implements Router

func (*Pipeline) GetPipes

func (r *Pipeline) GetPipes() []Pipe

GetPipes implements Router

func (*Pipeline) Run

func (r *Pipeline) Run() (ctrl, done chan Item, err error)

Run implements Router

type Router

type Router interface {
	// Run prepares all necessary things and start reader, writer and all
	// registered processors.
	// BaseRouter provides simple Run function with reader and writer but
	// You should implement this function for your specific router.
	Run() (control, done chan Item, err error)
	// AddPipe adds given data processor to the end of the processor list.
	// BaseRouter does not support pipeline and you should implement your
	// own AddPipe function for your router.
	AddPipe(pipe Pipe) error

	// SetReader sets given reader as a data source of the processing router.
	SetReader(reader Adapter) error
	// SetWriter sets given writer as a data sink of the processing router.
	SetWriter(writer Adapter) error
	// SetLogger sets given logger as a logger for the router.
	SetLogger(logger Logger) error
	// GetPipes returns a slice of pipe handlers.
	GetPipes() []Pipe
	// contains filtered or unexported methods
}

Router is an interface for the heart of the goul processing.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL