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
This commit is contained in:
Michael Schnerring 2022-07-02 16:56:41 +00:00 committed by GitHub
parent 0bf8e4b0ba
commit 5af697722d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 795 additions and 0 deletions

View File

@ -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

View File

@ -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}

View File

@ -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 }}

View File

@ -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 }}

View File

@ -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" . }}

View File

@ -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