Network proxy service (tentative)

master
xSmurf 9 years ago
parent 0f682806ab
commit e4da051f83

@ -0,0 +1,202 @@
package network
import(
//Builtin
"fmt"
"io"
"net"
"os"
"strconv"
"sync"
"github.com/subgraph/oz/ns"
"github.com/op/go-logging"
)
// Socket list, used to hold ports that should be forwarded
type ProxyConfig struct {
// One of client, server
Nettype string `json:"type"`
// One of tcp, udp, socket
Proto string
// TCP or UDP port number
Port int
// Unix socket to attach to
// applies to proto: socket only
Socket string
// Optional: Destination address
// In client mode: the host side address to connect to
// In server mode: the container side address to bind to
// If left empty, localhost is used
Destination string
}
var wgProxy sync.WaitGroup
func ProxySetup(childPid int, ozSockets []ProxyConfig, log *logging.Logger, ready sync.WaitGroup) error {
for _, socket := range ozSockets {
if socket.Nettype == "" || socket.Nettype == "client" {
err := newProxyClient(childPid, socket.Proto, socket.Destination, socket.Port, log, ready)
if err != nil {
return fmt.Errorf("Unable to setup client socket forwarding %+v, %s", socket, err)
}
} else if socket.Nettype == "server" {
err := newProxyServer(childPid, socket.Proto, socket.Destination, socket.Port, log, ready)
if err != nil {
return fmt.Errorf("Unable to setup server socket forwarding %+s, %s", socket, err)
}
}
}
return nil
}
/**
* Listener/Client
**/
func proxyClientConn(conn *net.Conn, proto, rAddr string, ready sync.WaitGroup) error {
rConn, err := net.Dial(proto, rAddr)
if err != nil {
return fmt.Errorf("Socket: %+v.\n", err)
}
go io.Copy(rConn, *conn)
go io.Copy(*conn, rConn)
return nil
}
func newProxyClient(pid int, proto, dest string, port int, log *logging.Logger, ready sync.WaitGroup) error {
if dest == "" {
dest = "127.0.0.1"
}
lAddr := net.JoinHostPort("127.0.0.1", strconv.Itoa(port))
rAddr := net.JoinHostPort(dest, strconv.Itoa(port))
log.Info("Starting socket client forwarding: %s://%s.", proto, rAddr)
listen, err := proxySocketListener(pid, proto, lAddr)
if err != nil {
return err
}
wgProxy.Add(1)
go func() {
defer wgProxy.Done()
for {
conn, err := listen.Accept()
if err != nil {
log.Error("Socket: %+v.", err)
//panic(err)
continue
}
go proxyClientConn(&conn, proto, rAddr, ready)
}
}()
return nil
}
func proxySocketListener(pid int, proto, lAddr string) (net.Listener, error) {
fd, err := ns.OpenProcess(pid, ns.CLONE_NEWNET)
defer ns.Close(fd)
if err != nil {
return nil, err
}
return nsSocketListener(fd, proto, lAddr)
}
func nsSocketListener(fd uintptr, proto, lAddr string) (net.Listener, error) {
origNs, _ := ns.OpenProcess(os.Getpid(), ns.CLONE_NEWNET)
defer ns.Close(origNs)
defer ns.Set(origNs, ns.CLONE_NEWNET)
err := ns.Set(uintptr(fd), ns.CLONE_NEWNET)
if err != nil {
return nil, err
}
return net.Listen(proto, lAddr)
}
/**
* Connect/Server
**/
func proxyServerConn(pid int, conn *net.Conn, proto, rAddr string, log *logging.Logger, ready sync.WaitGroup) (error) {
rConn, err := socketConnect(pid, proto, rAddr)
if err != nil {
log.Error("Socket: %+v.", err)
return err
}
go io.Copy(*conn, rConn)
go io.Copy(rConn, *conn)
return nil
}
func newProxyServer(pid int, proto, dest string, port int, log *logging.Logger, ready sync.WaitGroup) (error) {
if dest == "" {
dest = "127.0.0.1"
}
lAddr := net.JoinHostPort(dest, strconv.Itoa(port))
rAddr := net.JoinHostPort("127.0.0.1", strconv.Itoa(port))
log.Info("Starting socket server forwarding: %s://%s.", proto, lAddr)
listen, err := net.Listen(proto, lAddr)
if err != nil {
return err
}
wgProxy.Add(1)
go func() {
defer wgProxy.Done()
for {
conn, err := listen.Accept()
if err != nil {
log.Error("Socket: %+v.", err)
//panic(err)
continue
}
go proxyServerConn(pid, &conn, proto, rAddr, log, ready)
}
}()
return nil
}
func socketConnect(pid int, proto, rAddr string) (net.Conn, error) {
fd, err := ns.OpenProcess(pid, ns.CLONE_NEWNET)
defer ns.Close(fd)
if err != nil {
return nil, err
}
return nsProxySocketConnect(fd, proto, rAddr)
}
func nsProxySocketConnect(fd uintptr, proto, rAddr string) (net.Conn, error) {
origNs, _ := ns.OpenProcess(os.Getpid(), ns.CLONE_NEWNET)
defer ns.Close(origNs)
defer ns.Set(origNs, ns.CLONE_NEWNET)
err := ns.Set(uintptr(fd), ns.CLONE_NEWNET)
if err != nil {
return nil, err
}
return net.Dial(proto, rAddr)
}

