# First Steps to Managing the Cluster

> Octelium documentation. Canonical page: <https://octelium.com/docs/octelium/latest/overview/management>.

An Octelium *Cluster* is designed to be managed similarly to Kubernetes clusters. The main way to manage an Octelium *Cluster* is via the `octeliumctl` CLI. If you're accustomed to `kubectl`, you will feel right at home very quickly as it follows the same declarative philosophy enabling you to define your resources in one or more `yaml` files and by just using a single command (i.e. `octeliumctl apply`) you can apply all changes to the *Cluster* and synchronize its state (read more [here](https://octelium.com/docs/octelium/latest/management/core/overview.md)). The declarative way enables you to grow your *Cluster* resources very easily in an organized and trackable way that can be stored in a Git repository where you can effortlessly update/rollback your *Cluster* state with a single command.

> **Note:**
>
> Check out the quick installation guide [here](https://octelium.com/docs/octelium/latest/overview/quick-install.md) to quickly install an Octelium *Cluster* on any cloud VM/VPS.

## Creating our first Service

Each protected resource is represented in the *Cluster* by a *Service*. A *Service* is implemented by an identity-aware proxy (IaP) called [*Vigil*](https://octelium.com/docs/octelium/latest/reference/components.md#vigil) abstracting all dynamic network-layer details of the protected resource (i.e. upstream) behind it and is capable of providing secure secretless access that eliminates sharing and managing L7 credentials such as API keys and database passwords (read more about secretless access [here](https://octelium.com/docs/octelium/latest/management/core/service/secretless.md)) for various protocols including HTTP, SSH, PostgreSQL, MySQL, among others besides protecting generic TCP/UDP-based applications (read more about *Service* modes [here](https://octelium.com/docs/octelium/latest/management/core/service/overview.md#mode)). The upstream of the *Service* can be:

- An internal resource running in any private network (e.g. on-prem, private cloud, your own laptop behind a NAT, etc...) which can be a static IPv4 or IPv6 address or a FQDN with dynamic endpoints as is the case with Kubernetes services for example. Furthermore, you can use an internal resource directly accessible from the private network where the Octelium *Cluster* (and its underlying Kubernetes cluster) is running.
- A publicly protected resource such as SaaS APIs, databases and SSH servers. See some examples ([here](https://octelium.com/docs/octelium/latest/management/guide/service/http/api-gateway.md) and [here](https://octelium.com/docs/octelium/latest/management/guide/service/databases/neon.md)).
- Octelium can also automatically deploy, scale and secure access for your containerized apps and microservices to be served as *Services* (read more about managed containers [here](https://octelium.com/docs/octelium/latest/management/core/service/managed-containers.md)).

We are going to define our first *Service* with the name `first-service` whose upstream is the public URL `https://www.google.com`:

```yaml
kind: Service
metadata:
  name: first-service
spec:
  mode: HTTP
  config:
    upstream:
      url: https://www.google.com
```

Serving the public website `https://www.google.com` as a *Service*, however, isn't particularly useful, beyond demonstrating that a *Service* can serve public or internal/private FQDNs that might point to dynamic upstreams with changing IP addresses—a capability remote access VPNs lack since they operate at the network layer/layer-3. We can do something a little more interesting, like deploying the `nginx` container image and serve it as a *Service* (read more about managed containers [here](https://octelium.com/docs/octelium/latest/management/core/service/managed-containers.md)) as follows:

```yaml
kind: Service
metadata:
  name: first-service
spec:
  mode: HTTP
  config:
    upstream:
      container:
        port: 80
        image: nginx:latest
        replicas: 3
```

A *Service* can also provide *secretless* access for the authorized *Users* to an upstream that requires an application-layer (L7) credential such as HTTP bearer access tokens (read more about secretless access [here](https://octelium.com/docs/octelium/latest/management/core/service/secretless.md)). Octelium's application-layer awareness allows you to simply eliminate the need to manage and distribute such credentials, which are often long-lived and over-privileged in many practical cases, at any scale. Here is an example:

```yaml
kind: Service
metadata:
  name: first-service
spec:
  mode: HTTP
  config:
    upstream:
      url: https://api.openai.com
    http:
      path:
        addPrefix: /v1
      auth:
        bearer:
          #!mark
          fromSecret: openai-api-key
```

> **Note:**
>
> The highlighted `openai-api-key` is the name of a *Secret* that actually represents the API access token, referenced in the *Service* by its name in order to not store sensitive data along with other *Cluster* configurations which can be stored in git repositories for example  (read more about creating *Secrets* [here](https://octelium.com/docs/octelium/latest/management/core/secret.md)).

> **Note:**
>
> You can read more about HTTP-specific configuration that includes request/response header manipulation, JSON schema validation, caching, rate limiting, Lua scripting, Envoy ExtProc and dynamic direct responses [here](https://octelium.com/docs/octelium/latest/management/core/service/http.md) and [here](https://octelium.com/docs/octelium/latest/management/core/service/http-plugins.md).

Octelium's application-layer awareness is not exclusive to `HTTP`-based *Services*. You can also define an `SSH` *Service* that provides secretless access without having to distribute and share SSH passwords or private keys (read more [here](https://octelium.com/docs/octelium/latest/management/core/service/ssh.md#secretless-access)). Here is an example:

```yaml
kind: Service
metadata:
  name: ssh1
spec:
  mode: SSH
  port: 2022
  config:
    upstream:
      url: ssh://address-to-host
    ssh:
      user: root
      auth:
        password:
          fromSecret: ssh1-password
      upstreamHostKey:
        key: ssh-rsa AAAA...
```

> **Note:**
>
> Octelium also supports an "embedded SSH" mode, where you can serve SSH via an embedded SSH server that is running from within the `octelium` client without having to rely on an existing SSH server on the host. This can be especially useful for constrained environments such as containers and IoT fleets (read more about the embedded SSH mode [here](https://octelium.com/docs/octelium/latest/management/core/service/embedded-ssh.md)).

> **Note:**
>
> Octelium currently supports several L7 aware modes:  [SSH](https://octelium.com/docs/octelium/latest/management/core/service/ssh.md), [PostgreSQL](https://octelium.com/docs/octelium/latest/management/core/service/postgres.md), [MySQL](https://octelium.com/docs/octelium/latest/management/core/service/mysql.md), [DNS](https://octelium.com/docs/octelium/latest/management/core/service/dns.md),  [gRPC](https://octelium.com/docs/octelium/latest/management/core/service/http.md#grpc-mode),  [Web](https://octelium.com/docs/octelium/latest/management/core/service/http.md#web-app-mode),  [Kubernetes](https://octelium.com/docs/octelium/latest/management/core/service/kubernetes.md) in addition to the raw [TCP](https://octelium.com/docs/octelium/latest/management/core/service/tcp.md) and [UDP](https://octelium.com/docs/octelium/latest/management/core/service/udp.md) modes.

And you can also serve any remote upstream from the host of any connected *User* to the *Cluster* (read more [here](https://octelium.com/docs/octelium/latest/user/cli/connect.md#serving-services)) as follows:

```yaml
kind: Service
metadata:
  name: first-service
spec:
  mode: HTTP
  config:
    upstream:
      url: https://www.google.com
      # !mark
      user: john
```

This allows you to serve upstreams running behind NAT from anywhere (e.g. private clouds, on-prem, your own laptop, IoT devices, etc...). You can see a more detailed example [here](https://octelium.com/docs/octelium/latest/management/guide/service/http/open-source-self-hosted-ngrok-alternative.md).

Now, to actually apply the creation of our *Service* `first-service`, we use the `octeliumctl apply` command (read more [here](https://octelium.com/docs/octelium/latest/management/core/overview.md#applying-changes)) as follows:

```bash
octeliumctl apply --domain <DOMAIN> /path/to/octelium/config/dir
```

> **Note:**
>
> If you're familiar with Kubernetes, you might have noticed the YAML visual resemblance between Octelium resources and Kubernetes resources and now you might be wondering whether Octelium resources are just Kubernetes resources or simply CRDs. The answer is no. Octelium and its resources are completely separate from Kubernetes. Octelium has its own gRPC-based [*API Server*](https://octelium.com/docs/octelium/latest/reference/components.md#api-server), and its resources are stored in the PostgreSQL data store via the [*Resource Server*](https://octelium.com/docs/octelium/latest/reference/components.md#resource-server).

Since the Octelium CLIs (i.e. `octelium` and `octeliumctl`) are designed to work with multiple *Clusters* as defined by their own domains, you will have to add the `--domain` flag to every command. It's much easier to define the domain as an environment variable in your shell once and then use any command without having to add the `--domain` flag as follows:

```bash
export OCTELIUM_DOMAIN=<DOMAIN>
```

Or in Windows PowerShell as follows:

```powershell
$env:OCTELIUM_DOMAIN = "<DOMAIN>"
```

Now let's connect to our *Cluster* as follows:

```bash
# Connect in background via the detached mode
octelium connect -d
# OR in foreground
sudo -E octelium connect
```

> **Note:**
>
> In Windows, you need to run your PowerShell as administrator as equivalent of `sudo` in Linux and macOS. In Windows 11, however, `sudo` is natively supported.

Now that we're connected to the *Cluster*, we can access the *Service* `first-service` simply using any tool that can talk HTTP, let's try `curl` for example as follows:

```bash
curl first-service
# This is equivalent to
curl first-service.local.<DOMAIN>
```

> **Note:**
>
> Why does this hostname `first-service` work? Simply put, when you connect to a *Cluster* via the command `octelium connect`, Octelium automatically configures your machine DNS and adds the suffix `.local.<DOMAIN>`, which is the common suffix for all *Services* in the *Cluster*, to your machine's DNS search domains so you don't have to type the entire private FQDN yourself.

You can also run `octelium connect` as a completely unprivileged process and map the *Service* `first-service` to a localhost port (read more [here](https://octelium.com/docs/octelium/latest/user/cli/connect.md#mapping-services-to-host)) as follows:

```bash
octelium connect -p first-service:8080
```

In the above example, we mapped the *Service* to the localhost port `8080`. Now we can access it as follows:

```bash
curl localhost:8080
```

> **Note:**
>
> You can read more about connecting to *Clusters* via the `octelium` CLI and its more advanced options
> [here](https://octelium.com/docs/octelium/latest/user/cli/connect.md).

So far, in order to access our *Service* `first-service`, *Users* will have to use the `octelium` CLI and connect to the *Cluster* first. Octelium, however, also supports the clientless BeyondCorp mode, which enables you to securely expose an HTTP-based *Service* publicly in order to be accessed by authorized `HUMAN` *Users* via their browsers and even by `WORKLOAD` *Users* through standard OAuth2 client credentials authentication flow (read more [here](https://octelium.com/docs/octelium/latest/management/core/service/clientless.md)). You can very simply do so as follows:

```yaml
kind: Service
metadata:
  name: first-service
spec:
  mode: WEB
  # !mark
  isPublic: true
  config:
    upstream:
      container:
        port: 80
        image: nginx:latest
```

And now, authorized *Users* can publicly access the *Service* via their browsers at the public URL `https://first-service.<DOMAIN>`

Octelium can also fully expose a *Service* for anonymous access. This allows you to effectively use Octelium as a self-hosted PaaS or a hosting platform where you can publicly expose HTTP-based *Services* to the public internet. Such *Services*' upstreams might be running anywhere or be deployed as managed containers as we have seen earlier. You can read more about the anonymous access mode [here](https://octelium.com/docs/octelium/latest/management/core/service/anonymous-access.md). Here is an example:

```yaml
kind: Service
metadata:
  name: first-service
spec:
  mode: WEB
  # !mark(1:2)
  isPublic: true
  isAnonymous: true
  config:
    upstream:
      container:
        port: 80
        image: nginx:latest
```

You can also use dynamic configuration in order to, for example, route to different upstreams and/or set different upstream credentials mapping to different upstream accounts and permissions, set different request/response headers, etc... depending on the request's identity and/or context (read more about dynamic configuration [here](https://octelium.com/docs/octelium/latest/management/core/service/dynamic-config.md)). Here is a simple example that can be used for an API gateway use case (read more [here](https://octelium.com/docs/octelium/latest/management/guide/service/http/api-gateway.md)):

```yaml
kind: Service
metadata:
  name: my-api
spec:
  mode: HTTP
  isPublic: true
  dynamicConfig:
    configs:
      - name: v1
        upstream:
          url: https://apiv1.example.com
      - name: v2
        upstream:
          url: https://apiv2.example.com
    rules:
    - condition:
        match: ctx.request.http.path.startsWith("/v1")
      configName: v1
    - condition:
        match: ctx.request.http.path.startsWith("/v2")
      configName: v2
```

Dynamic configuration can also be used for other layer-7 aware modes such as `SSH`, `POSTGRES` and `MYSQL` as well as for `TCP` and `UDP`. For example, you can dynamically force certain *Users* to log in as certain SSH users based on identity or context via policy-as-code. For PostgreSQL or MySQL, you can enforce *Users* to use certain database users/passwords as well as different databases and even upstreams based on identity and context.

## Creating a User

There are 2 types of *Users* (You can read in detail about *User* management [here](https://octelium.com/docs/octelium/latest/management/core/user.md)): `HUMAN` *Users* and `WORKLOAD` *Users* which can be used by non-human entities (e.g. servers, VMs and microVMs, containers, applications, IoT devices, etc...).

Both *User* types can use the private client-based ZTNA mode, which acts as a zero-config VPN from the *User*'s perspective where *Users* can address *Services* via stable private FQDNs and hostnames assigned by the *Cluster*. Both *User* types can also securely access publicly exposed HTTP-based *Services* via the clientless, public BeyondCorp mode (read more [here](https://octelium.com/docs/octelium/latest/management/core/service/clientless.md)) as follows:

- `HUMAN` *Users* can access web-based *Services* via their browsers without having to install clients on their machines.
- `WORKLOAD` *Users* can use the OAuth2 client credentials flow enabling your applications written in any programming language to access any publicly exposed HTTP-based *Service* (e.g. HTTP and gRPC APIs, Kubernetes API servers, etc...) solely via standard OAuth2 libraries without having to use any special SDKs or install any clients (read more [here](https://octelium.com/docs/octelium/latest/management/core/credential.md#oauth2-client-credentials)). Moreover, Golang-based applications can use the official Golang SDK (read more [here](https://octelium.com/docs/octelium/latest/management/guide/sdk.md)) to control the *Cluster* and access its *Services*.

We are now going to create our first *User* for a friend in order to be able to connect to our *Cluster* and access our *Service* `first-service`. As always, we add a new `yaml` file, in this example `users.yaml`, in the same directory (i.e. `/path/to/octelium/config/dir`) dedicated to *Users*.

Now we can define our *User* `john` as follows:

```yaml
kind: User
metadata:
  name: john
spec:
  type: HUMAN
  email: john@example.com
```

And to actually apply the creation of our *User*, we use the `octeliumctl apply` command as follows:

```bash
octeliumctl apply /path/to/octelium/config/dir
```

## User Authentication

`HUMAN` *Users* can use their emails to authenticate to the *Cluster* via web browsers using *IdentityProviders*. There are currently 3 methods:

- GitHub OAuth *IdentityProvider* as shown in detail [here](https://octelium.com/docs/octelium/latest/management/core/identity-providers.md#github-oauth2)
- OpenID Connect *IdentityProviders* (e.g. Okta, Auth0, etc...) as shown [here](https://octelium.com/docs/octelium/latest/management/core/identity-providers.md#openid-connect).
- SAML 2.0 *IdentityProviders* (e.g. Okta, Entra ID, etc...) as shown [here](https://octelium.com/docs/octelium/latest/management/core/identity-providers.md#saml-20).

> **Note:**
>
> You can also read detailed guides on how to self-host FOSS identity providers (IdPs) on the same Kubernetes cluster that is running the Octelium *Cluster* for [Keycloak](https://octelium.com/docs/octelium/latest/management/guide/service/homelab/keycloak-helm-self-host.md), [Authentik](https://octelium.com/docs/octelium/latest/management/guide/service/homelab/authentik-helm-self-host.md) and [Dex](https://octelium.com/docs/octelium/latest/management/guide/service/homelab/dex-helm-self-host.md) and use them to create an OpenID Connect *IdentityProvider* that can be used to login into the *Cluster*.

Furthermore, `HUMAN` *Users* can register their FIDO2 *Authenticators* (e.g. Yubikeys) in order to natively login later via Passkey (read more [here](https://octelium.com/docs/octelium/latest/management/core/authenticator.md#passkey-login)).

> **Note:**
>
> You can read more about *Authenticators* and WebAuthn/TOTP MFA as shown [here](https://octelium.com/docs/octelium/latest/management/core/authenticator.md).

For `WORKLOAD` *Users*, they can authenticate themselves via the `octelium login` or `octeliumctl login` commands using various ways:

- Authentication token *Credentials* (read more [here](https://octelium.com/docs/octelium/latest/management/core/credential.md#authentication-tokens))
- OAuth2 client credentials (read more [here](https://octelium.com/docs/octelium/latest/management/core/credential.md#oauth2-client-credentials))
- "Secretless" OpenID Connect identity assertions (read more [here](https://octelium.com/docs/octelium/latest/management/core/identity-providers.md#oidc-assertion)).
- Access tokens directly issued and used as bearer authentication tokens (read more [here](https://octelium.com/docs/octelium/latest/management/core/credential.md#access-tokens)).

A *User* can interact with the *Cluster* and access its *Services* **only** through a valid *Session* that is automatically created by the *Cluster* upon a valid authentication via an authentication token, OAuth2 client credential authentication, or an *IdentityProvider*. A *User* needs to periodically re-authenticate to keep the *Session* valid until it eventually expires. You can read more about *Session* management [here](https://octelium.com/docs/octelium/latest/management/core/session.md).

## Access Control

Now, while our friend `john` can actually connect to our *Cluster*, he still cannot access the *Service* `first-service` unless we explicitly allow him via a *Policy*. For example, we can allow `john` to access everything in our *Cluster* by attaching the `allow-all` *Policy* as follows:

```yaml
kind: User
metadata:
  name: john
spec:
  type: HUMAN
  email: john@example.com
  # !mark(1:2)
  authorization:
    policies: ["allow-all"]
```

> **Note:**
>
> This `allow-all` *Policy* is automatically created during the *Cluster* installation and you can modify/delete it, whenever you want to.

Allowing `john` to access everything in the *Cluster*, however, does not sound like a very good idea since we seek to only grant access our `first-service` *Service*. For example, we can, instead, define an *inline* *Policy* in our *Service* to allow anybody whose email belongs to our domain `example.com` to access `first-service` as follows:

```yaml
kind: Service
metadata:
  name: first-service
spec:
  mode: HTTP
  config:
    upstream:
      url: https://example.com
  #!mark(1:7)
  authorization:
    inlinePolicies:
      - spec:
          rules:
            - effect: ALLOW
              condition:
                match: ctx.user.spec.email.endsWith("@example.com")
```

As you can see, you are free to either define your access control rules through inline *Policies* or standalone, reusable *Policies* that can be attached to any  *Service*, *Namespace*, *User*, *Group*, *Device* or *Session*. Here is how to re-define the above inline *Policy* as a standalone *Policy* that can be used and attached by other resources:

```yaml
kind: Policy
metadata:
  name: first-policy
spec:
  rules:
    - effect: ALLOW
      condition:
        match: ctx.user.spec.email.endsWith("@example.com")
```

Now we attach it to our *Service* as follows:

```yaml
kind: Service
metadata:
  name: first-service
spec:
  mode: HTTP
  config:
    upstream:
      url: https://example.com
  # !mark(1:2)
  authorization:
    policies: ["first-policy"]
```

We can also define a *Policy* for a whole set of *Users*. This is what a *Group* is for, among other functionalities (read more about *Groups* [here](https://octelium.com/docs/octelium/latest/management/core/group.md)). Let's define our first *Group* `friends` and attach `john` to it.

```yaml
kind: Group
metadata:
  name: friends
spec:
  # !mark(1:2)
  authorization:
    policies: ["first-policy"]
```

Now let us add `friends` as one of `john`'s *Groups* as follows:

```yaml title="users.yaml" {7}
kind: User
metadata:
  name: john
spec:
  type: HUMAN
  groups: ["friends"]
```

Now, any *User* belonging to the `friends` *Group* will be automatically subjected to all of its *Policies* and inline *Policies*' rules.

Octelium enables you to seamlessly define dynamic, fine-grained L7-aware *Policies*. For example, you can control access based on the HTTP request path, method, query parameters, and even serialized JSON body content (read more [here](https://octelium.com/docs/octelium/latest/management/core/service/http.md#access-control)). Here is an example:

```yaml
kind: Service
metadata:
  name: first-service
spec:
  mode: HTTP
  config:
    upstream:
      url: https://example.com
  authorization:
    inlinePolicies:
      - spec:
          rules:
          - effect: ALLOW
            condition:
              all:
                of:
                - match: ctx.request.http.method in ["GET", "POST", "PUT", "DELETE"]
                - match: ctx.request.http.path.startsWith("/apis")
                - match: ctx.request.http.uri == "/apis/users?name=john"
                - match: ctx.request.http.queryParams.name == "john"
                - match: ctx.request.http.headers["x-custom-header"] == "this-value"
                - match: ctx.request.http.scheme == "http"
                - match: string(ctx.request.http.body).toLower().contains("value1")
                # JSON body map
                - match: ctx.request.http.bodyMap.key1 == "value1"
```

Access control is the essence of the zero trust security model. This example is just the simplest use case of what you can do with Octelium's scalable, identity-based, fine-grained, context-aware, L7-aware access control system. Octelium supports defining *Policy* rules in Common Expression Language (CEL) and Open Policy Agent (OPA), rule priorities, nested conditions and extending access control by adding attributes to your different resources (e.g. *Users*, *Groups*, *Services*, etc...) from information provided by external tools such as IAM platforms, SIEM tools, threat intelligence tools, incident alerting and on-call management tools, etc.... You can read more in detail about *Policies* and access control [here](https://octelium.com/docs/octelium/latest/management/core/policy.md).

## What Now?

This was just a quick guide to show you the main different features of Octelium. Octelium's architecture is designed to be flexible enough to be used as a Zero Trust Network Access (ZTNA) solution, a complete solution for secure tunnels, an API gateway (read more [here](https://octelium.com/docs/octelium/latest/management/guide/service/http/api-gateway.md)), an AI gateway (read more [here](https://octelium.com/docs/octelium/latest/management/guide/service/ai/ai-gateway.md)) and even can be used as a more advanced Kubernetes ingress/load balancer alternative as well as a self-hosted PaaS-like deployment and hosting platform to deploy, scale and provide secure or public anonymous access for your containerized applications such as Vite.js/Next.js/Astro web apps (see more [here](https://octelium.com/docs/octelium/latest/management/guide/service/http/nextjs-vite.md)).
