initial
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
Wolfgang Hottgenroth 2024-01-30 10:53:37 +01:00
commit 6eacafe945
Signed by: wn
GPG Key ID: 836E9E1192A6B132
13 changed files with 351 additions and 0 deletions

3
.dockerignore Normal file
View File

@ -0,0 +1,3 @@
src/__pycache__
src/ENV
src/.venv

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
deployment/secrets.txt
src/.venv
src/__pycache__
src/ENV

32
.woodpecker.yml Normal file
View File

@ -0,0 +1,32 @@
steps:
build:
image: plugins/kaniko
settings:
repo: gitea.hottis.de/wn/oidc-python-example
registry:
from_secret: container_registry
tags: latest,${CI_COMMIT_SHA},${CI_COMMIT_TAG}
username:
from_secret: container_registry_username
password:
from_secret: container_registry_password
dockerfile: Dockerfile
when:
- event: [push, tag]
deploy:
image: portainer/kubectl-shell:latest
secrets:
- source: kube_config
target: KUBE_CONFIG_CONTENT
- source: encryption_key
target: ENCRYPTION_KEY
- source: secrets_checksum
target: MD5_CHECKSUM
commands:
- export IMAGE_TAG=$CI_COMMIT_TAG
- printf "$KUBE_CONFIG_CONTENT" > /tmp/kubeconfig
- export KUBECONFIG=/tmp/kubeconfig
- ./deployment/deploy.sh
when:
- event: tag

15
Dockerfile Normal file
View File

