# Secret

Kubernetes Secrets are used to store sensitive information such as passwords, API keys, and certificates in a secure way. They help separate configuration from application code, improving security and manageability.

## Type of Secret

Here are built-in secret type available in kubernetes:

| Type                                     | Usage                                    |
| ---------------------------------------- | ---------------------------------------- |
| **Opaque**:                              | arbitrary user-defined data.             |
| **kubernetes.io/service-account-token**: | ServiceAccount token.                    |
| **kubernetes.io/dockercfg**:             | serialized `~/.dockercfg` file.          |
| **kubernetes.io/dockerconfigjson**:      | serialized `~/.docker/config.json` file. |
| **kubernetes.io/basic-auth**:            | credentials for basic authentication.    |
| **kubernetes.io/ssh-auth**:              | credentials for SSH authentication.      |
| **kubernetes.io/tls**:                   | data for a TLS client or server.         |
| **bootstrap.kubernetes.io/token**:       | bootstrap token data.                    |

## Opaque Secret

Previously we already create a postgres service with persistent volume. But we still use plain text as the username and password config. We can use `secret` to properly store the sensitive configuration and use it in our postgres deployment.

Create new file called `postgres-secret.yaml` and put the secret definition there.

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: postgres-secret
type: Opaque
data:
  POSTGRES_USER: "YWRtaW4="
  POSTGRES_PASSWORD: "cGFzc3dvcmQ="
```

The `data` section contains key value pair of our sensitive data. In here we store 2 data for username and password with key `POSTGRES_USERNAME` and `POSTGRES_PASSWORD` respectively. The value of the pair need to be `base64` encoded first. We can use command `echo -n "admin" | base64` to encode it.

Lets apply and validate our secret.

```bash
➜ kubectl apply -f postgres-secret.yaml 
secret/postgres-secret created
```

```bash
➜ kubectl get secret  
NAME              TYPE     DATA   AGE
postgres-secret   Opaque   2      21s
```

As you can see above the secret is created with `DATA` length is `2`. We also can describe it to see the details.

```bash
➜ kubectl describe secrets postgres-secret
Name:         postgres-secret
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
POSTGRES_PASSWORD:  8 bytes
POSTGRES_USER:      5 bytes
```

## Using Secret

### Postgres

To use secret in our postgres deployment we need to update our env from plain text to value from secret.

```yaml
env:
- name: POSTGRES_USER
  value: "admin"
- name: POSTGRES_PASSWORD
  value: "password"
```

Replace above section with this section below. This will tell kubernetes to look the value from `secret` with name `postgres-secret` and key `POSTGRES_USER` for the user and `POSTGRES_PASSWORD` for the password.

```yaml
env:
- name: POSTGRES_USER
  valueFrom:
    secretKeyRef:
      name: postgres-secret
      key: POSTGRES_USER
- name: POSTGRES_PASSWORD
  valueFrom:
    secretKeyRef:
      name: postgres-secret
      key: POSTGRES_PASSWORD
```

Lets apply and validate if the pods running without error. We can do similar testing like before, by doing port forward and then connect using `psql`. We should still able to connect and see the data without any error.

```
admin=# \c mydb
You are now connected to database "mydb" as user "admin".
mydb=# SELECT * FROM message;
 id | content 
----+---------
  1 | Hello!
(1 row)
```

### Simple V2

Then lets update our apps to write and read from the database. You can just copy paste this and then run `go get .` to download all the dependencies.

```go
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"

    "github.com/gorilla/mux"
    "github.com/jmoiron/sqlx"
    _ "github.com/lib/pq"
)

type Message struct {
    ID      int    `json:"id" db:"id"`
    Content string `json:"content" db:"content"`
}

func main() {
    // Get user and password from env
    user := os.Getenv("POSTGRES_USER")
    password := os.Getenv("POSTGRES_PASSWORD")
    // Database connection
    host := "postgres.default.svc.cluster.local"
    connectionStr := fmt.Sprintf("host=%s user=%s password=%s dbname=mydb sslmode=disable", host, user, password)
    db, err := sqlx.Connect("postgres", connectionStr)
    if err != nil {
        log.Fatal(err)
    }

    // handlers
    mux := mux.NewRouter()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        // fetch data from database
        messages := []Message{}
        err := db.SelectContext(
            r.Context(),
            &messages,
            "SELECT * FROM message",
        )
        if err != nil {
            w.WriteHeader(http.StatusInternalServerError)
            w.Write([]byte(err.Error()))
            return
        }
        // return it as json response
        err = json.NewEncoder(w).Encode(messages)
        if err != nil {
            w.WriteHeader(http.StatusInternalServerError)
            w.Write([]byte(err.Error()))
            return
        }
    }).Methods(http.MethodGet)
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        // get content from json body
        message := &Message{}
        err := json.NewDecoder(r.Body).Decode(message)
        if err != nil {
            w.WriteHeader(http.StatusInternalServerError)
            w.Write([]byte(err.Error()))
            return
        }
        // save to database
        _, err = db.NamedExecContext(
            r.Context(),
            "INSERT INTO message (content) VALUES (:content)",
            message,
        )
        if err != nil {
            w.WriteHeader(http.StatusInternalServerError)
            w.Write([]byte(err.Error()))
            return
        }
        w.Write([]byte("SUCCESS"))
    }).Methods(http.MethodPost)

    // start server
    fmt.Println("Server is running on port 8080...")
    err = http.ListenAndServe(":8080", mux)
    if err != nil {
        log.Fatal(err)
    }
}
```

This app will get `user` and `password` configuration from environment variable and use it to connect to the server. And to access our service (postgres) from internal cluster we can use this format `<service_name>.<namespace>.svc.cluster.local`.

Build our app and tag it as `simple-go:v2`.

```bash
➜ docker build --tag simple-go:v2 .
```

Update our deployment to use image `simple-go:v2` and replace the `env` with this one below. This will make `POSTGRES_USER` and `POSTGRES_PASSWORD` environment variable available in our apps.

```yaml
env:
- name: POSTGRES_USER
  valueFrom:
    secretKeyRef:
      name: postgres-secret
      key: POSTGRES_USER
- name: POSTGRES_PASSWORD
  valueFrom:
    secretKeyRef:
      name: postgres-secret
      key: POSTGRES_PASSWORD
```

Apply and check if the pods running as expected.

To test it we can use `minkube service` command to expose our service. Then run `curl` to the given url.

```bash
➜ curl 'http://localhost:58834'
[{"id":1,"content":"Hello!"}]
```

We should see our data there. Try to send `POST` request to insert new data and `GET` again.

```bash
➜ curl -X POST -d '{"content": "Good Morning!"}' 'http://localhost:58834/'
SUCCESS
➜ curl 'http://localhost:58834'                                           
[{"id":1,"content":"Hello!"},{"id":2,"content":"Good Morning!"}]
```

We should see new data is properly inserted and returned.

## References

* <https://kubernetes.io/docs/concepts/configuration/secret/>
* <https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/>


---

# 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://bagus-cahyono.gitbook.io/programming-notes/cka/09_secret.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.
