c0c0n3 / kitt4sme.live

On a mission to bring AI to the shop floor: https://kitt4sme.eu/
MIT License
1 stars 28 forks source link

Secure external HTTP traffic #77

Open c0c0n3 opened 2 years ago

c0c0n3 commented 2 years ago

Summary

Make all HTTP traffic between the platform and external processes go through TLS channels.

Intended outcome

HTTP communication between external parties and platform services is private and the exchanged data can't be tampered with.

How will it work?

External processes can only do HTTP calls to platform services through a secure TLS channel. The Istio ingress gateway accepts external HTTPS traffic on port 443 and redirects plain HTTP (port 80 at the moment) to port 443. Also, the gateway gets configured with a valid certificate for kitt4sme.collab-cloud.eu so it can do TLS termination.

c0c0n3 commented 2 years ago

So I've tested the TLS setup a bit and we could start from this Istio gateway config which builds on the existing gateway definition to add a TLS endpoint, handle TLS termination and redirect plain HTTP traffic to the TLS endpoint.

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: "kitt4sme-gateway"
spec:
  selector:
    istio: ingressgateway
  servers:
  - hosts:
    - "*"
    port:
      name: http
      number: 80
      protocol: HTTP
    tls:
      httpsRedirect: true
  - hosts:
    - "*"
    port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: istio-gw-cert

Notice the credentialName: istio-gw-cert up there. That's where we've got to plonk down a valid certificate. Once we've got one, in principle we should just be able to create a K8s secret for it like this

$ kubectl create -n istio-system secret tls istio-gw-cert \
                 --key=istio-gw.key-pair.pem --cert=istio-gw.crt.pem

Notice we can't automate this step since the certificate and its private/pub key should be kept secret---so obviously we must not stash that away in source control.

See also:

rx2dev commented 2 years ago

I did some tests to address this issue, find then below. You can find the yaml files in the next repo: k8s-kc-tests

Certificates

Note: This part will not be required once we have the official certificates

Create root certificate

openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout root-example.com.key -out root-example.com.crt

Create certificate and key for Keycloak (kc.example.com)

openssl req -out kc.example.com.csr -newkey rsa:2048 -nodes -keyout kc.example.com.key -subj "/CN=kc.example.com/O=Keycloak Org"
openssl x509 -req -days 365 -CA root-example.com.crt -CAkey root-example.com.key -set_serial 0 -in kc.example.com.csr -out kc.example.com.crt

Get certificate info

openssl x509 -text -noout -in root-example.com.crt
openssl x509 -text -noout -in kc.example.com.crt

Create the certs dirs for being used as volume

mkdir certs
cp kc.example.com.crt certs/tls.crt
cp kc.example.com.key certs/tls.key

Test with Docker

Launch docker image (mapping the certs dir as a volume)

sudo docker run --name keycloak -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin -p 8443:8443 -v $(pwd)/certs:/etc/x509/https jboss/keycloak

Get certificate info

openssl s_client -showcerts -connect localhost:8443

Clean everything

sudo docker rm keycloak
sudo docker volume prune

Test with K8S

Without Ingress

Create specific namespace for Keycloak (optional)

kubectl create ns kc

Apply the yaml

kubectl -n kc apply -f keycloak-simple.yaml

Note: check your volumes > hostPath . It can be different in your setup

Forward port

kubectl -n kc port-forward svc/keycloak 8443:8443 --address 0.0.0.0

Get and check certificate info

openssl s_client -showcerts -connect localhost:8443

Clean Everything

kubectl delete ns kc

With Ingress and no TLS/SSL

Create specific namespace for Keycloak

kubectl create ns kc

Enable Istio injection

kubectl label namespace kc istio-injection=enabled

Apply the Keycloak yaml

kubectl -n kc apply -f keycloak-simple.yaml

Apply the gateway

kubectl -n kc apply -f kc-gw.yaml

Apply the virtual service for routing

kubectl -n kc apply -f kc-routing.yaml

Test the GW and the routing

curl -v localhost:80

Optional Add a Authorization Policy to block the access to some reources. (e.g jboss_community.png)

kubectl apply -f kc-img-block-policy.yaml

Optional Check the access to jboss_community.png is forbidden

curl -v http://localhost/auth/welcome-content/jboss_community.png -o /dev/null

Clean Everything

kubectl delete ns kc
kubectl -n istio-system delete authorizationpolicy ingress-img-block-policy

With Ingress and TLS/SSL

Create specific namespace for Keycloak

kubectl create ns kc

Enable Istio injection

kubectl label namespace kc istio-injection=enabled

Apply the Keycloak yaml

kubectl -n kc apply -f keycloak-simple.yaml

Note: due to the TLS termination the certificate in KC is not needed, you can replace the line above with keycloak without Certs

kubectl -n kc apply -f keycloak-nocert.yaml

Create the secret for the Ingress Gateway

kubectl create -n istio-system secret tls kc-tls --key=certs/tls.key --cert=certs/tls.crt

Apply the gateway

kubectl -n kc apply -f kc-sec-gw.yaml

Apply the virtual service for routing

kubectl -n kc apply -f kc-routing.yaml

Test the GW and the routing (with and without TLS/SSL)

curl -L -v http://localhost
curl -L -v https://localhost 
openssl s_client -showcerts -connect localhost:443

Clean Everything

kubectl delete ns kc
kubectl -n istio-system delete secret kc-tls
rx2dev commented 2 years ago

The last option With Ingress and TLS/SSL was tested with Keycloak requiring HTTPS for: 'None', 'External requests' and 'All requests'

rx2dev commented 2 years ago

To apply and test this above in the K4S mesh, we could update the Gateway to something similar to what @c0c0n3 described in (https://github.com/c0c0n3/kitt4sme.live/issues/77#issuecomment-1019264450), e.g:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: kitt4sme-gateway
  namespace: default
spec:
  selector:
    istio: ingressgateway
  servers:
    - hosts:
      - "*"
      port:
        number: 80
        name: http
        protocol: HTTP
      tls:
        httpsRedirect: false # set to true if you want to force the redirection to HTTPS
    - hosts:
      - "*"
      port:
        number: 443
        name: https
        protocol: HTTPS
      tls:
        mode: SIMPLE
        credentialName: kc-tls

and test it with:

curl --insecure -L -v https://localhost/auth

or

openssl s_client -showcerts -connect localhost:443

c0c0n3 commented 2 years ago

@karikolehmainen please wire in the TLS certificate you have so we can close this issue. Thanks!