Skip to content

Commit 4fce427

Browse files
committed
sgx: set epc limits via NRI annotations
Signed-off-by: Mikko Ylinen <[email protected]>
1 parent f916e66 commit 4fce427

File tree

9 files changed

+99
-0
lines changed

9 files changed

+99
-0
lines changed

deployments/operator/crd/bases/deviceplugin.intel.com_sgxdeviceplugins.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ spec:
7272
description: NodeSelector provides a simple way to constrain device
7373
plugin pods to nodes with particular labels.
7474
type: object
75+
nriImage:
76+
description: 'NRIImage is a container image with SGX Node Resource
77+
Interface (NRI) plugin executable. Set this value if SGX EPC cgroups
78+
limits enforcement is wanted. TODO: is this a good name?'
79+
type: string
7580
provisionLimit:
7681
description: ProvisionLimit is a number of containers that can share
7782
the same SGX provision device.

deployments/operator/samples/deviceplugin_v1_sgxdeviceplugin.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ metadata:
44
name: sgxdeviceplugin-sample
55
spec:
66
image: intel/intel-sgx-plugin:0.28.0
7+
nriImage: ghcr.io/containers/nri-plugins/nri-sgx-epc:unstable
78
enclaveLimit: 110
89
provisionLimit: 110
910
logLevel: 4

deployments/sgx_plugin/overlays/epc-nfd/kustomization.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ patches:
66
- path: nfd_label.yaml
77
target:
88
name: intel-sgx-plugin
9+
- path: nri_plugin_patch.yaml
10+
target:
11+
name: intel-sgx-plugin
912

1013
apiVersion: kustomize.config.k8s.io/v1beta1
1114
kind: Kustomization
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
apiVersion: apps/v1
2+
kind: DaemonSet
3+
metadata:
4+
name: intel-sgx-plugin
5+
spec:
6+
template:
7+
spec:
8+
containers:
9+
- name: nri-sgx-epc
10+
image: ghcr.io/containers/nri-plugins/nri-sgx-epc:unstable
11+
securityContext:
12+
readOnlyRootFilesystem: true
13+
allowPrivilegeEscalation: false
14+
imagePullPolicy: IfNotPresent
15+
volumeMounts:
16+
- name: nrisockets
17+
mountPath: /var/run/nri
18+
volumes:
19+
- name: nrisockets
20+
hostPath:
21+
path: /var/run/nri

