Compare commits

..

1 commit

Author SHA1 Message Date
6424c51a86
switch from ceph buckets to minio bucket 2024-05-08 18:37:29 -05:00
534 changed files with 25843 additions and 19409 deletions

9
.ansible-lint Normal file
View file

@ -0,0 +1,9 @@
---
skip_list:
- yaml[line-length]
- var-naming
warn_list:
- command-instead-of-shell
- deprecated-command-syntax
- experimental
- no-changed-when

View file

@ -0,0 +1,52 @@
---
# yaml-language-server: $schema=https://taskfile.dev/schema.json
version: "3"
vars:
PYTHON_BIN: python3
env:
PATH: "{{.ROOT_DIR}}/.venv/bin:$PATH"
VIRTUAL_ENV: "{{.ROOT_DIR}}/.venv"
ANSIBLE_COLLECTIONS_PATH: "{{.ROOT_DIR}}/.venv/galaxy"
ANSIBLE_ROLES_PATH: "{{.ROOT_DIR}}/.venv/galaxy/ansible_roles"
ANSIBLE_VARS_ENABLED: "host_group_vars,community.sops.sops"
tasks:
deps:
desc: Set up Ansible dependencies for the environment
cmds:
- task: .venv
run:
desc: Run an Ansible playbook for configuring a cluster
summary: |
Args:
cluster: Cluster to run command against (required)
playbook: Playbook to run (required)
prompt: Run Ansible playbook '{{.playbook}}' against the '{{.cluster}}' cluster... continue?
deps: ["deps"]
cmd: |
.venv/bin/ansible-playbook \
--inventory {{.ANSIBLE_DIR}}/{{.cluster}}/inventory/hosts.yaml \
{{.ANSIBLE_DIR}}/{{.cluster}}/playbooks/{{.playbook}}.yaml {{.CLI_ARGS}}
preconditions:
- { msg: "Argument (cluster) is required", sh: "test -n {{.cluster}}" }
- { msg: "Argument (playbook) is required", sh: "test -n {{.playbook}}" }
- { msg: "Venv not found", sh: "test -d {{.ROOT_DIR}}/.venv" }
- { msg: "Inventory not found", sh: "test -f {{.ANSIBLE_DIR}}/{{.cluster}}/inventory/hosts.yaml" }
- { msg: "Playbook not found", sh: "test -f {{.ANSIBLE_DIR}}/{{.cluster}}/playbooks/{{.playbook}}.yaml" }
.venv:
internal: true
cmds:
- true && {{.PYTHON_BIN}} -m venv {{.ROOT_DIR}}/.venv
- .venv/bin/python3 -m pip install --upgrade pip setuptools wheel
- .venv/bin/python3 -m pip install --upgrade --requirement {{.ANSIBLE_DIR}}/requirements.txt
- .venv/bin/ansible-galaxy install --role-file "{{.ANSIBLE_DIR}}/requirements.yaml" --force
sources:
- "{{.ANSIBLE_DIR}}/requirements.txt"
- "{{.ANSIBLE_DIR}}/requirements.yaml"
generates:
- "{{.ROOT_DIR}}/.venv/pyvenv.cfg"

View file

@ -1,81 +0,0 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/bjw-s/helm-charts/main/charts/other/app-template/schemas/helmrelease-helm-v2.schema.json
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: &app comfyui
spec:
interval: 30m
chart:
spec:
chart: app-template
version: 3.5.1
sourceRef:
kind: HelmRepository
name: bjw-s
namespace: flux-system
install:
remediation:
retries: 3
upgrade:
cleanupOnFail: true
remediation:
retries: 3
strategy: rollback
values:
controllers:
comfyui:
annotations:
reloader.stakater.com/auto: "true"
pod:
nodeSelector:
nvidia.com/gpu.present: "true"
runtimeClassName: nvidia
containers:
app:
image:
repository: docker.io/jahanson/comfyui
tag: v0.0.1
resources:
requests:
cpu: 500m
memory: 2Gi
limits:
memory: 60Gi
nvidia.com/gpu: 1 # requesting 1 GPU
service:
app:
controller: comfyui
ports:
http:
port: 7860
ingress:
app:
enabled: true
className: internal-nginx
hosts:
- host: &host "{{ .Release.Name }}.jahanson.tech"
paths:
- path: /
service:
identifier: app
port: http
tls:
- hosts:
- *host
persistence:
models:
enabled: true
existingClaim: stablediffusion-checkpoints
globalMounts:
- path: /data/models
config:
enabled: true
existingClaim: comfyui
globalMounts:
- path: /data/config
output:
enabled: true
type: emptyDir
globalMounts:
- path: /output

View file

@ -1,9 +0,0 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ./helmrelease.yaml
- ./pvc.yaml
- ../../../../templates/volsync
- ../../../../templates/gatus/internal

View file

@ -1,12 +0,0 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: stablediffusion-checkpoints
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 300Gi
storageClassName: openebs-hostpath

View file

@ -1,31 +0,0 @@
---
# yaml-language-server: $schema=https://ks.hsn.dev/kustomize.toolkit.fluxcd.io/kustomization_v1.json
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: &app comfyui
namespace: flux-system
spec:
targetNamespace: ai
commonMetadata:
labels:
app.kubernetes.io/name: *app
dependsOn:
- name: nvidia-device-plugin
- name: node-feature-discovery
- name: volsync
- name: rook-ceph-cluster
path: ./kubernetes/apps/ai/stable-diffusion/comfyui
prune: true
sourceRef:
kind: GitRepository
name: theshire
wait: false
interval: 30m
retryInterval: 1m
timeout: 5m
postBuild:
substitute:
APP: *app
VOLSYNC_CAPACITY: 5Gi
GATUS_SUBDOMAIN: comfyui

View file

@ -1,20 +0,0 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/external-secrets.io/externalsecret_v1beta1.json
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: nicehash
spec:
refreshInterval: 1m
secretStoreRef:
kind: ClusterSecretStore
name: onepassword-connect
target:
name: nicehash-secret
template:
type: Opaque
data:
MINING_ADDRESS: "{{ .MINING_ADDRESS }}"
dataFrom:
- extract:
key: nicehash

View file

@ -1,72 +0,0 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/bjw-s/helm-charts/main/charts/other/app-template/schemas/helmrelease-helm-v2.schema.json
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: nicehash
spec:
interval: 30m
chart:
spec:
chart: app-template
version: 3.5.1
sourceRef:
kind: HelmRepository
name: bjw-s
namespace: flux-system
install:
remediation:
retries: 3
upgrade:
cleanupOnFail: true
remediation:
strategy: rollback
retries: 3
values:
controllers:
nicehash:
annotations:
reloader.stakater.com/auto: "true"
containers:
app:
image:
repository: docker.io/dockerhubnh/nicehash
tag: latest
envFrom:
- secretRef:
name: nicehash-secret
env:
TZ: America/Chicago
MINING_WORKER_NAME: shadowfax
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: { drop: ["ALL"] }
resources:
requests:
cpu: 10m
limits:
nvidia.com/gpu: 1 # requesting 1 GPU
memory: 10Gi
defaultPodOptions:
securityContext:
runAsNonRoot: true
runAsUser: 568
runAsGroup: 568
fsGroup: 568
fsGroupChangePolicy: OnRootMismatch
seccompProfile: { type: RuntimeDefault }
nodeSelector:
nvidia.com/gpu.present: "true"
runtimeClassName: nvidia
persistence:
logs:
type: emptyDir
globalMounts:
- path: /var/log/
tmp:
type: emptyDir
cache:
existingClaim: nicehash
globalMounts:
- path: /var/cache/nhm4/

View file

@ -1,27 +0,0 @@
---
# yaml-language-server: $schema=https://ks.hsn.dev/kustomize.toolkit.fluxcd.io/kustomization_v1.json
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: &app nicehash
namespace: flux-system
spec:
targetNamespace: default
commonMetadata:
labels:
app.kubernetes.io/name: *app
dependsOn:
- name: external-secrets-stores
- name: rook-ceph-cluster
path: ./kubernetes/apps/default/nicehash/app
prune: true
sourceRef:
kind: GitRepository
name: theshire
wait: false
interval: 30m
timeout: 5m
postBuild:
substitute:
APP: *app
VOLSYNC_CAPACITY: 1Gi

View file

@ -1,34 +0,0 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/external-secrets.io/externalsecret_v1beta1.json
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: piped
spec:
refreshInterval: 1m
secretStoreRef:
name: crunchy-pgo-secrets
kind: ClusterSecretStore
target:
name: piped-secret
template:
type: Opaque
data:
config.properties: |
API_URL: https://piped-api.hsn.dev
COMPROMISED_PASSWORD_CHECK: true
DISABLE_REGISTRATION: true
FEED_RETENTION: 30
FRONTEND_URL: https://piped.hsn.dev
HTTP_WORKERS: 4
MATRIX_SERVER: https://element.infosec.exchange
PORT: 8080
PROXY_PART: https://piped-proxy.jahanson.tech
SENTRY_DSN:
hibernate.connection.driver_class: org.postgresql.Driver
hibernate.connection.url: jdbc:postgresql://{{ index . "host" }}:5432/{{ index . "dbname" }}
hibernate.connection.username: {{ index . "user" }}
hibernate.connection.password: {{ index . "password" }}
dataFrom:
- extract:
key: postgres-pguser-piped

View file

@ -1,182 +0,0 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/bjw-s/helm-charts/main/charts/other/app-template/schemas/helmrelease-helm-v2.schema.json
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: piped
spec:
chart:
spec:
chart: app-template
version: 3.5.1
interval: 30m
sourceRef:
kind: HelmRepository
name: bjw-s
namespace: flux-system
interval: 30m
values:
defaultPodOptions:
automountServiceAccountToken: false
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
fsGroupChangePolicy: "OnRootMismatch"
controllers:
backend:
strategy: RollingUpdate
annotations:
secret.reloader.stakater.com/reload: piped-secret
containers:
app:
image:
repository: 1337kavin/piped
tag: latest@sha256:18e77857414236edc7245bebb3fb8ab3ac49c44bd76701bfce24f6ba0170d4b8
probes:
liveness:
enabled: true
readiness:
enabled: true
resources:
requests:
cpu: 10m
memory: 500Mi
limits:
memory: 2000Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
frontend:
strategy: RollingUpdate
containers:
app:
image:
repository: ghcr.io/bjw-s-labs/piped-frontend
tag: 2024.11.4@sha256:0e413986606f39cdc6afa0379feca912d4a4abbdcbe67b408c9fbe19fbabd10f
env:
BACKEND_HOSTNAME: piped-api.hsn.dev
probes:
liveness:
enabled: true
readiness:
enabled: true
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
memory: 256Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
ytproxy:
strategy: RollingUpdate
containers:
app:
image:
repository: 1337kavin/piped-proxy
tag: latest@sha256:ab9e472107337886d71b0151b6e777fc4cba0dd8251a21d4788a7a7f165f545a
command:
- /app/piped-proxy
probes:
liveness:
enabled: true
readiness:
enabled: true
resources:
requests:
cpu: 10m
memory: 500Mi
limits:
memory: 2000Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
service:
backend:
controller: backend
ports:
http:
port: 8080
frontend:
controller: frontend
ports:
http:
port: 8080
ytproxy:
controller: ytproxy
ports:
http:
port: 8080
ingress:
backend:
className: "external-nginx"
annotations:
external-dns.alpha.kubernetes.io/target: external.hsn.dev
external-dns.alpha.kubernetes.io/cloudflare-proxied: "true"
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://piped.hsn.dev, https://piped-api.hsn.dev, https://piped-proxy.jahanson.tech"
hosts:
- host: piped-api.hsn.dev
paths:
- path: /
service:
identifier: backend
port: http
frontend:
className: "external-nginx"
annotations:
external-dns.alpha.kubernetes.io/target: external.hsn.dev
external-dns.alpha.kubernetes.io/cloudflare-proxied: "true"
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://piped.hsn.dev, https://piped-api.hsn.dev, https://piped-proxy.jahanson.tech"
hosts:
- host: piped.hsn.dev
paths:
- path: /
service:
identifier: frontend
port: http
ytproxy:
className: "internal-nginx"
annotations:
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://piped.hsn.dev, https://piped-api.hsn.dev, https://piped-proxy.jahanson.tech"
hosts:
- host: piped-proxy.jahanson.tech
paths:
- path: /
service:
identifier: ytproxy
port: http
persistence:
config:
type: secret
name: piped-secret
advancedMounts:
backend:
app:
- path: /app/config.properties
subPath: config.properties
readOnly: true

View file

@ -1,15 +1,16 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/bjw-s/helm-charts/main/charts/other/app-template/schemas/helmrelease-helm-v2.schema.json
apiVersion: helm.toolkit.fluxcd.io/v2
# yaml-language-server: $schema=https://raw.githubusercontent.com/bjw-s/helm-charts/main/charts/other/app-template/schemas/helmrelease-helm-v2beta2.schema.json
apiVersion: helm.toolkit.fluxcd.io/v2beta2
kind: HelmRelease
metadata:
name: &app jellyfin
name: jellyfin
namespace: default
spec:
interval: 30m
chart:
spec:
chart: app-template
version: 3.5.1
version: 3.1.0
sourceRef:
kind: HelmRepository
name: bjw-s
@ -20,31 +21,26 @@ spec:
upgrade:
cleanupOnFail: true
remediation:
strategy: rollback
retries: 3
dependsOn:
- name: nvidia-device-plugin
namespace: kube-system
- name: node-feature-discovery
namespace: kube-system
- name: rook-ceph-cluster
namespace: rook-ceph
- name: volsync
namespace: volsync-system
strategy: rollback
values:
controllers:
jellyfin:
type: statefulset
annotations:
reloader.stakater.com/auto: "true"
containers:
app:
image:
repository: ghcr.io/jellyfin/jellyfin
tag: 10.10.1@sha256:12b7aa2c8086e5566badc35370fab41b8cc8774dc3a80b07a1d6eb14f282b816
repository: jellyfin/jellyfin
tag: 10.8.13
env:
NVIDIA_VISIBLE_DEVICES: "all"
NVIDIA_DRIVER_CAPABILITIES: "compute,video,utility"
DOTNET_SYSTEM_IO_DISABLEFILELOCKING: "true"
JELLYFIN_FFmpeg__probesize: 50000000
JELLYFIN_FFmpeg__analyzeduration: 50000000
JELLYFIN_PublishedServerUrl: jelly.hsn.dev
TZ: America/Chicago
probes:
liveness: &probes
@ -63,76 +59,58 @@ spec:
enabled: false
resources:
requests:
cpu: 100m
limits:
nvidia.com/gpu: 1 # requesting 1 GPU
cpu: 100m
memory: 512Mi
limits:
nvidia.com/gpu: 1
memory: 4Gi
defaultPodOptions:
securityContext:
runAsNonRoot: true
runAsUser: 568
runAsGroup: 568
fsGroup: 568
fsGroupChangePolicy: OnRootMismatch
supplementalGroups: [44, 10000]
seccompProfile: { type: RuntimeDefault }
nodeSelector:
nvidia.com/gpu.present: "true"
runtimeClassName: nvidia
pod:
runtimeClassName: nvidia
enableServiceLinks: false
nodeSelector:
nvidia.com/gpu.present: "true"
securityContext:
runAsUser: 568
runAsGroup: 568
fsGroup: 568
fsGroupChangePolicy: OnRootMismatch
supplementalGroups: [44, 105, 10000]
service:
app:
controller: *app
type: LoadBalancer
annotations:
io.cilium/lb-ipam-ips: 10.1.1.40
controller: jellyfin
ports:
http:
port: *port
ingress:
app:
annotations:
external-dns.alpha.kubernetes.io/target: external.hsn.dev
external-dns.alpha.kubernetes.io/cloudflare-proxied: "true"
enabled: true
className: external-nginx
annotations:
external-dns.alpha.kubernetes.io/cloudflare-proxied: "true"
external-dns.alpha.kubernetes.io/target: external.hsn.dev
hosts:
- host: "{{ .Release.Name }}.hsn.dev"
- host: &host "jelly.hsn.dev"
paths:
- path: /
service:
identifier: app
port: *port
internal:
className: internal-nginx
hosts:
- host: &host "{{ .Release.Name }}.jahanson.tech"
paths:
- path: /
service:
identifier: app
port: *port
port: http
tls:
- hosts:
- *host
persistence:
config:
existingClaim: jellyfin
enabled: true
existingClaim: *app
globalMounts:
- path: /config
media:
type: nfs
server: shadowfax.jahanson.tech
path: /moria/media
globalMounts:
- path: /media
readOnly: true
transcode:
enabled: true
type: emptyDir
globalMounts:
- path: /transcode
cache:
media:
enabled: true
type: emptyDir
type: nfs
server: 10.1.1.12
path: /mnt/users/Media
globalMounts:
- path: /cache
- path: /media

View file

