From 0d52e31fe94aadb52d70abc396b78bb20c676c35 Mon Sep 17 00:00:00 2001 From: ieb <1@wa.vg> Date: Sat, 21 May 2022 18:24:35 +0800 Subject: [PATCH] add -login and -password server-side. --- README.md | 2 +- cert.go | 68 ++++++++++++++++++++++++++++++++++++++++ login.go | 54 ++++++++++++++++++++++++++++++++ main.go | 92 ++++++++----------------------------------------------- 4 files changed, 136 insertions(+), 80 deletions(-) create mode 100644 cert.go create mode 100644 login.go diff --git a/README.md b/README.md index 380846a..4d31701 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Self-hosted reverse-proxy for F1 web viewer, includes a web server at port 13331 ### Running Locally -Download binary from https://github.com/iebb/F1WebViewer-SelfHosted/releases, or build your own, or just `go run main.go` +Download binary from https://github.com/iebb/F1WebViewer-SelfHosted/releases, or build your own, or just `go run .` ### Running on a Server (Requires SSL to make DRM work): Encrypted Media requires secure context to work, which means https is required except localhost. diff --git a/cert.go b/cert.go new file mode 100644 index 0000000..750c977 --- /dev/null +++ b/cert.go @@ -0,0 +1,68 @@ +package main + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "math/big" + "net" + "sync" + "time" +) + +const RSABits = 2048 +const ValidFor = time.Hour * 1440 + +var certCache sync.Map + +func GetSelfSignedCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { + if cert, ok := certCache.Load(clientHello.ServerName); ok { + return cert.(*tls.Certificate), nil + } + priv, err := rsa.GenerateKey(rand.Reader, RSABits) + if err != nil { + return nil, err + } + + notBefore := time.Now() + notAfter := notBefore.Add(ValidFor) + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + return nil, err + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"Acme Co"}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + host := clientHello.ServerName + if ip := net.ParseIP(host); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, host) + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + if err != nil { + return nil, err + } + + cert := &tls.Certificate{ + Certificate: [][]byte{derBytes}, + PrivateKey: priv, + } + certCache.Store(clientHello.ServerName, cert) + return cert, err +} diff --git a/login.go b/login.go new file mode 100644 index 0000000..1b562e8 --- /dev/null +++ b/login.go @@ -0,0 +1,54 @@ +package main + +import ( + "bytes" + "encoding/json" + "io" + "net/http" +) + +type Crediential struct { + Login string `json:"Login"` + Password string `json:"Password"` +} + +const LoginUrl = "https://f1tokenx.deta.dev/authenticate" + +func handleLogin(w http.ResponseWriter, r *http.Request) { + var p Crediential + + if login != "" && password != "" { + p.Login = login + p.Password = password + } else { + err := json.NewDecoder(r.Body).Decode(&p) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + } + + jsonStr, _ := json.Marshal(p) + req, _ := http.NewRequest("POST", LoginUrl, bytes.NewBuffer(jsonStr)) + for k, vv := range r.Header { + for _, v := range vv { + req.Header.Add(k, v) + } + } + client := &http.Client{} + resp, _ := client.Do(req) + + for k, vv := range resp.Header { + for _, v := range vv { + w.Header().Add(k, v) + } + } + w.WriteHeader(resp.StatusCode) + _, _ = io.Copy(w, resp.Body) + _ = resp.Body.Close() +} + +func serverSideLoginRewriter(resp *http.Response) (err error) { + resp.Header.Add("Set-Cookie", "server_side_login=1; Max-Age=86400; Path=/") + return nil +} diff --git a/main.go b/main.go index 8e090ff..95aea51 100644 --- a/main.go +++ b/main.go @@ -1,86 +1,25 @@ package main import ( - "crypto/rand" - "crypto/rsa" "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" "flag" "fmt" "github.com/gorilla/mux" "github.com/pkg/browser" "golang.org/x/crypto/acme/autocert" "log" - "math/big" - "net" "net/http" "net/http/httputil" "net/url" "regexp" "strings" - "sync" - "time" ) var builtInSSL = false var selfSSL = false -var port = 13331 - -const RSABits = 2048 -const ValidFor = time.Hour * 1440 - -var certCache sync.Map - -func GetSelfSignedCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { - if cert, ok := certCache.Load(clientHello.ServerName); ok { - return cert.(*tls.Certificate), nil - } - priv, err := rsa.GenerateKey(rand.Reader, RSABits) - if err != nil { - return nil, err - } - - notBefore := time.Now() - notAfter := notBefore.Add(ValidFor) - - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - return nil, err - } - - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - Organization: []string{"Acme Co"}, - }, - NotBefore: notBefore, - NotAfter: notAfter, - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - } - - host := clientHello.ServerName - if ip := net.ParseIP(host); ip != nil { - template.IPAddresses = append(template.IPAddresses, ip) - } else { - template.DNSNames = append(template.DNSNames, host) - } - - derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) - if err != nil { - return nil, err - } - - cert := &tls.Certificate{ - Certificate: [][]byte{derBytes}, - PrivateKey: priv, - } - certCache.Store(clientHello.ServerName, cert) - return cert, err -} +var listenAddr = "localhost:13331" +var login = "" +var password = "" func init() { flag.BoolVar(&builtInSSL, "ssl", false, @@ -89,7 +28,9 @@ func init() { flag.BoolVar(&selfSSL, "self-sign", false, "self-signed ssl support. would display a warning on browsers", ) - flag.IntVar(&port, "port", 13331, "port number") + flag.StringVar(&listenAddr, "listen", "localhost:13331", "which IP and port to listen?") + flag.StringVar(&login, "login", "", "server-side login") + flag.StringVar(&password, "password", "", "server-side password") } func main() { @@ -126,31 +67,24 @@ func main() { } proxy.ServeHTTP(w, r) } - staticRemote, _ := url.Parse("https://f1vp.netlify.app/") + + staticRemote, _ := url.Parse("https://f1vp.netlify.app") staticProxy := httputil.NewSingleHostReverseProxy(staticRemote) + if login != "" && password != "" { + staticProxy.ModifyResponse = serverSideLoginRewriter + } staticHandler := func(w http.ResponseWriter, r *http.Request) { log.Printf("%s FE* - %s\n", r.RemoteAddr, r.URL.String()) r.Host = staticRemote.Host staticProxy.ServeHTTP(w, r) } - fnRemote, _ := url.Parse("https://fwv-us.deta.dev/") - fnProxy := httputil.NewSingleHostReverseProxy(fnRemote) - fnHandler := func(w http.ResponseWriter, r *http.Request) { - log.Printf("%s BE* - %s\n", r.RemoteAddr, r.URL.String()) - r.Host = fnRemote.Host - fnProxy.ServeHTTP(w, r) - } - r := mux.NewRouter() r.HandleFunc("/proxy/{url:https?://?.*}", handler) - r.PathPrefix("/authenticate").HandlerFunc(fnHandler) - r.PathPrefix("/66571939").HandlerFunc(fnHandler) + r.PathPrefix("/authenticate").HandlerFunc(handleLogin) r.PathPrefix("/").HandlerFunc(staticHandler) r.SkipClean(true) - listenAddr := fmt.Sprintf(":%d", port) - if builtInSSL { certManager := autocert.Manager{ Prompt: autocert.AcceptTOS, @@ -177,7 +111,7 @@ func main() { } log.Fatal(server.ListenAndServeTLS("", "")) } else { - _ = browser.OpenURL(fmt.Sprintf("http://localhost:%d/", port)) + _ = browser.OpenURL(fmt.Sprintf("http://%s/", listenAddr)) log.Println("Reverse Proxy Server running at " + listenAddr + "\n") log.Fatal(http.ListenAndServe(listenAddr, r)) }