Setup Ingress with vcluster
Ingress overview
An Ingress is a Kubernetes API object that defines rules for routing external HTTP/HTTPS traffic to Services running inside a vCluster. An Ingress controller enforces these rules by processing incoming requests and directing them to the appropriate backend Service.
This page demonstrates how to configure a simple Ingress that routes requests to the kuard Service based on the HTTP request path.
Before you begin
To comply with Samsung’s security policy, this Ingress resource supports only a single TLS port and assumes TLS termination at the Ingress layer. For more details, refer to the TLS section.
Because of these constraints, the following prerequisites apply when exposing your Service through an Ingress:
- You must provide your own domain (a valid DNS subdomain) from an approved DNS provider.
- Service access is limited to HTTPS on port 443 only (port 80 is not permitted).
- Ingress resources are not publicly accessible—they can only be accessed from authorized IP pools such as Samsung HQ (Suwon), Joyent (SRA), SRPOL, SRIN, and SPC VPN infrastructure.
- HTTPS and HTTP/2 protocols are supported.
- Only TLS 1.2 and TLS 1.3 are supported (TLSv1 and TLSv1.1 are not allowed).
- Source IP preservation is supported without requiring application modifications.
- In EKS-based vCluster environments, proxy-protocol v2 is supported.
- In MKS-based vCluster environments, proxy-protocol v1 is supported.
Prerequisites
- vCluster running Kubernetes v1.25.6 or v1.26.6
- cert-manager v1.13.1
- Ingress-NGINX-Controller v1.9.4 (installed by operator – you do not need to install it)
Ingress resource
A minimal Ingress resource example:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress
namespace: default
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: foo.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: test
port:
number: 80
An Ingress needs apiVersion, kind, metadata and spec fields. Each HTTP rule contains the following information:
Ingress configuration fields
metadata.annotations – Add Kubernetes annotations to an Ingress to customize its behavior. .
spec.ingressClassName – Set this to
nginxto use the NGINX Ingress Controller. You may omit this field becausenginxis the default.spec.rules.host – When specified, the rule applies to all incoming traffic for that host.
spec.rules.http.paths – Define one or more path rules. Each path must specify:
- a Service name
- a Service port (name or number) Incoming requests are routed to a Service only if both the host and path match.
spec.rules.http.paths.backend – Defines the Service and port to forward matching requests to. In this environment, only HTTPS traffic is routed to the backend.
TLS
To secure an Ingress, you must reference a Kubernetes Secret that contains a TLS private key and certificate. The Ingress resource supports only one TLS port: 443, and assumes TLS termination occurs at the Ingress layer before traffic is forwarded to backend Services and Pods.
The TLS Secret must include the following keys:
tls.crt– the TLS certificate, base64-encodedtls.key– the TLS private key, base64-encoded
Example TLS Secret
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
namespace: default
data:
tls.crt: <base64-encoded certificate>
tls.key: <base64-encoded private key>
type: kubernetes.io/tls
Referencing this Secret in an Ingress resource instructs the Ingress controller to secure client connections using TLS. Ensure that the certificate used to generate the Secret contains a valid Common Name (CN) or Fully Qualified Domain Name (FQDN) (for example, example.foo.com).
Example Ingress using TLS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-example-ingress
spec:
rules:
- host: example.foo.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service1
port:
number: 80
tls:
- hosts:
- example.foo.com
secretName: foo-secret
TLS with cert-manager and Let’s Encrypt
Instead of manually creating a Secret from tls.crt and tls.key, you can use cert-manager to automatically request and manage TLS certificates for your Ingress resources. This is done by adding annotations to the Ingress. cert-manager will handle creating Certificate resources and maintaining renewal on your behalf.
Deploy cert-manager
You can install cert-manager in multiple ways—for example, using Helm or through the Apps section in the KOSMOS UI. For alternative installation methods, refer to cert-manager installation documentation.
Installing from Apps