@ -2,7 +2,7 @@
# yaml-language-server: $schema=https://json.schemastore.org/kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: default
resources:
- ./gatus.yaml
- ./helmrelease.yaml
- ../../../../templates/volsync

View file

@ -3,23 +3,21 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: &app redlib
name: &app jellyfin
namespace: flux-system
spec:
targetNamespace: default
commonMetadata:
labels:
app.kubernetes.io/name: *app
dependsOn:
- name: external-secrets-stores
path: ./kubernetes/apps/default/redlib/app
path: ./kubernetes/apps/default/jellyfin/app
prune: true
sourceRef:
kind: GitRepository
name: theshire
name: homelab
wait: false
interval: 30m
retryInterval: 1m
timeout: 5m
postBuild:
substitute:
APP: *app
VOLSYNC_CAPACITY: 10Gi

View file

@ -0,0 +1,588 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.14.0
creationTimestamp: null
name: ciliumbgppeeringpolicies.cilium.io
spec:
group: cilium.io
names:
categories:
- cilium
- ciliumbgp
kind: CiliumBGPPeeringPolicy
listKind: CiliumBGPPeeringPolicyList
plural: ciliumbgppeeringpolicies
shortNames:
- bgpp
singular: ciliumbgppeeringpolicy
scope: Cluster
versions:
- additionalPrinterColumns:
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v2alpha1
schema:
openAPIV3Schema:
description: CiliumBGPPeeringPolicy is a Kubernetes third-party resource for
instructing Cilium's BGP control plane to create virtual BGP routers.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: Spec is a human readable description of a BGP peering policy
properties:
nodeSelector:
description: "NodeSelector selects a group of nodes where this BGP
Peering Policy applies. \n If empty / nil this policy applies to
all nodes."
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements.
The requirements are ANDed.
items:
description: A label selector requirement is a selector that
contains values, a key, and an operator that relates the key
and values.
properties:
key:
description: key is the label key that the selector applies
to.
type: string
operator:
description: operator represents a key's relationship to
a set of values. Valid operators are In, NotIn, Exists
and DoesNotExist.
enum:
- In
- NotIn
- Exists
- DoesNotExist
type: string
values:
description: values is an array of string values. If the
operator is In or NotIn, the values array must be non-empty.
If the operator is Exists or DoesNotExist, the values
array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
description: MatchLabelsValue represents the value from the
MatchLabels {key,value} pair.
maxLength: 63
pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$
type: string
description: matchLabels is a map of {key,value} pairs. A single
{key,value} in the matchLabels map is equivalent to an element
of matchExpressions, whose key field is "key", the operator
is "In", and the values array contains only "value". The requirements
are ANDed.
type: object
type: object
virtualRouters:
description: A list of CiliumBGPVirtualRouter(s) which instructs the
BGP control plane how to instantiate virtual BGP routers.
items:
description: CiliumBGPVirtualRouter defines a discrete BGP virtual
router configuration.
properties:
exportPodCIDR:
default: false
description: ExportPodCIDR determines whether to export the
Node's private CIDR block to the configured neighbors.
type: boolean
localASN:
description: LocalASN is the ASN of this virtual router. Supports
extended 32bit ASNs
format: int64
maximum: 4294967295
minimum: 0
type: integer
neighbors:
description: Neighbors is a list of neighboring BGP peers for
this virtual router
items:
description: CiliumBGPNeighbor is a neighboring peer for use
in a CiliumBGPVirtualRouter configuration.
properties:
advertisedPathAttributes:
description: AdvertisedPathAttributes can be used to apply
additional path attributes to selected routes when advertising
them to the peer. If empty / nil, no additional path
attributes are advertised.
items:
description: CiliumBGPPathAttributes can be used to
apply additional path attributes to matched routes
when advertising them to a BGP peer.
properties:
communities:
description: Communities defines a set of community
values advertised in the supported BGP Communities
path attributes. If nil / not set, no BGP Communities
path attribute will be advertised.
properties:
large:
description: Large holds a list of the BGP Large
Communities Attribute (RFC 8092) values.
items:
description: BGPLargeCommunity type represents
a value of the BGP Large Communities Attribute
(RFC 8092), as three 4-byte decimal numbers
separated by colons.
pattern: ^([0-9]|[1-9][0-9]{1,8}|[1-3][0-9]{9}|4[01][0-9]{8}|42[0-8][0-9]{7}|429[0-3][0-9]{6}|4294[0-8][0-9]{5}|42949[0-5][0-9]{4}|429496[0-6][0-9]{3}|4294967[01][0-9]{2}|42949672[0-8][0-9]|429496729[0-5]):([0-9]|[1-9][0-9]{1,8}|[1-3][0-9]{9}|4[01][0-9]{8}|42[0-8][0-9]{7}|429[0-3][0-9]{6}|4294[0-8][0-9]{5}|42949[0-5][0-9]{4}|429496[0-6][0-9]{3}|4294967[01][0-9]{2}|42949672[0-8][0-9]|429496729[0-5]):([0-9]|[1-9][0-9]{1,8}|[1-3][0-9]{9}|4[01][0-9]{8}|42[0-8][0-9]{7}|429[0-3][0-9]{6}|4294[0-8][0-9]{5}|42949[0-5][0-9]{4}|429496[0-6][0-9]{3}|4294967[01][0-9]{2}|42949672[0-8][0-9]|429496729[0-5])$
type: string
type: array
standard:
description: Standard holds a list of "standard"
32-bit BGP Communities Attribute (RFC 1997)
values defined as numeric values.
items:
description: BGPStandardCommunity type represents
a value of the "standard" 32-bit BGP Communities
Attribute (RFC 1997) as a 4-byte decimal
number or two 2-byte decimal numbers separated
by a colon (<0-65535>:<0-65535>). For example,
no-export community value is 65553:65281.
pattern: ^([0-9]|[1-9][0-9]{1,8}|[1-3][0-9]{9}|4[01][0-9]{8}|42[0-8][0-9]{7}|429[0-3][0-9]{6}|4294[0-8][0-9]{5}|42949[0-5][0-9]{4}|429496[0-6][0-9]{3}|4294967[01][0-9]{2}|42949672[0-8][0-9]|429496729[0-5])$|^([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]):([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$
type: string
type: array
wellKnown:
description: WellKnown holds a list "standard"
32-bit BGP Communities Attribute (RFC 1997)
values defined as well-known string aliases
to their numeric values.
items:
description: "BGPWellKnownCommunity type represents
a value of the \"standard\" 32-bit BGP Communities
Attribute (RFC 1997) as a well-known string
alias to its numeric value. Allowed values
and their mapping to the numeric values:
\n internet = 0x00000000
(0:0) planned-shut = 0xffff0000
(65535:0) accept-own = 0xffff0001
(65535:1) route-filter-translated-v4 = 0xffff0002
(65535:2) route-filter-v4 = 0xffff0003
(65535:3) route-filter-translated-v6 = 0xffff0004
(65535:4) route-filter-v6 = 0xffff0005
(65535:5) llgr-stale = 0xffff0006
(65535:6) no-llgr = 0xffff0007
(65535:7) blackhole = 0xffff029a
(65535:666) no-export =
0xffffff01\t(65535:65281) no-advertise =
0xffffff02 (65535:65282) no-export-subconfed
\ = 0xffffff03 (65535:65283) no-peer
\ = 0xffffff04 (65535:65284)"
enum:
- internet
- planned-shut
- accept-own
- route-filter-translated-v4
- route-filter-v4
- route-filter-translated-v6
- route-filter-v6
- llgr-stale
- no-llgr
- blackhole
- no-export
- no-advertise
- no-export-subconfed
- no-peer
type: string
type: array
type: object
localPreference:
description: LocalPreference defines the preference
value advertised in the BGP Local Preference path
attribute. As Local Preference is only valid for
iBGP peers, this value will be ignored for eBGP
peers (no Local Preference path attribute will
be advertised). If nil / not set, the default
Local Preference of 100 will be advertised in
the Local Preference path attribute for iBGP peers.
format: int64
maximum: 4294967295
minimum: 0
type: integer
selector:
description: Selector selects a group of objects
of the SelectorType resulting into routes that
will be announced with the configured Attributes.
If nil / not set, all objects of the SelectorType
are selected.
properties:
matchExpressions:
description: matchExpressions is a list of label
selector requirements. The requirements are
ANDed.
items:
description: A label selector requirement
is a selector that contains values, a key,
and an operator that relates the key and
values.
properties:
key:
description: key is the label key that
the selector applies to.
type: string
operator:
description: operator represents a key's
relationship to a set of values. Valid
operators are In, NotIn, Exists and
DoesNotExist.
enum:
- In
- NotIn
- Exists
- DoesNotExist
type: string
values:
description: values is an array of string
values. If the operator is In or NotIn,
the values array must be non-empty.
If the operator is Exists or DoesNotExist,
the values array must be empty. This
array is replaced during a strategic
merge patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
description: MatchLabelsValue represents the
value from the MatchLabels {key,value} pair.
maxLength: 63
pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$
type: string
description: matchLabels is a map of {key,value}
pairs. A single {key,value} in the matchLabels
map is equivalent to an element of matchExpressions,
whose key field is "key", the operator is
"In", and the values array contains only "value".
The requirements are ANDed.
type: object
type: object
selectorType:
description: 'SelectorType defines the object type
on which the Selector applies: - For "PodCIDR"
the Selector matches k8s CiliumNode resources
(path attributes apply to routes announced for
PodCIDRs of selected CiliumNodes. Only affects
routes of cluster scope / Kubernetes IPAM CIDRs,
not Multi-Pool IPAM CIDRs. - For "CiliumLoadBalancerIPPool"
the Selector matches CiliumLoadBalancerIPPool
custom resources (path attributes apply to routes
announced for selected CiliumLoadBalancerIPPools).
- For "CiliumPodIPPool" the Selector matches CiliumPodIPPool
custom resources (path attributes apply to routes
announced for allocated CIDRs of selected CiliumPodIPPools).'
enum:
- PodCIDR
- CiliumLoadBalancerIPPool
- CiliumPodIPPool
type: string
required:
- selectorType
type: object
type: array
authSecretRef:
description: AuthSecretRef is the name of the secret to
use to fetch a TCP authentication password for this
peer.
type: string
connectRetryTimeSeconds:
default: 120
description: ConnectRetryTimeSeconds defines the initial
value for the BGP ConnectRetryTimer (RFC 4271, Section
8).
format: int32
maximum: 2147483647
minimum: 1
type: integer
eBGPMultihopTTL:
default: 1
description: EBGPMultihopTTL controls the multi-hop feature
for eBGP peers. Its value defines the Time To Live (TTL)
value used in BGP packets sent to the neighbor. The
value 1 implies that eBGP multi-hop feature is disabled
(only a single hop is allowed). This field is ignored
for iBGP peers.
format: int32
maximum: 255
minimum: 1
type: integer
families:
description: "Families, if provided, defines a set of
AFI/SAFIs the speaker will negotiate with it's peer.
\n If this slice is not provided the default families
of IPv6 and IPv4 will be provided."
items:
description: CiliumBGPFamily represents a AFI/SAFI address
family pair.
properties:
afi:
description: Afi is the Address Family Identifier
(AFI) of the family.
enum:
- ipv4
- ipv6
- l2vpn
- ls
- opaque
type: string
safi:
description: Safi is the Subsequent Address Family
Identifier (SAFI) of the family.
enum:
- unicast
- multicast
- mpls_label
- encapsulation
- vpls
- evpn
- ls
- sr_policy
- mup
- mpls_vpn
- mpls_vpn_multicast
- route_target_constraints
- flowspec_unicast
- flowspec_vpn
- key_value
type: string
required:
- afi
- safi
type: object
type: array
gracefulRestart:
description: GracefulRestart defines graceful restart
parameters which are negotiated with this neighbor.
If empty / nil, the graceful restart capability is disabled.
properties:
enabled:
description: Enabled flag, when set enables graceful
restart capability.
type: boolean
restartTimeSeconds:
default: 120
description: RestartTimeSeconds is the estimated time
it will take for the BGP session to be re-established
with peer after a restart. After this period, peer
will remove stale routes. This is described RFC
4724 section 4.2.
format: int32
maximum: 4095
minimum: 1
type: integer
required:
- enabled
type: object
holdTimeSeconds:
default: 90
description: HoldTimeSeconds defines the initial value
for the BGP HoldTimer (RFC 4271, Section 4.2). Updating
this value will cause a session reset.
format: int32
maximum: 65535
minimum: 3
type: integer
keepAliveTimeSeconds:
default: 30
description: KeepaliveTimeSeconds defines the initial
value for the BGP KeepaliveTimer (RFC 4271, Section
8). It can not be larger than HoldTimeSeconds. Updating
this value will cause a session reset.
format: int32
maximum: 65535
minimum: 1
type: integer
peerASN:
description: PeerASN is the ASN of the peer BGP router.
Supports extended 32bit ASNs
format: int64
maximum: 4294967295
minimum: 0
type: integer
peerAddress:
description: PeerAddress is the IP address of the peer.
This must be in CIDR notation and use a /32 to express
a single host.
format: cidr
type: string
peerPort:
default: 179
description: PeerPort is the TCP port of the peer. 1-65535
is the range of valid port numbers that can be specified.
If unset, defaults to 179.
format: int32
maximum: 65535
minimum: 1
type: integer
required:
- peerASN
- peerAddress
type: object
minItems: 1
type: array
podIPPoolSelector:
description: "PodIPPoolSelector selects CiliumPodIPPools based
on labels. The virtual router will announce allocated CIDRs
of matching CiliumPodIPPools. \n If empty / nil no CiliumPodIPPools
will be announced."
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that relates
the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In, NotIn,
Exists and DoesNotExist.
enum:
- In
- NotIn
- Exists
- DoesNotExist
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists or
DoesNotExist, the values array must be empty. This
array is replaced during a strategic merge patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
description: MatchLabelsValue represents the value from
the MatchLabels {key,value} pair.
maxLength: 63
pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field is
"key", the operator is "In", and the values array contains
only "value". The requirements are ANDed.
type: object
type: object
serviceSelector:
description: "ServiceSelector selects a group of load balancer
services which this virtual router will announce. The loadBalancerClass
for a service must be nil or specify a class supported by
Cilium, e.g. \"io.cilium/bgp-control-plane\". Refer to the
following document for additional details regarding load balancer
classes: \n https://kubernetes.io/docs/concepts/services-networking/service/#load-balancer-class
\n If empty / nil no services will be announced."
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that relates
the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In, NotIn,
Exists and DoesNotExist.
enum:
- In
- NotIn
- Exists
- DoesNotExist
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists or
DoesNotExist, the values array must be empty. This
array is replaced during a strategic merge patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
description: MatchLabelsValue represents the value from
the MatchLabels {key,value} pair.
maxLength: 63
pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field is
"key", the operator is "In", and the values array contains
only "value". The requirements are ANDed.
type: object
type: object
required:
- localASN
- neighbors
type: object
minItems: 1
type: array
required:
- virtualRouters
type: object
required:
- metadata
type: object
served: true
storage: true
subresources: {}
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View file

@ -0,0 +1,36 @@
---
apiVersion: cilium.io/v2alpha1
kind: CiliumBGPPeeringPolicy
# comments courtesy of JJGadgets
# MAKE SURE CRDs ARE INSTALLED IN CLUSTER VIA cilium-config ConfigMap OR Cilium HelmRelease/values.yaml (bgpControlPlane.enabled: true), BEFORE THIS IS APPLIED!
# "CiliumBGPPeeringPolicy" Custom Resource will replace the old MetalLB BGP's "bgp-config" ConfigMap
# "CiliumBGPPeeringPolicy" is used with `bgpControlPlane.enabled: true` which uses GoBGP, NOT the old `bgp.enabled: true` which uses MetalLB
metadata:
name: bgp-loadbalancer-ip-main
spec:
nodeSelector:
matchLabels:
kubernetes.io/os: "linux" # match all Linux nodes, change this to match more granularly if more than 1 PeeringPolicy is to be used throughout cluster
virtualRouters:
- localASN: 64512
exportPodCIDR: false
serviceSelector: # this replaces address-pools, instead of defining the range of IPs that can be assigned to LoadBalancer services, now services have to match below selectors for their LB IPs to be announced
matchExpressions:
- {
key: thisFakeSelector,
operator: NotIn,
values: ["will-match-and-announce-all-services"],
}
neighbors:
- peerAddress: "10.1.1.1/32" # unlike bgp-config ConfigMap, peerAddress needs to be in CIDR notation
peerASN: 64512
---
# yaml-language-server: $schema=https://ks.hsn.dev/cilium.io/ciliumloadbalancerippool_v2alpha1.json
apiVersion: "cilium.io/v2alpha1"
kind: CiliumLoadBalancerIPPool
metadata:
name: main-pool
spec:
cidrs:
- cidr: 10.45.0.1/24

View file

@ -0,0 +1,78 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrelease-helm-v2beta2.json
apiVersion: helm.toolkit.fluxcd.io/v2beta2
kind: HelmRelease
metadata:
name: cilium
namespace: kube-system
spec:
interval: 30m
chart:
spec:
chart: cilium
version: 1.15.3
sourceRef:
kind: HelmRepository
name: cilium
namespace: flux-system
maxHistory: 2
install:
remediation:
retries: 3
upgrade:
cleanupOnFail: true
remediation:
retries: 3
uninstall:
keepHistory: false
values:
cluster:
name: homelab
id: 1
hubble:
relay:
enabled: true
ui:
enabled: true
metrics:
enableOpenMetrics: true
prometheus:
enabled: true
operator:
prometheus:
enabled: true
ipam:
mode: kubernetes
kubeProxyReplacement: true
k8sServiceHost: 127.0.0.1
k8sServicePort: 7445
rollOutCiliumPods: true
cgroup:
automount:
enabled: false
hostRoot: /sys/fs/cgroup
bgp:
enabled: false
announce:
loadbalancerIP: true
podCIDR: false
bgpControlPlane:
enabled: true
securityContext:
capabilities:
ciliumAgent:
- CHOWN
- KILL
- NET_ADMIN
- NET_RAW
- IPC_LOCK
- SYS_ADMIN
- SYS_RESOURCE
- DAC_OVERRIDE
- FOWNER
- SETGID
- SETUID
cleanCiliumState:
- NET_ADMIN
- SYS_ADMIN
- SYS_RESOURCE

View file

@ -0,0 +1,23 @@
# yaml-language-server: $schema=https://ks.hsn.dev/cilium.io/ciliumclusterwidenetworkpolicy_v2.json
---
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
name: allow-ssh
spec:
description: ""
nodeSelector:
matchLabels:
# node-access: ssh
node-role.kubernetes.io/control-plane: "true"
ingress:
- fromEntities:
- cluster
- toPorts:
- ports:
- port: "22"
protocol: TCP
- icmps:
- fields:
- type: 8
family: IPv4

View file

@ -0,0 +1,27 @@
# yaml-language-server: $schema=https://ks.hsn.dev/cilium.io/ciliumclusterwidenetworkpolicy_v2.json
---
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
name: api-server
spec:
nodeSelector:
# apply to master nodes
matchLabels:
node-role.kubernetes.io/control-plane: 'true'
ingress:
# load balancer -> api server
- fromCIDR:
- 167.235.217.82/32
toPorts:
- ports:
- port: '6443'
protocol: TCP
egress:
# api server -> kubelet
- toEntities:
- remote-node
toPorts:
- ports:
- port: '10250'
protocol: TCP

View file

@ -0,0 +1,41 @@
# yaml-language-server: $schema=https://ks.hsn.dev/cilium.io/ciliumclusterwidenetworkpolicy_v2.json
---
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
name: cilium-health
specs:
- endpointSelector:
# apply to health endpoints
matchLabels:
reserved:health: ''
ingress:
# cilium agent -> cilium agent
- fromEntities:
- host
- remote-node
toPorts:
- ports:
- port: '4240'
protocol: TCP
- nodeSelector:
# apply to all nodes
matchLabels: {}
ingress:
# cilium agent -> cilium agent
- fromEntities:
- health
- remote-node
toPorts:
- ports:
- port: '4240'
protocol: TCP
egress:
# cilium agent -> cilium agent
- toEntities:
- health
- remote-node
toPorts:
- ports:
- port: '4240'
protocol: TCP

View file

@ -0,0 +1,26 @@
# yaml-language-server: $schema=https://ks.hsn.dev/cilium.io/ciliumclusterwidenetworkpolicy_v2.json
---
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
name: cilium-vxlan
spec:
nodeSelector:
# apply to all nodes
matchLabels: {}
ingress:
# node -> vxlan
- fromEntities:
- remote-node
toPorts:
- ports:
- port: '8472'
protocol: UDP
egress:
# node -> vxlan
- toEntities:
- remote-node
toPorts:
- ports:
- port: '8472'
protocol: UDP

View file

@ -0,0 +1,65 @@
# yaml-language-server: $schema=https://ks.hsn.dev/cilium.io/ciliumnetworkpolicy_v2.json
---
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: core-dns
namespace: kube-system
specs:
- nodeSelector:
# apply to master nodes
matchLabels:
node-role.kubernetes.io/control-plane: 'true'
ingress:
# core dns -> api server
- fromEndpoints:
- matchLabels:
io.cilium.k8s.policy.serviceaccount: coredns
toPorts:
- ports:
- port: '6443'
protocol: TCP
- nodeSelector:
# apply to all nodes
matchLabels: {}
egress:
# kubelet -> core dns probes
- toEndpoints:
- matchLabels:
io.cilium.k8s.policy.serviceaccount: coredns
toPorts:
- ports:
- port: '8080'
protocol: TCP
- port: '8181'
protocol: TCP
- endpointSelector:
# apply to core dns pods
matchLabels:
io.cilium.k8s.policy.serviceaccount: coredns
ingress:
# kubelet -> core dns probes
- fromEntities:
- host
toPorts:
- ports:
- port: '8080'
protocol: TCP
- port: '8181'
protocol: TCP
egress:
# core dns -> api server
- toEntities:
- kube-apiserver
toPorts:
- ports:
- port: '6443'
protocol: TCP
# core dns -> upstream DNS
- toCIDR:
- 185.12.64.1/32
- 185.12.64.2/32
toPorts:
- ports:
- port: '53'
protocol: UDP

View file

@ -0,0 +1,27 @@
# yaml-language-server: $schema=https://ks.hsn.dev/cilium.io/ciliumclusterwidenetworkpolicy_v2.json
---
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
name: etcd
spec:
nodeSelector:
# apply to master nodes
matchLabels:
node-role.kubernetes.io/control-plane: 'true'
ingress:
# etcd peer -> etcd peer
- fromEntities:
- remote-node
toPorts:
- ports:
- port: '2380'
protocol: TCP
egress:
# etcd peer -> etcd peer
- toEntities:
- remote-node
toPorts:
- ports:
- port: '2380'
protocol: TCP

View file

@ -0,0 +1,15 @@
# yaml-language-server: $schema=https://ks.hsn.dev/cilium.io/ciliumclusterwidenetworkpolicy_v2.json
---
apiVersion: "cilium.io/v2"
kind: CiliumClusterwideNetworkPolicy
metadata:
name: allow-specific-traffic
spec:
endpointSelector: {}
ingress:
- fromEntities:
- host
toPorts:
- ports:
- port: '6443'
protocol: TCP

View file

@ -0,0 +1,50 @@
# yaml-language-server: $schema=https://ks.hsn.dev/cilium.io/ciliumnetworkpolicy_v2.json
---
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: hubble-relay
namespace: kube-system
specs:
- nodeSelector:
# apply to all nodes
matchLabels: {}
ingress:
# hubble relay -> hubble agent
- fromEndpoints:
- matchLabels:
io.cilium.k8s.policy.serviceaccount: hubble-relay
toPorts:
- ports:
- port: '4244'
protocol: TCP
egress:
# kubelet -> hubble relay probes
- toEndpoints:
- matchLabels:
io.cilium.k8s.policy.serviceaccount: hubble-relay
toPorts:
- ports:
- port: '4245'
protocol: TCP
- endpointSelector:
# apply to hubble relay pods
matchLabels:
io.cilium.k8s.policy.serviceaccount: hubble-relay
ingress:
# kubelet -> hubble relay probes
- fromEntities:
- host
toPorts:
- ports:
- port: '4245'
protocol: TCP
egress:
# hubble relay -> hubble agent
- toEntities:
- host
- remote-node
toPorts:
- ports:
- port: '4244'
protocol: TCP

View file

@ -0,0 +1,75 @@
# yaml-language-server: $schema=https://ks.hsn.dev/cilium.io/ciliumnetworkpolicy_v2.json
---
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: hubble-ui
namespace: kube-system
specs:
- nodeSelector:
# apply to master nodes
matchLabels:
node-role.kubernetes.io/control-plane: ''
ingress:
# hubble ui -> api server
- fromEndpoints:
- matchLabels:
io.cilium.k8s.policy.serviceaccount: hubble-ui
toPorts:
- ports:
- port: '6443'
protocol: TCP
- endpointSelector:
# apply to core dns endpoints
matchLabels:
io.cilium.k8s.policy.serviceaccount: coredns
ingress:
# hubble ui -> core dns
- fromEndpoints:
- matchLabels:
io.cilium.k8s.policy.serviceaccount: hubble-ui
toPorts:
- ports:
- port: '53'
protocol: UDP
- endpointSelector:
# apply to hubble relay endpoints
matchLabels:
io.cilium.k8s.policy.serviceaccount: hubble-relay
ingress:
# hubble ui -> hubble relay
- fromEndpoints:
- matchLabels:
io.cilium.k8s.policy.serviceaccount: hubble-ui
toPorts:
- ports:
- port: '4245'
protocol: TCP
- endpointSelector:
# apply to hubble ui endpoints
matchLabels:
io.cilium.k8s.policy.serviceaccount: hubble-ui
egress:
# hubble ui -> api server
- toEntities:
- kube-apiserver
toPorts:
- ports:
- port: '6443'
protocol: TCP
# hubble ui -> hubble relay
- toEndpoints:
- matchLabels:
io.cilium.k8s.policy.serviceaccount: hubble-relay
toPorts:
- ports:
- port: '4245'
protocol: TCP
# hubble ui -> core dns
- toEndpoints:
- matchLabels:
io.cilium.k8s.policy.serviceaccount: coredns
toPorts:
- ports:
- port: '53'
protocol: UDP

View file

@ -0,0 +1,28 @@
# yaml-language-server: $schema=https://ks.hsn.dev/cilium.io/ciliumclusterwidenetworkpolicy_v2.json
---
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
name: kubelet
spec:
nodeSelector:
# apply to all nodes
matchLabels: {}
ingress:
# api server -> kubelet
- fromEntities:
- kube-apiserver
toPorts:
- ports:
- port: '10250'
protocol: TCP
egress:
# kubelet -> load balancer
- toCIDR:
- 167.235.217.82/32
toEntities:
- host
toPorts:
- ports:
- port: '6443'
protocol: TCP

View file

@ -0,0 +1,16 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/kustomization.json
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: kube-system
resources:
- ./allow-ssh.yaml
- ./apiserver.yaml
- ./cilium-health.yaml
- ./cilium-vxlan.yaml
- ./core-dns.yaml
- ./etcd.yaml
- ./hubble-relay.yaml
- ./hubble-ui.yaml
- ./kubelet.yaml

View file

@ -3,18 +3,15 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: &app vault
name: cilium
namespace: flux-system
spec:
commonMetadata:
labels:
app.kubernetes.io/name: *app
interval: 1m
path: "./kubernetes/apps/security/vault/app"
interval: 30m
retryInterval: 1m
timeout: 5m
path: "./kubernetes/apps/kube-system/cilium/app"
prune: true
sourceRef:
kind: GitRepository
name: theshire
name: homelab
wait: false
dependsOn:
- name: rook-ceph-cluster

View file

@ -1,6 +1,6 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrelease-helm-v2beta2.json
apiVersion: helm.toolkit.fluxcd.io/v2
apiVersion: helm.toolkit.fluxcd.io/v2beta2
kind: HelmRelease
metadata:
name: rook-ceph-operator
@ -10,7 +10,7 @@ spec:
chart:
spec:
chart: rook-ceph
version: v1.15.5
version: v1.14.2
sourceRef:
kind: HelmRepository
name: rook-ceph
@ -29,6 +29,8 @@ spec:
namespace: volsync-system
values:
csi:
provisioner:
image: registry.k8s.io/sig-storage/csi-provisioner:v4.0.1
cephFSKernelMountOptions: ms_mode=prefer-crc
enableLiveness: true
serviceMonitor:

View file

@ -0,0 +1,26 @@
apiVersion: v1
kind: Secret
metadata:
name: rook-ceph-dashboard-password
stringData:
password: ENC[AES256_GCM,data:WWTt7SN6ssndLahsOA1gujEeGAM=,iv:YbHGNN+11wA/MLq9vFVM6v4mhPO58JmwXBDj0Qs7+Wk=,tag:5Xn0tqpiIiEt8ZWZHRTM3w==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age1eqlaq205y5jre9hu5hvulywa7w3d4qyxwmafneamxcn7nejesedsf4q9g6
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzb2ZpaDd0azNHNTJoUTB6
VVpKbm94ZEprSHplb2UrQnkzTzdGUEFjcGxBCnhxR1BwNmFIOExtMW5GRkVJWTl5
blQzSmZ0Tm5CWTk3N25nUUM0dFpKUTQKLS0tIEgwSHNlVXNRdHZvcE10VzExU0hE
L0dGK1lFd0ZSQ0lTcEdMNTBkSDJ6WWsKQuiJmRSLbvmgenlu4F2/CQYCCbZTtS/K
nz7NsY2om+mWMvPSvLAp1pOHDAdFW79ggQAiCyslDi9iOkaD8MOnxQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-01-16T23:22:39Z"
mac: ENC[AES256_GCM,data:djsWoz/MuUhEKsM03+iaGV/dZUjRAGkiBEz4hROi+rfNWeHLJG2/xXPSKYYgT3h7JOZGh2Gnz7NXiB7TuixlWrAfT2BUBzd+2o9/hzg3xQzLAjApSfZdyap6oafatKxZAR/JHBSw7s0saVNnop9d/DZK4c1Fb1qNKoTrnWqqrF8=,iv:oitjHdZl07CaoBtNtX/sOPLHu7AS/R4YE4TKBJKrUBw=,tag:Br8mBH+mATEwsLzSZmoVYg==,type:str]
pgp: []
encrypted_regex: ^(data|stringData)$
version: 3.8.1

View file

@ -1,6 +1,6 @@
---
# yaml-language-server: $schema=https://ks.hsn.dev/helm.toolkit.fluxcd.io/helmrelease_v2beta2.json
apiVersion: helm.toolkit.fluxcd.io/v2
apiVersion: helm.toolkit.fluxcd.io/v2beta2
kind: HelmRelease
metadata:
name: rook-ceph-cluster
@ -10,7 +10,7 @@ spec:
chart:
spec:
chart: rook-ceph-cluster
version: v1.15.5
version: v1.14.2
sourceRef:
kind: HelmRepository
name: rook-ceph
@ -49,11 +49,8 @@ spec:
bdev_enable_discard = true
bdev_async_discard = true
osd_class_update_on_start = false
osd_pool_default_size = 1
cephClusterSpec:
mgr:
modules:
- name: pg_autoscaler
enabled: true
network:
provider: host
connections:
@ -67,35 +64,33 @@ spec:
storage:
useAllNodes: true
useAllDevices: false
deviceFilter: "nvme0n1"
deviceFilter: "nvme2n1"
resources:
mgr:
requests:
cpu: 10m
cpu: 500m
memory: 512Mi
limits:
cpu: 2000m
memory: 2Gi
mon:
requests:
cpu: 10m
cpu: 500m
memory: 1Gi
limits:
cpu: 4000m
memory: 4Gi
osd:
requests:
cpu: 10m
memory: 1Gi
cpu: 500m
memory: 4Gi
limits:
cpu: 4000m
memory: 3Gi
memory: 8Gi
cephBlockPools:
- name: ceph-blockpool
spec:
failureDomain: host
replicated:
size: 3
storageClass:
enabled: true
name: ceph-block
@ -121,20 +116,16 @@ spec:
- name: ceph-filesystem
spec:
metadataPool:
replicated:
size: 3
dataPools:
- failureDomain: host
replicated:
size: 3
name: data0
metadataServer:
activeCount: 1
activeStandby: true
resources:
requests:
cpu: 10m
memory: 1Gi
cpu: 1000m
memory: 4Gi
limits:
memory: 4Gi
storageClass:
@ -162,19 +153,14 @@ spec:
spec:
metadataPool:
failureDomain: host
replicated:
size: 3
dataPool:
failureDomain: host
erasureCoded:
dataChunks: 2
codingChunks: 1
preservePoolsOnDelete: true
gateway:
port: 80
resources:
requests:
cpu: 10m
cpu: 1000m
memory: 1Gi
limits:
memory: 2Gi

View file

@ -14,9 +14,10 @@ spec:
prune: false # never should be deleted
sourceRef:
kind: GitRepository
name: theshire
name: homelab
wait: false
interval: 30m
retryInterval: 1m
timeout: 5m
---
# yaml-language-server: $schema=https://ks.hsn.dev/kustomize.toolkit.fluxcd.io/kustomization_v1.json
@ -34,7 +35,8 @@ spec:
prune: false # never should be deleted
sourceRef:
kind: GitRepository
name: theshire
name: homelab
wait: false
interval: 30m
retryInterval: 1m
timeout: 15m

View file

@ -1,9 +0,0 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
# Pre Flux-Kustomizations
- ./namespace.yaml
# Flux-Kustomizations
- ./system-upgrade-controller/ks.yaml

View file

@ -1,38 +0,0 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: system-upgrade
annotations:
kustomize.toolkit.fluxcd.io/prune: disabled
volsync.backube/privileged-movers: "true"
---
# yaml-language-server: $schema=https://kubernetes-schemas.pages.dev/notification.toolkit.fluxcd.io/provider_v1beta3.json
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Provider
metadata:
name: alert-manager
namespace: system-upgrade
spec:
type: alertmanager
address: http://alertmanager.observability.svc.cluster.local:9093/api/v2/alerts/
---
# yaml-language-server: $schema=https://kubernetes-schemas.pages.dev/notification.toolkit.fluxcd.io/alert_v1beta3.json
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Alert
metadata:
name: alert-manager
namespace: system-upgrade
spec:
providerRef:
name: alert-manager
eventSeverity: error
eventSources:
- kind: HelmRelease
name: "*"
exclusionList:
- "error.*lookup github\\.com"
- "error.*lookup raw\\.githubusercontent\\.com"
- "dial.*tcp.*timeout"
- "waiting.*socket"
suspend: false

View file

@ -1,101 +0,0 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/bjw-s/helm-charts/main/charts/other/app-template/schemas/helmrelease-helm-v2.schema.json
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: &app system-upgrade-controller
spec:
interval: 30m
chart:
spec:
chart: app-template
version: 3.5.1
sourceRef:
kind: HelmRepository
name: bjw-s
namespace: flux-system
install:
remediation:
retries: 3
upgrade:
cleanupOnFail: true
remediation:
strategy: rollback
retries: 3
values:
controllers:
system-upgrade-controller:
strategy: RollingUpdate
containers:
app:
image:
repository: docker.io/rancher/system-upgrade-controller
tag: v0.14.2@sha256:3cdbfdd90f814702cefb832fc4bdb09ea93865a4d06c6bafd019d1dc6a9f34c9
env:
SYSTEM_UPGRADE_CONTROLLER_DEBUG: false
SYSTEM_UPGRADE_CONTROLLER_THREADS: 2
SYSTEM_UPGRADE_JOB_ACTIVE_DEADLINE_SECONDS: 900
SYSTEM_UPGRADE_JOB_BACKOFF_LIMIT: 99
SYSTEM_UPGRADE_JOB_IMAGE_PULL_POLICY: IfNotPresent
SYSTEM_UPGRADE_JOB_KUBECTL_IMAGE: registry.k8s.io/kubectl:v1.31.1
SYSTEM_UPGRADE_JOB_POD_REPLACEMENT_POLICY: Failed
SYSTEM_UPGRADE_JOB_PRIVILEGED: true
SYSTEM_UPGRADE_JOB_TTL_SECONDS_AFTER_FINISH: 900
SYSTEM_UPGRADE_PLAN_POLLING_INTERVAL: 15m
SYSTEM_UPGRADE_CONTROLLER_NAME: *app
SYSTEM_UPGRADE_CONTROLLER_NAMESPACE:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: { drop: ["ALL"] }
seccompProfile:
type: RuntimeDefault
defaultPodOptions:
securityContext:
runAsNonRoot: true
runAsUser: 65534
runAsGroup: 65534
seccompProfile: { type: RuntimeDefault }
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: Exists
tolerations:
- key: CriticalAddonsOnly
operator: Exists
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
serviceAccount:
create: true
name: system-upgrade
persistence:
tmp:
type: emptyDir
etc-ssl:
type: hostPath
hostPath: /etc/ssl
hostPathType: DirectoryOrCreate
globalMounts:
- readOnly: true
etc-pki:
type: hostPath
hostPath: /etc/pki
hostPathType: DirectoryOrCreate
globalMounts:
- readOnly: true
etc-ca-certificates:
type: hostPath
hostPath: /etc/ca-certificates
hostPathType: DirectoryOrCreate
globalMounts:
- readOnly: true

View file

@ -1,21 +0,0 @@
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: system-upgrade
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: system-upgrade
namespace: system-upgrade
---
apiVersion: talos.dev/v1alpha1
kind: ServiceAccount
metadata:
name: talos
spec:
roles:
- os:admin

View file

@ -1,50 +0,0 @@
---
# yaml-language-server: $schema=https://kubernetes-schemas.pages.dev/kustomize.toolkit.fluxcd.io/kustomization_v1.json
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: &app system-upgrade-controller
namespace: flux-system
spec:
targetNamespace: system-upgrade
commonMetadata:
labels:
app.kubernetes.io/name: *app
dependsOn:
- name: node-feature-discovery-rules
path: ./kubernetes/apps/system-upgrade/system-upgrade-controller/app
prune: true
sourceRef:
kind: GitRepository
name: theshire
wait: true
interval: 30m
timeout: 5m
---
# yaml-language-server: $schema=https://kubernetes-schemas.pages.dev/kustomize.toolkit.fluxcd.io/kustomization_v1.json
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: &app system-upgrade-controller-plans
namespace: flux-system
spec:
targetNamespace: system-upgrade
commonMetadata:
labels:
app.kubernetes.io/name: *app
dependsOn:
- name: system-upgrade-controller
path: ./kubernetes/apps/system-upgrade/system-upgrade-controller/plans
prune: true
sourceRef:
kind: GitRepository
name: theshire
wait: false
interval: 30m
timeout: 5m
postBuild:
substitute:
# renovate: datasource=docker depName=ghcr.io/siderolabs/installer
TALOS_VERSION: v1.8.2
# renovate: datasource=docker depName=ghcr.io/siderolabs/kubelet
KUBERNETES_VERSION: v1.30.2

View file

@ -1,45 +0,0 @@
---
# yaml-language-server: $schema=https://kubernetes-schemas.pages.dev/upgrade.cattle.io/plan_v1.json
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
name: kubernetes
spec:
version: ${KUBERNETES_VERSION}
serviceAccountName: system-upgrade
secrets:
- name: talos
path: /var/run/secrets/talos.dev
ignoreUpdates: true
concurrency: 1
exclusive: true
nodeSelector:
matchExpressions:
- key: feature.node.kubernetes.io/system-os_release.ID
operator: In
values: ["talos"]
- key: node-role.kubernetes.io/control-plane
operator: Exists
tolerations:
- key: CriticalAddonsOnly
operator: Exists
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
prepare: &prepare
image: ghcr.io/siderolabs/talosctl:${TALOS_VERSION}
envs:
- name: NODE_IP
valueFrom:
fieldRef:
fieldPath: status.hostIP
args:
- --nodes=$(NODE_IP)
- health
- --server=false
upgrade:
<<: *prepare
args:
- --nodes=$(NODE_IP)
- upgrade-k8s
- --to=$(SYSTEM_UPGRADE_PLAN_LATEST_VERSION)

View file

@ -1,51 +0,0 @@
---
# yaml-language-server: $schema=https://kubernetes-schemas.pages.dev/upgrade.cattle.io/plan_v1.json
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
name: talos
spec:
version: ${TALOS_VERSION}
serviceAccountName: system-upgrade
secrets:
- name: talos
path: /var/run/secrets/talos.dev
ignoreUpdates: true
concurrency: 1
exclusive: true
nodeSelector:
matchExpressions:
- key: feature.node.kubernetes.io/system-os_release.ID
operator: In
values: ["talos"]
- key: feature.node.kubernetes.io/system-os_release.VERSION_ID
operator: NotIn
values: ["${TALOS_VERSION}"]
tolerations:
- key: CriticalAddonsOnly
operator: Exists
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
prepare: &prepare
image: ghcr.io/siderolabs/talosctl:${TALOS_VERSION}
envs:
- name: NODE_IP
valueFrom:
fieldRef:
fieldPath: status.hostIP
- name: TALOS_SCHEMATIC_ID
valueFrom:
fieldRef:
fieldPath: metadata.annotations['extensions.talos.dev/schematic']
args:
- --nodes=$(NODE_IP)
- health
- --server=false
upgrade:
<<: *prepare
args:
- --nodes=$(NODE_IP)
- upgrade
- --image=factory.talos.dev/installer/$(TALOS_SCHEMATIC_ID):$(SYSTEM_UPGRADE_PLAN_LATEST_VERSION)
- --wait=false

View file

@ -1,27 +0,0 @@
---
# yaml-language-server: $schema=https://ks.hsn.dev/external-secrets.io/externalsecret_v1beta1.json
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: vault
namespace: security
spec:
secretStoreRef:
kind: ClusterSecretStore
name: onepassword-connect
target:
name: vault-secret
creationPolicy: Owner
data:
- secretKey: AWS_SECRET_ACCESS_KEY
remoteRef:
key: vault
property: AWS_SECRET_ACCESS_KEY
- secretKey: AWS_ACCESS_KEY_ID
remoteRef:
key: vault
property: AWS_ACCESS_KEY_ID
- secretKey: VAULT_AWSKMS_SEAL_KEY_ID
remoteRef:
key: vault
property: VAULT_AWSKMS_SEAL_KEY_ID

View file

@ -1,141 +0,0 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrelease-helm-v2beta2.json
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: vault
spec:
interval: 30m
chart:
spec:
chart: vault
version: 0.28.1
sourceRef:
kind: HelmRepository
name: hashicorp
namespace: flux-system
install:
remediation:
retries: 3
upgrade:
cleanupOnFail: true
remediation:
retries: 3
strategy: uninstall
values:
server:
image:
repository: public.ecr.aws/hashicorp/vault
tag: "1.17.5"
logLevel: "info"
logFormat: "json"
ingress:
enabled: true
ingressClassName: internal-nginx
hosts:
- host: &host "vault.jahanson.tech"
paths: []
tls:
- hosts:
- *host
service:
type: "ClusterIP"
port: &port 8200
targetPort: *port
# off until it's online for the first time
readinessProbe:
enabled: true
path: "/v1/sys/health?standbyok=true&sealedcode=204&uninitcode=204"
livenessProbe:
enabled: true
path: "/v1/sys/health?standbyok=true"
initialDelaySeconds: 60
# If you need to use a http path instead of the default exec
# path: /v1/sys/health?standbyok=true
# Port number on which readinessProbe will be checked.
port: *port
extraEnvironmentVars:
# This is required because they will lose their values when the pod is upgraded in my experience.
# Probably a Flux thing.
VAULT_CLUSTER_ADDR: http://$(HOSTNAME).vault-internal:8201
extraSecretEnvironmentVars:
- envName: AWS_SECRET_ACCESS_KEY
secretName: vault-secret
secretKey: AWS_SECRET_ACCESS_KEY
- envName: AWS_ACCESS_KEY_ID
secretName: vault-secret
secretKey: AWS_ACCESS_KEY_ID
- envName: VAULT_AWSKMS_SEAL_KEY_ID
secretName: vault-secret
secretKey: VAULT_AWSKMS_SEAL_KEY_ID
# These are defaults but explicitly set here for clarity.
dataStorage:
size: 4Gi
mountPath: /vault/data
storageClass: ceph-block
auditStorage:
enabled: true
size: 10Gi
mountPath: /vault/audit
storageClass: ceph-block
# We want high availability. If standalone is true it sets the storage backend to file
# and the max replicas can only be 1.
standalone:
enabled: false
ha:
enabled: true
# maxUnavailable will default to (n/2)-1 where n is the number of replicas
# so if you have 6 replicas, maxUnavailable will be 2 unless you set it specifically.
replicas: 3
config: ""
raft:
enabled: true
config: |
ui = true
listener "tcp" {
tls_disable = 1
address = "[::]:8200"
cluster_address = "[::]:8201"
# For prometheus!
telemetry {
unauthenticated_metrics_access = "true"
}
}
storage "raft" {
path = "/vault/data"
retry_join {
auto_join = "provider=k8s label_selector=\"app.kubernetes.io/name=vault,component=server\" namespace=\"security\""
auto_join_scheme = "http"
}
}
seal "awskms" {
region = "us-east-2"
}
service_registration "kubernetes" {}
statefulSet:
securityContext:
pod:
runAsUser: 568
runAsGroup: 568
runAsNonRoot: true
fsGroup: 568
fsGroupChangePolicy: OnRootMismatch
supplementalGroups: [10000]
container:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: false
capabilities:
drop:
- "ALL"
ui:
enabled: true
publishNotReadyAddresses: true
# The service should only contain selectors for active Vault pod
activeVaultPodOnly: true
serviceType: "LoadBalancer"
externalPort: *port
targetPort: *port

View file

@ -1,8 +0,0 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/kustomization.json
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: security
resources:
- ./externalsecret.yaml
- ./helmrelease.yaml

12
.envrc
View file

@ -1,13 +1,5 @@
#shellcheck disable=SC2148,SC2155
export KUBECONFIG="$(expand_path ./kubeconfig)"
export SOPS_AGE_KEY_FILE="$(expand_path ./age.key)"
export TALOSCONFIG="$(expand_path ./kubernetes/bootstrap/talos/clusterconfig/talosconfig)"
export KREW_ROOT="$(expand_path ~/.krew/bin)"
export CLUSTER="theshire"
export KUBERNETES_DIR="$(expand_path ./kubernetes)"
#export MQTTUI_BROKER="mqtt://10.1.1.38"
#export MQTTUI_BROKER=$(op item get "emqx [jahanson]" --fields broker)
#export MQTTUI_USERNAME=$(op item get "emqx [jahanson]" --fields username)
#export MQTTUI_PASSWORD=$(op item get "emqx [jahanson]" --fields mqtt-password)
PATH_add $KREW_ROOT
use nix
export TALOSCONFIG="$(expand_path ./talosconfig.yaml)"
export OMNICONFIG="$(expand_path ./omniconfig.yaml)"

View file

@ -1,138 +0,0 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
name: "K8S json Schemas --> Cloudflare R2"
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * *" # Every day at midnight
push:
branches: ["main"]
paths: [".forgejo/workflows/schemas.yaml"]
jobs:
publish:
name: Schemas
runs-on: ["ubuntu-x86_64"]
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: https://github.com/actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Workflow Tools
shell: bash
run: |
curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin/
curl -LO "https://dl.min.io/client/mc/release/linux-amd64/mc"
chmod +x mc
mv mc /usr/local/bin/
- name: Setup Python
run: |
apt-get update
apt-get install -y python3 python3-pip python3-yaml
pip3 install --upgrade pip
- name: Write kubeconfig
id: kubeconfig
uses: https://github.com/timheuer/base64-to-file@v1
with:
encodedString: "${{ secrets.KUBECONFIG }}"
fileName: kubeconfig
fileDir: ${{ env.GITHUB_WORKSPACE }}
- name: Write mc
id: mcconfig
uses: https://github.com/timheuer/base64-to-file@v1
with:
encodedString: "${{ secrets.MCCONFIG }}"
fileName: config.json
fileDir: ${{ env.GITHUB_WORKSPACE }}
- name: Extracting CRDs to yaml and converting to JSON schema
env:
KUBECONFIG: "${{ steps.kubeconfig.outputs.filePath }}"
run: |
# kubeconfig
echo "kubeconfig location: $KUBECONFIG"
# Create temp folder for CRDs
TMP_CRD_DIR=$(mktemp -d)
echo "Temp directory: $TMP_CRD_DIR"
# Create final schemas directory
SCHEMAS_DIR=$GITHUB_WORKSPACE/crdSchemas
mkdir -p $SCHEMAS_DIR
echo "Schemas directory: $SCHEMAS_DIR"
# Create array to store CRD kinds and groups
ORGANIZE_BY_GROUP=true
declare -A CRD_GROUPS 2>/dev/null
if [ $? -ne 0 ]; then
# Array creation failed, signal to skip organization by group
ORGANIZE_BY_GROUP=false
fi
# Extract CRDs from cluster
NUM_OF_CRDS=0
while read -r crd
do
filename=${crd%% *}
kubectl get crds "$filename" -o yaml > "$TMP_CRD_DIR/$filename.yaml" 2>&1
echo "Extracted CRD: $filename"
resourceKind=$(grep "kind:" "$TMP_CRD_DIR/$filename.yaml" | awk 'NR==2{print $2}' | tr '[:upper:]' '[:lower:]')
resourceGroup=$(grep "group:" "$TMP_CRD_DIR/$filename.yaml" | awk 'NR==1{print $2}')
# Save name and group for later directory organization
CRD_GROUPS["$resourceKind"]="$resourceGroup"
let ++NUM_OF_CRDS
done < <(kubectl get crds 2>&1 | sed -n '/NAME/,$p' | tail -n +2)
echo numCRDs: $NUM_OF_CRDS
# Download converter script
curl https://raw.githubusercontent.com/yannh/kubeconform/master/scripts/openapi2jsonschema.py --output $TMP_CRD_DIR/openapi2jsonschema.py 2>/dev/null
# Convert crds to jsonSchema
cd $SCHEMAS_DIR
python3 $TMP_CRD_DIR/openapi2jsonschema.py $TMP_CRD_DIR/*.yaml
conversionResult=$?
# Copy and rename files to support kubeval
rm -rf $SCHEMAS_DIR/master-standalone
mkdir -p $SCHEMAS_DIR/master-standalone
cp $SCHEMAS_DIR/*.json $SCHEMAS_DIR/master-standalone
find $SCHEMAS_DIR/master-standalone -name '*json' -exec bash -c ' mv -f $0 ${0/\_/-stable-}' {} \;
# Organize schemas by group
if [ $ORGANIZE_BY_GROUP == true ]; then
for schema in $SCHEMAS_DIR/*.json
do
crdFileName=$(basename $schema .json)
crdKind=${crdFileName%%_*}
crdGroup=${CRD_GROUPS[$crdKind]}
if [ -z $crdGroup ]; then
crdGroup="uncategorized"
echo "CRD kind $crdKind has no group, moving to $crdGroup"
fi
echo making directory $crdGroup
mkdir -p $crdGroup
mv $schema ./$crdGroup
done
fi
rm -rf $TMP_CRD_DIR
- name: Deploy to Cloudflare R2
env:
MC_CONFIG_DIR: "${{ steps.mcconfig.outputs.fileDir }}"
shell: bash
run: |
echo $GITHUB_WORKSPACE/crdSchemas/
mc cp --recursive $GITHUB_WORKSPACE/crdSchemas/ r2-ks/kubernetes-schema

22
.gitignore vendored
View file

@ -1,29 +1,15 @@
# OS generated files
.DS_Store
Thumbs.db
# Development environments
.direnv
.idea/
.private/
.venv/
.pytest_cache/
# Infrastructure and deployment
.terraform
*.tfvars
kubeconfig*
*talosconfig.yaml
omniconfig.yaml
# Security and credentials
.private/
.decrypted~*
*.agekey
*.pub
*.key
*.pem
*.secrets
kubeconfig*
*talosconfig.yaml
omniconfig.yaml
config.xml
# syncthing
**/*sync-conflict*

View file

@ -1,4 +0,0 @@
.archive
.forgejo
.git
.taskfiles

View file

@ -47,7 +47,7 @@ repos:
args: [--severity=error]
additional_dependencies: []
- repo: https://github.com/onedr0p/sops-pre-commit
- repo: https://github.com/k8s-at-home/sops-pre-commit
rev: v2.1.1
hooks:
- id: forbid-secrets

View file

@ -1,4 +0,0 @@
{
"quoteProps": "preserve",
"trailingComma": "none"
}

View file

@ -1,26 +1,22 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"packageRules": [
{
"description": "Auto merge container digests",
"matchDatasources": ["docker"],
"automerge": true,
"automergeType": "branch",
"matchUpdateTypes": ["digest"],
"matchPackagePrefixes": [
"ghcr.io/onedr0p",
"ghcr.io/bjw-s",
"ghcr.io/bjw-s-labs"
],
"ignoreTests": true
},
{
"description": "Auto merge KPS minors and patches",
"matchDatasources": ["helm"],
"automerge": true,
"matchUpdateTypes": ["minor", "patch"],
"matchDepNames": ["kube-prometheus-stack"],
"ignoreTests": false
}
]
}
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"packageRules": [
{
"description": "Auto merge container digests",
"matchDatasources": ["docker"],
"automerge": true,
"automergeType": "branch",
"matchUpdateTypes": ["digest"],
"matchPackagePrefixes": ["ghcr.io/onedr0p", "ghcr.io/bjw-s"],
"ignoreTests": true
},
{
"description": "Auto merge KPS minors and patches",
"matchDatasources": ["helm"],
"automerge": true,
"matchUpdateTypes": ["minor", "patch"],
"matchDepNames": ["kube-prometheus-stack"],
"ignoreTests": false
}
]
}

View file

@ -1,16 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"commitMessageTopic": "{{depName}}",
"commitMessageExtra": "to {{newVersion}}",
"commitMessageSuffix": "",
"packageRules": [
{
"matchDatasources": ["helm"],
"commitMessageTopic": "chart {{depName}}"
},
{
"matchDatasources": ["docker"],
"commitMessageTopic": "image {{depName}}"
}
]
}

View file

@ -1,19 +1,37 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"customManagers": [
{
"customType": "regex",
"description": ["Process custom dependencies"],
"fileMatch": ["(^|/)kubernetes/.+\\.ya?ml(?:\\.j2)?$"],
"matchStrings": [
// # renovate: datasource=helm depName=cilium repository=https://helm.cilium.io
// version: 1.15.1
"datasource=(?<datasource>\\S+) depName=(?<depName>\\S+)( repository=(?<registryUrl>\\S+))?\\n.+: (&\\S+\\s)?(?<currentValue>\\S+)",
// # renovate: datasource=github-releases depName=rancher/system-upgrade-controller
// https://github.com/rancher/system-upgrade-controller/releases/download/v0.13.2/crd.yaml
"datasource=(?<datasource>\\S+) depName=(?<depName>\\S+)\\n.+/(?<currentValue>(v|\\d)[^/]+)"
],
"datasourceTemplate": "{{#if datasource}}{{{datasource}}}{{else}}github-releases{{/if}}"
}
]
}
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"customDatasources": {
"grafana-dashboards": {
"defaultRegistryUrlTemplate": "https://grafana.com/api/dashboards/{{packageName}}",
"format": "json",
"transformTemplates": [
"{\"releases\":[{\"version\": $string(revision)}]}"
]
}
},
"customManagers": [
{
"customType": "regex",
"description": "Process Grafana dashboards",
"fileMatch": [
"(^|/)kubernetes/.+\\.ya?ml(\\.j2)?$"
],
"matchStrings": [
"depName=\"(?<depName>\\S+)\"\\n.*?gnetId: (?<packageName>\\d+)\\n.*?revision: (?<currentValue>\\d+)"
],
"datasourceTemplate": "custom.grafana-dashboards",
"versioningTemplate": "regex:^(?<major>\\d+)$"
}
],
"packageRules": [
{
"addLabels": ["renovate/grafana-dashboard"],
"commitMessageExtra": "to revision {{newVersion}}",
"commitMessageTopic": "dashboard {{depName}}",
"matchDatasources": ["grafana-dashboards", "custom.grafana-dashboards"],
"matchUpdateTypes": ["major"],
"semanticCommitScope": "grafana-dashboards",
"semanticCommitType": "chore"
}
]
}

