Skip to content

Commit

Permalink
Workload identity (#335)
Browse files Browse the repository at this point in the history
* Add support for workload identity
  • Loading branch information
shyamradhakrishnan committed Oct 3, 2023
1 parent bf1c037 commit 241cf91
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 1 deletion.
2 changes: 2 additions & 0 deletions api/v1beta2/ociclusteridentity_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ const (
UserPrincipal PrincipalType = "UserPrincipal"
// InstancePrincipal represents a instance principal.
InstancePrincipal PrincipalType = "InstancePrincipal"
// WorkloadPrincipal represents a workload principal.
WorkloadPrincipal PrincipalType = "Workload"
)

// OCIClusterIdentitySpec defines the parameters that are used to create an OCIClusterIdentity.
Expand Down
79 changes: 79 additions & 0 deletions cloud/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ import (
"context"
"crypto/x509"
"fmt"
"io"
"net/http"
"os"
"reflect"
"time"

"github.com/go-logr/logr"
infrastructurev1beta2 "github.com/oracle/cluster-api-provider-oci/api/v1beta2"
Expand All @@ -41,6 +45,15 @@ import (
"sigs.k8s.io/cluster-api/util/patch"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
)

const (
instanceMetadataRegionInfoURLV2 = "http://169.254.169.254/opc/v2/instance/regionInfo/regionIdentifier"
)

var (
currentRegion *string
)

// GetClusterIdentityFromRef returns the OCIClusterIdentity referenced by the OCICluster.
Expand Down Expand Up @@ -97,6 +110,7 @@ func getOCIClientCertPool(ctx context.Context, c client.Client, namespace string

// GetOrBuildClientFromIdentity creates ClientProvider from OCIClusterIdentity object
func GetOrBuildClientFromIdentity(ctx context.Context, c client.Client, identity *infrastructurev1beta2.OCIClusterIdentity, defaultRegion string, clientOverrides *infrastructurev1beta2.ClientOverrides, namespace string) (*scope.ClientProvider, error) {
logger := log.FromContext(ctx)
if identity.Spec.Type == infrastructurev1beta2.UserPrincipal {
secretRef := identity.Spec.PrincipalSecret
key := types.NamespacedName{
Expand Down Expand Up @@ -155,6 +169,42 @@ func GetOrBuildClientFromIdentity(ctx context.Context, c client.Client, identity
OciAuthConfigProvider: provider,
ClientOverrides: clientOverrides})

if err != nil {
return nil, err
}
return clientProvider, nil
} else if identity.Spec.Type == infrastructurev1beta2.WorkloadPrincipal {
_, containsVersion := os.LookupEnv(auth.ResourcePrincipalVersionEnvVar)
if !containsVersion {
os.Setenv(auth.ResourcePrincipalVersionEnvVar, auth.ResourcePrincipalVersion2_2)
}
_, containsRegion := os.LookupEnv(auth.ResourcePrincipalRegionEnvVar)
if !containsRegion {
// initialize the current region from region metadata
if currentRegion == nil {
regionByte, err := getRegionInfoFromInstanceMetadataServiceProd()
if err != nil {
return nil, err
}
currentRegion = common.String(string(regionByte))
}
logger.Info(fmt.Sprintf("Looked up region %s from instance metadata", *currentRegion))
os.Setenv(auth.ResourcePrincipalRegionEnvVar, *currentRegion)
}

provider, err := auth.OkeWorkloadIdentityConfigurationProvider()
if err != nil {
return nil, err
}
pool, err := getOCIClientCertPool(ctx, c, namespace, clientOverrides)
if err != nil {
return nil, err
}
clientProvider, err := scope.NewClientProvider(scope.ClientProviderParams{
CertOverride: pool,
OciAuthConfigProvider: provider,
ClientOverrides: clientOverrides})

if err != nil {
return nil, err
}
Expand Down Expand Up @@ -406,6 +456,35 @@ func DeleteOrphanedMachinePoolMachines(ctx context.Context, params MachineParams

return nil
}
func getRegionInfoFromInstanceMetadataServiceProd() ([]byte, error) {
request, err := http.NewRequest(http.MethodGet, instanceMetadataRegionInfoURLV2, nil)
request.Header.Add("Authorization", "Bearer Oracle")

client := &http.Client{
Timeout: time.Second * 10,
}
resp, err := client.Do(request)
if err != nil {
return nil, errors.Wrap(err, "failed to call instance metadata service")
}

statusCode := resp.StatusCode

defer resp.Body.Close()

content, err := io.ReadAll(resp.Body)
if err != nil {
return nil, errors.Wrap(err, "failed to get region information from response body")
}

if statusCode != http.StatusOK {
err = fmt.Errorf("HTTP Get failed: URL: %s, Status: %s, Message: %s",
instanceMetadataRegionInfoURLV2, resp.Status, string(content))
return nil, err
}

return content, nil
}

// MachineParams specifies the params required to create or delete machinepool machines.
// Infra machine pool specifed below refers to OCIManagedMachinePool/OCIMachinePool/OCIVirtualMachinePool
Expand Down
19 changes: 18 additions & 1 deletion docs/src/gs/multi-tenancy.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,22 @@ spec:
allowedNamespaces: {}
```

## Cluster Identity using Workload Identity

Cluster Identity supports [Workload][workload] access to OCI resources also knows as Workload Identity. The example
`OCIClusterIdentity` spec shown below uses Workload Identity.

```yaml
---
kind: OCIClusterIdentity
metadata:
name: cluster-identity
namespace: default
spec:
type: Workload
allowedNamespaces: {}
```

[iam-user]: https://docs.oracle.com/en-us/iaas/Content/API/Concepts/apisigningkey.htm#Required_Keys_and_OCIDs
[instance-principals]: https://docs.oracle.com/en-us/iaas/Content/Identity/Tasks/callingservicesfrominstances.htm
[instance-principals]: https://docs.oracle.com/en-us/iaas/Content/Identity/Tasks/callingservicesfrominstances.htm
[workload]: https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contenggrantingworkloadaccesstoresources.htm

0 comments on commit 241cf91

Please sign in to comment.