Installing using Helm
$ helm repo add jetstack https://charts.jetstack.io
$ helm repo update
$ helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.13.2 \
--set installCRDs=true
Configure a Let’s Encrypt Issuer
cert-manager provides two Kubernetes Custom Resource Definitions (CRDs) to handle certificate requests:
- Issuers – request certificates within a namespace
- ClusterIssuers – request certificates cluster-wide
See cert-manager docs for more details:
In this example, we will configure a ClusterIssuer that uses Let’s Encrypt production with Cloudflare DNS01 solver. For Cloudflare setup details, see Configuring DNS01 Challenge Provider .
This example solves ACME DNS01 challenges to generate Certificate resources automatically by annotating your Ingress. For HTTP01 flow, see Configuring the HTTP01 Ingress solver .
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# The ACME server URL
server: https://acme-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: js.louis.you@samsung.com
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-prod
# Enable the DNS01 challenge provider
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-token
Once edited, apply the custom resource and check on the status of the issuer after you create it:
Apply the resource and verify status using kubectl.
Name: letsencrypt-prod
Labels: <none>
Annotations: <none>
API Version: cert-manager.io/v1
Kind: ClusterIssuer
Metadata:
Creation Timestamp: 2025-10-31T02:14:15Z
Generation: 1
Managed Fields:
API Version: cert-manager.io/v1
Fields Type: FieldsV1
fieldsV1:
f:metadata:
f:annotations:
.:
f:kubectl.kubernetes.io/last-applied-configuration:
f:spec:
.:
f:acme:
.:
f✉️
f:privateKeySecretRef:
.:
f:name:
f:server:
f:solvers:
Manager: kubectl-client-side-apply
Operation: Update
Time: 2023-10-31T02:14:15Z
API Version: cert-manager.io/v1
Fields Type: FieldsV1
fieldsV1:
f:status:
.:
f:acme:
.:
f:lastPrivateKeyHash:
f:lastRegisteredEmail:
f:uri:
f:conditions:
.:
k:{"type":"Ready"}:
.:
f:lastTransitionTime:
f:message:
f:observedGeneration:
f:reason:
f:status:
f:type:
Manager: cert-manager-clusterissuers
Operation: Update
Subresource: status
Time: 2023-10-31T02:14:17Z
Resource Version: 630
UID: 25819c47-5fbc-4584-a224-d4dc77d643ce
Spec:
Acme:
Email: js.louis.you@samsung.com
Private Key Secret Ref:
Name: letsencrypt-prod
Server: https://acme-v02.api.letsencrypt.org/directory
Solvers:
dns01:
Cloudflare:
API Token Secret Ref:
Key: api-token
Name: cloudflare-api-token-secret
Status:
Acme:
Last Private Key Hash: nrUJakzF1zaXMRDP5B9bUSQVkncJbRAfdlEFzzJumAk=
Last Registered Email: js.louis.you@samsung.com
Uri: https://acme-v02.api.letsencrypt.org/acme/acct/1387640706
Conditions:
Last Transition Time: 2023-10-31T02:15:00Z
Message: The ACME account was registered with the ACME server
Observed Generation: 1
Reason: ACMEAccountRegistered
Status: True
Type: Ready
Events: <none>
Deploy an example service
You may deploy your own chart or standalone manifests. The following example deploys a sample “kuard” application and exposes it through a Service.
apiVersion: apps/v1
kind: Deployment
metadata:
name: kuard
spec:
replicas: 3
selector:
matchLabels:
app: kuard
template:
metadata:
labels:
app: kuard
spec:
containers:
- image: gcr.io/kuar-demo/kuard-amd64:1
imagePullPolicy: Always
name: kuard
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: kuard
spec:
ports:
- port: 443
protocol: TCP
targetPort: 8080
selector:
app: kuard
Note: Using NodePort (e.g., spec.type: NodePort) consumes NodePort quota (limits.nodeport).
Deploy a TLS Ingress resource
Once prerequisites are complete, you can request a TLS certificate in one of two ways:
1. Using annotations in the Ingress
cert-manager will detect cert-related annotations on the Ingress, issue/renew certificates, and create/update the referenced Secret.
Note: You can specify the annotations related cert-manager on your Ingress resources. Please check this document for “Supported Annotations”.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kuard
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "false"
cert-manager.io/issuer: "letsencrypt-prod" <------ Add annotation in here with your Issuer or Clusterissuer
spec:
ingressClassName: nginx
rules:
- host: echoserver.mks.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kuard
port:
number: 443
tls:
- secretName: example-tls
hosts:
- echoserver.mks.example.com
cert-manager will read these annotations and use them to create a certificate, which you can request and can see:
Check certificate status:
k get certificate -A
Output
NAMESPACE NAME READY SECRET AGE
default example-tls True example-tls 2m
cert-manager will reflect the state of the process for every request in the certificate object. You can view this information using this command:
k describe certificate example-tls
Output
$ k describe certificate example-tls
Name: example-tls
Namespace: default
Labels: <none>
Annotations: <none>
API Version: cert-manager.io/v1
Kind: Certificate
Metadata:
Creation Timestamp: 2023-11-02T00:27:18Z
Generation: 1
Managed Fields:
API Version: cert-manager.io/v1
Fields Type: FieldsV1
fieldsV1:
f:status:
.:
f:conditions:
Manager: cert-manager-certificates-trigger
Operation: Update
Subresource: status
Time: 2023-11-02T00:27:18Z
API Version: cert-manager.io/v1
Fields Type: FieldsV1
fieldsV1:
f:metadata:
f:annotations:
.:
f:kubectl.kubernetes.io/last-applied-configuration:
f:spec:
.:
f:dnsNames:
f:isCA:
f:issuerRef:
.:
f:group:
f:kind:
f:name:
f:secretName:
f:usages:
Manager: kubectl-client-side-apply
Operation: Update
Time: 2023-11-02T00:27:18Z
API Version: cert-manager.io/v1
Fields Type: FieldsV1
fieldsV1:
f:status:
f:revision:
Manager: cert-manager-certificates-issuing
Operation: Update
Subresource: status
Time: 2023-11-02T00:27:24Z
API Version: cert-manager.io/v1
Fields Type: FieldsV1
fieldsV1:
f:status:
f:conditions:
k:{"type":"Ready"}:
.:
f:lastTransitionTime:
f:message:
f:observedGeneration:
f:reason:
f:status:
f:type:
f:notAfter:
f:notBefore:
f:renewalTime:
Manager: cert-manager-certificates-readiness
Operation: Update
Subresource: status
Time: 2023-11-02T00:27:24Z
Resource Version: 23958
UID: ab707f9e-1e66-4afe-bfd0-7047a418ba9b
Spec:
Dns Names:
bookinfo.mks.example.com
echoserver.mks.example.com
grpc.mks.example.com
httpbin.mks.example.com
kiali.mks.example.com
nginx.mks.example.com
Issuer Ref:
Group: cert-manager.io
Kind: ClusterIssuer
Name: letsencrypt-prod
Secret Name: example-tls
Usages:
digital signature
key encipherment
server auth
Status:
Conditions:
Last Transition Time: 2023-11-02T00:28:26Z
Message: Certificate is up to date and has not expired
Observed Generation: 1
Reason: Ready
Status: True
Type: Ready
Not After: 2024-01-30T23:28:01Z
Not Before: 2023-11-01T23:28:02Z
Renewal Time: 2023-12-31T23:28:01Z
Revision: 1
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Issuing 112s cert-manager-certificates-trigger Issuing certificate as Secret does not exist
Normal Generated 111s cert-manager-certificates-key-manager Stored new private key in temporary Secret resource "example-tls-w8hfk"
Normal Requested 111s cert-manager-certificates-request-manager Created new CertificateRequest resource "example-tls-1"
Normal Issuing 105s cert-manager-certificates-issuing The certificate has been successfully issued

