github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/allocrunner/taskrunner/getter/params.go (about)

     1  package getter
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"io"
     7  	"io/fs"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/hashicorp/go-cleanhttp"
    12  	"github.com/hashicorp/go-getter"
    13  	"github.com/hashicorp/nomad/helper"
    14  	"golang.org/x/exp/maps"
    15  	"golang.org/x/exp/slices"
    16  )
    17  
    18  // parameters is encoded by the Nomad client and decoded by the getter sub-process
    19  // so it can know what to do. We use standard IO to pass configuration to achieve
    20  // better control over input sanitization risks.
    21  // e.g. https://www.opencve.io/cve/CVE-2022-41716
    22  type parameters struct {
    23  	// Config
    24  	HTTPReadTimeout            time.Duration `json:"http_read_timeout"`
    25  	HTTPMaxBytes               int64         `json:"http_max_bytes"`
    26  	GCSTimeout                 time.Duration `json:"gcs_timeout"`
    27  	GitTimeout                 time.Duration `json:"git_timeout"`
    28  	HgTimeout                  time.Duration `json:"hg_timeout"`
    29  	S3Timeout                  time.Duration `json:"s3_timeout"`
    30  	DisableFilesystemIsolation bool          `json:"disable_filesystem_isolation"`
    31  	SetEnvironmentVariables    string        `json:"set_environment_variables"`
    32  
    33  	// Artifact
    34  	Mode        getter.ClientMode   `json:"artifact_mode"`
    35  	Source      string              `json:"artifact_source"`
    36  	Destination string              `json:"artifact_destination"`
    37  	Headers     map[string][]string `json:"artifact_headers"`
    38  
    39  	// Task Environment
    40  	TaskDir string `json:"task_dir"`
    41  }
    42  
    43  func (p *parameters) reader() io.Reader {
    44  	b, err := json.Marshal(p)
    45  	if err != nil {
    46  		b = nil
    47  	}
    48  	return strings.NewReader(string(b))
    49  }
    50  
    51  func (p *parameters) read(r io.Reader) error {
    52  	return json.NewDecoder(r).Decode(p)
    53  }
    54  
    55  // deadline returns an absolute deadline before the artifact download
    56  // sub-process forcefully terminates. The default is 1/2 hour, unless one or
    57  // more getter configurations is set higher. A 1 minute grace period is added
    58  // so that an internal timeout has a moment to complete before the process is
    59  // terminated via signal.
    60  func (p *parameters) deadline() time.Duration {
    61  	const minimum = 30 * time.Minute
    62  	max := minimum
    63  	max = helper.Max(max, p.HTTPReadTimeout)
    64  	max = helper.Max(max, p.GCSTimeout)
    65  	max = helper.Max(max, p.GitTimeout)
    66  	max = helper.Max(max, p.HgTimeout)
    67  	max = helper.Max(max, p.S3Timeout)
    68  	return max + 1*time.Minute
    69  }
    70  
    71  // Equal returns whether p and o are the same.
    72  func (p *parameters) Equal(o *parameters) bool {
    73  	if p == nil || o == nil {
    74  		return p == o
    75  	}
    76  
    77  	switch {
    78  	case p.HTTPReadTimeout != o.HTTPReadTimeout:
    79  		return false
    80  	case p.HTTPMaxBytes != o.HTTPMaxBytes:
    81  		return false
    82  	case p.GCSTimeout != o.GCSTimeout:
    83  		return false
    84  	case p.GitTimeout != o.GitTimeout:
    85  		return false
    86  	case p.HgTimeout != o.HgTimeout:
    87  		return false
    88  	case p.S3Timeout != o.S3Timeout:
    89  		return false
    90  	case p.DisableFilesystemIsolation != o.DisableFilesystemIsolation:
    91  		return false
    92  	case p.SetEnvironmentVariables != o.SetEnvironmentVariables:
    93  		return false
    94  	case p.Mode != o.Mode:
    95  		return false
    96  	case p.Source != o.Source:
    97  		return false
    98  	case p.Destination != o.Destination:
    99  		return false
   100  	case p.TaskDir != o.TaskDir:
   101  		return false
   102  	case !maps.EqualFunc(p.Headers, o.Headers, slices.Equal[string]):
   103  		return false
   104  	}
   105  
   106  	return true
   107  }
   108  
   109  const (
   110  	// stop privilege escalation via setuid/setgid
   111  	// https://github.com/hashicorp/nomad/issues/6176
   112  	umask = fs.ModeSetuid | fs.ModeSetgid
   113  )
   114  
   115  func (p *parameters) client(ctx context.Context) *getter.Client {
   116  	httpGetter := &getter.HttpGetter{
   117  		Netrc:  true,
   118  		Client: cleanhttp.DefaultClient(),
   119  		Header: p.Headers,
   120  
   121  		// Do not support the custom X-Terraform-Get header and
   122  		// associated logic.
   123  		XTerraformGetDisabled: true,
   124  
   125  		// Disable HEAD requests as they can produce corrupt files when
   126  		// retrying a download of a resource that has changed.
   127  		// hashicorp/go-getter#219
   128  		DoNotCheckHeadFirst: true,
   129  
   130  		// Read timeout for HTTP operations. Must be long enough to
   131  		// accommodate large/slow downloads.
   132  		ReadTimeout: p.HTTPReadTimeout,
   133  
   134  		// Maximum download size. Must be large enough to accommodate
   135  		// large downloads.
   136  		MaxBytes: p.HTTPMaxBytes,
   137  	}
   138  	return &getter.Client{
   139  		Ctx:             ctx,
   140  		Src:             p.Source,
   141  		Dst:             p.Destination,
   142  		Mode:            p.Mode,
   143  		Umask:           umask,
   144  		Insecure:        false,
   145  		DisableSymlinks: true,
   146  		Getters: map[string]getter.Getter{
   147  			"git": &getter.GitGetter{
   148  				Timeout: p.GitTimeout,
   149  			},
   150  			"hg": &getter.HgGetter{
   151  				Timeout: p.HgTimeout,
   152  			},
   153  			"gcs": &getter.GCSGetter{
   154  				Timeout: p.GCSTimeout,
   155  			},
   156  			"s3": &getter.S3Getter{
   157  				Timeout: p.S3Timeout,
   158  			},
   159  			"http":  httpGetter,
   160  			"https": httpGetter,
   161  		},
   162  	}
   163  }