diff --git a/run_common.go b/run_common.go index 80569e3299..dc0af41b03 100644 --- a/run_common.go +++ b/run_common.go @@ -47,6 +47,7 @@ import ( "github.com/containers/storage/pkg/lockfile" "github.com/containers/storage/pkg/mount" "github.com/containers/storage/pkg/reexec" + "github.com/containers/storage/pkg/regexp" "github.com/containers/storage/pkg/unshare" "github.com/opencontainers/go-digest" "github.com/opencontainers/runtime-spec/specs-go" @@ -57,6 +58,10 @@ import ( "golang.org/x/term" ) +const maxHostnameLen = 64 + +var validHostnames = regexp.Delayed("[A-Za-z0-9][A-Za-z0-9.-]+") + func (b *Builder) createResolvConf(rdir string, chownOpts *idtools.IDPair) (string, error) { cfile := filepath.Join(rdir, "resolv.conf") f, err := os.Create(cfile) @@ -2092,3 +2097,21 @@ func relabel(path, mountLabel string, shared bool) error { } return nil } + +// mapContainerNameToHostname returns the passed-in string with characters that +// don't match validHostnames (defined above) stripped out. +func mapContainerNameToHostname(containerName string) string { + match := validHostnames.FindStringIndex(containerName) + if match == nil { + return "" + } + trimmed := containerName[match[0]:] + match[1] -= match[0] + match[0] = 0 + for match[1] != len(trimmed) && match[1] < match[0]+maxHostnameLen { + trimmed = trimmed[:match[1]] + trimmed[match[1]+1:] + match = validHostnames.FindStringIndex(trimmed) + match[1] = min(match[1], maxHostnameLen) + } + return trimmed[:match[1]] +} diff --git a/run_common_test.go b/run_common_test.go new file mode 100644 index 0000000000..ff41354de5 --- /dev/null +++ b/run_common_test.go @@ -0,0 +1,31 @@ +package buildah + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMapContainerNameToHostname(t *testing.T) { + cases := [][2]string{ + {"trivial", "trivial"}, + {"Nottrivial", "Nottrivial"}, + {"0Nottrivial", "0Nottrivial"}, + {"0Nottrivi-al", "0Nottrivi-al"}, + {"-0Nottrivi-al", "0Nottrivi-al"}, + {".-0Nottrivi-.al", "0Nottrivi-.al"}, + {".-0Nottrivi-.al0123456789", "0Nottrivi-.al0123456789"}, + {".-0Nottrivi-.al0123456789+0123456789", "0Nottrivi-.al01234567890123456789"}, + {".-0Nottrivi-.al0123456789+0123456789/0123456789", "0Nottrivi-.al012345678901234567890123456789"}, + {".-0Nottrivi-.al0123456789+0123456789/0123456789%0123456789", "0Nottrivi-.al0123456789012345678901234567890123456789"}, + {".-0Nottrivi-.al0123456789+0123456789/0123456789%0123456789_0123456789", "0Nottrivi-.al01234567890123456789012345678901234567890123456789"}, + {".-0Nottrivi-.al0123456789+0123456789/0123456789%0123456789_0123456789:0123456", "0Nottrivi-.al012345678901234567890123456789012345678901234567890"}, + {".-0Nottrivi-.al0123456789+0123456789/0123456789%0123456789_0123456789:0123456789", "0Nottrivi-.al012345678901234567890123456789012345678901234567890"}, + } + for i := range cases { + t.Run(cases[i][0], func(t *testing.T) { + sanitized := mapContainerNameToHostname(cases[i][0]) + assert.Equalf(t, cases[i][1], sanitized, "mapping container name %q to a valid hostname", cases[i][0]) + }) + } +} diff --git a/run_freebsd.go b/run_freebsd.go index 5653b6d847..e7f2df4b9e 100644 --- a/run_freebsd.go +++ b/run_freebsd.go @@ -586,7 +586,17 @@ func (b *Builder) configureNamespaces(g *generate.Generator, options *RunOptions } else if b.Hostname() != "" { g.SetHostname(b.Hostname()) } else { - g.SetHostname(stringid.TruncateID(b.ContainerID)) + hostname := stringid.TruncateID(b.ContainerID) + defConfig, err := config.Default() + if err != nil { + return false, "", fmt.Errorf("failed to get container config: %w", err) + } + if defConfig.Containers.ContainerNameAsHostName { + if mapped := mapContainerNameToHostname(b.Container); mapped != "" { + hostname = mapped + } + } + g.SetHostname(hostname) } } else { g.SetHostname("") diff --git a/run_linux.go b/run_linux.go index 5d040cbb99..1af621c1ea 100644 --- a/run_linux.go +++ b/run_linux.go @@ -991,7 +991,17 @@ func (b *Builder) configureNamespaces(g *generate.Generator, options *RunOptions } else if b.Hostname() != "" { g.SetHostname(b.Hostname()) } else { - g.SetHostname(stringid.TruncateID(b.ContainerID)) + hostname := stringid.TruncateID(b.ContainerID) + defConfig, err := config.Default() + if err != nil { + return false, "", fmt.Errorf("failed to get container config: %w", err) + } + if defConfig.Containers.ContainerNameAsHostName { + if mapped := mapContainerNameToHostname(b.Container); mapped != "" { + hostname = mapped + } + } + g.SetHostname(hostname) } } else { g.SetHostname("") diff --git a/tests/run.bats b/tests/run.bats index d007658b04..4f0107fcb0 100644 --- a/tests/run.bats +++ b/tests/run.bats @@ -997,3 +997,21 @@ _EOF run_buildah ? bud --pull=false --layers . expect_output --substring -- "-c requires an argument" } + +@test "container_name_as_hostname" { + skip_if_no_runtime + + _prefetch alpine + echo '[containers]' > ${TEST_SCRATCH_DIR}/containers.conf + echo container_name_as_hostname = true >> ${TEST_SCRATCH_DIR}/containers.conf + name=alpine-working_containeR-4-test + sanitizedname=alpine-workingcontaineR-4-test + run_buildah from --name :"$name": --cidfile ${TEST_SCRATCH_DIR}/cid alpine + cname="$output" + run_buildah run "$cname" hostname + assert "$output" = $(cut -c1-12 < ${TEST_SCRATCH_DIR}/cid) + CONTAINERS_CONF=${TEST_SCRATCH_DIR}/containers.conf run_buildah run "$cname" hostname + expect_output "$sanitizedname" + CONTAINERS_CONF=${TEST_SCRATCH_DIR}/containers.conf run_buildah run "$(cat ${TEST_SCRATCH_DIR}/cid)" hostname + expect_output "$sanitizedname" +}