Skip to content

Commit 82b0ff4

Browse files
authored
feat: Add ReadonlyRootfs option (runfinch#233)
Signed-off-by: Arjun Raja Yogidas <[email protected]>
1 parent c4fc1c9 commit 82b0ff4

File tree

4 files changed

+69
-10
lines changed

4 files changed

+69
-10
lines changed

api/handlers/container/create.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ func (h *handler) create(w http.ResponseWriter, r *http.Request) {
193193
if req.HostConfig.Runtime != "" {
194194
runtime = req.HostConfig.Runtime
195195
}
196+
securityOpt := []string{}
197+
if req.HostConfig.SecurityOpt != nil {
198+
securityOpt = req.HostConfig.SecurityOpt
199+
}
196200

197201
globalOpt := ncTypes.GlobalCommandOptions(*h.Config)
198202
createOpt := ncTypes.ContainerCreateOptions{
@@ -251,7 +255,7 @@ func (h *handler) create(w http.ResponseWriter, r *http.Request) {
251255
// #endregion
252256

253257
// #region for security flags
254-
SecurityOpt: []string{}, // nerdctl default.
258+
SecurityOpt: securityOpt,
255259
CapAdd: capAdd,
256260
CapDrop: capDrop,
257261
Privileged: req.HostConfig.Privileged,
@@ -293,6 +297,11 @@ func (h *handler) create(w http.ResponseWriter, r *http.Request) {
293297
Stderr: nil,
294298
},
295299
// #endregion
300+
301+
// #region for rootfs flags
302+
ReadOnly: req.HostConfig.ReadonlyRootfs, // Is the container root filesystem in read-only
303+
// #endregion
304+
296305
}
297306

298307
portMappings, err := translatePortMappings(req.HostConfig.PortBindings)

api/handlers/container/create_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,29 @@ var _ = Describe("Container Create API ", func() {
905905
Expect(rr.Body).Should(MatchJSON(jsonResponse))
906906
})
907907

908+
It("should set ReadonlyRootfs and SecurityOpt option", func() {
909+
body := []byte(`{
910+
"Image": "test-image",
911+
"HostConfig": {
912+
"ReadonlyRootfs": true,
913+
"SecurityOpt": [ "seccomp=/path/to/custom_seccomp.json", "apparmor=unconfined"]
914+
}
915+
}`)
916+
req, _ := http.NewRequest(http.MethodPost, "/containers/create", bytes.NewReader(body))
917+
918+
// expected create options
919+
createOpt.ReadOnly = true
920+
createOpt.SecurityOpt = []string{"seccomp=/path/to/custom_seccomp.json", "apparmor=unconfined"}
921+
922+
service.EXPECT().Create(gomock.Any(), "test-image", nil, equalTo(createOpt), equalTo(netOpt)).Return(
923+
cid, nil)
924+
925+
// handler should return success message with 201 status code.
926+
h.create(rr, req)
927+
Expect(rr).Should(HaveHTTPStatus(http.StatusCreated))
928+
Expect(rr.Body).Should(MatchJSON(jsonResponse))
929+
})
930+
908931
Context("translate port mappings", func() {
909932
It("should return empty if port mappings is nil", func() {
910933
Expect(translatePortMappings(nil)).Should(BeEmpty())

api/types/container_types.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,15 @@ type ContainerHostConfig struct {
8686
OomKillDisable bool // specifies whether to disable OOM Killer
8787
// TODO: OomScoreAdj int // specifies the tune container’s OOM preferences (-1000 to 1000, rootless: 100 to 1000)
8888
// TODO: OomScoreAdjChanged bool // OomScoreAdjChanged specifies whether the OOM preferences
89-
PidMode string // PID namespace to use for the container
90-
Privileged bool // Is the container in privileged mode
91-
// TODO: ReadonlyRootfs bool // Is the container root filesystem in read-only
92-
// TODO: SecurityOpt []string // List of string values to customize labels for MLS systems, such as SELinux. (["key=value"])
93-
Tmpfs map[string]string `json:",omitempty"` // List of tmpfs (mounts) used for the container
94-
UTSMode string // UTS namespace to use for the container
95-
ShmSize int64 // Size of /dev/shm in bytes. The size must be greater than 0.
96-
Sysctls map[string]string `json:",omitempty"` // List of Namespaced sysctls used for the container
97-
Runtime string `json:",omitempty"` // Runtime to use with this container
89+
PidMode string // PID namespace to use for the container
90+
Privileged bool // Is the container in privileged mode
91+
ReadonlyRootfs bool // Is the container root filesystem in read-only
92+
SecurityOpt []string // List of string values to customize labels for MLS systems, such as SELinux. (["key=value"])
93+
Tmpfs map[string]string `json:",omitempty"` // List of tmpfs (mounts) used for the container
94+
UTSMode string // UTS namespace to use for the container
95+
ShmSize int64 // Size of /dev/shm in bytes. The size must be greater than 0.
96+
Sysctls map[string]string `json:",omitempty"` // List of Namespaced sysctls used for the container
97+
Runtime string `json:",omitempty"` // Runtime to use with this container
9898
// TODO: PublishAllPorts bool // Should docker publish all exposed port for the container
9999
// TODO: StorageOpt map[string]string `json:",omitempty"` // Storage driver options per container.
100100
// TODO: UsernsMode UsernsMode // The user namespace to use for the container

e2e/tests/container_create.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1355,6 +1355,33 @@ func ContainerCreate(opt *option.Option) {
13551355
Expect(ok).Should(BeTrue())
13561356
Expect(runtimeName).Should(Equal(options.HostConfig.Runtime))
13571357
})
1358+
1359+
It("should create a container with readonly root filesystem", func() {
1360+
// Define options
1361+
options.Cmd = []string{"sleep", "Infinity"}
1362+
options.HostConfig.ReadonlyRootfs = true
1363+
1364+
// Create container
1365+
statusCode, ctr := createContainer(uClient, url, testContainerName, options)
1366+
Expect(statusCode).Should(Equal(http.StatusCreated))
1367+
Expect(ctr.ID).ShouldNot(BeEmpty())
1368+
1369+
// Additional verification through native inspect
1370+
nativeResp := command.Stdout(opt, "inspect", "--mode=native", testContainerName)
1371+
var nativeInspect []map[string]interface{}
1372+
err := json.Unmarshal(nativeResp, &nativeInspect)
1373+
Expect(err).Should(BeNil())
1374+
Expect(nativeInspect).Should(HaveLen(1))
1375+
1376+
// Verify readonly root in the spec
1377+
spec, ok := nativeInspect[0]["Spec"].(map[string]interface{})
1378+
Expect(ok).Should(BeTrue())
1379+
root, ok := spec["root"].(map[string]interface{})
1380+
Expect(ok).Should(BeTrue())
1381+
readonly, ok := root["readonly"].(bool)
1382+
Expect(ok).Should(BeTrue())
1383+
Expect(readonly).Should(BeTrue())
1384+
})
13581385
})
13591386
}
13601387

0 commit comments

Comments
 (0)