/
manager.go
224 lines (195 loc) · 8.3 KB
/
manager.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Authors of Nimbus
package manager
import (
"context"
"fmt"
"strings"
"github.com/go-logr/logr"
kubearmorv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorController/api/security.kubearmor.com/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
intentv1 "github.com/5GSEC/nimbus/api/v1"
"github.com/5GSEC/nimbus/pkg/adapter/common"
"github.com/5GSEC/nimbus/pkg/adapter/k8s"
adapterutil "github.com/5GSEC/nimbus/pkg/adapter/util"
globalwatcher "github.com/5GSEC/nimbus/pkg/adapter/watcher"
"github.com/5GSEC/nimbus/pkg/adapter/nimbus-kubearmor/processor"
kspwatcher "github.com/5GSEC/nimbus/pkg/adapter/nimbus-kubearmor/watcher"
)
var (
scheme = runtime.NewScheme()
k8sClient client.Client
)
func init() {
utilruntime.Must(intentv1.AddToScheme(scheme))
utilruntime.Must(kubearmorv1.AddToScheme(scheme))
k8sClient = k8s.NewOrDie(scheme)
}
func Run(ctx context.Context) {
npCh := make(chan common.Request)
deletedNpCh := make(chan common.Request)
go globalwatcher.WatchNimbusPolicies(ctx, npCh, deletedNpCh)
clusterNpChan := make(chan string)
deletedClusterNpChan := make(chan string)
go globalwatcher.WatchClusterNimbusPolicies(ctx, clusterNpChan, deletedClusterNpChan)
updatedKspCh := make(chan common.Request)
deletedKspCh := make(chan common.Request)
go kspwatcher.WatchKsps(ctx, updatedKspCh, deletedKspCh)
for {
select {
case <-ctx.Done():
close(npCh)
close(deletedNpCh)
close(clusterNpChan)
close(deletedClusterNpChan)
close(updatedKspCh)
close(deletedKspCh)
return
case createdNp := <-npCh:
createOrUpdateKsp(ctx, createdNp.Name, createdNp.Namespace)
case deletedNp := <-deletedNpCh:
deleteKsp(ctx, deletedNp.Name, deletedNp.Namespace)
case updatedKsp := <-updatedKspCh:
reconcileKsp(ctx, updatedKsp.Name, updatedKsp.Namespace, false)
case deletedKsp := <-deletedKspCh:
reconcileKsp(ctx, deletedKsp.Name, deletedKsp.Namespace, true)
case _ = <-clusterNpChan: // Fixme: CreateKSP based on ClusterNP
fmt.Println("No-op for ClusterNimbusPolicy")
case _ = <-deletedClusterNpChan: // Fixme: DeleteKSP based on ClusterNP
fmt.Println("No-op for ClusterNimbusPolicy")
}
}
}
func reconcileKsp(ctx context.Context, kspName, namespace string, deleted bool) {
logger := log.FromContext(ctx)
npName := adapterutil.ExtractNpName(kspName)
var np intentv1.NimbusPolicy
err := k8sClient.Get(ctx, types.NamespacedName{Name: npName, Namespace: namespace}, &np)
if err != nil {
if !errors.IsNotFound(err) {
logger.Error(err, "failed to get NimbusPolicy", "NimbusPolicy.Name", npName, "NimbusPolicy.Namespace", namespace)
}
return
}
if deleted {
logger.V(2).Info("Reconciling deleted KubeArmorPolicy", "KubeArmorPolicy.Name", kspName, "KubeArmorPolicy.Namespace", namespace)
} else {
logger.V(2).Info("Reconciling modified KubeArmorPolicy", "KubeArmorPolicy.Name", kspName, "KubeArmorPolicy.Namespace", namespace)
}
createOrUpdateKsp(ctx, npName, namespace)
}
func createOrUpdateKsp(ctx context.Context, npName, npNamespace string) {
logger := log.FromContext(ctx)
var np intentv1.NimbusPolicy
if err := k8sClient.Get(ctx, types.NamespacedName{Name: npName, Namespace: npNamespace}, &np); err != nil {
logger.Error(err, "failed to get NimbusPolicy", "NimbusPolicy.Name", npName, "NimbusPolicy.Namespace", npNamespace)
return
}
if adapterutil.IsOrphan(np.GetOwnerReferences(), "SecurityIntentBinding") {
logger.V(4).Info("Ignoring orphan NimbusPolicy", "NimbusPolicy.Name", npName, "NimbusPolicy.Namespace", npNamespace)
return
}
deleteDanglingKsps(ctx, np, logger)
ksps := processor.BuildKspsFrom(logger, &np)
// Iterate using a separate index variable to avoid aliasing
for idx := range ksps {
ksp := ksps[idx]
// Set NimbusPolicy as the owner of the KSP
if err := ctrl.SetControllerReference(&np, &ksp, scheme); err != nil {
logger.Error(err, "failed to set OwnerReference on KubeArmorPolicy", "Name", ksp.Name)
return
}
var existingKsp kubearmorv1.KubeArmorPolicy
err := k8sClient.Get(ctx, types.NamespacedName{Name: ksp.Name, Namespace: ksp.Namespace}, &existingKsp)
if err != nil && !errors.IsNotFound(err) {
logger.Error(err, "failed to get existing KubeArmorPolicy", "KubeArmorPolicy.Name", ksp.Name, "KubeArmorPolicy.Namespace", ksp.Namespace)
return
}
if err != nil {
if errors.IsNotFound(err) {
if err = k8sClient.Create(ctx, &ksp); err != nil {
logger.Error(err, "failed to create KubeArmorPolicy", "KubeArmorPolicy.Name", ksp.Name, "KubeArmorPolicy.Namespace", ksp.Namespace)
return
}
logger.Info("KubeArmorPolicy created", "KubeArmorPolicy.Name", ksp.Name, "KubeArmorPolicy.Namespace", ksp.Namespace)
}
} else {
ksp.ObjectMeta.ResourceVersion = existingKsp.ObjectMeta.ResourceVersion
if err = k8sClient.Update(ctx, &ksp); err != nil {
logger.Error(err, "failed to configure existing KubeArmorPolicy", "KubeArmorPolicy.Name", existingKsp.Name, "KubeArmorPolicy.Namespace", existingKsp.Namespace)
return
}
logger.Info("KubeArmorPolicy configured", "KubeArmorPolicy.Name", existingKsp.Name, "KubeArmorPolicy.Namespace", existingKsp.Namespace)
}
//TODO: Due to adapters' dependency on nimbus module, the docker image build is
// failing. The relevant code is commented out below (lines 153-155). We shall
// uncomment this code in a subsequent PR.
//if err = adapterutil.UpdateNpStatus(ctx, k8sClient, "KubeArmorPolicy/"+ksp.Name, np.Name, np.Namespace, false); err != nil {
// logger.Error(err, "failed to update KubeArmorPolicies status in NimbusPolicy")
//}
}
}
func deleteKsp(ctx context.Context, npName, npNamespace string) {
logger := log.FromContext(ctx)
var ksps kubearmorv1.KubeArmorPolicyList
if err := k8sClient.List(ctx, &ksps, &client.ListOptions{Namespace: npNamespace}); err != nil {
logger.Error(err, "failed to list KubeArmorPolicies")
return
}
// Kubernetes GC automatically deletes the child when the parent/owner is
// deleted. So, we don't need to do anything in this case since NimbusPolicy is
// the owner and when it gets deleted corresponding KSPs will be automatically
// deleted.
for _, ksp := range ksps.Items {
logger.Info("KubeArmorPolicy already deleted due to NimbusPolicy deletion",
"KubeArmorPolicy.Name", ksp.Name, "KubeArmorPolicy.Namespace", ksp.Namespace,
"NimbusPolicy.Name", npName, "NimbusPolicy.Namespace", npNamespace,
)
}
}
func deleteDanglingKsps(ctx context.Context, np intentv1.NimbusPolicy, logger logr.Logger) {
var existingKsps kubearmorv1.KubeArmorPolicyList
if err := k8sClient.List(ctx, &existingKsps, client.InNamespace(np.Namespace)); err != nil {
logger.Error(err, "failed to list KubeArmorPolicies for cleanup")
return
}
var kspsOwnedByNp []kubearmorv1.KubeArmorPolicy
for _, ksp := range existingKsps.Items {
ownerRef := ksp.OwnerReferences[0]
if ownerRef.Name == np.Name && ownerRef.UID == np.UID {
kspsOwnedByNp = append(kspsOwnedByNp, ksp)
}
}
if len(kspsOwnedByNp) == 0 {
return
}
kspsToDelete := make(map[string]kubearmorv1.KubeArmorPolicy)
// Populate owned KSPs
for _, kspOwnedByNp := range kspsOwnedByNp {
kspsToDelete[kspOwnedByNp.Name] = kspOwnedByNp
}
for _, nimbusRule := range np.Spec.NimbusRules {
kspName := np.Name + "-" + strings.ToLower(nimbusRule.ID)
delete(kspsToDelete, kspName)
}
for kspName := range kspsToDelete {
ksp := kspsToDelete[kspName]
if err := k8sClient.Delete(ctx, &ksp); err != nil {
logger.Error(err, "failed to delete dangling KubeArmorPolicy", "KubeArmorPolicy.Name", ksp.Namespace, "KubeArmorPolicy.Namespace", ksp.Namespace)
continue
}
//TODO: Due to adapters' dependency on nimbus module, the docker image build is
// failing. The relevant code is commented out below (lines 217-219). We shall
// uncomment this code in a subsequent PR.
//if err := adapterutil.UpdateNpStatus(ctx, k8sClient, "KubeArmorPolicy/"+ksp.Name, np.Name, np.Namespace, true); err != nil {
// logger.Error(err, "failed to update KubeArmorPolicy status in NimbusPolicy")
//}
logger.Info("Dangling KubeArmorPolicy deleted", "KubeArmorPolicy.Name", ksp.Name, "KubeArmorPolicy.Namespace", ksp.Namespace)
}
}