Compare commits

...

25 Commits

Author SHA1 Message Date
uh-cli bot
77ea1dc571 gitops: update fuj-management
Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-12 18:01:46 +00:00
512b3cc2a6 gitops/fuj: split into per-app manifests, add fuj-management deployment
Rename generic deployment/service/httproute to per-app suffixes (_fujarna,
_fuj-management) and add fuj-management deployment, service, httproute, and
bot-credentials secret. Namespace name corrected to fuj.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 20:59:14 +02:00
3299373f3d gitops: rename namespace fujarna to fuj 2026-06-11 13:04:21 +02:00
80d0cc1168 misc: zot registry, k8s OIDC, server configs, sandbox experiments, and notes
- docker-30/zot: add Zot OCI registry with on-demand sync to docker.io,
  registry.k8s.io, ghcr.io, quay.io
- kubernetes-kvm-terraform: wire Kanidm OIDC via structured
  AuthenticationConfiguration; add reference apiserver manifest and
  join-node-02 helper
- servers: reorganize shadow/ under servers/, add saint vhost config and
  utility-101 VM definition, add shadow hrajfrisbee.cz vhost and
  storage-23 notes
- experiments: add notes and configs for e2b dev VM, kata + firecracker
  on kube, microsandbox, orb-stack k3s (terraform + cloud-init), rke2
