Compare commits

..

No commits in common. "main" and "v0.8.0-rc1" have entirely different histories.

70 changed files with 1211 additions and 2465 deletions

View File

@ -26,7 +26,7 @@ jobs:
- go
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- name: Initialize CodeQL
uses: github/codeql-action/init@d23060145bc9131d50558d5d4185494a20208101 # v2.2.8
with:

View File

@ -33,25 +33,25 @@ jobs:
digest: ${{ steps.build-and-push.outputs.digest }}
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
fetch-depth: 0
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0
- name: Set up Docker Buildx
id: Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
uses: docker/setup-buildx-action@2b51285047da1547ffb1b2203d8be4c0af6b1f20 # v3.2.0
- name: Login to Docker Hub
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
with:
username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_SECRET }}
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
with:
role-to-assume: arn:aws:iam::292999226676:role/github_actions-falcoctl-ecr
aws-region: us-east-1
@ -64,7 +64,7 @@ jobs:
- name: Docker Meta
id: meta_falcoctl
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1
with:
# list of Docker images to use as base name for tags
images: |
@ -78,7 +78,7 @@ jobs:
- name: Build and push
id: build-and-push
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0
with:
context: .
platforms: linux/amd64,linux/arm64
@ -92,7 +92,7 @@ jobs:
- name: Install Cosign
if: ${{ inputs.sign }}
uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 # v3.9.2
uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4 # v3.4.0
- name: Sign the images with GitHub OIDC Token
if: ${{ inputs.sign }}

View File

@ -23,14 +23,14 @@ jobs:
goos: windows
steps:
- name: Checkout commit
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
with:
go-version-file: 'go.mod'
go-version: '1.21'
check-latest: true
- name: Build Falcoctl
@ -47,14 +47,14 @@ jobs:
tar -czvf falcoctl-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz falcoctl LICENSE
- name: Upload falcoctl artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: falcoctl-${{ matrix.goos }}-${{ matrix.goarch }}
path: ./falcoctl-${{ matrix.goos }}-${{ matrix.goarch }}
retention-days: 1
- name: Upload falcoctl archives
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: falcoctl-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz
path: ./falcoctl-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz
@ -96,7 +96,7 @@ jobs:
actions: read # for detecting the Github Actions environment.
id-token: write # for creating OIDC tokens for signing.
packages: write # for uploading attestations.
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.10.0
with:
image: docker.io/falcosecurity/falcoctl
# The image digest is used to prevent TOCTOU issues.
@ -115,7 +115,7 @@ jobs:
id-token: write
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
with:
role-to-assume: arn:aws:iam::292999226676:role/github_actions-falcoctl-ecr
aws-region: us-east-1
@ -138,7 +138,7 @@ jobs:
actions: read # for detecting the Github Actions environment.
id-token: write # for creating OIDC tokens for signing.
packages: write # for uploading attestations.
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.10.0
with:
image: public.ecr.aws/falcosecurity/falcoctl
# The image digest is used to prevent TOCTOU issues.
@ -154,12 +154,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout commit
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
with:
go-version-file: 'go.mod'
go-version: '1.21'
check-latest: true
- name: Run tests

View File

@ -8,25 +8,24 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
ref: ${{ github.event.pull_request.head.sha }}
repository: ${{github.event.pull_request.head.repo.full_name}}
persist-credentials: false
- name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
with:
go-version: "^1.24.3"
go-version-file: "go.mod"
go-version: '1.21'
check-latest: true
cache: "false"
cache: 'false'
- name: golangci-lint
uses: golangci/golangci-lint-action@55c2c1448f86e01eaae002a5a3a9624417608d84 # v6.5.2
uses: golangci/golangci-lint-action@3cfe3a4abbb849e10058ce4af15d205b6da42804 # v4.0.0
with:
only-new-issues: true
version: v1.64.7
version: v1.55
args: --timeout=900s
gomodtidy:
@ -35,16 +34,16 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
ref: "${{ github.event.pull_request.head.sha }}"
repository: ${{github.event.pull_request.head.repo.full_name}}
persist-credentials: false
- name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
with:
go-version-file: "go.mod"
go-version: '1.21'
check-latest: true
- name: Execute go mod tidy and check the outcome

View File

@ -14,7 +14,7 @@ jobs:
hashes: ${{ steps.hash.outputs.hashes }}
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
fetch-depth: 0
@ -22,14 +22,14 @@ jobs:
run: git fetch --force --tags
- name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
with:
go-version-file: 'go.mod'
go-version: '1.21'
check-latest: true
- name: Run GoReleaser
id: run-goreleaser
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0
uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0
with:
distribution: goreleaser
version: latest
@ -53,7 +53,7 @@ jobs:
actions: read # To read the workflow path.
id-token: write # To sign the provenance.
contents: write # To add assets to a release.
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.10.0
with:
base64-subjects: "${{ needs.goreleaser.outputs.hashes }}"
upload-assets: true # upload to a new release
@ -64,7 +64,7 @@ jobs:
permissions: read-all
steps:
- name: Install the verifier
uses: slsa-framework/slsa-verifier/actions/installer@v2.7.1
uses: slsa-framework/slsa-verifier/actions/installer@v2.5.1
- name: Download assets
env:
@ -126,7 +126,7 @@ jobs:
actions: read # for detecting the Github Actions environment.
id-token: write # for creating OIDC tokens for signing.
packages: write # for uploading attestations.
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.10.0
with:
image: docker.io/falcosecurity/falcoctl
# The image digest is used to prevent TOCTOU issues.
@ -144,7 +144,7 @@ jobs:
id-token: write
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
with:
role-to-assume: arn:aws:iam::292999226676:role/github_actions-falcoctl-ecr
aws-region: us-east-1
@ -166,7 +166,7 @@ jobs:
actions: read # for detecting the Github Actions environment.
id-token: write # for creating OIDC tokens for signing.
packages: write # for uploading attestations.
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.10.0
with:
image: public.ecr.aws/falcosecurity/falcoctl
# The image digest is used to prevent TOCTOU issues.

View File

@ -44,11 +44,17 @@ linters-settings:
- opinionated
- performance
- style
disabled-checks:
# Conflicts with govet check-shadowing
- sloppyReassign
goimports:
local-prefixes: github.com/falcosecurity/falcoctl
govet:
check-shadowing: true
misspell:
locale: US
nolintlint:
allow-leading-space: true # don't require machine-readable nolint directives (i.e. with no leading space)
allow-unused: false # report any unused nolint directives
require-explanation: true # require an explanation for nolint directives
require-specific: true # require nolint directives to be specific about which linter is being skipped
@ -65,7 +71,7 @@ linters:
- errcheck
- errorlint
- exhaustive
- copyloopvar
- exportloopref
# - funlen
# - gochecknoglobals
# - gochecknoinits

View File

@ -1,5 +1,3 @@
version: 2
project_name: falcoctl
before:
hooks:
@ -47,6 +45,3 @@ release:
changelog:
use: github-native
git:
tag_sort: -version:creatordate

View File

@ -18,7 +18,6 @@ PROJECT?=github.com/falcosecurity/falcoctl
# todo(leogr): re-enable race when CLI tests can run with race enabled
TEST_FLAGS ?= -v -cover# -race
.PHONY: falcoctl
falcoctl:
$(GO) build -ldflags \
"-X '${PROJECT}/cmd/version.semVersion=${RELEASE}' \
@ -63,7 +62,7 @@ fmt: gci addlicense
.PHONY: golangci-lint
golangci-lint:
ifeq (, $(shell which golangci-lint))
@go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2
@go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.52.2
GOLANGCILINT=$(GOBIN)/golangci-lint
else
GOLANGCILINT=$(shell which golangci-lint)

1
OWNERS
View File

@ -5,6 +5,7 @@ approvers:
- fededp
- cpanato
- alacuku
reviewers:
- loresuso
emeritus_approvers:
- kris-nova

View File

