Skip to content

Commit

Permalink
EKS
Browse files Browse the repository at this point in the history
  • Loading branch information
mattlandis authored and wongma7 committed Nov 8, 2019
1 parent f3b285c commit 47f0d8e
Show file tree
Hide file tree
Showing 8 changed files with 552 additions and 12 deletions.
5 changes: 5 additions & 0 deletions Dockerfile.eks
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM amazonlinux:2
RUN mkdir -p /var/lib/kubernetes && touch /var/lib/kubernetes/empty
RUN yum update -y
COPY heptio-authenticator-aws /kubernetes-aws-authenticator
ENTRYPOINT ["/kubernetes-aws-authenticator"]
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ ifndef GORELEASER
$(error "goreleaser not found (`go get -u -v github.com/goreleaser/goreleaser` to fix)")
endif
$(GORELEASER) --skip-publish --rm-dist --snapshot

build-eks: test
CGO_ENABLED=0 go build $(GITHUB_REPO)/cmd/heptio-authenticator-aws

build-container-eks:
docker run -v $(shell pwd):/go/src/github.com/heptio/authenticator \
--workdir=/go/src/github.com/heptio/authenticator \
--env GOPATH=/go \
golang:1.10 make build-eks

container-eks: build-container-eks
docker build --network host -f Dockerfile.eks -t authenticator:latest .