View file

@ -1,38 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"customDatasources": {
"grafana-dashboards": {
"defaultRegistryUrlTemplate": "https://grafana.com/api/dashboards/{{packageName}}",
"format": "json",
"transformTemplates": [
"{\"releases\":[{\"version\": $string(revision)}]}"
]
}
},
"customManagers": [
{
"customType": "regex",
"description": ["Process Grafana dashboards"],
"fileMatch": ["(^|/)kubernetes/.+\\.ya?ml(?:\\.j2)?$"],
"matchStrings": [
"depName=\"(?<depName>.*)\"\\n(?<indentation>\\s+)gnetId: (?<packageName>\\d+)\\n.+revision: (?<currentValue>\\d+)"
],
"autoReplaceStringTemplate": "depName=\"{{{depName}}}\"\n{{{indentation}}}gnetId: {{{packageName}}}\n{{{indentation}}}revision: {{{newValue}}}",
"datasourceTemplate": "custom.grafana-dashboards",
"versioningTemplate": "regex:^(?<major>\\d+)$"
}
],
"packageRules": [
{
"addLabels": ["renovate/grafana-dashboard"],
"automerge": true,
"automergeType": "branch",
"matchDatasources": ["custom.grafana-dashboards"],
"matchUpdateTypes": ["major"],
"semanticCommitType": "chore",
"semanticCommitScope": "grafana-dashboards",
"commitMessageTopic": "dashboard {{depName}}",
"commitMessageExtra": "( {{currentVersion}} → {{newVersion}} )"
}
]
}

