Categories
Software development

Content (Delivery Network) is King

It’s all about the user experience. As a user, when I browse to either of the following domains, I should see the same content:

  1. www.mydomain.com (www, in this case, is a sub-domain)
  2. mydomain.com (when you refer to a domain w/o a sub-domain, it is generally called the apex or root)

For example, end-users may omit the “www”! Skipping characters saves precious characters and time…

In addition to expecting the same content, it (arguably must) be served using HTTPS. For example, if a request comes in using HTTP, transform it somehow to HTTPS.

This weekend I purchased a domain from GoDaddy for a side project. Initially I setup the domain using GoDaddy DNS to serve a SPA from a Standard Akamai CDN using Microsoft Azure. References:

  1. Setup a static website (front-end assets get pushed here)
  2. Link the site to a CDN (moving the assets closer to users)
  3. Use my custom domains for the CDN (including HTTPS)

Here is an example of the GoDaddy DNS configuration used to serve content from an Azure CDN:

TypeKeyValue
CNAMEwwwdomain.azureedge.net
CNAMEcdnverifycdnverify.domain.azuredge.net
GoDaddy doesn’t let you create a CNAME with a key of @…

But I ran into a couple problems!

  1. Azure CDN does not assign SSL certs to apex/root domains. I could have manually assigned a certificate, but I’m trying to avoid having to do manual things these days.
  2. Requests to http://mydomain.com would not serve content! The name would not resolve. GoDaddy (my DNS provider at the time), does not allow us to create a CNAME pointing at an apex/root domain.

I could have purchased a public static IPv4 address from Azure, linked it to my CDN, and setup an ANAME in GoDaddy to point at the public IP. But, considering both problems (above), I decided to take the following action:

  1. Delegate my domain’s DNS to point at an Azure DNS Zone
  2. Setup my local network (OpenWRT) to use Cloudflare for DNS resolution (my ISP was taking too long to resolve when testing these changes).
    1. This is a nice alternative to Google’s 8.8.8.8/8.8.4.4…why give one company all your data?!
  3. Delete the Standard Akamai CDN, and setup a Standard Microsoft CDN, which has a comprehensive rules engine, and other goodies.
  4. Use the Rules Engine to enforce a consistent experience…
    1. Redirect HTTP requests for my apex/root via a 301 response to https://www.mydomain.com
    2. Redirect HTTPS requests for my apex/root via a 301 response to https://www.mydomain.com
    3. Redirect all other HTTP requests via a 301 to HTTPS

Now…all your base are belong to us! I mean, requests for http://mydomain.com (insecure root), https://mydomain.com (secure root), and http://www.mydomain.com (insecure www sub-domain) will redirect to https://www.mydomain.com.

So, I’m using the cloud for DNS, everything has a cost.For example, two million queries with one zone will cost $1.30. Sounds like a good problem to have!

Categories
Software development

Using NATS and STAN in Kubernetes

In this article we’re going to:

  1. Install NATS and STAN using Helm into Kubernetes
  2. Follow along in a NATS minimal setup tutorial
    1. Create an administrative pod for NATS and STAN
    2. Confirm NATS sub/pub is working
    3. Confirm STAN sub/pub is working

This installs NATS so that clients can use it without authenticating!

At the time of this writing I’m not aware of a good way to install STAN supporting authentication. For example, like referencing a Config Map to get an account and Secret to get a password. Therefore, so I opted out of requiring client authentication for NATS so that I can do minimal testing.

I will follow-up in Github and in NATS documentation and circle back here to add the missing authentication settings.

# install NATS
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install nats bitnami/nats --set replicaCount=3 \
--set auth.enabled=false \
--set clusterAuth.enabled=true,clusterAuth.user=admin,clusterAuth.password=$password

# install STAN (notice how no creds are NOT needed!)
helm repo add nats https://nats-io.github.io/k8s/helm/charts/
helm install stan nats/stan \
--set store.type=file --set store.file.storageSize=1Gi \
--set stan.nats.url=nats://nats-client.default.svc.cluster.local:4222 \
--set store.cluster.enabled=true \
--set store.cluster.logPath=/data/stan/store/log

