package factory import ( "fmt" "log" "net" "net/http" "net/url" portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/crypto" "github.com/portainer/portainer/api/http/proxy/factory/dockercompose" ) // ProxyServer provide an extedned proxy with a local server to forward requests type ProxyServer struct { server *http.Server Port int } func (factory *ProxyFactory) NewDockerComposeAgentProxy(endpoint *portainer.Endpoint) (*ProxyServer, error) { if endpoint.Type == portainer.EdgeAgentOnDockerEnvironment { return &ProxyServer{ Port: factory.reverseTunnelService.GetTunnelDetails(endpoint.ID).Port, }, nil } endpointURL, err := url.Parse(endpoint.URL) if err != nil { return nil, err } endpointURL.Scheme = "http" httpTransport := &http.Transport{} if endpoint.TLSConfig.TLS || endpoint.TLSConfig.TLSSkipVerify { config, err := crypto.CreateTLSConfigurationFromDisk(endpoint.TLSConfig.TLSCACertPath, endpoint.TLSConfig.TLSCertPath, endpoint.TLSConfig.TLSKeyPath, endpoint.TLSConfig.TLSSkipVerify) if err != nil { return nil, err } httpTransport.TLSClientConfig = config endpointURL.Scheme = "https" } proxy := newSingleHostReverseProxyWithHostHeader(endpointURL) proxy.Transport = dockercompose.NewAgentTransport(factory.signatureService, httpTransport) proxyServer := &ProxyServer{ &http.Server{ Handler: proxy, }, 0, } return proxyServer, proxyServer.start() } func (proxy *ProxyServer) start() error { listener, err := net.Listen("tcp", ":0") if err != nil { return err } proxy.Port = listener.Addr().(*net.TCPAddr).Port go func() { proxyHost := fmt.Sprintf("127.0.0.1:%d", proxy.Port) log.Printf("Starting Proxy server on %s...\n", proxyHost) err := proxy.server.Serve(listener) log.Printf("Exiting Proxy server %s\n", proxyHost) if err != http.ErrServerClosed { log.Printf("Proxy server %s exited with an error: %s\n", proxyHost, err) } }() return nil } // Close shuts down the server func (proxy *ProxyServer) Close() { if proxy.server != nil { proxy.server.Close() } }