Octelium documentation · Latest

Workload Clientless Access to SaaS APIs with Go SDK

You can easily protect all your public SaaS APIs as Octelium Services and provide your clientless WORKLOAD Users such as your Golang-based microservices and applications with secretless access to all of your APIs without having to expose, manage and share the API keys, access tokens or OAuth2 client credentials required to access such APIs. In this short guide, we're going to use the Golang SDK (read more here) to access a generic HTTP SaaS API that requires a bearer access token.

We first create a Secret with the name apikey1 that stores the bearer access token required to access the protected SaaS API as follows:

octeliumctl create secret apikey1

You can read more about Secret management here. Note that Octelium supports more HTTP authentication types other than bearer authentication. You can read more here.

Now we actually create the Service representing our SaaS API as follows:

kind: Service metadata: name: my-api spec: mode: HTTP isPublic: true config: upstream: url: https://api.example.com http: auth: bearer: fromSecret: apikey1

You can now apply the Service as follows (read more here):

octeliumctl apply /PATH/TO/SERVICE.YAML

Now we need a Credential for the WORKLOAD User representing our application, which we name here microservice1. We create an authentication token Credential whose Sessions are CLIENTLESS (read more about authentication token Credentials here and Session types here) as follows:

octeliumctl create cred --user microservice1 --session-type clientless microservice1-cred # The output should be something like Authentication Token: AQpA0f-8AQE2SLLZG3UDvvi...

Now, we add the octelium-go library to our Golang-based application as follows:

go get github.com/octelium/octelium/octelium-go

And we can now use it to access the Service my-api as follows:

package main import ( "context" "fmt" "io" "os" octelium "github.com/octelium/octelium/octelium-go" ) func main() { if err := run(context.Background()); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func run(ctx context.Context) error { client, err := octelium.New(ctx, octelium.WithDomain("example.com"), octelium.WithAuthenticator( octelium.AuthenticationToken(os.Getenv("OCTELIUM_AUTH_TOKEN")), ), ) if err != nil { return err } defer client.Close() httpClient := client.HTTPClient() resp, err := httpClient.Get("https://my-api.example.com/v1/resources") if err != nil { return err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return err } fmt.Printf("Status: %s\nBody: %s\n", resp.Status, body) return nil }

The HTTPClient() method returns a standard *http.Client that attaches the Session access token to every request. The access token is obtained upon the first request and automatically refreshed before it expires, so your application does not have to worry about periodically re-authenticating the Session. By default, the access token is only attached to HTTPS requests to the Cluster domain and its subdomains, so a redirect or a misconfigured URL cannot leak it to a third party.

You can also use HTTPTransport() to embed the authenticated transport in your own http.Client, or TokenSource() to use the Session access token with any library that accepts an oauth2.TokenSource.

note

You can also omit the options and let octelium.New() read the Cluster domain and the authentication token from the OCTELIUM_DOMAIN and OCTELIUM_AUTH_TOKEN environment variables respectively.

An authentication token is treated as a one-time credential. The library keeps refreshing the resulting Session but it does not reuse the authentication token to create a new Session once the current one can no longer be refreshed. For long-running workloads, you might want to use secretless OIDC assertions instead as shown below.

Secretless Authentication via OIDC Assertions

Instead of issuing and distributing authentication tokens, workloads that already have OIDC identity tokens issued by the platform hosting them (e.g. Kubernetes service accounts, GitHub Actions, Azure, etc...) can use them to authenticate themselves to the Cluster via an OIDC assertion IdentityProvider (read more here). You only need to replace the authenticator as follows:

client, err := octelium.New(ctx, octelium.WithDomain("example.com"), octelium.WithAuthenticator( octelium.AssertionFromFile("/var/run/secrets/octelium/token"), ), )

AssertionFromFile() reads the assertion upon every authentication, which makes it suitable for tokens that are periodically rotated by the platform such as Kubernetes projected service account tokens. Since a fresh assertion is used every time, the library also creates a new Session automatically whenever the previous one expires. You can also set the OCTELIUM_ASSERTION_FILE environment variable instead. You can see a complete example for workloads running inside Kubernetes clusters here.

It's important to note that the Go SDK is not the only way to access publicly exposed BeyondCorp Services. You can also use the OAuth2 client credentials flow to access any such Service (read more here). This enables your applications and microservices written in any language to access the Cluster's Services via standard OAuth2 libraries that are supported in most major programming languages without having to use any clients or use specific SDKs. For Rust applications, the octelium crate provides an equivalent HTTP client via Client::http() (read more about the SDKs here).