hvanu commited on
Commit
9ef2fb2
·
1 Parent(s): 8549d8b
Files changed (4) hide show
  1. Dockerfile +1 -1
  2. go.mod +34 -0
  3. go.sum +82 -0
  4. main.go +143 -4
Dockerfile CHANGED
@@ -2,7 +2,7 @@
2
  FROM golang:1.23-alpine AS builder
3
 
4
  WORKDIR /home/appuser
5
- COPY go.mod .
6
  RUN go mod download
7
  COPY main.go .
8
 
 
2
  FROM golang:1.23-alpine AS builder
3
 
4
  WORKDIR /home/appuser
5
+ COPY go.mod go.sum .
6
  RUN go mod download
7
  COPY main.go .
8
 
go.mod CHANGED
@@ -1,3 +1,37 @@
1
  module example.com/m/v2
2
 
3
  go 1.23.5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  module example.com/m/v2
2
 
3
  go 1.23.5
4
+
5
+ require (
6
+ github.com/gin-contrib/cors v1.7.5
7
+ github.com/gin-gonic/gin v1.10.0
8
+ )
9
+
10
+ require (
11
+ github.com/bytedance/sonic v1.13.2 // indirect
12
+ github.com/bytedance/sonic/loader v0.2.4 // indirect
13
+ github.com/cloudwego/base64x v0.1.5 // indirect
14
+ github.com/gabriel-vasile/mimetype v1.4.8 // indirect
15
+ github.com/gin-contrib/sse v1.0.0 // indirect
16
+ github.com/go-playground/locales v0.14.1 // indirect
17
+ github.com/go-playground/universal-translator v0.18.1 // indirect
18
+ github.com/go-playground/validator/v10 v10.26.0 // indirect
19
+ github.com/goccy/go-json v0.10.5 // indirect
20
+ github.com/json-iterator/go v1.1.12 // indirect
21
+ github.com/klauspost/cpuid/v2 v2.2.10 // indirect
22
+ github.com/kr/text v0.2.0 // indirect
23
+ github.com/leodido/go-urn v1.4.0 // indirect
24
+ github.com/mattn/go-isatty v0.0.20 // indirect
25
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
26
+ github.com/modern-go/reflect2 v1.0.2 // indirect
27
+ github.com/pelletier/go-toml/v2 v2.2.3 // indirect
28
+ github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
29
+ github.com/ugorji/go/codec v1.2.12 // indirect
30
+ golang.org/x/arch v0.15.0 // indirect
31
+ golang.org/x/crypto v0.36.0 // indirect
32
+ golang.org/x/net v0.38.0 // indirect
33
+ golang.org/x/sys v0.31.0 // indirect
34
+ golang.org/x/text v0.23.0 // indirect
35
+ google.golang.org/protobuf v1.36.6 // indirect
36
+ gopkg.in/yaml.v3 v3.0.1 // indirect
37
+ )
go.sum ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
2
+ github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
3
+ github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
4
+ github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
5
+ github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
6
+ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
7
+ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8
+ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
9
+ github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
10
+ github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
11
+ github.com/gin-contrib/cors v1.7.5 h1:cXC9SmofOrRg0w9PigwGlHG3ztswH6bqq4vJVXnvYMk=
12
+ github.com/gin-contrib/cors v1.7.5/go.mod h1:4q3yi7xBEDDWKapjT2o1V7mScKDDr8k+jZ0fSquGoy0=
13
+ github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
14
+ github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
15
+ github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
16
+ github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
17
+ github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
18
+ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
19
+ github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
20
+ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
21
+ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
22
+ github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
23
+ github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
24
+ github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
25
+ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
26
+ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
27
+ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
28
+ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
29
+ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
30
+ github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
31
+ github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
32
+ github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
33
+ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
34
+ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
35
+ github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
36
+ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
37
+ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
38
+ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
39
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
40
+ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
41
+ github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
42
+ github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
43
+ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
44
+ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
45
+ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
46
+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
47
+ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
48
+ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
49
+ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
50
+ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
51
+ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
52
+ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
53
+ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
54
+ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
55
+ github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
56
+ github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
57
+ github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
58
+ github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
59
+ golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
60
+ golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
61
+ golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
62
+ golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
63
+ golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
64
+ golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
65
+ golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
66
+ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
67
+ golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
68
+ golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
69
+ golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
70
+ golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
71
+ golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
72
+ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
73
+ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
74
+ google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
75
+ google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
76
+ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
77
+ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
78
+ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
79
+ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
80
+ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
81
+ nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
82
+ rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
main.go CHANGED
@@ -1,10 +1,149 @@
1
- // print hello world
2
  package main
3
 
4
  import (
5
- "fmt"
 
 
 
 
 
 
 
 
 
6
  )
