portainer/api/http/proxy/factory/agent.go

103 lines
2.6 KiB
Go

package factory
import (
"fmt"
"net"
"net/http"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/crypto"
"github.com/portainer/portainer/api/http/proxy/factory/agent"
"github.com/portainer/portainer/api/internal/endpointutils"
"github.com/portainer/portainer/api/internal/url"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
)
// ProxyServer provide an extended proxy with a local server to forward requests
type ProxyServer struct {
server *http.Server
Port int
}
// NewAgentProxy creates a new instance of ProxyServer that wrap http requests with agent headers
func (factory *ProxyFactory) NewAgentProxy(endpoint *portainer.Endpoint) (*ProxyServer, error) {
urlString := endpoint.URL
if endpointutils.IsEdgeEndpoint(endpoint) {
tunnel, err := factory.reverseTunnelService.GetActiveTunnel(endpoint)
if err != nil {
return nil, errors.Wrap(err, "failed starting tunnel")
}
urlString = fmt.Sprintf("http://127.0.0.1:%d", tunnel.Port)
}
endpointURL, err := url.ParseURL(urlString)
if err != nil {
return nil, errors.Wrapf(err, "failed parsing url %s", endpoint.URL)
}
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, errors.WithMessage(err, "failed generating tls configuration")
}
httpTransport.TLSClientConfig = config
endpointURL.Scheme = "https"
}
proxy := newSingleHostReverseProxyWithHostHeader(endpointURL)
proxy.Transport = agent.NewTransport(factory.signatureService, httpTransport)
proxyServer := &ProxyServer{
server: &http.Server{
Handler: proxy,
},
Port: 0,
}
err = proxyServer.start()
if err != nil {
return nil, errors.Wrap(err, "failed starting proxy server")
}
return proxyServer, nil
}
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.Debug().Str("host", proxyHost).Msg("starting proxy server")
err := proxy.server.Serve(listener)
log.Debug().Str("host", proxyHost).Msg("exiting proxy server")
if err != nil && err != http.ErrServerClosed {
log.Debug().Str("host", proxyHost).Err(err).Msg("proxy server exited with an error")
}
}()
return nil
}
// Close shuts down the server
func (proxy *ProxyServer) Close() {
if proxy.server != nil {
proxy.server.Close()
}
}