commit 5140f1f399d22736db31c83c5d5330e87fad5472 Author: Alexander "PapaTutuWawa Date: Wed Sep 15 15:25:23 2021 +0200 Initial commit diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..648e8f1 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module papatutuwawa/miniproxy + +go 1.16 + +require ( + github.com/BurntSushi/toml v0.4.1 // indirect + github.com/gorilla/mux v1.8.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7937496 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= +github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= diff --git a/main.go b/main.go new file mode 100644 index 0000000..5f4a383 --- /dev/null +++ b/main.go @@ -0,0 +1,102 @@ +package main + +import ( + "net/http" + "net/url" + "log" + "os" + "strconv" + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "io" + "github.com/gorilla/mux" + "github.com/BurntSushi/toml" +) + +type tomlConfig struct { + Port int `toml:"port"` + Secret string `toml:"secret"` +} + +func proxyRequest(w http.ResponseWriter, r *http.Request, secret []byte) { + vars := mux.Vars(r) + requestUrl, err := url.QueryUnescape(vars["url"]) + if err != nil { + log.Printf("Failed to url decode url: %s", err) + return + } + macRaw, err := url.QueryUnescape(vars["hmac"]) + if err != nil { + log.Printf("Failed to url decode HMAC: %s", err) + return + } + + mac := hmac.New(sha256.New, secret) + mac.Write([]byte(requestUrl)) + + expectedMac := mac.Sum(nil) + receivedMac, err := base64.StdEncoding.DecodeString(macRaw) + if err != nil { + log.Printf("Failed to decode HMAC: %s", err) + return + } + + if !hmac.Equal(expectedMac, receivedMac) { + log.Printf("invalid-hmac:%s", r.RemoteAddr) + log.Printf("URL: '%s'", requestUrl) + http.Error(w, "Invalid HMAC", http.StatusUnauthorized) + return + } + + client := &http.Client{} + resp, err := client.Get(requestUrl) + defer resp.Body.Close() + + if err != nil { + log.Fatalf("Failed to perform GET: %s", err) + } + + w.WriteHeader(resp.StatusCode) + io.Copy(w, resp.Body) +} + +func makeProxyRequestHandler(secret []byte) http.HandlerFunc { + fn := func(w http.ResponseWriter, r *http.Request) { + proxyRequest(w, r, secret) + } + + return http.HandlerFunc(fn) +} + +func exists(path string) bool { + // Returns true if path exists. false otherwise + _, err := os.Stat(path) + return !os.IsNotExist(err) +} + +func main() { + config := tomlConfig { + Port: 8080, + } + + switch { + case exists("./config.toml"): + _, err := toml.DecodeFile("./config.toml", &config) + if err != nil { + log.Fatalf("Failed to read ./config.toml: %s", err) + } + case exists("/etc/miniproxy/config.toml"): + _, err := toml.DecodeFile("/etc/miniproxy/config.toml", &config) + if err != nil { + log.Fatalf("Failed to read /etc/miniproxy/config.toml: %s", err) + } + } + + log.Printf("Running on :%d\n", config.Port) + + r := mux.NewRouter() + r.UseEncodedPath() + r.HandleFunc("/proxy/{hmac}/{url:.+}", makeProxyRequestHandler([]byte(config.Secret))) + log.Fatal(http.ListenAndServe(":" + strconv.Itoa(config.Port), r)) +}