@ -0,0 +1,9 @@
package ns
import (
)
const (
SYS_SETNS = 308
)

@ -0,0 +1,86 @@
package ns
import(
"syscall"
"errors"
"os"
"path"
"strconv"
)
type Namespace struct {
Path string
Type uintptr
}
const (
CLONE_NEWNS = syscall.CLONE_NEWNS
CLONE_NEWUTS = syscall.CLONE_NEWUTS
CLONE_NEWIPC = syscall.CLONE_NEWIPC
CLONE_NEWNET = syscall.CLONE_NEWNET
CLONE_NEWUSER = syscall.CLONE_NEWUSER
CLONE_NEWPID = syscall.CLONE_NEWPID
)
var (
Types []Namespace
)
func init() {
Types = []Namespace{
Namespace{Path: "ns/user", Type: syscall.CLONE_NEWUSER},
Namespace{Path: "ns/ipc", Type: syscall.CLONE_NEWIPC},
Namespace{Path: "ns/uts", Type: syscall.CLONE_NEWUTS},
Namespace{Path: "ns/net", Type: syscall.CLONE_NEWNET},
Namespace{Path: "ns/pid", Type: syscall.CLONE_NEWPID},
Namespace{Path: "ns/mnt", Type: syscall.CLONE_NEWNS},
}
}
func Set(fd, nsType uintptr) (error) {
_, _, err := syscall.Syscall(SYS_SETNS, uintptr(fd), uintptr(nsType), 0)
if err != 0 {
return errors.New("Unable to set namespace")
}
return nil
}
func GetPath(pid int, nsType uintptr) (string, error) {
var nsPath string
for _, n := range Types {
if n.Type == nsType {
nsPath = path.Join("/", "proc", strconv.Itoa(pid), n.Path)
break
}
}
if nsPath == "" {
return "", errors.New("Unable to find namespace type")
}
return nsPath, nil
}
func OpenProcess(pid int, nsType uintptr) (uintptr, error) {
nsPath, err := GetPath(pid, nsType)
if err != nil {
return 0, err
}
return Open(nsPath)
}
func Open(nsPath string) (uintptr, error) {
fd, err := os.Open(nsPath)
if err != nil {
return 0, err
}
return fd.Fd(), nil
}
func Close(fd uintptr) (error) {
return syscall.Close(int(fd))
}

