feat(internal-service): adds an optional "internal" service
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
- [X] Yes, I updated the chart version
Additional Notes
N/A
Awesome! Can't wait to give it a try! Thank you! 😘
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?
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
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.
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.
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.
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!
Superseded by #683