Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support client certificates when communicating with REQ_URL #335

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/build_and_test.yaml
Original file line number Diff line number Diff line change
@@ -74,6 +74,16 @@ jobs:
with:
name: images
path: /tmp
- name: Install Helm
run: |
wget https://get.helm.sh/helm-v3.16.1-linux-amd64.tar.gz
tar -zxf helm-v3.16.1-linux-amd64.tar.gz
mv linux-amd64/helm /usr/local/bin/helm
- name: Install cert-manager
run: |
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager --version v1.11.0 --set installCRDs=true
- name: Load images into kind cluster
run: |
kind load image-archive /tmp/k8s-sidecar.tar --name sidecar-testing
@@ -94,6 +104,7 @@ jobs:
wait_for_pod_ready "sidecar"
wait_for_pod_ready "sidecar-basicauth-args"
wait_for_pod_ready "sidecar-5xx"
wait_for_pod_ready "sidecar-tls"
wait_for_pod_ready "sidecar-pythonscript"
wait_for_pod_ready "sidecar-pythonscript-logfile"
wait_for_pod_ready "sidecar-logtofile-pythonscript"
@@ -116,6 +127,7 @@ jobs:
wait_for_pod_log $p $r
done
done
wait_for_pod_log "sidecar-tls" "tls-url-configmap-200"
# 10 more seconds after the last thing appeared in the logs.
sleep 10
- name: Retrieve pod logs
@@ -127,6 +139,7 @@ jobs:
kubectl logs sidecar-pythonscript > /tmp/logs/sidecar-pythonscript.log
kubectl logs sidecar-pythonscript-logfile > /tmp/logs/sidecar-pythonscript-logfile.log
kubectl logs dummy-server-pod > /tmp/logs/dummy-server.log
kubectl logs sidecar-tls > /tmp/logs/sidecar-tls.log
- name: Upload artifacts (pod logs)
uses: actions/upload-artifact@v3
with:
@@ -135,6 +148,7 @@ jobs:
- name: Download expected files from cluster
run: |
mkdir /tmp/sidecar
mkdir /tmp/sidecar-tls
mkdir /tmp/sidecar-5xx
echo "Downloading resource files from sidecar..."
kubectl cp sidecar:/tmp/hello.world /tmp/sidecar/hello.world
@@ -170,12 +184,16 @@ jobs:
kubectl cp sidecar-5xx:/tmp-5xx/secured.txt /tmp/sidecar-5xx/secured.txt
kubectl cp sidecar-5xx:/tmp-5xx/similar-configmap.txt /tmp/sidecar-5xx/similar-configmap.txt
kubectl cp sidecar-5xx:/tmp-5xx/similar-secret.txt /tmp/sidecar-5xx/similar-secret.txt

