Developing microservices with OCI SDKs avoiding to include the private key file in the container image | Example in go


Oracle Cloud Infrastructure provides a number of Software Development Kits (SDKs) and a Command Line Interface (CLI) to facilitate development of custom solutions.

One of the mechanisms for authenticating the requests done by these SDKs is by configuring the SDK and CLI Configuration File. This file contains data needed for API signing keys.

For example, the following code provided in the SDK examples will work if the local configuration setup has been done.

simpleoci.go

package main
import (
    "context"
    "fmt"
    "github.com/oracle/oci-go-sdk/common"
    "github.com/oracle/oci-go-sdk/identity"
)

func main() {
    c, err := identity.NewIdentityClientWithConfigurationProvider(common.DefaultConfigProvider())
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
  
    // The OCID of the tenancy containing the compartment.
    tenancyID, err := common.DefaultConfigProvider().TenancyOCID()
    if err != nil {
        fmt.Println("Error:", err)
        return
        }
  
    request := identity.ListAvailabilityDomainsRequest{
        CompartmentId: &tenancyID,
    }
  
    r, err := c.ListAvailabilityDomains(context.Background(), request)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
  
    fmt.Printf("List of available domains: %v", r.Items)
    return
}

This is the output:

mbpj:tutorials javiermugueta$ go run simpleoci.go 
List of available domains: [{ Name=UcWn:EU-FRANKFURT-1-AD-1 Id=ocid1.availabilitydomain.oc1..a..dq CompartmentId=ocid1.tenancy.oc1..aaaaaaaalsemk...wua } { Name=UcWn:EU-FRANKFURT-1-AD-2 Id=ocid1.availabilitydomain.oc1..aaaaaaaalcdcbl7...m2q CompartmentId=ocid1.tenancy.oc1..aaaaa...wua } { Name=UcWn:EU-FRANKFURT-1-AD-3 Id=ocid1.availabilitydomain.oc1..aaaaaaaaiifj2...wna CompartmentId=ocid1.tenancy.oc1..aaaaaaaals...wua }]

Let’s change the code to meet our purpose. To run the code in local set the environment variables according with your OCI account and tenancy you own.

export tenancy="ocid1.tenancy.oc1..aa...a"
export user="ocid1.user.oc1..aaa...wa"
export region="eu-frankfurt-1"
export fingerprint="85:...d6"
export ppkfile="$(cat ./myppk)
export ppkpasswd="-"

nonlocalociauth.go

package main
import (
    "context"
    "fmt"
    "os"
    "time"
    "github.com/oracle/oci-go-sdk/common"
    "github.com/oracle/oci-go-sdk/identity"
)
func check(e error) {
    if e != nil {
        panic(e)
    }
}
func main() {

    tenancy := os.Getenv("tenancy")
    fmt.Printf("oci tenancy ocid: %s\n", tenancy)

    user := os.Getenv("user")
    fmt.Printf("oci user ocid: %s\n", user)

    region := os.Getenv("region")
    fmt.Printf("oci region: %s\n", region)

    fingerprint := os.Getenv("fingerprint")
    fmt.Printf("fingerprint: %s\n", fingerprint)

    ppkcontent := os.Getenv("ppkfile")

    ppkpasswd := os.Getenv("ppkpassword")
    fmt.Printf("ppk passwd: %s\n", "****")

    fmt.Printf("1\n")
	nrcp := common.NewRawConfigurationProvider(tenancy, user, region, fingerprint, string(ppkcontent), &ppkpasswd)
	c, err := identity.NewIdentityClientWithConfigurationProvider(nrcp)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
  
    fmt.Printf("2\n")
    tenancyID, err := nrcp.TenancyOCID()
    if err != nil {
        fmt.Println("Error:", err)
        return
        }
  
    request := identity.ListAvailabilityDomainsRequest{
        CompartmentId: &tenancyID,
    }
    fmt.Printf("3\n")
    r, err := c.ListAvailabilityDomains(context.Background(), request)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Printf("4\n")
    fmt.Printf("List of available domains: %v\n", r.Items)

    fmt.Printf("sleeping...\n")
    time.Sleep(time.Second * 8640000000)// cien mil días

    return
}
go run customociauth.go

Now, let’s build the container image (link to code samples at the bottom). Put the appropriate name of your container images registry

docker build -t yourepo/nonlocalociauth .
docker push yourepo/nonlocalociauth 

Put the content of a valid ppk in a file called myppk (avilable in the git repo) and then, let’s create the configmap to store the content of the ppk. The second line outputs the content of the configmap in stdout.

kubectl create configmap myppk --from-file=myppk=./myppk 
kubectl get configmaps myppk -o yaml
apiVersion: v1
data:
  myppk: |-
    -----BEGIN RSA PRIVATE KEY-----
    MIIEogIBAAKCAQEAwkh0qmaKSUXzaDRP2gCCYxUNbjqy16QKUyhAXiO/azX7b+aC
    ...
    PSI0kJvWD+Qmm3tmG7Ysu73wT/Zt7FuZ98QcTb6wNZhR14zKM70=
    -----END RSA PRIVATE KEY-----
kind: ConfigMap
metadata:
  crea...
  ...
  selfLink: /api/v1/namespaces/pruebas/configmaps/myppk
  uid: 5e34c740-2f24-4f47-aa7e-562e31dd91b9

So far, so good. Edit nonlocalociauth.yaml and set the values of the environment variables. Note that ppk content is obtained form the configmap:

Finally deploy to a k8s cluster.

kubectl apply -f nonlocalociauth.yaml

Check for the existence of a pod and then get the output

kubectl get po
NAME                             READY   STATUS    RESTARTS   AGE
customociauth-69...n6   1/1     Running   0          36m

kubectl logs customociauth-69...n6

Voilà, the execution works fine and no sensitive data is stored in the container image.

You can get the sample code from here

That’s all folks, hope it helps!! 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.