├── .dockerignore ├── .github └── workflows │ └── helm_publish.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── __daemon__ ├── __main__.py ├── helm ├── .gitignore ├── .helmignore ├── Chart.yaml ├── README.md ├── requirements.yaml ├── templates │ ├── _helpers.tpl │ ├── configmap.yaml │ ├── daemonset.yaml │ ├── psp.yaml │ └── serviceaccount.yaml └── values.yaml ├── requirements.txt └── sysbindings.yaml /.dockerignore: -------------------------------------------------------------------------------- 1 | # Ignore everything 2 | ** 3 | 4 | # Except this 5 | !/requirements.txt 6 | !/*.py 7 | !/__daemon__ 8 | -------------------------------------------------------------------------------- /.github/workflows/helm_publish.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | tags: 5 | - 'v[0-9]+.[0-9]+.[0-9]+*' 6 | 7 | jobs: 8 | release: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - 13 | name: Extract tag name 14 | run: echo "X_TAG=${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV 15 | - 16 | name: Publish Helm charts 17 | uses: stefanprodan/helm-gh-pages@master 18 | with: 19 | token: ${{ secrets.HELM_PUBLISH_TOKEN }} 20 | charts_dir: . 21 | charts_url: https://charts.wallarm.com 22 | linting: off 23 | repository: helm-charts 24 | branch: main 25 | target_dir: sysbindings 26 | index_dir: . 27 | app_version: "${{ env.X_TAG }}" 28 | chart_version: "${{ env.X_TAG }}" 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | sysbindings.*.yaml 3 | sysbindings-*.tgz 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10-alpine3.16 2 | 3 | WORKDIR /opt/sysbindings 4 | COPY . . 5 | RUN pip install --no-cache --no-cache-dir \ 6 | -r requirements.txt && \ 7 | chmod 700 __main__.py __daemon__ && \ 8 | ln -s /opt/sysbindings/__daemon__ \ 9 | /usr/local/bin/sysbindings 10 | 11 | ENV SYSBINDINGS_CONFIG=/opt/sysbindings/sysbindings.yaml 12 | ENV LOGLEVEL=INFO 13 | 14 | ENTRYPOINT ["/usr/local/bin/python", "-u", "/opt/sysbindings"] 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Wallarm Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SysBindings Daemon 2 | 3 | Little toolkit for control the sysctl/sysfs bindings on Kubernetes Cluster on the fly 4 | and without unnecessary restarts of cluster or node pool. Allows to control managed 5 | and/or own-architected and/or own-managed clusters because uses only well-known 6 | techniques. 7 | 8 | ## Helm chart 9 | 10 | You are welcome to try our official Helm Registry! 11 | 12 | ```bash 13 | helm repo add wallarm https://charts.wallarm.com 14 | helm repo update 15 | helm search repo wallarm/sysbindings -l 16 | ``` 17 | 18 | ## CLI 19 | 20 | See `sysbindings --help` for details: 21 | 22 | ```bash 23 | usage: sysbindings [-h] [--config CONFIG] [--oneshot] [--loglevel LOGLEVEL] 24 | 25 | Little toolkit for control the sysctl/sysfs bindings on Kubernetes Cluster on the flyand 26 | without unnecessary restarts of cluster or node pool. Allows to control managed and/or 27 | own-architected and/or own-managed clusters because uses only well-knowntehniques. 28 | 29 | optional arguments: 30 | -h, --help show this help message and exit 31 | --config CONFIG use specified configuration file 32 | --oneshot just apply configuration and exit, no daemonize 33 | --loglevel LOGLEVEL log verbosity: DEBUG, INFO, WARNING or ERROR 34 | ``` 35 | 36 | ## Configuration 37 | 38 | See detailed example in the `sysbindings.yaml` file. 39 | 40 | ## Environment 41 | 42 | Use this environment variables for configuring script: 43 | 44 | ```bash 45 | LOGLEVEL=INFO 46 | SYSBINDINGS_CONFIG=/opt/sysbindings/sysbindings.yaml 47 | ``` 48 | 49 | See details in the `sysbindings.yaml` file. 50 | 51 | ## Arguments Priority 52 | 53 | CLI arguments have maximal priority, ENVs is secondary and config entries 54 | just final. 55 | -------------------------------------------------------------------------------- /__daemon__: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | exec /usr/local/bin/python -u /opt/sysbindings $@ -------------------------------------------------------------------------------- /__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 -u 2 | import os, sys 3 | import signal 4 | import logging 5 | import argparse 6 | import time 7 | import yaml 8 | import hashlib 9 | 10 | __first_run__ = None 11 | __exit_trigger__ = None 12 | 13 | def file_hash(filename): 14 | with open(filename, 'r') as fd: 15 | return hashlib.sha1(fd.read().encode("utf-8")).hexdigest() 16 | 17 | def get_content(filename, after=None, before=None): 18 | ret = {} 19 | with open(filename, 'r') as fd: 20 | for line in fd.readlines(): 21 | line = line.strip(' \n') 22 | if len(line) == 0: # skip blank 23 | continue 24 | if line[0] == '#': # skip commented 25 | continue 26 | 27 | # Parse entry 28 | parts = line.split('=') 29 | if len(parts) != 2: # skip wrong entry 30 | continue 31 | key = parts[0].strip(' ') 32 | value = parts[1].strip(' ') 33 | 34 | ret[key] = value 35 | return ret 36 | 37 | def put_content(filename, content, top_cap='### --- BEGIN SYSBINDINGS DAEMON --- ###', bottom_cap='### --- END SYSBINDINGS DAEMON --- ###'): 38 | new = '' 39 | 40 | # Save old file 41 | saved = [] 42 | try: 43 | with open(filename, 'r') as fd: 44 | saved = fd.readlines() 45 | except FileNotFoundError: 46 | saved = [] 47 | 48 | # Calculate block 49 | to_put = [] 50 | for k, v in content.items(): 51 | to_put.append('{}={}\n'.format(k, v)) 52 | 53 | # Find the place to replace 54 | replace_begin = -1 55 | replace_end = -1 56 | for i in range(0, len(saved)): 57 | line = saved[i] 58 | if len(line) < len(top_cap): 59 | continue 60 | if line[0:len(top_cap)] == top_cap: 61 | replace_begin = i 62 | for i in range(0, len(saved)): 63 | line = saved[i] 64 | if len(line) < len(bottom_cap): 65 | continue 66 | if line[0:len(bottom_cap)] == bottom_cap: 67 | replace_end = i 68 | if replace_begin < 0 and replace_end < 0: # Have not configured yet 69 | # Just put to end 70 | new = ''.join(saved) + '\n' + top_cap + '\n' + ''.join(to_put) + bottom_cap + '\n' 71 | else: 72 | # Replace 73 | new = ''.join(saved[:replace_begin] + [top_cap + '\n'] + to_put + [bottom_cap + '\n'] + saved[replace_end+1:]) 74 | 75 | with open(filename, 'w') as fd: 76 | fd.seek(0) 77 | fd.write(new) 78 | 79 | return new 80 | 81 | def check_valid_entry(entry, path, chroot='/'): 82 | chroot = chroot.strip('/') 83 | if chroot != "": 84 | chroot = '/' + chroot 85 | path = path.strip('/') 86 | if path != "": 87 | path = '/' + path 88 | binding = entry.replace('.', '/') 89 | fullpath = chroot + path + '/' 90 | if not os.path.isfile(fullpath + binding): 91 | logging.warning("skipping \"{}\": entry doesn't exists in \"{}\" tree".format(entry, fullpath)) 92 | return False 93 | return True 94 | 95 | def get_config(filename): 96 | ret = {} 97 | with open(filename, 'r') as fd: 98 | ret = yaml.load(fd, Loader=yaml.Loader) 99 | return ret 100 | 101 | def patch_and_validate_config(cli, config): 102 | # Patching 103 | if 'chroot_path' in list(config.keys()): 104 | chroot_path = config['chroot_path'] 105 | elif os.getenv('SYSBINDINGS_CHROOT_PATH'): 106 | chroot_path = os.getenv('SYSBINDINGS_CHROOT_PATH') 107 | else: 108 | chroot_path = '/sys' 109 | 110 | if 'sysctl_conf_path' in list(config.keys()): 111 | sysctl_conf_path = config['sysctl_conf_path'] 112 | elif os.getenv('SYSBINDINGS_SYSCTL_CONF_PATH'): 113 | sysctl_conf_path = os.getenv('SYSBINDINGS_SYSCTL_CONF_PATH') 114 | else: 115 | sysctl_conf_path = '/etc/sysctl.conf' 116 | 117 | if 'sysfs_conf_path' in list(config.keys()): 118 | sysfs_conf_path = config['sysfs_conf_path'] 119 | elif os.getenv('SYSBINDINGS_SYSFS_CONF_PATH'): 120 | sysfs_conf_path = os.getenv('SYSBINDINGS_SYSFS_CONF_PATH') 121 | else: 122 | sysfs_conf_path = '/etc/sysfs.conf' 123 | 124 | if 'sysctl_path' in list(config.keys()): 125 | sysctl_path = config['sysctl_path'] 126 | elif os.getenv('SYSBINDINGS_SYSCTL_PATH'): 127 | sysctl_path = os.getenv('SYSBINDINGS_SYSCTL_PATH') 128 | else: 129 | sysctl_path = '/proc/sys' 130 | 131 | if 'sysfs_path' in list(config.keys()): 132 | sysfs_path = config['sysfs_path'] 133 | elif os.getenv('SYSBINDINGS_SYSFS_PATH'): 134 | sysfs_path = os.getenv('SYSBINDINGS_SYSFS_PATH') 135 | else: 136 | sysfs_path = '/sys' 137 | 138 | if 'interval' in list(config.keys()): 139 | interval = config['interval'] 140 | elif os.getenv('SYSBINDINGS_INTERVAL'): 141 | interval = os.getenv('SYSBINDINGS_INTERVAL') 142 | else: 143 | interval = '60' 144 | try: 145 | interval = int(interval) 146 | except: 147 | if __first_run__: 148 | logging.warning("invalid interval \"{}\", fallback to default \"60\"".format(interval)) 149 | interval = 60 150 | 151 | # Validating 152 | verified_sysctls = dict([(x, y) for (x, y) in config['sysctl'].items() if check_valid_entry(x, sysctl_path, chroot=chroot_path)]) 153 | verified_sysfss = dict([(x, y) for (x, y) in config['sysfs'].items() if check_valid_entry(x, sysfs_path, chroot=chroot_path)]) 154 | 155 | # Reconstruct config 156 | return { 157 | 'sysctl': verified_sysctls, 158 | 'sysfs': verified_sysfss, 159 | 'chroot_path': chroot_path, 160 | 'sysctl_conf_path': sysctl_conf_path, 161 | 'sysfs_conf_path': sysfs_conf_path, 162 | 'sysctl_path': sysctl_path, 163 | 'sysfs_path': sysfs_path, 164 | 'interval': interval, 165 | } 166 | 167 | def reread_config(filename, config): 168 | new_config = get_config(filename) 169 | 170 | # Validating 171 | verified_sysctls = dict([(x, y) for (x, y) in new_config['sysctl'].items() if check_valid_entry(x, config['sysctl_path'], chroot=config['chroot_path'])]) 172 | verified_sysfss = dict([(x, y) for (x, y) in new_config['sysfs'].items() if check_valid_entry(x, config['sysfs_path'], chroot=config['chroot_path'])]) 173 | 174 | config['sysctl'] = verified_sysctls 175 | config['sysfs'] = verified_sysfss 176 | 177 | return config 178 | 179 | def apply(config): 180 | chroot = config['chroot_path'].strip('/') 181 | if chroot != "": 182 | chroot = '/' + chroot 183 | 184 | # Patching files 185 | put_content(chroot + '/' + config['sysctl_conf_path'].strip('/'), config['sysctl']) 186 | put_content(chroot + '/' + config['sysfs_conf_path'].strip('/'), config['sysfs']) 187 | 188 | # Apply bindings 189 | for fs in ['sysctl', 'sysfs']: 190 | path = config[fs + '_path'].strip('/') 191 | if path != "": 192 | path = '/' + path 193 | for k, v in config[fs].items(): 194 | if fs == 'sysctl': 195 | binding = k.replace('.', '/') 196 | else: 197 | binding = k 198 | fullpath = chroot + path + '/' 199 | with open(fullpath + binding, 'w') as fd: 200 | fd.write(str(v)) 201 | 202 | def sleep(interval): 203 | global __exit_trigger__ 204 | for i in range(0, interval): 205 | if not __exit_trigger__: 206 | time.sleep(1) 207 | 208 | def term_signal(num, frame): 209 | global __exit_trigger__ 210 | logging.info("stopping daemon") 211 | __exit_trigger__ = True 212 | 213 | def main(): 214 | global __first_run__ 215 | global __exit_trigger__ 216 | 217 | __first_run__ = True 218 | parser = argparse.ArgumentParser( 219 | prog='sysbindings', 220 | description='Little toolkit for control the sysctl/sysfs bindings on Kubernetes Cluster on the fly ' 221 | 'and without unnecessary restarts of cluster or node pool. Allows to control managed ' 222 | 'and/or own-architected and/or own-managed clusters because uses only well-known ' 223 | 'tehniques.', 224 | ) 225 | parser.add_argument('--config', help='use specified configuration file', action='store') 226 | parser.add_argument('--oneshot', help='just apply configuration and exit, no daemonize', action='store_true') 227 | parser.add_argument('--loglevel', help='log verbosity: DEBUG, INFO, WARNING or ERROR', action='store') 228 | 229 | cli = parser.parse_args().__dict__ 230 | 231 | loglevel = os.getenv('LOGLEVEL') 232 | if cli['loglevel'] != None: 233 | loglevel = cli['loglevel'] 234 | if loglevel == None: 235 | loglevel = 'WARNING' 236 | 237 | logging.basicConfig( 238 | format='%(asctime)s [%(levelname)s:%(name)s] %(message)s', 239 | level=str(loglevel).upper(), 240 | ) 241 | 242 | config_file = os.getenv('SYSBINDINGS_CONFIG') 243 | if cli['config'] != None: 244 | config_file = cli['config'] 245 | if config_file == None: 246 | config_file = '/opt/sysbindings/sysbindings.yaml' 247 | 248 | # Initial reading configuration 249 | config = get_config(config_file) 250 | 251 | # Rebuilding config 252 | config = patch_and_validate_config(cli, config) 253 | 254 | # Single run and exit 255 | if cli['oneshot']: 256 | logging.info("running in oneshot mode") 257 | apply(config) 258 | 259 | return 0 260 | 261 | __first_run__ = False 262 | 263 | # Prepare daemonization 264 | __exit_trigger__ = False 265 | signal.signal(signal.SIGTERM, term_signal) 266 | signal.signal(signal.SIGINT, term_signal) 267 | logging.info("starting daemon") 268 | if os.getpid() != 1: 269 | logging.info("daemon pid is {}".format(os.getpid())) 270 | 271 | # Daemonize 272 | config_file_hash = "" 273 | while not __exit_trigger__: 274 | new_config_file_hash = file_hash(config_file) 275 | if config_file_hash != new_config_file_hash: 276 | config_file_hash = new_config_file_hash 277 | config = reread_config(config_file, config) 278 | apply(config) 279 | 280 | # Wait for next round 281 | sleep(config['interval']) 282 | 283 | return 0 284 | 285 | if __name__ == "__main__": 286 | sys.exit(main()) 287 | -------------------------------------------------------------------------------- /helm/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | values.dev.yaml 3 | *.tar.gz 4 | *.tgz 5 | _alias.sh -------------------------------------------------------------------------------- /helm/.helmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | values.dev.yaml 3 | *.tgz 4 | 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | 14 | # Common backup files 15 | *.swp 16 | *.bak 17 | *.tmp 18 | *~ 19 | 20 | # Various IDEs 21 | .project 22 | .idea/ 23 | *.tmproj 24 | 25 | _alias.sh 26 | 27 | test*.yml 28 | test*.yaml 29 | -------------------------------------------------------------------------------- /helm/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: SysBindings Daemon 3 | keywords: 4 | - sysctl 5 | - sysfs 6 | - operations 7 | home: https://github.com/wallarm/sysbindings 8 | icon: https://static.wallarm.com/wallarm-logo.svg 9 | maintainers: 10 | - email: aburyndin@wallarm.com 11 | name: Andrey Buryndin 12 | name: sysbindings 13 | sources: 14 | - https://github.com/wallarm/sysbindings 15 | version: 0.1.0 16 | appVersion: 0.1.0 17 | -------------------------------------------------------------------------------- /helm/README.md: -------------------------------------------------------------------------------- 1 | # SysBindings Daemon 2 | 3 | Little toolkit for control the sysctl/sysfs bindings on Kubernetes Cluster on the fly 4 | and without unnecessary restarts of cluster or node pool. Allows to control managed 5 | and/or own-architected and/or own-managed clusters because uses only well-known 6 | techniques. 7 | 8 | ## Helm chart 9 | 10 | You are welcome to try our official Helm Registry! 11 | 12 | ```bash 13 | helm repo add wallarm https://charts.wallarm.com 14 | helm repo update 15 | helm search repo wallarm/sysbindings -l 16 | ``` 17 | 18 | ## Configuration 19 | 20 | See comments in the `values.yaml` file. 21 | 22 | For configure the daemon itself, see the `.config` option in the `values.yaml` file. 23 | -------------------------------------------------------------------------------- /helm/requirements.yaml: -------------------------------------------------------------------------------- 1 | dependencies: [] 2 | -------------------------------------------------------------------------------- /helm/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | */}} 13 | {{- define "fullname" -}} 14 | {{- $name := default .Chart.Name .Values.nameOverride -}} 15 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 16 | {{- end -}} 17 | 18 | {{- define "image" -}} 19 | {{- $tag := .Values.image.tag | default (printf "v%s" .Chart.AppVersion) -}} 20 | {{- if .registry -}} 21 | {{- printf "%s/%s:%s" .Values.image.registry .Values.image.repository $tag -}} 22 | {{- else -}} 23 | {{- printf "%s:%s" .Values.image.repository $tag -}} 24 | {{- end -}} 25 | {{- end -}} 26 | -------------------------------------------------------------------------------- /helm/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: '{{ template "fullname" . }}' 5 | labels: 6 | app: '{{ template "fullname" . }}' 7 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 8 | release: "{{ .Release.Name }}" 9 | heritage: "{{ .Release.Service }}" 10 | data: 11 | sysbindings.yaml: |- 12 | {{ .Values.config | nindent 4 }} 13 | -------------------------------------------------------------------------------- /helm/templates/daemonset.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: DaemonSet 3 | metadata: 4 | name: '{{ template "fullname" . }}' 5 | labels: 6 | app: '{{ template "fullname" . }}' 7 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 8 | release: "{{ .Release.Name }}" 9 | heritage: "{{ .Release.Service }}" 10 | spec: 11 | revisionHistoryLimit: 3 12 | updateStrategy: 13 | rollingUpdate: 14 | maxUnavailable: 1 15 | type: RollingUpdate 16 | selector: 17 | matchLabels: 18 | app: '{{ template "fullname" . }}' 19 | release: "{{ .Release.Name }}" 20 | template: 21 | metadata: 22 | labels: 23 | app: '{{ template "fullname" . }}' 24 | release: "{{ .Release.Name }}" 25 | spec: 26 | serviceAccount: {{ template "fullname" . }} 27 | affinity: {{ .Values.affinity | toYaml | nindent 8 }} 28 | tolerations: {{ .Values.tolerations | toYaml | nindent 8 }} 29 | {{ if ne (len .Values.image.pullSecrets) 0 -}} 30 | imagePullSecrets: 31 | {{- range .Values.image.pullSecrets }} 32 | - name: {{ . }} 33 | {{- end }} 34 | {{ end -}} 35 | containers: 36 | - name: sysbindings 37 | image: {{ template "image" . }} 38 | imagePullPolicy: {{ .Values.image.pullPolicy }} 39 | args: 40 | - --config 41 | - /etc/sysbindings/sysbindings.yaml 42 | {{ if .Values.loglevel -}} 43 | - --loglevel 44 | - {{ .Values.loglevel }} 45 | {{- end }} 46 | resources: {{ .Values.resources | toYaml | nindent 12 }} 47 | securityContext: 48 | privileged: false 49 | capabilities: 50 | add: 51 | - SYS_ADMIN 52 | - NET_ADMIN 53 | volumeMounts: 54 | - name: config 55 | mountPath: /etc/sysbindings 56 | readOnly: true 57 | - name: host-proc 58 | mountPath: /hostroot/proc/sys 59 | - name: host-sys 60 | mountPath: /hostroot/sys 61 | - name: host-etc 62 | mountPath: /hostroot/etc 63 | restartPolicy: Always 64 | hostPID: true 65 | hostIPC: true 66 | hostNetwork: true 67 | securityContext: 68 | runAsUser: 0 69 | runAsGroup: 0 70 | fsGroup: 0 71 | terminationGracePeriodSeconds: 5 72 | volumes: 73 | - name: config 74 | configMap: 75 | name: {{ template "fullname" . }} 76 | - name: host-proc 77 | hostPath: 78 | path: /proc/sys 79 | - name: host-sys 80 | hostPath: 81 | path: /sys 82 | - name: host-etc 83 | hostPath: 84 | path: /etc 85 | -------------------------------------------------------------------------------- /helm/templates/psp.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.psp.enable -}} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: '{{ template "fullname" . }}-psp' 6 | labels: 7 | app: '{{ template "fullname" . }}' 8 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 9 | release: "{{ .Release.Name }}" 10 | heritage: "{{ .Release.Service }}" 11 | rules: 12 | - apiGroups: ['policy'] 13 | resources: ['podsecuritypolicies'] 14 | verbs: ['use'] 15 | resourceNames: 16 | - '{{ template "fullname" . }}' 17 | 18 | --- 19 | apiVersion: rbac.authorization.k8s.io/v1 20 | kind: RoleBinding 21 | metadata: 22 | name: '{{ template "fullname" . }}-psp' 23 | labels: 24 | app: '{{ template "fullname" . }}' 25 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 26 | release: "{{ .Release.Name }}" 27 | heritage: "{{ .Release.Service }}" 28 | roleRef: 29 | apiGroup: rbac.authorization.k8s.io 30 | kind: Role 31 | name: '{{ template "fullname" . }}-psp' 32 | subjects: 33 | - kind: ServiceAccount 34 | name: '{{ template "fullname" . }}' 35 | namespace: '{{ .Release.Namespace }}' 36 | 37 | --- 38 | apiVersion: policy/v1beta1 39 | kind: PodSecurityPolicy 40 | metadata: 41 | name: '{{ template "fullname" . }}' 42 | labels: 43 | app: '{{ template "fullname" . }}' 44 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 45 | release: "{{ .Release.Name }}" 46 | heritage: "{{ .Release.Service }}" 47 | spec: 48 | privileged: false 49 | allowedCapabilities: 50 | - SYS_ADMIN 51 | - NET_ADMIN 52 | allowPrivilegeEscalation: true 53 | volumes: 54 | - configMap 55 | - emptyDir 56 | - projected 57 | - secret 58 | - downwardAPI 59 | - hostPath 60 | hostNetwork: true 61 | hostIPC: true 62 | hostPID: true 63 | runAsUser: 64 | rule: 'RunAsAny' 65 | seLinux: 66 | rule: 'RunAsAny' 67 | supplementalGroups: 68 | rule: 'RunAsAny' 69 | fsGroup: 70 | rule: 'RunAsAny' 71 | readOnlyRootFilesystem: false 72 | allowedHostPaths: 73 | - pathPrefix: "/proc/sys" 74 | readOnly: false 75 | - pathPrefix: "/sys" 76 | readOnly: false 77 | - pathPrefix: "/etc" 78 | readOnly: false 79 | {{- end }} 80 | -------------------------------------------------------------------------------- /helm/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: '{{ template "fullname" . }}' 5 | labels: 6 | app: '{{ template "fullname" . }}' 7 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 8 | release: "{{ .Release.Name }}" 9 | heritage: "{{ .Release.Service }}" 10 | -------------------------------------------------------------------------------- /helm/values.yaml: -------------------------------------------------------------------------------- 1 | 2 | image: 3 | registry: "" 4 | repository: wallarm/sysbindings 5 | tag: "" # appVersion if empty 6 | pullPolicy: IfNotPresent 7 | 8 | pullSecrets: [] 9 | ## Optionally specify an array of imagePullSecrets. 10 | ## Secrets must be manually created in the namespace. 11 | ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ 12 | ## 13 | # pullSecrets: 14 | # - myRegistryKeySecretName 15 | 16 | config: |- 17 | # This is an example of configuration 18 | 19 | ## Syntax is same as common linux sysctl.conf file, but presents map (dictionary) 20 | ## See man: https://man7.org/linux/man-pages/man8/sysctl.8.html 21 | ## 22 | ## Use '.' (dot) as path separator. 23 | ## 24 | ## Automaticaly reload if changed 25 | ## 26 | sysctl: {} 27 | # sysctl: 28 | # vm.swappiness: 10 29 | # vm.dirty_ratio: 60 30 | # fs.file-max: 2097152 31 | # net.netfilter.nf_conntrack_max: 1048576 32 | 33 | ## Syntax is same as debian's origin "sysfsutils" package, but presents map (dictionary) 34 | ## See man: https://man7.org/linux/man-pages/man5/sysctl.conf.5.html 35 | ## 36 | ## Keep in mind: use '/' as path separator instead '.'! This is for 37 | ## compatibility with origin controller package. 38 | ## 39 | ## Automaticaly reload if changed 40 | ## 41 | sysfs: {} 42 | # sysfs: 43 | # block/nvme0n1/queue/scheduler: mq-deadline 44 | # block/nvme1n1/queue/scheduler: mq-deadline 45 | 46 | ## Can be overrided by "SYSBINDINGS_CHROOT_PATH" env 47 | ## 48 | chroot_path: '/hostroot' 49 | 50 | ## Can be overrided by "SYSBINDINGS_SYSCTL_CONF_PATH" env 51 | ## 52 | sysctl_conf_path: /etc/sysctl.conf 53 | 54 | ## Can be overrided by "SYSBINDINGS_SYSFS_CONF_PATH" env 55 | ## 56 | sysfs_conf_path: /etc/sysfs.conf 57 | 58 | ## Can be overrided by "SYSBINDINGS_SYSCTL_PATH" env 59 | ## 60 | sysctl_path: /proc/sys 61 | 62 | ## Can be overrided by "SYSBINDINGS_SYSFS_PATH" env 63 | ## 64 | sysfs_path: /sys 65 | 66 | ## Can be overrided by "SYSBINDINGS_INTERVAL" env 67 | ## 68 | interval: 60 69 | 70 | loglevel: WARNING 71 | 72 | resources: {} 73 | 74 | affinity: {} 75 | tolerations: [] 76 | 77 | psp: 78 | enable: false 79 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | PyYAML>=6.0 2 | -------------------------------------------------------------------------------- /sysbindings.yaml: -------------------------------------------------------------------------------- 1 | # This is an example of configuration 2 | 3 | ## Syntax is same as common linux sysctl.conf file, but presents map (dictionary) 4 | ## See man: https://man7.org/linux/man-pages/man8/sysctl.8.html 5 | ## 6 | ## Use '.' (dot) as path separator. 7 | ## 8 | ## Automaticaly reload if changed 9 | ## 10 | sysctl: 11 | vm.swappiness: 10 12 | vm.dirty_ratio: 60 13 | fs.file-max: 2097152 14 | net.netfilter.nf_conntrack_max: 1048576 15 | 16 | ## Syntax is same as debian's origin "sysfsutils" package, but presents map (dictionary) 17 | ## See man: https://man7.org/linux/man-pages/man5/sysctl.conf.5.html 18 | ## 19 | ## Keep in mind: use '/' as path separator instead '.'! This is for 20 | ## compatibility with origin controller package. 21 | ## 22 | ## Automaticaly reload if changed 23 | ## 24 | sysfs: 25 | block/nvme0n1/queue/scheduler: mq-deadline 26 | block/nvme1n1/queue/scheduler: mq-deadline 27 | 28 | ## Can be overrided by "SYSBINDINGS_CHROOT_PATH" env 29 | ## 30 | chroot_path: '/hostroot' 31 | 32 | ## Can be overrided by "SYSBINDINGS_SYSCTL_CONF_PATH" env 33 | ## 34 | sysctl_conf_path: /etc/sysctl.conf 35 | 36 | ## Can be overrided by "SYSBINDINGS_SYSFS_CONF_PATH" env 37 | ## 38 | sysfs_conf_path: /etc/sysfs.conf 39 | 40 | ## Can be overrided by "SYSBINDINGS_SYSCTL_PATH" env 41 | ## 42 | sysctl_path: /proc/sys 43 | 44 | ## Can be overrided by "SYSBINDINGS_SYSFS_PATH" env 45 | ## 46 | sysfs_path: /sys 47 | 48 | ## Can be overrided by "SYSBINDINGS_INTERVAL" env 49 | ## 50 | interval: 60 51 | --------------------------------------------------------------------------------