Files

215 lines
4.8 KiB
Go
Raw Permalink Normal View History

2020-12-07 21:41:45 -07:00
package boringproxy
import (
2020-10-27 15:47:17 -06:00
"bufio"
2020-09-29 20:12:54 -06:00
"crypto/tls"
2020-10-27 15:47:17 -06:00
"flag"
2020-09-29 20:12:54 -06:00
"fmt"
2020-10-02 17:09:14 -06:00
"github.com/caddyserver/certmagic"
2020-09-29 20:12:54 -06:00
"io"
"log"
2020-11-26 22:37:51 -07:00
"net"
"net/http"
2020-10-27 15:47:17 -06:00
"os"
2020-10-18 19:29:57 -06:00
"strings"
2020-11-26 22:37:51 -07:00
"sync"
2020-10-18 19:29:57 -06:00
"time"
)
2020-12-07 21:50:33 -07:00
type Config struct {
WebUiDomain string `json:"webui_domain"`
SshServerPort int `json:"ssh_server_port"`
2020-09-28 14:07:54 -06:00
}
type SmtpConfig struct {
2020-09-29 20:12:54 -06:00
Server string
Port int
Username string
Password string
2020-09-28 14:07:54 -06:00
}
2020-12-07 21:50:33 -07:00
type Server struct {
2020-11-26 22:37:51 -07:00
db *Database
tunMan *TunnelManager
httpClient *http.Client
httpListener *PassthroughListener
}
2020-10-05 18:12:31 -06:00
func Listen() {
2020-10-27 15:47:17 -06:00
flagSet := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
adminDomain := flagSet.String("admin-domain", "", "Admin Domain")
sshServerPort := flagSet.Int("ssh-server-port", 22, "SSH Server Port")
2020-11-27 21:20:38 -07:00
certDir := flagSet.String("cert-dir", "", "TLS cert directory")
2020-10-27 15:47:17 -06:00
flagSet.Parse(os.Args[2:])
2020-10-27 15:47:17 -06:00
webUiDomain := *adminDomain
2020-09-28 14:07:54 -06:00
2020-10-27 15:47:17 -06:00
if *adminDomain == "" {
reader := bufio.NewReader(os.Stdin)
log.Print("Enter Admin Domain: ")
text, _ := reader.ReadString('\n')
webUiDomain = strings.TrimSpace(text)
2020-09-29 20:12:54 -06:00
}
2020-09-28 14:07:54 -06:00
2020-12-07 21:50:33 -07:00
config := &Config{
WebUiDomain: webUiDomain,
SshServerPort: *sshServerPort,
}
2020-10-27 15:47:17 -06:00
certmagic.DefaultACME.DisableHTTPChallenge = true
2020-11-27 21:20:38 -07:00
if *certDir != "" {
certmagic.Default.Storage = &certmagic.FileStorage{*certDir}
}
2020-10-27 15:47:17 -06:00
//certmagic.DefaultACME.DisableTLSALPNChallenge = true
//certmagic.DefaultACME.CA = certmagic.LetsEncryptStagingCA
certConfig := certmagic.NewDefault()
err := certConfig.ManageSync([]string{config.WebUiDomain})
2020-09-29 20:12:54 -06:00
if err != nil {
2020-10-27 15:47:17 -06:00
log.Fatal(err)
2020-09-29 20:12:54 -06:00
}
2020-09-28 14:07:54 -06:00
2020-10-27 15:47:17 -06:00
log.Print("Successfully acquired admin certificate")
2020-10-05 23:25:36 -06:00
db, err := NewDatabase()
if err != nil {
log.Fatal(err)
}
users := db.GetUsers()
if len(users) == 0 {
db.AddUser("admin", true)
token, err := db.AddToken("admin")
if err != nil {
log.Fatal("Failed to initialize admin user")
}
log.Println("Admin token: " + token)
2020-10-29 18:49:59 -06:00
log.Println(fmt.Sprintf("Admin login link: https://%s/login?access_token=%s", webUiDomain, token))
}
2020-10-08 12:51:52 -06:00
tunMan := NewTunnelManager(config, db, certConfig)
2020-10-27 15:47:17 -06:00
auth := NewAuth(db)
2020-09-27 22:24:03 -06:00
2020-10-14 09:17:04 -06:00
api := NewApi(config, db, auth, tunMan)
2020-09-27 22:24:03 -06:00
2020-10-14 09:17:04 -06:00
webUiHandler := NewWebUiHandler(config, db, api, auth, tunMan)
2020-10-29 13:03:35 -06:00
httpClient := &http.Client{
// Don't follow redirects
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
2020-10-06 10:22:03 -06:00
2020-11-26 22:37:51 -07:00
httpListener := NewPassthroughListener()
2020-12-07 21:50:33 -07:00
p := &Server{db, tunMan, httpClient, httpListener}
2020-10-02 16:57:09 -06:00
2020-10-05 23:25:36 -06:00
tlsConfig := &tls.Config{
2020-10-05 18:12:31 -06:00
GetCertificate: certConfig.GetCertificate,
2020-10-07 10:44:37 -06:00
NextProtos: []string{"h2", "acme-tls/1"},
2020-10-05 23:25:36 -06:00
}
2020-11-26 22:37:51 -07:00
tlsListener := tls.NewListener(httpListener, tlsConfig)
2020-09-29 20:12:54 -06:00
2020-10-05 18:12:31 -06:00
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
2020-10-18 19:29:57 -06:00
timestamp := time.Now().Format(time.RFC3339)
srcIp := strings.Split(r.RemoteAddr, ":")[0]
fmt.Println(fmt.Sprintf("%s %s %s %s %s", timestamp, srcIp, r.Method, r.Host, r.URL.Path))
if r.Host == config.WebUiDomain {
2020-11-26 19:51:50 -07:00
if strings.HasPrefix(r.URL.Path, "/api/") {
http.StripPrefix("/api", api).ServeHTTP(w, r)
} else {
webUiHandler.handleWebUiRequest(w, r)
}
2020-10-05 23:25:36 -06:00
} else {
2020-11-27 15:36:07 -07:00
tunnel, exists := db.GetTunnel(r.Host)
if !exists {
errMessage := fmt.Sprintf("No tunnel attached to %s", r.Host)
w.WriteHeader(500)
io.WriteString(w, errMessage)
return
}
proxyRequest(w, r, tunnel, httpClient, tunnel.TunnelPort)
2020-10-05 23:25:36 -06:00
}
2020-10-05 18:12:31 -06:00
})
// taken from: https://stackoverflow.com/a/37537134/943814
go func() {
if err := http.ListenAndServe(":80", http.HandlerFunc(redirectTLS)); err != nil {
log.Fatalf("ListenAndServe error: %v", err)
}
}()
2020-10-07 10:44:37 -06:00
2020-11-26 22:37:51 -07:00
go http.Serve(tlsListener, nil)
listener, err := net.Listen("tcp", ":443")
if err != nil {
log.Fatal(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
continue
}
go p.handleConnection(conn)
}
}
2020-12-07 21:50:33 -07:00
func (p *Server) handleConnection(clientConn net.Conn) {
2020-11-26 22:37:51 -07:00
clientHello, clientReader, err := peekClientHello(clientConn)
if err != nil {
log.Println("peekClientHello error", err)
return
}
passConn := NewProxyConn(clientConn, clientReader)
tunnel, exists := p.db.GetTunnel(clientHello.ServerName)
2020-11-27 15:36:07 -07:00
if exists && (tunnel.TlsTermination == "client" || tunnel.TlsTermination == "passthrough") {
2020-11-26 22:37:51 -07:00
p.passthroughRequest(passConn, tunnel)
} else {
p.httpListener.PassConn(passConn)
}
}
2020-12-07 21:50:33 -07:00
func (p *Server) passthroughRequest(conn net.Conn, tunnel Tunnel) {
2020-11-26 22:37:51 -07:00
upstreamAddr := fmt.Sprintf("localhost:%d", tunnel.TunnelPort)
upstreamConn, err := net.Dial("tcp", upstreamAddr)
if err != nil {
log.Print(err)
return
}
defer upstreamConn.Close()
var wg sync.WaitGroup
wg.Add(2)
go func() {
io.Copy(conn, upstreamConn)
conn.(*ProxyConn).CloseWrite()
wg.Done()
}()
go func() {
io.Copy(upstreamConn, conn)
upstreamConn.(*net.TCPConn).CloseWrite()
wg.Done()
}()
wg.Wait()
2020-10-05 18:12:31 -06:00
}
2020-10-07 10:44:37 -06:00
func redirectTLS(w http.ResponseWriter, r *http.Request) {
url := fmt.Sprintf("https://%s:443%s", r.Host, r.RequestURI)
http.Redirect(w, r, url, http.StatusMovedPermanently)
2020-10-07 10:44:37 -06:00
}