avatarYitaek Hwang

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

5790

Abstract

e9"><pre>helm<span class="hljs-built_in"> upgrade </span>grafana stable/grafana --<span class="hljs-built_in">set</span> service.<span class="hljs-attribute">type</span>=NodePort</pre></div><p id="a9d6">Now let’s create the ingress. You can define it within the Grafana Helm chart, but in case we want to apply the IAP to Prometheus or other services with the same ingress, I’m going to create it as a separate file (change bolded values as needed):</p><div id="8c69"><pre><span class="hljs-attribute">apiVersion</span><span class="hljs-punctuation">:</span> <span class="hljs-string">networking.k8s.io/v1beta1</span> <span class="hljs-attribute">kind</span><span class="hljs-punctuation">:</span> <span class="hljs-string">Ingress</span> <span class="hljs-attribute">metadata</span><span class="hljs-punctuation">:</span> <span class="hljs-attribute">name</span><span class="hljs-punctuation">:</span> <span class="hljs-string">ingress-monitoring</span> <span class="hljs-attribute">annotations</span><span class="hljs-punctuation">:</span> <span class="hljs-attribute">kubernetes.io/ingress.global-static-ip-name</span><span class="hljs-punctuation">:</span> <span class="hljs-string">grafana-ip</span> <span class="hljs-attribute">networking.gke.io/managed-certificates</span><span class="hljs-punctuation">:</span> <span class="hljs-string">grafana-cert</span> <span class="hljs-attribute">spec</span><span class="hljs-punctuation">:</span> <span class="hljs-attribute">rules</span><span class="hljs-punctuation">:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">host: grafana.example.com</span> <span class="hljs-attribute">http</span><span class="hljs-punctuation">:</span> <span class="hljs-attribute">paths</span><span class="hljs-punctuation">:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">backend:</span> <span class="hljs-attribute">serviceName</span><span class="hljs-punctuation">:</span> <span class="hljs-string">grafana</span> <span class="hljs-attribute">servicePort</span><span class="hljs-punctuation">:</span> <span class="hljs-string">80</span></pre></div><p id="113c">Save it as <code>ingress-monitoring.yaml</code> and deploy:</p><div id="aef9"><pre>kubectl <span class="hljs-built_in">apply</span> -f ingress-monitoring.yaml</pre></div><p id="e26b">(If you are providing your own SSL certificate, use the following instead)</p><div id="649e"><pre><span class="hljs-attribute">apiVersion</span><span class="hljs-punctuation">:</span> <span class="hljs-string">networking.k8s.io/v1beta1</span> <span class="hljs-attribute">kind</span><span class="hljs-punctuation">:</span> <span class="hljs-string">Ingress</span> <span class="hljs-attribute">metadata</span><span class="hljs-punctuation">:</span> <span class="hljs-attribute">name</span><span class="hljs-punctuation">:</span> <span class="hljs-string">ingress-monitoring</span> <span class="hljs-attribute">annotations</span><span class="hljs-punctuation">:</span> <span class="hljs-attribute">kubernetes.io/ingress.global-static-ip-name</span><span class="hljs-punctuation">:</span> <span class="hljs-string">grafana-ip</span> <span class="hljs-attribute">spec</span><span class="hljs-punctuation">:</span> <span class="hljs-attribute">tls</span><span class="hljs-punctuation">:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">secretName: my-cert-secret</span> <span class="hljs-attribute">rules</span><span class="hljs-punctuation">:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">host: grafana.example.com</span> <span class="hljs-attribute">http</span><span class="hljs-punctuation">:</span> <span class="hljs-attribute">paths</span><span class="hljs-punctuation">:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">backend:</span> <span class="hljs-attribute">serviceName</span><span class="hljs-punctuation">:</span> <span class="hljs-string">grafana</span> <span class="hljs-attribute">servicePort</span><span class="hljs-punctuation">:</span> <span class="hljs-string">80</span></pre></div><p id="c174">Once the ingress is healthy, <a href="https://grafana.example.com">https://grafana.example.com</a> should redirect to your Grafana login page.</p><h2 id="e638">Enabling IAP</h2><p id="8018">First, we need to configure the project’s <a href="https://console.cloud.google.com/apis/credentials/consent?_ga=2.70534672.1206986393.1591391754-1171805911.1591391754">OAuth consent screen</a>:</p><figure id="f8a3"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*hAtiqwCNZlNIWvkWB6NMvQ.png"><figcaption></figcaption></figure><p id="2098">Since Grafana is an internal monitoring tool, choose <b>Internal,</b> and click <b>Create. </b>Fill out the <b>application name</b>, <b>support email, </b>and other optional fields:</p><figure id="91a7"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*m7iIS93Of8xGvNXY3w5wRQ.png"><figcaption></figcaption></figure><p id="980a">Now, go to the <a href="https://console.cloud.google.com/apis/credentials?_ga=2.39854242.1206986393.1591391754-1171805911.1591391754"><b>Credentials</b></a><b> </b>page<b> </b>to create the OAuth client credentials:</p><figure id="19f4"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*AXuZab5t39Mt2QBO9D2NlA.png"><figcaption></figcaption></figure><p id="5a11"><b>Application type </b>is <b>Web application, </b>give it a name and click <b>Create.</b> Make note of the client ID and secret that is generated and copy the ID to your clipboard.</p><p id="9f93">Go back and select the OAuth application and add the following URL to the <b>authorized redirect URIs </b>section:</p><div id="9433

