All Products
Search
Document Center

Alibaba Cloud Service Mesh:Issue certificates for ASM ingress gateways with an ACME CA

Last Updated:Mar 11, 2026

When you expose services through an ASM ingress gateway over HTTPS, the gateway needs a TLS certificate that browsers trust. Instead of manually requesting and renewing certificates, you can automate the entire lifecycle with the Automatic Certificate Management Environment (ACME) protocol.

The following steps walk through using cert-manager with Let's Encrypt to issue a browser-trusted HTTPS certificate for a Service Mesh (ASM) ingress gateway. The same approach works with any ACME-compatible CA, such as Sectigo.

How it works

When you create an ACME Issuer resource in cert-manager, cert-manager generates a private key for secure communication with the ACME CA server. To issue a certificate, the CA must first verify that you own the domain. It does this through challenges.

cert-manager supports two challenge types:

Challenge typeVerification methodHow cert-manager handles it
HTTP-01The CA sends an HTTP request to http://<your-domain>/.well-known/acme-challenge/<token>.cert-manager creates a temporary Ingress resource to route the request to a solver pod, which responds with the expected key.
DNS-01The CA looks up a DNS TXT record for your domain.With the right permissions, cert-manager creates this record automatically through your DNS provider.

The following steps use HTTP-01 challenges with the Ingress API. ASM also supports the Gateway API for solving challenges.

Note

Make sure your CA supports the ACME protocol before you begin. Any ACME-compatible CA works with this approach.

For the full ACME protocol specification, see RFC 8555.

Prerequisites

Step 1: Prepare a public domain

Point a public domain to the IP address of your ASM ingress gateway. The Let's Encrypt CA needs to reach your domain over the public Internet to complete HTTP-01 challenges.

For DNS configuration instructions, see the documentation of your DNS provider. If you use Alibaba Cloud DNS, see Add DNS records. For more information about Let's Encrypt, see Getting Started.

Step 2: Create an Issuer resource

An Issuer tells cert-manager how to request certificates from a CA. In this step, create an Issuer that connects to the Let's Encrypt production server.

Important

Let's Encrypt enforces strict rate limits on its production server. When testing this workflow for the first time, use the staging server URL https://acme-staging-v02.api.letsencrypt.org/directory instead. Staging certificates are not browser-trusted, but the workflow is identical. Switch to the production URL after you confirm everything works.

Use the kubeconfig file of the ACK cluster on the data plane and create the following Issuer resource:

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt-prod-issuer
  namespace: istio-system
spec:
  acme:
    # Email for certificate-related notifications (optional but recommended)
    email: '<your-email@example.com>'
    # ACME account private key stored in this Secret
    privateKeySecretRef:
      name: letsencrypt-prod
    # Let's Encrypt production server
    server: https://acme-v02.api.letsencrypt.org/directory
    solvers:
    # Use HTTP-01 challenges routed through the Istio Ingress class
    - http01:
        ingress:
          ingressClassName: istio

This Issuer uses an HTTP-01 solver with ingressClassName: istio. When a challenge is triggered, cert-manager creates a temporary Ingress resource with this class, which routes the challenge request through the ASM ingress gateway to a cert-manager solver pod.

Note

cert-manager supports two solver APIs: Ingress API and Gateway API. ASM supports both. This example uses the Ingress API.

Verify that the Issuer is ready:

kubectl -n istio-system get issuer letsencrypt-prod-issuer

Expected output:

NAME                      READY   AGE
letsencrypt-prod-issuer   True    8m3s

If READY shows False, see Troubleshooting.

Step 3: Issue a certificate

Create a Certificate resource that references the Issuer from Step 2. cert-manager uses this resource to request a certificate from Let's Encrypt for your domain.

Use the kubeconfig file of the ACK cluster on the data plane and create the following Certificate resource:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: istio-ingressgateway-certs
  namespace: istio-system
spec:
  # Domain names to include in the certificate
  dnsNames:
  - <your-domain.com>
  issuerRef:
    group: cert-manager.io
    kind: Issuer
    name: letsencrypt-prod-issuer
  # Secret where the issued certificate and private key are stored
  secretName: istio-ingressgateway-certs

Replace <your-domain.com> with your actual domain name (for example, example.com).

Verify that the Certificate is ready:

kubectl -n istio-system get certificate istio-ingressgateway-certs

Expected output:

NAME                         READY   SECRET                       AGE
istio-ingressgateway-certs   True    istio-ingressgateway-certs   59m

If READY shows False after several minutes, see Troubleshooting.

What happens during certificate issuance

After you create the Certificate resource, cert-manager triggers the following sequence:

  1. cert-manager sends a certificate signing request to Let's Encrypt through the Issuer.

  2. Let's Encrypt initiates an HTTP-01 challenge by requesting http://<your-domain>/.well-known/acme-challenge/<token>.

  3. cert-manager creates a temporary Ingress (with ingressClassName: istio), a Service, and a solver Deployment in the istio-system namespace. The ASM ingress gateway routes the challenge request to the solver pod.

  4. The solver pod responds with the expected key. Let's Encrypt validates domain ownership and issues the certificate.

  5. cert-manager stores the certificate and private key in the Secret istio-ingressgateway-certs.

  6. cert-manager deletes the temporary Ingress, Service, and Deployment resources.

