kubernetes / kubernetes

Production-Grade Container Scheduling and Management
https://kubernetes.io
Apache License 2.0
110.62k stars 39.55k forks source link

Ingress: Allow for multiple hosts #43633

Closed klausenbusk closed 5 years ago

klausenbusk commented 7 years ago

Is this a request for help? No

What keywords did you search in Kubernetes issues before filing this one? Ingress controller, "hosts", multiple host.

I found this issue: https://github.com/kubernetes/ingress/issues/87 but it was closed as it was in the wrong repo.


Is this a BUG REPORT or FEATURE REQUEST? (choose one): FEATURE REQUEST

With the current implementation, if you have a few sub-/domains which need to point to the same service, you get a "lot" of duplicated code.

Example:

spec:
  rules:
  - host: foobar.com
    http:
      paths:
      - backend:
          serviceName: foobar
          servicePort: 80
  - host: api.foobar.com
    http:
      paths:
      - backend:
          serviceName: foobar
          servicePort: 80
  - host: admin.foobar.com
    http:
      paths:
      - backend:
          serviceName: foobar
          servicePort: 80
  - host: status.foobar.com
    http:
      paths:
      - backend:
          serviceName: foobar
          servicePort: 80

So I propose that the Host field get changed from a single FQDN to a array of FQDN. So I could do something like:

spec:
  rules:
  - host: ["foobar.com", "api.foobar.com", "admin.foobar.com", "status.foobar.com"]
    http:
      paths:
      - backend:
          serviceName: foobar
          servicePort: 80

It save me 18 lines and makes the config more clear.

Maybe dup of: https://github.com/kubernetes/kubernetes/issues/41881 , but this seems easier to implement.

cc @aledbf (you closed the last issues, not sure if you "work" in this part of k8s)

Kubernetes version (use kubectl version):

Client Version: version.Info{Major:"1", Minor:"5", GitVersion:"v1.5.4", GitCommit:"7243c69eb523aa4377bce883e7c0dd76b84709a1", GitTreeState:"clean", BuildDate:"2017-03-07T23:53:09Z", GoVersion:"go1.7.4", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"5", GitVersion:"v1.5.4+coreos.0", GitCommit:"97c11b097b1a2b194f1eddca8ce5468fcc83331c", GitTreeState:"clean", BuildDate:"2017-03-08T23:54:21Z", GoVersion:"go1.7.4", Compiler:"gc", Platform:"linux/amd64"}

Environment: DigitalOcean, CoreOS.

deitch commented 7 years ago

+1 for this as well. As referenced by both @klausenbusk and me at https://github.com/kubernetes/kubernetes/issues/41881 , this would go a long way.

I prefer to have a wildcard or a subdomain-only option, so I can specify host: web.* or host: api.web.* and have anything that starts with that pattern pass through, leaving the service ignorant as to its final domain (is it running in .example.com? .prod.example.com? .beta.example.com? .it.internal?), but anything that moves it past the current monolithic "must list entire single domain in host: is a big step forward.

Personally, I also think the ingress controller (which usually is deployed by cluster admins) should have a restriction that says, "only service the following domains or subdomains of them...", but that is a separate issue.

Markbnj commented 7 years ago

Another +1. I think almost anyone with a non-trivial ingress use-case will benefit from this.

jbelinchonm commented 7 years ago

+1

fejta-bot commented 6 years ago

Issues go stale after 90d of inactivity. Mark the issue as fresh with /remove-lifecycle stale. Stale issues rot after an additional 30d of inactivity and eventually close.

Prevent issues from auto-closing with an /lifecycle frozen comment.

If this issue is safe to close now please do so with /close.

Send feedback to sig-testing, kubernetes/test-infra and/or @fejta. /lifecycle stale

deitch commented 6 years ago

/remove-lifecycle stale

deitch commented 6 years ago

Apparently, I cannot remove it from stale...

dims commented 6 years ago

looks like it worked @deitch !

kramarz commented 6 years ago

I also would like to see this feature, but as a workaround, I use YAML ids. Here is how it would look for given example.

