From b7d50da45dc6d8a63abe9400d184bb3e17f5625a Mon Sep 17 00:00:00 2001 From: Anders Swanson Date: Fri, 29 Mar 2024 11:05:06 -0700 Subject: [PATCH 1/2] OKE Workload Identity --- commons/oci/provider.go | 32 ++++++++++++++++++++++++++++++-- go.mod | 7 +++---- go.sum | 16 +++++++++------- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/commons/oci/provider.go b/commons/oci/provider.go index e875969c..152f1efd 100644 --- a/commons/oci/provider.go +++ b/commons/oci/provider.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2022 Oracle and/or its affiliates. +** Copyright (c) 2022, 2024 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -40,6 +40,8 @@ package oci import ( "errors" + "fmt" + "os" "github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/common/auth" @@ -65,7 +67,9 @@ type APIKeyAuth struct { } func GetOCIProvider(kubeClient client.Client, authData APIKeyAuth) (common.ConfigurationProvider, error) { - if authData.ConfigMapName != nil && authData.SecretName != nil { + if authData.ConfigMapName != nil && authData.SecretName == nil { + return getWorkloadIdentityProvider(kubeClient, authData) + } else if authData.ConfigMapName != nil && authData.SecretName != nil { provider, err := getProviderWithAPIKey(kubeClient, authData) if err != nil { return nil, err @@ -80,6 +84,30 @@ func GetOCIProvider(kubeClient client.Client, authData APIKeyAuth) (common.Confi } } +func getWorkloadIdentityProvider(kubeClient client.Client, authData APIKeyAuth) (common.ConfigurationProvider, error) { + ociConfigMap, err := k8s.FetchConfigMap(kubeClient, authData.Namespace, *authData.ConfigMapName) + if err != nil { + return nil, err + } + // Ensure configmap is set with proper data + if len(ociConfigMap.Data) == 0 { + return nil, fmt.Errorf("OCI ConfigMap %s has no data", ociConfigMap.Name) + } + region, ok := ociConfigMap.Data[regionKey] + if !ok || len(region) == 0 { + return nil, fmt.Errorf("OCI Region Key %s missing from OCI ConfigMap %s", regionKey, ociConfigMap.Name) + } + // OCI SDK requires specific, dynamic environment variables for workload identity. + if err = os.Setenv(auth.ResourcePrincipalVersionEnvVar, auth.ResourcePrincipalVersion2_2); err != nil { + + return nil, fmt.Errorf("unable to set OCI SDK environment variable %s: %v", auth.ResourcePrincipalVersionEnvVar, err) + } + if err = os.Setenv(auth.ResourcePrincipalRegionEnvVar, region); err != nil { + return nil, fmt.Errorf("unable to set OCI SDK environment variable %s: %v", auth.ResourcePrincipalRegionEnvVar, err) + } + return auth.OkeWorkloadIdentityConfigurationProvider() +} + func getProviderWithAPIKey(kubeClient client.Client, authData APIKeyAuth) (common.ConfigurationProvider, error) { var region, fingerprint, user, tenancy, passphrase, privatekeyValue string diff --git a/go.mod b/go.mod index 81c4d435..82878c3a 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/go-logr/logr v1.2.3 github.com/onsi/ginkgo/v2 v2.5.0 github.com/onsi/gomega v1.24.1 - github.com/oracle/oci-go-sdk/v65 v65.26.1 + github.com/oracle/oci-go-sdk/v65 v65.61.0 go.uber.org/zap v1.23.0 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.25.4 @@ -50,14 +50,13 @@ require ( github.com/prometheus/procfs v0.7.3 // indirect github.com/sony/gobreaker v0.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.4.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.7.0 // indirect golang.org/x/net v0.7.0 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect - golang.org/x/sys v0.5.0 // indirect + golang.org/x/sys v0.8.0 // indirect golang.org/x/term v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/text v0.7.0 golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index aacb116a..3977fc48 100644 --- a/go.sum +++ b/go.sum @@ -277,8 +277,8 @@ github.com/onsi/ginkgo/v2 v2.5.0 h1:TRtrvv2vdQqzkwrQ1ke6vtXf7IK34RBUJafIy1wMwls= github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= -github.com/oracle/oci-go-sdk/v65 v65.26.1 h1:Ms20RSRj+CuvQmw5ET1TkmzxLBI+bmLjZ6NYANA3gkk= -github.com/oracle/oci-go-sdk/v65 v65.26.1/go.mod h1:oyMrMa1vOzzKTmPN+kqrTR9y9kPA2tU1igN3NUSNTIE= +github.com/oracle/oci-go-sdk/v65 v65.61.0 h1:zASJu3G3PVTAqOkHyVoSU3Sfeoir4M0WCLr12+rtCkg= +github.com/oracle/oci-go-sdk/v65 v65.61.0/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -321,8 +321,9 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -330,7 +331,9 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -521,10 +524,9 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= From ef34ec02ef7c435a99bcec390806534918c2e497 Mon Sep 17 00:00:00 2001 From: Anders Swanson Date: Fri, 29 Mar 2024 11:26:21 -0700 Subject: [PATCH 2/2] Document workload identity --- .../adb/autonomousdatabase_create.yaml | 7 +++-- docs/adb/ADB_PREREQUISITES.md | 30 ++++++++++++++++++- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/config/samples/adb/autonomousdatabase_create.yaml b/config/samples/adb/autonomousdatabase_create.yaml index 3ecf966f..aa77a94c 100644 --- a/config/samples/adb/autonomousdatabase_create.yaml +++ b/config/samples/adb/autonomousdatabase_create.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, Oracle and/or its affiliates. +# Copyright (c) 2022, 2024, Oracle and/or its affiliates. # Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. # apiVersion: database.oracle.com/v1alpha1 @@ -30,7 +30,7 @@ spec: # # Uncomment this block to configure the network access type with the RESTRICTED option. # # This option lets you restrict access by defining access control rules in an Access Control List (ACL). - # # By specifying an ACL, the database will be accessible from a whitelisted set of IP addresses, CIDR (Classless Inter-Domain Routing) blocks, or VCNs. + # # By specifying an ACL, the database will be accessible from a whitelisted set of IP addresses, CIDR (Classless Inter-Domain Routing) blocks, or VCNs. # # Use a semicolon (;) as a deliminator between the VCN-specific subnets or IPs. # accessType: RESTRICTED # accessControlList: @@ -42,7 +42,7 @@ spec: # isMTLSConnectionRequired: true # # Uncomment this block to configure the network access type with the PRIVATE option. - # # This option assigns a private endpoint, private IP, and hostname to your database. + # # This option assigns a private endpoint, private IP, and hostname to your database. # # Specifying this option allows traffic only from the VCN you specify. # # This allows you to define security rules, ingress/egress, at the Network Security Group (NSG) level and to control traffic to your Autonomous Database. # accessType: PRIVATE @@ -61,4 +61,5 @@ spec: # Authorize the operator with API signing key pair. Comment out the ociConfig fields if your nodes are already authorized with instance principal. ociConfig: configMapName: oci-cred + # Comment out secretName if using OKE workload identity secretName: oci-privatekey \ No newline at end of file diff --git a/docs/adb/ADB_PREREQUISITES.md b/docs/adb/ADB_PREREQUISITES.md index 3eaf5b4a..c0fe84ed 100644 --- a/docs/adb/ADB_PREREQUISITES.md +++ b/docs/adb/ADB_PREREQUISITES.md @@ -12,7 +12,7 @@ To provide access, choose **one of the following approaches**: ## Authorized with API Key Authentication -API keys are supplied by users to authenticate the operator accessing Oracle Cloud Infrastructure (OCI) services. The operator reads the credintials of the OCI user from a ConfigMap and a Secret. If you're using Oracle Container Engine for Kubernetes (OKE), you may alternatively use [Instance Principal](#authorized-with-instance-principal) to avoid the need to configure user credentails or a configuration file. If the operator is deployed in a third-party Kubernetes cluster, then the credentials or a configuration file are needed, since Instance principal authorization applies only to instances that are running in the OCI. +API keys are supplied by users to authenticate the operator accessing Oracle Cloud Infrastructure (OCI) services. The operator reads the credentials of the OCI user from a ConfigMap and a Secret. If you're using Oracle Container Engine for Kubernetes (OKE), you may alternatively use [Instance Principal](#authorized-with-instance-principal) to avoid the need to configure user credentails or a configuration file. If the operator is deployed in a third-party Kubernetes cluster, then the credentials or a configuration file are needed, since Instance principal authorization applies only to instances that are running in the OCI. Oracle recommends using the helper script `set_ocicredentials.sh` in the root directory of the repository; this script will generate a ConfigMap and a Secret with the OCI credentials. By default, the script parses the **DEFAULT** profile in `~/.oci/config`. The default names of the ConfigMap and the Secret are, respectively: `oci-cred` and `oci-privatekey`. @@ -120,3 +120,31 @@ To set up the instance princials, you will have to: 3. To apply the policy, click Create. At this stage, the instances where the operator deploys have been granted sufficient permissions to call OCI services. You can now proceed to the installation. + +### Authorized with OKE Workload Identity + +OKE Workload Identity grants the operator pods policy-driven access to OCI resources using OCI Identity and Access Management (IAM). +When using OKE Workload Identity, only the region must be specified in the ConfigMap corresponding to the `ociConfigMap` attribute. The `ociSecret` attribute should not be specified in the `.yaml` file. + +To set up the OKE Workload Identity, you will have to: + +### Configure Cluster Region + +The operator reads the OCI region from a ConfigMap. + +```sh +kubectl create configmap oci-cred \ +--from-literal=region= +``` + +### Define Policies + +1. Get the compartment name where the database resides/will be created. +2. Get the OCID of the OKE Cluster where the Oracle Database Operator is running. +3. Create the following policy in OCI IAM, supplying your compartment name and OKE Cluster OCID: + +``` +Allow any-user to manage all-resources in compartment where all {request.principal.namespace='oracle-database-operator-system',request.principal.type='workload',request.principal.cluster_id='',request.principal.service_account='default'} +``` + +After creating the policy, operator pods will be granted sufficient permissions to call OCI services. You can now proceed to the installation. \ No newline at end of file