traefik-helm-chart icon indicating copy to clipboard operation
traefik-helm-chart copied to clipboard

feat(internal-service): adds an optional "internal" service

Open faust64 opened this issue 4 years ago • 7 comments

What does this PR do?

Introduces an optional internal Service, that could be used distinguishing "internal" accesses (eg: company LAN) from public accesses.

Motivation

As suggested by @muffl0n See https://github.com/traefik/traefik-helm-chart/issues/436

More

Additional Notes

N/A

faust64 avatar Jul 10 '21 10:07 faust64

Awesome! Can't wait to give it a try! Thank you! 😘

muffl0n avatar Jul 14 '21 17:07 muffl0n

Although this PR creates a new service that fronts Traefik, it doesn't provide any differentiator to Traefik that the request came through the internal service instead. If this is just a duplicate service, I'm not convinced it should be bundled in the chart, as it doesn't really provide any further functionality.

For example, if you have:

Service A (external) --> Traefik web EP
and
Service B (internal) --> Traefik web EP

Traefik won't know (or care) whether the request came from an external service. This would require additional entrypoints, and wiring the entrypoints to the same service ports forwarded by the internal service.

To accomplish this, you would require:

Service A (external) --> Traefik web EP
and
Service B (internal) --> Traefik web-internal EP

Thoughts?

dtomcej avatar Jul 14 '21 17:07 dtomcej

Ceates a new service that fronts Traefik, it doesn't provide any differentiator to Traefik that the request came through the internal service instead. If this is just a duplicate service,

I would disagree.

First, internal service is disabled by default. We don't duplicate anything.

Now, even if you just stick to the most simple example:

Service A (external) --> Traefik web EP
and
Service B (internal) --> Traefik web EP

This is one possibility, arguably. If I have two LB (metallb, ELB, ...) or one LB and a NodePort, or one NodePort and a ClusterIP, ... exposing the same endpoints doesn't mean you're exposing it to the same clients. By extension, exposing it with different services mean you may apply different firewalling/filtering.

Though for sure, trusting DNS resolution and selective firewalling would not prevent someone to query your public IPs, forging the host header of his request to access a service that would otherwise only be exposed using a private FQDN / private LB.

And then again, we could make that case for allowing connectivity from a subnet that doesn't have internet connectivity, yet is part of your LAN. Because I create two services pointing to the same stuff in Kubernetes doesn't always mean I want my webserver to handle those differently.

That being said, this PR already implements the case for:

Service A (external) --> Traefik web EP
and
Service B (internal) --> Traefik web-internal EP

Which is precisely what was requested from https://github.com/traefik/traefik-helm-chart/issues/436

This would be done with the following values:

ports:
  traefik:
   [...]
  web:
    port: 8000
    expose: true
    exposeInternal: true
    redirectTo: websecure
  webinternal:
    port: 8001
    expose: false
    exposeInternal: true
  [...]
  websecure:
    port: 8443
    expose: true
    exposeInternal: true
  [...]
  websecureinternal:
    port: 8444
    expose: false
    exposeInternal: true
  [...]

service:
  enabled: true
  annotations:
    networking.gke.io/internal-load-balancer-subnet: gke-services
  type: LoadBalancer
  internal:
    enabled: true
    annotations:
      networking.gke.io/internal-load-balancer-subnet: gke-services
      networking.gke.io/load-balancer-type: Internal
    loadBalancerSourceRanges:
    - 10.42.0.0/16
    type: LoadBalancer

faust64 avatar Jul 14 '21 18:07 faust64

I like that feature and I see the need of adding Internal Service. I run Traefik together with MetalLB and I have two IP pools: private and public. I also created additional entry points for Web internal, because some of the services need to reach Traefik internally. However, the Helm Chart provides only one service to expose ports. If I would like to expose e.g. port metrics and currently, I can only manually create a service and expose it locally by binding it to the proper MetalLB pool.

Here is the example service that I've created:

apiVersion: v1
kind: Service
metadata:
  annotations:
    metallb.universe.tf/address-pool: private-ips
  labels:
    app: traefik
spec:
  loadBalancerIP: 172.16.16.1
  ports:
  - name: web-int
    port: 80
    protocol: TCP
    targetPort: web-int
  - name: web-int-tls
    port: 443
    protocol: TCP
    targetPort: web-int-tls
  selector:
    app.kubernetes.io/instance: traefik-traefik
    app.kubernetes.io/name: traefik
  sessionAffinity: None
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 172.16.16.1

It would ideally be if Helm chart could create that service for me and I could decide what service the specific ports should be exposed to, whether internal or external.

Thanks for that contribution.

jakubhajek avatar Aug 30 '21 14:08 jakubhajek

Would this work with the Kubernetes Ingress provider? Trying to mimic this manually (create separate ports, service, etc), but it seems like even if I use the appropriate annotations to set the entrypoint, any ingresses that are created are assigned the status (load balancer IP/hostname) from the "public" service.

Based on what I currently have set up with nginx, I imagine one would still need to deploy two copies of traefik with different IngressClasses to achieve the same public/private split when using the Ingress provider.

somerandow avatar Sep 12 '21 13:09 somerandow

As you point out, you can annotate your Ingress designating which entrypoints should be used exposing their service -- while the ingressroute allows you do to this setting an array in its spec.

Sadly, I don't have a cluster with a cloud provider to test this with, if you know anything please share. Can anyone check?

if I use the appropriate annotations to set the entrypoint, any ingresses that are created are assigned the status (load balancer IP/hostname) from the "public" service.

In terms of traffic routing, as far as I've seen, entrypoints annotations are properly observed by Traefik. If my Ingress is annotated restricting its service to entrypoint A, then trying to query it through entrypoint B would not work, as expected.

Now, in terms of status, I am not sure. If there actually is an issue here, then I guess you should report this to https://github.com/traefik/traefik . There may be a bug or some edge case, within Traefik code itself. I don't think there is much to be done in Helm charts. Working mostly with bare metal, this isn't something I thought of, when patching that chart ... but if you're right, for sure it could look confusing.

Now, you could of course deploy two controllers. Use some IngressClasses, assigning your ingresses to either or both Traefik deployments. With Nginx, I would definitely do this. With Traefik, not necessarily. Though your security department could argue for it. The main argument IMHO would be: how likely it is, that an user may create an ingress exposing private data through a public endpoint, by mistake or maliciously.

faust64 avatar Sep 14 '21 18:09 faust64

Based on this, it seems like its a limitation of Traefik itself/Ingress in general. All ingresses for a given Ingress class get pointed a single endpoint. Deploying two instances of Traefik (each with their own IngressClass) seems to work like a charm however. Thanks for taking a look!

somerandow avatar Sep 16 '21 02:09 somerandow

Superseded by #683

mloiseleur avatar Oct 27 '22 14:10 mloiseleur