Now let’s follow along in the tutorial.

kubectl run -i --rm --tty nats-box --image=synadia/nats-box --restart=Never
nats-sub -s nats-client.default.svc.cluster.local -t test &
nats-pub -s nats-client.default.svc.cluster.local -t test "this is a test"

Which should yield:

2020/05/03 23:20:11 [#1] Received on [test]: 'this is a test'

stan-sub -c stan -s nats-client.default.svc.cluster.local test &
stan-pub -c stan -s nats-client.default.svc.cluster.local test "this is a test"

Which should yield:

[#1] Received: sequence:1 subject:"test" data:"this is a test" timestamp:1588550818187067125

Categories
Software development

HMAC auth with Kong in Kubernetes

This may help you setup hmac-auth with Kong in Kubernetes.

There is no guide for setting up HMAC, but, the JWT guide is great. It helped me understand Kong Plugins have specific expectations for Kubernetes Secrets.

In a nutshell, Kong Plugins expect Kubernetes Secrets to (1) reference the plugin type in the kongCredType field and (2) contain literals that match the Kong Credential type supported by your intended Kong Plugin.

kubectl create secret \ 
generic a-hmac-secret -n test-namespace \ 
--from-literal=kongCredType=hmac-auth \ 
--from-literal=username=hook-user \ 
--from-literal=secret=$hmackey

To setup the HMAC plugin, your Kubernetes secret must define the kongCredType, username, and secret. The kongCredType for HMAC is hmac-auth, username is a string and gets sent by your client in the Authorization header, and secret is a string that must be known by both the client and server to do encryption and decryption. Look at the properties on the credential object here for supporting detail.

The below YAML should help you get HMAC authorization working for your Ingress using Kong Plugins. Here are some tips:

  1. This assumes you have Kong installed in your Kubernetes cluster, already, and the IP address for the Kong Proxy ingress associated with proxy.mydomain.com.
  2. We intend to protect backend-service with HMAC auth, which exists in the test-namespace.
  3. Kubernetes objects we create to support HMAC auth for this service will be created in the same namespace, test-namespace.
  4. Environment variables (things beginning with $) are known and set prior to running scripts where applicable.
  5. Watch logs from your Kong deployment to observe whether Ingress/Plugin setup has errors kubectl logs deploy/kong-kong --follow --all-containers
  6. My ingress (below) has additional configuration to support http to https redirects, and provision SSL automatically (related links are included in comments).
---
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
  name: webhook-hmac
  namespace: test-namespace
plugin: hmac-auth
config:
  hide_credentials: true
  enforce_headers: 
  - date 
  - host
  - request-line
  algorithms: 
  - hmac-sha256
---
---
apiVersion: configuration.konghq.com/v1
kind: KongConsumer
metadata:
  name: hook-user
  namespace: test-namespace
username: hook-user
credentials:
- a-hmac-secret
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: webhook-frontend
  namespace: test-namespace
  annotations:
    konghq.com/plugins: webhook-hmac
    # https://github.com/Kong/kubernetes-ingress-controller/blob/master/docs/guides/cert-manager.md
    kubernetes.io/tls-acme: "true"
    cert-manager.io/cluster-issuer: letsencrypt-prod
    # https://github.com/Kong/kubernetes-ingress-controller/blob/master/docs/guides/configuring-https-redirect.md
    konghq.com/override: https-only
spec:
  # https://github.com/Kong/kubernetes-ingress-controller/blob/master/docs/guides/cert-manager.md
  tls:
  - secretName: proxy-mydomain-com
    hosts:
    - proxy.mydomain.com
  rules:
  - host: proxy.mydomain.com
    http:
      paths:
      - path: /my/protected/path
        backend:
          serviceName: backend-service
          servicePort: 8080 
---

Now, requests sent to https://proxy.mydomain.com/my/protected/path will require an Authorization header supporting HMAC as configured above, and if valid, be sent to the backend-service.