File: security_policies_worker.go

package info (click to toggle)
gitlab-agent 16.1.3-2
  • links: PTS, VCS
  • area: contrib
  • in suites: forky, sid, trixie
  • size: 6,324 kB
  • sloc: makefile: 175; sh: 52; ruby: 3
file content (129 lines) | stat: -rw-r--r-- 3,785 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package agent

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/module/modagent"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/errz"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/logz"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/retry"
	"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/pkg/agentcfg"
	"go.uber.org/zap"
)

const (
	policiesPollInterval      = 30 * time.Second
	policiesPollInitBackoff   = 10 * time.Second
	policiesPollMaxBackoff    = 5 * time.Minute
	policiesPollResetDuration = 10 * time.Minute
	policiesPollBackoffFactor = 2.0
	policiesPollJitter        = 1.0
)

type configurationToUpdateData struct {
	agentId                 int64
	containerScanningConfig *agentcfg.ContainerScanningCF
}

type securityPoliciesWorker struct {
	log     *zap.Logger
	api     modagent.Api
	updater chan<- configurationToUpdateData
}

type SecurityPolicyConfiguration struct {
	Cadence    string    `json:"cadence"`
	Namespaces []string  `json:"namespaces"`
	UpdatedAt  time.Time `json:"updated_at"`
}

type getSecurityPoliciesResponse struct {
	Policies []*SecurityPolicyConfiguration `json:"configurations"`
}

func (w *securityPoliciesWorker) Run(ctx context.Context) {
	_ = retry.PollWithBackoff(ctx, securityPoliciesPollConfig(), func(ctx context.Context) (error, retry.AttemptResult) {
		response, err := w.requestPolicy(ctx)
		if err != nil {
			w.log.Error("Error checking security policies", logz.Error(err))
			return nil, retry.Backoff
		}

		agentId, err := w.api.GetAgentId(ctx)
		if err != nil {
			w.log.Error("Could not retrieve agent information", logz.Error(err))
			return nil, retry.Backoff
		}

		updateData := configurationToUpdateData{
			agentId: agentId,
		}

		if len(response.Policies) > 0 {
			updateData.containerScanningConfig = convertPolicy(response.Policies[0])
		}

		select {
		case w.updater <- updateData:
			return nil, retry.Continue
		case <-ctx.Done():
			return nil, retry.Done
		}
	})
}

func (w *securityPoliciesWorker) requestPolicy(ctx context.Context) (policiesResponse *getSecurityPoliciesResponse, retError error) {
	resp, err := w.api.MakeGitLabRequest(
		ctx,
		"/policies_configuration",
		modagent.WithRequestMethod(http.MethodGet),
	)
	if err != nil {
		return nil, fmt.Errorf("could not retrieve security policies: %w", err)
	}

	defer errz.SafeClose(resp.Body, &retError)
	switch resp.StatusCode {
	case http.StatusNotFound, http.StatusPaymentRequired:
		// The project does not have an ultimate license.
		// Treat this the same as an empty policy response.
		return new(getSecurityPoliciesResponse), nil
	case http.StatusOK:
		body, err := io.ReadAll(resp.Body)
		if err != nil {
			return nil, fmt.Errorf("error reading response body: %w", err)
		}

		securityPoliciesResponse := new(getSecurityPoliciesResponse)
		err = json.Unmarshal(body, securityPoliciesResponse)
		if err != nil {
			return nil, fmt.Errorf("error parsing response body: %w", err)
		}

		return securityPoliciesResponse, nil
	default:
		return nil, fmt.Errorf("unexpected HTTP status code: %d", resp.StatusCode)
	}
}

func convertPolicy(policy *SecurityPolicyConfiguration) *agentcfg.ContainerScanningCF {
	return &agentcfg.ContainerScanningCF{
		Cadence:             policy.Cadence,
		VulnerabilityReport: &agentcfg.VulnerabilityReport{Namespaces: policy.Namespaces},
	}
}

func securityPoliciesPollConfig() retry.PollConfig {
	return retry.NewPollConfigFactory(policiesPollInterval, retry.NewExponentialBackoffFactory(
		policiesPollInitBackoff,
		policiesPollMaxBackoff,
		policiesPollResetDuration,
		policiesPollBackoffFactor,
		policiesPollJitter,
	))()
}