View file

@ -1,61 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"packageRules": [
{
"description": ["Dragonfly Operator Group"],
"groupName": "Dragonfly Operator",
"matchPackagePatterns": ["dragonfly(?:db)?.operator"],
"matchDatasources": ["docker", "github-releases"],
"group": {
"commitMessageTopic": "{{{groupName}}} group"
},
"separateMinorPatch": true
},
{
"description": ["Flux Group"],
"groupName": "Flux",
"matchPackagePatterns": ["fluxcd"],
"matchDatasources": ["docker", "github-tags"],
"versioning": "semver",
"group": {
"commitMessageTopic": "{{{groupName}}} group"
},
"separateMinorPatch": true
},
{
"description": ["Rook-Ceph Group"],
"groupName": "Rook-Ceph",
"matchPackagePatterns": ["rook.ceph"],
"matchDatasources": ["helm"],
"group": {
"commitMessageTopic": "{{{groupName}}} group"
},
"separateMinorPatch": true
},
{
"description": ["Talos Group"],
"groupName": "Talos",
"matchPackagePatterns": [
"ghcr.io/siderolabs/talosctl",
"ghcr.io/siderolabs/installer",
"factory.talos.dev/installer"
],
"matchDatasources": ["docker"],
"group": {
"commitMessageTopic": "{{{groupName}}} group"
},
"separateMinorPatch": true
},
{
"description": ["Volsync Group"],
"groupName": "Volsync",
"matchPackagePatterns": ["volsync"],
"matchDatasources": ["docker", "helm"],
"matchUpdateTypes": ["minor", "patch"],
"group": {
"commitMessageTopic": "{{{groupName}}} group"
},
"separateMinorPatch": true
}
]
}

