Merge pull request #9375 from rifelpet/irsa_no_api

Add support for AWS OIDC Provider
This commit is contained in:
Kubernetes Prow Robot 2020-06-16 14:08:04 -07:00 committed by GitHub
commit 97181b911f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 438 additions and 0 deletions

View File

@ -7,6 +7,7 @@ go_library(
"iaminstanceprofile.go",
"iamrole.go",
"iamrolepolicy.go",
"oidcprovider.go",
],
importpath = "k8s.io/kops/cloudmock/aws/mockiam",
visibility = ["//visibility:public"],

View File

@ -32,6 +32,7 @@ type MockIAM struct {
mutex sync.Mutex
InstanceProfiles map[string]*iam.InstanceProfile
Roles map[string]*iam.Role
OIDCProviders map[string]*iam.GetOpenIDConnectProviderOutput
RolePolicies []*rolePolicy
AttachedPolicies map[string][]*iam.AttachedPolicy
}

View File

@ -0,0 +1,123 @@
/*
Copyright 2020 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mockiam
import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/service/iam"
"k8s.io/klog"
)
func (m *MockIAM) ListOpenIDConnectProviders(request *iam.ListOpenIDConnectProvidersInput) (*iam.ListOpenIDConnectProvidersOutput, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
providers := make([]*iam.OpenIDConnectProviderListEntry, 0)
for arn := range m.OIDCProviders {
providers = append(providers, &iam.OpenIDConnectProviderListEntry{
Arn: &arn,
})
}
response := &iam.ListOpenIDConnectProvidersOutput{
OpenIDConnectProviderList: providers,
}
return response, nil
}
func (m *MockIAM) ListOpenIDConnectProvidersWithContext(aws.Context, *iam.ListOpenIDConnectProvidersInput, ...request.Option) (*iam.ListOpenIDConnectProvidersOutput, error) {
panic("Not implemented")
}
func (m *MockIAM) ListOpenIDConnectProvidersRequest(*iam.ListOpenIDConnectProvidersInput) (*request.Request, *iam.ListOpenIDConnectProvidersOutput) {
panic("Not implemented")
}
func (m *MockIAM) GetOpenIDConnectProviderWithContext(aws.Context, *iam.GetOpenIDConnectProviderInput, ...request.Option) (*iam.GetOpenIDConnectProviderOutput, error) {
panic("Not implemented")
}
func (m *MockIAM) GetOpenIDConnectProvider(*iam.GetOpenIDConnectProviderInput) (*iam.GetOpenIDConnectProviderOutput, error) {
panic("Not implemented")
}
func (m *MockIAM) GetOpenIDConnectProviderRequest(*iam.GetOpenIDConnectProviderInput) (*request.Request, *iam.GetOpenIDConnectProviderOutput) {
panic("Not implemented")
}
func (m *MockIAM) CreateOpenIDConnectProvider(request *iam.CreateOpenIDConnectProviderInput) (*iam.CreateOpenIDConnectProviderOutput, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
klog.Infof("CreateOpenIDConnectProvider: %v", request)
arn := fmt.Sprintf("arn:aws:iam::0000000000:oidc-provider/%s", *request.Url)
p := &iam.GetOpenIDConnectProviderOutput{
ClientIDList: request.ClientIDList,
ThumbprintList: request.ThumbprintList,
Url: request.Url,
}
if m.OIDCProviders == nil {
m.OIDCProviders = make(map[string]*iam.GetOpenIDConnectProviderOutput)
}
m.OIDCProviders[arn] = p
return &iam.CreateOpenIDConnectProviderOutput{OpenIDConnectProviderArn: &arn}, nil
}
func (m *MockIAM) CreateOpenIDConnectProviderWithContext(aws.Context, *iam.CreateOpenIDConnectProviderInput, ...request.Option) (*iam.CreateOpenIDConnectProviderOutput, error) {
panic("Not implemented")
}
func (m *MockIAM) CreateOpenIDConnectProviderRequest(*iam.CreateOpenIDConnectProviderInput) (*request.Request, *iam.CreateOpenIDConnectProviderOutput) {
panic("Not implemented")
}
func (m *MockIAM) DeleteOpenIDConnectProvider(request *iam.DeleteOpenIDConnectProviderInput) (*iam.DeleteOpenIDConnectProviderOutput, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
klog.Infof("DeleteOpenIDConnectProvider: %v", request)
arn := aws.StringValue(request.OpenIDConnectProviderArn)
o := m.OIDCProviders[arn]
if o == nil {
return nil, fmt.Errorf("OIDCProvider %q not found", arn)
}
delete(m.OIDCProviders, arn)
return &iam.DeleteOpenIDConnectProviderOutput{}, nil
}
func (m *MockIAM) DeleteOpenIDConnectProviderWithContext(aws.Context, *iam.DeleteOpenIDConnectProviderInput, ...request.Option) (*iam.DeleteOpenIDConnectProviderOutput, error) {
panic("Not implemented")
}
func (m *MockIAM) DeleteOpenIDConnectProviderRequest(*iam.DeleteOpenIDConnectProviderInput) (*request.Request, *iam.DeleteOpenIDConnectProviderOutput) {
panic("Not implemented")
}
func (m *MockIAM) UpdateOpenIDConnectProviderThumbprint(*iam.UpdateOpenIDConnectProviderThumbprintInput) (*iam.UpdateOpenIDConnectProviderThumbprintOutput, error) {
panic("Not implemented")
}
func (m *MockIAM) UpdateOpenIDConnectProviderThumbprintWithContext(aws.Context, *iam.UpdateOpenIDConnectProviderThumbprintInput, ...request.Option) (*iam.UpdateOpenIDConnectProviderThumbprintOutput, error) {
panic("Not implemented")
}
func (m *MockIAM) UpdateOpenIDConnectProviderThumbprintRequest(*iam.UpdateOpenIDConnectProviderThumbprintInput) (*request.Request, *iam.UpdateOpenIDConnectProviderThumbprintOutput) {
panic("Not implemented")
}

View File

@ -82,6 +82,7 @@ func ListResourcesAWS(cloud awsup.AWSCloud, clusterName string) (map[string]*res
// IAM
ListIAMInstanceProfiles,
ListIAMRoles,
ListIAMOIDCProviders,
}
if featureflag.Spotinst.Enabled() {
@ -2102,6 +2103,73 @@ func ListIAMInstanceProfiles(cloud fi.Cloud, clusterName string) ([]*resources.R
return resourceTrackers, nil
}
func ListIAMOIDCProviders(cloud fi.Cloud, clusterName string) ([]*resources.Resource, error) {
c := cloud.(awsup.AWSCloud)
var providers []*string
{
request := &iam.ListOpenIDConnectProvidersInput{}
response, err := c.IAM().ListOpenIDConnectProviders(request)
if err != nil {
return nil, fmt.Errorf("error listing IAM OIDC Providers: %v", err)
}
for _, provider := range response.OpenIDConnectProviderList {
arn := provider.Arn
descReq := &iam.GetOpenIDConnectProviderInput{
OpenIDConnectProviderArn: arn,
}
_, err := c.IAM().GetOpenIDConnectProvider(descReq)
if err != nil {
return nil, fmt.Errorf("error getting IAM OIDC Provider: %v", err)
}
// TODO: only delete oidc providers if they're owned by the cluster.
// We need to figure out how we can determine that given only a cluster name.
// Providers dont support tagging or naming.
// providers = append(providers, arn)
}
if err != nil {
return nil, fmt.Errorf("error listing IAM roles: %v", err)
}
}
var resourceTrackers []*resources.Resource
for _, arn := range providers {
resourceTracker := &resources.Resource{
Name: aws.StringValue(arn),
ID: aws.StringValue(arn),
Type: "oidc-provider",
Deleter: DeleteIAMOIDCProvider,
}
resourceTrackers = append(resourceTrackers, resourceTracker)
}
return resourceTrackers, nil
}
func DeleteIAMOIDCProvider(cloud fi.Cloud, r *resources.Resource) error {
c := cloud.(awsup.AWSCloud)
arn := r.Obj.(*string)
{
klog.V(2).Infof("Deleting IAM OIDC Provider %v", arn)
request := &iam.DeleteOpenIDConnectProviderInput{
OpenIDConnectProviderArn: arn,
}
_, err := c.IAM().DeleteOpenIDConnectProvider(request)
if err != nil {
if awsup.AWSErrorCode(err) == "NoSuchEntity" {
klog.V(2).Infof("Got NoSuchEntity deleting IAM OIDC Provider %v; will treat as already-deleted", arn)
return nil
}
return fmt.Errorf("error deleting IAM OIDC Provider %v: %v", arn, err)
}
}
return nil
}
func ListSpotinstResources(cloud fi.Cloud, clusterName string) ([]*resources.Resource, error) {
return spotinst.ListResources(cloud.(awsup.AWSCloud).Spotinst(), clusterName)
}

View File

@ -27,6 +27,8 @@ go_library(
"iaminstanceprofile_fitask.go",
"iaminstanceprofilerole.go",
"iaminstanceprofilerole_fitask.go",
"iamoidcprovider.go",
"iamoidcprovider_fitask.go",
"iamrole.go",
"iamrole_fitask.go",
"iamrolepolicy.go",

View File

@ -0,0 +1,168 @@
/*
Copyright 2020 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package awstasks
import (
"errors"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/iam"
"k8s.io/klog"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
)
//go:generate fitask -type=IAMOIDCProvider
type IAMOIDCProvider struct {
Lifecycle *fi.Lifecycle
ARN *string
ClientIDs []*string
Thumbprints []*string
URL *string
Name *string
}
var _ fi.CompareWithID = &IAMOIDCProvider{}
func (e *IAMOIDCProvider) CompareWithID() *string {
return e.Name
}
func (e *IAMOIDCProvider) Find(c *fi.Context) (*IAMOIDCProvider, error) {
cloud := c.Cloud.(awsup.AWSCloud)
response, err := cloud.IAM().ListOpenIDConnectProviders(&iam.ListOpenIDConnectProvidersInput{})
if err != nil {
return nil, fmt.Errorf("error listing oidc providers: %v", err)
}
providers := response.OpenIDConnectProviderList
for _, provider := range providers {
arn := provider.Arn
descResp, err := cloud.IAM().GetOpenIDConnectProvider(&iam.GetOpenIDConnectProviderInput{
OpenIDConnectProviderArn: arn,
})
if err != nil {
return nil, fmt.Errorf("error describing oidc provider: %v", err)
}
if fi.StringValue(descResp.Url) == fi.StringValue(e.URL) {
actual := &IAMOIDCProvider{
ClientIDs: descResp.ClientIDList,
Thumbprints: descResp.ThumbprintList,
URL: descResp.Url,
ARN: arn,
}
klog.V(2).Infof("found matching IAMOIDCProvider %q", aws.StringValue(arn))
return actual, nil
}
}
return nil, nil
}
func (e *IAMOIDCProvider) Run(c *fi.Context) error {
return fi.DefaultDeltaRunMethod(e, c)
}
func (s *IAMOIDCProvider) CheckChanges(a, e, changes *IAMOIDCProvider) error {
if a != nil {
if e.URL == nil {
return fi.RequiredField("URL")
}
if e.ClientIDs == nil {
return fi.RequiredField("ClientIDs")
}
if len(e.Thumbprints) == 0 {
return fi.RequiredField("Thumbprints")
}
} else {
if changes.ClientIDs != nil {
return fi.CannotChangeField("ClientIDs")
}
if changes.URL != nil {
return fi.CannotChangeField("URL")
}
}
return nil
}
func (p *IAMOIDCProvider) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *IAMOIDCProvider) error {
if a == nil {
klog.V(2).Infof("Creating IAMOIDCProvider with Name:%q", *e.Name)
request := &iam.CreateOpenIDConnectProviderInput{
ClientIDList: e.ClientIDs,
ThumbprintList: e.Thumbprints,
Url: e.URL,
}
response, err := t.Cloud.IAM().CreateOpenIDConnectProvider(request)
if err != nil {
return fmt.Errorf("error creating IAMOIDCProvider: %v", err)
}
e.ARN = response.OpenIDConnectProviderArn
} else {
if changes.Thumbprints != nil {
klog.V(2).Infof("Updating IAMOIDCProvider Thumbprints %q", *e.ARN)
request := &iam.UpdateOpenIDConnectProviderThumbprintInput{}
request.OpenIDConnectProviderArn = e.ARN
request.ThumbprintList = e.Thumbprints
_, err := t.Cloud.IAM().UpdateOpenIDConnectProviderThumbprint(request)
if err != nil {
return fmt.Errorf("error updating IAMOIDCProvider Thumbprints: %v", err)
}
}
}
return nil
}
type terraformIAMOIDCProvider struct {
URL *string `json:"url" cty:"url"`
ClientIDList []*string `json:"client_id_list" cty:"client_id_list"`
ThumbprintList []*string `json:"thumbprint_list" cty:"thumbprint_list"`
Name *string `json:"name" cty:"name"`
AssumeRolePolicy *terraform.Literal `json:"assume_role_policy" cty:"assume_role_policy"`
}
func (p *IAMOIDCProvider) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *IAMOIDCProvider) error {
tf := &terraformIAMOIDCProvider{
Name: e.Name,
URL: e.URL,
ClientIDList: e.ClientIDs,
ThumbprintList: e.Thumbprints,
}
return t.RenderResource("aws_iam_openid_connect_provider", *e.Name, tf)
}
func (e *IAMOIDCProvider) TerraformLink() *terraform.Literal {
return terraform.LiteralProperty("aws_iam_openid_connect_provider", *e.Name, "arn")
}
func (_ *IAMOIDCProvider) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *IAMOIDCProvider) error {
return errors.New("cloudformation does not support IAM OIDC Provider")
}

View File

@ -0,0 +1,75 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by ""fitask" -type=IAMOIDCProvider ."; DO NOT EDIT
package awstasks
import (
"encoding/json"
"k8s.io/kops/upup/pkg/fi"
)
// IAMOIDCProvider
// JSON marshaling boilerplate
type realIAMOIDCProvider IAMOIDCProvider
// UnmarshalJSON implements conversion to JSON, supporting an alternate specification of the object as a string
func (o *IAMOIDCProvider) UnmarshalJSON(data []byte) error {
var jsonName string
if err := json.Unmarshal(data, &jsonName); err == nil {
o.Name = &jsonName
return nil
}
var r realIAMOIDCProvider
if err := json.Unmarshal(data, &r); err != nil {
return err
}
*o = IAMOIDCProvider(r)
return nil
}
var _ fi.HasLifecycle = &IAMOIDCProvider{}
// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle
func (o *IAMOIDCProvider) GetLifecycle() *fi.Lifecycle {
return o.Lifecycle
}
// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle
func (o *IAMOIDCProvider) SetLifecycle(lifecycle fi.Lifecycle) {
o.Lifecycle = &lifecycle
}
var _ fi.HasName = &IAMOIDCProvider{}
// GetName returns the Name of the object, implementing fi.HasName
func (o *IAMOIDCProvider) GetName() *string {
return o.Name
}
// SetName sets the Name of the object, implementing fi.SetName
func (o *IAMOIDCProvider) SetName(name string) {
o.Name = &name
}
// String is the stringer function for the task, producing readable output using fi.TaskAsString
func (o *IAMOIDCProvider) String() string {
return fi.TaskAsString(o)
}