# Deploying HQ

{% hint style="info" %}
Lenses HQ is prerequisite for installation of Lenses Agent
{% endhint %}

{% hint style="info" %}
Latest images:

Latest HQ image available [here](https://hub.docker.com/r/lensesio/lenses-hq) from Docker Hub.

Latest Lenses CLI as [tarball](https://archive.lenses.io/lenses/6.1/cli/) or as a [container](https://hub.docker.com/r/lensesio/lenses-cli).
{% endhint %}

## Prerequisites

* Kubernetes 1.23+
* Helm 3.8.0+
* Available local Postgres database instance:
  * If you need to install Postgres on Kubernetes you can use one of the many publicly available Helm charts such as [Bitnami's](https://bitnami.com/stack/postgresql/helm).
  * Or you can use one of the cloud provider's Postgres services such as one of these: [AWS](https://aws.amazon.com/rds/postgresql/), [Azure](https://azure.microsoft.com/en-us/products/postgresql), or [GCP](https://cloud.google.com/sql/postgresql).
  * See Lenses [docs here](https://docs.lenses.io/latest/getting-started/connecting-lenses-to-your-environment/overview#postgres) for information on configuring Postgres to work with HQ .
  * username (and password) that has access to HQ database;
* [External Secrets Operator](https://external-secrets.io/latest/) is the only supported secrets operator.

## Configure HQ

To configure Lenses HQ properly we have to understand the parameter groups that the Chart offers.

Under the **lensesHq** parameter there are some key parameter groups that are used to set up HQ:

1. [**storage**](#configure-storage-postgres)
   * definition of connection towards database (Postgres is **the only storage** option)
2. [**auth**](#configure-auth-endpoint)
   * Password based authentication configuration
   * SAML / SSO configuration
   * definition of administrators or first users to access the HQ
3. [**http**](#configure-http-endpoint)
   * defines port under which HQ will be available for end users
   * defines values of special headers and cookies
   * types of connection such as TLS and non-TLS definitions
4. [**agents**](#configure-agents-connection-endpoint)
   * defines connection between HQ and the Agent such as port where HQ will be listening for agent connections.
   * types of connection such as TLS and non-TLS definitions
5. **license**
6. [**monitoring**](#configure-metrics-endpoint)
   * controls the metrics settings where Prometheus alike metrics will be exposed
7. [**loggers**](#configure-logging)
   * definition of logging level for HQ

Moving forward, in the same order you can start configuring your Helm chart.

***

{% stepper %}
{% step %}
**Configure storage (Postgres)**

{% hint style="info" %}
Postgres **is the only** available storage option.
{% endhint %}

Prerequisite:

* Running Postgres instance;
* Created database for HQ;
* Username (and password) which has access to the created database;

In order to successfully run HQ, ***storage*** within *values.yaml* has to be defined first.

Definition of *storage* object is as follows:

```yaml
lensesHq:
  storage:
    postgres:
      enabled: true
      host: ""
      port: 
      username: ""
      database: ""
      schema: ""
      tls: 
      params: {}
      passwordSecret:
        type: ""
```

Alongside Postgres password, which can be referenced / created through Helm chart, there are few more options which can help while setting up HQ.

**Username reference types**

There are two ways how username can be defined:

{% tabs %}
{% tab title="Plain Value" %}
The most straightforward way, if the username is not being changed, is by just defining it within the *username* parameter such as

{% code title="values.yaml" %}

```yaml
lensesHq:
  storage:
    postgres:
      enabled: true
      host: postgres-postgresql.postgres.svc.cluster.local
      port: 5432
      username: lenses
```

{% endcode %}
{% endtab %}

{% tab title="Reference from Secret" %}
In case Postgres *username* is being rotated or frequently changed it can be referenced from **pre-created** secret

{% code title="values.yaml" %}

```yaml
lensesHq:
  storage:
    postgres:
      enabled: true
      host: postgres-postgresql.postgres.svc.cluster.local
      port: 5432
      username: lenses
      useSecretForUsername:
        enabled: true
        existingSecret:
          name: my-secret
          key: username
```

{% endcode %}
{% endtab %}
{% endtabs %}

**Password reference types**

Postgres password can be handled in three ways using:

1. External Secret via [ExternalSecretOperator](https://external-secrets.io/latest/);
2. Pre-created secret;
3. Creating secret on the spot through **values.yaml;**

{% tabs %}
{% tab title="External Secrets" %}
{% hint style="warning" %}
To use this option, the External Secret Operator (ESO) has to be installed and available for usage in K8s cluster your are deploying HQ.
{% endhint %}

When specifying ***passwordSecret.type: "externalSecret",*** the chart will:

* create an ***ExternalSecret*** in the namespace where HQ is deployed;
* a secret is mounted for HQ to use.

{% code title="values.yaml" %}

```yaml
lensesHq:
  storage:
    postgres:
      enabled: true
      host: postgres-postgresql.playground.svc.cluster.local
      port: 5432
      username: lenses
      database: lenseshq
      passwordSecret:
        type: "externalSecret"
        # Secret name where database password will be read from
        name: hq-password
        # Key name under secret where database password is stored
        key: password
        externalSecret:
          additionalSpecs: {}
          secretStoreRef:
            type: SecretStore # or ClusterSecretStore
            name: secretstore-secrets            
```

{% endcode %}
{% endtab %}

{% tab title="Pre-created secrets" %}
{% hint style="info" %}
Make sure that secret you are going to use is already created in namespace where HQ will be installed.
{% endhint %}

{% code title="values.yaml" %}

```yaml
 lensesHq:
   storage:  
     postgres:
       enabled: true
       host: postgres-postgresql.playground.svc.cluster.local
       port: 5432
       username: lenses
       database: lenseshq
       passwordSecret:
         type: "precreated"
         # Secret name where database password will be read from
         name: hq-password
         # Key from secret's data where database password is being stored
         key: postgres-password
```

{% endcode %}
{% endtab %}

{% tab title="Create New Secret" %}
{% hint style="danger" %}
This option is **NOT** for PRODUCTION usage but rather just for demo / testing.
{% endhint %}

The chart will create a secret with defined values below and the same secret will be read by HQ in order to connect to Postgres.

{% code title="values.yaml" %}

```yaml
 lensesHq:
   storage:
     postgres:
       enabled: true
       host: [POSTGRES_HOSTNAME]
       port: 5432
       username: lenses
       database: lenseshq
       passwordSecret:
         type: "createNew"
         # name of a secret that will be created
         name: [K8s_SECRET_NAME]
         # Database password
         password: [DATABASE_USER_PASSWORD]
```

{% endcode %}
{% endtab %}
{% endtabs %}

**Advanced Postgres settings**

Sometimes to form the correct connection URI special parameters are needed. You can set the extra settings using *`params`**.***

**Example:**

{% code title="values.yaml" %}

```yaml
lensesHq:
  storage:
    postgres:
      enabled: true
      host: postgres-postgresql.postgres.svc.cluster.local
      port: 5432
      username: lenses
      params:
        sslmode: require
```

{% endcode %}
{% endstep %}

{% step %}
**Configure AUTH endpoint**

{% hint style="info" %}
SAML / SSO is available only with Enterprise license.
{% endhint %}

The second pre-requirement to successfully run HQ is setting initial authentication.

You can choose between:

* password-based authentication, which requires users to provide a username and password;
* and SAML/SSO (Single Sign-On) authentication, which allows users to authenticate through an external identity provider for a seamless and secure login experience.

{% hint style="info" %}
Assertion Consumer Service endpoint is following

```
/api/v2/auth/saml/callback?client_name=SAML2Client
```

{% endhint %}

The definition of *auth* object is as follows:

{% code title="values.yaml" %}

```yaml
lensesHq:
  auth:
    users:
      - username: admin
        # bcrypt("correcthorsebatterystaple").
        password: $2a$10$F66cb6ZhnJjGCZuxlvKP1e84eytTpT1MDJcpBblHaZgsqp1/Aa0LG
    administrators:
      - admin
      - admin@example.com
      - admin2@example.com
    saml:
      enabled: true
      baseURL: ""
      entityID: ""
      # -- Example: <?xml version="1.0" ... (big blob of xml) </md:EntityDescriptor>
      metadata:
        referenceFromSecret: false
        secretName: ""
        secretKeyName: ""
        stringData: |
          <?xml version="1.0" encoding="UTF-8" standalone="no"?>
          </md:EntityDescriptor>
  
      userCreationMode: "sso"
      usersGroupMembershipManagementMode: "sso"
      uiRootURL: "/"
      groupAttributeKey: "groups"
      authnRequestSignature:
        enabled: false
```

{% endcode %}

**First** to cover is the users property. **Users Property:** The `users` property is defined as an array, where each entry includes a `username` and a `password`. Passwords **must be** hashed using bcrypt before being placed within the *password* property, for security purposes, ensuring that they are stored correctly and securely.

{% tabs %}
{% tab title="Inline Bcrypt hash" %}
{% code title="values.yaml" %}

```yaml
lensesHq:
  auth:
    users:
      - username: admin
        # bcrypt("correcthorsebatterystaple").
        password: $2a$10$F66cb6ZhnJjGCZuxlvKP1e84eytTpT1MDJcpBblHaZgsqp1/Aa0LG
```

{% endcode %}
{% endtab %}

{% tab title="Reading from secret" %}
{% code title="values.yaml" %}

```yaml
lensesHq:
  auth:
    users:
      - username: $(ADMIN_USER)
        password: $(ADMIN_USER_PWD)
  additionalEnv:
    - name: ADMIN_USER
      valueFrom:
        secretKeyRef:
          name: multi-credentials-secret
          key: user1-username
    - name: ADMIN_USER_PWD
      valueFrom:
        secretKeyRef:
          name: multi-credentials-secret
          key: user1-password
```

{% endcode %}
{% endtab %}
{% endtabs %}

**Second,** to cover will be *administrators.* It serves as the definition of user emails have the highest level of permissions upon authentication to HQ.

**Third** attribute is *saml.metadata* field, needed for setting SAML / SSO authentication. In this step, you will need *metadata.xml* file which can be set in two ways:

1. Referencing *metadata.xml* file through pre-created secret;
2. Placing *metadata.xml* contents inline as a string.

{% tabs %}
{% tab title="Reference from Secret" %}

<pre class="language-yaml"><code class="lang-yaml"><strong>lensesHq:
</strong>  auth:
    address: ":8080"
    accessControlAllowOrigin:
      - 
    administrators:
      - admin@example.com
      - admin2@example.com
    saml:
      baseURL: ""
      entityID: ""
      metadata:
        referenceFromSecret: true
        secretName: hq-tls-mock-saml-metadata
        secretKeyName: metadata.xml
      userCreationMode: "sso"
      usersGroupMembershipManagementMode: "manual"
</code></pre>

{% endtab %}

{% tab title="Inline String" %}

```yaml
lensesHq:
  auth:
    address: ":8080"
    accessControlAllowOrigin:
      - 
    administrators:
      - admin@example.com
      - admin2@example.com
    saml:
      baseURL: ""
      entityID: ""
      metadata:
        referenceFromSecret: false
        stringData: |
          <?xml version="1.0" encoding="UTF-8" standalone="no"?>
          ...
          ...
          </md:EntityDescriptor>
      userCreationMode: "sso"
      usersGroupMembershipManagementMode: "sso"
```

{% endtab %}
{% endtabs %}

In case SAML IdP requires certificate verification, same can be enabled and provided in the following way:

{% tabs %}
{% tab title="Reference certificate from Secret" %}
{% code title="values.yaml" %}

```yaml
lensesHq:
  auth:
    saml:
      authnRequestSignature:
        enabled: true
        authnRequestSigningCert:
          referenceFromSecret: true
          secretName: hq-agent-test-authority
          secretKeyName: hq-tls-test.crt.pem
        authnRequestSigningKey:
          secret:
            name: saml-test
            key: privatekey.key
```

{% endcode %}
{% endtab %}

{% tab title="Certificate through String" %}
{% code title="values.yaml" %}

```yaml
lensesHq:
  auth:
    saml:
      authnRequestSignature:
        enabled: true
        authnRequestSigningCert:
          stringData: |
            -----BEGIN CERTIFICATE-----
            ....
            -----END CERTIFICATE-----
        authnRequestSigningKey:
          secret:
            name: saml-test
            key: privatekey.key

```

{% endcode %}
{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}
**Configure HTTP endpoint**

The third pre-requirement to successfully run HQ is the *`http`* definition. As previously mentioned, this parameter defines everything around the HTTP endpoint of the HQ itself and how users will interact with it.

Definition of HTTP object is as follows:

```yaml
lensesHq:
  http:
    address: ":8080"
    accessControlAllowOrigin:
      - 
    accessControlAllowCredentials: false
    secureSessionCookies: true
    tls:
      enabled: true
      cert:
      privateKey:
        secret:
          name: 
          key:
```

{% hint style="info" %}
Second part of HTTP definition would be enabling TLS and TLS definition itself. As previously defined for ***lensesHq.agents.tls*** same way of configuring TLS can be used for ***lensesHq*****.*****http*****.*****tls*** definition as well.
{% endhint %}
{% endstep %}

{% step %}
**Configure agent's connection endpoint**

After correctly configuring the authentication strategy and connection endpoint, the agent handling is the last most important box to tick.

The Agent's object is defined as follows:

```yaml
lensesHq:
  agents:
    # which port to listen on for agent requests
    address: ":10000" 
    tls:
      enabled: false
      verboseLogs: false
      cert:
      privateKey:
```

**Enabling TLS**

By default TLS for the communication between Agent and HQ is disabled. In case the requirement is to enable it, fthe ollowing has to be set:

* `lensesHq.agents.tls` *-* certificates to manage the connection between HQ and the Agents
* `lensesHq.http.tls`*-* certificates to manage connection with HQ's API

Unlike private keys which can be referenced and obtained only through a secret, Certificates can be referenced directly in **values.yaml** file as a string or as a secret.

{% tabs %}
{% tab title="Reference Certificate from Secret" %}
{% code title="values.yaml" %}

```yaml
lensesHq:
  agents:
    address: ":10000"
    tls:
      enabled: true
      cert:
        referenceFromSecret: true
        secretName: hq-agent-test-authority
        secretKeyName: hq-tls-test.crt.pem
      privateKey:
        secret:
          name: hq-agent-test-authority
          key: hq-tls-test.key.pem
```

{% endcode %}
{% endtab %}

{% tab title="Inline String" %}
{% code title="values.yaml" %}

```yaml
lensesHq:
  agents:
    address: ":10000"
    tls:
      enabled: true
      cert:
        stringData: |
          -----BEGIN CERTIFICATE-----
          ...
          ...
          -----END CERTIFICATE-----
      privateKey:
        secret:
          name: hq-agent-test-authority
          key: hq-tls-test.key.pem
```

{% endcode %}
{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}
**Configure license**

{% hint style="success" %}
In demo purposes and testing the product you can use our community license

```yaml
license_key_2SFZ0BesCNu6NFv0-EOSIvY22ChSzNWXa5nSds2l4z3y7aBgRPKCVnaeMlS57hHNVboR2kKaQ8Mtv1LFt0MPBBACGhDT5If8PmTraUM5xXLz4MYv
```

{% endhint %}

License can be read in multiple ways:

* from a pre-created secret
* directly as a string defined in *values.yaml* file

{% tabs %}
{% tab title="Reference Certificate from Secret" %}
{% code title="values.yaml" %}

```yaml
lensesHq:
  license:
    referenceFromSecret: true
    secretName: hq-license
    secretKeyName: key
    acceptEULA: true
```

{% endcode %}
{% endtab %}

{% tab title="Inline string" %}

<pre class="language-yaml" data-title="values.yaml"><code class="lang-yaml">lensesHq:
  licence:
    referenceFromSecret: false
<strong>    stringData: "license_key_*"
</strong>    acceptEULA: true
</code></pre>

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}
**Configure metrics endpoint**

Metrics are optionally available in a Prometheus format and by default served on port **9090**.

The port can be changed in the following way:

{% code title="values.yaml" %}

```yaml
lensesHq:
  metrics:
    prometheusAddress: ":9090"
```

{% endcode %}
{% endstep %}
{% endstepper %}

***

### (Optional) Configure Ingress & Services

{% hint style="info" %}
Whilst the chart supports setting TLS on Lenses HQ itself we recommend placing it on the Ingress resource
{% endhint %}

Ingress and service resources are optionally supported.

{% hint style="warning" %}
The `http` ingress is intended only for HTTP/S traffic, while the `agents` ingress is designed specifically for TCP protocol. Ensure appropriate ingress configuration for your use case.
{% endhint %}

Enable an Ingress resource in the **values.yaml**:

{% code title="values.yaml" %}

```yaml
ingress:
  http:
    enabled: true
    annotations:
      traefik.ingress.kubernetes.io/router.entrypoints: websecure
    host: example.com
    ingressClassName: ""
    tls:
      enabled: false
      # The TLS secret must contain keys named tls.crt and tls.key that contain the certificate and private key to use for TLS.
      secretName: ""


  agent:
    enabled: true
    agentIngressConfig:
      apiVersion: traefik.containo.us/v1alpha1
      kind: IngressRouteTCP
      metadata:
        name: agents
      spec:
        entryPoints:
          - agents
        routes:
          - match: HostSNI(`example.com`)  # HostSNI to match TLS for TCP
            services:
              - name: lenses-hq            # Replace with your service name
                port: 10000                # Agent default TCP port  
        tls: {}

```

{% endcode %}

Enable a service resource in the **values.yaml**:

{% code title="values.yaml" %}

```yaml
# Lenses HQ service
service:
  enabled: true
  type: ClusterIP
  annotations: {}
  externalTrafficPolicy:
  loadBalancerIP: 130.211.x.x
  loadBalancerSourceRanges:
    - 0.0.0.0/0
```

{% endcode %}

***

### (Optional) Configure Service Accounts

Lenses HQ, by default, uses the **default** Kubernetes service account but you can choose to use a specific one.

If the user defines the following:

{% code title="values.yaml" %}

```yaml
# serviceAccount is the Service account to be used by Lenses to deploy apps
serviceAccount:
  create: true
  annotations: {}
  name: lenses-hq
```

{% endcode %}

The chart will create a new service account in the defined namespace for HQ to use.

***

### (Optional) Enable RBAC

There are two options you can choose between:

1. **rbacEnable: true** - will enable the creation of **ClusterRole** and **ClusterRoleBinding** for the service account mentioned in the snippet above\\
2. **rbacEnable: true** and **namespaceScope: true** - will enable the creation of Role and RoleBinding which is more restrictive.

***

### Configure logging

There are different logging modes and levels that can be adjusted.

{% code title="values.yaml" %}

```yaml
lensesHq:
  logger:
    # Allowed values are: text | json
    mode: "text"

    # Allowed values are: info | debug
    level: "info"
```

{% endcode %}

***

## Add chart repository

First, add the Helm Chart repository using the Helm command line:

```bash
helm repo add lensesio https://helm.repo.lenses.io/
helm repo update
```

{% embed url="<https://github.com/lensesio/lenses-helm-charts/tree/release/6.1>" %}

## Installing HQ

{% hint style="info" %}
Be aware that for the time being and for alpha purposes usage of `--version`is mandatory when deploying Helm chart through Helm repository.
{% endhint %}

{% code title="terminal" %}

```bash
helm install lenses-hq lensesio/lenses-hq \
   --values values.yaml \
   --create-namespace --namespace lenses-hq \
   --version 6.1.2
```

{% endcode %}

## Example Values files

{% hint style="info" %}
Be aware that example of *values.yaml* shows only how all parameters should look at the end. Please fill them with correct values otherwise Helm installation might not be successful.
{% endhint %}

<details>

<summary>Example of <em>values.yaml</em></summary>

More about default values for Lenses HQ Helm Chart can be found in [*values.yaml*](https://github.com/lensesio/lenses-helm-charts/blob/release/6.1/charts/lenses-hq/values.yaml)*.* An example is below:

{% code title="values.yaml" %}

```yaml
resources:
  requests:
  #   cpu: 1
    memory: 4Gi
  limits:
  #   cpu: 2
    memory: 8Gi

image:
  repository: lensesio/lenses:latest

rbacEnable: true
namespaceScope: true

ingress:
  http:
    enabled: true
    annotations:
      traefik.ingress.kubernetes.io/router.entrypoints: websecure
    host: example.com

lensesHq:
  agents:
    address: ":10000"
    tls:
      enabled: true. # optional
      cert:
        referenceFromSecret: true
        secretName: hq-agent-test-authority
        secretKeyName: hq-tls-test.crt.pem
      privateKey:
        secret:
          name: hq-agent-test-authority
          key: hq-tls-test.key.pem
  auth:
    users:
      - username: admin
        # bcrypt("correcthorsebatterystaple").
        password: $2a$10$F66cb6ZhnJjGCZuxlvKP1e84eytTpT1MDJcpBblHaZgsqp1/Aa0LG
    administrators:
      - admin
      - admin@example.com
      - admin2@example.com
    saml:
      enabled: true    # optional
      baseURL: ""
      entityID: ""
      # -- Example: <?xml version="1.0" ... (big blob of xml) </md:EntityDescriptor>
      metadata:
        referenceFromSecret: false
        secretName: ""
        secretKeyName: ""
        stringData: |
          <?xml version="1.0" encoding="UTF-8" standalone="no"?>
          </md:EntityDescriptor>
  
      userCreationMode: "sso"
      usersGroupMembershipManagementMode: "sso"
      uiRootURL: "/"
      groupAttributeKey: "groups"
  http:
    address: ":8080"
    accessControlAllowOrigin:
      - 
    accessControlAllowCredentials: false
    secureSessionCookies: true
    tls:
      enabled: true    # optional
      cert:
      privateKey:
        secret:
          name: 
          key:
  # Find more details in https://docs.lenses.io/current/installation/kubernetes/helm/#helm-storage
  ## Postgres template example: "postgres://[username]:[pwd]@[host]:[port]/[database]?sslmode=require"
  storage:
    postgres:
      enabled: true
      host: [POSTGRES_HOST_URL]
      port: 5432
      username: [POSTGRES_USERNAME]
      database: [POSTGRES_USER_PWD]
      passwordSecret:
        type: "precreated"
        name: initContainer-2-db-secret
        key: password
```

{% endcode %}

</details>

## What's next?

After the successful configuration and installation of HQ, the next steps would be:

1. [**Deploying an Agent**](https://docs.lenses.io/latest/deployment/installation/helm/agent)
2. [**Example Policies**](https://docs.lenses.io/latest/deployment/installation/helm/broken-reference)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.lenses.io/latest/deployment/installation/helm/hq.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
