func main() { rHost := os.Getenv("RETHINKDB_PORT_28015_TCP_ADDR") rPort := os.Getenv("RETHINKDB_PORT_28015_TCP_PORT") rDb := os.Getenv("RETHINKDB_DATABASE") rAuthKey := os.Getenv("RETHINKDB_AUTH_KEY") if rHost != "" && rPort != "" { rethinkdbAddr = fmt.Sprintf("%s:%s", rHost, rPort) } if rDb != "" { rethinkdbDatabase = rDb } if rAuthKey != "" { rethinkdbAuthKey = rAuthKey } flag.Parse() if showVersion { fmt.Println(VERSION) os.Exit(0) } var ( mErr error globalMux = http.NewServeMux() ) logger.Infof("shipyard version %s", VERSION) controllerManager, mErr = manager.NewManager(rethinkdbAddr, rethinkdbDatabase, rethinkdbAuthKey, VERSION, disableUsageInfo) if mErr != nil { logger.Fatal(mErr) } apiRouter := mux.NewRouter() apiRouter.HandleFunc("/api/accounts", accounts).Methods("GET") apiRouter.HandleFunc("/api/accounts", addAccount).Methods("POST") apiRouter.HandleFunc("/api/accounts", deleteAccount).Methods("DELETE") apiRouter.HandleFunc("/api/roles", roles).Methods("GET") apiRouter.HandleFunc("/api/roles/{name}", role).Methods("GET") apiRouter.HandleFunc("/api/roles", addRole).Methods("POST") apiRouter.HandleFunc("/api/roles", deleteRole).Methods("DELETE") apiRouter.HandleFunc("/api/cluster/info", clusterInfo).Methods("GET") apiRouter.HandleFunc("/api/containers", containers).Methods("GET") apiRouter.HandleFunc("/api/containers", run).Methods("POST") apiRouter.HandleFunc("/api/containers/{id}", inspectContainer).Methods("GET") apiRouter.HandleFunc("/api/containers/{id}", destroy).Methods("DELETE") apiRouter.HandleFunc("/api/containers/{id}/stop", stopContainer).Methods("GET") apiRouter.HandleFunc("/api/containers/{id}/restart", restartContainer).Methods("GET") apiRouter.HandleFunc("/api/containers/{id}/scale", scaleContainer).Methods("GET") apiRouter.HandleFunc("/api/containers/{id}/logs", containerLogs).Methods("GET") apiRouter.HandleFunc("/api/events", events).Methods("GET") apiRouter.HandleFunc("/api/events", purgeEvents).Methods("DELETE") apiRouter.HandleFunc("/api/engines", engines).Methods("GET") apiRouter.HandleFunc("/api/engines", addEngine).Methods("POST") apiRouter.HandleFunc("/api/engines/{id}", inspectEngine).Methods("GET") apiRouter.HandleFunc("/api/engines/{id}", removeEngine).Methods("DELETE") apiRouter.HandleFunc("/api/extensions", extensions).Methods("GET") apiRouter.HandleFunc("/api/extensions/{id}", extension).Methods("GET") apiRouter.HandleFunc("/api/extensions", addExtension).Methods("POST") apiRouter.HandleFunc("/api/extensions/{id}", deleteExtension).Methods("DELETE") apiRouter.HandleFunc("/api/servicekeys", serviceKeys).Methods("GET") apiRouter.HandleFunc("/api/servicekeys", addServiceKey).Methods("POST") apiRouter.HandleFunc("/api/servicekeys", removeServiceKey).Methods("DELETE") apiRouter.HandleFunc("/api/webhookkeys", webhookKeys).Methods("GET") apiRouter.HandleFunc("/api/webhookkeys/{id}", webhookKey).Methods("GET") apiRouter.HandleFunc("/api/webhookkeys", addWebhookKey).Methods("POST") apiRouter.HandleFunc("/api/webhookkeys/{id}", deleteWebhookKey).Methods("DELETE") // global handler globalMux.Handle("/", http.FileServer(http.Dir("static"))) // api router; protected by auth apiAuthRouter := negroni.New() apiAuthRequired := auth.NewAuthRequired(controllerManager) apiAccessRequired := access.NewAccessRequired(controllerManager) apiAuthRouter.Use(negroni.HandlerFunc(apiAuthRequired.HandlerFuncWithNext)) apiAuthRouter.Use(negroni.HandlerFunc(apiAccessRequired.HandlerFuncWithNext)) apiAuthRouter.UseHandler(apiRouter) globalMux.Handle("/api/", apiAuthRouter) // account router ; protected by auth accountRouter := mux.NewRouter() accountRouter.HandleFunc("/account/changepassword", changePassword).Methods("POST") accountAuthRouter := negroni.New() accountAuthRequired := auth.NewAuthRequired(controllerManager) accountAuthRouter.Use(negroni.HandlerFunc(accountAuthRequired.HandlerFuncWithNext)) accountAuthRouter.UseHandler(accountRouter) globalMux.Handle("/account/", accountAuthRouter) // login handler; public loginRouter := mux.NewRouter() loginRouter.HandleFunc("/auth/login", login).Methods("POST") globalMux.Handle("/auth/", loginRouter) // hub handler; public hubRouter := mux.NewRouter() hubRouter.HandleFunc("/hub/webhook/{id}", hubWebhook).Methods("POST") globalMux.Handle("/hub/", hubRouter) // check for admin user if _, err := controllerManager.Account("admin"); err == manager.ErrAccountDoesNotExist { // create roles r := &shipyard.Role{ Name: "admin", } ru := &shipyard.Role{ Name: "user", } if err := controllerManager.SaveRole(r); err != nil { logger.Fatal(err) } if err := controllerManager.SaveRole(ru); err != nil { logger.Fatal(err) } role, err := controllerManager.Role(r.Name) if err != nil { logger.Fatal(err) } acct := &shipyard.Account{ Username: "******", Password: "******", Role: role, } if err := controllerManager.SaveAccount(acct); err != nil { logger.Fatal(err) } logger.Infof("created admin user: username: admin password: shipyard") } logger.Infof("controller listening on %s", listenAddr) if err := http.ListenAndServe(listenAddr, context.ClearHandler(globalMux)); err != nil { logger.Fatal(err) } }
func (a *Api) Run() error { globalMux := http.NewServeMux() controllerManager := a.manager client := a.manager.DockerClient() // forwarder for swarm var err error a.fwd, err = forward.New() if err != nil { return err } u := client.URL // setup redirect target to swarm scheme := "http://" // check if TLS is enabled and configure if so if client.TLSConfig != nil { log.Debug("configuring ssl for swarm redirect") scheme = "https://" // setup custom roundtripper with TLS transport r := forward.RoundTripper( &http.Transport{ TLSClientConfig: client.TLSConfig, }) f, err := forward.New(r) if err != nil { return err } a.fwd = f } a.dUrl = fmt.Sprintf("%s%s", scheme, u.Host) log.Debugf("configured docker proxy target: %s", a.dUrl) swarmRedirect := http.HandlerFunc(a.swarmRedirect) swarmHijack := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { a.swarmHijack(client.TLSConfig, a.dUrl, w, req) }) apiRouter := mux.NewRouter() apiRouter.HandleFunc("/api/accounts", a.accounts).Methods("GET") apiRouter.HandleFunc("/api/accounts", a.saveAccount).Methods("POST") apiRouter.HandleFunc("/api/accounts/{username}", a.account).Methods("GET") apiRouter.HandleFunc("/api/accounts/{username}", a.deleteAccount).Methods("DELETE") apiRouter.HandleFunc("/api/roles", a.roles).Methods("GET") apiRouter.HandleFunc("/api/roles/{name}", a.role).Methods("GET") apiRouter.HandleFunc("/api/nodes", a.nodes).Methods("GET") apiRouter.HandleFunc("/api/nodes/{name}", a.node).Methods("GET") apiRouter.HandleFunc("/api/containers/{id}/scale", a.scaleContainer).Methods("POST") apiRouter.HandleFunc("/api/events", a.events).Methods("GET") apiRouter.HandleFunc("/api/events", a.purgeEvents).Methods("DELETE") apiRouter.HandleFunc("/api/registries", a.registries).Methods("GET") apiRouter.HandleFunc("/api/registries", a.addRegistry).Methods("POST") apiRouter.HandleFunc("/api/registries/{name}", a.registry).Methods("GET") apiRouter.HandleFunc("/api/registries/{name}", a.removeRegistry).Methods("DELETE") apiRouter.HandleFunc("/api/registries/{name}/repositories", a.repositories).Methods("GET") apiRouter.HandleFunc("/api/registries/{name}/repositories/{repo:.*}", a.repository).Methods("GET") apiRouter.HandleFunc("/api/registries/{name}/repositories/{repo:.*}", a.deleteRepository).Methods("DELETE") apiRouter.HandleFunc("/api/servicekeys", a.serviceKeys).Methods("GET") apiRouter.HandleFunc("/api/servicekeys", a.addServiceKey).Methods("POST") apiRouter.HandleFunc("/api/servicekeys", a.removeServiceKey).Methods("DELETE") apiRouter.HandleFunc("/api/webhookkeys", a.webhookKeys).Methods("GET") apiRouter.HandleFunc("/api/webhookkeys/{id}", a.webhookKey).Methods("GET") apiRouter.HandleFunc("/api/webhookkeys", a.addWebhookKey).Methods("POST") apiRouter.HandleFunc("/api/webhookkeys/{id}", a.deleteWebhookKey).Methods("DELETE") apiRouter.HandleFunc("/api/consolesession/{container}", a.createConsoleSession).Methods("GET") apiRouter.HandleFunc("/api/consolesession/{token}", a.consoleSession).Methods("GET") apiRouter.HandleFunc("/api/consolesession/{token}", a.removeConsoleSession).Methods("DELETE") // global handler globalMux.Handle("/", http.FileServer(http.Dir("static"))) auditExcludes := []string{ "^/containers/json", "^/images/json", "^/api/events", } apiAuditor := audit.NewAuditor(controllerManager, auditExcludes) // api router; protected by auth apiAuthRouter := negroni.New() apiAuthRequired := mAuth.NewAuthRequired(controllerManager, a.authWhitelistCIDRs) apiAccessRequired := access.NewAccessRequired(controllerManager) apiAuthRouter.Use(negroni.HandlerFunc(apiAuthRequired.HandlerFuncWithNext)) apiAuthRouter.Use(negroni.HandlerFunc(apiAccessRequired.HandlerFuncWithNext)) apiAuthRouter.Use(negroni.HandlerFunc(apiAuditor.HandlerFuncWithNext)) apiAuthRouter.UseHandler(apiRouter) globalMux.Handle("/api/", apiAuthRouter) // account router ; protected by auth accountRouter := mux.NewRouter() accountRouter.HandleFunc("/account/changepassword", a.changePassword).Methods("POST") accountAuthRouter := negroni.New() accountAuthRequired := mAuth.NewAuthRequired(controllerManager, a.authWhitelistCIDRs) accountAuthRouter.Use(negroni.HandlerFunc(accountAuthRequired.HandlerFuncWithNext)) accountAuthRouter.Use(negroni.HandlerFunc(apiAuditor.HandlerFuncWithNext)) accountAuthRouter.UseHandler(accountRouter) globalMux.Handle("/account/", accountAuthRouter) // login handler; public loginRouter := mux.NewRouter() loginRouter.HandleFunc("/auth/login", a.login).Methods("POST") globalMux.Handle("/auth/", loginRouter) globalMux.Handle("/exec", websocket.Handler(a.execContainer)) // hub handler; public hubRouter := mux.NewRouter() hubRouter.HandleFunc("/hub/webhook/{id}", a.hubWebhook).Methods("POST") globalMux.Handle("/hub/", hubRouter) // swarm swarmRouter := mux.NewRouter() // these are pulled from the swarm api code to proxy and allow // usage with the standard Docker cli m := map[string]map[string]http.HandlerFunc{ "GET": { "/_ping": swarmRedirect, "/events": swarmRedirect, "/info": swarmRedirect, "/version": swarmRedirect, "/images/json": swarmRedirect, "/images/viz": swarmRedirect, "/images/search": swarmRedirect, "/images/get": swarmRedirect, "/images/{name:.*}/get": swarmRedirect, "/images/{name:.*}/history": swarmRedirect, "/images/{name:.*}/json": swarmRedirect, "/containers/ps": swarmRedirect, "/containers/json": swarmRedirect, "/containers/{name:.*}/export": swarmRedirect, "/containers/{name:.*}/changes": swarmRedirect, "/containers/{name:.*}/json": swarmRedirect, "/containers/{name:.*}/top": swarmRedirect, "/containers/{name:.*}/logs": swarmRedirect, "/containers/{name:.*}/stats": swarmRedirect, "/containers/{name:.*}/attach/ws": swarmHijack, "/exec/{execid:.*}/json": swarmRedirect, }, "POST": { "/auth": swarmRedirect, "/commit": swarmRedirect, "/build": swarmRedirect, "/images/create": swarmRedirect, "/images/load": swarmRedirect, "/images/{name:.*}/push": swarmRedirect, "/images/{name:.*}/tag": swarmRedirect, "/containers/create": swarmRedirect, "/containers/{name:.*}/kill": swarmRedirect, "/containers/{name:.*}/pause": swarmRedirect, "/containers/{name:.*}/unpause": swarmRedirect, "/containers/{name:.*}/rename": swarmRedirect, "/containers/{name:.*}/restart": swarmRedirect, "/containers/{name:.*}/start": swarmRedirect, "/containers/{name:.*}/stop": swarmRedirect, "/containers/{name:.*}/wait": swarmRedirect, "/containers/{name:.*}/resize": swarmRedirect, "/containers/{name:.*}/attach": swarmHijack, "/containers/{name:.*}/copy": swarmRedirect, "/containers/{name:.*}/exec": swarmRedirect, "/exec/{execid:.*}/start": swarmHijack, "/exec/{execid:.*}/resize": swarmRedirect, }, "DELETE": { "/containers/{name:.*}": swarmRedirect, "/images/{name:.*}": swarmRedirect, }, "OPTIONS": { "": swarmRedirect, }, } for method, routes := range m { for route, fct := range routes { localRoute := route localFct := fct wrap := func(w http.ResponseWriter, r *http.Request) { if a.enableCors { writeCorsHeaders(w, r) } localFct(w, r) } localMethod := method // add the new route swarmRouter.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(wrap) swarmRouter.Path(localRoute).Methods(localMethod).HandlerFunc(wrap) } } swarmAuthRouter := negroni.New() swarmAuthRequired := mAuth.NewAuthRequired(controllerManager, a.authWhitelistCIDRs) swarmAccessRequired := access.NewAccessRequired(controllerManager) swarmAuthRouter.Use(negroni.HandlerFunc(swarmAuthRequired.HandlerFuncWithNext)) swarmAuthRouter.Use(negroni.HandlerFunc(swarmAccessRequired.HandlerFuncWithNext)) swarmAuthRouter.Use(negroni.HandlerFunc(apiAuditor.HandlerFuncWithNext)) swarmAuthRouter.UseHandler(swarmRouter) globalMux.Handle("/containers/", swarmAuthRouter) globalMux.Handle("/_ping", swarmAuthRouter) globalMux.Handle("/commit", swarmAuthRouter) globalMux.Handle("/build", swarmAuthRouter) globalMux.Handle("/events", swarmAuthRouter) globalMux.Handle("/version", swarmAuthRouter) globalMux.Handle("/images/", swarmAuthRouter) globalMux.Handle("/exec/", swarmAuthRouter) globalMux.Handle("/v1.14/", swarmAuthRouter) globalMux.Handle("/v1.15/", swarmAuthRouter) globalMux.Handle("/v1.16/", swarmAuthRouter) globalMux.Handle("/v1.17/", swarmAuthRouter) globalMux.Handle("/v1.18/", swarmAuthRouter) globalMux.Handle("/v1.19/", swarmAuthRouter) globalMux.Handle("/v1.20/", swarmAuthRouter) // check for admin user if _, err := controllerManager.Account("admin"); err == manager.ErrAccountDoesNotExist { // create roles acct := &auth.Account{ Username: "******", Password: "******", FirstName: "Shipyard", LastName: "Admin", Roles: []string{"admin"}, } if err := controllerManager.SaveAccount(acct); err != nil { log.Fatal(err) } log.Infof("created admin user: username: admin password: shipyard") } log.Infof("controller listening on %s", a.listenAddr) s := &http.Server{ Addr: a.listenAddr, Handler: context.ClearHandler(globalMux), } var runErr error if a.tlsCertPath != "" && a.tlsKeyPath != "" { log.Infof("using TLS for communication: cert=%s key=%s", a.tlsCertPath, a.tlsKeyPath, ) // setup TLS config var caCert []byte if a.tlsCACertPath != "" { ca, err := ioutil.ReadFile(a.tlsCACertPath) if err != nil { return err } caCert = ca } serverCert, err := ioutil.ReadFile(a.tlsCertPath) if err != nil { return err } serverKey, err := ioutil.ReadFile(a.tlsKeyPath) if err != nil { return err } tlsConfig, err := tlsutils.GetServerTLSConfig(caCert, serverCert, serverKey, a.allowInsecure) if err != nil { return err } s.TLSConfig = tlsConfig runErr = s.ListenAndServeTLS(a.tlsCertPath, a.tlsKeyPath) } else { runErr = s.ListenAndServe() } return runErr }