sigs.k8s.io/cluster-api-provider-aws@v1.5.5/pkg/cloud/services/iamauth/configmap.go (about)

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package iamauth
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	corev1 "k8s.io/api/core/v1"
    25  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/types"
    28  	kerrors "k8s.io/apimachinery/pkg/util/errors"
    29  	crclient "sigs.k8s.io/controller-runtime/pkg/client"
    30  	"sigs.k8s.io/yaml"
    31  
    32  	ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/controlplane/eks/api/v1beta1"
    33  )
    34  
    35  const (
    36  	configMapName = "aws-auth"
    37  	configMapNS   = metav1.NamespaceSystem
    38  
    39  	roleKey  = "mapRoles"
    40  	usersKey = "mapUsers"
    41  )
    42  
    43  type configMapBackend struct {
    44  	client crclient.Client
    45  }
    46  
    47  func (b *configMapBackend) MapRole(mapping ekscontrolplanev1.RoleMapping) error {
    48  	if errs := mapping.Validate(); errs != nil {
    49  		return kerrors.NewAggregate(errs)
    50  	}
    51  
    52  	authConfig, err := b.getAuthConfig()
    53  	if err != nil {
    54  		return fmt.Errorf("getting auth config: %w", err)
    55  	}
    56  
    57  	for _, existingMapping := range authConfig.RoleMappings {
    58  		if cmp.Equal(existingMapping, mapping) {
    59  			// A mapping already exists that matches, so ignore
    60  			return nil
    61  		}
    62  	}
    63  
    64  	authConfig.RoleMappings = append(authConfig.RoleMappings, mapping)
    65  
    66  	return b.saveAuthConfig(authConfig)
    67  }
    68  
    69  func (b *configMapBackend) MapUser(mapping ekscontrolplanev1.UserMapping) error {
    70  	if errs := mapping.Validate(); errs != nil {
    71  		return kerrors.NewAggregate(errs)
    72  	}
    73  
    74  	authConfig, err := b.getAuthConfig()
    75  	if err != nil {
    76  		return fmt.Errorf("getting auth config: %w", err)
    77  	}
    78  
    79  	for _, existingMapping := range authConfig.UserMappings {
    80  		if cmp.Equal(existingMapping, mapping) {
    81  			// A mapping already exists that matches, so ignore
    82  			return nil
    83  		}
    84  	}
    85  
    86  	authConfig.UserMappings = append(authConfig.UserMappings, mapping)
    87  
    88  	return b.saveAuthConfig(authConfig)
    89  }
    90  
    91  func (b *configMapBackend) getAuthConfig() (*ekscontrolplanev1.IAMAuthenticatorConfig, error) {
    92  	ctx := context.Background()
    93  
    94  	configMapRef := types.NamespacedName{
    95  		Name:      configMapName,
    96  		Namespace: configMapNS,
    97  	}
    98  
    99  	authConfigMap := &corev1.ConfigMap{}
   100  
   101  	err := b.client.Get(ctx, configMapRef, authConfigMap)
   102  	if err != nil && !apierrors.IsNotFound(err) {
   103  		return nil, fmt.Errorf("getting %s/%s config map: %w", configMapName, configMapNS, err)
   104  	}
   105  
   106  	authConfig := &ekscontrolplanev1.IAMAuthenticatorConfig{
   107  		RoleMappings: []ekscontrolplanev1.RoleMapping{},
   108  		UserMappings: []ekscontrolplanev1.UserMapping{},
   109  	}
   110  	if authConfigMap.Data == nil {
   111  		return authConfig, nil
   112  	}
   113  
   114  	mappedRoles, err := b.getMappedRoles(authConfigMap)
   115  	if err != nil {
   116  		return nil, fmt.Errorf("getting mapped roles: %w", err)
   117  	}
   118  	authConfig.RoleMappings = mappedRoles
   119  
   120  	mappedUsers, err := b.getMappedUsers(authConfigMap)
   121  	if err != nil {
   122  		return nil, fmt.Errorf("getting mapped users: %w", err)
   123  	}
   124  	authConfig.UserMappings = mappedUsers
   125  
   126  	return authConfig, nil
   127  }
   128  
   129  func (b *configMapBackend) saveAuthConfig(authConfig *ekscontrolplanev1.IAMAuthenticatorConfig) error {
   130  	ctx := context.Background()
   131  
   132  	configMapRef := types.NamespacedName{
   133  		Name:      configMapName,
   134  		Namespace: configMapNS,
   135  	}
   136  
   137  	authConfigMap := &corev1.ConfigMap{}
   138  
   139  	err := b.client.Get(ctx, configMapRef, authConfigMap)
   140  	if err != nil && !apierrors.IsNotFound(err) {
   141  		return fmt.Errorf("getting %s/%s config map: %w", configMapName, configMapNS, err)
   142  	}
   143  
   144  	if authConfigMap.Data == nil {
   145  		authConfigMap.Data = make(map[string]string)
   146  	}
   147  	authConfigMap = authConfigMap.DeepCopy()
   148  
   149  	delete(authConfigMap.Data, roleKey)
   150  	delete(authConfigMap.Data, usersKey)
   151  
   152  	if len(authConfig.RoleMappings) > 0 {
   153  		roleMappings, err := yaml.Marshal(authConfig.RoleMappings)
   154  		if err != nil {
   155  			return fmt.Errorf("marshalling auth config roles: %w", err)
   156  		}
   157  		authConfigMap.Data[roleKey] = string(roleMappings)
   158  	}
   159  
   160  	if len(authConfig.UserMappings) > 0 {
   161  		userMappings, err := yaml.Marshal(authConfig.UserMappings)
   162  		if err != nil {
   163  			return fmt.Errorf("marshalling auth config users: %w", err)
   164  		}
   165  		authConfigMap.Data[usersKey] = string(userMappings)
   166  	}
   167  
   168  	if authConfigMap.UID == "" {
   169  		authConfigMap.Name = configMapName
   170  		authConfigMap.Namespace = configMapNS
   171  		return b.client.Create(ctx, authConfigMap)
   172  	}
   173  
   174  	return b.client.Update(ctx, authConfigMap)
   175  }
   176  
   177  func (b *configMapBackend) getMappedRoles(cm *corev1.ConfigMap) ([]ekscontrolplanev1.RoleMapping, error) {
   178  	mappedRoles := []ekscontrolplanev1.RoleMapping{}
   179  
   180  	rolesSection, ok := cm.Data[roleKey]
   181  	if !ok {
   182  		return mappedRoles, nil
   183  	}
   184  
   185  	if err := yaml.Unmarshal([]byte(rolesSection), &mappedRoles); err != nil {
   186  		return nil, fmt.Errorf("unmarshalling mapped roles: %w", err)
   187  	}
   188  
   189  	return mappedRoles, nil
   190  }
   191  
   192  func (b *configMapBackend) getMappedUsers(cm *corev1.ConfigMap) ([]ekscontrolplanev1.UserMapping, error) {
   193  	mappedUsers := []ekscontrolplanev1.UserMapping{}
   194  
   195  	usersSection, ok := cm.Data[usersKey]
   196  	if !ok {
   197  		return mappedUsers, nil
   198  	}
   199  
   200  	if err := yaml.Unmarshal([]byte(usersSection), &mappedUsers); err != nil {
   201  		return nil, fmt.Errorf("unmarshalling mapped users: %w", err)
   202  	}
   203  
   204  	return mappedUsers, nil
   205  }