From 8d41c7d2eb71a642b7fe9175ad8fefc4f7b9ce62 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 20 Oct 2022 09:21:38 +0200 Subject: [PATCH 01/14] quadlet tests: Fix handling of stderr checks We were looking at stdout, not stderr, and one of the testcases were wrong. Signed-off-by: Alexander Larsson --- test/e2e/quadlet/noimage.container | 2 +- test/e2e/quadlet_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/quadlet/noimage.container b/test/e2e/quadlet/noimage.container index e55d07f604..909c8c54a1 100644 --- a/test/e2e/quadlet/noimage.container +++ b/test/e2e/quadlet/noimage.container @@ -1,4 +1,4 @@ ## assert-failed -## assert-stderr-contains "No Image key specified" +## assert-stderr-contains "no Image key specified" [Container] diff --git a/test/e2e/quadlet_test.go b/test/e2e/quadlet_test.go index 7da6f0730f..06eaa51afd 100644 --- a/test/e2e/quadlet_test.go +++ b/test/e2e/quadlet_test.go @@ -80,7 +80,7 @@ func findSublist(full []string, sublist []string) int { } func (t *quadletTestcase) assertStdErrContains(args []string, session *PodmanSessionIntegration) bool { - return strings.Contains(session.OutputToString(), args[0]) + return strings.Contains(session.ErrorToString(), args[0]) } func (t *quadletTestcase) assertKeyIs(args []string, unit *parser.UnitFile) bool { From 0de98b1b6c8424893e7b5d80e2a9f3949515e9de Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 20 Oct 2022 09:22:32 +0200 Subject: [PATCH 02/14] quadlet tests: Run the tests even for (exected) failed tests Otherwise the noimage test doesn't look at the stderr assertion. Signed-off-by: Alexander Larsson --- test/e2e/quadlet_test.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/test/e2e/quadlet_test.go b/test/e2e/quadlet_test.go index 06eaa51afd..76b98efdbb 100644 --- a/test/e2e/quadlet_test.go +++ b/test/e2e/quadlet_test.go @@ -174,7 +174,10 @@ func (t *quadletTestcase) doAssert(check []string, unit *parser.UnitFile, sessio } if !ok { - s, _ := unit.ToString() + s := "(nil)" + if unit != nil { + s, _ = unit.ToString() + } return fmt.Errorf("Failed assertion for %s: %s\n\n%s", t.serviceName, strings.Join(check, " "), s) } return nil @@ -189,12 +192,18 @@ func (t *quadletTestcase) check(generateDir string, session *PodmanSessionIntegr } file := filepath.Join(generateDir, t.serviceName) - if _, err := os.Stat(file); os.IsNotExist(err) && expectFail { - return // Successful fail + _, err := os.Stat(file) + if expectFail { + Expect(err).To(MatchError(os.ErrNotExist)) + } else { + Expect(err).ToNot(HaveOccurred()) } - unit, err := parser.ParseUnitFile(file) - Expect(err).To(BeNil()) + var unit *parser.UnitFile + if !expectFail { + unit, err = parser.ParseUnitFile(file) + Expect(err).To(BeNil()) + } for _, check := range t.checks { err := t.doAssert(check, unit, session) From 998f834b047cae86ee5f3baf736a730d4c1b3ae4 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 20 Oct 2022 09:51:46 +0200 Subject: [PATCH 03/14] quadlet: Change ReadOnly to default to enabled This makees much more sense for typical service loads, and can easily be reverted by `ReadOnly=no`. Also updates and adds various tests for this. Signed-off-by: Alexander Larsson --- docs/source/markdown/podman-systemd.unit.5.md | 2 +- pkg/systemd/quadlet/quadlet.go | 2 +- test/e2e/quadlet/basepodman.container | 4 ++-- test/e2e/quadlet/basic.container | 3 ++- test/e2e/quadlet/readonly-notmpfs.container | 6 ++++++ test/e2e/quadlet/readwrite-notmpfs.container | 7 +++++++ test/e2e/quadlet/readwrite.container | 6 ++++++ test/e2e/quadlet_test.go | 3 +++ 8 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 test/e2e/quadlet/readonly-notmpfs.container create mode 100644 test/e2e/quadlet/readwrite-notmpfs.container create mode 100644 test/e2e/quadlet/readwrite.container diff --git a/docs/source/markdown/podman-systemd.unit.5.md b/docs/source/markdown/podman-systemd.unit.5.md index 22cfbd4617..d4667c0f36 100644 --- a/docs/source/markdown/podman-systemd.unit.5.md +++ b/docs/source/markdown/podman-systemd.unit.5.md @@ -136,7 +136,7 @@ By default, the container runs with no capabilities (due to DropCapabilities='al caps are needed, then add them with this key. For example using `AddCapability=CAP_DAC_OVERRIDE`. This can be listed multiple times. -#### `ReadOnly=` (defaults to `no`) +#### `ReadOnly=` (defaults to `yes`) If enabled, makes image read-only, with /var/tmp, /tmp and /run a tmpfs (unless disabled by `VolatileTmp=no`). diff --git a/pkg/systemd/quadlet/quadlet.go b/pkg/systemd/quadlet/quadlet.go index b1869cca23..e962c719ca 100644 --- a/pkg/systemd/quadlet/quadlet.go +++ b/pkg/systemd/quadlet/quadlet.go @@ -412,7 +412,7 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile podman.addf("--cap-add=%s", strings.ToLower(caps)) } - readOnly := container.LookupBoolean(ContainerGroup, KeyReadOnly, false) + readOnly := container.LookupBoolean(ContainerGroup, KeyReadOnly, true) if readOnly { podman.add("--read-only") } diff --git a/test/e2e/quadlet/basepodman.container b/test/e2e/quadlet/basepodman.container index aa97351438..8204a293b9 100644 --- a/test/e2e/quadlet/basepodman.container +++ b/test/e2e/quadlet/basepodman.container @@ -1,10 +1,10 @@ -## assert-podman-final-args run --name=systemd-%N --cidfile=%t/%N.cid --replace --rm -d --log-driver journald --pull=never --runtime /usr/bin/crun --cgroups=split --sdnotify=conmon imagename +## assert-podman-final-args run --name=systemd-%N --cidfile=%t/%N.cid --replace --rm -d --log-driver passthrough --pull=never --runtime /usr/bin/crun --cgroups=split --sdnotify=conmon imagename [Container] Image=imagename # Disable all default features to get as empty podman run command as we can -RemapUsers=no +ReadOnly=no NoNewPrivileges=no DropCapability= RunInit=no diff --git a/test/e2e/quadlet/basic.container b/test/e2e/quadlet/basic.container index ab770b10c7..f86084cd4c 100644 --- a/test/e2e/quadlet/basic.container +++ b/test/e2e/quadlet/basic.container @@ -12,7 +12,8 @@ ## assert-podman-args "--sdnotify=conmon" ## assert-podman-args "--security-opt=no-new-privileges" ## assert-podman-args "--cap-drop=all" -## assert-podman-args "--tmpfs" "/tmp:rw,size=512M,mode=1777" +## assert-podman-args "--read-only" +## !assert-podman-args "--read-only-tmpfs=false" ## assert-key-is "Unit" "RequiresMountsFor" "%t/containers" ## assert-key-is "Service" "KillMode" "mixed" ## assert-key-is "Service" "Delegate" "yes" diff --git a/test/e2e/quadlet/readonly-notmpfs.container b/test/e2e/quadlet/readonly-notmpfs.container new file mode 100644 index 0000000000..cddc7b7142 --- /dev/null +++ b/test/e2e/quadlet/readonly-notmpfs.container @@ -0,0 +1,6 @@ +## assert-podman-args "--read-only-tmpfs=false" +## assert-podman-args "--read-only" + +[Container] +Image=localhost/imagename +VolatileTmp=no diff --git a/test/e2e/quadlet/readwrite-notmpfs.container b/test/e2e/quadlet/readwrite-notmpfs.container new file mode 100644 index 0000000000..c7349a8ce0 --- /dev/null +++ b/test/e2e/quadlet/readwrite-notmpfs.container @@ -0,0 +1,7 @@ +## !assert-podman-args "--read-only" +## !assert-podman-args "--tmpfs" "/tmp:rw,size=512M,mode=1777" + +[Container] +Image=localhost/imagename +VolatileTmp=no +ReadOnly=no diff --git a/test/e2e/quadlet/readwrite.container b/test/e2e/quadlet/readwrite.container new file mode 100644 index 0000000000..61905c0e6d --- /dev/null +++ b/test/e2e/quadlet/readwrite.container @@ -0,0 +1,6 @@ +## !assert-podman-args "--read-only" +## assert-podman-args "--tmpfs" "/tmp:rw,size=512M,mode=1777" + +[Container] +Image=localhost/imagename +ReadOnly=no diff --git a/test/e2e/quadlet_test.go b/test/e2e/quadlet_test.go index 76b98efdbb..7b86308d0f 100644 --- a/test/e2e/quadlet_test.go +++ b/test/e2e/quadlet_test.go @@ -284,6 +284,9 @@ var _ = Describe("quadlet system generator", func() { Entry("podmanargs.container", "podmanargs.container"), Entry("ports.container", "ports.container"), Entry("ports_ipv6.container", "ports_ipv6.container"), + Entry("readonly-notmpfs.container", "readonly-notmpfs.container"), + Entry("readwrite.container", "readwrite.container"), + Entry("readwrite-notmpfs.container", "readwrite-notmpfs.container"), Entry("socketactivated.container", "socketactivated.container"), Entry("timezone.container", "timezone.container"), Entry("user.container", "user.container"), From d7e248dcff69473ca44def6381f573cc27f77032 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 20 Oct 2022 09:52:28 +0200 Subject: [PATCH 04/14] quadlet: Switch log-driver to passthrough This is much better for the systemd case becase we pass the journal socket fds directly to the container. This means less copying of the logs, but it also means the journal will correctly get the peer process id when it tries to extract things like the name of what is logging something. With this we correctly name the logging process rather than claim everything comes from conmon. Signed-off-by: Alexander Larsson --- pkg/systemd/quadlet/quadlet.go | 3 +-- test/e2e/quadlet/basic.container | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/systemd/quadlet/quadlet.go b/pkg/systemd/quadlet/quadlet.go index e962c719ca..090118b96e 100644 --- a/pkg/systemd/quadlet/quadlet.go +++ b/pkg/systemd/quadlet/quadlet.go @@ -353,8 +353,7 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile "-d", // But we still want output to the journal, so use the log driver. - // TODO: Once available we want to use the passthrough log-driver instead. - "--log-driver", "journald", + "--log-driver", "passthrough", // Never try to pull the image during service start "--pull=never") diff --git a/test/e2e/quadlet/basic.container b/test/e2e/quadlet/basic.container index f86084cd4c..c5a97ea1b0 100644 --- a/test/e2e/quadlet/basic.container +++ b/test/e2e/quadlet/basic.container @@ -4,7 +4,7 @@ ## assert-podman-args "--rm" ## assert-podman-args "--replace" ## assert-podman-args "-d" -## assert-podman-args "--log-driver" "journald" +## assert-podman-args "--log-driver" "passthrough" ## assert-podman-args "--pull=never" ## assert-podman-args "--init" ## assert-podman-args "--runtime" "/usr/bin/crun" From 2b0d9cd94bfeecc7209d49c12f7f6632dc0e4c84 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 20 Oct 2022 09:59:21 +0200 Subject: [PATCH 05/14] quadlet: Drop the SocketActivated key This was added in the old quadlet to work around issues with podman not passing on notify fds and pids. However, these are now fixed with: https://github.com/containers/podman/pull/11316 https://github.com/openSUSE/catatonit/pull/15 So, remove this key (which was never in a podman release anyway) Signed-off-by: Alexander Larsson --- pkg/systemd/quadlet/quadlet.go | 14 -------------- test/e2e/quadlet_test.go | 1 - 2 files changed, 15 deletions(-) diff --git a/pkg/systemd/quadlet/quadlet.go b/pkg/systemd/quadlet/quadlet.go index 090118b96e..6709797f0a 100644 --- a/pkg/systemd/quadlet/quadlet.go +++ b/pkg/systemd/quadlet/quadlet.go @@ -56,7 +56,6 @@ const ( KeyRemapUIDRanges = "RemapUidRanges" KeyRemapGIDRanges = "RemapGidRanges" KeyNotify = "Notify" - KeySocketActivated = "SocketActivated" KeyExposeHostPort = "ExposeHostPort" KeyPublishPort = "PublishPort" KeyKeepID = "KeepId" @@ -89,7 +88,6 @@ var supportedContainerKeys = map[string]bool{ KeyRemapUIDRanges: true, KeyRemapGIDRanges: true, KeyNotify: true, - KeySocketActivated: true, KeyExposeHostPort: true, KeyPublishPort: true, KeyKeepID: true, @@ -428,18 +426,6 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile podman.add("--read-only-tmpfs=false") } - socketActivated := container.LookupBoolean(ContainerGroup, KeySocketActivated, false) - if socketActivated { - // TODO: This will not be needed with later podman versions that support activation directly: - // https://github.com/containers/podman/pull/11316 - podman.add("--preserve-fds=1") - podmanEnv["LISTEN_FDS"] = "1" - - // TODO: This will not be 2 when catatonit forwards fds: - // https://github.com/openSUSE/catatonit/pull/15 - podmanEnv["LISTEN_PID"] = "2" - } - defaultContainerUID := uint32(0) defaultContainerGID := uint32(0) diff --git a/test/e2e/quadlet_test.go b/test/e2e/quadlet_test.go index 7b86308d0f..6cd30faa07 100644 --- a/test/e2e/quadlet_test.go +++ b/test/e2e/quadlet_test.go @@ -287,7 +287,6 @@ var _ = Describe("quadlet system generator", func() { Entry("readonly-notmpfs.container", "readonly-notmpfs.container"), Entry("readwrite.container", "readwrite.container"), Entry("readwrite-notmpfs.container", "readwrite-notmpfs.container"), - Entry("socketactivated.container", "socketactivated.container"), Entry("timezone.container", "timezone.container"), Entry("user.container", "user.container"), Entry("user-host.container", "user-host.container"), From af67f15bc7e8afb20ce9b0f4e67c3e727d990373 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 20 Oct 2022 10:04:23 +0200 Subject: [PATCH 06/14] quadlet: Embed the correct binary name in the generated comment The binary name is not the same as in the old quadlet, and can anyway differ in system and user runs, so use os.Args[0] to get the right name in the comment. Signed-off-by: Alexander Larsson --- cmd/quadlet/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/quadlet/main.go b/cmd/quadlet/main.go index 261e14d00e..a40e69dd4a 100644 --- a/cmd/quadlet/main.go +++ b/cmd/quadlet/main.go @@ -134,7 +134,7 @@ func generateServiceFile(service *parser.UnitFile) error { Debugf("writing '%s'", service.Path) service.PrependComment("", - "Automatically generated by quadlet-generator", + fmt.Sprintf("Automatically generated by %s", os.Args[0]), "") f, err := os.Create(service.Path) From a9f0957c24e5061fb95297bb668245558b952073 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 20 Oct 2022 10:15:51 +0200 Subject: [PATCH 07/14] quadlet: Allow multiple elements on each Add/DropCaps line You can still use multiple lines, but this is not necessary. Signed-off-by: Alexander Larsson --- docs/source/markdown/podman-systemd.unit.5.md | 16 ++++++++++++++-- pkg/systemd/quadlet/quadlet.go | 4 ++-- test/e2e/quadlet/capabilities.container | 3 ++- test/e2e/quadlet/capabilities2.container | 9 +++++++++ test/e2e/quadlet_test.go | 1 + 5 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 test/e2e/quadlet/capabilities2.container diff --git a/docs/source/markdown/podman-systemd.unit.5.md b/docs/source/markdown/podman-systemd.unit.5.md index d4667c0f36..f45ac6a606 100644 --- a/docs/source/markdown/podman-systemd.unit.5.md +++ b/docs/source/markdown/podman-systemd.unit.5.md @@ -128,13 +128,25 @@ setuid and file capabilities. Drop these capabilities from the default container capability set. The default is `all`, allowing addition of capabilities with `AddCapability`. Set this to empty to drop no capabilities. -This can be listed multiple times. + +This is a space separated list of capabilities. This key can be listed multiple times. + +For example: +``` +DropCapability=CAP_DAC_OVERRIDE CAP_IPC_OWNER +``` #### `AddCapability=` By default, the container runs with no capabilities (due to DropCapabilities='all'). If any specific caps are needed, then add them with this key. For example using `AddCapability=CAP_DAC_OVERRIDE`. -This can be listed multiple times. + +This is a space separated list of capabilities. This key can be listed multiple times. + +For example: +``` +AddCapability=CAP_DAC_OVERRIDE CAP_IPC_OWNER +``` #### `ReadOnly=` (defaults to `yes`) diff --git a/pkg/systemd/quadlet/quadlet.go b/pkg/systemd/quadlet/quadlet.go index 6709797f0a..5c3c83c27d 100644 --- a/pkg/systemd/quadlet/quadlet.go +++ b/pkg/systemd/quadlet/quadlet.go @@ -396,7 +396,7 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile dropCaps := []string{"all"} // Default if container.HasKey(ContainerGroup, KeyDropCapability) { - dropCaps = container.LookupAll(ContainerGroup, KeyDropCapability) + dropCaps = container.LookupAllStrv(ContainerGroup, KeyDropCapability) } for _, caps := range dropCaps { @@ -404,7 +404,7 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile } // But allow overrides with AddCapability - addCaps := container.LookupAll(ContainerGroup, KeyAddCapability) + addCaps := container.LookupAllStrv(ContainerGroup, KeyAddCapability) for _, caps := range addCaps { podman.addf("--cap-add=%s", strings.ToLower(caps)) } diff --git a/test/e2e/quadlet/capabilities.container b/test/e2e/quadlet/capabilities.container index ec32ed0237..6c944ed41a 100644 --- a/test/e2e/quadlet/capabilities.container +++ b/test/e2e/quadlet/capabilities.container @@ -1,8 +1,9 @@ ## assert-podman-args "--cap-drop=all" ## assert-podman-args "--cap-add=cap_dac_override" +## assert-podman-args "--cap-add=cap_audit_write" ## assert-podman-args "--cap-add=cap_ipc_owner" [Container] Image=imagename -AddCapability=CAP_DAC_OVERRIDE +AddCapability=CAP_DAC_OVERRIDE CAP_AUDIT_WRITE AddCapability=CAP_IPC_OWNER diff --git a/test/e2e/quadlet/capabilities2.container b/test/e2e/quadlet/capabilities2.container new file mode 100644 index 0000000000..b7bda66485 --- /dev/null +++ b/test/e2e/quadlet/capabilities2.container @@ -0,0 +1,9 @@ +## !assert-podman-args "--cap-drop=all" +## assert-podman-args "--cap-drop=cap_dac_override" +## assert-podman-args "--cap-drop=cap_audit_write" +## assert-podman-args "--cap-drop=cap_ipc_owner" + +[Container] +Image=localhost/imagename +DropCapability=CAP_DAC_OVERRIDE CAP_AUDIT_WRITE +DropCapability=CAP_IPC_OWNER diff --git a/test/e2e/quadlet_test.go b/test/e2e/quadlet_test.go index 6cd30faa07..a5df0698a5 100644 --- a/test/e2e/quadlet_test.go +++ b/test/e2e/quadlet_test.go @@ -269,6 +269,7 @@ var _ = Describe("quadlet system generator", func() { Entry("annotation.container", "annotation.container"), Entry("basepodman.container", "basepodman.container"), Entry("capabilities.container", "capabilities.container"), + Entry("capabilities2.container", "capabilities2.container"), Entry("env.container", "env.container"), Entry("escapes.container", "escapes.container"), Entry("exec.container", "exec.container"), From f6f65f49db5cf702a90f3986123352e4f31d7c2a Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 20 Oct 2022 10:41:24 +0200 Subject: [PATCH 08/14] quadlet: Add support for setting seccomp profile Signed-off-by: Alexander Larsson --- docs/source/markdown/podman-systemd.unit.5.md | 5 +++++ pkg/systemd/quadlet/quadlet.go | 8 ++++++++ test/e2e/quadlet/seccomp.container | 5 +++++ test/e2e/quadlet_test.go | 1 + 4 files changed, 19 insertions(+) create mode 100644 test/e2e/quadlet/seccomp.container diff --git a/docs/source/markdown/podman-systemd.unit.5.md b/docs/source/markdown/podman-systemd.unit.5.md index f45ac6a606..5d3cf47372 100644 --- a/docs/source/markdown/podman-systemd.unit.5.md +++ b/docs/source/markdown/podman-systemd.unit.5.md @@ -154,6 +154,11 @@ If enabled, makes image read-only, with /var/tmp, /tmp and /run a tmpfs (unless **NOTE:** Podman will automatically copy any content from the image onto the tmpfs +#### `SeccompProfile=` + +Set the seccomp profile to use in the container. If unset, the default podman profile is used. +Set to either the pathname of a json file, or `unconfined` to disable the seccomp filters. + #### `RemapUsers=` (defaults to `no`) If this is enabled, then host user and group ids are remapped in the container, such that all the uids diff --git a/pkg/systemd/quadlet/quadlet.go b/pkg/systemd/quadlet/quadlet.go index 5c3c83c27d..4674bf15b1 100644 --- a/pkg/systemd/quadlet/quadlet.go +++ b/pkg/systemd/quadlet/quadlet.go @@ -70,6 +70,7 @@ const ( KeyRunInit = "RunInit" KeyVolatileTmp = "VolatileTmp" KeyTimezone = "Timezone" + KeySeccompProfile = "SeccompProfile" ) // Supported keys in "Container" group @@ -102,6 +103,7 @@ var supportedContainerKeys = map[string]bool{ KeyRunInit: true, KeyVolatileTmp: true, KeyTimezone: true, + KeySeccompProfile: true, } // Supported keys in "Volume" group @@ -394,6 +396,12 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile podman.add("--security-opt=no-new-privileges") } + // Default to no higher level privileges or caps + seccompProfile, hasSeccompProfile := container.Lookup(ContainerGroup, KeySeccompProfile) + if hasSeccompProfile { + podman.add("--security-opt", fmt.Sprintf("seccomp=%s", seccompProfile)) + } + dropCaps := []string{"all"} // Default if container.HasKey(ContainerGroup, KeyDropCapability) { dropCaps = container.LookupAllStrv(ContainerGroup, KeyDropCapability) diff --git a/test/e2e/quadlet/seccomp.container b/test/e2e/quadlet/seccomp.container new file mode 100644 index 0000000000..5bfddffa5f --- /dev/null +++ b/test/e2e/quadlet/seccomp.container @@ -0,0 +1,5 @@ +## assert-podman-args --security-opt seccomp=unconfined + +[Container] +Image=localhost/imagename +SeccompProfile=unconfined diff --git a/test/e2e/quadlet_test.go b/test/e2e/quadlet_test.go index a5df0698a5..640aaf1c4b 100644 --- a/test/e2e/quadlet_test.go +++ b/test/e2e/quadlet_test.go @@ -288,6 +288,7 @@ var _ = Describe("quadlet system generator", func() { Entry("readonly-notmpfs.container", "readonly-notmpfs.container"), Entry("readwrite.container", "readwrite.container"), Entry("readwrite-notmpfs.container", "readwrite-notmpfs.container"), + Entry("seccomp.container", "seccomp.container"), Entry("timezone.container", "timezone.container"), Entry("user.container", "user.container"), Entry("user-host.container", "user-host.container"), From 6042ca7fd052ed9a9562634e8aea2290993a0baf Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 20 Oct 2022 12:26:35 +0200 Subject: [PATCH 09/14] quadlet: Add support for AddDevice= This lets you add custom device nodes into the container Signed-off-by: Alexander Larsson --- docs/source/markdown/podman-systemd.unit.5.md | 10 ++++++++++ pkg/systemd/quadlet/quadlet.go | 8 ++++++++ test/e2e/quadlet/devices.container | 7 +++++++ test/e2e/quadlet_test.go | 1 + 4 files changed, 26 insertions(+) create mode 100644 test/e2e/quadlet/devices.container diff --git a/docs/source/markdown/podman-systemd.unit.5.md b/docs/source/markdown/podman-systemd.unit.5.md index 5d3cf47372..e67bc41039 100644 --- a/docs/source/markdown/podman-systemd.unit.5.md +++ b/docs/source/markdown/podman-systemd.unit.5.md @@ -258,6 +258,16 @@ allocated port can be found with the `podman port` command. This key can be listed multiple times. +#### `AddDevice=` + +Adds a device node from the host into the container. The format of this is +`HOST-DEVICE[:CONTAINER-DEVICE][:PERMISSIONS]`, where `HOST-DEVICE` is the path of +the device node on the host, `CONTAINER-DEVICE` is the path of the device node in +the container, and `PERMISSIONS` is a list of permissions combining 'r' for read, +'w' for write, and 'm' for mknod(2). + +This key can be listed multiple times. + #### `PodmanArgs=` This key contains a list of arguments passed directly to the end of the `podman run` command diff --git a/pkg/systemd/quadlet/quadlet.go b/pkg/systemd/quadlet/quadlet.go index 4674bf15b1..877e10516b 100644 --- a/pkg/systemd/quadlet/quadlet.go +++ b/pkg/systemd/quadlet/quadlet.go @@ -71,6 +71,7 @@ const ( KeyVolatileTmp = "VolatileTmp" KeyTimezone = "Timezone" KeySeccompProfile = "SeccompProfile" + KeyAddDevice = "AddDevice" ) // Supported keys in "Container" group @@ -104,6 +105,7 @@ var supportedContainerKeys = map[string]bool{ KeyVolatileTmp: true, KeyTimezone: true, KeySeccompProfile: true, + KeyAddDevice: true, } // Supported keys in "Volume" group @@ -396,6 +398,12 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile podman.add("--security-opt=no-new-privileges") } + // But allow overrides with AddCapability + devices := container.LookupAllStrv(ContainerGroup, KeyAddDevice) + for _, device := range devices { + podman.addf("--device=%s", device) + } + // Default to no higher level privileges or caps seccompProfile, hasSeccompProfile := container.Lookup(ContainerGroup, KeySeccompProfile) if hasSeccompProfile { diff --git a/test/e2e/quadlet/devices.container b/test/e2e/quadlet/devices.container new file mode 100644 index 0000000000..2e958c0db6 --- /dev/null +++ b/test/e2e/quadlet/devices.container @@ -0,0 +1,7 @@ +## assert-podman-args --device=/dev/fuse +## assert-podman-args --device=/dev/loop0:r + +[Container] +Image=localhost/imagename +AddDevice=/dev/fuse +AddDevice=/dev/loop0:r diff --git a/test/e2e/quadlet_test.go b/test/e2e/quadlet_test.go index 640aaf1c4b..b453d08d58 100644 --- a/test/e2e/quadlet_test.go +++ b/test/e2e/quadlet_test.go @@ -270,6 +270,7 @@ var _ = Describe("quadlet system generator", func() { Entry("basepodman.container", "basepodman.container"), Entry("capabilities.container", "capabilities.container"), Entry("capabilities2.container", "capabilities2.container"), + Entry("devices.container", "devices.container"), Entry("env.container", "env.container"), Entry("escapes.container", "escapes.container"), Entry("exec.container", "exec.container"), From 721922fa7eac3315ee9a3e33783971784c61d223 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 20 Oct 2022 12:37:54 +0200 Subject: [PATCH 10/14] Fix manpage for podman run --network option This just fixes the indentation which was previously breaking the list such that the various network modes were just mixed into one large paragraph instead of a list. Signed-off-by: Alexander Larsson --- docs/source/markdown/options/network.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/markdown/options/network.md b/docs/source/markdown/options/network.md index 32f650d260..36cfca0a7a 100644 --- a/docs/source/markdown/options/network.md +++ b/docs/source/markdown/options/network.md @@ -15,7 +15,8 @@ Valid _mode_ values are: - **mac=MAC**: Specify a static mac address for this container. - **interface_name**: Specify a name for the created network interface inside the container. - For example to set a static ipv4 address and a static mac address, use `--network bridge:ip=10.88.0.10,mac=44:33:22:11:00:99`. + For example to set a static ipv4 address and a static mac address, use `--network bridge:ip=10.88.0.10,mac=44:33:22:11:00:99`. + - \[:OPTIONS,...]: Connect to a user-defined network; this is the network name or ID from a network created by **[podman network create](podman-network-create.1.md)**. Using the network name implies the bridge network mode. It is possible to specify the same options described under the bridge mode above. You can use the **--network** option multiple times to specify additional networks. - **none**: Create a network namespace for the container but do not configure network interfaces for it, thus the container has no network connectivity. - **container:**_id_: Reuse another container's network stack. From 8716de2ac39e4699188d8b1890a3b0bc8783e3e7 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 20 Oct 2022 12:51:27 +0200 Subject: [PATCH 11/14] quadlet: Add support for Network=... This just gets translated to --network=... Signed-off-by: Alexander Larsson --- docs/source/markdown/podman-systemd.unit.5.md | 6 ++++++ pkg/systemd/quadlet/quadlet.go | 7 +++++++ test/e2e/quadlet/network.container | 5 +++++ test/e2e/quadlet_test.go | 1 + 4 files changed, 19 insertions(+) create mode 100644 test/e2e/quadlet/network.container diff --git a/docs/source/markdown/podman-systemd.unit.5.md b/docs/source/markdown/podman-systemd.unit.5.md index e67bc41039..c8b39cf16b 100644 --- a/docs/source/markdown/podman-systemd.unit.5.md +++ b/docs/source/markdown/podman-systemd.unit.5.md @@ -234,6 +234,12 @@ created by using a `$name.volume` quadlet file. This key can be listed multiple times. +#### `Network=` + +Specify a custom network for the container. This has the same format as the `--network` option +to `podman run`. For example, use `host` to use the host network in the container, or `none` to +not set up networking in the container. + #### `ExposeHostPort=` Exposes a port, or a range of ports (e.g. `50-59`), from the host to the container. Equivalent diff --git a/pkg/systemd/quadlet/quadlet.go b/pkg/systemd/quadlet/quadlet.go index 877e10516b..ffb1d6f9fe 100644 --- a/pkg/systemd/quadlet/quadlet.go +++ b/pkg/systemd/quadlet/quadlet.go @@ -72,6 +72,7 @@ const ( KeyTimezone = "Timezone" KeySeccompProfile = "SeccompProfile" KeyAddDevice = "AddDevice" + KeyNetwork = "Network" ) // Supported keys in "Container" group @@ -106,6 +107,7 @@ var supportedContainerKeys = map[string]bool{ KeyTimezone: true, KeySeccompProfile: true, KeyAddDevice: true, + KeyNetwork: true, } // Supported keys in "Volume" group @@ -371,6 +373,11 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile podman.addf("--tz=%s", timezone) } + network, ok := container.Lookup(ContainerGroup, KeyNetwork) + if ok && len(network) > 0 { + podman.addf("--network=%s", network) + } + // Run with a pid1 init to reap zombies by default (as most apps don't do that) runInit := container.LookupBoolean(ContainerGroup, KeyRunInit, true) if runInit { diff --git a/test/e2e/quadlet/network.container b/test/e2e/quadlet/network.container new file mode 100644 index 0000000000..89179a29ac --- /dev/null +++ b/test/e2e/quadlet/network.container @@ -0,0 +1,5 @@ +## assert-podman-args "--network=host" + +[Container] +Image=localhost/imagename +Network=host diff --git a/test/e2e/quadlet_test.go b/test/e2e/quadlet_test.go index b453d08d58..d34934cdbf 100644 --- a/test/e2e/quadlet_test.go +++ b/test/e2e/quadlet_test.go @@ -278,6 +278,7 @@ var _ = Describe("quadlet system generator", func() { Entry("install.container", "install.container"), Entry("label.container", "label.container"), Entry("name.container", "name.container"), + Entry("network.container", "network.container"), Entry("noimage.container", "noimage.container"), Entry("noremapuser2.container", "noremapuser2.container"), Entry("noremapuser.container", "noremapuser.container"), From b07ba2441962b81f02444ba54332bab2f16a6f32 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 20 Oct 2022 13:19:55 +0200 Subject: [PATCH 12/14] quadlet: Support multiple Network= This is supported by podman run with --network, so makes sense. Signed-off-by: Alexander Larsson --- docs/source/markdown/podman-systemd.unit.5.md | 2 ++ pkg/systemd/quadlet/quadlet.go | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/source/markdown/podman-systemd.unit.5.md b/docs/source/markdown/podman-systemd.unit.5.md index c8b39cf16b..aa0667c31b 100644 --- a/docs/source/markdown/podman-systemd.unit.5.md +++ b/docs/source/markdown/podman-systemd.unit.5.md @@ -240,6 +240,8 @@ Specify a custom network for the container. This has the same format as the `--n to `podman run`. For example, use `host` to use the host network in the container, or `none` to not set up networking in the container. +This key can be listed multiple times. + #### `ExposeHostPort=` Exposes a port, or a range of ports (e.g. `50-59`), from the host to the container. Equivalent diff --git a/pkg/systemd/quadlet/quadlet.go b/pkg/systemd/quadlet/quadlet.go index ffb1d6f9fe..d3bde16fdc 100644 --- a/pkg/systemd/quadlet/quadlet.go +++ b/pkg/systemd/quadlet/quadlet.go @@ -373,9 +373,11 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile podman.addf("--tz=%s", timezone) } - network, ok := container.Lookup(ContainerGroup, KeyNetwork) - if ok && len(network) > 0 { - podman.addf("--network=%s", network) + networks := container.LookupAll(ContainerGroup, KeyNetwork) + for _, network := range networks { + if len(network) > 0 { + podman.addf("--network=%s", network) + } } // Run with a pid1 init to reap zombies by default (as most apps don't do that) From 33eb45c475b6b26a8e094a0aef7622eeca964b4f Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 20 Oct 2022 15:28:09 +0200 Subject: [PATCH 13/14] quadlet tests: Disable kmsg logging while testing Signed-off-by: Alexander Larsson --- cmd/quadlet/main.go | 6 ++++++ test/e2e/quadlet_test.go | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/cmd/quadlet/main.go b/cmd/quadlet/main.go index a40e69dd4a..6b99f87f57 100644 --- a/cmd/quadlet/main.go +++ b/cmd/quadlet/main.go @@ -23,6 +23,7 @@ import ( var ( verboseFlag bool // True if -v passed + noKmsgFlag bool isUser bool // True if run as quadlet-user-generator executable ) @@ -219,6 +220,10 @@ func main() { enableDebug() } + if noKmsgFlag { + noKmsg = true + } + if flag.NArg() < 1 { Logf("Missing output directory argument") os.Exit(1) @@ -270,4 +275,5 @@ func main() { func init() { flag.BoolVar(&verboseFlag, "v", false, "Print debug information") + flag.BoolVar(&noKmsgFlag, "no-kmsg-log", false, "Don't log to kmsg") } diff --git a/test/e2e/quadlet_test.go b/test/e2e/quadlet_test.go index d34934cdbf..eb05a582bc 100644 --- a/test/e2e/quadlet_test.go +++ b/test/e2e/quadlet_test.go @@ -253,7 +253,7 @@ var _ = Describe("quadlet system generator", func() { Expect(err).To(BeNil()) // Run quadlet to convert the file - session := podmanTest.Quadlet([]string{generatedDir}, quadletDir) + session := podmanTest.Quadlet([]string{"-no-kmsg-log", generatedDir}, quadletDir) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) From bac907abf8f10f2ed2c0f2d260effd6670db72e8 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 21 Oct 2022 08:07:13 +0200 Subject: [PATCH 14/14] Clarify the docs on DropCapability It was a bit unclear what setting it to empty means. Also, add to the tests verification that this works. Signed-off-by: Alexander Larsson --- docs/source/markdown/podman-systemd.unit.5.md | 4 ++-- test/e2e/quadlet/capabilities.container | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/source/markdown/podman-systemd.unit.5.md b/docs/source/markdown/podman-systemd.unit.5.md index aa0667c31b..b8e6a408e5 100644 --- a/docs/source/markdown/podman-systemd.unit.5.md +++ b/docs/source/markdown/podman-systemd.unit.5.md @@ -126,8 +126,8 @@ setuid and file capabilities. #### `DropCapability=` (defaults to `all`) -Drop these capabilities from the default container capability set. The default is `all`, allowing -addition of capabilities with `AddCapability`. Set this to empty to drop no capabilities. +Drop these capabilities from the default podman capability set, or `all` for all capabilities. The default if no +`DropCapability` is set is `all`. Set this to empty (i.e. `DropCapability=`) to use the default podman capability set. This is a space separated list of capabilities. This key can be listed multiple times. diff --git a/test/e2e/quadlet/capabilities.container b/test/e2e/quadlet/capabilities.container index 6c944ed41a..4faa7ab85f 100644 --- a/test/e2e/quadlet/capabilities.container +++ b/test/e2e/quadlet/capabilities.container @@ -1,9 +1,11 @@ -## assert-podman-args "--cap-drop=all" +## !assert-podman-args "--cap-drop=all" ## assert-podman-args "--cap-add=cap_dac_override" ## assert-podman-args "--cap-add=cap_audit_write" ## assert-podman-args "--cap-add=cap_ipc_owner" [Container] Image=imagename +# Verify that we can reset to the default cap set +DropCapability= AddCapability=CAP_DAC_OVERRIDE CAP_AUDIT_WRITE AddCapability=CAP_IPC_OWNER