Kubernetes: ConfigMaps and Secrets on a Gorush server example

By | 02/14/2020
 

We have a Gorush server from the Kubernetes: running a push-server with Gorush behind an AWS LoadBalancer post, and I’d like to ad an ability to configure it via a Github repository and run it with a different set of settings – for Staging and Production environments.

Let’s use Kubernetes ConfigMap to store a config-file content, and Kubernetes Secrets – for confidential data.

At this moment in the gorush-configmap.yaml we have  default configMap for the Gorush with Redis connection settings:

apiVersion: v1
kind: ConfigMap
metadata:
  name: gorush-config
  namespace: gorush
data:
  # stat
  stat.engine: redis
  stat.redis.host: redis:6379

Also, need to add:

  • an SSL certificate used for Apple Push service authentification
  • a password for this certificate

We will add two secrets – one for the password, and one for the key-file.

Kubernetes Secrets

Create a new file gorush-secrets.yaml and add a p12-key body.

As this is p12 – use the data type for the secret, get your certificate in the base64:

cat apns_wl.p12 | base64
MIIM9QIBAzCCDLwGCSqGSIb3DQEHAaCCDK0EggypMIIMpTCCBycGCSqGSIb3DQEHBqCCBxgwggcU
AgEAMIIHDQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQI+BcNML0eE1oCAggAgIIG4KVvRBPp
qRiubF1QeqOE1n6x1+k92pjpxvpJph+ZhS+1ztMSFOOIlwAPNMBYDd1Opg8x3lXCxN88OzuyFSHu
nLSzqyx1b8sPEEhUejeKlzqDQczOhJTuvO9lDNZQS/4Cbn1LRlr6DbrqSexE1GRgpal5Hcc5i9rr
...

Add a data map named ios-push-rsa-key:

---
apiVersion: v1
kind: Secret
metadata:
  name: ios-push-secret
  namespace: gorush
type: Opaque
data:
  ios-push-rsa-key: |
    MIIM9QIBAzCCDLwGCSqGSIb3DQEHAaCCDK0EggypMIIMpTCCBycGCSqGSIb3DQEHBqCCBxgwggcU
    AgEAMIIHDQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQI+BcNML0eE1oCAggAgIIG4KVvRBPp
    ...

Or. if it is a PEM-encoded key – you can use stringData without base64, just a plaintext:

---
apiVersion: v1
kind: Secret
metadata:
  name: ios-push-secret
  namespace: gorush
type: Opaque
stringData:
  ios-push-rsa-key: |
    -----BEGIN CERTIFICATE-----
    MIIGJDCCBQygAwIBAgIIAtw7YRUahfYwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
    BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
    ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
...

Create the secret:

kubectl apply -f gorush-secrets.yaml
secret/ios-push-secret created

Check it:

kubectl -n gorush describe secret ios-push-secret
Name:         ios-push-secret
Namespace:    gorush
Labels:       <none>
Annotations:
Type:         Opaque
Data
====
ios-push-rsa-key:  4586 bytes

Now, we need to mount this certificate to pods and containers.

Kubernetes Secrets and volumeMounts – secrets, as files

Update the gorush-deployment.yaml file – add volumes from the sectret:

...
      volumes:
      - name: ios-push-secret-vol
        secret:
          secretName: ios-push-secret
          items:
          - key: ios-push-p12-key
            path: apns-crt.p12

And add it to the pod’s template:

...
    spec:
      containers:
      - image: appleboy/gorush
        name: gorush
        imagePullPolicy: Always
        ports:
        - containerPort: 8088
...
        volumeMounts:
        - name: ios-push-secret-vol
          mountPath: /data/ssl/apns-crt.p12
          subPath: apns-crt.p12
          readOnly: true

To make it clear – how they all are tied to each other:

  • kind: Secret: // just a Secret type
    • name: ios-push-secret // a secret name –  will use in the spec > volumes > secret > secretName
    • data:
      • ios-push-rsa-key // a cert body
  • volumes: // set in the spec, will add volumes
    • name: ios-push-secret-vol // a created volume name, will be used in the spec > containers >volumeMounts > name
    • secret: // defining a data to be used
      • secretName: ios-push-secret // lookup a secret with the name: ios-push-secret name
      • items: // andinsideofthesecret
        • - key: ios-push-rsa-key // look for the Secrets > data > ios-push-rsa-key
        • path: apns-rsa.pem // and how it wmust be mounted
  • volumeMounts: // used in the spec > containers
    • name: ios-push-secret-vol // a volume name to mount – spec > volumes > name: ios-push-secret-vol
    • mountPath: /data/ssl/apns-rsa.pem // a full mount path
    • subPath: apns-rsa.pem // a file name in a directory if a Secret is mounted as a dedicated file

Deploy and check:

kubectl -n gorush exec -ti gorush-7ff8fd9f4c-gds8r -c bastion cat /data/ssl/apns-rsa.pem
Bag Attributes
friendlyName: Apple Push Services: ***
localKeyID: C0 3A 96 69 CB C4 9E E6 14 EB 43 1F 31 30 97 4D 85 89 A0 8D
subject=/UID=***/CN=Apple Push Services: ***/OU=7MF8BB6LXN/O=***/C=VG
issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authorit
-----BEGIN CERTIFICATE-----
MIIGJDCCBQygAwIBAgIIAtw7YRUahfYwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
...