View file

@ -1,37 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"packageRules": [
{
"matchUpdateTypes": ["major"],
"labels": ["type/major"]
},
{
"matchUpdateTypes": ["minor"],
"labels": ["type/minor"]
},
{
"matchUpdateTypes": ["patch"],
"labels": ["type/patch"]
},
{
"matchUpdateTypes": ["digest"],
"labels": ["type/digest"]
},
{
"matchDatasources": ["docker"],
"addLabels": ["renovate/container"]
},
{
"matchDatasources": ["helm"],
"addLabels": ["renovate/helm"]
},
{
"matchDatasources": ["github-releases", "github-tags"],
"addLabels": ["renovate/github-release"]
},
{
"matchManagers": ["github-actions"],
"addLabels": ["renovate/github-action"]
}
]
}

View file

@ -1,23 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"packageRules": [
{
"description": ["Loose versioning for non-semver packages"],
"matchDatasources": ["docker"],
"matchPackagePatterns": ["cross-seed", "plex"],
"versioning": "loose"
},
{
"description": ["Custom schedule for frequently updated packages"],
"matchDataSources": ["docker", "helm"],
"matchPackagePatterns": ["minio", "reloader"],
"schedule": ["on the first day of the month"]
},
{
"description": ["Custom versioning for minio"],
"matchDatasources": ["docker"],
"matchPackagePatterns": ["minio"],
"versioning": "regex:^RELEASE\\.(?<major>\\d+)-(?<minor>\\d+)-(?<patch>\\d+)T.*Z$"
}
]
}

View file

@ -1,24 +1,15 @@
---
creation_rules:
- # IMPORTANT: Keep this rule first
path_regex: kubernetes/bootstrap/talos/talsecret(\.sops)?\.ya?ml
input_type: yaml
encrypted_regex: ^(token|crt|key|id|secret|secretboxencryptionsecret|ca|bootstraptoken)$
age: >-
age1gr4js8ln65khjzjkf9gs5c32a2vrrv6jlv5asuz6hccqq8pddc4sjflprn
- path_regex: kubernetes/.*/talos/.*\.sops\.ya?ml$
age: >-
age1gr4js8ln65khjzjkf9gs5c32a2vrrv6jlv5asuz6hccqq8pddc4sjflprn
- path_regex: kubernetes/.*\.sops\.ya?ml
encrypted_regex: "^(data|stringData)$"
# Homelab
age: >-
age1gr4js8ln65khjzjkf9gs5c32a2vrrv6jlv5asuz6hccqq8pddc4sjflprn
age1eqlaq205y5jre9hu5hvulywa7w3d4qyxwmafneamxcn7nejesedsf4q9g6
- path_regex: .*\.sops\.(env|ini|json|toml)
# Homelab
age: >-
age1gr4js8ln65khjzjkf9gs5c32a2vrrv6jlv5asuz6hccqq8pddc4sjflprn
age1eqlaq205y5jre9hu5hvulywa7w3d4qyxwmafneamxcn7nejesedsf4q9g6
- path_regex: (ansible|terraform|talos)/.*\.sops\.ya?ml
# Homelab
age: >-
age1gr4js8ln65khjzjkf9gs5c32a2vrrv6jlv5asuz6hccqq8pddc4sjflprn
age1eqlaq205y5jre9hu5hvulywa7w3d4qyxwmafneamxcn7nejesedsf4q9g6

View file

@ -0,0 +1,19 @@
---
apiVersion: batch/v1
kind: Job
metadata:
name: "list-${rsrc}-${ts}"
namespace: "${namespace}"
spec:
ttlSecondsAfterFinished: 3600
template:
spec:
automountServiceAccountToken: false
restartPolicy: OnFailure
containers:
- name: list
image: docker.io/restic/restic:0.16.0
args: ["snapshots"]
envFrom:
- secretRef:
name: "${rsrc}-restic-secret"

View file

@ -0,0 +1,25 @@
---
apiVersion: volsync.backube/v1alpha1
kind: ReplicationDestination
metadata:
name: "${rsrc}-${claim}-${ts}"
namespace: "${namespace}"
spec:
trigger:
manual: restore-once
restic:
repository: "${rsrc}-restic-secret"
destinationPVC: "${claim}"
copyMethod: Direct
storageClassName: openebs-zfs
# IMPORTANT NOTE:
# Set to the last X number of snapshots to restore from
previous: ${previous}
# OR;
# IMPORTANT NOTE:
# On bootstrap set `restoreAsOf` to the time the old cluster was destroyed.
# This will essentially prevent volsync from trying to restore a backup
# from a application that started with default data in the PVC.
# Do not restore snapshots made after the following RFC3339 Timestamp.
# date --rfc-3339=seconds (--utc)
# restoreAsOf: "2022-12-10T16:00:00-05:00"

View file

