Proxy working with authentications
This commit is contained in:
parent
28a44da3ad
commit
b561b94918
|
@ -29,13 +29,13 @@ import AnimateButton from '../../../components/@extended/AnimateButton';
|
||||||
import RestartModal from './restart';
|
import RestartModal from './restart';
|
||||||
|
|
||||||
|
|
||||||
export const CosmosInputText = ({ name, placeholder, label, formik }) => {
|
export const CosmosInputText = ({ name, type, placeholder, label, formik }) => {
|
||||||
return <Grid item xs={12}>
|
return <Grid item xs={12}>
|
||||||
<Stack spacing={1}>
|
<Stack spacing={1}>
|
||||||
<InputLabel htmlFor={name}>{label}</InputLabel>
|
<InputLabel htmlFor={name}>{label}</InputLabel>
|
||||||
<OutlinedInput
|
<OutlinedInput
|
||||||
id={name}
|
id={name}
|
||||||
type="text"
|
type={type ? type : 'text'}
|
||||||
value={formik.values[name]}
|
value={formik.values[name]}
|
||||||
name={name}
|
name={name}
|
||||||
onBlur={formik.handleBlur}
|
onBlur={formik.handleBlur}
|
||||||
|
|
|
@ -44,6 +44,7 @@ const RouteManagement = ({ routeConfig, setRouteConfig, up, down, deleteRoute })
|
||||||
Mode: routeConfig.Mode,
|
Mode: routeConfig.Mode,
|
||||||
Target: routeConfig.Target,
|
Target: routeConfig.Target,
|
||||||
UseHost: routeConfig.UseHost,
|
UseHost: routeConfig.UseHost,
|
||||||
|
AuthEnabled: routeConfig.AuthEnabled,
|
||||||
Host: routeConfig.Host,
|
Host: routeConfig.Host,
|
||||||
UsePathPrefix: routeConfig.UsePathPrefix,
|
UsePathPrefix: routeConfig.UsePathPrefix,
|
||||||
PathPrefix: routeConfig.PathPrefix,
|
PathPrefix: routeConfig.PathPrefix,
|
||||||
|
@ -146,10 +147,17 @@ const RouteManagement = ({ routeConfig, setRouteConfig, up, down, deleteRoute })
|
||||||
|
|
||||||
<CosmosFormDivider title={'Security'}/>
|
<CosmosFormDivider title={'Security'}/>
|
||||||
|
|
||||||
|
<CosmosCheckbox
|
||||||
|
name="AuthEnabled"
|
||||||
|
label="Authentication Required"
|
||||||
|
formik={formik}
|
||||||
|
/>
|
||||||
|
|
||||||
<CosmosInputText
|
<CosmosInputText
|
||||||
name="Timeout"
|
name="Timeout"
|
||||||
label="Timeout in milliseconds (0 for no timeout, at least 30000 or less recommended)"
|
label="Timeout in milliseconds (0 for no timeout, at least 30000 or less recommended)"
|
||||||
placeholder="Timeout"
|
placeholder="Timeout"
|
||||||
|
type="number"
|
||||||
formik={formik}
|
formik={formik}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -157,6 +165,7 @@ const RouteManagement = ({ routeConfig, setRouteConfig, up, down, deleteRoute })
|
||||||
name="ThrottlePerMinute"
|
name="ThrottlePerMinute"
|
||||||
label="Maximum number of requests Per Minute (0 for no limit, at least 100 or less recommended)"
|
label="Maximum number of requests Per Minute (0 for no limit, at least 100 or less recommended)"
|
||||||
placeholder="Throttle Per Minute"
|
placeholder="Throttle Per Minute"
|
||||||
|
type="number"
|
||||||
formik={formik}
|
formik={formik}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ FROM debian
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY build/cosmos .
|
COPY build/cosmos .
|
||||||
COPY static .
|
COPY static ./static
|
||||||
|
|
||||||
VOLUME /config
|
VOLUME /config
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"certificate": "sh generate-certificate.sh",
|
"certificate": "sh generate-certificate.sh",
|
||||||
"client": "g vite dev",
|
"client": "g vite dev",
|
||||||
|
"dockerdev": "g ci/build; g vite build --base=/ui/; docker build --tag cosmos-dev .",
|
||||||
"start": "build/bin"
|
"start": "build/bin"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -86,6 +87,6 @@
|
||||||
},
|
},
|
||||||
"description": "Cosmos Server",
|
"description": "Cosmos Server",
|
||||||
"name": "cosmos-server",
|
"name": "cosmos-server",
|
||||||
"version": "0.0.5",
|
"version": "0.0.6",
|
||||||
"wrapInstallFolder": "src"
|
"wrapInstallFolder": "src"
|
||||||
}
|
}
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"strings"
|
"strings"
|
||||||
"strconv"
|
"strconv"
|
||||||
"regexp"
|
|
||||||
"time"
|
"time"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
|
@ -75,6 +74,7 @@ func startHTTPSServer(router *mux.Router, tlsCert string, tlsKey string) {
|
||||||
WriteTimeout: 0,
|
WriteTimeout: 0,
|
||||||
IdleTimeout: 30 * time.Second,
|
IdleTimeout: 30 * time.Second,
|
||||||
Handler: router,
|
Handler: router,
|
||||||
|
DisableGeneralOptionsHandler: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// start https server
|
// start https server
|
||||||
|
@ -99,19 +99,6 @@ func tokenMiddleware(next http.Handler) http.Handler {
|
||||||
r.Header.Set("x-cosmos-user", u.Nickname)
|
r.Header.Set("x-cosmos-user", u.Nickname)
|
||||||
r.Header.Set("x-cosmos-role", strconv.Itoa((int)(u.Role)))
|
r.Header.Set("x-cosmos-role", strconv.Itoa((int)(u.Role)))
|
||||||
|
|
||||||
// TODO: If external application, remove the cookie from the request
|
|
||||||
// to prevent leaking, and generate new JWT token
|
|
||||||
if false {
|
|
||||||
cookies := r.Header.Get("Cookie")
|
|
||||||
// This prob dowsnt work
|
|
||||||
cookieRemoveRegex := regexp.MustCompile(`jwttoken=[^;]*;`)
|
|
||||||
cookies = cookieRemoveRegex.ReplaceAllString(cookies, "")
|
|
||||||
r.Header.Set("Cookie", cookies)
|
|
||||||
|
|
||||||
// Replace the token with a application speicfic one
|
|
||||||
r.Header.Set("x-cosmos-token", "1234567890")
|
|
||||||
}
|
|
||||||
|
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -157,12 +144,11 @@ func StartServer() {
|
||||||
|
|
||||||
router.Use(middleware.Recoverer)
|
router.Use(middleware.Recoverer)
|
||||||
router.Use(middleware.Logger)
|
router.Use(middleware.Logger)
|
||||||
router.Use(tokenMiddleware)
|
|
||||||
router.Use(utils.SetSecurityHeaders)
|
router.Use(utils.SetSecurityHeaders)
|
||||||
|
|
||||||
router.HandleFunc("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
router.HandleFunc("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Redirect(w, r, "/ui", http.StatusMovedPermanently)
|
http.Redirect(w, r, "/ui", http.StatusMovedPermanently)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
srapi := router.PathPrefix("/cosmos").Subrouter()
|
srapi := router.PathPrefix("/cosmos").Subrouter()
|
||||||
|
|
||||||
|
@ -178,6 +164,7 @@ func StartServer() {
|
||||||
srapi.HandleFunc("/api/users", user.UsersRoute)
|
srapi.HandleFunc("/api/users", user.UsersRoute)
|
||||||
|
|
||||||
// srapi.Use(utils.AcceptHeader("*/*"))
|
// srapi.Use(utils.AcceptHeader("*/*"))
|
||||||
|
srapi.Use(tokenMiddleware)
|
||||||
srapi.Use(utils.CORSHeader(utils.GetMainConfig().HTTPConfig.Hostname))
|
srapi.Use(utils.CORSHeader(utils.GetMainConfig().HTTPConfig.Hostname))
|
||||||
srapi.Use(utils.MiddlewareTimeout(20 * time.Second))
|
srapi.Use(utils.MiddlewareTimeout(20 * time.Second))
|
||||||
srapi.Use(httprate.Limit(60, 1*time.Minute,
|
srapi.Use(httprate.Limit(60, 1*time.Minute,
|
||||||
|
|
|
@ -6,9 +6,44 @@ import (
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"time"
|
"time"
|
||||||
"../utils"
|
"../utils"
|
||||||
|
"../user"
|
||||||
|
"strconv"
|
||||||
"github.com/go-chi/httprate"
|
"github.com/go-chi/httprate"
|
||||||
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func tokenMiddleware(enabled bool) func(next http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
r.Header.Set("x-cosmos-user", "")
|
||||||
|
r.Header.Set("x-cosmos-role", "")
|
||||||
|
|
||||||
|
u, err := user.RefreshUserToken(w, r)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Header.Set("x-cosmos-user", u.Nickname)
|
||||||
|
r.Header.Set("x-cosmos-role", strconv.Itoa((int)(u.Role)))
|
||||||
|
|
||||||
|
ogcookies := r.Header.Get("Cookie")
|
||||||
|
cookieRemoveRegex := regexp.MustCompile(`jwttoken=[^;]*;`)
|
||||||
|
cookies := cookieRemoveRegex.ReplaceAllString(ogcookies, "")
|
||||||
|
r.Header.Set("Cookie", cookies)
|
||||||
|
|
||||||
|
// Replace the token with a application speicfic one
|
||||||
|
r.Header.Set("x-cosmos-token", "1234567890")
|
||||||
|
|
||||||
|
if(enabled) {
|
||||||
|
utils.LoggedInOnlyWithRedirect(w, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func RouterGen(route utils.ProxyRouteConfig, router *mux.Router, destination *httputil.ReverseProxy) *mux.Route {
|
func RouterGen(route utils.ProxyRouteConfig, router *mux.Router, destination *httputil.ReverseProxy) *mux.Route {
|
||||||
var realDestination http.Handler
|
var realDestination http.Handler
|
||||||
realDestination = destination
|
realDestination = destination
|
||||||
|
@ -49,6 +84,7 @@ func RouterGen(route utils.ProxyRouteConfig, router *mux.Router, destination *ht
|
||||||
}
|
}
|
||||||
|
|
||||||
origin.Handler(
|
origin.Handler(
|
||||||
|
tokenMiddleware(route.AuthEnabled)(
|
||||||
utils.CORSHeader(originCORS)(
|
utils.CORSHeader(originCORS)(
|
||||||
utils.MiddlewareTimeout(timeout * time.Millisecond)(
|
utils.MiddlewareTimeout(timeout * time.Millisecond)(
|
||||||
httprate.Limit(throttlePerMinute, 1*time.Minute,
|
httprate.Limit(throttlePerMinute, 1*time.Minute,
|
||||||
|
@ -59,7 +95,7 @@ func RouterGen(route utils.ProxyRouteConfig, router *mux.Router, destination *ht
|
||||||
http.StatusTooManyRequests, "HTTP003")
|
http.StatusTooManyRequests, "HTTP003")
|
||||||
return
|
return
|
||||||
}),
|
}),
|
||||||
)(realDestination))))
|
)(realDestination)))))
|
||||||
|
|
||||||
return origin
|
return origin
|
||||||
}
|
}
|
|
@ -95,6 +95,10 @@ func logOutUser(w http.ResponseWriter) {
|
||||||
Name: "jwttoken",
|
Name: "jwttoken",
|
||||||
Value: "",
|
Value: "",
|
||||||
Expires: time.Now().Add(-time.Hour * 24 * 365),
|
Expires: time.Now().Add(-time.Hour * 24 * 365),
|
||||||
|
Path: "/",
|
||||||
|
Secure: true,
|
||||||
|
HttpOnly: true,
|
||||||
|
Domain: utils.GetMainConfig().HTTPConfig.Hostname,
|
||||||
}
|
}
|
||||||
|
|
||||||
http.SetCookie(w, &cookie)
|
http.SetCookie(w, &cookie)
|
||||||
|
@ -141,17 +145,11 @@ func SendUserToken(w http.ResponseWriter, user utils.User) {
|
||||||
Name: "jwttoken",
|
Name: "jwttoken",
|
||||||
Value: tokenString,
|
Value: tokenString,
|
||||||
Expires: expiration,
|
Expires: expiration,
|
||||||
|
Path: "/",
|
||||||
|
Secure: true,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
// TODO: high level cookie for SSO
|
Domain: utils.GetMainConfig().HTTPConfig.Hostname,
|
||||||
// Should re-generate app specific cookies on subdomains
|
|
||||||
// Domain: "yoursite.com",
|
|
||||||
}
|
}
|
||||||
// cookie2 := http.Cookie{
|
|
||||||
// Name: "dummy",
|
|
||||||
// Value: "asdasdadsasd",
|
|
||||||
// Expires: expiration,
|
|
||||||
// HttpOnly: true,
|
|
||||||
// }
|
|
||||||
|
|
||||||
http.SetCookie(w, &cookie)
|
http.SetCookie(w, &cookie)
|
||||||
// http.SetCookie(w, &cookie2)
|
// http.SetCookie(w, &cookie2)
|
||||||
|
|
|
@ -39,7 +39,8 @@ func SetSecurityHeaders(next http.Handler) http.Handler {
|
||||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||||
w.Header().Set("X-Frame-Options", "DENY")
|
w.Header().Set("X-Frame-Options", "DENY")
|
||||||
w.Header().Set("X-XSS-Protection", "1; mode=block")
|
w.Header().Set("X-XSS-Protection", "1; mode=block")
|
||||||
// w.Header().Set("Referrer-Policy", "no-referrer")
|
w.Header().Set("X-Served-By-Cosmos", "1")
|
||||||
|
w.Header().Set("Referrer-Policy", "no-referrer")
|
||||||
|
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
|
@ -48,6 +49,7 @@ func SetSecurityHeaders(next http.Handler) http.Handler {
|
||||||
func CORSHeader(origin string) func(next http.Handler) http.Handler {
|
func CORSHeader(origin string) func(next http.Handler) http.Handler {
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||||
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
||||||
|
|
|
@ -95,6 +95,7 @@ type ProxyRouteConfig struct {
|
||||||
ThrottlePerMinute int
|
ThrottlePerMinute int
|
||||||
CORSOrigin string
|
CORSOrigin string
|
||||||
StripPathPrefix bool
|
StripPathPrefix bool
|
||||||
|
AuthEnabled bool
|
||||||
Target string `validate:"required"`
|
Target string `validate:"required"`
|
||||||
Mode ProxyMode
|
Mode ProxyMode
|
||||||
}
|
}
|
||||||
|
|
|
@ -197,8 +197,20 @@ func RestartServer() {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LoggedInOnlyWithRedirect(w http.ResponseWriter, req *http.Request) error {
|
||||||
|
userNickname := req.Header.Get("x-cosmos-user")
|
||||||
|
role, _ := strconv.Atoi(req.Header.Get("x-cosmos-role"))
|
||||||
|
isUserLoggedIn := role > 0
|
||||||
|
|
||||||
func loggedInOnly(w http.ResponseWriter, req *http.Request) error {
|
if !isUserLoggedIn || userNickname == "" {
|
||||||
|
Error("LoggedInOnlyWithRedirect: User is not logged in", nil)
|
||||||
|
http.Redirect(w, req, "/ui/login?notlogged=1&redirect=" + req.URL.Path, http.StatusFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoggedInOnly(w http.ResponseWriter, req *http.Request) error {
|
||||||
userNickname := req.Header.Get("x-cosmos-user")
|
userNickname := req.Header.Get("x-cosmos-user")
|
||||||
role, _ := strconv.Atoi(req.Header.Get("x-cosmos-role"))
|
role, _ := strconv.Atoi(req.Header.Get("x-cosmos-role"))
|
||||||
isUserLoggedIn := role > 0
|
isUserLoggedIn := role > 0
|
||||||
|
|
Loading…
Reference in a new issue