Skip to content

Commit 8a4a937

Browse files
committed
AIDE migration
This PR adds ability to run aide 0.18 config checks and migration. The aide0.18 will be complied as binary and used by FIO in a container, we will try to see if we can perform the migration if a user defined config is being detected. We will issue warning messages in the log, and have a failed annotation key in the FIO instance if we are not able to pre migrate the config.
1 parent 4508dba commit 8a4a937

16 files changed

Lines changed: 902 additions & 50 deletions

File tree

Dockerfile.ci

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,33 @@ WORKDIR /go/src/github.com/openshift/file-integrity-operator
66

77
ENV GOFLAGS="-mod=vendor"
88

9+
RUN dnf -y install git make gcc automake autoconf libtool flex gettext-devel e2fsprogs-devel audit-libs-devel libattr-devel flex bison zlib-devel libgcrypt-devel audit-libs-devel libacl-devel libselinux-devel libtool && dnf clean all
10+
911
COPY . .
1012

13+
RUN git clone https://github.com/autoconf-archive/autoconf-archive.git && \
14+
cp autoconf-archive/m4/*.m4 /usr/share/aclocal/
15+
1116
RUN make build
1217

18+
RUN make aide-0.18
19+
1320
# Step two: containerize file-integrity-operator and AIDE together
1421
FROM registry.fedoraproject.org/fedora-minimal:37
15-
RUN microdnf -y install aide-0.16
16-
RUN microdnf -y install aide golang && microdnf clean all
22+
23+
# Install AIDE 0.16 and dependencies
24+
RUN microdnf -y install aide-0.16 golang && microdnf clean all
1725

1826
ENV OPERATOR=/usr/local/bin/file-integrity-operator \
1927
USER_UID=1001 \
2028
USER_NAME=file-integrity-operator
2129

30+
RUN mkdir -p /usr/local/libs
31+
2232
# install operator binary
2333
COPY --from=builder /go/src/github.com/openshift/file-integrity-operator/build/bin/manager ${OPERATOR}
2434
COPY build/bin /usr/local/bin
35+
COPY --from=builder /go/src/github.com/openshift/file-integrity-operator/build/bin/aide-0.18 /usr/sbin/aide-0.18
2536
RUN /usr/local/bin/user_setup
2637

2738
ENTRYPOINT ["/usr/local/bin/entrypoint"]

Makefile

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,31 @@ else
161161
$(eval OPENSHIFT_USER = $(shell oc whoami))
162162
endif
163163

164+
165+
.PHONY: aide-0.18
166+
aide-0.18:
167+
cd build && \
168+
rm -rf aide && \
169+
git clone https://github.com/aide/aide.git && \
170+
cd aide && \
171+
git checkout v0.18.8 && \
172+
sh ./autogen.sh && \
173+
./configure \
174+
--with-zlib \
175+
--disable-static \
176+
--with-posix-acl \
177+
--with-gcrypt \
178+
--with-selinux \
179+
--with-xattr \
180+
--with-e2fsattrs \
181+
--with-audit && \
182+
$(MAKE) && \
183+
chmod +x ./aide && \
184+
cd .. && \
185+
cd .. && \
186+
cp ./build/aide/aide ./build/bin/aide-0.18
187+
188+
164189
.PHONY: check-operator-version
165190
check-operator-version:
166191
ifndef VERSION

build/Dockerfile

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,38 @@
11
# Step one: build file-integrity-operator
2-
FROM golang:1.22 as builder
2+
FROM registry.ci.openshift.org/openshift/release:rhel-9-release-golang-1.22-openshift-4.17 AS builder
33
USER root
44

55
WORKDIR /go/src/github.com/openshift/file-integrity-operator
66

77
ENV GOFLAGS="-mod=vendor"
88

9+
RUN dnf -y install git make gcc automake autoconf libtool flex gettext-devel e2fsprogs-devel audit-libs-devel libattr-devel flex bison zlib-devel libgcrypt-devel audit-libs-devel libacl-devel libselinux-devel libtool && dnf clean all
10+
911
COPY . .
1012

13+
RUN git clone https://github.com/autoconf-archive/autoconf-archive.git && \
14+
cp autoconf-archive/m4/*.m4 /usr/share/aclocal/
15+
1116
RUN make build
1217

18+
RUN make aide-0.18
19+
1320
# Step two: containerize file-integrity-operator and AIDE together
1421
FROM registry.fedoraproject.org/fedora-minimal:37
15-
RUN microdnf -y install aide-0.16
16-
RUN microdnf -y install aide golang && microdnf clean all
22+
23+
# Install AIDE 0.16 and dependencies
24+
RUN microdnf -y install aide-0.16 golang && microdnf clean all
1725

1826
ENV OPERATOR=/usr/local/bin/file-integrity-operator \
1927
USER_UID=1001 \
2028
USER_NAME=file-integrity-operator
2129

30+
RUN mkdir -p /usr/local/libs
31+
2232
# install operator binary
2333
COPY --from=builder /go/src/github.com/openshift/file-integrity-operator/build/bin/manager ${OPERATOR}
2434
COPY build/bin /usr/local/bin
35+
COPY --from=builder /go/src/github.com/openshift/file-integrity-operator/build/bin/aide-0.18 /usr/sbin/aide-0.18
2536
RUN /usr/local/bin/user_setup
2637

2738
ENTRYPOINT ["/usr/local/bin/entrypoint"]

cmd/manager/daemon.go

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,13 @@ const (
4444
aideReadDBFileName = "aide.db.gz"
4545
aideReadLogFileName = "aide.log"
4646
// Files output from AIDE init and scan log. We copy the files AIDE writes from here.
47-
aideWritingDBFileName = "aide.db.gz.new"
48-
aideWritingLogFileName = "aide.log.new"
49-
aideReinitFileName = "aide.reinit"
50-
aideConfigFileName = "aide.conf"
51-
backupTimeFormat = "20060102T15_04_05"
52-
pprofAddr = "127.0.0.1:6060"
47+
aideWritingDBFileName = "aide.db.gz.new"
48+
aideWritingLogFileName = "aide.log.new"
49+
aideReinitFileName = "aide.reinit"
50+
aideConfigFileName = "aide.conf"
51+
aideMigrateConfigFileName = common.PreMigrationConfDataKey
52+
backupTimeFormat = "20060102T15_04_05"
53+
pprofAddr = "127.0.0.1:6060"
5354
)
5455

5556
var DaemonCmd = &cobra.Command{
@@ -78,6 +79,7 @@ type daemonConfig struct {
7879
FileDir string
7980
RunDir string
8081
ConfigDir string
82+
RunMigrationCheck bool
8183
}
8284

8385
type daemonRuntime struct {
@@ -196,6 +198,7 @@ func defineFlags(cmd *cobra.Command) {
196198
cmd.Flags().Bool("debug", false, "Print debug messages")
197199
cmd.Flags().Bool("local", false, "Run the daemon locally, using KUBECONFIG. Should only be used when debugging.")
198200
cmd.Flags().Bool("pprof", false, "Enable /debug/pprof endpoints. Should only be used when debugging.")
201+
cmd.Flags().Bool("migration-check", false, "Run the migration check")
199202
}
200203

201204
func parseDaemonConfig(cmd *cobra.Command) *daemonConfig {
@@ -207,7 +210,8 @@ func parseDaemonConfig(cmd *cobra.Command) *daemonConfig {
207210
conf.RunDir = getValidStringArg(cmd, "aideruntimedir")
208211
conf.ConfigDir = getValidStringArg(cmd, "aideconfigdir")
209212
conf.LogCollectorNode = os.Getenv("NODE_NAME")
210-
conf.LogCollectorConfigMapName = getConfigMapName(getValidStringArg(cmd, "lc-config-map-prefix"), conf.LogCollectorNode)
213+
conf.RunMigrationCheck, _ = cmd.Flags().GetBool("migration-check")
214+
conf.LogCollectorConfigMapName = getConfigMapName(getValidStringArg(cmd, "lc-config-map-prefix"), conf.LogCollectorNode, conf.RunMigrationCheck)
211215
conf.LogCollectorTimeout, _ = cmd.Flags().GetInt64("lc-timeout")
212216
conf.Interval, _ = cmd.Flags().GetInt64("interval")
213217
conf.MaxBackups, _ = cmd.Flags().GetInt("maxbackups")
@@ -279,11 +283,17 @@ func daemonMainLoop(cmd *cobra.Command, args []string) {
279283
// integrityInstanceLoop is not added to the waitgroup because the watcher would stall wg.Wait() indefinitely. It
280284
// will just die with the process and does not have anything to clean up.
281285
go integrityInstanceLoop(ctx, rt, conf, errChan)
282-
wg.Add(4)
283-
go reinitLoop(ctx, rt, conf, errChan, &wg)
284-
go holdOffLoop(ctx, rt, conf, errChan, &wg)
285-
go aideLoop(ctx, rt, conf, errChan, &wg)
286-
go logCollectorMainLoop(ctx, rt, conf, errChan, &wg)
286+
// check if we are running in a migration scenario and if so, only run the migration check
287+
if conf.RunMigrationCheck {
288+
wg.Add(1)
289+
go migrationCheckLoop(ctx, rt, conf, errChan, &wg)
290+
} else {
291+
wg.Add(4)
292+
go reinitLoop(ctx, rt, conf, errChan, &wg)
293+
go holdOffLoop(ctx, rt, conf, errChan, &wg)
294+
go aideLoop(ctx, rt, conf, errChan, &wg)
295+
go logCollectorMainLoop(ctx, rt, conf, errChan, &wg)
296+
}
287297

288298
var finalErr error
289299
for {

cmd/manager/daemon_util.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ func aideConfigPath(c *daemonConfig) string {
6767
return path.Join(c.ConfigDir, aideConfigFileName)
6868
}
6969

70+
func aideMigrateConfigPath(c *daemonConfig) string {
71+
return path.Join(c.ConfigDir, aideMigrateConfigFileName)
72+
}
73+
7074
func logAndTryReportingDaemonError(ctx context.Context, rt *daemonRuntime, conf *daemonConfig, fmt string, err error) {
7175
LOG(fmt, err)
7276
if reportErr := reportDaemonError(ctx, rt, conf, fmt, err); reportErr != nil {
@@ -92,9 +96,16 @@ func createErrorEvent(ctx context.Context, rt *daemonRuntime, conf *daemonConfig
9296
}
9397

9498
func reportDaemonError(ctx context.Context, rt *daemonRuntime, conf *daemonConfig, format string, err error) error {
95-
if reportErr := reportError(ctx, fmt.Sprintf(format, err), conf, rt); reportErr != nil {
96-
return reportErr
99+
if err != nil {
100+
if reportErr := reportError(ctx, fmt.Sprintf(format, err), conf, rt); reportErr != nil {
101+
return reportErr
102+
}
103+
} else {
104+
if reportErr := reportError(ctx, format, conf, rt); reportErr != nil {
105+
return reportErr
106+
}
97107
}
108+
98109
return createErrorEvent(ctx, rt, conf, format, err)
99110
}
100111

@@ -118,6 +129,23 @@ func runAideInitDBCmd(ctx context.Context, c *daemonConfig) error {
118129
}, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), maxRetries))
119130
}
120131

132+
// runAideMigrationCheckCmdSaveOutput runs the AIDE migration check command and saves the output
133+
func runAideMigrationCheckCmdSaveOutput(ctx context.Context, c *daemonConfig) (string, error) {
134+
configPath := aideMigrateConfigPath(c)
135+
136+
cmd := exec.CommandContext(ctx, "aide-0.18", "-c", configPath, "-D")
137+
_, err := cmd.Output()
138+
if err != nil {
139+
// Check if the error is an exec.ExitError to get the stderr output
140+
if exitErr, ok := err.(*exec.ExitError); ok {
141+
errString := string(exitErr.Stderr)
142+
return strings.TrimSpace(strings.TrimSuffix(errString, "\n")), err
143+
}
144+
return "", err
145+
}
146+
return "", nil
147+
}
148+
121149
func runAideScanCmd(ctx context.Context, c *daemonConfig) error {
122150
configPath := aideConfigPath(c)
123151
// CWE-78 - configPath is only made of user input during standalone debugging

cmd/manager/logcollector_util.go

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ const (
4747
configMapMaxSize = 1048570 // 1MB for etcd limit. Over this, you get an error.
4848
)
4949

50-
func getConfigMapName(prefix, nodeName string) string {
50+
func getConfigMapName(prefix string, nodeName string, migrationMode bool) string {
51+
if migrationMode {
52+
return prefix + "-migration"
53+
}
5154
return prefix + "-" + nodeName
5255
}
5356

@@ -176,7 +179,7 @@ func newLogConfigMap(owner *unstructured.Unstructured, configMapName, contentkey
176179
}
177180
}
178181

179-
func newInformationalConfigMap(owner *unstructured.Unstructured, configMapName string, node string, annotations map[string]string) *corev1.ConfigMap {
182+
func newInformationalConfigMap(owner *unstructured.Unstructured, configMapName string, node string, annotations map[string]string, labels map[string]string) *corev1.ConfigMap {
180183
return &corev1.ConfigMap{
181184
TypeMeta: metav1.TypeMeta{
182185
APIVersion: "v1",
@@ -193,21 +196,38 @@ func newInformationalConfigMap(owner *unstructured.Unstructured, configMapName s
193196
UID: owner.GetUID(),
194197
},
195198
},
196-
Labels: map[string]string{
197-
common.IntegrityOwnerLabelKey: owner.GetName(),
198-
common.IntegrityLogLabelKey: "",
199-
common.IntegrityConfigMapNodeLabelKey: node,
200-
},
199+
Labels: labels,
201200
},
202201
}
203202
}
204203

204+
func getLogCollectorConfigMapLabels(owner *unstructured.Unstructured, node string, migrationMode bool) map[string]string {
205+
if migrationMode {
206+
return map[string]string{
207+
common.IntegrityOwnerLabelKey: owner.GetName(),
208+
common.IntegrityMigrationLabelKey: "",
209+
}
210+
} else {
211+
return map[string]string{
212+
common.IntegrityOwnerLabelKey: owner.GetName(),
213+
common.IntegrityLogLabelKey: "",
214+
common.IntegrityConfigMapNodeLabelKey: node,
215+
}
216+
}
217+
}
218+
205219
// reportOK creates a blank configMap with no error annotation. This is treated by the controller as an OK signal.
206220
func reportOK(ctx context.Context, conf *daemonConfig, rt *daemonRuntime) error {
207221
DBG("creating temporary configMap '%s' to report a successful scan result", conf.LogCollectorConfigMapName)
208222
return backoff.Retry(func() error {
209223
fi := rt.GetFileIntegrityInstance()
210-
confMap := newInformationalConfigMap(fi, conf.LogCollectorConfigMapName, conf.LogCollectorNode, nil)
224+
// check if we are on migration mode if so we need to create a configmap with the migration annotation
225+
annotations := map[string]string{}
226+
if conf.RunMigrationCheck {
227+
annotations[common.AideConfigAutoMigrationFailedAnnotationKey] = "false"
228+
}
229+
labels := getLogCollectorConfigMapLabels(fi, conf.LogCollectorNode, conf.RunMigrationCheck)
230+
confMap := newInformationalConfigMap(fi, conf.LogCollectorConfigMapName, conf.LogCollectorNode, annotations, labels)
211231
_, err := rt.clientset.CoreV1().ConfigMaps(conf.Namespace).Create(ctx, confMap, metav1.CreateOptions{})
212232
return err
213233
}, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), maxRetries))
@@ -218,9 +238,11 @@ func reportError(ctx context.Context, msg string, conf *daemonConfig, rt *daemon
218238
return backoff.Retry(func() error {
219239
fi := rt.GetFileIntegrityInstance()
220240
annotations := map[string]string{
221-
common.IntegrityLogErrorAnnotationKey: msg,
241+
common.IntegrityLogErrorAnnotationKey: msg,
242+
common.AideConfigAutoMigrationFailedAnnotationKey: "true",
222243
}
223-
confMap := newInformationalConfigMap(fi, conf.LogCollectorConfigMapName, conf.LogCollectorNode, annotations)
244+
labels := getLogCollectorConfigMapLabels(fi, conf.LogCollectorNode, conf.RunMigrationCheck)
245+
confMap := newInformationalConfigMap(fi, conf.LogCollectorConfigMapName, conf.LogCollectorNode, annotations, labels)
224246
_, err := rt.clientset.CoreV1().ConfigMaps(conf.Namespace).Create(ctx, confMap, metav1.CreateOptions{})
225247
return err
226248
}, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), maxRetries))

cmd/manager/loops.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,43 @@ func aideLoop(ctx context.Context, rt *daemonRuntime, conf *daemonConfig, errCha
7878
}
7979
}
8080

81+
// The migrationCheckLoop checks if the migrated AIDE config file exists and if the new AIDE config works.
82+
func migrationCheckLoop(ctx context.Context, rt *daemonRuntime, conf *daemonConfig, errChan chan<- error, wg *sync.WaitGroup) {
83+
defer wg.Done()
84+
migrateCtx, migrateCancel := context.WithCancel(ctx)
85+
defer migrateCancel()
86+
87+
for {
88+
select {
89+
case <-migrateCtx.Done():
90+
DBG("Migration loop cancelled by the main routine!")
91+
return
92+
default:
93+
output, err := runAideMigrationCheckCmdSaveOutput(migrateCtx, conf)
94+
aideResult := common.GetAideExitCode(err)
95+
if aideResult == 0 {
96+
LOG("AIDE migration check passed. The configuration is safe to run using AIDE 0.18")
97+
if err := reportOK(migrateCtx, conf, rt); err != nil {
98+
// Considering this a non-fatal error right now.
99+
LOG("Failed reporting migration check result: %v", err)
100+
}
101+
return
102+
} else if aideResult == 17 {
103+
// This is an AIDE config line error.
104+
newErr := fmt.Sprintf("Detected configuration error during the migration check for AIDE 0.18: %s, output: %s ", common.GetAideErrorMessage(aideResult), output)
105+
logAndTryReportingDaemonError(migrateCtx, rt, conf, newErr, nil)
106+
errChan <- err
107+
return
108+
} else {
109+
logAndTryReportingDaemonError(ctx, rt, conf, fmt.Sprintf("Error running migration check: %s",
110+
common.GetAideErrorMessage(aideResult)), err)
111+
errChan <- err
112+
}
113+
time.Sleep(time.Second * time.Duration(conf.Interval))
114+
}
115+
}
116+
}
117+
81118
// The holdoff file is the signal from the node controller to pause the aide scan.
82119
// We do not make this pause the logCollector loop, and we might want to.
83120
func holdOffLoop(ctx context.Context, rt *daemonRuntime, conf *daemonConfig, errChan chan<- error, wg *sync.WaitGroup) {

0 commit comments

Comments
 (0)