- vms/docker: document tailscale + node-exporter setup
- blog: stub post on Gateway API
- chore: gitignore tmp/, smtp_password, and the two local-only
  credential caches; add per-project .claude/settings.json

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 18:12:38 +02:00
5ca27a832b gitops: upgrade Cilium to 1.19.1, add fujarna app, flux web UI, OIDC RBAC, and experiments
- Upgrade Cilium helm release from 1.18.5 to 1.19.1 with gatewayClass creation enabled
- Escalate gitea CI service account to cluster-admin, add OIDC cluster-admin binding
- Deploy fujarna app with full manifest set (deployment, service, PVC, httproutes, external secret)
- Add Flux web UI via flux-operator OCI repository and helm release
- Add experiments kustomization with test resources for gateway API and certificates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 22:23:12 +02:00
Jan Novak
96ba77a606 docker-30: gitea CI/CD integration with Vault and Kanidm, misc updates
vault:
- Add JWT auth backend bound to Gitea (jwks_url from gitea OIDC keys)
- Add gitea-ci-read policy scoped to secret/data/gitea/*
- Add JWT role gitea-ci (sub claim, bound to Gitea audience, 10m TTL)
- Add AppRole gitea-ci as alternative auth method for the same policy
- Add gitea-access-into-vault.md documenting the setup end-to-end
- Update terraform.tfstate (OpenTofu 1.11.5, new gitea-ci resources)

kanidm:
- Add run.sh with docker run command (pinned to v1.9.1)
- Add gitea-action-kubernetes-access.md documenting how to set up
  a Kanidm service account and OAuth2 client for Gitea CI k8s access
- readme: add upgrade procedure, recover-account command, and
  service account + API token setup for gitea-ci-token

maru-hleda-byt:
- Add --restart=always to docker run command

fuj-management:
- Add run.sh (new service config)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 23:09:52 +01:00
Jan Novak
dda6a9d032 vms: add monitoring stack and node-exporter for docker host
utility-101-shadow:
- Add full monitoring stack (Prometheus + Blackbox Exporter + Alertmanager)
  with Docker Compose and a systemd unit (monitoring.service)
- Prometheus scrapes: itself, blackbox-exporter, and node-exporter on
  the docker host (docker:9100); blackbox probes cover HTTPS endpoints
  with TLS cert monitoring
- Alertmanager routes warnings to Slack/Discord, critical alerts also
  to email (Gmail SMTP); inhibit rule suppresses SSLCertExpiringSoon
  when SSLCertExpired already fires
- Alert rules: 11 node-exporter alerts (host down, CPU, memory, disk
  fill/prediction, iowait, OOM kill, systemd failed units) + 3 blackbox
  alerts (probe failed, SSL expiring, SSL expired)
- readme: add services list and Docker Engine installation steps

docker host:
- Add node-exporter container running with host pid/network and
  read-only mounts of /proc, /sys, / for full host metrics visibility
- Enable --collector.systemd for systemd unit state metrics
- Add systemd unit (node-exporter.service) to manage the container

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 23:07:44 +01:00
Jan Novak
1b6015f732 gitops: fix kustomization: 00-rbac 2026-03-01 14:39:36 +01:00
Jan Novak
9877b093e8 gitops: add rbac kustomization + store some forgotten older changes in
repo
2026-03-01 14:33:56 +01:00
Jan Novak
0eab64c954 hosting: some config files for host: shadow, some named conf for
utility-101-shadow vm
2026-02-20 02:16:16 +01:00
Jan Novak
be362a5ab7 gitops/cilium: configure gateway and wildcard certificate it needs 2026-02-20 02:15:02 +01:00
Jan Novak
bb9f2ae3ce docker-30: several new and forgotten config files relevant to services
running in docker
2026-02-20 02:13:55 +01:00
Jan Novak
dc947165a4 gitops/ghost: add httproute resource aka gatewayApi instead of ingress 2026-02-20 02:13:09 +01:00
Jan Novak
1cd7625220 gitops/cert-manager: add dns challenger cluster issuer, add
deployment/service with socat proxy that works around my internet
provider's medling into dns traffic on port 53.
2026-02-20 02:11:50 +01:00
Jan Novak
409f8247e6 gitops/cert-manager: enable Gateway API support
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 01:43:04 +01:00
Jan Novak
8608696909 gitops/cilium: fix gateway.yaml indentation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 01:04:18 +01:00
Jan Novak
6454c893cb gitops/cilium: move gateway listeners from helm values to Gateway resource
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 01:02:14 +01:00
Jan Novak
b2daa822a6 gitops/cilium: configure gateway listeners and allow routes from all namespaces
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 00:51:37 +01:00
Jan Novak
8ae7b086a5 gitops/00-crds: add Gateway API v1.2.0 CRDs for Cilium gateway support
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 12:17:46 +01:00
Jan Novak
4b7ed6085b gitops/cilium: enable Gateway API and add HTTPRoute for ghost
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 11:55:49 +01:00
Jan Novak
0d97a796e9 gitops/velero: add manifests and runbook - kustomization is yet to be
created
2026-01-17 00:07:03 +01:00
Jan Novak
b9f99c2950 gitops/plane: fix issuer on ingress 2026-01-16 13:21:15 +01:00
Jan Novak
a20ae55b8f gitops/cilium: specify which interfaces it handles to not clash with
tailscaled
2026-01-15 01:24:49 +01:00
Jan Novak
36f447c39c gitops: assorted leftovers and fixes 2026-01-14 14:49:54 +01:00
Jan Novak
76e3ff9d03 kubernetes/terraform: several updates 2026-01-14 14:49:19 +01:00
145 changed files with 17681 additions and 50 deletions

10
.claude/settings.json Normal file
View File

@@ -0,0 +1,10 @@
{
"permissions": {
"allow": [
"Bash(for f:*)",
"Bash(do echo:*)",
"Read(//Users/jan.novak/srv/personal/home-kubernetes/**)",
"Bash(done)"
]
}
}

16
.gitignore vendored
View File

@@ -1,4 +1,14 @@
.terraform/
.DS_Store
./kubernetes-kvm-terraform/join-command.txt
./kubernetes-kvm-terraform/kubeconfig
.terraform/
.terraform.lock.hcl
kubernetes-kvm-terraform/join-command.txt
kubernetes-kvm-terraform/kubeconfig
tmp/
vms/utility-101-shadow/docker/monitoring/smtp_password
docker-30/zot/sync-credentials.json
kubernetes-kvm-terraform/gke_gcloud_auth_plugin_cache

View File

@@ -0,0 +1,7 @@
# How i did have some fun with gatewayApi
As nginx-ingress will be no longer maintained it looks like the time to upgrade is coming even for the lazy old schoolers who were very happy with it (even though annotations and custom snippets might not be the cleanest way, people experienced with nginx might have liked it for a reason). Although I tried to pretend that there is no gatewayApi and ingress is the thing for quite some time recently everything aligned for me to give it a go.
## Which implementation to choose?
There is a number of options and I'm only vaguely aware of them as I was not interested in it for a long time. But i have a tendency of trying to do something with Envoy (personal feeling that it is the right next thing here?) and I'm using the Cilium network plugin in my homelab kubernetes cluster. All this considered answer is obvious: use envoy gateway which is part of cilium.

View File

@@ -0,0 +1,10 @@
#!/bin/bash
docker rm -f fuj-management
# gitea registry login with kacerr / token
docker run -d --name fuj-management \
--restart=always \
-p 8081:5001 \
-v /srv/fuj-management/data:/app/data \
gitea.home.hrajfrisbee.cz/kacerr/fuj-management:latest

View File

@@ -57,6 +57,15 @@ services:
- GITEA__server__ROOT_URL=https://gitea.home.hrajfrisbee.cz
- GITEA__security__SECRET_KEY=${GITEA_SECRET_KEY}
- GITEA__security__INTERNAL_TOKEN=${INTERNAL_TOKEN}
- GITEA__mailer__ENABLED=true
- GITEA__mailer__PROTOCOL=smtps
- GITEA__mailer__SMTP_ADDR=smtp.gmail.com
- GITEA__mailer__SMTP_PORT=465
- GITEA__mailer__USER=kacerr.cz@gmail.com
- GITEA__mailer__PASSWD=${GMAIL_GITEA_APP_PASSWORD}
- GITEA__mailer__FROM=kacerr.cz+gitea@gmail.com
- GITEA__packages__ENABLED=true
#- GITEA__storage__STORAGE_TYPE=minio
#- GITEA__storage__MINIO_ENDPOINT=minio:9000
#- GITEA__storage__MINIO_ACCESS_KEY_ID=gitea
@@ -83,10 +92,12 @@ services:
depends_on:
- gitea
environment:
GITEA_INSTANCE_URL: http://gitea:3000
GITEA_INSTANCE_URL: https://gitea.home.hrajfrisbee.cz/
GITEA_RUNNER_REGISTRATION_TOKEN: ${RUNNER_TOKEN}
CONFIG_FILE: /config/config.yaml
volumes:
- ./runner-data:/data
- ./runner-config.yaml:/config/config.yaml:ro
- /var/run/docker.sock:/var/run/docker.sock
networks:
- gitea-network

View File

@@ -0,0 +1,84 @@
## 1. Create Kanidm service account + OAuth2 client
```bash
# Create a service account for CI
kanidm service-account create gitea_ci "Gitea CI Deploy" idm_admins --name idm_admin
# Create a group and add the service account
kanidm group create k8s_deployers
kanidm group add-members k8s_deployers gitea_ci
# Create the OAuth2 client (or reuse existing k8s one)
# If you already have a k8s OIDC client, just add scope maps:
kanidm system oauth2 update-scope-map k8s k8s_deployers openid groups
# Generate an API token for the service account
kanidm service-account api-token generate --name idm_admin gitea_ci "gitea-ci-token"
# ⚠️ Save the output token — this is the subject_token for exchange
```
## 2. RBAC in Kubernetes
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: gitea-ci-deploy
subjects:
- kind: User
name: "gitea_ci@idm.home.hrajfrisbee.cz" # matches preferred_username claim
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: edit # scope down as needed
apiGroup: rbac.authorization.k8s.io
```
## 3. Token exchange + kubeconfig setup (test in bash)
```bash
vault-login # prepared alias
#!/usr/bin/env bash
# set -euo pipefail
KANIDM_URL="https://idm.home.hrajfrisbee.cz"
OAUTH2_CLIENT_ID="k8s" # your k8s OIDC client name in Kanidm
API_TOKEN=$(vault kv get -format=json -mount="secret" "k8s_home/gitea/gitea-ci-token" |jq -r .data.data.token)
K8S_API="https://192.168.0.31:6443"
# Exchange the API token for an OIDC token via RFC 8693
RESPONSE=$(curl -sf -X POST "${KANIDM_URL}/oauth2/token" \
-d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "client_id=${OAUTH2_CLIENT_ID}" \
-d "subject_token=${API_TOKEN}" \
-d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
-d "audience=${OAUTH2_CLIENT_ID}" \
-d "scope=openid groups")
ID_TOKEN=$(echo "$RESPONSE" | jq -r '.id_token')
# Inspect claims (sanity check)
echo "$ID_TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq .
# Build kubeconfig
export KUBECONFIG=$(mktemp)
kubectl config set-cluster mycluster \
--server="${K8S_API}" \
--certificate-authority=/path/to/ca.crt
kubectl config set-credentials gitea-ci \
--token="${ID_TOKEN}"
kubectl config set-context gitea-ci \
--cluster=mycluster \
--user=gitea-ci
kubectl config use-context gitea-ci
# Test
kubectl auth whoami
kubectl get ns
```

View File

@@ -1,3 +1,27 @@
## Upgrade
```bash
docker exec -it kanidmd kanidmd domain upgrade-check
# make sure backup exists: /srv/docker/kanidm/data/kanidm/backups
# change container image in: /srv/docker/kanidm/run.sh
# kanidm data restore
docker stop kanidmd
docker run --rm -it \
-v kanidmd:/data \
-v kanidmd_backups:/backup \
kanidm/server:latest \
/sbin/kanidmd database restore -c /data/server.toml /backup/kanidm.backup.json
docker start kanidmd
```
## Recover passwords from kanidm instance
```bash
docker exec -i -t kanidmd kanidmd recover-account idm_admin
```
## add user to k8s group
based on: https://blog.kammel.dev/post/k8s_home_lab_2025_06/
@@ -54,6 +78,50 @@ kanidm person get novakj | grep memberof
kanidm group get idm_people_self_name_write
```
## configure oauth proxy
```bash
kanidm system oauth2 create oauth2-proxy "OAuth2 Proxy" https://oauth2-proxy.lab.home.hrajfrisbee.cz/oauth2/callback
kanidm system oauth2 set-landing-url oauth2-proxy https://oauth2-proxy.lab.home.hrajfrisbee.cz
kanidm system oauth2 enable-pkce oauth2-proxy
kanidm system oauth2 warning-insecure-client-disable-pkce oauth2-proxy # if proxy doesn't support PKCE
kanidm system oauth2 get oauth2-proxy # note the client secret
# update incorrect urls if needed
remove-redirect-url
kanidm system oauth2 add-redirect-url oauth2-proxy https://oauth2-proxy.lab.home.hrajfrisbee.cz/oauth2/callback
kanidm system oauth2 set-landing-url oauth2-proxy https://oauth2-proxy.lab.home.hrajfrisbee.cz
# output
✔ Multiple authentication tokens exist. Please select one · idm_admin@idm.home.hrajfrisbee.cz
---
class: account
class: key_object
class: key_object_internal
class: key_object_jwe_a128gcm
class: key_object_jwt_es256
class: memberof
class: oauth2_resource_server
class: oauth2_resource_server_basic
class: object
displayname: OAuth2 Proxy
key_internal_data: 69df0a387991455f7c9800f13b881803: valid jwe_a128gcm 0
key_internal_data: c5f61c48a9c0eb61ba993a36748826cc: valid jws_es256 0
name: oauth2-proxy
oauth2_allow_insecure_client_disable_pkce: true
oauth2_rs_basic_secret: hidden
oauth2_rs_origin_landing: https://oauth2-proxylab.home.hrajfrisbee.cz/
oauth2_strict_redirect_uri: true
spn: oauth2-proxy@idm.home.hrajfrisbee.cz
uuid: d0dcbad5-90e4-4e36-a51b-653624069009
secret: 7KJbUe5x35NVCT1VbzZfhYBU19cz9Xe9Z1fvw4WazrkHX2c8
kanidm system oauth2 update-scope-map oauth2-proxy k8s_users openid profile email
```
```bash
@@ -71,4 +139,31 @@ docker run --rm -i -t -v --restart=always \
docker.io/kanidm/server:latest \
kanidmd cert-generate
```
## Service account for gitea runner
```bash
# create service account
#kanidm service-account create \
# gitea_ci \ # account name
# "Gitea CI Deploy" \ # display name
# idm_admins \ # entry-managed-by (delegation group)
# --name idm_admin # authenticate as this user
kanidm service-account create gitea_ci "Gitea CI Deploy" idm_admins --name idm_admin
# Create a group and add the service account
kanidm group create k8s_deployers
kanidm group add-members k8s_deployers gitea_ci
# Create the OAuth2 client (or reuse existing k8s one)
# If you already have a k8s OIDC client, just add scope maps:
kanidm system oauth2 update-scope-map k8s k8s_deployers openid groups
# Generate an API token for the service account
kanidm service-account api-token generate --name idm_admin gitea_ci "gitea-ci-token"
# ⚠️ Save the output token — this is the subject_token for exchange
```

9
docker-30/kanidm/run.sh Normal file
View File

@@ -0,0 +1,9 @@
docker rm -f kanidmd
docker run -d --name=kanidmd --restart=always \
-p '8443:8443' \
-p '3636:3636' \
--volume /srv/docker/kanidm/data:/data \
docker.io/kanidm/server:1.9.1
# previous version: 1.8.5

View File

@@ -0,0 +1,46 @@
# nginx.conf
error_log /dev/stderr;
http {
server {
listen 9080;
location / {
proxy_pass http://192.168.0.35:80;
proxy_set_header Host $host;
}
}
log_format detailed '$remote_addr - [$time_local] '
'"$request_method $host$request_uri" '
'$status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
access_log /dev/stdout detailed;
}
stream {
# Stream doesn't log by default, enable explicitly:
log_format stream_log '$remote_addr [$time_local] '
'$protocol $ssl_preread_server_name '
'$status $bytes_sent $bytes_received $session_time';
access_log /dev/stdout stream_log;
# Nginx ingress in kubernetes
server {
listen 9443;
proxy_pass 192.168.0.35:443;
}
# Gateway provided by cilium/envoy
server {
listen 9444;
proxy_pass 192.168.0.36:443;
}
}
events {}

View File

@@ -0,0 +1,9 @@
docker rm -f lab-proxy || /usr/bin/true
docker run -d --name lab-proxy \
--restart unless-stopped \
-v /srv/docker/lab-proxy/nginx.conf:/etc/nginx/nginx.conf:ro \
-p 9443:9443 \
-p 9444:9444 \
-p 9080:9080 \
nginx:alpine

View File

@@ -0,0 +1,10 @@
#!/bin/bash
docker rm -f maru-hleda-byt
# gitea registry login with kacerr / token
docker run -d --name maru-hleda-byt \
--restart=always \
-p 8080:8080 \
-v /srv/maru-hleda-byt/data:/app/data \
gitea.home.hrajfrisbee.cz/littlemeat/maru-hleda-byt:0.01

View File

@@ -0,0 +1,22 @@
server {
listen 443 ssl http2;
server_name gitea.home.hrajfrisbee.cz;
ssl_certificate /etc/letsencrypt/live/gitea.home.hrajfrisbee.cz/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/gitea.home.hrajfrisbee.cz/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
location / {
proxy_pass http://192.168.0.30:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Gitea Git over HTTP
client_max_body_size 512m;
}

View File

@@ -0,0 +1,35 @@
server {
listen 443 ssl http2;
server_name jellyfin.home.hrajfrisbee.cz;
ssl_certificate /etc/letsencrypt/live/gitea.home.hrajfrisbee.cz/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/gitea.home.hrajfrisbee.cz/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# Security headers for media streaming
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
# Increase body size for high-res movie posters
client_max_body_size 20M;
location / {
# Proxy to your Synology or VM IP and Jellyfin port (default 8096)
proxy_pass http://192.168.0.2:8096;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
# Disable buffering for smoother streaming
proxy_buffering off;
}
}

View File

@@ -0,0 +1,10 @@
## 1. Enable & configure JWT auth in Vault
```bash
vault auth enable jwt
vault write auth/jwt/config \
bound_issuer="https://gitea.home.hrajfrisbee.cz" \
jwks_url="https://gitea.home.hrajfrisbee.cz/login/oauth/keys"
```

View File

@@ -4,6 +4,13 @@ resource "vault_mount" "kv" {
description = "KV v2 secrets engine"
}
resource "vault_jwt_auth_backend" "gitea" {
path = "jwt"
type = "jwt"
bound_issuer = "https://gitea.home.hrajfrisbee.cz"
jwks_url = "https://gitea.home.hrajfrisbee.cz/login/oauth/keys"
}
resource "vault_policy" "eso_read" {
name = "external-secrets-read"
policy = <<-EOT
@@ -16,6 +23,37 @@ resource "vault_policy" "eso_read" {
EOT
}
# for now i allow my gitea to read everything in /v1/secret/data/gitea
resource "vault_policy" "gitea_ci_read" {
name = "gitea-ci-read"
policy = <<-EOT
path "${vault_mount.kv.path}/data/gitea/*" {
capabilities = ["read"]
}
path "${vault_mount.kv.path}/metadata/gitea/*" {
capabilities = ["read", "list"]
}
EOT
}
resource "vault_jwt_auth_backend_role" "gitea_ci" {
backend = vault_jwt_auth_backend.gitea.path
role_name = "gitea-ci"
role_type = "jwt"
token_policies = [vault_policy.gitea_ci_read.name]
user_claim = "sub"
bound_audiences = ["https://gitea.home.hrajfrisbee.cz"]
# allow any valid jwt token when commented out
# bound_claims = {
# repository = "myorg/repo1,myorg/repo3"
# }
token_ttl = 600
token_max_ttl = 1200
}
resource "vault_auth_backend" "approle" {
type = "approle"
}
@@ -46,4 +84,32 @@ output "role_id" {
output "secret_id" {
value = vault_approle_auth_backend_role_secret_id.eso.secret_id
sensitive = true
}
resource "vault_approle_auth_backend_role" "gitea_ci" {
backend = vault_auth_backend.approle.path
role_name = "gitea-ci"
token_policies = [vault_policy.gitea_ci_read.name]
token_ttl = 600
token_max_ttl = 1200
}
data "vault_approle_auth_backend_role_id" "gitea_ci" {
backend = vault_auth_backend.approle.path
role_name = vault_approle_auth_backend_role.gitea_ci.role_name
}
resource "vault_approle_auth_backend_role_secret_id" "gitea_ci" {
backend = vault_auth_backend.approle.path
role_name = vault_approle_auth_backend_role.gitea_ci.role_name
}
output "gitea_ci_role_id" {
value = data.vault_approle_auth_backend_role_id.gitea_ci.role_id
sensitive = true
}
output "gitea_ci_secret_id" {
value = vault_approle_auth_backend_role_secret_id.gitea_ci.secret_id
sensitive = true
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -29,10 +29,10 @@ log "Backup size: ${BACKUP_SIZE} bytes"
# --- Upload to MinIO ---
log "Uploading to ${MC_ALIAS}/${S3_BUCKET}..."
set -x
mc cp --quiet "${BACKUP_FILE}" "${MC_ALIAS}/${S3_BUCKET}/vault-backup-${TIMESTAMP}.tar.gz"
minio-cli cp --quiet "${BACKUP_FILE}" "${MC_ALIAS}/${S3_BUCKET}/vault-backup-${TIMESTAMP}.tar.gz"
# --- Prune old backups ---
log "Pruning backups older than ${RETENTION_DAYS} days..."
mc rm --quiet --recursive --force --older-than "${RETENTION_DAYS}d" "${MC_ALIAS}/${S3_BUCKET}/"
minio-cli rm --quiet --recursive --force --older-than "${RETENTION_DAYS}d" "${MC_ALIAS}/${S3_BUCKET}/"
log "Backup complete: vault-backup-${TIMESTAMP}.tar.gz"

52
docker-30/zot/config.yaml Normal file
View File

@@ -0,0 +1,52 @@
distSpecVersion: "1.1.0"
storage:
rootDirectory: /var/lib/zot
gc: true
gcDelay: "24h"
dedupe: true
http:
address: 0.0.0.0
port: 5000
compat:
- docker2s2
log:
# level: info
level: debug
extensions:
ui:
enable: true
search:
enable: true
sync:
enable: true
credentialsFile: "/etc/zot/sync-credentials.json"
registries:
- urls: ["https://registry-1.docker.io"]
onDemand: true
tlsVerify: true
content:
- prefix: "library/**"
destination: "/docker.io/library"
- prefix: "democraticcsi/**"
destination: "/democraticcsi"
- prefix: "**"
destination: "/docker.io"
- urls: ["https://registry.k8s.io"]
onDemand: true
tlsVerify: true
content:
- prefix: "**"
destination: "/registry.k8s.io"
- urls: ["https://ghcr.io"]
onDemand: true
tlsVerify: true
content:
- prefix: "**"
destination: "/ghcr.io"
- urls: ["https://quay.io"]
onDemand: true
tlsVerify: true
content:
- prefix: "**"
destination: "/quay.io"

View File

@@ -0,0 +1,12 @@
services:
zot:
image: ghcr.io/project-zot/zot-linux-amd64:latest
container_name: zot
restart: unless-stopped
command: serve /etc/zot/config.yaml
ports:
- "5000:5000"
volumes:
- ./config.yaml:/etc/zot/config.yaml:ro
- ./sync-credentials.json:/etc/zot/sync-credentials.json:ro
- /srv/container-registry-data:/var/lib/zot

View File

@@ -0,0 +1,391 @@
# E2B Dev VM Setup on KVM Homelab
## Context
The user wants to run the E2B infrastructure dev stack on a KVM virtual machine in their homelab. E2B uses Firecracker microVMs (which need `/dev/kvm`), so the guest VM needs **nested virtualization** (KVM-in-KVM). This guide covers VM creation, OS configuration, toolchain installation, and running the full dev stack.
> **Note from upstream**: [DEV-LOCAL.md](DEV-LOCAL.md) says "Linux is required. This is a work in progress. Not everything will function as expected."
---
## Phase 1: Create the KVM VM on the Hypervisor Host
### 1.1 Enable nested virtualization on the host
**Intel:**
```bash
cat /sys/module/kvm_intel/parameters/nested # check
sudo modprobe -r kvm_intel && sudo modprobe kvm_intel nested=1
echo "options kvm_intel nested=1" | sudo tee /etc/modprobe.d/kvm-nested.conf
```
**AMD:**
```bash
cat /sys/module/kvm_amd/parameters/nested
sudo modprobe -r kvm_amd && sudo modprobe kvm_amd nested=1
echo "options kvm_amd nested=1" | sudo tee /etc/modprobe.d/kvm-nested.conf
# get details about loaded kernel module
systool -v -m kvm_amd
```
### 1.2 Create the VM
```bash
virt-install \
--name e2b-dev \
--ram 16384 \
--vcpus 8 \
--cpu host-passthrough \
--os-variant ubuntu24.04 \
--disk path=/var/lib/libvirt/images/e2b-dev.qcow2,size=100,format=qcow2,bus=virtio \
--network bridge=virbr0,model=virtio \
--graphics none \
--console pty,target_type=serial \
--cdrom /path/to/ubuntu-24.04-live-server-amd64.iso \
--extra-args 'console=ttyS0,115200n8'
```
**Why these specs:**
| Resource | Value | Rationale |
|----------|-------|-----------|
| RAM | 16 GB (24-32 better) | 4 GB for huge pages (2048 x 2MB), ~4 GB for 10 Docker containers, rest for Go services + Firecracker VMs |
| vCPUs | 8 | Firecracker VMs consume vCPUs; Go services are concurrent |
| Disk | 100 GB | Docker images, FC binaries, kernels, rootfs, Go cache, build artifacts |
| CPU | `host-passthrough` | **Mandatory** -- exposes VMX/SVM to guest so `/dev/kvm` works inside the VM |
### 1.3 Alternative: libvirt XML
If you manage VMs declaratively, the critical part is:
```xml
<cpu mode='host-passthrough' check='none' migratable='off'/>
```
### 1.4 Verify nested KVM works (after OS install)
```bash
ls -la /dev/kvm # must exist
lsmod | grep kvm # kvm + kvm_intel/kvm_amd
grep -cE '(vmx|svm)' /proc/cpuinfo # must be > 0
```
If `/dev/kvm` is missing, go back to 1.1.
---
## Phase 2: OS Configuration (inside the guest VM)
**Recommended OS:** Ubuntu 24.04 LTS Server (matches CI, well-tested with Firecracker)
### 2.1 Base packages
```bash
sudo apt update && sudo apt upgrade -y
sudo apt install -y \
build-essential git curl wget unzip jq make gcc pkg-config \
iptables iproute2 net-tools ca-certificates gnupg \
lsb-release software-properties-common gettext-base
```
### 2.2 Kernel modules
```bash
# Load now
sudo modprobe nbd nbds_max=64
sudo modprobe kvm
sudo modprobe kvm_intel # or kvm_amd
sudo modprobe tun
sudo modprobe veth
sudo modprobe nf_tables
sudo modprobe nft_nat
# Persist across reboots
cat <<'EOF' | sudo tee /etc/modules-load.d/e2b.conf
nbd
kvm
kvm_intel
tun
veth
nf_tables
nft_nat
EOF
echo "options nbd nbds_max=64" | sudo tee /etc/modprobe.d/nbd.conf
```
### 2.3 Sysctl
```bash
cat <<'EOF' | sudo tee /etc/sysctl.d/99-e2b.conf
vm.nr_hugepages=2048
vm.max_map_count=1048576
vm.swappiness=10
vm.vfs_cache_pressure=50
net.ipv4.ip_forward=1
net.core.somaxconn=65535
net.core.netdev_max_backlog=65535
net.ipv4.tcp_max_syn_backlog=65535
EOF
sudo sysctl --system
```
### 2.4 Udev rules (suppress NBD inotify noise)
```bash
cat <<'EOF' | sudo tee /etc/udev/rules.d/99-e2b-nbd.rules
KERNEL=="nbd*", OPTIONS+="nowatch"
EOF
sudo udevadm control --reload-rules && sudo udevadm trigger
```
### 2.5 File descriptor limits
```bash
cat <<'EOF' | sudo tee /etc/security/limits.d/e2b.conf
* soft nofile 1048576
* hard nofile 1048576
root soft nofile 1048576
root hard nofile 1048576
EOF
```
---
## Phase 3: Install Toolchain
### 3.1 Docker
```bash
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Log out and back in
docker --version && docker compose version
```
### 3.2 mise (manages all tools from `.tool-versions`)
```bash
curl https://mise.run | sh
echo 'eval "$(~/.local/bin/mise activate bash)"' >> ~/.bashrc
source ~/.bashrc
```
### 3.3 Install project tools
The repo's [.tool-versions](.tool-versions) pins:
| Tool | Version |
| --- | --- |
| golang | 1.25.4 |
| buf | 1.28.1 |
| bun | 1.3.2 |
| protoc | 29.3 |
| protoc-gen-go | 1.28.1 |
| protoc-gen-go-grpc | 1.6.1 |
| protoc-gen-connect-go | 1.18.1 |
| golangci-lint | 2.8.0 |
| terraform | 1.5.7 |
| packer | 1.13.1 |
| python | 3.13.11 |
| gcloud | 534.0.0 |
```bash
cd ~/e2b-infra # after cloning
mise install # installs everything from .tool-versions
```
> **Note:** `gcloud` is only needed for `gsutil` to download prebuilt artifacts. If you don't have GCP credentials, you can download them via HTTPS instead (see Phase 5).
---
## Phase 4: Clone Repository
```bash
git clone https://github.com/e2b-dev/infra.git ~/e2b-infra
cd ~/e2b-infra
go work sync
```
---
## Phase 5: Download Prebuilt Artifacts
Firecracker binaries and Linux kernels must be downloaded from the public GCS bucket.
**With gsutil (if gcloud is configured):**
```bash
make download-public-kernels
make download-public-firecrackers
```
**Without GCP credentials (HTTPS alternative):**
The bucket `e2b-prod-public-builds` is publicly accessible. You'll need to browse/list it to find available versions, then download manually:
```bash
# Install gsutil standalone (no GCP project needed for public buckets)
# OR use curl/wget against:
# https://storage.googleapis.com/e2b-prod-public-builds/
# to discover and download kernel and firecracker builds
```
The simplest path is to install just the `gcloud` CLI (via mise) and run the make targets -- no GCP project or auth is needed for public bucket reads.
---
## Phase 6: Prepare Local Environment
### 6.1 Start infrastructure containers
```bash
make local-infra
# Starts: PostgreSQL 17.4, Redis 7.4.2, ClickHouse 25.4.5.24,
# Grafana 12.0.0, Loki 3.4.1, Tempo 2.8.2, Mimir 2.17.1,
# OTEL Collector 0.146.0, Vector, Memcached 1.6.38
```
Wait for all containers to be healthy:
```bash
docker compose -f packages/local-dev/docker-compose.yaml ps
```
### 6.2 Initialize databases
```bash
make -C packages/db migrate-local # PostgreSQL
make -C packages/clickhouse migrate-local # ClickHouse
```
### 6.3 Build envd (in-VM daemon)
```bash
make -C packages/envd build
```
### 6.4 Seed database with dev credentials
```bash
make -C packages/local-dev seed-database
```
Creates test user, team, API key, and access token for local development.
---
## Phase 7: Run the Dev Stack
Each service runs in the foreground. Use **tmux**, **screen**, or separate SSH sessions.
| Terminal | Command | Listens on |
| --- | --- | --- |
| 1 | `make local-infra` | (Docker containers) |
| 2 | `make -C packages/api run-local` | `:3000` |
| 3 | `make -C packages/orchestrator build-debug && sudo make -C packages/orchestrator run-local` | `:5008` |
| 4 | `make -C packages/client-proxy run-local` | `:3002` |
> The orchestrator **requires sudo** -- Firecracker needs root for `/dev/kvm`, network namespaces, veth pairs, nftables rules, and NBD devices.
---
## Phase 8: Verify
### 8.1 Health checks
```bash
curl -s http://localhost:3000/health # API
curl -s -o /dev/null -w "%{http_code}" http://localhost:53000 # Grafana (expect 302)
curl -s 'http://localhost:8123/?query=SELECT%201' # ClickHouse
redis-cli -h localhost -p 6379 ping # Redis
```
### 8.2 Build the base template
```bash
make -C packages/shared/scripts local-build-base-template
```
### 8.3 Test with E2B client SDK
```bash
export E2B_API_KEY=e2b_53ae1fed82754c17ad8077fbc8bcdd90
export E2B_ACCESS_TOKEN=sk_e2b_89215020937a4c989cde33d7bc647715
export E2B_API_URL=http://localhost:3000
export E2B_SANDBOX_URL=http://localhost:3002
# Use E2B SDK/CLI to create a sandbox
```
---
## Phase 9: Access from Host (Optional)
### SSH port forwarding
```bash
ssh -N \
-L 3000:localhost:3000 \
-L 3002:localhost:3002 \
-L 53000:localhost:53000 \
-L 5432:localhost:5432 \
-L 8123:localhost:8123 \
user@<vm-ip>
```
### Or use bridged networking
If the VM has a routable IP on your LAN, services are directly accessible (Docker binds to `0.0.0.0`, Go services listen on all interfaces).
---
## Service Endpoints Reference
| Service | URL |
| --- | --- |
| E2B API | `http://localhost:3000` |
| E2B Client Proxy | `http://localhost:3002` |
| E2B Orchestrator | `http://localhost:5008` |
| Grafana | `http://localhost:53000` |
| PostgreSQL | `postgres://postgres:postgres@localhost:5432` |
| ClickHouse (HTTP) | `http://localhost:8123` |
| ClickHouse (native) | `localhost:9000` |
| Redis | `localhost:6379` |
| OTEL Collector (gRPC) | `localhost:4317` |
| OTEL Collector (HTTP) | `localhost:4318` |
| Loki | `http://localhost:3100` |
| Vector | `localhost:30006` |
---
## Troubleshooting
| Problem | Fix |
| --- | --- |
| `/dev/kvm` missing in guest | Enable nested virt on host (Phase 1.1), use `host-passthrough` CPU |
| `modprobe nbd` fails | Check kernel has NBD support: `modinfo nbd` |
| Orchestrator permission errors | Must run with `sudo` |
| Huge pages < 2048 | Not enough contiguous memory; increase VM RAM or set earlier in boot |
| Docker containers won't start | Check `systemctl status docker`, port conflicts with `ss -tlnp` |
| `gsutil` not found | Install via `mise install gcloud` or download artifacts via HTTPS |
---
## After VM Reboot Checklist
```bash
# 1. Verify kernel modules and sysctl (should be persistent)
lsmod | grep nbd
cat /proc/sys/vm/nr_hugepages # expect 2048
# 2. Start infra
cd ~/e2b-infra && make local-infra
# 3. Start services (separate terminals)
make -C packages/api run-local
sudo make -C packages/orchestrator run-local
make -C packages/client-proxy run-local
```

View File

@@ -0,0 +1,95 @@
## dev-vm on beelink
```bash
virt-install \
--name e2b-dev \
--ram 16384 \
--vcpus 8 \
--cpu host-passthrough \
--os-variant ubuntu24.04 \
--disk path=/srv/vms/e2b-dev.qcow2,size=100,format=qcow2,bus=virtio \
--network bridge=br0,model=virtio \
--graphics none \
--console pty,target_type=serial \
--location /srv/vms/isos/ubuntu-24.04.3-live-server-amd64.iso,kernel=casper/vmlinuz,initrd=casper/initrd \
--extra-args 'console=ttyS0,115200n8'
# base packages
sudo apt update && sudo apt upgrade -y
sudo apt install -y \
build-essential git curl wget unzip jq make gcc pkg-config \
iptables iproute2 net-tools ca-certificates gnupg \
lsb-release software-properties-common gettext-base
# kernel modules
# Load now
sudo modprobe nbd nbds_max=64
sudo modprobe kvm
sudo modprobe kvm_amd # or kvm_amd
sudo modprobe tun
sudo modprobe veth
sudo modprobe nf_tables
sudo modprobe nft_nat
# Persist across reboots
cat <<'EOF' | sudo tee /etc/modules-load.d/e2b.conf
nbd
kvm
kvm_amd
tun
veth
nf_tables
nft_nat
EOF
echo "options nbd nbds_max=64" | sudo tee /etc/modprobe.d/nbd.conf
# sysctl
cat <<'EOF' | sudo tee /etc/sysctl.d/99-e2b.conf
vm.nr_hugepages=2048
vm.max_map_count=1048576
vm.swappiness=10
vm.vfs_cache_pressure=50
net.ipv4.ip_forward=1
net.core.somaxconn=65535
net.core.netdev_max_backlog=65535
net.ipv4.tcp_max_syn_backlog=65535
EOF
sudo sysctl --system
# udev rules
cat <<'EOF' | sudo tee /etc/udev/rules.d/99-e2b-nbd.rules
KERNEL=="nbd*", OPTIONS+="nowatch"
EOF
sudo udevadm control --reload-rules && sudo udevadm trigger
# file descriptor limits
cat <<'EOF' | sudo tee /etc/security/limits.d/e2b.conf
* soft nofile 1048576
* hard nofile 1048576
root soft nofile 1048576
root hard nofile 1048576
EOF
```
## install toolchain
```bash
# docker
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Log out and back in
docker --version && docker compose version
# mise
curl https://mise.run | sh
echo 'eval "$(~/.local/bin/mise activate bash)"' >> ~/.bashrc
source ~/.bashrc
```
```

View File

@@ -0,0 +1,280 @@
# E2B Sandbox Usage Guide
A practical guide for creating, observing, and using E2B sandboxes via the API.
## Prerequisites
### Local Dev Setup
Seed the database to create a test team and API key:
```bash
make -C packages/local-dev seed-database
```
### Environment Variables
```bash
E2B_API_KEY=e2b_53ae1fed82754c17ad8077fbc8bcdd90
E2B_ACCESS_TOKEN=sk_e2b_89215020937a4c989cde33d7bc647715
E2B_API_URL=http://localhost:3000
E2B_SANDBOX_URL=http://localhost:3002
```
All examples below use `$E2B_API_URL` and `$E2B_API_KEY` — export them in your shell for convenience:
```bash
export E2B_API_URL=http://localhost:3000
export E2B_API_KEY=e2b_53ae1fed82754c17ad8077fbc8bcdd90
```
---
## 1. Create a Sandbox
```bash
curl -X POST $E2B_API_URL/sandboxes \
-H "X-API-Key: $E2B_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"templateID": "base",
"timeout": 300
}'
```
**Response:**
```json
{
"sandboxID": "i1234567890abcdef",
"templateID": "base",
"envdVersion": "0.5.8",
"domain": "i1234567890abcdef.your-domain"
}
```
Save the `sandboxID` and `domain` from the response for subsequent commands.
### Optional Parameters
| Parameter | Type | Description |
|-----------|------|-------------|
| `secure` | bool | Returns an `envdAccessToken` for authenticated envd access |
| `envVars` | object | Inject environment variables into the VM |
| `metadata` | object | Tag the sandbox for filtering (e.g. `{"purpose": "test"}`) |
| `autoPause` | bool | Pause instead of kill on timeout |
| `allow_internet_access` | bool | Allow internet egress from the VM |
| `network.allowPublicTraffic` | bool | Allow inbound public traffic |
| `network.allowOut` | array | Allowed egress destinations (IPs, CIDRs, domains) |
| `network.denyOut` | array | Denied egress destinations (IPs/CIDRs only) |
| `volumeMounts` | array | Mount persistent volumes (`{"name": "vol", "path": "/data"}`) |
**Example with options:**
```bash
curl -X POST $E2B_API_URL/sandboxes \
-H "X-API-Key: $E2B_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"templateID": "base",
"timeout": 300,
"secure": true,
"envVars": {"MY_VAR": "hello"},
"metadata": {"purpose": "demo"}
}'
```
---
## 2. Observe Sandbox State
### Get details of a specific sandbox
```bash
curl $E2B_API_URL/sandboxes/{sandboxID} \
-H "X-API-Key: $E2B_API_KEY"
```
Returns state (`running`/`paused`), CPU/memory/disk config, network settings, and TTL (`endAt`).
### List all sandboxes
```bash
# Running only
curl "$E2B_API_URL/v2/sandboxes?state=running" \
-H "X-API-Key: $E2B_API_KEY"
# Running and paused
curl "$E2B_API_URL/v2/sandboxes?state=running&state=paused" \
-H "X-API-Key: $E2B_API_KEY"
# Filter by metadata
curl "$E2B_API_URL/v2/sandboxes?metadata=purpose%3Ddemo" \
-H "X-API-Key: $E2B_API_KEY"
```
### Get resource metrics (CPU, memory, disk)
```bash
curl "$E2B_API_URL/sandboxes/{sandboxID}/metrics?start=$(date -v-5M +%s)&end=$(date +%s)" \
-H "X-API-Key: $E2B_API_KEY"
```
**Response:**
```json
[
{
"timestampUnix": 1234567890,
"cpuCount": 2,
"cpuUsedPct": 25.5,
"memUsed": 268435456,
"memTotal": 536870912,
"diskUsed": 1073741824,
"diskTotal": 5368709120
}
]
```
### Get sandbox logs
```bash
curl "$E2B_API_URL/v2/sandboxes/{sandboxID}/logs?limit=100&direction=backward" \
-H "X-API-Key: $E2B_API_KEY"
```
Optional query parameters: `cursor` (ms timestamp), `level` (min log level), `search` (substring match).
---
## 3. Use the Sandbox
The in-VM daemon (**envd**) runs on port 49983 inside each sandbox, exposed via the sandbox's `domain`.
### Upload a file
```bash
curl -X POST https://{domain}/files \
-H "Content-Type: multipart/form-data" \
-F "file=@script.py" \
-F "path=/home/user/script.py"
```
### Using the E2B Python SDK
Point the SDK at your local API:
```python
from e2b import Sandbox
sbx = Sandbox("base", api_url="http://localhost:3000")
# Run a command
result = sbx.commands.run("echo 'Hello from Firecracker VM!'")
print(result.stdout)
# Write and execute a file
sbx.files.write("/home/user/hello.py", "print('Hello world')")
result = sbx.commands.run("python3 /home/user/hello.py")
print(result.stdout)
# List files
files = sbx.files.list("/home/user")
for f in files:
print(f.name)
sbx.kill()
```
### Envd Connect RPC API
The envd daemon exposes Connect RPC services for programmatic access:
**Process Service:**
- `Start(ProcessConfig)` — start a new process
- `List()` — list running processes
- `Connect(ProcessSelector)` — connect to process stdio
- `Signal(ProcessSelector, Signal)` — send signal to process
**Filesystem Service:**
- `ListDir(Path)` — list directory contents
- `Stat(Path)` — get file metadata
- `WatchDir(Path)` — watch for changes
- `Move(Source, Dest)` — move/rename file
- `RemoveDir(Path)` — remove directory
---
## 4. Lifecycle Management
### Extend timeout
```bash
# Add 60 seconds to the TTL
curl -X POST $E2B_API_URL/sandboxes/{sandboxID}/refreshes \
-H "X-API-Key: $E2B_API_KEY" \
-H "Content-Type: application/json" \
-d '{"duration": 60}'
# Or set an absolute timeout (seconds from now)
curl -X POST $E2B_API_URL/sandboxes/{sandboxID}/timeout \
-H "X-API-Key: $E2B_API_KEY" \
-H "Content-Type: application/json" \
-d '{"timeout": 120}'
```
### Pause (snapshot to disk)
```bash
curl -X POST $E2B_API_URL/sandboxes/{sandboxID}/pause \
-H "X-API-Key: $E2B_API_KEY"
```
### Resume / reconnect
```bash
curl -X POST $E2B_API_URL/sandboxes/{sandboxID}/connect \
-H "X-API-Key: $E2B_API_KEY" \
-H "Content-Type: application/json" \
-d '{"timeout": 300}'
```
Returns `200` if already running, `201` if resumed from paused state.
### Kill
```bash
curl -X DELETE $E2B_API_URL/sandboxes/{sandboxID} \
-H "X-API-Key: $E2B_API_KEY"
```
### Create a snapshot (template from running sandbox)
```bash
curl -X POST $E2B_API_URL/sandboxes/{sandboxID}/snapshots \
-H "X-API-Key: $E2B_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "my-snapshot"}'
```
---
## 5. Network Configuration
Update network rules on a running sandbox:
```bash
curl -X PUT $E2B_API_URL/sandboxes/{sandboxID}/network \
-H "X-API-Key: $E2B_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"allowOut": ["8.8.8.8", "example.com"],
"denyOut": ["0.0.0.0/0"]
}'
```
**Rules:**
- If `allowOut` contains domain names, `denyOut` must include `0.0.0.0/0`
- Domain names are not supported in `denyOut` (IPs/CIDRs only)
- `allowOut` entries take precedence over `denyOut`
- Omitting both fields clears all rules

View File

@@ -0,0 +1,46 @@
## VMs
```bash
E2B_API_URL=http://192.168.0.61:3000
# create vm
curl -X POST $E2B_API_URL/sandboxes \
-H "X-API-Key: $E2B_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"templateID": "base",
"timeout": 300
}'
# response
{"alias":"base","clientID":"6532622b","domain":null,"envdVersion":"0.5.8","sandboxID":"in3h60s6h0ie3kigrcls4","templateID":"pz1l1owhmy0w84e12eqv","trafficAccessToken":null}
sandboxID=in3h60s6h0ie3kigrcls4
# create sandboxVM and capture id
sandboxID=$(curl -s -X POST http://localhost:3000/sandboxes -H "X-API-Key: $E2B_API_KEY" -H "Content-Type: application/json" -d '{
"templateID": "base",
"timeout": 300
}' | jq -r .sandboxID
)
# get state
curl http://localhost:3000/sandboxes/${sandboxID} \
-H "X-API-Key: $E2B_API_KEY"
# list running sandboxes
curl "http://localhost:3000/v2/sandboxes?state=running" \
-H "X-API-Key: $E2B_API_KEY"
# sandbox resource metrics
curl "http://localhost:3000/sandboxes/{sandboxID}/metrics?start=$(date -v-5M +%s)&end=$(date +%s)" \
-H "X-API-Key: $E2B_API_KEY"
# sandbox logs
curl "http://localhost:3000/v2/sandboxes/{sandboxID}/logs?limit=100&direction=backward" \
-H "X-API-Key: $E2B_API_KEY"
```

View File

@@ -0,0 +1,22 @@
# Disable the default all-in-one installation
shims:
disableAll: true
firecracker:
enabled: true
# In this chart version, 'env' must be a map, not a list.
# The template converts these keys into Environment Variables for you.
env:
kataArtifacts: "firecracker"
multiInstallSuffix: "" # This fixes the specific error you saw
installArtifactsOnly: false
# Automatically create the RuntimeClass
runtimeClass:
create: true
name: kata-fc
handler: kata-fc
# Set Firecracker as the default for amd64 nodes
defaultShim:
amd64: firecracker

View File

@@ -0,0 +1,14 @@
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: kata-fc
handler: kata-fc
scheduling:
nodeSelector:
# Ensure these pods only land on your specialized node
runtime: kata-fc
tolerations:
- key: "dedicated"
operator: "Equal"
value: "kata-fc"
effect: "NoSchedule"

View File

@@ -0,0 +1,198 @@
## Node: kube-node-34
```bash
apt install cpu-checker
# check kvm availability
kvm-ok
cat /sys/module/kvm_amd/parameters/nested
lsmod | grep kvm
# install kata containers
# using kata-deploy ?
export VERSION=$(curl -sSL https://api.github.com/repos/kata-containers/kata-containers/releases/latest | jq .tag_name | tr -d '"')
export CHART="oci://ghcr.io/kata-containers/kata-deploy-charts/kata-deploy"
helm upgrade --install kata-deploy "${CHART}" --version "${VERSION}" --values experiments/kata_and_fc_on_kube/manifests/kata-deploy-values.yaml
# label only specific nodes to be used with kata-fc
kubectl label node kube-node-34 runtime=kata-fc
# taint node with NoSchedule
kubectl taint nodes kube-node-34 dedicated=kata-fc:NoSchedule
# configure runtime class
k apply -f experiments/kata_and_fc_on_kube/manifests//runtime-class_kata-fc.yaml
# devmapper configuration for firecracker in containerd
ctr plugins ls | grep devmapper
# create data and metadata files
# might want to replace this with LVM !!!!
# sudo mkdir -p /var/lib/containerd/devmapper
# sudo truncate -s 10G /var/lib/containerd/devmapper/data
# sudo truncate -s 1G /var/lib/containerd/devmapper/init_metadata
# # Associate the files with Loop Devices
# sudo losetup /dev/loop10 /var/lib/containerd/devmapper/data
# sudo losetup /dev/loop11 /var/lib/containerd/devmapper/init_metadata
# # Use dmsetup to create the pool:
# # This command creates the mapping.
# # The numbers '0 20971520' represent the size in 512-byte sectors (for a 10GB file).
# sudo dmsetup create containerd-pool --table "0 20971520 thin-pool /dev/loop11 /dev/loop10 128 32768 1"
lvcreate -L 10G -T kata-vg/kata-pool
# Add the configuration to /etc/containerd/config.toml:
# Find the [plugins."io.containerd.snapshotter.v1.devmapper"] section and update it:
[plugins."io.containerd.snapshotter.v1.devmapper"]
# LVM uses a specific naming convention in /dev/mapper/
# It is VolumeGroupName-LogicalVolumeName
pool_name = "kata--vg-kata--pool"
root_path = "/var/lib/containerd/devmapper"
base_image_size = "10GB"
discard_blocks = true
# restart containerd
systemctl restart containerd
```
### kube node preparation & deployment
```bash
# ---------------------------------------------------------------------------
# Packages
# ---------------------------------------------------------------------------
apt-get update
apt-get install -y \
qemu-guest-agent \
openssh-server \
apt-transport-https \
ca-certificates \
curl \
gnupg \
nvme-cli
systemctl enable --now qemu-guest-agent
systemctl enable --now ssh
# ---------------------------------------------------------------------------
# nvme-tcp
# ---------------------------------------------------------------------------
apt-get install -y linux-modules-extra-$(uname -r)
modprobe nvme-tcp
echo "nvme-tcp" >> /etc/modules-load.d/nvme-tcp.conf
# ---------------------------------------------------------------------------
# Kernel modules for Kubernetes
# ---------------------------------------------------------------------------
cat > /etc/modules-load.d/k8s.conf <<'EOF'
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter
cat > /etc/sysctl.d/k8s.conf <<'EOF'
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system
# ---------------------------------------------------------------------------
# containerd
# ---------------------------------------------------------------------------
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \
> /etc/apt/sources.list.d/docker.list
apt-get update && apt-get install -y containerd.io
cat > /etc/containerd/config.toml <<'EOF'
version = 2
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"
EOF
# Registry mirrors pointing to Zot at 192.168.0.30:5000
mkdir -p \
/etc/containerd/certs.d/docker.io \
/etc/containerd/certs.d/registry.k8s.io \
/etc/containerd/certs.d/ghcr.io \
/etc/containerd/certs.d/quay.io
cat > /etc/containerd/certs.d/docker.io/hosts.toml <<'EOF'
server = "https://registry-1.docker.io"
[host."http://192.168.0.30:5000/v2/docker.io"]
capabilities = ["pull", "resolve"]
skip_verify = true
override_path = true
EOF
cat > /etc/containerd/certs.d/registry.k8s.io/hosts.toml <<'EOF'
server = "https://registry.k8s.io"
[host."http://192.168.0.30:5000/v2/registry.k8s.io"]
capabilities = ["pull", "resolve"]
skip_verify = true
override_path = true
EOF
cat > /etc/containerd/certs.d/ghcr.io/hosts.toml <<'EOF'
server = "https://ghcr.io"
[host."http://192.168.0.30:5000/v2/ghcr.io"]
capabilities = ["pull", "resolve"]
skip_verify = true
override_path = true
EOF
cat > /etc/containerd/certs.d/quay.io/hosts.toml <<'EOF'
server = "https://quay.io"
[host."http://192.168.0.30:5000/v2/quay.io"]
capabilities = ["pull", "resolve"]
skip_verify = true
override_path = true
EOF
systemctl restart containerd
# ---------------------------------------------------------------------------
# kubelet systemd drop-in
# ---------------------------------------------------------------------------
mkdir -p /etc/systemd/system/kubelet.service.d
cat > /etc/systemd/system/kubelet.service.d/10-containerd.conf <<'EOF'
[Unit]
After=containerd.service
Requires=containerd.service
[Service]
ExecStartPre=/bin/bash -c 'until [ -S /var/run/containerd/containerd.sock ]; do sleep 1; done'
ExecStartPre=/usr/bin/crictl info
EOF
# ---------------------------------------------------------------------------
# kubeadm / kubelet / kubectl v1.32
# ---------------------------------------------------------------------------
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.32/deb/Release.key \
| gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.32/deb/ /" \
> /etc/apt/sources.list.d/kubernetes.list
apt-get update && apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl
# join kube cluster
JOIN_COMMAND="kubeadm join 192.168.0.31:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash>"

View File

@@ -0,0 +1,14 @@
```bash
virt-install \
--name microsandbox \
--ram 8192\
--vcpus 4 \
--cpu host-passthrough \
--os-variant ubuntu24.04 \
--disk path=/srv/vms/microsandbox.qcow2,size=100,format=qcow2,bus=virtio \
--network bridge=br0,model=virtio \
--graphics none \
--console pty,target_type=serial \
--location /srv/vms/isos/ubuntu-24.04.3-live-server-amd64.iso,kernel=casper/vmlinuz,initrd=casper/initrd \
--extra-args 'console=ttyS0,115200n8'
```

View File

@@ -0,0 +1,27 @@
#cloud-config
hostname: ${hostname}
manage_etc_hosts: true
# Kernel modules for container networking
write_files:
- path: /etc/modules-load.d/k8s.conf
content: |
overlay
br_netfilter
- path: /etc/sysctl.d/k8s.conf
content: |
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
packages:
- curl
- gpg
- apt-transport-https
runcmd:
- modprobe overlay
- modprobe br_netfilter
- sysctl --system
- swapoff -a
- sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab

View File

@@ -0,0 +1,74 @@
terraform {
required_providers {
orbstack = {
source = "robertdebock/orbstack"
version = "~> 3.0"
}
cloudinit = {
source = "hashicorp/cloudinit"
version = "~> 2.0"
}
}
}
variable "node_count" {
description = "Number of nodes to deploy"
type = number
default = 3
}
variable "name" {
description = "Base name for the machines"
type = string
}
variable "distro" {
description = "OS distribution"
type = string
default = "ubuntu"
}
variable "distro_version" {
description = "OS distribution version/codename"
type = string
default = "noble"
}
variable "extra_cloud_init_parts" {
description = "Additional cloud-init parts to layer on top of the base config"
type = list(object({ content = string, content_type = string }))
default = []
}
data "cloudinit_config" "this" {
count = var.node_count
part {
content_type = "text/cloud-config"
content = templatefile("${path.module}/cloud-init-base.yaml", {
hostname = "${var.name}-${count.index + 1}"
})
}
dynamic "part" {
for_each = var.extra_cloud_init_parts
content {
content_type = part.value.content_type
content = part.value.content
}
}
}
resource "orbstack_machine" "this" {
count = var.node_count
name = "${var.name}-${count.index + 1}"
distro = var.distro
region = var.distro_version
user_data = data.cloudinit_config.this[count.index].rendered
}
output "machines" {
value = { for m in orbstack_machine.this : m.name => m.ip_address }
}

View File

@@ -0,0 +1,11 @@
#cloud-config
merge_how:
- name: list
settings: [append]
- name: dict
settings: [recurse_array]
runcmd:
- curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server" K3S_TOKEN="${k3s_token}" sh -
- until kubectl get nodes; do sleep 2; done
- cp /etc/rancher/k3s/k3s.yaml /root/kubeconfig.yaml

View File

@@ -0,0 +1,9 @@
#cloud-config
merge_how:
- name: list
settings: [append]
- name: dict
settings: [recurse_array]
runcmd:
- curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="agent" K3S_URL="https://${cp_ip}:6443" K3S_TOKEN="${join_token}" sh -

View File

@@ -0,0 +1,39 @@
variable "k3s_token" {
description = "Shared secret for k3s cluster join (set via TF_VAR or tfvars)"
type = string
sensitive = true
}
module "control_plane" {
source = "../../modules/base-template"
name = "k3s-cp"
node_count = 1
extra_cloud_init_parts = [{
content_type = "text/cloud-config"
content = templatefile("${path.module}/cloud-init-cp.yaml", {
k3s_token = var.k3s_token
})
}]
}
module "workers" {
source = "../../modules/base-template"
name = "k3s-worker"
node_count = 2
extra_cloud_init_parts = [{
content_type = "text/cloud-config"
content = templatefile("${path.module}/cloud-init-worker.yaml", {
cp_ip = values(module.control_plane.machines)[0]
join_token = var.k3s_token
})
}]
}
output "cluster" {
value = {
control_plane = module.control_plane.machines
workers = module.workers.machines
}
}

View File

@@ -0,0 +1,219 @@
## install
```bash
# master node
curl -sfL https://get.rke2.io | INSTALL_RKE2_VERSION=v1.32.12+rke2r1 sh -
systemctl enable rke2-server.service
systemctl start rke2-server.service
journalctl -u rke2-server -f
# open firewalld
sudo firewall-cmd --permanent --add-port=9345/tcp
sudo firewall-cmd --permanent --add-port=6443/tcp
sudo firewall-cmd --permanent --add-port=10250/tcp # Kubelet
sudo firewall-cmd --reload
# install nerdctl
# Set the version
VERSION="2.2.1" # Check GitHub for the latest version
# Download the tarball
wget https://github.com/containerd/nerdctl/releases/download/v${VERSION}/nerdctl-${VERSION}-linux-arm64.tar.gz
# Extract to your path
sudo tar -C /usr/local/bin -xzvf nerdctl-${VERSION}-linux-arm64.tar.gz nerdctl
# configure nerdctl
sudo mkdir -p /etc/nerdctl
sudo tee /etc/nerdctl/nerdctl.toml <<EOF
address = "unix:///run/k3s/containerd/containerd.sock"
namespace = "k8s.io"
EOF
# install buildkit
# Set current stable version
BK_VER="0.28.0"
# Download arm64 binary
wget https://github.com/moby/buildkit/releases/download/v${BK_VER}/buildkit-v${BK_VER}.linux-arm64.tar.gz
# Extract only the binaries to /usr/local/bin
sudo tar -C /usr/local/bin -xzvf buildkit-v${BK_VER}.linux-arm64.tar.gz --strip-components=1 bin/
# Create the service file
sudo tee /etc/systemd/system/buildkit.service <<EOF
[Unit]
Description=BuildKit
Documentation=https://github.com/moby/buildkit
[Service]
ExecStart=/usr/local/bin/buildkitd --addr unix:///run/buildkit/buildkitd.sock
[Install]
WantedBy=multi-user.target
EOF
# Reload and Start
sudo systemctl daemon-reload
sudo systemctl enable --now buildkit
# ---------------------------------------------
# agent/worker node
curl -sfL https://get.rke2.io | INSTALL_RKE2_TYPE="agent" INSTALL_RKE2_VERSION=v1.32.12+rke2r1 sh -
systemctl enable rke2-agent.service
mkdir -p /etc/rancher/rke2/
# token from master node
# cat /var/lib/rancher/rke2/server/node-token
cat <<EOF | sudo tee /etc/rancher/rke2/config.yaml
server: https://192.168.64.3:9345
token: K107618960f87b9efb3a3255ce00a9743d29f1db9376820c9144cb85fa3c554dc69::server:06b2effdf0c9ce3952efc8a5d80bf084
EOF
systemctl start rke2-agent.service
journalctl -u rke2-agent -f
# Set up kubectl on the server node
echo 'export KUBECONFIG=/etc/rancher/rke2/rke2.yaml' >> ~/.bashrc
echo 'export PATH=$PATH:/var/lib/rancher/rke2/bin' >> ~/.bashrc
source ~/.bashrc
```
## build and deploy application
```bash
# build container with nerdctl
nerdctl --namespace k8s.io build --tag hello-world:latest .
# export image as tar on master node
nerdctl save hello-world:latest -o hello-world.tar
# copy it over to worker node
scp hello-world.tar novakj@192.168.64.4:~/
# import image on the agent node
sudo /var/lib/rancher/rke2/bin/ctr --address /run/k3s/containerd/containerd.sock -n k8s.io images import hello-world.tar
kubectl create namespace rke2-apps
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-world-deployment
namespace: rke2-apps
labels:
type: staticwebapp
spec:
replicas: 1
selector:
matchLabels:
type: staticwebapp
template:
metadata:
labels:
type: staticwebapp
spec:
containers:
- name: staticwebapp
image: hello-world:latest
imagePullPolicy: Never
ports:
- containerPort: 80
resources:
requests:
memory: "32Mi"
cpu: "200m"
limits:
memory: "64Mi"
cpu: "300m"
EOF
kubectl create -f deployment.yaml
# expose deployment
kubectl expose deployment hello-world-deployment --name hello-world-service --port=8080 --target-port=80 -n rke2-apps
# install ingress-nginx (even though i thought that there is ingress controller already deployed)
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.2/deploy/static/provider/cloud/deploy.yaml
# create ingress with "localhost" as host
kubectl create ingress hello-world-ingress --class=nginx --rule="test-host/*=hello-world-service:8080" -n rke2-apps
kubectl port-forward -n ingress-nginx service/ingress-nginx-controller 8081:80
# incomplete completion configuration ;-)
dnf install bash-completion -y
alias 'k=kubectl'
# ~/.bashrc
# 1. Load the main bash-completion package first
# On Rocky/RHEL, it's usually at this path:
[[ -r "/usr/share/bash-completion/bash_completion" ]] && . "/usr/share/bash-completion/bash_completion"
# Enable kubectl bash completion
source <(kubectl completion bash)
# Set up the alias
alias k=kubectl
# Link the kubectl completion logic to the 'k' alias
complete -o default -F __start_kubectl k
```
## upgrading RKE2
```bash
# install upgrade controller
kubectl apply -f https://github.com/rancher/system-upgrade-controller/releases/download/v0.9.1/system-upgrade-controller.yaml
# server upgrade
cat <<EOF | kubectl apply -f -
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
name: rke2-server-upgrade
namespace: system-upgrade
spec:
concurrency: 1
cordon: true
nodeSelector:
matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: In
values: ["true"]
serviceAccountName: system-upgrade
upgrade:
image: rancher/rke2-upgrade
version: v1.33.9+rke2r1
EOF
# agent upgrade
cat <<EOF | kubectl apply -f -
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
name: rke2-agent-upgrade
namespace: system-upgrade
spec:
concurrency: 1
cordon: true
nodeSelector:
matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: DoesNotExist
prepare:
# Logic: "Don't start workers until servers are done"
args: ["wait-for-plan", "rke2-server-upgrade"]
image: rancher/rke2-upgrade
serviceAccountName: system-upgrade
upgrade:
image: rancher/rke2-upgrade
version: v1.33.9+rke2r1
EOF
```

View File

@@ -0,0 +1,215 @@
## VMS creation
```bash
# hypervisor: beelink (192.168.0.6)
vms_path=/srv/vms/images
isos_path=/srv/vms/isos
cd $isos_path
# Grab Ubuntu 24.04 cloud image
wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
# Create a base disk from the cloud image (repeat per node)
for NODE in rke2-server rke2-agent1 rke2-agent2; do
DISK_SIZE="30G"
[[ "$NODE" == rke2-agent* ]] && DISK_SIZE="50G"
qemu-img create -f qcow2 -F qcow2 -b $isos_path/noble-server-cloudimg-amd64.img $vms_path/${NODE}.qcow2
qemu-img resize $vms_path/${NODE}.qcow2 ${DISK_SIZE}
done
```
```bash
# prepare cloud-init/user-data
apt install cloud-image-utils
# prepare cloud-init and launch VMs
declare -A nodes=(
[rke2-server]="192.168.0.51"
[rke2-agent1]="192.168.0.52"
[rke2-agent2]="192.168.0.53"
)
for node in "${!nodes[@]}"; do
ip="${nodes[$node]}"
# user-data
cat <<EOF > user-data-${node}
#cloud-config
hostname: ${node}
manage_etc_hosts: false
users:
- name: sre
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
ssh_authorized_keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINQxxkqmvtVI+8c5BkTaJ5c2HfBFRXJWMmEcevvfP9tV jan.novak@Jans-MacBook-Air.local
write_files:
- path: /etc/hosts
append: true
content: |
192.168.0.51 rke2-server
192.168.0.52 rke2-agent1
192.168.0.53 rke2-agent2
- path: /etc/modules-load.d/rke2.conf
content: |
br_netfilter
overlay
- path: /etc/sysctl.d/99-rke2.conf
content: |
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
swap:
filename: /swap.img
size: 0
maxsize: 0
runcmd:
- swapoff -a
- sed -i '/swap/d' /etc/fstab
- modprobe br_netfilter
- modprobe overlay
- sysctl --system
package_update: true
packages:
- qemu-guest-agent
- nfs-common
- open-iscsi
power_state:
mode: reboot
EOF
# network config
cat <<EOF > network-config-${node}
network:
version: 2
ethernets:
eth0:
match:
driver: "virtio_net"
addresses:
- "${ip}/24"
nameservers:
addresses:
- 8.8.8.8
routes:
- to: "default"
via: "192.168.0.4"
EOF
cloud-localds --network-config=./network-config-${node} \
$vms_path/${node}-seed.iso ./user-data-${node}
done
# Launch VMs
for node in "${!nodes[@]}"; do
virt-install \
--name ${node} \
--ram 4096 --vcpus 2 \
--os-variant ubuntu24.04 \
--disk $vms_path/${node}.qcow2,bus=virtio \
--disk $vms_path/${node}-seed.iso,device=cdrom \
--network bridge=br0,model=virtio \
--graphics none \
--console pty,target_type=serial \
--noautoconsole \
--import
done
```
## RKE2 installation
```bash
# there are no .deb packages - only rpm or tarball
# "magic" install script can handle that
curl -sfL https://get.rke2.io | INSTALL_RKE2_CHANNEL=v1.32 sudo sh -
# Create config directory
sudo mkdir -p /etc/rancher/rke2
# Server configuration
cat <<EOF | sudo tee /etc/rancher/rke2/config.yaml
# Bind the API to the node's IP (not 127.0.0.1)
tls-san:
- rke2-server
- 192.168.0.51
# Write kubeconfig readable by non-root
write-kubeconfig-mode: "0644"
# CNI - canal is default and fine for home lab
# Alternatives: cilium, calico, multus
cni:
- canal
# Disable servicelb if you plan to use metallb
disable:
- rke2-service-lb
EOF
# Enable and start
sudo systemctl enable rke2-server.service
sudo systemctl start rke2-server.service
# Watch bootstrap (takes 2-3 min on first run)
sudo journalctl -u rke2-server -f
```
## Grab the join token and kubeconfig
```bash
# Token for agents to join
sudo cat /var/lib/rancher/rke2/server/node-token
# Save this somewhere - you'll need it on every agent
# Set up kubectl on the server node
echo 'export KUBECONFIG=/etc/rancher/rke2/rke2.yaml' >> ~/.bashrc
echo 'export PATH=$PATH:/var/lib/rancher/rke2/bin' >> ~/.bashrc
source ~/.bashrc
# Verify
kubectl get nodes
```
## Install RKE2 Agents (Workers)
```bash
# Install RKE2 agent - same channel as server
curl -sfL https://get.rke2.io | INSTALL_RKE2_CHANNEL=v1.32 INSTALL_RKE2_TYPE=agent sudo sh -
# Create config
sudo mkdir -p /etc/rancher/rke2
cat <<EOF | sudo tee /etc/rancher/rke2/config.yaml
server: https://192.168.0.51:9345
token: K10dba0bfff01d610ffed41c6b82a1b8861ee19e5af34a3bdd21970936823a846da::server:1f02fb36a288dcd06770cab28b015bd7
EOF
# Enable and start
sudo systemctl enable rke2-agent.service
sudo systemctl start rke2-agent.service
# Watch join
sudo journalctl -u rke2-agent -f
```
## Copy Kubeconfig to Your Workstation
```bash
# On your workstation (not the VMs)
scp sre@192.168.0.51:/etc/rancher/rke2/rke2.yaml ~/.kube/rke2-homelab.yaml
# Fix the server address (it'll say 127.0.0.1)
gsed -i 's/127.0.0.1/192.168.0.51/' ~/.kube/rke2-homelab.yaml
export KUBECONFIG=~/.kube/rke2-homelab.yaml
kubectl get nodes
```

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,20 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: gitea-ci-deploy
subjects:
- kind: User
name: "gitea_ci@idm.home.hrajfrisbee.cz" # matches preferred_username claim
apiGroup: rbac.authorization.k8s.io
roleRef:
# kind: ClusterRole
# name: edit # scope down as needed
# apiGroup: rbac.authorization.k8s.io
# this is obviously too much permissions
# but we can live with it for homelab
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io

View File

@@ -0,0 +1,15 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: oidc-cluster-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: https://idm.home.hrajfrisbee.cz/oauth2/openid/k8s#35842461-a1c4-4ad6-8b29-697c5ddbfe84
- apiGroup: rbac.authorization.k8s.io
kind: User
name: novakj@idm.home.hrajfrisbee.cz

View File

@@ -0,0 +1,22 @@
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod-dns
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: kacerr.cz@gmail.com
privateKeySecretRef:
name: letsencrypt-dns-account-key
solvers:
- dns01:
rfc2136:
nameserver: dns-update-proxy.cert-manager.svc.cluster.local:53
tsigKeyName: acme-update-key
tsigAlgorithm: HMACSHA512
tsigSecretSecretRef:
name: acme-update-key
key: acme-update-key
selector:
dnsZones:
- "lab.home.hrajfrisbee.cz"

View File

@@ -0,0 +1,33 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: dns-update-proxy
namespace: cert-manager
spec:
replicas: 1
selector:
matchLabels:
app: dns-update-proxy
template:
metadata:
labels:
app: dns-update-proxy
spec:
containers:
- name: socat-tcp
image: alpine/socat
args:
- TCP-LISTEN:53,fork,reuseaddr
- TCP:87.236.195.209:5353
ports:
- containerPort: 53
protocol: TCP
- name: socat-udp
image: alpine/socat
args:
- -T5
- UDP-RECVFROM:53,fork,reuseaddr
- UDP:87.236.195.209:5353
ports:
- containerPort: 53
protocol: UDP

View File

@@ -0,0 +1,18 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: acme-update-key
namespace: cert-manager
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend # or your store
kind: ClusterSecretStore
target:
name: acme-update-key
creationPolicy: Owner
data:
- secretKey: acme-update-key
remoteRef:
key: k8s_home/cert-manager
property: acme-update-key

View File

@@ -19,8 +19,14 @@ spec:
upgrade:
crds: CreateReplace
values:
global:
logLevel: 6
crds:
enabled: false
config:
apiVersion: controller.config.cert-manager.io/v1alpha1
kind: ControllerConfiguration
enableGatewayAPI: true
prometheus:
enabled: true
extraObjects:

View File

@@ -0,0 +1,17 @@
apiVersion: v1
kind: Service
metadata:
name: dns-update-proxy
namespace: cert-manager
spec:
selector:
app: dns-update-proxy
ports:
- name: dns-tcp
port: 53
targetPort: 53
protocol: TCP
- name: dns-udp
port: 53
targetPort: 53
protocol: UDP

View File

@@ -0,0 +1,12 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-lab-home-hrajfrisbee
namespace: kube-system
spec:
secretName: wildcard-lab-home-hrajfrisbee-tls
issuerRef:
name: letsencrypt-prod-dns
kind: ClusterIssuer
dnsNames:
- "*.lab.home.hrajfrisbee.cz"

View File

@@ -0,0 +1,27 @@
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: cilium-gateway
namespace: kube-system
spec:
gatewayClassName: cilium
listeners:
- name: http
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: All
- name: lab-home-hrajfrisbee-https-wildcard
hostname: "*.lab.home.hrajfrisbee.cz"
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: wildcard-lab-home-hrajfrisbee-tls
allowedRoutes:
namespaces:
from: All

View File

@@ -13,16 +13,19 @@ spec:
kind: HelmRepository
name: cilium
namespace: flux-system
version: 1.18.5
version: 1.19.1
interval: 5m0s
values:
cluster:
name: "home-kube"
devices: "eth+ bond+ en+"
hubble:
relay:
enabled: true
ui:
enabled: true
ingressController:
enabled: true
ipam:
mode: cluster-pool
operator:
@@ -30,6 +33,15 @@ spec:
clusterPoolIPv4PodCIDRList: "10.96.0.0/16"
l2announcements:
enabled: true
gatewayAPI:
enabled: true
gatewayClass:
create: "true"
kubeProxyReplacement: true
k8sServiceHost: 192.168.0.31 # or LB IP
k8sServicePort: 6443
# disable envoy daemonset - i guess that is stupid idea anyway
# envoy:
# enabled: false
# l7Proxy: false

View File

@@ -0,0 +1,17 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: operator-test-lab-home-hrajfrisbee-2
namespace: default
annotations:
gateway-cert-operator.io/gateway-name: "cilium-gateway"
gateway-cert-operator.io/gateway-namespace: "kube-system"
spec:
secretName: operator-test-lab-home-hrajfrisbee-2
issuerRef:
group: cert-manager.io
kind: ClusterIssuer
name: letsencrypt-prod
dnsNames:
- "operator-test-2.lab.home.hrajfrisbee.cz"

View File

@@ -0,0 +1,17 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: operator-test-lab-home-hrajfrisbee
namespace: default
annotations:
gateway-cert-operator.io/gateway-name: "cilium-gateway"
gateway-cert-operator.io/gateway-namespace: "kube-system"
spec:
secretName: operator-test-lab-home-hrajfrisbee
issuerRef:
group: cert-manager.io
kind: ClusterIssuer
name: letsencrypt-prod
dnsNames:
- "operator-test.lab.home.hrajfrisbee.cz"

View File

@@ -0,0 +1,30 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: operator-test
namespace: default
labels:
app: operator-test
spec:
replicas: 1
selector:
matchLabels:
app: operator-test
template:
metadata:
labels:
app: operator-test
spec:
containers:
- name: whoami
image: traefik/whoami
ports:
- containerPort: 80
name: http
resources:
requests:
cpu: 10m
memory: 16Mi
limits:
memory: 32Mi

View File

@@ -0,0 +1,25 @@
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: operator-test-redirect
namespace: default
labels:
app: operator-test
spec:
parentRefs:
- name: cilium-gateway
namespace: kube-system
sectionName: http
hostnames:
- operator-test.lab.home.hrajfrisbee.cz
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301

View File

@@ -0,0 +1,24 @@
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: operator-test
namespace: default
labels:
app: operator-test
spec:
parentRefs:
- name: cilium-gateway
namespace: kube-system
sectionName: auto-operator-test-lab-home-hrajfrisbee-operator-test-lab-home-hrajfrisbee-cz
hostnames:
- operator-test.lab.home.hrajfrisbee.cz
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: operator-test
namespace: default
port: 80

View File

@@ -0,0 +1,14 @@
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-kube-system-gateway
namespace: default
spec:
from:
- group: gateway.networking.k8s.io
kind: Gateway
namespace: kube-system
to:
- group: ""
kind: Secret
name: operator-test-lab-home-hrajfrisbee

View File

@@ -0,0 +1,15 @@
---
apiVersion: v1
kind: Service
metadata:
name: operator-test
namespace: default
spec:
type: ClusterIP
selector:
app: operator-test
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http

View File

@@ -6,13 +6,13 @@ metadata:
spec:
provider:
vault:
server: "https://vault.hrajfrisbee.cz:8200"
server: "https://vault.hrajfrisbee.cz"
path: "secret"
version: "v2"
auth:
appRole:
path: "approle"
roleId: "8833d0f8-d35d-d7ea-658b-c27837d121ab" # or reference a secret
roleId: "864e352d-2064-2bf9-2c73-dbd676a95368" # or reference a secret
secretRef:
name: vault-approle
key: secret-id

View File

@@ -6,5 +6,5 @@ metadata:
annotations:
kustomize.toolkit.fluxcd.io/reconcile: disabled
type: Opaque
data:
secret-id: # --- find me in keepass bro ---
stringData:
secret-id: --- fill in the secret_id ---

View File

@@ -1,18 +0,0 @@
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: vault-backend
namespace: external-secrets
spec:
provider:
vault:
server: "https://vault.hrajfrisbee.cz:8200"
path: "secret"
version: "v2"
auth:
appRole:
path: "approle"
roleId: "864e352d-2064-2bf9-2c73-dbd676a95368" # or reference a secret
secretRef:
name: vault-approle
key: secret-id

View File

@@ -14,6 +14,19 @@ spec:
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: 00-rbac
namespace: flux-system
spec:
interval: 10m0s
path: ./gitops/home-kubernetes/00-rbac
prune: true
sourceRef:
kind: GitRepository
name: flux-system
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: cilium
namespace: flux-system
@@ -117,6 +130,19 @@ spec:
sourceRef:
kind: GitRepository
name: flux-system
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: experiments
namespace: flux-system
spec:
interval: 10m0s
path: ./gitops/home-kubernetes/experiments
prune: true
sourceRef:
kind: GitRepository
name: flux-system
# ---
# apiVersion: kustomize.toolkit.fluxcd.io/v1
# kind: Kustomization

View File

@@ -0,0 +1,16 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: flux-web
namespace: flux-system
spec:
interval: 30m
releaseName: flux-web
chartRef:
kind: OCIRepository
name: flux-operator
values:
fullnameOverride: flux-web
installCRDs: true
web:
serverOnly: true

View File

@@ -0,0 +1,13 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
name: flux-operator
namespace: flux-system
spec:
interval: 30m
url: oci://ghcr.io/controlplaneio-fluxcd/charts/flux-operator
layerSelector:
mediaType: "application/vnd.cncf.helm.chart.content.v1.tar+gzip"
operation: copy
ref:
semver: "0.x"

View File

@@ -0,0 +1,63 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: fuj-management
namespace: fuj
labels:
app: fuj-management
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: fuj-management
template:
metadata:
labels:
app: fuj-management
spec:
imagePullSecrets:
- name: gitea-registry
containers:
- name: fuj-management
image: gitea.home.hrajfrisbee.cz/kacerr/fuj-management:0.39-go
ports:
- containerPort: 8080
name: http
env:
- name: PORT
value: "8080"
- name: TZ
value: Europe/Prague
- name: CREDENTIALS_PATH
value: /secrets/credentials.json
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
readinessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 2
periodSeconds: 10
timeoutSeconds: 3
livenessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 5
periodSeconds: 30
timeoutSeconds: 5
volumeMounts:
- name: bot-credentials
mountPath: /secrets
readOnly: true
volumes:
- name: bot-credentials
secret:
secretName: fuj-management-bot-credentials

View File

@@ -0,0 +1,60 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: fujarna
namespace: fuj
labels:
app: fujarna
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: fujarna
template:
metadata:
labels:
app: fujarna
spec:
imagePullSecrets:
- name: gitea-registry
containers:
- name: fujarna
image: gitea.home.hrajfrisbee.cz/kacerr/fujarna:v0.0.2
ports:
- containerPort: 8080
name: http
env:
- name: PORT
value: "8080"
- name: TZ
value: Europe/Prague
volumeMounts:
- name: data
mountPath: /app/data
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
readinessProbe:
httpGet:
path: /api/tournaments
port: 8080
initialDelaySeconds: 2
periodSeconds: 10
timeoutSeconds: 3
livenessProbe:
httpGet:
path: /api/tournaments
port: 8080
initialDelaySeconds: 5
periodSeconds: 30
timeoutSeconds: 5
volumes:
- name: data
persistentVolumeClaim:
claimName: tournament-data

View File

@@ -0,0 +1,22 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: gitea-registry
namespace: fuj
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: gitea-registry
creationPolicy: Owner
template:
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: "{{ .token }}"
data:
- secretKey: token
remoteRef:
key: k8s_home/gitea/container-registry
property: token

View File

@@ -0,0 +1,30 @@
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: fujarna-redirect
namespace: fuj
labels:
app: fujarna
app.kubernetes.io/name: fujarna-httproute
app.kubernetes.io/instance: fujarna
app.kubernetes.io/version: '6.0'
app.kubernetes.io/component: httproute
app.kubernetes.io/part-of: fujarna
spec:
parentRefs:
- name: cilium-gateway
namespace: kube-system
sectionName: http
hostnames:
- fujarna.lab.home.hrajfrisbee.cz
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301

View File

@@ -0,0 +1,29 @@
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: fujarna
namespace: fuj
labels:
app: fujarna
app.kubernetes.io/name: fujarna-httproute
app.kubernetes.io/instance: fujarna
app.kubernetes.io/version: '6.0'
app.kubernetes.io/component: httproute
app.kubernetes.io/part-of: fujarna
spec:
parentRefs:
- name: cilium-gateway
namespace: kube-system
sectionName: lab-home-hrajfrisbee-https-wildcard
hostnames:
- fujarna.lab.home.hrajfrisbee.cz
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: fujarna
namespace: fuj
port: 80

View File

@@ -0,0 +1,28 @@
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: fuj-management
namespace: fuj
labels:
app: fuj-management
app.kubernetes.io/name: fuj-management-httproute
app.kubernetes.io/instance: fuj-management
app.kubernetes.io/component: httproute
app.kubernetes.io/part-of: fuj-management
spec:
parentRefs:
- name: cilium-gateway
namespace: kube-system
sectionName: lab-home-hrajfrisbee-https-wildcard
hostnames:
- fuj-management.lab.home.hrajfrisbee.cz
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: fuj-management
namespace: fuj
port: 80

View File

@@ -0,0 +1,30 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: fujarna
namespace: fuj
annotations:
# WebSocket support - increase timeouts for live scoring connections
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
# Uncomment for cert-manager TLS:
# cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx # change to "traefik" for k3s default
rules:
- host: hrajfrisbee.cz # change to your domain
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: fujarna
port:
number: 80
# Uncomment for TLS:
# tls:
# - hosts:
# - hrajfrisbee.cz
# secretName: fujarna-tls

View File

@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: fuj

View File

@@ -0,0 +1,12 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: tournament-data
namespace: fuj
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
# storageClassName: local-path # uncomment to override cluster default

View File

@@ -0,0 +1,8 @@
apiVersion: v1
data:
credentials.json: ewogICAgInR5cGUiOiAic2VydmljZV9hY2NvdW50IiwKICAgICJwcm9qZWN0X2lkIjogInBsYXRlZC1tZXNoLTQ4OTAxMC1zMSIsCiAgICAicHJpdmF0ZV9rZXlfaWQiOiAiYzQxM2VhYWUzMzk0NGFmZDljZjkyZjgzNGYzYmM1NDM5OGI1ZGY5YyIsCiAgICAicHJpdmF0ZV9rZXkiOiAiLS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tXG5NSUlFdmdJQkFEQU5CZ2txaGtpRzl3MEJBUUVGQUFTQ0JLZ3dnZ1NrQWdFQUFvSUJBUURuTzBJQ3p2NDNaMWFKXG5MYi9NSkFZbld2U2NWM2tlelBGSHpHNVd2WUE3SXBxM2ZkcUp6ZWYxSzcxeGJodGY5NFB6T2RNVGdrYXcxcmZYXG40QjhOMS9laWFZaWUyZkkwenA4emptejl0YnpDUjVwM0hiK3M0eHROR3hVUDJBWWQzVmRrbE5TRFlacGJ5Qm8rXG5qNWl1cy9BOXdYV1ZkQ2EvNnA1MmtYSU1Ld3o4b05rbmpMRTlzV21ZV1kvQmJZT1o0UExPSjNTWEdEMFJNQzlGXG4wbjBLUDJTRWxvaTFPc2wvV2s0Q21WazJFc0lNSjBiMHA1Wm1GZUZ6SlAzSFZUS0VtOExHam5vWjVwQmwzWGM0XG5GOThKSVFFQzZQallOcXQvSFlxdWRLb2hrelVSS2h5ekVJdHp3NXBrUnk3d1l1OGJHSGJOMzF0dWFpeDRMdFNjXG5zYU5oOHJhVEFnTUJBQUVDZ2dFQWFXZ2U4RTdSbmdueWJiZzRMV1BpbGtBbEw3dkVUK0VXd0NjVnlWNjdQTG5LXG5rNGpBZlg4cWxSMnFUekhsTXJzUElHb2txVWtDMW93YTVFS2JoV0VFMXJtSytQYmJMVzFmTDA1bXFzVVUxZTkwXG5INGFsSUxlcWMxeThIaXZZcGZhSFp5ZGROTUxpYmFKckxFZWpUMGhoWkpWeTkzMHFTT1EzOWs3WkVXanlrNW9WXG5WcUk2SE4yVGUrRTN0aklmNFcrN0lKN0c3RU9ZVG4zZHlrQnZCN1QwLzR2M0pUeGMzTk1IN0RZYzFyd1dicEdKXG5JcmNidndOa3VyVHhsdWg1VGtvSkZjd3ZXdEl4NE52MFFPNkk5TlpjMllPakZUMncwMUFrKzNDSTJLNmJOVE9qXG43WWFxYlRoNDk2cU1Md2NtRmFUZy9tbXEzMGZycEVsMWdQTWJWZFJqTVFLQmdRRDY3SDMvb0pGU1ZDWnFYQjFEXG5tdHZRblVMSWUvZkU5TXc4T2VrcUdMclQxbGlUTllhTks4YnRsOGFrbFlZY3FXU09zWk53ZkdyNmNhOGhUSEwyXG5pWXZNbkR4aVJPWkVkeElPUFJGY2l4SlNpdG5hb3kzSmtMMml0K2cxMUVGdUEyS3dGd29jc0RrRkQ1QTh0ZEFpXG5UOGhEeEc4dE5saFFrTUY0ekRMNTE2cHRLUUtCZ1FEcjZNZitLelBoNHM2Qm1GVzkrUzNtZEFZZkJETkJsZHJrXG56L1hrY2NteHNWeUpNNTErR2MzY3kzYmhIRDU4aGVLb3U1ZHdYanNqZkRCZlZKcjBpTHM0R3dGbjNlWlJYNmluXG5NVmlycmpyUWt5WnlSVDJkcHl5ODQ5MGN1WC9xQ01hbTVRYzY0UmpJN3JhRTM5ZDBRcnRRd0hWbnZCN1RHaEcvXG50WElXeThMQld3S0JnRGRtMi9NQyt0TVlyVnF5ZCt1alY0RmFGNUs0ZkY1S1JvWE5xNE9WN2pvemkxOEZaRzNyXG5Db01QRGRidEJLMXpZVFo0VXJ0MVhSRnE1R1lQd1JwYUNEbm5EUllOYkdJVmdERVFucksyS2hkN2ZOcGZTUTdHXG5ISGkrN2xCa1N3WUZUVmJmMzJXeUt3VEpDT20rb3ZQUWRjRXdyL05sbWw0L2Q5ZzZEYzlJSk9MeEFvR0JBTi9yXG5QaGwwN0hRV0VpbFViTjQ3TEdRMzhLOTBEeXJMa00vcXpOMGZYSEM3aHJmOE5CdERadkdTZHE1SlhzNVhUc01WXG4vREorWHVmWnN4aHBlQU52M1RUdC9UR1hvRXZRM29hRUFrUkN4alMvMTVoVFBKN3QxVHNkbGZ6ejNnUklmRFpoXG40YkR6ZzlFRk5GMS9Fa1NhS3E0Rlgrc3MxcDFOZTFkYVpJWmF0ZXROQW9HQkFLeStZTGRjQVh6dElaMTkvS0wwXG5sWTBPZi92azVJNUtKNlNiVUVheU9HTFJkWTJBdGk1R1QwQlpjVEV3QjYzWTNyWEVNQ3BsM25Ca1pVRklRczNEXG5wSE9DUDdDMXQ3STRSZlBGaGlSTzEvQnVsajZjUXNrUHFJWlpadGxOQjdROG1BVEttMnpXVThZTjRNbFhZSVBVXG5naGRrZHVQRWIyRC9ueDl3U3F2NzdUeWJcbi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS1cbiIsCiAgICAiY2xpZW50X2VtYWlsIjogImZ1ai1tYW5hZ2VtZW50LWJvdEBwbGF0ZWQtbWVzaC00ODkwMTAtczEuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICAgImNsaWVudF9pZCI6ICIxMDM0ODY2MjM3NTQxOTMzODgxMzQiLAogICAgImF1dGhfdXJpIjogImh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi9hdXRoIiwKICAgICJ0b2tlbl91cmkiOiAiaHR0cHM6Ly9vYXV0aDIuZ29vZ2xlYXBpcy5jb20vdG9rZW4iLAogICAgImF1dGhfcHJvdmlkZXJfeDUwOV9jZXJ0X3VybCI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9vYXV0aDIvdjEvY2VydHMiLAogICAgImNsaWVudF94NTA5X2NlcnRfdXJsIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3JvYm90L3YxL21ldGFkYXRhL3g1MDkvZnVqLW1hbmFnZW1lbnQtYm90JTQwcGxhdGVkLW1lc2gtNDg5MDEwLXMxLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJ1bml2ZXJzZV9kb21haW4iOiAiZ29vZ2xlYXBpcy5jb20iCn0=
kind: Secret
metadata:
name: fuj-management-bot-credentials
namespace: fuj
type: Opaque

View File

@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: fuj-management
namespace: fuj
spec:
type: ClusterIP
selector:
app: fuj-management
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http

View File

@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: fujarna
namespace: fuj
spec:
type: ClusterIP
selector:
app: fujarna
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http

View File

@@ -0,0 +1,30 @@
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: ghost-on-kubernetes-redirect
namespace: ghost-on-kubernetes
labels:
app: ghost-on-kubernetes
app.kubernetes.io/name: ghost-on-kubernetes-httproute
app.kubernetes.io/instance: ghost-on-kubernetes
app.kubernetes.io/version: '6.0'
app.kubernetes.io/component: httproute
app.kubernetes.io/part-of: ghost-on-kubernetes
spec:
parentRefs:
- name: cilium-gateway
namespace: kube-system
sectionName: http
hostnames:
- ghost.lab.home.hrajfrisbee.cz
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301

View File

@@ -0,0 +1,29 @@
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: ghost-on-kubernetes
namespace: ghost-on-kubernetes
labels:
app: ghost-on-kubernetes
app.kubernetes.io/name: ghost-on-kubernetes-httproute
app.kubernetes.io/instance: ghost-on-kubernetes
app.kubernetes.io/version: '6.0'
app.kubernetes.io/component: httproute
app.kubernetes.io/part-of: ghost-on-kubernetes
spec:
parentRefs:
- name: cilium-gateway
namespace: kube-system
sectionName: lab-home-hrajfrisbee-https-wildcard
hostnames:
- ghost.lab.home.hrajfrisbee.cz
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: ghost-on-kubernetes-service
namespace: ghost-on-kubernetes
port: 2368

View File

@@ -25,7 +25,7 @@ spec:
http:
paths:
- path: /
pathType: ImplementationSpecific
pathType: Prefix
backend:
service:
name: ghost-on-kubernetes-service

View File

@@ -11,11 +11,13 @@ spec:
sourceRef:
kind: HelmRepository
name: ingress-nginx
version: 4.12.0
version: 4.14.1
values:
controller:
admissionWebhooks:
enabled: false
patch:
enabled: false
config:
annotations-risk-level: "Critical"
interval: 5m0s

View File

@@ -17,7 +17,7 @@ data:
ttl 30
}
hosts {
192.168.0.30 vault.hrajfrisbee.cz
# 192.168.0.30 vault.hrajfrisbee.cz
fallthrough
}
prometheus :9153

View File

@@ -0,0 +1,19 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: mariadb-operator-crds
namespace: mariadb-operator
spec:
interval: 1h
chart:
spec:
chart: mariadb-operator-crds
version: "25.10.*"
sourceRef:
kind: HelmRepository
name: mariadb-operator
namespace: flux-system
install:
crds: Create
upgrade:
crds: CreateReplace

View File

@@ -0,0 +1,31 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: mariadb-operator
namespace: mariadb-operator
spec:
interval: 1h
dependsOn:
- name: mariadb-operator-crds
chart:
spec:
chart: mariadb-operator
version: "25.10.*"
sourceRef:
kind: HelmRepository
name: mariadb-operator
namespace: flux-system
values:
# uses built-in cert-controller for webhook TLS (no cert-manager dep)
webhook:
cert:
certManager:
enabled: false
# disable HA for operator itself (fine for testing)
ha:
enabled: false
# optional: enable metrics
metrics:
enabled: false
serviceMonitor:
enabled: false

View File

@@ -0,0 +1,8 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: mariadb-operator
namespace: flux-system
spec:
interval: 1h
url: https://helm.mariadb.com/mariadb-operator

View File

@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: mariadb-operator

View File

@@ -0,0 +1,34 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: nextcloud-secrets
namespace: nextcloud
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend # or your store
kind: ClusterSecretStore
target:
name: nextcloud-secrets
creationPolicy: Owner
data:
- secretKey: nextcloud-password
remoteRef:
key: k8s_home/nextcloud/admin
property: password
- secretKey: nextcloud-username
remoteRef:
key: k8s_home/nextcloud/admin
property: username
- secretKey: db-username
remoteRef:
key: k8s_home/nextcloud/postgres
property: db-username
- secretKey: postgres-password
remoteRef:
key: k8s_home/nextcloud/postgres
property: password
- secretKey: redis-password
remoteRef:
key: k8s_home/nextcloud/redis
property: password

View File

@@ -0,0 +1,263 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: nextcloud
namespace: nextcloud
spec:
interval: 30m
timeout: 15m # Nextcloud init can be slow
chart:
spec:
chart: nextcloud
version: "8.6.0" # Latest as of Jan 2025
sourceRef:
kind: HelmRepository
name: nextcloud
namespace: flux-system
interval: 12h
install:
crds: CreateReplace
remediation:
retries: 3
upgrade:
crds: CreateReplace
cleanupOnFail: true
remediation:
retries: 3
remediateLastFailure: true
# CRITICAL: Suspend during major version upgrades to prevent restart loops
# suspend: true
values:
image:
repository: nextcloud
tag: 32.0.3-apache # Latest as of Jan 2025. For fresh installs only.
# UPGRADE PATH: If upgrading from older version, go sequentially:
# 29.x → 30.0.x → 31.0.x → 32.0.x (one major at a time)
pullPolicy: IfNotPresent
replicaCount: 1 # >1 requires Redis, see below
nextcloud:
host: nextcloud.lab.home.hrajfrisbee.cz # Substitute or hardcode
# existingSecret: nextcloud-admin # Alternative to inline credentials
existingSecret:
enabled: true
secretName: nextcloud-secrets
# usernameKey: username
passwordKey: nextcloud-password
username: admin
# password set via valuesFrom secret
# PHP tuning - critical for stability
phpConfigs:
uploadLimit.ini: |
upload_max_filesize = 16G
post_max_size = 16G
max_input_time = 3600
max_execution_time = 3600
www-conf.ini: |
[www]
pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
pm.max_requests = 500
memory.ini: |
memory_limit = 1G
opcache.ini: |
opcache.enable = 1
opcache.interned_strings_buffer = 32
opcache.max_accelerated_files = 10000
opcache.memory_consumption = 256
opcache.save_comments = 1
opcache.revalidate_freq = 60
; Set to 0 if using ConfigMap-mounted configs
configs:
# Proxy and overwrite settings - CRITICAL for ingress
proxy.config.php: |-
<?php
$CONFIG = array (
'trusted_proxies' => array(
0 => '127.0.0.1',
1 => '10.0.0.0/8',
2 => '172.16.0.0/12',
3 => '192.168.0.0/16',
),
'forwarded_for_headers' => array('HTTP_X_FORWARDED_FOR'),
'overwriteprotocol' => 'https',
);
# Performance and maintenance
custom.config.php: |-
<?php
$CONFIG = array (
'default_phone_region' => 'US',
'maintenance_window_start' => 1,
'filelocking.enabled' => true,
'memcache.local' => '\\OC\\Memcache\\APCu',
'memcache.distributed' => '\\OC\\Memcache\\Redis',
'memcache.locking' => '\\OC\\Memcache\\Redis',
'redis' => array(
'host' => 'nextcloud-redis-master',
'port' => 6379,
'password' => getenv('REDIS_PASSWORD'),
),
);
extraEnv:
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: nextcloud-secrets
key: redis-password
# Ingress - adjust for your ingress controller
ingress:
enabled: true
className: nginx # or traefik, etc.
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "16G"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/server-snippet: |
server_tokens off;
proxy_hide_header X-Powered-By;
rewrite ^/.well-known/webfinger /index.php/.well-known/webfinger last;
rewrite ^/.well-known/nodeinfo /index.php/.well-known/nodeinfo last;
rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json;
location = /.well-known/carddav {
return 301 $scheme://$host/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $scheme://$host/remote.php/dav;
}
cert-manager.io/cluster-issuer: letsencrypt-prod
tls:
- secretName: nextcloud-tls
hosts:
- nextcloud.lab.home.hrajfrisbee.cz
# PostgreSQL - strongly recommended over MariaDB for Nextcloud
postgresql:
enabled: true
global:
postgresql:
auth:
username: nextcloud
database: nextcloud
existingSecret: nextcloud-secrets
secretKeys:
userPasswordKey: postgres-password
primary:
persistence:
enabled: true
size: 8Gi
storageClass: "" # Use default or specify
resources:
requests:
memory: 256Mi
cpu: 100m
limits:
memory: 512Mi
# Redis - required for file locking and sessions
redis:
enabled: true
auth:
enabled: true
existingSecret: nextcloud-secrets
existingSecretPasswordKey: redis-password
architecture: standalone
master:
persistence:
enabled: true
size: 1Gi
# Disable built-in databases we're not using
mariadb:
enabled: false
internalDatabase:
enabled: false
externalDatabase:
enabled: true
type: postgresql
host: nextcloud-postgresql # Service name created by subchart
user: nextcloud
database: nextcloud
existingSecret:
enabled: true
secretName: nextcloud-secrets
passwordKey: postgres-password
# Cron job - CRITICAL: never use AJAX cron
cronjob:
enabled: true
schedule: "*/5 * * * *"
resources:
requests:
memory: 256Mi
cpu: 50m
limits:
memory: 512Mi
# Main persistence
persistence:
enabled: true
storageClass: "" # Specify your storage class
size: 100Gi
accessMode: ReadWriteOnce
# nextcloudData - separate PVC for user data (recommended)
nextcloudData:
enabled: true
storageClass: ""
size: 500Gi
accessMode: ReadWriteOnce
# Resource limits - tune based on usage
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
memory: 2Gi
# Liveness/Readiness - tuned to prevent upgrade restart loops
livenessProbe:
enabled: true
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 6
successThreshold: 1
readinessProbe:
enabled: true
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 6
successThreshold: 1
startupProbe:
enabled: true
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 30 # 15 minutes for upgrades
# Security context - avoid fsGroup recursive chown
securityContext:
fsGroupChangePolicy: OnRootMismatch
podSecurityContext:
fsGroup: 33 # www-data
# Metrics - optional but recommended
metrics:
enabled: false # Enable if you have Prometheus
# serviceMonitor:
# enabled: true

