-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
195 lines (174 loc) · 6.08 KB
/
main.go
File metadata and controls
195 lines (174 loc) · 6.08 KB
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
package main
import (
"context"
"errors"
"fmt"
policyManager "github.com/compliance-framework/agent/policy-manager"
"github.com/compliance-framework/agent/runner"
"github.com/compliance-framework/agent/runner/proto"
"github.com/compliance-framework/plugin-apt-versions/internal"
"github.com/hashicorp/go-hclog"
goplugin "github.com/hashicorp/go-plugin"
"os"
"slices"
)
type AptVersion struct {
logger hclog.Logger
config map[string]string
}
// Configure, and Eval are called at different times during the plugin execution lifecycle,
// and are responsible for different tasks:
//
// Configure is called on plugin startup. It is primarily used to configure a plugin for its lifetime.
// Here you should store any configurations like usernames and password required by the plugin.
//
// Eval is called once for each scheduled execution with a list of policy paths and it is responsible
// for evaluating each of these policy paths against the data it requires to evaluate those policies.
// The plugin is responsible for collecting the data it needs to evaluate the policies in the Eval
// method and then running the policies against that data.
//
// The simplest way to handle multiple policies is to do an initial lookup of all the data that may
// be required for all policies in the method, and then run the policies against that data. This,
// however, may not be the most efficient way to run policies, and you may want to optimize this
// while writing plugins to reduce the amount of data you need to collect and store in memory. It
// is the plugins responsibility to ensure that it is (reasonably) efficient in its use of
// resources.
//
// A user starts the agent, and passes the plugin and any policy bundles.
//
// The agent will:
// - Start the plugin
// - Call Configure() with teh required config
// - Call Eval() with the first policy bundles (one by one, in turn),
// so the plugin can report any violations against the configuration
func (l *AptVersion) Configure(req *proto.ConfigureRequest) (*proto.ConfigureResponse, error) {
// Configure is used to set up any configuration needed by this plugin over its lifetime.
// This will likely only be called once on plugin startup, which may then run for an extended period of time.
l.config = req.GetConfig()
return &proto.ConfigureResponse{}, nil
}
func (l *AptVersion) Eval(request *proto.EvalRequest, apiHelper runner.ApiHelper) (*proto.EvalResponse, error) {
ctx := context.TODO()
activities := make([]*proto.Activity, 0)
data, getInstalledPackagesSteps, err := internal.GetInstalledPackages(l.logger)
l.logger.Trace(fmt.Sprintf("Packages output: %s", data))
if err != nil {
return nil, fmt.Errorf("error getting installed packages: %w", err)
}
activities = append(activities, &proto.Activity{
Title: "Collect OS packages installed",
Description: "Collect OS packages installed on the host machine, and prepare collected data for validation in policy engine",
Steps: getInstalledPackagesSteps,
})
evidence, err := l.evaluatePolicies(ctx, activities, data, request)
if err != nil {
return &proto.EvalResponse{
Status: proto.ExecutionStatus_FAILURE,
}, err
}
if err = apiHelper.CreateEvidence(ctx, evidence); err != nil {
l.logger.Error("Failed to send evidence", "error", err)
return &proto.EvalResponse{
Status: proto.ExecutionStatus_FAILURE,
}, err
}
return &proto.EvalResponse{
Status: proto.ExecutionStatus_SUCCESS,
}, err
}
func (l *AptVersion) evaluatePolicies(ctx context.Context, activities []*proto.Activity, packageData map[string]interface{}, req *proto.EvalRequest) ([]*proto.Evidence, error) {
var accumulatedErrors error
evidences := make([]*proto.Evidence, 0)
l.logger.Trace("config", l.config)
hostname := os.Getenv("HOSTNAME")
labels := map[string]string{
"type": "apt",
"hostname": hostname,
}
actors := []*proto.OriginActor{
{
Title: "The Continuous Compliance Framework",
Type: "assessment-platform",
Links: []*proto.Link{
{
Href: "https://compliance-framework.github.io/docs/",
Rel: internal.StringAddressed("reference"),
Text: internal.StringAddressed("The Continuous Compliance Framework"),
},
},
Props: nil,
},
{
Title: "Continuous Compliance Framework - Local APT Installed Packages Plugin",
Type: "tool",
Links: []*proto.Link{
{
Href: "https://github.com/compliance-framework/plugin-apt-versions",
Rel: internal.StringAddressed("reference"),
Text: internal.StringAddressed("The Continuous Compliance Framework' Local APT Installed Packages Plugin"),
},
},
Props: nil,
},
}
components := []*proto.Component{}
inventory := []*proto.InventoryItem{
{
Identifier: fmt.Sprintf("machine-instance/%s", hostname),
Type: "web-server",
Title: fmt.Sprintf("Machine Instance %s", hostname),
Props: []*proto.Property{
{
Name: "hostname",
Value: hostname,
Remarks: policyManager.Pointer("The local hostname of the machine where the plugin has been executed"),
},
},
},
}
subjects := []*proto.Subject{
{
Type: proto.SubjectType_SUBJECT_TYPE_INVENTORY_ITEM,
Identifier: fmt.Sprintf("machine-instance/%s", hostname),
},
}
for _, policyPath := range req.GetPolicyPaths() {
// Explicitly reset steps to make things readable
processor := policyManager.NewPolicyProcessor(
l.logger,
internal.MergeMaps(
labels,
map[string]string{},
),
subjects,
components,
inventory,
actors,
activities,
)
evidence, err := processor.GenerateResults(ctx, policyPath, packageData)
evidences = slices.Concat(evidences, evidence)
if err != nil {
accumulatedErrors = errors.Join(accumulatedErrors, err)
}
}
return evidences, nil
}
func main() {
logger := hclog.New(&hclog.LoggerOptions{
Level: hclog.Debug,
JSONFormat: true,
})
aptVersionObj := &AptVersion{
logger: logger,
}
goplugin.Serve(&goplugin.ServeConfig{
HandshakeConfig: runner.HandshakeConfig,
Plugins: map[string]goplugin.Plugin{
"runner": &runner.RunnerGRPCPlugin{
Impl: aptVersionObj,
},
},
GRPCServer: goplugin.DefaultGRPCServer,
})
}