To confirm that the challenge was completed, check the ASM ingress gateway access logs:

kubectl -n istio-system logs <ingress-gateway-pod-name> | grep letsencrypt | tail -1

Replace <ingress-gateway-pod-name> with the name of your ASM ingress gateway pod.

Example output

{
    "authority_for": "xxxxxxx",
    "bytes_received": "0",
    "bytes_sent": "87",
    "downstream_local_address": "xx.xx.xx.xx:80",
    "downstream_remote_address": "xx.xx.xx.xx:57101",
    "duration": "0",
    "istio_policy_status": "-",
    "method": "GET",
    "path": "/.well-known/acme-challenge/JfKvfdSNmkR7UqmCQU0OSkJC3EsnP4ZUiCc28OLLLxA",
    "protocol": "HTTP/1.1",
    "request_id": "e6806d08-0469-4383-be8e-4d7506b39ec5",
    "requested_server_name": "-",
    "response_code": "200",
    "response_flags": "-",
    "route_name": "-",
    "start_time": "2024-04-08T12:04:06.153Z",
    "trace_id": "-",
    "upstream_cluster": "outbound|8089||cm-acme-http-solver-c4ch9.istio-system.svc.cluster.local",
    "upstream_host": "xx.xx.xx.xx:8089",
    "upstream_local_address": "xx.xx.xx.xx:55886",
    "upstream_response_time": "0",
    "upstream_service_time": "0",
    "upstream_transport_failure_reason": "-",
    "user_agent": "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)",
    "x_forwarded_for": "xx.xx.xx.xx"
}

Key indicators of a successful challenge:

  • response_code is 200.

  • path starts with /.well-known/acme-challenge/.

  • upstream_cluster routes to the cert-manager solver (port 8089).

  • user_agent identifies the Let's Encrypt validation server.

Note

After the certificate is issued, kubectl -n istio-system get ingress does not show the solver Ingress because cert-manager automatically cleans up all solver-related resources.

Step 4: Configure the ASM ingress gateway to use the certificate

Create an Istio Gateway resource

Create an Istio Gateway that binds the issued certificate to port 443 on the ASM ingress gateway. For more information, see Manage Istio gateways.

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: httpbin-https
  namespace: default
spec:
  selector:
    istio: ingressgateway
  servers:
  - hosts:
    - <your-domain.com>
    port:
      name: https
      number: 443
      protocol: HTTPS
    tls:
      # Reference the Secret created by cert-manager
      credentialName: istio-ingressgateway-certs
      mode: SIMPLE

Replace <your-domain.com> with the same domain used in the Certificate resource.

Update the VirtualService

Add the new Gateway to your existing VirtualService so it handles both HTTP and HTTPS traffic. For more information, see Manage virtual services.

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: httpbin-vs
  namespace: default
spec:
  gateways:
    - httpbin
    - httpbin-https    # Add the HTTPS Gateway
  hosts:
    - '*'
  http:
    - name: test
      route:
        - destination:
            host: httpbin.default.svc.cluster.local
            port:
              number: 8000

Verify the certificate

Open https://<your-domain.com> in a browser. Click the lock icon in the address bar. The browser displays Connection is secure, confirming that it trusts the Let's Encrypt certificate.

Browser showing a secure connection with a Let's Encrypt certificate

To verify from the command line:

curl -v https://<your-domain.com> 2>&1 | grep -E "SSL certificate|issuer"

The output shows Let's Encrypt as the certificate issuer.

Troubleshooting

Issuer stays in not-ready state

Check the Issuer status for error details:

kubectl -n istio-system describe issuer letsencrypt-prod-issuer

Common causes:

CauseSolution
Network connectivityThe ACK cluster cannot reach the Let's Encrypt server at https://acme-v02.api.letsencrypt.org/directory. Verify outbound Internet access.
Invalid emailThe email field in the Issuer spec contains an invalid address.

Certificate stays in not-ready state

Check the Certificate status and related resources:

kubectl -n istio-system describe certificate istio-ingressgateway-certs

Then check the associated CertificateRequest and Order:

kubectl -n istio-system get certificaterequest
kubectl -n istio-system get order
kubectl -n istio-system get challenge

Common causes:

CauseSolution
DNS not propagatedThe domain does not resolve to the ASM ingress gateway IP. Verify with dig <your-domain.com> or nslookup <your-domain.com>.
Port 80 blockedHTTP-01 challenges require inbound traffic on port 80. Confirm that the ingress gateway exposes port 80 and that no firewall or security group blocks it.
Rate limit exceededLet's Encrypt may have rate-limited your account on the production server. Switch to the staging server URL and try again.
Ingress class mismatchThe ingressClassName in the Issuer must be istio. Confirm with kubectl get ingressclass.

Certificate issued but HTTPS does not work

CheckCommand
Secret existskubectl -n istio-system get secret istio-ingressgateway-certs
Gateway credentialName matches the Secret nameVerify credentialName: istio-ingressgateway-certs in the Gateway spec.
VirtualService includes the HTTPS GatewayVerify httpbin-https is listed under gateways in the VirtualService spec.

What's next