security #6

Merged
JakeHillion merged 15 commits from security into develop 2021-01-23 16:05:08 +00:00
20 changed files with 239 additions and 179 deletions

133
README.md
View File

@ -4,11 +4,11 @@
### Linux
#### Policy Based Routing
ip route flush 10
ip route flush table 10
ip route add table 10 to 1.1.1.0/24 dev eth1
ip rule add from 1.1.1.4 table 10 priority 10
ip route flush 11
ip route flush table 11
ip route add table 11 to 1.1.1.0/24 dev eth2
ip rule add from 1.1.1.5 table 11 priority 11
@ -20,69 +20,114 @@
See http://kb.linuxvirtualserver.org/wiki/Using_arp_announce/arp_ignore_to_disable_ARP
### Setup Scripts
These are functional setup scripts that make the application run as intended on Linux. They should later be split into
component parts, or incorporated into the main application.
These are functional setup scripts that make the application run as intended on Linux.
#### Remote Portal
### Remote Portal
#### Pre-Start
#!/bin/bash
set -e
# IPv4 Forwarding
## Set up variables
REMOTE_PORTAL_ADDRESS=A.B.C.D
## IPv4 Forwarding
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv4.conf.eth0.proxy_arp=1
# Tunnel addr/up
## Transfer the local routing table to a much lower priority
(ip rule show | grep '20:') > /dev/null || ip rule add from all table local priority 20
ip rule del priority 0 2> /dev/null || true
## Ports to route locally
### MPBL3P
ip rule del priority 1 2> /dev/null || true
ip rule add to "$REMOTE_PORTAL_ADDRESS" dport 1234 table local priority 1
### SSH
ip rule del priority 2 2> /dev/null || true
ip rule add to "$REMOTE_PORTAL_ADDRESS" dport 22 table local priority 2
#### Post-Start
#!/bin/bash
set -e
## Set up variables
REMOTE_PORTAL_ADDRESS=A.B.C.D
## Tunnel addr/up
ip addr add 172.19.152.2/31 dev nc0
ip link set up nc0
# Deliberately break local routing
ip rule add from all table local priority 20
ip rule del 0 || true
# Route packets to the interface but for nc to this host
ip rule add to 1.1.1.3 dport 1234 table local priority 9
# Route packets to the interface but not for nc via the tunnel
ip route flush 10
ip route add table 10 to 1.1.1.3 via 172.19.152.3 dev nc0
ip rule add to 1.1.1.3 table 10 priority 10
ip route flush table 19
ip route add table 19 to "$REMOTE_PORTAL_ADDRESS" via 172.19.152.3 dev nc0
ip rule del priority 19 2> /dev/null || true
ip rule add to "$REMOTE_PORTAL_ADDRESS" table 19 priority 19
#### Local Portal
### Local Portal
#### Pre-Start
#!/bin/bash
set -e
## Set up variables
GATEWAY_INTERFACE=eth0
GATEWAY_ADDRESS=10.36.12.1
## Fix ARP
sysctl -w net.ipv4.conf.all.arp_announce=1
sysctl -w net.ipv4.conf.all.arp_ignore=1
## IPv4 Forwarding
sysctl -w net.ipv4.ip_forward=1
## Gateway Interface Setup
ip addr flush dev "$GATEWAY_INTERFACE"
ip addr add "$GATEWAY_ADDRESS"/32 dev "$GATEWAY_INTERFACE"
ip link set up "$GATEWAY_INTERFACE"
## Per-Interface Routing Tables
### 10.10.0.0/24
ip route flush table 10
ip route add table 10 default via 10.10.0.1
ip rule del priority 10 2> /dev/null || true
ip rule add from 10.10.0.0/24 table 10 priority 10
### 192.168.0.0/24
ip route flush table 11
ip route add table 11 default via 192.168.0.1
ip rule del priority 11 2> /dev/null || true
ip rule add from 192.168.0.0/24 table 11 priority 11
#### Post-Start
#!/bin/bash
set -e
# Fix ARP
sysctl -w net.ipv4.conf.all.arp_announce=1
sysctl -w net.ipv4.conf.all.arp_ignore=1
## Set up variables
REMOTE_PORTAL_ADDRESS=A.B.C.D
GATEWAY_INTERFACE=eth0
# IPv4 Forwarding
sysctl -w net.ipv4.ip_forward=1
# Tunnel addr/up
## Tunnel Address and Enable
ip addr add 172.19.152.3/31 dev nc0
ip link set up nc0
# Fix routing out of the correct interfaces
ip route flush 10
ip route add table 10 to 1.1.1.0/24 dev eth1
ip rule add from 1.1.1.4 table 10 priority 10
## Route Outbound Packets Correctly
ip route flush table 20
ip route add table 20 default via 172.19.152.2 dev nc0
ip rule del priority 20 2> /dev/null || true
ip rule add from "$REMOTE_PORTAL_ADDRESS" iif "$GATEWAY_INTERFACE" table 20 priority 20
ip route flush 11
ip route add table 11 to 1.1.1.0/24 dev eth2
ip rule add from 1.1.1.5 table 11 priority 11
# Route packets from the remote portal's address on the client interface via the tunnel
ip route flush 12
ip route add table 12 to 1.1.1.0/24 via 172.19.152.2 dev nc0
ip rule add from 1.1.1.3 iif eth3 table 12 priority 12
# Route packets to the remote portal's address out of the client interface
ip route flush 13
ip route add table 13 to 1.1.1.3 dev eth3
ip rule add to 1.1.1.3 table 13 priority 13
## Route Inbound Packets Correctly
ip route flush table 21
ip route add table 21 to "$REMOTE_PORTAL_ADDRESS" dev "$GATEWAY_INTERFACE"
ip rule del priority 21 2> /dev/null || true
ip rule add to "$REMOTE_PORTAL_ADDRESS" table 21 priority 21
#### Client
No configuration needed. Simply set the IP to that of the remote server/32 with a gateway of 192.168.1.1.
Connect to `GATEWAY_INTERFACE` and set the IP to `REMOTE_PORTAL_ADDRESS`/32 with a gateway of `GATEWAY_ADDRESS`.

