Hide setup key from non-admin users (#539)

This commit is contained in:
Misha Bragin 2022-11-03 17:02:31 +01:00 committed by GitHub
parent 2e0bf61e9a
commit 6aa7a2c5e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 48 additions and 12 deletions

View File

@ -214,8 +214,9 @@ func isBuiltinModule(name string) (bool, error) {
} }
// /proc/modules // /proc/modules
// name | memory size | reference count | references | state: <Live|Loading|Unloading> //
// macvlan 28672 1 macvtap, Live 0x0000000000000000 // name | memory size | reference count | references | state: <Live|Loading|Unloading>
// macvlan 28672 1 macvtap, Live 0x0000000000000000
func moduleStatus(name string) (status, error) { func moduleStatus(name string) (status, error) {
state := unknown state := unknown
f, err := os.Open("/proc/modules") f, err := os.Open("/proc/modules")

View File

@ -23,7 +23,7 @@ func APIHandler(accountManager s.AccountManager, authIssuer string, authAudience
corsMiddleware := cors.AllowAll() corsMiddleware := cors.AllowAll()
acMiddleware := middleware.NewAccessControll( acMiddleware := middleware.NewAccessControl(
authAudience, authAudience,
accountManager.IsUserAdmin) accountManager.IsUserAdmin)

View File

@ -1,32 +1,38 @@
package middleware package middleware
import ( import (
"context"
"fmt" "fmt"
"net/http" "net/http"
"github.com/netbirdio/netbird/management/server/jwtclaims" "github.com/netbirdio/netbird/management/server/jwtclaims"
) )
const (
IsUserAdminProperty = "isAdminUser"
)
type IsUserAdminFunc func(claims jwtclaims.AuthorizationClaims) (bool, error) type IsUserAdminFunc func(claims jwtclaims.AuthorizationClaims) (bool, error)
// AccessControll middleware to restrict to make POST/PUT/DELETE requests by admin only // AccessControl middleware to restrict to make POST/PUT/DELETE requests by admin only
type AccessControll struct { type AccessControl struct {
jwtExtractor jwtclaims.ClaimsExtractor jwtExtractor jwtclaims.ClaimsExtractor
isUserAdmin IsUserAdminFunc isUserAdmin IsUserAdminFunc
audience string audience string
} }
// NewAccessControll instance constructor // NewAccessControl instance constructor
func NewAccessControll(audience string, isUserAdmin IsUserAdminFunc) *AccessControll { func NewAccessControl(audience string, isUserAdmin IsUserAdminFunc) *AccessControl {
return &AccessControll{ return &AccessControl{
isUserAdmin: isUserAdmin, isUserAdmin: isUserAdmin,
audience: audience, audience: audience,
jwtExtractor: *jwtclaims.NewClaimsExtractor(nil), jwtExtractor: *jwtclaims.NewClaimsExtractor(nil),
} }
} }
// Handler method of the middleware which forbinneds all modify requests for non admin users // Handler method of the middleware which forbids all modify requests for non admin users
func (a *AccessControll) Handler(h http.Handler) http.Handler { // It also adds
func (a *AccessControl) Handler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
jwtClaims := a.jwtExtractor.ExtractClaimsFromRequestContext(r, a.audience) jwtClaims := a.jwtExtractor.ExtractClaimsFromRequestContext(r, a.audience)
@ -34,7 +40,6 @@ func (a *AccessControll) Handler(h http.Handler) http.Handler {
if err != nil { if err != nil {
http.Error(w, fmt.Sprintf("error get user from JWT: %v", err), http.StatusUnauthorized) http.Error(w, fmt.Sprintf("error get user from JWT: %v", err), http.StatusUnauthorized)
return return
} }
if !ok { if !ok {
@ -45,6 +50,10 @@ func (a *AccessControll) Handler(h http.Handler) http.Handler {
} }
} }
newRequest := r.Clone(context.WithValue(r.Context(), IsUserAdminProperty, ok)) //nolint
// Update the current request with the new context information.
*r = *newRequest
h.ServeHTTP(w, r) h.ServeHTTP(w, r)
}) })
} }

View File

@ -6,12 +6,15 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/netbirdio/netbird/management/server" "github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/http/api" "github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/middleware"
"github.com/netbirdio/netbird/management/server/jwtclaims" "github.com/netbirdio/netbird/management/server/jwtclaims"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"net/http" "net/http"
"strings"
"time" "time"
"unicode/utf8"
) )
// SetupKeys is a handler that returns a list of setup keys of the account // SetupKeys is a handler that returns a list of setup keys of the account
@ -107,6 +110,11 @@ func (h *SetupKeys) GetSetupKeyHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
isUserAdmin, ok := r.Context().Value(middleware.IsUserAdminProperty).(bool)
if !ok || !isUserAdmin {
key = hideKey(key)
}
writeSuccess(w, key) writeSuccess(w, key)
} }
@ -175,6 +183,11 @@ func (h *SetupKeys) GetAllSetupKeysHandler(w http.ResponseWriter, r *http.Reques
return return
} }
isUserAdmin, ok := r.Context().Value(middleware.IsUserAdminProperty).(bool)
if !ok {
isUserAdmin = false
}
setupKeys, err := h.accountManager.ListSetupKeys(account.Id) setupKeys, err := h.accountManager.ListSetupKeys(account.Id)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
@ -183,12 +196,23 @@ func (h *SetupKeys) GetAllSetupKeysHandler(w http.ResponseWriter, r *http.Reques
} }
apiSetupKeys := make([]*api.SetupKey, 0) apiSetupKeys := make([]*api.SetupKey, 0)
for _, key := range setupKeys { for _, key := range setupKeys {
apiSetupKeys = append(apiSetupKeys, toResponseBody(key)) k := key.Copy()
if !isUserAdmin {
k = hideKey(key)
}
apiSetupKeys = append(apiSetupKeys, toResponseBody(k))
} }
writeJSONObject(w, apiSetupKeys) writeJSONObject(w, apiSetupKeys)
} }
func hideKey(key *server.SetupKey) *server.SetupKey {
k := key.Copy()
prefix := k.Key[0:5]
k.Key = prefix + strings.Repeat("*", utf8.RuneCountInString(key.Key)-len(prefix))
return k
}
func writeSuccess(w http.ResponseWriter, key *server.SetupKey) { func writeSuccess(w http.ResponseWriter, key *server.SetupKey) {
w.WriteHeader(200) w.WriteHeader(200)
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")

View File

@ -2,6 +2,7 @@ package http
import ( import (
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/gorilla/mux" "github.com/gorilla/mux"
@ -159,6 +160,7 @@ func TestSetupKeysHandlers(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody) req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody)
req = req.Clone(context.WithValue(context.TODO(), "isAdminUser", true)) //nolint
router := mux.NewRouter() router := mux.NewRouter()
router.HandleFunc("/api/setup-keys", handler.GetAllSetupKeysHandler).Methods("GET", "OPTIONS") router.HandleFunc("/api/setup-keys", handler.GetAllSetupKeysHandler).Methods("GET", "OPTIONS")