-
Notifications
You must be signed in to change notification settings - Fork 4
feat: Operator best practices - Security hardening and GitHub Pages fixes #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
varun-krishnamurthy
wants to merge
33
commits into
main
Choose a base branch
from
feature/operator-best-practices
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
ab5dee5
feat: Implement Kubernetes operator best practices
c1f90f0
feat: Implement comprehensive Kubernetes operator best practices
615531b
Fix unit tests: add Flexible App Installation and Asynchronous Site Dβ¦
varun-krishnamurthy fc44be5
Fix unit tests: improve test setup and ResourceVersion handling
varun-krishnamurthy 75d085d
feat: Implement SiteBackup CRD with full bench backup options support
4acbf42
feat: Enhance SiteBackup CRD with improved error handling
46bd393
docs: Add comprehensive SiteBackup documentation
5949df6
ci: Add E2E testing framework with Kind for PR validation
4bd3382
feat: Add e2e-test target to Makefile for local E2E testing
b00bf8b
feat(backup): Enhance SiteBackup controller and docs
varun-krishnamurthy b9edbde
feat: set up Helm repository on GitHub Pages
varun-krishnamurthy 8489f4f
chore: publish initial Helm chart
varun-krishnamurthy b8e100f
fix: correct import for Job and CronJob in e2e tests
varun-krishnamurthy b347c81
chore: remove unnecessary documentation files
varun-krishnamurthy 994c840
chore: remove gap analysis document
varun-krishnamurthy 3536d61
chore: fixes
varun-krishnamurthy 2fa460f
restore
varun-krishnamurthy 6fef2ee
fix: Docker tag format in CI workflow
8b7ea10
version patch
cff7f5b
fix: Docker build hanging issues in CI
c4be7bc
chore: use commit SHA as Docker image tag only
aa38cf6
feat: implement OpenShift-compatible security context with configurabβ¦
0d58877
docs: update documentation for security context configuration
776d478
Security: Implement minimal privilege model for database access
26930e8
docs: Add credential security enhancement guide
b23672a
feat: Implement secret-based credential handling for site init and deβ¦
aea5882
fix: resolve GitHub Pages configuration conflicts
5cf999f
docs: optimize README for quick start and redirect to docs site
ac3b21c
Initial plan (#7)
Copilot 20c220e
fix: helm chart publishing pipeline and security context configuration
varun-krishnamurthy dc8b83d
Merge remote changes from feature/operator-best-practices
varun-krishnamurthy ad77634
fix: remove duplicate int64Ptr function declaration
22ce03d
Merge branch 'main' into feature/operator-best-practices
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| package controllers | ||
|
|
||
| import ( | ||
| "os" | ||
| "strconv" | ||
|
|
||
| corev1 "k8s.io/api/core/v1" | ||
|
|
||
| vyogotechv1alpha1 "github.com/vyogotech/frappe-operator/api/v1alpha1" | ||
| ) | ||
|
|
||
| const ( | ||
| // Default UID/GID for Frappe containers (non-root user) | ||
| defaultRunAsUserID int64 = 1001 | ||
| defaultRunAsGroupID int64 = 1001 | ||
| defaultFSGroupID int64 = 1001 | ||
|
|
||
| // Environment variable names for operator-level security context configuration | ||
| envRunAsUserID = "FRAPPE_RUN_AS_USER" | ||
| envRunAsGroupID = "FRAPPE_RUN_AS_GROUP" | ||
| envFSGroupID = "FRAPPE_FS_GROUP" | ||
| ) | ||
|
|
||
| // getConfiguredSecurityIDs returns the configured UID/GID values | ||
| // Priority: environment variables > defaults | ||
| func getConfiguredSecurityIDs() (userID, groupID, fsGroupID int64) { | ||
| userID = defaultRunAsUserID | ||
| groupID = defaultRunAsGroupID | ||
| fsGroupID = defaultFSGroupID | ||
|
|
||
| // Override from environment if set | ||
| if envUser := os.Getenv(envRunAsUserID); envUser != "" { | ||
| if parsed, err := strconv.ParseInt(envUser, 10, 64); err == nil { | ||
| userID = parsed | ||
| } | ||
| } | ||
|
|
||
| if envGroup := os.Getenv(envRunAsGroupID); envGroup != "" { | ||
| if parsed, err := strconv.ParseInt(envGroup, 10, 64); err == nil { | ||
| groupID = parsed | ||
| } | ||
| } | ||
|
|
||
| if envFS := os.Getenv(envFSGroupID); envFS != "" { | ||
| if parsed, err := strconv.ParseInt(envFS, 10, 64); err == nil { | ||
| fsGroupID = parsed | ||
| } | ||
| } | ||
|
|
||
| return | ||
| } | ||
|
|
||
| // applyBenchSecurityDefaults ensures that Pods created by the operator adhere to the | ||
| // minimum security posture required by hardened clusters while still allowing | ||
| // benches to override the defaults through spec.security. | ||
| func applyBenchSecurityDefaults(podSpec *corev1.PodSpec, bench *vyogotechv1alpha1.FrappeBench) { | ||
| if podSpec == nil { | ||
| return | ||
| } | ||
|
|
||
| defaultPodContext := getBenchPodSecurityContext(bench) | ||
| if podSpec.SecurityContext == nil { | ||
| podSpec.SecurityContext = defaultPodContext | ||
| } else { | ||
| mergePodSecurityContext(podSpec.SecurityContext, defaultPodContext) | ||
| } | ||
|
|
||
| for i := range podSpec.InitContainers { | ||
| ensureContainerSecurityContext(&podSpec.InitContainers[i], bench) | ||
| } | ||
|
|
||
| for i := range podSpec.Containers { | ||
| ensureContainerSecurityContext(&podSpec.Containers[i], bench) | ||
| } | ||
| } | ||
|
|
||
| func ensureContainerSecurityContext(container *corev1.Container, bench *vyogotechv1alpha1.FrappeBench) { | ||
| if container == nil { | ||
| return | ||
| } | ||
|
|
||
| defaultCtx := getBenchContainerSecurityContext(bench) | ||
| if container.SecurityContext == nil { | ||
| container.SecurityContext = defaultCtx | ||
| return | ||
| } | ||
|
|
||
| mergeContainerSecurityContext(container.SecurityContext, defaultCtx) | ||
| } | ||
|
|
||
| func getBenchPodSecurityContext(bench *vyogotechv1alpha1.FrappeBench) *corev1.PodSecurityContext { | ||
| if bench != nil && bench.Spec.Security != nil && bench.Spec.Security.PodSecurityContext != nil { | ||
| return bench.Spec.Security.PodSecurityContext.DeepCopy() | ||
| } | ||
|
|
||
| userID, groupID, fsGroupID := getConfiguredSecurityIDs() | ||
|
|
||
| return &corev1.PodSecurityContext{ | ||
| RunAsUser: int64Ptr(userID), | ||
| RunAsGroup: int64Ptr(groupID), | ||
| FSGroup: int64Ptr(fsGroupID), | ||
| SeccompProfile: &corev1.SeccompProfile{ | ||
| Type: corev1.SeccompProfileTypeRuntimeDefault, | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| func getBenchContainerSecurityContext(bench *vyogotechv1alpha1.FrappeBench) *corev1.SecurityContext { | ||
| if bench != nil && bench.Spec.Security != nil && bench.Spec.Security.SecurityContext != nil { | ||
| return bench.Spec.Security.SecurityContext.DeepCopy() | ||
| } | ||
|
|
||
| userID, groupID, _ := getConfiguredSecurityIDs() | ||
|
|
||
| return &corev1.SecurityContext{ | ||
| RunAsUser: int64Ptr(userID), | ||
| RunAsGroup: int64Ptr(groupID), | ||
| AllowPrivilegeEscalation: boolPtr(false), | ||
| Capabilities: &corev1.Capabilities{ | ||
| Drop: []corev1.Capability{"ALL"}, | ||
| }, | ||
| ReadOnlyRootFilesystem: boolPtr(false), | ||
| } | ||
| } | ||
|
|
||
| func mergePodSecurityContext(target, defaults *corev1.PodSecurityContext) { | ||
| if target == nil || defaults == nil { | ||
| return | ||
| } | ||
|
|
||
| if target.RunAsUser == nil { | ||
| target.RunAsUser = defaults.RunAsUser | ||
| } | ||
|
|
||
| if target.RunAsGroup == nil { | ||
| target.RunAsGroup = defaults.RunAsGroup | ||
| } | ||
|
|
||
| if target.FSGroup == nil { | ||
| target.FSGroup = defaults.FSGroup | ||
| } | ||
|
|
||
| if target.SeccompProfile == nil && defaults.SeccompProfile != nil { | ||
| target.SeccompProfile = defaults.SeccompProfile.DeepCopy() | ||
| } | ||
| } | ||
|
|
||
| func mergeContainerSecurityContext(target, defaults *corev1.SecurityContext) { | ||
| if target == nil || defaults == nil { | ||
| return | ||
| } | ||
|
|
||
| if target.RunAsUser == nil { | ||
| target.RunAsUser = defaults.RunAsUser | ||
| } | ||
|
|
||
| if target.RunAsGroup == nil { | ||
| target.RunAsGroup = defaults.RunAsGroup | ||
| } | ||
|
|
||
| if target.AllowPrivilegeEscalation == nil { | ||
| target.AllowPrivilegeEscalation = defaults.AllowPrivilegeEscalation | ||
| } | ||
|
|
||
| if target.ReadOnlyRootFilesystem == nil { | ||
| target.ReadOnlyRootFilesystem = defaults.ReadOnlyRootFilesystem | ||
| } | ||
|
|
||
| if target.SeccompProfile == nil && defaults.SeccompProfile != nil { | ||
| target.SeccompProfile = defaults.SeccompProfile.DeepCopy() | ||
| } | ||
|
|
||
| if target.Capabilities == nil && defaults.Capabilities != nil { | ||
| target.Capabilities = defaults.Capabilities.DeepCopy() | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The environment variable names and default values here are inconsistent with the existing implementation in utils.go. The existing code uses FRAPPE_DEFAULT_UID, FRAPPE_DEFAULT_GID, and FRAPPE_DEFAULT_FSGROUP with defaults of 1001/0/0 (OpenShift-compatible), while this new code uses FRAPPE_RUN_AS_USER, FRAPPE_RUN_AS_GROUP, and FRAPPE_FS_GROUP with defaults of 1001/1001/1001. This creates confusion about which environment variables operators should use and results in different security contexts depending on which code path is used. Consider either using the existing functions from utils.go or consolidating these approaches into a single consistent implementation.