This commit is contained in:
2023-10-19 12:39:29 +02:00
parent b77d8da90e
commit 823049e14b
16 changed files with 398 additions and 92 deletions

35
database/main.yml Normal file
View File

@ -0,0 +1,35 @@
---
apiVersion: stackgres.io/v1
kind: SGPostgresConfig
metadata:
name: database
namespace: mainscnt
spec:
postgresVersion: '15'
postgresql.conf:
shared_preload_libraries: timescaledb
max_worker_processes: '32'
max_parallel_workers: '16'
---
apiVersion: stackgres.io/v1
kind: SGCluster
metadata:
name: database
namespace: mainscnt
spec:
instances: 1
postgres:
version: '15'
extensions:
- name: timescaledb
ssl:
enabled: true
configurations:
sgPostgresConfig: database
pods:
persistentVolume:
size: '1000Gi'
storageClass: nfs-client
disableConnectionPooling: true

8
database/restart.yml Normal file
View File

@ -0,0 +1,8 @@
apiVersion: stackgres.io/v1
kind: SGDbOps
metadata:
name: restart-002
spec:
sgCluster: database
op: restart

48
database/setup-database.sh Executable file
View File

@ -0,0 +1,48 @@
#!/bin/bash
DATABASE=mainscnt
LOGIN=sink
PASSWORD=`openssl rand -base64 24`
SUPERUSER_LOGIN=$(kubectl get secret -n mainscnt database -o jsonpath="{.data.superuser-username}" | base64 --decode)
SUPERUSER_PASSWORD=$(kubectl get secret -n mainscnt database -o jsonpath="{.data.superuser-password}" | base64 --decode)
kubectl run psql \
--stdin=true \
--tty=true \
--rm=true \
--image=postgres:15-alpine \
-n $NAMESPACE \
--restart=Never \
--env="PGPASSWORD=$SUPERUSER_PASSWORD" \
--env="PGUSER=$SUPERUSER_LOGIN" \
-- \
psql -h database.mainscnt.svc.cluster.local <<EOF
do
\$\$
begin
if not exists (SELECT * FROM pg_database WHERE datname = '$DATABASE') then
CREATE DATABASE $DATABASE;
end if;
if not exists (SELECT * FROM pg_user WHERE usename = '$LOGIN') then
CREATE USER $LOGIN WITH PASSWORD '$PASSWORD';
else
ALTER USER $LOGIN WITH PASSWORD '$PASSWORD';
end if;
GRANT ALL PRIVILEGES ON DATABASE $DATABASE TO $LOGIN;
end
\$\$
;
commit;
EOF
kubectl create secret generic sinkserver-secret \
--dry-run=client \
-o yaml \
--save-config \
--from-literal=dbpass="$PASSWORD" | \
kubectl apply -f - -n $NAMESPACE

22
grafana/ingress.yml Normal file
View File

@ -0,0 +1,22 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: grafana
annotations:
cert-manager.io/cluster-issuer: letsencrypt-production-http
spec:
tls:
- hosts:
- grafana.mainscnt.eu
secretName: grafana-cert
rules:
- host: grafana.mainscnt.eu
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: mainscntgrafana
port:
number: 80

28
grafana/install.sh Executable file
View File

@ -0,0 +1,28 @@
#!/bin/bash
ARG1=$1
NAMESPACE=$(cat namespace)
echo "Namespace: $NAMESPACE"
if [ "$ARG1" = "initial" ]; then
psql << EOF
create user mainscntgrafana;
commit;
create database mainscntgrafana with owner mainscntgrafana;
EOF
fi
./roll-db-credential.sh
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
helm upgrade --install -f values.yml mainscntgrafana grafana/grafana --version 6.59.0 --namespace=$NAMESPACE
kubectl annotate deployments mainscntgrafana -n $NAMESPACE --overwrite=true secret.reloader.stakater.com/reload=grafana-db-cred
kubectl -f ingress.yml -n $NAMESPACE apply

1
grafana/namespace Normal file
View File

@ -0,0 +1 @@
mainscnt

108
grafana/provisioning.json Normal file
View File

