49

Say I have a service that isn't hosted on Kubernetes. I also have an ingress controller and cert-manager set up on my kubernetes cluster.

Because it's so much simpler and easy to use kubernetes ingress to control access to services, I wanted to have a kubernetes ingress that points to a non-kubernetes service.

For example, I have a service that's hosted at https://10.0.40.1:5678 (ssl required, but self signed certificate) and want to access at service.example.com.

cclloyd
  • 8,171
  • 16
  • 57
  • 104
  • 2
    You can try setting up a headless service (https://kubernetes.io/docs/concepts/services-networking/service/#headless-services) for the external service, and have the ingress forward traffic to that service. I never tried such a thing though. – Burak Serdar Sep 03 '19 at 02:14
  • 1
    Could you describe a lot more of what you'd like to accomplish? Do you want this to work for requests coming from workloads running within the K8s cluster, requests coming from the public Internet, both? Should clients have to request `service.example.com:5678` or just `https://service.example.com` which most browsers, tools, and libraries automatically treat as port 443? Do you expect clients to be able to properly verify the cert that is presented or do you just want SSL to encrypt the traffic (i.e. not worried about MITM and other attacks)? – Amit Kumar Gupta Sep 03 '19 at 04:57
  • 1
    @AmitKumarGupta my goal was just to consolidate my ingress and certificate management. I already use cert-manager to issue LE certs. So I wanted to be able to do that for non k8s services as well. Also I want it to proxy from 443 to whatever port is required, no typing port. – cclloyd Sep 03 '19 at 08:12

5 Answers5

50

You can do it by manual creation of Service and Endpoint objects for your external server.

Objects will looks like that:

apiVersion: v1
kind: Service
metadata:
  name: external-ip
spec:
  ports:
  - name: app
    port: 80
    protocol: TCP
    targetPort: 5678
  clusterIP: None
  type: ClusterIP
---
apiVersion: v1
kind: Endpoints
metadata:
  name: external-ip
subsets:
- addresses:
  - ip: 10.0.40.1
  ports:
  - name: app
    port: 5678
    protocol: TCP

Also, it is possible to use an EndpointSlice object instead of Endpoints.

Then, you can create an Ingress object which will point to Service external-ip with port 80:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: external-service
spec:
  rules:
  - host: service.example.com
    http:
      paths:
      - backend:
          serviceName: external-ip
          servicePort: 80
        path: /
Anton Kostenko
  • 8,200
  • 2
  • 30
  • 37
  • 1
    What if I need to connect to the non-kubernetes backend with SSL over non standard port? (In this case, Proxmox, so HTTPS over 8006. – cclloyd Sep 08 '19 at 09:39
  • You can set a backend port to 8006. SSL certificate is not sensitive to a port number, so if your SSL certificate will be valid for a service name (or it's IP) you can connect to a service without any issues, otherwise you will not be able to validate an SSL cert (which not always a problem, you can always ignore it for a testing purposes). That scheme works on a TCP level, so it will not effect levels above it (like HTTP/TLS etc). – Anton Kostenko Sep 08 '19 at 17:13
  • The "Endpoints" object is the key I was looking for to host apps I hosted on my locahost and serve up locally running apps that I previously hosted via a standalone nginx installation.The addresses.ip I used, was created via a Microk8s "host-access" feature, which posts an IP 10.0.1,1 for the host-machine that is accessible by the cluster. This allows me to deprecate the nginx, and serve via kubernetes so I can incrementally containerize and migrate apps into k8s managed deployments. – Matthew Dowell Mar 28 '23 at 03:09
13

So I got this working using ingress-nginx to proxy an managed external service over a non-standard port

apiVersion: v1
kind: Service
metadata:
  name: external-service-expose
  namespace: default
spec:
  type: ExternalName
  externalName: <external-service> # eg example.example.com
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: external-service-expose
  namespace: default
  annotations:
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" #important
spec:
  rules:
  - host: <some-host-on-your-side> # eg external-service.yourdomain.com
    http:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: external-service
            port:
              number: <port of external service> # eg 4589
  tls:
  - hosts:
    - external-service.yourdomain.com
    secretName: <tls secret for your domain>

of-course you need to make sure that the managed url is reachable from inside the cluster, a simple check can be done by launching a debug pod and doing

curl -v https://example.example.com:4589
Moulick
  • 4,342
  • 1
  • 13
  • 19
  • Just tried this with `nginx.ingress.kubernetes.io/rewrite-target: /dev/$2` but doesn't work. – Antoine Jan 21 '21 at 13:45
  • 1
    I think this is missing the map key `spec.rules.http.paths` before the array containing the `paths` (in this case `/`). Otherwise very helpful, thanks! – afirth Aug 08 '22 at 09:20
9

If your external service has a dns entry configured on it, you can use kubernetes externalName service.

---
apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: myexternal.http.service.com
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: externalNameservice
  namespace: prod
spec:
  rules:
  - host: service.example.com
    http:
      paths:
      - backend:
          serviceName: my-service
          servicePort: 80
        path: /

In this way, kubernetes create cname record my-service pointing to myexternal.http.service.com

c4f4t0r
  • 1,563
  • 15
  • 24
  • 1
    CNAME is not going to help because the problem requires translating the port from 80 to 5678. – Amit Kumar Gupta Sep 03 '19 at 09:59
  • @AmitKumarGupta yes, ExternalName doesn't translate ports, but I prefer to avoid to point directly to the ip, if someone change the ip, your service will stop to work – c4f4t0r Sep 03 '19 at 10:08
  • @c4f4t0r Yes I prefer avoiding directly pointing to the ip but Can you say how to handle SSL on port 443 using your method? – 0xMH Feb 03 '21 at 14:44
  • 1
    Beware that this [does not work with HA Proxy Ingress Controller](https://github.com/haproxytech/kubernetes-ingress/issues/100) – Peter V. Mørch Dec 17 '21 at 15:53
8

I just want to update @Moulick answer here according to Kubernetes version v1.21.1, as for ingress the configuration has changed a little bit. In my example I am using Let's Encrypt for my nginx controller:

apiVersion: v1
kind: Service
metadata:
  name: external-service
  namespace: default
spec:
  type: ExternalName
  externalName: <some-host-on-your-side> eg managed.yourdomain.com
  ports:
  - port: <port of external service> eg 4589

---
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
  name: external-service
  namespace: default
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    nginx.ingress.kubernetes.io/proxy-body-size: 100m
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" #important
spec:
  tls:
  - hosts:
    - <some-host-on-your-side> eg managed.yourdomain.com
    secretName: tls-external-service
  rules:
  - host: <some-host-on-your-side> eg managed.yourdomain.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: external-service
            port:
              number: <port of external service> eg 4589
Ralph
  • 4,500
  • 9
  • 48
  • 87
1

Here's a working copy of my configuration for Kubernetes 1.26 with the EndpointSlice approach and NGINX Ingress 1.7 :

apiVersion: v1
kind: Service
metadata:
  name: external-service
spec:
  ports:
  - name: https
    port: 5678
    targetPort: 5678
---
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: external-service-1
  labels:
    kubernetes.io/service-name: external-service
addressType: IPv4
ports:
  - name: ''
    appProtocol: http
    protocol: TCP
    port: 5678
endpoints:
  - addresses:
      - "10.0.40.1"
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: external-service
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
  rules:
  - host: service.example.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: external-service
            port:
              number: 5678
secavfr
  • 628
  • 1
  • 9
  • 24