Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ec4d07939 | |||
| 3d773a4a62 | |||
| b2ecdad53b | |||
| a2fe61b1f6 |
@@ -1,4 +1,3 @@
|
||||
{{- $auth := printf "elastic:%s" .Values.elasticPassword | b64enc }}
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
@@ -74,22 +73,20 @@ spec:
|
||||
- name: es-data
|
||||
mountPath: /usr/share/elasticsearch/data
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /_cluster/health?local=true
|
||||
port: 9200
|
||||
httpHeaders:
|
||||
- name: Authorization
|
||||
value: Basic {{ $auth }}
|
||||
exec:
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- curl -sf -u "elastic:${ELASTIC_PASSWORD}" http://127.0.0.1:9200/_cluster/health?local=true
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /_cluster/health?local=true
|
||||
port: 9200
|
||||
httpHeaders:
|
||||
- name: Authorization
|
||||
value: Basic {{ $auth }}
|
||||
exec:
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- curl -sf -u "elastic:${ELASTIC_PASSWORD}" http://127.0.0.1:9200/_cluster/health?local=true
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 30
|
||||
timeoutSeconds: 10
|
||||
|
||||
@@ -123,4 +123,8 @@ PLATFORM_DOMAIN / preview domain from the first entry only. The panel host
|
||||
value: {{ .Values.build.images.alpineGit | quote }}
|
||||
- name: BASE_IMAGE_REGISTRY
|
||||
value: {{ .Values.build.baseImageRegistry | quote }}
|
||||
{{- if .Values.build.egressProxySecret }}
|
||||
- name: BUILD_EGRESS_PROXY_SECRET
|
||||
value: {{ .Values.build.egressProxySecret | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -98,8 +98,33 @@ spec:
|
||||
secretKeyRef:
|
||||
name: {{ include "cloudhost-platform.secretName" . }}
|
||||
key: cluster-kubeconfig-key
|
||||
- name: ELASTIC_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "cloudhost-platform.secretName" . }}
|
||||
key: elastic-password
|
||||
- name: FRONTEND_URL
|
||||
value: {{ include "cloudhost-platform.corsOrigins" . | quote }}
|
||||
{{- if .Values.backend.sms.enabled }}
|
||||
- name: SMS_PROVIDER
|
||||
value: {{ .Values.backend.sms.provider | default "mizbansms" | quote }}
|
||||
- name: MIZBANSMS_FROM
|
||||
value: {{ .Values.backend.sms.from | default "5000467254" | quote }}
|
||||
- name: MIZBANSMS_API
|
||||
value: {{ .Values.backend.sms.api | default "2016" | quote }}
|
||||
- name: MIZBANSMS_USERTYPE
|
||||
value: {{ .Values.backend.sms.userType | default "2" | quote }}
|
||||
- name: MIZBANSMS_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "cloudhost-platform.secretName" . }}
|
||||
key: mizbansms-username
|
||||
- name: MIZBANSMS_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "cloudhost-platform.secretName" . }}
|
||||
key: mizbansms-password
|
||||
{{- end }}
|
||||
{{- include "cloudhost-platform.buildEnv" . | nindent 12 }}
|
||||
{{- range $key, $val := .Values.backend.env }}
|
||||
- name: {{ $key }}
|
||||
|
||||
@@ -37,6 +37,9 @@ build:
|
||||
alpineGit: registry.abrban.com/proxy-dockerhub/alpine/git:2.43.0
|
||||
# Prefix for Docker Hub images in generated user-app Dockerfiles (node, php, …)
|
||||
baseImageRegistry: registry.abrban.com/proxy-dockerhub/library
|
||||
# Secret with HTTP_PROXY/HTTPS_PROXY for Kaniko build jobs (npm, apk, git clone).
|
||||
# Set to registry-egress-proxy in production; leave empty when nodes have direct egress.
|
||||
egressProxySecret: ""
|
||||
|
||||
postgres:
|
||||
enabled: true
|
||||
@@ -89,6 +92,13 @@ backend:
|
||||
cpu: "2"
|
||||
memory: 2Gi
|
||||
extraEnv: {}
|
||||
# OTP SMS — credentials live in the platform Secret (mizbansms-username/password).
|
||||
sms:
|
||||
enabled: false
|
||||
provider: mizbansms
|
||||
from: "5000467254"
|
||||
api: "2016"
|
||||
userType: "2"
|
||||
env:
|
||||
NODE_ENV: production
|
||||
PORT: "4000"
|
||||
@@ -124,7 +134,7 @@ secrets:
|
||||
# Use a pre-created Secret instead of chart-managed one. Required for GitOps
|
||||
# (Argo CD renders with `helm template`, so lookup/randAlphaNum regenerate on
|
||||
# every sync). Secret must contain keys: postgres-password, jwt-secret,
|
||||
# jwt-refresh-secret, cluster-kubeconfig-key.
|
||||
# jwt-refresh-secret, cluster-kubeconfig-key, redis-password, elastic-password.
|
||||
existingSecret: ""
|
||||
jwtSecret: ""
|
||||
jwtRefreshSecret: ""
|
||||
|
||||
@@ -181,24 +181,20 @@ spec:
|
||||
- name: data
|
||||
mountPath: /usr/share/elasticsearch/data
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /_cluster/health?local=true
|
||||
port: 9200
|
||||
scheme: HTTP
|
||||
httpHeaders:
|
||||
- name: Authorization
|
||||
value: "Basic ZWxhc3RpYzpDbG91ZEhvc3QyMDI0IVNlY3VyZQ=="
|
||||
exec:
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- curl -sf -u "elastic:${ELASTIC_PASSWORD}" http://127.0.0.1:9200/_cluster/health?local=true
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /_cluster/health?local=true
|
||||
port: 9200
|
||||
scheme: HTTP
|
||||
httpHeaders:
|
||||
- name: Authorization
|
||||
value: "Basic ZWxhc3RpYzpDbG91ZEhvc3QyMDI0IVNlY3VyZQ=="
|
||||
exec:
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- curl -sf -u "elastic:${ELASTIC_PASSWORD}" http://127.0.0.1:9200/_cluster/health?local=true
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 30
|
||||
timeoutSeconds: 10
|
||||
|
||||
@@ -137,4 +137,29 @@ describe('BuildService', () => {
|
||||
expect(dockerfile).toContain('dotnet publish "$CSPROJ"');
|
||||
});
|
||||
});
|
||||
|
||||
describe('egressProxyEnvFrom', () => {
|
||||
it('returns secretRef when BUILD_EGRESS_PROXY_SECRET is set', () => {
|
||||
const config = (service as any).configService as { get: jest.Mock };
|
||||
config.get.mockImplementation((key: string) => {
|
||||
if (key === 'build.egressProxySecret') return 'registry-egress-proxy';
|
||||
return undefined;
|
||||
});
|
||||
|
||||
expect((service as any).egressProxyEnvFrom()).toEqual([
|
||||
{ secretRef: { name: 'registry-egress-proxy' } },
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns undefined when egress proxy is disabled', () => {
|
||||
const config = (service as any).configService as { get: jest.Mock };
|
||||
config.get.mockImplementation((key: string) => {
|
||||
if (key === 'build.egressProxySecret') return '';
|
||||
return undefined;
|
||||
});
|
||||
|
||||
expect((service as any).egressProxyEnvFrom()).toBeUndefined();
|
||||
expect((service as any).withEgressProxy({ name: 'kaniko' })).toEqual({ name: 'kaniko' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -91,6 +91,22 @@ export class BuildService {
|
||||
return this.baseImage(dockerHubFallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Egress HTTP(S) proxy for build pods on restricted networks (Iran).
|
||||
* Kaniko forwards these env vars into Dockerfile RUN steps (npm, apk, composer, pip).
|
||||
*/
|
||||
private egressProxyEnvFrom(): k8s.V1EnvFromSource[] | undefined {
|
||||
const secretName = this.configService.get<string>('build.egressProxySecret');
|
||||
if (!secretName?.trim()) return undefined;
|
||||
return [{ secretRef: { name: secretName.trim() } }];
|
||||
}
|
||||
|
||||
private withEgressProxy<T extends Record<string, unknown>>(container: T): T {
|
||||
const envFrom = this.egressProxyEnvFrom();
|
||||
if (!envFrom) return container;
|
||||
return { ...container, envFrom };
|
||||
}
|
||||
|
||||
/**
|
||||
* Git branch names come from users and end up in a shell command — accept
|
||||
* only conservative ref characters and reject anything option-like.
|
||||
@@ -552,7 +568,7 @@ export class BuildService {
|
||||
});
|
||||
|
||||
// Add init container that unzips the source code from PVC
|
||||
initContainers.push({
|
||||
initContainers.push(this.withEgressProxy({
|
||||
name: 'unzip-source',
|
||||
image: this.resolveBuildImage('alpine', 'alpine:3.19'),
|
||||
imagePullPolicy: 'IfNotPresent',
|
||||
@@ -607,7 +623,7 @@ export class BuildService {
|
||||
},
|
||||
{ name: 'source-pvc', mountPath: '/source-pvc' },
|
||||
],
|
||||
});
|
||||
}));
|
||||
} else if (hasGitUrl) {
|
||||
// Validate user-controlled values before they get anywhere near a shell.
|
||||
this.assertSafeGitUrl(app.gitUrl!);
|
||||
@@ -632,7 +648,7 @@ export class BuildService {
|
||||
}
|
||||
|
||||
// Clone git repo into /workspace/source, then copy our generated Dockerfile
|
||||
initContainers.push({
|
||||
initContainers.push(this.withEgressProxy({
|
||||
name: 'git-clone',
|
||||
image: this.resolveBuildImage('alpineGit', 'alpine/git:2.43.0'),
|
||||
imagePullPolicy: 'IfNotPresent',
|
||||
@@ -670,7 +686,7 @@ export class BuildService {
|
||||
{ name: 'workspace', mountPath: '/workspace-out' },
|
||||
{ name: 'dockerfile', mountPath: '/dockerfile' },
|
||||
],
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
// Kaniko container volume mounts
|
||||
@@ -719,7 +735,7 @@ export class BuildService {
|
||||
serviceAccountName: this.configService.get<string>('build.serviceAccount'),
|
||||
initContainers: initContainers.length > 0 ? initContainers : undefined,
|
||||
containers: [
|
||||
{
|
||||
this.withEgressProxy({
|
||||
name: 'kaniko',
|
||||
image: this.getKanikoImage(),
|
||||
imagePullPolicy: 'IfNotPresent',
|
||||
@@ -735,7 +751,7 @@ export class BuildService {
|
||||
memory: this.configService.get<string>('build.kaniko.memoryLimit') || '4Gi',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
restartPolicy: 'Never',
|
||||
volumes,
|
||||
|
||||
@@ -150,6 +150,12 @@ export default () => ({
|
||||
alpine: (process.env.BUILD_ALPINE_IMAGE || 'registry.abrban.com/proxy-dockerhub/library/alpine:3.19').trim(),
|
||||
alpineGit: (process.env.BUILD_ALPINE_GIT_IMAGE || 'registry.abrban.com/proxy-dockerhub/alpine/git:2.43.0').trim(),
|
||||
},
|
||||
/**
|
||||
* Secret name with HTTP_PROXY / HTTPS_PROXY / NO_PROXY for build pods
|
||||
* (Kaniko RUN steps: npm, apk, composer, pip; init containers: apk, git clone).
|
||||
* Empty = disabled (clusters with direct egress).
|
||||
*/
|
||||
egressProxySecret: (process.env.BUILD_EGRESS_PROXY_SECRET || '').trim(),
|
||||
/** Kaniko build container resources — tune for large images. */
|
||||
kaniko: {
|
||||
cpuRequest: process.env.KANIKO_CPU_REQUEST || '500m',
|
||||
|
||||
@@ -35,6 +35,8 @@ build:
|
||||
alpine: registry.abrban.com/proxy-dockerhub/library/alpine:3.19
|
||||
alpineGit: registry.abrban.com/proxy-dockerhub/alpine/git:2.43.0
|
||||
baseImageRegistry: registry.abrban.com/proxy-dockerhub/library
|
||||
# Kaniko + init containers (npm/apk/composer/pip/git clone) on restricted egress.
|
||||
egressProxySecret: registry-egress-proxy
|
||||
|
||||
postgres:
|
||||
enabled: true
|
||||
@@ -112,6 +114,13 @@ backend:
|
||||
KIBANA_SYSTEM_PASSWORD: "CHANGE_VIA_SEALEDSECRET_OR_KUBECTL"
|
||||
# Swagger disabled in production unless explicitly enabled
|
||||
# SWAGGER_ENABLED: "true"
|
||||
# OTP SMS — username/password in abrban-platform-secrets (SealedSecret).
|
||||
sms:
|
||||
enabled: true
|
||||
provider: mizbansms
|
||||
from: "5000467254"
|
||||
api: "2016"
|
||||
userType: "2"
|
||||
|
||||
frontend:
|
||||
enabled: true
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
# Real SealedSecret lives in cloud-host-gitops/sealed-secrets/ — never commit plaintext passwords.
|
||||
#
|
||||
# Required keys (must match backend Deployment + validate-production-config):
|
||||
# postgres-password, jwt-secret, jwt-refresh-secret, cluster-kubeconfig-key, redis-password
|
||||
# postgres-password, jwt-secret, jwt-refresh-secret, cluster-kubeconfig-key,
|
||||
# redis-password, elastic-password (must match elasticsearch-credentials in logging),
|
||||
# mizbansms-username, mizbansms-password (OTP SMS — required when backend.sms.enabled)
|
||||
#
|
||||
# Generate (replace CHANGE_ME_* with strong random values):
|
||||
#
|
||||
@@ -13,6 +15,9 @@
|
||||
# --from-literal=jwt-refresh-secret='CHANGE_ME_REFRESH_32CHARS_MIN' \
|
||||
# --from-literal=cluster-kubeconfig-key='0123456789abcdef0123456789abcdef' \
|
||||
# --from-literal=redis-password='CHANGE_ME_REDIS' \
|
||||
# --from-literal=elastic-password='CHANGE_ME_ELASTIC' \
|
||||
# --from-literal=mizbansms-username='CHANGE_ME_SMS_USER' \
|
||||
# --from-literal=mizbansms-password='CHANGE_ME_SMS_PASS' \
|
||||
# --dry-run=client -o json \
|
||||
# | kubeseal \
|
||||
# --controller-name=sealed-secrets-controller \
|
||||
|
||||
Reference in New Issue
Block a user