View File

@@ -0,0 +1,8 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: nextcloud
namespace: flux-system
spec:
interval: 24h
url: https://nextcloud.github.io/helm/

View File

@@ -0,0 +1,7 @@
apiVersion: v1
kind: Namespace
metadata:
name: nextcloud
labels:
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/warn: restricted

View File

@@ -39,6 +39,7 @@ spec:
ingress:
enabled: true
className: nginx
pathType: Prefix
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:

View File

@@ -4,7 +4,7 @@ metadata:
name: oauth2-proxy-secrets
namespace: oauth2-proxy
annotations:
kustomize.toolkit.fluxcd.io/reconcile: disabled
kustomize.toolkit.fluxcd.io/reconcile: disabled
stringData:
client-id: oauth2-proxy
client-secret: <REPLACE_WITH_KANIDM_SECRET>

View File

@@ -33,7 +33,7 @@ spec:
rabbitmqHost: "plane-mq.lab.home.hrajfrisbee.cz" # optional
ingressClass: nginx
ingress_annotations:
cert-manager.io/cluster-issuer: letsencrypt-production
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/auth-url: "https://oauth2-proxy.lab.home.hrajfrisbee.cz/oauth2/auth"
nginx.ingress.kubernetes.io/auth-signin: "https://oauth2-proxy.lab.home.hrajfrisbee.cz/oauth2/start?rd=$scheme://$host$escaped_request_uri"
nginx.ingress.kubernetes.io/auth-response-headers: "X-Auth-Request-User,X-Auth-Request-Email,Authorization"

