Skip to main content
Version: 6.16.0

Development Considerations when Building Applications

Routing

SmoothGlue uses the Istio service mesh for routing traffic to and from deployed applications. In general, incoming traffic for a SmoothGlue Run cluster enters through a cluster load balancer, and an Istio Ingress Gateway routes the traffic to the correct application. Each application's Helm chart should create an Istio Virtual Service to inform Istio of the application's hostname, Service address, and port so that it can route traffic appropriately.

The following example manifest shows what an example Virtual Service might look like after being rendered. The java-gradle-example and javascript-npm-example Helm charts include examples of how the Virtual Service manifest can be parameterized using Helm templating so that values can be configured in Argo CD.

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
labels:
name: example-virtual-service
namespace: example
spec:
gateways:
- istio-system/public
hosts:
- example.run.smoothglue.io
http:
- route:
- destination:
host: example-frontend
port:
number: 80
  • The Virtual Service is configured with the istio-system/public gateway, which is the default Ingress Gateway used for application deployments. The gateway must be specified; if it is not, the Virtual Service will only be available internally within the Istio service mesh.
  • The hostname example.run.smoothglue.io will generally be a direct subdomain of the SmoothGlue Run cluster's domain name and the cluster's Ingress Gateway will be configured with a wildcard TLS certificate for this domain. If the application needs to be served on a different domain, contact your platform administrator to configure the necessary routing and SNI hostnames.
  • The Virtual Service routes HTTP traffic to the destination example-frontend, which in this case is a shorthand for the internal DNS hostname example-frontend.example.svc.cluster.local created by the Service example-frontend in the example namespace. This destination Service should expose port 80.
    • Because the Istio Ingress Gateway automatically handles TLS termination, the application's Service can serve simple HTTP, allowing Istio to serve HTTPS via the Virtual Service.
    • Note that only applications serving HTTP traffic are supported out of the box. If an application requires special handling or protocols, such as raw TCP or mTLS, consult with a platform administrator regarding a customized Ingress Gateway configuration.

Authentication

SmoothGlue allows deployed applications to utilize Authservice to automatically authenticate users against the cluster's Keycloak realm before granting them access.

To enable authentication via Authservice, each application's Pod must have the label protect: keycloak applied to it. Additionally, the application must have a matching chain configured within Authservice, which should be configured by a platform administrator during initial setup. (See Preparing Applications for Deployment for more information.) The label can be applied to the Pod using the following block in the application's Deployment:

spec:
template:
metadata:
labels:
protect: keycloak

Once authenticated against the Keycloak Realm, if authorization and role-based access control are desired, these must be implemented by the application. This can be achieved using the JWT returned by Keycloak.

Example JWT Payload
{
"exp": 1234567890,
"iat": 1234567890,
"jti": "01234567-89ab-cdef-0123-456789abcdef",
"iss": "https://login.smoothglue.io/auth/realms/structsure",
"aud": ["realm-management", "account"],
"sub": "01234567-89ab-cdef-0123-456789abcdef",
"typ": "Bearer",
"azp": "structsure-console",
"sid": "01234567-89ab-cdef-0123-456789abcdef",
"acr": "1",
"allowed-origins": ["https://your-application.run.smoothglue.io"],
"realm_access": {
"roles": [
"offline_access",
"default-roles-structsure",
"uma_authorization"
]
},
"resource_access": {
"realm-management": {
"roles": [
"view-identity-providers",
"view-realm",
"manage-identity-providers",
"impersonation",
"realm-admin",
"create-client",
"manage-users",
"query-realms",
"view-authorization",
"query-clients",
"query-users",
"manage-events",
"manage-realm",
"view-events",
"view-users",
"view-clients",
"manage-authorization",
"manage-clients",
"query-groups"
]
},
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
}
},
"scope": "openid profile email",
"email_verified": true,
"name": "John Q User",
"groups": [
"/YourOrganization", # organization member
"/YourOrganization/_admins", # organization administrator
"/_structsureAdmins", # platform administrator
"/_structsureAudit" # all users
],
"preferred_username": "john.q.user@example.com",
"given_name": "John",
"locale": "en",
"family_name": "User",
"email": "john.q.user@example.com"
}

When encoded, this payload will look something like a blob of base64-encoded data:

