// Run starts an http server for the static assets listening on the configured // bind address func (c *AssetConfig) Run() { publicURL, err := url.Parse(c.Options.PublicURL) if err != nil { glog.Fatal(err) } mux := http.NewServeMux() err = c.addHandlers(mux) if err != nil { glog.Fatal(err) } if publicURL.Path != "/" { mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { http.Redirect(w, req, publicURL.Path, http.StatusFound) }) } timeout := c.Options.ServingInfo.RequestTimeoutSeconds if timeout == -1 { timeout = 0 } server := &http.Server{ Addr: c.Options.ServingInfo.BindAddress, Handler: mux, ReadTimeout: time.Duration(timeout) * time.Second, WriteTimeout: time.Duration(timeout) * time.Second, MaxHeaderBytes: 1 << 20, } isTLS := configapi.UseTLS(c.Options.ServingInfo.ServingInfo) go util.Forever(func() { if isTLS { extraCerts, err := configapi.GetNamedCertificateMap(c.Options.ServingInfo.NamedCertificates) if err != nil { glog.Fatal(err) } server.TLSConfig = crypto.SecureTLSConfig(&tls.Config{ // Set SNI certificate func GetCertificate: cmdutil.GetCertificateFunc(extraCerts), }) glog.Infof("Web console listening at https://%s", c.Options.ServingInfo.BindAddress) glog.Fatal(cmdutil.ListenAndServeTLS(server, c.Options.ServingInfo.BindNetwork, c.Options.ServingInfo.ServerCert.CertFile, c.Options.ServingInfo.ServerCert.KeyFile)) } else { glog.Infof("Web console listening at http://%s", c.Options.ServingInfo.BindAddress) glog.Fatal(server.ListenAndServe()) } }, 0) // Attempt to verify the server came up for 20 seconds (100 tries * 100ms, 100ms timeout per try) cmdutil.WaitForSuccessfulDial(isTLS, c.Options.ServingInfo.BindNetwork, c.Options.ServingInfo.BindAddress, 100*time.Millisecond, 100*time.Millisecond, 100) glog.Infof("Web console available at %s", c.Options.PublicURL) }
// serve starts serving the provided http.Handler using security settings derived from the MasterConfig func (c *MasterConfig) serve(handler http.Handler, extra []string) { timeout := c.Options.ServingInfo.RequestTimeoutSeconds if timeout == -1 { timeout = 0 } server := &http.Server{ Addr: c.Options.ServingInfo.BindAddress, Handler: handler, ReadTimeout: time.Duration(timeout) * time.Second, WriteTimeout: time.Duration(timeout) * time.Second, MaxHeaderBytes: 1 << 20, } go utilwait.Forever(func() { for _, s := range extra { glog.Infof(s, c.Options.ServingInfo.BindAddress) } if c.TLS { extraCerts, err := configapi.GetNamedCertificateMap(c.Options.ServingInfo.NamedCertificates) if err != nil { glog.Fatal(err) } server.TLSConfig = crypto.SecureTLSConfig(&tls.Config{ // Populate PeerCertificates in requests, but don't reject connections without certificates // This allows certificates to be validated by authenticators, while still allowing other auth types ClientAuth: tls.RequestClientCert, ClientCAs: c.ClientCAs, // Set SNI certificate func GetCertificate: cmdutil.GetCertificateFunc(extraCerts), }) glog.Fatal(cmdutil.ListenAndServeTLS(server, c.Options.ServingInfo.BindNetwork, c.Options.ServingInfo.ServerCert.CertFile, c.Options.ServingInfo.ServerCert.KeyFile)) } else { glog.Fatal(server.ListenAndServe()) } }, 0) }
func BuildKubernetesNodeConfig(options configapi.NodeConfig) (*NodeConfig, error) { kubeClient, _, err := configapi.GetKubeClient(options.MasterKubeConfig) if err != nil { return nil, err } if options.NodeName == "localhost" { glog.Warningf(`Using "localhost" as node name will not resolve from all locations`) } var dnsIP net.IP if len(options.DNSIP) > 0 { dnsIP = net.ParseIP(options.DNSIP) if dnsIP == nil { return nil, fmt.Errorf("Invalid DNS IP: %s", options.DNSIP) } } clientCAs, err := util.CertPoolFromFile(options.ServingInfo.ClientCA) if err != nil { return nil, err } imageTemplate := variable.NewDefaultImageTemplate() imageTemplate.Format = options.ImageConfig.Format imageTemplate.Latest = options.ImageConfig.Latest var path string var fileCheckInterval int64 if options.PodManifestConfig != nil { path = options.PodManifestConfig.Path fileCheckInterval = options.PodManifestConfig.FileCheckIntervalSeconds } var dockerExecHandler dockertools.ExecHandler switch options.DockerConfig.ExecHandlerName { case configapi.DockerExecHandlerNative: dockerExecHandler = &dockertools.NativeExecHandler{} case configapi.DockerExecHandlerNsenter: dockerExecHandler = &dockertools.NsenterExecHandler{} } kubeAddressStr, kubePortStr, err := net.SplitHostPort(options.ServingInfo.BindAddress) if err != nil { return nil, fmt.Errorf("cannot parse node address: %v", err) } kubePort, err := strconv.Atoi(kubePortStr) if err != nil { return nil, fmt.Errorf("cannot parse node port: %v", err) } kubeAddress := net.ParseIP(kubeAddressStr) if kubeAddress == nil { return nil, fmt.Errorf("Invalid DNS IP: %s", kubeAddressStr) } // declare the OpenShift defaults from config server := kapp.NewKubeletServer() server.Config = path server.RootDirectory = options.VolumeDirectory // kubelet finds the node IP address by doing net.ParseIP(hostname) and if that fails, // it does net.LookupIP(NodeName) and picks the first non-loopback address. // Pass node IP as hostname to make kubelet use the desired IP address. if len(options.NodeIP) > 0 { server.HostnameOverride = options.NodeIP } else { server.HostnameOverride = options.NodeName } server.AllowPrivileged = true server.RegisterNode = true server.Address = kubeAddress server.Port = uint(kubePort) server.ReadOnlyPort = 0 // no read only access server.CAdvisorPort = 0 // no unsecured cadvisor access server.HealthzPort = 0 // no unsecured healthz access server.ClusterDNS = dnsIP server.ClusterDomain = options.DNSDomain server.NetworkPluginName = options.NetworkConfig.NetworkPluginName server.HostNetworkSources = strings.Join([]string{kubelettypes.ApiserverSource, kubelettypes.FileSource}, ",") server.HostPIDSources = strings.Join([]string{kubelettypes.ApiserverSource, kubelettypes.FileSource}, ",") server.HostIPCSources = strings.Join([]string{kubelettypes.ApiserverSource, kubelettypes.FileSource}, ",") server.HTTPCheckFrequency = 0 // no remote HTTP pod creation access server.FileCheckFrequency = time.Duration(fileCheckInterval) * time.Second server.PodInfraContainerImage = imageTemplate.ExpandOrDie("pod") server.CPUCFSQuota = true // enable cpu cfs quota enforcement by default // prevents kube from generating certs server.TLSCertFile = options.ServingInfo.ServerCert.CertFile server.TLSPrivateKeyFile = options.ServingInfo.ServerCert.KeyFile if value := cmdutil.Env("OPENSHIFT_CONTAINERIZED", ""); len(value) > 0 { server.Containerized = value == "true" } // resolve extended arguments // TODO: this should be done in config validation (along with the above) so we can provide // proper errors if err := cmdflags.Resolve(options.KubeletArguments, server.AddFlags); len(err) > 0 { return nil, errors.NewAggregate(err) } cfg, err := server.UnsecuredKubeletConfig() if err != nil { return nil, err } // provide any config overrides cfg.NodeName = options.NodeName cfg.StreamingConnectionIdleTimeout = 5 * time.Minute // TODO: should be set cfg.KubeClient = kubeClient cfg.DockerExecHandler = dockerExecHandler // Setup auth osClient, osClientConfig, err := configapi.GetOpenShiftClient(options.MasterKubeConfig) if err != nil { return nil, err } authnTTL, err := time.ParseDuration(options.AuthConfig.AuthenticationCacheTTL) if err != nil { return nil, err } authn, err := newAuthenticator(clientCAs, clientcmd.AnonymousClientConfig(*osClientConfig), authnTTL, options.AuthConfig.AuthenticationCacheSize) if err != nil { return nil, err } authzAttr, err := newAuthorizerAttributesGetter(options.NodeName) if err != nil { return nil, err } authzTTL, err := time.ParseDuration(options.AuthConfig.AuthorizationCacheTTL) if err != nil { return nil, err } authz, err := newAuthorizer(osClient, authzTTL, options.AuthConfig.AuthorizationCacheSize) if err != nil { return nil, err } cfg.Auth = kubelet.NewKubeletAuth(authn, authzAttr, authz) // Make sure the node doesn't think it is in standalone mode // This is required for the node to enforce nodeSelectors on pods, to set hostIP on pod status updates, etc cfg.StandaloneMode = false // TODO: could be cleaner if configapi.UseTLS(options.ServingInfo) { extraCerts, err := configapi.GetNamedCertificateMap(options.ServingInfo.NamedCertificates) if err != nil { return nil, err } cfg.TLSOptions = &kubelet.TLSOptions{ Config: crypto.SecureTLSConfig(&tls.Config{ // RequestClientCert lets us request certs, but allow requests without client certs // Verification is done by the authn layer ClientAuth: tls.RequestClientCert, ClientCAs: clientCAs, // Set SNI certificate func // Do not use NameToCertificate, since that requires certificates be included in the server's tlsConfig.Certificates list, // which we do not control when running with http.Server#ListenAndServeTLS GetCertificate: cmdutil.GetCertificateFunc(extraCerts), }), CertFile: options.ServingInfo.ServerCert.CertFile, KeyFile: options.ServingInfo.ServerCert.KeyFile, } } else { cfg.TLSOptions = nil } // Prepare cloud provider cloud, err := cloudprovider.InitCloudProvider(server.CloudProvider, server.CloudConfigFile) if err != nil { return nil, err } if cloud != nil { glog.V(2).Infof("Successfully initialized cloud provider: %q from the config file: %q\n", server.CloudProvider, server.CloudConfigFile) } cfg.Cloud = cloud config := &NodeConfig{ BindAddress: options.ServingInfo.BindAddress, AllowDisabledDocker: options.AllowDisabledDocker, Client: kubeClient, VolumeDir: options.VolumeDirectory, KubeletServer: server, KubeletConfig: cfg, IPTablesSyncPeriod: options.IPTablesSyncPeriod, } return config, nil }
func BuildKubernetesNodeConfig(options configapi.NodeConfig, enableProxy, enableDNS bool) (*NodeConfig, error) { originClient, _, err := configapi.GetOpenShiftClient(options.MasterKubeConfig, options.MasterClientConnectionOverrides) if err != nil { return nil, err } _, kubeClient, _, err := configapi.GetKubeClient(options.MasterKubeConfig, options.MasterClientConnectionOverrides) if err != nil { return nil, err } // Make a separate client for event reporting, to avoid event QPS blocking node calls _, eventClient, _, err := configapi.GetKubeClient(options.MasterKubeConfig, options.MasterClientConnectionOverrides) if err != nil { return nil, err } if options.NodeName == "localhost" { glog.Warningf(`Using "localhost" as node name will not resolve from all locations`) } clientCAs, err := kcrypto.CertPoolFromFile(options.ServingInfo.ClientCA) if err != nil { return nil, err } imageTemplate := variable.NewDefaultImageTemplate() imageTemplate.Format = options.ImageConfig.Format imageTemplate.Latest = options.ImageConfig.Latest var path string var fileCheckInterval int64 if options.PodManifestConfig != nil { path = options.PodManifestConfig.Path fileCheckInterval = options.PodManifestConfig.FileCheckIntervalSeconds } kubeAddressStr, kubePortStr, err := net.SplitHostPort(options.ServingInfo.BindAddress) if err != nil { return nil, fmt.Errorf("cannot parse node address: %v", err) } kubePort, err := strconv.Atoi(kubePortStr) if err != nil { return nil, fmt.Errorf("cannot parse node port: %v", err) } if err = validateNetworkPluginName(originClient, options.NetworkConfig.NetworkPluginName); err != nil { return nil, err } // Defaults are tested in TestKubeletDefaults server := kubeletoptions.NewKubeletServer() // Adjust defaults server.RequireKubeConfig = true server.PodManifestPath = path server.RootDirectory = options.VolumeDirectory server.NodeIP = options.NodeIP server.HostnameOverride = options.NodeName server.AllowPrivileged = true server.RegisterNode = true server.Address = kubeAddressStr server.Port = int32(kubePort) server.ReadOnlyPort = 0 // no read only access server.CAdvisorPort = 0 // no unsecured cadvisor access server.HealthzPort = 0 // no unsecured healthz access server.HealthzBindAddress = "" // no unsecured healthz access server.ClusterDNS = options.DNSIP server.ClusterDomain = options.DNSDomain server.NetworkPluginName = options.NetworkConfig.NetworkPluginName server.HostNetworkSources = []string{kubelettypes.ApiserverSource, kubelettypes.FileSource} server.HostPIDSources = []string{kubelettypes.ApiserverSource, kubelettypes.FileSource} server.HostIPCSources = []string{kubelettypes.ApiserverSource, kubelettypes.FileSource} server.HTTPCheckFrequency = unversioned.Duration{Duration: time.Duration(0)} // no remote HTTP pod creation access server.FileCheckFrequency = unversioned.Duration{Duration: time.Duration(fileCheckInterval) * time.Second} server.PodInfraContainerImage = imageTemplate.ExpandOrDie("pod") server.CPUCFSQuota = true // enable cpu cfs quota enforcement by default server.MaxPods = 250 server.PodsPerCore = 10 server.SerializeImagePulls = false // disable serialized image pulls by default server.EnableControllerAttachDetach = false // stay consistent with existing config, but admins should enable it if enableDNS { // if we are running local DNS, skydns will load the default recursive nameservers for us server.ResolverConfig = "" } server.DockerExecHandlerName = string(options.DockerConfig.ExecHandlerName) if sdnapi.IsOpenShiftNetworkPlugin(server.NetworkPluginName) { // set defaults for openshift-sdn server.HairpinMode = componentconfig.HairpinNone server.ConfigureCBR0 = false } // prevents kube from generating certs server.TLSCertFile = options.ServingInfo.ServerCert.CertFile server.TLSPrivateKeyFile = options.ServingInfo.ServerCert.KeyFile containerized := cmdutil.Env("OPENSHIFT_CONTAINERIZED", "") == "true" server.Containerized = containerized // resolve extended arguments // TODO: this should be done in config validation (along with the above) so we can provide // proper errors if err := cmdflags.Resolve(options.KubeletArguments, server.AddFlags); len(err) > 0 { return nil, kerrors.NewAggregate(err) } proxyconfig, err := buildKubeProxyConfig(options) if err != nil { return nil, err } // Initialize SDN before building kubelet config so it can modify options iptablesSyncPeriod, err := time.ParseDuration(options.IPTablesSyncPeriod) if err != nil { return nil, fmt.Errorf("Cannot parse the provided ip-tables sync period (%s) : %v", options.IPTablesSyncPeriod, err) } sdnPlugin, err := sdnplugin.NewNodePlugin(options.NetworkConfig.NetworkPluginName, originClient, kubeClient, options.NodeName, options.NodeIP, iptablesSyncPeriod, options.NetworkConfig.MTU) if err != nil { return nil, fmt.Errorf("SDN initialization failed: %v", err) } if sdnPlugin != nil { // SDN plugin pod setup/teardown is implemented as a CNI plugin server.NetworkPluginName = kubeletcni.CNIPluginName server.NetworkPluginDir = kubeletcni.DefaultNetDir server.HairpinMode = componentconfig.HairpinNone server.ConfigureCBR0 = false } deps, err := kubeletapp.UnsecuredKubeletDeps(server) if err != nil { return nil, err } // Initialize cloud provider cloud, err := buildCloudProvider(server) if err != nil { return nil, err } deps.Cloud = cloud // Replace the kubelet-created CNI plugin with the SDN plugin // Kubelet must be initialized with NetworkPluginName="cni" but // the SDN plugin (if available) needs to be the only one used if sdnPlugin != nil { deps.NetworkPlugins = []kubeletnetwork.NetworkPlugin{sdnPlugin} } // provide any config overrides //deps.NodeName = options.NodeName deps.KubeClient = kubeClient deps.EventClient = eventClient // Setup auth authnTTL, err := time.ParseDuration(options.AuthConfig.AuthenticationCacheTTL) if err != nil { return nil, err } authn, err := newAuthenticator(kubeClient.Authentication(), clientCAs, authnTTL, options.AuthConfig.AuthenticationCacheSize) if err != nil { return nil, err } authzAttr, err := newAuthorizerAttributesGetter(options.NodeName) if err != nil { return nil, err } authzTTL, err := time.ParseDuration(options.AuthConfig.AuthorizationCacheTTL) if err != nil { return nil, err } authz, err := newAuthorizer(originClient, authzTTL, options.AuthConfig.AuthorizationCacheSize) if err != nil { return nil, err } deps.Auth = kubeletserver.NewKubeletAuth(authn, authzAttr, authz) // TODO: could be cleaner if configapi.UseTLS(options.ServingInfo) { extraCerts, err := configapi.GetNamedCertificateMap(options.ServingInfo.NamedCertificates) if err != nil { return nil, err } deps.TLSOptions = &kubeletserver.TLSOptions{ Config: crypto.SecureTLSConfig(&tls.Config{ // RequestClientCert lets us request certs, but allow requests without client certs // Verification is done by the authn layer ClientAuth: tls.RequestClientCert, ClientCAs: clientCAs, // Set SNI certificate func // Do not use NameToCertificate, since that requires certificates be included in the server's tlsConfig.Certificates list, // which we do not control when running with http.Server#ListenAndServeTLS GetCertificate: cmdutil.GetCertificateFunc(extraCerts), }), CertFile: options.ServingInfo.ServerCert.CertFile, KeyFile: options.ServingInfo.ServerCert.KeyFile, } } else { deps.TLSOptions = nil } sdnProxy, err := sdnplugin.NewProxyPlugin(options.NetworkConfig.NetworkPluginName, originClient, kubeClient) if err != nil { return nil, fmt.Errorf("SDN proxy initialization failed: %v", err) } config := &NodeConfig{ BindAddress: options.ServingInfo.BindAddress, AllowDisabledDocker: options.AllowDisabledDocker, Containerized: containerized, Client: kubeClient, VolumeDir: options.VolumeDirectory, KubeletServer: server, KubeletDeps: deps, ServicesReady: make(chan struct{}), ProxyConfig: proxyconfig, EnableUnidling: options.EnableUnidling, SDNPlugin: sdnPlugin, SDNProxy: sdnProxy, } if enableDNS { dnsConfig, err := dns.NewServerDefaults() if err != nil { return nil, fmt.Errorf("DNS configuration was not possible: %v", err) } if len(options.DNSIP) > 0 { dnsConfig.DnsAddr = options.DNSIP + ":53" } dnsConfig.Domain = server.ClusterDomain + "." dnsConfig.Local = "openshift.default.svc." + dnsConfig.Domain services, serviceStore := dns.NewCachedServiceAccessorAndStore() endpoints, endpointsStore := dns.NewCachedEndpointsAccessorAndStore() if !enableProxy { endpoints = deps.KubeClient endpointsStore = nil } // TODO: use kubeletConfig.ResolverConfig as an argument to etcd in the event the // user sets it, instead of passing it to the kubelet. config.ServiceStore = serviceStore config.EndpointsStore = endpointsStore config.DNSServer = &dns.Server{ Config: dnsConfig, Services: services, Endpoints: endpoints, MetricsName: "node", } } return config, nil }
func TestOAuthBasicAuthPassword(t *testing.T) { remotePrefix := "remote" expectedLogin := "******" expectedPassword := "******" expectedAuthHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(expectedLogin+":"+expectedPassword)) expectedUsername := remotePrefix + expectedLogin // Create tempfiles with certs and keys we're going to use certNames := map[string]string{} for certName, certContents := range basicAuthCerts { f, err := ioutil.TempFile("", certName) if err != nil { t.Fatalf("unexpected error: %v", err) } defer os.Remove(f.Name()) if err := ioutil.WriteFile(f.Name(), certContents, os.FileMode(0600)); err != nil { t.Fatalf("unexpected error: %v", err) } certNames[certName] = f.Name() } // Build client cert pool clientCAs, err := util.CertPoolFromFile(certNames[basicAuthRemoteCACert]) if err != nil { t.Fatalf("unexpected error: %v", err) } // Build remote handler remoteHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { if req.TLS == nil { w.WriteHeader(http.StatusUnauthorized) t.Fatalf("Expected TLS") } if len(req.TLS.VerifiedChains) != 1 { w.WriteHeader(http.StatusUnauthorized) t.Fatalf("Expected peer cert verified by server") } if req.Header.Get("Authorization") != expectedAuthHeader { w.WriteHeader(http.StatusUnauthorized) t.Fatalf("Unexpected auth header: %s", req.Header.Get("Authorization")) } w.Header().Set("Content-Type", "application/json") w.Write([]byte(fmt.Sprintf(`{"sub":"%s"}`, expectedUsername))) }) // Start remote server remoteAddr, err := testserver.FindAvailableBindAddress(9443, 9999) if err != nil { t.Fatalf("Couldn't get free address for test server: %v", err) } remoteServer := &http.Server{ Addr: remoteAddr, Handler: remoteHandler, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, TLSConfig: crypto.SecureTLSConfig(&tls.Config{ // RequireAndVerifyClientCert lets us limit requests to ones with a valid client certificate ClientAuth: tls.RequireAndVerifyClientCert, ClientCAs: clientCAs, }), } go func() { if err := remoteServer.ListenAndServeTLS(certNames[basicAuthRemoteServerCert], certNames[basicAuthRemoteServerKey]); err != nil { t.Fatalf("unexpected error: %v", err) } }() // Build master config testutil.RequireEtcd(t) masterOptions, err := testserver.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } masterOptions.OAuthConfig.IdentityProviders[0] = configapi.IdentityProvider{ Name: "basicauth", UseAsChallenger: true, UseAsLogin: true, MappingMethod: "claim", Provider: &configapi.BasicAuthPasswordIdentityProvider{ RemoteConnectionInfo: configapi.RemoteConnectionInfo{ URL: fmt.Sprintf("https://%s", remoteAddr), CA: certNames[basicAuthRemoteCACert], ClientCert: configapi.CertInfo{ CertFile: certNames[basicAuthClientCert], KeyFile: certNames[basicAuthClientKey], }, }, }, } // Start server clusterAdminKubeConfig, err := testserver.StartConfiguredMaster(masterOptions) if err != nil { t.Fatalf("unexpected error: %v", err) } clientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // Use the server and CA info anonConfig := restclient.Config{} anonConfig.Host = clientConfig.Host anonConfig.CAFile = clientConfig.CAFile anonConfig.CAData = clientConfig.CAData // Make sure we can get a token accessToken, err := tokencmd.RequestToken(&anonConfig, nil, expectedLogin, expectedPassword) if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(accessToken) == 0 { t.Errorf("Expected access token, got none") } // Make sure we can use the token, and it represents who we expect userConfig := anonConfig userConfig.BearerToken = accessToken userClient, err := client.New(&userConfig) if err != nil { t.Fatalf("Unexpected error: %v", err) } user, err := userClient.Users().Get("~") if err != nil { t.Fatalf("Unexpected error: %v", err) } if user.Name != expectedUsername { t.Fatalf("Expected username as the user, got %v", user) } }
func TestOAuthBasicAuthPassword(t *testing.T) { expectedLogin := "******" expectedPassword := "******" expectedAuthHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(expectedLogin+":"+expectedPassword)) testcases := map[string]struct { RemoteStatus int RemoteHeaders http.Header RemoteBody []byte ExpectUsername string ExpectSuccess bool ExpectErrStatus int32 }{ "success": { RemoteStatus: 200, RemoteHeaders: http.Header{"Content-Type": []string{"application/json"}}, RemoteBody: []byte(`{"sub":"remoteusername"}`), ExpectSuccess: true, ExpectUsername: "******", }, "401": { RemoteStatus: 401, RemoteHeaders: http.Header{"Content-Type": []string{"application/json"}}, RemoteBody: []byte(`{"error":"bad-user"}`), ExpectSuccess: false, ExpectUsername: "", ExpectErrStatus: 401, }, "301": { RemoteStatus: 301, RemoteHeaders: http.Header{"Location": []string{"http://www.example.com"}}, ExpectSuccess: false, ExpectUsername: "", ExpectErrStatus: 500, }, "302": { RemoteStatus: 302, RemoteHeaders: http.Header{"Location": []string{"http://www.example.com"}}, ExpectSuccess: false, ExpectUsername: "", ExpectErrStatus: 500, }, "303": { RemoteStatus: 303, RemoteHeaders: http.Header{"Location": []string{"http://www.example.com"}}, ExpectSuccess: false, ExpectUsername: "", ExpectErrStatus: 500, }, "304": { RemoteStatus: 304, RemoteHeaders: http.Header{"Location": []string{"http://www.example.com"}}, ExpectSuccess: false, ExpectUsername: "", ExpectErrStatus: 500, }, "305": { RemoteStatus: 305, RemoteHeaders: http.Header{"Location": []string{"http://www.example.com"}}, ExpectSuccess: false, ExpectUsername: "", ExpectErrStatus: 500, }, "404": { RemoteStatus: 404, ExpectSuccess: false, ExpectUsername: "", ExpectErrStatus: 500, }, "500": { RemoteStatus: 500, ExpectSuccess: false, ExpectUsername: "", ExpectErrStatus: 500, }, } // Create tempfiles with certs and keys we're going to use certNames := map[string]string{} for certName, certContents := range basicAuthCerts { f, err := ioutil.TempFile("", certName) if err != nil { t.Fatalf("unexpected error: %v", err) } defer os.Remove(f.Name()) if err := ioutil.WriteFile(f.Name(), certContents, os.FileMode(0600)); err != nil { t.Fatalf("unexpected error: %v", err) } certNames[certName] = f.Name() } // Build client cert pool clientCAs, err := util.CertPoolFromFile(certNames[basicAuthRemoteCACert]) if err != nil { t.Fatalf("unexpected error: %v", err) } // Build remote handler var ( remoteStatus int remoteHeaders http.Header remoteBody []byte ) remoteHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { if req.TLS == nil { w.WriteHeader(http.StatusUnauthorized) t.Fatalf("Expected TLS") } if len(req.TLS.VerifiedChains) != 1 { w.WriteHeader(http.StatusUnauthorized) t.Fatalf("Expected peer cert verified by server") } if req.Header.Get("Authorization") != expectedAuthHeader { w.WriteHeader(http.StatusUnauthorized) t.Fatalf("Expected auth header %s got %s", expectedAuthHeader, req.Header.Get("Authorization")) } for k, values := range remoteHeaders { for _, v := range values { w.Header().Add(k, v) } } w.WriteHeader(remoteStatus) w.Write(remoteBody) }) // Start remote server remoteAddr, err := testserver.FindAvailableBindAddress(9443, 9999) if err != nil { t.Fatalf("Couldn't get free address for test server: %v", err) } remoteServer := &http.Server{ Addr: remoteAddr, Handler: remoteHandler, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, TLSConfig: crypto.SecureTLSConfig(&tls.Config{ // RequireAndVerifyClientCert lets us limit requests to ones with a valid client certificate ClientAuth: tls.RequireAndVerifyClientCert, ClientCAs: clientCAs, }), } go func() { if err := remoteServer.ListenAndServeTLS(certNames[basicAuthRemoteServerCert], certNames[basicAuthRemoteServerKey]); err != nil { t.Fatalf("unexpected error: %v", err) } }() // Build master config testutil.RequireEtcd(t) masterOptions, err := testserver.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } masterOptions.OAuthConfig.IdentityProviders[0] = configapi.IdentityProvider{ Name: "basicauth", UseAsChallenger: true, UseAsLogin: true, MappingMethod: "claim", Provider: &configapi.BasicAuthPasswordIdentityProvider{ RemoteConnectionInfo: configapi.RemoteConnectionInfo{ URL: fmt.Sprintf("https://%s", remoteAddr), CA: certNames[basicAuthRemoteCACert], ClientCert: configapi.CertInfo{ CertFile: certNames[basicAuthClientCert], KeyFile: certNames[basicAuthClientKey], }, }, }, } // Start server clusterAdminKubeConfig, err := testserver.StartConfiguredMaster(masterOptions) if err != nil { t.Fatalf("unexpected error: %v", err) } clientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // Use the server and CA info anonConfig := restclient.Config{} anonConfig.Host = clientConfig.Host anonConfig.CAFile = clientConfig.CAFile anonConfig.CAData = clientConfig.CAData for k, tc := range testcases { // Specify the remote server's response remoteStatus = tc.RemoteStatus remoteHeaders = tc.RemoteHeaders remoteBody = tc.RemoteBody // Attempt to obtain a token accessToken, err := tokencmd.RequestToken(&anonConfig, nil, expectedLogin, expectedPassword) // Expected error if !tc.ExpectSuccess { if err == nil { t.Errorf("%s: Expected error, got token=%v", k, accessToken) } else if statusErr, ok := err.(*apierrs.StatusError); !ok { t.Errorf("%s: expected status error, got %#v", k, err) } else if statusErr.ErrStatus.Code != tc.ExpectErrStatus { t.Errorf("%s: expected error status %d, got %#v", k, tc.ExpectErrStatus, statusErr) } continue } // Expected success if err != nil { t.Errorf("%s: Unexpected error: %v", k, err) continue } // Make sure we can use the token, and it represents who we expect userConfig := anonConfig userConfig.BearerToken = accessToken userClient, err := client.New(&userConfig) if err != nil { t.Fatalf("%s: Unexpected error: %v", k, err) } user, err := userClient.Users().Get("~") if err != nil { t.Fatalf("%s: Unexpected error: %v", k, err) } if user.Name != tc.ExpectUsername { t.Fatalf("%s: Expected %v as the user, got %v", k, tc.ExpectUsername, user) } } }
func BuildKubernetesNodeConfig(options configapi.NodeConfig) (*NodeConfig, error) { originClient, _, err := configapi.GetOpenShiftClient(options.MasterKubeConfig) if err != nil { return nil, err } kubeClient, _, err := configapi.GetKubeClient(options.MasterKubeConfig) if err != nil { return nil, err } // Make a separate client for event reporting, to avoid event QPS blocking node calls eventClient, _, err := configapi.GetKubeClient(options.MasterKubeConfig) if err != nil { return nil, err } if options.NodeName == "localhost" { glog.Warningf(`Using "localhost" as node name will not resolve from all locations`) } clientCAs, err := util.CertPoolFromFile(options.ServingInfo.ClientCA) if err != nil { return nil, err } imageTemplate := variable.NewDefaultImageTemplate() imageTemplate.Format = options.ImageConfig.Format imageTemplate.Latest = options.ImageConfig.Latest var path string var fileCheckInterval int64 if options.PodManifestConfig != nil { path = options.PodManifestConfig.Path fileCheckInterval = options.PodManifestConfig.FileCheckIntervalSeconds } var dockerExecHandler dockertools.ExecHandler switch options.DockerConfig.ExecHandlerName { case configapi.DockerExecHandlerNative: dockerExecHandler = &dockertools.NativeExecHandler{} case configapi.DockerExecHandlerNsenter: dockerExecHandler = &dockertools.NsenterExecHandler{} } kubeAddressStr, kubePortStr, err := net.SplitHostPort(options.ServingInfo.BindAddress) if err != nil { return nil, fmt.Errorf("cannot parse node address: %v", err) } kubePort, err := strconv.Atoi(kubePortStr) if err != nil { return nil, fmt.Errorf("cannot parse node port: %v", err) } // declare the OpenShift defaults from config server := kubeletoptions.NewKubeletServer() server.Config = path server.RootDirectory = options.VolumeDirectory server.NodeIP = options.NodeIP server.HostnameOverride = options.NodeName server.AllowPrivileged = true server.RegisterNode = true server.Address = kubeAddressStr server.Port = uint(kubePort) server.ReadOnlyPort = 0 // no read only access server.CAdvisorPort = 0 // no unsecured cadvisor access server.HealthzPort = 0 // no unsecured healthz access server.ClusterDNS = options.DNSIP server.ClusterDomain = options.DNSDomain server.NetworkPluginName = options.NetworkConfig.NetworkPluginName server.HostNetworkSources = strings.Join([]string{kubelettypes.ApiserverSource, kubelettypes.FileSource}, ",") server.HostPIDSources = strings.Join([]string{kubelettypes.ApiserverSource, kubelettypes.FileSource}, ",") server.HostIPCSources = strings.Join([]string{kubelettypes.ApiserverSource, kubelettypes.FileSource}, ",") server.HTTPCheckFrequency = unversioned.Duration{Duration: time.Duration(0)} // no remote HTTP pod creation access server.FileCheckFrequency = unversioned.Duration{Duration: time.Duration(fileCheckInterval) * time.Second} server.PodInfraContainerImage = imageTemplate.ExpandOrDie("pod") server.CPUCFSQuota = true // enable cpu cfs quota enforcement by default server.MaxPods = 110 // prevents kube from generating certs server.TLSCertFile = options.ServingInfo.ServerCert.CertFile server.TLSPrivateKeyFile = options.ServingInfo.ServerCert.KeyFile containerized := cmdutil.Env("OPENSHIFT_CONTAINERIZED", "") == "true" server.Containerized = containerized // resolve extended arguments // TODO: this should be done in config validation (along with the above) so we can provide // proper errors if err := cmdflags.Resolve(options.KubeletArguments, server.AddFlags); len(err) > 0 { return nil, kerrors.NewAggregate(err) } proxyconfig, err := buildKubeProxyConfig(options) if err != nil { return nil, err } cfg, err := kubeletapp.UnsecuredKubeletConfig(server) if err != nil { return nil, err } // Replace the standard k8s emptyDir volume plugin with a wrapper version // which offers XFS quota functionality, but only if the node config // specifies an empty dir quota to apply to projects: if options.VolumeConfig.LocalQuota.PerFSGroup != nil { glog.V(2).Info("Replacing empty-dir volume plugin with quota wrapper") wrappedEmptyDirPlugin := false quotaApplicator, err := empty_dir.NewQuotaApplicator(options.VolumeDirectory) if err != nil { return nil, err } // Create a volume spec with emptyDir we can use to search for the // emptyDir plugin with CanSupport: emptyDirSpec := &volume.Spec{ Volume: &kapi.Volume{ VolumeSource: kapi.VolumeSource{ EmptyDir: &kapi.EmptyDirVolumeSource{}, }, }, } for idx, plugin := range cfg.VolumePlugins { // Can't really do type checking or use a constant here as they are not exported: if plugin.CanSupport(emptyDirSpec) { wrapper := empty_dir.EmptyDirQuotaPlugin{ Wrapped: plugin, Quota: *options.VolumeConfig.LocalQuota.PerFSGroup, QuotaApplicator: quotaApplicator, } cfg.VolumePlugins[idx] = &wrapper wrappedEmptyDirPlugin = true } } // Because we can't look for the k8s emptyDir plugin by any means that would // survive a refactor, error out if we couldn't find it: if !wrappedEmptyDirPlugin { return nil, errors.New("unable to wrap emptyDir volume plugin for quota support") } } else { glog.V(2).Info("Skipping replacement of empty-dir volume plugin with quota wrapper, no local fsGroup quota specified") } // provide any config overrides cfg.NodeName = options.NodeName cfg.KubeClient = internalclientset.FromUnversionedClient(kubeClient) cfg.EventClient = internalclientset.FromUnversionedClient(eventClient) cfg.DockerExecHandler = dockerExecHandler // docker-in-docker (dind) deployments are used for testing // networking plugins. Running openshift under dind won't work // with the real oom adjuster due to the state of the cgroups path // in a dind container that uses systemd for init. Similarly, // cgroup manipulation of the nested docker daemon doesn't work // properly under centos/rhel and should be disabled by setting // the name of the container to an empty string. // // This workaround should become unnecessary once user namespaces if value := cmdutil.Env("OPENSHIFT_DIND", ""); value == "true" { glog.Warningf("Using FakeOOMAdjuster for docker-in-docker compatibility") cfg.OOMAdjuster = oom.NewFakeOOMAdjuster() } // Setup auth osClient, osClientConfig, err := configapi.GetOpenShiftClient(options.MasterKubeConfig) if err != nil { return nil, err } authnTTL, err := time.ParseDuration(options.AuthConfig.AuthenticationCacheTTL) if err != nil { return nil, err } authn, err := newAuthenticator(clientCAs, clientcmd.AnonymousClientConfig(osClientConfig), authnTTL, options.AuthConfig.AuthenticationCacheSize) if err != nil { return nil, err } authzAttr, err := newAuthorizerAttributesGetter(options.NodeName) if err != nil { return nil, err } authzTTL, err := time.ParseDuration(options.AuthConfig.AuthorizationCacheTTL) if err != nil { return nil, err } authz, err := newAuthorizer(osClient, authzTTL, options.AuthConfig.AuthorizationCacheSize) if err != nil { return nil, err } cfg.Auth = kubeletserver.NewKubeletAuth(authn, authzAttr, authz) // Make sure the node doesn't think it is in standalone mode // This is required for the node to enforce nodeSelectors on pods, to set hostIP on pod status updates, etc cfg.StandaloneMode = false // TODO: could be cleaner if configapi.UseTLS(options.ServingInfo) { extraCerts, err := configapi.GetNamedCertificateMap(options.ServingInfo.NamedCertificates) if err != nil { return nil, err } cfg.TLSOptions = &kubeletserver.TLSOptions{ Config: crypto.SecureTLSConfig(&tls.Config{ // RequestClientCert lets us request certs, but allow requests without client certs // Verification is done by the authn layer ClientAuth: tls.RequestClientCert, ClientCAs: clientCAs, // Set SNI certificate func // Do not use NameToCertificate, since that requires certificates be included in the server's tlsConfig.Certificates list, // which we do not control when running with http.Server#ListenAndServeTLS GetCertificate: cmdutil.GetCertificateFunc(extraCerts), }), CertFile: options.ServingInfo.ServerCert.CertFile, KeyFile: options.ServingInfo.ServerCert.KeyFile, } } else { cfg.TLSOptions = nil } // Prepare cloud provider cloud, err := cloudprovider.InitCloudProvider(server.CloudProvider, server.CloudConfigFile) if err != nil { return nil, err } if cloud != nil { glog.V(2).Infof("Successfully initialized cloud provider: %q from the config file: %q\n", server.CloudProvider, server.CloudConfigFile) } cfg.Cloud = cloud sdnPlugin, endpointFilter, err := factory.NewPlugin(options.NetworkConfig.NetworkPluginName, originClient, kubeClient, options.NodeName, options.NodeIP) if err != nil { return nil, fmt.Errorf("SDN initialization failed: %v", err) } if sdnPlugin != nil { cfg.NetworkPlugins = append(cfg.NetworkPlugins, sdnPlugin) } config := &NodeConfig{ BindAddress: options.ServingInfo.BindAddress, AllowDisabledDocker: options.AllowDisabledDocker, Containerized: containerized, Client: kubeClient, VolumeDir: options.VolumeDirectory, KubeletServer: server, KubeletConfig: cfg, ProxyConfig: proxyconfig, MTU: options.NetworkConfig.MTU, SDNPlugin: sdnPlugin, FilteringEndpointsHandler: endpointFilter, } return config, nil }
func BuildKubernetesNodeConfig(options configapi.NodeConfig) (*NodeConfig, error) { originClient, osClientConfig, err := configapi.GetOpenShiftClient(options.MasterKubeConfig) if err != nil { return nil, err } kubeClient, _, err := configapi.GetKubeClient(options.MasterKubeConfig) if err != nil { return nil, err } // Make a separate client for event reporting, to avoid event QPS blocking node calls eventClient, _, err := configapi.GetKubeClient(options.MasterKubeConfig) if err != nil { return nil, err } if options.NodeName == "localhost" { glog.Warningf(`Using "localhost" as node name will not resolve from all locations`) } clientCAs, err := kcrypto.CertPoolFromFile(options.ServingInfo.ClientCA) if err != nil { return nil, err } imageTemplate := variable.NewDefaultImageTemplate() imageTemplate.Format = options.ImageConfig.Format imageTemplate.Latest = options.ImageConfig.Latest var path string var fileCheckInterval int64 if options.PodManifestConfig != nil { path = options.PodManifestConfig.Path fileCheckInterval = options.PodManifestConfig.FileCheckIntervalSeconds } var dockerExecHandler dockertools.ExecHandler switch options.DockerConfig.ExecHandlerName { case configapi.DockerExecHandlerNative: dockerExecHandler = &dockertools.NativeExecHandler{} case configapi.DockerExecHandlerNsenter: dockerExecHandler = &dockertools.NsenterExecHandler{} } kubeAddressStr, kubePortStr, err := net.SplitHostPort(options.ServingInfo.BindAddress) if err != nil { return nil, fmt.Errorf("cannot parse node address: %v", err) } kubePort, err := strconv.Atoi(kubePortStr) if err != nil { return nil, fmt.Errorf("cannot parse node port: %v", err) } // Defaults are tested in TestKubeletDefaults server := kubeletoptions.NewKubeletServer() // Adjust defaults server.Config = path server.RootDirectory = options.VolumeDirectory server.NodeIP = options.NodeIP server.HostnameOverride = options.NodeName server.AllowPrivileged = true server.RegisterNode = true server.Address = kubeAddressStr server.Port = uint(kubePort) server.ReadOnlyPort = 0 // no read only access server.CAdvisorPort = 0 // no unsecured cadvisor access server.HealthzPort = 0 // no unsecured healthz access server.HealthzBindAddress = "" // no unsecured healthz access server.ClusterDNS = options.DNSIP server.ClusterDomain = options.DNSDomain server.NetworkPluginName = options.NetworkConfig.NetworkPluginName server.HostNetworkSources = strings.Join([]string{kubelettypes.ApiserverSource, kubelettypes.FileSource}, ",") server.HostPIDSources = strings.Join([]string{kubelettypes.ApiserverSource, kubelettypes.FileSource}, ",") server.HostIPCSources = strings.Join([]string{kubelettypes.ApiserverSource, kubelettypes.FileSource}, ",") server.HTTPCheckFrequency = unversioned.Duration{Duration: time.Duration(0)} // no remote HTTP pod creation access server.FileCheckFrequency = unversioned.Duration{Duration: time.Duration(fileCheckInterval) * time.Second} server.PodInfraContainerImage = imageTemplate.ExpandOrDie("pod") server.CPUCFSQuota = true // enable cpu cfs quota enforcement by default server.MaxPods = 110 server.SerializeImagePulls = false // disable serial image pulls by default switch server.NetworkPluginName { case ovs.SingleTenantPluginName, ovs.MultiTenantPluginName: // set defaults for openshift-sdn server.HairpinMode = componentconfig.HairpinNone server.ConfigureCBR0 = false } // prevents kube from generating certs server.TLSCertFile = options.ServingInfo.ServerCert.CertFile server.TLSPrivateKeyFile = options.ServingInfo.ServerCert.KeyFile containerized := cmdutil.Env("OPENSHIFT_CONTAINERIZED", "") == "true" server.Containerized = containerized // resolve extended arguments // TODO: this should be done in config validation (along with the above) so we can provide // proper errors if err := cmdflags.Resolve(options.KubeletArguments, server.AddFlags); len(err) > 0 { return nil, kerrors.NewAggregate(err) } proxyconfig, err := buildKubeProxyConfig(options) if err != nil { return nil, err } cfg, err := kubeletapp.UnsecuredKubeletConfig(server) if err != nil { return nil, err } // provide any config overrides cfg.NodeName = options.NodeName cfg.KubeClient = clientadapter.FromUnversionedClient(kubeClient) cfg.EventClient = clientadapter.FromUnversionedClient(eventClient) cfg.DockerExecHandler = dockerExecHandler // Setup auth authnTTL, err := time.ParseDuration(options.AuthConfig.AuthenticationCacheTTL) if err != nil { return nil, err } authn, err := newAuthenticator(clientCAs, clientcmd.AnonymousClientConfig(osClientConfig), authnTTL, options.AuthConfig.AuthenticationCacheSize) if err != nil { return nil, err } authzAttr, err := newAuthorizerAttributesGetter(options.NodeName) if err != nil { return nil, err } authzTTL, err := time.ParseDuration(options.AuthConfig.AuthorizationCacheTTL) if err != nil { return nil, err } authz, err := newAuthorizer(originClient, authzTTL, options.AuthConfig.AuthorizationCacheSize) if err != nil { return nil, err } cfg.Auth = kubeletserver.NewKubeletAuth(authn, authzAttr, authz) // Make sure the node doesn't think it is in standalone mode // This is required for the node to enforce nodeSelectors on pods, to set hostIP on pod status updates, etc cfg.StandaloneMode = false // TODO: could be cleaner if configapi.UseTLS(options.ServingInfo) { extraCerts, err := configapi.GetNamedCertificateMap(options.ServingInfo.NamedCertificates) if err != nil { return nil, err } cfg.TLSOptions = &kubeletserver.TLSOptions{ Config: crypto.SecureTLSConfig(&tls.Config{ // RequestClientCert lets us request certs, but allow requests without client certs // Verification is done by the authn layer ClientAuth: tls.RequestClientCert, ClientCAs: clientCAs, // Set SNI certificate func // Do not use NameToCertificate, since that requires certificates be included in the server's tlsConfig.Certificates list, // which we do not control when running with http.Server#ListenAndServeTLS GetCertificate: cmdutil.GetCertificateFunc(extraCerts), }), CertFile: options.ServingInfo.ServerCert.CertFile, KeyFile: options.ServingInfo.ServerCert.KeyFile, } } else { cfg.TLSOptions = nil } // Prepare cloud provider cloud, err := cloudprovider.InitCloudProvider(server.CloudProvider, server.CloudConfigFile) if err != nil { return nil, err } if cloud != nil { glog.V(2).Infof("Successfully initialized cloud provider: %q from the config file: %q\n", server.CloudProvider, server.CloudConfigFile) } cfg.Cloud = cloud sdnPlugin, err := factory.NewNodePlugin(options.NetworkConfig.NetworkPluginName, originClient, kubeClient, options.NodeName, options.NodeIP) if err != nil { return nil, fmt.Errorf("SDN initialization failed: %v", err) } if sdnPlugin != nil { cfg.NetworkPlugins = append(cfg.NetworkPlugins, sdnPlugin) } endpointFilter, err := factory.NewProxyPlugin(options.NetworkConfig.NetworkPluginName, originClient, kubeClient) if err != nil { return nil, fmt.Errorf("SDN proxy initialization failed: %v", err) } config := &NodeConfig{ BindAddress: options.ServingInfo.BindAddress, AllowDisabledDocker: options.AllowDisabledDocker, Containerized: containerized, Client: kubeClient, VolumeDir: options.VolumeDirectory, KubeletServer: server, KubeletConfig: cfg, ProxyConfig: proxyconfig, MTU: options.NetworkConfig.MTU, SDNPlugin: sdnPlugin, FilteringEndpointsHandler: endpointFilter, } return config, nil }
// Execute runs the Docker registry. func Execute(configFile io.Reader) { config, err := configuration.Parse(configFile) if err != nil { log.Fatalf("Error parsing configuration file: %s", err) } tokenPath := "/openshift/token" // If needed, generate and populate the token realm URL in the config. // Must be done prior to instantiating the app, so our auth provider has the config available. _, usingOpenShiftAuth := config.Auth[server.OpenShiftAuth] _, hasTokenRealm := config.Auth[server.OpenShiftAuth][server.TokenRealmKey].(string) if usingOpenShiftAuth && !hasTokenRealm { registryHost := os.Getenv(server.DockerRegistryURLEnvVar) if len(registryHost) == 0 { log.Fatalf("%s is required", server.DockerRegistryURLEnvVar) } tokenURL := &url.URL{Scheme: "https", Host: registryHost, Path: tokenPath} if len(config.HTTP.TLS.Certificate) == 0 { tokenURL.Scheme = "http" } if config.Auth[server.OpenShiftAuth] == nil { config.Auth[server.OpenShiftAuth] = configuration.Parameters{} } config.Auth[server.OpenShiftAuth][server.TokenRealmKey] = tokenURL.String() } ctx := context.Background() ctx, err = configureLogging(ctx, config) if err != nil { log.Fatalf("error configuring logger: %v", err) } log.Infof("version=%s", version.Version) // inject a logger into the uuid library. warns us if there is a problem // with uuid generation under low entropy. uuid.Loggerf = context.GetLogger(ctx).Warnf app := handlers.NewApp(ctx, config) // Add a token handling endpoint if usingOpenShiftAuth { app.NewRoute().Methods("GET").PathPrefix(tokenPath).Handler(server.NewTokenHandler(ctx, server.DefaultRegistryClient)) } // TODO add https scheme adminRouter := app.NewRoute().PathPrefix("/admin/").Subrouter() pruneAccessRecords := func(*http.Request) []auth.Access { return []auth.Access{ { Resource: auth.Resource{ Type: "admin", }, Action: "prune", }, } } app.RegisterRoute( // DELETE /admin/blobs/<digest> adminRouter.Path("/blobs/{digest:"+reference.DigestRegexp.String()+"}").Methods("DELETE"), // handler server.BlobDispatcher, // repo name not required in url handlers.NameNotRequired, // custom access records pruneAccessRecords, ) app.RegisterHealthChecks() handler := alive("/", app) // TODO: temporarily keep for backwards compatibility; remove in the future handler = alive("/healthz", handler) handler = health.Handler(handler) handler = panicHandler(handler) handler = gorillahandlers.CombinedLoggingHandler(os.Stdout, handler) if config.HTTP.TLS.Certificate == "" { context.GetLogger(app).Infof("listening on %v", config.HTTP.Addr) if err := http.ListenAndServe(config.HTTP.Addr, handler); err != nil { context.GetLogger(app).Fatalln(err) } } else { tlsConf := crypto.SecureTLSConfig(&tls.Config{ClientAuth: tls.NoClientCert}) if len(config.HTTP.TLS.ClientCAs) != 0 { pool := x509.NewCertPool() for _, ca := range config.HTTP.TLS.ClientCAs { caPem, err := ioutil.ReadFile(ca) if err != nil { context.GetLogger(app).Fatalln(err) } if ok := pool.AppendCertsFromPEM(caPem); !ok { context.GetLogger(app).Fatalln(fmt.Errorf("Could not add CA to pool")) } } for _, subj := range pool.Subjects() { context.GetLogger(app).Debugf("CA Subject: %s", string(subj)) } tlsConf.ClientAuth = tls.RequireAndVerifyClientCert tlsConf.ClientCAs = pool } context.GetLogger(app).Infof("listening on %v, tls", config.HTTP.Addr) server := &http.Server{ Addr: config.HTTP.Addr, Handler: handler, TLSConfig: tlsConf, } if err := server.ListenAndServeTLS(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key); err != nil { context.GetLogger(app).Fatalln(err) } } }
// Execute runs the Docker registry. func Execute(configFile io.Reader) { config, err := configuration.Parse(configFile) if err != nil { log.Fatalf("Error parsing configuration file: %s", err) } ctx := context.Background() ctx, err = configureLogging(ctx, config) if err != nil { log.Fatalf("error configuring logger: %v", err) } log.Infof("version=%s", version.Version) // inject a logger into the uuid library. warns us if there is a problem // with uuid generation under low entropy. uuid.Loggerf = context.GetLogger(ctx).Warnf app := handlers.NewApp(ctx, config) // TODO add https scheme adminRouter := app.NewRoute().PathPrefix("/admin/").Subrouter() pruneAccessRecords := func(*http.Request) []auth.Access { return []auth.Access{ { Resource: auth.Resource{ Type: "admin", }, Action: "prune", }, } } app.RegisterRoute( // DELETE /admin/blobs/<digest> adminRouter.Path("/blobs/{digest:"+reference.DigestRegexp.String()+"}").Methods("DELETE"), // handler server.BlobDispatcher, // repo name not required in url handlers.NameNotRequired, // custom access records pruneAccessRecords, ) app.RegisterHealthChecks() handler := alive("/", app) // TODO: temporarily keep for backwards compatibility; remove in the future handler = alive("/healthz", handler) handler = health.Handler(handler) handler = panicHandler(handler) handler = gorillahandlers.CombinedLoggingHandler(os.Stdout, handler) if config.HTTP.TLS.Certificate == "" { context.GetLogger(app).Infof("listening on %v", config.HTTP.Addr) if err := http.ListenAndServe(config.HTTP.Addr, handler); err != nil { context.GetLogger(app).Fatalln(err) } } else { tlsConf := crypto.SecureTLSConfig(&tls.Config{ClientAuth: tls.NoClientCert}) if len(config.HTTP.TLS.ClientCAs) != 0 { pool := x509.NewCertPool() for _, ca := range config.HTTP.TLS.ClientCAs { caPem, err := ioutil.ReadFile(ca) if err != nil { context.GetLogger(app).Fatalln(err) } if ok := pool.AppendCertsFromPEM(caPem); !ok { context.GetLogger(app).Fatalln(fmt.Errorf("Could not add CA to pool")) } } for _, subj := range pool.Subjects() { context.GetLogger(app).Debugf("CA Subject: %s", string(subj)) } tlsConf.ClientAuth = tls.RequireAndVerifyClientCert tlsConf.ClientCAs = pool } context.GetLogger(app).Infof("listening on %v, tls", config.HTTP.Addr) server := &http.Server{ Addr: config.HTTP.Addr, Handler: handler, TLSConfig: tlsConf, } if err := server.ListenAndServeTLS(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key); err != nil { context.GetLogger(app).Fatalln(err) } } }
// Execute runs the Docker registry. func Execute(configFile io.Reader) { config, err := configuration.Parse(configFile) if err != nil { log.Fatalf("Error parsing configuration file: %s", err) } logLevel, err := log.ParseLevel(string(config.Log.Level)) if err != nil { log.Errorf("Error parsing log level %q: %s", config.Log.Level, err) logLevel = log.InfoLevel } log.SetLevel(logLevel) log.Infof("version=%s", version.Version) ctx := context.Background() app := handlers.NewApp(ctx, *config) // register OpenShift routes // TODO: change this to an anonymous Access record app.RegisterRoute(app.NewRoute().Path("/healthz"), server.HealthzHandler, handlers.NameNotRequired, handlers.NoCustomAccessRecords) // TODO add https scheme adminRouter := app.NewRoute().PathPrefix("/admin/").Subrouter() pruneAccessRecords := func(*http.Request) []auth.Access { return []auth.Access{ { Resource: auth.Resource{ Type: "admin", }, Action: "prune", }, } } app.RegisterRoute( // DELETE /admin/blobs/<digest> adminRouter.Path("/blobs/{digest:"+digest.DigestRegexp.String()+"}").Methods("DELETE"), // handler server.BlobDispatcher, // repo name not required in url handlers.NameNotRequired, // custom access records pruneAccessRecords, ) app.RegisterRoute( // DELETE /admin/<repo>/manifests/<digest> adminRouter.Path("/{name:"+v2.RepositoryNameRegexp.String()+"}/manifests/{digest:"+digest.DigestRegexp.String()+"}").Methods("DELETE"), // handler server.ManifestDispatcher, // repo name required in url handlers.NameRequired, // custom access records pruneAccessRecords, ) app.RegisterRoute( // DELETE /admin/<repo>/layers/<digest> adminRouter.Path("/{name:"+v2.RepositoryNameRegexp.String()+"}/layers/{digest:"+digest.DigestRegexp.String()+"}").Methods("DELETE"), // handler server.LayerDispatcher, // repo name required in url handlers.NameRequired, // custom access records pruneAccessRecords, ) handler := gorillahandlers.CombinedLoggingHandler(os.Stdout, app) if config.HTTP.TLS.Certificate == "" { context.GetLogger(app).Infof("listening on %v", config.HTTP.Addr) if err := http.ListenAndServe(config.HTTP.Addr, handler); err != nil { context.GetLogger(app).Fatalln(err) } } else { tlsConf := crypto.SecureTLSConfig(&tls.Config{ClientAuth: tls.NoClientCert}) if len(config.HTTP.TLS.ClientCAs) != 0 { pool := x509.NewCertPool() for _, ca := range config.HTTP.TLS.ClientCAs { caPem, err := ioutil.ReadFile(ca) if err != nil { context.GetLogger(app).Fatalln(err) } if ok := pool.AppendCertsFromPEM(caPem); !ok { context.GetLogger(app).Fatalln(fmt.Errorf("Could not add CA to pool")) } } for _, subj := range pool.Subjects() { context.GetLogger(app).Debugf("CA Subject: %s", string(subj)) } tlsConf.ClientAuth = tls.RequireAndVerifyClientCert tlsConf.ClientCAs = pool } context.GetLogger(app).Infof("listening on %v, tls", config.HTTP.Addr) server := &http.Server{ Addr: config.HTTP.Addr, Handler: handler, TLSConfig: tlsConf, } if err := server.ListenAndServeTLS(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key); err != nil { context.GetLogger(app).Fatalln(err) } } }