spec:
  rules:
  - host: foobar.com
    http: &http_rules
      paths:
      - backend:
          serviceName: foobar
          servicePort: 80
  - host: api.foobar.com
    http: *http_rules
  - host: admin.foobar.com
    http: *http_rules
  - host: status.foobar.com
    http: *http_rules
gtirloni commented 6 years ago

I don't have much to add except to say this would be very welcome. Hopefully the rules for beta APIs still allow some flexibility to modify this.

alex-sainer commented 6 years ago

same here - have to add multiple domains i.e.

*.foobar.com
*.foobar.net
*.foobar.biz

what is not really comfortable in the current implementation...

so, vote up...

fejta-bot commented 6 years ago

Issues go stale after 90d of inactivity. Mark the issue as fresh with /remove-lifecycle stale. Stale issues rot after an additional 30d of inactivity and eventually close.

If this issue is safe to close now please do so with /close.

Send feedback to sig-testing, kubernetes/test-infra and/or fejta. /lifecycle stale

gtirloni commented 6 years ago

/remove-lifecycle stale

aclowkey commented 6 years ago

This is much needed :((

shubb30 commented 6 years ago

+1 for this feature.

I also tried @kramarz suggested workaround, and I get the following error: Error: YAML parse error on mysite-web/templates/ingress.yaml: error converting YAML to JSON: yaml: line 4: mapping values are not allowed in this context

gexinworks commented 6 years ago

+1 for this feature.

fejta-bot commented 5 years ago

Issues go stale after 90d of inactivity. Mark the issue as fresh with /remove-lifecycle stale. Stale issues rot after an additional 30d of inactivity and eventually close.

If this issue is safe to close now please do so with /close.

Send feedback to sig-testing, kubernetes/test-infra and/or fejta. /lifecycle stale

deitch commented 5 years ago

/remove-lifecycle stale

aelmanaa commented 5 years ago

feature needed indeed

alexeldeib commented 5 years ago

Use case I haven't seen yet, we have some domains that look like api[0-9].domain.io -- existing wildcard domains don't cover this case. The set is small enough to manage manually using yaml IDs, but would be ideal to not need that fallback (e.g., regex domain names)

ayyazzafar commented 5 years ago

We really need this feature

mkstephenson commented 5 years ago

Another vote for this feature

ar4hc commented 5 years ago

another work around using range loop:

rules:
          {{- range .Values.ingress.hosts }}
    - host: {{ . }}
      http:
        paths:
          - path: /
            backend:
              serviceName: {{ $fullName }}
              servicePort: 80
              {{- end }}

and in values yaml:

ingress:
  hosts:
    - "bla.blub"
    - "foo.bar"

and set a variable fullName...

Still i'd prefer an array or regex or ... for the hosts element

thockin commented 5 years ago

This is almost certainly not happening in Ingress , but we should keep it in mind for the API to follow Ingress (proposals coming soon?)

deitch commented 5 years ago

API to follow Ingress, @thockin? Is there a replacement in the works?

HighwayofLife commented 5 years ago

This is almost certainly not happening in Ingress

No? Seems a frequently requested feature to avoid a lot of duplication. -- What's our alternatives?

catalinpan commented 5 years ago

Might be useful for someone, I use server-alias option on the nginx ingress to have multiple domains for a single ingress.

angelogwapo commented 5 years ago

Might be useful for someone, I use server-alias option on the nginx ingress to have multiple domains for a single ingress.

@catalinpan how does it work on your end? My server-alias doesn't work with this config.

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: foobar-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/server-alias: "two-app.foobar.dev"
spec:
  tls:
  - secretName: tls-secret
  rules:
  - host: "one-app.foobar.dev"
    http:
      paths:
      - backend:
          serviceName: foobar
          servicePort: 80
catalinpan commented 5 years ago

@angelogwapo is worth checking if the nginx ingress version you have supports the alias implementation. If the version is newer check here how the rewrite-target syntax should look like. There is a case where your alias will not work if there is another ingress which uses the same domain, here is the explanation.

As long as the above case doesn't apply to you and the DNS record for two-app.foobar.dev points to the same load balancer as one-app.foobar.dev your example should work.

I manage the server alias in Route 53 using terraform because of my specific use case, multiple deployments in different regions = multiple server alias CNAMEs pointing to multiple hosts with health checks and few more options.

example: CNAME two-app.foobar.dev --> one-app-ew1.foobar.dev CNAME two-app.foobar.dev --> one-app-ase1.foobar.dev

Below is an example of something I use.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/server-alias: public_url
  name: app1
spec:
  tls:
  - hosts:
    - public_region_url
      secretName: my_wildcard
  rules:
    - host: public_region_url
      http:
        paths:
          - path: /
            backend:
              serviceName: app1
              servicePort: 80
FabianSperrle commented 4 years ago

This feature would be great to have for users of traefik who are also using the automated Let's Encrypt feature. Here, traefik expects a comma-separated list of domains as the Host (https://docs.traefik.io/v1.7/configuration/acme/#onhostrule), where the first domain will become the main domain of the certificate and all others will be SANs.

jacqinthebox commented 4 years ago

@catalinpan I am also struggling with the server-alias annotation. I have almost the same config as @angelogwapo. I have two domains:

Both are pointing to the same loadbalancer. Both have a valid TLS certificate. Below my config. I get directed to the correct page, but the TLS certificate is only valid for the domain mentioned as the host. When I browse to the other domain, I am presented the Kubernetes Ingress Controller Fake Certificate. And vice versa. Any help is appreciated...

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    app.kubernetes.io/instance: ngress-rules
    cert-manager.io/issuer: letsencrypt-prod
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/server-alias: two-app.two-domain.dev
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
  name: ingress-rules
  namespace: default
spec:
  rules:
  - host: one-app.one-domain.dev
    http:
      paths:
      - backend:
          serviceName: service1
          servicePort: 80
        path: /
      - backend:
          serviceName: service2
          servicePort: 80
        path: /portal/
  tls:
  - hosts:
    - one-app.one-domain.dev
    secretName: one-app-tls
  - hosts:
    - two-app.two-domain.dev
    secretName: two-app-tls

Btw, I am using version 0.27 which should contain this change: https://github.com/kubernetes/ingress-nginx/pull/4472/files#diff-9bba411a7c28f1ef63c3a5339db109d5

anton-johansson commented 4 years ago

@jacqinthebox: Your Ingress configuration actually only configures one host (one-app.one-domain.dev). Sure, your tls configures both of them, but you still need one rule for each domain. Specifically see the array under spec.rules. Currently, there is nothing that tells NGINX which service that the 2nd domain should proxy to.

EDIT: My bad, I was too quick. I didn't realize you were talking about the server alias annotation.

voron commented 4 years ago

@jacqinthebox

Both are pointing to the same loadbalancer. Both have a valid TLS certificate.

you may use multiple independent domains in one TLS secret

  - hosts:
    - one-app.one-domain.dev
    - two-app.two-domain.dev
    secretName: all-app-tls
ioMatrix commented 4 years ago

You can always use a helm chart to create the dup config for each domain in an array. "If" you are using helm of course :)

owenson commented 4 years ago

+1

shenmou98 commented 4 years ago

@kramarz , Thanks, this workround work for me.

AndreKoepke commented 4 years ago

The workaround works also perfectly with https://github.com/jetstack/cert-manager .
Here is my example

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
   name: site
   annotations:
      cert-manager.io/issuer: letsencrypt-prod
      nginx.ingress.kubernetes.io/server-alias: host2.com
spec:
   rules:
   - host: host1.com
     http:
      paths:
      - path: /
        backend:
           serviceName: site
           servicePort: 8080
   tls:
   - secretName: nice-name
     hosts:
     - host1.com
     - host2.com
benjick commented 4 years ago

@AndreKoepke thank you for this. It worked somewhat for me but after adding domain.com to spec.rules[0].host and www.domain.com,domain.se,www.domain.se to nginx.ingress.kubernetes.io/server-alias I get the following error: controller.go:1155] Conflicting hostname (domain.com) and alias (www.domain.se). Removing alias to avoid conflicts.