pkg/apis/deviceplugin/v1/sgxdeviceplugin_types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ type SgxDevicePluginSpec struct {
3535
// Recommendation is to leave this unset and prefer the SGX NodeFeatureRule instead.
3636
InitImage string `json:"initImage,omitempty"`
3737

38+
// NRIImage is a container image with SGX Node Resource Interface (NRI) plugin executable. Set
39+
// this value if SGX EPC cgroups limits enforcement is wanted.
40+
// TODO: is this a good name?
41+
NRIImage string `json:"nriImage,omitempty"`
42+
3843
// EnclaveLimit is a number of containers that can share the same SGX enclave device.
3944
// +kubebuilder:validation:Minimum=1
4045
EnclaveLimit int `json:"enclaveLimit,omitempty"`

pkg/controllers/sgx/controller.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,27 @@ func setInitContainer(spec *v1.PodSpec, imageName string) {
112112
addVolumeIfMissing(spec, "nfd-features", "/etc/kubernetes/node-feature-discovery/source.d/", v1.HostPathDirectoryOrCreate)
113113
}
114114

115+
func setNRIContainer(spec *v1.PodSpec, imageName string) {
116+
yes := true
117+
no := false
118+
spec.Containers = append(spec.Containers, v1.Container{
119+
Name: "nri-sgx-epc",
120+
Image: imageName,
121+
ImagePullPolicy: "IfNotPresent",
122+
SecurityContext: &v1.SecurityContext{
123+
ReadOnlyRootFilesystem: &yes,
124+
AllowPrivilegeEscalation: &no,
125+
},
126+
VolumeMounts: []v1.VolumeMount{
127+
{
128+
Name: "nrisockets",
129+
MountPath: "/var/run/nri",
130+
},
131+
},
132+
})
133+
addVolumeIfMissing(spec, "nrisockets", "/var/run/nri", v1.HostPathDirectoryOrCreate)
134+
}
135+
115136
func (c *controller) NewDaemonSet(rawObj client.Object) *apps.DaemonSet {
116137
devicePlugin := rawObj.(*devicepluginv1.SgxDevicePlugin)
117138

@@ -131,6 +152,10 @@ func (c *controller) NewDaemonSet(rawObj client.Object) *apps.DaemonSet {
131152
if devicePlugin.Spec.InitImage != "" {
132153
setInitContainer(&daemonSet.Spec.Template.Spec, devicePlugin.Spec.InitImage)
133154
}
155+
// add the optional NRI plugin container
156+
if devicePlugin.Spec.NRIImage != "" {
157+
setNRIContainer(&daemonSet.Spec.Template.Spec, devicePlugin.Spec.NRIImage)
158+
}
134159

135160
return daemonSet
136161
}
@@ -166,6 +191,26 @@ func (c *controller) UpdateDaemonSet(rawObj client.Object, ds *apps.DaemonSet) (
166191
updated = true
167192
}
168193

194+
// remove NRI plugin
195+
if len(ds.Spec.Template.Spec.Containers) > 1 && dp.Spec.NRIImage == "" {
196+
ds.Spec.Template.Spec.Containers = []v1.Container{ds.Spec.Template.Spec.Containers[0]}
197+
ds.Spec.Template.Spec.Volumes = removeVolume(ds.Spec.Template.Spec.Volumes, "nrisockets")
198+
updated = true
199+
}
200+
201+
// update NRI plugin image
202+
if len(ds.Spec.Template.Spec.Containers) > 1 && ds.Spec.Template.Spec.Containers[1].Image != dp.Spec.NRIImage {
203+
ds.Spec.Template.Spec.Containers[1].Image = dp.Spec.NRIImage
204+
updated = true
205+
}
206+
207+
// add NRI plugin image
208+
if len(ds.Spec.Template.Spec.Containers) == 1 && dp.Spec.NRIImage != "" {
209+
setNRIContainer(&ds.Spec.Template.Spec, dp.Spec.NRIImage)
210+
211+
updated = true
212+
}
213+
169214
if len(dp.Spec.NodeSelector) > 0 {
170215
if !reflect.DeepEqual(ds.Spec.Template.Spec.NodeSelector, dp.Spec.NodeSelector) {
171216
ds.Spec.Template.Spec.NodeSelector = dp.Spec.NodeSelector

pkg/webhooks/sgx/sgx.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ var ErrObjectType = errors.New("invalid runtime object type")
3535
type Mutator struct{}
3636

3737
const (
38+
epcLimitKey = "epc-limit.nri.io/container"
3839
namespace = "sgx.intel.com"
3940
encl = namespace + "/enclave"
4041
epc = namespace + "/epc"
@@ -148,6 +149,8 @@ func (s *Mutator) Default(ctx context.Context, obj runtime.Object) error {
148149
continue
149150
}
150151

152+
pod.Annotations[fmt.Sprintf("%s.%s", epcLimitKey, container.Name)] = fmt.Sprintf("%d", epcSize)
153+
151154
totalEpc += epcSize
152155

153156
// Quote Generation Modes:

test/e2e/sgxadmissionwebhook/sgxaadmissionwebhook.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ func describe() {
6969

7070
ginkgo.By("checking the pod total EPC size annotation is correctly set")
7171
gomega.Expect(pod.Annotations["sgx.intel.com/epc"]).To(gomega.Equal("1Mi"))
72+
gomega.Expect(pod.Annotations["epc-limit.nri.io/container.test"]).To(gomega.Equal("1048576"))
7273
})
7374
ginkgo.It("mutates created pods when the container contains the quote generation libraries", func(ctx context.Context) {
7475
ginkgo.By("submitting the pod")
@@ -79,6 +80,7 @@ func describe() {
7980

8081
ginkgo.By("checking the pod total EPC size annotation is correctly set")
8182
gomega.Expect(pod.Annotations["sgx.intel.com/epc"]).To(gomega.Equal("1Mi"))
83+
gomega.Expect(pod.Annotations["epc-limit.nri.io/container.test"]).To(gomega.Equal("1048576"))
8284
})
8385
ginkgo.It("mutates created pods when the container uses aesmd from a side-car container to generate quotes", func(ctx context.Context) {
8486
ginkgo.By("submitting the pod")
@@ -93,6 +95,8 @@ func describe() {
9395
gomega.Expect(pod.Spec.Containers[0].Env[0].Value).To(gomega.Equal("1"))
9496
ginkgo.By("checking the pod total EPC size annotation is correctly set")
9597
gomega.Expect(pod.Annotations["sgx.intel.com/epc"]).To(gomega.Equal("2Mi"))
98+
gomega.Expect(pod.Annotations["epc-limit.nri.io/container.test"]).To(gomega.Equal("1048576"))
99+
gomega.Expect(pod.Annotations["epc-limit.nri.io/container.aesmd"]).To(gomega.Equal("1048576"))
96100
})
97101
ginkgo.It("mutates created pods where one container uses host/daemonset aesmd to generate quotes", func(ctx context.Context) {
98102
ginkgo.By("submitting the pod")
@@ -106,6 +110,7 @@ func describe() {
106110
gomega.Expect(pod.Spec.Containers[0].Env[0].Value).To(gomega.Equal("1"))
107111
ginkgo.By("checking the pod total EPC size annotation is correctly set")
108112
gomega.Expect(pod.Annotations["sgx.intel.com/epc"]).To(gomega.Equal("1Mi"))
113+
gomega.Expect(pod.Annotations["epc-limit.nri.io/container.test"]).To(gomega.Equal("1048576"))
109114
})
110115
ginkgo.It("mutates created pods where three containers use host/daemonset aesmd to generate quotes", func(ctx context.Context) {
111116
ginkgo.By("submitting the pod")
@@ -125,6 +130,9 @@ func describe() {
125130
gomega.Expect(pod.Spec.Containers[2].Env[0].Value).To(gomega.Equal("1"))
126131
ginkgo.By("checking the pod total EPC size annotation is correctly set")
127132
gomega.Expect(pod.Annotations["sgx.intel.com/epc"]).To(gomega.Equal("3Mi"))
133+
gomega.Expect(pod.Annotations["epc-limit.nri.io/container.test1"]).To(gomega.Equal("1048576"))
134+
gomega.Expect(pod.Annotations["epc-limit.nri.io/container.test2"]).To(gomega.Equal("1048576"))
135+
gomega.Expect(pod.Annotations["epc-limit.nri.io/container.test3"]).To(gomega.Equal("1048576"))
128136
})
129137
ginkgo.It("checks that Volumes and VolumeMounts are created only once", func(ctx context.Context) {
130138
ginkgo.By("submitting the pod")

test/envtest/sgxdeviceplugin_controller_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ var _ = Describe("SgxDevicePlugin Controller", func() {
3838
spec := devicepluginv1.SgxDevicePluginSpec{
3939
Image: "sgx-testimage",
4040
InitImage: "sgx-testinitimage",
41+
NRIImage: "sgx-testnriimage",
4142
NodeSelector: map[string]string{"sgx-nodeselector": "true"},
4243
}
4344

@@ -76,13 +77,15 @@ var _ = Describe("SgxDevicePlugin Controller", func() {
7677
By("updating SgxDevicePlugin successfully")
7778
updatedImage := "updated-sgx-testimage"
7879
updatedInitImage := "updated-sgx-testinitimage"
80+
updatedNRIImage := "updated-sgx-testnriimage"
7981
updatedLogLevel := 2
8082
updatedEnclaveLimit := 2
8183
updatedProvisionLimit := 2
8284
updatedNodeSelector := map[string]string{"updated-sgx-nodeselector": "true"}
8385

8486
fetched.Spec.Image = updatedImage
8587
fetched.Spec.InitImage = updatedInitImage
88+
fetched.Spec.NRIImage = updatedNRIImage
8689
fetched.Spec.LogLevel = updatedLogLevel
8790
fetched.Spec.EnclaveLimit = updatedEnclaveLimit
8891
fetched.Spec.ProvisionLimit = updatedProvisionLimit
@@ -112,13 +115,17 @@ var _ = Describe("SgxDevicePlugin Controller", func() {
112115
Expect(ds.Spec.Template.Spec.Containers[0].Args).Should(ConsistOf(expectArgs))
113116
Expect(ds.Spec.Template.Spec.Containers[0].Image).Should(Equal(updatedImage))
114117
Expect(ds.Spec.Template.Spec.InitContainers).To(HaveLen(1))
118+
Expect(ds.Spec.Template.Spec.Containers).To(HaveLen(2))
119+
Expect(ds.Spec.Template.Spec.Containers[1].Image).Should(Equal(updatedNRIImage))
115120
Expect(ds.Spec.Template.Spec.InitContainers[0].Image).To(Equal(updatedInitImage))
116121
Expect(ds.Spec.Template.Spec.NodeSelector).Should(Equal(updatedNodeSelector))
117122

118123
By("updating SgxDevicePlugin with different values successfully")
119124
updatedInitImage = ""
125+
updatedNRIImage = ""
120126
updatedNodeSelector = map[string]string{}
121127
fetched.Spec.InitImage = updatedInitImage
128+
fetched.Spec.NRIImage = updatedNRIImage
122129
fetched.Spec.NodeSelector = updatedNodeSelector
123130

124131
Expect(k8sClient.Update(context.Background(), fetched)).Should(Succeed())
@@ -128,6 +135,7 @@ var _ = Describe("SgxDevicePlugin Controller", func() {
128135
err = k8sClient.Get(context.Background(), types.NamespacedName{Namespace: ns, Name: expectedDsName}, ds)
129136
Expect(err).To(BeNil())
130137
Expect(ds.Spec.Template.Spec.InitContainers).To(HaveLen(0))
138+
Expect(ds.Spec.Template.Spec.Containers).To(HaveLen(1))
131139
Expect(ds.Spec.Template.Spec.NodeSelector).Should(And(HaveLen(1), HaveKeyWithValue("kubernetes.io/arch", "amd64")))
132140

133141
By("deleting SgxDevicePlugin successfully")

0 commit comments

Comments
 (0)