Kubernetes Loadbalancer redirecting to HTTPs when application is on port 80 - kubernetes

I've deployed a series of deployments and services to a Kubernetes cluster with a load balancer. When I try to access my app this does not work as my application is exposed on port 80 but the URL is always redirected to port 443 (HTTPS). I suspect this is to do with the fact that the cluster IP is on port 443.
Any ideas on how I can fix this?
db NodePort 10.245.175.203 <none> 5432:30029/TCP 25m
kubernetes ClusterIP 10.245.0.1 <none> 443/TCP 8m
redis NodePort 10.245.197.157 <none> 6379:31277/TCP 25m
web LoadBalancer 10.245.126.122 123.12.123.123 80:31430/TCP 25m

This is likely due to your application itself redirecting to port 443. What type of application is it?
This service exposed on port 443 has nothing to do with your application:
kubernetes ClusterIP 10.245.0.1 <none> 443/TCP 8m
It's basically an internal service that allows you to access the kube-apiserver within your cluster.
You could try just setting up the LoadBalancer to listen on port 443 directly. Only you would have to port 80 traffic wouldn't work. If you want the port 80 redirects to work I suggest you use an Ingress controller like nginx. Something like this:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: your-ingress
annotations:
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
tls:
- hosts:
- yourhostname.com
secretName: tls-secret
rules:
- host: yourhostname.com
http:
paths:
- path: /
backend:
serviceName: web
servicePort: 443
You will also have to create a TLS secret holding your cert and key:
$ kubectl create secret tls tls-secret --key /tmp/tls.key --cert /tmp/tls.crt

Related

istio unable to access kubernetes dashboard