@ -137,6 +137,14 @@ func (d *daemonState) launch(p *oz.Profile, pwd string, args, env []string, uid,
go func () {
sbox.ready.Wait()
if p.Networking.Nettype != "host" && len(p.Networking.Sockets) > 0 {
err := network.ProxySetup(sbox.init.Process.Pid, p.Networking.Sockets, d.log, sbox.ready)
if err != nil {
log.Warning("Unable to create connection proxy: %+s", err)
}
}
go sbox.launchProgram(pwd, args, log)
}()

@ -5,6 +5,8 @@ import (
"fmt"
"io/ioutil"
"path"
"github.com/subgraph/oz/network"
)
type Profile struct {
@ -64,14 +66,14 @@ type EnvVar struct {
// Container network definition
type NetworkProfile struct {
// One of empty, host, bridge
Nettype string
Nettype string `json:"type"`
// Name of the bridge to attach to
//Bridge string
// List of Sockets we want to attach to the jail
// Applies to Nettype: bridge and empty only
//Sockets []nsnat.ConfigSocket
Sockets []network.ProxyConfig
}
const defaultProfileDirectory = "/var/lib/oz/cells.d"

@ -8,7 +8,7 @@
, "use_pulse_audio": false
}
, "networking":{
"nettype":"empty"
"type":"empty"
}
, "whitelist": [
]

@ -7,9 +7,9 @@
, "disable_audio":true
}
, "networking":{
"nettype":"empty"
"type":"empty"
, "sockets": [
{"nettype":"client", "proto":"tcp", "port":9050}
{"type":"client", "proto":"tcp", "port":9050}
]
}
, "whitelist": [

@ -7,9 +7,9 @@
, "disable_audio": true
}
, "networking":{
"nettype":"bridge"
"type":"bridge"
, "sockets": [
{"nettype":"client", "proto":"tcp", "port":9050}
{"type":"client", "proto":"tcp", "port":9050}
]
}
, "whitelist": [

@ -6,9 +6,9 @@
, "tray_icon":"/usr/share/icons/hicolor/scalable/apps/iceweasel.svg"
}
, "networking":{
"nettype":"bridge"
"type":"bridge"
, "sockets": [
{"nettype":"client", "proto":"tcp", "port":9050}
{"type":"client", "proto":"tcp", "port":9050}
]
}
, "whitelist": [

@ -8,9 +8,9 @@
, "_tray_icon":"/usr/share/icons/gnome-colors-common/scalable/apps/liferea.svg"
}
, "networking":{
"nettype":"bridge"
"type":"bridge"
, "sockets": [
{"nettype":"client", "proto":"tcp", "port":9050}
{"type":"client", "proto":"tcp", "port":9050}
]
}
, "whitelist": [

@ -6,7 +6,7 @@
, "tray_icon":"/usr/share/icons/gnome-colors-common/scalable/apps/pidgin-menu.svg"
}
, "networking":{
"nettype":"bridge"
"type":"bridge"
}
, "whitelist": [
{"path":"${HOME}/.purple"}

@ -9,10 +9,10 @@
, "use_pulse_audio": false
}
, "networking":{
"nettype":"empty"
"type":"empty"
, "sockets": [
{"nettype":"client", "proto":"tcp", "port":9050}
, {"nettype":"client", "proto":"tcp", "port":30003}
{"type":"client", "proto":"tcp", "port":9050}
, {"type":"client", "proto":"tcp", "port":30003}
]
}
, "whitelist": [

@ -9,9 +9,9 @@
, "tray_icon":"/usr/share/pixmaps/torbrowser80.xpm"
}
, "networking":{
"nettype":"empty"
"type":"empty"
, "sockets": [
{"nettype":"client", "proto":"tcp", "port":9050}
{"type":"client", "proto":"tcp", "port":9050}
]
}
, "whitelist": [

@ -7,9 +7,9 @@
, "disable_audio": true
}
, "networking":{
"nettype":"empty"
"type":"empty"
, "sockets": [
{"nettype":"client", "destination": "10.10.0.90", "proto":"tcp", "port":57000}
{"type":"client", "destination": "10.10.0.90", "proto":"tcp", "port":57000}
]
}
, "whitelist": [

Loading…
Cancel
Save