echo "Downloading resource files from sidecar-tls pod"
kubectl cp sidecar-tls:/tmp/200-tls.txt /tmp/sidecar-tls/200-tls.txt
- name: Upload artifacts (expected files from cluster)
uses: actions/upload-artifact@v3
with:
name: expected-files_${{ matrix.k8s.maj_min }}
path: |
/tmp/sidecar/**
/tmp/sidecar-tls/**
/tmp/sidecar-5xx/**
- name: Update Configmaps and Secrets
run: |
@@ -208,6 +226,8 @@ jobs:
echo -n "I'm very similar" | diff - /tmp/sidecar/similar-configmap.txt &&
echo -n "I'm very similar" | diff - /tmp/sidecar/similar-secret.txt &&
echo -n "allowed" | diff - /tmp/sidecar/secured.txt &&
[ -f /tmp/sidecar-tls/200-tls.txt ] && echo "200-tls.txt file created" &&
echo -n "200" | diff - /tmp/sidecar-tls/200-tls.txt &&
[ ! -f /tmp/sidecar/500.txt ] && echo "No 5xx file created" &&
ls /tmp/sidecar/script_result
- name: Verify sidecar-basicauth-args pod file after initial sync
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -84,6 +84,8 @@ If the filename ends with `.url` suffix, the content will be processed as a URL
| `REQ_USERNAME` | Username to use for basic authentication for requests to `REQ_URL` and for `*.url` triggered requests | false | - | string |
| `REQ_PASSWORD` | Password to use for basic authentication for requests to `REQ_URL` and for `*.url` triggered requests | false | - | string |
| `REQ_BASIC_AUTH_ENCODING` | Which encoding to use for username and password as [by default it's undefined](https://datatracker.ietf.org/doc/html/rfc7617) (e.g. `utf-8`). | false | `latin1` | string |
| `REQ_CLIENT_CERT` | [PATH] Client Certificate to be used by k8s-sidecar when communicating with the `REQ_URL` | false | - | string |
| `REQ_CLIENT_KEY` | [PATH] Client Key to be used by k8s-sidecar when communicating with the `REQ_URL` | false | - | string |
| `SCRIPT` | Absolute path to a script to execute after a configmap got reloaded. It runs before calls to `REQ_URI`. If the file is not executable it will be passed to `sh`. Otherwise it's executed as is. [Shebangs](https://en.wikipedia.org/wiki/Shebang_(Unix)) known to work are `#!/bin/sh` and `#!/usr/bin/env python` | false | - | string |
| `ERROR_THROTTLE_SLEEP` | How many seconds to wait before watching resources again when an error occurs | false | `5` | integer |
| `SKIP_TLS_VERIFY` | Set to `true` to skip tls verification for kube api calls | false | - | boolean |
18 changes: 15 additions & 3 deletions src/helpers.py
Original file line number Diff line number Diff line change
@@ -165,12 +165,18 @@ def request(url, method, enable_5xx=False, payload=None):
if url is None:
logger.warning(f"No url provided. Doing nothing.")
return

client_cert, client_key = get_client_cert_and_key_paths()
if client_cert and client_key:
logger.debug("Client certificates are configured: Client cert path: %s, Client key path: %s", client_cert, client_key)
cert = (client_cert, client_key)
else:
logger.debug("Client certificates are not configured. Skipping client certificate authentication.")
cert = None
# If method is not provided use GET as default
if method == "GET" or not method:
res = r.get("%s" % url, auth=auth, timeout=REQ_TIMEOUT, verify=REQ_TLS_VERIFY)
res = r.get("%s" % url, cert=cert, auth=auth, timeout=REQ_TIMEOUT, verify=REQ_TLS_VERIFY)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can cert be None?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the default value is None for cert if not specified.
Reference: https://docs.python-requests.org/en/latest/_modules/requests/sessions/#Session

elif method == "POST":
res = r.post("%s" % url, auth=auth, json=payload, timeout=REQ_TIMEOUT, verify=REQ_TLS_VERIFY)
res = r.post("%s" % url, cert=cert, auth=auth, json=payload, timeout=REQ_TIMEOUT, verify=REQ_TLS_VERIFY)
else:
logger.warning(f"Invalid REQ_METHOD: '{method}', please use 'GET' or 'POST'. Doing nothing.")
return
@@ -216,3 +222,9 @@ def execute(script_path):
logger.debug(f"Script exit code: {result.returncode}")
except subprocess.CalledProcessError as e:
logger.error(f"Script failed with error: {e}")


def get_client_cert_and_key_paths():
client_cert = os.getenv("REQ_CLIENT_CERT")
client_key = os.getenv("REQ_CLIENT_KEY")
return client_cert, client_key
10 changes: 10 additions & 0 deletions test/resources/resources.yaml
Original file line number Diff line number Diff line change
@@ -101,3 +101,13 @@ metadata:
binaryData:
# Base64 encoded url is 'http://dummy-server/static/kubelogo.png'
url-downloaded-kubelogo.png.url: "aHR0cDovL2R1bW15LXNlcnZlci9zdGF0aWMva3ViZWxvZ28ucG5n"
---
# For client-certificate-auth
apiVersion: v1
kind: ConfigMap
metadata:
name: tls-url-configmap-200
labels:
findme-with-tls: "yup"
data:
200-tls.txt.url: "https://dummy-server-tls/200"
153 changes: 152 additions & 1 deletion test/resources/sidecar.yaml
Original file line number Diff line number Diff line change
@@ -388,4 +388,155 @@ metadata:
type: Opaque
stringData:
username: "user1"
password: "abcdefghijklmnopqrstuvwxyz"
password: "abcdefghijklmnopqrstuvwxyz"
---
# For client-certificate-auth
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: selfsigned-issuer
namespace: default
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: root-ca
namespace: default
spec:
isCA: true
commonName: root-ca
subject:
organizations:
- Kiwigrid
organizationalUnits:
- k8s-sidecar
secretName: ca-secret
privateKey:
algorithm: ECDSA
size: 256
issuerRef:
name: selfsigned-issuer
kind: Issuer
group: cert-manager.io
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: root-ca-issuer
namespace: default
spec:
ca:
secretName: ca-secret
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: dummy-server
namespace: default
spec:
secretName: server-tls
isCA: false
usages:
- server auth
- client auth
dnsNames:
- "dummy-server-tls.default.svc.cluster.local"
- "dummy-server-tls"
issuerRef:
name: root-ca-issuer
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: sidecar-client
namespace: default
spec:
secretName: client-tls
isCA: false
usages:
- server auth
- client auth
dnsNames:
- "dummy-server-tls.default.svc.cluster.local"
- "dummy-server-tls"
issuerRef:
name: root-ca-issuer
---
apiVersion: v1
kind: Pod
metadata:
name: dummy-server-tls-pod
namespace: default
labels:
app: dummyserver-tls
spec:
serviceAccountName: sample-acc
containers:
- name: dummy-server-tls
command: ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "443", "--ssl-keyfile", "/opt/server-creds/tls.key", "--ssl-certfile", "/opt/server-creds/tls.crt", "--ssl-cert-reqs", "2", "--ssl-ca-certs","/opt/root-creds/ca.crt"]
image: dummy-server:1.0.0
ports:
- containerPort: 443
volumeMounts:
- name: server-creds
mountPath: /opt/server-creds/
- name: root-creds
mountPath: /opt/root-creds/
volumes:
- name: server-creds
secret:
secretName: server-tls
- name: root-creds
secret:
secretName: ca-secret
---
apiVersion: v1
kind: Service
metadata:
name: dummy-server-tls
spec:
selector:
app: dummyserver-tls
ports:
- port: 443
targetPort: 443
name: https
---
apiVersion: v1
kind: Pod
metadata:
name: sidecar-tls
namespace: default
spec:
serviceAccountName: sample-acc
containers:
- name: sidecar-tls
image: kiwigrid/k8s-sidecar:testing
volumeMounts:
- name: shared-volume
mountPath: /tmp/
- name: client-tls-creds
mountPath: /opt/client-tls-creds/
env:
- name: LABEL
value: "findme-with-tls"
- name: FOLDER
value: /tmp/
- name: RESOURCE
value: both
- name: LOG_LEVEL
value: "DEBUG"
- name: REQ_CLIENT_CERT
value: "/opt/client-tls-creds/tls.crt"
- name: REQ_CLIENT_KEY
value: "/opt/client-tls-creds/tls.key"
- name: REQ_SKIP_TLS_VERIFY
value: "true"
volumes:
- name: shared-volume
emptyDir: {}
- name: client-tls-creds
secret:
secretName: client-tls