mirror of
https://github.com/boringproxy/boringproxy.git
synced 2025-02-25 18:55:29 -06:00
Merge branch 'boringproxy:master' into update-upstream
This commit is contained in:
commit
a7fccf99ff
20
api.go
20
api.go
@ -374,10 +374,26 @@ func (a *Api) CreateTunnel(tokenData TokenData, params url.Values) (*Tunnel, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
tlsTerm := params.Get("tls-termination")
|
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")
|
return nil, errors.New("Invalid tls-termination parameter")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sshServerAddr := a.db.GetAdminDomain()
|
||||||
|
sshServerAddrParam := params.Get("ssh-server-addr")
|
||||||
|
if sshServerAddrParam != "" {
|
||||||
|
sshServerAddr = sshServerAddrParam
|
||||||
|
}
|
||||||
|
|
||||||
|
sshServerPort := a.config.SshServerPort
|
||||||
|
sshServerPortParam := params.Get("ssh-server-port")
|
||||||
|
if sshServerPortParam != "" {
|
||||||
|
var err error
|
||||||
|
sshServerPort, err = strconv.Atoi(sshServerPortParam)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("Invalid ssh-server-port parameter")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
request := Tunnel{
|
request := Tunnel{
|
||||||
Domain: domain,
|
Domain: domain,
|
||||||
Owner: owner,
|
Owner: owner,
|
||||||
@ -389,6 +405,8 @@ func (a *Api) CreateTunnel(tokenData TokenData, params url.Values) (*Tunnel, err
|
|||||||
AuthUsername: username,
|
AuthUsername: username,
|
||||||
AuthPassword: password,
|
AuthPassword: password,
|
||||||
TlsTermination: tlsTerm,
|
TlsTermination: tlsTerm,
|
||||||
|
ServerAddress: sshServerAddr,
|
||||||
|
ServerPort: sshServerPort,
|
||||||
}
|
}
|
||||||
|
|
||||||
tunnel, err := a.tunMan.RequestCreateTunnel(request)
|
tunnel, err := a.tunMan.RequestCreateTunnel(request)
|
||||||
|
@ -338,11 +338,11 @@ func Listen() {
|
|||||||
continue
|
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)
|
clientHello, clientReader, err := peekClientHello(clientConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -356,6 +356,13 @@ func (p *Server) handleConnection(clientConn net.Conn) {
|
|||||||
|
|
||||||
if exists && (tunnel.TlsTermination == "client" || tunnel.TlsTermination == "passthrough") || tunnel.TlsTermination == "client-tls" {
|
if exists && (tunnel.TlsTermination == "client" || tunnel.TlsTermination == "passthrough") || tunnel.TlsTermination == "client-tls" {
|
||||||
p.passthroughRequest(passConn, tunnel)
|
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 {
|
} else {
|
||||||
p.httpListener.PassConn(passConn)
|
p.httpListener.PassConn(passConn)
|
||||||
}
|
}
|
||||||
|
98
client.go
98
client.go
@ -6,12 +6,10 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -321,18 +319,6 @@ func (c *Client) BoreTunnel(ctx context.Context, tunnel Tunnel) error {
|
|||||||
|
|
||||||
} else {
|
} 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() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
conn, err := listener.Accept()
|
conn, err := listener.Accept()
|
||||||
@ -346,17 +332,14 @@ func (c *Client) BoreTunnel(ctx context.Context, tunnel Tunnel) error {
|
|||||||
//continue
|
//continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// If ALPN type is acme-tls/1, certmagic will do its thing under the hood, and the
|
var useTls bool
|
||||||
// connection should not be used.
|
if tunnel.TlsTermination == "client-tls" {
|
||||||
if tlsConn, ok := conn.(*tls.Conn); ok {
|
useTls = true
|
||||||
tlsConn.Handshake()
|
} else {
|
||||||
if tlsConn.ConnectionState().NegotiatedProtocol == "acme-tls/1" {
|
useTls = false
|
||||||
tlsConn.Close()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
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{}) {
|
func printJson(data interface{}) {
|
||||||
d, _ := json.MarshalIndent(data, "", " ")
|
d, _ := json.MarshalIndent(data, "", " ")
|
||||||
fmt.Println(string(d))
|
fmt.Println(string(d))
|
||||||
|
@ -2,9 +2,13 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/boringproxy/boringproxy"
|
"github.com/boringproxy/boringproxy"
|
||||||
)
|
)
|
||||||
@ -15,6 +19,7 @@ Commands:
|
|||||||
version Prints version information.
|
version Prints version information.
|
||||||
server Start a new server.
|
server Start a new server.
|
||||||
client Connect to a server.
|
client Connect to a server.
|
||||||
|
tuntls Tunnel a raw TLS connection.
|
||||||
|
|
||||||
Use "%[1]s command -h" for a list of flags for the command.
|
Use "%[1]s command -h" for a list of flags for the command.
|
||||||
`
|
`
|
||||||
@ -25,7 +30,6 @@ func fail(msg string) {
|
|||||||
fmt.Fprintln(os.Stderr, msg)
|
fmt.Fprintln(os.Stderr, msg)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) < 2 {
|
if len(os.Args) < 2 {
|
||||||
fmt.Fprintln(os.Stderr, os.Args[0]+": Need a command")
|
fmt.Fprintln(os.Stderr, os.Args[0]+": Need a command")
|
||||||
@ -40,6 +44,44 @@ func main() {
|
|||||||
fmt.Println(Version)
|
fmt.Println(Version)
|
||||||
case "help", "-h", "--help", "-help":
|
case "help", "-h", "--help", "-help":
|
||||||
fmt.Printf(usage, os.Args[0])
|
fmt.Printf(usage, os.Args[0])
|
||||||
|
case "tuntls":
|
||||||
|
// This command is a direct port of https://github.com/anderspitman/tuntls
|
||||||
|
flagSet := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||||
|
server := flagSet.String("server", "", "boringproxy server")
|
||||||
|
port := flagSet.Int("port", 0, "Local port to bind to")
|
||||||
|
err := flagSet.Parse(os.Args[2:])
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: parsing flags: %s\n", os.Args[0], err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *server == "" {
|
||||||
|
fmt.Fprintf(os.Stderr, "server argument is required\n")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *port == 0 {
|
||||||
|
// one-time tunnel over stdin/stdout
|
||||||
|
doTlsTunnel(*server, os.Stdin, os.Stdout)
|
||||||
|
} else {
|
||||||
|
// listen on a port and create tunnels for each connection
|
||||||
|
fmt.Fprintf(os.Stderr, "Listening on port %d\n", *port)
|
||||||
|
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
go doTlsTunnel(*server, conn, conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
case "server":
|
case "server":
|
||||||
boringproxy.Listen()
|
boringproxy.Listen()
|
||||||
case "client":
|
case "client":
|
||||||
@ -97,3 +139,32 @@ func main() {
|
|||||||
fail(os.Args[0] + ": Invalid command " + command)
|
fail(os.Args[0] + ": Invalid command " + command)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func doTlsTunnel(server string, in io.Reader, out io.Writer) {
|
||||||
|
fmt.Fprintf(os.Stderr, "tuntls connecting to server: %s\n", server)
|
||||||
|
|
||||||
|
conn, err := tls.Dial("tcp", fmt.Sprintf("%s:443", server), &tls.Config{
|
||||||
|
//RootCAs: roots,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to connect: "+err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
io.Copy(conn, in)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
io.Copy(out, conn)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
<option value="client">Client HTTPS</option>
|
<option value="client">Client HTTPS</option>
|
||||||
<option value="server">Server HTTPS</option>
|
<option value="server">Server HTTPS</option>
|
||||||
<option value="client-tls">Client raw TLS</option>
|
<option value="client-tls">Client raw TLS</option>
|
||||||
|
<option value="server-tls">Server raw TLS</option>
|
||||||
<option value="passthrough">Passthrough</option>
|
<option value="passthrough">Passthrough</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -58,6 +59,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class='input'>
|
||||||
|
<label for="ssh-server-addr">Override SSH Server Address:</label>
|
||||||
|
<input type="text" id="ssh-server-addr" name="ssh-server-addr">
|
||||||
|
</div>
|
||||||
|
<div class='input'>
|
||||||
|
<label for="ssh-server-port">Override SSH Server Port:</label>
|
||||||
|
<input type="text" id="ssh-server-port" name="ssh-server-port">
|
||||||
|
</div>
|
||||||
|
|
||||||
<button class='button' type="submit">Submit</button>
|
<button class='button' type="submit">Submit</button>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
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 {
|
if config.autoCerts {
|
||||||
for domainName, tun := range db.GetTunnels() {
|
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})
|
err = certConfig.ManageSync(context.Background(), []string{domainName})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("CertMagic error at startup")
|
log.Println("CertMagic error at startup")
|
||||||
@ -63,7 +63,7 @@ func (m *TunnelManager) RequestCreateTunnel(tunReq Tunnel) (Tunnel, error) {
|
|||||||
return Tunnel{}, errors.New("Owner required")
|
return Tunnel{}, errors.New("Owner required")
|
||||||
}
|
}
|
||||||
|
|
||||||
if tunReq.TlsTermination == "server" {
|
if tunReq.TlsTermination == "server" || tunReq.TlsTermination == "server-tls" {
|
||||||
if m.config.autoCerts {
|
if m.config.autoCerts {
|
||||||
err := m.certConfig.ManageSync(context.Background(), []string{tunReq.Domain})
|
err := m.certConfig.ManageSync(context.Background(), []string{tunReq.Domain})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -98,8 +98,6 @@ func (m *TunnelManager) RequestCreateTunnel(tunReq Tunnel) (Tunnel, error) {
|
|||||||
return Tunnel{}, err
|
return Tunnel{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tunReq.ServerAddress = m.db.GetAdminDomain()
|
|
||||||
tunReq.ServerPort = m.config.SshServerPort
|
|
||||||
tunReq.ServerPublicKey = ""
|
tunReq.ServerPublicKey = ""
|
||||||
tunReq.Username = m.user.Username
|
tunReq.Username = m.user.Username
|
||||||
tunReq.TunnelPrivateKey = privKey
|
tunReq.TunnelPrivateKey = privKey
|
||||||
|
Loading…
Reference in New Issue
Block a user