diff --git a/cloud/scope/clients.go b/cloud/scope/clients.go index 277bada1c..5d9fcc7a5 100644 --- a/cloud/scope/clients.go +++ b/cloud/scope/clients.go @@ -28,14 +28,11 @@ type IBMVPCClients struct { //ServiceEndPoint string } -func (c *IBMVPCClients) setIBMVPCService(iamEndpoint string, svcEndpoint string, apiKey string) error { +func (c *IBMVPCClients) setIBMVPCService(authenticator core.Authenticator, svcEndpoint string) error { var err error c.VPCService, err = vpcv1.NewVpcV1(&vpcv1.VpcV1Options{ - Authenticator: &core.IamAuthenticator{ - ApiKey: apiKey, - URL: iamEndpoint, - }, - URL: svcEndpoint, + Authenticator: authenticator, + URL: svcEndpoint, }) return err diff --git a/cloud/scope/cluster.go b/cloud/scope/cluster.go index a709b5777..1ef2f8379 100644 --- a/cloud/scope/cluster.go +++ b/cloud/scope/cluster.go @@ -52,7 +52,7 @@ type ClusterScope struct { IBMVPCCluster *infrav1.IBMVPCCluster } -func NewClusterScope(params ClusterScopeParams, iamEndpoint string, apiKey string, svcEndpoint string) (*ClusterScope, error) { +func NewClusterScope(params ClusterScopeParams, authenticator core.Authenticator, svcEndpoint string) (*ClusterScope, error) { if params.Cluster == nil { return nil, errors.New("failed to generate new scope from nil Cluster") } @@ -69,7 +69,7 @@ func NewClusterScope(params ClusterScopeParams, iamEndpoint string, apiKey strin return nil, errors.Wrap(err, "failed to init patch helper") } - vpcErr := params.IBMVPCClients.setIBMVPCService(iamEndpoint, svcEndpoint, apiKey) + vpcErr := params.IBMVPCClients.setIBMVPCService(authenticator, svcEndpoint) if vpcErr != nil { return nil, errors.Wrap(vpcErr, "failed to create IBM VPC session") } diff --git a/cloud/scope/machine.go b/cloud/scope/machine.go index 6043c731b..4e5c81b81 100644 --- a/cloud/scope/machine.go +++ b/cloud/scope/machine.go @@ -23,6 +23,7 @@ import ( "github.com/go-logr/logr" "github.com/pkg/errors" + "github.com/IBM/go-sdk-core/v5/core" "github.com/IBM/vpc-go-sdk/vpcv1" corev1 "k8s.io/api/core/v1" @@ -58,7 +59,7 @@ type MachineScope struct { IBMVPCMachine *infrav1.IBMVPCMachine } -func NewMachineScope(params MachineScopeParams, iamEndpoint string, apiKey string, svcEndpoint string) (*MachineScope, error) { +func NewMachineScope(params MachineScopeParams, authenticator core.Authenticator, svcEndpoint string) (*MachineScope, error) { if params.Machine == nil { return nil, errors.New("failed to generate new scope from nil Machine") } @@ -75,7 +76,7 @@ func NewMachineScope(params MachineScopeParams, iamEndpoint string, apiKey strin return nil, errors.Wrap(err, "failed to init patch helper") } - vpcErr := params.IBMVPCClients.setIBMVPCService(iamEndpoint, svcEndpoint, apiKey) + vpcErr := params.IBMVPCClients.setIBMVPCService(authenticator, svcEndpoint) if vpcErr != nil { return nil, errors.Wrap(vpcErr, "failed to create IBM VPC session") } diff --git a/config/default/credentials.yaml b/config/default/credentials.yaml new file mode 100644 index 000000000..69aa7881a --- /dev/null +++ b/config/default/credentials.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: manager-bootstrap-credentials + namespace: system +type: Opaque +stringData: + ibm-credentials.env: |- + IBMCLOUD_AUTH_TYPE=iam + IBMCLOUD_APIKEY=${IBMCLOUD_API_KEY} + IBMCLOUD_AUTH_URL=https://iam.cloud.ibm.com diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 6a9e11997..9cfc1c9e8 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -24,10 +24,14 @@ bases: # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. #- ../prometheus +resources: +- credentials.yaml + patchesStrategicMerge: # Protect the /metrics endpoint by putting it behind auth. # If you want your controller-manager to expose the /metrics # endpoint w/o any authn/z, please comment the following line. +- manager_credentials_patch.yaml - manager_auth_proxy_patch.yaml - manager_image_patch.yaml diff --git a/config/default/manager_credentials_patch.yaml b/config/default/manager_credentials_patch.yaml new file mode 100644 index 000000000..cb8211dd3 --- /dev/null +++ b/config/default/manager_credentials_patch.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager + env: + - name: IBM_CREDENTIALS_FILE + value: /home/.ibmcloud/ibm-credentials.env + volumeMounts: + - name: credentials + mountPath: /home/.ibmcloud + volumes: + - name: credentials + secret: + secretName: manager-bootstrap-credentials diff --git a/controllers/ibmvpccluster_controller.go b/controllers/ibmvpccluster_controller.go index 5d0a0830e..0c099ef58 100644 --- a/controllers/ibmvpccluster_controller.go +++ b/controllers/ibmvpccluster_controller.go @@ -37,6 +37,7 @@ import ( infrastructurev1alpha3 "sigs.k8s.io/cluster-api-provider-ibmcloud/api/v1alpha3" "sigs.k8s.io/cluster-api-provider-ibmcloud/cloud/scope" + "sigs.k8s.io/cluster-api-provider-ibmcloud/pkg" ) // IBMVPCClusterReconciler reconciles a IBMVPCCluster object @@ -75,16 +76,20 @@ func (r *IBMVPCClusterReconciler) Reconcile(ctx context.Context, req ctrl.Reques } // Create the scope. - iamEndpoint := os.Getenv("IAM_ENDPOINT") - apiKey := os.Getenv("API_KEY") + // TODO: Will be removed once we find a better way of overriding the service endpoint, generate via spec svcEndpoint := os.Getenv("SERVICE_ENDPOINT") + authenticator, err := pkg.GetAuthenticator() + if err != nil { + return ctrl.Result{}, errors.Wrapf(err, "failed to get authenticator") + } + clusterScope, err := scope.NewClusterScope(scope.ClusterScopeParams{ Client: r.Client, Logger: log, Cluster: cluster, IBMVPCCluster: ibmCluster, - }, iamEndpoint, apiKey, svcEndpoint) + }, authenticator, svcEndpoint) // Always close the scope when exiting this function so we can persist any GCPMachine changes. defer func() { diff --git a/controllers/ibmvpcmachine_controller.go b/controllers/ibmvpcmachine_controller.go index 67f37c185..7edeedc94 100644 --- a/controllers/ibmvpcmachine_controller.go +++ b/controllers/ibmvpcmachine_controller.go @@ -38,6 +38,7 @@ import ( infrastructurev1alpha3 "sigs.k8s.io/cluster-api-provider-ibmcloud/api/v1alpha3" "sigs.k8s.io/cluster-api-provider-ibmcloud/cloud/scope" + "sigs.k8s.io/cluster-api-provider-ibmcloud/pkg" ) // IBMVPCMachineReconciler reconciles a IBMVPCMachine object @@ -97,10 +98,13 @@ func (r *IBMVPCMachineReconciler) Reconcile(ctx context.Context, req ctrl.Reques } // Create the cluster scope - iamEndpoint := os.Getenv("IAM_ENDPOINT") - apiKey := os.Getenv("API_KEY") svcEndpoint := os.Getenv("SERVICE_ENDPOINT") + authenticator, err := pkg.GetAuthenticator() + if err != nil { + return ctrl.Result{}, errors.Wrapf(err, "failed to get authenticator") + } + // Create the machine scope machineScope, err := scope.NewMachineScope(scope.MachineScopeParams{ Client: r.Client, @@ -109,7 +113,7 @@ func (r *IBMVPCMachineReconciler) Reconcile(ctx context.Context, req ctrl.Reques IBMVPCCluster: ibmCluster, Machine: machine, IBMVPCMachine: ibmVpcMachine, - }, iamEndpoint, apiKey, svcEndpoint) + }, authenticator, svcEndpoint) if err != nil { return ctrl.Result{}, errors.Errorf("failed to create scope: %+v", err) } diff --git a/pkg/authenticator.go b/pkg/authenticator.go new file mode 100644 index 000000000..0f8777ec7 --- /dev/null +++ b/pkg/authenticator.go @@ -0,0 +1,51 @@ +/* +Copyright 2021 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 pkg + +import ( + "fmt" + "github.com/IBM/go-sdk-core/v5/core" +) + +// This expects the credential file in the following search order: +// 1) ${IBM_CREDENTIALS_FILE} +// 2) /ibm-credentials.env +// 3) /ibm-credentials.env +// +// and the format is: +// $ cat ibm-credentials.env +// IBMCLOUD_AUTH_TYPE=iam +// IBMCLOUD_APIKEY=xxxxxxxxxxxxx +// IBMCLOUD_AUTH_URL=https://iam.cloud.ibm.com + +const ( + serviceIBMCloud = "IBMCLOUD" +) + +// GetAuthenticator instantiates an Authenticator from external config file +func GetAuthenticator() (core.Authenticator, error) { + auth, err := core.GetAuthenticatorFromEnvironment(serviceIBMCloud) + if err != nil { + return nil, err + } + switch auth.(type) { + case *core.IamAuthenticator: + return auth, nil + default: + return nil, fmt.Errorf("only IAM authenticator is supported") + } +} diff --git a/pkg/client.go b/pkg/client.go index d84e2edd4..2c96ca690 100644 --- a/pkg/client.go +++ b/pkg/client.go @@ -22,11 +22,13 @@ import ( "github.com/golang-jwt/jwt" + "github.com/IBM-Cloud/bluemix-go" "github.com/IBM-Cloud/bluemix-go/api/resource/resourcev2/controllerv2" "github.com/IBM-Cloud/bluemix-go/authentication" "github.com/IBM-Cloud/bluemix-go/http" "github.com/IBM-Cloud/bluemix-go/rest" bxsession "github.com/IBM-Cloud/bluemix-go/session" + "github.com/IBM/go-sdk-core/v5/core" "k8s.io/klog/v2" ) @@ -98,7 +100,16 @@ func fetchUserDetails(sess *bxsession.Session, generation int) (*User, error) { func NewClient() *Client { c := &Client{} - bxSess, err := bxsession.New() + authenticator, err := GetAuthenticator() + if err != nil { + klog.Fatal(err) + } + //TODO: this will be removed once power-go-client migrated to go-sdk-core + auth, ok := authenticator.(*core.IamAuthenticator) + if !ok { + klog.Fatal("failed to assert the authenticator as IAM type, please check the ibm-credentials.env file") + } + bxSess, err := bxsession.New(&bluemix.Config{BluemixAPIKey: auth.ApiKey}) if err != nil { klog.Fatal(err) }