mirror of
https://github.com/boringproxy/boringproxy.git
synced 2024-07-04 11:22:57 -05:00
Implement raw server TLS tunnels
Also cleaned up things a bit by moving the ProxyTcp logic into a separate file and sharing it between the client and server.
This commit is contained in:
parent
3350d12c0e
commit
197e202d69
2
api.go
2
api.go
|
@ -374,7 +374,7 @@ func (a *Api) CreateTunnel(tokenData TokenData, params url.Values) (*Tunnel, err
|
|||
}
|
||||
|
||||
tlsTerm := params.Get("tls-termination")
|
||||
if tlsTerm != "server" && tlsTerm != "client" && tlsTerm != "passthrough" && tlsTerm != "client-tls" {
|
||||
if tlsTerm != "server" && tlsTerm != "client" && tlsTerm != "passthrough" && tlsTerm != "client-tls" && tlsTerm != "server-tls" {
|
||||
return nil, errors.New("Invalid tls-termination parameter")
|
||||
}
|
||||
|
||||
|
|
|
@ -340,11 +340,11 @@ func Listen() {
|
|||
continue
|
||||
}
|
||||
|
||||
go p.handleConnection(conn)
|
||||
go p.handleConnection(conn, certConfig)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Server) handleConnection(clientConn net.Conn) {
|
||||
func (p *Server) handleConnection(clientConn net.Conn, certConfig *certmagic.Config) {
|
||||
|
||||
clientHello, clientReader, err := peekClientHello(clientConn)
|
||||
if err != nil {
|
||||
|
@ -358,6 +358,13 @@ func (p *Server) handleConnection(clientConn net.Conn) {
|
|||
|
||||
if exists && (tunnel.TlsTermination == "client" || tunnel.TlsTermination == "passthrough") || tunnel.TlsTermination == "client-tls" {
|
||||
p.passthroughRequest(passConn, tunnel)
|
||||
} else if exists && tunnel.TlsTermination == "server-tls" {
|
||||
useTls := true
|
||||
err := ProxyTcp(passConn, "127.0.0.1", tunnel.TunnelPort, useTls, certConfig)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
p.httpListener.PassConn(passConn)
|
||||
}
|
||||
|
|
98
client.go
98
client.go
|
@ -6,12 +6,10 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -321,18 +319,6 @@ func (c *Client) BoreTunnel(ctx context.Context, tunnel Tunnel) error {
|
|||
|
||||
} else {
|
||||
|
||||
if tunnel.TlsTermination == "client-tls" {
|
||||
tlsConfig := &tls.Config{
|
||||
GetCertificate: c.certConfig.GetCertificate,
|
||||
}
|
||||
|
||||
tlsConfig.NextProtos = append([]string{"http/1.1", "h2", "acme-tls/1"}, tlsConfig.NextProtos...)
|
||||
|
||||
tlsListener := tls.NewListener(listener, tlsConfig)
|
||||
|
||||
listener = tlsListener
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
|
@ -346,17 +332,14 @@ func (c *Client) BoreTunnel(ctx context.Context, tunnel Tunnel) error {
|
|||
//continue
|
||||
}
|
||||
|
||||
// If ALPN type is acme-tls/1, certmagic will do its thing under the hood, and the
|
||||
// connection should not be used.
|
||||
if tlsConn, ok := conn.(*tls.Conn); ok {
|
||||
tlsConn.Handshake()
|
||||
if tlsConn.ConnectionState().NegotiatedProtocol == "acme-tls/1" {
|
||||
tlsConn.Close()
|
||||
continue
|
||||
}
|
||||
var useTls bool
|
||||
if tunnel.TlsTermination == "client-tls" {
|
||||
useTls = true
|
||||
} else {
|
||||
useTls = false
|
||||
}
|
||||
|
||||
go c.handleConnection(conn, tunnel.ClientAddress, tunnel.ClientPort)
|
||||
go ProxyTcp(conn, tunnel.ClientAddress, tunnel.ClientPort, useTls, c.certConfig)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
@ -376,75 +359,6 @@ func (c *Client) BoreTunnel(ctx context.Context, tunnel Tunnel) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) handleConnection(conn net.Conn, upstreamAddr string, port int) {
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
useTls := false
|
||||
addr := upstreamAddr
|
||||
|
||||
if strings.HasPrefix(upstreamAddr, "https://") {
|
||||
addr = upstreamAddr[len("https://"):]
|
||||
useTls = true
|
||||
}
|
||||
|
||||
var upstreamConn net.Conn
|
||||
var err error
|
||||
|
||||
if useTls {
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
upstreamConn, err = tls.Dial("tcp", fmt.Sprintf("%s:%d", addr, port), tlsConfig)
|
||||
} else {
|
||||
upstreamConn, err = net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
|
||||
defer upstreamConn.Close()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
|
||||
// Copy request to upstream
|
||||
go func() {
|
||||
_, err := io.Copy(upstreamConn, conn)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
|
||||
if c, ok := upstreamConn.(*net.TCPConn); ok {
|
||||
c.CloseWrite()
|
||||
} else if c, ok := upstreamConn.(*tls.Conn); ok {
|
||||
c.CloseWrite()
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
// Copy response to downstream
|
||||
go func() {
|
||||
_, err := io.Copy(conn, upstreamConn)
|
||||
//conn.(*net.TCPConn).CloseWrite()
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
// TODO: I added this to fix a bug where the copy to
|
||||
// upstreamConn was never closing, even though the copy to
|
||||
// conn was. It seems related to persistent connections going
|
||||
// idle and upstream closing the connection. I'm a bit worried
|
||||
// this might not be thread safe.
|
||||
conn.Close()
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func printJson(data interface{}) {
|
||||
d, _ := json.MarshalIndent(data, "", " ")
|
||||
fmt.Println(string(d))
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
<option value="client">Client HTTPS</option>
|
||||
<option value="server">Server HTTPS</option>
|
||||
<option value="client-tls">Client raw TLS</option>
|
||||
<option value="server-tls">Server raw TLS</option>
|
||||
<option value="passthrough">Passthrough</option>
|
||||
</select>
|
||||
</div>
|
||||
|
|
108
tls_proxy.go
Normal file
108
tls_proxy.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
package boringproxy
|
||||
|
||||
import (
|
||||
//"errors"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
)
|
||||
|
||||
func ProxyTcp(conn net.Conn, addr string, port int, useTls bool, certConfig *certmagic.Config) error {
|
||||
|
||||
if useTls {
|
||||
tlsConfig := &tls.Config{
|
||||
GetCertificate: certConfig.GetCertificate,
|
||||
}
|
||||
|
||||
tlsConfig.NextProtos = append([]string{"http/1.1", "h2", "acme-tls/1"}, tlsConfig.NextProtos...)
|
||||
|
||||
tlsConn := tls.Server(conn, tlsConfig)
|
||||
|
||||
tlsConn.Handshake()
|
||||
if tlsConn.ConnectionState().NegotiatedProtocol == "acme-tls/1" {
|
||||
tlsConn.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
go handleConnection(tlsConn, addr, port)
|
||||
} else {
|
||||
go handleConnection(conn, addr, port)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleConnection(conn net.Conn, upstreamAddr string, port int) {
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
useTls := false
|
||||
addr := upstreamAddr
|
||||
|
||||
if strings.HasPrefix(upstreamAddr, "https://") {
|
||||
addr = upstreamAddr[len("https://"):]
|
||||
useTls = true
|
||||
}
|
||||
|
||||
var upstreamConn net.Conn
|
||||
var err error
|
||||
|
||||
if useTls {
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
upstreamConn, err = tls.Dial("tcp", fmt.Sprintf("%s:%d", addr, port), tlsConfig)
|
||||
} else {
|
||||
upstreamConn, err = net.Dial("tcp", fmt.Sprintf("%s:%d", addr, port))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
|
||||
defer upstreamConn.Close()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
|
||||
// Copy request to upstream
|
||||
go func() {
|
||||
_, err := io.Copy(upstreamConn, conn)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
|
||||
if c, ok := upstreamConn.(*net.TCPConn); ok {
|
||||
c.CloseWrite()
|
||||
} else if c, ok := upstreamConn.(*tls.Conn); ok {
|
||||
c.CloseWrite()
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
// Copy response to downstream
|
||||
go func() {
|
||||
_, err := io.Copy(conn, upstreamConn)
|
||||
//conn.(*net.TCPConn).CloseWrite()
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
// TODO: I added this to fix a bug where the copy to
|
||||
// upstreamConn was never closing, even though the copy to
|
||||
// conn was. It seems related to persistent connections going
|
||||
// idle and upstream closing the connection. I'm a bit worried
|
||||
// this might not be thread safe.
|
||||
conn.Close()
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
}
|
|
@ -35,7 +35,7 @@ func NewTunnelManager(config *Config, db *Database, certConfig *certmagic.Config
|
|||
|
||||
if config.autoCerts {
|
||||
for domainName, tun := range db.GetTunnels() {
|
||||
if tun.TlsTermination == "server" {
|
||||
if tun.TlsTermination == "server" || tun.TlsTermination == "server-tls" {
|
||||
err = certConfig.ManageSync(context.Background(), []string{domainName})
|
||||
if err != nil {
|
||||
log.Println("CertMagic error at startup")
|
||||
|
@ -63,7 +63,7 @@ func (m *TunnelManager) RequestCreateTunnel(tunReq Tunnel) (Tunnel, error) {
|
|||
return Tunnel{}, errors.New("Owner required")
|
||||
}
|
||||
|
||||
if tunReq.TlsTermination == "server" {
|
||||
if tunReq.TlsTermination == "server" || tunReq.TlsTermination == "server-tls" {
|
||||
if m.config.autoCerts {
|
||||
err := m.certConfig.ManageSync(context.Background(), []string{tunReq.Domain})
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in New Issue
Block a user