mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
func: Allow custom paths to be added the the getter landlock (#20349)
* func: Allow custom paths to be added the the getter landlock Fixes: 20315 * fix: slices imports fix: more meaningful examples fix: improve documentation fix: quote error output
This commit is contained in:
3
.changelog/20315.txt
Normal file
3
.changelog/20315.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
```release-note:improvement
|
||||||
|
func: Allow custom paths to be added the the getter landlock
|
||||||
|
```
|
||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/go-getter"
|
"github.com/hashicorp/go-getter"
|
||||||
|
"github.com/hashicorp/nomad/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
// parameters is encoded by the Nomad client and decoded by the getter sub-process
|
// parameters is encoded by the Nomad client and decoded by the getter sub-process
|
||||||
@@ -22,16 +23,17 @@ import (
|
|||||||
// e.g. https://www.opencve.io/cve/CVE-2022-41716
|
// e.g. https://www.opencve.io/cve/CVE-2022-41716
|
||||||
type parameters struct {
|
type parameters struct {
|
||||||
// Config
|
// Config
|
||||||
HTTPReadTimeout time.Duration `json:"http_read_timeout"`
|
HTTPReadTimeout time.Duration `json:"http_read_timeout"`
|
||||||
HTTPMaxBytes int64 `json:"http_max_bytes"`
|
HTTPMaxBytes int64 `json:"http_max_bytes"`
|
||||||
GCSTimeout time.Duration `json:"gcs_timeout"`
|
GCSTimeout time.Duration `json:"gcs_timeout"`
|
||||||
GitTimeout time.Duration `json:"git_timeout"`
|
GitTimeout time.Duration `json:"git_timeout"`
|
||||||
HgTimeout time.Duration `json:"hg_timeout"`
|
HgTimeout time.Duration `json:"hg_timeout"`
|
||||||
S3Timeout time.Duration `json:"s3_timeout"`
|
S3Timeout time.Duration `json:"s3_timeout"`
|
||||||
DecompressionLimitFileCount int `json:"decompression_limit_file_count"`
|
DecompressionLimitFileCount int `json:"decompression_limit_file_count"`
|
||||||
DecompressionLimitSize int64 `json:"decompression_limit_size"`
|
DecompressionLimitSize int64 `json:"decompression_limit_size"`
|
||||||
DisableFilesystemIsolation bool `json:"disable_filesystem_isolation"`
|
DisableFilesystemIsolation bool `json:"disable_filesystem_isolation"`
|
||||||
SetEnvironmentVariables string `json:"set_environment_variables"`
|
FilesystemIsolationExtraPaths []string `json:"filesystem_isolation_extra_paths"`
|
||||||
|
SetEnvironmentVariables string `json:"set_environment_variables"`
|
||||||
|
|
||||||
// Artifact
|
// Artifact
|
||||||
Mode getter.ClientMode `json:"artifact_mode"`
|
Mode getter.ClientMode `json:"artifact_mode"`
|
||||||
@@ -98,6 +100,8 @@ func (p *parameters) Equal(o *parameters) bool {
|
|||||||
return false
|
return false
|
||||||
case p.DisableFilesystemIsolation != o.DisableFilesystemIsolation:
|
case p.DisableFilesystemIsolation != o.DisableFilesystemIsolation:
|
||||||
return false
|
return false
|
||||||
|
case !helper.SliceSetEq(p.FilesystemIsolationExtraPaths, o.FilesystemIsolationExtraPaths):
|
||||||
|
return false
|
||||||
case p.SetEnvironmentVariables != o.SetEnvironmentVariables:
|
case p.SetEnvironmentVariables != o.SetEnvironmentVariables:
|
||||||
return false
|
return false
|
||||||
case p.Mode != o.Mode:
|
case p.Mode != o.Mode:
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ const paramsAsJSON = `
|
|||||||
"decompression_limit_file_count": 3,
|
"decompression_limit_file_count": 3,
|
||||||
"decompression_limit_size": 98765,
|
"decompression_limit_size": 98765,
|
||||||
"disable_filesystem_isolation": true,
|
"disable_filesystem_isolation": true,
|
||||||
|
"filesystem_isolation_extra_paths": [
|
||||||
|
"f:r:/dev/urandom",
|
||||||
|
"d:rx:/opt/bin",
|
||||||
|
"d:r:/tmp/stash"
|
||||||
|
],
|
||||||
"set_environment_variables": "",
|
"set_environment_variables": "",
|
||||||
"artifact_mode": 2,
|
"artifact_mode": 2,
|
||||||
"artifact_insecure": false,
|
"artifact_insecure": false,
|
||||||
@@ -47,7 +52,11 @@ var paramsAsStruct = ¶meters{
|
|||||||
DecompressionLimitFileCount: 3,
|
DecompressionLimitFileCount: 3,
|
||||||
DecompressionLimitSize: 98765,
|
DecompressionLimitSize: 98765,
|
||||||
DisableFilesystemIsolation: true,
|
DisableFilesystemIsolation: true,
|
||||||
|
FilesystemIsolationExtraPaths: []string{
|
||||||
|
"f:r:/dev/urandom",
|
||||||
|
"d:rx:/opt/bin",
|
||||||
|
"d:r:/tmp/stash",
|
||||||
|
},
|
||||||
Mode: getter.ClientModeFile,
|
Mode: getter.ClientModeFile,
|
||||||
Source: "https://example.com/file.txt",
|
Source: "https://example.com/file.txt",
|
||||||
Destination: "local/out.txt",
|
Destination: "local/out.txt",
|
||||||
|
|||||||
@@ -44,16 +44,17 @@ func (s *Sandbox) Get(env interfaces.EnvReplacer, artifact *structs.TaskArtifact
|
|||||||
|
|
||||||
params := ¶meters{
|
params := ¶meters{
|
||||||
// downloader configuration
|
// downloader configuration
|
||||||
HTTPReadTimeout: s.ac.HTTPReadTimeout,
|
HTTPReadTimeout: s.ac.HTTPReadTimeout,
|
||||||
HTTPMaxBytes: s.ac.HTTPMaxBytes,
|
HTTPMaxBytes: s.ac.HTTPMaxBytes,
|
||||||
GCSTimeout: s.ac.GCSTimeout,
|
GCSTimeout: s.ac.GCSTimeout,
|
||||||
GitTimeout: s.ac.GitTimeout,
|
GitTimeout: s.ac.GitTimeout,
|
||||||
HgTimeout: s.ac.HgTimeout,
|
HgTimeout: s.ac.HgTimeout,
|
||||||
S3Timeout: s.ac.S3Timeout,
|
S3Timeout: s.ac.S3Timeout,
|
||||||
DecompressionLimitFileCount: s.ac.DecompressionLimitFileCount,
|
DecompressionLimitFileCount: s.ac.DecompressionLimitFileCount,
|
||||||
DecompressionLimitSize: s.ac.DecompressionLimitSize,
|
DecompressionLimitSize: s.ac.DecompressionLimitSize,
|
||||||
DisableFilesystemIsolation: s.ac.DisableFilesystemIsolation,
|
DisableFilesystemIsolation: s.ac.DisableFilesystemIsolation,
|
||||||
SetEnvironmentVariables: s.ac.SetEnvironmentVariables,
|
FilesystemIsolationExtraPaths: s.ac.FilesystemIsolationExtraPaths,
|
||||||
|
SetEnvironmentVariables: s.ac.SetEnvironmentVariables,
|
||||||
|
|
||||||
// artifact configuration
|
// artifact configuration
|
||||||
Mode: mode,
|
Mode: mode,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// lockdown is not implemented by default
|
// lockdown is not implemented by default
|
||||||
func lockdown(string, string) error {
|
func lockdown(string, string, []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ func defaultEnvironment(taskDir string) map[string]string {
|
|||||||
// dir - the task directory
|
// dir - the task directory
|
||||||
//
|
//
|
||||||
// Only applies to Linux, when available.
|
// Only applies to Linux, when available.
|
||||||
func lockdown(allocDir, taskDir string) error {
|
func lockdown(allocDir, taskDir string, extra []string) error {
|
||||||
// landlock not present in the kernel, do not sandbox
|
// landlock not present in the kernel, do not sandbox
|
||||||
if !landlock.Available() {
|
if !landlock.Available() {
|
||||||
return nil
|
return nil
|
||||||
@@ -68,6 +68,13 @@ func lockdown(allocDir, taskDir string) error {
|
|||||||
landlock.Dir(taskDir, "rwc"),
|
landlock.Dir(taskDir, "rwc"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, p := range extra {
|
||||||
|
path, err := landlock.ParsePath(p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
paths = append(paths, path)
|
||||||
|
}
|
||||||
paths = append(paths, additionalFilesForVCS()...)
|
paths = append(paths, additionalFilesForVCS()...)
|
||||||
locker := landlock.New(paths...)
|
locker := landlock.New(paths...)
|
||||||
return locker.Lock(landlock.Mandatory)
|
return locker.Lock(landlock.Mandatory)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// lockdown is not implemented on Windows
|
// lockdown is not implemented on Windows
|
||||||
func lockdown(string, string) error {
|
func lockdown(string, string, []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func init() {
|
|||||||
|
|
||||||
// sandbox the host filesystem for this process
|
// sandbox the host filesystem for this process
|
||||||
if !env.DisableFilesystemIsolation {
|
if !env.DisableFilesystemIsolation {
|
||||||
if err := lockdown(env.AllocDir, env.TaskDir); err != nil {
|
if err := lockdown(env.AllocDir, env.TaskDir, env.FilesystemIsolationExtraPaths); err != nil {
|
||||||
subproc.Print("failed to sandbox %s process: %v", SubCommand, err)
|
subproc.Print("failed to sandbox %s process: %v", SubCommand, err)
|
||||||
return subproc.ExitFailure
|
return subproc.ExitFailure
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
@@ -25,8 +26,9 @@ type ArtifactConfig struct {
|
|||||||
DecompressionLimitFileCount int
|
DecompressionLimitFileCount int
|
||||||
DecompressionLimitSize int64
|
DecompressionLimitSize int64
|
||||||
|
|
||||||
DisableFilesystemIsolation bool
|
DisableFilesystemIsolation bool
|
||||||
SetEnvironmentVariables string
|
FilesystemIsolationExtraPaths []string
|
||||||
|
SetEnvironmentVariables string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ArtifactConfigFromAgent creates a new internal readonly copy of the client
|
// ArtifactConfigFromAgent creates a new internal readonly copy of the client
|
||||||
@@ -68,17 +70,19 @@ func ArtifactConfigFromAgent(c *config.ArtifactConfig) (*ArtifactConfig, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &ArtifactConfig{
|
return &ArtifactConfig{
|
||||||
HTTPReadTimeout: httpReadTimeout,
|
HTTPReadTimeout: httpReadTimeout,
|
||||||
HTTPMaxBytes: int64(httpMaxSize),
|
HTTPMaxBytes: int64(httpMaxSize),
|
||||||
GCSTimeout: gcsTimeout,
|
GCSTimeout: gcsTimeout,
|
||||||
GitTimeout: gitTimeout,
|
GitTimeout: gitTimeout,
|
||||||
HgTimeout: hgTimeout,
|
HgTimeout: hgTimeout,
|
||||||
S3Timeout: s3Timeout,
|
S3Timeout: s3Timeout,
|
||||||
DecompressionLimitFileCount: *c.DecompressionFileCountLimit,
|
DecompressionLimitFileCount: *c.DecompressionFileCountLimit,
|
||||||
DecompressionLimitSize: int64(decompressionSizeLimit),
|
DecompressionLimitSize: int64(decompressionSizeLimit),
|
||||||
DisableFilesystemIsolation: *c.DisableFilesystemIsolation,
|
DisableFilesystemIsolation: *c.DisableFilesystemIsolation,
|
||||||
SetEnvironmentVariables: *c.SetEnvironmentVariables,
|
FilesystemIsolationExtraPaths: slices.Clone(c.FilesystemIsolationExtraPaths),
|
||||||
|
SetEnvironmentVariables: *c.SetEnvironmentVariables,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *ArtifactConfig) Copy() *ArtifactConfig {
|
func (a *ArtifactConfig) Copy() *ArtifactConfig {
|
||||||
|
|||||||
@@ -129,14 +129,15 @@ func TestArtifactConfig_Copy(t *testing.T) {
|
|||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
|
|
||||||
ac := &ArtifactConfig{
|
ac := &ArtifactConfig{
|
||||||
HTTPReadTimeout: time.Minute,
|
HTTPReadTimeout: time.Minute,
|
||||||
HTTPMaxBytes: 1000,
|
HTTPMaxBytes: 1000,
|
||||||
GCSTimeout: 2 * time.Minute,
|
GCSTimeout: 2 * time.Minute,
|
||||||
GitTimeout: time.Second,
|
GitTimeout: time.Second,
|
||||||
HgTimeout: time.Hour,
|
HgTimeout: time.Hour,
|
||||||
S3Timeout: 5 * time.Minute,
|
S3Timeout: 5 * time.Minute,
|
||||||
DisableFilesystemIsolation: true,
|
DisableFilesystemIsolation: true,
|
||||||
SetEnvironmentVariables: "FOO,BAR",
|
FilesystemIsolationExtraPaths: []string{"f:r:/dev/urandom"},
|
||||||
|
SetEnvironmentVariables: "FOO,BAR",
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure values are copied.
|
// make sure values are copied.
|
||||||
@@ -151,16 +152,18 @@ func TestArtifactConfig_Copy(t *testing.T) {
|
|||||||
configCopy.HgTimeout = 2 * time.Hour
|
configCopy.HgTimeout = 2 * time.Hour
|
||||||
configCopy.S3Timeout = 10 * time.Minute
|
configCopy.S3Timeout = 10 * time.Minute
|
||||||
configCopy.DisableFilesystemIsolation = false
|
configCopy.DisableFilesystemIsolation = false
|
||||||
|
configCopy.FilesystemIsolationExtraPaths = []string{"f:rx:/opt/bin/runme"}
|
||||||
configCopy.SetEnvironmentVariables = "BAZ"
|
configCopy.SetEnvironmentVariables = "BAZ"
|
||||||
|
|
||||||
must.Eq(t, &ArtifactConfig{
|
must.Eq(t, &ArtifactConfig{
|
||||||
HTTPReadTimeout: time.Minute,
|
HTTPReadTimeout: time.Minute,
|
||||||
HTTPMaxBytes: 1000,
|
HTTPMaxBytes: 1000,
|
||||||
GCSTimeout: 2 * time.Minute,
|
GCSTimeout: 2 * time.Minute,
|
||||||
GitTimeout: time.Second,
|
GitTimeout: time.Second,
|
||||||
HgTimeout: time.Hour,
|
HgTimeout: time.Hour,
|
||||||
S3Timeout: 5 * time.Minute,
|
S3Timeout: 5 * time.Minute,
|
||||||
DisableFilesystemIsolation: true,
|
DisableFilesystemIsolation: true,
|
||||||
SetEnvironmentVariables: "FOO,BAR",
|
FilesystemIsolationExtraPaths: []string{"f:r:/dev/urandom"},
|
||||||
|
SetEnvironmentVariables: "FOO,BAR",
|
||||||
}, ac)
|
}, ac)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -898,6 +898,7 @@ func convertClientConfig(agentConfig *Config) (*clientconfig.Config, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid artifact config: %v", err)
|
return nil, fmt.Errorf("invalid artifact config: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
conf.Artifact = artifactConfig
|
conf.Artifact = artifactConfig
|
||||||
|
|
||||||
drainConfig, err := clientconfig.DrainConfigFromAgent(agentConfig.Client.Drain)
|
drainConfig, err := clientconfig.DrainConfigFromAgent(agentConfig.Client.Drain)
|
||||||
|
|||||||
@@ -6,10 +6,13 @@ package config
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
|
"github.com/hashicorp/nomad/helper"
|
||||||
"github.com/hashicorp/nomad/helper/pointer"
|
"github.com/hashicorp/nomad/helper/pointer"
|
||||||
|
"github.com/shoenig/go-landlock"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ArtifactConfig is the configuration specific to the Artifact block
|
// ArtifactConfig is the configuration specific to the Artifact block
|
||||||
@@ -55,6 +58,10 @@ type ArtifactConfig struct {
|
|||||||
// read only from specific locations on the host filesystem.
|
// read only from specific locations on the host filesystem.
|
||||||
DisableFilesystemIsolation *bool `hcl:"disable_filesystem_isolation"`
|
DisableFilesystemIsolation *bool `hcl:"disable_filesystem_isolation"`
|
||||||
|
|
||||||
|
// FilesystemIsolationExtraPaths allows extra paths to be included in
|
||||||
|
// the sandbox used by the artifact downloader
|
||||||
|
FilesystemIsolationExtraPaths []string `hcl:"filesystem_isolation_extra_paths"`
|
||||||
|
|
||||||
// SetEnvironmentVariables is a comma-separated list of environment
|
// SetEnvironmentVariables is a comma-separated list of environment
|
||||||
// variable names to inherit from the Nomad Client and set in the artifact
|
// variable names to inherit from the Nomad Client and set in the artifact
|
||||||
// download sandbox process.
|
// download sandbox process.
|
||||||
@@ -66,16 +73,17 @@ func (a *ArtifactConfig) Copy() *ArtifactConfig {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &ArtifactConfig{
|
return &ArtifactConfig{
|
||||||
HTTPReadTimeout: pointer.Copy(a.HTTPReadTimeout),
|
HTTPReadTimeout: pointer.Copy(a.HTTPReadTimeout),
|
||||||
HTTPMaxSize: pointer.Copy(a.HTTPMaxSize),
|
HTTPMaxSize: pointer.Copy(a.HTTPMaxSize),
|
||||||
GCSTimeout: pointer.Copy(a.GCSTimeout),
|
GCSTimeout: pointer.Copy(a.GCSTimeout),
|
||||||
GitTimeout: pointer.Copy(a.GitTimeout),
|
GitTimeout: pointer.Copy(a.GitTimeout),
|
||||||
HgTimeout: pointer.Copy(a.HgTimeout),
|
HgTimeout: pointer.Copy(a.HgTimeout),
|
||||||
S3Timeout: pointer.Copy(a.S3Timeout),
|
S3Timeout: pointer.Copy(a.S3Timeout),
|
||||||
DecompressionFileCountLimit: pointer.Copy(a.DecompressionFileCountLimit),
|
DecompressionFileCountLimit: pointer.Copy(a.DecompressionFileCountLimit),
|
||||||
DecompressionSizeLimit: pointer.Copy(a.DecompressionSizeLimit),
|
DecompressionSizeLimit: pointer.Copy(a.DecompressionSizeLimit),
|
||||||
DisableFilesystemIsolation: pointer.Copy(a.DisableFilesystemIsolation),
|
DisableFilesystemIsolation: pointer.Copy(a.DisableFilesystemIsolation),
|
||||||
SetEnvironmentVariables: pointer.Copy(a.SetEnvironmentVariables),
|
FilesystemIsolationExtraPaths: slices.Clone(a.FilesystemIsolationExtraPaths),
|
||||||
|
SetEnvironmentVariables: pointer.Copy(a.SetEnvironmentVariables),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +94,7 @@ func (a *ArtifactConfig) Merge(o *ArtifactConfig) *ArtifactConfig {
|
|||||||
case o == nil:
|
case o == nil:
|
||||||
return a.Copy()
|
return a.Copy()
|
||||||
default:
|
default:
|
||||||
return &ArtifactConfig{
|
result := &ArtifactConfig{
|
||||||
HTTPReadTimeout: pointer.Merge(a.HTTPReadTimeout, o.HTTPReadTimeout),
|
HTTPReadTimeout: pointer.Merge(a.HTTPReadTimeout, o.HTTPReadTimeout),
|
||||||
HTTPMaxSize: pointer.Merge(a.HTTPMaxSize, o.HTTPMaxSize),
|
HTTPMaxSize: pointer.Merge(a.HTTPMaxSize, o.HTTPMaxSize),
|
||||||
GCSTimeout: pointer.Merge(a.GCSTimeout, o.GCSTimeout),
|
GCSTimeout: pointer.Merge(a.GCSTimeout, o.GCSTimeout),
|
||||||
@@ -98,6 +106,14 @@ func (a *ArtifactConfig) Merge(o *ArtifactConfig) *ArtifactConfig {
|
|||||||
DisableFilesystemIsolation: pointer.Merge(a.DisableFilesystemIsolation, o.DisableFilesystemIsolation),
|
DisableFilesystemIsolation: pointer.Merge(a.DisableFilesystemIsolation, o.DisableFilesystemIsolation),
|
||||||
SetEnvironmentVariables: pointer.Merge(a.SetEnvironmentVariables, o.SetEnvironmentVariables),
|
SetEnvironmentVariables: pointer.Merge(a.SetEnvironmentVariables, o.SetEnvironmentVariables),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if o.FilesystemIsolationExtraPaths != nil {
|
||||||
|
result.FilesystemIsolationExtraPaths = slices.Clone(o.FilesystemIsolationExtraPaths)
|
||||||
|
} else {
|
||||||
|
result.FilesystemIsolationExtraPaths = slices.Clone(a.FilesystemIsolationExtraPaths)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,6 +140,8 @@ func (a *ArtifactConfig) Equal(o *ArtifactConfig) bool {
|
|||||||
return false
|
return false
|
||||||
case !pointer.Eq(a.DisableFilesystemIsolation, o.DisableFilesystemIsolation):
|
case !pointer.Eq(a.DisableFilesystemIsolation, o.DisableFilesystemIsolation):
|
||||||
return false
|
return false
|
||||||
|
case !helper.SliceSetEq(a.FilesystemIsolationExtraPaths, o.FilesystemIsolationExtraPaths):
|
||||||
|
return false
|
||||||
case !pointer.Eq(a.SetEnvironmentVariables, o.SetEnvironmentVariables):
|
case !pointer.Eq(a.SetEnvironmentVariables, o.SetEnvironmentVariables):
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -209,6 +227,12 @@ func (a *ArtifactConfig) Validate() error {
|
|||||||
return fmt.Errorf("disable_filesystem_isolation must be set")
|
return fmt.Errorf("disable_filesystem_isolation must be set")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, p := range a.FilesystemIsolationExtraPaths {
|
||||||
|
if _, err := landlock.ParsePath(p); err != nil {
|
||||||
|
return fmt.Errorf("filesystem_isolation_extra_paths contains invalid lockdown path %q", p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if a.SetEnvironmentVariables == nil {
|
if a.SetEnvironmentVariables == nil {
|
||||||
return fmt.Errorf("set_environment_variables must be set")
|
return fmt.Errorf("set_environment_variables must be set")
|
||||||
}
|
}
|
||||||
@@ -254,6 +278,9 @@ func DefaultArtifactConfig() *ArtifactConfig {
|
|||||||
// Toggle for disabling filesystem isolation, where available.
|
// Toggle for disabling filesystem isolation, where available.
|
||||||
DisableFilesystemIsolation: pointer.Of(false),
|
DisableFilesystemIsolation: pointer.Of(false),
|
||||||
|
|
||||||
|
// No Filesystem Isolation Extra Locations by default
|
||||||
|
FilesystemIsolationExtraPaths: nil,
|
||||||
|
|
||||||
// No environment variables are inherited from Client by default.
|
// No environment variables are inherited from Client by default.
|
||||||
SetEnvironmentVariables: pointer.Of(""),
|
SetEnvironmentVariables: pointer.Of(""),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ func TestArtifactConfig_Copy(t *testing.T) {
|
|||||||
ci.Parallel(t)
|
ci.Parallel(t)
|
||||||
|
|
||||||
a := DefaultArtifactConfig()
|
a := DefaultArtifactConfig()
|
||||||
|
a.FilesystemIsolationExtraPaths = []string{
|
||||||
|
"f:r:/dev/urandom",
|
||||||
|
"d:rx:/opt/bin",
|
||||||
|
"d:r:/tmp/stash",
|
||||||
|
}
|
||||||
b := a.Copy()
|
b := a.Copy()
|
||||||
must.Equal(t, a, b)
|
must.Equal(t, a, b)
|
||||||
must.Equal(t, b, a)
|
must.Equal(t, b, a)
|
||||||
@@ -26,6 +31,10 @@ func TestArtifactConfig_Copy(t *testing.T) {
|
|||||||
b.DecompressionFileCountLimit = pointer.Of(7)
|
b.DecompressionFileCountLimit = pointer.Of(7)
|
||||||
b.DecompressionSizeLimit = pointer.Of("2GB")
|
b.DecompressionSizeLimit = pointer.Of("2GB")
|
||||||
must.NotEqual(t, a, b)
|
must.NotEqual(t, a, b)
|
||||||
|
|
||||||
|
b = a.Copy()
|
||||||
|
b.FilesystemIsolationExtraPaths[1] = "f:rx:/opt/bin/runme"
|
||||||
|
must.NotEqual(t, a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestArtifactConfig_Merge(t *testing.T) {
|
func TestArtifactConfig_Merge(t *testing.T) {
|
||||||
@@ -49,7 +58,12 @@ func TestArtifactConfig_Merge(t *testing.T) {
|
|||||||
DecompressionFileCountLimit: pointer.Of(4096),
|
DecompressionFileCountLimit: pointer.Of(4096),
|
||||||
DecompressionSizeLimit: pointer.Of("100GB"),
|
DecompressionSizeLimit: pointer.Of("100GB"),
|
||||||
DisableFilesystemIsolation: pointer.Of(false),
|
DisableFilesystemIsolation: pointer.Of(false),
|
||||||
SetEnvironmentVariables: pointer.Of(""),
|
FilesystemIsolationExtraPaths: []string{
|
||||||
|
"f:r:/dev/urandom",
|
||||||
|
"d:rx:/opt/bin",
|
||||||
|
"d:r:/tmp/stash",
|
||||||
|
},
|
||||||
|
SetEnvironmentVariables: pointer.Of(""),
|
||||||
},
|
},
|
||||||
other: &ArtifactConfig{
|
other: &ArtifactConfig{
|
||||||
HTTPReadTimeout: pointer.Of("5m"),
|
HTTPReadTimeout: pointer.Of("5m"),
|
||||||
@@ -61,7 +75,11 @@ func TestArtifactConfig_Merge(t *testing.T) {
|
|||||||
DecompressionFileCountLimit: pointer.Of(100),
|
DecompressionFileCountLimit: pointer.Of(100),
|
||||||
DecompressionSizeLimit: pointer.Of("8GB"),
|
DecompressionSizeLimit: pointer.Of("8GB"),
|
||||||
DisableFilesystemIsolation: pointer.Of(true),
|
DisableFilesystemIsolation: pointer.Of(true),
|
||||||
SetEnvironmentVariables: pointer.Of("FOO,BAR"),
|
FilesystemIsolationExtraPaths: []string{
|
||||||
|
"d:rw:/opt/certs",
|
||||||
|
"f:rx:/opt/bin/runme",
|
||||||
|
},
|
||||||
|
SetEnvironmentVariables: pointer.Of("FOO,BAR"),
|
||||||
},
|
},
|
||||||
expected: &ArtifactConfig{
|
expected: &ArtifactConfig{
|
||||||
HTTPReadTimeout: pointer.Of("5m"),
|
HTTPReadTimeout: pointer.Of("5m"),
|
||||||
@@ -73,7 +91,11 @@ func TestArtifactConfig_Merge(t *testing.T) {
|
|||||||
DecompressionFileCountLimit: pointer.Of(100),
|
DecompressionFileCountLimit: pointer.Of(100),
|
||||||
DecompressionSizeLimit: pointer.Of("8GB"),
|
DecompressionSizeLimit: pointer.Of("8GB"),
|
||||||
DisableFilesystemIsolation: pointer.Of(true),
|
DisableFilesystemIsolation: pointer.Of(true),
|
||||||
SetEnvironmentVariables: pointer.Of("FOO,BAR"),
|
FilesystemIsolationExtraPaths: []string{
|
||||||
|
"d:rw:/opt/certs",
|
||||||
|
"f:rx:/opt/bin/runme",
|
||||||
|
},
|
||||||
|
SetEnvironmentVariables: pointer.Of("FOO,BAR"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -89,7 +111,11 @@ func TestArtifactConfig_Merge(t *testing.T) {
|
|||||||
DecompressionFileCountLimit: pointer.Of(100),
|
DecompressionFileCountLimit: pointer.Of(100),
|
||||||
DecompressionSizeLimit: pointer.Of("8GB"),
|
DecompressionSizeLimit: pointer.Of("8GB"),
|
||||||
DisableFilesystemIsolation: pointer.Of(true),
|
DisableFilesystemIsolation: pointer.Of(true),
|
||||||
SetEnvironmentVariables: pointer.Of("FOO,BAR"),
|
FilesystemIsolationExtraPaths: []string{
|
||||||
|
"d:rw:/opt/certs",
|
||||||
|
"f:rx:/opt/bin/runme",
|
||||||
|
},
|
||||||
|
SetEnvironmentVariables: pointer.Of("FOO,BAR"),
|
||||||
},
|
},
|
||||||
expected: &ArtifactConfig{
|
expected: &ArtifactConfig{
|
||||||
HTTPReadTimeout: pointer.Of("5m"),
|
HTTPReadTimeout: pointer.Of("5m"),
|
||||||
@@ -101,7 +127,11 @@ func TestArtifactConfig_Merge(t *testing.T) {
|
|||||||
DecompressionFileCountLimit: pointer.Of(100),
|
DecompressionFileCountLimit: pointer.Of(100),
|
||||||
DecompressionSizeLimit: pointer.Of("8GB"),
|
DecompressionSizeLimit: pointer.Of("8GB"),
|
||||||
DisableFilesystemIsolation: pointer.Of(true),
|
DisableFilesystemIsolation: pointer.Of(true),
|
||||||
SetEnvironmentVariables: pointer.Of("FOO,BAR"),
|
FilesystemIsolationExtraPaths: []string{
|
||||||
|
"d:rw:/opt/certs",
|
||||||
|
"f:rx:/opt/bin/runme",
|
||||||
|
},
|
||||||
|
SetEnvironmentVariables: pointer.Of("FOO,BAR"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -116,7 +146,12 @@ func TestArtifactConfig_Merge(t *testing.T) {
|
|||||||
DecompressionFileCountLimit: pointer.Of(4096),
|
DecompressionFileCountLimit: pointer.Of(4096),
|
||||||
DecompressionSizeLimit: pointer.Of("100GB"),
|
DecompressionSizeLimit: pointer.Of("100GB"),
|
||||||
DisableFilesystemIsolation: pointer.Of(true),
|
DisableFilesystemIsolation: pointer.Of(true),
|
||||||
SetEnvironmentVariables: pointer.Of("FOO,BAR"),
|
FilesystemIsolationExtraPaths: []string{
|
||||||
|
"f:r:/dev/urandom",
|
||||||
|
"d:rx:/opt/bin",
|
||||||
|
"d:r:/tmp/stash",
|
||||||
|
},
|
||||||
|
SetEnvironmentVariables: pointer.Of("FOO,BAR"),
|
||||||
},
|
},
|
||||||
other: nil,
|
other: nil,
|
||||||
expected: &ArtifactConfig{
|
expected: &ArtifactConfig{
|
||||||
@@ -129,7 +164,54 @@ func TestArtifactConfig_Merge(t *testing.T) {
|
|||||||
DecompressionFileCountLimit: pointer.Of(4096),
|
DecompressionFileCountLimit: pointer.Of(4096),
|
||||||
DecompressionSizeLimit: pointer.Of("100GB"),
|
DecompressionSizeLimit: pointer.Of("100GB"),
|
||||||
DisableFilesystemIsolation: pointer.Of(true),
|
DisableFilesystemIsolation: pointer.Of(true),
|
||||||
SetEnvironmentVariables: pointer.Of("FOO,BAR"),
|
FilesystemIsolationExtraPaths: []string{
|
||||||
|
"f:r:/dev/urandom",
|
||||||
|
"d:rx:/opt/bin",
|
||||||
|
"d:r:/tmp/stash",
|
||||||
|
},
|
||||||
|
SetEnvironmentVariables: pointer.Of("FOO,BAR"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "null fsIsolationLocation",
|
||||||
|
source: &ArtifactConfig{
|
||||||
|
HTTPReadTimeout: pointer.Of("30m"),
|
||||||
|
HTTPMaxSize: pointer.Of("100GB"),
|
||||||
|
GCSTimeout: pointer.Of("30m"),
|
||||||
|
GitTimeout: pointer.Of("30m"),
|
||||||
|
HgTimeout: pointer.Of("30m"),
|
||||||
|
S3Timeout: pointer.Of("30m"),
|
||||||
|
DecompressionFileCountLimit: pointer.Of(4096),
|
||||||
|
DecompressionSizeLimit: pointer.Of("100GB"),
|
||||||
|
DisableFilesystemIsolation: pointer.Of(false),
|
||||||
|
FilesystemIsolationExtraPaths: nil,
|
||||||
|
SetEnvironmentVariables: pointer.Of(""),
|
||||||
|
},
|
||||||
|
other: &ArtifactConfig{
|
||||||
|
HTTPReadTimeout: pointer.Of("5m"),
|
||||||
|
HTTPMaxSize: pointer.Of("2GB"),
|
||||||
|
GCSTimeout: pointer.Of("1m"),
|
||||||
|
GitTimeout: pointer.Of("2m"),
|
||||||
|
HgTimeout: pointer.Of("3m"),
|
||||||
|
S3Timeout: pointer.Of("4m"),
|
||||||
|
DecompressionFileCountLimit: pointer.Of(100),
|
||||||
|
DecompressionSizeLimit: pointer.Of("8GB"),
|
||||||
|
DisableFilesystemIsolation: pointer.Of(true),
|
||||||
|
FilesystemIsolationExtraPaths: nil,
|
||||||
|
SetEnvironmentVariables: pointer.Of("FOO,BAR"),
|
||||||
|
},
|
||||||
|
expected: &ArtifactConfig{
|
||||||
|
HTTPReadTimeout: pointer.Of("5m"),
|
||||||
|
HTTPMaxSize: pointer.Of("2GB"),
|
||||||
|
GCSTimeout: pointer.Of("1m"),
|
||||||
|
GitTimeout: pointer.Of("2m"),
|
||||||
|
HgTimeout: pointer.Of("3m"),
|
||||||
|
S3Timeout: pointer.Of("4m"),
|
||||||
|
DecompressionFileCountLimit: pointer.Of(100),
|
||||||
|
DecompressionSizeLimit: pointer.Of("8GB"),
|
||||||
|
DisableFilesystemIsolation: pointer.Of(true),
|
||||||
|
FilesystemIsolationExtraPaths: nil,
|
||||||
|
SetEnvironmentVariables: pointer.Of("FOO,BAR"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -400,6 +482,16 @@ func TestArtifactConfig_Validate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expErr: "disable_filesystem_isolation must be set",
|
expErr: "disable_filesystem_isolation must be set",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "fs isolation extra paths contains invalid path",
|
||||||
|
config: func(a *ArtifactConfig) {
|
||||||
|
a.FilesystemIsolationExtraPaths = []string{
|
||||||
|
"f:r:/dev/urandom",
|
||||||
|
"failure",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expErr: "filesystem_isolation_extra_paths contains invalid lockdown path \"failure\"",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "env not set",
|
name: "env not set",
|
||||||
config: func(a *ArtifactConfig) {
|
config: func(a *ArtifactConfig) {
|
||||||
|
|||||||
@@ -436,6 +436,13 @@ see the [drivers documentation](/nomad/docs/drivers).
|
|||||||
isolation should be disabled for artifact downloads. Applies only to systems
|
isolation should be disabled for artifact downloads. Applies only to systems
|
||||||
where filesystem isolation via [landlock] is possible (Linux kernel 5.13+).
|
where filesystem isolation via [landlock] is possible (Linux kernel 5.13+).
|
||||||
|
|
||||||
|
- `filesystem_isolation_extra_paths` `([]string: nil)` - Allow extra paths
|
||||||
|
in the filesystem isolation. Paths are specified in the form `[kind]:[mode]:[path]`
|
||||||
|
where `kind` must be either `f` or `d` (file or directory) and
|
||||||
|
`mode` must be zero or more of `r`, `w`, `c`, `x` (read, write, create, execute) e.g.
|
||||||
|
`f:r:/dev/urandom` would enable reading the /dev/urandom file,
|
||||||
|
`d:rx:/opt/bin` would enable reading and executing from the /opt/bin directory
|
||||||
|
|
||||||
- `set_environment_variables` `(string:"")` - Specifies a comma separated list
|
- `set_environment_variables` `(string:"")` - Specifies a comma separated list
|
||||||
of environment variables that should be inherited by the artifact sandbox from
|
of environment variables that should be inherited by the artifact sandbox from
|
||||||
the Nomad client's environment. By default a minimal environment is set including
|
the Nomad client's environment. By default a minimal environment is set including
|
||||||
|
|||||||
Reference in New Issue
Block a user