Skip to content

Commit

Permalink
add upstream auth check
Browse files Browse the repository at this point in the history
  • Loading branch information
Cypherspark committed Sep 7, 2023
1 parent 2bddf43 commit c3ee41a
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 9 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
)

require (
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
Expand Down
72 changes: 63 additions & 9 deletions pkg/auth/authenticator.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"regexp"
"sync"

"github.com/asaskevich/govalidator"
"github.com/go-logr/logr"
cerberusv1alpha1 "github.com/snapp-incubator/Cerberus/api/v1alpha1"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -15,7 +16,8 @@ import (
)

type Authenticator struct {
logger logr.Logger
logger logr.Logger
httpClient *http.Client

accessCache *AccessCache
servicesCache *ServicesCache
Expand All @@ -41,13 +43,17 @@ type ServicesCacheEntry struct {
type CerberusReason string

const (
CerberusReasonOK CerberusReason = "ok"
CerberusReasonUnauthorized CerberusReason = "unauthorized"
CerberusReasonTokenEmpty CerberusReason = "token-empty"
CerberusReasonLookupEmpty CerberusReason = "lookup-empty"
CerberusReasonLookupIdentifierEmpty CerberusReason = "lookup-identifier-empty"
CerberusReasonTokenNotFound CerberusReason = "token-notfound"
CerberusReasonWebserviceNotFound CerberusReason = "webservice-notfound"
CerberusReasonOK CerberusReason = "ok"
CerberusReasonUnauthorized CerberusReason = "unauthorized"
CerberusReasonTokenEmpty CerberusReason = "token-empty"
CerberusReasonLookupEmpty CerberusReason = "lookup-empty"
CerberusReasonLookupIdentifierEmpty CerberusReason = "lookup-identifier-empty"
CerberusReasonTokenNotFound CerberusReason = "token-notfound"
CerberusReasonWebserviceNotFound CerberusReason = "webservice-notfound"
CerberusReasonInvalidUpstreamAddress CerberusReason = "invalid-auth-upstream"
CerberusReasonSourceAuthTokenEmpty CerberusReason = "upstream-source-identifier-empty"
CerberusReasonTargetAuthTokenEmpty CerberusReason = "upstream-target-identifier-empty"
CerberusReasonUpstreamAuthFailed CerberusReason = "upstream-auth-failed"
)

//+kubebuilder:rbac:groups=cerberus.snappcloud.io,resources=accesstokens,verbs=get;list;watch;
Expand Down Expand Up @@ -200,6 +206,9 @@ func (a *Authenticator) Check(ctx context.Context, request *Request) (*Response,
if ok {
ok, reason, extraHeaders = a.TestAccess(wsvc, token)
}
if ok {
ok, reason = a.checkServiceUpstreamAuth(wsvc, request, &extraHeaders)
}

a.logger.Info("checking request", "reason", reason, "req", request)
if ok {
Expand Down Expand Up @@ -228,7 +237,8 @@ func (a *Authenticator) Check(ctx context.Context, request *Request) (*Response,

func NewAuthenticator(logger logr.Logger) (*Authenticator, error) {
a := Authenticator{
logger: logger,
logger: logger,
httpClient: &http.Client{},
}
return &a, nil
}
Expand Down Expand Up @@ -261,3 +271,47 @@ func CheckDomain(domain string, domainAllowedList []string) (bool, error) {
}
return false, nil
}

func (a *Authenticator) checkServiceUpstreamAuth(wsvc string, request *Request, extraHeaders *ExtraHeaders) (bool, CerberusReason) {
service, ok := (*a.servicesCache)[wsvc]
if !ok {
return false, CerberusReasonWebserviceNotFound
}
if service.Spec.UpstreamHttpAuth.ReadTokenFrom == "" {
return false, CerberusReasonSourceAuthTokenEmpty
}
if service.Spec.UpstreamHttpAuth.WriteTokenTo == "" {
return false, CerberusReasonTargetAuthTokenEmpty
}
if !govalidator.IsRequestURL(service.Spec.UpstreamHttpAuth.Address) {
return false, CerberusReasonInvalidUpstreamAddress
}

token := request.Request.Header.Get(service.Spec.UpstreamHttpAuth.ReadTokenFrom)

// TODO: get http method from webservice crd
req, err := http.NewRequest("GET", service.Spec.UpstreamHttpAuth.Address, nil)
if err != nil {
return false, CerberusReasonUpstreamAuthFailed
}

req.Header = http.Header{
service.Spec.UpstreamHttpAuth.WriteTokenTo: {token},
"Content-Type": {"application/json"},
}

resp, err := a.httpClient.Do(req)
if err != nil {
return false, CerberusReasonUpstreamAuthFailed
}

var headersString string
for header, values := range resp.Header {
for _, value := range values {
headersString += header + ": " + value + "\n"
}
}
(*extraHeaders)["X-Cerberus-Upstream-Headers"] = headersString

return true, CerberusReasonOK
}

0 comments on commit c3ee41a

Please sign in to comment.