Skip to content

Commit b3f1da8

Browse files
authored
Merge pull request #4417 from Laitr0n/validate_specified_dns
feat: validate IP address in `--dns` flag
2 parents 865836c + 7720234 commit b3f1da8

File tree

3 files changed

+130
-0
lines changed

3 files changed

+130
-0
lines changed

cmd/nerdctl/container/container_run_network.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@
1717
package container
1818

1919
import (
20+
"errors"
21+
"fmt"
2022
"net"
2123

2224
"github.com/spf13/cobra"
2325

2426
"github.com/containerd/go-cni"
2527

2628
"github.com/containerd/nerdctl/v2/pkg/api/types"
29+
"github.com/containerd/nerdctl/v2/pkg/dnsutil"
2730
"github.com/containerd/nerdctl/v2/pkg/portutil"
2831
"github.com/containerd/nerdctl/v2/pkg/strutil"
2932
)
@@ -109,6 +112,14 @@ func loadNetworkFlags(cmd *cobra.Command, globalOpts types.GlobalCommandOptions)
109112
if err != nil {
110113
return netOpts, err
111114
}
115+
if len(dnsSlice) == 0 {
116+
return netOpts, errors.New("--dns flag was specified but no DNS server was provided")
117+
}
118+
for _, dns := range dnsSlice {
119+
if _, err := dnsutil.ValidateIPAddress(dns); err != nil {
120+
return netOpts, fmt.Errorf("%w with --dns flag", err)
121+
}
122+
}
112123
} else {
113124
dnsSlice = globalOpts.DNS
114125
}

pkg/dnsutil/dnsutil.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ package dnsutil
1818

1919
import (
2020
"context"
21+
"fmt"
22+
"net"
23+
"strings"
2124

2225
"github.com/containerd/nerdctl/v2/pkg/rootlessutil"
2326
)
@@ -39,3 +42,14 @@ func GetSlirp4netnsDNS() ([]string, error) {
3942
}
4043
return dns, nil
4144
}
45+
46+
// ValidateIPAddress validates if the given value is a correctly formatted
47+
// IP address, and returns the value in normalized form. Leading and trailing
48+
// whitespace is allowed, but it does not allow IPv6 addresses surrounded by
49+
// square brackets ("[::1]"). Refer to [net.ParseIP] for accepted formats.
50+
func ValidateIPAddress(val string) (string, error) {
51+
if ip := net.ParseIP(strings.TrimSpace(val)); ip != nil {
52+
return ip.String(), nil
53+
}
54+
return "", fmt.Errorf("ip address is not correctly formatted: %q", val)
55+
}

pkg/dnsutil/dnsutil_test.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package dnsutil
18+
19+
import (
20+
"testing"
21+
22+
"gotest.tools/v3/assert"
23+
)
24+
25+
func TestValidateIPAddress(t *testing.T) {
26+
tests := []struct {
27+
name string
28+
input string
29+
expectedOut string
30+
expectedErr string
31+
}{
32+
{
33+
name: "IPv4 loopback",
34+
input: `127.0.0.1`,
35+
expectedOut: `127.0.0.1`,
36+
},
37+
{
38+
name: "IPv4 loopback with whitespace",
39+
input: ` 127.0.0.1 `,
40+
expectedOut: `127.0.0.1`,
41+
},
42+
{
43+
name: "IPv6 loopback long form",
44+
input: `0:0:0:0:0:0:0:1`,
45+
expectedOut: `::1`,
46+
},
47+
{
48+
name: "IPv6 loopback",
49+
input: `::1`,
50+
expectedOut: `::1`,
51+
},
52+
{
53+
name: "IPv6 loopback with whitespace",
54+
input: ` ::1 `,
55+
expectedOut: `::1`,
56+
},
57+
{
58+
name: "IPv6 lowercase",
59+
input: `2001:db8::68`,
60+
expectedOut: `2001:db8::68`,
61+
},
62+
{
63+
name: "IPv6 uppercase",
64+
input: `2001:DB8::68`,
65+
expectedOut: `2001:db8::68`,
66+
},
67+
{
68+
name: "IPv6 with brackets",
69+
input: `[::1]`,
70+
expectedErr: `ip address is not correctly formatted: "[::1]"`,
71+
},
72+
{
73+
name: "IPv4 partial",
74+
input: `127`,
75+
expectedErr: `ip address is not correctly formatted: "127"`,
76+
},
77+
{
78+
name: "random invalid string",
79+
input: `random invalid string`,
80+
expectedErr: `ip address is not correctly formatted: "random invalid string"`,
81+
},
82+
{
83+
name: "empty string",
84+
input: ``,
85+
expectedErr: `ip address is not correctly formatted: ""`,
86+
},
87+
{
88+
name: "only whitespace",
89+
input: ` `,
90+
expectedErr: `ip address is not correctly formatted: " "`,
91+
},
92+
}
93+
94+
for _, tc := range tests {
95+
t.Run(tc.input, func(t *testing.T) {
96+
actualOut, actualErr := ValidateIPAddress(tc.input)
97+
assert.Equal(t, tc.expectedOut, actualOut)
98+
if tc.expectedErr == "" {
99+
assert.Check(t, actualErr)
100+
} else {
101+
assert.Equal(t, tc.expectedErr, actualErr.Error())
102+
}
103+
})
104+
}
105+
}

0 commit comments

Comments
 (0)