Task 2 - Creating and Managing ConfigMaps and Secrets

Objective

Learn how cFOS can use ConfigMaps and Secrets to Config itself

Access External Data with ConfigMap

cFOS can continusely watch the Add/Del/Update of the ConfigMap in K8s, then use configMap data to config cFOS.

ConfigMap holds configuration data for pods to consume. configuration data can be binary or text data , both is a map of string. cnofigmap data can be set to “immutable” to prevent the change.

cFOS has build in feature can read the configMap from k8s via k8s API. when cFOS POD serviceaccount configured with a permission to read configMaps, cFOS can read configMap as it’s configuration such as license data , firewall policy related config etc.,

Task: Create a configMap for cFOS to import license

  • First we create CFOS without license
cd $HOME
kubectl create namespace cfostest
kubectl apply -f cfosimagepullsecret.yaml -n cfostest
kubectl apply -f $scriptDir/k8s-201-workshop/scripts/cfos/Task1_1_create_cfos_serviceaccount.yaml  -n cfostest

k8sdnsip=$(k get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
cat << EOF | tee > cfos7210250-deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cfos7210250-deployment
  labels:
    app: cfos
spec:
  replicas: 1
  selector:
    matchLabels:
      app: cfos
  template:
    metadata:
      annotations:
        container.apparmor.security.beta.kubernetes.io/cfos7210250-container: unconfined
      labels:
        app: cfos
    spec:
      initContainers:
      - name: init-myservice
        image: busybox
        command:
        - sh
        - -c
        - |
          echo "nameserver $k8sdnsip" > /mnt/resolv.conf
          echo "search default.svc.cluster.local svc.cluster.local cluster.local" >> /mnt/resolv.conf;
        volumeMounts:
        - name: resolv-conf
          mountPath: /mnt
      serviceAccountName: cfos-serviceaccount
      containers:
      - name: cfos7210250-container
        image: $cfosimage
        securityContext:
          privileged: false
          capabilities:
            add: ["NET_ADMIN","SYS_ADMIN","NET_RAW"]
        ports:
        - containerPort: 80
        volumeMounts:
        - mountPath: /data
          name: data-volume
        - mountPath: /etc/resolv.conf
          name: resolv-conf
          subPath: resolv.conf
      volumes:
      - name: data-volume
        emptyDir: {}
      - name: resolv-conf
        emptyDir: {}
      dnsPolicy: ClusterFirst
EOF
kubectl apply -f cfos7210250-deployment.yaml -n cfostest
kubectl rollout status deployment cfos7210250-deployment -n cfostest
  • Check cFOS running in restricted mode due to no license applied
kubectl logs --tail=100 -n cfostest -l app=cfos | grep license
  • Create a configmap file for cfos license
Tip

labels “app: fos” and “category: config” are required. Especially category is used to distinguish from other ConfigMaps such as license. cFOS only read those configMaps with label “app: fos”.

cat <<EOF | tee cfos_license.yaml
apiVersion: v1
kind: ConfigMap
metadata:
    name: cfos-license
    labels:
        app: fos
        category: license
data:
    license: |+
EOF

now you created a configmap with an empty cFOS license.

Tip

| (Pipe): This is a block indicator used for literal style, where line breaks and leading spaces are preserved. It’s commonly used to define multi-line strings.

The |+ ensures that all the line breaks within the license text

category: license indicate this is a license

  • Add your license

get your license file, then append the content to yaml file, replace “CFOSVLTM24000016.lic” with your actual file name

licfile="CFOSVLTM24000016.lic"
while read -r line; do printf "      %s\n" "$line"; done < $licfile >> cfos_license.yaml
  • Apply the resource
kubectl create -f cfos_license.yaml -n cfostest  

cFOS will “watch” ConfigMap has with label= “app: fos”, then import the license into cFOS.

  • Check cFOS log
kubectl logs -f  -l app=cfos -n cfostest
  • Check whether license applied from cFOS cli
podname=$(kubectl get pod -n cfostest -l app=cfos -o jsonpath='{.items[*].metadata.name}')
kubectl exec -it po/$podname -n cfostest -- /bin/cli

input username “admin”, the default password has not been setup, just press enter key. then issue command

diag sys license

you shall see output like

cFOS # diagnose sys license
Status: Valid license
SN: CFOSVLTM240000**
Valid From: 2024-05-23
Valid To: 2024-07-25

use exit to exit the cFOS command parser

  • Troubleshooting license apply issue

In case you hit license issue, shell into cFOS, run execute update-now to check more detail

Task 2 - Use cFOS ConfigMap for Firewall VIP config

cat << EOF | tee fosconfigmapfirewallvip.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: foscfgvip
  labels:
      app: fos
      category: config
data:
  type: partial
  config: |-
    config firewall vip
           edit "test"
               set extip "10.244.166.15"
               set mappedip "10.244.166.18"
               set extintf eth0
               set portforward enable
               set extport "8888"
               set mappedport "80"
           next
       end
EOF
kubectl create -f fosconfigmapfirewallvip.yaml -n cfostest

use show firewall vip from cFOS cli to check the cFOS vip configuration, cFOS configuration can contains one or more CLI commands. There are two types configurations: partial and full. For a partial configuration, it will be applied on top of current configuration in cFOS. Multiple partial configurations are accepted, so a bigger configuration can be splitted into small ones and apply them one by one. For full configuration, the active configuration will be wiped out and the new configuration will be fully restored.

Tip

type: partial indicates this is a partial configuration

category: config indicates this is a configuation

  • Check Result

Check cFOS container log with kubectl logs -f -l app=cfos -n cfostest . you can find

2024-05-14_10:57:18.63416 INFO: 2024/05/14 10:57:18 received a new fos configmap
2024-05-14_10:57:18.63417 INFO: 2024/05/14 10:57:18 configmap name: foscfgvip, labels: map[app:fos category:config]
2024-05-14_10:57:18.63417 INFO: 2024/05/14 10:57:18 got a fos config
2024-05-14_10:57:18.63417 INFO: 2024/05/14 10:57:18 applying a partial fos config...
2024-05-14_10:57:19.42525 INFO: 2024/05/14 10:57:19 fos config is applied successfully.
  • Delete ConfigMap

Take special care: delete a ConfigMap will not delete configuration on the running cFOS, but you can create a ConfigMap with delete command to delete the configuration.

  • use kubectl delete cm <configMap Name> to delete configmap.
  • Create ConfigMap for cFOS to delete a Firewall Config
cat << EOF | kubectl create -n cfostest -f - 
apiVersion: v1
kind: ConfigMap
metadata:
  name: foscfgvip-del
  labels:
      app: fos
      category: config
data:
  type: partial
  config: |-
    config firewall vip
           del "test"
    end
EOF

Above will delete the configuration from cFOS.

  • Update ConfigMap

Update a Configmap will also update the configuration on cFOS

  • cFOS configMap with data type: full

if data: type is set to full

cFOS will use this configuration to replace all current configuration. cFOS will be reloaded then load this function.

cat << EOF | kubectl -n cfostest apply -f - 

apiVersion: v1
data:
  config: |
  type: full
kind: ConfigMap
metadata:
  labels:
    app: fos
    category: config
  name: cm-full-empty
EOF

Expected Result

kubectl logs -f -l app=cfos -n cfostest
2024-05-14_12:22:58.24465 INFO: 2024/05/14 12:22:58 received a new fos configmap
2024-05-14_12:22:58.24466 INFO: 2024/05/14 12:22:58 configmap name: cm-full-empty, labels: map[app:fos category:config]
2024-05-14_12:22:58.24466 INFO: 2024/05/14 12:22:58 got a fos config
2024-05-14_12:22:58.24493 INFO: 2024/05/14 12:22:58 applying a full fos config...

then cFOS will be reloaded with this empty configuraiton, effectively, this is reset cFOS back to the factory default.

Access External Data with Secrets

Kubernetes Secrets are objects that store sensitive data such as passwords, OAuth tokens, SSH keys, etc. The primary purpose of using secrets is to protect sensitive configuration from being exposed in your application code or script. Secrets provide a mechanism to supply containerized applications with confidential data while keeping the deployment manifests or source code non-confidential.

Benefits of Using Secrets

  • Security: Secrets keep sensitive data out of your application code and Pod definitions. Management: Simplifies sensitive data management as updates to secrets do not require image rebuilds or application redeployments.
  • Flexibility: Can be mounted as data volumes or exposed as environment variables to be used by a container in a Pod. Also, they can be used by the Kubernetes system itself for things like accessing a private image registry.

How to Create Secrets

  • use KubeCTL or YAML file
    kubectl create secret generic ipsec-shared-key --from-literal=ipsec-shared-pass=12345678 -n cfostest

    use kubectl get secret ipsec-shared-key -o yaml -n cfostest can check the secret just created.

    the password “12345678” encoded with base64 and saved in k8s. you can still see the original password with

    kubectl get secret ipsec-shared-key -o json -n cfostest | jq -r '.data["ipsec-shared-pass"]' | base64 -d
    cat << EOF | kubectl apply -n cfostest -f - 
    apiVersion: v1
    kind: Secret
    metadata:
      name: ipsec-shared-key
    data:
      ipsec-shared-pass: $(echo 12345678 | base64)
    type: Opaque
    EOF

    The type field helps Kubernetes software and developers know how to treat the contents of the secret. The type Opaque is one of several predefined types that Kubernetes supports for secrets.

    Info

    Opaque: This is the default type for a secret. It indicates that the secret contains arbitrary data that isn’t structured in any predefined way specific to Kubernetes. This type is used when you are storing secret data that doesn’t fit into any of the other types of secrets that Kubernetes understands (like docker-registry or tls). the other options for type are : “kubernetes.io/service-account-token:”, “kubernetes.io/dockerconfigjson”,“kubernetes.io/tls” etc., when we create secret for store docker login secret, we have to use type: kubernetes.io/dockerconfigjson.

Consuming Secrets in a Pod

  • Environment Variables

Secret can be passed into POD as environment variables.

  • Mount Secret as Volume

  • ImagePullSecrets

Secret can be used in field “ImagePullSecrets” in serviceaccount or POD manifest for example. you can define a secriceaccount to include an ImagePullSecrets. or you can use secret in Pod or “Deployment” manifest for pod to pull image with secret.

  • As port of ConfigMap

Secret can be part of the ConfigMap for configuration purpose. for example, we can embeded secret in configMap for cFOS.

  • Use external secret management system

for example, HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault . These systems can dynamically inject secrets into your applications, often using a sidecar container or a mutating webhook to provide secrets to the application securely.

Task 1 - use secret in configMap

  • create secret with key to include the shared password
kubectl create secret generic ipsec-psks --from-literal=psk1="12345678"
  • create a clustersvc for cfos ipsec

create a clusterIP svc for cfos to get an ip for ipsec

kubectl apply -f $scriptDir/k8s-201-workshop/scripts/cfos/02_clusterip_cfos.yaml -n cfostest
  • use secret in configmap data
cat << EOF | kubectl apply -n cfostest -f - 
apiVersion: v1
data:
  type: partial
  config: |-
    config vpn ipsec phase1-interface
        edit "test-p1"
           set interface "eth0"
           set remote-gw 10.96.17.42
           set peertype any
           set proposal aes128-sha256 aes256-sha256 aes128gcm-prfsha256 aes256gcm-prfsha384 chacha20poly1305-prfsha256
           set psksecret {{ipsec-psks:psk1}}
           set auto-negotiate disable
         next
     end
    config vpn ipsec phase2-interface
        edit "test-p2"
            set phase1name "test-p1"
            set proposal aes128-sha1 aes256-sha1 aes128-sha256 aes256-sha256 aes128gcm aes256gcm chacha20poly1305
            set dhgrp 14 15 5
            set src-subnet 10.4.96.0 255.255.240.0
            set dst-subnet 10.0.4.0 255.255.255.0
        next
    end
kind: ConfigMap
metadata:
  labels:
    app: fos
    category: config
  name: cm-ipsecvpn
EOF

in above configmap. inside the configuration. the line set psksecret {{ipsec-psks:psk1}} is reference to a secret. the secret name is “ipsec-psks”, the key is psk1. the actual psksecret “12345678” is saved inside the key “psk1” of secret “ipsec-psks”.

k8s configmap does not support use secret in config data. it is up to cFOS application to parse secret. In above, it is cFOS responsibility to substitute {{ipsec-psks:psk1}} with actual k8s secret ipsec-psks.

use

podname=$(kubectl get pod -n cfostest -l app=cfos -o jsonpath='{.items[*].metadata.name}')
kubectl exec -it po/$podname -n cfostest -- /bin/cli

then use show vpn ipsec phase1-interface and show vpn ipsec phase2-interface from cFOS cli to check cFOS configuration.

Summary

cFOS has build-in support for read data from k8s configMaps and Secrets , which enable multiple cFOS container in one cluster to share the configuration data.

clean up

kubectl delete -f cfos7210250-deployment.yaml -n cfostest
kubectl delete svc ipsec -n cfostest
kubectl delete clusterrole configmap-reader
kubectl delete clusterrole secrets-reader
kubectl delete cm cm-full-empty -n cfostest 
kubectl delete cm cm-full-empty -n cfostest
kubectl delete cm foscfgvip -n cfostest 
kubectl delete cm foscfgvip-del -n cfostest 
kubectl delete cm cm-ipsecvpn -n cfostest