Any suggestions?

ulfgebhardt commented 3 years ago
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
  name:  ingress-{{ .Release.Name }}-webapp
  labels:
    app.kubernetes.io/name:       "{{ .Chart.Name }}"
    app.kubernetes.io/instance:   "{{ .Release.Name }}"
    app.kubernetes.io/version:    "{{ .Chart.AppVersion }}"
    app.kubernetes.io/component:  "ingress-webapp"
    app.kubernetes.io/part-of:    "{{ .Chart.Name }}"
    app.kubernetes.io/managed-by: "{{ .Release.Service }}"
    helm.sh/chart:                "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}"
  annotations:
    kubernetes.io/ingress.class: "nginx"
    cert-manager.io/cluster-issuer: {{ .Values.LETSENCRYPT.ISSUER }}
    nginx.ingress.kubernetes.io/proxy-body-size: {{ .Values.NGINX.PROXY_BODY_SIZE }}
spec:
  tls:
    - hosts:
    {{- range .Values.LETSENCRYPT.DOMAINS }}
      - {{ . }}
    {{- end }}
      secretName: tls
  rules:
    {{- range .Values.LETSENCRYPT.DOMAINS }}
    - host: {{ . }}
      http:
        paths:
          - path: /
            pathType: ImplementationSpecific
            backend:
              service:
                name: {{ $.Release.Name }}-webapp
                port:
                  number: 3000
    {{- end }}