@ -0,0 +1,158 @@
---
version: "3"
x-task-vars: &task-vars
rsrc: '{{.rsrc}}'
controller: '{{.controller}}'
namespace: '{{.namespace}}'
claim: '{{.claim}}'
ts: '{{.ts}}'
kustomization: '{{.kustomization}}'
previous: '{{.previous}}'
vars:
destinationTemplate: "{{.ROOT_DIR}}/.taskfiles/VolSync/ReplicationDestination.tmpl.yaml"
wipeJobTemplate: "{{.ROOT_DIR}}/.taskfiles/VolSync/WipeJob.tmpl.yaml"
waitForJobScript: "{{.ROOT_DIR}}/.taskfiles/VolSync/wait-for-job.sh"
listJobTemplate: "{{.ROOT_DIR}}/.taskfiles/VolSync/ListJob.tmpl.yaml"
unlockJobTemplate: "{{.ROOT_DIR}}/.taskfiles/VolSync/UnlockJob.tmpl.yaml"
ts: '{{now | date "150405"}}'
tasks:
list:
desc: List all snapshots taken by restic for a given ReplicationSource (ex. task volsync:list rsrc=plex [namespace=default])
silent: true
cmds:
- envsubst < <(cat {{.listJobTemplate}}) | kubectl apply -f -
- bash {{.waitForJobScript}} list-{{.rsrc}}-{{.ts}} {{.namespace}}
- kubectl -n {{.namespace}} wait job/list-{{.rsrc}}-{{.ts}} --for condition=complete --timeout=1m
- kubectl -n {{.namespace}} logs job/list-{{.rsrc}}-{{.ts}} --container list
- kubectl -n {{.namespace}} delete job list-{{.rsrc}}-{{.ts}}
vars:
rsrc: '{{ or .rsrc (fail "ReplicationSource `rsrc` is required") }}'
namespace: '{{.namespace | default "default"}}'
env: *task-vars
preconditions:
# - sh: test -f {{.waitForJobScript}}
- sh: test -f {{.listJobTemplate}}
unlock:
desc: Unlocks restic repository for a given ReplicationSource (ex. task volsync:unlock rsrc=plex [namespace=default])
silent: true
cmds:
- envsubst < <(cat {{.unlockJobTemplate}}) | kubectl apply -f -
# - bash {{.waitForJobScript}} unlock-{{.rsrc}}-{{.ts}} {{.namespace}}
- kubectl -n {{.namespace}} wait job/unlock-{{.rsrc}}-{{.ts}} --for condition=complete --timeout=1m
- kubectl -n {{.namespace}} logs job/unlock-{{.rsrc}}-{{.ts}} --container unlock
- kubectl -n {{.namespace}} delete job unlock-{{.rsrc}}-{{.ts}}
vars:
rsrc: '{{ or .rsrc (fail "ReplicationSource `rsrc` is required") }}'
namespace: '{{.namespace | default "default"}}'
env: *task-vars
preconditions:
# - sh: test -f {{.waitForJobScript}}
- sh: test -f {{.unlockJobTemplate}}
# To run backup jobs in parallel for all replicationsources:
# - kubectl get replicationsources --all-namespaces --no-headers | awk '{print $2, $1}' | xargs --max-procs=4 -l bash -c 'task volsync:snapshot rsrc=$0 namespace=$1'
#
snapshot:
desc: Trigger a Restic ReplicationSource snapshot (ex. task volsync:snapshot rsrc=plex [namespace=default])
cmds:
- kubectl -n {{.namespace}} patch replicationsources {{.rsrc}} --type merge -p '{"spec":{"trigger":{"manual":"{{.ts}}"}}}'
- bash {{.waitForJobScript}} volsync-src-{{.rsrc}} {{.namespace}}
- kubectl -n {{.namespace}} wait job/volsync-src-{{.rsrc}} --for condition=complete --timeout=120m
# TODO: Find a way to output logs
# Error from server (NotFound): jobs.batch "volsync-src-zzztest" not found
# - kubectl -n {{.namespace}} logs job/volsync-src-{{.rsrc}}
vars:
rsrc: '{{ or .rsrc (fail "ReplicationSource `rsrc` is required") }}'
namespace: '{{.namespace | default "default"}}'
env: *task-vars
preconditions:
# - sh: test -f {{.waitForJobScript}}
- sh: kubectl -n {{.namespace}} get replicationsources {{.rsrc}}
msg: "ReplicationSource '{{.rsrc}}' not found in namespace '{{.namespace}}'"
# To run restore jobs in parallel for all replicationdestinations:
# - kubectl get replicationsources --all-namespaces --no-headers | awk '{print $2, $1}' | xargs --max-procs=2 -l bash -c 'task volsync:restore rsrc=$0 namespace=$1'
#
restore:
desc: Trigger a Restic ReplicationSource restore (ex. task volsync:restore rsrc=plex [namespace=default])
cmds:
- task: restore-suspend-app
vars: *task-vars
- task: restore-wipe-job
vars: *task-vars
- task: restore-volsync-job
vars: *task-vars
- task: restore-resume-app
vars: *task-vars
vars:
rsrc: '{{ or .rsrc (fail "Variable `rsrc` is required") }}'
namespace: '{{.namespace | default "default"}}'
# 1) Query to find the Flux Kustomization associated with the ReplicationSource (rsrc)
kustomization:
sh: |
kubectl -n {{.namespace}} get replicationsource {{.rsrc}} \
-o jsonpath="{.metadata.labels.kustomize\.toolkit\.fluxcd\.io/name}"
# 2) Query to find the Claim associated with the ReplicationSource (rsrc)
claim:
sh: |
kubectl -n {{.namespace}} get replicationsource {{.rsrc}} \
-o jsonpath="{.spec.sourcePVC}"
# 3) Query to find the controller associated with the PersistentVolumeClaim (claim)
controller:
sh: |
app=$(kubectl -n {{.namespace}} get persistentvolumeclaim {{.claim}} -o jsonpath="{.metadata.labels.app\.kubernetes\.io/name}")
if kubectl -n {{ .namespace }} get deployment.apps/$app >/dev/null 2>&1 ; then
echo "deployment.apps/$app"
else
echo "statefulset.apps/$app"
fi
previous: "{{.previous | default 2}}"
env: *task-vars
preconditions:
- sh: test -f {{.wipeJobTemplate}}
- sh: test -f {{.destinationTemplate}}
# - sh: test -f {{.waitForJobScript}}
# Suspend the Flux ks and hr
restore-suspend-app:
internal: true
cmds:
- flux -n flux-system suspend kustomization {{.kustomization}}
- flux -n {{.namespace}} suspend helmrelease {{.rsrc}}
- kubectl -n {{.namespace}} scale {{.controller}} --replicas 0
- kubectl -n {{.namespace}} wait pod --for delete --selector="app.kubernetes.io/name={{.rsrc}}" --timeout=2m
env: *task-vars
# Wipe the PVC of all data
restore-wipe-job:
internal: true
cmds:
- envsubst < <(cat {{.wipeJobTemplate}}) | kubectl apply -f -
- bash {{.waitForJobScript}} wipe-{{.rsrc}}-{{.claim}}-{{.ts}} {{.namespace}}
- kubectl -n {{.namespace}} wait job/wipe-{{.rsrc}}-{{.claim}}-{{.ts}} --for condition=complete --timeout=120m
- kubectl -n {{.namespace}} logs job/wipe-{{.rsrc}}-{{.claim}}-{{.ts}} --container wipe
- kubectl -n {{.namespace}} delete job wipe-{{.rsrc}}-{{.claim}}-{{.ts}}
env: *task-vars
# Create VolSync replicationdestination CR to restore data
restore-volsync-job:
internal: true
cmds:
- envsubst < <(cat {{.destinationTemplate}}) | kubectl apply -f -
- bash {{.waitForJobScript}} volsync-dst-{{.rsrc}}-{{.claim}}-{{.ts}} {{.namespace}}
- kubectl -n {{.namespace}} wait job/volsync-dst-{{.rsrc}}-{{.claim}}-{{.ts}} --for condition=complete --timeout=120m
- kubectl -n {{.namespace}} delete replicationdestination {{.rsrc}}-{{.claim}}-{{.ts}}
env: *task-vars
# Resume Flux ks and hr
restore-resume-app:
internal: true
cmds:
- flux -n {{.namespace}} resume helmrelease {{.rsrc}}
- flux -n flux-system resume kustomization {{.kustomization}}
env: *task-vars

View file

@ -0,0 +1,38 @@
---
apiVersion: batch/v1
kind: Job
metadata:
name: "unlock-${rsrc}-${ts}"
namespace: "${namespace}"
spec:
ttlSecondsAfterFinished: 3600
template:
spec:
automountServiceAccountToken: false
restartPolicy: OnFailure
containers:
- name: unlock
image: docker.io/restic/restic:0.16.0
args: ["unlock", "--remove-all"]
envFrom:
- secretRef:
name: "${rsrc}-volsync-r2-secret"
---
apiVersion: batch/v1
kind: Job
metadata:
name: "unlock-${rsrc}-r2-${ts}"
namespace: "${namespace}"
spec:
ttlSecondsAfterFinished: 3600
template:
spec:
automountServiceAccountToken: false
restartPolicy: OnFailure
containers:
- name: unlock
image: docker.io/restic/restic:0.16.0
args: ["unlock", "--remove-all"]
envFrom:
- secretRef:
name: "${rsrc}-volsync-secret"

View file

@ -0,0 +1,25 @@
---
apiVersion: batch/v1
kind: Job
metadata:
name: "wipe-${rsrc}-${claim}-${ts}"
namespace: "${namespace}"
spec:
ttlSecondsAfterFinished: 3600
template:
spec:
automountServiceAccountToken: false
restartPolicy: OnFailure
containers:
- name: wipe
image: public.ecr.aws/docker/library/busybox:latest
command: ["/bin/sh", "-c", "cd /config; find . -delete"]
volumeMounts:
- name: config
mountPath: /config
securityContext:
privileged: true
volumes:
- name: config
persistentVolumeClaim:
claimName: "${claim}"

View file

@ -0,0 +1,14 @@
#!/usr/bin/env bash
JOB_NAME=$1
NAMESPACE="${2:-default}"
[[ -z "${JOB_NAME}" ]] && echo "Job name not specified" && exit 1
while true; do
STATUS="$(kubectl -n "${NAMESPACE}" get pod -l job-name="${JOB_NAME}" -o jsonpath='{.items[*].status.phase}')"
if [ "${STATUS}" == "Pending" ]; then
break
fi
sleep 1
done

View file

@ -2,64 +2,11 @@
version: "3"
tasks:
cleanup-pods:
desc: Clean up leftover Pods
hubble:
desc: forward the hubble relay
cmds:
- for:
matrix:
PHASE: [Failed, Succeeded, Pending]
cmd: kubectl delete pods --field-selector status.phase={{.ITEM.PHASE}} -A --ignore-not-found=true
sync-secrets:
desc: Sync ExternalSecret resources
vars:
secret: '{{ .secret | default ""}}'
namespace: '{{.namespace | default "default"}}'
cmd: |
{{if eq .secret ""}}
kubectl get externalsecret.external-secrets.io --all-namespaces --no-headers -A | awk '{print $1, $2}' \
| xargs --max-procs=4 -l bash -c 'kubectl -n $0 annotate externalsecret.external-secrets.io $1 force-sync=$(date +%s) --overwrite'
{{else}}
kubectl -n {{.namespace}} annotate externalsecret.external-secrets.io {{.secret}} force-sync=$(date +%s) --overwrite
{{end}}
preconditions:
- kubectl -n {{.namespace}} get externalsecret {{.secret}}
mount-volume:
desc: Mount a PersistentVolumeClaim to a temporary pod
interactive: true
vars:
claim: '{{ or .claim (fail "PersistentVolumeClaim `claim` is required") }}'
namespace: '{{.namespace | default "default"}}'
cmd: |
kubectl run -n {{.namespace}} debug-{{.claim}} -i --tty --rm --image=null --privileged --overrides='
{
"apiVersion": "v1",
"spec": {
"containers": [
{
"name": "debug",
"image": "docker.io/library/alpine:latest",
"command": ["/bin/ash"],
"stdin": true,
"stdinOnce": true,
"tty": true,
"volumeMounts": [
{
"name": "config",
"mountPath": "/config"
}
]
}
],
"volumes": [
{
"name": "config",
"persistentVolumeClaim": {
"claimName": "{{.claim}}"
}
}
],
"restartPolicy": "Never"
}
}'
preconditions:
- kubectl -n {{.namespace}} get pvc {{.claim}}
- cilium hubble port-forward &
hubble-ui:
desc: port-forward hubble to 8888
cmds:
- kubectl port-forward -n kube-system svc/hubble-ui 8888:80

View file

@ -0,0 +1,104 @@
---
version: "3"
x-task-vars: &task-vars
node: "{{.node}}"
ceph_disk: "{{.ceph_disk}}"
ts: "{{.ts}}"
jobName: "{{.jobName}}"
vars:
waitForJobScript: "../_scripts/wait-for-k8s-job.sh"
ts: '{{now | date "150405"}}'
tasks:
wipe-node-aule:
desc: Trigger a wipe of Rook-Ceph data on node "aule"
cmds:
- task: wipe-disk
vars:
node: "{{.node}}"
ceph_disk: "/dev/disk/by-id/scsi-0HC_Volume_37460833"
- task: wipe-data
vars:
node: "{{.node}}"
vars:
node: aule
wipe-node-orome:
desc: Trigger a wipe of Rook-Ceph data on node "orome"
cmds:
- task: wipe-disk
vars:
node: "{{.node}}"
ceph_disk: "/dev/disk/by-id/scsi-0HC_Volume_37645333"
- task: wipe-data
vars:
node: "{{.node}}"
vars:
node: orome
wipe-node-eonwe:
desc: Trigger a wipe of Rook-Ceph data on node "eonwe"
cmds:
- task: wipe-disk
vars:
node: "{{.node}}"
ceph_disk: "/dev/disk/by-id/scsi-0HC_Volume_37460887"
- task: wipe-data
vars:
node: "{{.node}}"
vars:
node: eonwe
wipe-node-arlen:
desc: Trigger a wipe of Rook-Ceph data on node "arlen"
cmds:
- task: wipe-disk
vars:
node: "{{.node}}"
ceph_disk: "/dev/disk/by-id/scsi-0HC_Volume_37460897"
- task: wipe-data
vars:
node: "{{.node}}"
vars:
node: arlen
wipe-disk:
desc: Wipe all remnants of rook-ceph from a given disk (ex. task rook:wipe-disk node=aule ceph_disk="/dev/nvme0n1")
silent: true
internal: true
cmds:
- envsubst < <(cat {{.wipeRookDiskJobTemplate}}) | kubectl apply -f -
- bash {{.waitForJobScript}} {{.wipeCephDiskJobName}} default
- kubectl -n default wait job/{{.wipeCephDiskJobName}} --for condition=complete --timeout=1m
- kubectl -n default logs job/{{.wipeCephDiskJobName}} --container list
- kubectl -n default delete job {{.wipeCephDiskJobName}}
vars:
node: '{{ or .node (fail "`node` is required") }}'
ceph_disk: '{{ or .ceph_disk (fail "`ceph_disk` is required") }}'
jobName: 'wipe-disk-{{- .node -}}-{{- .ceph_disk | replace "/" "-" -}}-{{- .ts -}}'
wipeRookDiskJobTemplate: "WipeDiskJob.tmpl.yaml"
env: *task-vars
preconditions:
- sh: test -f {{.waitForJobScript}}
- sh: test -f {{.wipeRookDiskJobTemplate}}
wipe-data:
desc: Wipe all remnants of rook-ceph from a given disk (ex. task rook:wipe-data node=aule)
silent: true
internal: true
cmds:
- envsubst < <(cat {{.wipeRookDataJobTemplate}}) | kubectl apply -f -
- bash {{.waitForJobScript}} {{.wipeRookDataJobName}} default
- kubectl -n default wait job/{{.wipeRookDataJobName}} --for condition=complete --timeout=1m
- kubectl -n default logs job/{{.wipeRookDataJobName}} --container list
- kubectl -n default delete job {{.wipeRookDataJobName}}
vars:
node: '{{ or .node (fail "`node` is required") }}'
jobName: "wipe-rook-data-{{- .node -}}-{{- .ts -}}"
wipeRookDataJobTemplate: "WipeRookDataJob.tmpl.yaml"
env: *task-vars
preconditions:
- sh: test -f {{.waitForJobScript}}
- sh: test -f {{.wipeRookDataJobTemplate}}

View file

@ -0,0 +1,26 @@
---
apiVersion: batch/v1
kind: Job
metadata:
name: "${jobName}"
namespace: "default"
spec:
ttlSecondsAfterFinished: 3600
template:
spec:
automountServiceAccountToken: false
restartPolicy: Never
nodeName: ${node}
containers:
- name: disk-wipe
image: ghcr.io/onedr0p/alpine:3.17.3@sha256:999384960b6114496a5e4036e945141c205d064ce23b87326bd3f8d878c5a9d4
securityContext:
privileged: true
resources: {}
command: ["/bin/sh", "-c"]
args:
- apk add --no-cache sgdisk util-linux parted;
sgdisk --zap-all ${ceph_disk};
blkdiscard ${ceph_disk};
dd if=/dev/zero bs=1M count=10000 oflag=direct of=${ceph_disk};
partprobe ${ceph_disk};

View file

@ -0,0 +1,29 @@
---
apiVersion: batch/v1
kind: Job
metadata:
name: "${jobName}"
namespace: "default"
spec:
ttlSecondsAfterFinished: 3600
template:
spec:
automountServiceAccountToken: false
restartPolicy: Never
nodeName: ${node}
containers:
- name: disk-wipe
image: ghcr.io/onedr0p/alpine:3.17.3@sha256:999384960b6114496a5e4036e945141c205d064ce23b87326bd3f8d878c5a9d4
securityContext:
privileged: true
resources: {}
command: ["/bin/sh", "-c"]
args:
- rm -rf /mnt/host_var/lib/rook
volumeMounts:
- mountPath: /mnt/host_var
name: host-var
volumes:
- name: host-var
hostPath:
path: /var

19
.taskfiles/rook/pod.yaml Normal file
View file

@ -0,0 +1,19 @@
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: disk-wipe
image: ghcr.io/onedr0p/alpine:3.17.3@sha256:999384960b6114496a5e4036e945141c205d064ce23b87326bd3f8d878c5a9d4
securityContext:
privileged: true
resources: {}
command: ["/bin/sh", "-c"]
args:
- apk add --no-cache sgdisk util-linux parted e2fsprogs;
sgdisk --zap-all /dev/nvme1n1;
blkdiscard /dev/nvme1n1;
dd if=/dev/zero bs=1M count=10000 oflag=direct of=/dev/nvme1n1;
sgdisk /dev/nvme1n1
partprobe /dev/nvme1n1;

View file

