TLS SANs not applied to Logstash custom service certificate in ECK

Context

I have filebeat installed on a VM and I’m trying to send logs to a logstash instance deployed on a K3S cluster.

Description of the issue

When configuring a custom Logstash service with selfSignedCertificate and subjectAltNames in ECK, the generated certificate does not include the specified SANs. This causes TLS handshake failures when clients (e.g., Filebeat) validate the certificate against the hostname used for connection.

I’m using the eck-stack Helm Chart. Here’s the snippet for eck-logstash :

eck-logstash:
  enabled: true
  fullnameOverride: logstash

  count: 1
  elasticsearchRefs:
    - clusterName: elasticsearch-es
      name: elasticsearch
  monitoring:
    metrics:
      elasticsearchRefs:
      - name: elasticsearch
    logs:
      elasticsearchRefs:
      - name: elasticsearch
  config:
    pipeline.workers: 4
    log.level: debug
  pipelines:
    - pipeline.id: main
      config.string: |
        [...]
  services:
    - name: filebeat-to-logstash
      service:
        spec:
          type: LoadBalancer
          ports:
          - port: 5044
            targetPort: 5044
            name: "filebeat-to-logstash"
            protocol: TCP
  http:
    service:
      spec:
        type: LoadBalancer
    tls:
      selfSignedCertificate:
        subjectAltNames:
        - dns: logstash-vm.example.com
  podTemplate:
    spec:
      ports:
        - containerPort: 5044
          name: filebeat-to-logstash

I did this according to the documentation and it works for Fleet Server but Logstash does not take it into account.

The particularity is that I had to create a custom LoadBalancer service in order to expose the port that will be used by my beats (5044) because only the api (9600) has a service created by Operator by default.

Is this supported by the Chart ? Am I doing it the wrong way ?
Initially, I wanted to open an issue on Github but I’m not sure how to categorize it, so I’m posting it here first.

Steps to reproduce :

Deploy Logstash with ECK using the provided configuration.

  1. Check the certificate exposed on port 5044 using:

    openssl s_client -connect logstash-vm.example.com:5044 -showcerts
    
  2. Observe that the certificate does not include the SAN logstash-vm.example.com

  3. Filebeat fails to connect with the error:

x509: certificate is valid for logstash-ls-http.collog.ls.local, logstash-ls-http, logstash-ls-api.collog.svc, logstash-ls-api.collog, not logstash-vm.example.com

Your configuration uses http.tls.selfSignedCertificate.subjectAltNames, but the Logstash CRD does not have an http field like Elasticsearch and Kibana do. This configuration is being silently ignored.

Looking at the Logstash API types:

type LogstashService struct {
	Name string `json:"name,omitempty"`
	// Service defines the template for the associated Kubernetes Service object.
	Service commonv1.ServiceTemplate `json:"service,omitempty"`
	// TLS defines options for configuring TLS for HTTP.
	TLS commonv1.TLSOptions `json:"tls,omitempty"`
}

Why This Happens

ECK only generates certificates for the API service (port 9600), not for custom services like the beats input (port 5044). Looking at the controller code:

	_, results = certificates.Reconciler{
		K8sClient:             params.Client,
		DynamicWatches:        params.Watches,
		Owner:                 &params.Logstash,
		TLSOptions:            apiSvcTLS,
		Namer:                 logstashv1alpha1.Namer,
		Metadata:              params.Meta,
		Services:              []corev1.Service{apiSvc},  // Only API service!
		GlobalCA:              params.OperatorParams.GlobalCA,
		CACertRotation:        params.OperatorParams.CACertRotation,
		CertRotation:          params.OperatorParams.CertRotation,
		GarbageCollectSecrets: true,
	}.ReconcileCAAndHTTPCerts(params.Context)

The certificate reconciler only includes the API service, so custom input services don't get certificates with the specified SANs.

Current Workarounds

Option 1: Use the existing ECK-generated certificate with Filebeat's ssl.verification_mode

If you can accept certificate verification without hostname checking:

# In Filebeat configuration
output.logstash:
  hosts: ["logstash-vm.example.com:5044"]
  ssl.enabled: true
  ssl.verification_mode: certificate  # Verifies CA but not hostname
  ssl.certificate_authorities: ["/path/to/ca.crt"]

Option 2: Use cert-manager to generate certificates

Create certificates externally with the correct SANs and mount them into the Logstash pod:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: logstash-beats-cert
spec:
  secretName: logstash-beats-tls
  issuerRef:
    name: your-issuer
    kind: ClusterIssuer
  dnsNames:
    - logstash-vm.example.com
  # ... rest of cert config

Then configure the Logstash pipeline to use these certificates:

input {
  beats {
    port => 5044
    ssl_enabled => true
    ssl_certificate => "/path/to/tls.crt"
    ssl_key => "/path/to/tls.key"
  }
}

Option 3: Disable TLS on the beats input (not recommended for production)

If this is for development/testing only:

input {
  beats {
    port => 5044
    ssl_enabled => false
  }
}

Option 4: Expose Logstash through Ingress/Gateway

You could use an Ingress for TLS termination and use dedicated non-self-signed certificates for that, but there are some gotchas as the Beats input is not HTTP but TCP (lumberjack protocol). So you would have to use a vendor specific feature e.g. tcp-routing in the Nginx controller. Or you use the newer Gateway API that has good support for TCP routes:

---
# Gateway with a listener for beats traffic
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: logstash-gateway
spec:
  gatewayClassName: nginx  # or istio, envoy, cilium, etc.
  listeners:
    - name: beats
      port: 5044
      protocol: TLS
      hostname: logstash-external.example.com
      tls:
        mode: Terminate
        certificateRefs:
          - name: logstash-external-cert  # Secret with correct SANs
            kind: Secret
      allowedRoutes:
        kinds:
          - kind: TCPRoute

---
# TCPRoute to forward traffic to Logstash
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TCPRoute
metadata:
  name: logstash-beats-route
spec:
  parentRefs:
    - name: logstash-gateway
      sectionName: beats
  rules:
    - backendRefs:
        - name: logstash-ls-beats  # Logstash service
          port: 5044

---
# Certificate with correct SANs (e.g., from cert-manager)
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: logstash-external-cert
spec:
  secretName: logstash-external-cert
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
    - logstash-external.example.com

Is This a Bug or Missing Feature?

This is a feature gap rather than a bug. The documentation for selfSignedCertificate.subjectAltNames applies to Elasticsearch and Kibana's http configuration, but Logstash has a different architecture with its services array. I have raised Support TLS certificate generation with custom SANs for Logstash custom services (e.g., beats input) · Issue #8981 · elastic/cloud-on-k8s · GitHub to address it. I assume that most production setups use dedicated, proper certificates and or seperate TLS termination, which is why we haven't had any requests for this yet.

1 Like