Options

"><pre>https:<span class="hljs-regexp">//i</span>ap.googleapis.com<span class="hljs-regexp">/v1/</span>oauth<span class="hljs-regexp">/clientIds/</span><MY_CLIENT_ID>:handleRedirect</pre></div><figure id="298e"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*CEMdOVQ9bKPyA9HrYaCnsQ.png"><figcaption></figcaption></figure><p id="bfde">After saving the changes, it’s time to use the OAuth credentials to configure the Kubernetes BackendConfig. Copy the Client ID and Secret Key to create a Kubernetes secret called <code>iap-credentials</code> :</p><div id="d97b"><pre>kubectl create<span class="hljs-built_in"> secret </span>generic iap-credentials
<span class="hljs-attribute">--from-literal</span>=client_id=<client_id_key>
<span class="hljs-attribute">--from-literal</span>=client_secret=<client_secret_key></pre></div><p id="e7a0">We can now use the secret to update the BackendConfig to enable our Ingresses to use IAP:</p><div id="0e67"><pre><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">cloud.google.com/v1</span> <span class="hljs-attr">kind:</span> <span class="hljs-string">BackendConfig</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">iap-config</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">iap:</span> <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span> <span class="hljs-attr">oauthclientCredentials:</span> <span class="hljs-attr">secretName:</span> <span class="hljs-string">iap-credentials</span></pre></div><p id="f26e">Deploy the BackendConfig changes: <code>kubectl apply -f <backendconfig.yaml></code></p><p id="8592">Finally, we need to update the annotations in Grafana services to associate our new BackendConfig to the NodePort (save as <code>grafana.yaml</code>):</p><div id="72cc"><pre><span class="hljs-title">service</span>:
<span class="hljs-class"><span class="hljs-keyword">type</span>: <span class="hljs-type">NodePort</span> </span> annotations: beta.cloud.google.com/backend-config: '{<span class="hljs-string">"default"</span>: <span class="hljs-string">"iap-config"</span>}'</pre></div><p id="c4db">Issue the Helm upgrade command:</p><div id="c862"><pre>helm upgrade grafana stable/grafana -f grafana.yaml</pre></div><p id="cc9c">(Note: if you run into issues, refer to <a href="https://cloud.google.com/iap/docs/enabling-kubernetes-howto">Google’s GKE IAP guide</a> for debugging tips.)</p><h2 id="74c3">Configuring IAP access</h2><p id="2224">Navigate back to the <a href="https://console.cloud.google.com/security/iap?_ga=2.104148736.1206986393.1591391754-1171805911.1591391754">IAP page</a> and you should see the Grafana backend service for use:</p><figure id="a491"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*B6gQYtrtLHOGkOxLg_tYgw.png"><figcaption></figcaption></figure><p id="526d">Click on the IAP toggle button to turn on IAP. At this point, if you attempt to access Grafana over the Internet, you will be asked to authenticate with your Google credentials and be subsequently denied access:</p><figure id="007e"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*nSJ6xCaZWzH5_jXOGczQpg.png"><figcaption></figcaption></figure><p id="6845">In order to provide access, you must configure IAP settings and add members to your Grafana app. Click on the checkbox on the Grafana backend service, and a panel should appear on the right. Click <b>add member </b>and give <b>IAP-secured Web App User</b> role to users to access Grafana.</p><p id="f1f2">Once the Status turns from Error to Warning (it warns about internal firewall rules that can bypass IAP controls), you should now be able to access Grafana on the Internet after logging in with your Google account.</p><h1 id="7276">Wrapping Up</h1><p id="7b82">While IAP provides an excellent alternative solution to using VPN or bastion hosts to lock down access to internal apps, it does present some challenges:</p><ul><li>It’s not possible to fully-automate this setup process (i.e. OAuth consent screen setup, generating client IDs and redirect URIs, etc)</li><li>Using IAP in hybrid cloud settings is a bit more complicated (although there’s an excellent video on how <a href="https://www.youtube.com/watch?v=Sq9gp8KBsY0">Airbnb does it here</a>)</li></ul><p id="8090">Overall, in this series, we looked at setting up a more production-ready monitoring stack using popular open-source tools. As the shift to cloud and cloud-native architectures continue to mature, scalable observability will be in more demand.</p><p id="ea5b">For more resources, check out:</p><ul><li><a href="https://www.robustperception.io/">Robust Perception</a></li><li><a href="https://landing.google.com/sre/sre-book/chapters/preface/">Google SRE Book</a></li><li><a href="https://landing.google.com/sre/workbook/toc/">Site Reliability Workbook</a></li><li><a href="https://static.googleusercontent.com/media/landing.google.com/en//sre/static/pdf/Building_Secure_and_Reliable_Systems.pdf">Building Secure & Reliable Systems</a></li></ul><p id="93d6">To read other posts in these series:</p><ul><li>Part I: <a href="https://readmedium.com/practical-monitoring-with-prometheus-grafana-part-i-22d0f172f993">Installing Prometheus + Grafana via Helm in 5 Minutes</a></li><li>Part II: <a href="https://readmedium.com/practical-monitoring-with-prometheus-grafana-part-ii-5020be20ebf6">Using Prometheus blackbox exporter for free uptime checks</a></li><li>Part III: <a href="https://towardsdatascience.com/practical-monitoring-with-prometheus-grafana-part-iii-81f019ecee19">Applying simple statistics for anomaly detection using Prometheus</a></li></ul></article></body>

