Skip to content

Using Traefik to provide a Vanity URL for Oracle APEX

Published: at 08:29 PM (5 min read)

How to use our free cluster to provide a memorable URL for a free APEX app, rather than their autogenerated word soup.

Table of contents

Open Table of contents

Background

Within our free tier we get access to a free APEX workspace. I created a workspace and used one of the demo apps to have something to test with. After 7 days of no use you get an email alert that it will be paused and deleted without any use. The URL you get given is a bit of generated word soup, that is not easy to remember to be able to access the app and reset the clock.

Reading the Oracle documentation, there is the ability to use a custom URL, but this needs another load balancer and you only get 1 free one and we are using it for our K8s cluster.

So, can we use our cluster to provide a vanity URL?

How to connect to something not in the K8s cluster, from inside the K8s cluster

ExternalNames, these are an object that we can use in the cluster to provide it a way to know where to route our traffic.

The URL for my APEX workspace is g2f2e11ad187d04-apex.adb.uk-london-1.oraclecloudapps.com, so I will provide it that, then I can reuse these same rules and patterns with any app in the workspace.

Below I am creating a resource in the external-services namespace pointing to that URL.

apiVersion: v1
kind: Service
metadata:
  name: apex
  namespace: external-services
spec:
  type: ExternalName
  externalName: g2f2e11ad187d04-apex.adb.uk-london-1.oraclecloudapps.com

But First, Update the Traefik Config

By default Traefik deployed by Helm is set to not allow routing to ExternalNames, both via the CRD and normal Ingress. In the helm values, look for allowExternalNameServices and update it to true.

Routing to the external name

This took a lot of faffing about with. Most of the examples online I could find were using a (possibly old) different CRD and when I tried to use it my Traefik did not pick it up. I also tried to use a normal Ingress but Traefik did not pick this up either. In the end I final got the following to work. I will try to explain it as best I can.

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: apex
  namespace: external-services
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`apex.smale.cloud`)
      kind: Rule
      services:
        - name: apex
          passHostHeader: false
          namespace: external-services
          port: 443

The top 4 liens are standard for this type of resource and sets it to our namespace.

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: apex
  namespace: external-services

In the spec, we are using the websecure entrypoint, which is HTTPS. We are setting the match rule to be a subdomain (and I have a wildcard match on my DNS for *.smale.cloud) to be routed to the cluster. The passHostHeader set to false stops our domain name being forwarded to the APEX URL, that caused it to error as it was not known. We are pointing it at our ExternalName and giving it a port. In the docs for IngressRoute - scroll down for ExternalName where it gives some more details on how to match them up.

spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`apex.smale.cloud`)
      kind: Rule
      services:
        - name: apex
          passHostHeader: false
          namespace: external-services
          port: 443

With both of these, we are able to go to https://apex.smale.cloud/ords/r/dev/apex-pwa-reference/home and it will be proxied to https://g2f2e11ad187d04-apex.adb.uk-london-1.oraclecloudapps.com/ords/r/dev/apex-pwa-reference/home without the user knowing. This does have one drawback, you need to still know the path to get to the app.

Path Redirection

To enable us to visit a short URL (something like apex.smale.cloud/pwa) and have it redirect to the full URL we can add another routes match and some middleware.

IngressRoute

Update the above IngressRoute to look like the following:

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: apex
  namespace: external-services
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`apex.smale.cloud`) &&  PathPrefix(`/pwa`)
      kind: Rule
      middlewares:
        - name: apex-pwa-redirect
      services:
        - name: apex
          passHostHeader: false
          namespace: external-services
          port: 443
    - match: Host(`apex.smale.cloud`)
      kind: Rule
      services:
        - name: apex
          passHostHeader: false
          namespace: external-services
          port: 443

I think the more specific matcher needs to go first, or it might be internally that Traefik uses the more specific one first, but what we are doing here is adding a PathPrefix to the route and defining some middleware that should handle the redirection.

Middleware

We can now define the middleware to redirect any URL that has a path of /pwa to the host with the /pwa replaced with the home route of the APEX app.

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: apex-pwa-redirect
  namespace: external-services
spec:
  redirectRegex:
    regex: ^(.*)/pwa$
    replacement: ${1}/ords/r/dev/apex-pwa-reference/home

Conclusion

So there you have it, you can now go to https://apex.smale.cloud/pwa, and get taken to the example PWA app that I have deployed to my APEX workspace, without needing any word soup URLs. This will also help with brand recognition should that be needed too.