View File

@ -1,7 +1,10 @@
package config
import (
"encoding/base64"
"fmt"
"mpbl3p/crypto"
"mpbl3p/crypto/sharedkey"
"mpbl3p/proxy"
"mpbl3p/tcp"
"mpbl3p/tun"
@ -10,24 +13,33 @@ import (
"time"
)
// TODO: Delete this code as soon as an alternative is available
type UselessMac struct{}
func (UselessMac) CodeLength() int {
return 0
}
func (UselessMac) Generate([]byte) []byte {
return nil
}
func (u UselessMac) Verify([]byte, []byte) error {
return nil
}
func (c Configuration) Build() (*proxy.Proxy, error) {
p := proxy.NewProxy(0)
p.Generator = UselessMac{}
var g func() proxy.MacGenerator
var v func() proxy.MacVerifier
switch c.Host.Crypto {
case "None":
g = func() proxy.MacGenerator { return crypto.None{} }
v = func() proxy.MacVerifier { return crypto.None{} }
case "Blake2s":
key, err := base64.StdEncoding.DecodeString(c.Host.SharedKey)
if err != nil {
return nil, err
}
if _, err := sharedkey.NewBlake2s(key); err != nil {
return nil, err
}
g = func() proxy.MacGenerator {
g, _ := sharedkey.NewBlake2s(key)
return g
}
v = func() proxy.MacVerifier {
v, _ := sharedkey.NewBlake2s(key)
return v
}
}
if c.Host.InterfaceName == "" {
c.Host.InterfaceName = "nc%d"
@ -44,13 +56,11 @@ func (c Configuration) Build() (*proxy.Proxy, error) {
for _, peer := range c.Peers {
switch peer.Method {
case "TCP":
err := buildTcp(p, peer)
if err != nil {
if err := buildTcp(p, peer, g, v); err != nil {
return nil, err
}
case "UDP":
err := buildUdp(p, peer)
if err != nil {
if err := buildUdp(p, peer, g, v); err != nil {
return nil, err
}
}
@ -59,7 +69,7 @@ func (c Configuration) Build() (*proxy.Proxy, error) {
return p, nil
}
func buildTcp(p *proxy.Proxy, peer Peer) error {
func buildTcp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() proxy.MacVerifier) error {
if peer.RemoteHost != "" {
f, err := tcp.InitiateFlow(
fmt.Sprintf("%s:", peer.LocalHost),
@ -70,13 +80,13 @@ func buildTcp(p *proxy.Proxy, peer Peer) error {
return err
}
p.AddConsumer(f)
p.AddProducer(f, UselessMac{})
p.AddConsumer(f, g())
p.AddProducer(f, v())
return nil
}
err := tcp.NewListener(p, fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort), UselessMac{})
err := tcp.NewListener(p, fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort), v, g)
if err != nil {
return err
}
@ -84,7 +94,7 @@ func buildTcp(p *proxy.Proxy, peer Peer) error {
return nil
}
func buildUdp(p *proxy.Proxy, peer Peer) error {
func buildUdp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() proxy.MacVerifier) error {
var c func() udp.Congestion
switch peer.Congestion {
case "None":
@ -99,8 +109,8 @@ func buildUdp(p *proxy.Proxy, peer Peer) error {
f, err := udp.InitiateFlow(
fmt.Sprintf("%s:", peer.LocalHost),
fmt.Sprintf("%s:%d", peer.RemoteHost, peer.RemotePort),
UselessMac{},
UselessMac{},
v(),
g(),
c(),
time.Duration(peer.KeepAlive)*time.Second,
)
@ -109,19 +119,13 @@ func buildUdp(p *proxy.Proxy, peer Peer) error {
return err
}
p.AddConsumer(f)
p.AddProducer(f, UselessMac{})
p.AddConsumer(f, g())
p.AddProducer(f, v())
return nil
}
err := udp.NewListener(
p,
fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort),
UselessMac{},
UselessMac{},
c,
)
err := udp.NewListener(p, fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort), v, g, c)
if err != nil {
return err
}

View File

@ -10,13 +10,14 @@ type Configuration struct {
}
type Host struct {
PrivateKey string `validate:"required"`
InterfaceName string
Crypto string `validate:"required,oneof=None Blake2s"`
SharedKey string `validate:"required_if=Crypto Blake2s"`
}
type Peer struct {
PublicKey string `validate:"required"`
Method string `validate:"oneof=TCP UDP"`
Method string `validate:"oneof=TCP UDP"`
LocalHost string `validate:"omitempty,ip"`
LocalPort uint `validate:"max=65535"`

15
crypto/none.go Normal file
View File

@ -0,0 +1,15 @@
package crypto
type None struct{}
func (None) CodeLength() int {
return 0
}
func (None) Generate([]byte) []byte {
return nil
}
func (None) Verify([]byte, []byte) error {
return nil
}

View File

@ -0,0 +1,40 @@
package sharedkey
import (
"bytes"
"golang.org/x/crypto/blake2s"
"mpbl3p/shared"
)
type Blake2s struct {
key []byte
}
func NewBlake2s(key []byte) (*Blake2s, error) {
_, err := blake2s.New128(key)
if err != nil {
return nil, err
}
return &Blake2s{key: key}, nil
}
func (b Blake2s) CodeLength() int {
return blake2s.Size128
}
func (b Blake2s) Generate(d []byte) []byte {
h, _ := blake2s.New128(b.key)
h.Write(d)
return h.Sum([]byte{})
}
func (b Blake2s) Verify(d []byte, s []byte) error {
h, _ := blake2s.New128(b.key)
h.Write(d)
sum := h.Sum([]byte{})
if !bytes.Equal(sum, s) {
return shared.ErrBadChecksum
}
return nil
}

1
go.mod
View File

@ -7,5 +7,6 @@ require (
github.com/pkg/taptun v0.0.0-20160424131934-bbbd335672ab
github.com/smartystreets/goconvey v1.6.4 // indirect
github.com/stretchr/testify v1.4.0
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
gopkg.in/ini.v1 v1.62.0
)

View File

@ -10,49 +10,26 @@ type Packet interface {
Contents() []byte
}
type SimplePacket struct {
Data []byte
timestamp time.Time
}
// create a packet from the raw data of an IP packet
func NewSimplePacket(data []byte) Packet {
return SimplePacket{
Data: data,
timestamp: time.Now(),
}
}
// rebuild a packet from the wrapped format
func UnmarshalSimplePacket(data []byte) (SimplePacket, error) {
p := SimplePacket{
Data: data[:len(data)-8],
}
unixTime := int64(binary.LittleEndian.Uint64(data[len(data)-8:]))
p.timestamp = time.Unix(unixTime, 0)
return p, nil
}
type SimplePacket []byte
// get the raw data of the IP packet
func (p SimplePacket) Marshal() []byte {
footer := make([]byte, 8)
unixTime := uint64(p.timestamp.Unix())
binary.LittleEndian.PutUint64(footer, unixTime)
return append(p.Data, footer...)
return p
}
func (p SimplePacket) Contents() []byte {
return p.Data
return p
}
func AppendMac(b []byte, g MacGenerator) []byte {
footer := make([]byte, 8)
unixTime := uint64(time.Now().Unix())
binary.LittleEndian.PutUint64(footer, unixTime)
b = append(b, footer...)
mac := g.Generate(b)
b = append(b, mac...)
return b
return append(b, mac...)
}
func StripMac(b []byte, v MacVerifier) ([]byte, error) {
@ -63,5 +40,7 @@ func StripMac(b []byte, v MacVerifier) ([]byte, error) {
return nil, err
}
return data, nil
// TODO: Verify timestamp
return data[:len(data)-8], nil
}

View File

@ -8,51 +8,20 @@ import (
"testing"
)
func TestPacket_Marshal(t *testing.T) {
testContent := []byte("A test string is the content of this packet.")
testPacket := NewSimplePacket(testContent)
t.Run("Length", func(t *testing.T) {
marshalled := testPacket.Marshal()
assert.Len(t, marshalled, len(testContent)+8)
})
}
func TestUnmarshalPacket(t *testing.T) {
testContent := []byte("A test string is the content of this packet.")
testPacket := NewSimplePacket(testContent)
testMarshalled := testPacket.Marshal()
t.Run("Length", func(t *testing.T) {
p, err := UnmarshalSimplePacket(testMarshalled)
require.Nil(t, err)
assert.Len(t, p.Contents(), len(testContent))
})
t.Run("Contents", func(t *testing.T) {
p, err := UnmarshalSimplePacket(testMarshalled)
require.Nil(t, err)
assert.Equal(t, p.Contents(), testContent)
})
}
func TestAppendMac(t *testing.T) {
testContent := []byte("A test string is the content of this packet.")
testMac := mocks.AlmostUselessMac{}
testPacket := NewSimplePacket(testContent)
testPacket := SimplePacket(testContent)
testMarshalled := testPacket.Marshal()
appended := AppendMac(testMarshalled, testMac)
t.Run("Length", func(t *testing.T) {
assert.Len(t, appended, len(testMarshalled)+4)
assert.Len(t, appended, len(testMarshalled)+8+4)
})
t.Run("Mac", func(t *testing.T) {
assert.Equal(t, []byte{'a', 'b', 'c', 'd'}, appended[len(testMarshalled):])
assert.Equal(t, []byte{'a', 'b', 'c', 'd'}, appended[len(testMarshalled)+8:])
})
t.Run("Original", func(t *testing.T) {
@ -63,7 +32,7 @@ func TestAppendMac(t *testing.T) {
func TestStripMac(t *testing.T) {
testContent := []byte("A test string is the content of this packet.")
testMac := mocks.AlmostUselessMac{}
testPacket := NewSimplePacket(testContent)
testPacket := SimplePacket(testContent)
testMarshalled := testPacket.Marshal()
appended := AppendMac(testMarshalled, testMac)

View File

@ -31,8 +31,6 @@ type Proxy struct {
Source Source
Sink Sink
Generator MacGenerator
proxyChan chan Packet
sinkChan chan Packet
}
@ -67,7 +65,7 @@ func (p Proxy) Start() {
}()
}
func (p Proxy) AddConsumer(c Consumer) {
func (p Proxy) AddConsumer(c Consumer, g MacGenerator) {
go func() {
_, reconnectable := c.(Reconnectable)
@ -85,7 +83,7 @@ func (p Proxy) AddConsumer(c Consumer) {
}
for c.IsAlive() {
if err := c.Consume(<-p.proxyChan, p.Generator); err != nil {
if err := c.Consume(<-p.proxyChan, g); err != nil {
log.Println(err)
break
}

View File

@ -4,3 +4,4 @@ import "errors"
var ErrBadChecksum = errors.New("the packet had a bad checksum")
var ErrDeadConnection = errors.New("the connection is dead")
var ErrNotEnoughBytes = errors.New("not enough bytes")

View File

@ -2,7 +2,6 @@ package tcp
import (
"encoding/binary"
"errors"
"fmt"
"io"
"mpbl3p/proxy"
@ -12,8 +11,6 @@ import (
"time"
)
var ErrNotEnoughBytes = errors.New("not enough bytes")
type Conn interface {
Read(b []byte) (n int, err error)
Write(b []byte) (n int, err error)
@ -150,7 +147,7 @@ func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) {
return nil, err
}
return proxy.UnmarshalSimplePacket(b)
return proxy.SimplePacket(b), nil
}
func (f *Flow) produceMarshalled() ([]byte, error) {
@ -158,7 +155,7 @@ func (f *Flow) produceMarshalled() ([]byte, error) {
if n, err := io.LimitReader(f.conn, 4).Read(lengthBytes); err != nil {
return nil, err
} else if n != 4 {
return nil, ErrNotEnoughBytes
return nil, shared.ErrNotEnoughBytes
}
length := binary.LittleEndian.Uint32(lengthBytes)

View File

@ -11,7 +11,7 @@ import (
func TestFlow_Consume(t *testing.T) {
testContent := []byte("A test string is the content of this packet.")
testPacket := proxy.NewSimplePacket(testContent)
testPacket := proxy.SimplePacket(testContent)
testMac := mocks.AlmostUselessMac{}
t.Run("Length", func(t *testing.T) {

View File

@ -6,7 +6,7 @@ import (
"net"
)
func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier) error {
func NewListener(p *proxy.Proxy, local string, v func() proxy.MacVerifier, g func() proxy.MacGenerator) error {
laddr, err := net.ResolveTCPAddr("tcp", local)
if err != nil {
return err
@ -33,8 +33,8 @@ func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier) error {
log.Printf("received new tcp connection: %v\n", f)
p.AddConsumer(&f)
p.AddProducer(&f, v)
p.AddConsumer(&f, g())
p.AddProducer(&f, v())
}
}()

View File

@ -68,7 +68,7 @@ func (t *SourceSink) Source() (proxy.Packet, error) {
return nil, io.EOF
}
return proxy.NewSimplePacket(buf[:read]), nil
return proxy.SimplePacket(buf[:read]), nil
}
var good, bad float64

View File

@ -96,6 +96,9 @@ func (c *NewReno) ReceivedAck(ack uint32) {
c.rtt = c.rtt*(1-RttExponentialFactor) + rtt*RttExponentialFactor
// Free Window
// TODO: Check for freshness
// TODO: Don't expect an ACK per packet
atomic.AddInt32(&c.inFlight, -1)
select {
case c.ackNotifier <- struct{}{}:
@ -120,6 +123,8 @@ func (c *NewReno) ReceivedAck(ack uint32) {
func (c *NewReno) ReceivedNack(nack uint32) {
log.Printf("nack received for %d", nack)
// TODO : Check for freshness
// End slow start
c.slowStart = false
if s := c.windowSize; s > 1 {

View File

@ -115,7 +115,7 @@ func (f *InitiatedFlow) Reconnect() error {
ack: 0,
nack: 0,
seq: seq,
data: proxy.NewSimplePacket(nil),
data: proxy.SimplePacket(nil),
}
_ = f.sendPacket(p, f.g)
@ -266,7 +266,7 @@ func (f *Flow) earlyUpdateLoop(g proxy.MacGenerator, keepalive time.Duration) {
ack: f.congestion.NextAck(),
nack: f.congestion.NextNack(),
seq: seq,
data: proxy.NewSimplePacket(nil),
data: proxy.SimplePacket(nil),
}
_ = f.sendPacket(p, g)

View File

@ -13,7 +13,7 @@ import (
func TestFlow_Consume(t *testing.T) {
testContent := []byte("A test string is the content of this packet.")
testPacket := proxy.NewSimplePacket(testContent)
testPacket := proxy.SimplePacket(testContent)
testMac := mocks.AlmostUselessMac{}
t.Run("Length", func(t *testing.T) {
@ -42,7 +42,7 @@ func TestFlow_Produce(t *testing.T) {
ack: 42,
nack: 26,
seq: 128,
data: proxy.NewSimplePacket(testContent),
data: proxy.SimplePacket(testContent),
}
testMac := mocks.AlmostUselessMac{}

View File

@ -25,7 +25,7 @@ func fromUdpAddress(address net.UDPAddr) ComparableUdpAddress {
}
}
func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier, g proxy.MacGenerator, c func() Congestion) error {
func NewListener(p *proxy.Proxy, local string, v func() proxy.MacVerifier, g func() proxy.MacGenerator, c func() Congestion) error {
laddr, err := net.ResolveUDPAddr("udp", local)
if err != nil {
return err
@ -63,6 +63,9 @@ func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier, g proxy.MacG
continue
}
v := v()
g := g()
f := newFlow(c(), v)
f.writer = pconn
@ -75,7 +78,7 @@ func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier, g proxy.MacG
receivedConnections[raddr] = &f
p.AddConsumer(&f)
p.AddConsumer(&f, g)
p.AddProducer(&f, v)
log.Println("handling...")

View File

@ -3,6 +3,7 @@ package udp
import (
"encoding/binary"
"mpbl3p/proxy"
"mpbl3p/shared"
)
type Packet struct {
@ -14,14 +15,15 @@ type Packet struct {
}
func UnmarshalPacket(b []byte) (p Packet, err error) {
if len(b) < 12 {
return Packet{}, shared.ErrNotEnoughBytes
}
p.ack = binary.LittleEndian.Uint32(b[0:4])
p.nack = binary.LittleEndian.Uint32(b[4:8])
p.seq = binary.LittleEndian.Uint32(b[8:12])
p.data, err = proxy.UnmarshalSimplePacket(b[12:])
if err != nil {
return Packet{}, err
}
p.data = proxy.SimplePacket(b[12:])
return p, nil
}

View File

@ -13,14 +13,14 @@ func TestPacket_Marshal(t *testing.T) {
ack: 18,
nack: 29,
seq: 431,
data: proxy.NewSimplePacket(testContent),
data: proxy.SimplePacket(testContent),
}
t.Run("Length", func(t *testing.T) {
marshalled := testPacket.Marshal()
// 12 header + 8 timestamp
assert.Len(t, marshalled, len(testContent)+12+8)
assert.Len(t, marshalled, len(testContent)+12)
})
}
@ -30,7 +30,7 @@ func TestUnmarshalPacket(t *testing.T) {
ack: 18,
nack: 29,
seq: 431,
data: proxy.NewSimplePacket(testContent),
data: proxy.SimplePacket(testContent),
}
testMarshalled := testPacket.Marshal()