I am trying to access the Kubernetes Dashboard through an Istio Gateway + Virtual Service.
However, all I get is 404 page not found when I try to access the dashboard with browser. Accessing the Dashboard through k8s NodePort or k8s LoadBalancer service works just as expected. The pod, however, complains in the logs about http: TLS handshake error from 127.0.0.6:52483: remote error: tls: bad certificate.
Running httpbin through Istio (as given in their documentation) works as expected, so Istio seem to be working fine as well.
I am using the official Kubernetes Dashboard YAML-s. I am giving the service below (with type: LoadBalancer added, although it doesn't seem to make a difference for Istio, although it allows me to access the Dashboard through a separate IP).
Just for the record, my k8s cluster is comprised of VirtualBox machines running MetalLB.
kubectl get services --all-namespaces returns the following:
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 11d
httpbin httpbin ClusterIP 10.100.186.188 <none> 8000/TCP 47h
istio-system istio-egressgateway ClusterIP 10.109.231.163 <none> 80/TCP,443/TCP 5d3h
istio-system istio-ingressgateway LoadBalancer 10.111.188.94 192.168.56.46 15021:31440/TCP,80:31647/TCP,443:32715/TCP 5d3h
istio-system istiod ClusterIP 10.104.236.247 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 5d3h
kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 11d
kubernetes-dashboard dashboard-metrics-scraper ClusterIP 10.101.131.136 <none> 8000/TCP 43h
kubernetes-dashboard kubernetes-dashboard-service LoadBalancer 10.103.130.244 192.168.56.47 443:30041/TCP 43h
kubernetes-dashboard kubernetes-dashboard-service-np NodePort 10.100.49.224 <none> 8443:30002/TCP 43h
If I try to access the LoadBalancer directly via the IP from above and through browser, I get the usual Kubernetes Dashboard login page. The browser url is https://192.168.56.47.
YAML-s:
istio-gateway.yaml:
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: kubernetes-dashboard-gateway
namespace: kubernetes-dashboard
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: PASSTHROUGH
hosts:
- "*"
istio-virtual-service.yaml:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: kubernetes-dashboard-virtual-service
namespace: kubernetes-dashboard
spec:
hosts:
- "*"
gateways:
- kubernetes-dashboard-gateway
tls:
- match:
- sniHosts: ["*"]
route:
- destination:
host: kubernetes-dashboard-service
port:
number: 443
dashboard-service.yaml:
kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard-service
namespace: kubernetes-dashboard
spec:
ports:
- port: 443
targetPort: 8443
# - port: 8000
# targetPort: 9090
selector:
k8s-app: kubernetes-dashboard
type: LoadBalancer
User suren has mentioned:
your gateway is listening 443. not 80
Yes, this could be a problem. You are trying to reach port 80, but you are exposing only port 443. Try to change your configuration or change your port during request.
See albo documentation about Deploy and Access the Kubernetes Dashboard.
Hm, I got it working with the configuration as above and with explicitly specifying a host in all places where I have previously placed a "*". I had to add that host in /etc/hosts to be able to access it in browser.
It seems that this last part was key, as well as specifying the sniHost in the Virtual Service. The other problems were mostly configuration issues with the TLS. Setting it to PASSTHROUGH seems to work, because it forces Istio to sort of forward the HTTPS request to the Kubernetes Dashboard, which is responsible for decrypting etc.

Exposing kubernetes Dashboard with clusterIP service externally using Ingress rules

I am trying to expose kubernetes-dashboard app externally using Ingress resource. I have installed Nginx Controller and a service called Kubernetes-dashboard is clusterIP type service with port 443.
I have created Ingress resource with YAML file and pointing to backend service which is kubernetes-dashboard but somehow I am not getting the IP address of my host (dashboard.com) so that I can add this entry in /etc/hosts file. what is the resolution here. I am not able to paste the yaml file here as this website complain about code formatting.
I tried to put YAML file here in various ways but it does not work.
yaml file of kubernetes-dashboard as below:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: dashboard-ingress namespace: kubernetes-dashboard spec: tls: - hosts: - dashboard.com secretName: kubernetes-dashboard-certs rules: - host: dashboard.com http: paths: - pathType: ImplementationSpecific path: / backend: service: name: kubernetes-dashboard port: number: 443
Kubernetes-dashboard service config as below: Name: kubernetes-dashboard Namespace: kubernetes-dashboard Labels: k8s-app=kubernetes-dashboard Annotations: Selector: k8s-app=kubernetes-dashboard Type: ClusterIP IP Family Policy: SingleStack IP Families: IPv4 IP: 10.106.1.186 IPs: 10.106.1.186 Port: 443/TCP TargetPort: 8443/TCP Endpoints: 10.44.0.3:8443 Session Affinity: None Events:
I am not getting the IP address of my host
You have to use the Nginx ingress controller service IP everywhere so traffic gets forwarded and managed via Nginx ingress.
you can check the IP of Nginx controller using the
Kubectl get svc -n ingress-nginx
Nginx controller service will be exposed as the type LoadBalancer you can use this IP into the DNS route as A or CNAME record.
Any request coming to your domain will get forwarded to
ingress > Nginx ingress controller > K8s service > K8s PODs

Connection refused when calling ingress from pod

I'm running a .NET 5 API application on Kubernetes as well as Identity Server in separate pods on my laptop using Docker Desktop. Both applications are made available for consumers using the nginx ingress controller. When I call the ingress in the browser from outside of the cluster, it works well. Also, when I run the API from Visual Studio, the connection to Identity Server via the ingress is successful.
But when I deploy the API to Kubernetes, the pod that runs the API can't connect to the ingress. The API uses the ingress to call Identity Server because it has to happen over https. SSL is terminated by the ingress and forwarding traffic to the pods over http.
The ingress address to IdentityServer is https://k8s-local.com/identityserver. From the API pod, the DNS is known; when I execute a ping, it goes ok. When I perform a curl command to this address from the pod, I'm getting a Connection Refused error:
curl: (7) Failed to connect to k8s-local.com port 443: Connection refused
How do I make the ingress DNS available to the pods?
This is what my ingress.yml looks like:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
spec:
tls:
- hosts:
- k8s-local.com
secretName: local-tls
rules:
- host: k8s-local.com
http:
paths:
- pathType: Prefix
path: /identityserver/
backend:
service:
name: identityserver-service
port:
number: 5000
- pathType: Prefix
path: /customer-api/
backend:
service:
name: customer-api-service
port:
number: 5000
This is what I get when I run kubectl get services -n ingress-nginx:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.109.3.50 localhost 80:31964/TCP,443:32210/TCP 11d
ingress-nginx-controller-admission ClusterIP 10.105.158.51 <none> 443/TCP 11d
And kubectl get ingress:
NAME CLASS HOSTS ADDRESS PORTS AGE
my-ingress <none> k8s-local.com localhost 80, 443 6d11h

Kubernetes Ingress Flask Application

I have a simple demo Flask application that is deployed to kubernetes using minikube. I am able to access the app using the Services. But I am not able to connect using ingress.
Services.yaml
apiVersion: v1
kind: Service
metadata:
name: services-app-service
spec:
selector:
app: services-app
type: ClusterIP
ports:
- protocol: TCP
port: 5000 # External connection
targetPort: 5000 # Internal connection
D:Path>kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
db ClusterIP None <none> 3306/TCP 120m
kubernetes ClusterIP 10.20.30.1 <none> 443/TCP 3h38m
services-app-service ClusterIP 10.20.30.40 <none> 5000/TCP 18m
I am able to access the app using minikube.
D:Path>minikube service services-app-service --url
* service default/services-app-service has no node port
* Starting tunnel for service services-app-service.
|-----------|----------------------|-------------|------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|----------------------|-------------|------------------------|
| default | services-app-service | | http://127.0.0.1:50759 |
|-----------|----------------------|-------------|------------------------|
http://127.0.0.1:50759
! Because you are using a Docker driver on windows, the terminal needs to be open to run it.
Ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: services-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: mydemo.info
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: services-app-service
port:
number: 5000
D:Path>kubectl get ing
NAME CLASS HOSTS ADDRESS PORTS AGE
services-ingress <none> mydemo.info 192.168.40.1 80 15m
Are there any additional configuration required to access the app via ingress?
The ingress, and ingress-dns addons are currently only supported on Linux. Currently not supported on windows.
MoreInfo
Not Supported on Windows:
minikube version: v1.16.0
minikube version: v1.17.1
The issue is that you need to access it with a Host head of mydemo.info for that Ingress spec to work. You also need to confirm you have an Ingress Controller installed, usually ingress-nginx for new users but there are many options. Then you would look for the Ingress Controllers NodePort or LoadBalancer service and access through that.
I've been searching for ages. I confirm this doesn't work on MacOS either.
Using minikube tunnel is the only way I found.

Expose service on local kubernetes

I'm running a local kubernetes bundled with docker on Mac OS.
How can I expose a service, so that I can access the service via a browser on my Mac?
I've created:
a) deployment including apache httpd.
b) service via yaml:
apiVersion: v1
kind: Service
metadata:
name: apaches
spec:
selector:
app: web
type: NodePort
ports:
- protocol: TCP
port: 80
externalIPs:
- 192.168.1.10 # Network IP of my Mac
My service looks like:
$ kubectl get service apaches
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
apaches NodePort 10.102.106.158 192.168.1.10 80:31137/TCP 14m
I can locally access the service in my kubernetes cluster by wget $CLUSTER-IP
I tried to call http://192.168.1.10/ on my Mac, but it doesn't work.
This question deals to a similar issue. But the solution does not help, because I do not know which IP I can use.
Update
Thanks to Michael Hausenblas I worked out a solution using Ingress.
Nevertheless there are still some open questions:
What is the meaning of a service's externalIP? Why do I need an externalIP when I do not directly access a service from external?
What is the meaning of the service port 31137?
The kubernetes docs describe a method to [publish a service in minikube via NodePort][4]. Is this also possible with kubernetes bundled on docker?
There are several solutions to expose services in kubernetes:
http://alesnosek.com/blog/2017/02/14/accessing-kubernetes-pods-from-outside-of-the-cluster/
Here are my solutions according to alesnosek for a local kubernetes bundled with docker:
1. hostNetwork
hostNetwork: true
Dirty (the host network should not be shared for security reasons) => I did not check this solution.
2. hostPort
hostPort: 8086
Does not apply to services => I did not check this solution.
3. NodePort
Expose the service by defining a nodePort:
apiVersion: v1
kind: Service
metadata:
name: apaches
spec:
type: NodePort
ports:
- port: 80
nodePort: 30000
selector:
app: apache
4. LoadBalancer
EDIT
#MathObsessed posted the solution in his anwer.
5. Ingress
a. Install Ingress Controller
git clone https://github.com/jnewland/local-dev-with-docker-for-mac-kubernetes.git
kubectl apply -f nginx-ingress/namespaces/nginx-ingress.yaml -Rf nginx-ingress
b. Configure Ingress
kubectl apply -f apache-ing.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: apache-ingress
spec:
rules:
- host: localhost
http:
paths:
- path: /
backend:
serviceName: apaches
servicePort: 80
Now I can access my apache deployed with kubernetes by calling http://localhost/
Remarks for using local-dev-with-docker-for-mac-kubernetes
The repo simplifies the deployment of the offical ingress-nginx controller
For production use I would follow the official guide.
The repos ships with a tiny full featured ingress example. Very useful for getting quickly a working example application.
Further documentation
https://kubernetes.io/docs/concepts/services-networking/ingress
For those still looking for an answer. I've managed to achieve this by adding another Kube service just to expose my app to localhost calls (via browser or Postman):
kind: Service
apiVersion: v1
metadata:
name: apaches-published
spec:
ports:
- name: http
port: 8080
targetPort: 80
protocol: TCP
selector:
app: web
type: LoadBalancer
Try it now on: http://localhost:8080
Really simple example
METHOD1
$ kubectl create deployment nginx-dep --image=nginx --replicas=2
Get the pods
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-dep-5c5477cb4-76t9q 1/1 Running 0 7h5m
nginx-dep-5c5477cb4-9g84j 1/1 Running 0 7h5m
Access the pod using kubectl port
$ kubectl port-forward nginx-dep-5c5477cb4-9g84j 8888:80
Forwarding from 127.0.0.1:8888 -> 80
Forwarding from [::1]:8888 -> 80
Now do a curl to the localhost:8888
$ curl -v http://localhost:8888
METHOD2
You can expose port 80 of the deployment (where the application is runnin i.e. nginx port)
via a NodePort
$ kubectl expose deployment nginx-dep --name=nginx-dep-svc --type=NodePort --port=80
Get the service
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 31d
nginx-dep-svc NodePort 10.110.80.21 <none> 80:31239/TCP 21m
Access the deployment using hte NodePort
$ curl http://localhost:31239
As already mentioned in Matthias Ms answer there are several ways.
As the offical Kubernetes documentation specifically describes using a Service with a type NodePort I wanted to describe the workflow.
NodePort: Exposes the Service on each Node’s IP at a static port (the NodePort). A ClusterIP Service, to which the NodePort Service routes, is automatically created. You’ll be able to contact the NodePort Service, from outside the cluster, by requesting <NodeIP>:<NodePort>.
If you set the type field to NodePort, the Kubernetes control plane allocates a port from a range specified by --service-node-port-range flag (default: 30000-32767). Each node proxies that port (the same port number on every Node) into your Service. Your Service reports the allocated port in its .spec.ports[*].nodePort field.
Setup a Service with a type of NodePort
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
clusterIP: 10.0.171.239
type: NodePort
Then you can check on which port the Service is exposed to via
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service NodePort 10.103.218.215 <none> 9376:31040/TCP 52s
and access it via localhost using the exposed port. E.g.
curl http://localhost:31040