mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-27 16:39:58 +00:00
TUN-2640: Users can configure per-origin config. Unify single-rule CLI
flow with multi-rule config file code.
This commit is contained in:
181
ingress/origin_service.go
Normal file
181
ingress/origin_service.go
Normal file
@@ -0,0 +1,181 @@
|
||||
package ingress
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/cloudflare/cloudflared/hello"
|
||||
"github.com/cloudflare/cloudflared/logger"
|
||||
"github.com/cloudflare/cloudflared/socks"
|
||||
"github.com/cloudflare/cloudflared/websocket"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// OriginService is something a tunnel can proxy traffic to.
|
||||
type OriginService interface {
|
||||
Address() string
|
||||
// Start the origin service if it's managed by cloudflared, e.g. proxy servers or Hello World.
|
||||
// If it's not managed by cloudflared, this is a no-op because the user is responsible for
|
||||
// starting the origin service.
|
||||
Start(wg *sync.WaitGroup, log logger.Service, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error
|
||||
String() string
|
||||
// RewriteOriginURL modifies the HTTP request from cloudflared to the origin, so that it apply
|
||||
// this particular type of origin service's specific routing logic.
|
||||
RewriteOriginURL(*url.URL)
|
||||
}
|
||||
|
||||
// UnixSocketPath is an OriginService representing a unix socket (which accepts HTTP)
|
||||
type UnixSocketPath string
|
||||
|
||||
func (o UnixSocketPath) Address() string {
|
||||
return string(o)
|
||||
}
|
||||
|
||||
func (o UnixSocketPath) String() string {
|
||||
return "unix socket: " + string(o)
|
||||
}
|
||||
|
||||
func (o UnixSocketPath) Start(wg *sync.WaitGroup, log logger.Service, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o UnixSocketPath) RewriteOriginURL(u *url.URL) {
|
||||
// No changes necessary because the origin request URL isn't used.
|
||||
// Instead, HTTPTransport's dial is already configured to address the unix socket.
|
||||
}
|
||||
|
||||
// URL is an OriginService listening on a TCP address
|
||||
type URL struct {
|
||||
// The URL for the user's origin service
|
||||
RootURL *url.URL
|
||||
// The URL that cloudflared should send requests to.
|
||||
// If this origin requires starting a proxy, this is the proxy's address,
|
||||
// and that proxy points to RootURL. Otherwise, this is equal to RootURL.
|
||||
URL *url.URL
|
||||
}
|
||||
|
||||
func (o *URL) Address() string {
|
||||
return o.URL.String()
|
||||
}
|
||||
|
||||
func (o *URL) Start(wg *sync.WaitGroup, log logger.Service, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error {
|
||||
staticHost := o.staticHost()
|
||||
if !originRequiresProxy(staticHost, cfg) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start a listener for the proxy
|
||||
proxyAddress := net.JoinHostPort(cfg.ProxyAddress, strconv.Itoa(int(cfg.ProxyPort)))
|
||||
listener, err := net.Listen("tcp", proxyAddress)
|
||||
if err != nil {
|
||||
log.Errorf("Cannot start Websocket Proxy Server: %s", err)
|
||||
return errors.Wrap(err, "Cannot start Websocket Proxy Server")
|
||||
}
|
||||
|
||||
// Start the proxy itself
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
streamHandler := websocket.DefaultStreamHandler
|
||||
// This origin's config specifies what type of proxy to start.
|
||||
switch cfg.ProxyType {
|
||||
case socksProxy:
|
||||
log.Info("SOCKS5 server started")
|
||||
streamHandler = func(wsConn *websocket.Conn, remoteConn net.Conn, _ http.Header) {
|
||||
dialer := socks.NewConnDialer(remoteConn)
|
||||
requestHandler := socks.NewRequestHandler(dialer)
|
||||
socksServer := socks.NewConnectionHandler(requestHandler)
|
||||
|
||||
socksServer.Serve(wsConn)
|
||||
}
|
||||
case "":
|
||||
log.Debug("Not starting any websocket proxy")
|
||||
default:
|
||||
log.Errorf("%s isn't a valid proxy (valid options are {%s})", cfg.ProxyType, socksProxy)
|
||||
}
|
||||
|
||||
errC <- websocket.StartProxyServer(log, listener, staticHost, shutdownC, streamHandler)
|
||||
}()
|
||||
|
||||
// Modify this origin, so that it no longer points at the origin service directly.
|
||||
// Instead, it points at the proxy to the origin service.
|
||||
newURL, err := url.Parse("http://" + listener.Addr().String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.URL = newURL
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *URL) String() string {
|
||||
return o.Address()
|
||||
}
|
||||
|
||||
func (o *URL) RewriteOriginURL(u *url.URL) {
|
||||
u.Host = o.URL.Host
|
||||
u.Scheme = o.URL.Scheme
|
||||
}
|
||||
|
||||
func (o *URL) staticHost() string {
|
||||
|
||||
addPortIfMissing := func(uri *url.URL, port int) string {
|
||||
if uri.Port() != "" {
|
||||
return uri.Host
|
||||
}
|
||||
return fmt.Sprintf("%s:%d", uri.Hostname(), port)
|
||||
}
|
||||
|
||||
switch o.URL.Scheme {
|
||||
case "ssh":
|
||||
return addPortIfMissing(o.URL, 22)
|
||||
case "rdp":
|
||||
return addPortIfMissing(o.URL, 3389)
|
||||
case "smb":
|
||||
return addPortIfMissing(o.URL, 445)
|
||||
case "tcp":
|
||||
return addPortIfMissing(o.URL, 7864) // just a random port since there isn't a default in this case
|
||||
}
|
||||
return ""
|
||||
|
||||
}
|
||||
|
||||
// HelloWorld is the built-in Hello World service. Used for testing and experimenting with cloudflared.
|
||||
type HelloWorld struct {
|
||||
server net.Listener
|
||||
}
|
||||
|
||||
func (o *HelloWorld) Address() string {
|
||||
return o.server.Addr().String()
|
||||
}
|
||||
|
||||
func (o *HelloWorld) String() string {
|
||||
return "Hello World static HTML service"
|
||||
}
|
||||
|
||||
// Start starts a HelloWorld server and stores its address in the Service receiver.
|
||||
func (o *HelloWorld) Start(wg *sync.WaitGroup, log logger.Service, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error {
|
||||
helloListener, err := hello.CreateTLSListener("127.0.0.1:")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Cannot start Hello World Server")
|
||||
}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
_ = hello.StartHelloWorldServer(log, helloListener, shutdownC)
|
||||
}()
|
||||
o.server = helloListener
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *HelloWorld) RewriteOriginURL(u *url.URL) {
|
||||
u.Host = o.Address()
|
||||
u.Scheme = "https"
|
||||
}
|
||||
|
||||
func originRequiresProxy(staticHost string, cfg OriginRequestConfig) bool {
|
||||
return staticHost != "" || cfg.BastionMode
|
||||
}
|
Reference in New Issue
Block a user