Encoded Example JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEyMzQ1Njc4OTAsImlhdCI6MTIzNDU2Nzg5MCwianRpIjoiMDEyMzQ1NjctODlhYi1jZGVmLTAxMjMtNDU2Nzg5YWJjZGVmIiwiaXNzIjoiaHR0cHM6Ly9sb2dpbi5zbW9vdGhnbHVlLmlvL2F1dGgvcmVhbG1zL3N0cnVjdHN1cmUiLCJhdWQiOlsicmVhbG0tbWFuYWdlbWVudCIsImFjY291bnQiXSwic3ViIjoiMDEyMzQ1NjctODlhYi1jZGVmLTAxMjMtNDU2Nzg5YWJjZGVmIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoic3RydWN0c3VyZS1jb25zb2xlIiwic2lkIjoiMDEyMzQ1NjctODlhYi1jZGVmLTAxMjMtNDU2Nzg5YWJjZGVmIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwczovL3lvdXItYXBwbGljYXRpb24ucnVuLnNtb290aGdsdWUuaW8iXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwiZGVmYXVsdC1yb2xlcy1zdHJ1Y3RzdXJlIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJyZWFsbS1tYW5hZ2VtZW50Ijp7InJvbGVzIjpbInZpZXctaWRlbnRpdHktcHJvdmlkZXJzIiwidmlldy1yZWFsbSIsIm1hbmFnZS1pZGVudGl0eS1wcm92aWRlcnMiLCJpbXBlcnNvbmF0aW9uIiwicmVhbG0tYWRtaW4iLCJjcmVhdGUtY2xpZW50IiwibWFuYWdlLXVzZXJzIiwicXVlcnktcmVhbG1zIiwidmlldy1hdXRob3JpemF0aW9uIiwicXVlcnktY2xpZW50cyIsInF1ZXJ5LXVzZXJzIiwibWFuYWdlLWV2ZW50cyIsIm1hbmFnZS1yZWFsbSIsInZpZXctZXZlbnRzIiwidmlldy11c2VycyIsInZpZXctY2xpZW50cyIsIm1hbmFnZS1hdXRob3JpemF0aW9uIiwibWFuYWdlLWNsaWVudHMiLCJxdWVyeS1ncm91cHMiXX0sImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwibmFtZSI6IkpvaG4gUSBVc2VyIiwiZ3JvdXBzIjpbIi9Zb3VyT3JnYW5pemF0aW9uIiwiL1lvdXJPcmdhbml6YXRpb24vX2FkbWlucyIsIi9fc3RydWN0c3VyZUFkbWlucyIsIi9fc3RydWN0c3VyZUF1ZGl0Il0sInByZWZlcnJlZF91c2VybmFtZSI6ImpvaG4ucS51c2VyQGV4YW1wbGUuY29tIiwiZ2l2ZW5fbmFtZSI6IkpvaG4iLCJsb2NhbGUiOiJlbiIsImZhbWlseV9uYW1lIjoiVXNlciIsImVtYWlsIjoiam9obi5xLnVzZXJAZXhhbXBsZS5jb20ifQ.Md7VMzhDWf-FHcWmxRkgLwq1lB4UeSVamkrj6AB_oOM

This encoded JWT can be decoded by the application. For example, for a Python-based application, the following code snippet might be useful to decode the provided JWT:

Example Python JWT Decoder
#!/usr/bin/env python

import json, jwt

payload = sys.stdin.read()
secret = "yoursecret"
decoded = jwt.decode(payload, secret, algorithm="HS256")
print(json.dumps(decoded))

This snippet requires the pyJWT module, which can be installed with the command:

pip install pyJWT

SmoothGlue recommends using the groups attribute to determine users' group memberships, which should provide the fully qualified group path for each Console organization and individual roles within that organization. Each Console organization generates a top-level group directory, and roles within each organization create subgroups within that top-level directory. For example, for an organization named SmoothGlue, organization administrators would be members of the subgroup /SmoothGlue/_admins.

Platform administrators are members of a special top-level group called /_structsureAdmins.

See Console Roles for more information on specific roles within organizations.

Kyverno Policies

SmoothGlue deploys Kyverno Policies to enforce certain Kubernetes best practices. If your Application's Deployment is unhealthy in Argo CD and errors are being generated by Kyverno, then your deployment should be re-configured to adhere to best practices before Kyverno will allow deployment of the application.

The event will include details regarding the specific Kyverno policy being violated, but a few examples of the best practices inspected by Kyverno are listed below. Keep these in mind when developing each application's Helm chart:

  • Avoid deploying applications using mutable image tags like latest, as this can lead to code changes being applied to environments inadvertently.
  • Creation of NodePort services is not allowed. The Istio Ingress Gateway is the only recommended ingress method for applications.
  • Pods are not allowed to interact with the AWS instance metadata service (IMDS). If a pod needs to interact with AWS services, contact a platform administrator for assistance.
  • Pods with privileged containers or privilege escalation are prohibited. This means that:
    • securityContext.privileged and securityContext.allowPrivilegeEscalation must not be true.
    • securityContext.runAsUser must be greater than 0 if set. If not set, then securityContext.runAsNonRoot should be set.
    • securityContext.runAsGroup must be greater than 0.
    • securityContext.fsGroup should be greater than 0 if set.
  • Setting the SELinux role is not allowed. This means that securityContext.seLinuxOptions.role must not be set.

Some Cluster Policies automatically implement security best practices on the cluster by mutating deployed Kubernetes resources. These should not require user action, but are still useful to know about, in case the deployed configuration looks different from the expected configuration. Some of the mutating ClusterPolicies which are currently bundled with SmoothGlue include the following:

  • Containers in Pods will automatically be mutated to drop all capabilities that have not been explicitly granted, in order to maintain least privilege for all containers.
  • Containers without a security context set will be automatically mutated to run as non-root with a high UID/GID. However, if a specific UID/GID is required, be sure to set this explicitly within the configuration.
  • Containers will be mutated so they do not mount service account tokens automatically. If this functionality is required, enable it explicitly.