View File

@@ -24,7 +24,7 @@ spec:
- host: podinfo.lab.home.hrajfrisbee.cz
paths:
- path: /
pathType: ImplementationSpecific
pathType: Prefix
tls: []
# - secretName: chart-example-tls
# hosts:

View File

@@ -0,0 +1,12 @@
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'kacerr.cz@gmail.com'
EMAIL_HOST_PASSWORD = 'zeyd ppmy gfqu gaws' # App Password, not your login password
EMAIL_PORT = 587
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
SERVER_EMAIL = EMAIL_HOST_USER
FILE_SERVER_ROOT = 'https://seafile.lab.home.hrajfrisbee.cz/seafhttp'
DEBUG = True

View File

@@ -0,0 +1,30 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: seafile-secret
namespace: seafile
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend # or your store
kind: ClusterSecretStore
target:
name: seafile-secret
creationPolicy: Owner
data:
- secretKey: JWT_PRIVATE_KEY
remoteRef:
key: k8s_home/seafile
property: JWT_PRIVATE_KEY
- secretKey: SEAFILE_MYSQL_DB_PASSWORD
remoteRef:
key: k8s_home/seafile
property: SEAFILE_MYSQL_DB_PASSWORD
- secretKey: INIT_SEAFILE_ADMIN_PASSWORD
remoteRef:
key: k8s_home/seafile
property: INIT_SEAFILE_ADMIN_PASSWORD
- secretKey: INIT_SEAFILE_MYSQL_ROOT_PASSWORD
remoteRef:
key: k8s_home/seafile
property: INIT_SEAFILE_MYSQL_ROOT_PASSWORD

