From 5af697722d93e1d94f64981c62e889c80fdd8fe6 Mon Sep 17 00:00:00 2001 From: Michael Schnerring <3743342+schnerring@users.noreply.github.com> Date: Sat, 2 Jul 2022 16:56:41 +0000 Subject: [PATCH] feat(radicale): add Radicale to incubator (#3030) * Add default Radicale config file * Add Chart.yaml * Add initial {questions,values}.yaml * Fix config persistence * Add Linux capabilities and set PUID * Add server and encoding config * Add storage, web, and logging config * Add rights config * Add auth settings * Change mask_passwords to bool * Add authentication * Fix questions --- charts/incubator/radicale/Chart.yaml | 42 ++ charts/incubator/radicale/questions.yaml | 483 ++++++++++++++++++ .../radicale/templates/_configmap.tpl | 160 ++++++ .../incubator/radicale/templates/_secret.tpl | 16 + .../incubator/radicale/templates/common.yaml | 11 + charts/incubator/radicale/values.yaml | 83 +++ 6 files changed, 795 insertions(+) create mode 100644 charts/incubator/radicale/Chart.yaml create mode 100644 charts/incubator/radicale/questions.yaml create mode 100644 charts/incubator/radicale/templates/_configmap.tpl create mode 100644 charts/incubator/radicale/templates/_secret.tpl create mode 100644 charts/incubator/radicale/templates/common.yaml create mode 100644 charts/incubator/radicale/values.yaml diff --git a/charts/incubator/radicale/Chart.yaml b/charts/incubator/radicale/Chart.yaml new file mode 100644 index 00000000000..137150eb40a --- /dev/null +++ b/charts/incubator/radicale/Chart.yaml @@ -0,0 +1,42 @@ +apiVersion: v2 +appVersion: "3.1.7.0" +dependencies: + - name: common + repository: https://library-charts.truecharts.org + version: 10.1.4 +deprecated: false +description: Radicale is a small but powerful CalDAV (calendars, to-do lists) and CardDAV (contacts) server. +home: https://github.com/truecharts/apps/tree/master/charts/incubator/radicale +icon: https://truecharts.org/_static/img/appicons/radicale.png +keywords: + - radicale + - CalDAV + - CardDAV + - calendars + - contacts + - tasks + - to-do +kubeVersion: ">=1.16.0-0" +maintainers: + - email: info@truecharts.org + name: TrueCharts + url: https://truecharts.org +name: radicale +sources: + - https://radicale.org/ + - https://github.com/Kozea/Radicale + - https://github.com/tomsquest/docker-radicale + - https://hub.docker.com/r/tomsquest/docker-radicale +type: application +version: 0.0.1 +annotations: + truecharts.org/catagories: | + - radicale + - CalDAV + - CardDAV + - calendars + - contacts + - tasks + - to-do + truecharts.org/SCALE-support: "true" + truecharts.org/grade: U diff --git a/charts/incubator/radicale/questions.yaml b/charts/incubator/radicale/questions.yaml new file mode 100644 index 00000000000..ec7092fdc78 --- /dev/null +++ b/charts/incubator/radicale/questions.yaml @@ -0,0 +1,483 @@ +# Include{groups} +portals: + open: + protocols: + - "$kubernetes-resource_configmap_portal_protocol" + host: + - "$kubernetes-resource_configmap_portal_host" + ports: + - "$kubernetes-resource_configmap_portal_port" +questions: + - variable: portal + group: "Container Image" + label: "Configure Portal Button" + schema: + type: dict + hidden: true + attrs: + - variable: enabled + label: "Enable" + description: "enable the portal button" + schema: + hidden: true + editable: false + type: boolean + default: true +# Include{global} + + - variable: controller + group: "Controller" + label: "" + schema: + additional_attrs: true + type: dict + attrs: + - variable: advanced + label: "Show Advanced Controller Settings" + schema: + type: boolean + default: false + show_subquestions_if: true + subquestions: + - variable: type + description: "Please specify type of workload to deploy" + label: "(Advanced) Controller Type" + schema: + type: string + default: "deployment" + required: true + enum: + - value: "deployment" + description: "Deployment" + - value: "statefulset" + description: "Statefulset" + - value: "daemonset" + description: "Daemonset" + - variable: replicas + description: "Number of desired pod replicas" + label: "Desired Replicas" + schema: + type: int + default: 1 + required: true + - variable: strategy + description: "Please specify type of workload to deploy" + label: "(Advanced) Update Strategy" + schema: + type: string + default: "Recreate" + required: true + enum: + - value: "Recreate" + description: "Recreate: Kill existing pods before creating new ones" + - value: "RollingUpdate" + description: "RollingUpdate: Create new pods and then kill old ones" + - value: "OnDelete" + description: "(Legacy) OnDelete: ignore .spec.template changes" +# Include{controllerExpert} + + - variable: radicale + group: Container Configuration + label: Radicale Configuration + schema: + additional_attrs: true + type: dict + attrs: + - variable: server + label: Server Configuration + schema: + additional_attrs: true + type: dict + attrs: + - variable: max_connections + label: max_connections + description: The maximum number of parallel connections. Set to 0 to disable the limit. + schema: + type: int + default: 8 + required: true + - variable: max_content_length + label: max_content_length + description: The maximum size of the request body. (bytes) + schema: + type: int + default: 100000000 + required: true + - variable: timeout + label: timeout + description: Socket timeout. (seconds) + schema: + type: int + default: 30 + required: true + - variable: encoding + label: Encoding Configuration + schema: + additional_attrs: true + type: dict + attrs: + - variable: request + label: request + description: Encoding for responding requests. + schema: + type: string + default: utf-8 + required: true + - variable: stock + label: stock + description: Encoding for storing local collections. + schema: + type: string + default: utf-8 + required: true + - variable: auth + label: Auth Configuration + schema: + additional_attrs: true + type: dict + attrs: + - variable: type + label: type + description: The method to verify usernames and passwords. + schema: + type: string + default: htpasswd + required: true + enum: + - value: none + description: none + - value: htpasswd + description: htpasswd + - value: http_x_remote_user + description: http_x_remote_user + - variable: delay + label: delay + description: Average delay after failed login attempts in seconds. + schema: + type: int + default: 1 + required: true + - variable: realm + label: realm + description: Message displayed in the client when a password is needed. + schema: + type: string + default: Radicale - Password Required + required: true + - variable: users + label: Basic Authentication Users (htpasswd) + schema: + type: list + default: [] + items: + - variable: usersEntry + label: "" + schema: + additional_attrs: true + type: dict + attrs: + - variable: username + label: "Username" + schema: + type: string + required: true + default: "" + - variable: password + label: "Password" + schema: + type: string + required: true + private: true + default: "" + + - variable: rights + label: Rights Configuration + schema: + additional_attrs: true + type: dict + attrs: + - variable: type + label: type + description: The backend that is used to check the access rights of collections. + schema: + type: string + default: owner_only + required: true + enum: + - value: authenticated + description: authenticated + - value: owner_only + description: owner_only + - value: owner_write + description: owner_write + - variable: storage + label: Storage Configuration + schema: + additional_attrs: true + type: dict + attrs: + - variable: type + label: type + description: The backend that is used to store data. + schema: + type: string + default: multifilesystem + required: true + enum: + - value: multifilesystem + description: multifilesystem + - value: multifilesystem_nolock + description: multifilesystem_nolock + - variable: max_sync_token_age + label: max_sync_token_age + description: Delete sync token that are older (seconds) + schema: + type: int + default: 2592000 + required: true + - variable: web + label: Web Configuration + schema: + additional_attrs: true + type: dict + attrs: + - variable: type + label: type + description: The backend that provides the web interface of Radicale. + schema: + type: string + default: internal + required: true + enum: + - value: none + description: none + - value: internal + description: internal + - variable: logging + label: Logging Configuration + schema: + additional_attrs: true + type: dict + attrs: + - variable: level + label: level + description: Set the logging level. + schema: + type: string + default: warning + required: true + enum: + - value: debug + description: debug + - value: info + description: info + - value: warning + description: warning + - value: error + description: error + - value: critical + description: critical + - variable: mask_passwords + label: mask_passwords + description: Don't include passwords in logs. + schema: + type: boolean + default: true + +# Include{containerConfig} + + - variable: service + group: "Networking and Services" + label: "Configure Service(s)" + schema: + additional_attrs: true + type: dict + attrs: + - variable: main + label: "Main Service" + description: "The Primary service on which the healthcheck runs, often the webUI" + schema: + additional_attrs: true + type: dict + attrs: +# Include{serviceSelector} + - variable: main + label: "Main Service Port Configuration" + schema: + additional_attrs: true + type: dict + attrs: + - variable: port + label: "Port" + description: "This port exposes the container port on the service" + schema: + type: int + default: 10255 + required: true + - variable: advanced + label: "Show Advanced settings" + schema: + type: boolean + default: false + show_subquestions_if: true + subquestions: + - variable: protocol + label: "Port Type" + schema: + type: string + default: "HTTP" + enum: + - value: HTTP + description: "HTTP" + - value: "HTTPS" + description: "HTTPS" + - value: TCP + description: "TCP" + - value: "UDP" + description: "UDP" + - variable: nodePort + label: "Node Port (Optional)" + description: "This port gets exposed to the node. Only considered when service type is NodePort, Simple or LoadBalancer" + schema: + type: int + min: 9000 + max: 65535 + - variable: targetPort + label: "Target Port" + description: "The internal(!) port on the container the Application runs on" + schema: + type: int + default: 5232 + + - variable: serviceexpert + group: "Networking and Services" + label: "Show Expert Config" + schema: + type: boolean + default: false + show_subquestions_if: true + subquestions: + - variable: hostNetwork + group: "Networking and Services" + label: "Host-Networking (Complicated)" + schema: + type: boolean + default: false + +# Include{serviceExpert} + +# Include{serviceList} + + - variable: persistence + label: "Integrated Persistent Storage" + description: "Integrated Persistent Storage" + group: "Storage and Persistence" + schema: + additional_attrs: true + type: dict + attrs: + - variable: data + label: "App Data Storage" + description: "Stores the Application Data." + schema: + additional_attrs: true + type: dict + attrs: +# Include{persistenceBasic} +# Include{persistenceAdvanced} +# Include{persistenceList} + + - variable: ingress + label: "" + group: "Ingress" + schema: + additional_attrs: true + type: dict + attrs: + - variable: main + label: "Main Ingress" + schema: + additional_attrs: true + type: dict + attrs: + +# Include{ingressDefault} + +# Include{ingressTLS} + +# Include{ingressTraefik} + +# Include{ingressExpert} + +# Include{ingressList} + +# Include{security} + + - variable: advancedSecurity + label: "Show Advanced Security Settings" + group: "Security and Permissions" + schema: + type: boolean + default: false + show_subquestions_if: true + subquestions: + - variable: securityContext + label: "Security Context" + schema: + additional_attrs: true + type: dict + attrs: + - variable: privileged + label: "Privileged mode" + schema: + type: boolean + default: false + - variable: readOnlyRootFilesystem + label: "ReadOnly Root Filesystem" + schema: + type: boolean + default: true + - variable: allowPrivilegeEscalation + label: "Allow Privilege Escalation" + schema: + type: boolean + default: false + - variable: runAsNonRoot + label: "runAsNonRoot" + schema: + type: boolean + default: true +# Include{securityContextAdvanced} + + - variable: podSecurityContext + group: "Security and Permissions" + label: "Pod Security Context" + schema: + additional_attrs: true + type: dict + attrs: + - variable: runAsUser + label: "runAsUser" + description: "The UserID of the user running the application" + schema: + type: int + default: 2999 + - variable: runAsGroup + label: "runAsGroup" + description: "The groupID this App of the user running the application" + schema: + type: int + default: 2999 + - variable: fsGroup + label: "fsGroup" + description: "The group that should own ALL storage." + schema: + type: int + default: 2999 +# Include{podSecurityContextAdvanced} + +# Include{resources} + +# Include{advanced} + +# Include{addons} diff --git a/charts/incubator/radicale/templates/_configmap.tpl b/charts/incubator/radicale/templates/_configmap.tpl new file mode 100644 index 00000000000..2a2f4d2e33d --- /dev/null +++ b/charts/incubator/radicale/templates/_configmap.tpl @@ -0,0 +1,160 @@ +{{/* + Radicale config file. See also default config files: + Kozea/Radicale https://github.com/Kozea/Radicale/blob/master/config + tomsquest/docker-radicale https://github.com/tomsquest/docker-radicale/blob/master/config +*/}} +{{- define "radicale.configmap" -}} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: radicale-config + labels: + {{- include "tc.common.labels" . | nindent 4 }} +data: + config: |- + # -*- mode: conf -*- + # vim:ft=cfg + + # Config file for Radicale - A simple calendar server + # + # Place it into /etc/radicale/config (global) + # or ~/.config/radicale/config (user) + # + # The current values are the default ones + + + [server] + + # CalDAV server hostnames separated by a comma + # IPv4 syntax: address:port + # IPv6 syntax: [address]:port + # For example: 0.0.0.0:9999, [::]:9999 + #hosts = localhost:5232 + hosts = 0.0.0.0:5232 + + # Max parallel connections + #max_connections = 8 + max_connections = {{ .Values.radicale.server.max_connections }} + + # Max size of request body (bytes) + #max_content_length = 100000000 + {{- /* + Multiply by 1, so large integers aren't rendered in scientific notation + See: https://github.com/helm/helm/issues/1707#issuecomment-1167860346 + */}} + max_content_length = {{ mul .Values.radicale.server.max_content_length 1 }} + + # Socket timeout (seconds) + #timeout = 30 + timeout = {{ .Values.radicale.server.timeout }} + + # SSL flag, enable HTTPS protocol + #ssl = False + + # SSL certificate path + #certificate = /etc/ssl/radicale.cert.pem + + # SSL private key + #key = /etc/ssl/radicale.key.pem + + # CA certificate for validating clients. This can be used to secure + # TCP traffic between Radicale and a reverse proxy + #certificate_authority = + + + [encoding] + + # Encoding for responding requests + #request = utf-8 + request = {{ .Values.radicale.encoding.request }} + + # Encoding for storing local collections + #stock = utf-8 + stock = {{ .Values.radicale.encoding.stock }} + + + [auth] + + # Authentication method + # Value: none | htpasswd | remote_user | http_x_remote_user + #type = none + + # Htpasswd filename + #htpasswd_filename = /etc/radicale/users + + # Htpasswd encryption method + # Value: plain | bcrypt | md5 + # bcrypt requires the installation of radicale[bcrypt]. + #htpasswd_encryption = md5 + htpasswd_encryption = bcrypt + + # Incorrect authentication delay (seconds) + #delay = 1 + delay = {{ .Values.radicale.auth.delay }} + + # Message displayed in the client when a password is needed + #realm = Radicale - Password Required + realm = {{ .Values.radicale.auth.realm }} + + [rights] + + # Rights backend + # Value: none | authenticated | owner_only | owner_write | from_file + #type = owner_only + type = {{ .Values.radicale.rights.type }} + + # File for rights management from_file + #file = /etc/radicale/rights + + + [storage] + + # Storage backend + # Value: multifilesystem | multifilesystem_nolock + #type = multifilesystem + type = {{ .Values.radicale.storage.type }} + + # Folder for storing local collections, created if not present + #filesystem_folder = /var/lib/radicale/collections + filesystem_folder = /data/collections + + # Delete sync token that are older (seconds) + #max_sync_token_age = 2592000 + {{- /* + Multiply by 1, so large integers aren't rendered in scientific notation + See: https://github.com/helm/helm/issues/1707#issuecomment-1167860346 + */}} + max_sync_token_age = {{ mul .Values.radicale.storage.max_sync_token_age 1 }} + + # Command that is run after changes to storage + # Example: ([ -d .git ] || git init) && git add -A && (git diff --cached --quiet || git commit -m "Changes by "%(user)s) + #hook = + + + [web] + + # Web interface backend + # Value: none | internal + #type = internal + type = {{ .Values.radicale.web.type }} + + + [logging] + + # Threshold for the logger + # Value: debug | info | warning | error | critical + #level = warning + level = {{ .Values.radicale.logging.level }} + + # Don't include passwords in logs + #mask_passwords = True + mask_passwords = {{ .Values.radicale.logging.mask_passwords | ternary "True" "False" }} + + + [headers] + + # Additional HTTP headers + #Access-Control-Allow-Origin = * + +{{- end }} diff --git a/charts/incubator/radicale/templates/_secret.tpl b/charts/incubator/radicale/templates/_secret.tpl new file mode 100644 index 00000000000..1f46c8690ba --- /dev/null +++ b/charts/incubator/radicale/templates/_secret.tpl @@ -0,0 +1,16 @@ +{{/* Radicale htpasswd file */}} +{{- define "radicale.secret" -}} +--- +apiVersion: v1 +kind: Secret +metadata: + name: radicale-secret + labels: + {{- include "tc.common.labels" . | nindent 4 }} +stringData: + users: |- + {{- range .Values.radicale.auth.users }} + {{ htpasswd .username .password }} + {{- end }} + +{{- end }} diff --git a/charts/incubator/radicale/templates/common.yaml b/charts/incubator/radicale/templates/common.yaml new file mode 100644 index 00000000000..3bb40a834af --- /dev/null +++ b/charts/incubator/radicale/templates/common.yaml @@ -0,0 +1,11 @@ +{{/* Make sure all variables are set properly */}} +{{- include "tc.common.loader.init" . }} + +{{/* Render config map for radicale */}} +{{- include "radicale.configmap" . }} + +{{/* Render secret for radicale */}} +{{- include "radicale.secret" . }} + +{{/* Render the templates */}} +{{ include "tc.common.loader.apply" . }} diff --git a/charts/incubator/radicale/values.yaml b/charts/incubator/radicale/values.yaml new file mode 100644 index 00000000000..745087090eb --- /dev/null +++ b/charts/incubator/radicale/values.yaml @@ -0,0 +1,83 @@ +image: + repository: tomsquest/docker-radicale + pullPolicy: IfNotPresent + tag: 3.1.7.0@sha256:ffc26d09d9ef85a477b9f22aa2f08bf35af8ddbee7bfbba192df7c567e21ebb5 + +# Docker image configuration docs: +# https://github.com/tomsquest/docker-radicale#custom-configuration + +# Radicale configuration docs: +# https://radicale.org/v3.html#configuration + +radicale: + server: + max_connections: 8 + max_content_length: 100000000 + timeout: 30 + encoding: + request: utf-8 + stock: utf-8 + auth: + delay: 1 + realm: Radicale - Password Required + users: [] + rights: + type: owner_only + storage: + type: multifilesystem + max_sync_token_age: 2592000 + web: + type: internal + logging: + level: warning + mask_passwords: true + +security: + PUID: 2999 + +securityContext: + capabilities: + drop: + - ALL + add: + - SETUID + - SETGID + - KILL + +podSecurityContext: + runAsUser: 2999 + runAsGroup: 2999 + fsGroup: 2999 + +env: + # Skip chown on /data by entrypoint.sh + TAKE_FILE_OWNERSHIP: false + +service: + main: + protocol: HTTP + ports: + main: + targetPort: 5232 + port: 10255 + +persistence: + config: + enabled: true + type: custom + readOnly: true + volumeSpec: + configMap: + name: radicale-config + users: + enabled: true + type: custom + readOnly: true + mountPath: /etc/radicale/users + subPath: users + volumeSpec: + secret: + secretName: radicale-secret + data: + enabled: true + mountPath: /data