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 hostnameexample-frontend.example.svc.cluster.local
created by the Serviceexample-frontend
in theexample
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
andsecurityContext.allowPrivilegeEscalation
must not betrue
.securityContext.runAsUser
must be greater than0
if set. If not set, thensecurityContext.runAsNonRoot
should be set.securityContext.runAsGroup
must be greater than0
.securityContext.fsGroup
should be greater than0
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.