@ -1,148 +0,0 @@
---
# yaml-language-server: $schema=https://taskfile.dev/schema.json
version: "3"
tasks:
bootstrap:
desc: Bootstrap Talos
summary: |
Args:
CONTROLLER: Controller node to run command against (required)
prompt: Bootstrap Talos on the '{{.K8S_CLUSTER}}' cluster... continue?
cmds:
- task: bootstrap-etcd
vars: &vars
CONTROLLER: "{{.CONTROLER}}"
- task: fetch-kubeconfig
vars: *vars
- task: bootstrap-integrations
vars: *vars
requires:
vars:
- K8S_CLUSTER
- CONTROLLER
bootstrap-etcd:
desc: Bootstrap Etcd
cmd: until talosctl --nodes {{.CONTROLLER}} bootstrap; do sleep 10; done
requires:
vars:
- CONTROLLER
bootstrap-integrations:
desc: Bootstrap core integrations needed for Talos
cmds:
- until kubectl wait --for=condition=Ready=False nodes --all --timeout=600s; do sleep 10; done
- helmfile --kube-context {{.K8S_CLUSTER}} --file {{.K8S_CLUSTER_DIR}}/bootstrap/helmfile.yaml apply --skip-diff-on-install --suppress-diff
- until kubectl wait --for=condition=Ready nodes --all --timeout=600s; do sleep 10; done
requires:
vars:
- K8S_CLUSTER
preconditions:
- which helmfile
- sh: kubectl config get-contexts {{.K8S_CLUSTER}}
msg: "Kubectl context {{.K8S_CLUSTER}} not found"
- test -f {{.K8S_CLUSTER_DIR}}/bootstrap/helmfile.yaml
fetch-kubeconfig:
desc: Fetch kubeconfig from Talos controllers
cmd: |
talosctl kubeconfig --nodes {{.CONTROLLER}} \
--force --force-context-name {{.K8S_CLUSTER}} {{.K8S_CLUSTER_DIR}}
requires:
vars:
- K8S_CLUSTER
generate-clusterconfig:
desc: Generate clusterconfig for Talos
cmds:
- talhelper genconfig
--env-file {{.K8S_CLUSTER_DIR}}/bootstrap/talos/talenv.sops.yaml
--secret-file {{.K8S_CLUSTER_DIR}}/bootstrap/talos/talsecret.sops.yaml
--config-file {{.K8S_CLUSTER_DIR}}/bootstrap/talos/talconfig.yaml
--out-dir {{.K8S_CLUSTER_DIR}}/bootstrap/talos/clusterconfig
requires:
vars:
- K8S_CLUSTER
preconditions:
- test -f {{.K8S_CLUSTER_DIR}}/bootstrap/talos/talenv.sops.yaml
- test -f {{.K8S_CLUSTER_DIR}}/bootstrap/talos/talsecret.sops.yaml
- test -f {{.K8S_CLUSTER_DIR}}/bootstrap/talos/talconfig.yaml
upgrade:
desc: Upgrade Talos version for a node
vars:
TALOS_VERSION:
sh: |
yq -r ".talosVersion" {{.K8S_CLUSTER_DIR}}/bootstrap/talos/talconfig.yaml
TALOS_IMAGE:
sh: |
talhelper genurl installer \
--env-file {{.K8S_CLUSTER_DIR}}/bootstrap/talos/talenv.sops.yaml \
--config-file {{.K8S_CLUSTER_DIR}}/bootstrap/talos/talconfig.yaml \
| grep {{.NODE}} \
| awk '{split($0,u," "); print u[2]}'
cmds:
- talosctl upgrade -n {{.NODE}} --image {{.TALOS_IMAGE }}
requires:
vars:
- K8S_CLUSTER
- NODE
preconditions:
- test -f {{.K8S_CLUSTER_DIR}}/bootstrap/talos/talenv.sops.yaml
- test -f {{.K8S_CLUSTER_DIR}}/bootstrap/talos/talconfig.yaml
- msg: "Talos image could not be determined for node={{.NODE}}"
sh: 'test -n "{{.TALOS_IMAGE}}"'
upgrade-k8s:
desc: Upgrade Kubernetes version for a Talos cluster
silent: false
vars:
KUBERNETES_VERSION:
sh: |
yq -r ".kubernetesVersion" {{.K8S_CLUSTER_DIR}}/bootstrap/talos/talconfig.yaml
TALOS_CONTROLLER:
sh: talosctl config info --output json | jq --raw-output '.endpoints[]' | shuf -n 1
cmds:
- until kubectl wait --timeout=5m --for=condition=Complete jobs --all --all-namespaces; do sleep 10; done
- talosctl upgrade-k8s -n {{.TALOS_CONTROLLER}} --to {{.KUBERNETES_VERSION}}
requires:
vars:
- K8S_CLUSTER
preconditions:
- talosctl config info &>/dev/null
- talosctl --nodes {{.TALOS_CONTROLLER}} get machineconfig &>/dev/null
apply-clusterconfig:
desc: Apply clusterconfig for a Talos cluster
vars:
CLUSTERCONFIG_FILES:
sh: find {{.K8S_CLUSTER_DIR}}/bootstrap/talos/clusterconfig -type f -name '*.yaml' -printf '%f\n'
cmds:
- for:
var: CLUSTERCONFIG_FILES
task: _apply-machineconfig
vars:
filename: "{{.ITEM}}"
hostname: |-
{{ trimPrefix (printf "%s-" .K8S_CLUSTER) .ITEM | trimSuffix ".yaml" }}
DRY_RUN: "{{ .DRY_RUN }}"
requires:
vars:
- K8S_CLUSTER
_apply-machineconfig:
internal: true
desc: Apply a single Talos machineConfig to a Talos node
cmds:
- talosctl apply-config
--nodes "{{.hostname}}"
--file "{{.K8S_CLUSTER_DIR}}/bootstrap/talos/clusterconfig/{{.filename}}"
{{ if eq "true" .DRY_RUN }}--dry-run{{ end }}
requires:
vars:
- K8S_CLUSTER
- hostname
- filename
preconditions:
- test -f {{.K8S_CLUSTER_DIR}}/bootstrap/talos/clusterconfig/{{.filename}}

View file

@ -1,97 +0,0 @@
---
# yaml-language-server: $schema=https://taskfile.dev/schema.json
version: '3'
# Taskfile used to manage certain VolSync tasks for a given application, limitations are as followed.
# 1. Fluxtomization, HelmRelease, PVC, ReplicationSource all have the same name (e.g. plex)
# 2. ReplicationSource and ReplicationDestination are a Restic repository
# 3. Each application only has one PVC that is being replicated
vars:
VOLSYNC_RESOURCES_DIR: '{{.ROOT_DIR}}/.taskfiles/volsync/resources'
tasks:
state-*:
desc: Suspend or resume Volsync [CLUSTER=main]
cmds:
- flux --namespace flux-system {{.STATE}} kustomization volsync
- flux --namespace volsync-system {{.STATE}} helmrelease volsync
- kubectl --namespace volsync-system scale deployment volsync --replicas {{if eq .STATE "suspend"}}0{{else}}1{{end}}
vars:
STATE: '{{index .MATCH 0}}'
requires:
vars: [CLUSTER]
preconditions:
- '[[ "{{.STATE}}" == "suspend" || "{{.STATE}}" == "resume" ]]'
- which flux kubectl
unlock:
desc: Unlock all restic source repos [CLUSTER=main]
cmds:
- for: { var: SOURCES, split: "\n" }
cmd: kubectl --namespace {{splitList "," .ITEM | first}} patch --field-manager=flux-client-side-apply replicationsources {{splitList "," .ITEM | last}} --type merge --patch "{\"spec\":{\"restic\":{\"unlock\":\"{{now | unixEpoch}}\"}}}"
vars:
SOURCES:
sh: kubectl get replicationsources --all-namespaces --no-headers --output=jsonpath='{range .items[*]}{.metadata.namespace},{.metadata.name}{"\n"}{end}'
requires:
vars: [CLUSTER]
preconditions:
- which kubectl
snapshot:
desc: Snapshot an app [CLUSTER=main] [NS=default] [APP=required]
cmds:
- kubectl --namespace {{.NS}} patch replicationsources {{.APP}} --type merge -p '{"spec":{"trigger":{"manual":"{{now | unixEpoch}}"}}}'
- until kubectl --namespace {{.NS}} get job/{{.JOB}} &>/dev/null; do sleep 5; done
- kubectl --namespace {{.NS}} wait job/{{.JOB}} --for=condition=complete --timeout=120m
vars:
NS: '{{.NS | default "default"}}'
JOB: volsync-src-{{.APP}}
requires:
vars: [CLUSTER, APP]
preconditions:
- kubectl --namespace {{.NS}} get replicationsources {{.APP}}
- which kubectl
restore:
desc: Restore an app [CLUSTER=main] [NS=default] [APP=required] [PREVIOUS=required]
cmds:
# Suspend
- flux --namespace flux-system suspend kustomization {{.APP}}
- flux --namespace {{.NS}} suspend helmrelease {{.APP}}
- kubectl --namespace {{.NS}} scale {{.CONTROLLER}}/{{.APP}} --replicas 0
- kubectl --namespace {{.NS}} wait pod --for=delete --selector="app.kubernetes.io/name={{.APP}}" --timeout=5m
# Restore
- minijinja-cli {{.VOLSYNC_RESOURCES_DIR}}/replicationdestination.yaml.j2 | kubectl apply --server-side --filename -
- until kubectl --namespace {{.NS}} get job/volsync-dst-{{.APP}}-manual &>/dev/null; do sleep 5; done
- kubectl --namespace {{.NS}} wait job/volsync-dst-{{.APP}}-manual --for=condition=complete --timeout=120m
- kubectl --namespace {{.NS}} delete replicationdestination {{.APP}}-manual
# Resume
- flux --namespace flux-system resume kustomization {{.APP}}
- flux --namespace {{.NS}} resume helmrelease {{.APP}}
- flux --namespace {{.NS}} reconcile helmrelease {{.APP}} --force
- kubectl --namespace {{.NS}} wait pod --for=condition=ready --selector="app.kubernetes.io/name={{.APP}}" --timeout=5m
vars:
NS: '{{.NS | default "default"}}'
CONTROLLER:
sh: kubectl --namespace {{.NS}} get deployment {{.APP}} &>/dev/null && echo deployment || echo statefulset
env:
NS: '{{.NS}}'
APP: '{{.APP}}'
PREVIOUS: '{{.PREVIOUS}}'
CLAIM:
sh: kubectl --namespace {{.NS}} get replicationsources/{{.APP}} --output=jsonpath="{.spec.sourcePVC}"
ACCESS_MODES:
sh: kubectl --namespace {{.NS}} get replicationsources/{{.APP}} --output=jsonpath="{.spec.restic.accessModes}"
STORAGE_CLASS_NAME:
sh: kubectl --namespace {{.NS}} get replicationsources/{{.APP}} --output=jsonpath="{.spec.restic.storageClassName}"
PUID:
sh: kubectl --namespace {{.NS}} get replicationsources/{{.APP}} --output=jsonpath="{.spec.restic.moverSecurityContext.runAsUser}"
PGID:
sh: kubectl --namespace {{.NS}} get replicationsources/{{.APP}} --output=jsonpath="{.spec.restic.moverSecurityContext.runAsGroup}"
requires:
vars: [CLUSTER, APP, PREVIOUS]
preconditions:
- test -f {{.VOLSYNC_RESOURCES_DIR}}/replicationdestination.yaml.j2
- which flux kubectl minijinja-cli

View file

@ -1,23 +0,0 @@
---
apiVersion: volsync.backube/v1alpha1
kind: ReplicationDestination
metadata:
name: {{ ENV.APP }}-manual
namespace: {{ ENV.NS }}
spec:
trigger:
manual: restore-once
restic:
repository: {{ ENV.APP }}-volsync-secret
destinationPVC: {{ ENV.CLAIM }}
copyMethod: Direct
storageClassName: {{ ENV.STORAGE_CLASS_NAME }}
accessModes: {{ ENV.ACCESS_MODES }}
previous: {{ ENV.PREVIOUS }}
enableFileDeletion: true
cleanupCachePVC: true
cleanupTempPVC: true
moverSecurityContext:
runAsUser: {{ ENV.PUID }}
runAsGroup: {{ ENV.PGID }}
fsGroup: {{ ENV.PGID }}

View file

@ -5,7 +5,6 @@
"redhat.vscode-yaml",
"signageos.signageos-vscode-sops",
"pkief.material-icon-theme",
"ms-vscode-remote.remote-ssh",
"editorconfig.editorconfig"
"ms-vscode-remote.remote-ssh"
]
}

View file

@ -42,9 +42,5 @@
"files.trimTrailingWhitespace": true,
"ansible.python.interpreterPath": "/usr/bin/python3",
"sops.defaults.ageKeyFile": "age.key",
"ansible.validation.lint.path": "~/projects/valinor/.venv/bin/ansible-lint",
"prettier.quoteProps": "preserve",
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"ansible.validation.lint.path": "~/projects/valinor/.venv/bin/ansible-lint"
}

View file

@ -1,4 +1 @@
Talos & 6x Dell USFF nodes with 2 Beefy VM works with GPUs.
Fancier README to come! :)
Kubernetes with talos @ Home

View file

@ -5,10 +5,6 @@ vars:
KUBERNETES_DIR: "{{.ROOT_DIR}}/kubernetes"
CLUSTER_SECRETS_FILE: "{{.CLUSTER_DIR}}/flux/vars/cluster-secrets.sops.env"
CLUSTER_SETTINGS_FILE: "{{.CLUSTER_DIR}}/flux/vars/cluster-settings.env"
K8S_CLUSTER: '{{.K8S_CLUSTER | default "theshire"}}'
K8S_CLUSTER_DIR: '{{.KUBERNETES_DIR}}'
CLUSTER: '{{.CLUSTER | default "theshire"}}'
CLUSTER_DIR: '{{.KUBERNETES_DIR}}'
env:
KUBECONFIG: "{{.ROOT_DIR}}/kubeconfig"
@ -17,12 +13,105 @@ env:
K8S_AUTH_KUBECONFIG: "{{.ROOT_DIR}}/kubeconfig"
includes:
volsync: .taskfiles/volsync
precommit: .taskfiles/precommit
k8s: .taskfiles/k8s
flux: .taskfiles/flux
talos: .taskfiles/talos
volsync: .taskfiles/VolSync/Tasks.yaml
precommit: .taskfiles/PreCommit/Tasks.yaml
k8s: .taskfiles/k8s/Taskfile.yaml
rook:
taskfile: ".taskfiles/rook"
dir: .taskfiles/rook
flux:
dir: .taskfiles/flux
taskfile: .taskfiles/flux
tasks:
default:
silent: true
cmds: ["task -l"]
init:
desc: Initialize workstation dependencies with Brew
cmds:
- brew install {{.DEPS}} {{.CLI_ARGS}}
preconditions:
- sh: command -v brew
msg: |
Homebrew is not installed. Using MacOS, Linux or WSL?
Head over to https://brew.sh to get up and running.
vars:
DEPS: >-
age
ansible
cilium-cli
direnv
derailed/k9s/k9s
fluxcd/tap/flux
go-task/tap/go-task
helm
ipcalc
jq
kubernetes-cli
kustomize
pre-commit
prettier
shellcheck
sops
stern
talhelper
yamllint
yq
sync-secrets:
desc: Sync ExternalSecret resources
vars:
secret: '{{ .secret | default ""}}'
namespace: '{{.namespace | default "default"}}'
cmd: |
{{if eq .secret ""}}
kubectl get externalsecret.external-secrets.io --all-namespaces --no-headers -A | awk '{print $1, $2}' \
| xargs --max-procs=4 -l bash -c 'kubectl -n $0 annotate externalsecret.external-secrets.io $1 force-sync=$(date +%s) --overwrite'
{{else}}
kubectl -n {{.namespace}} annotate externalsecret.external-secrets.io {{.secret}} force-sync=$(date +%s) --overwrite
{{end}}
preconditions:
- kubectl -n {{.namespace}} get externalsecret {{.secret}}
mount-volume:
desc: Mount a PersistentVolumeClaim to a temporary pod
interactive: true
vars:
claim: '{{ or .claim (fail "PersistentVolumeClaim `claim` is required") }}'
namespace: '{{.namespace | default "default"}}'
cmd: |
kubectl run -n {{.namespace}} debug-{{.claim}} -i --tty --rm --image=null --privileged --overrides='
{
"apiVersion": "v1",
"spec": {
"containers": [
{
"name": "debug",
"image": "docker.io/library/alpine:3.19.1",
"command": ["/bin/bash"],
"stdin": true,
"stdinOnce": true,
"tty": true,
"volumeMounts": [
{
"name": "config",
"mountPath": "/config"
}
]
}
],
"volumes": [
{
"name": "config",
"persistentVolumeClaim": {
"claimName": "{{.claim}}"
}
}
],
"restartPolicy": "Never"
}
}'
preconditions:
- kubectl -n {{.namespace}} get pvc {{.claim}}

View file

