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 }