From cebb518e058e73d48904a2a50dd58d50de7ecc5a Mon Sep 17 00:00:00 2001 From: Bel LaPointe Date: Sun, 21 Mar 2021 12:44:21 -0500 Subject: [PATCH] impl authelia I think --- config/.config.go.un~ | Bin 0 -> 7723 bytes config/.new.go.un~ | Bin 0 -> 6255 bytes config/config.go | 9 ++++ config/new.go | 7 ++- server/new.go | 5 +- server/proxy.go | 2 +- server/server.go | 118 +++++++++++++++++++++++++++++++++++++----- 7 files changed, 125 insertions(+), 16 deletions(-) create mode 100644 config/.config.go.un~ create mode 100644 config/.new.go.un~ diff --git a/config/.config.go.un~ b/config/.config.go.un~ new file mode 100644 index 0000000000000000000000000000000000000000..430af022dabbbfc82607b3b8cfa9fd71d720557a GIT binary patch literal 7723 zcmeHM&2QX96yGGJ?S#@ss#Fj~&BUvuwraNR0U=Ta)DMJ^5RJMmNJJrWw%)|7zp%X# zA@qPa@gHz1w<5tA^}vM#DnbIpnL7fZN^s!}?>C;Y$Lo|u(BS=BK{^R!t@OGxP@q=WTmOVPWa@G7#YJ#E&P5+QRKyZ8{;Apk}c!Fea z(E}{;0E#MgyPy;hRrqvj>zVfjz&X}fm^Xk8IRf+sVCj{Kbp-Go0q|4_rGSw%3TQaz z5-A{ZKr>{9#)t>e8l;pbCe;&CUyY_>M9Rvf0~nYIiUbWHJwlIa0I&f~d1K-|fp%{c zG}O+Ro_hTRoT@GYsDqfx1`vy?+hm(^%cOb&?pqx=9*sWYwMf4nQP|BXxV%zur3JJg%uup zKHrlF15u{!0%!x_<^ZZWWpcxD2p?rEakF6(^1kDzt^^Gr&1yygz$PsM2h<$fuz55<`3%+u?(VvT4KS(iGq4a4Hn71H|4Sp-z7wa< z>HJncditDIH8C1U!1Nr0i2x6)L~E`K$`h073&3V_z;r#xo1?MQ8#r68xGQ+-0)E&F z;wZ_uNgQ*`aKm17lWq`aags5Jc90qUAd@3M+QMj-8TlzS#q1C>GB0p3oU-T-+VN~R zcKK=&y|c@Sb=b@J9+79+^$2rPK(nb^V5N4ZKMZ?3rbCk}mYAtzvbdRZ+qo=DNO_FK zC>DZs4>D#Ml?{*#7;%#M7)uK~-PrdsGqt&48!&GU$2H##P2b|r^K(Mt5hf(8aDVOG zI`PnNM~(Bm)Xm}!Z33+4OV*<*8ORWui<>nhpc!IwbTzwl_2Mwwe#6u@g+qS6CyQ-H z4!wRCrmM@GX$YEOX*WA;;gr%Q*VtSwrgNq6LE+ldN;`Dxs+C_$6BgMpb;T*R)DKEc z{UVEY(~?ToX{VUlK5p3Um+O-5PTS;c<+&@>?PtXQ>d*c>y876^eKjw^sPy6&EK zVR1Cf+>~tAZm4;7BdwR1?~Pg&Bll|&VX>QT-wVCWi$W1W)hvY=Bx;_i3oH?8vE9HK zxaO#GY{6pvDB*O1C7~uFq^4`3&_%xxb0&32jn4UQH_k~#!bBQtQRGf`qh0|(JT1{lv8vK{Q1+r0Q^B# ARsaA1 literal 0 HcmV?d00001 diff --git a/config/.new.go.un~ b/config/.new.go.un~ new file mode 100644 index 0000000000000000000000000000000000000000..9ee28f21289ab080e7c805b3b130f2762aa3d2c2 GIT binary patch literal 6255 zcmeI%yDtPm90%|@@AC*M%0uE6&Jhwq;nhflNYHZF$ccNnrJ$n|jsHNeCB%tqRHD&n z^r97sMC0-Mu33JL+g)P1nPhIp>>abePkz6<8`F{K-c)Qjek*cz(v|h;r|XxRg00K` zi>>Ebb0W37eco2S9_XDKdRP)d^iU7^!)9$#uu9~#c9tt(lYg(ySQ1?+UqmG8r?qcTtCY^_?J~4BJtJHGd2kQ}+TBu_buhiaW z_(`|_j{tETSR6t;R@1LX=!4X|@uxmkplYpjZCYr+$;C5d10bFQd5wl3C~bsx_!AnE z7Bs(3T*(T?JhpQXYgQHzwNp3N*35eBSd6794D;E(;S19$tm*oSD5z{?dgPRgM{b1WVVfpPgj7g0YZo9mJZI1w`$XSdX33wKGe)E^b5; zZnU5*V%rC^rey(9J9VS|YsP}n&$bR?u-0Kszri93DjTt$IAv^imW-W`M-xfI#8=m! zzs$;l49*gE1EAKtEFfy9#G7*B?eM^xaY`X!0a;4DSl$4THLMJV`hj3ionV_iz{+DM z2@1e6wsGJB)H=A(g_uP~$&^6PoIv|LfEuyz+_Dj{YjY$X`$`N_-oUMic|?@XKZt#w tM}s&lw9lP-{z}}$YO;L?NN3b$`0CG6c2ty(Zf~{?9(?hCfUeEE{Wqn`a4G-* literal 0 HcmV?d00001 diff --git a/config/config.go b/config/config.go index e4e9317..6df26a8 100755 --- a/config/config.go +++ b/config/config.go @@ -25,6 +25,11 @@ func parseProxy(s string) (string, Proxy) { return key, p } +func GetAuthelia() (string, bool) { + authelia := conf.Get("authelia").GetString() + return authelia, authelia != "" +} + func GetBOAuthZ() (string, bool) { boauthz := conf.Get("oauth").GetString() return boauthz, boauthz != "" @@ -99,3 +104,7 @@ func GetNoPath(key string) bool { _, ok := m[key] return ok } + +func GetCompression() bool { + return conf.GetBool("compression") +} diff --git a/config/new.go b/config/new.go index d2cf952..663f7a5 100755 --- a/config/new.go +++ b/config/new.go @@ -3,6 +3,7 @@ package config import ( "fmt" "local/args" + "local/logb" "log" "os" "strings" @@ -26,6 +27,7 @@ func Refresh() error { return err } conf = as + logb.Set(logb.LevelFromString(as.GetString("level"))) return nil } @@ -42,14 +44,17 @@ func parseArgs() (*args.ArgSet, error) { as.Append(args.INT, "ap", "alt port for always http service", 51556) as.Append(args.INT, "r", "rate per second for requests", 100) as.Append(args.INT, "b", "burst requests", 100) + as.Append(args.BOOL, "compress", "enable compression", true) as.Append(args.STRING, "crt", "path to crt for ssl", "") as.Append(args.STRING, "key", "path to key for ssl", "") as.Append(args.STRING, "tcp", "address for tcp only tunnel", "") as.Append(args.DURATION, "timeout", "timeout for tunnel", time.Minute) - as.Append(args.STRING, "proxy", "double-comma separated (+ if oauth)from,scheme://to.tld:port,oauth,,", "") + as.Append(args.STRING, "proxy", "double-comma separated (+ if auth)from,scheme://to.tld:port,,", "") as.Append(args.STRING, "oauth", "url for boauthz", "") + as.Append(args.STRING, "authelia", "url for authelia", "") as.Append(args.STRING, "cors", "json dict key:true for keys to set CORS permissive headers, like {\"from\":true}", "{}") as.Append(args.STRING, "nopath", "json dict key:true for keys to remove all path info from forwarded request, like -cors", "{}") + as.Append(args.STRING, "level", "log level", "info") err := as.Parse() return as, err diff --git a/server/new.go b/server/new.go index 517eab2..b5be006 100755 --- a/server/new.go +++ b/server/new.go @@ -11,10 +11,13 @@ func New() *Server { port := config.GetPort() altport := config.GetAltPort() r, b := config.GetRate() - return &Server{ + server := &Server{ db: storage.NewMap(), addr: port, altaddr: altport, limiter: rate.NewLimiter(rate.Limit(r), b), } + _, server.auth.BOAuthZ = config.GetBOAuthZ() + _, server.auth.Authelia = config.GetAuthelia() + return server } diff --git a/server/proxy.go b/server/proxy.go index ba0c3ba..a2565c9 100755 --- a/server/proxy.go +++ b/server/proxy.go @@ -49,7 +49,7 @@ func (s *Server) lookup(host string) (*url.URL, error) { return v.URL(), err } -func (s *Server) lookupBOAuthZ(host string) (bool, error) { +func (s *Server) lookupAuth(host string) (bool, error) { v := packable.NewString() err := s.db.Get(nsBOAuthZ, host, v) return v.String() == "true", err diff --git a/server/server.go b/server/server.go index 7fa9c5a..706a531 100755 --- a/server/server.go +++ b/server/server.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "io" + "local/logb" "local/oauth2/oauth2client" "local/rproxy3/config" "local/rproxy3/storage" @@ -52,6 +53,10 @@ type Server struct { username string password string limiter *rate.Limiter + auth struct { + BOAuthZ bool + Authelia bool + } } func (s *Server) Route(src string, dst config.Proxy) error { @@ -99,19 +104,81 @@ func (s *Server) Run() error { return errors.New("did not load server") } -func (s *Server) doAuth(foo http.HandlerFunc) http.HandlerFunc { +func (s *Server) doAuthelia(foo http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + authelia, ok := config.GetAuthelia() + if !ok { + panic("howd i get here") + } + url, err := url.Parse(authelia) + if err != nil { + panic(fmt.Sprintf("bad config for authelia url: %v", err)) + } + url.Path = "/api/verify" + req, err := http.NewRequest(http.MethodGet, url.String(), nil) + if err != nil { + panic(err.Error()) + } + r2 := r.Clone(r.Context()) + if r2.URL.Host == "" { + r2.URL.Host = r2.Host + } + r2.URL.Host, _, _ = net.SplitHostPort(r2.URL.Host) + if r2.URL.Scheme == "" { + r2.URL.Scheme = "https" + } + for _, httpreq := range []*http.Request{r, req} { + httpreq.Header.Set("X-Original-URL", r2.URL.String()) + httpreq.Header.Set("X-Forwarded-Proto", r2.URL.Scheme) + httpreq.Header.Set("X-Forwarded-Host", r2.URL.Host) + httpreq.Header.Set("X-Forwarded-URI", r2.URL.String()) + } + if cookie, err := r.Cookie("authelia_session"); err == nil { + req.AddCookie(cookie) + } + c := &http.Client{ + Timeout: time.Minute, + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + } + + autheliaKey := mapKey(req.Host) + if strings.HasPrefix(r.Host, autheliaKey) { + logb.Debugf("no authelia for %s because it has prefix %s", r.Host, autheliaKey) + foo(w, r) + return + } + + resp, err := c.Do(req) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + logb.Debugf( + "authelia: %+v, %+v \n\t-> \n\t(%d) %+v, %+v", + req, + req.Cookies(), + resp.StatusCode, + resp.Header, + resp.Cookies(), + ) + defer resp.Body.Close() + if resp.StatusCode == http.StatusOK { + foo(w, r) + return + } + url.Path = "" + q := url.Query() + q.Set("rd", r2.URL.String()) + url.RawQuery = q.Encode() + http.Redirect(w, r, url.String(), http.StatusFound) + } +} + +func (s *Server) doBOAuthZ(foo http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { key := mapKey(r.Host) - if config.GetCORS(key) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Headers", "X-Auth-Token, content-type, Content-Type") - if r.Method == "OPTIONS" { - w.Header().Set("Content-Length", "0") - w.Header().Set("Content-Type", "text/plain") - w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS, TRACE, PATCH, HEAD, DELETE") - return - } - } rusr, rpwd, ok := config.GetAuth() if ok { usr, pwd, ok := r.BasicAuth() @@ -121,7 +188,7 @@ func (s *Server) doAuth(foo http.HandlerFunc) http.HandlerFunc { return } } - ok, err := s.lookupBOAuthZ(key) + ok, err := s.lookupAuth(key) if err != nil { w.WriteHeader(http.StatusInternalServerError) return @@ -177,7 +244,16 @@ func (s *Server) Pre(foo http.HandlerFunc) http.HandlerFunc { w.WriteHeader(http.StatusTooManyRequests) return } - s.doAuth(foo)(w, r) + if did := s.doCORS(w, r); did { + return + } + if s.auth.BOAuthZ { + s.doBOAuthZ(foo)(w, r) + } else if s.auth.Authelia { + s.doAuthelia(foo)(w, r) + } else { + foo(w, r) + } } } @@ -185,6 +261,22 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.Pre(s.Proxy)(w, r) } +func (s *Server) doCORS(w http.ResponseWriter, r *http.Request) bool { + key := mapKey(r.Host) + if !config.GetCORS(key) { + return false + } + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Headers", "X-Auth-Token, content-type, Content-Type") + if r.Method != "OPTIONS" { + return false + } + w.Header().Set("Content-Length", "0") + w.Header().Set("Content-Type", "text/plain") + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS, TRACE, PATCH, HEAD, DELETE") + return true +} + func getProxyAuth(r *http.Request) (string, string) { proxyAuthHeader := r.Header.Get("Proxy-Authorization") proxyAuthB64 := strings.TrimPrefix(proxyAuthHeader, "Basic ")