Post

Guacamole on kubetnetes

Requirements

  • K3s cluster or stand alone node
  • Kubectl installed on your machine and configured to connect to your node / cluster
  • A public domain within cloudflare
  • Traekfik installed in your cluster
  • Reloader installed in your cluster

Set folder structure

We want to split this out into two parts, Guacd and Guacamole. Example Tree:

└───Manifests
    ├───Guacamole
    └───guacd

Installing guacd into the cluster

Guacd is the service in the backend that will be connecting out to the other devices in the network. To begin with we will install Guacd into the cluster as it’s own set of pods. Start with creating a basic Namespace for the application

guacd-ns.yaml

1
2
3
4
5
6
apiVersion: v1
kind: Namespace
metadata:
  name: guacd
  labels:
    name: guacd

Now we want to create a deployment for the application, at the time of writing this version 1.4.0 was the latest version. I reccomend fixing to a certain version and not using the latest tag for deployments.

guacd-deployment.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
---
apiVersion: apps/v1
kind: Deployment
metadata:
   name: guacd
   namespace: guacd
spec:
  selector:
    matchLabels:
      app: guacd
  replicas: 1 # < Set to as many replicas as you desire
  template:
     metadata:
       labels:
         app: guacd
     spec:
        containers:
         - name: guacd
           image: guacamole/guacd:1.4.0
           ports:
           - containerPort: 4822

Create a service for the guacamole service to talk to later on.

guacd-Service.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
---
apiVersion: v1
kind: Service
metadata:
  name: guacd-svc
  namespace: guacd
  labels:
    app: guacd
spec:
  selector:
    app: guacd
  ports:
  - port: 4822
    protocol: TCP 

Apply the manifests and check that the deployment has spun up

1
kubectl get pods -o wide -n guacd

Now to deploy Guacamole front end

Create a new namespace for guacamole

Guacamole-namespace.yaml

1
2
3
4
kind: Namespace
apiVersion: v1
metadata:
  name: guacamole

Create a config map for the deployment to look at for configuration. We are using a config map keep the server details in, sadly it is in XML format. Set yourself a login and a password, encode it using an md5 tool.

Guacamole-configmap.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
apiVersion: v1
kind: ConfigMap
metadata:
  name: guacamole-conf
  namespace: guacamole
data:
  user-mapping.xml: |
    <user-mapping>
    <authorize 
    username="myaccount" 
    password="apasswordinmd5encoding" 
    encoding="md5">
    <connection name="aservername">
    <protocol>ssh</protocol>
    <param name="hostname">serveriphere</param>
    <param name="port">22</param>
    <param name="username">myadminaccont</param>
    </connection>
    <connection name="anotherserver">
    <protocol>rdp</protocol>
    <param name="hostname">anotherip</param>
    <param name="port">3389</param>
    <param name="username">Administrator</param>
    <param name="ignore-cert">true</param>
    </connection>
    </authorize>
    </user-mapping>

Create a deployment for the guacamole service

Guacamole-Deployment.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
--- 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: guacamole
  namespace: guacamole
spec:
  selector:
    matchLabels:
      app: guacamole
  replicas: 1
  strategy:
    type: RollingUpdate
  revisionHistoryLimit: 6 
  template:
    metadata:
      labels:
        app: guacamole
    spec:
      containers:
      - name: guacamole
        image: guacamole/guacamole:1.4.0 #< Change this to match the version of GuacD you are using
        env:
          - name: GUACD_HOSTNAME
            value: "guacd-svc.guacd" # < Change the guacd service dns name if you changed it in the guacd-service yaml file
          - name: GUACD_PORT
            value: "4822"
          - name: GUACAMOLE_HOME
            value: /etc/guacamole/
        resources:
          limits:
            memory: "512Mi" # < Adjust memory and cpu limits here if needed
            cpu: "800m"
        ports:
        - name: http
          containerPort: 8080 
        volumeMounts:
          - name: guacamole-conf
            mountPath: /etc/guacamole/
        livenessProbe:
          httpGet:
            path: /guacamole/
            port: 8080
          initialDelaySeconds: 45 # < Adjust this if your pods take a while to spin up.. 45 seconds should be plenty
          timeoutSeconds: 10
        readinessProbe:
          httpGet:
              path: /guacamole/
              port: 8080
          initialDelaySeconds: 45 # < Adjust this if your pods take a while to spin up.. 45 seconds should be plenty
          timeoutSeconds: 40
      volumes:
        - name: guacamole-conf
          configMap:
            name: guacamole-conf

Now we can ceate a service for the deploment

Guacamole-Service.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
---
apiVersion: v1
kind: Service
metadata:
  name: guacamole-svc
  namespace: guacamole
  labels:
    app: guacamole
spec:
  selector:
    app: guacamole
  ports:
  - port: 8080
    protocol: TCP

And now create and ingress for the service above. We are going to use a middleware also to auto add the /guacmole onto the url so you don’t run into an apache 404 error when targetting your URL.

Guacamole-ingress.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: guacamole
  namespace: guacamole
  annotations: 
    kubernetes.io/ingress.class: traefik-external
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`guacamole.domain.tld`)
      kind: Rule
      services:
        - name: guacamole-svc
          port: 8080
      middlewares:
        - name: prepend-path-guacamole
  tls:
    secretName: prod-guacamole-domain-tld
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: prepend-path-guacamole
spec:
  addPrefix:
    prefix: /guacamole
---
    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: guacamole.domain.tld
      namespace: guacamole
    spec:
      secretName: prod-guacamole-domain-tld
      issuerRef:
        name: letsencrypt-production
        kind: ClusterIssuer
      commonName: "guacamole.domain.tld"
      dnsNames:
      - "guacamole.domain.tld"

Configure a DNS name on your DNS server etc and you should be greeted with the guacamole login page.

This post is licensed under CC BY 4.0 by the author.