11package main
22
33import (
4- "bufio"
54 "crypto/tls"
6- "fmt "
5+ "encoding/base64 "
76 "io"
87 "log"
98 "net"
109 "net/http"
1110 "net/netip"
1211 "os"
1312 "strings"
14- "sync"
1513 "time"
1614
17- "github.com/fsnotify/fsnotify "
15+ "github.com/google/uuid "
1816)
1917
2018func handleTunneling (w http.ResponseWriter , r * http.Request ) {
@@ -63,84 +61,25 @@ func copyHeader(dst, src http.Header) {
6361 }
6462}
6563
66- var (
67- whitelist sync.Map
68- whitelistName = "conf/whitelist"
69- whitelistPath = "conf"
70- )
71-
72- func listenWhitelist () {
73- log .Printf ("start watching %s..." , whitelistName )
74- watcher , err := fsnotify .NewWatcher ()
75- if err != nil {
76- log .Printf ("watch %s failed: %s" , whitelistName , err .Error ())
64+ func basicProxyAuth (proxyAuth string ) (username , password string , ok bool ) {
65+ if proxyAuth == "" {
7766 return
7867 }
79- defer watcher .Close ()
80- defer log .Printf ("watcher closed!!!" )
81-
82- go func () {
83- for {
84- select {
85- case event , ok := <- watcher .Events :
86- if ! ok {
87- return
88- }
89- if event .Has (fsnotify .Write ) && event .Name == whitelistName {
90- updateWhitelist ()
91- }
92- case err , ok := <- watcher .Errors :
93- if ! ok {
94- return
95- }
96- log .Println ("watcher error:" , err )
97- }
98- }
99- }()
10068
101- err = watcher .Add (whitelistPath )
102- if err != nil {
103- log .Fatalf ("watcher add %s failed: %s" , whitelistName , err )
69+ if ! strings .HasPrefix (proxyAuth , "Basic " ) {
70+ return
10471 }
105-
106- <- make (chan struct {})
107- }
108-
109- func updateWhitelist () {
110- log .Printf ("start loading %s..." , whitelistName )
111- defer log .Printf ("%s has been loaded." , whitelistName )
112- f , err := os .Open (whitelistName )
72+ c , err := base64 .StdEncoding .DecodeString (strings .TrimPrefix (proxyAuth , "Basic " ))
11373 if err != nil {
114- log . Fatalf ( "open file %s failed: %s" , whitelistName , err )
74+ return
11575 }
116- defer f .Close ()
117-
118- newWhitelist := make (map [string ]struct {}, 0 )
119-
120- scanner := bufio .NewScanner (f )
121- for i := 0 ; scanner .Scan (); i ++ {
122- line := strings .TrimSpace (scanner .Text ())
123- ipPrefix , err := netip .ParsePrefix (line )
124- if err != nil {
125- log .Printf ("line %d: \" %s\" parse failed: %s" , i , line , err .Error ())
126- }
127- newWhitelist [line ] = struct {}{}
128- whitelist .Store (line , ipPrefix )
76+ cs := string (c )
77+ s := strings .IndexByte (cs , ':' )
78+ if s < 0 {
79+ return
12980 }
13081
131- whitelist .Range (func (key , value any ) bool {
132- if _ , ok := newWhitelist [key .(string )]; ! ok {
133- whitelist .Delete (key )
134- }
135- return true
136- })
137- }
138-
139- func init () {
140-
141- updateWhitelist ()
142-
143- go listenWhitelist ()
82+ return cs [:s ], cs [s + 1 :], true
14483}
14584
14685func handler (w http.ResponseWriter , r * http.Request ) {
@@ -153,19 +92,19 @@ func handler(w http.ResponseWriter, r *http.Request) {
15392
15493 log .Printf ("%-15s %-7s %s %s" , addrPort .Addr (), r .Method , r .Host , r .URL .Path )
15594
156- ok := true
157- whitelist .Range (func (k , v any ) bool {
158- ok = ! ok
159- if ok = v .(netip.Prefix ).Contains (addrPort .Addr ()); ok {
160- return false
95+ if auth == authOn {
96+ _ , p , ok := basicProxyAuth (r .Header .Get ("Proxy-Authorization" ))
97+ if ! ok {
98+ w .Header ().Set ("Proxy-Authenticate" , `Basic realm=go` )
99+ http .Error (w , "proxy auth required" , http .StatusProxyAuthRequired )
100+ return
161101 }
162- return true
163- })
164102
165- if ! ok {
166- log .Printf ("ip \" %s\" not configed in IPWhitelist" , addrPort .Addr ())
167- http .Error (w , fmt .Sprintf ("ip \" %s\" not configed in IPWhitelist" , addrPort .Addr ()), http .StatusForbidden )
168- return
103+ if p != pass {
104+ http .Error (w , "proxy authentication failed" , http .StatusForbidden )
105+ return
106+ }
107+ r .Header .Del ("Proxy-Authorization" )
169108 }
170109
171110 if r .Method == http .MethodConnect {
@@ -175,16 +114,45 @@ func handler(w http.ResponseWriter, r *http.Request) {
175114 }
176115}
177116
178- func main () {
179- httpProxyListenAddr := ":8888"
180- s , b := os .LookupEnv ("HTTP_PROXY_LISTEN_ADDR" )
117+ var (
118+ addr = ":38888"
119+ auth = "on"
120+ pass = ""
121+ )
122+
123+ const (
124+ authOn = "on"
125+ authOff = "off"
126+ )
127+
128+ func init () {
129+ addrEnv , b := os .LookupEnv ("HTTP_PROXY_ADDR" )
130+ if b {
131+ addr = addrEnv
132+ }
133+ authEnv , b := os .LookupEnv ("HTTP_PROXY_AUTH" )
181134 if b {
182- httpProxyListenAddr = s
135+ auth = authEnv
136+ }
137+ if auth == authOn {
138+ passEnv , b := os .LookupEnv ("HTTP_PROXY_PASS" )
139+ if b {
140+ pass = passEnv
141+ } else {
142+ pass = uuid .New ().String ()
143+ }
144+ }
145+ }
146+
147+ func main () {
148+ log .Printf ("Listen on: %s\n " , addr )
149+ log .Printf ("Auth: %s\n " , auth )
150+ if auth == authOn {
151+ log .Printf ("Password: %s\n " , pass )
183152 }
184153
185- log .Printf ("Listen on %s" , httpProxyListenAddr )
186154 server := & http.Server {
187- Addr : httpProxyListenAddr ,
155+ Addr : addr ,
188156 Handler : http .HandlerFunc (handler ),
189157 // Disable HTTP/2.
190158 TLSNextProto : make (map [string ]func (* http.Server , * tls.Conn , http.Handler )),
0 commit comments