Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added validation on client metadata keys #5158

Merged
merged 10 commits into from
Jan 9, 2019
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ __BACKWARDS INCOMPATIBILITIES:__
* client: Task config interpolation requires names to be valid identifiers
(`node.region` or `NOMAD_DC`). Interpolating other variables requires a new
indexing syntax: `env[".invalid.identifier."]`. [[GH-4843](https://github.com/hashicorp/nomad/issues/4843)]
* client: Node metadata variables must have valid identifiers, whether
specified in the config file (`.client.meta` stanza) or on the command line
cgbaker marked this conversation as resolved.
Show resolved Hide resolved
(`-meta`). [[GH-5158](https://github.com/hashicorp/nomad/pull/5158)]
cgbaker marked this conversation as resolved.
Show resolved Hide resolved

IMPROVEMENTS:
* core: Added advertise address to client node meta data [[GH-4390](https://github.com/hashicorp/nomad/issues/4390)]
Expand Down
8 changes: 8 additions & 0 deletions command/agent/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os/signal"
"path/filepath"
"reflect"
"regexp"
"sort"
"strconv"
"strings"
Expand Down Expand Up @@ -186,6 +187,8 @@ func (c *Command) readConfig() *Config {
// Parse the meta flags.
metaLength := len(meta)
if metaLength != 0 {
validKeyRe, _ := regexp.Compile(`^[^.]+(\.[^.]+)*$`)
cgbaker marked this conversation as resolved.
Show resolved Hide resolved

cmdConfig.Client.Meta = make(map[string]string, metaLength)
for _, kv := range meta {
parts := strings.SplitN(kv, "=", 2)
Expand All @@ -194,6 +197,11 @@ func (c *Command) readConfig() *Config {
return nil
}

if !validKeyRe.MatchString(parts[0]) {
c.Ui.Error(fmt.Sprintf("Invalid Client.Meta key: %v", parts[0]))
return nil
}

cmdConfig.Client.Meta[parts[0]] = parts[1]
}
}
Expand Down
12 changes: 12 additions & 0 deletions command/agent/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ func TestCommand_Args(t *testing.T) {
[]string{"-client", "-alloc-dir="},
"Must specify the state, alloc dir, and plugin dir if data-dir is omitted.",
},
{
[]string{"-client", "-data-dir=" + tmpDir, "-meta=invalid..key=inaccessible-value"},
"Invalid Client.Meta key: invalid..key",
},
{
[]string{"-client", "-data-dir=" + tmpDir, "-meta=.invalid=inaccessible-value"},
"Invalid Client.Meta key: .invalid",
},
{
[]string{"-client", "-data-dir=" + tmpDir, "-meta=invalid.=inaccessible-value"},
"Invalid Client.Meta key: invalid.",
},
}
for _, tc := range tcases {
// Make a new command. We preemptively close the shutdownCh
Expand Down
8 changes: 8 additions & 0 deletions command/agent/config_parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"
"os"
"path/filepath"
"regexp"
"time"

multierror "github.com/hashicorp/go-multierror"
Expand Down Expand Up @@ -437,6 +438,13 @@ func parseClient(result **ClientConfig, list *ast.ObjectList) error {
return err
}
}

validKeyRe, _ := regexp.Compile(`^[^.]+(\.[^.]+)*$`)
for k := range config.Meta {
if !validKeyRe.MatchString(k) {
return fmt.Errorf("invalid Client.Meta key: %v", k)
}
}
}

// Parse out chroot_env fields. These are in HCL as a list so we need to
Expand Down
25 changes: 25 additions & 0 deletions command/agent/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"reflect"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -418,6 +419,30 @@ func TestConfig_ParseConfigFile(t *testing.T) {
}
}

func TestConfig_ParseInvalidClientMeta(t *testing.T) {
tcases := []string{
"foo..invalid",
".invalid",
"invalid.",
}

for _, tc := range tcases {
reader := strings.NewReader(fmt.Sprintf(`client{
enabled = true
meta = {
"valid" = "yes"
"` + tc + `" = "kaboom!"
"nested.var" = "is nested"
"deeply.nested.var" = "is deeply nested"
}
}`))

if _, err := ParseConfig(reader); err == nil {
t.Fatalf("expected load error, got nothing")
}
}
}

func TestConfig_LoadConfigDir(t *testing.T) {
// Fails if the dir doesn't exist.
if _, err := LoadConfigDir("/unicorns/leprechauns"); err == nil {
Expand Down