[go: up one dir, main page]

Skip to content

Commit

Permalink
registry: support ipv6 addresses
Browse files Browse the repository at this point in the history
Current registry reference use a subset of dns and IPv4 addresses to
represent a registry domain.

Since registries are mostly compatible with rfc3986, that defines the
URI generic syntax, this adds support for IPv6 enclosed in squared
brackets based on the mentioned rfc.

The regexp is only expanded to match on IPv6 addreses enclosed between
square brackets, considering only regular IPv6 addresses represented
as compressed or uncompressed, excluding special IPv6 address
representations.

Signed-off-by: Antonio Ojea <antonio.ojea.garcia@gmail.com>
  • Loading branch information
aojea committed Jun 25, 2022
1 parent 3e4f8a0 commit d897a19
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 8 deletions.
12 changes: 12 additions & 0 deletions reference/normalize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,14 @@ func TestValidateReferenceName(t *testing.T) {
"127.0.0.1:5000/docker/docker",
"127.0.0.1:5000/library/debian",
"127.0.0.1:5000/debian",
"192.168.0.1",
"192.168.0.1:80",
"192.168.0.1:8/debian",
"192.168.0.2:25000/debian",
"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
"[fc00::1]:5000/docker",
"[fc00::1]:5000/docker/docker",
"[fc00:1:2:3:4:5:6:7]:5000/library/debian",

// This test case was moved from invalid to valid since it is valid input
// when specified with a hostname, it removes the ambiguity from about
Expand All @@ -40,6 +47,11 @@ func TestValidateReferenceName(t *testing.T) {
"docker///docker",
"docker.io/docker/Docker",
"docker.io/docker///docker",
"[fc00::1]",
"[fc00::1]:5000",
"fc00::1:5000/debian",
"[fe80::1%eth0]:5000/debian",
"[2001:db8:3:4::192.0.2.33]:5000/debian",
"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
}

Expand Down
4 changes: 3 additions & 1 deletion reference/reference.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
//
// reference := name [ ":" tag ] [ "@" digest ]
// name := [domain '/'] path-component ['/' path-component]*
// domain := domain-component ['.' domain-component]* [':' port-number]
// domain := host [':' port-number]
// host := domain-name | IPv4address | \[ IPv6address \] ; rfc3986 appendix-A
// domain-name := domain-component ['.' domain-component]*
// domain-component := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
// port-number := /[0-9]+/
// path-component := alpha-numeric [separator alpha-numeric]*
Expand Down
95 changes: 95 additions & 0 deletions reference/reference_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,101 @@ func TestReferenceParse(t *testing.T) {
repository: "foo/foo_bar.com",
tag: "8080",
},
{
input: "192.168.1.1",
repository: "192.168.1.1",
},
{
input: "192.168.1.1:tag",
repository: "192.168.1.1",
tag: "tag",
},
{
input: "192.168.1.1:5000",
repository: "192.168.1.1",
tag: "5000",
},
{
input: "192.168.1.1/repo",
domain: "192.168.1.1",
repository: "192.168.1.1/repo",
},
{
input: "192.168.1.1:5000/repo",
domain: "192.168.1.1:5000",
repository: "192.168.1.1:5000/repo",
},
{
input: "192.168.1.1:5000/repo:5050",
domain: "192.168.1.1:5000",
repository: "192.168.1.1:5000/repo",
tag: "5050",
},
{
input: "[2001:db8::1]",
err: ErrReferenceInvalidFormat,
},
{
input: "[2001:db8::1]:5000",
err: ErrReferenceInvalidFormat,
},
{
input: "[2001:db8::1]:tag",
err: ErrReferenceInvalidFormat,
},
{
input: "[2001:db8::1]/repo",
domain: "[2001:db8::1]",
repository: "[2001:db8::1]/repo",
},
{
input: "[2001:db8:1:2:3:4:5:6]/repo:tag",
domain: "[2001:db8:1:2:3:4:5:6]",
repository: "[2001:db8:1:2:3:4:5:6]/repo",
tag: "tag",
},
{
input: "[2001:db8::1]:5000/repo",
domain: "[2001:db8::1]:5000",
repository: "[2001:db8::1]:5000/repo",
},
{
input: "[2001:db8::1]:5000/repo:tag",
domain: "[2001:db8::1]:5000",
repository: "[2001:db8::1]:5000/repo",
tag: "tag",
},
{
input: "[2001:db8::1]:5000/repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
domain: "[2001:db8::1]:5000",
repository: "[2001:db8::1]:5000/repo",
digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
},
{
input: "[2001:db8::1]:5000/repo:tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
domain: "[2001:db8::1]:5000",
repository: "[2001:db8::1]:5000/repo",
tag: "tag",
digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
},
{
input: "[2001:db8::]:5000/repo",
domain: "[2001:db8::]:5000",
repository: "[2001:db8::]:5000/repo",
},
{
input: "[::1]:5000/repo",
domain: "[::1]:5000",
repository: "[::1]:5000/repo",
},
{
input: "[fe80::1%eth0]:5000/repo",
err: ErrReferenceInvalidFormat,
},
{
input: "[fe80::1%@invalidzone]:5000/repo",
err: ErrReferenceInvalidFormat,
},
}
for _, testcase := range referenceTestcases {
failf := func(format string, v ...interface{}) {
Expand Down
41 changes: 34 additions & 7 deletions reference/regexp.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,42 @@ var (
alphaNumeric,
optional(repeated(separator, alphaNumeric)))

// domainComponent restricts the registry domain component of a
// repository name to start with a component as defined by DomainRegexp
// and followed by an optional port.
domainComponent = `(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`

// domainNameComponent restricts the registry domain component of a
// repository name to start with a component as defined by DomainRegexp.
domainNameComponent = `(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`

// ipv6address are enclosed between square brackets and may be represented
// in many ways, see rfc5952. Only IPv6 in compressed or uncompressed format
// are allowed, IPv6 zone identifiers (rfc6874) or Special addresses such as
// IPv4-Mapped are deliberately excluded.
ipv6address = expression(
literal(`[`), `(?:[a-fA-F0-9:]+)`, literal(`]`),
)

// domainName defines the structure of potential domain components
// that may be part of image names. This is purposely a subset of what is
// allowed by DNS to ensure backwards compatibility with Docker image
// names. This includes IPv4 addresses on decimal format.
domainName = expression(
domainNameComponent,
optional(repeated(literal(`.`), domainNameComponent)),
)

// host defines the structure of potential domains based on the URI
// Host subcomponent on rfc3986. It may be a subset of DNS domain name,
// or an IPv4 address in decimal format, or an IPv6 address between square
// brackets (including zone identifiers as defined by rfc6874).
// Ref:
// https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2
// https://www.rfc-editor.org/rfc/rfc6874.txt
host = `(?:` + domainName + `|` + ipv6address + `)`

// allowed by the URI Host subcomponent on rfc3986 to ensure backwards
// compatibility with Docker image names.
domain = expression(
domainComponent,
optional(repeated(literal(`.`), domainComponent)),
host,
optional(literal(`:`), `[0-9]+`))

// DomainRegexp defines the structure of potential domain components
// that may be part of image names. This is purposely a subset of what is
// allowed by DNS to ensure backwards compatibility with Docker image
Expand Down
40 changes: 40 additions & 0 deletions reference/regexp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,46 @@ func TestDomainRegexp(t *testing.T) {
input: "Asdf.com", // uppercase character
match: true,
},
{
input: "192.168.1.1:75050", // ipv4
match: true,
},
{
input: "192.168.1.1:750050", // port with more than 5 digits, it will fail on validation
match: true,
},
{
input: "[fd00:1:2::3]:75050", // ipv6 compressed
match: true,
},
{
input: "[fd00:1:2::3]75050", // ipv6 wrong port separator
match: false,
},
{
input: "[fd00:1:2::3]::75050", // ipv6 wrong port separator
match: false,
},
{
input: "[fd00:1:2::3%eth0]:75050", // ipv6 with zone
match: false,
},
{
input: "[fd00123123123]:75050", // ipv6 wrong format, will fail in validation
match: true,
},
{
input: "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:75050", // ipv6 long format
match: true,
},
{
input: "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:750505", // ipv6 long format and invalid port, it will fail in validation
match: true,
},
{
input: "fd00:1:2::3:75050", // bad ipv6 without square brackets
match: false,
},
}
r := regexp.MustCompile(`^` + DomainRegexp.String() + `$`)
for i := range hostcases {
Expand Down

0 comments on commit d897a19

Please sign in to comment.