Add egress proxy to user-app Kaniko build jobs.
Inject registry-egress-proxy into Kaniko and network init containers so npm/apk/composer/pip/git clone work on restricted egress clusters. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -123,4 +123,8 @@ PLATFORM_DOMAIN / preview domain from the first entry only. The panel host
|
|||||||
value: {{ .Values.build.images.alpineGit | quote }}
|
value: {{ .Values.build.images.alpineGit | quote }}
|
||||||
- name: BASE_IMAGE_REGISTRY
|
- name: BASE_IMAGE_REGISTRY
|
||||||
value: {{ .Values.build.baseImageRegistry | quote }}
|
value: {{ .Values.build.baseImageRegistry | quote }}
|
||||||
|
{{- if .Values.build.egressProxySecret }}
|
||||||
|
- name: BUILD_EGRESS_PROXY_SECRET
|
||||||
|
value: {{ .Values.build.egressProxySecret | quote }}
|
||||||
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ build:
|
|||||||
alpineGit: registry.abrban.com/proxy-dockerhub/alpine/git:2.43.0
|
alpineGit: registry.abrban.com/proxy-dockerhub/alpine/git:2.43.0
|
||||||
# Prefix for Docker Hub images in generated user-app Dockerfiles (node, php, …)
|
# Prefix for Docker Hub images in generated user-app Dockerfiles (node, php, …)
|
||||||
baseImageRegistry: registry.abrban.com/proxy-dockerhub/library
|
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:
|
postgres:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|||||||
@@ -137,4 +137,29 @@ describe('BuildService', () => {
|
|||||||
expect(dockerfile).toContain('dotnet publish "$CSPROJ"');
|
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);
|
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
|
* Git branch names come from users and end up in a shell command — accept
|
||||||
* only conservative ref characters and reject anything option-like.
|
* 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
|
// Add init container that unzips the source code from PVC
|
||||||
initContainers.push({
|
initContainers.push(this.withEgressProxy({
|
||||||
name: 'unzip-source',
|
name: 'unzip-source',
|
||||||
image: this.resolveBuildImage('alpine', 'alpine:3.19'),
|
image: this.resolveBuildImage('alpine', 'alpine:3.19'),
|
||||||
imagePullPolicy: 'IfNotPresent',
|
imagePullPolicy: 'IfNotPresent',
|
||||||
@@ -607,7 +623,7 @@ export class BuildService {
|
|||||||
},
|
},
|
||||||
{ name: 'source-pvc', mountPath: '/source-pvc' },
|
{ name: 'source-pvc', mountPath: '/source-pvc' },
|
||||||
],
|
],
|
||||||
});
|
}));
|
||||||
} else if (hasGitUrl) {
|
} else if (hasGitUrl) {
|
||||||
// Validate user-controlled values before they get anywhere near a shell.
|
// Validate user-controlled values before they get anywhere near a shell.
|
||||||
this.assertSafeGitUrl(app.gitUrl!);
|
this.assertSafeGitUrl(app.gitUrl!);
|
||||||
@@ -632,7 +648,7 @@ export class BuildService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clone git repo into /workspace/source, then copy our generated Dockerfile
|
// Clone git repo into /workspace/source, then copy our generated Dockerfile
|
||||||
initContainers.push({
|
initContainers.push(this.withEgressProxy({
|
||||||
name: 'git-clone',
|
name: 'git-clone',
|
||||||
image: this.resolveBuildImage('alpineGit', 'alpine/git:2.43.0'),
|
image: this.resolveBuildImage('alpineGit', 'alpine/git:2.43.0'),
|
||||||
imagePullPolicy: 'IfNotPresent',
|
imagePullPolicy: 'IfNotPresent',
|
||||||
@@ -670,7 +686,7 @@ export class BuildService {
|
|||||||
{ name: 'workspace', mountPath: '/workspace-out' },
|
{ name: 'workspace', mountPath: '/workspace-out' },
|
||||||
{ name: 'dockerfile', mountPath: '/dockerfile' },
|
{ name: 'dockerfile', mountPath: '/dockerfile' },
|
||||||
],
|
],
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kaniko container volume mounts
|
// Kaniko container volume mounts
|
||||||
@@ -719,7 +735,7 @@ export class BuildService {
|
|||||||
serviceAccountName: this.configService.get<string>('build.serviceAccount'),
|
serviceAccountName: this.configService.get<string>('build.serviceAccount'),
|
||||||
initContainers: initContainers.length > 0 ? initContainers : undefined,
|
initContainers: initContainers.length > 0 ? initContainers : undefined,
|
||||||
containers: [
|
containers: [
|
||||||
{
|
this.withEgressProxy({
|
||||||
name: 'kaniko',
|
name: 'kaniko',
|
||||||
image: this.getKanikoImage(),
|
image: this.getKanikoImage(),
|
||||||
imagePullPolicy: 'IfNotPresent',
|
imagePullPolicy: 'IfNotPresent',
|
||||||
@@ -735,7 +751,7 @@ export class BuildService {
|
|||||||
memory: this.configService.get<string>('build.kaniko.memoryLimit') || '4Gi',
|
memory: this.configService.get<string>('build.kaniko.memoryLimit') || '4Gi',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
],
|
],
|
||||||
restartPolicy: 'Never',
|
restartPolicy: 'Never',
|
||||||
volumes,
|
volumes,
|
||||||
|
|||||||
@@ -150,6 +150,12 @@ export default () => ({
|
|||||||
alpine: (process.env.BUILD_ALPINE_IMAGE || 'registry.abrban.com/proxy-dockerhub/library/alpine:3.19').trim(),
|
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(),
|
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 build container resources — tune for large images. */
|
||||||
kaniko: {
|
kaniko: {
|
||||||
cpuRequest: process.env.KANIKO_CPU_REQUEST || '500m',
|
cpuRequest: process.env.KANIKO_CPU_REQUEST || '500m',
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ build:
|
|||||||
alpine: registry.abrban.com/proxy-dockerhub/library/alpine:3.19
|
alpine: registry.abrban.com/proxy-dockerhub/library/alpine:3.19
|
||||||
alpineGit: registry.abrban.com/proxy-dockerhub/alpine/git:2.43.0
|
alpineGit: registry.abrban.com/proxy-dockerhub/alpine/git:2.43.0
|
||||||
baseImageRegistry: registry.abrban.com/proxy-dockerhub/library
|
baseImageRegistry: registry.abrban.com/proxy-dockerhub/library
|
||||||
|
# Kaniko + init containers (npm/apk/composer/pip/git clone) on restricted egress.
|
||||||
|
egressProxySecret: registry-egress-proxy
|
||||||
|
|
||||||
postgres:
|
postgres:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|||||||
Reference in New Issue
Block a user