github.com/pwn-term/docker@v0.0.0-20210616085119-6e977cce2565/libnetwork/cmd/dnet/dnet.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"net"
    12  	"net/http"
    13  	"net/http/httptest"
    14  	"net/url"
    15  	"os"
    16  	"os/signal"
    17  	"strconv"
    18  	"strings"
    19  	"syscall"
    20  	"time"
    21  
    22  	"github.com/BurntSushi/toml"
    23  	"github.com/docker/docker/api/types/network"
    24  	"github.com/docker/docker/pkg/discovery"
    25  	"github.com/docker/docker/pkg/reexec"
    26  	"github.com/docker/libnetwork"
    27  	"github.com/docker/libnetwork/api"
    28  	"github.com/docker/libnetwork/cluster"
    29  	"github.com/docker/libnetwork/config"
    30  	"github.com/docker/libnetwork/datastore"
    31  	"github.com/docker/libnetwork/driverapi"
    32  	"github.com/docker/libnetwork/netlabel"
    33  	"github.com/docker/libnetwork/netutils"
    34  	"github.com/docker/libnetwork/options"
    35  	"github.com/docker/libnetwork/types"
    36  	"github.com/gorilla/mux"
    37  	"github.com/moby/term"
    38  	"github.com/sirupsen/logrus"
    39  	"github.com/urfave/cli"
    40  )
    41  
    42  const (
    43  	// DefaultHTTPHost is used if only port is provided to -H flag e.g. docker -d -H tcp://:8080
    44  	DefaultHTTPHost = "0.0.0.0"
    45  	// DefaultHTTPPort is the default http port used by dnet
    46  	DefaultHTTPPort = 2385
    47  	// DefaultUnixSocket exported
    48  	DefaultUnixSocket = "/var/run/dnet.sock"
    49  	cfgFileEnv        = "LIBNETWORK_CFG"
    50  	defaultCfgFile    = "/etc/default/libnetwork.toml"
    51  	defaultHeartbeat  = time.Duration(10) * time.Second
    52  	ttlFactor         = 2
    53  )
    54  
    55  var epConn *dnetConnection
    56  
    57  func main() {
    58  	if reexec.Init() {
    59  		return
    60  	}
    61  
    62  	_, stdout, stderr := term.StdStreams()
    63  	logrus.SetOutput(stderr)
    64  
    65  	err := dnetApp(stdout, stderr)
    66  	if err != nil {
    67  		os.Exit(1)
    68  	}
    69  }
    70  
    71  // ParseConfig parses the libnetwork configuration file
    72  func (d *dnetConnection) parseOrchestrationConfig(tomlCfgFile string) error {
    73  	dummy := &dnetConnection{}
    74  
    75  	if _, err := toml.DecodeFile(tomlCfgFile, dummy); err != nil {
    76  		return err
    77  	}
    78  
    79  	if dummy.Orchestration != nil {
    80  		d.Orchestration = dummy.Orchestration
    81  	}
    82  	return nil
    83  }
    84  
    85  func (d *dnetConnection) parseConfig(cfgFile string) (*config.Config, error) {
    86  	if strings.Trim(cfgFile, " ") == "" {
    87  		cfgFile = os.Getenv(cfgFileEnv)
    88  		if strings.Trim(cfgFile, " ") == "" {
    89  			cfgFile = defaultCfgFile
    90  		}
    91  	}
    92  
    93  	if err := d.parseOrchestrationConfig(cfgFile); err != nil {
    94  		return nil, err
    95  	}
    96  	return config.ParseConfig(cfgFile)
    97  }
    98  
    99  func processConfig(cfg *config.Config) []config.Option {
   100  	options := []config.Option{}
   101  	if cfg == nil {
   102  		return options
   103  	}
   104  
   105  	dn := "bridge"
   106  	if strings.TrimSpace(cfg.Daemon.DefaultNetwork) != "" {
   107  		dn = cfg.Daemon.DefaultNetwork
   108  	}
   109  	options = append(options, config.OptionDefaultNetwork(dn))
   110  
   111  	dd := "bridge"
   112  	if strings.TrimSpace(cfg.Daemon.DefaultDriver) != "" {
   113  		dd = cfg.Daemon.DefaultDriver
   114  	}
   115  	options = append(options, config.OptionDefaultDriver(dd))
   116  
   117  	if cfg.Daemon.Labels != nil {
   118  		options = append(options, config.OptionLabels(cfg.Daemon.Labels))
   119  	}
   120  
   121  	if dcfg, ok := cfg.Scopes[datastore.GlobalScope]; ok && dcfg.IsValid() {
   122  		options = append(options, config.OptionKVProvider(dcfg.Client.Provider))
   123  		options = append(options, config.OptionKVProviderURL(dcfg.Client.Address))
   124  	}
   125  
   126  	dOptions, err := startDiscovery(&cfg.Cluster)
   127  	if err != nil {
   128  		logrus.Infof("Skipping discovery : %s", err.Error())
   129  	} else {
   130  		options = append(options, dOptions...)
   131  	}
   132  
   133  	return options
   134  }
   135  
   136  func startDiscovery(cfg *config.ClusterCfg) ([]config.Option, error) {
   137  	if cfg == nil {
   138  		return nil, errors.New("discovery requires a valid configuration")
   139  	}
   140  
   141  	hb := time.Duration(cfg.Heartbeat) * time.Second
   142  	if hb == 0 {
   143  		hb = defaultHeartbeat
   144  	}
   145  	logrus.Infof("discovery : %s %s", cfg.Discovery, hb.String())
   146  	d, err := discovery.New(cfg.Discovery, hb, ttlFactor*hb, map[string]string{})
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  
   151  	if cfg.Address == "" {
   152  		iface, err := net.InterfaceByName("eth0")
   153  		if err != nil {
   154  			return nil, err
   155  		}
   156  		addrs, err := iface.Addrs()
   157  		if err != nil || len(addrs) == 0 {
   158  			return nil, err
   159  		}
   160  		ip, _, _ := net.ParseCIDR(addrs[0].String())
   161  		cfg.Address = ip.String()
   162  	}
   163  
   164  	if ip := net.ParseIP(cfg.Address); ip == nil {
   165  		return nil, errors.New("address config should be either ipv4 or ipv6 address")
   166  	}
   167  
   168  	if err := d.Register(cfg.Address + ":0"); err != nil {
   169  		return nil, err
   170  	}
   171  
   172  	options := []config.Option{config.OptionDiscoveryWatcher(d), config.OptionDiscoveryAddress(cfg.Address)}
   173  	go func() {
   174  		for {
   175  			select {
   176  			case <-time.After(hb):
   177  				if err := d.Register(cfg.Address + ":0"); err != nil {
   178  					logrus.Warn(err)
   179  				}
   180  			}
   181  		}
   182  	}()
   183  	return options, nil
   184  }
   185  
   186  func dnetApp(stdout, stderr io.Writer) error {
   187  	app := cli.NewApp()
   188  
   189  	app.Name = "dnet"
   190  	app.Usage = "A self-sufficient runtime for container networking."
   191  	app.Flags = dnetFlags
   192  	app.Before = processFlags
   193  	app.Commands = dnetCommands
   194  
   195  	app.Run(os.Args)
   196  	return nil
   197  }
   198  
   199  func createDefaultNetwork(c libnetwork.NetworkController) {
   200  	nw := c.Config().Daemon.DefaultNetwork
   201  	d := c.Config().Daemon.DefaultDriver
   202  	createOptions := []libnetwork.NetworkOption{}
   203  	genericOption := options.Generic{}
   204  
   205  	if nw != "" && d != "" {
   206  		// Bridge driver is special due to legacy reasons
   207  		if d == "bridge" {
   208  			genericOption[netlabel.GenericData] = map[string]string{
   209  				"BridgeName":    "docker0",
   210  				"DefaultBridge": "true",
   211  			}
   212  			createOptions = append(createOptions,
   213  				libnetwork.NetworkOptionGeneric(genericOption),
   214  				ipamOption(nw))
   215  		}
   216  
   217  		if n, err := c.NetworkByName(nw); err == nil {
   218  			logrus.Debugf("Default network %s already present. Deleting it", nw)
   219  			if err = n.Delete(); err != nil {
   220  				logrus.Debugf("Network could not be deleted: %v", err)
   221  				return
   222  			}
   223  		}
   224  
   225  		_, err := c.NewNetwork(d, nw, "", createOptions...)
   226  		if err != nil {
   227  			logrus.Errorf("Error creating default network : %s : %v", nw, err)
   228  		}
   229  	}
   230  }
   231  
   232  type dnetConnection struct {
   233  	// proto holds the client protocol i.e. unix.
   234  	proto string
   235  	// addr holds the client address.
   236  	addr          string
   237  	Orchestration *NetworkOrchestration
   238  	configEvent   chan cluster.ConfigEventType
   239  }
   240  
   241  // NetworkOrchestration exported
   242  type NetworkOrchestration struct {
   243  	Agent   bool
   244  	Manager bool
   245  	Bind    string
   246  	Peer    string
   247  }
   248  
   249  func (d *dnetConnection) dnetDaemon(cfgFile string) error {
   250  	if err := startTestDriver(); err != nil {
   251  		return fmt.Errorf("failed to start test driver: %v", err)
   252  	}
   253  
   254  	cfg, err := d.parseConfig(cfgFile)
   255  	var cOptions []config.Option
   256  	if err == nil {
   257  		cOptions = processConfig(cfg)
   258  	} else {
   259  		logrus.Errorf("Error parsing config %v", err)
   260  	}
   261  
   262  	bridgeConfig := options.Generic{
   263  		"EnableIPForwarding": true,
   264  		"EnableIPTables":     true,
   265  	}
   266  
   267  	bridgeOption := options.Generic{netlabel.GenericData: bridgeConfig}
   268  
   269  	cOptions = append(cOptions, config.OptionDriverConfig("bridge", bridgeOption))
   270  
   271  	controller, err := libnetwork.New(cOptions...)
   272  	if err != nil {
   273  		fmt.Println("Error starting dnetDaemon :", err)
   274  		return err
   275  	}
   276  	controller.SetClusterProvider(d)
   277  
   278  	if d.Orchestration.Agent || d.Orchestration.Manager {
   279  		d.configEvent <- cluster.EventNodeReady
   280  	}
   281  
   282  	createDefaultNetwork(controller)
   283  	httpHandler := api.NewHTTPHandler(controller)
   284  	r := mux.NewRouter().StrictSlash(false)
   285  	post := r.PathPrefix("/{.*}/networks").Subrouter()
   286  	post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
   287  	post = r.PathPrefix("/networks").Subrouter()
   288  	post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
   289  	post = r.PathPrefix("/{.*}/services").Subrouter()
   290  	post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
   291  	post = r.PathPrefix("/services").Subrouter()
   292  	post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
   293  	post = r.PathPrefix("/{.*}/sandboxes").Subrouter()
   294  	post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
   295  	post = r.PathPrefix("/sandboxes").Subrouter()
   296  	post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
   297  
   298  	handleSignals(controller)
   299  	setupDumpStackTrap()
   300  
   301  	return http.ListenAndServe(d.addr, r)
   302  }
   303  
   304  func (d *dnetConnection) IsManager() bool {
   305  	return d.Orchestration.Manager
   306  }
   307  
   308  func (d *dnetConnection) IsAgent() bool {
   309  	return d.Orchestration.Agent
   310  }
   311  
   312  func (d *dnetConnection) GetAdvertiseAddress() string {
   313  	return d.Orchestration.Bind
   314  }
   315  
   316  func (d *dnetConnection) GetDataPathAddress() string {
   317  	return d.Orchestration.Bind
   318  }
   319  
   320  func (d *dnetConnection) GetLocalAddress() string {
   321  	return d.Orchestration.Bind
   322  }
   323  
   324  func (d *dnetConnection) GetListenAddress() string {
   325  	return d.Orchestration.Bind
   326  }
   327  
   328  func (d *dnetConnection) GetRemoteAddressList() []string {
   329  	return []string{d.Orchestration.Peer}
   330  }
   331  
   332  func (d *dnetConnection) ListenClusterEvents() <-chan cluster.ConfigEventType {
   333  	return d.configEvent
   334  }
   335  
   336  func (d *dnetConnection) AttachNetwork(string, string, []string) (*network.NetworkingConfig, error) {
   337  	return nil, nil
   338  }
   339  
   340  func (d *dnetConnection) DetachNetwork(string, string) error {
   341  	return nil
   342  }
   343  
   344  func (d *dnetConnection) UpdateAttachment(string, string, *network.NetworkingConfig) error {
   345  	return nil
   346  }
   347  
   348  func (d *dnetConnection) WaitForDetachment(context.Context, string, string, string, string) error {
   349  	return nil
   350  }
   351  
   352  func handleSignals(controller libnetwork.NetworkController) {
   353  	c := make(chan os.Signal, 1)
   354  	signals := []os.Signal{os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}
   355  	signal.Notify(c, signals...)
   356  	go func() {
   357  		for range c {
   358  			controller.Stop()
   359  			os.Exit(0)
   360  		}
   361  	}()
   362  }
   363  
   364  func startTestDriver() error {
   365  	mux := http.NewServeMux()
   366  	server := httptest.NewServer(mux)
   367  	if server == nil {
   368  		return errors.New("Failed to start an HTTP Server")
   369  	}
   370  
   371  	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
   372  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   373  		fmt.Fprintf(w, `{"Implements": ["%s"]}`, driverapi.NetworkPluginEndpointType)
   374  	})
   375  
   376  	mux.HandleFunc(fmt.Sprintf("/%s.GetCapabilities", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   377  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   378  		fmt.Fprint(w, `{"Scope":"global"}`)
   379  	})
   380  
   381  	mux.HandleFunc(fmt.Sprintf("/%s.CreateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   382  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   383  		fmt.Fprint(w, "null")
   384  	})
   385  
   386  	mux.HandleFunc(fmt.Sprintf("/%s.DeleteNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   387  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   388  		fmt.Fprint(w, "null")
   389  	})
   390  
   391  	mux.HandleFunc(fmt.Sprintf("/%s.CreateEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   392  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   393  		fmt.Fprint(w, "null")
   394  	})
   395  
   396  	mux.HandleFunc(fmt.Sprintf("/%s.DeleteEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   397  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   398  		fmt.Fprint(w, "null")
   399  	})
   400  
   401  	mux.HandleFunc(fmt.Sprintf("/%s.Join", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   402  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   403  		fmt.Fprint(w, "null")
   404  	})
   405  
   406  	mux.HandleFunc(fmt.Sprintf("/%s.Leave", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   407  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   408  		fmt.Fprint(w, "null")
   409  	})
   410  
   411  	if err := os.MkdirAll("/etc/docker/plugins", 0755); err != nil {
   412  		return err
   413  	}
   414  
   415  	return ioutil.WriteFile("/etc/docker/plugins/test.spec", []byte(server.URL), 0644)
   416  }
   417  
   418  func newDnetConnection(val string) (*dnetConnection, error) {
   419  	url, err := parseHost(val)
   420  	if err != nil {
   421  		return nil, err
   422  	}
   423  	protoAddrParts := strings.SplitN(url, "://", 2)
   424  	if len(protoAddrParts) != 2 {
   425  		return nil, errors.New("bad format, expected tcp://ADDR")
   426  	}
   427  	if strings.ToLower(protoAddrParts[0]) != "tcp" {
   428  		return nil, errors.New("dnet currently only supports tcp transport")
   429  	}
   430  
   431  	return &dnetConnection{protoAddrParts[0], protoAddrParts[1], &NetworkOrchestration{}, make(chan cluster.ConfigEventType, 10)}, nil
   432  }
   433  
   434  const (
   435  	defaultUnixSocket = "unix:///var/run/docker.sock"
   436  	defaultTCPHost    = "tcp://localhost:2375"
   437  )
   438  
   439  // parseHost and set defaults for a Daemon host string.
   440  func parseHost(val string) (string, error) {
   441  	host := strings.TrimSpace(val)
   442  	if host == "" {
   443  		return defaultUnixSocket, nil
   444  	}
   445  
   446  	addrParts := strings.SplitN(host, "://", 2)
   447  	if len(addrParts) == 1 && addrParts[0] != "" {
   448  		addrParts = []string{"tcp", addrParts[0]}
   449  	}
   450  	if addrParts[0] != "tcp" {
   451  		return "", errors.New("dnet currently only supports tcp transport")
   452  	}
   453  
   454  	return parseTCPAddr(addrParts[1], defaultTCPHost)
   455  }
   456  
   457  // parseTCPAddr parses and validates that the specified address is a valid TCP
   458  // address. It returns a formatted TCP address, either using the address parsed
   459  // from tryAddr, or the contents of defaultAddr if tryAddr is a blank string.
   460  // tryAddr is expected to have already been Trim()'d
   461  // defaultAddr must be in the full `tcp://host:port` form
   462  func parseTCPAddr(tryAddr string, defaultAddr string) (string, error) {
   463  	if tryAddr == "" || tryAddr == "tcp://" {
   464  		return defaultAddr, nil
   465  	}
   466  	addr := strings.TrimPrefix(tryAddr, "tcp://")
   467  	if strings.Contains(addr, "://") || addr == "" {
   468  		return "", fmt.Errorf("Invalid proto, expected tcp: %s", tryAddr)
   469  	}
   470  
   471  	defaultAddr = strings.TrimPrefix(defaultAddr, "tcp://")
   472  	defaultHost, defaultPort, err := net.SplitHostPort(defaultAddr)
   473  	if err != nil {
   474  		return "", err
   475  	}
   476  	u, err := url.Parse("tcp://" + addr)
   477  	if err != nil {
   478  		return "", err
   479  	}
   480  	host, port, err := net.SplitHostPort(u.Host)
   481  	if err != nil {
   482  		// try port addition once
   483  		host, port, err = net.SplitHostPort(net.JoinHostPort(u.Host, defaultPort))
   484  	}
   485  	if err != nil {
   486  		return "", fmt.Errorf("Invalid bind address format: %s", tryAddr)
   487  	}
   488  
   489  	if host == "" {
   490  		host = defaultHost
   491  	}
   492  	if port == "" {
   493  		port = defaultPort
   494  	}
   495  	p, err := strconv.Atoi(port)
   496  	if err != nil && p == 0 {
   497  		return "", fmt.Errorf("Invalid bind address format: %s", tryAddr)
   498  	}
   499  
   500  	return fmt.Sprintf("tcp://%s%s", net.JoinHostPort(host, port), u.Path), nil
   501  }
   502  
   503  func (d *dnetConnection) httpCall(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, http.Header, int, error) {
   504  	var in io.Reader
   505  	in, err := encodeData(data)
   506  	if err != nil {
   507  		return nil, nil, -1, err
   508  	}
   509  
   510  	req, err := http.NewRequest(method, fmt.Sprintf("%s", path), in)
   511  	if err != nil {
   512  		return nil, nil, -1, err
   513  	}
   514  
   515  	setupRequestHeaders(method, data, req, headers)
   516  
   517  	req.URL.Host = d.addr
   518  	req.URL.Scheme = "http"
   519  
   520  	httpClient := &http.Client{}
   521  	resp, err := httpClient.Do(req)
   522  	statusCode := -1
   523  	if resp != nil {
   524  		statusCode = resp.StatusCode
   525  	}
   526  	if err != nil {
   527  		return nil, nil, statusCode, fmt.Errorf("error when trying to connect: %v", err)
   528  	}
   529  
   530  	if statusCode < 200 || statusCode >= 400 {
   531  		body, err := ioutil.ReadAll(resp.Body)
   532  		if err != nil {
   533  			return nil, nil, statusCode, err
   534  		}
   535  		return nil, nil, statusCode, fmt.Errorf("error : %s", bytes.TrimSpace(body))
   536  	}
   537  
   538  	return resp.Body, resp.Header, statusCode, nil
   539  }
   540  
   541  func setupRequestHeaders(method string, data interface{}, req *http.Request, headers map[string][]string) {
   542  	if data != nil {
   543  		if headers == nil {
   544  			headers = make(map[string][]string)
   545  		}
   546  		headers["Content-Type"] = []string{"application/json"}
   547  	}
   548  
   549  	expectedPayload := (method == "POST" || method == "PUT")
   550  
   551  	if expectedPayload && req.Header.Get("Content-Type") == "" {
   552  		req.Header.Set("Content-Type", "text/plain")
   553  	}
   554  
   555  	if headers != nil {
   556  		for k, v := range headers {
   557  			req.Header[k] = v
   558  		}
   559  	}
   560  }
   561  
   562  func encodeData(data interface{}) (*bytes.Buffer, error) {
   563  	params := bytes.NewBuffer(nil)
   564  	if data != nil {
   565  		if err := json.NewEncoder(params).Encode(data); err != nil {
   566  			return nil, err
   567  		}
   568  	}
   569  	return params, nil
   570  }
   571  
   572  func ipamOption(bridgeName string) libnetwork.NetworkOption {
   573  	if nws, _, err := netutils.ElectInterfaceAddresses(bridgeName); err == nil {
   574  		ipamV4Conf := &libnetwork.IpamConf{PreferredPool: nws[0].String()}
   575  		hip, _ := types.GetHostPartIP(nws[0].IP, nws[0].Mask)
   576  		if hip.IsGlobalUnicast() {
   577  			ipamV4Conf.Gateway = nws[0].IP.String()
   578  		}
   579  		return libnetwork.NetworkOptionIpam("default", "", []*libnetwork.IpamConf{ipamV4Conf}, nil, nil)
   580  	}
   581  	return nil
   582  }