@ -0,0 +1,108 @@
[
{
"name": "createClientMainscntGrafana",
"method": "POST",
"url": "https://auth2.hottis.de/admin/realms/hottis/clients",
"data": {
"protocol": "openid-connect",
"enabled": "true",
"clientId": "mainscnt-grafana",
"name": "Grafana2",
"baseUrl": "https://grafana.mainscnt.eu",
"redirectUris": [
"https://grafana.mainscnt.eu/login/generic_oauth"
],
"standardFlowEnabled": "true",
"implicitFlowEnabled": "false",
"publicClient": "false"
}
},
{
"name": "createClientRoleMaincntGrafanaAdmin",
"method": "POST",
"url": "https://auth2.hottis.de/admin/realms/hottis/clients/$result/roles",
"use": {
"url": "https://auth2.hottis.de/admin/realms/hottis/clients",
"data": { "clientId": "mainscnt-grafana" },
"resultIndexes": [ 0, "id" ]
},
"data": {
"name": "Admin"
}
},
{
"name": "createClientRoleMainscntGrafanaGrafanaAdmin",
"method": "POST",
"url": "https://auth2.hottis.de/admin/realms/hottis/clients/$result/roles",
"use": {
"url": "https://auth2.hottis.de/admin/realms/hottis/clients",
"data": { "clientId": "mainscnt-grafana" },
"resultIndexes": [ 0, "id" ]
},
"data": {
"name": "GrafanaAdmin"
}
},
{
"name": "createClientRoleMainscntGrafanaViewer",
"method": "POST",
"url": "https://auth2.hottis.de/admin/realms/hottis/clients/$result/roles",
"use": {
"url": "https://auth2.hottis.de/admin/realms/hottis/clients",
"data": { "clientId": "mainscnt-grafana" },
"resultIndexes": [ 0, "id" ]
},
"data": {
"name": "Viewer"
}
},
{
"name": "createClientRoleMainscntGrafanaEditor",
"method": "POST",
"url": "https://auth2.hottis.de/admin/realms/hottis/clients/$result/roles",
"use": {
"url": "https://auth2.hottis.de/admin/realms/hottis/clients",
"data": { "clientId": "mainscnt-grafana" },
"resultIndexes": [ 0, "id" ]
},
"data": {
"name": "Editor"
}
},
{
"name": "createRoleMapperMainscntGrafana",
"method": "POST",
"url": "https://auth2.hottis.de/admin/realms/hottis/clients/$result/protocol-mappers/add-models",
"use": {
"url": "https://auth2.hottis.de/admin/realms/hottis/clients",
"data": { "clientId": "mainscnt-grafana" },
"resultIndexes": [ 0, "id" ]
},
"data": [
{
"name": "client roles",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-client-role-mapper",
"consentRequired": false,
"config": {
"multivalued": "true",
"user.attribute": "foo",
"access.token.claim": "true",
"claim.name": "roles",
"jsonType.label": "String",
"id.token.claim": "true",
"access.token.claim": "true",
"clientId": "mainscnt-grafana"
}
}
]
},
{
"name": "getClientMainscntGrafana",
"method": "GET",
"url": "https://auth2.hottis.de/admin/realms/hottis/clients",
"data": { "clientId": "mainscnt-grafana" }
}
]

5
grafana/readme.md Normal file
View File

@ -0,0 +1,5 @@
For the OAuth configuration follow the docs at
https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/generic-oauth/
With keycloak create under "client scopes" a dedicated mapper to fill the client roles into the claim roles.

33
grafana/roll-db-credential.sh Executable file
View File

@ -0,0 +1,33 @@
#!/bin/bash
DATABASE=mainscntgrafana
LOGIN=mainscntgrafana
PASSWORD=`openssl rand -base64 24`
NAMESPACE=$(cat namespace)
psql <<EOF
do
\$\$
begin
if not exists (SELECT * FROM pg_user WHERE usename = '$LOGIN') then
CREATE USER $LOGIN WITH PASSWORD '$PASSWORD';
else
ALTER USER $LOGIN WITH PASSWORD '$PASSWORD';
end if;
GRANT ALL PRIVILEGES ON DATABASE $DATABASE TO $LOGIN;
end
\$\$
;
commit;
EOF
kubectl create secret generic grafana-db-cred \
--dry-run=client \
-o yaml \
--save-config \
--from-literal=GF_DATABASE_USER="$LOGIN" \
--from-literal=GF_DATABASE_PASSWORD="$PASSWORD" | \
kubectl apply -f - -n $NAMESPACE

22
grafana/test.yml Normal file
View File

@ -0,0 +1,22 @@
apiVersion: v1
kind: Pod
metadata:
name: secretloader
labels:
app: secretloader
spec:
containers:
- name: secretloader
image: nginx
env:
- name: USERNAME
valueFrom:
secretKeyRef:
name: grafana-admin-credentials
key: username
- name: PASSWORD
valueFrom:
secretKeyRef:
name: grafana-admin-credentials
key: password