7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  func main() {
9
- fmt.Println("Hello, World!")
10
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  package main
2
 
3
  import (
4
+ "io"
5
+ "log"
6
+ "net/http"
7
+ "net/url"
8
+
9
+ "strings"
10
+ "time"
11
+
12
+ "github.com/gin-contrib/cors"
13
+ "github.com/gin-gonic/gin"
14
  )
15
 
16
+ // SecureHeaders sets security-related HTTP headers
17
+ func SecureHeaders() gin.HandlerFunc {
18
+ return func(c *gin.Context) {
19
+ c.Writer.Header().Set("X-Content-Type-Options", "nosniff")
20
+ c.Next()
21
+ }
22
+ }
23
+
24
+ // ValidReqPath checks the X-Req-Path is safe for proxying
25
+ func ValidReqPath(path string) bool {
26
+ if !strings.HasPrefix(path, "https://") {
27
+ return false
28
+ }
29
+ if strings.Contains(path, "..") {
30
+ return false
31
+ }
32
+ if strings.TrimSpace(path) == "" {
33
+ return false
34
+ }
35
+ return true
36
+ }
37
+
38
+ // Extract the root domain (e.g., "example.com" from "sub.example.com")
39
+ func extractRootDomain(urlStr string) (string, error) {
40
+ parsedURL, err := url.Parse(urlStr)
41
+ if err != nil {
42
+ return "", err
43
+ }
44
+
45
+ hostname := parsedURL.Hostname()
46
+ parts := strings.Split(hostname, ".")
47
+
48
+ // Handle special cases with fewer than 2 parts
49
+ if len(parts) < 2 {
50
+ return hostname, nil
51
+ }
52
+
53
+ // Get the last two parts as the root domain
54
+ domain := parts[len(parts)-2] + "." + parts[len(parts)-1]
55
+ return domain, nil
56
+ }
57
+
58
+ // IsValidBackendURL validates that the constructed URL belongs to the expected backend
59
+ func IsValidBackendURL(targetURL, backend string) bool {
60
+ targetApexDomain, err := extractRootDomain(targetURL)
61
+ if err != nil {
62
+ return false
63
+ }
64
+
65
+ backendApexDomain, err := extractRootDomain(targetURL)
66
+ if err != nil {
67
+ return false
68
+ }
69
+
70
+ return targetApexDomain == backendApexDomain
71
+ }
72
+
73
  func main() {
74
+ r := gin.Default()
75
+ r.Use(SecureHeaders())
76
+
77
+ // CORS config for /api/req
78
+ api := r.Group("/api")
79
+ api.Use(cors.New(cors.Config{
80
+ AllowOrigins: []string{"*"},
81
+ AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
82
+ AllowHeaders: []string{"Origin", "Content-Type", "Authorization", "X-Req-Path"},
83
+ ExposeHeaders: []string{"Content-Length"},
84
+ AllowCredentials: true,
85
+ MaxAge: 12 * time.Hour,
86
+ }))
87
+
88
+ // Secure proxy forward
89
+ api.Any("/req", func(c *gin.Context) {
90
+ const backend = "https://deeploy.ml"
91
+
92
+ reqPath := c.GetHeader("X-Req-Path")
93
+ if !ValidReqPath(reqPath) {
94
+ c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid X-Req-Path"})
95
+ return
96
+ }
97
+
98
+ targetURL := reqPath
99
+ if !IsValidBackendURL(targetURL, backend) {
100
+ c.JSON(http.StatusForbidden, gin.H{"error": "Target not allowed"})
101
+ return
102
+ }
103
+
104
+ // Prepare the proxied request
105
+ req, err := http.NewRequest(c.Request.Method, targetURL, c.Request.Body)
106
+ if err != nil {
107
+ c.JSON(http.StatusInternalServerError, gin.H{"error": "Request creation failed"})
108
+ return
109
+ }
110
+ req.Header = c.Request.Header.Clone()
111
+ req.Header.Del("X-Req-Path") // Don't forward custom header
112
+
113
+ client := &http.Client{
114
+ Timeout: 60 * time.Second,
115
+ }
116
+ resp, err := client.Do(req)
117
+ if err != nil {
118
+ log.Printf("Error proxying request: Method=%s URL=%s Headers=%v Error=%v",
119
+ req.Method, req.URL, req.Header, err)
120
+ c.JSON(http.StatusBadGateway, gin.H{"error": "Backend request failed."})
121
+ return
122
+ }
123
+
124
+ defer resp.Body.Close()
125
+
126
+ // Copy headers & status from the backend
127
+ for k, v := range resp.Header {
128
+ for _, vv := range v {
129
+ c.Writer.Header().Add(k, vv)
130
+ }
131
+ }
132
+ c.Status(resp.StatusCode)
133
+ io.Copy(c.Writer, resp.Body)
134
+ })
135
+
136
+ // Serve static files from ./frontend, no directory listing
137
+ r.StaticFS("/app", gin.Dir("./frontend/", false))
138
+
139
+ // Route to serve parsed HTML template partials
140
+ r.GET("/partials", func(c *gin.Context) {
141
+ // Load HTML templates from ./partials folder
142
+ r.LoadHTMLGlob("./frontend/partials/*")
143
+ // Render the template by name
144
+ c.HTML(http.StatusOK, "index_partial.html", gin.H{})
145
+ })
146
+
147
+ log.Println("Serving on http://localhost:8080")
148
+ r.Run(":8080")
149
+ }