View File

@@ -0,0 +1,114 @@
# apps/seafile/helmrelease.yaml
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: seafile
namespace: seafile
spec:
interval: 30m
chart:
spec:
chart: ce
version: "13.0.2"
sourceRef:
kind: HelmRepository
name: seafile
namespace: flux-system
install:
createNamespace: true
remediation:
retries: 3
upgrade:
remediation:
retries: 3
# Post-render patches
postRenderers:
- kustomize:
patches:
# Remove imagePullSecrets from all Deployments
- target:
kind: Deployment
patch: |
- op: remove
path: /spec/template/spec/imagePullSecrets
# Remove from StatefulSets (MariaDB, etc.)
- target:
kind: StatefulSet
patch: |
- op: remove
path: /spec/template/spec/imagePullSecrets
# Remove from Pods if any
- target:
kind: Pod
patch: |
- op: remove
path: /spec/imagePullSecrets
values:
seafile:
initMode: true
# The following are the configurations of seafile container
configs:
image: seafileltd/seafile-mc:13.0-latest
seafileDataVolume:
storage: 10Gi
# The following are environments of seafile services
env:
# for Seafile server
TIME_ZONE: "UTC"
SEAFILE_LOG_TO_STDOUT: "true"
SITE_ROOT: "/"
SEAFILE_SERVER_HOSTNAME: "seafile.lab.home.hrajfrisbee.cz"
SEAFILE_SERVER_PROTOCOL: "https"
# for database
SEAFILE_MYSQL_DB_HOST: "seafile-mariadb"
SEAFILE_MYSQL_DB_PORT: "3306"
SEAFILE_MYSQL_DB_USER: "seafile"
#SEAFILE_MYSQL_DB_CCNET_DB_NAME: "ccnet-db"
#SEAFILE_MYSQL_DB_SEAFILE_DB_NAME: "seafile-db"
#SEAFILE_MYSQL_DB_SEAHUB_DB_NAME: "seahub-db"
# for cache
CACHE_PROVIDER: "redis"
## for redis
REDIS_HOST: "redis"
REDIS_PORT: "6379"
## for memcached
#MEMCACHED_HOST: ""
#MEMCACHED_PORT: "11211"
# for notification
ENABLE_NOTIFICATION_SERVER: "false"
NOTIFICATION_SERVER_URL: ""
# for seadoc
ENABLE_SEADOC: "false"
SEADOC_SERVER_URL: "" # only valid in ENABLE_SEADOC = true
# for Seafile AI
ENABLE_SEAFILE_AI: "false"
SEAFILE_AI_SERVER_URL: ""
# for Metadata server
MD_FILE_COUNT_LIMIT: "100000"
# initialization (only valid in first-time deployment and initMode = true)
## for Seafile admin
INIT_SEAFILE_ADMIN_EMAIL: "kacerr.cz@gmail.com"
# if you are using another secret name / key for seafile or mysql, please make correct the following fields:
#secretsMap:
# DB_ROOT_PASSWD: # Env's name
# secret: seafile-secret # secret's name, `seafile-secret` if not specify
# key: INIT_SEAFILE_MYSQL_ROOT_PASSWORD # secret's key, `Env's name` if not specify
# extra configurations
extraResources: {}
extraEnv: []
extraVolumes: []

