mirror of https://github.com/kubernetes/kops.git
Merge pull request #9375 from rifelpet/irsa_no_api
Add support for AWS OIDC Provider
This commit is contained in:
commit
97181b911f
|
@ -7,6 +7,7 @@ go_library(
|
|||
"iaminstanceprofile.go",
|
||||
"iamrole.go",
|
||||
"iamrolepolicy.go",
|
||||
"oidcprovider.go",
|
||||
],
|
||||
importpath = "k8s.io/kops/cloudmock/aws/mockiam",
|
||||
visibility = ["//visibility:public"],
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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)
|
||||
}
|
Loading…
Reference in New Issue