This topic tells you how to obtain a TLS certificate in different scenarios for Application Single Sign-On (commonly called AppSSO).
AuthServer is a piece of security infrastructure. It is imperative to configure TLS for it, so that its issuer URI’s scheme is https://.
AuthServer.spec.tls accommodates different scenarios for obtaining a TLS certificate. Select the scenario that matches your case.
The recommended path is to install AppSSO with a default issuer. In that case, you can omit AuthServer.spec.tls and a TLS certificate is obtained automatically.
Each of the scenarios requires that the AppSSO package is installed and configured. In particular, its domain_name must match the ingress domain of your cluster. The presented YAML resources assume my-tap.example.com as the ingress domain. Therefore, the AppSSO configuration values look as follows:
#! AppSSO values
domain_name: "my-tap.example.com"
The default domain_template: "{{.Name}}.{{.Namespace}}.{{.Domain}}" works for most scenarios. If a scenario requires a bespoke domain_template, it contains the relevant instructions.
After applying each scenario, wait for your AuthServer to become ready and then test it by running:
kubectl wait --namespace login authserver/sso --for condition=Ready=True --timeout 500s
curl --location "$(kubectl get --namespace login authserver sso --output=jsonpath='{.status.issuerURI}')/.well-known/openid-configuration"
Alternatively, visit the AuthServer with your browser. You can obtain its issuer URI by running:
kubectl get --namespace login authserver sso --output=jsonpath='{.status.issuerURI}'
CautionBefore applying each scenario, you must configure your AppSSO correctly, and make sure that all certificates and DNS names comply with your setup.
VMware recommend using a default issuer,
because this approach separates the responsibilities of platform operators and service operators. In this case, the Authserver.spec.tls field is not required.
To verify whether AppSSO was installed with a default issuer, run:
kctrl package installed get --namespace tap-install --package-install tap --values-file-output tap-values.yaml
If a shared.ingress_issuer appears in your tap-values.yaml file, you have a default issuer.
ImportantEnsure kctrl is installed.
apiVersion: v1
kind: Namespace
metadata:
name: login
---
apiVersion: sso.apps.tanzu.vmware.com/v1alpha1
kind: AuthServer
metadata:
name: sso
namespace: login
annotations:
sso.apps.tanzu.vmware.com/allow-unsafe-identity-provider: ""
sso.apps.tanzu.vmware.com/documentation: Uses the default issuer for TLS
spec:
identityProviders:
- name: test-users
internalUnsafe:
users:
- username: user
password: password
tokenSignature:
signAndVerifyKeyRef:
name: signing-key
---
apiVersion: secretgen.k14s.io/v1alpha1
kind: RSAKey
metadata:
name: signing-key
namespace: login
spec:
secretTemplate:
type: Opaque
stringData:
key.pem: $(privateKey)
pub.pem: $(publicKey)
ClusterIssuerA ClusterIssuer is a cluster-scoped API provided by cert-manager from which certificates can be obtained programmatically.
This scenario puts all resources into a single YAML file and uses Let’s Encrypt’s production API. You might get the ClusterIssuer from your platform operators.
For more information, see cert-manager documentation.
CautionLet’s Encrypt’s production API rate limits apply.
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-production
spec:
acme:
privateKeySecretRef:
name: letsencrypt-production
server: https://acme-v02.api.letsencrypt.org/directory
solvers:
- http01:
ingress:
class: contour
---
apiVersion: v1
kind: Namespace
metadata:
name: login
---
apiVersion: sso.apps.tanzu.vmware.com/v1alpha1
kind: AuthServer
metadata:
name: sso
namespace: login
annotations:
sso.apps.tanzu.vmware.com/allow-unsafe-identity-provider: ""
spec:
#! --- TLS ---
tls:
issuerRef:
name: letsencrypt-production
kind: ClusterIssuer
#! -----------
identityProviders:
- name: test-users
internalUnsafe:
users:
- username: user
password: password
tokenSignature:
signAndVerifyKeyRef:
name: signing-key
---
apiVersion: secretgen.k14s.io/v1alpha1
kind: RSAKey
metadata:
name: signing-key
namespace: login
spec:
secretTemplate:
type: Opaque
stringData:
key.pem: $(privateKey)
pub.pem: $(publicKey)
IssuerThis scenario is identical to Using a ClusterIssuer, except that the Issuer is scoped to a namespace and must be located in the same namespace as the AuthServer.
---
apiVersion: v1
kind: Namespace
metadata:
name: login
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: letsencrypt-production
namespace: login
spec:
acme:
privateKeySecretRef:
name: letsencrypt-production
server: https://acme-v02.api.letsencrypt.org/directory
solvers:
- http01:
ingress:
class: contour
---
apiVersion: sso.apps.tanzu.vmware.com/v1alpha1
kind: AuthServer
metadata:
name: sso
namespace: login
annotations:
sso.apps.tanzu.vmware.com/allow-unsafe-identity-provider: ""
spec:
#! --- TLS ---
tls:
issuerRef:
name: letsencrypt-production
#! -----------
identityProviders:
- name: test-users
internalUnsafe:
users:
- username: user
password: password
tokenSignature:
signAndVerifyKeyRef:
name: signing-key
---
apiVersion: secretgen.k14s.io/v1alpha1
kind: RSAKey
metadata:
name: signing-key
namespace: login
spec:
secretTemplate:
type: Opaque
stringData:
key.pem: $(privateKey)
pub.pem: $(publicKey)
CertificateA Certificate is an API provided by cert-manager that is scoped to a namespace and represents a TLS certificate obtained from a (Cluster)Issuer. To create a Certificate, you must know the name and kind of your issuer.
These scenarios use Let’s Encrypt’s production API and require that a ClusterIssuer by the name letsencrypt-production exists. See Using a ClusterIssuer for how to set up the issuer.
When using Certificate, its .spec.dnsNames must contain the FQDN of the templated issuer URI. The domain_name and domain_template of your AppSSO package installation must comply with your DNS name.
If you have an existing Certificate in the same Namespace where the AuthServer is installed, use the following AppSSO configuration values:
---
apiVersion: v1
kind: Namespace
metadata:
name: login
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: sso
namespace: login
spec:
dnsNames:
- "sso.login.my-tap.example.com"
issuerRef:
name: letsencrypt-production
kind: ClusterIssuer
secretName: sso-cert
---
apiVersion: sso.apps.tanzu.vmware.com/v1alpha1
kind: AuthServer
metadata:
name: sso
namespace: login
annotations:
sso.apps.tanzu.vmware.com/allow-unsafe-identity-provider: ""
spec:
#! --- TLS ---
tls:
certificateRef:
name: sso
#! -----------
identityProviders:
- name: test-users
internalUnsafe:
users:
- username: user
password: password
tokenSignature:
signAndVerifyKeyRef:
name: signing-key
---
apiVersion: secretgen.k14s.io/v1alpha1
kind: RSAKey
metadata:
name: signing-key
namespace: login
spec:
secretTemplate:
type: Opaque
stringData:
key.pem: $(privateKey)
pub.pem: $(publicKey)
secretgen-controller allows you to export and import Secrets across namespaces. When your Certificate is located in another namespace, for example, it’s controlled by another team, you can import its Secret to other namespaces. If you have an existing Certificate in another Namespace, use the following AppSSO configuration values:
---
apiVersion: v1
kind: Namespace
metadata:
name: tls
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: sso
namespace: tls
spec:
dnsNames:
- "sso.login.my-tap.example.com"
issuerRef:
name: letsencrypt-production
kind: ClusterIssuer
secretName: sso-cert
---
apiVersion: secretgen.carvel.dev/v1alpha1
kind: SecretExport
metadata:
name: sso-cert
namespace: tls
spec:
toNamespace: login
---
apiVersion: v1
kind: Namespace
metadata:
name: login
---
apiVersion: secretgen.carvel.dev/v1alpha1
kind: SecretImport
metadata:
name: sso-cert
namespace: login
spec:
fromNamespace: tls
---
apiVersion: sso.apps.tanzu.vmware.com/v1alpha1
kind: AuthServer
metadata:
name: sso
namespace: login
annotations:
sso.apps.tanzu.vmware.com/allow-unsafe-identity-provider: ""
spec:
#! --- TLS ---
tls:
secretRef:
name: sso-cert
#! -----------
identityProviders:
- name: test-users
internalUnsafe:
users:
- username: user
password: password
tokenSignature:
signAndVerifyKeyRef:
name: signing-key
---
apiVersion: secretgen.k14s.io/v1alpha1
kind: RSAKey
metadata:
name: signing-key
namespace: login
spec:
secretTemplate:
type: Opaque
stringData:
key.pem: $(privateKey)
pub.pem: $(publicKey)
CautionBe cautious when using
SecretExportandSecretImportto facilitate the transfer across namespaces.
If you have an existing TLS certificate and private key, for example, if your TLS certificate was created outside the cluster, you can apply it directly.
If you don’t have a TLS certificate, there are numerous ways to obtain TLS certificates. One of the simplest methods is to use a tool such as mkcert, step or openssl in GitHub.
If you have an existing TLS certificate in the same Namespace where the AuthServer is installed, use the following AppSSO configuration values:
---
apiVersion: v1
kind: Namespace
metadata:
name: login
---
apiVersion: v1
kind: Secret
type: kubernetes.io/tls
metadata:
name: my-cert
namespace: login
stringData:
#! --- ReplaceMe - certificate and private key for "sso.login.my-tap.example.com ---
tls.crt: |
-----BEGIN CERTIFICATE-----
# redacted
-----END CERTIFICATE-----
tls.key: |
-----BEGIN PRIVATE KEY-----
# redacted
-----END PRIVATE KEY-----
#! ------------------------------------------------------------------------
---
apiVersion: sso.apps.tanzu.vmware.com/v1alpha1
kind: AuthServer
metadata:
name: sso
namespace: login
annotations:
sso.apps.tanzu.vmware.com/allow-unsafe-identity-provider: ""
spec:
#! --- TLS ---
tls:
secretRef:
name: my-cert
#! -----------
identityProviders:
- name: test-users
internalUnsafe:
users:
- username: user
password: password
tokenSignature:
signAndVerifyKeyRef:
name: signing-key
---
apiVersion: secretgen.k14s.io/v1alpha1
kind: RSAKey
metadata:
name: signing-key
namespace: login
spec:
secretTemplate:
type: Opaque
stringData:
key.pem: $(privateKey)
pub.pem: $(publicKey)
ImportantThe TLS certificate
tls.crtand its corresponding private keytls.keymust be stored in a secret with these keys.
If you have an existing TLS certificate in another Namespace, use the following AppSSO configuration values:
---
apiVersion: v1
kind: Namespace
metadata:
name: tls
---
apiVersion: v1
kind: Secret
type: kubernetes.io/tls
metadata:
name: my-cert
namespace: tls
stringData:
#! --- ReplaceMe - certificate and private key for "sso.login.my-tap.example.com ---
tls.crt: |
-----BEGIN CERTIFICATE-----
# redacted
-----END CERTIFICATE-----
tls.key: |
-----BEGIN PRIVATE KEY-----
# redacted
-----END PRIVATE KEY-----
#! ------------------------------------------------------------------------
---
apiVersion: secretgen.carvel.dev/v1alpha1
kind: SecretExport
metadata:
name: my-cert
namespace: tls
spec:
toNamespace: login
---
apiVersion: v1
kind: Namespace
metadata:
name: login
---
apiVersion: secretgen.carvel.dev/v1alpha1
kind: SecretImport
metadata:
name: my-cert
namespace: login
spec:
fromNamespace: tls
---
apiVersion: sso.apps.tanzu.vmware.com/v1alpha1
kind: AuthServer
metadata:
name: sso
namespace: login
annotations:
sso.apps.tanzu.vmware.com/allow-unsafe-identity-provider: ""
spec:
#! --- TLS ---
tls:
secretRef:
name: my-cert
#! -----------
identityProviders:
- name: test-users
internalUnsafe:
users:
- username: user
password: password
tokenSignature:
signAndVerifyKeyRef:
name: signing-key
---
apiVersion: secretgen.k14s.io/v1alpha1
kind: RSAKey
metadata:
name: signing-key
namespace: login
spec:
secretTemplate:
type: Opaque
stringData:
key.pem: $(privateKey)
pub.pem: $(publicKey)
ImportantThe TLS certificate
tls.crtand its corresponding private keytls.keymust be stored in a secret with these keys.Be cautious when using
SecretExportandSecretImportto facilitate the transfer across namespaces.
To use wildcard certificates for DNS names such as *.my-tap.example.com, you must edit the AppSSO’s domain_template so that the templated issuer URIs for AuthServer match the wildcard. For example:
sso.login.my-tap.example.com does not match the wildcard.sso-login.my-tap.example.com matches the wildcard.The following AppSSO configuration values accommodates a wildcard certificate for *.my-tap.example.com:
#! AppSSO values
domain_name: "my-tap.example.com"
domain_template: "{{.Name}}-{{.Namespace}}.{{.Domain}}"
#! ^ note the dash
The following scenarios require TLS Secrets, but the same concept applies to Certificate.
ImportantWhen using a
(Cluster)Issuerfor Let’s Encrypt, you cannot request wildcard certificates when it uses the http01 challenge solver.
If you have an existing wildcard TLS certificate in the same Namespace where the AuthServer is installed, use the following AppSSO configuration values:
---
apiVersion: v1
kind: Namespace
metadata:
name: login
---
apiVersion: v1
kind: Secret
type: kubernetes.io/tls
metadata:
name: my-wildcard-cert
namespace: login
stringData:
#! --- ReplaceMe - certificate and private key for "*.my-tap.example.com ---
tls.crt: |
-----BEGIN CERTIFICATE-----
# redacted
-----END CERTIFICATE-----
tls.key: |
-----BEGIN PRIVATE KEY-----
# redacted
-----END PRIVATE KEY-----
#! ------------------------------------------------------------------------
---
apiVersion: sso.apps.tanzu.vmware.com/v1alpha1
kind: AuthServer
metadata:
name: sso
namespace: login
annotations:
sso.apps.tanzu.vmware.com/allow-unsafe-identity-provider: ""
spec:
#! --- TLS ---
tls:
secretRef:
name: my-wildcard-cert
#! -----------
identityProviders:
- name: test-users
internalUnsafe:
users:
- username: user
password: password
tokenSignature:
signAndVerifyKeyRef:
name: signing-key
---
apiVersion: secretgen.k14s.io/v1alpha1
kind: RSAKey
metadata:
name: signing-key
namespace: login
spec:
secretTemplate:
type: Opaque
stringData:
key.pem: $(privateKey)
pub.pem: $(publicKey)
NoteThis scenario is similar to using an existing TLS certificate in the same namespace, except that the certificate is a wildcard.
If you have an existing wildcard TLS certificate in another Namespace, use the following AppSSO configuration values:
---
apiVersion: v1
kind: Namespace
metadata:
name: tls
---
apiVersion: v1
kind: Secret
type: kubernetes.io/tls
metadata:
name: my-wildcard-cert
namespace: login
stringData:
#! --- Certificate and private key for "*.my-tap.example.com ---
tls.crt: |
-----BEGIN CERTIFICATE-----
# redacted
-----END CERTIFICATE-----
tls.key: |
-----BEGIN PRIVATE KEY-----
# redacted
-----END PRIVATE KEY-----
#! -------------------------------------------------------------
---
apiVersion: secretgen.carvel.dev/v1alpha1
kind: SecretExport
metadata:
name: my-cert
namespace: tls
spec:
toNamespace: login
---
apiVersion: v1
kind: Namespace
metadata:
name: login
---
apiVersion: secretgen.carvel.dev/v1alpha1
kind: SecretImport
metadata:
name: my-cert
namespace: login
spec:
fromNamespace: tls
---
apiVersion: sso.apps.tanzu.vmware.com/v1alpha1
kind: AuthServer
metadata:
name: sso
namespace: login
annotations:
sso.apps.tanzu.vmware.com/allow-unsafe-identity-provider: ""
spec:
#! --- TLS ---
tls:
secretRef:
name: my-wildcard-cert
#! -----------
identityProviders:
- name: test-users
internalUnsafe:
users:
- username: user
password: password
tokenSignature:
signAndVerifyKeyRef:
name: signing-key
---
apiVersion: secretgen.k14s.io/v1alpha1
kind: RSAKey
metadata:
name: signing-key
namespace: login
spec:
secretTemplate:
type: Opaque
stringData:
key.pem: $(privateKey)
pub.pem: $(publicKey)
NoteThis scenario is similar to using an existing TLS certificate in another namespace, except that the certificate is a wildcard.