Kong Ingress controller defining multiple HTTPS ports - kubernetes

I am setting up a Kong ingress controller, however, the default ingress controller listening ports are 80/443. I need to add an additional HTTPS port (8080). I can change the default HTTPS port using the servicePort in the values file, however this replaces the default HTTPS port 443 with 8080. Is there a way to add an additional HTTPS port? In the API Gateway we used to be able to define it in the proxy_listen config, however, this doesn't seem to work in the Kong Ingress controller.

You will need customize the chart,
{{- if .tls.enabled }}
- name: kong-{{ .serviceName }}-tls
port: {{ .tls.servicePort }}
targetPort: {{ .tls.overrideServiceTargetPort | default .tls.containerPort }}
appProtocol: https
- name: kong-{{ .serviceName }}-tls-customize
port: {{ .tls.servicePort_custom }}
targetPort: {{ .tls.overrideServiceTargetPort | default .tls.containerPort }}
appProtocol: https
_helpers.tpl#L176
and it will produce something like this
- name: kong-proxy-tls
port: 443
targetPort: 8443
appProtocol: https
- name: kong-proxy-custom-tls
port: 8080
targetPort: 8443
But i see something in the ENV, which seems like some binding so i do not belive it will work
- name: KONG_PORT_MAPS
value: "80:8000, 443:8443"
so seems like multiple change is involve or might be some limitations but this how you can play with chart, download it and customize it.
you can also try proxy_listen that was suppose to working with you before.
proxy_helpers.tpl#L209
{{- if .tls -}}
{{- if .tls.enabled -}}
{{/*
This is a bit of a hack to support always including "ssl" in the parameter
list for TLS listens. It's not possible to set a variable to an object from
.Values and then modify one of the objects values locally, although
https://github.com/helm/helm/issues/4987 indicates it should be. Instead,
this creates a new object and new parameters list built from the original.
*/}}
{{- $listenConfig := dict -}}
{{- $listenConfig := merge $listenConfig .tls -}}
{{- $parameters := append .tls.parameters "ssl" -}}
{{- $_ := set $listenConfig "parameters" $parameters -}}
{{- $_ := set $listenConfig "address" (default "0.0.0.0" .address) -}}
{{- $tlsListen := (include "kong.singleListen" $listenConfig) -}}
{{- $unifiedListen = append $unifiedListen $tlsListen -}}
{{- end -}}
{{- end -}}
listen-values.yaml#L145
listen_helpers.tpl#L238

Related

Merging two Dictionaries in Helm

I am using Helm 3. I have two values.yaml files. In common/values.yaml I have defined:
deployment:
ports:
- name: http
protocol: TCP
The common is of the type library. In my-app, which is of the type application, the common is added as a dependency. In my-app/values.yaml I have added:
deployment:
ports:
- containerPort: 8081
I have defined a template _deployment.yaml in common/templates. In this file I am trying to merge these two deployment dictionaries into one by using:
{{- $deployment := merge .Values.common.deployment .Values.deployment -}}
When I am printing {{ $deployment }}, it is giving output:
map[ports:[map[containerPort:8080 name:http protocol:TCP]]]
And if I do:
{{- $deployment := merge .Values.deployment .Values.common.deployment -}}
The output of {{ $deployment }} is:
map[ports:[map[containerPort:8081]]]
Moreover the output of {{ .Values.common.deployment }} is:
map[ports:[map[name:http protocol:TCP]]]
And the output of {{ .Values.deployment }} is:
map[ports:[map[containerPort:8081]]]
What I would like to have after merging is:
deployment:
ports:
- name: http
protocol: TCP
containerPort: 8081
Any advice you could give would be much appreciated.
Looks like the merge operation does not work as expected on lists (it's a common problem, as the merge operation is ambiguous on list: should a list be appended or replaced when merging ?)
Anyway, I would suggest to merge the ports data with:
{{- $ports := merge .Values.deployment.ports[0] .Values.common.deployment.ports[0] -}}
and render the result with:
deployment:
ports:
- {{- toJson $ports }}
HTH

Helm template not able to read ip address - can't evaluate field ipAddress in type string

I am trying to create a helm template for Istio's ServiceEntry which has a list of addresses for static ip addresses. In values.yaml, I have
- name: test-se
namespace: test-se-ns
egressUrls:
- mydbhost.com
port: 32306
protocol: TCP
ipAddress: 10.2.2.2
In the .tpl file I am trying to add the value of ipAddress to a list
{{- with .ipAddress }}
addresses:
- {{ .ipAddress | quote }}
{{- end }}
Always fails with exception
templates/_service_entry.tpl:18:13: executing "common.serviceentry.tpl" at <.ipAddress>: can't evaluate field ipAddress in type string
Any idea what I am doing wrong?
If you use with you make the thing that you have used as with the context inside that block.
So, use the dot to refer to it.
{{- with .ipAddress }}
addresses:
- {{ . | quote }}
{{- end }}
From the docs:
{{with pipeline}} T1 {{end}}
If the value of the pipeline is empty, no output is generated;
otherwise, dot is set to the value of the pipeline and T1 is
executed.
In this case, an if seems also fitting, since you do not much with the new context.
{{- if .ipAddress }}
addresses:
- {{ .ipAddress | quote }}
{{- end }}
when you use with in Helm, you change the scope of the ., so Helm looks for an object and not a string, you can read more about it in the docs.
but anyway, I think that in your case, you need to use range instead of with, you can see an example here

Query Values in helm template

I am trying to generate a TLS certificate for all of the ingress resources in my Helm chart. My helm chart contains an application with multiple backends so my Values.yaml is structed like this:
backend1:
ingress:
host: testing.app.com
tls:
- secretName: my-tls-cert
hosts:
- testing.app.com
backend2:
ingress:
host: testing.app.com
tls:
- secretName: idp-cts-cert
hosts:
- idp-cts
db
creds: ""
serviceName: ""
Notice there is a mixture of maps and string values. My goal is to use a utility template I wrote to call genSignedCert and generate one TLS cert that has the hosts listed as a CN or alternate name:
{{/*
Return a self-signed TLS certificate
{{ include "common.certs.ingress-tls" .hosts }}
*/}}
{{- define "common.certs.gen-cert" -}}
{{- $hostlist := toStrings . -}}
{{- $cn := (first $hostlist) -}}
{{- $altnames := uniq (rest $hostlist) -}}
{{- $ca := genCA "idp-ca" 365 -}}
{{- $cert := genSignedCert $cn nil $altnames 365 $ca -}}
tls.crt: {{ $cert.Cert | b64enc }}
tls.key: {{ $cert.Key | b64enc }}
{{- end -}}
I have tried iterating over the Values and but I cannot come up with workable code to do this.
Edit1: I am aware of the security implications of using self-signed certificates. The bad values.yaml structure is inherited from the fact that this is an umbrella chart and each backed is also it's own chart. A refactor of the charts structure may be required, but I wanted to exhaust all options first.
Consider generating the TLS certificate outside Helm, and injecting it via values (or storing its components in a Secret directly). This avoids some complicated code here. There is a more serious problem, though: every time you call genCA and genSignedCert it creates a new certificate, so every time you upgrade you'll get a different certificate, and for that matter if you call this template once per Ingress object, each will have a different certificate.
It'd help this problem to restructure the values.yaml slightly. It's hard for code to tell that backend1 is a backend specification, but serviceName isn't. If you just have a list of backends this gets easier:
backends:
- ingress:
host: testing.app.com
...
- ingress:
host: testing.app.com
...
You'll then hit a couple of limitations of Helm templates as a full-featured programming language. Templates only ever return strings, so you can't write a template that returns a list. You can't pass a function as a parameter to a template, so you can't write a general-purpose map (in limited cases you can pass a template name and include it).
What you can do is write a recursive function that passes the partial list forward to the next iteration, and then invokes the final generator when it's done. In Python, we might write:
def generateCertificate(backends, tls, hosts):
# If `tls` is non-empty, take the first item from it and add its
# hosts to the `hosts` list; then recurse with the same backend
# list, the remaining `tls` items, and the updated `hosts`:
if len(tls) > 0:
return generateCertificate(backends, tls[1:], hosts + tls[0].hosts)
# If `tls` is empty but `backends` is non-empty, take the first
# backend, and recurse with the remaining `backends`, the `tls` items
# from the selected backend, and the same `hosts`:
else if len(backends) > 0:
return generateCertificate(backends[1:], backends[0].tls, hosts)
# If `tls` and `backends` are both empty, we're done
else:
return buildTheCertificate(hosts)
certificate = generateCertificate(values.backends, [], [])
We can convert this logic into Go templates:
{{/* Emit a TLS certificate given the list of backends. The
parameter is a dictionary with keys `backends`, `tls`, and `hosts`. */}}
{{- define "common.certs.gen-cert" -}}
{{- if .tls -}}
{{- include "common.certs.gen-cert" (dict "backends" .backend "tls" (last .tls) "hosts" (concat .hosts (head .tls).hosts)) -}}
{{- else if .backends -}}
{{- include "common.certs.gen-cert" (dict "backends" (tail .backends) "tls" (head .backends).tls "hosts" .hosts) -}}
{{- else -}}
{{- include "common.certs.gen-cert-hosts" .hosts -}}
{{- end -}}
{{- end -}}
{{/* Actually generate a TLS certificate from a list of host names.
Note, the certificate will be regenerated on every call. The
single parameter is a list of names. */}}
{{- define "common.certs.gen-cert-hosts" -}}
{{- $cn := first . -}}
{{- $altnames := rest . | uniq -}}
{{- $ca := genCA "idp-ca" 365 -}}
{{- $cert := genSignedCert $cn nil $altnames 365 $ca -}}
tls.crt: {{ $cert.Cert | b64enc }}
tls.key: {{ $cert.Key | b64enc }}
{{- end -}}
{{- include "common.certs.gen-cert" (dict "backends" .Values.backends) -}}
This is enough intricate code that it's probably worth unit-testing it. Setting this up is left as an exercise; Helm does not have any sort of native support here.

Unable to define range of values for ports

My goal is to have multiple ports defined for a service of type LoadBalancer and I do not want to copy paste the same thing over and over again.
I did come to a solution, but sure how I could define the range - I need all values from 50000 to 50999.
In my service, I define the range:
{{- range $service.ports }}
- name: tport
protocol: TCP
port: {{ . }}
{{- end }}
And in my values file:
ports:
- 50000
- 50001
- 50999
How could I define the ports or update the service template to do this?
Put the min and max port as two different values on your values.yaml and use the range on your template like this:
{{- range untilStep (.Values.config.min_port|int) (.Values.config.max_port|int) 1 }}
- port: {{ . }}
targetPort: "tcp-{{ . }}"
protocol: TCP
name: "tcp-{{ . }}"
{{ -end }}

helm getting subchart service names

Whats the best way to get the helm subchart service names to reference into my ingress controller that will sit in the parent chart
values.yaml
---
ingress:
paths:
- serviceName: app-1
path: /app-1/*
port: 8080
- serviceName: app-2
path: /app-2/*
port: 8080
ingress.yaml
---
{{- range .Values.ingress.paths }}
- path: {{ .path }}
backend:
{{- $subchart := .serviceName -}}
serviceName: {{- include "$subchart.fullname" .}}
servicePort: {{ .port }}
{{- end }}
template: no template "$subchart.fullname" associated with template "gotpl"
helm 3.7 version has solved the problem
https://github.com/helm/helm/pull/9957.
You can use like this
{{ template "bar.fullname" .Subcharts.bar }}
How about hardcoded subchart name scoped by release ?
{{ .Release.Name }}-<subchart_name>
I have found that the best way to reference a service name is to override the template that they are using. There are some caveats to doing this however.
The subchart and your chart will have different contexts so they will most likely render the template differently
There are some things that are only available to the subchart
Most charts have a template similar to the one below in their _helpers.tpl file.
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "newchart.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
The subchart has different .Values to your chart. We will fix this when we render this template by creating a context that is similar to the subcharts context.
Instead of calling it with the . context we create a new context by replacing the .Values with the subcharts .Values.
{{ template "newchart.fullname" (set (deepCopy .) "Values" .Values.newchart }}
We use deepCopy so that we don't actually change the . context but rather create a new one to use.
The subchart has access to its own .Chart values that we can't replicate. In this case we will have to hardcode the value of .Chart.Name to the template. In this we can just replace it with the chart name newchart.
Once we have done this both nameOverride and fullnameOverride on the subchart will work without you having to manually change anything in your template files.
If the subchart uses the fullname function from _helpers.tpl (provided by helm by default for new charts) you can use this (replace postgresql with the name of the subchart):
{{- $fullName := include "postgresql.fullname" (mustMerge (dict "Chart" (dict "Name" "postgresql") "Values" .Values.postgresql) (deepCopy .)) -}}
It depends on the sub-chart definition!
As an example, elasticsearch chart, see here https://github.com/elastic/helm-charts/blob/master/elasticsearch/templates/service.yaml, is defining 2 services.
Both services name can be declared as value clusterName.