Kubernetes Secrets and env – secrets, as environment variables

The following step is to add a password for this certificate.

There are two options – using a config file, or via variables.

In the config file, Gorush has an ios.password parameter:

...
    ios:
      enabled: true
      key_path: "/data/ssl/apns-crt.p12"
      password: "p@ssw0rd"
      production: true
...

And if using a variable – Gorush will add a GORUSH prefix:

...
  viper.SetEnvPrefix("gorush") // will be uppercased automatically
...

So, our variable name will be GORUSH_IOS_PASSWORD.

Add new values to the ios-push-secret.

Add an additional map with the stringData type:

---
apiVersion: v1
kind: Secret
metadata:
  name: ios-push-secret
  namespace: gorush-stage
type: Opaque
data:
  ios-push-p12-key: |
    MIIM9QIBAzCCDLwGCSqGSIb3DQEHAaCCDK0EggypMIIMpTCCBycGCSqGSIb3DQEHBqCCBxgwggcU
    AgEAMIIHDQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQI+BcNML0eE1oCAggAgIIG4KVvRBPp
...
stringData:
  ios-push-p12-pass: p@ssw0rd

Check it:

kubectl apply -f gorush-secrets.yaml
secret/ios-push-secret configured

And check now – there must be two objects in the secret – view them using describe secret:

kubectl -n gorush describe secret ios-push-secret
Name:         ios-push-secret
Namespace:    gorush
Labels:       <none>
Annotations:
Type:         Opaque
Data
====
ios-push-p12-key:   3321 bytes
ios-push-p12-pass:  13 bytes

Add the GORUSH_IOS_PASSWORD variable to the deployment using  secretKeyRef:

...
    spec:
      containers:
      - image: appleboy/gorush
        name: gorush
        imagePullPolicy: Always
        ports:
        - containerPort: 8088
...
        env:
        - name: GORUSH_IOS_PASSWORD
          valueFrom:
            secretKeyRef:
              name: ios-push-secret
              key: ios-push-p12-pass
        volumeMounts:
        - name: ios-push-secret-vol
          mountPath: /data/ssl/apns-crt.p12
          subPath: apns-crt.p12
          readOnly: true
...

Deploy and check:

kubectl -n gorush exec -ti gorush-58bcd746c4-67b9n -c gorush env | grep GORUSH_IOS
GORUSH_IOS_PASSWORD=p@ssw0rd

It’s here.

ConfigMap

Kubernetes ConfigMap – configs, as files

The last thing to do is to add a dedicated config-file for the Gorush itself.

The file will be the same for the Production and Staging, but keys will be passed from a varicose Secrets.

In the same way, using variables and a ConfigMap we can pass any other parameters for different environments.

So, let use a ConfigMap to keep the config.yml file’s content to map it later to pods.

Gorush already has a gorush-configmap.yaml file – add a new config called gorush-config-file:

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: gorush-config
  namespace: gorush
data:
  # stat
  stat.engine: redis
  stat.redis.host: redis:6379
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: gorush-config-file
  namespace: gorush
data:
  gorush-config-file: |
    core:
      enabled: true
      mode: "release"
      port: "8088"
      max_notification: 1000
      sync: false
...

Add it to the volumes in our deployment:

...
      volumes:
      - name: ios-push-secret-vol
        secret:
          secretName: ios-push-secret
          items:
          - key: ios-push-p12-key
            path: apns-crt.p12
      - name: gorush-config-file-vol
        configMap:
          name: gorush-config-file
          items:
            - key: gorush-config-file
              path: config.yml

And the on the volumeMounts in the pods template:

...
    spec:
      containers:
      - image: appleboy/gorush
        name: gorush
        imagePullPolicy: Always
        ports:
        - containerPort: 8088
        ...
        env:
        - name: GORUSH_IOS_PASSWORD
          valueFrom:
            secretKeyRef:
              name: ios-push-secret
              key: ios-push-p12-pass
        volumeMounts:
        - name: ios-push-secret-vol
          mountPath: /data/ssl/apns-crt.p12
          subPath: apns-crt.p12
          readOnly: true
        - name: gorush-config-file-vol
          mountPath: /config.yml
          readOnly: true
          subPath: config.yml
...

Update configmaps:

kubectl apply -f gorush-configmap.yaml
configmap/gorush-config unchanged
configmap/gorush-config-file created

Check them:

kubectl -n gorush get configmap
NAME                 DATA   AGE
gorush-config        2      28h
gorush-config-file   1      76s

Apply the deployment and check config in a pod:

kubectl -n gorush-stage exec -ti gorush-6bc78df55d-9w9m8 -c gorush cat /config.yml
core:
enabled: true
mode: "release"
port: "8088"
max_notification: 1000
sync: false
...
ios:
enabled: true
key_path: "/data/ssl/apns-crt.p12"
password: ""
production: true

To auto-apply new values from ConfigMaps or Secrets – you can use the Reloader controller, see the Kubernetes: ConfigMap and Secrets – data auto-reload in pods post.

Done.



Also published on Medium.