Practical Monitoring with Prometheus & Grafana (Part IV)

Securing Grafana with Identity-Aware Proxy

Photo by Mitchell Luo on Unsplash

In Parts I, II, and III of the Practical Monitoring with Prometheus & Grafana series, we deployed Prometheus and Grafana on Kubernetes, installed blackbox probes, and configured anomaly detection to make a foundation for our monitoring stack. In this last post, we will complete the setup and look at securing Grafana using Google Cloud’s Identity-Aware Proxy (IAP).

Identity-Aware Proxy (IAP)

Grafana, by default, provides a username/password authentication mechanism to restrict access to the dashboards. Admin passwords are autogenerated at the time of install and stored as a Kubernetes secret. It also supports Lightweight Directory Access Protocol (LDAP) to integrate Active Directory and other directory services for authentication.

Default Grafana Login Page

So that deals with user authentication, but how do we deal with access to Grafana? It’s not feasible to expect SREs or engineers on call to use port-forwarding on every cluster to access Grafana on localhost. Projects like kube-forwarder make the mechanism of switching contexts and forwarding ports easy, but what if you don’t want to give everyone access to Kubernetes but still want them to see the Grafana dashboards? The other option is to expose Grafana via an Ingress and configure firewall rules to restrict access via VPN or bastion hosts, which can be tricky and potentially expensive to set up.

Google Cloud’s IAP is a simpler authentication and authorization mechanism to enable a zero-trust security model without VPNs. It builds on Google’s BeyondCorp model of shifting security from network perimeters to individuals users and uses context to determine access. In our Grafana use case, it adds a multi-factor authentication layer so that only authorized users can access our endpoint and subsequently login via Grafana username and password.

Context-aware access: High-level architecture

Configuring IAP on GKE

Without further ado, let’s configure IAP to secure access to Grafana. Some prerequisites before we begin:

  • Global, reserved static IP
  • SSL certificate
  • Verified ownership of your domain
  • GCP project compute.securityAdmin and compute.backendServices.update role (or Editor/Owner)

Assuming you have Cloud SDK installed (or using Cloud Shell):

gcloud compute addresses create grafana-ip --global

If you already have an SSL certificate, you can mount it as a Kubernetes secret and skip to the next step. If not, we can use Google’s managed certificate option:

apiVersion: networking.gke.io/v1beta2
kind: ManagedCertificate
metadata:
  name: grafana-cert
spec:
  domains:
    - grafana.example.com

Save the file as cert.yaml and create the resource (takes about 15–20 mins to become active):

