mirror of
https://github.com/gitpod-io/gitpod.git
synced 2025-12-08 17:36:30 +00:00
174 lines
4.0 KiB
Go
174 lines
4.0 KiB
Go
// Copyright (c) 2020 Gitpod GmbH. All rights reserved.
|
|
// Licensed under the GNU Affero General Public License (AGPL).
|
|
// See License-AGPL.txt in the project root for license information.
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
"os/signal"
|
|
"strconv"
|
|
"syscall"
|
|
|
|
"golang.org/x/xerrors"
|
|
)
|
|
|
|
const (
|
|
realProxy = "docker-proxy"
|
|
)
|
|
|
|
var (
|
|
containerIP = flag.String("container-ip", "", "container ip")
|
|
containerPort = flag.Int("container-port", -1, "container port")
|
|
hostIP = flag.String("host-ip", "", "host ip")
|
|
hostPort = flag.Int("host-port", -1, "host port")
|
|
proto = flag.String("proto", "tcp", "proxy protocol")
|
|
)
|
|
|
|
// drop-in replacement for docker-proxy.
|
|
// needs to be executed in the child namespace.
|
|
func main() {
|
|
f := os.NewFile(3, "signal-parent")
|
|
defer f.Close()
|
|
if err := xmain(f); err != nil {
|
|
// success: "0\n" (written by realProxy)
|
|
// error: "1\n" (written by either rootlesskit-docker-proxy or realProxy)
|
|
fmt.Fprintf(f, "1\n%s", err)
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
type request struct {
|
|
Execute string `json:"execute"`
|
|
Arguments interface{} `json:"arguments"`
|
|
}
|
|
|
|
type reply struct {
|
|
Return map[string]interface{} `json:"return,omitempty"`
|
|
Error map[string]interface{} `json:"error,omitempty"`
|
|
}
|
|
|
|
func xmain(f *os.File) error {
|
|
flag.Parse()
|
|
|
|
socketPath := os.Getenv("DOCKERUP_SLIRP4NETNS_SOCKET")
|
|
if socketPath == "" {
|
|
return errors.New("$DOCKERUP_SLIRP4NETNS_SOCKET needs to be set")
|
|
}
|
|
|
|
id, err := exposePort(socketPath)
|
|
if err != nil {
|
|
return xerrors.Errorf("cannot expose slirp4net port: %w", err)
|
|
}
|
|
defer closePort(socketPath, id)
|
|
|
|
cmd := exec.Command(realProxy,
|
|
"-container-ip", *containerIP,
|
|
"-container-port", strconv.Itoa(*containerPort),
|
|
"-host-ip", "127.0.0.1",
|
|
"-host-port", strconv.Itoa(*hostPort),
|
|
"-proto", *proto)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
cmd.Env = os.Environ()
|
|
cmd.ExtraFiles = append(cmd.ExtraFiles, f)
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
Pdeathsig: syscall.SIGKILL,
|
|
}
|
|
if err := cmd.Start(); err != nil {
|
|
return xerrors.Errorf("error while starting %s: %w", realProxy, err)
|
|
}
|
|
|
|
ch := make(chan os.Signal, 1)
|
|
signal.Notify(ch, os.Interrupt)
|
|
<-ch
|
|
if err := cmd.Process.Kill(); err != nil {
|
|
return xerrors.Errorf("error while killing %s: %w", realProxy, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func exposePort(socketPath string) (id int, err error) {
|
|
type addHostFwdArguments struct {
|
|
Proto string `json:"proto"`
|
|
HostAddr string `json:"host_addr"`
|
|
HostPort int `json:"host_port"`
|
|
GuestAddr string `json:"guest_addr"`
|
|
GuestPort int `json:"guest_port"`
|
|
}
|
|
|
|
resp, err := sendRequest(socketPath, request{
|
|
Execute: "add_hostfwd",
|
|
Arguments: addHostFwdArguments{
|
|
GuestAddr: "10.0.2.100",
|
|
GuestPort: *hostPort,
|
|
HostAddr: *hostIP,
|
|
HostPort: *hostPort,
|
|
Proto: *proto,
|
|
},
|
|
})
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
idIntf, ok := resp["id"]
|
|
if !ok {
|
|
return 0, xerrors.Errorf("unexpected reply: %+v", resp)
|
|
}
|
|
idFloat, ok := idIntf.(float64)
|
|
if !ok {
|
|
return 0, xerrors.Errorf("unexpected id: %+v", idIntf)
|
|
}
|
|
return int(idFloat), nil
|
|
}
|
|
|
|
func closePort(socketPath string, id int) error {
|
|
type removeHostFwdArguments struct {
|
|
ID int `json:"id"`
|
|
}
|
|
|
|
_, err := sendRequest(socketPath, request{
|
|
Execute: "remove_hostfwd",
|
|
Arguments: removeHostFwdArguments{
|
|
ID: id,
|
|
},
|
|
})
|
|
return err
|
|
}
|
|
|
|
func sendRequest(socketPath string, req request) (resp map[string]interface{}, err error) {
|
|
conn, err := net.DialUnix("unix", nil, &net.UnixAddr{Name: socketPath, Net: "unix"})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer conn.Close()
|
|
|
|
if err := json.NewEncoder(conn).Encode(req); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := conn.CloseWrite(); err != nil {
|
|
return nil, err
|
|
}
|
|
b, err := ioutil.ReadAll(conn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var rep reply
|
|
if err := json.Unmarshal(b, &rep); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(rep.Error) > 0 {
|
|
return nil, fmt.Errorf("error reply: %+v", rep.Error)
|
|
}
|
|
return rep.Return, nil
|
|
}
|