View File

@@ -0,0 +1,8 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: seafile
namespace: flux-system
spec:
interval: 1h
url: https://haiwen.github.io/seafile-helm-chart/repo

View File

@@ -0,0 +1,35 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
meta.helm.sh/release-name: seafile
meta.helm.sh/release-namespace: seafile
nginx.ingress.kubernetes.io/proxy-body-size: "100m" # 0 = unlimited, or "500m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
nginx.ingress.kubernetes.io/proxy-request-buffering: "off"
labels:
app.kubernetes.io/component: app
app.kubernetes.io/instance: seafile
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: seafile
name: seafile
namespace: seafile
spec:
ingressClassName: nginx
rules:
- host: seafile.lab.home.hrajfrisbee.cz
http:
paths:
- backend:
service:
name: seafile
port:
number: 80
path: /
pathType: Prefix
tls:
- hosts:
- seafile.lab.home.hrajfrisbee.cz
secretName: seafile-tls

View File

@@ -0,0 +1,10 @@
apiVersion: k8s.mariadb.com/v1alpha1
kind: Database
metadata:
name: ccnet-db
namespace: seafile
spec:
mariaDbRef:
name: seafile-mariadb
characterSet: utf8mb4
collate: utf8mb4_general_ci

View File

@@ -0,0 +1,10 @@
apiVersion: k8s.mariadb.com/v1alpha1
kind: Database
metadata:
name: seafile-db
namespace: seafile
spec:
mariaDbRef:
name: seafile-mariadb
characterSet: utf8mb4
collate: utf8mb4_general_ci