kubectl apply -f cert.yaml

Finally, you need to verify the ownership of your domain. Use Google’s webmaster tool or register your domain with Google Domains.

Setting Up Ingress

IAP on GKE is enabled via GCE Ingress, which means that we need to expose our Grafana service as a NodePort for the HTTPS Load Balancer to pass its health checks. Modify Grafana Helm chart to use NodePort (service type is immutable so may need to delete and reinstall):

helm upgrade grafana stable/grafana --set service.type=NodePort

Now let’s create the ingress. You can define it within the Grafana Helm chart, but in case we want to apply the IAP to Prometheus or other services with the same ingress, I’m going to create it as a separate file (change bolded values as needed):

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress-monitoring
  annotations:
    kubernetes.io/ingress.global-static-ip-name: grafana-ip
    networking.gke.io/managed-certificates: grafana-cert
spec:
  rules:
  - host: grafana.example.com
    http:
      paths:
      - backend:
          serviceName: grafana
          servicePort: 80

Save it as ingress-monitoring.yaml and deploy:

kubectl apply -f ingress-monitoring.yaml

(If you are providing your own SSL certificate, use the following instead)

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress-monitoring
  annotations:
    kubernetes.io/ingress.global-static-ip-name: grafana-ip
spec:
  tls:
  - secretName: my-cert-secret
  rules:
  - host: grafana.example.com
    http:
      paths:
      - backend:
          serviceName: grafana
          servicePort: 80

Once the ingress is healthy, https://grafana.example.com should redirect to your Grafana login page.

Enabling IAP

First, we need to configure the project’s OAuth consent screen:

Since Grafana is an internal monitoring tool, choose Internal, and click Create. Fill out the application name, support email, and other optional fields:

Now, go to the Credentials page to create the OAuth client credentials:

Application type is Web application, give it a name and click Create. Make note of the client ID and secret that is generated and copy the ID to your clipboard.

Go back and select the OAuth application and add the following URL to the authorized redirect URIs section:

https://iap.googleapis.com/v1/oauth/clientIds/<MY_CLIENT_ID>:handleRedirect

After saving the changes, it’s time to use the OAuth credentials to configure the Kubernetes BackendConfig. Copy the Client ID and Secret Key to create a Kubernetes secret called iap-credentials :

kubectl create secret generic iap-credentials \
  --from-literal=client_id=<client_id_key> \
  --from-literal=client_secret=<client_secret_key>

We can now use the secret to update the BackendConfig to enable our Ingresses to use IAP:

apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
  name: iap-config
spec:
  iap:
    enabled: true
    oauthclientCredentials:
      secretName: iap-credentials

Deploy the BackendConfig changes: kubectl apply -f <backendconfig.yaml>

Finally, we need to update the annotations in Grafana services to associate our new BackendConfig to the NodePort (save as grafana.yaml):

service:  
  type: NodePort  
  annotations: 
    beta.cloud.google.com/backend-config: '{"default": "iap-config"}'

Issue the Helm upgrade command:

helm upgrade grafana stable/grafana -f grafana.yaml

(Note: if you run into issues, refer to Google’s GKE IAP guide for debugging tips.)

Configuring IAP access

Navigate back to the IAP page and you should see the Grafana backend service for use:

Click on the IAP toggle button to turn on IAP. At this point, if you attempt to access Grafana over the Internet, you will be asked to authenticate with your Google credentials and be subsequently denied access:

In order to provide access, you must configure IAP settings and add members to your Grafana app. Click on the checkbox on the Grafana backend service, and a panel should appear on the right. Click add member and give IAP-secured Web App User role to users to access Grafana.

Once the Status turns from Error to Warning (it warns about internal firewall rules that can bypass IAP controls), you should now be able to access Grafana on the Internet after logging in with your Google account.

Wrapping Up

While IAP provides an excellent alternative solution to using VPN or bastion hosts to lock down access to internal apps, it does present some challenges:

  • It’s not possible to fully-automate this setup process (i.e. OAuth consent screen setup, generating client IDs and redirect URIs, etc)
  • Using IAP in hybrid cloud settings is a bit more complicated (although there’s an excellent video on how Airbnb does it here)

Overall, in this series, we looked at setting up a more production-ready monitoring stack using popular open-source tools. As the shift to cloud and cloud-native architectures continue to mature, scalable observability will be in more demand.

For more resources, check out:

To read other posts in these series:

Grafana
Monitoring
Google Cloud Platform
Identity Aware Proxy
Recommended from ReadMedium