Skip to content
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
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ require (
k8s.io/client-go v0.36.1
)

require gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect
require (
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect
)

require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yosssi/boltstore v1.0.1-0.20150916121936-36632d491655 h1:6MtnxkRnXNw3zdUX/y271QqrkSw89vGpqfO76brQ7+4=
github.com/yosssi/boltstore v1.0.1-0.20150916121936-36632d491655/go.mod h1:zSl4HJqNcQ6mxETA0wdp73Pu6rgP37VWQh+xer52324=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
Expand Down
13 changes: 6 additions & 7 deletions handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func (s *server) callback(w http.ResponseWriter, r *http.Request) {
}

// ZONE: Get the authservice cookie value to store it in a k8s secret for easy access with calls from notebook pods
if err := setupZoneK8sSecret(w, userID, s.kubeclient, oauth2Tokens); err != nil {
if err := setupZoneK8sSecret(w, userID, s.kubeclient, s.roleBindingLister, oauth2Tokens); err != nil {
logger.Errorf("Couldn't create or update the oidc-authservice secret: %v", err)
}

Expand Down Expand Up @@ -418,12 +418,11 @@ func (s *server) getPassthroughToken(w http.ResponseWriter, r *http.Request) {
// Convert response body to JSON
decoder := json.NewDecoder(res.Body)
tokenResponse := struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int64 `json:"expires_in"`
Scope string `json:"scope"`
RefreshToken string `json:"refresh_token"`
ExpiresOn int64 `json:"expires_on"` // Not actually in response body
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int64 `json:"expires_in"`
Scope string `json:"scope"`
ExpiresOn int64 `json:"expires_on"` // Not actually in response body
}{}
err = decoder.Decode(&tokenResponse)
if err != nil {
Expand Down
13 changes: 12 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import (
"github.com/yosssi/boltstore/reaper"
"github.com/yosssi/boltstore/store"
"golang.org/x/oauth2"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
v1 "k8s.io/client-go/listers/rbac/v1"
clientconfig "sigs.k8s.io/controller-runtime/pkg/client/config"
)

Expand Down Expand Up @@ -50,7 +52,8 @@ type server struct {
caBundle []byte

// ZONE: K8s client for creating k8s resources
kubeclient *kubernetes.Clientset
kubeclient *kubernetes.Clientset
roleBindingLister v1.RoleBindingLister
}

type userIDOpts struct {
Expand Down Expand Up @@ -175,6 +178,13 @@ func main() {
restConfig, err := clientconfig.GetConfig()
kubeclient := kubernetes.NewForConfigOrDie(restConfig)

// Zone: setup a rolebinding lister to get namespaces from emails
informerFactory := informers.NewSharedInformerFactory(kubeclient, time.Minute*60)
roleBindingLister := informerFactory.Rbac().V1().RoleBindings().Lister()
stop := make(chan struct{})
informerFactory.Start(stop)
informerFactory.WaitForCacheSync(stop)

// Set the server values.
// The isReady atomic variable should protect it from concurrency issues.

Expand All @@ -199,6 +209,7 @@ func main() {
sessionMaxAgeSeconds: sessionMaxAgeSeconds,
caBundle: caBundle,
kubeclient: kubeclient,
roleBindingLister: roleBindingLister,
}

// Setup complete, mark server ready
Expand Down
55 changes: 33 additions & 22 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ import (
"net/http"
"net/url"
"os"
"regexp"
"strings"

log "github.com/sirupsen/logrus"
"golang.org/x/oauth2"
v1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/kubernetes"
listerv1 "k8s.io/client-go/listers/rbac/v1"
)

func loggerForRequest(r *http.Request) *log.Entry {
Expand Down Expand Up @@ -143,27 +144,36 @@ func doRequest(ctx context.Context, req *http.Request) (*http.Response, error) {
return client.Do(req.WithContext(ctx))
}

// Zone: Helper function to convert the userID(email) into a namespace value
func getNamespaceFromEmail(email string) string {
namespaceName := ""

split_userID := strings.Split(email, "@")
if len(split_userID) > 0 {
username := split_userID[0]

// Remove any non-alphanumeric and non-underscore character from username
regexNonAlpha := regexp.MustCompile(`[^\w]|\.`)
regexNonAlphaResult := regexNonAlpha.ReplaceAllString(username, "-")
// Zone: return the namespace that is associated with the given userID(email)
// Uses a RoleBinding lister to match the userID to the namespace
func getNamespaceFromRoleBindings(userID string, roleBindingLister listerv1.RoleBindingLister) (string, error) {
// List the role bindings in all namespaces
roleBindings, err := roleBindingLister.RoleBindings("").List(labels.Everything())
if err != nil {
return "", err
}

// Remove any leading or trailing underscores from username
regexTrail := regexp.MustCompile(`^-+|-+$|_`)
regexTrailResult := regexTrail.ReplaceAllString(regexNonAlphaResult, "")
namespace := ""
// for every roleBinding, check if the user annotation matches the given userID
for _, roleBinding := range roleBindings {
userVal, ok := roleBinding.Annotations["user"]
if !ok {
continue
}
if userID != "" && userID != userVal {
continue
}
// if the rolebinding that matches the userID is found, break out of the loop
namespace = roleBinding.Namespace
break
}

// lower case the namespace value
namespaceName = strings.ToLower(regexTrailResult)
// return an error if no namespace was found
if namespace == "" {
return "", fmt.Errorf("The namespace could not be retrieved for userID %s", userID)
}

return namespaceName
return namespace, nil
}

// Zone: Updates the K8s secret for the authenticated user's with their authservice session cookie value and the access token's expiry time.
Expand Down Expand Up @@ -213,10 +223,11 @@ func updateZoneSecret(kubeclient *kubernetes.Clientset, namespace string, tokenE
// Zone: Gets the namespace of the requesting user
// and the authservice session ID value from the cookies of the ResponseWriter.
// Proceeds to create/update the user's authservice k8s secret.
func setupZoneK8sSecret(w http.ResponseWriter, userID string, kubeclient *kubernetes.Clientset, oauth2Tokens *oauth2.Token) error {
// Get namespace from userID(which should be an email)
namespace := getNamespaceFromEmail(userID)
if namespace == "" {
func setupZoneK8sSecret(w http.ResponseWriter, userID string, kubeclient *kubernetes.Clientset,
roleBindingLister listerv1.RoleBindingLister, oauth2Tokens *oauth2.Token) error {
// Get namespace value for userID(which should be an email) from the roleBindings
namespace, err := getNamespaceFromRoleBindings(userID, roleBindingLister)
if err != nil {
return fmt.Errorf("Couldn't get namespace for userID: %s. Skipping creating the K8s secret", userID)
}

Expand Down
Loading