View File

@@ -0,0 +1,10 @@
apiVersion: k8s.mariadb.com/v1alpha1
kind: Database
metadata:
name: seahub-db
namespace: seafile
spec:
mariaDbRef:
name: seafile-mariadb
characterSet: utf8mb4
collate: utf8mb4_general_ci

View File

@@ -0,0 +1,61 @@
apiVersion: k8s.mariadb.com/v1alpha1
kind: Grant
metadata:
name: all-privileges
spec:
mariaDbRef:
name: seafile-mariadb
username: seafile
database: "*"
table: "*"
privileges:
- ALL PRIVILEGES
grantOption: true
# ---
# apiVersion: k8s.mariadb.com/v1alpha1
# kind: Grant
# metadata:
# name: seafile-grant
# namespace: seafile
# spec:
# mariaDbRef:
# name: seafile-mariadb
# privileges:
# - ALL PRIVILEGES
# database: seafile-db
# table: "*"
# username: seafile
# host: "%"
# grantOption: false
# ---
# apiVersion: k8s.mariadb.com/v1alpha1
# kind: Grant
# metadata:
# name: seahub-grant
# namespace: seafile
# spec:
# mariaDbRef:
# name: seafile-mariadb
# privileges:
# - ALL PRIVILEGES
# database: seahub-db
# table: "*"
# username: seafile
# host: "%"
# grantOption: false
# ---
# apiVersion: k8s.mariadb.com/v1alpha1
# kind: Grant
# metadata:
# name: ccnet-grant
# namespace: seafile
# spec:
# mariaDbRef:
# name: seafile-mariadb
# privileges:
# - ALL PRIVILEGES
# database: ccnet-db
# table: "*"
# username: seafile
# host: "%"
# grantOption: false

