jina verze
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
#mta.krumax.cz
|
||||
#api.krumax.cz
|
||||
#us1.krumax.cz
|
||||
us2.krumax.cz
|
||||
us3.krumax.cz
|
||||
us4.krumax.cz
|
||||
us5.krumax.cz
|
||||
us6.krumax.cz
|
||||
us7.krumax.cz
|
||||
us8.krumax.cz
|
||||
us9.krumax.cz
|
||||
us10.krumax.cz
|
||||
us11.krumax.cz
|
||||
us12.krumax.cz
|
||||
us13.krumax.cz
|
||||
us14.krumax.cz
|
||||
us15.krumax.cz
|
||||
us16.krumax.cz
|
||||
us17.krumax.cz
|
||||
us18.krumax.cz
|
||||
us19.krumax.cz
|
||||
us20.krumax.cz
|
||||
us21.krumax.cz
|
||||
us22.krumax.cz
|
||||
us23.krumax.cz
|
||||
us24.krumax.cz
|
||||
us25.krumax.cz
|
||||
us26.krumax.cz
|
||||
us27.krumax.cz
|
||||
us28.krumax.cz
|
||||
us29.krumax.cz
|
||||
us30.krumax.cz
|
||||
us31.krumax.cz
|
||||
us32.krumax.cz
|
||||
us33.krumax.cz
|
||||
us34.krumax.cz
|
||||
us35.krumax.cz
|
||||
us36.krumax.cz
|
||||
us37.krumax.cz
|
||||
us38.krumax.cz
|
||||
us39.krumax.cz
|
||||
us40.krumax.cz
|
||||
us41.krumax.cz
|
||||
us42.krumax.cz
|
||||
us43.krumax.cz
|
||||
us44.krumax.cz
|
||||
us45.krumax.cz
|
||||
us46.krumax.cz
|
||||
us47.krumax.cz
|
||||
us48.krumax.cz
|
||||
us49.krumax.cz
|
||||
@@ -0,0 +1,4 @@
|
||||
apiVersion: v2
|
||||
name: stack
|
||||
version: 0.1.0
|
||||
type: application
|
||||
@@ -0,0 +1,16 @@
|
||||
profiles:
|
||||
mariadbMaster:
|
||||
cpuRequest: 1000m
|
||||
cpuLimit: 4000m
|
||||
threadPoolSize: 4
|
||||
mariadbSlave:
|
||||
cpuRequest: 250m
|
||||
cpuLimit: 1000m
|
||||
threadPoolSize: 1
|
||||
redis:
|
||||
cpuRequest: 250m
|
||||
cpuLimit: 1000m
|
||||
maxClients: 20000
|
||||
openlitespeed:
|
||||
cpuRequest: 3000m
|
||||
cpuLimit: 7000m
|
||||
@@ -0,0 +1,16 @@
|
||||
profiles:
|
||||
mariadbMaster:
|
||||
cpuRequest: 2000m
|
||||
cpuLimit: 8000m
|
||||
threadPoolSize: 6
|
||||
mariadbSlave:
|
||||
cpuRequest: 500m
|
||||
cpuLimit: 2000m
|
||||
threadPoolSize: 1
|
||||
redis:
|
||||
cpuRequest: 750m
|
||||
cpuLimit: 4000m
|
||||
maxClients: 30000
|
||||
openlitespeed:
|
||||
cpuRequest: 5000m
|
||||
cpuLimit: 12000m
|
||||
@@ -0,0 +1,16 @@
|
||||
profiles:
|
||||
mariadbMaster:
|
||||
cpuRequest: 500m
|
||||
cpuLimit: 1500m
|
||||
threadPoolSize: 2
|
||||
mariadbSlave:
|
||||
cpuRequest: 100m
|
||||
cpuLimit: 500m
|
||||
threadPoolSize: 1
|
||||
redis:
|
||||
cpuRequest: 100m
|
||||
cpuLimit: 500m
|
||||
maxClients: 8000
|
||||
openlitespeed:
|
||||
cpuRequest: 750m
|
||||
cpuLimit: 2000m
|
||||
@@ -0,0 +1,16 @@
|
||||
profiles:
|
||||
mariadbMaster:
|
||||
cpuRequest: 750m
|
||||
cpuLimit: 2500m
|
||||
threadPoolSize: 3
|
||||
mariadbSlave:
|
||||
cpuRequest: 200m
|
||||
cpuLimit: 750m
|
||||
threadPoolSize: 1
|
||||
redis:
|
||||
cpuRequest: 150m
|
||||
cpuLimit: 750m
|
||||
maxClients: 12000
|
||||
openlitespeed:
|
||||
cpuRequest: 1250m
|
||||
cpuLimit: 3000m
|
||||
@@ -0,0 +1,26 @@
|
||||
profiles:
|
||||
mariadbMaster:
|
||||
memoryRequest: 28Gi
|
||||
memoryLimit: 40Gi
|
||||
maxConnections: 600
|
||||
innodbBufferPoolSize: 30G
|
||||
innodbBufferPoolInstances: 16
|
||||
tableOpenCache: 16000
|
||||
tableOpenCacheInstances: 16
|
||||
tmpTableSize: 64M
|
||||
mariadbSlave:
|
||||
memoryRequest: 8Gi
|
||||
memoryLimit: 12Gi
|
||||
maxConnections: 50
|
||||
innodbBufferPoolSize: 8G
|
||||
innodbBufferPoolInstances: 8
|
||||
tableOpenCache: 8000
|
||||
tableOpenCacheInstances: 8
|
||||
tmpTableSize: 64M
|
||||
redis:
|
||||
memoryRequest: 2Gi
|
||||
memoryLimit: 5Gi
|
||||
maxmemory: 3500mb
|
||||
openlitespeed:
|
||||
memoryRequest: 8Gi
|
||||
memoryLimit: 24Gi
|
||||
@@ -0,0 +1,26 @@
|
||||
profiles:
|
||||
mariadbMaster:
|
||||
memoryRequest: 2Gi
|
||||
memoryLimit: 4Gi
|
||||
maxConnections: 80
|
||||
innodbBufferPoolSize: 2G
|
||||
innodbBufferPoolInstances: 2
|
||||
tableOpenCache: 2000
|
||||
tableOpenCacheInstances: 4
|
||||
tmpTableSize: 8M
|
||||
mariadbSlave:
|
||||
memoryRequest: 512Mi
|
||||
memoryLimit: 1Gi
|
||||
maxConnections: 30
|
||||
innodbBufferPoolSize: 512M
|
||||
innodbBufferPoolInstances: 1
|
||||
tableOpenCache: 1000
|
||||
tableOpenCacheInstances: 2
|
||||
tmpTableSize: 8M
|
||||
redis:
|
||||
memoryRequest: 128Mi
|
||||
memoryLimit: 512Mi
|
||||
maxmemory: 350mb
|
||||
openlitespeed:
|
||||
memoryRequest: 2Gi
|
||||
memoryLimit: 4Gi
|
||||
@@ -0,0 +1,26 @@
|
||||
profiles:
|
||||
mariadbMaster:
|
||||
memoryRequest: 4Gi
|
||||
memoryLimit: 8Gi
|
||||
maxConnections: 150
|
||||
innodbBufferPoolSize: 5G
|
||||
innodbBufferPoolInstances: 5
|
||||
tableOpenCache: 4000
|
||||
tableOpenCacheInstances: 8
|
||||
tmpTableSize: 16M
|
||||
mariadbSlave:
|
||||
memoryRequest: 1Gi
|
||||
memoryLimit: 2Gi
|
||||
maxConnections: 50
|
||||
innodbBufferPoolSize: 1G
|
||||
innodbBufferPoolInstances: 1
|
||||
tableOpenCache: 2000
|
||||
tableOpenCacheInstances: 4
|
||||
tmpTableSize: 16M
|
||||
redis:
|
||||
memoryRequest: 256Mi
|
||||
memoryLimit: 1Gi
|
||||
maxmemory: 700mb
|
||||
openlitespeed:
|
||||
memoryRequest: 3Gi
|
||||
memoryLimit: 6Gi
|
||||
@@ -0,0 +1,26 @@
|
||||
profiles:
|
||||
mariadbMaster:
|
||||
memoryRequest: 8Gi
|
||||
memoryLimit: 16Gi
|
||||
maxConnections: 250
|
||||
innodbBufferPoolSize: 10G
|
||||
innodbBufferPoolInstances: 10
|
||||
tableOpenCache: 8000
|
||||
tableOpenCacheInstances: 16
|
||||
tmpTableSize: 32M
|
||||
mariadbSlave:
|
||||
memoryRequest: 2Gi
|
||||
memoryLimit: 4Gi
|
||||
maxConnections: 50
|
||||
innodbBufferPoolSize: 2G
|
||||
innodbBufferPoolInstances: 2
|
||||
tableOpenCache: 4000
|
||||
tableOpenCacheInstances: 8
|
||||
tmpTableSize: 32M
|
||||
redis:
|
||||
memoryRequest: 512Mi
|
||||
memoryLimit: 2Gi
|
||||
maxmemory: 1500mb
|
||||
openlitespeed:
|
||||
memoryRequest: 8Gi
|
||||
memoryLimit: 16Gi
|
||||
@@ -0,0 +1,19 @@
|
||||
{{- if .Values.debugHelm }}
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata: { name: debug, namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
containers:
|
||||
- name: debug
|
||||
image: nicolaka/netshoot:latest
|
||||
command: ["sh","-c","sleep infinity"]
|
||||
stdin: true
|
||||
tty: true
|
||||
securityContext:
|
||||
capabilities:
|
||||
add: ["NET_RAW","NET_ADMIN"] # for tcpdump/mtr
|
||||
restartPolicy: Never
|
||||
|
||||
{{- end }}
|
||||
@@ -0,0 +1,299 @@
|
||||
{{- if .Values.mariadbHelm }}
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata: { name: "{{ .Values.mariadbMaster }}-config", namespace: "{{ .Values.namespace }}" }
|
||||
data:
|
||||
replication.cnf: |
|
||||
[mysqld]
|
||||
skip_name_resolve=1
|
||||
server-id=1
|
||||
|
||||
# --- log ---
|
||||
log_bin=mysql-bin
|
||||
binlog_format=ROW
|
||||
binlog_expire_logs_seconds=604800
|
||||
max_binlog_total_size=32212254720
|
||||
|
||||
# --- durability ---
|
||||
innodb_flush_log_at_trx_commit=1
|
||||
sync_binlog=1
|
||||
|
||||
# --- connection / thread pool ---
|
||||
max_connections={{ .Values.profiles.mariadbMaster.maxConnections }}
|
||||
thread_handling=pool-of-threads
|
||||
thread_pool_size={{ .Values.profiles.mariadbMaster.threadPoolSize }}
|
||||
thread_pool_max_threads=128
|
||||
thread_pool_stall_limit=500
|
||||
|
||||
innodb_buffer_pool_size={{ .Values.profiles.mariadbMaster.innodbBufferPoolSize }}
|
||||
innodb_buffer_pool_instances={{ .Values.profiles.mariadbMaster.innodbBufferPoolInstances }}
|
||||
innodb_flush_method=O_DIRECT
|
||||
innodb_flush_neighbors=0
|
||||
innodb_io_capacity=2000
|
||||
innodb_io_capacity_max=4000
|
||||
innodb_log_file_size=1G
|
||||
innodb_log_buffer_size=64M
|
||||
|
||||
# --- cache ---
|
||||
query_cache_type=0
|
||||
query_cache_size=0
|
||||
table_open_cache={{ .Values.profiles.mariadbMaster.tableOpenCache }}
|
||||
table_open_cache_instances={{ .Values.profiles.mariadbMaster.tableOpenCacheInstances }}
|
||||
table_definition_cache={{ .Values.profiles.mariadbMaster.tableOpenCache }}
|
||||
open_files_limit=65535
|
||||
|
||||
# --- buffers ---
|
||||
tmp_table_size={{ .Values.profiles.mariadbMaster.tmpTableSize }}
|
||||
max_heap_table_size={{ .Values.profiles.mariadbMaster.tmpTableSize }}
|
||||
sort_buffer_size=2M
|
||||
join_buffer_size=2M
|
||||
read_buffer_size=256K
|
||||
read_rnd_buffer_size=512K
|
||||
|
||||
# --- timeouts ---
|
||||
wait_timeout=60
|
||||
interactive_timeout=300
|
||||
|
||||
max_allowed_packet=64M
|
||||
|
||||
slow_query_log=1
|
||||
long_query_time=1
|
||||
slow_query_log_file=/var/lib/mysql/slow.log
|
||||
log_error=/var/lib/mysql/error.log
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata: { name: "{{ .Values.mariadbSlave }}-config", namespace: "{{ .Values.namespace }}" }
|
||||
data:
|
||||
replication.cnf: |
|
||||
[mysqld]
|
||||
skip_name_resolve=1
|
||||
server-id=2
|
||||
read_only=1
|
||||
|
||||
# --- log ---
|
||||
relay-log=relay-log
|
||||
relay_log_purge=1
|
||||
relay_log_recovery=1
|
||||
|
||||
# --- connection / thread pool ---
|
||||
max_connections={{ .Values.profiles.mariadbSlave.maxConnections }}
|
||||
thread_handling=pool-of-threads
|
||||
thread_pool_size={{ .Values.profiles.mariadbSlave.threadPoolSize }}
|
||||
thread_pool_max_threads=32
|
||||
thread_pool_stall_limit=500
|
||||
|
||||
# --- InnoDB ---
|
||||
innodb_buffer_pool_size={{ .Values.profiles.mariadbSlave.innodbBufferPoolSize }}
|
||||
innodb_buffer_pool_instances={{ .Values.profiles.mariadbSlave.innodbBufferPoolInstances }}
|
||||
innodb_flush_method=O_DIRECT
|
||||
innodb_flush_neighbors=0
|
||||
innodb_io_capacity=1000
|
||||
innodb_io_capacity_max=2000
|
||||
innodb_log_file_size=512M
|
||||
innodb_log_buffer_size=32M
|
||||
|
||||
# --- durability (for standby recovery replica) ---
|
||||
innodb_flush_log_at_trx_commit=1
|
||||
sync_binlog=1
|
||||
|
||||
# --- cache ---
|
||||
query_cache_type=0
|
||||
query_cache_size=0
|
||||
table_open_cache={{ .Values.profiles.mariadbSlave.tableOpenCache }}
|
||||
table_open_cache_instances={{ .Values.profiles.mariadbSlave.tableOpenCacheInstances }}
|
||||
table_definition_cache={{ .Values.profiles.mariadbSlave.tableOpenCache }}
|
||||
open_files_limit=65535
|
||||
|
||||
# --- buffers ---
|
||||
tmp_table_size={{ .Values.profiles.mariadbSlave.tmpTableSize }}
|
||||
max_heap_table_size={{ .Values.profiles.mariadbSlave.tmpTableSize }}
|
||||
sort_buffer_size=1M
|
||||
join_buffer_size=1M
|
||||
read_buffer_size=256K
|
||||
read_rnd_buffer_size=512K
|
||||
|
||||
# --- timeouts ---
|
||||
wait_timeout=60
|
||||
interactive_timeout=300
|
||||
|
||||
max_allowed_packet=64M
|
||||
|
||||
# --- logs ---
|
||||
slow_query_log=0
|
||||
log_error=/var/lib/mysql/error.log
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata: { name: "{{ .Values.mariadbMaster }}-pv" }
|
||||
spec:
|
||||
capacity: { storage: 100Gi }
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteOnce ]
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
hostPath: { path: "{{ .Values.mariadbMasterPath }}", type: DirectoryOrCreate }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata: { name: "{{ .Values.mariadbMaster }}-pvc", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
volumeName: "{{ .Values.mariadbMaster }}-pv"
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteOnce ]
|
||||
resources: { requests: { storage: 100Gi } }
|
||||
storageClassName: ""
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata: { name: "{{ .Values.mariadbSlave }}-pv" }
|
||||
spec:
|
||||
capacity: { storage: 100Gi }
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteOnce ]
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
hostPath: { path: "{{ .Values.mariadbSlavePath }}", type: DirectoryOrCreate }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata: { name: "{{ .Values.mariadbSlave }}-pvc", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
volumeName: "{{ .Values.mariadbSlave }}-pv"
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteOnce ]
|
||||
resources: { requests: { storage: 100Gi } }
|
||||
storageClassName: ""
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata: { name: "{{ .Values.mariadbMaster }}", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
serviceName: "{{ .Values.mariadbMaster }}-headless"
|
||||
replicas: 1
|
||||
selector: { matchLabels: { app: "{{ .Values.mariadbMaster }}" } }
|
||||
template:
|
||||
metadata: { labels: { app: "{{ .Values.mariadbMaster }}" } }
|
||||
spec:
|
||||
terminationGracePeriodSeconds: 60
|
||||
containers:
|
||||
- name: "{{ .Values.mariadbMaster }}"
|
||||
image: mariadb:12.2
|
||||
resources:
|
||||
requests: { cpu: "{{ .Values.profiles.mariadbMaster.cpuRequest }}", memory: "{{ .Values.profiles.mariadbMaster.memoryRequest }}" }
|
||||
limits: { cpu: "{{ .Values.profiles.mariadbMaster.cpuLimit }}", memory: "{{ .Values.profiles.mariadbMaster.memoryLimit }}" }
|
||||
ports:
|
||||
- { containerPort: 3306 }
|
||||
env:
|
||||
- { name: MARIADB_ROOT_PASSWORD, valueFrom: { secretKeyRef: { name: "{{ .Values.mariadb }}-secret", key: root-password } } }
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["sh","-lc",'mariadb-admin ping -h 127.0.0.1 -uroot -p"$MARIADB_ROOT_PASSWORD" --silent']
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 6
|
||||
livenessProbe:
|
||||
exec:
|
||||
command: ["sh","-lc",'mariadb-admin ping -h 127.0.0.1 -uroot -p"$MARIADB_ROOT_PASSWORD" --silent']
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 6
|
||||
volumeMounts:
|
||||
- { name: "{{ .Values.mariadbMaster }}-volume", mountPath: /var/lib/mysql }
|
||||
- { name: "{{ .Values.mariadbMaster }}-config-volume", mountPath: /etc/mysql/conf.d/replication.cnf, subPath: replication.cnf }
|
||||
volumes:
|
||||
- name: "{{ .Values.mariadbMaster }}-volume"
|
||||
persistentVolumeClaim: { claimName: "{{ .Values.mariadbMaster }}-pvc" }
|
||||
- name: "{{ .Values.mariadbMaster }}-config-volume"
|
||||
configMap: { name: "{{ .Values.mariadbMaster }}-config" }
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata: { name: "{{ .Values.mariadbSlave }}", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
serviceName: "{{ .Values.mariadbSlave }}-headless"
|
||||
replicas: 1
|
||||
selector: { matchLabels: { app: "{{ .Values.mariadbSlave }}" } }
|
||||
template:
|
||||
metadata: { labels: { app: "{{ .Values.mariadbSlave }}" } }
|
||||
spec:
|
||||
terminationGracePeriodSeconds: 60
|
||||
containers:
|
||||
- name: "{{ .Values.mariadbSlave }}"
|
||||
image: mariadb:12.2
|
||||
resources:
|
||||
requests: { cpu: "{{ .Values.profiles.mariadbSlave.cpuRequest }}", memory: "{{ .Values.profiles.mariadbSlave.memoryRequest }}" }
|
||||
limits: { cpu: "{{ .Values.profiles.mariadbSlave.cpuLimit }}", memory: "{{ .Values.profiles.mariadbSlave.memoryLimit }}" }
|
||||
ports:
|
||||
- { containerPort: 3306 }
|
||||
env:
|
||||
- { name: MARIADB_ROOT_PASSWORD, valueFrom: { secretKeyRef: { name: "{{ .Values.mariadb }}-secret", key: root-password } } }
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["sh","-lc",'mariadb-admin ping -h 127.0.0.1 -uroot -p"$MARIADB_ROOT_PASSWORD" --silent']
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 6
|
||||
livenessProbe:
|
||||
exec:
|
||||
command: ["sh","-lc",'mariadb-admin ping -h 127.0.0.1 -uroot -p"$MARIADB_ROOT_PASSWORD" --silent']
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 6
|
||||
volumeMounts:
|
||||
- { name: "{{ .Values.mariadbSlave }}-volume", mountPath: /var/lib/mysql }
|
||||
- { name: "{{ .Values.mariadbSlave }}-config-volume", mountPath: /etc/mysql/conf.d/replication.cnf, subPath: replication.cnf }
|
||||
volumes:
|
||||
- name: "{{ .Values.mariadbSlave }}-volume"
|
||||
persistentVolumeClaim: { claimName: "{{ .Values.mariadbSlave }}-pvc" }
|
||||
- name: "{{ .Values.mariadbSlave }}-config-volume"
|
||||
configMap: { name: "{{ .Values.mariadbSlave }}-config" }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata: { name: "{{ .Values.mariadbMaster }}", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
selector: { app: "{{ .Values.mariadbMaster }}" }
|
||||
ports: [ { name: mysql, protocol: TCP, port: 3306, targetPort: 3306 } ]
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata: { name: "{{ .Values.mariadbSlave }}", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
selector: { app: "{{ .Values.mariadbSlave }}" }
|
||||
ports: [ { name: mysql, protocol: TCP, port: 3306, targetPort: 3306 } ]
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata: { name: "{{ .Values.mariadbMaster }}-headless", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
clusterIP: None
|
||||
selector: { app: "{{ .Values.mariadbMaster }}" }
|
||||
ports:
|
||||
- { name: mysql, protocol: TCP, port: 3306, targetPort: 3306 }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata: { name: "{{ .Values.mariadbSlave }}-headless", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
clusterIP: None
|
||||
selector: { app: "{{ .Values.mariadbSlave }}" }
|
||||
ports:
|
||||
- { name: mysql, protocol: TCP, port: 3306, targetPort: 3306 }
|
||||
|
||||
{{- end }}
|
||||
@@ -0,0 +1,128 @@
|
||||
{{- if .Values.metricHelm }}
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata: { name: "{{ .Values.metric }}-node-exporter", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
selector: { app: "{{ .Values.metric }}-node-exporter" }
|
||||
ports: [ { name: metrics, protocol: TCP, port: 9100, targetPort: 9100 } ]
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata: { name: "{{ .Values.metric }}-node-exporter", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
selector:
|
||||
matchLabels: { app: "{{ .Values.metric }}-node-exporter" }
|
||||
template:
|
||||
metadata:
|
||||
labels: { app: "{{ .Values.metric }}-node-exporter" }
|
||||
spec:
|
||||
hostNetwork: true
|
||||
hostPID: true
|
||||
dnsPolicy: ClusterFirstWithHostNet
|
||||
tolerations:
|
||||
- operator: "Exists"
|
||||
containers:
|
||||
- name: "{{ .Values.metric }}-node-exporter"
|
||||
image: quay.io/prometheus/node-exporter:v1.8.2
|
||||
args:
|
||||
- --web.listen-address=:9100
|
||||
- --path.procfs=/host/proc
|
||||
- --path.sysfs=/host/sys
|
||||
- --path.rootfs=/host/root
|
||||
- --collector.textfile.directory={{ .Values.metricPromPath }}
|
||||
ports: [ { containerPort: 9100, hostPort: 9100, name: metrics } ]
|
||||
resources:
|
||||
requests: { cpu: "10m", memory: "32Mi" }
|
||||
limits: { cpu: "150m", memory: "200Mi" }
|
||||
securityContext:
|
||||
readOnlyRootFilesystem: true
|
||||
allowPrivilegeEscalation: false
|
||||
volumeMounts:
|
||||
- { name: proc, mountPath: /host/proc, readOnly: true }
|
||||
- { name: sys, mountPath: /host/sys, readOnly: true }
|
||||
- { name: root, mountPath: /host/root, readOnly: true }
|
||||
- { name: textfile, mountPath: "{{ .Values.metricPromPath }}", readOnly: true }
|
||||
volumes:
|
||||
- name: proc
|
||||
hostPath: { path: /proc, type: Directory }
|
||||
- name: sys
|
||||
hostPath: { path: /sys, type: Directory }
|
||||
- name: root
|
||||
hostPath: { path: /, type: Directory }
|
||||
- name: textfile
|
||||
hostPath: { path: "{{ .Values.metricPromPath }}", type: DirectoryOrCreate }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata: { name: "{{ .Values.metric }}-vmagent", namespace: "{{ .Values.namespace }}" }
|
||||
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata: { name: "{{ .Values.metric }}-vmagent" }
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes", "nodes/proxy", "services", "endpoints", "pods"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["discovery.k8s.io"]
|
||||
resources: ["endpointslices"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata: { name: "{{ .Values.metric }}-vmagent" }
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: "{{ .Values.metric }}-vmagent"
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: "{{ .Values.metric }}-vmagent"
|
||||
namespace: "{{ .Values.namespace }}"
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata: { name: "{{ .Values.metric }}-vmagent", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
selector: { app: "{{ .Values.metric }}-vmagent" }
|
||||
ports: [ { name: http, protocol: TCP, port: 8429, targetPort: 8429 } ]
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata: { name: "{{ .Values.metric }}-vmagent", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels: { app: "{{ .Values.metric }}-vmagent" }
|
||||
template:
|
||||
metadata:
|
||||
labels: { app: "{{ .Values.metric }}-vmagent" }
|
||||
spec:
|
||||
serviceAccountName: "{{ .Values.metric }}-vmagent"
|
||||
containers:
|
||||
- name: "{{ .Values.metric }}-vmagent"
|
||||
image: victoriametrics/vmagent:v1.112.0
|
||||
args:
|
||||
- -promscrape.config=/etc/vmagent/vmagent.yml
|
||||
- -httpListenAddr=:8429
|
||||
- -loggerLevel=INFO
|
||||
- -remoteWrite.url={{ .Values.metricApiUrl }}
|
||||
- -remoteWrite.label=server={{ .Values.namespace }}
|
||||
ports: [ { containerPort: 8429, name: http } ]
|
||||
resources:
|
||||
requests: { cpu: "30m", memory: "128Mi" }
|
||||
limits: { cpu: "300m", memory: "512Mi" }
|
||||
volumeMounts:
|
||||
- { name: "{{ .Values.metric }}-vmagent-config-volume", mountPath: /etc/vmagent/vmagent.yml, subPath: vmagent.yml, readOnly: true }
|
||||
volumes:
|
||||
- name: "{{ .Values.metric }}-vmagent-config-volume"
|
||||
hostPath: { path: "{{ .Values.metricVmagentPath }}", type: DirectoryOrCreate }
|
||||
|
||||
{{- end }}
|
||||
@@ -0,0 +1,301 @@
|
||||
{{- if .Values.openlitespeedHelm }}
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata: { name: "{{ .Values.openlitespeed }}-vhosts-pv" }
|
||||
spec:
|
||||
capacity: { storage: 500Gi }
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteMany ]
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
hostPath: { path: "{{ .Values.vhostsPath }}", type: DirectoryOrCreate }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata: { name: "{{ .Values.openlitespeed }}-vhosts-pvc", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
volumeName: "{{ .Values.openlitespeed }}-vhosts-pv"
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteMany ]
|
||||
resources: { requests: { storage: 500Gi } }
|
||||
storageClassName: ""
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata: { name: "{{ .Values.openlitespeed }}-config-pv" }
|
||||
spec:
|
||||
capacity: { storage: 1Gi }
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteMany ]
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
hostPath: { path: "{{ .Values.openlitespeedConfigPath }}", type: DirectoryOrCreate }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata: { name: "{{ .Values.openlitespeed }}-config-pvc", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
volumeName: "{{ .Values.openlitespeed }}-config-pv"
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteMany ]
|
||||
resources: { requests: { storage: 1Gi } }
|
||||
storageClassName: ""
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata: { name: "{{ .Values.openlitespeed }}-admin-pv" }
|
||||
spec:
|
||||
capacity: { storage: 1Gi }
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteMany ]
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
hostPath: { path: "{{ .Values.openlitespeedAdminPath }}", type: DirectoryOrCreate }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata: { name: "{{ .Values.openlitespeed }}-admin-pvc", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
volumeName: "{{ .Values.openlitespeed }}-admin-pv"
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteMany ]
|
||||
resources: { requests: { storage: 1Gi } }
|
||||
storageClassName: ""
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata: { name: "{{ .Values.openlitespeed }}-phpini-pv" }
|
||||
spec:
|
||||
capacity: { storage: 1Gi }
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteMany ]
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
hostPath: { path: "{{ .Values.openlitespeedPhpIniPath }}", type: DirectoryOrCreate }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata: { name: "{{ .Values.openlitespeed }}-phpini-pvc", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
volumeName: "{{ .Values.openlitespeed }}-phpini-pv"
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteMany ]
|
||||
resources: { requests: { storage: 1Gi } }
|
||||
storageClassName: ""
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata: { name: "{{ .Values.openlitespeed }}-logs-pv" }
|
||||
spec:
|
||||
capacity: { storage: 10Gi }
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteMany ]
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
storageClassName: ""
|
||||
hostPath: { path: "{{ .Values.openlitespeedLogsPath }}", type: DirectoryOrCreate }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata: { name: "{{ .Values.openlitespeed }}-logs-pvc", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
volumeName: "{{ .Values.openlitespeed }}-logs-pv"
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteMany ]
|
||||
resources: { requests: { storage: 10Gi } }
|
||||
storageClassName: ""
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata: { name: "{{ .Values.openlitespeed }}-vhost-data-pv" }
|
||||
spec:
|
||||
capacity: { storage: 5Gi }
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteMany ]
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
hostPath: { path: "{{ .Values.openlitespeedVhostDataPath }}", type: DirectoryOrCreate }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata: { name: "{{ .Values.openlitespeed }}-vhost-data-pvc", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
volumeName: "{{ .Values.openlitespeed }}-vhost-data-pv"
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteMany ]
|
||||
resources: { requests: { storage: 5Gi } }
|
||||
storageClassName: ""
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata: { name: "{{ .Values.openlitespeed }}-vhost-shared-pv" }
|
||||
spec:
|
||||
capacity: { storage: 1Gi }
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteMany ]
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
hostPath: { path: "{{ .Values.openlitespeedVhostSharedPath }}", type: DirectoryOrCreate }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata: { name: "{{ .Values.openlitespeed }}-vhost-shared-pvc", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
volumeName: "{{ .Values.openlitespeed }}-vhost-shared-pv"
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteMany ]
|
||||
resources: { requests: { storage: 1Gi } }
|
||||
storageClassName: ""
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata: { name: "{{ .Values.openlitespeed }}", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
replicas: 2
|
||||
strategy: { type: RollingUpdate, rollingUpdate: { maxSurge: 0, maxUnavailable: 1 } }
|
||||
selector: { matchLabels: { app: "{{ .Values.openlitespeed }}" } }
|
||||
template:
|
||||
metadata: { labels: { app: "{{ .Values.openlitespeed }}" } }
|
||||
spec:
|
||||
initContainers:
|
||||
- name: "{{ .Values.openlitespeed }}-init"
|
||||
image: litespeedtech/openlitespeed:1.8.5-lsphp85
|
||||
command: ["sh", "-c"]
|
||||
args:
|
||||
- |
|
||||
if [ ! -f /mnt/config/httpd_config.conf ]; then
|
||||
cp -a /usr/local/lsws/conf/. /mnt/config/
|
||||
fi
|
||||
if [ ! -f /mnt/admin/admin_config.conf ]; then
|
||||
cp -a /usr/local/lsws/admin/conf/. /mnt/admin/
|
||||
fi
|
||||
volumeMounts:
|
||||
- { name: "{{ .Values.openlitespeed }}-config-volume", mountPath: /mnt/config }
|
||||
- { name: "{{ .Values.openlitespeed }}-admin-volume", mountPath: /mnt/admin }
|
||||
shareProcessNamespace: true
|
||||
containers:
|
||||
- name: "{{ .Values.openlitespeed }}"
|
||||
image: litespeedtech/openlitespeed:1.8.5-lsphp85
|
||||
ports:
|
||||
- { containerPort: 80 }
|
||||
- { containerPort: 7080 }
|
||||
resources:
|
||||
requests: { cpu: "{{ .Values.profiles.openlitespeed.cpuRequest }}", memory: "{{ .Values.profiles.openlitespeed.memoryRequest }}" }
|
||||
limits: { cpu: "{{ .Values.profiles.openlitespeed.cpuLimit }}", memory: "{{ .Values.profiles.openlitespeed.memoryLimit }}" }
|
||||
readinessProbe: { tcpSocket: { port: 80 }, initialDelaySeconds: 5, periodSeconds: 5, failureThreshold: 3 }
|
||||
livenessProbe: { tcpSocket: { port: 80 }, initialDelaySeconds: 10, periodSeconds: 10, failureThreshold: 5 }
|
||||
volumeMounts:
|
||||
- { name: "{{ .Values.openlitespeed }}-vhosts-volume", mountPath: /var/www/vhosts }
|
||||
- { name: "{{ .Values.openlitespeed }}-config-volume", mountPath: /usr/local/lsws/conf }
|
||||
- { name: "{{ .Values.openlitespeed }}-admin-volume", mountPath: /usr/local/lsws/admin/conf }
|
||||
- { name: "{{ .Values.openlitespeed }}-phpini-volume", mountPath: /etc/ols-php-ini }
|
||||
- { name: "{{ .Values.openlitespeed }}-logs-volume", mountPath: /usr/local/lsws/logs }
|
||||
- { name: "{{ .Values.openlitespeed }}-cache-volume", mountPath: /usr/local/lsws/cachedata }
|
||||
- { name: "{{ .Values.openlitespeed }}-tmp-volume", mountPath: /tmp/lshttpd }
|
||||
- { name: "{{ .Values.openlitespeed }}-vhost-data-volume", mountPath: /var/www/data, readOnly: true }
|
||||
- { name: "{{ .Values.openlitespeed }}-vhost-shared-volume", mountPath: /var/www/shared, readOnly: true }
|
||||
volumes:
|
||||
- name: "{{ .Values.openlitespeed }}-vhosts-volume"
|
||||
persistentVolumeClaim: { claimName: "{{ .Values.openlitespeed }}-vhosts-pvc" }
|
||||
- name: "{{ .Values.openlitespeed }}-config-volume"
|
||||
persistentVolumeClaim: { claimName: "{{ .Values.openlitespeed }}-config-pvc" }
|
||||
- name: "{{ .Values.openlitespeed }}-admin-volume"
|
||||
persistentVolumeClaim: { claimName: "{{ .Values.openlitespeed }}-admin-pvc" }
|
||||
- name: "{{ .Values.openlitespeed }}-phpini-volume"
|
||||
persistentVolumeClaim: { claimName: "{{ .Values.openlitespeed }}-phpini-pvc" }
|
||||
- name: "{{ .Values.openlitespeed }}-logs-volume"
|
||||
persistentVolumeClaim: { claimName: "{{ .Values.openlitespeed }}-logs-pvc" }
|
||||
- name: "{{ .Values.openlitespeed }}-cache-volume"
|
||||
emptyDir: { sizeLimit: 100Gi }
|
||||
- name: "{{ .Values.openlitespeed }}-tmp-volume"
|
||||
emptyDir: { sizeLimit: 100Gi }
|
||||
- name: "{{ .Values.openlitespeed }}-vhost-data-volume"
|
||||
persistentVolumeClaim: { claimName: "{{ .Values.openlitespeed }}-vhost-data-pvc" }
|
||||
- name: "{{ .Values.openlitespeed }}-vhost-shared-volume"
|
||||
persistentVolumeClaim: { claimName: "{{ .Values.openlitespeed }}-vhost-shared-pvc" }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata: { name: "{{ .Values.openlitespeed }}", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
selector: { app: "{{ .Values.openlitespeed }}" }
|
||||
ports:
|
||||
- { name: http, protocol: TCP, port: 80, targetPort: 80 }
|
||||
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata: { name: "{{ .Values.openlitespeed }}-ingress", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
rules:
|
||||
- http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service: { name: "{{ .Values.openlitespeed }}", port: { number: 80 } }
|
||||
|
||||
# -------------------------
|
||||
# Admin panel
|
||||
# -------------------------
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata: { name: "{{ .Values.openlitespeed }}-admin", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
selector: { app: "{{ .Values.openlitespeed }}" }
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- { name: admin, protocol: TCP, port: 7080, targetPort: 7080 }
|
||||
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: MiddlewareTCP
|
||||
metadata: { name: "{{ .Values.openlitespeed }}-admin-allowlist", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
ipAllowList: { sourceRange: [ {{ .Values.openlitespeedAdminWhiteList }} ] }
|
||||
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRouteTCP
|
||||
metadata: { name: "{{ .Values.openlitespeed }}-admin", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
entryPoints: [ "olsadmin" ]
|
||||
routes:
|
||||
- match: HostSNI(`*`)
|
||||
middlewares:
|
||||
- name: "{{ .Values.openlitespeed }}-admin-allowlist"
|
||||
services:
|
||||
- name: "{{ .Values.openlitespeed }}-admin"
|
||||
port: 7080
|
||||
tls:
|
||||
passthrough: true
|
||||
|
||||
---
|
||||
apiVersion: helm.cattle.io/v1
|
||||
kind: HelmChartConfig
|
||||
metadata: { name: traefik, namespace: kube-system }
|
||||
spec:
|
||||
valuesContent: |-
|
||||
ports:
|
||||
olsadmin:
|
||||
port: 9443
|
||||
expose:
|
||||
default: true
|
||||
exposedPort: 9443
|
||||
protocol: TCP
|
||||
additionalArguments:
|
||||
- "--entryPoints.olsadmin.address=:9443/tcp"
|
||||
|
||||
{{- end }}
|
||||
@@ -0,0 +1,188 @@
|
||||
{{- if .Values.postfixHelm }}
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata: { name: "{{ .Values.postfix }}-tls", namespace: "{{ .Values.namespace }}" }
|
||||
type: kubernetes.io/tls
|
||||
data:
|
||||
tls.crt: {{ .Values.postfixTlsCrtB64 }}
|
||||
tls.key: {{ .Values.postfixTlsKeyB64 }}
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata: { name: "{{ .Values.postfix }}-sasl", namespace: "{{ .Values.namespace }}" }
|
||||
data:
|
||||
smtpd.conf: |
|
||||
pwcheck_method: auxprop
|
||||
auxprop_plugin: sasldb
|
||||
sasldb_path: /config/sasldb2
|
||||
mech_list: PLAIN LOGIN
|
||||
default_realm: {{ .Values.postfixDefaultRealm }}
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata: { name: "{{ .Values.postfix }}-config-pv" }
|
||||
spec:
|
||||
capacity: { storage: 1Gi }
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteOnce ]
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
hostPath: { path: "{{ .Values.postfixConfigPath }}", type: DirectoryOrCreate }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata: { name: "{{ .Values.postfix }}-dkim-pv" }
|
||||
spec:
|
||||
capacity: { storage: 1Gi }
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteOnce ]
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
hostPath: { path: "{{ .Values.postfixDkimPath }}", type: DirectoryOrCreate }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata: { name: "{{ .Values.postfix }}-config-pvc", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
volumeName: "{{ .Values.postfix }}-config-pv"
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteOnce ]
|
||||
resources: { requests: { storage: 1Gi } }
|
||||
storageClassName: ""
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata: { name: "{{ .Values.postfix }}-dkim-pvc", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
volumeName: "{{ .Values.postfix }}-dkim-pv"
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteOnce ]
|
||||
resources: { requests: { storage: 1Gi } }
|
||||
storageClassName: ""
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata: { name: "{{ .Values.postfix }}-queue-pvc", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources: { requests: { storage: 5Gi } }
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata: { name: "{{ .Values.postfix }}", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
replicas: 1
|
||||
selector: { matchLabels: { app: "{{ .Values.postfix }}" } }
|
||||
template:
|
||||
metadata: { labels: { app: "{{ .Values.postfix }}" } }
|
||||
spec:
|
||||
enableServiceLinks: false
|
||||
initContainers:
|
||||
- name: "{{ .Values.postfix }}-dkim-init"
|
||||
image: boky/postfix:4.4.0-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
command: ["/bin/sh", "-lc"]
|
||||
args:
|
||||
- |
|
||||
set -e
|
||||
if [ ! -f /dkim/opendkim.conf ]; then
|
||||
cp -a /etc/opendkim/* /dkim/
|
||||
fi
|
||||
touch /dkim/TrustedHosts /dkim/SigningTable /dkim/KeyTable
|
||||
chown opendkim:opendkim /dkim/TrustedHosts /dkim/KeyTable /dkim/SigningTable
|
||||
chmod 0644 /dkim/TrustedHosts /dkim/KeyTable /dkim/SigningTable
|
||||
printf '%s\n' 127.0.0.1 localhost 10.42.0.0/16 10.43.0.0/16 > /dkim/TrustedHosts
|
||||
|
||||
volumeMounts:
|
||||
- name: "{{ .Values.postfix }}-dkim-volume"
|
||||
mountPath: /dkim
|
||||
containers:
|
||||
- name: "{{ .Values.postfix }}"
|
||||
image: boky/postfix:4.4.0-alpine
|
||||
ports:
|
||||
- { containerPort: 25, name: smtp }
|
||||
- { containerPort: 587, name: submission }
|
||||
env:
|
||||
- { name: POSTFIX_myhostname, value: "{{ .Values.postfixHost }}" }
|
||||
- { name: POSTFIX_smtpd_banner, value: "$myhostname ESMTP" }
|
||||
- { name: POSTFIX_mynetworks, value: "127.0.0.0/8" }
|
||||
- { name: POSTFIX_inet_interfaces, value: "all" }
|
||||
|
||||
# Rules
|
||||
- { name: POSTFIX_smtpd_client_restrictions, value: "permit_mynetworks, permit_sasl_authenticated, reject" }
|
||||
- { name: POSTFIX_smtpd_relay_restrictions, value: "permit_sasl_authenticated, reject_unauth_destination" }
|
||||
- { name: POSTFIX_smtpd_sender_restrictions, value: "reject_non_fqdn_sender,reject_unknown_sender_domain,reject_sender_login_mismatch,permit_sasl_authenticated,reject_unauth_destination" }
|
||||
|
||||
# TLS
|
||||
- { name: POSTFIX_smtpd_tls_cert_file, value: "/etc/ssl/mail/tls.crt" }
|
||||
- { name: POSTFIX_smtpd_tls_key_file, value: "/etc/ssl/mail/tls.key" }
|
||||
- { name: POSTFIX_smtpd_tls_security_level, value: "may" }
|
||||
- { name: POSTFIX_smtp_tls_security_level, value: "may" }
|
||||
|
||||
# SASL (Cyrus, sasldb2)
|
||||
- { name: POSTFIX_smtpd_sasl_auth_enable, value: "yes" }
|
||||
- { name: POSTFIX_smtpd_sasl_type, value: "cyrus" }
|
||||
- { name: POSTFIX_smtpd_sasl_path, value: "smtpd" }
|
||||
- { name: POSTFIX_cyrus_sasl_config_path, value: "/etc/sasl2" }
|
||||
|
||||
# Maps (Virtual domain/alias and senders)
|
||||
- { name: POSTFIX_virtual_alias_domains, value: "lmdb:/config/{{ .Values.postfixDomainsFile }}" }
|
||||
- { name: POSTFIX_virtual_alias_maps, value: "lmdb:/config/{{ .Values.postfixAliasesFile }}" }
|
||||
- { name: POSTFIX_smtpd_sender_login_maps, value: "lmdb:/config/{{ .Values.postfixSendersFile }}" }
|
||||
|
||||
# DKIM
|
||||
- { name: DKIM_SELECTOR, value: "{{ .Values.postfixDkimSelector }}" }
|
||||
- { name: POSTFIX_smtpd_milters, value: "inet:localhost:8891" }
|
||||
- { name: POSTFIX_non_smtpd_milters, value: "$smtpd_milters" }
|
||||
- { name: POSTFIX_milter_default_action, value: "accept" }
|
||||
- { name: POSTFIX_milter_protocol, value: "6" }
|
||||
|
||||
# Other
|
||||
- { name: ALLOW_EMPTY_SENDER_DOMAINS, value: "true" }
|
||||
- { name: ALLOWED_SENDER_DOMAINS, value: "" }
|
||||
|
||||
volumeMounts:
|
||||
- { name: "{{ .Values.postfix }}-tls-volume", mountPath: /etc/ssl/mail, readOnly: true }
|
||||
- { name: "{{ .Values.postfix }}-sasl-volume", mountPath: /etc/sasl2 }
|
||||
- { name: "{{ .Values.postfix }}-config-volume", mountPath: /config }
|
||||
- { name: "{{ .Values.postfix }}-dkim-volume", mountPath: /etc/opendkim }
|
||||
- { name: "{{ .Values.postfix }}-dkim-volume", mountPath: /etc/opendkim/keys, subPath: keys }
|
||||
- { name: "{{ .Values.postfix }}-queue-volume", mountPath: /var/spool/postfix }
|
||||
volumes:
|
||||
- name: "{{ .Values.postfix }}-tls-volume"
|
||||
secret: { secretName: "{{ .Values.postfix }}-tls" }
|
||||
- name: "{{ .Values.postfix }}-sasl-volume"
|
||||
configMap: { name: "{{ .Values.postfix }}-sasl" }
|
||||
- name: "{{ .Values.postfix }}-config-volume"
|
||||
persistentVolumeClaim: { claimName: "{{ .Values.postfix }}-config-pvc" }
|
||||
- name: "{{ .Values.postfix }}-dkim-volume"
|
||||
persistentVolumeClaim: { claimName: "{{ .Values.postfix }}-dkim-pvc" }
|
||||
- name: "{{ .Values.postfix }}-queue-volume"
|
||||
persistentVolumeClaim: { claimName: "{{ .Values.postfix }}-queue-pvc" }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata: { name: "{{ .Values.postfix }}-public", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
externalTrafficPolicy: Local
|
||||
selector: { app: "{{ .Values.postfix }}" }
|
||||
ports: [ { name: smtp, port: 25, targetPort: 25 } ]
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata: { name: "{{ .Values.postfix }}", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
selector: { app: "{{ .Values.postfix }}" }
|
||||
ports: [ { name: submission, port: 587, targetPort: 587 } ]
|
||||
|
||||
{{- end }}
|
||||
@@ -0,0 +1,416 @@
|
||||
{{- if .Values.redisHelm }}
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata: { name: "{{ .Values.redis }}-config", namespace: "{{ .Values.namespace }}" }
|
||||
data:
|
||||
redis.conf: |
|
||||
bind 0.0.0.0
|
||||
port 6379
|
||||
dir /data
|
||||
|
||||
# --- ACL ---
|
||||
aclfile /data-acl/{{ .Values.redisFileUsersAcl }}
|
||||
|
||||
# --- all in one DB ---
|
||||
databases 1
|
||||
|
||||
# --- network ---
|
||||
protected-mode yes
|
||||
tcp-backlog 1024
|
||||
tcp-keepalive 300
|
||||
timeout 0
|
||||
|
||||
# --- connections ---
|
||||
maxclients {{ .Values.profiles.redis.maxClients }}
|
||||
|
||||
# --- memory (tune) ---
|
||||
maxmemory {{ .Values.profiles.redis.maxmemory }}
|
||||
maxmemory-policy allkeys-lfu
|
||||
maxmemory-samples 5
|
||||
maxmemory-eviction-tenacity 10
|
||||
maxmemory-clients 5%
|
||||
client-query-buffer-limit 256mb
|
||||
client-output-buffer-limit normal 0 0 0
|
||||
client-output-buffer-limit replica 256mb 64mb 60
|
||||
client-output-buffer-limit pubsub 32mb 8mb 60
|
||||
|
||||
# --- replication ---
|
||||
replica-serve-stale-data yes
|
||||
replica-read-only yes
|
||||
repl-backlog-size 64mb
|
||||
repl-backlog-ttl 3600
|
||||
min-replicas-to-write 0
|
||||
#min-replicas-max-lag 10
|
||||
|
||||
# --- persistence ---
|
||||
appendonly yes
|
||||
appendfsync everysec
|
||||
aof-rewrite-incremental-fsync yes
|
||||
rdb-save-incremental-fsync yes
|
||||
save ""
|
||||
|
||||
# --- log ---
|
||||
slowlog-log-slower-than 10000
|
||||
slowlog-max-len 128
|
||||
latency-monitor-threshold 0
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata: { name: "{{ .Values.redis }}-users-pv" }
|
||||
spec:
|
||||
capacity: { storage: 1Gi }
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteMany ]
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
hostPath: { path: "{{ .Values.redisPath }}", type: DirectoryOrCreate }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata: { name: "{{ .Values.redis }}-users-pvc", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
volumeName: "{{ .Values.redis }}-users-pv"
|
||||
volumeMode: Filesystem
|
||||
accessModes: [ ReadWriteMany ]
|
||||
resources: { requests: { storage: 1Gi } }
|
||||
storageClassName: ""
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata: { name: "{{ .Values.redis }}", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
serviceName: "{{ .Values.redis }}"
|
||||
replicas: 2
|
||||
selector: { matchLabels: { app: "{{ .Values.redis }}" } }
|
||||
persistentVolumeClaimRetentionPolicy: { whenDeleted: Delete, whenScaled: Retain }
|
||||
template:
|
||||
metadata: { labels: { app: "{{ .Values.redis }}" } }
|
||||
spec:
|
||||
terminationGracePeriodSeconds: 30
|
||||
initContainers:
|
||||
- name: init-redis-acl
|
||||
image: redis:8.6-alpine
|
||||
resources:
|
||||
requests: { cpu: "10m", memory: "32Mi" }
|
||||
limits: { cpu: "100m", memory: "128Mi" }
|
||||
env:
|
||||
- { name: POD_NAME, valueFrom: { fieldRef: { fieldPath: metadata.name } } }
|
||||
- { name: REDIS_PASSWORD, valueFrom: { secretKeyRef: { name: "{{ .Values.redis }}-secret", key: root-password } } }
|
||||
command: ["/bin/sh","-c"]
|
||||
args:
|
||||
- |
|
||||
set -eu
|
||||
ACL="/data-acl/{{ .Values.redisFileUsersAcl }}"
|
||||
HASH=$(printf '%s' "$REDIS_PASSWORD" | sha256sum | awk '{print $1}')
|
||||
mkdir -p /data-acl
|
||||
|
||||
if [ "$POD_NAME" = "{{ .Values.redis }}-0" ]; then
|
||||
tmp="${ACL}.tmp"
|
||||
|
||||
if [ -f "$ACL" ]; then
|
||||
awk '!(tolower($1)=="user" && $2=="default")' "$ACL" > "$tmp"
|
||||
else
|
||||
: > "$tmp"
|
||||
fi
|
||||
|
||||
# default user is used by replication auth and HAProxy health checks
|
||||
printf 'user default on sanitize-payload #%s ~* &* +@all\n' "$HASH" >> "$tmp"
|
||||
mv "$tmp" "$ACL"
|
||||
chmod 600 "$ACL"
|
||||
else
|
||||
i=0
|
||||
while [ ! -f "$ACL" ] && [ $i -lt 300 ]; do
|
||||
sleep 1
|
||||
i=$((i+1))
|
||||
done
|
||||
[ -f "$ACL" ] || exit 1
|
||||
fi
|
||||
volumeMounts:
|
||||
- { name: "{{ .Values.redis }}-users-volume", mountPath: /data-acl }
|
||||
containers:
|
||||
- name: "{{ .Values.redis }}"
|
||||
image: redis:8.6-alpine
|
||||
resources:
|
||||
requests: { cpu: "{{ .Values.profiles.redis.cpuRequest }}", memory: "{{ .Values.profiles.redis.memoryRequest }}" }
|
||||
limits: { cpu: "{{ .Values.profiles.redis.cpuLimit }}", memory: "{{ .Values.profiles.redis.memoryLimit }}" }
|
||||
ports:
|
||||
- { name: redis, containerPort: 6379 }
|
||||
env:
|
||||
- { name: POD_NAME, valueFrom: { fieldRef: { fieldPath: metadata.name } } }
|
||||
- { name: POD_IP, valueFrom: { fieldRef: { fieldPath: status.podIP } } }
|
||||
- { name: REDIS_PASSWORD, valueFrom: { secretKeyRef: { name: "{{ .Values.redis }}-secret", key: root-password } } }
|
||||
- { name: SENTINEL_HOST, value: "{{ .Values.redisSentinel }}" }
|
||||
- { name: SENTINEL_PORT, value: "26379" }
|
||||
- { name: MASTER_NAME, value: "mymaster" }
|
||||
command: ["/bin/sh","-c"]
|
||||
args:
|
||||
- |
|
||||
set -eu
|
||||
|
||||
POD_DNS_SHORT="${POD_NAME}.{{ .Values.redis }}"
|
||||
POD_DNS_FQDN="${POD_NAME}.{{ .Values.redis }}.{{ .Values.namespace }}.svc.cluster.local"
|
||||
|
||||
get_master() {
|
||||
REDISCLI_AUTH="$REDIS_PASSWORD" \
|
||||
redis-cli -h "$SENTINEL_HOST" -p "$SENTINEL_PORT" \
|
||||
SENTINEL get-master-addr-by-name "$MASTER_NAME" 2>/dev/null | tr -d '\r'
|
||||
}
|
||||
|
||||
out=""
|
||||
i=0
|
||||
while [ $i -lt 20 ]; do
|
||||
out="$(get_master || true)"
|
||||
[ -n "$out" ] && break
|
||||
i=$((i+1))
|
||||
sleep 1
|
||||
done
|
||||
|
||||
master_host="$(printf '%s\n' "$out" | sed -n '1p')"
|
||||
master_port="$(printf '%s\n' "$out" | sed -n '2p')"
|
||||
[ -n "$master_port" ] || master_port="6379"
|
||||
|
||||
is_me_master() {
|
||||
[ "$master_host" = "$POD_IP" ] || [ "$master_host" = "$POD_DNS_SHORT" ] || [ "$master_host" = "$POD_DNS_FQDN" ]
|
||||
}
|
||||
|
||||
if [ -n "$master_host" ]; then
|
||||
if is_me_master; then
|
||||
exec redis-server /etc/redis/redis.conf --masteruser default --masterauth "$REDIS_PASSWORD"
|
||||
else
|
||||
exec redis-server /etc/redis/redis.conf --replicaof "$master_host" "$master_port" --masteruser default --masterauth "$REDIS_PASSWORD"
|
||||
fi
|
||||
else
|
||||
# bootstrap if sentinel is not ready yet
|
||||
if [ "$POD_NAME" = "{{ .Values.redis }}-0" ]; then
|
||||
exec redis-server /etc/redis/redis.conf
|
||||
else
|
||||
exec redis-server /etc/redis/redis.conf --replicaof {{ .Values.redis }}-0.{{ .Values.redis }} 6379 --masteruser default --masterauth "$REDIS_PASSWORD"
|
||||
fi
|
||||
fi
|
||||
volumeMounts:
|
||||
- { name: "{{ .Values.redis }}-data-volume", mountPath: /data }
|
||||
- { name: "{{ .Values.redis }}-config-volume", mountPath: /etc/redis }
|
||||
- { name: "{{ .Values.redis }}-users-volume", mountPath: /data-acl }
|
||||
volumes:
|
||||
- name: "{{ .Values.redis }}-config-volume"
|
||||
configMap: { name: "{{ .Values.redis }}-config" }
|
||||
- name: "{{ .Values.redis }}-users-volume"
|
||||
persistentVolumeClaim: { claimName: "{{ .Values.redis }}-users-pvc" }
|
||||
volumeClaimTemplates:
|
||||
- metadata: { name: "{{ .Values.redis }}-data-volume" }
|
||||
spec:
|
||||
accessModes: [ ReadWriteOnce ]
|
||||
resources: { requests: { storage: 10Gi } }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata: { name: "{{ .Values.redis }}", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
clusterIP: None
|
||||
selector: { app: "{{ .Values.redis }}" }
|
||||
ports:
|
||||
- { name: redis, protocol: TCP, port: 6379, targetPort: 6379 }
|
||||
|
||||
# -------------------------
|
||||
# Sentinel (1) with PVC
|
||||
# -------------------------
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata: { name: "{{ .Values.redisSentinel }}-config", namespace: "{{ .Values.namespace }}" }
|
||||
data:
|
||||
sentinel.conf.tmpl: |
|
||||
bind 0.0.0.0
|
||||
port 26379
|
||||
dir /data
|
||||
|
||||
sentinel deny-scripts-reconfig yes
|
||||
sentinel resolve-hostnames yes
|
||||
sentinel announce-hostnames yes
|
||||
|
||||
# quorum=1 (single sentinel)
|
||||
sentinel monitor mymaster {{ .Values.redis }}-0.{{ .Values.redis }} 6379 1
|
||||
sentinel down-after-milliseconds mymaster 5000
|
||||
sentinel failover-timeout mymaster 60000
|
||||
sentinel parallel-syncs mymaster 1
|
||||
|
||||
sentinel auth-user mymaster default
|
||||
sentinel auth-pass mymaster __PASSWORD__
|
||||
|
||||
requirepass __PASSWORD__
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata: { name: "{{ .Values.redisSentinel }}", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
replicas: 1
|
||||
serviceName: "{{ .Values.redisSentinel }}"
|
||||
selector: { matchLabels: { app: "{{ .Values.redisSentinel }}" } }
|
||||
persistentVolumeClaimRetentionPolicy: { whenDeleted: Delete, whenScaled: Retain }
|
||||
template:
|
||||
metadata: { labels: { app: "{{ .Values.redisSentinel }}" } }
|
||||
spec:
|
||||
containers:
|
||||
- name: "{{ .Values.redisSentinel }}"
|
||||
image: redis:8.6-alpine
|
||||
resources:
|
||||
requests: { cpu: "50m", memory: "128Mi" }
|
||||
limits: { cpu: "200m", memory: "256Mi" }
|
||||
ports:
|
||||
- { name: sentinel, containerPort: 26379 }
|
||||
env:
|
||||
- { name: REDIS_PASSWORD, valueFrom: { secretKeyRef: { name: "{{ .Values.redis }}-secret", key: root-password } } }
|
||||
command: ["/bin/sh","-c"]
|
||||
args:
|
||||
- |
|
||||
set -eu
|
||||
CONF=/data/sentinel.conf
|
||||
if [ ! -f "$CONF" ]; then
|
||||
pwd_escaped=$(printf '%s' "$REDIS_PASSWORD" | sed -e 's/[\/&]/\\&/g')
|
||||
sed "s/__PASSWORD__/${pwd_escaped}/g" /tmpl/sentinel.conf.tmpl > "$CONF"
|
||||
chmod 600 "$CONF"
|
||||
fi
|
||||
exec redis-server "$CONF" --sentinel
|
||||
volumeMounts:
|
||||
- { name: "{{ .Values.redisSentinel }}-tmpl", mountPath: /tmpl }
|
||||
- { name: "{{ .Values.redisSentinel }}-data", mountPath: /data }
|
||||
volumes:
|
||||
- name: "{{ .Values.redisSentinel }}-tmpl"
|
||||
configMap: { name: "{{ .Values.redisSentinel }}-config" }
|
||||
volumeClaimTemplates:
|
||||
- metadata: { name: "{{ .Values.redisSentinel }}-data" }
|
||||
spec:
|
||||
accessModes: [ ReadWriteOnce ]
|
||||
resources: { requests: { storage: 1Gi } }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata: { name: "{{ .Values.redisSentinel }}", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
selector: { app: "{{ .Values.redisSentinel }}" }
|
||||
ports:
|
||||
- { name: sentinel, port: 26379, targetPort: 26379 }
|
||||
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata: { name: "{{ .Values.redisSentinel }}-allow-only-checker", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
podSelector: { matchLabels: { app: "{{ .Values.redisSentinel }}" } }
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector: { matchLabels: { app: "{{ .Values.redis }}" } }
|
||||
- podSelector: { matchLabels: { app: "{{ .Values.redisSentinel }}" } }
|
||||
ports:
|
||||
- { protocol: TCP, port: 26379 }
|
||||
|
||||
# -------------------------
|
||||
# HAProxy: single master endpoint
|
||||
# -------------------------
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata: { name: "{{ .Values.redis }}-haproxy-config", namespace: "{{ .Values.namespace }}" }
|
||||
data:
|
||||
haproxy.cfg: |
|
||||
global
|
||||
log stdout format raw local0
|
||||
maxconn 20000
|
||||
|
||||
resolvers kubedns
|
||||
nameserver dns1 10.43.0.10:53
|
||||
accepted_payload_size 8192
|
||||
resolve_retries 3
|
||||
timeout resolve 1s
|
||||
timeout retry 1s
|
||||
hold valid 10s
|
||||
hold obsolete 30s
|
||||
|
||||
defaults
|
||||
mode tcp
|
||||
log global
|
||||
option tcplog
|
||||
option log-health-checks
|
||||
timeout connect 5s
|
||||
timeout client 1m
|
||||
timeout server 1m
|
||||
timeout check 1s
|
||||
|
||||
frontend fe_redis_master
|
||||
bind *:6379
|
||||
default_backend be_redis_master
|
||||
|
||||
backend be_redis_master
|
||||
mode tcp
|
||||
balance first
|
||||
option tcp-check
|
||||
option srvtcpka
|
||||
timeout queue 2s
|
||||
timeout connect 2s
|
||||
timeout check 3s
|
||||
timeout server 10m
|
||||
tcp-check connect
|
||||
tcp-check send-lf "AUTH default $REDIS_PASSWORD\r\n"
|
||||
tcp-check expect string +OK
|
||||
tcp-check send INFO\ replication\r\n
|
||||
tcp-check expect string role:master
|
||||
tcp-check send QUIT\r\n
|
||||
tcp-check expect string +OK
|
||||
server redis0 {{ .Values.redis }}-0.{{ .Values.redis }}.{{ .Values.namespace }}.svc.cluster.local:6379 check resolvers kubedns resolve-prefer ipv4 init-addr libc,none inter 5s fall 2 rise 2
|
||||
server redis1 {{ .Values.redis }}-1.{{ .Values.redis }}.{{ .Values.namespace }}.svc.cluster.local:6379 check resolvers kubedns resolve-prefer ipv4 init-addr libc,none inter 5s fall 2 rise 2
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata: { name: "{{ .Values.redis }}-haproxy", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
replicas: 1
|
||||
selector: { matchLabels: { app: "{{ .Values.redis }}-haproxy" } }
|
||||
template:
|
||||
metadata:
|
||||
labels: { app: "{{ .Values.redis }}-haproxy" }
|
||||
spec:
|
||||
containers:
|
||||
- name: "{{ .Values.redis }}-haproxy"
|
||||
image: haproxy:2.9-alpine
|
||||
resources:
|
||||
requests: { cpu: "50m", memory: "64Mi" }
|
||||
limits: { cpu: "500m", memory: "256Mi" }
|
||||
ports:
|
||||
- { name: redis, containerPort: 6379 }
|
||||
env:
|
||||
- { name: REDIS_PASSWORD, valueFrom: { secretKeyRef: { name: "{{ .Values.redis }}-secret", key: root-password } } }
|
||||
command: ["/bin/sh","-c"]
|
||||
args:
|
||||
- |
|
||||
set -eu
|
||||
haproxy -c -f /usr/local/etc/haproxy/haproxy.cfg
|
||||
exec haproxy -f /usr/local/etc/haproxy/haproxy.cfg -db
|
||||
readinessProbe: { tcpSocket: { port: 6379 }, initialDelaySeconds: 1, periodSeconds: 2, failureThreshold: 3 }
|
||||
livenessProbe: { tcpSocket: { port: 6379 }, initialDelaySeconds: 10, periodSeconds: 10, failureThreshold: 3 }
|
||||
volumeMounts:
|
||||
# - { name: cfg, mountPath: /usr/local/etc/haproxy/haproxy.cfg, subPath: haproxy.cfg }
|
||||
- { name: cfg, mountPath: /usr/local/etc/haproxy }
|
||||
volumes:
|
||||
- name: cfg
|
||||
configMap: { name: "{{ .Values.redis }}-haproxy-config" }
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata: { name: "{{ .Values.redis }}-master", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
selector: { app: "{{ .Values.redis }}-haproxy" }
|
||||
ports:
|
||||
- { name: redis, port: 6379, targetPort: 6379 }
|
||||
|
||||
{{- end }}
|
||||
@@ -0,0 +1,52 @@
|
||||
{{- if .Values.workerHelm }}
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata: { name: "{{ .Values.worker }}", namespace: "{{ .Values.namespace }}" }
|
||||
spec:
|
||||
replicas: 1
|
||||
selector: { matchLabels: { app: "{{ .Values.worker }}" } }
|
||||
template:
|
||||
metadata: { labels: { app: "{{ .Values.worker }}" } }
|
||||
spec:
|
||||
containers:
|
||||
- name: "{{ .Values.worker }}"
|
||||
image: python:3.12-slim
|
||||
imagePullPolicy: IfNotPresent
|
||||
resources:
|
||||
requests: { cpu: "50m", memory: "64Mi" }
|
||||
limits: { cpu: "200m", memory: "256Mi" }
|
||||
ports:
|
||||
- { containerPort: 8080 }
|
||||
workingDir: /app/agent
|
||||
command: ["/bin/sh","-c"]
|
||||
args:
|
||||
- |
|
||||
set -e
|
||||
if [ ! -f /app/agent/worker.py ]; then
|
||||
echo "ERROR: /app/agent/worker.py not found" >&2
|
||||
ls -la /app/agent >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
exec python -u /app/agent/worker.py
|
||||
env:
|
||||
- { name: WORKER_UUID, value: "{{ .Values.workerUuid }}" }
|
||||
- { name: WORKER_NAME, value: "{{ .Values.workerName }}" }
|
||||
- { name: WORKER_POOL, value: "{{ .Values.workerPool }}" }
|
||||
- { name: API_URL, value: "{{ .Values.workerApiUrl }}" }
|
||||
- { name: GETTING_PAUSE, value: "{{ .Values.workerApiGettingPause }}" }
|
||||
- { name: SENDING_PAUSE, value: "{{ .Values.workerApiSendingPause }}" }
|
||||
volumeMounts:
|
||||
- { name: "{{ .Values.worker }}-agent-volume", mountPath: /app/agent }
|
||||
- { name: "{{ .Values.worker }}-tasks-volume", mountPath: /app/tasks }
|
||||
readinessProbe: { httpGet: { path: /healthz, port: 8080 }, periodSeconds: 5, timeoutSeconds: 2 }
|
||||
livenessProbe: { httpGet: { path: /healthz, port: 8080 }, periodSeconds: 10, timeoutSeconds: 2, failureThreshold: 3 }
|
||||
startupProbe: { httpGet: { path: /healthz, port: 8080 }, periodSeconds: 2, timeoutSeconds: 2, failureThreshold: 30 }
|
||||
volumes:
|
||||
- name: "{{ .Values.worker }}-agent-volume"
|
||||
hostPath: { path: "{{ .Values.workerAgentPath }}", type: DirectoryOrCreate }
|
||||
- name: "{{ .Values.worker }}-tasks-volume"
|
||||
hostPath: { path: "{{ .Values.workerTasksPath }}", type: DirectoryOrCreate }
|
||||
|
||||
{{- end }}
|
||||
@@ -0,0 +1,42 @@
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
scrape_timeout: 10s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: "metric-node-exporter"
|
||||
kubernetes_sd_configs:
|
||||
- role: pod
|
||||
relabel_configs:
|
||||
- action: keep
|
||||
source_labels: [__meta_kubernetes_pod_label_app]
|
||||
regex: metric-node-exporter
|
||||
|
||||
- action: keep
|
||||
source_labels: [__meta_kubernetes_pod_container_port_number]
|
||||
regex: "9100"
|
||||
|
||||
- action: replace
|
||||
source_labels: [__meta_kubernetes_pod_node_name]
|
||||
target_label: node
|
||||
|
||||
- job_name: "metric-kubelet-cadvisor"
|
||||
scheme: https
|
||||
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||
tls_config:
|
||||
insecure_skip_verify: true
|
||||
kubernetes_sd_configs:
|
||||
- role: node
|
||||
relabel_configs:
|
||||
- target_label: __address__
|
||||
replacement: kubernetes.default.svc:443
|
||||
|
||||
- source_labels: [__meta_kubernetes_node_name]
|
||||
target_label: __metrics_path__
|
||||
replacement: /api/v1/nodes/$1/proxy/metrics/cadvisor
|
||||
|
||||
- source_labels: [__meta_kubernetes_node_name]
|
||||
target_label: node
|
||||
metric_relabel_configs:
|
||||
- source_labels: [__name__]
|
||||
action: keep
|
||||
regex: 'container_memory_working_set_bytes|container_memory_usage_bytes|container_memory_rss|container_memory_cache|container_cpu_usage_seconds_total|container_cpu_system_seconds_total|container_cpu_user_seconds_total'
|
||||
@@ -0,0 +1,15 @@
|
||||
From: "Monitoring" <postmaster@mta.krumax.cz>
|
||||
To: Maksym <maksym.krugol@wedos.org>
|
||||
Subject: Test message (RFC822 raw)
|
||||
Date: Thu, 05 Feb 2026 10:15:00 +0100
|
||||
Message-ID: <test-20260205-101500.1@mta.krumax.cz>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Hello!
|
||||
|
||||
This is a raw RFC822 message file sent via sendmail -t.
|
||||
|
||||
Regards,
|
||||
Monitoring
|
||||
@@ -0,0 +1,52 @@
|
||||
expose_php = Off
|
||||
allow_url_fopen = Off
|
||||
allow_url_include = Off
|
||||
enable_dl = Off
|
||||
short_open_tag = Off
|
||||
|
||||
; --- block user_ini ---
|
||||
user_ini.filename =
|
||||
|
||||
; -- disable functions ---
|
||||
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,putenv,dl,show_source,highlight_file,symlink,readlink,link,proc_terminate,proc_get_status,pfsockopen,posix_kill,posix_setuid,posix_setgid,posix_setsid,posix_setpgid,posix_getgrnam,posix_getpgid,posix_getpwuid,posix_getpwnam,posix_getrlimit,posix_initgroups,posix_mkfifo,posix_mknod,posix_uname,register_tick_function,openlog,syslog,proc_close,proc_nice,diskfreespace,disk_free_space,disk_total_space,leak,pcntl_alarm,pcntl_async_signals,pcntl_exec,pcntl_fork,pcntl_get_last_error,pcntl_getcpuaffinity,pcntl_getpriority,pcntl_rfork,pcntl_setcpuaffinity,pcntl_setpriority,pcntl_signal,pcntl_signal_dispatch,pcntl_signal_get_handler,pcntl_sigprocmask,pcntl_sigtimedwait,pcntl_sigwaitinfo,pcntl_strerror,pcntl_unshare,pcntl_wait,pcntl_waitid,pcntl_waitpid,pcntl_wexitstatus,pcntl_wifexited,pcntl_wifsignaled,pcntl_wifstopped,pcntl_wstopsig,pcntl_wtermsig,sys_getloadavg
|
||||
|
||||
; not use for LSAPI
|
||||
;pm.max_children = {{children}}
|
||||
|
||||
; --- memory limit ---
|
||||
max_memory_limit = {{max_memory_limit}}
|
||||
|
||||
; --- default (redefined per vhost) ---
|
||||
memory_limit = {{memory_limit}}
|
||||
max_execution_time = {{max_execution_time}}
|
||||
max_input_time = 30
|
||||
max_input_vars = 1000
|
||||
output_buffering = 4096
|
||||
|
||||
; --- uploads ---
|
||||
post_max_size = {{post_max_size}}
|
||||
upload_max_filesize = {{upload_max_filesize}}
|
||||
max_file_uploads = 10
|
||||
|
||||
; --- log --- (to stderr k3s?)
|
||||
display_errors = Off
|
||||
log_errors = On
|
||||
;error_log = /usr/local/lsws/conf/logs/php_errors.log
|
||||
error_log = /proc/self/fd/2
|
||||
|
||||
; --- sessions (redefined per vhost) ---
|
||||
session.save_path = "/tmp"
|
||||
session.use_only_cookies = 1
|
||||
session.cookie_httponly = 1
|
||||
session.cookie_secure = 1
|
||||
session.cookie_samesite = "Lax"
|
||||
|
||||
; --- OPcache ---
|
||||
opcache.enable = 1
|
||||
opcache.enable_cli = 0
|
||||
opcache.memory_consumption = 256
|
||||
opcache.interned_strings_buffer = 16
|
||||
opcache.max_accelerated_files = 100000
|
||||
opcache.validate_timestamps = 1
|
||||
opcache.revalidate_freq = 2
|
||||
opcache.jit = "disable"
|
||||
@@ -0,0 +1,6 @@
|
||||
children=16
|
||||
max_memory_limit=512M
|
||||
memory_limit=512M
|
||||
max_execution_time=30
|
||||
post_max_size=512M
|
||||
upload_max_filesize=512M
|
||||
@@ -0,0 +1,6 @@
|
||||
children=4
|
||||
max_memory_limit=512M
|
||||
memory_limit=512M
|
||||
max_execution_time=30
|
||||
post_max_size=128M
|
||||
upload_max_filesize=128M
|
||||
@@ -0,0 +1,6 @@
|
||||
children=6
|
||||
max_memory_limit=512M
|
||||
memory_limit=512M
|
||||
max_execution_time=30
|
||||
post_max_size=128M
|
||||
upload_max_filesize=128M
|
||||
@@ -0,0 +1,6 @@
|
||||
children=8
|
||||
max_memory_limit=512M
|
||||
memory_limit=512M
|
||||
max_execution_time=30
|
||||
post_max_size=128M
|
||||
upload_max_filesize=128M
|
||||
@@ -0,0 +1,8 @@
|
||||
RewriteEngine On
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
|
||||
# Front-controller
|
||||
RewriteRule ^/?index\.php$ - [L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule . /index.php [L]
|
||||
@@ -0,0 +1,11 @@
|
||||
RewriteEngine On
|
||||
|
||||
# no www -> add www + https
|
||||
RewriteCond %{HTTP_HOST} !^www\. [NC]
|
||||
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
|
||||
|
||||
# www, http -> https
|
||||
RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC]
|
||||
RewriteCond %{HTTP:Forwarded} !proto=https [NC]
|
||||
RewriteCond %{HTTPS} !=on
|
||||
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
|
||||
@@ -0,0 +1,11 @@
|
||||
RewriteEngine On
|
||||
|
||||
# www -> non-www + https
|
||||
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
|
||||
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]
|
||||
|
||||
# http -> https
|
||||
RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC]
|
||||
RewriteCond %{HTTP:Forwarded} !proto=https [NC]
|
||||
RewriteCond %{HTTPS} !=on
|
||||
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
|
||||
@@ -0,0 +1,7 @@
|
||||
RewriteEngine On
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
|
||||
# Laravel
|
||||
RewriteRule . /public/index.php [L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
@@ -0,0 +1,14 @@
|
||||
RewriteEngine On
|
||||
|
||||
# Unigma 1.0.0 fix
|
||||
RewriteCond %{REQUEST_URI} ^/v(8[1-5])/ [OR]
|
||||
RewriteCond %{REQUEST_URI} ^/v(8[1-5])/router\.lua [OR]
|
||||
RewriteCond %{REQUEST_URI} ^/router\.lua
|
||||
RewriteRule ^ - [L]
|
||||
|
||||
RewriteCond %{REQUEST_URI} !^/router\.lua
|
||||
RewriteCond %{REQUEST_URI} !^/[^/]+/www/
|
||||
RewriteCond %{HTTP_HOST} ^([a-z0-9.-]+)$
|
||||
RewriteRule ^/?(.*)$ /%1/www/$1
|
||||
|
||||
RewriteRule ^/?(.*\.php)$ /router.lua?orig=/$1 [L]
|
||||
@@ -0,0 +1,33 @@
|
||||
docRoot /var/www/vhosts/{{domain}}/www/
|
||||
vhDomain {{domain}}
|
||||
vhAliases *.{{domain}}
|
||||
|
||||
index {
|
||||
useServer 0
|
||||
indexFiles index.php
|
||||
}
|
||||
|
||||
phpIniOverride {
|
||||
# --- paths ---
|
||||
php_admin_value upload_tmp_dir /var/www/vhosts/{{domain}}/tmp
|
||||
php_admin_value session.save_path /var/www/vhosts/{{domain}}/session
|
||||
php_admin_value open_basedir "/var/www/vhosts/{{domain}}/www:/var/www/vhosts/{{domain}}/tmp:/var/www/vhosts/{{domain}}/session:/var/www/data/{{domain}}:/var/www/shared"
|
||||
php_admin_value auto_prepend_file /var/www/data/{{domain}}/bootstrap.php
|
||||
|
||||
# --- hard limits ---
|
||||
php_admin_value memory_limit {{memory_limit}}
|
||||
php_admin_value max_execution_time {{max_execution_time}}
|
||||
php_admin_value max_input_time 30
|
||||
php_admin_value max_input_vars 1000
|
||||
php_admin_value post_max_size {{post_max_size}}
|
||||
php_admin_value upload_max_filesize {{upload_max_filesize}}
|
||||
}
|
||||
|
||||
rewrite {
|
||||
enable 1
|
||||
autoLoadHtaccess 1
|
||||
rules <<<END_rules
|
||||
rewriteFile /usr/local/lsws/conf/rules/https.rules
|
||||
rewriteFile /usr/local/lsws/conf/rules/front-controller.rules
|
||||
END_rules
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# OLS
|
||||
aliasLimit=0
|
||||
phpChildren=16
|
||||
maxExecutionTime=30
|
||||
maxMemoryLimit=512M
|
||||
memoryLimit=512M
|
||||
postMaxSize=512M
|
||||
uploadMaxFilesize=512M
|
||||
|
||||
# Filesystem
|
||||
blockSoftLimit=0
|
||||
blockHardLimit=0
|
||||
inodeSoftLimit=0
|
||||
inodeHardLimit=0
|
||||
|
||||
# Database
|
||||
databaseSoftLimit=0
|
||||
|
||||
# Backup 1/7
|
||||
backupPeriod=7
|
||||
|
||||
# Mail 50/500
|
||||
mailDayLimit=0
|
||||
@@ -0,0 +1,23 @@
|
||||
# OLS
|
||||
aliasLimit=0
|
||||
phpChildren=16
|
||||
maxExecutionTime=30
|
||||
maxMemoryLimit=512M
|
||||
memoryLimit=512M
|
||||
postMaxSize=512M
|
||||
uploadMaxFilesize=512M
|
||||
|
||||
# Filesystem
|
||||
blockSoftLimit=0
|
||||
blockHardLimit=0
|
||||
inodeSoftLimit=0
|
||||
inodeHardLimit=0
|
||||
|
||||
# Database
|
||||
databaseSoftLimit=0
|
||||
|
||||
# Backup 1/7
|
||||
backupPeriod=7
|
||||
|
||||
# Mail 50/500
|
||||
mailDayLimit=0
|
||||
@@ -0,0 +1,23 @@
|
||||
# OLS
|
||||
aliasLimit=0
|
||||
phpChildren=16
|
||||
maxExecutionTime=30
|
||||
maxMemoryLimit=512M
|
||||
memoryLimit=512M
|
||||
postMaxSize=512M
|
||||
uploadMaxFilesize=512M
|
||||
|
||||
# Filesystem
|
||||
blockSoftLimit=0
|
||||
blockHardLimit=0
|
||||
inodeSoftLimit=0
|
||||
inodeHardLimit=0
|
||||
|
||||
# Database
|
||||
databaseSoftLimit=0
|
||||
|
||||
# Backup 1/7
|
||||
backupPeriod=7
|
||||
|
||||
# Mail 50/500
|
||||
mailDayLimit=0
|
||||
@@ -0,0 +1,20 @@
|
||||
# --- KUBE SFTP GLOBAL BEGIN ---
|
||||
|
||||
PermitRootLogin no
|
||||
PermitUserEnvironment no
|
||||
|
||||
LoginGraceTime 30
|
||||
MaxAuthTries 3
|
||||
MaxSessions 5
|
||||
MaxStartups 200:30:500
|
||||
|
||||
Compression no
|
||||
DebianBanner no
|
||||
|
||||
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org
|
||||
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes128-ctr
|
||||
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
|
||||
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256
|
||||
PubkeyAcceptedAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256
|
||||
|
||||
# --- KUBE SFTP GLOBAL END ---
|
||||
@@ -0,0 +1,22 @@
|
||||
# --- KUBE SFTP GROUP BEGIN ---
|
||||
|
||||
Match Group {{sftp_access_group}}
|
||||
ChrootDirectory %h
|
||||
ForceCommand internal-sftp -d /www -u 027
|
||||
|
||||
PasswordAuthentication yes
|
||||
KbdInteractiveAuthentication no
|
||||
PubkeyAuthentication yes
|
||||
|
||||
PermitTTY no
|
||||
PermitTunnel no
|
||||
|
||||
AllowTcpForwarding no
|
||||
AllowAgentForwarding no
|
||||
AllowStreamLocalForwarding no
|
||||
X11Forwarding no
|
||||
|
||||
ClientAliveInterval 300
|
||||
ClientAliveCountMax 2
|
||||
|
||||
# --- KUBE SFTP GROUP END ---
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
echo '<pre>pid: ' . getmypid() . '</pre>';
|
||||
echo phpinfo();
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
|
||||
header('Pragma: no-cache');
|
||||
|
||||
echo "time: <b>" . time() . "</b> | hostname: <b>" . gethostname() . "</b> | pid: <b>" . getmypid() . "</b><br>";
|
||||
|
||||
phpinfo();
|
||||
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/wp-load.php';
|
||||
|
||||
define('MAIL_TEST_KEY', 'dev');
|
||||
|
||||
$smtpLog = '';
|
||||
add_action('phpmailer_init', function ($phpmailer) use (&$smtpLog) {
|
||||
$phpmailer->SMTPDebug = 2;
|
||||
$phpmailer->Debugoutput = function ($str, $level) use (&$smtpLog) {
|
||||
$smtpLog .= date('Y-m-d H:i:s') . ' ' . htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, 'UTF-8') . "<br>\n";
|
||||
};
|
||||
});
|
||||
|
||||
$success = '';
|
||||
$error = '';
|
||||
$err = null;
|
||||
add_action('wp_mail_failed', function($e) use (&$err) {
|
||||
if (is_wp_error($e)) {
|
||||
$err = $e->get_error_message();
|
||||
} else {
|
||||
$err = 'Unknown wp_mail error';
|
||||
}
|
||||
});
|
||||
|
||||
$domain = wp_parse_url(home_url(), PHP_URL_HOST);
|
||||
$to = "maksym.krugol@wedos.org";
|
||||
$headers = [
|
||||
"List-Unsubscribe: <mailto:unsubscribe@{$domain}?subject=unsubscribe>, <https://{$domain}/unsubscribe/{$to}>"
|
||||
];
|
||||
|
||||
$subject = "Service status update and new API keys - " . (new DateTime())->format('d.m.Y');
|
||||
$message = '';
|
||||
$message .= "Service: host-" . bin2hex(random_bytes(2)) . PHP_EOL;
|
||||
$message .= "Status: active" . PHP_EOL;
|
||||
$message .= "Service tag: " . bin2hex(random_bytes(6)) . PHP_EOL . PHP_EOL;
|
||||
for ($i = 1; $i < 9; $i++) {
|
||||
$message .= "API key" . $i . ": " . bin2hex(random_bytes(40)) . PHP_EOL;
|
||||
}
|
||||
$message .= PHP_EOL . "Thank you for your understanding." . PHP_EOL . PHP_EOL . "Support team US1" . PHP_EOL . (new DateTime())->format('d.m.Y H:i');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$to = isset($_POST['to']) ? trim($_POST['to']) : $to;
|
||||
$subject = isset($_POST['subject']) ? trim($_POST['subject']) : $subject;
|
||||
$message = isset($_POST['message']) ? trim($_POST['message']) : $message;
|
||||
$key = isset($_POST['key']) ? trim($_POST['key']) : '';
|
||||
|
||||
if (!hash_equals(MAIL_TEST_KEY, $key)) {
|
||||
$error = 'Incorrect key.';
|
||||
} elseif ($to === '' || !is_email($to)) {
|
||||
$error = 'Incorrect recipient address.';
|
||||
} elseif ($subject === '') {
|
||||
$error = 'Incorrect subject.';
|
||||
} else {
|
||||
|
||||
$sent = wp_mail($to, $subject, $message, $headers);
|
||||
if ($sent) {
|
||||
$success = "Success";
|
||||
} else {
|
||||
$error = "Failed | " . ($err ? htmlspecialchars($err, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') : 'See PHP/WP error_log for details.');
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Test send mail</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 16px;
|
||||
padding: 0;
|
||||
background-color: #1e1f22;
|
||||
color: #bcbec4;
|
||||
font-family: Consolas, "DejaVu Sans Mono", "Liberation Mono", Menlo, Monaco, "Courier New", monospace;
|
||||
font-size: 14px;
|
||||
}
|
||||
input, textarea {
|
||||
padding: 0 8px;
|
||||
width: 282px;
|
||||
line-height: 24px;
|
||||
background-color: transparent;
|
||||
color: #bcbec4;
|
||||
border: 1px solid #393b40;
|
||||
border-radius: 4px;
|
||||
}
|
||||
button {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
background: #f5f5f5;
|
||||
cursor: pointer;
|
||||
}
|
||||
hr {
|
||||
border: none;
|
||||
height: 1px;
|
||||
background-color: #393b40;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Test send mail <?php echo uniqid() ?></h1>
|
||||
<form method="post">
|
||||
<p>
|
||||
<label>
|
||||
Recipient:<br>
|
||||
<input type="email" name="to" required style="width: 400px;" value="<?php echo $to; ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Subject:<br>
|
||||
<input type="text" name="subject" required style="width: 800px;" value="<?php echo $subject; ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Message:<br>
|
||||
<textarea name="message" rows="10" style="width: 800px;"><?php echo esc_textarea($message); ?></textarea>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
Key:<br>
|
||||
<input type="password" name="key" required style="width: 100px;">
|
||||
</label>
|
||||
<button type="submit">Send</button>
|
||||
</p>
|
||||
</form>
|
||||
<?php echo $domain ?>
|
||||
<?php if ($success): ?><p style="color: green;"><?php echo esc_html($success); ?></p><?php endif; ?>
|
||||
<?php if ($error): ?><p style="color: red;"><?php echo esc_html($error); ?></p><?php endif; ?>
|
||||
<?php if (!empty($smtpLog)): echo '<hr><h2>SMTP debug log</h2><div>' . $smtpLog . '</div>'; endif; ?>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
|
||||
header('Pragma: no-cache');
|
||||
|
||||
echo "hostname: " . gethostname() . "\npid: " . getmypid() . "\nopcache_get_status: ";
|
||||
|
||||
print_r(opcache_get_status(false));
|
||||
@@ -0,0 +1,646 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
|
||||
$SCORE = ['PASS' => 0, 'WARN' => 0, 'FAIL' => 0];
|
||||
$FINDINGS = [];
|
||||
|
||||
function add_result(string $level, string $title, string $detail = ''): void {
|
||||
global $SCORE, $FINDINGS;
|
||||
if (!isset($SCORE[$level])) {
|
||||
$level = 'WARN';
|
||||
}
|
||||
$SCORE[$level]++;
|
||||
$FINDINGS[] = [$level, $title, $detail];
|
||||
}
|
||||
|
||||
function line(string $label, $value = null): void {
|
||||
if ($value === null) {
|
||||
echo $label . PHP_EOL;
|
||||
return;
|
||||
}
|
||||
if (is_bool($value)) {
|
||||
$value = $value ? 'YES' : 'NO';
|
||||
} elseif (is_array($value) || is_object($value)) {
|
||||
$value = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
echo str_pad($label, 46) . ': ' . $value . PHP_EOL;
|
||||
}
|
||||
|
||||
function section(string $title): void {
|
||||
echo PHP_EOL . "--- {$title} ---" . PHP_EOL;
|
||||
}
|
||||
|
||||
function bytes_from_ini(?string $val): ?int {
|
||||
if ($val === null || $val === '') return null;
|
||||
$val = trim($val);
|
||||
if ($val === '-1') return -1;
|
||||
$last = strtolower(substr($val, -1));
|
||||
$num = (float)$val;
|
||||
return match($last) {
|
||||
'g' => (int)($num * 1024 * 1024 * 1024),
|
||||
'm' => (int)($num * 1024 * 1024),
|
||||
'k' => (int)($num * 1024),
|
||||
default => (int)$num,
|
||||
};
|
||||
}
|
||||
|
||||
function with_error_capture(callable $fn): array {
|
||||
$error = null;
|
||||
set_error_handler(function($severity, $message) use (&$error) {
|
||||
$error = $message;
|
||||
return true;
|
||||
});
|
||||
try {
|
||||
$result = $fn();
|
||||
restore_error_handler();
|
||||
return ['result' => $result, 'error' => $error];
|
||||
} catch (Throwable $e) {
|
||||
restore_error_handler();
|
||||
return ['result' => null, 'error' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
function try_read_file(string $path): array {
|
||||
return with_error_capture(function() use ($path) {
|
||||
$data = @file_get_contents($path);
|
||||
if ($data === false) return false;
|
||||
return 'len=' . strlen($data);
|
||||
}) + ['path' => $path];
|
||||
}
|
||||
|
||||
function try_scandir_path(string $path): array {
|
||||
return with_error_capture(function() use ($path) {
|
||||
$data = @scandir($path);
|
||||
if ($data === false) return false;
|
||||
return array_slice($data, 0, 15);
|
||||
}) + ['path' => $path];
|
||||
}
|
||||
|
||||
function try_socket(string $target, int $port, float $timeout = 1.2): array {
|
||||
$errno = 0;
|
||||
$errstr = '';
|
||||
$fp = @fsockopen($target, $port, $errno, $errstr, $timeout);
|
||||
$ok = is_resource($fp);
|
||||
if ($ok) fclose($fp);
|
||||
return [
|
||||
'target' => $target,
|
||||
'port' => $port,
|
||||
'connected' => $ok,
|
||||
'errno' => $errno,
|
||||
'errstr' => $errstr,
|
||||
];
|
||||
}
|
||||
|
||||
function try_unix_socket(string $path, float $timeout = 1.0): array {
|
||||
$errno = 0;
|
||||
$errstr = '';
|
||||
$fp = @stream_socket_client('unix://' . $path, $errno, $errstr, $timeout);
|
||||
$ok = is_resource($fp);
|
||||
if ($ok) fclose($fp);
|
||||
return [
|
||||
'path' => $path,
|
||||
'connected' => $ok,
|
||||
'errno' => $errno,
|
||||
'errstr' => $errstr,
|
||||
];
|
||||
}
|
||||
|
||||
echo "=== SHARED HOSTING SAFE AUDIT v2.1 ===" . PHP_EOL;
|
||||
echo "Time: " . date('c') . PHP_EOL;
|
||||
|
||||
$docRoot = realpath($_SERVER['DOCUMENT_ROOT'] ?? getcwd()) ?: getcwd();
|
||||
$scriptFile = $_SERVER['SCRIPT_FILENAME'] ?? __FILE__;
|
||||
$baseTmp = $docRoot . '/.audit_tmp_' . getmypid() . '_' . mt_rand(1000, 9999);
|
||||
@mkdir($baseTmp, 0700, true);
|
||||
|
||||
section('Runtime identity');
|
||||
line('PHP version', PHP_VERSION);
|
||||
line('SAPI', PHP_SAPI);
|
||||
line('OS', PHP_OS_FAMILY);
|
||||
line('Document root', $docRoot);
|
||||
line('Script filename', $scriptFile);
|
||||
line('Current dir', getcwd());
|
||||
line('Loaded php.ini', php_ini_loaded_file() ?: 'none');
|
||||
line('Additional .ini files', php_ini_scanned_files() ?: 'none');
|
||||
line('Hostname', php_uname('n'));
|
||||
line('Server software', $_SERVER['SERVER_SOFTWARE'] ?? 'n/a');
|
||||
line('Server addr', $_SERVER['SERVER_ADDR'] ?? 'n/a');
|
||||
line('Server port', $_SERVER['SERVER_PORT'] ?? 'n/a');
|
||||
line('Remote addr', $_SERVER['REMOTE_ADDR'] ?? 'n/a');
|
||||
|
||||
section('PHP limits and config');
|
||||
$iniKeys = [
|
||||
'memory_limit',
|
||||
'max_execution_time',
|
||||
'max_input_time',
|
||||
'max_input_vars',
|
||||
'post_max_size',
|
||||
'upload_max_filesize',
|
||||
'open_basedir',
|
||||
'disable_functions',
|
||||
'disable_classes',
|
||||
'user_ini.filename',
|
||||
'user_ini.cache_ttl',
|
||||
'file_uploads',
|
||||
'allow_url_fopen',
|
||||
'allow_url_include',
|
||||
'session.save_path',
|
||||
'upload_tmp_dir',
|
||||
'sys_temp_dir',
|
||||
'display_errors',
|
||||
'log_errors',
|
||||
'expose_php',
|
||||
'mail.add_x_header',
|
||||
'mysqli.default_socket',
|
||||
'pdo_mysql.default_socket',
|
||||
];
|
||||
foreach ($iniKeys as $k) {
|
||||
line($k, ini_get($k));
|
||||
}
|
||||
|
||||
section('Dangerous functions present');
|
||||
$dangerFns = [
|
||||
'exec','shell_exec','system','passthru','proc_open','popen',
|
||||
'pcntl_exec','pcntl_fork','putenv','mail','symlink','link',
|
||||
'stream_socket_client','fsockopen'
|
||||
];
|
||||
foreach ($dangerFns as $fn) {
|
||||
line("function_exists($fn)", function_exists($fn));
|
||||
}
|
||||
|
||||
section('Loaded extensions');
|
||||
$exts = ['mysqli','pdo_mysql','redis','ftp','curl','openssl','pcntl','posix','sockets','imap','intl'];
|
||||
foreach ($exts as $ext) {
|
||||
line("extension_loaded($ext)", extension_loaded($ext));
|
||||
}
|
||||
|
||||
section('Filesystem isolation');
|
||||
$fsTests = [
|
||||
$docRoot,
|
||||
$docRoot . '/../',
|
||||
$docRoot . '/../../',
|
||||
'/var/www',
|
||||
'/var/www/vhosts',
|
||||
'/etc/passwd',
|
||||
'/etc/hosts',
|
||||
'/proc/self/environ',
|
||||
'/proc/meminfo',
|
||||
'/tmp',
|
||||
'/var/tmp',
|
||||
'/run',
|
||||
'/run/php',
|
||||
'/run/mysqld',
|
||||
'/dev/shm',
|
||||
'/var/spool/postfix',
|
||||
];
|
||||
foreach ($fsTests as $path) {
|
||||
$isDir = @is_dir($path);
|
||||
$result = $isDir ? try_scandir_path($path) : try_read_file($path);
|
||||
line($path, $result);
|
||||
}
|
||||
|
||||
section('Path traversal / realpath checks');
|
||||
$traversalTests = [
|
||||
$docRoot . '/../www',
|
||||
$docRoot . '/../tmp',
|
||||
$docRoot . '/../session',
|
||||
$docRoot . '/../../../../etc/passwd',
|
||||
$docRoot . '/../other-vhost/www',
|
||||
];
|
||||
foreach ($traversalTests as $path) {
|
||||
line("realpath($path)", @realpath($path) ?: 'false');
|
||||
line("read($path)", try_read_file($path));
|
||||
}
|
||||
|
||||
section('Allowed path write tests');
|
||||
$writeFile = $baseTmp . '/write-test.txt';
|
||||
$res = with_error_capture(function() use ($writeFile) {
|
||||
return file_put_contents($writeFile, "audit\n");
|
||||
});
|
||||
line('Write file in docroot tmp', ['path' => $writeFile] + $res);
|
||||
line('File exists after write', file_exists($writeFile));
|
||||
line('File readable after write', is_readable($writeFile));
|
||||
line('File writable after write', is_writable($writeFile));
|
||||
|
||||
$renameTo = $baseTmp . '/write-test-renamed.txt';
|
||||
$res = with_error_capture(function() use ($writeFile, $renameTo) {
|
||||
return @rename($writeFile, $renameTo);
|
||||
});
|
||||
line('Rename inside allowed path', ['from' => $writeFile, 'to' => $renameTo] + $res);
|
||||
|
||||
$copyToSession = dirname($docRoot) . '/session/audit-copy.txt';
|
||||
$res = with_error_capture(function() use ($renameTo, $copyToSession) {
|
||||
return @copy($renameTo, $copyToSession);
|
||||
});
|
||||
line('Copy from docroot to session dir', ['to' => $copyToSession] + $res);
|
||||
|
||||
section('Symlink / hardlink inside allowed path');
|
||||
$src = $baseTmp . '/src.txt';
|
||||
@file_put_contents($src, 'x');
|
||||
$symlinkTarget = $baseTmp . '/sym.txt';
|
||||
$hardlinkTarget = $baseTmp . '/hard.txt';
|
||||
$symlinkRes = with_error_capture(fn() => @symlink($src, $symlinkTarget));
|
||||
$hardlinkRes = with_error_capture(fn() => @link($src, $hardlinkTarget));
|
||||
line('Symlink allowed path', $symlinkRes);
|
||||
line('Hardlink allowed path', $hardlinkRes);
|
||||
line('Symlink exists', is_link($symlinkTarget));
|
||||
line('Hardlink exists', file_exists($hardlinkTarget));
|
||||
|
||||
section('Runtime override attempts');
|
||||
$overrideTests = [
|
||||
'memory_limit' => '2048M',
|
||||
'max_execution_time' => '600',
|
||||
'upload_max_filesize' => '2048M',
|
||||
'post_max_size' => '2048M',
|
||||
'open_basedir' => '/',
|
||||
];
|
||||
$overrideResults = [];
|
||||
foreach ($overrideTests as $key => $value) {
|
||||
$before = ini_get($key);
|
||||
$ret = @ini_set($key, $value);
|
||||
$after = ini_get($key);
|
||||
$overrideResults[$key] = [
|
||||
'before' => $before,
|
||||
'return' => $ret,
|
||||
'after' => $after,
|
||||
'changed' => ($before !== $after),
|
||||
];
|
||||
line("ini_set($key)", $overrideResults[$key]);
|
||||
}
|
||||
|
||||
section('Execution capability tests');
|
||||
$execResults = [];
|
||||
|
||||
if (function_exists('exec')) {
|
||||
$execResults['exec'] = with_error_capture(function() {
|
||||
$out = [];
|
||||
$rc = 0;
|
||||
@exec('id 2>&1', $out, $rc);
|
||||
return ['rc' => $rc, 'out' => implode("\n", array_slice($out, 0, 5))];
|
||||
});
|
||||
line('exec("id")', $execResults['exec']);
|
||||
}
|
||||
|
||||
if (function_exists('shell_exec')) {
|
||||
$execResults['shell_exec'] = with_error_capture(function() {
|
||||
$out = @shell_exec('whoami 2>&1');
|
||||
return $out === null ? null : trim($out);
|
||||
});
|
||||
line('shell_exec("whoami")', $execResults['shell_exec']);
|
||||
}
|
||||
|
||||
if (function_exists('system')) {
|
||||
$execResults['system'] = with_error_capture(function() {
|
||||
ob_start();
|
||||
$rc = 0;
|
||||
@system('pwd 2>&1', $rc);
|
||||
$out = ob_get_clean();
|
||||
return ['rc' => $rc, 'out' => trim((string)$out)];
|
||||
});
|
||||
line('system("pwd")', $execResults['system']);
|
||||
}
|
||||
|
||||
if (function_exists('proc_open')) {
|
||||
$execResults['proc_open'] = with_error_capture(function() {
|
||||
$desc = [
|
||||
0 => ['pipe', 'r'],
|
||||
1 => ['pipe', 'w'],
|
||||
2 => ['pipe', 'w'],
|
||||
];
|
||||
$proc = @proc_open('id', $desc, $pipes);
|
||||
if (!is_resource($proc)) return 'proc_open failed';
|
||||
fclose($pipes[0]);
|
||||
$stdout = stream_get_contents($pipes[1]);
|
||||
$stderr = stream_get_contents($pipes[2]);
|
||||
fclose($pipes[1]);
|
||||
fclose($pipes[2]);
|
||||
$code = proc_close($proc);
|
||||
return ['rc' => $code, 'stdout' => trim($stdout), 'stderr' => trim($stderr)];
|
||||
});
|
||||
line('proc_open("id")', $execResults['proc_open']);
|
||||
}
|
||||
|
||||
if (function_exists('popen')) {
|
||||
$execResults['popen'] = with_error_capture(function() {
|
||||
$h = @popen('id 2>&1', 'r');
|
||||
if (!is_resource($h)) return 'popen failed';
|
||||
$out = stream_get_contents($h);
|
||||
$rc = pclose($h);
|
||||
return ['rc' => $rc, 'out' => trim((string)$out)];
|
||||
});
|
||||
line('popen("id")', $execResults['popen']);
|
||||
}
|
||||
|
||||
section('Fork capability');
|
||||
line('extension_loaded(pcntl)', extension_loaded('pcntl'));
|
||||
line('function_exists(pcntl_fork)', function_exists('pcntl_fork'));
|
||||
line('Active fork test', 'SKIPPED in web SAPI for safety');
|
||||
|
||||
section('Controlled memory probe');
|
||||
$memoryLimit = bytes_from_ini(ini_get('memory_limit'));
|
||||
$chunks = [];
|
||||
$allocated = 0;
|
||||
$chunkSize = 4 * 1024 * 1024;
|
||||
$target = ($memoryLimit !== null && $memoryLimit > 0) ? (int)($memoryLimit * 0.70) : 64 * 1024 * 1024;
|
||||
$oom = false;
|
||||
$oomMsg = null;
|
||||
try {
|
||||
while ($allocated + $chunkSize <= $target) {
|
||||
$chunks[] = str_repeat('A', $chunkSize);
|
||||
$allocated += $chunkSize;
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
$oom = true;
|
||||
$oomMsg = $e->getMessage();
|
||||
}
|
||||
line('memory_limit parsed', $memoryLimit);
|
||||
line('allocated safely', $allocated);
|
||||
line('oom caught', $oom);
|
||||
line('oom message', $oomMsg ?: 'none');
|
||||
unset($chunks);
|
||||
|
||||
section('Controlled CPU / timeout probe');
|
||||
$start = microtime(true);
|
||||
$iterations = 0;
|
||||
$limitSeconds = max(1, (int)ini_get('max_execution_time'));
|
||||
$softBudget = min(3, max(1, $limitSeconds - 1));
|
||||
while ((microtime(true) - $start) < $softBudget) {
|
||||
hash('sha256', random_bytes(256), false);
|
||||
$iterations++;
|
||||
}
|
||||
line('elapsed', round(microtime(true) - $start, 3) . 's');
|
||||
line('iterations', $iterations);
|
||||
line('soft budget', $softBudget . 's');
|
||||
|
||||
section('set_time_limit test');
|
||||
$setTimeLimitRes = with_error_capture(function() {
|
||||
return @set_time_limit(600);
|
||||
});
|
||||
line('set_time_limit(600)', $setTimeLimitRes);
|
||||
line('max_execution_time after set_time_limit', ini_get('max_execution_time'));
|
||||
|
||||
section('Network reachability');
|
||||
$netTargets = [
|
||||
['127.0.0.1', 25],
|
||||
['127.0.0.1', 3306],
|
||||
['127.0.0.1', 6379],
|
||||
['127.0.0.1', 11211],
|
||||
['127.0.0.1', 7080],
|
||||
['127.0.0.1', 80],
|
||||
['127.0.0.1', 443],
|
||||
];
|
||||
$netResults = [];
|
||||
foreach ($netTargets as [$host, $port]) {
|
||||
$netResults["$host:$port"] = try_socket($host, $port);
|
||||
line("$host:$port", $netResults["$host:$port"]);
|
||||
}
|
||||
|
||||
section('Unix socket reachability');
|
||||
$unixCandidates = [
|
||||
'/run/mysqld/mysqld.sock',
|
||||
'/var/run/mysqld/mysqld.sock',
|
||||
'/tmp/mysql.sock',
|
||||
'/run/redis/redis-server.sock',
|
||||
'/var/run/redis/redis.sock',
|
||||
'/tmp/redis.sock',
|
||||
'/usr/local/lsws/admin/tmp/admin.sock',
|
||||
];
|
||||
$unixResults = [];
|
||||
foreach ($unixCandidates as $sock) {
|
||||
$unixResults[$sock] = try_unix_socket($sock);
|
||||
line($sock, $unixResults[$sock]);
|
||||
}
|
||||
|
||||
section('Stream wrappers');
|
||||
$wrappers = stream_get_wrappers();
|
||||
sort($wrappers);
|
||||
line('wrappers', $wrappers);
|
||||
|
||||
$wrapperReads = [
|
||||
'php://memory',
|
||||
'php://temp',
|
||||
'data://text/plain;base64,SGVsbG8=',
|
||||
];
|
||||
foreach ($wrapperReads as $wrapper) {
|
||||
line("read $wrapper", with_error_capture(function() use ($wrapper) {
|
||||
$d = @file_get_contents($wrapper);
|
||||
if ($d === false) return false;
|
||||
return 'len=' . strlen($d) . ' data=' . substr($d, 0, 40);
|
||||
}));
|
||||
}
|
||||
|
||||
section('Include wrapper tests');
|
||||
$includeFile = $baseTmp . '/include-test.php';
|
||||
file_put_contents($includeFile, "<?php return ['ok' => true, 'time' => time()];");
|
||||
$includeLocal = with_error_capture(function() use ($includeFile) {
|
||||
return include $includeFile;
|
||||
});
|
||||
$includeData = with_error_capture(function() {
|
||||
return @include 'data://text/plain;base64,PD9waHAgcmV0dXJuIFsiZGF0YSI9PnRydWVdOw==';
|
||||
});
|
||||
line('include local file', $includeLocal);
|
||||
line('include data:// wrapper', $includeData);
|
||||
|
||||
section('Environment leakage');
|
||||
$envKeys = ['HOME','USER','LOGNAME','PATH','TMPDIR','TEMP','HOSTNAME'];
|
||||
$envOut = [];
|
||||
foreach ($envKeys as $k) {
|
||||
$envOut[$k] = getenv($k);
|
||||
}
|
||||
line('getenv selected', $envOut);
|
||||
line('_ENV count', is_array($_ENV) ? count($_ENV) : 'n/a');
|
||||
line('_SERVER selected', [
|
||||
'PATH' => $_SERVER['PATH'] ?? null,
|
||||
'USER' => $_SERVER['USER'] ?? null,
|
||||
'HOME' => $_SERVER['HOME'] ?? null,
|
||||
]);
|
||||
|
||||
section('Self-request capability');
|
||||
$selfUrl = null;
|
||||
if (!empty($_SERVER['HTTP_HOST'])) {
|
||||
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
|
||||
$selfUrl = $scheme . '://' . $_SERVER['HTTP_HOST'] . ($_SERVER['REQUEST_URI'] ?? '/');
|
||||
}
|
||||
line('self url', $selfUrl ?: 'n/a');
|
||||
if ($selfUrl && function_exists('file_get_contents')) {
|
||||
line('self file_get_contents', with_error_capture(function() use ($selfUrl) {
|
||||
$ctx = stream_context_create(['http' => ['timeout' => 2]]);
|
||||
$data = @file_get_contents($selfUrl, false, $ctx);
|
||||
if ($data === false) return false;
|
||||
return 'len=' . strlen($data);
|
||||
}));
|
||||
}
|
||||
|
||||
section('Session basics');
|
||||
$sessionRes = with_error_capture(function() {
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
@session_start();
|
||||
}
|
||||
$_SESSION['audit_test'] = 'ok';
|
||||
return session_id();
|
||||
});
|
||||
line('session.save_path', ini_get('session.save_path'));
|
||||
line('session_start()', $sessionRes);
|
||||
line('session file expected', ini_get('session.save_path') . '/sess_' . session_id());
|
||||
|
||||
section('mail() basics');
|
||||
line('function_exists(mail)', function_exists('mail'));
|
||||
line('sendmail_path', ini_get('sendmail_path'));
|
||||
line('mail() active send test', 'SKIPPED by design');
|
||||
|
||||
section('.user.ini verification hint');
|
||||
$userIniFile = $docRoot . '/.user.ini.audit-test';
|
||||
$userIniContent = <<<TXT
|
||||
; Rename this file to .user.ini for a live test, then wait for user_ini.cache_ttl
|
||||
memory_limit=3072M
|
||||
max_execution_time=900
|
||||
upload_max_filesize=3072M
|
||||
post_max_size=3072M
|
||||
auto_prepend_file=
|
||||
TXT;
|
||||
@file_put_contents($userIniFile, $userIniContent);
|
||||
line('Prepared helper file', $userIniFile);
|
||||
line('How to test .user.ini', 'Rename .user.ini.audit-test -> .user.ini, wait cache_ttl, reload script, compare values.');
|
||||
|
||||
section('Assessment');
|
||||
|
||||
$openBasedir = (string)ini_get('open_basedir');
|
||||
$disableFunctions = (string)ini_get('disable_functions');
|
||||
$userIni = (string)ini_get('user_ini.filename');
|
||||
$allowUrlInclude = (string)ini_get('allow_url_include');
|
||||
|
||||
if ($openBasedir !== '') {
|
||||
add_result('PASS', 'open_basedir is set', $openBasedir);
|
||||
} else {
|
||||
add_result('FAIL', 'open_basedir is empty');
|
||||
}
|
||||
|
||||
if (!empty($overrideResults['memory_limit']['changed'])) {
|
||||
add_result('FAIL', 'memory_limit can be changed via ini_set()', json_encode($overrideResults['memory_limit']));
|
||||
} else {
|
||||
add_result('PASS', 'memory_limit is not changeable via ini_set()');
|
||||
}
|
||||
|
||||
if (!empty($overrideResults['max_execution_time']['changed'])) {
|
||||
add_result('FAIL', 'max_execution_time can be changed via ini_set()', json_encode($overrideResults['max_execution_time']));
|
||||
} else {
|
||||
add_result('PASS', 'max_execution_time is not changeable via ini_set()');
|
||||
}
|
||||
|
||||
if (!empty($overrideResults['upload_max_filesize']['changed'])) {
|
||||
add_result('FAIL', 'upload_max_filesize can be changed via ini_set()', json_encode($overrideResults['upload_max_filesize']));
|
||||
} else {
|
||||
add_result('PASS', 'upload_max_filesize resisted ini_set()');
|
||||
}
|
||||
|
||||
if (!empty($overrideResults['post_max_size']['changed'])) {
|
||||
add_result('FAIL', 'post_max_size can be changed via ini_set()', json_encode($overrideResults['post_max_size']));
|
||||
} else {
|
||||
add_result('PASS', 'post_max_size resisted ini_set()');
|
||||
}
|
||||
|
||||
if (!empty($overrideResults['open_basedir']['changed'])) {
|
||||
add_result('FAIL', 'open_basedir can be changed via ini_set()', json_encode($overrideResults['open_basedir']));
|
||||
} else {
|
||||
add_result('PASS', 'open_basedir resisted ini_set()');
|
||||
}
|
||||
|
||||
$dangerousAvailable = [];
|
||||
foreach (['exec','shell_exec','system','passthru','proc_open','popen'] as $fn) {
|
||||
if (function_exists($fn) && !str_contains($disableFunctions, $fn)) {
|
||||
$dangerousAvailable[] = $fn;
|
||||
}
|
||||
}
|
||||
if ($dangerousAvailable) {
|
||||
add_result('FAIL', 'Command execution functions are available', implode(', ', $dangerousAvailable));
|
||||
} else {
|
||||
add_result('PASS', 'Command execution functions are blocked');
|
||||
}
|
||||
|
||||
if (extension_loaded('pcntl')) {
|
||||
add_result('FAIL', 'pcntl extension is loaded', 'Not recommended for shared hosting web SAPI');
|
||||
} else {
|
||||
add_result('PASS', 'pcntl extension is not loaded');
|
||||
}
|
||||
|
||||
if ($userIni === '' || strtolower($userIni) === 'none') {
|
||||
add_result('PASS', '.user.ini appears disabled');
|
||||
} else {
|
||||
add_result('WARN', '.user.ini appears enabled', $userIni);
|
||||
}
|
||||
|
||||
if ($allowUrlInclude === '' || $allowUrlInclude === '0') {
|
||||
add_result('PASS', 'allow_url_include is off');
|
||||
} else {
|
||||
add_result('FAIL', 'allow_url_include is on');
|
||||
}
|
||||
|
||||
if (is_link($symlinkTarget)) {
|
||||
add_result('WARN', 'Symlink creation works inside allowed path', $symlinkTarget);
|
||||
} else {
|
||||
add_result('PASS', 'Symlink creation did not work');
|
||||
}
|
||||
|
||||
if (file_exists($hardlinkTarget)) {
|
||||
add_result('WARN', 'Hardlink creation works inside allowed path', $hardlinkTarget);
|
||||
} else {
|
||||
add_result('PASS', 'Hardlink creation did not work');
|
||||
}
|
||||
|
||||
$localhostSensitiveOpen = [];
|
||||
foreach (['127.0.0.1:25','127.0.0.1:3306','127.0.0.1:6379','127.0.0.1:7080'] as $k) {
|
||||
if (!empty($netResults[$k]['connected'])) {
|
||||
$localhostSensitiveOpen[] = $k;
|
||||
}
|
||||
}
|
||||
if ($localhostSensitiveOpen) {
|
||||
add_result('WARN', 'Sensitive localhost TCP ports reachable', implode(', ', $localhostSensitiveOpen));
|
||||
} else {
|
||||
add_result('PASS', 'Sensitive localhost TCP ports not reachable');
|
||||
}
|
||||
|
||||
$reachableUnix = [];
|
||||
foreach ($unixResults as $sock => $res) {
|
||||
if (!empty($res['connected'])) {
|
||||
$reachableUnix[] = $sock;
|
||||
}
|
||||
}
|
||||
if ($reachableUnix) {
|
||||
add_result('WARN', 'Sensitive UNIX sockets reachable', implode(', ', $reachableUnix));
|
||||
} else {
|
||||
add_result('PASS', 'Sensitive UNIX sockets not reachable');
|
||||
}
|
||||
|
||||
section('Score');
|
||||
line('PASS', $SCORE['PASS']);
|
||||
line('WARN', $SCORE['WARN']);
|
||||
line('FAIL', $SCORE['FAIL']);
|
||||
|
||||
section('Findings');
|
||||
foreach ($FINDINGS as [$level, $title, $detail]) {
|
||||
echo '[' . $level . '] ' . $title;
|
||||
if ($detail !== '') {
|
||||
echo ' :: ' . $detail;
|
||||
}
|
||||
echo PHP_EOL;
|
||||
}
|
||||
|
||||
section('Cleanup');
|
||||
$cleanupFiles = [
|
||||
$renameTo,
|
||||
$copyToSession,
|
||||
$src,
|
||||
$symlinkTarget,
|
||||
$hardlinkTarget,
|
||||
$includeFile,
|
||||
$userIniFile,
|
||||
];
|
||||
foreach ($cleanupFiles as $f) {
|
||||
if (is_link($f) || file_exists($f)) {
|
||||
@unlink($f);
|
||||
}
|
||||
}
|
||||
@rmdir($baseTmp);
|
||||
|
||||
echo PHP_EOL . "Done." . PHP_EOL;
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
if (is_readable('/var/www/shared/mu-plugins/load.php')) {
|
||||
require_once('/var/www/shared/mu-plugins/load.php');
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Plugin Name: MU Test Plugin
|
||||
* Description: MU Test plugin.
|
||||
* Author: WEDOS
|
||||
* Version: 1.0.0
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
add_action('admin_notices', function () {
|
||||
if (!current_user_can('manage_options')) {
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<div class="notice notice-success is-dismissible">';
|
||||
echo '<p><strong>MU TEST OK</strong> — shared MU plugin.</p>';
|
||||
echo '</div>';
|
||||
});
|
||||
|
||||
add_action('admin_bar_menu', function ($wp_admin_bar) {
|
||||
if (!current_user_can('manage_options')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$wp_admin_bar->add_node([
|
||||
'id' => 'mu-test-ok',
|
||||
'title' => 'MU TEST OK',
|
||||
'href' => admin_url('plugins.php?plugin_status=mustuse'),
|
||||
'meta' => [
|
||||
'title' => 'MU plugin',
|
||||
],
|
||||
]);
|
||||
}, 100);
|
||||
|
||||
add_action('wp_footer', function () {
|
||||
if (!current_user_can('manage_options')) {
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<div style="position:fixed;right:16px;bottom:16px;z-index:99999;padding:10px 14px;background:#16a34a;color:#fff;border-radius:8px;font:14px/1.4 sans-serif;box-shadow:0 4px 16px rgba(0,0,0,.2);">MU TEST OK</div>';
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
if (!defined('SODEW_SMTP_ENABLE') || SODEW_SMTP_ENABLE === true) {
|
||||
if (is_readable(__DIR__ . '/sodew-smtp.php')) {
|
||||
require(__DIR__ . '/sodew-smtp.php');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author Maksym Krugol <maksym.krugol@wedos.org>
|
||||
* @copyright SODEW, s.r.o.
|
||||
*
|
||||
* @wordpress-plugin
|
||||
* Plugin Name: SODEW SMTP config
|
||||
* Plugin URI: https://sodew.ai
|
||||
* Description: SMTP config
|
||||
* Author: SODEW
|
||||
* Author URI: https://sodew.ai
|
||||
* Version: 1.1
|
||||
*/
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
add_action('phpmailer_init', function ($phpmailer) {
|
||||
$domain = wp_parse_url(home_url(), PHP_URL_HOST);
|
||||
$fromName = defined('SODEW_SMTP_FROM_NAME') ? SODEW_SMTP_FROM_NAME : 'WordPress';
|
||||
$replyTo = defined('SODEW_SMTP_REPLY_TO') ? SODEW_SMTP_REPLY_TO : "wordpress@$domain";
|
||||
$replyToName = defined('SODEW_SMTP_REPLY_TO_NAME') ? SODEW_SMTP_REPLY_TO_NAME : $fromName;
|
||||
|
||||
$phpmailer->isSMTP();
|
||||
$phpmailer->XMailer = 'sodewMailer 1.1';
|
||||
$phpmailer->Host = 'postfix';
|
||||
$phpmailer->Port = 587;
|
||||
$phpmailer->SMTPAuth = true;
|
||||
$phpmailer->SMTPSecure = 'tls';
|
||||
$phpmailer->SMTPAutoTLS = true;
|
||||
$phpmailer->SMTPOptions = [
|
||||
'ssl' => [
|
||||
'verify_peer' => true,
|
||||
'verify_peer_name' => true,
|
||||
'peer_name' => SODEW_SMTP_HOST,
|
||||
'allow_self_signed' => true,
|
||||
],
|
||||
];
|
||||
|
||||
$phpmailer->Username = SODEW_SMTP_USER;
|
||||
$phpmailer->Password = SODEW_SMTP_PASS;
|
||||
$phpmailer->setFrom(SODEW_SMTP_POSTMASTER, $fromName, false);
|
||||
$phpmailer->Sender = SODEW_SMTP_POSTMASTER;
|
||||
$phpmailer->clearReplyTos();
|
||||
$phpmailer->addReplyTo($replyTo, $replyToName);
|
||||
});
|
||||
@@ -0,0 +1,269 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import threading
|
||||
import http.server
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlencode
|
||||
from urllib.request import Request, urlopen
|
||||
from urllib.error import URLError, HTTPError
|
||||
from collections.abc import Mapping
|
||||
from urllib.response import addinfourl
|
||||
from typing import cast, Any
|
||||
from datetime import datetime
|
||||
|
||||
class H(http.server.BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
if self.path == "/healthz":
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
self.wfile.write(b"ok")
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
def log_message(self, format, *args):
|
||||
pass
|
||||
|
||||
def int_env(name: str, default: int) -> int:
|
||||
try:
|
||||
return int(os.getenv(name, str(default)))
|
||||
except Exception:
|
||||
return default
|
||||
|
||||
TASKS_PATH = Path("/app/tasks")
|
||||
API_URL = os.getenv("API_URL", "https://api.sodew.ai/api/tasks").rstrip("/")
|
||||
WORKER_UUID = os.getenv("WORKER_UUID", "worker-uuid")
|
||||
WORKER_NAME = os.getenv("WORKER_NAME", "worker-name")
|
||||
WORKER_POOL = os.getenv("WORKER_POOL", "")
|
||||
WORKER_VERSION = "6.3.445"
|
||||
GETTING_PAUSE = int_env("GETTING_PAUSE", 30)
|
||||
SENDING_PAUSE = int_env("SENDING_PAUSE", 15)
|
||||
|
||||
HTTP_TIMEOUT = 15
|
||||
DEFAULT_HEADERS = {"Accept": "application/json", "Content-Type": "application/json", "User-Agent": "kube-worker/1.1"}
|
||||
|
||||
def start_health():
|
||||
t = threading.Thread(target=http.server.HTTPServer(('0.0.0.0', 8080), H).serve_forever, daemon=True)
|
||||
t.start()
|
||||
|
||||
def ensure_dir() -> None:
|
||||
TASKS_PATH.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def log_info(text: str) -> None:
|
||||
print(datetime.now().strftime("[%Y.%m.%d %H:%M:%S]") + f" {text}")
|
||||
|
||||
def format_error_body(body: Any) -> str:
|
||||
if body is None:
|
||||
return "<no body>"
|
||||
|
||||
if isinstance(body, dict):
|
||||
msg = body.get("message")
|
||||
if isinstance(msg, str) and msg.strip():
|
||||
return msg
|
||||
|
||||
try:
|
||||
return json.dumps(body, ensure_ascii=False, sort_keys=True)
|
||||
except Exception:
|
||||
return repr(body)
|
||||
|
||||
if isinstance(body, list):
|
||||
try:
|
||||
return json.dumps(body, ensure_ascii=False, sort_keys=True)
|
||||
except Exception:
|
||||
return repr(body)
|
||||
|
||||
try:
|
||||
return str(body)
|
||||
except Exception:
|
||||
return "<unprintable body>"
|
||||
|
||||
def task_read(path: Path) -> dict[str, str]:
|
||||
result: dict[str, str] = {}
|
||||
for line in path.read_text(encoding="utf-8", errors="ignore").splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
k, v = line.split("=", 1)
|
||||
result[k.strip()] = v.strip()
|
||||
return result
|
||||
|
||||
def task_save(path: Path, data: Mapping[str, object]) -> None:
|
||||
flat: dict[str, object] = dict(data)
|
||||
lines: list[str] = []
|
||||
for key, value in flat.items():
|
||||
if not isinstance(key, str):
|
||||
key = str(key)
|
||||
|
||||
if value is None:
|
||||
value_str = ""
|
||||
elif isinstance(value, (dict, list)):
|
||||
try:
|
||||
value_str = json.dumps(value, ensure_ascii=False)
|
||||
except Exception:
|
||||
value_str = str(value)
|
||||
else:
|
||||
value_str = str(value)
|
||||
|
||||
value_str = value_str.replace("\n", " ").replace("\r", " ")
|
||||
lines.append(f"{key}={value_str}")
|
||||
|
||||
content = "\n".join(lines) + "\n"
|
||||
path.write_text(content, encoding="utf-8")
|
||||
|
||||
def http_request_json(
|
||||
method: str,
|
||||
url: str,
|
||||
*,
|
||||
params: Mapping[str, str] | None = None,
|
||||
json_body: Mapping[str, object] | None = None,
|
||||
headers: Mapping[str, str] | None = None,
|
||||
timeout: int = HTTP_TIMEOUT,
|
||||
) -> tuple[int, object | None]:
|
||||
if params:
|
||||
qs = urlencode(params)
|
||||
url = f"{url}?{qs}"
|
||||
body_bytes = None
|
||||
req_headers = dict(DEFAULT_HEADERS)
|
||||
if headers:
|
||||
req_headers.update(headers)
|
||||
if json_body is not None:
|
||||
body_bytes = json.dumps(json_body).encode("utf-8")
|
||||
req = Request(url, data=body_bytes, headers=req_headers, method=method)
|
||||
try:
|
||||
with urlopen(req, timeout=timeout) as resp:
|
||||
resp_typed = cast(addinfourl, resp)
|
||||
code = resp_typed.getcode() or 0
|
||||
body = resp_typed.read()
|
||||
|
||||
except HTTPError as e:
|
||||
try:
|
||||
err_bytes = e.read()
|
||||
except Exception:
|
||||
err_bytes = b""
|
||||
if not err_bytes:
|
||||
return e.code, None
|
||||
try:
|
||||
return e.code, json.loads(err_bytes.decode("utf-8", errors="replace"))
|
||||
except Exception:
|
||||
return e.code, err_bytes.decode("utf-8", errors="replace")
|
||||
except URLError as e:
|
||||
return 0, {"error": "network", "reason": str(e)}
|
||||
|
||||
if not body:
|
||||
return code, None
|
||||
try:
|
||||
return code, json.loads(body.decode("utf-8", errors="replace"))
|
||||
except Exception:
|
||||
return code, None
|
||||
|
||||
def task_receive() -> dict[str, Any] | None:
|
||||
log_info(f"Receiving new tasks from API | Worker: {WORKER_NAME}")
|
||||
url = f"{API_URL}/receive"
|
||||
payload: dict[str, str] = {
|
||||
"worker_name": WORKER_NAME,
|
||||
"worker_uuid": WORKER_UUID,
|
||||
"worker_version": WORKER_VERSION
|
||||
}
|
||||
if WORKER_POOL and WORKER_POOL.strip():
|
||||
payload["worker_pool"] = WORKER_POOL.strip()
|
||||
|
||||
code, body = http_request_json("POST", url, json_body=payload)
|
||||
|
||||
if code == 204:
|
||||
log_info("Code 204 | No tasks from API")
|
||||
return None
|
||||
|
||||
if code == 200:
|
||||
try:
|
||||
log_info("Code: 200 | Raw data: " + (json.dumps(body, ensure_ascii=False, sort_keys=True) if isinstance(body, (dict, list)) else repr(body)))
|
||||
except Exception:
|
||||
log_info("Code: 200 | Raw data: <unserializable>")
|
||||
|
||||
if isinstance(body, dict):
|
||||
d = cast(dict[str, object], body)
|
||||
try:
|
||||
log_info("Task: received | " + json.dumps(d, ensure_ascii=False, sort_keys=True))
|
||||
except Exception:
|
||||
log_info("Task: received | <unserializable dict>")
|
||||
|
||||
if "uuid" in d and "action" in d:
|
||||
return d
|
||||
|
||||
log_info("Task: missing required fields 'uuid' or 'action'")
|
||||
else:
|
||||
log_info("Task: not recognized (body is not a dict)")
|
||||
else:
|
||||
message_error = format_error_body(body)
|
||||
log_info(f"Code: {code} | Error: {message_error}")
|
||||
|
||||
return None
|
||||
|
||||
def main() -> None:
|
||||
start_health()
|
||||
ensure_dir()
|
||||
|
||||
while True:
|
||||
task_files = list(TASKS_PATH.glob("*.task"))
|
||||
task_count = len(task_files)
|
||||
log_info(f"Task files found: {task_count}")
|
||||
|
||||
if not task_files:
|
||||
task = task_receive()
|
||||
if task:
|
||||
uuid = str(task.get("uuid", "")).strip()
|
||||
action = str(task.get("action", "")).strip()
|
||||
task.pop("uuid", None)
|
||||
task["status"] = "waiting"
|
||||
if uuid and action:
|
||||
log_info(f"Task from API: {uuid}")
|
||||
path = TASKS_PATH / f"{uuid}.task"
|
||||
if not path.exists():
|
||||
task_save(path=path, data=task)
|
||||
else:
|
||||
log_info(f"Task: {uuid} | Error: uuid or action is empty, skip")
|
||||
time.sleep(GETTING_PAUSE)
|
||||
else:
|
||||
for path in task_files:
|
||||
uuid = path.stem
|
||||
try:
|
||||
data = task_read(path)
|
||||
log_info(f"Task: {uuid} | Parse task success")
|
||||
except Exception:
|
||||
log_info(f"Task: {uuid} | Task not recognized")
|
||||
continue
|
||||
action = (data.get("action") or "unknown").strip().lower()
|
||||
if action == "pause":
|
||||
continue
|
||||
status = (data.get("status") or "unknown").strip().lower()
|
||||
|
||||
payload: dict[str, str] = dict(data)
|
||||
payload["worker_uuid"] = WORKER_UUID
|
||||
payload["status"] = status
|
||||
log_info(f"Task: {uuid} | Payload: {json.dumps(payload, ensure_ascii=False)}")
|
||||
url = f"{API_URL}/{uuid}/status"
|
||||
code, body = http_request_json("PATCH", url, json_body=payload)
|
||||
if not (200 <= code < 300):
|
||||
message_error = format_error_body(body)
|
||||
log_info(f"Task: {uuid} | Code: {code} | Error: {message_error}")
|
||||
continue
|
||||
|
||||
if status not in {"new", "waiting", "execution"}:
|
||||
log_info(f"Task: {uuid} | Remove...")
|
||||
try:
|
||||
path.unlink(missing_ok=True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
time.sleep(SENDING_PAUSE)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(f"[ Worker Agent {WORKER_VERSION} | {datetime.now():%Y-%m-%d %H-%M-%S} ______________ ]")
|
||||
print(f"🔹Worker: {WORKER_NAME} | {WORKER_UUID}")
|
||||
print(f"🔹Tasks path: {TASKS_PATH}")
|
||||
print(f"🔹API URL: {API_URL}")
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print("🔥Interrupted by user.")
|
||||
Reference in New Issue
Block a user