diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 43052d13b6..5af5ce28ca 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -4290,3 +4290,21 @@ func (s *DockerSuite) TestRunVolumeCopyFlag(c *check.C) { out, _, err = dockerCmdWithError("run", "-v", "/foo:/bar:nocopy", "busybox", "true") c.Assert(err, checker.NotNil, check.Commentf(out)) } + +func (s *DockerSuite) TestRunTooLongHostname(c *check.C) { + // Test case in #21445 + hostname1 := "this-is-a-way-too-long-hostname-but-it-should-give-a-nice-error.local" + out, _, err := dockerCmdWithError("run", "--hostname", hostname1, "busybox", "echo", "test") + c.Assert(err, checker.NotNil, check.Commentf("Expected docker run to fail!")) + c.Assert(out, checker.Contains, "invalid hostname format for --hostname:", check.Commentf("Expected to have 'invalid hostname format for --hostname:' in the output, get: %s!", out)) + + // HOST_NAME_MAX=64 so 65 bytes will fail + hostname2 := "this-is-a-hostname-with-65-bytes-so-it-should-give-an-error.local" + out, _, err = dockerCmdWithError("run", "--hostname", hostname2, "busybox", "echo", "test") + c.Assert(err, checker.NotNil, check.Commentf("Expected docker run to fail!")) + c.Assert(out, checker.Contains, "invalid hostname format for --hostname:", check.Commentf("Expected to have 'invalid hostname format for --hostname:' in the output, get: %s!", out)) + + // 64 bytes will be OK + hostname3 := "this-is-a-hostname-with-64-bytes-so-will-not-give-an-error.local" + dockerCmd(c, "run", "--hostname", hostname3, "busybox", "echo", "test") +} diff --git a/runconfig/opts/parse.go b/runconfig/opts/parse.go index 722b8d8b3a..6543b406df 100644 --- a/runconfig/opts/parse.go +++ b/runconfig/opts/parse.go @@ -244,8 +244,9 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host // Validate if the given hostname is RFC 1123 (https://tools.ietf.org/html/rfc1123) compliant. hostname := *flHostname if hostname != "" { + // Linux hostname is limited to HOST_NAME_MAX=64, not not including the terminating null byte. matched, _ := regexp.MatchString("^(([[:alnum:]]|[[:alnum:]][[:alnum:]\\-]*[[:alnum:]])\\.)*([[:alnum:]]|[[:alnum:]][[:alnum:]\\-]*[[:alnum:]])$", hostname) - if !matched { + if len(hostname) > 64 || !matched { return nil, nil, nil, cmd, fmt.Errorf("invalid hostname format for --hostname: %s", hostname) } } diff --git a/runconfig/opts/parse_test.go b/runconfig/opts/parse_test.go index b88944f32a..1885544317 100644 --- a/runconfig/opts/parse_test.go +++ b/runconfig/opts/parse_test.go @@ -390,6 +390,7 @@ func TestParseHostname(t *testing.T) { "host-name": "host-name", "hostname123": "hostname123", "123hostname": "123hostname", + "hostname-of-64-bytes-long-should-be-valid-and-without-any-errors": "hostname-of-64-bytes-long-should-be-valid-and-without-any-errors", } invalidHostnames := map[string]string{ "^hostname": "invalid hostname format for --hostname: ^hostname", @@ -397,6 +398,7 @@ func TestParseHostname(t *testing.T) { "host&name": "invalid hostname format for --hostname: host&name", "-hostname": "invalid hostname format for --hostname: -hostname", "host_name": "invalid hostname format for --hostname: host_name", + "hostname-of-65-bytes-long-should-be-invalid-and-be-given-an-error": "invalid hostname format for --hostname: hostname-of-65-bytes-long-should-be-invalid-and-be-given-an-error", } hostnameWithDomain := "--hostname=hostname.domainname" hostnameWithDomainTld := "--hostname=hostname.domainname.tld"