Skip to content

Commit ad99aa5

Browse files
anandfsvghadi
andauthored
fix for setting GODEBUG environment variable to fips140=on in a FIPS cluster (#1822) (#1873)
* Fix for setting GODEBUG env var to on for repo server deployments if FIPS enabled Signed-off-by: Anand Francis Joseph <anjoseph@redhat.com> * Fixed failing test compilation Signed-off-by: Anand Francis Joseph <anjoseph@redhat.com> * Removed additional spaces for formatting Signed-off-by: Anand Francis Joseph <anjoseph@redhat.com> * Fix linting Signed-off-by: Siddhesh Ghadi <sghadi1203@gmail.com> * fix lint Signed-off-by: Siddhesh Ghadi <sghadi1203@gmail.com> --------- Signed-off-by: Anand Francis Joseph <anjoseph@redhat.com> Signed-off-by: Siddhesh Ghadi <sghadi1203@gmail.com> Co-authored-by: Siddhesh Ghadi <sghadi1203@gmail.com>
1 parent 909b13b commit ad99aa5

File tree

6 files changed

+357
-4
lines changed

6 files changed

+357
-4
lines changed

cmd/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"github.com/argoproj-labs/argocd-operator/common"
3838
"github.com/argoproj-labs/argocd-operator/controllers/argocd"
3939
"github.com/argoproj-labs/argocd-operator/controllers/argocdexport"
40+
"github.com/argoproj-labs/argocd-operator/controllers/argoutil"
4041

4142
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
4243

@@ -243,6 +244,7 @@ func main() {
243244
LocalUsers: &argocd.LocalUsersInfo{
244245
TokenRenewalTimers: map[string]*argocd.TokenRenewalTimer{},
245246
},
247+
FipsConfigChecker: argoutil.NewLinuxFipsConfigChecker(),
246248
}).SetupWithManager(mgr); err != nil {
247249
setupLog.Error(err, "unable to create controller", "controller", "ArgoCD")
248250
os.Exit(1)

controllers/argocd/argocd_controller.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ import (
2626

2727
argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1"
2828
"github.com/argoproj-labs/argocd-operator/common"
29+
"github.com/argoproj-labs/argocd-operator/controllers/argoutil"
2930

3031
corev1 "k8s.io/api/core/v1"
3132
"k8s.io/apimachinery/pkg/api/errors"
3233
"k8s.io/apimachinery/pkg/labels"
3334
"k8s.io/apimachinery/pkg/runtime"
3435
"k8s.io/apimachinery/pkg/types"
35-
3636
"k8s.io/client-go/kubernetes"
3737

3838
ctrl "sigs.k8s.io/controller-runtime"
@@ -83,6 +83,8 @@ type ReconcileArgoCD struct {
8383

8484
K8sClient kubernetes.Interface
8585
LocalUsers *LocalUsersInfo
86+
// FipsConfigChecker checks if the deployment needs FIPS specific environment variables set.
87+
FipsConfigChecker argoutil.FipsConfigChecker
8688
}
8789

8890
var log = logr.Log.WithName("controller_argocd")
@@ -131,9 +133,9 @@ func (r *ReconcileArgoCD) Reconcile(ctx context.Context, request ctrl.Request) (
131133
message = err.Error()
132134
}
133135

134-
if error := updateStatusConditionOfArgoCD(ctx, createCondition(message), argocd, r.Client, log); error != nil {
135-
log.Error(error, "unable to update status of ArgoCD")
136-
return reconcile.Result{}, error
136+
if err := updateStatusConditionOfArgoCD(ctx, createCondition(message), argocd, r.Client, log); err != nil {
137+
log.Error(err, "unable to update status of ArgoCD")
138+
return reconcile.Result{}, err
137139
}
138140

139141
return result, err

controllers/argocd/deployment.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,21 @@ func (r *ReconcileArgoCD) reconcileRepoDeployment(cr *argoproj.ArgoCD, useTLSFor
10041004
repoEnv = argoutil.EnvMerge(repoEnv, []corev1.EnvVar{{Name: "ARGOCD_EXEC_TIMEOUT", Value: fmt.Sprintf("%ds", *cr.Spec.Repo.ExecTimeout)}}, true)
10051005
}
10061006

1007+
// if running in a FIPS enabled host, set the GODEBUG env wit appropriate value
1008+
fipsConfigChecker := r.FipsConfigChecker
1009+
if fipsConfigChecker != nil {
1010+
fipsEnabled, err := fipsConfigChecker.IsFipsEnabled()
1011+
if err != nil {
1012+
return err
1013+
}
1014+
if fipsEnabled {
1015+
repoEnv = append(repoEnv, corev1.EnvVar{
1016+
Name: "GODEBUG",
1017+
Value: "fips140=on",
1018+
})
1019+
}
1020+
}
1021+
10071022
AddSeccompProfileForOpenShift(r.Client, &deploy.Spec.Template.Spec)
10081023

10091024
deploy.Spec.Template.Spec.InitContainers = []corev1.Container{{

controllers/argocd/deployment_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@ var (
4242
"argocd-server"}
4343
)
4444

45+
type MockTrueFipsChecker struct{}
46+
47+
func (a *MockTrueFipsChecker) IsFipsEnabled() (bool, error) {
48+
return true, nil
49+
}
50+
51+
type MockFalseFipsChecker struct{}
52+
53+
func (a *MockFalseFipsChecker) IsFipsEnabled() (bool, error) {
54+
return false, nil
55+
}
56+
4557
func TestReconcileArgoCD_reconcileRepoDeployment_replicas(t *testing.T) {
4658
logf.SetLogger(ZapLogger(true))
4759

@@ -2877,3 +2889,72 @@ func TestSetReplicasAndEnvVar_WhenServerReplicasIsDefined(t *testing.T) {
28772889
})
28782890

28792891
}
2892+
2893+
func TestReconcileArgoCD_reconcileRepoServerWithFipsEnabled(t *testing.T) {
2894+
cr := makeTestArgoCD()
2895+
2896+
resObjs := []client.Object{cr}
2897+
subresObjs := []client.Object{cr}
2898+
runtimeObjs := []runtime.Object{}
2899+
sch := makeTestReconcilerScheme(argoproj.AddToScheme)
2900+
cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
2901+
r := makeTestReconciler(cl, sch, testclient.NewSimpleClientset())
2902+
r.FipsConfigChecker = &MockTrueFipsChecker{}
2903+
repoServerRemote := "https://remote.repo-server.instance"
2904+
2905+
cr.Spec.Repo.Remote = &repoServerRemote
2906+
assert.NoError(t, r.reconcileRepoDeployment(cr, false))
2907+
2908+
d := &appsv1.Deployment{}
2909+
2910+
assert.ErrorContains(t, r.Get(context.TODO(), types.NamespacedName{Name: cr.Name + "-repo-server", Namespace: cr.Namespace}, d),
2911+
"deployments.apps \""+cr.Name+"-repo-server\" not found")
2912+
2913+
// once remote is set to nil, reconciliation should trigger deployment resource creation
2914+
cr.Spec.Repo.Remote = nil
2915+
2916+
assert.NoError(t, r.reconcileRepoDeployment(cr, false))
2917+
assert.NoError(t, r.Get(context.TODO(), types.NamespacedName{Name: cr.Name + "-repo-server", Namespace: cr.Namespace}, d))
2918+
foundEnv := false
2919+
for _, env := range d.Spec.Template.Spec.Containers[0].Env {
2920+
if env.Name == "GODEBUG" {
2921+
foundEnv = true
2922+
assert.Equal(t, env.Value, "fips140=on", "GODEBUG environment must be set to fips140=on when fips is enabled")
2923+
}
2924+
}
2925+
assert.True(t, foundEnv, "environment GODEBUG must be set when FIPS is enabled")
2926+
}
2927+
2928+
func TestReconcileArgoCD_reconcileRepoServerWithFipsDisabled(t *testing.T) {
2929+
cr := makeTestArgoCD()
2930+
2931+
resObjs := []client.Object{cr}
2932+
subresObjs := []client.Object{cr}
2933+
runtimeObjs := []runtime.Object{}
2934+
sch := makeTestReconcilerScheme(argoproj.AddToScheme)
2935+
cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
2936+
r := makeTestReconciler(cl, sch, testclient.NewSimpleClientset())
2937+
r.FipsConfigChecker = &MockFalseFipsChecker{}
2938+
repoServerRemote := "https://remote.repo-server.instance"
2939+
2940+
cr.Spec.Repo.Remote = &repoServerRemote
2941+
assert.NoError(t, r.reconcileRepoDeployment(cr, false))
2942+
2943+
d := &appsv1.Deployment{}
2944+
2945+
assert.ErrorContains(t, r.Get(context.TODO(), types.NamespacedName{Name: cr.Name + "-repo-server", Namespace: cr.Namespace}, d),
2946+
"deployments.apps \""+cr.Name+"-repo-server\" not found")
2947+
2948+
// once remote is set to nil, reconciliation should trigger deployment resource creation
2949+
cr.Spec.Repo.Remote = nil
2950+
2951+
assert.NoError(t, r.reconcileRepoDeployment(cr, false))
2952+
assert.NoError(t, r.Get(context.TODO(), types.NamespacedName{Name: cr.Name + "-repo-server", Namespace: cr.Namespace}, d))
2953+
foundEnv := false
2954+
for _, env := range d.Spec.Template.Spec.Containers[0].Env {
2955+
if env.Name == "GODEBUG" {
2956+
foundEnv = true
2957+
}
2958+
}
2959+
assert.False(t, foundEnv, "environment GODEBUG must NOT be set when FIPS is disabled")
2960+
}

controllers/argoutil/fips.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package argoutil
2+
3+
import (
4+
"os"
5+
)
6+
7+
// FipsConfigChecker defines the behavior for reading the FIPS config.
8+
type FipsConfigChecker interface {
9+
IsFipsEnabled() (bool, error)
10+
}
11+
12+
// LinuxFipsConfigChecker implements the config reader for checking if the Linux based host is running in FIPS enabled mode.
13+
type LinuxFipsConfigChecker struct {
14+
ConfigFilePath string
15+
}
16+
17+
// NewLinuxFipsConfigChecker returns a Linux based config checker
18+
func NewLinuxFipsConfigChecker() *LinuxFipsConfigChecker {
19+
return &LinuxFipsConfigChecker{
20+
ConfigFilePath: "/proc/sys/crypto/fips_enabled",
21+
}
22+
}
23+
24+
// IsFipsEnabled reads the specified FIPS config file in a Linux based host and returns true FIPS is enabled, false otherwise.
25+
func (l *LinuxFipsConfigChecker) IsFipsEnabled() (bool, error) {
26+
found, err := fileExists(l.ConfigFilePath)
27+
if err != nil {
28+
return false, err
29+
}
30+
if !found {
31+
return false, nil
32+
}
33+
b, err := os.ReadFile(l.ConfigFilePath)
34+
if err != nil {
35+
return false, err
36+
}
37+
return b[0] == '1', err
38+
}
39+
40+
// fileExists checks if a file with the given path exists
41+
func fileExists(path string) (bool, error) {
42+
info, err := os.Stat(path)
43+
if err != nil {
44+
if os.IsNotExist(err) {
45+
return false, nil
46+
}
47+
return false, err
48+
}
49+
return !info.IsDir(), nil
50+
}

0 commit comments

Comments
 (0)