Skip to content

Instantly share code, notes, and snippets.

@moxli
Last active December 21, 2022 19:33
Show Gist options
  • Save moxli/02397491b90fee26edd8f5036948ea2a to your computer and use it in GitHub Desktop.
Save moxli/02397491b90fee26edd8f5036948ea2a to your computer and use it in GitHub Desktop.
OpenVAS / Greenbone Security Assistant on Google Kubernetes Engine with Google Identity-Aware Proxy

OpenVAS / Greenbone Security Assistant on Google Kubernetes Engine with Google Identity-Aware Proxy

Intro

This is a way to expose a OpenVAS / GSAD running in a GKE cluster (Google Kubernetes Engine) to the public internet securely with Google Identity-Aware Proxy in front of it.

This will display the normal GSAD login screen after the Google authentication since the GSAD app code can not handle the Google user identity per default.

With this configuration the default data directory is stored on a persistent disk to make all OpenVAS scan/task/target/etc data persistent.

Required

Variables

I have referenced some variables in my configuration files below. These variables need to be replaced either manually or by your preferred templating solution.

Variable Description
$NAMESPACE The namespace you want to deploy your OpenVAS to.
$OPENVAS_IMAGE The image path incl. version of your OpenVAS image.
$NGINX_IMAGE The image path incl. version of your Nginx image.
$DOMAIN The FQDN under which your OpenVAS will be reachable.
$IP The actual IP address you reserved for OpenVAS.
$IP_NAME The name of the IP address your reserved for OpenVAS.
$SECRET The name of the secret where your OAuth credentials are stored.

Feel free to adjust other values (e.g. the resource requests/limits) to your needs, these are just examples.

Identity-Aware Proxy

To prepare the IAP please follow this guide: https://cloud.google.com/iap/docs/enabling-kubernetes-howto You will need to store the OAuth ID and secret in a Kubernetes secret($SECRET in the variables section).

GSAD

To make sure GSAD works as intended you need to make sure the "--allow-header-host" parameter is set to your FQDN.

--allow-header-host=$DOMAIN

As an example, I am using the following command: gsad -f --listen=0.0.0.0 --port=9392 --mlisten=127.0.0.1 --mport=9390 --timeout=60 --http-only --allow-header-host=$DOMAIN

I did increase the session timeout to 60 minutes and disabled https. I am fine with the Google managed certificate and want to avoid warning due to the self signed certificate. With https disabled GSAD will display a warning message on the login screen. This should be fine on GKE since afaik the underlying K8S network is encrypted by Google. This depends on your situation and risk profile of course.

I did not test this with https enabled!

Nginx

The reason why we need a seperate reverse proxy in front of OpenVAS is described in this issue: mikesplain/openvas-docker#188 (comment)

Essentially we need to provide the X-Real-IP header containing the actual IP of your client to GSAD. The real ip module for Nginx is used. With this module we are able to extract the client IP from the X-Forwarded-For header, which is then automatically stored in $remote_addr. We also need to add the source IP ranges of the Google HTTP(S) load balancer with set_real_ip_from. These IP ranges can be found here: https://cloud.google.com/load-balancing/docs/https#source_ip_addresses

The X-Forwarded-For header is sent from the Google Ingress with the client IP and the external IP of the Ingress.

---
apiVersion: cloud.google.com/v1beta1
kind: BackendConfig
metadata:
name: openvas-backend-config
namespace: $NAMESPACE
spec:
iap:
enabled: true
oauthclientCredentials:
secretName: $SECRET
---
apiVersion: v1
kind: ConfigMap
metadata:
name: openvas-nginx-vhost-configs
namespace: $NAMESPACE
data:
default.conf: |
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name $DOMAIN;
set_real_ip_from 35.191.0.0/16;
set_real_ip_from 130.211.0.0/22;
set_real_ip_from $IP;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
location / {
proxy_set_header X-Real-IP "$remote_addr";
proxy_pass http://127.0.0.1:9392;
}
}
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: openvas
namespace: $NAMESPACE
annotations:
# we can't use this, since this breaks when using managed certificates. The IAP setup will cause and automatic
# redirect to HTTPS, so it's fine to not have this setting
# kubernetes.io/ingress.allow-http: "false"
kubernetes.io/ingress.global-static-ip-name: $IP_NAME
networking.gke.io/managed-certificates: openvas
spec:
rules:
- http:
paths:
- path: /*
backend:
serviceName: openvas
servicePort: 443
---
apiVersion: networking.gke.io/v1beta1
kind: ManagedCertificate
metadata:
name: openvas
namespace: $NAMESPACE
spec:
domains:
- $DOMAIN
---
apiVersion: v1
kind: Service
metadata:
name: openvas
namespace: $NAMESPACE
annotations:
cloud.google.com/neg: '{"ingress": true}'
beta.cloud.google.com/backend-config: '{"ports": {"443":"openvas-backend-config"}}'
spec:
type: NodePort
selector:
app: openvas
ports:
- name: https
protocol: TCP
port: 443
targetPort: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: openvas
namespace: $NAMESPACE
spec:
serviceName: openvas
replicas: 1
selector:
matchLabels:
app: openvas
env: $NAMESPACE
template:
metadata:
labels:
app: openvas
env: $NAMESPACE
spec:
volumes:
- name: openvas-nginx-vhost-configs
configMap:
name: openvas-nginx-vhost-configs
containers:
- name: openvas
image: $OPENVAS_IMAGE
volumeMounts:
- mountPath: /var/lib/openvas/mgr/
name: data
ports:
- containerPort: 9392
resources:
requests:
cpu: 100m
memory: 1024Mi
limits:
cpu: 2
memory: 2048Mi
readinessProbe:
httpGet:
path: /login/login.html
port: 9392
scheme: HTTP
initialDelaySeconds: 240
periodSeconds: 20
timeoutSeconds: 5
- name: nginx
image: $NGINX_IMAGE
volumeMounts:
- name: openvas-nginx-vhost-configs
mountPath: /etc/nginx/conf.d
ports:
- containerPort: 80
resources:
requests:
cpu: 10m
memory: 100Mi
limits:
cpu: 100m
memory: 200Mi
volumeClaimTemplates:
- metadata:
name: data
namespace: $NAMESPACE
spec:
accessModes: [ "ReadWriteOnce" ]
# The default StorageClass is used when a PersistentVolumeClaim doesn't specify a StorageClassName
#storageClassName: "my-storage-class"
resources:
requests:
storage: 10Gi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment