From 7174ee833ae8f54f5194a9acb4b3db2cd71a4789 Mon Sep 17 00:00:00 2001 From: Danny Grove Date: Wed, 7 Sep 2022 17:09:02 -0700 Subject: [PATCH] Add support for headers to requests --- README.md | 3 ++- src/helpers.py | 6 +++-- test/resources/resources.yaml | 9 +++++++ test/resources/sidecar.yaml | 48 +++++++++++++++++++++++++++++------ test/server/server.py | 10 ++++++-- 5 files changed, 63 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index e3515db6..dbe003bb 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Both are identical multi-arch images built for `amd64`, `arm64`, `arm/v7`, `ppc6 - Values can also be base64 encoded URLs that download binary data e.g. executables - The key in the `ConfigMap`/`Secret` must end with "`.url`" ([see](https://github.com/kiwigrid/k8s-sidecar/blob/master/test/resources/resources.yaml#L84)) -# Usage +# Usage Example for a simple deployment can be found in [`example.yaml`](./example.yaml). Depending on the cluster setup you have to grant yourself admin rights first: ```shell @@ -77,6 +77,7 @@ 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_HEADERS` | Headers for use for request to `REQ_URL` and for `*.url` triggered request (ex. `{"x-apikey": "token"}`) | `` | - | string | | `SCRIPT` | Absolute path to shell script to execute after a configmap got reloaded. It runs before calls to `REQ_URI` | 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 | diff --git a/src/helpers.py b/src/helpers.py index 2fa34d02..3d937a61 100755 --- a/src/helpers.py +++ b/src/helpers.py @@ -4,6 +4,7 @@ import hashlib import os import subprocess +import json from datetime import datetime import requests @@ -22,6 +23,7 @@ REQ_RETRY_BACKOFF_FACTOR = 1.1 if os.getenv("REQ_RETRY_BACKOFF_FACTOR") is None else float( os.getenv("REQ_RETRY_BACKOFF_FACTOR")) REQ_TIMEOUT = 10 if os.getenv("REQ_TIMEOUT") is None else float(os.getenv("REQ_TIMEOUT")) +REQ_HEADERS = {} if os.getenv("REQ_HEADERS") is None else json.loads(os.getenv("REQ_HEADERS")) # Tune default timeouts as outlined in # https://github.com/kubernetes-client/python/issues/1148#issuecomment-626184613 @@ -127,9 +129,9 @@ def request(url, method, enable_5xx=False, payload=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) + res = r.get("%s" % url, auth=auth, headers=REQ_HEADERS, timeout=REQ_TIMEOUT) elif method == "POST": - res = r.post("%s" % url, auth=auth, json=payload, timeout=REQ_TIMEOUT) + res = r.post("%s" % url, auth=auth, headers=REQ_HEADERS, json=payload, timeout=REQ_TIMEOUT) else: logger.warning(f"Invalid REQ_METHOD: '{method}', please use 'GET' or 'POST'. Doing nothing.") return diff --git a/test/resources/resources.yaml b/test/resources/resources.yaml index 5bb4a105..251f7d7c 100644 --- a/test/resources/resources.yaml +++ b/test/resources/resources.yaml @@ -82,3 +82,12 @@ metadata: binaryData: # Base64 encoded url is 'http://dummy-server/static/kubelogo.png' url-downloaded-kubelogo.png.url: "aHR0cDovL2R1bW15LXNlcnZlci9zdGF0aWMva3ViZWxvZ28ucG5n" +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: url-configmap-headers + labels: + findme: "yup" +data: + secured.url: "http://dummy-server/secured-token" diff --git a/test/resources/sidecar.yaml b/test/resources/sidecar.yaml index 4d3cbb0e..a7717edf 100644 --- a/test/resources/sidecar.yaml +++ b/test/resources/sidecar.yaml @@ -61,13 +61,10 @@ spec: value: both - name: SCRIPT value: "/opt/script.sh" - - name: REQ_USERNAME - value: "user1" - - name: REQ_PASSWORD - value: "abcdefghijklmnopqrstuvwxyz" - - name: REQ_BASIC_AUTH_ENCODING - # the python server we're using for the tests expects ascii encoding of basic auth credentials, hence we can't use non-ascii characters in the password or username - value: "ascii" + - name: REQ_URL + value: "http://dummy-server/secure-token" + - name: REQ_HEADERS + value: '{"x-apitoken": "test"}' - name: LOG_LEVEL value: "DEBUG" volumes: @@ -117,6 +114,41 @@ spec: --- apiVersion: v1 kind: Pod +metadata: + name: sidecar-headers + namespace: default +spec: + serviceAccountName: sample-acc + containers: + - name: sidecar + image: kiwigrid/k8s-sidecar:testing + volumeMounts: + - name: shared-volume + mountPath: /tmp/ + - name: script-volume + mountPath: /opt/script.sh + subPath: script.sh + env: + - name: LABEL + value: "findme" + - name: FOLDER + value: /tmp/ + - name: RESOURCE + value: both + - name: SCRIPT + value: "/opt/script.sh" + - name: LOG_LEVEL + value: "DEBUG" + volumes: + - name: shared-volume + emptyDir: {} + - name: script-volume + configMap: + name: script-configmap + defaultMode: 0777 +--- +apiVersion: v1 +kind: Pod metadata: name: dummy-server-pod namespace: default @@ -140,4 +172,4 @@ spec: ports: - port: 80 targetPort: 80 - name: http \ No newline at end of file + name: http diff --git a/test/server/server.py b/test/server/server.py index 23cf915c..8b01645f 100644 --- a/test/server/server.py +++ b/test/server/server.py @@ -2,7 +2,7 @@ from fastapi.logger import logger from fastapi.security import HTTPBasic, HTTPBasicCredentials from fastapi.staticfiles import StaticFiles -from starlette.responses import PlainTextResponse +from starlette.responses import PlainTextResponse, JSONResponse app = FastAPI() @@ -45,4 +45,10 @@ async def read_secure_data(auth: HTTPBasicCredentials = Depends(basic_auth_schem detail=f"Incorrect user (${auth.username}) or password (${auth.password})", headers={"WWW-Authenticate": "Basic"}, ) - return 'allowed' \ No newline at end of file + return 'allowed' + +@app.get("/secured-token") +async def read_secure_header(x_apitoken: str | None = Header(default=None)): + if x_apitoken is None: + return 400 + return 200