21
votes

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.

4
You can try setting up a headless service (kubernetes.io/docs/concepts/services-networking/service/…) for the external service, and have the ingress forward traffic to that service. I never tried such a thing though. - Burak Serdar
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
@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

4 Answers

24
votes

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

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: /
5
votes

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

2
votes

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

apiVersion: v1
kind: Service
metadata:
  name: managed-expose
  namespace: default
spec:
  type: ExternalName
  externalName: <external-service> eg example.example.com
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: managed-expose
  namespace: default
  annotations:
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" #important
spec:
  rules:
  - host: <some-host-on-your-side> eg managed.yourdomain.com
    http:
      paths:
      - backend:
          serviceName: managed-expose
          servicePort: <port of external service> eg 4589
        path: /
  tls:
  - hosts:
    - managed.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 like

curl -v https://example.example.com:4589
0
votes

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