View File

@@ -0,0 +1,13 @@
apiVersion: k8s.mariadb.com/v1alpha1
kind: User
metadata:
name: seafile
namespace: seafile
spec:
mariaDbRef:
name: seafile-mariadb
passwordSecretKeyRef:
name: seafile-secret
key: SEAFILE_MYSQL_DB_PASSWORD
maxUserConnections: 20
host: "%"

View File

@@ -0,0 +1,33 @@
apiVersion: k8s.mariadb.com/v1alpha1
kind: MariaDB
metadata:
name: seafile-mariadb
namespace: seafile
spec:
rootPasswordSecretKeyRef:
name: seafile-secret
key: INIT_SEAFILE_MYSQL_ROOT_PASSWORD
image: mariadb:11.4
port: 3306
storage:
size: 10Gi
# storageClassName: your-storage-class
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
memory: 1Gi
myCnf: |
[mariadb]
bind-address=*
default_storage_engine=InnoDB
binlog_format=row
innodb_autoinc_lock_mode=2
innodb_buffer_pool_size=256M
max_allowed_packet=256M

View File

@@ -0,0 +1,39 @@
# apiVersion: apps/v1
# kind: Deployment
# metadata:
# name: seafile-memcached
# namespace: seafile
# spec:
# replicas: 1
# selector:
# matchLabels:
# app: seafile-memcached
# template:
# metadata:
# labels:
# app: seafile-memcached
# spec:
# containers:
# - name: memcached
# image: memcached:1.6-alpine
# args: ["-m", "128"] # 128MB memory limit
# ports:
# - containerPort: 11211
# resources:
# requests:
# memory: 64Mi
# cpu: 25m
# limits:
# memory: 192Mi
# ---
# apiVersion: v1
# kind: Service
# metadata:
# name: seafile-memcached
# namespace: seafile
# spec:
# selector:
# app: seafile-memcached
# ports:
# - port: 11211
# targetPort: 11211

Some files were not shown because too many files have changed in this diff Show More