diff --git a/.github/workflows/charts_tests.yaml b/.github/workflows/charts_tests.yaml index 65b99e3e55..640fc768f4 100644 --- a/.github/workflows/charts_tests.yaml +++ b/.github/workflows/charts_tests.yaml @@ -20,7 +20,7 @@ jobs: helm-version: - v3.9.4 - v3.10.3 - - v3.11.3 + - v3.12.0 steps: - name: Checkout uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3 @@ -87,7 +87,7 @@ jobs: # We run tests on Helm version of latest SCALE release, SCALE nightly and manually defined "latest" helm-version: - v3.9.4 - - v3.11.3 + - v3.12.0 # We run tests on k3s version of latest SCALE release, SCALE nightly and manually defined "latest" k3s-version: - v1.25.3+k3s1 diff --git a/.github/workflows/common_library_tests.yaml b/.github/workflows/common_library_tests.yaml index a8c9c7fc50..cde33cb063 100644 --- a/.github/workflows/common_library_tests.yaml +++ b/.github/workflows/common_library_tests.yaml @@ -19,7 +19,7 @@ jobs: helm-version: - v3.9.4 - v3.10.3 - - v3.11.1 + - v3.12.0 steps: - name: Checkout uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3 @@ -57,7 +57,7 @@ jobs: helm-version: - v3.9.4 - v3.10.3 - - v3.11.1 + - v3.12.0 steps: - name: Checkout uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3 @@ -97,7 +97,7 @@ jobs: - v1.25.3+k3s1 # We run tests on Helm version of latest SCALE release, SCALE nightly and manually defined "latest" helm-version: - - v3.11.1 + - v3.12.0 values: - basic-values.yaml - configmap-values.yaml diff --git a/library/common-test/tests/pod/securityContext.yaml b/library/common-test/tests/pod/securityContext.yaml index 6da8baaaa3..6fcae6982b 100644 --- a/library/common-test/tests/pod/securityContext.yaml +++ b/library/common-test/tests/pod/securityContext.yaml @@ -220,6 +220,43 @@ tests: - name: net.ipv4.ip_unprivileged_port_start value: "443" + - it: should pass with sysctls net.ipv4.ip_unprivileged_port_start NOT appended with hostnet + set: + workload: + workload-name1: + enabled: true + primary: true + type: Deployment + podSpec: + hostNetwork: true + workload-name2: + enabled: true + type: Deployment + podSpec: {} + service: + service-name: + enabled: true + primary: true + type: ClusterIP + targetSelector: workload-name2 + ports: + port-name: + enabled: true + primary: true + port: 443 + asserts: + - documentIndex: &deploymentDoc 0 + isKind: + of: Deployment + - documentIndex: *deploymentDoc + equal: + path: spec.template.spec.securityContext + value: + fsGroup: 568 + fsGroupChangePolicy: OnRootMismatch + supplementalGroups: [] + sysctls: [] + - it: should pass with fsGroup 0 set: securityContext: diff --git a/library/common/Chart.yaml b/library/common/Chart.yaml index a1d497477e..82c1183773 100644 --- a/library/common/Chart.yaml +++ b/library/common/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: common description: A library chart for iX Official Catalog type: library -version: 1.0.7 +version: 1.0.8 appVersion: v1 annotations: title: Common Library Chart diff --git a/library/common/templates/app_functions/_permissions.tpl b/library/common/templates/app_functions/_permissions.tpl index afbc8d8f26..da7370eabf 100644 --- a/library/common/templates/app_functions/_permissions.tpl +++ b/library/common/templates/app_functions/_permissions.tpl @@ -10,6 +10,7 @@ GID: GID to change permissions to {{- $type := .type | default "install" -}} {{- $containerName := .containerName | default "permissions" -}} {{- $mode := .mode | default "always" -}} + {{- $chmod := .chmod | default "" -}} {{- $UID := .UID -}} {{- $GID := .GID -}} @@ -47,6 +48,9 @@ GID: GID to change permissions to capabilities: add: - CHOWN + {{- if $chmod }} + - FOWNER + {{- end }} command: bash args: - -c @@ -57,30 +61,51 @@ GID: GID to change permissions to continue fi - echo "Current Permissions on ["$dir"]:" - stat -c "%u %g" "$dir" + echo "Current Ownership and Permissions on ["$dir"]:" + echo "chown: $(stat -c "%u %g" "$dir")" + echo "chmod: $(stat -c "%a" "$dir")" {{- if eq $mode "check" }} {{/* If mode is check, check parent dir */}} if [ $(stat -c %u "$dir") -eq {{ $UID }} ] && [ $(stat -c %g "$dir") -eq {{ $GID }} ]; then - echo "Permissions are correct. Skipping..." - fix_perms="false" + echo "Ownership is correct. Skipping..." + fix_owner="false" else - echo "Permissions are incorrect. Fixing..." - fix_perms="true" + echo "Ownership is incorrect. Fixing..." + fix_owner="true" fi + {{- if $chmod }} {{/* Only if chmod value is given */}} + if [ $(stat -c %a "$dir") -eq {{ $chmod }} ]; then + echo "Permissions are correct. Skipping..." + fix_perms="false" + else + echo "Permissions are incorrect. Fixing..." + fix_perms="true" + fi + {{- end }} + {{- else if eq $mode "always" }} {{/* If mode is always, always fix perms */}} - + fix_owner="true" fix_perms="true" - {{- end }} - if [ "$fix_perms" = "true" ]; then + {{/* Apply changes */}} + if [ "$fix_owner" = "true" ]; then echo "Changing ownership to {{ $UID }}:{{ $GID }} on: ["$dir"]" chown -R {{ $UID }}:{{ $GID }} "$dir" echo "Finished changing ownership" - echo "Permissions after changing ownership:" + echo "Ownership after changes:" stat -c "%u %g" "$dir" fi + + {{- if $chmod }} {{/* Only if chmod value is given */}} + if [ "$fix_perms" = "true" ]; then + echo "Changing permissions to {{ $chmod }} on: ["$dir"]" + chmod -R {{ $chmod }} "$dir" + echo "Finished changing permissions" + echo "Permissions after changes:" + stat -c "%a" "$dir" + fi + {{- end }} done {{- end -}} diff --git a/library/common/templates/lib/pod/_podSecurityContext.tpl b/library/common/templates/lib/pod/_podSecurityContext.tpl index 9204db3413..9667619258 100644 --- a/library/common/templates/lib/pod/_podSecurityContext.tpl +++ b/library/common/templates/lib/pod/_podSecurityContext.tpl @@ -15,13 +15,13 @@ objectData: The object data to be used to render the Pod. {{/* Initialize from the "global" option */}} {{- $secContext := mustDeepCopy $rootCtx.Values.securityContext.pod -}} - {{/* Override with pod's option */}} + {{/* Override with pods option */}} {{- with $objectData.podSpec.securityContext -}} {{- $secContext = mustMergeOverwrite $secContext . -}} {{- end -}} {{/* TODO: Add supplemental groups - devices (5, 10, 20, 24) (Only when devices is assigned on the pod's containers) + devices (5, 10, 20, 24) (Only when devices is assigned on the pods containers) TODO: Unit Test the above cases */}} @@ -32,7 +32,7 @@ objectData: The object data to be used to render the Pod. {{- if mustHas $objectData.shortName ($GPUValues.targetSelector | keys) -}} {{- $gpuAdded = true -}} {{- end -}} - {{/* If there isn't a selector, but pod is primary */}} + {{/* If there isnt a selector, but pod is primary */}} {{- else if $objectData.primary -}} {{- $gpuAdded = true -}} {{- end -}} @@ -44,7 +44,9 @@ objectData: The object data to be used to render the Pod. {{- $portRange := fromJson (include "ix.v1.common.lib.helpers.securityContext.getPortRange" (dict "rootCtx" $rootCtx "objectData" $objectData)) -}} {{- if and $portRange.low (le (int $portRange.low) 1024) -}} {{/* If a container wants to bind a port <= 1024 change the unprivileged_port_start */}} - {{- $_ := set $secContext "sysctls" (mustAppend $secContext.sysctls (dict "name" "net.ipv4.ip_unprivileged_port_start" "value" (printf "%v" $portRange.low))) -}} + {{- if ne (include "ix.v1.common.lib.pod.hostNetwork" (dict "rootCtx" $rootCtx "objectData" $objectData)) "true" -}} + {{- $_ := set $secContext "sysctls" (mustAppend $secContext.sysctls (dict "name" "net.ipv4.ip_unprivileged_port_start" "value" (printf "%v" $portRange.low))) -}} + {{- end -}} {{- end -}} {{- if or (kindIs "invalid" $secContext.fsGroup) (eq (toString $secContext.fsGroup) "") -}} diff --git a/library/ix-dev/community/tftpd-hpa/Chart.lock b/library/ix-dev/community/tftpd-hpa/Chart.lock new file mode 100644 index 0000000000..15552d20da --- /dev/null +++ b/library/ix-dev/community/tftpd-hpa/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common + repository: file://../../../common + version: 1.0.8 +digest: sha256:254efaa1285f634b7a80b7baadeadbd20a680f7fee49d1d9d3c4618aa0d657ad +generated: "2023-05-15T16:04:33.567206466+03:00" diff --git a/library/ix-dev/community/tftpd-hpa/Chart.yaml b/library/ix-dev/community/tftpd-hpa/Chart.yaml new file mode 100644 index 0000000000..15963505c5 --- /dev/null +++ b/library/ix-dev/community/tftpd-hpa/Chart.yaml @@ -0,0 +1,26 @@ +name: tftpd-hpa +description: A lightweight tftp-server +annotations: + title: TFTP Server +type: application +version: 1.0.0 +apiVersion: v2 +appVersion: 1.0.0 +kubeVersion: '>=1.16.0-0' +maintainers: + - name: truenas + url: https://www.truenas.com/ + email: dev@ixsystems.com +dependencies: + - name: common + repository: file://../../../common + version: 1.0.8 +home: https://github.com/truenas/containers/tree/main/apps/tftpd-hpa +icon: https://avatars.githubusercontent.com/u/53482242 +sources: + - https://github.com/truenas/containers/tree/main/apps/tftpd-hpa + - https://github.com/truenas/charts/tree/master/community/tftpd-hpa + - https://hub.docker.com/r/ixsystems/tftpd-hpa +keywords: + - tftp + - netboot diff --git a/library/ix-dev/community/tftpd-hpa/README.md b/library/ix-dev/community/tftpd-hpa/README.md new file mode 100644 index 0000000000..48458faf5c --- /dev/null +++ b/library/ix-dev/community/tftpd-hpa/README.md @@ -0,0 +1,14 @@ +# TFTP + +[TFTP](https://manpages.debian.org/testing/tftpd-hpa/tftpd.8.en.html) is a server for the Trivial File Transfer Protocol. + +The app runs as `root` user and drops privileges to `tftp` (9069) user for the TFTP service. + +> On every application start, a container will be launched with **root** privileges. +> This will check the parent directory permissions and ownership. +> If there is a mismatch it will apply the correct permissions to the TFTP directories. +> When "Allow Create" is checked, the above container will also check and `chmod` if needed +> to `757` the TFTP directories and to `555` when not checked. +> Afterward, the `TFTP` container will run as a **root** user, dropping privileges +> to `tftp` (9069) user for the TFTP service. +> Note: You need to have configured DHCP server for network boot to work. diff --git a/library/ix-dev/community/tftpd-hpa/app-readme.md b/library/ix-dev/community/tftpd-hpa/app-readme.md new file mode 100644 index 0000000000..48458faf5c --- /dev/null +++ b/library/ix-dev/community/tftpd-hpa/app-readme.md @@ -0,0 +1,14 @@ +# TFTP + +[TFTP](https://manpages.debian.org/testing/tftpd-hpa/tftpd.8.en.html) is a server for the Trivial File Transfer Protocol. + +The app runs as `root` user and drops privileges to `tftp` (9069) user for the TFTP service. + +> On every application start, a container will be launched with **root** privileges. +> This will check the parent directory permissions and ownership. +> If there is a mismatch it will apply the correct permissions to the TFTP directories. +> When "Allow Create" is checked, the above container will also check and `chmod` if needed +> to `757` the TFTP directories and to `555` when not checked. +> Afterward, the `TFTP` container will run as a **root** user, dropping privileges +> to `tftp` (9069) user for the TFTP service. +> Note: You need to have configured DHCP server for network boot to work. diff --git a/library/ix-dev/community/tftpd-hpa/charts/common-1.0.8.tgz b/library/ix-dev/community/tftpd-hpa/charts/common-1.0.8.tgz new file mode 100644 index 0000000000..68abac5c10 Binary files /dev/null and b/library/ix-dev/community/tftpd-hpa/charts/common-1.0.8.tgz differ diff --git a/library/ix-dev/community/tftpd-hpa/ci/create-values.yaml b/library/ix-dev/community/tftpd-hpa/ci/create-values.yaml new file mode 100644 index 0000000000..cb5d2b9113 --- /dev/null +++ b/library/ix-dev/community/tftpd-hpa/ci/create-values.yaml @@ -0,0 +1,6 @@ +tftpStorage: + tftpboot: + type: hostPath + hostPath: /mnt/{{ .Release.Name }}/tftpboot +tftpConfig: + allowCreate: true diff --git a/library/ix-dev/community/tftpd-hpa/ci/readonly-values.yaml b/library/ix-dev/community/tftpd-hpa/ci/readonly-values.yaml new file mode 100644 index 0000000000..3b23db6b2a --- /dev/null +++ b/library/ix-dev/community/tftpd-hpa/ci/readonly-values.yaml @@ -0,0 +1,6 @@ +tftpStorage: + tftpboot: + type: hostPath + hostPath: /mnt/{{ .Release.Name }}/tftpboot +tftpConfig: + allowCreate: false diff --git a/library/ix-dev/community/tftpd-hpa/item.yaml b/library/ix-dev/community/tftpd-hpa/item.yaml new file mode 100644 index 0000000000..493c2eae43 --- /dev/null +++ b/library/ix-dev/community/tftpd-hpa/item.yaml @@ -0,0 +1,6 @@ +icon_url: https://avatars.githubusercontent.com/u/53482242 +screenshots: [] +categories: + - networking +tags: + - tftp diff --git a/library/ix-dev/community/tftpd-hpa/metadata.yaml b/library/ix-dev/community/tftpd-hpa/metadata.yaml new file mode 100644 index 0000000000..ff86e6ead8 --- /dev/null +++ b/library/ix-dev/community/tftpd-hpa/metadata.yaml @@ -0,0 +1,16 @@ +runAsContext: + - userName: root + groupName: root + gid: 0 + uid: 0 + description: TFTP requires root privileges to start it's processes. +capabilities: + - name: NET_BIND_SERVICE + description: TFTP requires this ability to bind to port 69 for TFTP. + - name: SETUID + description: TFTP requires this ability to switch user for sub-processes. + - name: SETGID + description: TFTP requires this ability to switch group for sub-processes. + - name: SYS_CHROOT + description: TFTP requires this ability to use chroot for it's sub-processes. +hostMounts: [] diff --git a/library/ix-dev/community/tftpd-hpa/questions.yaml b/library/ix-dev/community/tftpd-hpa/questions.yaml new file mode 100644 index 0000000000..8d852d747e --- /dev/null +++ b/library/ix-dev/community/tftpd-hpa/questions.yaml @@ -0,0 +1,155 @@ +groups: + - name: TFTP Configuration + description: Configure TFTP + - name: Network Configuration + description: Configure Network for TFTP + - name: Storage Configuration + description: Configure Storage for TFTP + - name: Resources Configuration + description: Configure Resources for TFTP + +questions: + - variable: TZ + group: TFTP Configuration + label: Timezone + schema: + type: string + default: Etc/UTC + required: true + $ref: + - definitions/timezone + + - variable: tftpConfig + label: "" + group: TFTP Configuration + schema: + type: dict + attrs: + - variable: allowCreate + label: Allow Create + description: | + Allow new files to be created. This is disabled by default.
+ Enabling this will change the permissions of the tftpboot directory to 757. + Disabling this will change the permissions of the tftpboot directory to 555. + schema: + type: boolean + default: false + - variable: additionalEnvs + label: Additional Environment Variables + description: Configure additional environment variables for TFTP. + schema: + type: list + default: [] + items: + - variable: env + label: Environment Variable + schema: + type: dict + attrs: + - variable: name + label: Name + schema: + type: string + required: true + - variable: value + label: Value + schema: + type: string + required: true + + - variable: tftpNetwork + label: "" + group: Network Configuration + schema: + type: dict + attrs: + - variable: hostNetwork + label: Host Network + description: | + Bind to the host network. It's recommended to keep this disabled.
+ schema: + type: boolean + default: true + - variable: tftpPort + label: TFTP Port + description: | + The port for TFTP. + When hostNetwork is enabled, the port tftp port will be 69. + schema: + type: int + default: 30000 + show_if: [["hostNetwork", "=", false]] + min: 9000 + max: 65535 + required: true + + - variable: tftpStorage + label: "" + group: Storage Configuration + schema: + type: dict + attrs: + - variable: tftpboot + label: TFTP Boot Storage + description: The path to store TFTP Boot files. + schema: + type: dict + attrs: + - variable: type + label: Type + description: | + ixVolume: Is dataset created automatically by the system.
+ Host Path: Is a path that already exists on the system. + schema: + type: string + required: true + default: "ixVolume" + enum: + - value: "hostPath" + description: Host Path (Path that already exists on the system) + - value: "ixVolume" + description: ixVolume (Dataset created automatically by the system) + - variable: datasetName + label: Dataset Name + schema: + type: string + show_if: [["type", "=", "ixVolume"]] + required: true + hidden: true + immutable: true + default: "tftpboot" + $ref: + - "normalize/ixVolume" + - variable: hostPath + label: Host Path + schema: + type: hostpath + show_if: [["type", "=", "hostPath"]] + immutable: true + required: true + + - variable: resources + group: Resources Configuration + label: "" + schema: + type: dict + attrs: + - variable: limits + label: Limits + schema: + type: dict + attrs: + - variable: cpu + label: CPU + description: CPU limit for TFTP. + schema: + type: string + default: "4000m" + required: true + - variable: memory + label: Memory + description: Memory limit for TFTP. + schema: + type: string + default: "8Gi" + required: true diff --git a/library/ix-dev/community/tftpd-hpa/templates/NOTES.txt b/library/ix-dev/community/tftpd-hpa/templates/NOTES.txt new file mode 100644 index 0000000000..ba4e01146c --- /dev/null +++ b/library/ix-dev/community/tftpd-hpa/templates/NOTES.txt @@ -0,0 +1 @@ +{{ include "ix.v1.common.lib.chart.notes" $ }} diff --git a/library/ix-dev/community/tftpd-hpa/templates/_tftp.tpl b/library/ix-dev/community/tftpd-hpa/templates/_tftp.tpl new file mode 100644 index 0000000000..fee1b44fc3 --- /dev/null +++ b/library/ix-dev/community/tftpd-hpa/templates/_tftp.tpl @@ -0,0 +1,102 @@ +{{- define "tftp.workload" -}} +workload: + tftp: + enabled: true + primary: true + type: Deployment + podSpec: + hostNetwork: {{ .Values.tftpNetwork.hostNetwork }} + containers: + tftp: + enabled: true + primary: true + imageSelector: image + securityContext: + runAsUser: 0 + runAsGroup: 0 + runAsNonRoot: false + readOnlyRootFilesystem: false + capabilities: + add: + - NET_BIND_SERVICE + - SETGID + - SETUID + - SYS_CHROOT + env: + MAPFILE: "" + SECURE: "1" + CREATE: {{ ternary "1" "0" .Values.tftpConfig.allowCreate | quote }} + fixedEnv: + UMASK: {{ ternary "020" "" .Values.tftpConfig.allowCreate | quote }} + {{ with .Values.tftpConfig.additionalEnvs }} + envList: + {{ range $env := . }} + - name: {{ $env.name }} + value: {{ $env.value }} + {{ end }} + {{ end }} + probes: + liveness: + enabled: true + type: exec + command: + - /bin/sh + - -c + - | + getent services tftp + readiness: + enabled: true + type: exec + command: + - /bin/sh + - -c + - | + getent services tftp + startup: + enabled: true + type: exec + command: + - /bin/sh + - -c + - | + getent services tftp + initContainers: + {{- include "ix.v1.common.app.permissions" (dict "containerName" "01-permissions" + "UID" 9069 + "GID" 9069 + "mode" "check" + "chmod" (ternary "757" "555" .Values.tftpConfig.allowCreate) + "type" "init") | nindent 8 }} + + +{{/* Service */}} +service: + tftp: + enabled: true + primary: true + type: NodePort + targetSelector: tftp + ports: + tftp: + enabled: true + primary: true + port: {{ .Values.tftpNetwork.tftpPort }} + nodePort: {{ .Values.tftpNetwork.tftpPort }} + targetPort: 69 + protocol: udp + targetSelector: tftp + +{{/* Persistence */}} +persistence: + tftpboot: + enabled: true + type: {{ .Values.tftpStorage.tftpboot.type }} + datasetName: {{ .Values.tftpStorage.tftpboot.datasetName | default "" }} + hostPath: {{ .Values.tftpStorage.tftpboot.hostPath | default "" }} + targetSelector: + tftp: + tftp: + mountPath: /tftpboot + 01-permissions: + mountPath: /mnt/directories/tftpboot +{{- end -}} diff --git a/library/ix-dev/community/tftpd-hpa/templates/common.yaml b/library/ix-dev/community/tftpd-hpa/templates/common.yaml new file mode 100644 index 0000000000..ff8b07ce11 --- /dev/null +++ b/library/ix-dev/community/tftpd-hpa/templates/common.yaml @@ -0,0 +1,6 @@ +{{- include "ix.v1.common.loader.init" . -}} + +{{/* Merge the templates with Values */}} +{{- $_ := mustMergeOverwrite .Values (include "tftp.workload" $ | fromYaml) -}} + +{{- include "ix.v1.common.loader.apply" . -}} diff --git a/library/ix-dev/community/tftpd-hpa/upgrade_info.json b/library/ix-dev/community/tftpd-hpa/upgrade_info.json new file mode 100644 index 0000000000..767388094a --- /dev/null +++ b/library/ix-dev/community/tftpd-hpa/upgrade_info.json @@ -0,0 +1 @@ +{"filename": "values.yaml", "keys": ["image"]} diff --git a/library/ix-dev/community/tftpd-hpa/upgrade_strategy b/library/ix-dev/community/tftpd-hpa/upgrade_strategy new file mode 100755 index 0000000000..aef37b7f05 --- /dev/null +++ b/library/ix-dev/community/tftpd-hpa/upgrade_strategy @@ -0,0 +1,30 @@ +#!/usr/bin/python3 +import json +import re +import sys + +from catalog_update.upgrade_strategy import semantic_versioning + +RE_STABLE_VERSION = re.compile(r'[1-9]+\.[0-9]+\.[0-9]+') + + +def newer_mapping(image_tags): + key = list(image_tags.keys())[0] + tags = {t: t for t in image_tags[key] if RE_STABLE_VERSION.fullmatch(t)} + version = semantic_versioning(list(tags)) + if not version: + return {} + + return { + 'tags': {key: tags[version]}, + 'app_version': version, + } + + +if __name__ == '__main__': + try: + versions_json = json.loads(sys.stdin.read()) + except ValueError: + raise ValueError('Invalid json specified') + + print(json.dumps(newer_mapping(versions_json))) diff --git a/library/ix-dev/community/tftpd-hpa/values.yaml b/library/ix-dev/community/tftpd-hpa/values.yaml new file mode 100644 index 0000000000..01b821b5cb --- /dev/null +++ b/library/ix-dev/community/tftpd-hpa/values.yaml @@ -0,0 +1,21 @@ +image: + repository: ixsystems/tftpd-hpa + pullPolicy: IfNotPresent + tag: '1.0.0' + +resources: + limits: + cpu: 4000m + memory: 8Gi + +tftpConfig: + allowCreate: true + additionalEnvs: [] +tftpNetwork: + hostNetwork: true + # Only used if hostNetwork is false + tftpPort: 30000 +tftpStorage: + tftpboot: + type: ixVolume + datasetName: tftpboot