test:
go test -v -cover -race $(GITHUB_REPO)/...
Expand Down
1 change: 1 addition & 0 deletions cmd/aws-iam-authenticator/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ func getConfig() (config.Config, error) {
Kubeconfig: viper.GetString("server.kubeconfig"),
Master: viper.GetString("server.master"),
FeatureGates: featureGates,
UpdateConfigFromConfigMap: viper.GetBool("server.UpdateConfigFromConfigMap"),
}
if err := viper.UnmarshalKey("server.mapRoles", &cfg.RoleMappings); err != nil {
return cfg, fmt.Errorf("invalid server role mappings: %v", err)
Expand Down
3 changes: 3 additions & 0 deletions pkg/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,7 @@ type Config struct {

// FeatureGates stores all the mutable feature gates for controlling how the authenticator works
FeatureGates featuregate.MutableFeatureGate

// UpdateConfigFromConfigMap is set to true to enable loading mappings from the api server configmap.
UpdateConfigFromConfigMap bool
}
201 changes: 201 additions & 0 deletions pkg/mappings/configmap/configmap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
package configmap

import (
"errors"
"sync"

"fmt"

"strings"
"time"

"github.com/heptio/authenticator/pkg/config"
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
core_v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/tools/clientcmd"
)

type MapStore struct {
mutex sync.RWMutex
users map[string]config.UserMapping
roles map[string]config.RoleMapping
// TODO: Use kubernetes set.
// Used as set.
awsAccounts map[string]interface{}
configMap v1.ConfigMapInterface
}

func New(masterURL, kubeConfig string) (*MapStore, error) {
clientconfig, err := clientcmd.BuildConfigFromFlags(masterURL, kubeConfig)
if err != nil {
return nil, err
}
clientset, err := kubernetes.NewForConfig(clientconfig)
if err != nil {
return nil, err
}

ms := MapStore{}
// TODO: Should we use a namespace? Make it configurable?
ms.configMap = clientset.CoreV1().ConfigMaps("kube-system")
ms.startLoadConfigMap()
return &ms, nil
}

// Starts a go routine which will watch the configmap and update the in memory data
// when the values change.
func (ms *MapStore) startLoadConfigMap() {
go func() {
for {
watcher, err := ms.configMap.Watch(metav1.ListOptions{
Watch: true,
FieldSelector: fields.OneTermEqualSelector("metadata.name", "aws-auth").String(),
})
if err != nil {
logrus.Warn("Unable to re-establish watch. Sleeping for 5 seconds")
time.Sleep(5 * time.Second)
continue
}
for r := range watcher.ResultChan() {
switch r.Type {
case watch.Error:
logrus.WithFields(logrus.Fields{"error": r}).Error("recieved a watch error")
case watch.Deleted:
logrus.Info("Resetting configmap on delete")
userMappings := make([]config.UserMapping, 0)
roleMappings := make([]config.RoleMapping, 0)
awsAccounts := make([]string, 0)
ms.saveMap(userMappings, roleMappings, awsAccounts)
case watch.Added, watch.Modified:
// Type assertion is not working
//
// cm, ok := r.Object.(*core_v1.ConfigMap)
//if !ok || cm.Name != "aws-auth" {
// break
//}
switch cm := r.Object.(type) {
case *core_v1.ConfigMap:
// TODO: Only watch on configmap/awsauth
if cm.Name != "aws-auth" {
break
}
logrus.Info("Received aws-auth watch event")
userMappings, roleMappings, awsAccounts, err := ms.parseMap(cm.Data)
if err != nil {
logrus.Errorf("There was an error parsing the config maps. Only saving data that was good, %+v", err)
}
ms.saveMap(userMappings, roleMappings, awsAccounts)
if err != nil {
logrus.Error(err)
}
}

}
}
logrus.Error("Watch channel closed.")
}
}()
}

type ErrParsingMap struct {
errors []error
}

func (err ErrParsingMap) Error() string {
return fmt.Sprintf("error parsing config map: %v", err.errors)
}

// Acquire lock before calling
func (ms *MapStore) parseMap(m map[string]string) ([]config.UserMapping, []config.RoleMapping, []string, error) {
// TODO: Look at errors.Wrap().
errs := make([]error, 0)
userMappings := make([]config.UserMapping, 0)
if userData, ok := m["mapUsers"]; ok {
err := yaml.Unmarshal([]byte(userData), &userMappings)
if err != nil {
errs = append(errs, err)
}
}

roleMappings := make([]config.RoleMapping, 0)
if roleData, ok := m["mapRoles"]; ok {
err := yaml.Unmarshal([]byte(roleData), &roleMappings)
if err != nil {
errs = append(errs, err)
}
}

awsAccounts := make([]string, 0)
if accountsData, ok := m["mapAccounts"]; ok {
err := yaml.Unmarshal([]byte(accountsData), &awsAccounts)
if err != nil {
errs = append(errs, err)
}
}

// TODO: Check for empty user and role mappings.

var err error
if len(errs) > 0 {
logrus.Warnf("Errors parsing configmap: %+v", errs)
err = ErrParsingMap{errors: errs}
}
return userMappings, roleMappings, awsAccounts, err
}

func (ms *MapStore) saveMap(userMappings []config.UserMapping, roleMappings []config.RoleMapping, awsAccounts []string) {
ms.mutex.Lock()
defer ms.mutex.Unlock()
ms.users = make(map[string]config.UserMapping)
ms.roles = make(map[string]config.RoleMapping)
ms.awsAccounts = make(map[string]interface{})

for _, user := range userMappings {
ms.users[strings.ToLower(user.UserARN)] = user
}
for _, role := range roleMappings {
ms.roles[strings.ToLower(role.RoleARN)] = role
}
for _, awsAccount := range awsAccounts {
ms.awsAccounts[awsAccount] = nil
}
}

// UserNotFound is the error returned when the user is not found in the config map.
var UserNotFound = errors.New("User not found in configmap")

// RoleNotFound is the error returned when the role is not found in the config map.
var RoleNotFound = errors.New("Role not found in configmap")

func (ms *MapStore) UserMapping(arn string) (config.UserMapping, error) {
ms.mutex.RLock()
defer ms.mutex.RUnlock()
if user, ok := ms.users[arn]; !ok {
return config.UserMapping{}, UserNotFound
} else {
return user, nil
}
}

func (ms *MapStore) RoleMapping(arn string) (config.RoleMapping, error) {
ms.mutex.RLock()
defer ms.mutex.RUnlock()
if role, ok := ms.roles[arn]; !ok {
return config.RoleMapping{}, RoleNotFound
} else {
return role, nil
}
}

func (ms *MapStore) AWSAccount(id string) bool {
ms.mutex.RLock()
defer ms.mutex.RUnlock()
_, ok := ms.awsAccounts[id]
return ok
}

0 comments on commit 47f0d8e

Please sign in to comment.