This commit is contained in:
commit
6eacafe945
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@ -0,0 +1,3 @@
|
||||
src/__pycache__
|
||||
src/ENV
|
||||
src/.venv
|
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
deployment/secrets.txt
|
||||
src/.venv
|
||||
src/__pycache__
|
||||
src/ENV
|
||||
|
32
.woodpecker.yml
Normal file
32
.woodpecker.yml
Normal 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
15
Dockerfile
Normal 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
43
deployment/decrypt-secrets.sh
Executable 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
|
||||
|
||||
|
70
deployment/deploy-yml.tmpl
Normal file
70
deployment/deploy-yml.tmpl
Normal 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
41
deployment/deploy.sh
Executable 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
27
deployment/encrypt-secrets.sh
Executable 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
|
||||
|
13
deployment/oidc-config.json
Normal file
13
deployment/oidc-config.json
Normal 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
5
deployment/secrets.enc
Normal file
@ -0,0 +1,5 @@
|
||||
U2FsdGVkX1+uD433QQ1ZrBZgwtJsOHAXSLLhEoqQKwnt6IMztghrgnFihkUxkPo8
|
||||
LMjaH2aDwvEFh6BgzHgf+AAsbdQc0eUj1TJnIK9nd5e/5JZTRY9oQW5sMFjx7HDB
|
||||
VZ/YLX4/hsPMIsfG1cYXkE0dcpYPGgj2Y/YT33mNVcyU0VQ+DL7qRsMZXwohGQHE
|
||||
7R3sRXcUNLAnfdQWXOzkPtOg8UGE01Rki/DLEoDJgsLye6skqyIjrCQ7siaW5fER
|
||||
GoJCSzckwUUnzCrVa6b1tg==
|
74
src/app.py
Normal file
74
src/app.py
Normal 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
19
src/requirements.txt
Normal 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
|
Loading…
x
Reference in New Issue
Block a user