Skip to content

Commit

Permalink
client/template: configuration for function blacklist and sandboxing
Browse files Browse the repository at this point in the history
When rendering a task template, the `plugin` function is no longer
permitted by default and will raise an error. An operator can opt-in
to permitting this function with the new `template.function_blacklist`
field in the client configuration.

When rendering a task template, path parameters for the `file`
function will be treated as relative to the task directory by
default. Relative paths or symlinks that point outside the task
directory will raise an error. An operator can opt-out of this
protection with the new `template.disable_file_sandbox` field in the
client configuration.
  • Loading branch information
tgross committed Aug 12, 2019
1 parent e4e7ca0 commit ffb83e1
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 23 deletions.
14 changes: 9 additions & 5 deletions client/allocrunner/taskrunner/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -545,11 +545,11 @@ func maskProcessEnv(env map[string]string) map[string]string {

// parseTemplateConfigs converts the tasks templates in the config into
// consul-templates
func parseTemplateConfigs(config *TaskTemplateManagerConfig) (map[ctconf.TemplateConfig]*structs.Template, error) {
func parseTemplateConfigs(config *TaskTemplateManagerConfig) (map[*ctconf.TemplateConfig]*structs.Template, error) {
allowAbs := config.ClientConfig.ReadBoolDefault(hostSrcOption, true)
taskEnv := config.EnvBuilder.Build()

ctmpls := make(map[ctconf.TemplateConfig]*structs.Template, len(config.Templates))
ctmpls := make(map[*ctconf.TemplateConfig]*structs.Template, len(config.Templates))
for _, tmpl := range config.Templates {
var src, dest string
if tmpl.SourcePath != "" {
Expand All @@ -573,6 +573,10 @@ func parseTemplateConfigs(config *TaskTemplateManagerConfig) (map[ctconf.Templat
ct.Contents = &tmpl.EmbeddedTmpl
ct.LeftDelim = &tmpl.LeftDelim
ct.RightDelim = &tmpl.RightDelim
ct.FunctionBlacklist = config.ClientConfig.TemplateConfig.FunctionBlacklist
if !config.ClientConfig.TemplateConfig.DisableSandbox {
ct.SandboxPath = &config.TaskDir
}

// Set the permissions
if tmpl.Perms != "" {
Expand All @@ -585,7 +589,7 @@ func parseTemplateConfigs(config *TaskTemplateManagerConfig) (map[ctconf.Templat
}
ct.Finalize()

ctmpls[*ct] = tmpl
ctmpls[ct] = tmpl
}

return ctmpls, nil
Expand All @@ -594,7 +598,7 @@ func parseTemplateConfigs(config *TaskTemplateManagerConfig) (map[ctconf.Templat
// newRunnerConfig returns a consul-template runner configuration, setting the
// Vault and Consul configurations based on the clients configs.
func newRunnerConfig(config *TaskTemplateManagerConfig,
templateMapping map[ctconf.TemplateConfig]*structs.Template) (*ctconf.Config, error) {
templateMapping map[*ctconf.TemplateConfig]*structs.Template) (*ctconf.Config, error) {

cc := config.ClientConfig
conf := ctconf.DefaultConfig()
Expand All @@ -603,7 +607,7 @@ func newRunnerConfig(config *TaskTemplateManagerConfig,
flat := ctconf.TemplateConfigs(make([]*ctconf.TemplateConfig, 0, len(templateMapping)))
for ctmpl := range templateMapping {
local := ctmpl
flat = append(flat, &local)
flat = append(flat, local)
}
conf.Templates = &flat

Expand Down
9 changes: 7 additions & 2 deletions client/allocrunner/taskrunner/template/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,13 @@ func newTestHarness(t *testing.T, templates []*structs.Template, consul, vault b
mockHooks: NewMockTaskHooks(),
templates: templates,
node: mock.Node(),
config: &config.Config{Region: region},
emitRate: DefaultMaxTemplateEventRate,
config: &config.Config{
Region: region,
TemplateConfig: &config.ClientTemplateConfig{
FunctionBlacklist: []string{"plugin"},
DisableSandbox: false,
}},
emitRate: DefaultMaxTemplateEventRate,
}

// Build the task environment
Expand Down
44 changes: 28 additions & 16 deletions client/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ type Config struct {
// DisableRemoteExec disables remote exec targeting tasks on this client
DisableRemoteExec bool

// TemplateConfig includes configuration for template rendering
TemplateConfig *ClientTemplateConfig

// BackwardsCompatibleMetrics determines whether to show methods of
// displaying metrics for older versions, or to only show the new format
BackwardsCompatibleMetrics bool
Expand Down Expand Up @@ -239,6 +242,11 @@ type Config struct {
HostVolumes map[string]*structs.ClientHostVolumeConfig
}

type ClientTemplateConfig struct {
FunctionBlacklist []string
DisableSandbox bool
}

func (c *Config) Copy() *Config {
nc := new(Config)
*nc = *c
Expand All @@ -254,22 +262,26 @@ func (c *Config) Copy() *Config {
// DefaultConfig returns the default configuration
func DefaultConfig() *Config {
return &Config{
Version: version.GetVersion(),
VaultConfig: config.DefaultVaultConfig(),
ConsulConfig: config.DefaultConsulConfig(),
LogOutput: os.Stderr,
Region: "global",
StatsCollectionInterval: 1 * time.Second,
TLSConfig: &config.TLSConfig{},
LogLevel: "DEBUG",
GCInterval: 1 * time.Minute,
GCParallelDestroys: 2,
GCDiskUsageThreshold: 80,
GCInodeUsageThreshold: 70,
GCMaxAllocs: 50,
NoHostUUID: true,
DisableTaggedMetrics: false,
DisableRemoteExec: false,
Version: version.GetVersion(),
VaultConfig: config.DefaultVaultConfig(),
ConsulConfig: config.DefaultConsulConfig(),
LogOutput: os.Stderr,
Region: "global",
StatsCollectionInterval: 1 * time.Second,
TLSConfig: &config.TLSConfig{},
LogLevel: "DEBUG",
GCInterval: 1 * time.Minute,
GCParallelDestroys: 2,
GCDiskUsageThreshold: 80,
GCInodeUsageThreshold: 70,
GCMaxAllocs: 50,
NoHostUUID: true,
DisableTaggedMetrics: false,
DisableRemoteExec: false,
TemplateConfig: &ClientTemplateConfig{
FunctionBlacklist: []string{"plugin"},
DisableSandbox: false,
},
BackwardsCompatibleMetrics: false,
RPCHoldTimeout: 5 * time.Second,
}
Expand Down
2 changes: 2 additions & 0 deletions command/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,8 @@ func convertClientConfig(agentConfig *Config) (*clientconfig.Config, error) {
conf.ClientMaxPort = uint(agentConfig.Client.ClientMaxPort)
conf.ClientMinPort = uint(agentConfig.Client.ClientMinPort)
conf.DisableRemoteExec = agentConfig.Client.DisableRemoteExec
conf.TemplateConfig.FunctionBlacklist = agentConfig.Client.TemplateConfig.FunctionBlacklist
conf.TemplateConfig.DisableSandbox = agentConfig.Client.TemplateConfig.DisableSandbox

hvMap := make(map[string]*structs.ClientHostVolumeConfig, len(agentConfig.Client.HostVolumes))
for _, v := range agentConfig.Client.HostVolumes {
Expand Down
29 changes: 29 additions & 0 deletions command/agent/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,9 @@ type ClientConfig struct {
// DisableRemoteExec disables remote exec targeting tasks on this client
DisableRemoteExec bool `hcl:"disable_remote_exec"`

// TemplateConfig includes configuration for template rendering
TemplateConfig *ClientTemplateConfig `hcl:"template"`

// ServerJoin contains information that is used to attempt to join servers
ServerJoin *ServerJoin `hcl:"server_join"`

Expand All @@ -266,6 +269,20 @@ type ClientConfig struct {
BridgeNetworkSubnet string `hcl:"bridge_network_subnet"`
}

// ClientTemplateConfig is configuration on the client specific to template
// rendering
type ClientTemplateConfig struct {

// FunctionBlacklist disables functions in consul-template that
// are unsafe because they expose information from the client host.
FunctionBlacklist []string `hcl:"function_blacklist"`

// DisableSandbox allows templates to access arbitrary files on the
// client host. By default templates can access files only within
// the task directory.
DisableSandbox bool `hcl:"disable_file_sandbox"`
}

// ACLConfig is configuration specific to the ACL system
type ACLConfig struct {
// Enabled controls if we are enforce and manage ACLs
Expand Down Expand Up @@ -675,6 +692,10 @@ func DevConfig() *Config {
conf.Client.GCDiskUsageThreshold = 99
conf.Client.GCInodeUsageThreshold = 99
conf.Client.GCMaxAllocs = 50
conf.Client.TemplateConfig = &ClientTemplateConfig{
FunctionBlacklist: []string{"plugin"},
DisableSandbox: false,
}
conf.Telemetry.PrometheusMetrics = true
conf.Telemetry.PublishAllocationMetrics = true
conf.Telemetry.PublishNodeMetrics = true
Expand Down Expand Up @@ -716,6 +737,10 @@ func DefaultConfig() *Config {
RetryInterval: 30 * time.Second,
RetryMaxAttempts: 0,
},
TemplateConfig: &ClientTemplateConfig{
FunctionBlacklist: []string{"plugin"},
DisableSandbox: false,
},
},
Server: &ServerConfig{
Enabled: false,
Expand Down Expand Up @@ -1295,6 +1320,10 @@ func (a *ClientConfig) Merge(b *ClientConfig) *ClientConfig {
result.DisableRemoteExec = b.DisableRemoteExec
}

if b.TemplateConfig != nil {
result.TemplateConfig = b.TemplateConfig
}

// Add the servers
result.Servers = append(result.Servers, b.Servers...)

Expand Down
8 changes: 8 additions & 0 deletions command/agent/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ func TestConfig_Merge(t *testing.T) {
MaxKillTimeout: "20s",
ClientMaxPort: 19996,
DisableRemoteExec: false,
TemplateConfig: &ClientTemplateConfig{
FunctionBlacklist: []string{"plugin"},
DisableSandbox: false,
},
Reserved: &Resources{
CPU: 10,
MemoryMB: 10,
Expand Down Expand Up @@ -253,6 +257,10 @@ func TestConfig_Merge(t *testing.T) {
MemoryMB: 105,
MaxKillTimeout: "50s",
DisableRemoteExec: false,
TemplateConfig: &ClientTemplateConfig{
FunctionBlacklist: []string{"plugin"},
DisableSandbox: false,
},
Reserved: &Resources{
CPU: 15,
MemoryMB: 15,
Expand Down
17 changes: 17 additions & 0 deletions website/source/docs/configuration/client.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ driver) but will be removed in a future release.
- `bridge_network_subnet` `(string: "172.26.66.0/23")` - Specifies the subnet
which the client will use to allocate IP addresses from.

- `template` <code>([Template](#template-parameters): nil)</code> - Specifies
controls on the behavior of task [`template`](/docs/job-specification/template.html) stanzas.


### `chroot_env` Parameters

Drivers based on [isolated fork/exec](/docs/drivers/exec.html) implement file
Expand Down Expand Up @@ -329,6 +333,19 @@ see the [drivers documentation](/docs/drivers/index.html).
reserve on all fingerprinted network devices. Ranges can be specified by using
a hyphen separated the two inclusive ends.


### `template` Parameters

- `function_blacklist` `([]string: ["plugin"])` - Specifies a list of template
rendering functions that should be disallowed in job specs. By default the
`plugin` function is disallowed as it allows running arbitrary commands on
the host as root (unless Nomad is configured to run as a non-root user).

- `disable_file_sandbox` `(bool: false)` - Allows templates access to arbitrary
files on the client host via the `file` function. By default templates can
access files only within the task directory.


## `client` Examples

### Common Setup
Expand Down

0 comments on commit ffb83e1

Please sign in to comment.