2. Creating a Certificate resource directly
A Certificate resource defines the desired state of a certificate. cert-manager will request and manage renewal automatically.
You can read more on how to configure your Certificate resources .
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: example-tls
namespace: default
spec:
isCA: true
secretName: example-tls
dnsNames:
- echoserver.example.com
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
group: cert-manager.io
usages:
- digital signature
- key encipherment
- server auth
After apply, cert-manager will read this Certificate resource and use them to create a certificate, which you can request and see:
Verify via:
k get certificate -A
Output
NAMESPACE NAME READY SECRET AGE
default example-tls True example-tls 2m39s
cert-manager will reflect the state of the process for every request in the certificate object. You can view this information using this command:
k describe certificate example-tls
Output
$ k describe certificate example-tls
Name: example-tls
Namespace: default
Labels: <none>
Annotations: <none>
API Version: cert-manager.io/v1
Kind: Certificate
Metadata:
Creation Timestamp: 2023-11-02T00:27:18Z
Generation: 1
Managed Fields:
API Version: cert-manager.io/v1
Fields Type: FieldsV1
fieldsV1:
f:status:
.:
f:conditions:
Manager: cert-manager-certificates-trigger
Operation: Update
Subresource: status
Time: 2023-11-02T00:27:18Z
API Version: cert-manager.io/v1
Fields Type: FieldsV1
fieldsV1:
f:metadata:
f:annotations:
.:
f:kubectl.kubernetes.io/last-applied-configuration:
f:spec:
.:
f:dnsNames:
f:isCA:
f:issuerRef:
.:
f:group:
f:kind:
f:name:
f:secretName:
f:usages:
Manager: kubectl-client-side-apply
Operation: Update
Time: 2023-11-02T00:27:18Z
API Version: cert-manager.io/v1
Fields Type: FieldsV1
fieldsV1:
f:status:
f:revision:
Manager: cert-manager-certificates-issuing
Operation: Update
Subresource: status
Time: 2023-11-02T00:27:24Z
API Version: cert-manager.io/v1
Fields Type: FieldsV1
fieldsV1:
f:status:
f:conditions:
k:{"type":"Ready"}:
.:
f:lastTransitionTime:
f:message:
f:observedGeneration:
f:reason:
f:status:
f:type:
f:notAfter:
f:notBefore:
f:renewalTime:
Manager: cert-manager-certificates-readiness
Operation: Update
Subresource: status
Time: 2023-11-02T00:27:24Z
Resource Version: 23958
UID: ab707f9e-1e66-4afe-bfd0-7047a418ba9b
Spec:
Dns Names:
bookinfo.mks.example.com
echoserver.mks.example.com
grpc.mks.example.com
httpbin.mks.example.com
kiali.mks.example.com
nginx.mks.example.com
Issuer Ref:
Group: cert-manager.io
Kind: ClusterIssuer
Name: letsencrypt-prod
Secret Name: example-tls
Usages:
digital signature
key encipherment
server auth
Status:
Conditions:
Last Transition Time: 2023-11-02T00:28:26Z
Message: Certificate is up to date and has not expired
Observed Generation: 1
Reason: Ready
Status: True
Type: Ready
Not After: 2024-01-30T23:28:01Z
Not Before: 2023-11-01T23:28:02Z
Renewal Time: 2023-12-31T23:28:01Z
Revision: 1
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Issuing 112s cert-manager-certificates-trigger Issuing certificate as Secret does not exist
Normal Generated 111s cert-manager-certificates-key-manager Stored new private key in temporary Secret resource "example-tls-w8hfk"
Normal Requested 111s cert-manager-certificates-request-manager Created new CertificateRequest resource "example-tls-1"
Normal Issuing 105s cert-manager-certificates-issuing The certificate has been successfully issued
Once ready, cert-manager will generate the TLS Secret referenced in your Ingress.
k describe secret example-tls
Output
Name: example-tls
Namespace: default
Labels: controller.cert-manager.io/fao=true
Annotations: cert-manager.io/alt-names:
bookinfo.mks.example.com,echoserver.mks.example.com,grpc.mks.example.com,httpbin.mks.example.com,kiali.mks.example...
cert-manager.io/certificate-name: example-tls
cert-manager.io/common-name: bookinfo.mks.example.com
cert-manager.io/ip-sans:
cert-manager.io/issuer-group: cert-manager.io
cert-manager.io/issuer-kind: ClusterIssuer
cert-manager.io/issuer-name: letsencrypt-prod
cert-manager.io/uri-sans:
Type: kubernetes.io/tls
Data
====
tls.crt: 5766 bytes
tls.key: 1675 bytes
Update your Ingress to reference that Secret.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "false"
name: kuard
spec:
ingressClassName: nginx
rules:
- host: echoserver.mks.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kuard
port:
number: 443
tls:
- secretName: example-tls
hosts:
- echoserver.mks.example.com
Verify ingress status and hostname assignment:
k get ingress
Output
| NAME | CLASS | HOSTS | ADDRESS | PORTS | AGE |
|-------|---------|--------|----------|---------|----------|
| kuard | nginx | echoserver.mks.example.com |a2bb84a10ddad46ada764c79b020ebe8-4010412680.elb.ap-southeast-1.samsungspc.cloud | 80, 443 | 4h9m |
k describe ingress kuard
Output
Name: kuard
Namespace: default
Address: a2bb84a10ddad46ada764c79b020ebe8-4010412680.elb.ap-southeast-1.samsungspc.cloud
Default backend: default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
TLS:
example-tls terminates echoserver.mks.example.com
Rules:
Host Path Backends
---- ---- --------
echoserver.mks.example.com
/ kuard:443 (172.16.81.66:8080,172.16.85.220:8080,172.16.86.75:8080)
Annotations: nginx.ingress.kubernetes.io/ssl-redirect: false
Events: <none>
Next, configure DNS (e.g., via your registrar) by creating a CNAME record pointing to the LoadBalancer address.
Governance
To protect workloads and enforce best practices for all vClusters, the following policies apply:
- Only the nginx Ingress class is permitted
- Hostnames must be explicitly defined
- Wildcard hosts are not allowed
- Certain path patterns are rejected to mitigate:
- CVE-2021-25745
- CVE-2021-25746
- CVE-2023-5043, CVE-2023-5044, CVE-2022-4886 mitigations enabled
- Host + path combinations must be unique across clusters
- WAF is automatically enabled to monitor malicious traffic
Web Application Firewall (WAF)
All Ingress resources are protected by a managed WAF (Web Application Firewall). No action is required from users.
Example – WAF detecting a malicious path traversal attempt:
curl https://example.com/etc/passwd

Troubleshooting
“propagation check failed” during ACME DNS01 challenge
cert-manager validates DNS propagation before attempting DNS challenges. This may fail when IP addresses of DNS provider traffic is blocked.
Use cert-manager flags to override recursive DNS resolution behavior:
--dns-recursive-nameservers: comma separated string with host and port of the recursive nameservers cert-manager should query.--dns-recursive-nameservers-only: forces cert-manager to only use the recursive nameservers for verification. Enabling this option could cause the DNS01 self check to take longer due to caching performed by recursive nameservers.
Example usage:
extraArgs:
--dns-recursive-nameservers-only
--dns-recursive-nameservers=kube-dns.kube-system.svc.cluster.local:53