Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add authentication to grpc api calls in frontend #299

Merged
merged 11 commits into from
Aug 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/blendle/zapdriver v1.3.1
github.com/cenkalti/backoff/v4 v4.1.2
github.com/go-git/go-billy/v5 v5.3.1
github.com/golang-jwt/jwt/v4 v4.4.2
github.com/google/go-cmp v0.5.8
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/hashicorp/golang-lru v0.5.4
Expand All @@ -29,6 +30,7 @@ require (
cloud.google.com/go v0.65.0 // indirect
github.com/DataDog/datadog-go v4.4.0+incompatible // indirect
github.com/DataDog/sketches-go v1.0.0 // indirect
github.com/MicahParks/keyfunc v1.2.2 // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
github.com/go-logr/logr v0.4.0 // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ github.com/DataDog/datadog-go/v5 v5.0.2/go.mod h1:ZI9JFB4ewXbw1sBnF4sxsR2k1H3xjV
github.com/DataDog/gostackparse v0.5.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM=
github.com/DataDog/sketches-go v1.0.0 h1:chm5KSXO7kO+ywGWJ0Zs6tdmWU8PBXSbywFVciL6BG4=
github.com/DataDog/sketches-go v1.0.0/go.mod h1:O+XkJHWk9w4hDwY2ZUDU31ZC9sNYlYo8DiFsxjYeo1k=
github.com/MicahParks/keyfunc v1.2.2 h1:MtA/6bnmhcyDMarVF9QRQmfKg1ssKxH1C+k3Gw8DY1g=
github.com/MicahParks/keyfunc v1.2.2/go.mod h1:GWZYIBflWXRPShQPMFRrUg+8buvyD0IWeg3Fi2rcrME=
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
Expand Down Expand Up @@ -122,6 +124,9 @@ github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
Expand Down
149 changes: 149 additions & 0 deletions pkg/auth/azure.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*This file is part of kuberpult.

Kuberpult is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Kuberpult is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with kuberpult. If not, see <http://www.gnu.org/licenses/>.

Copyright 2021 freiheit.com*/
package auth

import (
"context"
"fmt"
"log"
"net/http"
"time"

"github.com/MicahParks/keyfunc"
jwt "github.com/golang-jwt/jwt/v4"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)

func JWKSInitAzure(ctx context.Context) (*keyfunc.JWKS, error) {
jwksURL := "https://login.microsoftonline.com/common/discovery/v2.0/keys"
options := keyfunc.Options{
Ctx: ctx,
RefreshErrorHandler: func(err error) {
log.Printf("There was an error with the jwt.Keyfunc. Error: %s", err.Error())
},
RefreshInterval: time.Hour,
RefreshRateLimit: time.Minute * 5,
RefreshTimeout: time.Second * 10,
RefreshUnknownKID: true,
}
var err error
jwks, err := keyfunc.Get(jwksURL, options)
if err != nil {
return nil, fmt.Errorf("Failed to create JWKS from resource at the given URL. Error: %s", err.Error())
}
return jwks, nil
}

func ValidateToken(jwtB64 string, jwks *keyfunc.JWKS, clientId string, tenantId string) error {
var token *jwt.Token
if jwks == nil {
return fmt.Errorf("JWKS not initialized.")
}
claims := jwt.MapClaims{}
token, err := jwt.ParseWithClaims(jwtB64, claims, jwks.Keyfunc)
if err != nil {
return fmt.Errorf("Failed to parse the JWT.\nError: %s", err.Error())
}
if !token.Valid {
return fmt.Errorf("Invalid token provided.")
}
if val, ok := claims["aud"]; ok {
if val != clientId {
return fmt.Errorf("Unknown client id provided: %s", val)
}
} else {
return fmt.Errorf("Client id not found in token.")
}

if val, ok := claims["tid"]; ok {
if val != tenantId {
return fmt.Errorf("Unknown tenant id provided: %s", val)
}
} else {
return fmt.Errorf("Tenant id not found in token.")
}

return nil
}

func authorize(ctx context.Context, jwks *keyfunc.JWKS, clientId string, tenantId string) error {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return status.Errorf(codes.InvalidArgument, "Retrieving metadata failed")
}

authHeader, ok := md["authorization"]
if !ok {
return status.Errorf(codes.Unauthenticated, "Authorization token not supplied")
}

token := authHeader[0]
err := ValidateToken(token, jwks, clientId, tenantId)

if err != nil {
return status.Errorf(codes.Unauthenticated, "Invalid authorization token provided")
}
return nil
}

func UnaryInterceptor(ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
jwks *keyfunc.JWKS,
clientId string,
tenantId string) (interface{}, error) {
if info.FullMethod != "/api.v1.FrontendConfigService/GetConfig" {
err := authorize(ctx, jwks, clientId, tenantId)
if err != nil {
return nil, err
}
}

h, err := handler(ctx, req)
return h, err
}

func StreamInterceptor(
srv interface{},
stream grpc.ServerStream,
info *grpc.StreamServerInfo,
handler grpc.StreamHandler,
jwks *keyfunc.JWKS,
clientId string,
tenantId string,

) error {
err := authorize(stream.Context(), jwks, clientId, tenantId)
if err != nil {
return err
}
return handler(srv, stream)
}

func HttpAuthMiddleWare(resp http.ResponseWriter, req *http.Request, jwks *keyfunc.JWKS, clientId string, tenantId string) error {
token := req.Header.Get("authorization")
err := ValidateToken(token, jwks, clientId, tenantId)
if err != nil {
resp.WriteHeader(http.StatusUnauthorized)
resp.Write([]byte("Invalid authorization header provided"))
}
return err
}
Loading