51
grafana/values.yml Normal file
View File

@ -0,0 +1,51 @@
persistence:
enabled: true
storageClassName: nfs-client
grafana.ini:
server:
root_url: https://grafana.mainscnt.eu
smtp:
enabled: true
host: smtp.system.svc.cluster.local
from_address: grafana@mainscnt.eu
from_name: "Mainscnt Grafana Pseudouser"
log:
level: debug
emails:
welcome_email_on_sign_up: true
security:
cookie_secure: true
cookie_samesite: none
auth:
disable_login_form: true
auth.generic_oauth:
enabled: true
name: Mainscnt Grafana via Keycloak
allow_sign_up: true
client_id: mainscnt-grafana
client_secret: ...
scopes: openid email profile offline_access roles
email_attribute_path: email
login_attribute_path: username
name_attribute_path: fullname
auth_url: https://auth2.hottis.de/realms/hottis/protocol/openid-connect/auth
token_url: https://auth2.hottis.de/realms/hottis/protocol/openid-connect/token
api_url: https://auth2.hottis.de/realms/hottis/protocol/openid-connect/userinfo
role_attribute_path: "contains(roles[*], 'GrafanaAdmin') && 'GrafanaAdmin' || contains(roles[*], 'Admin') && 'Admin' || contains(roles[*], 'Editor') && 'Editor' || contains(roles[*], 'Viewer') && 'Viewer'"
role_attribute_strict: true
allow_assign_grafana_admin: true
tls_skip_verify_insecure: true
database:
type: postgres
host: timescaledb.database.svc.cluster.local
name: mainscntgrafana
ssl_mode: require
# add the oauth client secret in this secret with the key GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET
# example:
# kubectl create secret generic grafana-oauth-secret --from-literal=GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET="geheim"
envFromSecrets:
- name: grafana-oauth-secret
- name: grafana-db-cred

18
install.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/bash
export NAMESPACE=$(cat namespace)
kubectl create namespace $NAMESPACE --dry-run=client -o yaml | kubectl -f - apply
kubectl apply -f ./database/main.yml -n $NAMESPACE
while true; do
kubectl get -n $NAMESPACE pod database-0 && break
echo "pod not yet available"
sleep 5
done
echo "waiting for database to get ready"
kubectl wait --for=condition=ready -n $NAMESPACE --timeout=600s pod database-0
./database/setup-database.sh

1
namespace Normal file
View File

@ -0,0 +1 @@
mainscnt

9
nodered/install.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/bash
if [ "$NAMESPACE" = "" ]; then
NAMESPACE=$(cat ../namespace)
fi
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm repo update
helm upgrade --install -n $NAMESPACE -f values.yml nodered geek-cookbook/node-red --version 10.3.2

8
nodered/values.yml Normal file
View File

@ -0,0 +1,8 @@
env:
NODE_RED_ENABLE_PROJECTS: true
persistence:
data:
enabled: true
storageClass: nfs-client
accessMode: ReadWriteOnce
size: 1Gi

View File

@ -1,25 +1,3 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
namespace: mainscnt
name: deny-all-but-dns
spec:
podSelector:
matchLabels: {}
policyTypes:
- Egress
- Ingress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
---
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@ -28,7 +6,7 @@ metadata:
labels: labels:
app: sinkserver app: sinkserver
spec: spec:
replicas: 3 replicas: 1
selector: selector:
matchLabels: matchLabels:
app: sinkserver app: sinkserver
@ -70,72 +48,3 @@ spec:
- protocol: UDP - protocol: UDP
port: 20169 port: 20169
targetPort: 20169 targetPort: 20169
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-database-sinkserver
namespace: database
spec:
podSelector:
matchLabels:
app: timescaledb
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: sinkserver
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: mainscnt
ports:
- protocol: TCP
port: 5432
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-sinkserver-database
namespace: mainscnt
spec:
podSelector:
matchLabels:
app: sinkserver
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: timescaledb
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: database
ports:
- protocol: TCP
port: 5432
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-sinkserver-ingress
namespace: mainscnt
spec:
podSelector:
matchLabels:
app: sinkserver
policyTypes:
- Ingress
ingress:
- from:
- ipBlock:
cidr: 0.0.0.0/0
ports:
- protocol: UDP
port: 20169