@ -0,0 +1,15 @@
FROM python:3.12-alpine3.19
ARG APP_DIR="/opt/app"
COPY ./src/* ${APP_DIR}/
COPY start.sh ${APP_DIR}/
WORKDIR ${APP_DIR}
RUN pip install -r requirements.txt
EXPOSE 8080
CMD "./start.sh"

43
deployment/decrypt-secrets.sh Executable file
View File

@ -0,0 +1,43 @@
#!/bin/bash
if [ "$ENCRYPTION_KEY" = "" ]; then
echo "ENCRYPTION_KEY not set"
exit 1
fi
if [ "$MD5_CHECKSUM" = "" ]; then
echo "No checksum given"
exit 1
fi
SECRETS_CIPHERTEXT_FILE=secrets.enc
SECRETS_PLAINTEXT_FILE=/tmp/secrets
TMP_FILE=`mktemp`
POD_NAME_SUFFIX=`date +%s`
cat $SECRETS_CIPHERTEXT_FILE | \
kubectl run openssl-$POD_NAME_SUFFIX \
--rm \
--image bitnami/debian-base-buildpack:latest \
--env KEY=$ENCRYPTION_KEY \
-i \
-q \
-- \
/bin/sh -c "openssl enc -aes-256-cbc -salt -pass env:KEY -a -d" > \
$TMP_FILE
if [ `uname` = "Darwin" ]; then
CALCULATED_CHECKSUM=`cat $TMP_FILE | md5`
elif [ `uname` = "Linux" ]; then
CALCULATED_CHECKSUM=`cat $TMP_FILE | md5sum - | awk '{print $1}'`
fi
if [ "$MD5_CHECKSUM" != "$CALCULATED_CHECKSUM" ]; then
echo "Invalid checksum"
exit 1
fi
# cat $TMP_FILE
mv $TMP_FILE $SECRETS_PLAINTEXT_FILE

View File

@ -0,0 +1,70 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: locsrv
namespace: homea
labels:
app: locsrv
spec:
replicas: 1
selector:
matchLabels:
app: locsrv
template:
metadata:
labels:
app: locsrv
spec:
containers:
- name: locsrv
image: %IMAGE%
envFrom:
- secretRef:
name: locsrv-db-cred
env:
- name: MQTT_BROKER
value: mqtt://emqx01-anonymous-cluster-internal.broker.svc.cluster.local:1883
- name: MQTT_TOPIC
value: locative/event
- name: GIN_MODE
value: release
ports:
- containerPort: 8080
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: locative
spec:
type: ClusterIP
selector:
app: locsrv
ports:
- name: http
targetPort: 8080
port: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: locative
annotations:
cert-manager.io/cluster-issuer: letsencrypt-production-http
spec:
tls:
- hosts:
- locative.hottis.de
secretName: locative-cert
rules:
- host: locative.hottis.de
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: locative
port:
number: 80

41
deployment/deploy.sh Executable file
View File

@ -0,0 +1,41 @@
#!/bin/bash
if [ "$IMAGE_TAG" == "" ]; then
echo "Make sure IMAGE_TAG is set"
exit 1
fi
IMAGE_NAME=gitea.hottis.de/wn/oidc-python-example
NAMESPACE=oidc-python-example
DEPLOYMENT_DIR=$PWD/deployment
pushd $DEPLOYMENT_DIR > /dev/null
./decrypt-secrets.sh || exit 1
. /tmp/secrets
rm /tmp/secrets
kubectl create namespace $NAMESPACE \
--dry-run=client \
-o yaml | \
kubectl -f - apply
kubectl create secret generic secrets \
--dry-run=client \
-o yaml \
--save-config \
--from-literal=SECRET="$SECRET" \
--from-literal=CLIENT_SECRETS="$CLIENT_SECRETS" \
--from-literal=PGUSER="$PGUSER" \
--from-literal=PGPASSWORD="$PGPASSWORD" \
--from-literal=PGDATABASE="$PGDATABASE" \
--from-literal=PGHOST="timescaledb.database.svc.cluster.local" \
--from-literal=PGSSLMODE="require" | \
kubectl apply -f - -n $NAMESPACE
cat $DEPLOYMENT_DIR/deploy-yml.tmpl | \
sed -e 's,%IMAGE%,'$IMAGE_NAME':'$IMAGE_TAG','g | \
kubectl apply -f - -n $NAMESPACE
popd > /dev/null

27
deployment/encrypt-secrets.sh Executable file
View File

@ -0,0 +1,27 @@
#!/bin/bash
ENCRYPTION_KEY=`openssl rand -hex 32`
echo $ENCRYPTION_KEY
SECRETS_PLAINTEXT_FILE=secrets.txt
SECRETS_CIPHERTEXT_FILE=secrets.enc
if [ `uname` = "Darwin" ]; then
cat $SECRETS_PLAINTEXT_FILE | md5
elif [ `uname` = "Linux" ]; then
cat $SECRETS_PLAINTEXT_FILE | md5sum - | awk '{print $1}'
fi
POD_NAME_SUFFIX=`date +%s`
cat $SECRETS_PLAINTEXT_FILE | \
kubectl run openssl-$POD_NAME_SUFFIX \
--rm \
--image bitnami/debian-base-buildpack:latest \
--env KEY=$ENCRYPTION_KEY \
-i \
-q \
-- \
/bin/sh -c "openssl enc -aes-256-cbc -salt -pass env:KEY -a" > \
$SECRETS_CIPHERTEXT_FILE

View File

@ -0,0 +1,13 @@
{
"web": {
"issuer": "https://auth2.hottis.de/realms/hottis",
"auth_uri": "https://auth2.hottis.de/ealms/hottis/protocol/openid-connect/auth",
"client_id": "oidc-python-example",
"client_secret": "%CLIENT_SECRET%",
"redirect_uris": [
"http://localhost:8080/*"
],
"userinfo_uri": "https://auth2.hottis.de/realms/hottis/protocol/openid-connect/userinfo",
"token_uri": "https://auth2.hottis.de/realms/hottis/protocol/openid-connect/token"
}
}

5
deployment/secrets.enc Normal file
View File

@ -0,0 +1,5 @@
U2FsdGVkX1+uD433QQ1ZrBZgwtJsOHAXSLLhEoqQKwnt6IMztghrgnFihkUxkPo8
LMjaH2aDwvEFh6BgzHgf+AAsbdQc0eUj1TJnIK9nd5e/5JZTRY9oQW5sMFjx7HDB
VZ/YLX4/hsPMIsfG1cYXkE0dcpYPGgj2Y/YT33mNVcyU0VQ+DL7qRsMZXwohGQHE
7R3sRXcUNLAnfdQWXOzkPtOg8UGE01Rki/DLEoDJgsLye6skqyIjrCQ7siaW5fER
GoJCSzckwUUnzCrVa6b1tg==

74
src/app.py Normal file
View File

@ -0,0 +1,74 @@
import json
import logging
import os
from flask import Flask, g
from flask_oidc import OpenIDConnect
import requests
logging.basicConfig(level=logging.DEBUG)
app = Flask(__name__)
app.config.update({
'SECRET_KEY': os.environ['SECRET'],
'DEBUG': False,
'OIDC_CLIENT_SECRETS': json.loads(os.environ['CLIENT_SECRETS']),
'OIDC_ID_TOKEN_COOKIE_SECURE': False,
'OIDC_USER_INFO_ENABLED': True,
'OIDC_OPENID_REALM': 'hottis',
'OIDC_SCOPES': ['openid', 'email', 'profile']
})
oidc = OpenIDConnect(app)
@app.route('/')
def hello_world():
if oidc.user_loggedin:
return ('Hello, %s, <a href="/private">See private</a> '
'<a href="/logout">Log out</a>') % \
oidc.user_getfield('preferred_username')
else:
return 'Welcome anonymous, <a href="/private">Log in</a>'
@app.route('/private')
@oidc.require_login
def hello_me():
"""Example for protected endpoint that extracts private information from the OpenID Connect id_token.
Uses the accompanied access_token to access a backend service.
"""
info = oidc.user_getinfo(['preferred_username', 'email', 'sub', 'roles'])
username = info.get('preferred_username')
email = info.get('email')
user_id = info.get('sub')
roles = info.get('roles')
return ("""%s, %s, %s, %s!
<ul>
<li><a href="/">Home</a></li>
<li><a href="/logout">Logout</a></li>
</ul>""" %
(username, email, user_id, roles))
# @app.route('/api', methods=['POST'])
# @oidc.accept_token(require_token=True, scopes_required=['openid'])
# def hello_api():
# """OAuth 2.0 protected API endpoint accessible via AccessToken"""
#
# return json.dumps({'hello': 'Welcome %s' % g.oidc_token_info['sub']})
@app.route('/logout')
def logout():
"""Performs local logout by removing the session cookie."""
oidc.logout()
return 'Hi, you have been logged out! <a href="/">Return</a>'
if __name__ == '__main__':
app.run(port=8080)

19
src/requirements.txt Normal file
View File

@ -0,0 +1,19 @@
Authlib==1.3.0
blinker==1.7.0
certifi==2023.11.17
cffi==1.16.0
charset-normalizer==3.3.2
click==8.1.7
cryptography==42.0.1
Flask==3.0.1
flask-oidc==2.1.1
gunicorn==21.2.0
idna==3.6
itsdangerous==2.1.2
Jinja2==3.1.3
MarkupSafe==2.1.4
packaging==23.2
pycparser==2.21
requests==2.31.0
urllib3==2.1.0
Werkzeug==3.0.1

4
start.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
gunicorn 'app:app' --bind 0.0.0.0:8080 --log-level=info --workers=4