@ -216,8 +216,6 @@ Indices for *falcoctl* can be retrieved from various storage backends. The suppo
| http | http:// | Can be used to retrieve indices via simple HTTP GET requests. |
| https | https:// | Convenience alias for the HTTP backend. |
| gcs | gs:// | For indices stored as Google Cloud Storage objects. Supports application default credentials. |
| file | file:// | For indices stored on the local file system. |
| s3 | s3:// | For indices stored as AWS S3 objects. Supports default credentials, IRSA. |
#### falcoctl index add
@ -344,7 +342,6 @@ $ falcoctl registry push --type=plugin ghcr.io/falcosecurity/plugins/plugin/clou
```
The type denotes the **artifact** type in this case *plugins*. The `ghcr.io/falcosecurity/plugins/plugin/cloudtrail:0.3.0` is the unique reference that points to the **artifact**.
Currently, *falcoctl* supports only two types of artifacts: **plugin** and **rulesfile**. Based on **artifact type** the commands accepts different flags:
* `--add-floating-tags`: add the floating tags for the major and minor versions
* `--annotation-source`: set annotation source for the artifact;
* `--depends-on`: set an artifact dependency (can be specified multiple times). Example: `--depends-on my-plugin:1.2.3`
* `--tag`: additional artifact tag. Can be repeated multiple time

View File

@ -1,4 +1,4 @@
FROM cgr.dev/chainguard/go AS builder
FROM golang:1.21 as builder
WORKDIR /tmp/builder
ARG RELEASE
@ -29,8 +29,14 @@ RUN CGO_ENABLED=0 \
RUN echo ${RELEASE}
FROM cgr.dev/chainguard/static:latest
FROM alpine:3.18.4
COPY --from=builder /tmp/builder/falcoctl /usr/bin/falcoctl
RUN apk update --no-cache && \
apk add --upgrade --no-cache libssl3 libcrypto3
RUN rm -rf /var/cache/apk/*
ENTRYPOINT [ "/usr/bin/falcoctl" ]
ARG BIN_NAME="falcoctl"
COPY --from=builder /tmp/builder/${BIN_NAME} /usr/bin/${BIN_NAME}
RUN ln -s /usr/bin/${BIN_NAME} /usr/bin/falcoctl-bin
ENTRYPOINT [ "/usr/bin/falcoctl-bin" ]

View File

@ -19,9 +19,6 @@ const (
// FlagAllowedTypes is the name of the flag to specify allowed artifact types.
FlagAllowedTypes = "allowed-types"
// FlagPlatform is the name of the flag to override the platform.
FlagPlatform = "platform"
// FlagResolveDeps is the name of the flag to enable artifact dependencies resolution.
FlagResolveDeps = "resolve-deps"

View File

@ -21,7 +21,6 @@ import (
"os"
"path/filepath"
"runtime"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@ -52,11 +51,11 @@ separated by a semicolon ';'. Other arguments, if passed through environment var
with "FALCOCTL_" and be followed by the hierarchical keys used in the configuration file separated by
an underscore "_".
A reference is either a simple name or a fully qualified reference ("<registry>/<repository>"),
A reference is either a simple name or a fully qualified reference ("<registry>/<repository>"),
optionally followed by ":<tag>" (":latest" is assumed by default when no tag is given).
When providing just the name of the artifact, the command will search for the artifacts in
the configured index files, and if found, it will use the registry and repository specified
When providing just the name of the artifact, the command will search for the artifacts in
the configured index files, and if found, it will use the registry and repository specified
in the indexes.
Example - Install "latest" tag of "k8saudit-rules" artifact by relying on index metadata:
@ -75,9 +74,6 @@ type artifactInstallOptions struct {
*options.Registry
*options.Directory
allowedTypes oci.ArtifactTypeSlice
platform string // Raw string from command line
platformArch string // Architecture portion of parsed platform string
platformOS string // OS portion of parsed platform string
resolveDeps bool
noVerify bool
}
@ -169,15 +165,6 @@ func NewArtifactInstallCmd(ctx context.Context, opt *options.Common) *cobra.Comm
}
}
// Parse "platform" into OS and Arch
if len(o.platform) > 0 {
parts := strings.Split(o.platform, "/")
if len(parts) != 2 {
return fmt.Errorf("invalid %q: must be in the format OS/Arch", FlagPlatform)
}
o.platformOS, o.platformArch = parts[0], parts[1]
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
@ -190,11 +177,9 @@ func NewArtifactInstallCmd(ctx context.Context, opt *options.Common) *cobra.Comm
cmd.Flags().Var(&o.allowedTypes, FlagAllowedTypes,
fmt.Sprintf(`list of artifact types that can be installed. If not specified or configured, all types are allowed.
It accepts comma separated values or it can be repeated multiple times.
Examples:
Examples:
--%s="rulesfile,plugin"
--%s=rulesfile --%s=plugin`, FlagAllowedTypes, FlagAllowedTypes, FlagAllowedTypes))
cmd.Flags().StringVar(&o.platform, "platform", fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
"os and architecture of the artifact in OS/ARCH format")
cmd.Flags().BoolVar(&o.resolveDeps, FlagResolveDeps, true,
"whether this command should resolve dependencies or not")
cmd.Flags().BoolVar(&o.noVerify, FlagNoVerify, false,
@ -240,7 +225,7 @@ func (o *artifactInstallOptions) RunArtifactInstall(ctx context.Context, args []
return nil, err
}
artifactConfig, err := puller.ArtifactConfig(ctx, ref, o.platformOS, o.platformArch)
artifactConfig, err := puller.ArtifactConfig(ctx, ref, runtime.GOOS, runtime.GOARCH)
if err != nil {
return nil, err
}
@ -279,33 +264,31 @@ func (o *artifactInstallOptions) RunArtifactInstall(ctx context.Context, args []
logger.Info("Installing artifacts", logger.Args("refs", refs))
for _, ref := range refs {
resolvedRef, err := o.IndexCache.ResolveReference(ref)
ref, err = o.IndexCache.ResolveReference(ref)
if err != nil {
return err
}
if signatures[resolvedRef] == nil {
if sig := o.IndexCache.SignatureForIndexRef(ref); sig != nil {
signatures[resolvedRef] = sig
}
}
logger.Info("Preparing to pull artifact", logger.Args("ref", ref))
logger.Info("Preparing to pull artifact", logger.Args("ref", resolvedRef))
if err := puller.CheckAllowedType(ctx, resolvedRef, o.platformOS, o.platformArch, o.allowedTypes.Types); err != nil {
if err := puller.CheckAllowedType(ctx, ref, runtime.GOOS, runtime.GOARCH, o.allowedTypes.Types); err != nil {
return err
}
// Install will always install artifact for the current OS and architecture
result, err := puller.Pull(ctx, resolvedRef, tmpDir, o.platformOS, o.platformArch)
result, err := puller.Pull(ctx, ref, tmpDir, runtime.GOOS, runtime.GOARCH)
if err != nil {
return err
}
sig := signatures[resolvedRef]
sig, ok := signatures[ref]
if !ok {
// try to get the signature from the index
sig = o.IndexCache.SignatureForIndexRef(ref)
}
if sig != nil && !o.noVerify {
repo, err := utils.RepositoryFromRef(resolvedRef)
repo, err := utils.RepositoryFromRef(ref)
if err != nil {
return err
}
@ -367,7 +350,7 @@ func (o *artifactInstallOptions) RunArtifactInstall(ctx context.Context, args []
if o.Printer.Spinner != nil {
_ = o.Printer.Spinner.Stop()
}
logger.Info("Artifact successfully installed", logger.Args("name", resolvedRef, "type", result.Type, "digest", result.Digest, "directory", destDir))
logger.Info("Artifact successfully installed", logger.Args("name", ref, "type", result.Type, "digest", result.Digest, "directory", destDir))
}
return nil

View File

@ -45,7 +45,6 @@ Flags:
--allowed-types=rulesfile --allowed-types=plugin
-h, --help help for install
--plain-http allows interacting with remote registry via plain http requests
--platform string os and architecture of the artifact in OS/ARCH format (default "linux/amd64")
--plugins-dir string directory where to install plugins. (default "/usr/share/falco/plugins")
--resolve-deps whether this command should resolve dependencies or not (default true)
--rulesfiles-dir string directory where to install rules. (default "/etc/falco")
@ -74,11 +73,11 @@ separated by a semicolon ';'. Other arguments, if passed through environment var
with "FALCOCTL_" and be followed by the hierarchical keys used in the configuration file separated by
an underscore "_".
A reference is either a simple name or a fully qualified reference ("<registry>/<repository>"),
A reference is either a simple name or a fully qualified reference ("<registry>/<repository>"),
optionally followed by ":<tag>" (":latest" is assumed by default when no tag is given).
When providing just the name of the artifact, the command will search for the artifacts in
the configured index files, and if found, it will use the registry and repository specified
When providing just the name of the artifact, the command will search for the artifacts in
the configured index files, and if found, it will use the registry and repository specified
in the indexes.
Example - Install "latest" tag of "k8saudit-rules" artifact by relying on index metadata:
@ -219,7 +218,7 @@ var artifactInstallTests = Describe("install", func() {
Expect(result).ToNot(BeNil())
ref = registry + repoAndTag
Expect(err).To(BeNil())
args = []string{artifactCmd, installCmd, ref, "--plain-http", "--platform", testPluginPlatform1,
args = []string{artifactCmd, installCmd, ref, "--plain-http",
"--config", configFilePath, "--allowed-types", "rulesfile"}
})
@ -311,7 +310,7 @@ var artifactInstallTests = Describe("install", func() {
Expect(result).ToNot(BeNil())
ref = registry + repoAndTag
Expect(err).To(BeNil())
args = []string{artifactCmd, installCmd, ref, "--plain-http", "--platform", testPluginPlatform1,
args = []string{artifactCmd, installCmd, ref, "--plain-http",
"--config", configFilePath, "--plugins-dir", destDir}
})
@ -349,7 +348,7 @@ var artifactInstallTests = Describe("install", func() {
Expect(result).ToNot(BeNil())
ref = registry + repoAndTag
Expect(err).To(BeNil())
args = []string{artifactCmd, installCmd, ref, "--plain-http", "--platform", testPluginPlatform1,
args = []string{artifactCmd, installCmd, ref, "--plain-http",
"--config", configFilePath, "--plugins-dir", destDir}
})
@ -438,28 +437,6 @@ var artifactInstallTests = Describe("install", func() {
})
})
When("not --platform is not of the correct format", func() {
BeforeEach(func() {
destDir = GinkgoT().TempDir()
err = os.Remove(destDir)
Expect(err).To(BeNil())
baseDir := GinkgoT().TempDir()
configFilePath := baseDir + "/config.yaml"
content := []byte(correctIndexConfig)
err := os.WriteFile(configFilePath, content, 0o644)
Expect(err).To(BeNil())
ref = registry + repoAndTag
args = []string{artifactCmd, installCmd, ref, "--config", configFile, "--platform", "this/is/invalid"}
})
It("check that fails and the usage is not printed", func() {
expectedError := `ERROR invalid "platform": must be in the format OS/Arch`
Expect(output).ShouldNot(gbytes.Say(regexp.QuoteMeta(artifactInstallUsage)))
Expect(output).Should(gbytes.Say(regexp.QuoteMeta(expectedError)))
})
})
})
})

View File

@ -42,7 +42,8 @@ func NewDriverCleanupCmd(ctx context.Context, opt *options.Common, driver *optio
Use: "cleanup [flags]",
DisableFlagsInUseLine: true,
Short: "Cleanup a driver",
Long: `Cleans a driver up, eg for kmod, by removing it from dkms.`,
Long: `Cleans a driver up, eg for kmod, by removing it from dkms.
** This command is in preview and under development. **`,
RunE: func(cmd *cobra.Command, args []string) error {
return o.RunDriverCleanup(ctx)
},

View File

@ -27,6 +27,7 @@ import (
//nolint:lll // no need to check for line length.
var driverCleanupHelp = `Cleans a driver up, eg for kmod, by removing it from dkms.
** This command is in preview and under development. **
Usage:
falcoctl driver cleanup [flags]
@ -43,7 +44,7 @@ Global Flags:
--log-level string Set level for logs (info, warn, debug, trace) (default "info")
--name string Driver name to be used. (default "falco")
--repo strings Driver repo to be used. (default [https://download.falco.org/driver])
--type strings Driver types allowed in descending priority order (ebpf, kmod, modern_ebpf) (default [modern_ebpf,kmod,ebpf])
--type strings Driver types allowed in descending priority order (ebpf, kmod, modern_ebpf) (default [modern_ebpf,ebpf,kmod])
--version string Driver version to be used.
`

View File

@ -1,326 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2024 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package driverconfig
import (
"context"
"fmt"
"os"
"path/filepath"
"testing"
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
drivertype "github.com/falcosecurity/falcoctl/pkg/driver/type"
"github.com/falcosecurity/falcoctl/pkg/options"
)
const (
falcoName = "falco"
)
func newOptions() *driverConfigOptions {
common := options.NewOptions()
common.Initialize()
// Parse the driver type.
dType, _ := drivertype.Parse("modern_ebpf")
return &driverConfigOptions{
Common: common,
Driver: &options.Driver{
Type: dType,
Name: falcoName,
Repos: []string{"https://download.falco.org/driver"},
Version: "6.0.0+driver",
HostRoot: "/",
Distro: nil,
Kr: kernelrelease.KernelRelease{},
},
update: false,
namespace: "",
kubeconfig: "",
configmap: "",
configDir: "",
}
}
func createFalcoConfigFile(cfg falcoCfg, configDir string) error {
engineKind, err := yaml.Marshal(cfg)
if err != nil {
return fmt.Errorf("unable to marshal falco config: %w", err)
}
// Write the engine configuration to a specialized config file.
if err := os.WriteFile(filepath.Join(configDir, "falco.yaml"), engineKind, 0o600); err != nil {
return fmt.Errorf("unable to write falco.yaml file: %w", err)
}
return nil
}
func createFalcoConfigMap(cfg falcoCfg, dataKey string) (*v1.ConfigMap, error) {
engineKind, err := yaml.Marshal(cfg)
if err != nil {
return nil, fmt.Errorf("unable to marshal falco config: %w", err)
}
cm := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: falcoName,
Namespace: falcoName,
},
Data: map[string]string{
dataKey: string(engineKind),
},
}
return cm, nil
}
func TestDriverConfigOptions_Commit_Host(t *testing.T) {
testCases := []struct {
name string
args func(t *testing.T) *driverConfigOptions
expected func(t *testing.T, opt *driverConfigOptions, err error)
}{
{
"no falco config file",
func(t *testing.T) *driverConfigOptions {
opt := newOptions()
opt.configDir = "no-file-at-all"
opt.update = true
return opt
},
func(t *testing.T, opt *driverConfigOptions, err error) {
require.Error(t, err, "should error since falco configuration file does not exist")
require.ErrorContains(t, err, "open no-file-at-all/falco.yaml: no such file or directory")
},
},
{
"update-falco-config",
func(t *testing.T) *driverConfigOptions {
opt := newOptions()
dir, err := os.MkdirTemp("", "falcoctl-driver-config-test")
require.NoError(t, err)
// Write falco configuration file.
cfg := falcoCfg{engineCfg{Kind: "modern_ebpf"}}
err = createFalcoConfigFile(cfg, dir)
require.NoError(t, err)
opt.configDir = dir
return opt
},
func(t *testing.T, opt *driverConfigOptions, err error) {
require.NoError(t, err, "should not error")
// Config file.
specCfgFile := filepath.Join(opt.configDir, "config.d", falcoDriverConfigFile)
// Check that config file has been created.
_, err = os.Stat(specCfgFile)
require.NoError(t, err)
content, err := os.ReadFile(specCfgFile)
require.NoError(t, err)
cfg := falcoCfg{}
err = yaml.Unmarshal(content, &cfg)
require.NoError(t, err)
require.Equal(t, opt.Type.String(), cfg.Engine.Kind)
},
},
{
"falco-not-in-driver-mode",
func(t *testing.T) *driverConfigOptions {
opt := newOptions()
dir, err := os.MkdirTemp("", "falcoctl-driver-config-test")
require.NoError(t, err)
// Write falco configuration file.
cfg := falcoCfg{engineCfg{Kind: "nodriver"}}
err = createFalcoConfigFile(cfg, dir)
require.NoError(t, err)
opt.configDir = dir
return opt
},
func(t *testing.T, opt *driverConfigOptions, err error) {
require.NoError(t, err, "should not error")
// Config file.
specCfgFile := filepath.Join(opt.configDir, "config.d", falcoDriverConfigFile)
// Check that config file has been created.
_, err = os.Stat(specCfgFile)
require.True(t, os.IsNotExist(err))
},
},
}
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
opt := testCase.args(t)
err := opt.Commit(context.Background(), nil, opt.Type)
testCase.expected(t, opt, err)
})
}
}
func TestDriverConfigOptions_Commit_K8S(t *testing.T) {
testCases := []struct {
name string
args func(t *testing.T) (*driverConfigOptions, *v1.ConfigMap)
expected func(t *testing.T, opt *driverConfigOptions, err error)
}{
{
"no falco configmap, wrong namespace",
func(t *testing.T) (*driverConfigOptions, *v1.ConfigMap) {
opt := newOptions()
opt.namespace = "wrong-namespace"
opt.configmap = falcoName
cm, err := createFalcoConfigMap(falcoCfg{engineCfg{Kind: "modern_ebpf"}}, "falco.yaml")
require.NoError(t, err)
return opt, cm
},
func(t *testing.T, opt *driverConfigOptions, err error) {
require.Error(t, err, "should error since falco configmap does not exist")
require.ErrorContains(t, err, "unable to get configmap falco in namespace wrong-namespace")
},
},
{
"no falco configmap, wrong name",
func(t *testing.T) (*driverConfigOptions, *v1.ConfigMap) {
opt := newOptions()
opt.namespace = falcoName
opt.configmap = "wrong-name"
cm, err := createFalcoConfigMap(falcoCfg{engineCfg{Kind: "modern_ebpf"}}, "falco.yaml")
require.NoError(t, err)
return opt, cm
},
func(t *testing.T, opt *driverConfigOptions, err error) {
require.Error(t, err, "should error since falco configmap does not exist")
require.ErrorContains(t, err, "unable to get configmap wrong-name in namespace falco")
},
},
{
"no falco config, wrong data key",
func(t *testing.T) (*driverConfigOptions, *v1.ConfigMap) {
opt := newOptions()
opt.namespace = falcoName
opt.configmap = falcoName
cm, err := createFalcoConfigMap(falcoCfg{engineCfg{Kind: "modern_ebpf"}}, "wrong-data-key")
require.NoError(t, err)
return opt, cm
},
func(t *testing.T, opt *driverConfigOptions, err error) {
require.Error(t, err, "should error since falco configmap does not exist")
require.ErrorContains(t, err, "configMap falco does not contain key \"falco.yaml\"")
},
},
{
"update-falco-config",
func(t *testing.T) (*driverConfigOptions, *v1.ConfigMap) {
opt := newOptions()
opt.namespace = falcoName
opt.configmap = falcoName
dir, err := os.MkdirTemp("", "falcoctl-driver-config-test")
require.NoError(t, err)
opt.configDir = dir
cm, err := createFalcoConfigMap(falcoCfg{engineCfg{Kind: "modern_ebpf"}}, "falco.yaml")
require.NoError(t, err)
return opt, cm
},
func(t *testing.T, opt *driverConfigOptions, err error) {
require.NoError(t, err, "should not error")
// Config file.
specCfgFile := filepath.Join(opt.configDir, "config.d", falcoDriverConfigFile)
// Check that config file has been created.
_, err = os.Stat(specCfgFile)
require.NoError(t, err)
content, err := os.ReadFile(specCfgFile)
require.NoError(t, err)
cfg := falcoCfg{}
err = yaml.Unmarshal(content, &cfg)
require.NoError(t, err)
require.Equal(t, opt.Type.String(), cfg.Engine.Kind)
},
},
{
"falco-not-in-driver-mode",
func(t *testing.T) (*driverConfigOptions, *v1.ConfigMap) {
opt := newOptions()
opt.namespace = falcoName
opt.configmap = falcoName
dir, err := os.MkdirTemp("", "falcoctl-driver-config-test")
require.NoError(t, err)
cm, err := createFalcoConfigMap(falcoCfg{engineCfg{Kind: "nodriver"}}, "falco.yaml")
require.NoError(t, err)
opt.configDir = dir
return opt, cm
},
func(t *testing.T, opt *driverConfigOptions, err error) {
require.NoError(t, err, "should not error")
// Config file.
specCfgFile := filepath.Join(opt.configDir, "config.d", falcoDriverConfigFile)
// Check that config file has been created.
_, err = os.Stat(specCfgFile)
require.True(t, os.IsNotExist(err))
},
},
}
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
opt, cm := testCase.args(t)
// Create fake client.
fakeClient := fake.NewSimpleClientset(cm)
err := opt.Commit(context.Background(), fakeClient, opt.Type)
testCase.expected(t, opt, err)
})
}
}

View File

@ -1,5 +1,5 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2024 The Falco Authors
// Copyright (C) 2023 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -16,50 +16,44 @@
package driverconfig
import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"golang.org/x/net/context"
"gopkg.in/yaml.v3"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"github.com/falcosecurity/falcoctl/internal/config"
"github.com/falcosecurity/falcoctl/internal/utils"
drivertype "github.com/falcosecurity/falcoctl/pkg/driver/type"
"github.com/falcosecurity/falcoctl/pkg/options"
)
const (
longConfig = `Configure a driver for future usages with other driver subcommands.
configMapEngineKindKey = "engine.kind"
longConfig = `Configure a driver for future usages with other driver subcommands.
It will also update local Falco configuration or k8s configmap depending on the environment where it is running, to let Falco use chosen driver.
Only supports deployments of Falco that use a driver engine, ie: one between kmod, ebpf and modern-ebpf.
If engine.kind key is set to a non-driver driven engine, Falco configuration won't be touched.
** This command is in preview and under development. **
`
falcoConfigFile = "falco.yaml"
falcoDriverConfigFile = "engine-kind-falcoctl.yaml"
)
type driverConfigOptions struct {
*options.Common
*options.Driver
update bool
namespace string
kubeconfig string
configmap string
configDir string
}
type engineCfg struct {
Kind string `yaml:"kind"`
}
type falcoCfg struct {
Engine engineCfg `yaml:"engine"`
Update bool
Namespace string
KubeConfig string
}
// NewDriverConfigCmd configures a driver and stores it in config.
@ -74,34 +68,14 @@ func NewDriverConfigCmd(ctx context.Context, opt *options.Common, driver *option
DisableFlagsInUseLine: true,
Short: "Configure a driver",
Long: longConfig,
PreRunE: func(cmd *cobra.Command, args []string) error {
viper.AutomaticEnv()
_ = viper.BindPFlag("driver.config.configmap", cmd.Flags().Lookup("configmap"))
_ = viper.BindPFlag("driver.config.namespace", cmd.Flags().Lookup("namespace"))
_ = viper.BindPFlag("driver.config.update_falco", cmd.Flags().Lookup("update-falco"))
_ = viper.BindPFlag("driver.config.kubeconfig", cmd.Flags().Lookup("kubeconfig"))
_ = viper.BindPFlag("driver.config.configdir", cmd.Flags().Lookup("falco-config-dir"))
o.configmap = viper.GetString("driver.config.configmap")
o.namespace = viper.GetString("driver.config.namespace")
o.kubeconfig = viper.GetString("driver.config.kubeconfig")
o.update = viper.GetBool("driver.config.update_falco")
o.configDir = viper.GetString("driver.config.configdir")
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
return o.RunDriverConfig(ctx)
},
}
cmd.Flags().BoolVar(&o.update, "update-falco", true, "Whether to overwrite Falco configuration")
cmd.Flags().StringVar(&o.namespace, "namespace", "", "Kubernetes namespace.")
cmd.Flags().StringVar(&o.kubeconfig, "kubeconfig", "", "Kubernetes config.")
cmd.Flags().StringVar(&o.configmap, "configmap", "", "Falco configmap name.")
cmd.Flags().StringVar(&o.configDir, "falco-config-dir", "/etc/falco", "Falco configuration directory.")
cmd.Flags().BoolVar(&o.Update, "update-falco", true, "Whether to update Falco config/configmap.")
cmd.Flags().StringVar(&o.Namespace, "namespace", "", "Kubernetes namespace.")
cmd.Flags().StringVar(&o.KubeConfig, "kubeconfig", "", "Kubernetes config.")
return cmd
}
@ -114,149 +88,115 @@ func (o *driverConfigOptions) RunDriverConfig(ctx context.Context) error {
"host-root", o.Driver.HostRoot,
"repos", strings.Join(o.Driver.Repos, ",")))
if o.update {
var cl kubernetes.Interface
var err error
if o.namespace != "" {
// Create a new clientset.
if cl, err = setupClient(o.kubeconfig); err != nil {
return err
}
}
if err := o.Commit(ctx, cl, o.Driver.Type); err != nil {
if o.Update {
if err := o.commit(ctx, o.Driver.Type); err != nil {
return err
}
}
o.Printer.Logger.Info("Storing falcoctl driver config")
return config.StoreDriver(o.Driver.ToDriverConfig(), o.ConfigFile)
}
func checkFalcoRunsWithDrivers(engineKind string) bool {
func checkFalcoRunsWithDrivers(engineKind string) error {
// Modify the data in the ConfigMap/Falco config file ONLY if engine.kind is set to a known driver type.
// This ensures that we modify the config only for Falcos running with drivers, and not plugins/gvisor.
// Scenario: user has multiple Falco pods deployed in its cluster, one running with driver,
// other running with plugins. We must only touch the one running with driver.
if _, err := drivertype.Parse(engineKind); err != nil {
return false
return fmt.Errorf("engine.kind is not driver driven: %s", engineKind)
}
return true
}
func (o *driverConfigOptions) IsRunningInDriverModeHost() (bool, error) {
o.Printer.Logger.Debug("Checking if Falco is running in driver mode on host system")
falcoCfgFile := filepath.Join(o.configDir, falcoConfigFile)
yamlFile, err := os.ReadFile(filepath.Clean(falcoCfgFile))
if err != nil {
return false, err
}
cfg := falcoCfg{}
if err = yaml.Unmarshal(yamlFile, &cfg); err != nil {
return false, fmt.Errorf("unable to unmarshal falco.yaml to falcoCfg struct: %w", err)
}
return checkFalcoRunsWithDrivers(cfg.Engine.Kind), nil
}
func (o *driverConfigOptions) IsRunningInDriverModeK8S(ctx context.Context, cl kubernetes.Interface) (bool, error) {
o.Printer.Logger.Debug("Checking if Falco is running in driver mode in Kubernetes")
configMap, err := cl.CoreV1().ConfigMaps(o.namespace).Get(ctx, o.configmap, metav1.GetOptions{})
if err != nil {
return false, fmt.Errorf("unable to get configmap %s in namespace %s: %w", o.configmap, o.namespace, err)
}
// Check that this is a Falco config map
falcoYaml, present := configMap.Data["falco.yaml"]
if !present {
o.Printer.Logger.Debug("Skip non Falco-related config map",
o.Printer.Logger.Args("configMap", configMap.Name))
return false, fmt.Errorf("configMap %s does not contain key \"falco.yaml\"", o.configmap)
}
// Check that Falco is configured to run with a driver
var falcoConfig falcoCfg
err = yaml.Unmarshal([]byte(falcoYaml), &falcoConfig)
if err != nil {
return false, fmt.Errorf("unable to unmarshal falco.yaml to falcoCfg struct: %w", err)
}
return checkFalcoRunsWithDrivers(falcoConfig.Engine.Kind), nil
}
// Commit saves the updated driver type to Falco config,
// in a specialized configuration file under /etc/falco/config.d.
func (o *driverConfigOptions) Commit(ctx context.Context, cl kubernetes.Interface, driverType drivertype.DriverType) error {
// If set to true, then we need to overwrite the driver type.
var overwrite bool
var err error
if cl != nil {
if overwrite, err = o.IsRunningInDriverModeK8S(ctx, cl); err != nil {
return err
}
} else {
if overwrite, err = o.IsRunningInDriverModeHost(); err != nil {
return err
}
}
if overwrite {
o.Printer.Logger.Info("Committing driver config to specialized configuration file under",
o.Printer.Logger.Args("directory", filepath.Join(o.configDir, "config.d")))
return overwriteDriverType(o.configDir, driverType)
}
o.Printer.Logger.Info("Falco is not configured to run with a driver, no need to set driver type.")
return nil
}
func setupClient(kubeconfig string) (kubernetes.Interface, error) {
var cfg *rest.Config
var err error
func (o *driverConfigOptions) replaceDriverTypeInFalcoConfig(driverType drivertype.DriverType) error {
falcoCfgFile := filepath.Clean(filepath.Join(string(os.PathSeparator), "etc", "falco", "falco.yaml"))
type engineCfg struct {
Kind string `yaml:"kind"`
}
type falcoCfg struct {
Engine engineCfg `yaml:"engine"`
}
yamlFile, err := os.ReadFile(filepath.Clean(falcoCfgFile))
if err != nil {
return err
}
cfg := falcoCfg{}
if err = yaml.Unmarshal(yamlFile, &cfg); err != nil {
return err
}
if err = checkFalcoRunsWithDrivers(cfg.Engine.Kind); err != nil {
o.Printer.Logger.Warn("Avoid updating Falco configuration",
o.Printer.Logger.Args("config", falcoCfgFile, "reason", err))
return nil
}
const configKindKey = "kind: "
return utils.ReplaceTextInFile(falcoCfgFile, configKindKey+cfg.Engine.Kind, configKindKey+driverType.String(), 1)
}
// Create the rest config.
if kubeconfig != "" {
cfg, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
func (o *driverConfigOptions) replaceDriverTypeInK8SConfigMap(ctx context.Context, driverType drivertype.DriverType) error {
var (
err error
cfg *rest.Config
)
if o.KubeConfig != "" {
cfg, err = clientcmd.BuildConfigFromFlags("", o.KubeConfig)
} else {
cfg, err = rest.InClusterConfig()
}
if err != nil {
return nil, err
}
// Create the clientset.
return kubernetes.NewForConfig(cfg)
}
func overwriteDriverType(configDir string, driverType drivertype.DriverType) error {
var falcoConfig falcoCfg
configDir = filepath.Join(configDir, "config.d")
// First thing, check if config.d folder exists in the configuration directory.
_, err := os.Stat(configDir)
if os.IsNotExist(err) {
// Create it.
// #nosec G301 -- under /etc we want 755 permissions
if err := os.MkdirAll(configDir, 0o755); err != nil {
return fmt.Errorf("unable to create directory %s: %w", configDir, err)
}
} else if err != nil && !os.IsNotExist(err) {
return err
}
falcoConfig.Engine.Kind = driverType.String()
engineKind, err := yaml.Marshal(falcoConfig)
cl, err := kubernetes.NewForConfig(cfg)
if err != nil {
return fmt.Errorf("unable to marshal falco config: %w", err)
return err
}
// Write the engine configuration to a specialized config file.
// #nosec G306 //under /etc we want 644 permissions
if err := os.WriteFile(filepath.Join(configDir, falcoDriverConfigFile), engineKind, 0o644); err != nil {
return fmt.Errorf("unable to persist engine kind to filesystem: %w", err)
configMapList, err := cl.CoreV1().ConfigMaps(o.Namespace).List(ctx, metav1.ListOptions{
LabelSelector: "app.kubernetes.io/instance: falco",
})
if err != nil {
return err
}
if configMapList.Size() == 0 {
return errors.New(`no configmaps matching "app.kubernetes.io/instance: falco" label were found`)
}
type patchDriverTypeValue struct {
Op string `json:"op"`
Path string `json:"path"`
Value string `json:"value"`
}
payload := []patchDriverTypeValue{{
Op: "replace",
Path: "/data/" + configMapEngineKindKey,
Value: driverType.String(),
}}
plBytes, _ := json.Marshal(payload)
for i := 0; i < configMapList.Size(); i++ {
configMap := configMapList.Items[i]
currEngineKind := configMap.Data[configMapEngineKindKey]
if err = checkFalcoRunsWithDrivers(currEngineKind); err != nil {
o.Printer.Logger.Warn("Avoid updating Falco configMap",
o.Printer.Logger.Args("configMap", configMap.Name, "reason", err))
continue
}
// Patch the configMap
if _, err = cl.CoreV1().ConfigMaps(configMap.Namespace).Patch(
ctx, configMap.Name, types.JSONPatchType, plBytes, metav1.PatchOptions{}); err != nil {
return err
}
}
return nil
}
// commit saves the updated driver type to Falco config,
// either to the local falco.yaml or updating the deployment configmap.
func (o *driverConfigOptions) commit(ctx context.Context, driverType drivertype.DriverType) error {
if o.Namespace != "" {
// Ok we are on k8s
return o.replaceDriverTypeInK8SConfigMap(ctx, driverType)
}
return o.replaceDriverTypeInFalcoConfig(driverType)
}

View File

@ -30,17 +30,16 @@ var driverConfigHelp = `Configure a driver for future usages with other driver s
It will also update local Falco configuration or k8s configmap depending on the environment where it is running, to let Falco use chosen driver.
Only supports deployments of Falco that use a driver engine, ie: one between kmod, ebpf and modern-ebpf.
If engine.kind key is set to a non-driver driven engine, Falco configuration won't be touched.
** This command is in preview and under development. **
Usage:
falcoctl driver config [flags]
Flags:
--configmap string Falco configmap name.
--falco-config-dir string Falco configuration directory. (default "/etc/falco")
-h, --help help for config
--kubeconfig string Kubernetes config.
--namespace string Kubernetes namespace.
--update-falco Whether to overwrite Falco configuration (default true)
-h, --help help for config
--kubeconfig string Kubernetes config.
--namespace string Kubernetes namespace.
--update-falco Whether to update Falco config/configmap. (default true)
Global Flags:
--config string config file to be used for falcoctl (default "/etc/falcoctl/falcoctl.yaml")
@ -51,7 +50,7 @@ Global Flags:
--log-level string Set level for logs (info, warn, debug, trace) (default "info")
--name string Driver name to be used. (default "falco")
--repo strings Driver repo to be used. (default [https://download.falco.org/driver])
--type strings Driver types allowed in descending priority order (ebpf, kmod, modern_ebpf) (default [modern_ebpf,kmod,ebpf])
--type strings Driver types allowed in descending priority order (ebpf, kmod, modern_ebpf) (default [modern_ebpf,ebpf,kmod])
--version string Driver version to be used.
`

View File

@ -55,7 +55,8 @@ func NewDriverCmd(ctx context.Context, opt *options.Common) *cobra.Command {
Use: "driver",
DisableFlagsInUseLine: true,
Short: "Interact with falcosecurity driver",
Long: `Interact with falcosecurity driver.`,
Long: `Interact with falcosecurity driver.
** This command is in preview and under development. **`,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
opt.Initialize()
if err := config.Load(opt.ConfigFile); err != nil {
@ -138,8 +139,6 @@ func NewDriverCmd(ctx context.Context, opt *options.Common) *cobra.Command {
return err
}
allowedDriverTypes = append(allowedDriverTypes, drvType)
opt.Printer.Logger.Debug("Allowed driver",
opt.Printer.Logger.Args("type", drvType))
}
// Step 2: fetch system info (kernel release/version and distro)

View File

@ -41,9 +41,8 @@ type driverDownloadOptions struct {
type driverInstallOptions struct {
*options.Common
*options.Driver
Download bool
Compile bool
DownloadHeaders bool
Download bool
Compile bool
driverDownloadOptions
}
@ -61,7 +60,8 @@ func NewDriverInstallCmd(ctx context.Context, opt *options.Common, driver *optio
Use: "install [flags]",
DisableFlagsInUseLine: true,
Short: "Install previously configured driver",
Long: `Install previously configured driver, either downloading it or attempting a build.`,
Long: `Install previously configured driver, either downloading it or attempting a build.
** This command is in preview and under development. **`,
RunE: func(cmd *cobra.Command, args []string) error {
dest, err := o.RunDriverInstall(ctx)
if dest != "" {
@ -78,7 +78,6 @@ func NewDriverInstallCmd(ctx context.Context, opt *options.Common, driver *optio
cmd.Flags().BoolVar(&o.Download, "download", true, "Whether to enable download of prebuilt drivers")
cmd.Flags().BoolVar(&o.Compile, "compile", true, "Whether to enable local compilation of drivers")
cmd.Flags().BoolVar(&o.DownloadHeaders, "download-headers", true, "Whether to enable automatic kernel headers download where supported")
cmd.Flags().BoolVar(&o.InsecureDownload, "http-insecure", false, "Whether you want to allow insecure downloads or not")
cmd.Flags().DurationVar(&o.HTTPTimeout, "http-timeout", 60*time.Second, "Timeout for each http try")
cmd.Flags().StringVar(&o.HTTPHeaders, "http-headers",
@ -193,7 +192,7 @@ func (o *driverInstallOptions) RunDriverInstall(ctx context.Context) (string, er
if !o.Printer.DisableStyling {
o.Printer.Spinner, _ = o.Printer.Spinner.Start("Trying to build the driver")
}
dest, err = driverdistro.Build(ctx, o.Distro, o.Printer.WithWriter(&buf), o.Kr, o.Driver.Name, o.Driver.Type, o.Driver.Version, o.DownloadHeaders)
dest, err = driverdistro.Build(ctx, o.Distro, o.Printer.WithWriter(&buf), o.Kr, o.Driver.Name, o.Driver.Type, o.Driver.Version)
if o.Printer.Spinner != nil {
_ = o.Printer.Spinner.Stop()
}
@ -207,6 +206,7 @@ func (o *driverInstallOptions) RunDriverInstall(ctx context.Context) (string, er
}
buf.Reset()
if err == nil {
o.Printer.Logger.Info("Driver built.", o.Printer.Logger.Args("path", dest))
return dest, nil
}
if errors.Is(err, driverdistro.ErrAlreadyPresent) {

View File

@ -27,6 +27,7 @@ import (
//nolint:lll // no need to check for line length.
var driverInstallHelp = `Install previously configured driver, either downloading it or attempting a build.
** This command is in preview and under development. **
Usage:
falcoctl driver install [flags]
@ -34,7 +35,6 @@ Usage:
Flags:
--compile Whether to enable local compilation of drivers (default true)
--download Whether to enable download of prebuilt drivers (default true)
--download-headers Whether to enable automatic kernel headers download where supported (default true)
-h, --help help for install
--http-headers string Optional comma-separated list of headers for the http GET request (e.g. --http-headers='x-emc-namespace: default,Proxy-Authenticate: Basic'). Not necessary if default repo is used
--http-insecure Whether you want to allow insecure downloads or not
@ -49,7 +49,7 @@ Global Flags:
--log-level string Set level for logs (info, warn, debug, trace) (default "info")
--name string Driver name to be used. (default "falco")
--repo strings Driver repo to be used. (default [https://download.falco.org/driver])
--type strings Driver types allowed in descending priority order (ebpf, kmod, modern_ebpf) (default [modern_ebpf,kmod,ebpf])
--type strings Driver types allowed in descending priority order (ebpf, kmod, modern_ebpf) (default [modern_ebpf,ebpf,kmod])
--version string Driver version to be used.
`

View File

@ -40,7 +40,8 @@ func NewDriverPrintenvCmd(ctx context.Context, opt *options.Common, driver *opti
Use: "printenv [flags]",
DisableFlagsInUseLine: true,
Short: "Print env vars",
Long: `Print variables used by driver as env vars.`,
Long: `Print variables used by driver as env vars.
** This command is in preview and under development. **`,
RunE: func(_ *cobra.Command, _ []string) error {
return o.RunDriverPrintenv(ctx)
},

View File

@ -30,6 +30,7 @@ import (
//nolint:lll // no need to check for line length.
var driverPrintenvHelp = `Print variables used by driver as env vars.
** This command is in preview and under development. **
Usage:
falcoctl driver printenv [flags]
@ -46,7 +47,7 @@ Global Flags:
--log-level string Set level for logs (info, warn, debug, trace) (default "info")
--name string Driver name to be used. (default "falco")
--repo strings Driver repo to be used. (default [https://download.falco.org/driver])
--type strings Driver types allowed in descending priority order (ebpf, kmod, modern_ebpf) (default [modern_ebpf,kmod,ebpf])
--type strings Driver types allowed in descending priority order (ebpf, kmod, modern_ebpf) (default [modern_ebpf,ebpf,kmod])
--version string Driver version to be used.
`

View File

@ -16,16 +16,10 @@
package basic
import (
"bufio"
"context"
"fmt"
"io"
"os"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"golang.org/x/term"
"oras.land/oras-go/v2/registry/remote/credentials"
"github.com/falcosecurity/falcoctl/internal/config"
@ -33,14 +27,10 @@ import (
"github.com/falcosecurity/falcoctl/internal/utils"
"github.com/falcosecurity/falcoctl/pkg/oci/authn"
"github.com/falcosecurity/falcoctl/pkg/options"
"github.com/falcosecurity/falcoctl/pkg/output"
)
type loginOptions struct {
*options.Common
username string
password string
passwordFromStdin bool
}
// NewBasicCmd returns the basic command.
@ -53,56 +43,22 @@ func NewBasicCmd(ctx context.Context, opt *options.Common) *cobra.Command {
Use: "basic [hostname]",
DisableFlagsInUseLine: true,
Short: "Login to an OCI registry",
Long: `Login to an OCI registry
Example - Log in with username and password from command line flags:
falcoctl registry auth basic -u username -p password localhost:5000
Example - Login with username and password from env variables:
FALCOCTL_REGISTRY_AUTH_BASIC_USERNAME=username FALCOCTL_REGISTRY_AUTH_BASIC_PASSWORD=password falcoctl registry auth basic localhost:5000
Example - Login with username and password from stdin:
falcoctl registry auth basic -u username --password-stdin localhost:5000
Example - Login with username and password in an interactive prompt:
falcoctl registry auth basic localhost:5000
`,
Args: cobra.ExactArgs(1),
PreRunE: func(cmd *cobra.Command, args []string) error {
_ = viper.BindPFlag("registry.auth.basic.username", cmd.Flags().Lookup("username"))
_ = viper.BindPFlag("registry.auth.basic.password", cmd.Flags().Lookup("password"))
_ = viper.BindPFlag("registry.auth.basic.password_stdin", cmd.Flags().Lookup("password-stdin"))
o.username = viper.GetString("registry.auth.basic.username")
o.password = viper.GetString("registry.auth.basic.password")
o.passwordFromStdin = viper.GetBool("registry.auth.basic.password_stdin")
return nil
},
Long: "Login to an OCI registry to push and pull artifacts",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return o.RunBasic(ctx, args)
},
}
cmd.Flags().StringVarP(&o.username, "username", "u", "", "registry username")
cmd.Flags().StringVarP(&o.password, "password", "p", "", "registry password")
cmd.Flags().BoolVar(&o.passwordFromStdin, "password-stdin", false, "read password from stdin")
return cmd
}
// RunBasic executes the business logic for the basic command.
func (o *loginOptions) RunBasic(ctx context.Context, args []string) error {
var reg string
reg := args[0]
logger := o.Printer.Logger
// Allow to have the registry expressed as a ref, but actually extract it.
reg, err := utils.GetRegistryFromRef(args[0])
user, token, err := utils.GetCredentials(o.Printer)
if err != nil {
reg = args[0]
}
if err := getCredentials(o.Printer, o); err != nil {
return err
}
@ -117,46 +73,11 @@ func (o *loginOptions) RunBasic(ctx context.Context, args []string) error {
return fmt.Errorf("unable to create new store: %w", err)
}
if err := basic.Login(ctx, client, credentialStore, reg, o.username, o.password); err != nil {
if err := basic.Login(ctx, client, credentialStore, reg, user, token); err != nil {
return err
}
logger.Debug("Credentials added", logger.Args("credential store", config.RegistryCredentialConfPath()))
logger.Info("Login succeeded", logger.Args("registry", reg, "user", o.username))
return nil
}
// getCredentials is used to retrieve username and password from standard input.
func getCredentials(p *output.Printer, opt *loginOptions) error {
reader := bufio.NewReader(os.Stdin)
if opt.username == "" {
p.DefaultText.Print(p.FormatTitleAsLoggerInfo("Enter username:"))
username, err := reader.ReadString('\n')
if err != nil {
return err
}
opt.username = strings.TrimSpace(username)
}
if opt.password == "" {
if opt.passwordFromStdin {
password, err := io.ReadAll(os.Stdin)
if err != nil {
return err
}
opt.password = strings.TrimSuffix(string(password), "\n")
opt.password = strings.TrimSuffix(opt.password, "\r")
} else {
p.DefaultText.Print(p.FormatTitleAsLoggerInfo("Enter password: "))
bytePassword, err := term.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return err
}
opt.password = string(bytePassword)
}
}
logger.Info("Login succeeded", logger.Args("registry", reg, "user", user))
return nil
}

View File

@ -59,34 +59,7 @@ Global Flags:
`
//nolint:unused // false positive
var registryAuthBasicHelp = `Login to an OCI registry
Example - Log in with username and password from command line flags:
falcoctl registry auth basic -u username -p password localhost:5000
Example - Login with username and password from env variables:
FALCOCTL_REGISTRY_AUTH_BASIC_USERNAME=username FALCOCTL_REGISTRY_AUTH_BASIC_PASSWORD=password falcoctl registry auth basic localhost:5000
Example - Login with username and password from stdin:
falcoctl registry auth basic -u username --password-stdin localhost:5000
Example - Login with username and password in an interactive prompt:
falcoctl registry auth basic localhost:5000
Usage:
falcoctl registry auth basic [hostname]
Flags:
-h, --help help for basic
-p, --password string registry password
--password-stdin read password from stdin
-u, --username string registry username
Global Flags:
--config string config file to be used for falcoctl (default "/etc/falcoctl/falcoctl.yaml")
--log-format string Set formatting for logs (color, text, json) (default "color")
--log-level string Set level for logs (info, warn, debug, trace) (default "info")
`
var registryAuthBasicHelp = `Login to an OCI registry to push and pull artifacts`
//nolint:unused // false positive
var registryAuthBasicAssertFailedBehavior = func(usage, specificError string) {

View File

@ -52,10 +52,6 @@ Example - Push artifact "myplugin.tar.gz" of type "plugin" for multiple platform
Example - Push artifact "myrulesfile.tar.gz" of type "rulesfile":
falcoctl registry push --type rulesfile --version "0.1.2" localhost:5000/myrulesfile:latest myrulesfile.tar.gz
Example - Push artifact "myrulesfile.tar.gz" of type "rulesfile" with floating tags for the major and minor versions (0 and 0.1):
falcoctl registry push --type rulesfile --version "0.1.2" localhost:5000/myrulesfile:latest myrulesfile.tar.gz \
--add-floating-tags
Example - Push artifact "myrulesfile.tar.gz" of type "rulesfile" to an insecure registry:
falcoctl registry push --type rulesfile --version "0.1.2" --plain-http localhost:5000/myrulesfile:latest myrulesfile.tar.gz
@ -174,7 +170,7 @@ func (o *pushOptions) runPush(ctx context.Context, args []string) error {
return err
}
}
path, err := utils.CreateTarGzArchive("", p, true)
path, err := utils.CreateTarGzArchive("", p)
if err != nil {
return err
}
@ -196,14 +192,6 @@ func (o *pushOptions) runPush(ctx context.Context, args []string) error {
return err
}
if o.AutoFloatingTags {
v, err := semver.Parse(o.Version)
if err != nil {
return fmt.Errorf("expected semver for the flag \"--version\": %w", err)
}
o.Tags = append(o.Tags, o.Version, fmt.Sprintf("%v", v.Major), fmt.Sprintf("%v.%v", v.Major, v.Minor))
}
opts := ocipusher.Options{
ocipusher.WithTags(o.Tags...),
ocipusher.WithAnnotationSource(o.AnnotationSource),

View File

@ -350,21 +350,6 @@ var _ = Describe("pushing rulesfiles", func() {
map[string]string{})
})
When("with add-floating-tags and the required flags", func() {
BeforeEach(func() {
rulesfile = rulesfileyaml
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
"--add-floating-tags", "--plain-http"}
// Set name to the expected one.
artifactNameInConfigLayer = repoName
// The semver tags are expected to be set.
pushedTags = []string{"1.1.1", "1.1", "1"}
})
AssertSuccesBehaviour([]oci.ArtifactDependency{},
[]oci.ArtifactRequirement{},
map[string]string{})
})
When("with full flags and args but in tar.gz format", func() {
BeforeEach(func() {
rulesfile = rulesfiletgz

View File

@ -31,7 +31,6 @@ var registryPushUsage = `Usage:
falcoctl registry push hostname/repo[:tag|@digest] file [flags]
Flags:
--add-floating-tags add the floating tags for the major and minor versions
--annotation-source string set annotation source for the artifact
-d, --depends-on stringArray set an artifact dependency (can be specified multiple times). Example: "--depends-on my-plugin:1.2.3"
-h, --help help for push
@ -66,10 +65,6 @@ Example - Push artifact "myplugin.tar.gz" of type "plugin" for multiple platform
Example - Push artifact "myrulesfile.tar.gz" of type "rulesfile":
falcoctl registry push --type rulesfile --version "0.1.2" localhost:5000/myrulesfile:latest myrulesfile.tar.gz
Example - Push artifact "myrulesfile.tar.gz" of type "rulesfile" with floating tags for the major and minor versions (0 and 0.1):
falcoctl registry push --type rulesfile --version "0.1.2" localhost:5000/myrulesfile:latest myrulesfile.tar.gz \
--add-floating-tags
Example - Push artifact "myrulesfile.tar.gz" of type "rulesfile" to an insecure registry:
falcoctl registry push --type rulesfile --version "0.1.2" --plain-http localhost:5000/myrulesfile:latest myrulesfile.tar.gz
@ -90,7 +85,6 @@ Usage:
falcoctl registry push hostname/repo[:tag|@digest] file [flags]
Flags:
--add-floating-tags add the floating tags for the major and minor versions
--annotation-source string set annotation source for the artifact
-d, --depends-on stringArray set an artifact dependency (can be specified multiple times). Example: "--depends-on my-plugin:1.2.3"
-h, --help help for push
@ -196,22 +190,6 @@ var _ = Describe("push", func() {
"registry \"noregistry\": Get \"http://noregistry/v2/\": dial tcp: lookup noregistry")
})
When("wrong semver for --version flag with --add-floating-tags", func() {
BeforeEach(func() {
args = []string{registryCmd, pushCmd, rulesRepo, rulesfiletgz, "--config", configFile, "--type", "rulesfile",
"--version", "notSemVer", "--add-floating-tags", "--plain-http"}
})
pushAssertFailedBehavior(registryPushUsage, "ERROR expected semver for the flag \"--version\": No Major.Minor.Patch elements found")
})
When("invalid character in semver for --version flag with --add-floating-tags", func() {
BeforeEach(func() {
args = []string{registryCmd, pushCmd, rulesRepo, rulesfiletgz, "--config", configFile, "--type", "rulesfile",
"--version", "1.1.a", "--add-floating-tags", "--plain-http"}
})
pushAssertFailedBehavior(registryPushUsage, "ERROR expected semver for the flag \"--version\": Invalid character(s) found in patch number \"a\"")
})
When("missing repository", func() {
BeforeEach(func() {
args = []string{registryCmd, pushCmd, registry, rulesfiletgz, "--config", configFile, "--type", "rulesfile", "--version", "1.1.1", "--plain-http"}

440
go.mod
View File

@ -1,76 +1,72 @@
module github.com/falcosecurity/falcoctl
go 1.24.3
go 1.21.0
toolchain go1.22.1
require (
cloud.google.com/go/storage v1.51.0
github.com/aws/aws-sdk-go-v2 v1.36.3
github.com/aws/aws-sdk-go-v2/config v1.29.9
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3
cloud.google.com/go/storage v1.40.0
github.com/blang/semver v3.5.1+incompatible
github.com/blang/semver/v4 v4.0.0
github.com/cilium/ebpf v0.17.3
github.com/distribution/distribution/v3 v3.0.0
github.com/docker/cli v28.3.2+incompatible
github.com/docker/docker v28.3.3+incompatible
github.com/falcosecurity/driverkit v0.21.2
github.com/go-oauth2/oauth2/v4 v4.5.3
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/google/go-containerregistry v0.20.3
github.com/cilium/ebpf v0.14.0
github.com/distribution/distribution/v3 v3.0.0-alpha.1
github.com/docker/cli v26.0.0+incompatible
github.com/docker/docker v26.0.0+incompatible
github.com/falcosecurity/driverkit v0.18.2
github.com/go-oauth2/oauth2/v4 v4.5.2
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/google/go-containerregistry v0.19.1
github.com/gookit/color v1.5.4
github.com/mattn/go-isatty v0.0.20
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c
github.com/onsi/ginkgo/v2 v2.23.3
github.com/onsi/gomega v1.36.3
github.com/opencontainers/image-spec v1.1.1
github.com/pterm/pterm v0.12.80
github.com/mitchellh/mapstructure v1.5.0
github.com/onsi/ginkgo/v2 v2.17.1
github.com/onsi/gomega v1.32.0
github.com/opencontainers/image-spec v1.1.0
github.com/pterm/pterm v0.12.79
github.com/robfig/cron/v3 v3.0.1
github.com/sigstore/cosign/v2 v2.4.3
github.com/sigstore/sigstore v1.9.1
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.9.1
github.com/sigstore/sigstore/pkg/signature/kms/azure v1.9.1
github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.9.1
github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.1
github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.6
github.com/spf13/viper v1.20.0
github.com/stretchr/testify v1.10.0
golang.org/x/crypto v0.38.0
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f
golang.org/x/net v0.40.0
golang.org/x/oauth2 v0.28.0
golang.org/x/sys v0.33.0
golang.org/x/term v0.32.0
google.golang.org/api v0.227.0
github.com/sigstore/cosign/v2 v2.2.3
github.com/sigstore/sigstore v1.8.3
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.3
github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.3
github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.3
github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.8.3
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.18.2
golang.org/x/crypto v0.21.0
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8
google.golang.org/api v0.172.0
gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.32.3
k8s.io/apimachinery v0.32.3
k8s.io/client-go v0.32.3
k8s.io/apimachinery v0.29.3
k8s.io/client-go v0.29.3
k8s.io/utils v0.0.0-20240310230437-4693a0247e57
oras.land/oras-go/v2 v2.5.0
)
require (
github.com/docker/docker-credential-helpers v0.8.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
golang.org/x/sync v0.6.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
require (
atomicgo.dev/cursor v0.2.0 // indirect
atomicgo.dev/keyboard v0.2.9 // indirect
atomicgo.dev/schedule v0.1.0 // indirect
cel.dev/expr v0.19.2 // indirect
cloud.google.com/go v0.118.3 // indirect
cloud.google.com/go/auth v0.15.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect
cloud.google.com/go/iam v1.4.1 // indirect
cloud.google.com/go/kms v1.21.0 // indirect
cloud.google.com/go/longrunning v0.6.5 // indirect
cloud.google.com/go/monitoring v1.24.0 // indirect
github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/provider v0.14.0 // indirect
cloud.google.com/go v0.112.1 // indirect
cloud.google.com/go/compute v1.24.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.1.7 // indirect
cloud.google.com/go/kms v1.15.8 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0 // indirect
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.1.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect
@ -79,13 +75,9 @@ require (
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.0.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c // indirect
github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect
github.com/alibabacloud-go/cr-20160607 v1.0.1 // indirect
@ -94,53 +86,46 @@ require (
github.com/alibabacloud-go/debug v1.0.0 // indirect
github.com/alibabacloud-go/endpoint-util v1.1.1 // indirect
github.com/alibabacloud-go/openapi-util v0.1.0 // indirect
github.com/alibabacloud-go/tea v1.2.2 // indirect
github.com/alibabacloud-go/tea v1.2.1 // indirect
github.com/alibabacloud-go/tea-utils v1.4.5 // indirect
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
github.com/aliyun/credentials-go v1.3.3 // indirect
github.com/aliyun/credentials-go v1.3.1 // indirect
github.com/andybalholm/brotli v1.0.6 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.62 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // indirect
github.com/aws/aws-sdk-go-v2/service/ecr v1.40.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.31.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect
github.com/aws/aws-sdk-go-v2/service/kms v1.38.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 // indirect
github.com/aws/smithy-go v1.22.2 // indirect
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.9.1 // indirect
github.com/aws/aws-sdk-go-v2 v1.26.0 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.9 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.9 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/service/ecr v1.20.2 // indirect
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.18.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.6 // indirect
github.com/aws/aws-sdk-go-v2/service/kms v1.30.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.20.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.28.5 // indirect
github.com/aws/smithy-go v1.20.1 // indirect
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bshuster-repo/logrus-logstash-hook v1.1.0 // indirect
github.com/buildkite/agent/v3 v3.92.1 // indirect
github.com/buildkite/go-pipeline v0.13.3 // indirect
github.com/buildkite/interpolate v0.1.5 // indirect
github.com/buildkite/roko v1.3.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chai2010/gettext-go v1.0.3 // indirect
github.com/buildkite/agent/v3 v3.62.0 // indirect
github.com/buildkite/go-pipeline v0.3.2 // indirect
github.com/buildkite/interpolate v0.0.0-20200526001904-07f35b4ae251 // indirect
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
github.com/coreos/go-oidc/v3 v3.12.0 // indirect
github.com/containerd/console v1.0.3 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/coreos/go-oidc/v3 v3.9.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/creasty/defaults v1.7.0 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20231217050601-ba74d44ecf5f // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect
@ -148,157 +133,120 @@ require (
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.2 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emicklei/go-restful/v3 v3.12.0 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
github.com/evanphx/json-patch v5.9.0+incompatible // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/fatih/camelcase v1.0.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/fvbommel/sortorder v1.1.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/go-chi/chi v4.1.2+incompatible // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/analysis v0.23.0 // indirect
github.com/go-openapi/errors v0.22.0 // indirect
github.com/go-openapi/analysis v0.22.0 // indirect
github.com/go-openapi/errors v0.21.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/loads v0.22.0 // indirect
github.com/go-openapi/runtime v0.28.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/strfmt v0.23.0 // indirect
github.com/go-openapi/loads v0.21.5 // indirect
github.com/go-openapi/runtime v0.27.1 // indirect
github.com/go-openapi/spec v0.20.13 // indirect
github.com/go-openapi/strfmt v0.22.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-openapi/validate v0.24.0 // indirect
github.com/go-piv/piv-go/v2 v2.3.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.24.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
github.com/go-openapi/validate v0.22.4 // indirect
github.com/go-piv/piv-go v1.11.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang-jwt/jwt/v5 v5.2.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/certificate-transparency-go v1.3.1 // indirect
github.com/google/certificate-transparency-go v1.1.7 // indirect
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-github/v55 v55.0.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
github.com/gorilla/handlers v1.5.2 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.3 // indirect
github.com/gorilla/handlers v1.5.1 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 // indirect
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
github.com/hashicorp/go-sockaddr v1.0.6 // indirect
github.com/hashicorp/go-sockaddr v1.0.5 // indirect
github.com/hashicorp/golang-lru/arc/v2 v2.0.5 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
github.com/hashicorp/vault/api v1.16.0 // indirect
github.com/in-toto/attestation v1.1.0 // indirect
github.com/hashicorp/vault/api v1.12.2 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/in-toto/in-toto-golang v0.9.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 // indirect
github.com/jellydator/ttlcache/v3 v3.3.0 // indirect
github.com/jellydator/ttlcache/v3 v3.2.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/compress v1.17.7 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/letsencrypt/boulder v0.0.0-20231026200631-000cd05d5491 // indirect
github.com/lithammer/fuzzysearch v1.1.8 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/go-archive v0.1.0 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/user v0.4.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/moby/term v0.5.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/mozillazg/docker-credential-acr-helper v0.4.0 // indirect
github.com/mozillazg/docker-credential-acr-helper v0.3.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/oleiade/reflections v1.1.0 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/oleiade/reflections v1.0.1 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pborman/uuid v1.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pelletier/go-toml/v2 v2.2.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/client_golang v1.18.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 // indirect
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 // indirect
github.com/redis/go-redis/v9 v9.7.3 // indirect
github.com/redis/go-redis/v9 v9.3.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sassoftware/relic v7.2.1+incompatible // indirect
github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/sigstore/fulcio v1.6.6 // indirect
github.com/sigstore/protobuf-specs v0.4.0 // indirect
github.com/sigstore/rekor v1.3.9 // indirect
github.com/sigstore/sigstore-go v0.7.0 // indirect
github.com/sigstore/timestamp-authority v1.2.4 // indirect
github.com/sigstore/fulcio v1.4.3 // indirect
github.com/sigstore/rekor v1.3.4 // indirect
github.com/sigstore/timestamp-authority v1.2.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.12.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spiffe/go-spiffe/v2 v2.1.7 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
github.com/thales-e-security/pool v0.0.2 // indirect
github.com/theupdateframework/go-tuf v0.7.0 // indirect
github.com/theupdateframework/go-tuf/v2 v2.0.2 // indirect
github.com/tidwall/btree v1.6.0 // indirect
github.com/tidwall/buntdb v1.3.0 // indirect
github.com/tidwall/gjson v1.16.0 // indirect
@ -311,71 +259,67 @@ require (
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/transparency-dev/merkle v0.0.2 // indirect
github.com/valyala/fasthttp v1.50.0 // indirect
github.com/vbatts/tar-split v0.11.6 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
github.com/vbatts/tar-split v0.11.5 // indirect
github.com/xanzy/go-gitlab v0.96.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/zeebo/errs v1.4.0 // indirect
gitlab.com/gitlab-org/api/client-go v0.123.0 // indirect
go.mongodb.org/mongo-driver v1.15.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect
go.opentelemetry.io/contrib/exporters/autoexport v0.57.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 // indirect
go.opentelemetry.io/otel/exporters/prometheus v0.54.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0 // indirect
go.opentelemetry.io/otel/log v0.8.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
go.opentelemetry.io/otel/sdk/log v0.8.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
go.starlark.net v0.0.0-20240507195648-35fe9f26b4bc // indirect
github.com/zeebo/errs v1.3.0 // indirect
go.mongodb.org/mongo-driver v1.13.1 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/exporters/autoexport v0.46.1 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 // indirect
go.opentelemetry.io/otel/exporters/prometheus v0.44.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.44.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.22.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.21.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.step.sm/crypto v0.42.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/mod v0.23.0 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/text v0.25.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.30.0 // indirect
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
google.golang.org/grpc v1.71.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.19.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/cli-runtime v0.30.0 // indirect
k8s.io/component-base v0.30.0 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
k8s.io/kubectl v0.30.0 // indirect
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
k8s.io/api v0.29.3 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
k8s.io/kube-openapi v0.0.0-20240322212309-b815d8309940 // indirect
modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b // indirect
modernc.org/libc v1.50.5 // indirect
modernc.org/libc v1.49.0 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/sqlite v1.29.9 // indirect
modernc.org/memory v1.7.2 // indirect
modernc.org/sqlite v1.29.5 // indirect
modernc.org/strutil v1.2.0 // indirect
modernc.org/token v1.1.0 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/kustomize/api v0.17.1 // indirect
sigs.k8s.io/kustomize/kyaml v0.17.0 // indirect
sigs.k8s.io/release-utils v0.11.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/release-utils v0.7.7 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
require (
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/mattn/go-isatty v0.0.20
github.com/stretchr/testify v1.9.0
golang.org/x/net v0.22.0
golang.org/x/oauth2 v0.18.0
golang.org/x/sys v0.18.0
golang.org/x/term v0.18.0
)

1067
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -30,7 +30,6 @@ import (
"github.com/mitchellh/mapstructure"
"github.com/spf13/viper"
"github.com/falcosecurity/falcoctl/internal/utils"
drivertype "github.com/falcosecurity/falcoctl/pkg/driver/type"
"github.com/falcosecurity/falcoctl/pkg/oci"
)
@ -204,12 +203,11 @@ func init() {
IndexesDir = filepath.Join(FalcoctlPath, "indexes")
ClientCredentialsFile = filepath.Join(FalcoctlPath, "clientcredentials.json")
DefaultIndex = Index{
Name: "falcosecurity",
URL: "https://falcosecurity.github.io/falcoctl/index.yaml",
Backend: "https",
Name: "falcosecurity",
URL: "https://falcosecurity.github.io/falcoctl/index.yaml",
}
DefaultDriver = Driver{
Type: []string{drivertype.TypeModernBpf, drivertype.TypeKmod, drivertype.TypeBpf},
Type: []string{drivertype.TypeModernBpf, drivertype.TypeBpf, drivertype.TypeKmod},
Name: "falco",
Repos: []string{"https://download.falco.org/driver"},
Version: "",
@ -392,14 +390,8 @@ func basicAuthListHookFunc() mapstructure.DecodeHookFuncType {
return data, fmt.Errorf("not valid token %q", token)
}
// Allow to have the registry expressed as a ref, but actually extract it.
registry, err := utils.GetRegistryFromRef(values[0])
if err != nil {
registry = values[0]
}
auths[i] = BasicAuth{
Registry: registry,
Registry: values[0],
User: values[1],
Password: values[2],
}

View File

@ -1,5 +1,5 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2025 The Falco Authors
// Copyright (C) 2023 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -203,83 +203,52 @@ func (f *Follower) follow(ctx context.Context) {
return
}
// Move files to their destination
if err := f.moveFiles(filePaths, dstDir); err != nil {
return
}
f.logger.Info("Artifact correctly installed",
f.logger.Args("followerName", f.ref, "artifactName", f.ref, "type", res.Type, "digest", res.Digest, "directory", dstDir))
f.currentDigest = desc.Digest.String()
}
// moveFiles moves files from their temporary location to the destination directory.
// It preserves the directory structure relative to the temporary directory.
// For example, if a file is at "tmpDir/subdir/file.yaml", it will be moved to
// "dstDir/subdir/file.yaml". This ensures that files in subdirectories are moved
// correctly as individual files, not as entire directories.
func (f *Follower) moveFiles(filePaths []string, dstDir string) error {
// Install the artifacts if necessary.
for _, path := range filePaths {
// Get the relative path from the temporary directory to preserve directory structure
relPath, err := filepath.Rel(f.tmpDir, path)
if err != nil {
f.logger.Error("Unable to get relative path", f.logger.Args("followerName", f.ref, "path", path, "reason", err.Error()))
return err
}
dstPath := filepath.Join(dstDir, relPath)
// Ensure the parent directory exists
if err := os.MkdirAll(filepath.Dir(dstPath), 0o750); err != nil {
f.logger.Error("Unable to create destination directory", f.logger.Args(
"followerName", f.ref,
"directory", filepath.Dir(dstPath),
"reason", err.Error(),
))
return err
}
f.logger.Debug("Installing file", f.logger.Args("followerName", f.ref, "path", relPath))
baseName := filepath.Base(path)
f.logger.Debug("Installing file", f.logger.Args("followerName", f.ref, "fileName", baseName))
dstPath := filepath.Join(dstDir, baseName)
// Check if the file exists.
f.logger.Debug("Checking if file already exists", f.logger.Args("followerName", f.ref, "path", relPath, "directory", dstDir))
f.logger.Debug("Checking if file already exists", f.logger.Args("followerName", f.ref, "fileName", baseName, "directory", dstDir))
exists, err := utils.FileExists(dstPath)
if err != nil {
f.logger.Error("Unable to check existence for file", f.logger.Args("followerName", f.ref, "path", relPath, "reason", err.Error()))
return err
f.logger.Error("Unable to check existence for file", f.logger.Args("followerName", f.ref, "fileName", baseName, "reason", err.Error()))
return
}
if !exists {
f.logger.Debug("Moving file", f.logger.Args("followerName", f.ref, "path", relPath, "destDirectory", dstDir))
f.logger.Debug("Moving file", f.logger.Args("followerName", f.ref, "fileName", baseName, "destDirectory", dstDir))
if err = utils.Move(path, dstPath); err != nil {
f.logger.Error("Unable to move file", f.logger.Args("followerName", f.ref, "path", relPath, "destDirectory", dstDir, "reason", err.Error()))
return err
f.logger.Error("Unable to move file", f.logger.Args("followerName", f.ref, "fileName", baseName, "destDirectory", dstDir, "reason", err.Error()))
return
}
f.logger.Debug("File correctly installed", f.logger.Args("followerName", f.ref, "path", path))
// It's done, move to the next file.
continue
}
f.logger.Debug(fmt.Sprintf("file %q already exists in %q, checking if it is equal to the existing one", relPath, dstDir),
f.logger.Debug(fmt.Sprintf("file %q already exists in %q, checking if it is equal to the existing one", baseName, dstDir),
f.logger.Args("followerName", f.ref))
// Check if the files are equal.
eq, err := equal([]string{path, dstPath})
if err != nil {
f.logger.Error("Unable to compare files", f.logger.Args("followerName", f.ref, "existingFile", dstPath, "reason", err.Error()))
return err
f.logger.Error("Unable to compare files", f.logger.Args("followerName", f.ref, "newFile", path, "existingFile", dstPath, "reason", err.Error()))
return
}
if !eq {
f.logger.Debug(fmt.Sprintf("Overwriting file %q with file %q", dstPath, path), f.logger.Args("followerName", f.ref))
if err = utils.Move(path, dstPath); err != nil {
f.logger.Error("Unable to overwrite file", f.logger.Args("followerName", f.ref, "existingFile", dstPath, "reason", err.Error()))
return err
return
}
} else {
f.logger.Debug("The two file are equal, nothing to be done")
}
}
return nil
f.logger.Info("Artifact correctly installed",
f.logger.Args("followerName", f.ref, "artifactName", f.ref, "type", res.Type, "digest", res.Digest, "directory", dstDir))
f.currentDigest = desc.Digest.String()
}
// pull downloads, extracts, and installs the artifact.

View File

@ -1,5 +1,5 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2025 The Falco Authors
// Copyright (C) 2024 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -17,7 +17,6 @@ package follower
import (
"os"
"path/filepath"
"testing"
"github.com/pterm/pterm"
@ -136,170 +135,3 @@ func TestCheckRequirements(t *testing.T) {
})
}
}
func TestMoveFiles(t *testing.T) {
type testFile struct {
path string
content string
replace bool
}
tests := []struct {
name string
files []testFile
existing []testFile
}{
{
name: "basic file at root",
files: []testFile{
{
path: "file1.yaml",
content: "content1",
},
},
},
{
name: "file in subdirectory",
files: []testFile{
{
path: "subdir/file2.yaml",
content: "content2",
},
},
},
{
name: "multiple files in different directories",
files: []testFile{
{
path: "file1.yaml",
content: "content1",
},
{
path: "subdir/file2.yaml",
content: "content2",
},
{
path: "subdir/nested/file3.yaml",
content: "content3",
},
},
},
{
name: "existing file with identical content",
files: []testFile{
{
path: "file1.yaml",
content: "content1",
replace: false,
},
},
existing: []testFile{
{
path: "file1.yaml",
content: "content1",
},
},
},
{
name: "existing file with different content",
files: []testFile{
{
path: "file1.yaml",
content: "new content",
replace: true,
},
},
existing: []testFile{
{
path: "file1.yaml",
content: "old content",
},
},
},
{
name: "mix of new and existing files",
files: []testFile{
{
path: "file1.yaml",
content: "content1",
replace: false,
},
{
path: "subdir/file2.yaml",
content: "new content2",
replace: true,
},
},
existing: []testFile{
{
path: "file1.yaml",
content: "content1",
},
{
path: "subdir/file2.yaml",
content: "old content2",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "falcoctl-test-*")
assert.NoError(t, err)
defer os.RemoveAll(tmpDir)
dstDir, err := os.MkdirTemp("", "falcoctl-dst-*")
assert.NoError(t, err)
defer os.RemoveAll(dstDir)
// Setup existing files
for _, ef := range tt.existing {
dstPath := filepath.Join(dstDir, ef.path)
err = os.MkdirAll(filepath.Dir(dstPath), 0o755)
assert.NoError(t, err)
err = os.WriteFile(dstPath, []byte(ef.content), 0o644)
assert.NoError(t, err)
}
f, err := New("test-registry/test-ref", output.NewPrinter(pterm.LogLevelDebug, pterm.LogFormatterJSON, os.Stdout), &Config{
RulesfilesDir: dstDir,
TmpDir: tmpDir,
})
assert.NoError(t, err)
var paths []string
for _, tf := range tt.files {
fullPath := filepath.Join(f.tmpDir, tf.path)
err = os.MkdirAll(filepath.Dir(fullPath), 0o755)
assert.NoError(t, err)
err = os.WriteFile(fullPath, []byte(tf.content), 0o644)
assert.NoError(t, err)
paths = append(paths, fullPath)
}
f.currentDigest = "test-digest"
err = f.moveFiles(paths, dstDir)
assert.NoError(t, err)
for _, tf := range tt.files {
dstPath := filepath.Join(dstDir, tf.path)
_, err = os.Stat(dstPath)
assert.NoError(t, err, "file should exist at %s", dstPath)
content, err := os.ReadFile(dstPath)
assert.NoError(t, err)
assert.Equal(t, tf.content, string(content), "file content should match at %s", dstPath)
// For files marked as replace=false, verify they have identical content with existing files
if !tf.replace {
for _, ef := range tt.existing {
if ef.path == tf.path {
assert.Equal(t, ef.content, string(content), "file content should not change when replace=false: %s", dstPath)
}
}
}
}
})
}
}

View File

@ -28,7 +28,7 @@ import (
// Login checks if passed gcp credentials are correct.
func Login(ctx context.Context, reg string) error {
// Check that we can find a valid token source using GCE or ApplicationDefault.
ts, err := google.DefaultTokenSource(ctx, "https://www.googleapis.com/auth/cloud-platform")
ts, err := google.DefaultTokenSource(ctx)
if err != nil {
return fmt.Errorf("wrong GCP token source, unable to find a valid source: %w", err)
}

View File

@ -31,7 +31,7 @@ import (
const TmpDirPrefix = "falcoctl-registry-push-"
// CreateTarGzArchive compresses and saves in a tar archive the passed file.
func CreateTarGzArchive(dir, path string, stripComponents bool) (file string, err error) {
func CreateTarGzArchive(dir, path string) (file string, err error) {
cleanedPath := filepath.Clean(path)
if dir == "" {
dir = TmpDirPrefix
@ -96,13 +96,13 @@ func CreateTarGzArchive(dir, path string, stripComponents bool) (file string, er
return nil
}
return copyToTarGz(path, tw, info, stripComponents)
return copyToTarGz(path, tw, info)
})
if err != nil {
return "", err
}
} else {
if err = copyToTarGz(path, tw, fInfo, stripComponents); err != nil {
if err = copyToTarGz(path, tw, fInfo); err != nil {
return "", err
}
}
@ -110,17 +110,9 @@ func CreateTarGzArchive(dir, path string, stripComponents bool) (file string, er
return outFile.Name(), err
}
func copyToTarGz(path string, tw *tar.Writer, info fs.FileInfo, stripComponents bool) error {
var headerName string
if stripComponents {
headerName = filepath.Base(path)
} else {
headerName = path
}
func copyToTarGz(path string, tw *tar.Writer, info fs.FileInfo) error {
header := &tar.Header{
Name: headerName,
Name: path,
Size: info.Size(),
Mode: int64(info.Mode()),
Typeflag: tar.TypeReg,

View File

@ -36,25 +36,25 @@ func TestCreateTarGzArchiveFile(t *testing.T) {
dir := t.TempDir()
f1, err := os.Create(filepath.Join(dir, filename1))
if err != nil {
t.Fatal(err.Error())
t.Fatalf(err.Error())
}
defer f1.Close()
tarball, err := CreateTarGzArchive(tmpPrefix, filepath.Join(dir, filename1), false)
tarball, err := CreateTarGzArchive(tmpPrefix, filepath.Join(dir, filename1))
if err != nil {
t.Fatal(err.Error())
t.Fatalf(err.Error())
}
defer os.RemoveAll(filepath.Dir(tarball))
file, err := os.Open(tarball)
if err != nil {
t.Fatal(err.Error())
t.Fatalf(err.Error())
}
paths, err := listHeaders(file)
fmt.Println(paths)
if err != nil {
t.Fatal(err.Error())
t.Fatalf(err.Error())
}
if len(paths) != 1 {
@ -67,41 +67,6 @@ func TestCreateTarGzArchiveFile(t *testing.T) {
}
}
func TestCreateTarGzArchiveFileStripComponents(t *testing.T) {
dir := t.TempDir()
f1, err := os.Create(filepath.Join(dir, filename1))
if err != nil {
t.Fatal(err.Error())
}
defer f1.Close()
tarball, err := CreateTarGzArchive(tmpPrefix, filepath.Join(dir, filename1), true)
if err != nil {
t.Fatal(err.Error())
}
defer os.RemoveAll(filepath.Dir(tarball))
file, err := os.Open(tarball)
if err != nil {
t.Fatal(err.Error())
}
paths, err := listHeaders(file)
fmt.Println(paths)
if err != nil {
t.Fatal(err.Error())
}
if len(paths) != 1 {
t.Fatalf("Expected 1 path, got %d", len(paths))
}
base := paths[0]
if base != filename1 {
t.Errorf("Expected file1, got %s", base)
}
}
func TestCreateTarGzArchiveDir(t *testing.T) {
// Test that we can compress directories
dir := t.TempDir()
@ -109,30 +74,30 @@ func TestCreateTarGzArchiveDir(t *testing.T) {
// add some files
f1, err := os.Create(filepath.Join(dir, filename1))
if err != nil {
t.Fatal(err.Error())
t.Fatalf(err.Error())
}
defer f1.Close()
f2, err := os.Create(filepath.Join(dir, filename2))
if err != nil {
t.Fatal(err.Error())
t.Fatalf(err.Error())
}
defer f2.Close()
tarball, err := CreateTarGzArchive(tmpPrefix, dir, false)
tarball, err := CreateTarGzArchive(tmpPrefix, dir)
if err != nil {
t.Fatal(err.Error())
t.Fatalf(err.Error())
}
defer os.RemoveAll(filepath.Dir(tarball))
file, err := os.Open(tarball)
if err != nil {
t.Fatal(err.Error())
t.Fatalf(err.Error())
}
defer file.Close()
paths, err := listHeaders(file)
if err != nil {
t.Fatal(err.Error())
t.Fatalf(err.Error())
}
if len(paths) != 3 {

View File

@ -0,0 +1,46 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2023 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package utils
import (
"bufio"
"os"
"strings"
"golang.org/x/term"
"github.com/falcosecurity/falcoctl/pkg/output"
)
// GetCredentials is used to retrieve username and password from standard input.
func GetCredentials(p *output.Printer) (username, password string, err error) {
reader := bufio.NewReader(os.Stdin)
p.DefaultText.Print(p.FormatTitleAsLoggerInfo("Enter username:"))
username, err = reader.ReadString('\n')
if err != nil {
return "", "", err
}
p.Logger.Info("Enter password: ")
bytePassword, err := term.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return "", "", err
}
password = string(bytePassword)
return strings.TrimSpace(username), strings.TrimSpace(password), nil
}

View File

@ -86,6 +86,7 @@ func ExtractTarGz(ctx context.Context, gzipStream io.Reader, destDir string, str
continue
}
info := header.FileInfo()
files = append(files, path)
switch header.Typeflag {
case tar.TypeDir:
@ -105,7 +106,6 @@ func ExtractTarGz(ctx context.Context, gzipStream io.Reader, destDir string, str
if err = outFile.Close(); err != nil {
return nil, err
}
files = append(files, path)
case tar.TypeLink:
name := header.Linkname
if stripPathComponents > 0 {

View File

@ -69,7 +69,7 @@ func (c *cos) customizeBuild(ctx context.Context,
}
printer.Logger.Info("COS detected, using COS kernel headers.", printer.Logger.Args("build ID", c.buildID))
bpfKernelSrcURL := fmt.Sprintf("https://storage.googleapis.com/cos-tools/%s/kernel-headers.tgz", c.buildID)
kr.Extraversion = "+"
env, err := downloadKernelSrc(ctx, printer, &kr, bpfKernelSrcURL, 0)
if err != nil {
return nil, err

View File

@ -17,6 +17,8 @@
package driverdistro
import (
"bufio"
"bytes"
"compress/gzip"
"errors"
"fmt"
@ -29,8 +31,7 @@ import (
"strings"
"github.com/docker/docker/pkg/homedir"
"github.com/falcosecurity/driverkit/cmd"
"github.com/falcosecurity/driverkit/pkg/driverbuilder"
"github.com/falcosecurity/driverkit/pkg/driverbuilder/builder"
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
"golang.org/x/net/context"
"gopkg.in/ini.v1"
@ -108,7 +109,7 @@ func getOSReleaseDistro(kr *kernelrelease.KernelRelease) (Distro, error) {
}
idKey, err := cfg.Section("").GetKey("ID")
if err != nil {
return nil, err
return nil, nil
}
id := strings.ToLower(idKey.String())
@ -129,6 +130,36 @@ func getOSReleaseDistro(kr *kernelrelease.KernelRelease) (Distro, error) {
return distro, nil
}
//nolint:gocritic // the method shall not be able to modify kr
func loadKernelHeadersFromDk(distro string, kr kernelrelease.KernelRelease) (string, func(), error) {
// Try to load kernel headers from driverkit. Don't error out if unable to.
b, err := builder.Factory(builder.Type(distro))
if err != nil {
return "", nil, nil
}
script, err := builder.KernelDownloadScript(b, nil, kr)
if err != nil {
return "", nil, err
}
script += "\necho $KERNELDIR"
out, err := exec.Command("bash", "-c", script).Output() //nolint:gosec // false positive
var path string
if err == nil {
// Scan all stdout line by line and
// store last line as KERNELDIR path.
reader := bytes.NewReader(out)
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
path = scanner.Text()
}
}
return path, func() {
_ = os.RemoveAll("/tmp/kernel-download")
_ = os.RemoveAll(path)
}, err
}
func toURL(repo, driverVer, fileName, arch string) string {
return fmt.Sprintf("%s/%s/%s/%s", repo, url.QueryEscape(driverVer), arch, fileName)
}
@ -169,13 +200,11 @@ func Build(ctx context.Context,
driverName string,
driverType drivertype.DriverType,
driverVer string,
downloadHeaders bool,
) (string, error) {
printer.Logger.Info("Trying to compile the requested driver")
driverFileName := toFilename(d, &kr, driverName, driverType)
destPath := toLocalPath(driverVer, driverFileName, kr.Architecture.ToNonDeb())
if exist, _ := utils.FileExists(destPath); exist {
return destPath, ErrAlreadyPresent
destination := toLocalPath(driverVer, driverFileName, kr.Architecture.ToNonDeb())
if exist, _ := utils.FileExists(destination); exist {
return destination, ErrAlreadyPresent
}
env, err := d.customizeBuild(ctx, printer, driverType, kr)
@ -183,54 +212,42 @@ func Build(ctx context.Context,
return "", err
}
ro, err := getDKRootOptions(d, kr, driverType, driverVer, driverName, destPath)
if env == nil {
env = make(map[string]string)
}
// If customizeBuild did not set any KERNELDIR env variable,
// try to load kernel headers urls from driverkit.
if _, found := env[drivertype.KernelDirEnv]; !found {
printer.Logger.Debug("Trying to automatically fetch kernel headers.")
// KernelVersion needs to be fixed up; it is only used by driverkit Ubuntu builder
// and we must ensure that it is correctly set.
fixedKr := d.FixupKernel(kr)
kVerFixedKr := kr
kVerFixedKr.KernelVersion = fixedKr.KernelVersion
kernelHeadersPath, cleaner, err := loadKernelHeadersFromDk(d.String(), kVerFixedKr)
if cleaner != nil {
defer cleaner()
}
if err == nil {
printer.Logger.Debug("Downloaded and extracted kernel headers.", printer.Logger.Args("path", kernelHeadersPath))
env[drivertype.KernelDirEnv] = kernelHeadersPath
}
}
path, err := driverType.Build(ctx, printer, kr, driverName, driverVer, env)
if err != nil {
return "", err
}
// Disable automatic kernel headers fetching
// if customizeBuild already retrieved kernel headers for us
// (and has set the KernelDirEnv key)
if _, ok := env[drivertype.KernelDirEnv]; ok {
downloadHeaders = false
}
srcPath := fmt.Sprintf("/usr/src/%s-%s", driverName, driverVer)
err = driverbuilder.NewLocalBuildProcessor(true, downloadHeaders, true, srcPath, env, 1000).Start(ro.ToBuild(printer))
return destPath, err
}
//nolint:gocritic // the method shall not be able to modify kr
func getDKRootOptions(d Distro,
kr kernelrelease.KernelRelease,
driverType drivertype.DriverType,
driverVer,
driverName,
destPath string,
) (*cmd.RootOptions, error) {
ro, err := cmd.NewRootOptions()
// Copy the path to the expected location.
// NOTE: for kmod, this is not useful since the driver will
// be loaded directly by dkms.
printer.Logger.Info("Copying built driver to its destination.", printer.Logger.Args("src", path, "dst", destination))
f, err := os.Open(filepath.Clean(path))
if err != nil {
return nil, err
return "", err
}
ro.Architecture = kr.Architecture.String()
ro.DriverVersion = driverVer
// We pass just the fixed kernelversion down to driverkit.
// it is only used by ubuntu builder,
// all the other builders do not use the kernelversion field.
fixedKr := d.FixupKernel(kr)
ro.KernelVersion = fixedKr.KernelVersion
ro.ModuleDriverName = driverName
ro.ModuleDeviceName = driverName
ro.KernelRelease = kr.String()
ro.Target = d.String()
ro.Output = driverType.ToOutput(destPath)
// This should never happen since both kmod and bpf implement ToOutput;
// the only case this can happen is if a Build is requested for modern-bpf driver type,
// But "install" cmd is smart enough to avoid that situation
// by using HasArtifacts() method.
if !ro.Output.HasOutputs() {
return nil, errors.New("build on non-artifacts driver attempted")
}
return ro, nil
return destination, copyDataToLocalPath(destination, f)
}
// Download will try to download drivers for a distro trying specified repos.

View File

@ -50,7 +50,7 @@ func TestDiscoverDistro(t *testing.T) {
return nil
},
postFn: func() {},
distroExpected: &generic{},
distroExpected: nil,
errExpected: true,
},
{
@ -63,8 +63,8 @@ func TestDiscoverDistro(t *testing.T) {
postFn: func() {
_ = os.Remove(osReleaseFile)
},
distroExpected: &generic{},
errExpected: true,
distroExpected: nil,
errExpected: false,
},
{
// os-release ID "foo" mapped to generic
@ -167,28 +167,6 @@ func TestDiscoverDistro(t *testing.T) {
distroExpected: &bottlerocket{},
errExpected: false,
},
{
// os-release ID "ol" maps to oracle
krInput: "5.10.0-2047.510.5.5.el7uek.x86_64",
preFn: func() error {
type brCfg struct {
OsID string `ini:"ID"`
}
f := ini.Empty()
err := f.ReflectFrom(&brCfg{
OsID: "ol",
})
if err != nil {
return err
}
return f.SaveTo(osReleaseFile)
},
postFn: func() {
_ = os.Remove(osReleaseFile)
},
distroExpected: &ol{},
errExpected: false,
},
{
// No os-release but "centos-release" file present maps to centos
krInput: "5.10.0",
@ -228,8 +206,9 @@ func TestDiscoverDistro(t *testing.T) {
d, err := Discover(kr, localHostRoot)
if tCase.errExpected {
assert.Error(t, err)
} else {
assert.IsType(t, tCase.distroExpected, d)
}
assert.IsType(t, tCase.distroExpected, d)
tCase.postFn()
}
}

View File

@ -1,46 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2024 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package driverdistro
import (
"strings"
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
drivertype "github.com/falcosecurity/falcoctl/pkg/driver/type"
)
func init() {
distros["ol"] = &ol{generic: &generic{}}
}
type ol struct {
*generic
}
//nolint:gocritic // the method shall not be able to modify kr
func (o *ol) PreferredDriver(kr kernelrelease.KernelRelease, allowedDriverTypes []drivertype.DriverType) drivertype.DriverType {
for _, allowedDrvType := range allowedDriverTypes {
// Skip dkms on UEK hosts because it will always fail
if allowedDrvType.String() == drivertype.TypeKmod && strings.Contains(kr.String(), "uek") {
continue
}
if allowedDrvType.Supported(kr) {
return allowedDrvType
}
}
return nil
}

View File

@ -54,7 +54,11 @@ func (u *ubuntu) FixupKernel(kr kernelrelease.KernelRelease) kernelrelease.Kerne
// from the following `uname -v` result
// `#26~22.04.1-Ubuntu SMP Mon Apr 24 01:58:15 UTC 2023`
// we obtain the kernelversion`26~22.04.1`.
// Another example: "#1 SMP PREEMPT_DYNAMIC Tue, 10 Oct 2023 21:10:21 +0000" and return "1".
// NOTE: driverkernel.FetchInfo() already trims leading "#"
// and everything starting from the first whitespace,
// so that eg: we receive "26~22.04.1-Ubuntu",
// therefore we only need to drop "-Ubuntu" suffix
// Take eg: "#1 SMP PREEMPT_DYNAMIC Tue, 10 Oct 2023 21:10:21 +0000" and return "1".
kv := strings.TrimLeft(kr.KernelVersion, "#")
kv = strings.Split(kv, " ")[0]
kr.KernelVersion = strings.TrimSuffix(kv, "-Ubuntu")

View File

@ -18,15 +18,21 @@ package drivertype
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"github.com/docker/docker/pkg/homedir"
"github.com/falcosecurity/driverkit/cmd"
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
"golang.org/x/net/context"
"k8s.io/utils/mount"
"github.com/falcosecurity/falcoctl/internal/utils"
"github.com/falcosecurity/falcoctl/pkg/output"
)
// TypeBpf is the string for the bpf driver type.
const TypeBpf = "ebpf"
func init() {
driverTypes[TypeBpf] = &bpf{}
}
@ -72,8 +78,46 @@ func (b *bpf) Supported(kr kernelrelease.KernelRelease) bool {
return kr.SupportsProbe()
}
func (b *bpf) ToOutput(destPath string) cmd.OutputOptions {
return cmd.OutputOptions{
Probe: destPath,
//nolint:gocritic // the method shall not be able to modify kr
func (b *bpf) Build(ctx context.Context,
printer *output.Printer,
_ kernelrelease.KernelRelease,
driverName, driverVersion string,
env map[string]string,
) (string, error) {
// We don't fail if this fails; let's try to build a probe anyway.
_ = mountKernelDebug(printer)
srcPath := fmt.Sprintf("/usr/src/%s-%s/bpf", driverName, driverVersion)
makeCmdArgs := fmt.Sprintf(`make -C %q`, filepath.Clean(srcPath))
makeCmd := exec.CommandContext(ctx, "bash", "-c", makeCmdArgs) //nolint:gosec // false positive
// Append requested env variables to the command env
makeCmd.Env = os.Environ()
for key, val := range env {
makeCmd.Env = append(makeCmd.Env, fmt.Sprintf("%s=%s", key, val))
}
out, err := makeCmd.CombinedOutput()
if err != nil {
printer.DefaultText.Print(string(out))
}
outProbe := fmt.Sprintf("%s/probe.o", srcPath)
return outProbe, err
}
func mountKernelDebug(printer *output.Printer) error {
// Mount /sys/kernel/debug that is needed on old (pre 4.17) kernel releases,
// since these releases still did not support raw tracepoints.
// BPF_PROG_TYPE_RAW_TRACEPOINT was introduced in 4.17 indeed:
// https://github.com/torvalds/linux/commit/c4f6699dfcb8558d138fe838f741b2c10f416cf9
exists, _ := utils.FileExists("/sys/kernel/debug/tracing")
if exists {
return nil
}
printer.Logger.Info("Mounting debugfs for bpf driver.")
mounter := mount.New("/bin/mount")
err := mounter.Mount("debugfs", "/sys/kernel/debug", "debugfs", []string{"nodev"})
if err != nil {
printer.Logger.Warn("Failed to mount debugfs.", printer.Logger.Args("err", err))
}
return err
}

View File

@ -1,26 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2025 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package drivertype
const (
// TypeKmod is the string for the kernel module driver type.
TypeKmod = "kmod"
// TypeModernBpf is the string for the modern bpf driver type.
TypeModernBpf = "modern_ebpf"
// TypeBpf is the string for the bpf driver type.
TypeBpf = "ebpf"
)

View File

@ -19,17 +19,21 @@ import (
"bufio"
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/falcosecurity/driverkit/cmd"
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
"golang.org/x/net/context"
"github.com/falcosecurity/falcoctl/pkg/output"
)
const (
// TypeKmod is the string for the bpf driver type.
TypeKmod = "kmod"
maxRmmodWait = 10
rmmodWaitTime = 5 * time.Second
)
@ -158,8 +162,96 @@ func (k *kmod) Supported(kr kernelrelease.KernelRelease) bool {
return kr.SupportsModule()
}
func (k *kmod) ToOutput(destPath string) cmd.OutputOptions {
return cmd.OutputOptions{
Module: destPath,
func createDKMSMakeFile(gcc string) error {
file, err := os.OpenFile("/tmp/falco-dkms-make", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o777) //nolint:gosec // we need the file to be executable
if err != nil {
return err
}
defer file.Close()
_, err = fmt.Fprintln(file, "#!/usr/bin/env bash")
if err == nil {
_, err = fmt.Fprintln(file, `make CC=`+gcc+` $@`)
}
return err
}
//nolint:gocritic // the method shall not be able to modify kr
func (k *kmod) Build(ctx context.Context,
printer *output.Printer,
kr kernelrelease.KernelRelease,
driverName, driverVersion string,
env map[string]string,
) (string, error) {
// Skip dkms on UEK hosts because it will always fail
if strings.Contains(kr.String(), "uek") {
printer.Logger.Warn("Skipping because the dkms install always fail (on UEK hosts).")
return "", fmt.Errorf("unsupported on uek hosts")
}
out, err := exec.Command("which", "gcc").Output()
if err != nil {
return "", err
}
gccDir := filepath.Dir(string(out))
gccs, err := filepath.Glob(gccDir + "/gcc*")
if err != nil {
return "", err
}
for _, gcc := range gccs {
// Filter away gcc-{ar,nm,...}
// Only gcc compiler has `-print-search-dirs` option.
gccSearchArgs := fmt.Sprintf(`%s -print-search-dirs 2>&1 | grep "install:"`, gcc)
_, err = exec.Command("bash", "-c", gccSearchArgs).Output() //nolint:gosec // false positive
if err != nil {
continue
}
printer.Logger.Info("Trying to dkms install module.", printer.Logger.Args("gcc", gcc))
err = createDKMSMakeFile(gcc)
if err != nil {
printer.Logger.Warn("Could not fill /tmp/falco-dkms-make content.")
continue
}
var dkmsCmdArgs string
if kernelDir, found := env[KernelDirEnv]; found {
dkmsCmdArgs = fmt.Sprintf(`dkms install --kernelsourcedir %q --directive="MAKE='/tmp/falco-dkms-make'" -m %q -v %q -k %q --verbose`,
kernelDir, driverName, driverVersion, kr.String())
} else {
dkmsCmdArgs = fmt.Sprintf(`dkms install --directive="MAKE='/tmp/falco-dkms-make'" -m %q -v %q -k %q --verbose`,
driverName, driverVersion, kr.String())
}
// Try the build through dkms
out, err = exec.CommandContext(ctx, "bash", "-c", dkmsCmdArgs).CombinedOutput() //nolint:gosec // false positive
if err == nil {
koGlob := fmt.Sprintf("/var/lib/dkms/%s/%s/%s/%s/module/%s", driverName, driverVersion, kr.String(), kr.Architecture.ToNonDeb(), driverName)
var koFiles []string
koFiles, err = filepath.Glob(koGlob + ".*")
if err != nil || len(koFiles) == 0 {
printer.Logger.Warn("Module file not found.")
continue
}
koFile := koFiles[0]
printer.Logger.Info("Module installed in dkms.", printer.Logger.Args("file", koFile))
return koFile, nil
}
printer.DefaultText.Print(string(out))
dkmsLogFile := fmt.Sprintf("/var/lib/dkms/%s/%s/build/make.log", driverName, driverVersion)
logs, err := os.ReadFile(filepath.Clean(dkmsLogFile))
if err != nil {
printer.Logger.Warn("Running dkms build failed, couldn't find dkms log", printer.Logger.Args("file", dkmsLogFile))
} else {
printer.Logger.Warn("Running dkms build failed. Dumping dkms log.", printer.Logger.Args("file", dkmsLogFile))
logBuf := bytes.NewBuffer(logs)
scanner := bufio.NewScanner(logBuf)
for scanner.Scan() {
m := scanner.Text()
printer.DefaultText.Println(m)
}
}
}
return "", fmt.Errorf("failed to compile the module")
}

View File

@ -13,21 +13,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build linux
package drivertype
import (
_ "unsafe" // Needed for go:linkname to be able to access a private function from cilium/ebpf/features.
// Needed for go:linkname to be able to access a private function from cilium/ebpf/features.
_ "unsafe"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/features"
"github.com/falcosecurity/driverkit/cmd"
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
"golang.org/x/net/context"
"github.com/falcosecurity/falcoctl/pkg/output"
)
// TypeModernBpf is the string for the bpf driver type.
const TypeModernBpf = "modern_ebpf"
func init() {
driverTypes[TypeModernBpf] = &modernBpf{}
}
@ -79,6 +81,7 @@ func (m *modernBpf) Supported(_ kernelrelease.KernelRelease) bool {
return features.HaveMapType(ebpf.RingBuf) == nil
}
func (m *modernBpf) ToOutput(_ string) cmd.OutputOptions {
return cmd.OutputOptions{}
//nolint:gocritic // the method shall not be able to modify kr
func (m *modernBpf) Build(_ context.Context, _ *output.Printer, _ kernelrelease.KernelRelease, _, _ string, _ map[string]string) (string, error) {
return "", nil
}

View File

@ -19,8 +19,8 @@ package drivertype
import (
"fmt"
"github.com/falcosecurity/driverkit/cmd"
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
"golang.org/x/net/context"
"github.com/falcosecurity/falcoctl/pkg/output"
)
@ -37,7 +37,8 @@ type DriverType interface {
Load(printer *output.Printer, src, driverName string, fallback bool) error
Extension() string
HasArtifacts() bool
ToOutput(destPath string) cmd.OutputOptions
Build(ctx context.Context, printer *output.Printer, kr kernelrelease.KernelRelease,
driverName, driverVersion string, env map[string]string) (string, error)
Supported(kr kernelrelease.KernelRelease) bool
}

View File

@ -1,17 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2024 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package enum implements the generic logic to manage enum values for Cobra.
package enum

View File

@ -22,10 +22,8 @@ import (
"strings"
"github.com/falcosecurity/falcoctl/pkg/index/config"
"github.com/falcosecurity/falcoctl/pkg/index/fetch/file"
"github.com/falcosecurity/falcoctl/pkg/index/fetch/gcs"
"github.com/falcosecurity/falcoctl/pkg/index/fetch/http"
"github.com/falcosecurity/falcoctl/pkg/index/fetch/s3"
"github.com/falcosecurity/falcoctl/pkg/index/index"
)
@ -48,15 +46,11 @@ func NewFetcher() *Fetcher {
"http": http.Fetch,
"https": http.Fetch,
"gcs": gcs.Fetch,
"file": file.Fetch,
"s3": s3.Fetch,
},
schemeDefaultBackends: map[string]string{
"http": "http",
"https": "https",
"gs": "gcs",
"file": "file",
"s3": "s3",
},
}
}

View File

@ -1,17 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2024 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package file implements all the logic for fetching indexes from the local file system.
package file

View File

@ -1,41 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2024 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package file
import (
"context"
"fmt"
pkgurl "net/url"
"os"
"github.com/falcosecurity/falcoctl/pkg/index/config"
)
// Fetch fetches the raw index file from the local file system.
func Fetch(_ context.Context, conf *config.Entry) ([]byte, error) {
// Expect URL to be file:///some/directory/filename.yaml
url, err := pkgurl.Parse(conf.URL)
if err != nil {
return nil, fmt.Errorf("cannot parse URL: %w", err)
}
data, err := os.ReadFile(url.Path)
if err != nil {
return nil, fmt.Errorf("reading file: %w", err)
}
return data, nil
}

View File

@ -1,96 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2024 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package file
import (
"context"
"fmt"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"github.com/falcosecurity/falcoctl/pkg/index/config"
"github.com/falcosecurity/falcoctl/pkg/index/index"
)
func TestFileFetchWithValidFile(t *testing.T) {
filename := "TestFileFetchWithValidFile-filename.yaml"
entries := []index.Entry{{
Name: "test",
Type: "rulesfile",
Registry: "test.io",
Repository: "test",
Maintainers: index.Maintainer{
{
Email: "test@local",
Name: "test",
},
},
Sources: []string{"/test"},
Keywords: []string{"test"},
}}
ctx := context.Background()
configDir := t.TempDir()
configFile := filepath.Join(configDir, filename)
entryBytes, err := yaml.Marshal(entries)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
err = os.WriteFile(configFile, entryBytes, os.FileMode(0o644))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
b, err := Fetch(ctx, &config.Entry{
Name: "test",
URL: fmt.Sprintf("file://%s/%s", configDir, filename),
Backend: "GCS",
})
assert.NoError(t, err, "error should not occur")
assert.NotNil(t, b, "returned bytes should not be nil")
var resultEntries []index.Entry
err = yaml.Unmarshal(b, &resultEntries)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
assert.Equal(t, entries, resultEntries)
}
func TestFileFetchWithNonExistentFile(t *testing.T) {
filename := "TestFileFetchWithNonExistentFile-filename.yaml"
ctx := context.Background()
configDir := t.TempDir()
// We intentionally do not write out the file here
_, err := Fetch(ctx, &config.Entry{
Name: "test",
URL: fmt.Sprintf("file://%s/%s", configDir, filename),
Backend: "GCS",
})
expectedError := fmt.Sprintf("reading file: open %s/%s: no such file or directory", configDir, filename)
assert.EqualError(t, err, expectedError)
}

View File

@ -1,17 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2024 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package s3 implements all the logic for fetching indexes from AWS S3.
package s3

View File

@ -1,63 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2024 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package s3
import (
"context"
"fmt"
"io"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
indexConfig "github.com/falcosecurity/falcoctl/pkg/index/config"
)
// Fetch fetches the raw index file from an S3 object.
func Fetch(ctx context.Context, conf *indexConfig.Entry) ([]byte, error) {
o, err := s3ObjectFromURI(conf.URL)
if err != nil {
return nil, err
}
// Create a new AWS config
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
// handle error
return nil, fmt.Errorf("unable to create AWS config: %w", err)
}
svc := s3.NewFromConfig(cfg)
// Get the object from S3
res, err := svc.GetObject(ctx, &s3.GetObjectInput{
Bucket: aws.String(o.Bucket),
Key: aws.String(o.Key),
})
if err != nil {
return nil, fmt.Errorf("unable to get S3 object: %w", err)
}
defer res.Body.Close()
// Read the object data
bytes, err := io.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("error reading S3 object: %w", err)
}
return bytes, nil
}

View File

@ -1,55 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2024 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package s3
import (
"fmt"
"net/url"
"strings"
)
const s3Scheme = "s3"
// s3Object represents an S3 object with its bucket and key.
type s3Object struct {
Bucket string
Key string
}
// s3ObjectFromURI parses S3 URIs (s3://<bucket>/<object>) and returns a s3Object.
func s3ObjectFromURI(uri string) (*s3Object, error) {
parsedURI, err := url.Parse(uri)
if err != nil {
return nil, fmt.Errorf("unable to parse URI: %w", err)
}
if !strings.EqualFold(parsedURI.Scheme, s3Scheme) {
return nil, fmt.Errorf("invalid S3 URI: scheme should be '%s' but got '%s'", s3Scheme, parsedURI.Scheme)
}
if parsedURI.Host == "" {
return nil, fmt.Errorf("invalid S3 URI: missing bucket name")
}
if parsedURI.Path == "" {
return nil, fmt.Errorf("invalid S3 URI: missing object name")
}
return &s3Object{
Bucket: parsedURI.Host,
Key: parsedURI.Path[1:], // Remove the leading slash
}, nil
}

View File

@ -55,7 +55,7 @@ func GCPCredential(ctx context.Context, reg string) (auth.Credential, error) {
// load saved tokenSource or saves it
if SavedTokenSource == nil {
tokenSource, err = google.DefaultTokenSource(ctx, "https://www.googleapis.com/auth/cloud-platform")
tokenSource, err = google.DefaultTokenSource(ctx)
if err != nil {
return auth.EmptyCredential, fmt.Errorf("error while trying to identify a GCP TokenSource %w", err)
}

View File

@ -64,7 +64,7 @@ func WithPlainHTTP(plainHTTP bool) func(r *Repository) {
func (r *Repository) Tags(ctx context.Context) ([]string, error) {
var result []string
var tagRetriever = func(tags []string) error {
result = append(result, tags...)
result = tags
return nil
}

View File

@ -88,8 +88,8 @@ func HumanReadableMediaType(s string) string {
return string(Asset)
}
// If we do not have a match for a well known mediaType then we return the original mediaType.
return s
// should never happen
return ""
}
// ArtifactTypeSlice is a slice of ArtifactType, can be passed as comma separated values.

View File

@ -35,7 +35,6 @@ type Artifact struct {
Dependencies []string
Requirements []string
Tags []string
AutoFloatingTags bool
AnnotationSource string
}
@ -65,9 +64,6 @@ func (art *Artifact) AddFlags(cmd *cobra.Command) error {
cmd.Flags().StringArrayVarP(&art.Tags, "tag", "t", nil,
"additional artifact tag. Can be repeated multiple times")
cmd.Flags().BoolVar(&art.AutoFloatingTags, "add-floating-tags", false,
"add the floating tags for the major and minor versions")
cmd.Flags().Var(&art.ArtifactType, "type",
`type of artifact to be pushed. Allowed values: "rulesfile", "plugin", "asset"`)
if err := cmd.MarkFlagRequired("type"); err != nil {

View File

@ -46,15 +46,15 @@ type Common struct {
// IndexCache caches the entries for the configured indexes.
IndexCache *cache.Cache
logLevel *output.LogLevel
logFormat *output.LogFormat
logLevel *LogLevel
logFormat *LogFormat
}
// NewOptions returns a new Common struct.
func NewOptions() *Common {
return &Common{
logLevel: output.NewLogLevel(),
logFormat: output.NewLogFormat(),
logLevel: NewLogLevel(),
logFormat: NewLogFormat(),
}
}

View File

@ -27,12 +27,11 @@ import (
"github.com/falcosecurity/falcoctl/internal/config"
driverdistro "github.com/falcosecurity/falcoctl/pkg/driver/distro"
drivertype "github.com/falcosecurity/falcoctl/pkg/driver/type"
"github.com/falcosecurity/falcoctl/pkg/enum"
)
// DriverTypes data structure for driver types.
type DriverTypes struct {
*enum.Enum
*Enum
}
// NewDriverTypes returns a new Enum configured for the driver types.
@ -42,7 +41,7 @@ func NewDriverTypes() *DriverTypes {
return &DriverTypes{
// Default value is not used.
// This enum is only used to print allowed values.
Enum: enum.NewEnum(types, drivertype.TypeModernBpf),
Enum: NewEnum(types, drivertype.TypeModernBpf),
}
}

View File

@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package enum
package options
import (
"fmt"
@ -26,21 +26,21 @@ import (
// can have a limited set of values.
type Enum struct {
allowed []string
Value string
value string
}
// NewEnum returns an enum struct. The firs argument is a set of values allowed for the flag.
// The second argument is the default Value of the flag.
// The second argument is the default value of the flag.
func NewEnum(allowed []string, d string) *Enum {
return &Enum{
allowed: allowed,
Value: d,
value: d,
}
}
// String returns the Value.
// String returns the value.
func (e *Enum) String() string {
return e.Value
return e.value
}
// Allowed returns the list of allowed values enclosed in parenthesis.
@ -48,12 +48,12 @@ func (e *Enum) Allowed() string {
return fmt.Sprintf("(%s)", strings.Join(e.allowed, ", "))
}
// Set the Value for the flag.
// Set the value for the flag.
func (e *Enum) Set(p string) error {
if !slices.Contains(e.allowed, p) {
return fmt.Errorf("invalid argument %q, please provide one of (%s)", p, strings.Join(e.allowed, ", "))
}
e.Value = p
e.value = p
return nil
}

View File

@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package enum
package options
import (
. "github.com/onsi/ginkgo/v2"
@ -41,7 +41,7 @@ var _ = Describe("Enum", func() {
})
It("should set the default values", func() {
Expect(enum.Value).Should(Equal(defValue))
Expect(enum.value).Should(Equal(defValue))
})
It("should set the allowed values", func() {
@ -64,9 +64,9 @@ var _ = Describe("Enum", func() {
val = newVal
})
It("Should set the correct value", func() {
It("Should set the correct val", func() {
Expect(err).ShouldNot(HaveOccurred())
Expect(enum.Value).Should(Equal(newVal))
Expect(enum.value).Should(Equal(newVal))
})
})

View File

@ -13,12 +13,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package output
package options
import (
"github.com/pterm/pterm"
"github.com/falcosecurity/falcoctl/pkg/enum"
)
const (
@ -34,13 +32,13 @@ var logFormats = []string{LogFormatColor, LogFormatText, LogFormatJSON}
// LogFormat data structure for log-format flag.
type LogFormat struct {
*enum.Enum
*Enum
}
// NewLogFormat returns a new Enum configured for the log formats flag.
func NewLogFormat() *LogFormat {
return &LogFormat{
Enum: enum.NewEnum(logFormats, LogFormatColor),
Enum: NewEnum(logFormats, LogFormatColor),
}
}
@ -48,9 +46,8 @@ func NewLogFormat() *LogFormat {
func (lg *LogFormat) ToPtermFormatter() pterm.LogFormatter {
var formatter pterm.LogFormatter
switch lg.Value {
switch lg.value {
case LogFormatColor:
pterm.EnableColor()
formatter = pterm.LogFormatterColorful
case LogFormatText:
pterm.DisableColor()

View File

@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package output
package options
import (
"github.com/gookit/color"
@ -33,8 +33,8 @@ var _ = Describe("LogFormat", func() {
Context("NewLogFormat Func", func() {
It("should return a new logFormatter", func() {
Expect(logFormatter).ShouldNot(BeNil())
Expect(logFormatter.Value).Should(Equal(LogFormatColor))
Expect(logFormatter.Allowed()).Should(Equal("(color, text, json)"))
Expect(logFormatter.value).Should(Equal(LogFormatColor))
Expect(logFormatter.allowed).Should(Equal(logFormats))
})
})

View File

@ -13,12 +13,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package output
package options
import (
"github.com/pterm/pterm"
"github.com/falcosecurity/falcoctl/pkg/enum"
)
const (
@ -36,20 +34,20 @@ var logLevels = []string{LogLevelInfo, LogLevelWarn, LogLevelDebug, LogLevelTrac
// LogLevel data structure for log-level flag.
type LogLevel struct {
*enum.Enum
*Enum
}
// NewLogLevel returns a new Enum configured for the log level flag.
func NewLogLevel() *LogLevel {
return &LogLevel{
Enum: enum.NewEnum(logLevels, LogLevelInfo),
Enum: NewEnum(logLevels, LogLevelInfo),
}
}
// ToPtermLogLevel converts the current log level to pterm.LogLevel.
func (ll *LogLevel) ToPtermLogLevel() pterm.LogLevel {
var level pterm.LogLevel
switch ll.Value {
switch ll.value {
case LogLevelInfo:
level = pterm.LogLevelInfo
case LogLevelWarn:

View File

@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package output
package options
import (
. "github.com/onsi/ginkgo/v2"
@ -32,8 +32,8 @@ var _ = Describe("LogLevel", func() {
Context("NewLogLevel Func", func() {
It("should return a new LogLevel", func() {
Expect(logLevel).ShouldNot(BeNil())
Expect(logLevel.Value).Should(Equal(LogLevelInfo))
Expect(logLevel.Allowed()).Should(Equal("(info, warn, debug, trace)"))
Expect(logLevel.value).Should(Equal(LogLevelInfo))
Expect(logLevel.allowed).Should(Equal(logLevels))
})
})

View File

@ -43,7 +43,7 @@ import (
"github.com/go-oauth2/oauth2/v4/models"
"github.com/go-oauth2/oauth2/v4/server"
"github.com/go-oauth2/oauth2/v4/store"
"github.com/golang-jwt/jwt/v5"
"github.com/golang-jwt/jwt"
)
// RegistryTLSConfig maintains all certificate informations.