mirror of
https://github.com/openfaas/faas.git
synced 2025-06-08 16:26:47 +00:00
Deprecate external auth plugins
There is no need for OpenFaaS CE to have external auth plugins since this added extra overhead and was never used. OpenFaaS Pro retains the option so it can use the OIDC auth plugin. It's still possible, as it ever was to put a proxy in front of any HTTP server like the gateway. Tested with a local KinD cluster, auth still worked for the API and UI. Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alex@openfaas.com>
This commit is contained in:
parent
a7d486eee6
commit
4f9c61b5d2
@ -1,47 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MakeExternalAuthHandler make an authentication proxy handler
|
||||
func MakeExternalAuthHandler(next http.HandlerFunc, upstreamTimeout time.Duration, upstreamURL string, passBody bool) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
req, _ := http.NewRequest(http.MethodGet, upstreamURL, nil)
|
||||
|
||||
copyHeaders(req.Header, &r.Header)
|
||||
|
||||
deadlineContext, cancel := context.WithTimeout(
|
||||
context.Background(),
|
||||
upstreamTimeout)
|
||||
|
||||
defer cancel()
|
||||
|
||||
res, err := http.DefaultClient.Do(req.WithContext(deadlineContext))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
log.Printf("ExternalAuthHandler: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if res.Body != nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
|
||||
if res.StatusCode == http.StatusOK {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
copyHeaders(w.Header(), &res.Header)
|
||||
w.WriteHeader(res.StatusCode)
|
||||
|
||||
if res.Body != nil {
|
||||
io.Copy(w, res.Body)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,239 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_External_Auth_Wrapper_FailsInvalidAuth(t *testing.T) {
|
||||
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
next := func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
passBody := false
|
||||
handler := MakeExternalAuthHandler(next, time.Second*5, s.URL, passBody)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, s.URL, nil)
|
||||
rr := httptest.NewRecorder()
|
||||
handler(rr, req)
|
||||
|
||||
if rr.Code == http.StatusOK {
|
||||
t.Errorf("Status incorrect, did not want: %d, but got %d", http.StatusOK, rr.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_External_Auth_Wrapper_FailsInvalidAuth_WritesBody(t *testing.T) {
|
||||
|
||||
wantBody := []byte(`invalid credentials`)
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
w.Write(wantBody)
|
||||
}))
|
||||
|
||||
defer s.Close()
|
||||
|
||||
next := func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
passBody := false
|
||||
handler := MakeExternalAuthHandler(next, time.Second*5, s.URL, passBody)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, s.URL, nil)
|
||||
rr := httptest.NewRecorder()
|
||||
handler(rr, req)
|
||||
|
||||
if rr.Code == http.StatusOK {
|
||||
t.Errorf("Status incorrect, did not want: %d, but got %d", http.StatusOK, rr.Code)
|
||||
}
|
||||
|
||||
if bytes.Compare(rr.Body.Bytes(), wantBody) != 0 {
|
||||
t.Errorf("Body incorrect, want: %s, but got %s", []byte(wantBody), rr.Body)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_External_Auth_Wrapper_PassesValidAuth(t *testing.T) {
|
||||
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
next := func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
passBody := false
|
||||
handler := MakeExternalAuthHandler(next, time.Second*5, s.URL, passBody)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, s.URL, nil)
|
||||
rr := httptest.NewRecorder()
|
||||
handler(rr, req)
|
||||
want := http.StatusNotImplemented
|
||||
if rr.Code != want {
|
||||
t.Errorf("Status incorrect, want: %d, but got %d", want, rr.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_External_Auth_Wrapper_WithoutRequiredHeaderFailsAuth(t *testing.T) {
|
||||
wantToken := "secret-key"
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("X-Token") == wantToken {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
next := func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
passBody := false
|
||||
handler := MakeExternalAuthHandler(next, time.Second*5, s.URL, passBody)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, s.URL, nil)
|
||||
|
||||
// use an invalid token
|
||||
req.Header.Set("X-Token", "invalid-key")
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler(rr, req)
|
||||
want := http.StatusUnauthorized
|
||||
if rr.Code != want {
|
||||
t.Errorf("Status incorrect, want: %d, but got %d", want, rr.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_External_Auth_Wrapper_WithoutRequiredHeaderFailsAuth_ProxiesServerHeaders(t *testing.T) {
|
||||
wantToken := "secret-key"
|
||||
wantRealm := `Basic realm="Restricted"`
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("X-Token") == wantToken {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Www-Authenticate", wantRealm)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
next := func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
passBody := false
|
||||
handler := MakeExternalAuthHandler(next, time.Second*5, s.URL, passBody)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, s.URL, nil)
|
||||
|
||||
// use an invalid token
|
||||
req.Header.Set("X-Token", "invalid-key")
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler(rr, req)
|
||||
want := http.StatusUnauthorized
|
||||
if rr.Code != want {
|
||||
t.Errorf("Status incorrect, want: %d, but got %d", want, rr.Code)
|
||||
}
|
||||
|
||||
got := rr.Header().Get("Www-Authenticate")
|
||||
if got != wantRealm {
|
||||
t.Errorf("Www-Authenticate header, want: %s, but got %s, %q", wantRealm, got, rr.Header())
|
||||
}
|
||||
}
|
||||
|
||||
func Test_External_Auth_Wrapper_WithRequiredHeaderPassesValidAuth(t *testing.T) {
|
||||
wantToken := "secret-key"
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("X-Token") == wantToken {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
next := func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
passBody := false
|
||||
handler := MakeExternalAuthHandler(next, time.Second*5, s.URL, passBody)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, s.URL, nil)
|
||||
req.Header.Set("X-Token", wantToken)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler(rr, req)
|
||||
want := http.StatusNotImplemented
|
||||
if rr.Code != want {
|
||||
t.Errorf("Status incorrect, want: %d, but got %d", want, rr.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_External_Auth_Wrapper_TimeoutGivesInternalServerError(t *testing.T) {
|
||||
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
next := func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
passBody := false
|
||||
handler := MakeExternalAuthHandler(next, time.Millisecond*10, s.URL, passBody)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, s.URL, nil)
|
||||
rr := httptest.NewRecorder()
|
||||
handler(rr, req)
|
||||
|
||||
want := http.StatusInternalServerError
|
||||
if rr.Code != want {
|
||||
t.Errorf("Status incorrect, want: %d, but got %d", want, rr.Code)
|
||||
}
|
||||
wantSubstring := "context deadline exceeded\n"
|
||||
if !strings.HasSuffix(string(rr.Body.Bytes()), wantSubstring) {
|
||||
t.Errorf("Body incorrect, want to have suffix: %q, but got %q", []byte(wantSubstring), rr.Body)
|
||||
}
|
||||
}
|
||||
|
||||
// // Test_External_Auth_Wrapper_PassesValidAuthButOnly200IsValid this test exists
|
||||
// // to document the TODO action to consider all "2xx" statuses as valid.
|
||||
// func Test_External_Auth_Wrapper_PassesValidAuthButOnly200IsValid(t *testing.T) {
|
||||
|
||||
// s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// w.WriteHeader(http.StatusAccepted)
|
||||
// }))
|
||||
// defer s.Close()
|
||||
|
||||
// next := func(w http.ResponseWriter, r *http.Request) {
|
||||
// w.WriteHeader(http.StatusNotImplemented)
|
||||
// }
|
||||
|
||||
// passBody := false
|
||||
// handler := MakeExternalAuthHandler(next, time.Second*5, s.URL, passBody)
|
||||
|
||||
// req := httptest.NewRequest(http.MethodGet, s.URL, nil)
|
||||
// rr := httptest.NewRecorder()
|
||||
// handler(rr, req)
|
||||
// want := http.StatusUnauthorized
|
||||
// if rr.Code != want {
|
||||
// t.Errorf("Status incorrect, want: %d, but got %d", want, rr.Code)
|
||||
// }
|
||||
// }
|
@ -105,8 +105,6 @@ func main() {
|
||||
serviceAuthInjector = &middleware.BasicAuthInjector{Credentials: credentials}
|
||||
}
|
||||
|
||||
decorateExternalAuth := handlers.MakeExternalAuthHandler
|
||||
|
||||
// externalServiceQuery is used to query metadata from the provider about a function
|
||||
externalServiceQuery := plugin.NewExternalServiceQuery(*config.FunctionsProviderURL, serviceAuthInjector)
|
||||
|
||||
@ -178,27 +176,27 @@ func main() {
|
||||
|
||||
if credentials != nil {
|
||||
faasHandlers.Alert =
|
||||
decorateExternalAuth(faasHandlers.Alert, config.UpstreamTimeout, config.AuthProxyURL, config.AuthProxyPassBody)
|
||||
auth.DecorateWithBasicAuth(faasHandlers.Alert, credentials)
|
||||
faasHandlers.UpdateFunction =
|
||||
decorateExternalAuth(faasHandlers.UpdateFunction, config.UpstreamTimeout, config.AuthProxyURL, config.AuthProxyPassBody)
|
||||
auth.DecorateWithBasicAuth(faasHandlers.UpdateFunction, credentials)
|
||||
faasHandlers.DeleteFunction =
|
||||
decorateExternalAuth(faasHandlers.DeleteFunction, config.UpstreamTimeout, config.AuthProxyURL, config.AuthProxyPassBody)
|
||||
auth.DecorateWithBasicAuth(faasHandlers.DeleteFunction, credentials)
|
||||
faasHandlers.DeployFunction =
|
||||
decorateExternalAuth(faasHandlers.DeployFunction, config.UpstreamTimeout, config.AuthProxyURL, config.AuthProxyPassBody)
|
||||
auth.DecorateWithBasicAuth(faasHandlers.DeployFunction, credentials)
|
||||
faasHandlers.ListFunctions =
|
||||
decorateExternalAuth(faasHandlers.ListFunctions, config.UpstreamTimeout, config.AuthProxyURL, config.AuthProxyPassBody)
|
||||
auth.DecorateWithBasicAuth(faasHandlers.ListFunctions, credentials)
|
||||
faasHandlers.ScaleFunction =
|
||||
decorateExternalAuth(faasHandlers.ScaleFunction, config.UpstreamTimeout, config.AuthProxyURL, config.AuthProxyPassBody)
|
||||
auth.DecorateWithBasicAuth(faasHandlers.ScaleFunction, credentials)
|
||||
faasHandlers.FunctionStatus =
|
||||
decorateExternalAuth(faasHandlers.FunctionStatus, config.UpstreamTimeout, config.AuthProxyURL, config.AuthProxyPassBody)
|
||||
auth.DecorateWithBasicAuth(faasHandlers.FunctionStatus, credentials)
|
||||
faasHandlers.InfoHandler =
|
||||
decorateExternalAuth(faasHandlers.InfoHandler, config.UpstreamTimeout, config.AuthProxyURL, config.AuthProxyPassBody)
|
||||
auth.DecorateWithBasicAuth(faasHandlers.InfoHandler, credentials)
|
||||
faasHandlers.SecretHandler =
|
||||
decorateExternalAuth(faasHandlers.SecretHandler, config.UpstreamTimeout, config.AuthProxyURL, config.AuthProxyPassBody)
|
||||
auth.DecorateWithBasicAuth(faasHandlers.SecretHandler, credentials)
|
||||
faasHandlers.LogProxyHandler =
|
||||
decorateExternalAuth(faasHandlers.LogProxyHandler, config.UpstreamTimeout, config.AuthProxyURL, config.AuthProxyPassBody)
|
||||
auth.DecorateWithBasicAuth(faasHandlers.LogProxyHandler, credentials)
|
||||
faasHandlers.NamespaceListerHandler =
|
||||
decorateExternalAuth(faasHandlers.NamespaceListerHandler, config.UpstreamTimeout, config.AuthProxyURL, config.AuthProxyPassBody)
|
||||
auth.DecorateWithBasicAuth(faasHandlers.NamespaceListerHandler, credentials)
|
||||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
@ -238,9 +236,11 @@ func main() {
|
||||
uiHandler := http.StripPrefix("/ui", fsCORS)
|
||||
if credentials != nil {
|
||||
r.PathPrefix("/ui/").Handler(
|
||||
decorateExternalAuth(uiHandler.ServeHTTP, config.UpstreamTimeout, config.AuthProxyURL, config.AuthProxyPassBody)).Methods(http.MethodGet)
|
||||
auth.DecorateWithBasicAuth(uiHandler.ServeHTTP, credentials)).
|
||||
Methods(http.MethodGet)
|
||||
} else {
|
||||
r.PathPrefix("/ui/").Handler(uiHandler).Methods(http.MethodGet)
|
||||
r.PathPrefix("/ui/").Handler(uiHandler).
|
||||
Methods(http.MethodGet)
|
||||
}
|
||||
|
||||
//Start metrics server in a goroutine
|
||||
|
Loading…
x
Reference in New Issue
Block a user