open-nomad/client/config/artifact.go
Michael Schurter 2965dc6a1a
artifact: fix numerous go-getter security issues
Fix numerous go-getter security issues:

- Add timeouts to http, git, and hg operations to prevent DoS
- Add size limit to http to prevent resource exhaustion
- Disable following symlinks in both artifacts and `job run`
- Stop performing initial HEAD request to avoid file corruption on
  retries and DoS opportunities.

**Approach**

Since Nomad has no ability to differentiate a DoS-via-large-artifact vs
a legitimate workload, all of the new limits are configurable at the
client agent level.

The max size of HTTP downloads is also exposed as a node attribute so
that if some workloads have large artifacts they can specify a high
limit in their jobspecs.

In the future all of this plumbing could be extended to enable/disable
specific getters or artifact downloading entirely on a per-node basis.
2022-05-24 16:29:39 -04:00

75 lines
1.7 KiB
Go

package config
import (
"fmt"
"time"
"github.com/dustin/go-humanize"
"github.com/hashicorp/nomad/nomad/structs/config"
)
// ArtifactConfig is the internal readonly copy of the client agent's
// ArtifactConfig.
type ArtifactConfig struct {
HTTPReadTimeout time.Duration
HTTPMaxBytes int64
GCSTimeout time.Duration
GitTimeout time.Duration
HgTimeout time.Duration
S3Timeout time.Duration
}
// ArtifactConfigFromAgent creates a new internal readonly copy of the client
// agent's ArtifactConfig. The config should have already been validated.
func ArtifactConfigFromAgent(c *config.ArtifactConfig) (*ArtifactConfig, error) {
newConfig := &ArtifactConfig{}
t, err := time.ParseDuration(*c.HTTPReadTimeout)
if err != nil {
return nil, fmt.Errorf("error parsing HTTPReadTimeout: %w", err)
}
newConfig.HTTPReadTimeout = t
s, err := humanize.ParseBytes(*c.HTTPMaxSize)
if err != nil {
return nil, fmt.Errorf("error parsing HTTPMaxSize: %w", err)
}
newConfig.HTTPMaxBytes = int64(s)
t, err = time.ParseDuration(*c.GCSTimeout)
if err != nil {
return nil, fmt.Errorf("error parsing GCSTimeout: %w", err)
}
newConfig.GCSTimeout = t
t, err = time.ParseDuration(*c.GitTimeout)
if err != nil {
return nil, fmt.Errorf("error parsing GitTimeout: %w", err)
}
newConfig.GitTimeout = t
t, err = time.ParseDuration(*c.HgTimeout)
if err != nil {
return nil, fmt.Errorf("error parsing HgTimeout: %w", err)
}
newConfig.HgTimeout = t
t, err = time.ParseDuration(*c.S3Timeout)
if err != nil {
return nil, fmt.Errorf("error parsing S3Timeout: %w", err)
}
newConfig.S3Timeout = t
return newConfig, nil
}
func (a *ArtifactConfig) Copy() *ArtifactConfig {
if a == nil {
return nil
}
newCopy := *a
return &newCopy
}