@ -1,87 +0,0 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/bjw-s/helm-charts/main/charts/other/app-template/schemas/helmrelease-helm-v2.schema.json
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: &app ollama
spec:
interval: 30m
chart:
spec:
chart: app-template
version: 3.5.1
sourceRef:
kind: HelmRepository
name: bjw-s
namespace: flux-system
install:
remediation:
retries: 3
upgrade:
cleanupOnFail: true
remediation:
retries: 3
strategy: rollback
values:
controllers:
ollama:
annotations:
reloader.stakater.com/auto: "true"
pod:
nodeSelector:
nvidia.com/gpu.present: "true"
runtimeClassName: nvidia
containers:
app:
image:
repository: docker.io/ollama/ollama
tag: 0.4.1
env:
- name: OLLAMA_HOST
value: 0.0.0.0
- name: OLLAMA_ORIGINS
value: "*"
- name: OLLAMA_MODELS
value: &modelPath "/models"
- name: OLLAMA_KEEP_ALIVE
value: "24h"
resources:
requests:
cpu: 500m
memory: 2Gi
limits:
memory: 16Gi
nvidia.com/gpu: 1 # requesting 1 GPU
service:
app:
controller: ollama
ports:
http:
port: 11434
ingress:
app:
enabled: true
className: internal-nginx
hosts:
- host: &host "{{ .Release.Name }}.jahanson.tech"
paths:
- path: /
service:
identifier: app
port: http
tls:
- hosts:
- *host
persistence:
models:
enabled: true
existingClaim: ollama-models
advancedMounts:
ollama:
app:
- path: *modelPath
config:
enabled: true
existingClaim: ollama
globalMounts:
- path: /root/.ollama

View file

@ -1,8 +0,0 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ./helmrelease.yaml
- ./pvc.yaml
- ../../../../templates/volsync

View file

@ -1,12 +0,0 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ollama-models
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
storageClassName: openebs-hostpath

View file

@ -1,29 +0,0 @@
---
# yaml-language-server: $schema=https://ks.hsn.dev/kustomize.toolkit.fluxcd.io/kustomization_v1.json
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: &app ollama
namespace: flux-system
spec:
targetNamespace: ai
commonMetadata:
labels:
app.kubernetes.io/name: *app
dependsOn:
- name: nvidia-device-plugin
- name: node-feature-discovery
- name: volsync
- name: rook-ceph-cluster
path: ./kubernetes/apps/ai/ollama/app
prune: true
sourceRef:
kind: GitRepository
name: theshire
wait: false
interval: 30m
timeout: 5m
postBuild:
substitute:
APP: *app
VOLSYNC_CAPACITY: 1Gi

View file

@ -1,77 +0,0 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/bjw-s/helm-charts/main/charts/other/app-template/schemas/helmrelease-helm-v2.schema.json
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: &app open-webui
spec:
interval: 30m
chart:
spec:
chart: app-template
version: 3.5.1
sourceRef:
kind: HelmRepository
name: bjw-s
namespace: flux-system
dependsOn:
- name: ollama
install:
remediation:
retries: 3
upgrade:
cleanupOnFail: true
remediation:
retries: 3
strategy: rollback
values:
controllers:
open-webui:
annotations:
reloader.stakater.com/auto: "true"
containers:
app:
image:
repository: ghcr.io/open-webui/open-webui
tag: 0.3.35
env:
- name: OLLAMA_BASE_URL
value: http://ollama.ai.svc.cluster.local:11434
- name: ENABLE_RAG_WEB_SEARCH
value: true
- name: RAG_WEB_SEARCH_ENGINE
value: searxng
- name: SEARXNG_QUERY_URL
value: http://searxng.default.svc.cluster.local:8080/search?q=<query>
resources:
requests:
cpu: 500m
memory: 2Gi
limits:
memory: 2Gi
service:
app:
controller: open-webui
ports:
http:
port: 8080
ingress:
app:
enabled: true
className: internal-nginx
hosts:
- host: &host "chat.jahanson.tech"
paths:
- path: /
service:
identifier: app
port: http
tls:
- hosts:
- *host
persistence:
config:
enabled: true
existingClaim: *app
globalMounts:
- path: /app/backend/data

View file

@ -1,8 +0,0 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ./helmrelease.yaml
- ../../../../templates/volsync
- ../../../../templates/gatus/internal

View file

@ -1,28 +0,0 @@
---
# yaml-language-server: $schema=https://ks.hsn.dev/kustomize.toolkit.fluxcd.io/kustomization_v1.json
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: &app open-webui
namespace: flux-system
spec:
targetNamespace: ai
commonMetadata:
labels:
app.kubernetes.io/name: *app
dependsOn:
- name: volsync
- name: ollama
path: ./kubernetes/apps/ai/open-webui/app
prune: true
sourceRef:
kind: GitRepository
name: theshire
wait: false
interval: 30m
timeout: 5m
postBuild:
substitute:
APP: *app
VOLSYNC_CAPACITY: 5Gi
GATUS_SUBDOMAIN: chat

View file

@ -1,8 +0,0 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ./helmrelease.yaml
- ../../../../templates/gatus/external
- ../../../../templates/volsync

View file

@ -1,29 +0,0 @@
---
# yaml-language-server: $schema=https://ks.hsn.dev/kustomize.toolkit.fluxcd.io/kustomization_v1.json
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: &app jellyfin
namespace: flux-system
spec:
targetNamespace: anime
commonMetadata:
labels:
app.kubernetes.io/name: *app
dependsOn:
- name: node-feature-discovery
- name: nvidia-device-plugin
- name: volsync
path: ./kubernetes/apps/anime/jellyfin/app
prune: true
sourceRef:
kind: GitRepository
name: theshire
wait: false
interval: 30m
timeout: 5m
postBuild:
substitute:
APP: *app
GATUS_PATH: /web/index.html
VOLSYNC_CAPACITY: 20Gi

View file

@ -1,104 +0,0 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/bjw-s/helm-charts/main/charts/other/app-template/schemas/helmrelease-helm-v2.schema.json
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: &app jellyseerr
spec:
interval: 30m
chart:
spec:
chart: app-template
version: 3.5.1
sourceRef:
kind: HelmRepository
name: bjw-s
namespace: flux-system
install:
remediation:
retries: 3
upgrade:
cleanupOnFail: true
remediation:
strategy: rollback
retries: 3
dependsOn:
- name: rook-ceph-cluster
namespace: rook-ceph
- name: volsync
namespace: volsync-system
values:
controllers:
jellyseerr:
annotations:
reloader.stakater.com/auto: "true"
containers:
app:
image:
repository: fallenbagel/jellyseerr
tag: 2.0.1
env:
TZ: America/Chicago
LOG_LEVEL: "info"
PORT: &port 80
probes:
liveness: &probes
enabled: true
custom: true
spec:
httpGet:
path: /status
port: *port
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 1
failureThreshold: 3
readiness: *probes
startup:
enabled: false
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: { drop: ["ALL"] }
defaultPodOptions:
securityContext:
runAsNonRoot: true
runAsUser: 568
runAsGroup: 568
fsGroup: 568
fsGroupChangePolicy: OnRootMismatch
seccompProfile: { type: RuntimeDefault }
service:
app:
controller: jellyseerr
ports:
http:
port: *port
ingress:
app:
annotations:
external-dns.alpha.kubernetes.io/target: external.hsn.dev
external-dns.alpha.kubernetes.io/cloudflare-proxied: "true"
className: external-nginx
hosts:
- host: "{{ .Release.Name }}.hsn.dev"
paths:
- path: /
service:
identifier: app
port: http
persistence:
config:
existingClaim: *app
globalMounts:
- path: /app/config
cache:
type: emptyDir
globalMounts:
- path: /app/config/cache
logs:
type: emptyDir
globalMounts:
- path: /app/config/logs
tmp:
type: emptyDir

View file

@ -1,8 +0,0 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ./helmrelease.yaml
- ../../../../templates/gatus/external
- ../../../../templates/volsync

View file

@ -1,27 +0,0 @@
---
# yaml-language-server: $schema=https://ks.hsn.dev/kustomize.toolkit.fluxcd.io/kustomization_v1.json
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: &app jellyseerr
namespace: flux-system
spec:
targetNamespace: anime
commonMetadata:
labels:
app.kubernetes.io/name: *app
dependsOn:
- name: rook-ceph-cluster
- name: volsync
path: ./kubernetes/apps/anime/jellyseerr/app
prune: true
sourceRef:
kind: GitRepository
name: theshire
wait: false
interval: 30m
timeout: 5m
postBuild:
substitute:
APP: *app
VOLSYNC_CAPACITY: 5Gi

View file

@ -1,13 +0,0 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/kustomization.json
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
# Pre Flux-Kustomizations
- ./namespace.yaml
# Flux-Kustomizations
- ./jellyfin/ks.yaml # sqlite
- ./jellyseerr/ks.yaml # sqlite
- ./radarr/ks.yaml # postgres
# - ./shoko/ks.yaml # sqlite
- ./sonarr/ks.yaml # postgres

View file

@ -1,46 +0,0 @@
---
# yaml-language-server: $schema=https://ks.hsn.dev/external-secrets.io/externalsecret_v1beta1.json
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: radarr
spec:
secretStoreRef:
kind: ClusterSecretStore
name: onepassword-connect
target:
name: radarr-secret
template:
engineVersion: v2
data:
PUSHOVER_TOKEN: "{{ .radarr_token }}"
PUSHOVER_USER_KEY: "{{ .userkey_jahanson }}"
RADARR__AUTH__APIKEY: "{{ .api_key_anime }}"
dataFrom:
- extract:
key: pushover
- extract:
key: radarr
---
# yaml-language-server: $schema=https://ks.hsn.dev/external-secrets.io/externalsecret_v1beta1.json
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: radarr-db
spec:
secretStoreRef:
name: crunchy-pgo-secrets
kind: ClusterSecretStore
target:
name: radarr-db-secret
template:
engineVersion: v2
data:
RADARR__POSTGRES__HOST: "{{ index . \"pgbouncer-host\" }}"
RADARR__POSTGRES__USER: "{{ .user }}"
RADARR__POSTGRES__PASSWORD: "{{ .password }}"
RADARR__POSTGRES__PORT: "{{ .port }}"
RADARR__POSTGRES__MAINDB: "{{ .dbname }}"
dataFrom:
- extract:
key: postgres-pguser-radarr-anime

View file

@ -1,119 +0,0 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/bjw-s/helm-charts/main/charts/other/app-template/schemas/helmrelease-helm-v2.schema.json
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: &app radarr-anime
spec:
interval: 30m
chart:
spec:
chart: app-template
version: 3.5.1
sourceRef:
kind: HelmRepository
name: bjw-s
namespace: flux-system
install:
remediation:
retries: 3
upgrade:
cleanupOnFail: true
remediation:
retries: 3
strategy: rollback
values:
controllers:
radarr:
annotations:
reloader.stakater.com/auto: "true"
containers:
app:
image:
repository: ghcr.io/onedr0p/radarr-develop
tag: 5.15.0.9412
env:
RADARR__APP__INSTANCENAME: Radarr-Anime
RADARR__APP__THEME: dark
RADARR__AUTH__METHOD: External
RADARR__AUTH__REQUIRED: DisabledForLocalAddresses
RADARR__LOG__DBENABLED: "False"
RADARR__LOG__LEVEL: info
RADARR__SERVER__PORT: &port 80
RADARR__UPDATE__BRANCH: develop
TZ: America/Chicago
envFrom:
- secretRef:
name: radarr-secret
- secretRef:
name: radarr-db-secret
probes:
liveness: &probes
enabled: true
custom: true
spec:
httpGet:
path: /ping
port: *port
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 1
failureThreshold: 3
readiness: *probes
startup:
enabled: false
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: { drop: ["ALL"] }
resources:
requests:
cpu: 10m
limits:
memory: 6Gi
pod:
securityContext:
runAsUser: 568
runAsGroup: 568
runAsNonRoot: true
fsGroup: 568
fsGroupChangePolicy: OnRootMismatch
supplementalGroups: [10000]
service:
app:
controller: radarr
ports:
http:
port: *port
ingress:
app:
enabled: true
className: internal-nginx
hosts:
- host: &host "{{ .Release.Name }}.jahanson.tech"
paths:
- path: /
service:
identifier: app
port: http
tls:
- hosts:
- *host
persistence:
config:
enabled: true
existingClaim: *app
tmp:
type: emptyDir
media:
type: nfs
server: 10.1.1.13
path: /eru/media
globalMounts:
- path: /data/nas-media
moria-media:
type: nfs
server: 10.1.1.61
path: /moria/media/
globalMounts:
- path: /data/moria-media

View file

@ -1,8 +0,0 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ./externalsecret.yaml
- ./helmrelease.yaml
- ../../../../templates/volsync

View file

@ -1,47 +0,0 @@
---
# yaml-language-server: $schema=https://ks.hsn.dev/external-secrets.io/externalsecret_v1beta1.json
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: sonarr
spec:
refreshInterval: 1m
secretStoreRef:
kind: ClusterSecretStore
name: onepassword-connect
target:
name: sonarr-secret
template:
engineVersion: v2
data:
PUSHOVER_TOKEN: "{{ .sonarr_token }}"
PUSHOVER_USER_KEY: "{{ .userkey_jahanson }}"
SONARR__AUTH__APIKEY: "{{ .api_key_anime }}"
dataFrom:
- extract:
key: pushover
- extract:
key: sonarr
---
# yaml-language-server: $schema=https://ks.hsn.dev/external-secrets.io/externalsecret_v1beta1.json
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: sonarr-db
spec:
secretStoreRef:
name: crunchy-pgo-secrets
kind: ClusterSecretStore
target:
name: sonarr-db-secret
template:
engineVersion: v2
data:
SONARR__POSTGRES__HOST: "{{ index . \"pgbouncer-host\" }}"
SONARR__POSTGRES__USER: "{{ .user }}"
SONARR__POSTGRES__PASSWORD: "{{ .password }}"
SONARR__POSTGRES__PORT: "{{ .port }}"
SONARR__POSTGRES__MAINDB: "{{ .dbname }}"
dataFrom:
- extract:
key: postgres-pguser-sonarr-anime

View file

@ -1,119 +0,0 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/bjw-s/helm-charts/main/charts/other/app-template/schemas/helmrelease-helm-v2.schema.json
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: &app sonarr-anime
spec:
interval: 30m
chart:
spec:
chart: app-template
version: 3.5.1
sourceRef:
kind: HelmRepository
name: bjw-s
namespace: flux-system
install:
remediation:
retries: 3
upgrade:
cleanupOnFail: true
remediation:
retries: 3
strategy: rollback
values:
controllers:
sonarr:
annotations:
reloader.stakater.com/auto: "true"
containers:
app:
image:
repository: ghcr.io/onedr0p/sonarr-develop
tag: 4.0.10.2624
env:
SONARR__APP__INSTANCENAME: Sonarr-Anime
SONARR__APP__THEME: dark
SONARR__AUTH__METHOD: External
SONARR__AUTH__REQUIRED: DisabledForLocalAddresses
SONARR__LOG__DBENABLED: "False"
SONARR__LOG__LEVEL: info
SONARR__SERVER__PORT: &port 80
SONARR__UPDATE__BRANCH: develop
TZ: America/Chicago
envFrom:
- secretRef:
name: sonarr-secret
- secretRef:
name: sonarr-db-secret
probes:
liveness: &probes
enabled: true
custom: true
spec:
httpGet:
path: /ping
port: *port
initialDelaySeconds: 0
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readiness: *probes
startup:
enabled: false
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: { drop: ["ALL"] }
resources:
requests:
cpu: 10m
limits:
memory: 6Gi
pod:
securityContext:
runAsUser: 568
runAsGroup: 568
runAsNonRoot: true
fsGroup: 568
fsGroupChangePolicy: OnRootMismatch
supplementalGroups: [10000]
service:
app:
controller: sonarr
ports:
http:
port: *port
ingress:
main:
enabled: true
className: internal-nginx
hosts:
- host: &host "{{ .Release.Name }}.jahanson.tech"
paths:
- path: /
service:
identifier: app
port: http
tls:
- hosts:
- *host
persistence:
config:
enabled: true
existingClaim: *app
tmp:
type: emptyDir
media:
type: nfs
server: 10.1.1.13
path: /eru/media
globalMounts:
- path: /data/nas-media
moria-media:
type: nfs
server: 10.1.1.61
path: /moria/media/
globalMounts:
- path: /data/moria-media

View file

@ -1,8 +0,0 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ./externalsecret.yaml
- ./helmrelease.yaml
- ../../../../templates/volsync

View file

@ -1,6 +1,6 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/fluxcd-community/flux2-schemas/main/helmrelease-helm-v2beta2.json
apiVersion: helm.toolkit.fluxcd.io/v2
apiVersion: helm.toolkit.fluxcd.io/v2beta2
kind: HelmRelease
metadata:
name: cert-manager
@ -10,7 +10,7 @@ spec:
chart:
spec:
chart: cert-manager
version: v1.16.1
version: v1.14.5
sourceRef:
kind: HelmRepository
name: jetstack
@ -25,6 +25,9 @@ spec:
values:
installCRDs: true
webhook:
enabled: true
extraArgs:
- --dns01-recursive-nameservers=1.1.1.1:53,9.9.9.9:53
- --dns01-recursive-nameservers-only

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