LETSENCRYPT:
  ...
  DOMAINS:
    - "wir.social"
    - "www.wir.social"
dstibbe commented 3 years ago

I also would like to see this feature, but as a workaround, I use YAML ids. Here is how it would look for given example.

spec:
  rules:
  - host: foobar.com
    http: &http_rules
      paths:
      - backend:
          serviceName: foobar
          servicePort: 80
  - host: api.foobar.com
    http: *http_rules
  - host: admin.foobar.com
    http: *http_rules
  - host: status.foobar.com
    http: *http_rules

this is the way :)

Brianm0823 commented 1 year ago

The workaround works also perfectly with https://github.com/jetstack/cert-manager . Here is my example

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
   name: site
   annotations:
      cert-manager.io/issuer: letsencrypt-prod
      nginx.ingress.kubernetes.io/server-alias: host2.com
spec:
   rules:
   - host: host1.com
     http:
      paths:
      - path: /
        backend:
           serviceName: site
           servicePort: 8080
   tls:
   - secretName: nice-name
     hosts:
     - host1.com
     - host2.com

Does host2.com return a valid cert or do you get "Kubernetes Ingress Controller Fake Certificate"? How did you combine them so both domains use the same secret

Brianm0823 commented 1 year ago

@catalinpan I am also struggling with the server-alias annotation. I have almost the same config as @angelogwapo. I have two domains:

* one-app.one-domain.dev

* two-app.two-domain.dev

Both are pointing to the same loadbalancer. Both have a valid TLS certificate. Below my config. I get directed to the correct page, but the TLS certificate is only valid for the domain mentioned as the host. When I browse to the other domain, I am presented the Kubernetes Ingress Controller Fake Certificate. And vice versa. Any help is appreciated...

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    app.kubernetes.io/instance: ngress-rules
    cert-manager.io/issuer: letsencrypt-prod
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/server-alias: two-app.two-domain.dev
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
  name: ingress-rules
  namespace: default
spec:
  rules:
  - host: one-app.one-domain.dev
    http:
      paths:
      - backend:
          serviceName: service1
          servicePort: 80
        path: /
      - backend:
          serviceName: service2
          servicePort: 80
        path: /portal/
  tls:
  - hosts:
    - one-app.one-domain.dev
    secretName: one-app-tls
  - hosts:
    - two-app.two-domain.dev
    secretName: two-app-tls

Btw, I am using version 0.27 which should contain this change: https://github.com/kubernetes/ingress-nginx/pull/4472/files#diff-9bba411a7c28f1ef63c3a5339db109d5

What was the fix for this? I cannot get it to return anything but "Kubernetes Ingress Controller Fake Certificate" for the aliased domain.