Skip to content

Commit

Permalink
moved authentication logic
Browse files Browse the repository at this point in the history
Signed-off-by: Amir Malka <amirm@armosec.io>
  • Loading branch information
amirmalka committed Nov 12, 2023
1 parent 8b8f0de commit 6ed31f9
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 69 deletions.
7 changes: 4 additions & 3 deletions cmd/client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,10 @@ func start(ctx context.Context, cfg config.Config, adapter adapters.Adapter, dia
}
defer conn.Close()

ctx = context.WithValue(ctx, domain.ContextKeyAccessKey, cfg.InCluster.AccessKey) //nolint
ctx = context.WithValue(ctx, domain.ContextKeyAccount, cfg.InCluster.Account) //nolint
ctx = context.WithValue(ctx, domain.ContextKeyClusterName, cfg.InCluster.ClusterName) //nolint
ctx = context.WithValue(ctx, domain.ContextKeyClientIdentifier, domain.ClientIdentifier{
Account: cfg.InCluster.Account,
Cluster: cfg.InCluster.ClusterName,
})

// synchronizer
synchronizer := core.NewSynchronizerClient(ctx, adapter, conn)
Expand Down
67 changes: 67 additions & 0 deletions cmd/server/authentication/authentication.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package authentication

import (
"context"
"net/http"
"net/url"

"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/synchronizer/config"
"github.com/kubescape/synchronizer/core"
"github.com/kubescape/synchronizer/domain"
)

func AuthenticationServerMiddleware(client *http.Client, cfg config.AuthenticationServerConfig, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

accessKey := r.Header.Get(core.AccessKeyHeader)
account := r.Header.Get(core.AccountHeader)
cluster := r.Header.Get(core.ClusterNameHeader)

if accessKey == "" || account == "" || cluster == "" {
w.WriteHeader(http.StatusUnauthorized)
return
}

if client != nil {
u, err := url.Parse(cfg.Url)
if err != nil {
panic(err)
}

// copy headers to authentication request query params (configurable)
q := u.Query()
for header, queryParam := range cfg.HeaderToQueryParamMapping {
q.Set(queryParam, r.Header.Get(header))
}
u.RawQuery = q.Encode()

authenticationRequest, err := http.NewRequestWithContext(r.Context(), http.MethodGet, u.String(), nil)
if err != nil {
logger.L().Error("unable to create authentication request", helpers.Error(err))
w.WriteHeader(http.StatusUnauthorized)
return
}

for origin, dest := range cfg.HeaderToHeaderMapping {
authenticationRequest.Header.Set(dest, r.Header.Get(origin))
}

response, err := client.Do(authenticationRequest)
if err != nil || response.StatusCode != http.StatusOK {
w.WriteHeader(http.StatusUnauthorized)
return
}
}

// create new context with client identifier
ctx := context.WithValue(r.Context(), domain.ContextKeyClientIdentifier, domain.ClientIdentifier{
Account: account,
Cluster: cluster,
})
// create new request using the new context
authenticatedRequest := r.WithContext(ctx)
next.ServeHTTP(w, authenticatedRequest)
})
}
71 changes: 29 additions & 42 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ import (
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/synchronizer/adapters"
"github.com/kubescape/synchronizer/adapters/backend/v1"
"github.com/kubescape/synchronizer/cmd/server/authentication"

"github.com/kubescape/synchronizer/config"
"github.com/kubescape/synchronizer/core"
"github.com/kubescape/synchronizer/domain"
)

var (
authHttpClient *http.Client
)

func main() {
Expand All @@ -31,6 +35,12 @@ func main() {
logger.L().Fatal("unable to load configuration", helpers.Error(err))
}

if cfg.Backend.AuthenticationServer == nil || cfg.Backend.AuthenticationServer.Url == "" {
logger.L().Warning("authentication server is not set; Incoming connections will not be authenticated")
} else {
authHttpClient = &http.Client{}
}

// backend adapter
ctx, cancel := context.WithCancel(ctx)
defer cancel()
Expand Down Expand Up @@ -64,45 +74,22 @@ func main() {
}

// websocket server
_ = http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authorizedCtx, isAuthorized := authorizedContext(ctx, r)
if !isAuthorized {
w.WriteHeader(http.StatusUnauthorized)
return
}

conn, _, _, err := ws.UpgradeHTTP(r, w)
if err != nil {
logger.L().Error("unable to upgrade connection", helpers.Error(err))
return
}
go func() {
defer conn.Close()
synchronizer := core.NewSynchronizerServer(authorizedCtx, adapter, conn)
err = synchronizer.Start(authorizedCtx)
if err != nil {
logger.L().Error("error during sync", helpers.Error(err))
return
}
}()
}))
}

// authorize checks if the request is authorized, and if so, returns an authorized context.
func authorizedContext(ctx context.Context, r *http.Request) (context.Context, bool) {
accessKey := r.Header.Get(core.AccessKeyHeader)
account := r.Header.Get(core.AccountHeader)
cluster := r.Header.Get(core.ClusterNameHeader)

if accessKey == "" || account == "" || cluster == "" {
return ctx, false
}

// TODO: validate access key and account, maybe also cluster name

// updates the context with the access key and account
ctx = context.WithValue(ctx, domain.ContextKeyAccessKey, accessKey) //nolint
ctx = context.WithValue(ctx, domain.ContextKeyAccount, account) //nolint
ctx = context.WithValue(ctx, domain.ContextKeyClusterName, cluster) //nolint
return ctx, true
_ = http.ListenAndServe(":8080",
authentication.AuthenticationServerMiddleware(authHttpClient, *cfg.Backend.AuthenticationServer,
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
conn, _, _, err := ws.UpgradeHTTP(r, w)
if err != nil {
logger.L().Error("unable to upgrade connection", helpers.Error(err))
return
}
go func() {
defer conn.Close()
synchronizer := core.NewSynchronizerServer(r.Context(), adapter, conn)
err = synchronizer.Start(r.Context())
if err != nil {
logger.L().Error("error during sync", helpers.Error(err))
return
}
}()
})))
}
13 changes: 10 additions & 3 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ type Config struct {
}

type Backend struct {
Subscription string `mapstructure:"subscription"`
PulsarConfig *pulsarconfig.PulsarConfig `mapstructure:"pulsarConfig"`
Topic pulsarconnector.TopicName `mapstructure:"topic"`
AuthenticationServer *AuthenticationServerConfig `mapstructure:"authenticationServer"`
Subscription string `mapstructure:"subscription"`
PulsarConfig *pulsarconfig.PulsarConfig `mapstructure:"pulsarConfig"`
Topic pulsarconnector.TopicName `mapstructure:"topic"`
}

type InCluster struct {
Expand All @@ -36,6 +37,12 @@ type Resource struct {
Strategy domain.Strategy `mapstructure:"strategy"`
}

type AuthenticationServerConfig struct {
Url string `mapstructure:"url"`
HeaderToQueryParamMapping map[string]string `mapstructure:"headerToQueryParamMapping"`
HeaderToHeaderMapping map[string]string `mapstructure:"headerToHeaderMapping"`
}

// Kind returns group/version/resource as a string.
func (r Resource) String() string {
return strings.Join([]string{r.Group, r.Version, r.Resource}, "/")
Expand Down
4 changes: 2 additions & 2 deletions configuration/client/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"inCluster": {
"backendUrl": "ws://127.0.0.1:8080/",
"clusterName": "cluster-1",
"account": "285f6ed5-8fb0-4e52-86d4-c29afa6966ac",
"accessKey": "test-123"
"account": "",
"accessKey": ""
},
"resources": [
{
Expand Down
9 changes: 9 additions & 0 deletions configuration/server/config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
{
"backend": {
"authenticationServer": {
"url": "https://api.armosec.io/api/v1",
"headerToHeaderMapping": {
"X-API-KEY": "X-API-KEY"
},
"headerToQueryParamMapping": {
"X-API-ACCOUNT": "customerGUID"
}
},
"subscription": "subscription",
"topic": "synchronizer",
"pulsarConfig": {
Expand Down
10 changes: 3 additions & 7 deletions core/headers.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
package core

import (
beServerV1 "github.com/kubescape/backend/pkg/server/v1"
)

// These headers are required for client-server authentication
const (
AccessKeyHeader = beServerV1.AccessKeyHeader
AccountHeader = "Account"
ClusterNameHeader = "ClusterName"
AccessKeyHeader = "X-API-KEY"
AccountHeader = "X-API-ACCOUNT"
ClusterNameHeader = "X-API-CLUSTER"
)
10 changes: 5 additions & 5 deletions domain/context.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package domain

type contextKey string

const (
ContextKeyAccount = "account"
ContextKeyAccessKey = "accessKey"
ContextKeyClusterName = "clusterName"
ContextKeyDepth = "depth"
ContextKeyMsgId = "msgId"
ContextKeyClientIdentifier contextKey = "clientIdentifier"
ContextKeyDepth contextKey = "depth"
ContextKeyMsgId contextKey = "msgId"
)
5 changes: 1 addition & 4 deletions domain/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,5 @@ func KindFromString(kind string) *Kind {
}

func ClientIdentifierFromContext(ctx context.Context) ClientIdentifier {
return ClientIdentifier{
Cluster: ctx.Value(ContextKeyClusterName).(string),
Account: ctx.Value(ContextKeyAccount).(string),
}
return ctx.Value(ContextKeyClientIdentifier).(ClientIdentifier)
}
7 changes: 4 additions & 3 deletions utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@ func ContextFromGeneric(parent context.Context, generic domain.Generic) context.
}

func ContextFromIdentifiers(parent context.Context, id domain.ClientIdentifier) context.Context {
ctx := context.WithValue(parent, domain.ContextKeyAccount, id.Account)
ctx = context.WithValue(ctx, domain.ContextKeyClusterName, id.Cluster)
return ctx
return context.WithValue(parent, domain.ContextKeyClientIdentifier, domain.ClientIdentifier{
Account: id.Account,
Cluster: id.Cluster,
})
}

func KeyToNsName(key string) (string, string) {
Expand Down

0 comments on commit 6ed31f9

Please sign in to comment.