Compare commits
358 Commits
v0.7.0-rc1
...
main
Author | SHA1 | Date |
---|---|---|
|
9f30cce152 | |
|
090d95418e | |
|
6a25fa9f5a | |
|
ffb6e688e8 | |
|
a7b6cc6b6b | |
|
fd12beb1cd | |
|
d3c99d5dfc | |
|
6d9b57671f | |
|
54c47d097f | |
|
7a94528218 | |
|
876dcf4653 | |
|
581cbbc316 | |
|
b26c309f4d | |
|
15dd8fde15 | |
|
abdf330e55 | |
|
2b06411214 | |
|
295c633684 | |
|
bccca00d90 | |
|
54ef1cbbf1 | |
|
464a8a3cf9 | |
|
e632903105 | |
|
48a247e521 | |
|
47ad8d0e29 | |
|
3bf89dd0c9 | |
|
bb64751b9f | |
|
1765e1d326 | |
|
a6dcad415c | |
|
cead711238 | |
|
33c01252f6 | |
|
537ebcf446 | |
|
918e88464d | |
|
4bfeb0b0b5 | |
|
dfa4c583b1 | |
|
7d5aee8bb2 | |
|
0bce031350 | |
|
84d7fc852e | |
|
a420868063 | |
|
dabadcec4f | |
|
68484cd32b | |
|
3319e217d0 | |
|
99e76506f0 | |
|
4da81510d1 | |
|
1a71698e3f | |
|
94b56ea805 | |
|
ec3025e878 | |
|
6c71d1551e | |
|
d715341096 | |
|
335e365a78 | |
|
946d8f7b05 | |
|
088e2b45ec | |
|
6bcfad674e | |
|
f9734bb752 | |
|
dc7634cc28 | |
|
e71898ce53 | |
|
8a4febabe5 | |
|
cd992ad99c | |
|
730f80f471 | |
|
18c4322102 | |
|
769a3e5683 | |
|
38f2b1d703 | |
|
ba2730f048 | |
|
b6e47e6ea5 | |
|
84edb08963 | |
|
6bcda9881d | |
|
186b281e71 | |
|
2b11bc95df | |
|
c1ac24469d | |
|
bbdf8ac2f2 | |
|
310a119170 | |
|
f222cf4174 | |
|
af1ebe264b | |
|
70dda77939 | |
|
40b9e11683 | |
|
be7d5c2a55 | |
|
6f64f37774 | |
|
9dafcec9af | |
|
6f241ae803 | |
|
5bf4186068 | |
|
98c4183dd5 | |
|
9f360e12dd | |
|
a40d97f766 | |
|
cdd984dbf2 | |
|
61d9c71d96 | |
|
5776706e3c | |
|
46a22251cd | |
|
28ccc66497 | |
|
4599fe26b8 | |
|
a06072bd76 | |
|
3967730442 | |
|
076aef28c1 | |
|
cb7fd150ba | |
|
afaa782db6 | |
|
cc97e5ebaa | |
|
c29cad76b0 | |
|
2604fe2228 | |
|
1ac62368af | |
|
063491c111 | |
|
5846155f97 | |
|
131abecc4b | |
|
d35acb1b98 | |
|
2e87813a52 | |
|
eb9e67af1f | |
|
029726ec5d | |
|
ad33f00b56 | |
|
23094128e8 | |
|
3d077a8cac | |
|
503961ae91 | |
|
b041a7c6df | |
|
c3cd34939b | |
|
fb0a65a113 | |
|
f737d5f5ac | |
|
59245fdd66 | |
|
d6b1cfbff3 | |
|
f5722b9482 | |
|
7c57d3ee69 | |
|
fac6f31c61 | |
|
133952e1a6 | |
|
1f19a08b6a | |
|
c3a8850de6 | |
|
ebafd49a81 | |
|
3662765ac3 | |
|
46a47e0ef0 | |
|
3b3b535632 | |
|
fad320efff | |
|
7a741b57a9 | |
|
11b95ab75f | |
|
b00c0bd462 | |
|
4f562a2884 | |
|
7e06ca9fe2 | |
|
0a0cd490c5 | |
|
4fce8b537b | |
|
9c510af202 | |
|
db496e591d | |
|
27d569627e | |
|
ce6e1eef36 | |
|
e0d71e7da4 | |
|
7e6b79e9bd | |
|
fcf4def293 | |
|
1632a2d4f7 | |
|
7e05ec1b73 | |
|
006301cd5a | |
|
d1a3559878 | |
|
1a5aee911f | |
|
45af0b205a | |
|
73bfdb5b56 | |
|
cfe101856a | |
|
2dcedc0511 | |
|
d2a010445f | |
|
36b951fff3 | |
|
228cb06d4f | |
|
f55a5e6a06 | |
|
fc648dcf0e | |
|
8c56a3c85c | |
|
1f8d186326 | |
|
913d8b44eb | |
|
ee92cbf49b | |
|
150ef391f5 | |
|
76be634364 | |
|
096b8eaac2 | |
|
d6dd5cdf2f | |
|
11182eb1b8 | |
|
1b6596b5f2 | |
|
dbf56c0a2c | |
|
f5ebba87f8 | |
|
cd5ac2c073 | |
|
d7d61f4a55 | |
|
eb8fbb741e | |
|
9d429de90d | |
|
f82978f817 | |
|
ab0bebd8b3 | |
|
0d663fc1c6 | |
|
219f3d7955 | |
|
6c5f43833f | |
|
72e7129727 | |
|
addd0cd577 | |
|
5cb609d1f9 | |
|
048e8a1375 | |
|
dd7b7169a4 | |
|
5af9b5290a | |
|
655f0ea1d4 | |
|
6b4ddd46eb | |
|
ae5a30be6b | |
|
da932e4bc8 | |
|
8ec7466b1c | |
|
d3f4b8c902 | |
|
6b870bc5ed | |
|
04abd653ef | |
|
8fb4c37130 | |
|
f660a9bca0 | |
|
9584f0e57a | |
|
ffbdac3d85 | |
|
352ceeb019 | |
|
ff7bc7817e | |
|
0469ee9353 | |
|
30429211f5 | |
|
7982692c6d | |
|
d0523cae9d | |
|
00d41694af | |
|
bf405ec0d1 | |
|
d772260f9b | |
|
67e2bb4e28 | |
|
2524b16440 | |
|
b98eff4e7a | |
|
cce5b80106 | |
|
64f00cd2dd | |
|
ea3fe222cc | |
|
058f959467 | |
|
913962c6cf | |
|
aaf3c4d778 | |
|
27c653f9e5 | |
|
da9865e209 | |
|
d2be4ab6b8 | |
|
c30089c970 | |
|
5bb003a24b | |
|
72bc9dd259 | |
|
d26a3eded2 | |
|
10cb99c4c9 | |
|
aefeac7221 | |
|
24c44967e8 | |
|
6de519341a | |
|
38ea1a3aea | |
|
390edc9e54 | |
|
f6b76de25c | |
|
da016311a5 | |
|
c36c065856 | |
|
f7bcb4af5c | |
|
ecf420f9f7 | |
|
5a9cc2909c | |
|
e5bc056ce6 | |
|
72decef12a | |
|
7c36297edf | |
|
363047407e | |
|
3e10f205ef | |
|
f0827ad844 | |
|
c73bac5113 | |
|
358abeb70a | |
|
75a2e12512 | |
|
5d5b16f3fd | |
|
7783ee535c | |
|
d94e19ec24 | |
|
388b8b02ba | |
|
0296002808 | |
|
4e3c510ed2 | |
|
1bda127c79 | |
|
c81c1b8c8b | |
|
1c8c697051 | |
|
a46a849389 | |
|
a1e10ab84a | |
|
92188837b5 | |
|
7a2d69e2e3 | |
|
064c201df3 | |
|
e596dea6bb | |
|
8f98b0db57 | |
|
d5539af528 | |
|
1c6ab282db | |
|
c2dead34f1 | |
|
72a5d38754 | |
|
a89aa89890 | |
|
cf958dc2d9 | |
|
7977184710 | |
|
18982d3434 | |
|
7667649a2e | |
|
8ec25c9287 | |
|
1d9d19d833 | |
|
0532e71ccd | |
|
580d49884a | |
|
46a3602fcf | |
|
1673558c55 | |
|
b6b799bc96 | |
|
477f2c3786 | |
|
d73a859e62 | |
|
fb82fec00c | |
|
82c2307d1a | |
|
d1f70386ea | |
|
e6665cd8fc | |
|
379832c738 | |
|
803c08e374 | |
|
816f79cf1e | |
|
4dd3f2a631 | |
|
c95c65af00 | |
|
be3ce60a75 | |
|
2d0a52a967 | |
|
3575a81966 | |
|
1b23e3f4f7 | |
|
9163a85547 | |
|
cf773bcfc0 | |
|
b9d5516f68 | |
|
6e39c4665f | |
|
ad38eaf8f3 | |
|
15b4ff9017 | |
|
f2a5a9060e | |
|
e005be04e7 | |
|
5d5632c5f0 | |
|
85f1edd708 | |
|
c62ffbd24f | |
|
e827ba1cee | |
|
ac15f50d35 | |
|
2ed594744c | |
|
0763d7d1bb | |
|
f5a8d65b88 | |
|
8859d1e625 | |
|
4e745c1813 | |
|
1fd04e0dbf | |
|
869fcbe642 | |
|
3337612782 | |
|
43f1d3c7f9 | |
|
5d2bfa7b49 | |
|
2b5982cd0b | |
|
6ee5a87614 | |
|
16dd7b093c | |
|
de9b39bad1 | |
|
18bae1d745 | |
|
d08fead3d3 | |
|
e03c73c33d | |
|
270f36234b | |
|
0ff99bd776 | |
|
bf23a65bcc | |
|
95104b15c6 | |
|
d3dbcbd6bb | |
|
215569116f | |
|
3eb92959c3 | |
|
0a29ccc7fd | |
|
16e2d1a45d | |
|
128916b4b4 | |
|
4f9b935aea | |
|
919a307bc2 | |
|
da51356ef5 | |
|
ef15eb2e4a | |
|
ff244b6d34 | |
|
e4ced876ba | |
|
3a8ac8e708 | |
|
bb3ee1d550 | |
|
4ec8660ed2 | |
|
139527dad6 | |
|
e87c302591 | |
|
1e299e6b48 | |
|
8717a9eeff | |
|
405e341449 | |
|
79a1b7f9f6 | |
|
478c7c6630 | |
|
d86ed68ad1 | |
|
889a6346ea | |
|
28dd5f6307 | |
|
a5f0ecd1cf | |
|
bf2aec883d | |
|
e6d75ca083 | |
|
1f079780d7 | |
|
d63deb6dcb | |
|
606672e3a3 | |
|
c14e50cc46 | |
|
c26ed79dd3 | |
|
dce407905a | |
|
72999a66dd | |
|
ca1be8ab1e | |
|
01986a74cf | |
|
58b66f5ec7 | |
|
c89a6a82eb | |
|
13434184bd |
|
@ -1,6 +1,6 @@
|
||||||
<!-- Thanks for sending a pull request! Here are some tips for you:
|
<!-- Thanks for sending a pull request! Here are some tips for you:
|
||||||
|
|
||||||
1. If this is your first time, please read our contributor guidelines in the [CONTRIBUTING.md](https://github.com/falcosecurity/falco/blob/dev/CONTRIBUTING.md) file in the Falco repository.
|
1. If this is your first time, please read our contributor guidelines in the [CONTRIBUTING.md](https://github.com/falcosecurity/.github/blob/main/CONTRIBUTING.md) file in the Falco `.github` repository.
|
||||||
2. Please label this pull request according to what type of issue you are addressing.
|
2. Please label this pull request according to what type of issue you are addressing.
|
||||||
3. Please add a release note!
|
3. Please add a release note!
|
||||||
4. If the PR is unfinished while opening it specify a wip in the title before the actual title, for example, "wip: my awesome feature"
|
4. If the PR is unfinished while opening it specify a wip in the title before the actual title, for example, "wip: my awesome feature"
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: gomod
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
groups:
|
||||||
|
gomod:
|
||||||
|
update-types:
|
||||||
|
- "patch"
|
||||||
|
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
groups:
|
||||||
|
actions:
|
||||||
|
update-types:
|
||||||
|
- "minor"
|
||||||
|
- "patch"
|
|
@ -26,7 +26,7 @@ jobs:
|
||||||
- go
|
- go
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@d23060145bc9131d50558d5d4185494a20208101 # v2.2.8
|
uses: github/codeql-action/init@d23060145bc9131d50558d5d4185494a20208101 # v2.2.8
|
||||||
with:
|
with:
|
||||||
|
|
|
@ -21,38 +21,55 @@ on:
|
||||||
description: The digest of the pushed image.
|
description: The digest of the pushed image.
|
||||||
value: ${{ jobs.docker-image.outputs.digest }}
|
value: ${{ jobs.docker-image.outputs.digest }}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker-image:
|
docker-image:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
outputs:
|
outputs:
|
||||||
image: ${{ steps.build-and-push.outputs.image }}
|
image: ${{ steps.build-and-push.outputs.image }}
|
||||||
digest: ${{ steps.build-and-push.outputs.digest }}
|
digest: ${{ steps.build-and-push.outputs.digest }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0
|
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: Buildx
|
id: Buildx
|
||||||
uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c # v2.5.0
|
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2.1.0
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USER }}
|
username: ${{ secrets.DOCKERHUB_USER }}
|
||||||
password: ${{ secrets.DOCKERHUB_SECRET }}
|
password: ${{ secrets.DOCKERHUB_SECRET }}
|
||||||
|
|
||||||
|
- name: Configure AWS credentials
|
||||||
|
uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1
|
||||||
|
with:
|
||||||
|
role-to-assume: arn:aws:iam::292999226676:role/github_actions-falcoctl-ecr
|
||||||
|
aws-region: us-east-1
|
||||||
|
|
||||||
|
- name: Login to Amazon ECR
|
||||||
|
id: login-ecr-public
|
||||||
|
uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2.0.1
|
||||||
|
with:
|
||||||
|
registry-type: public
|
||||||
|
|
||||||
- name: Docker Meta
|
- name: Docker Meta
|
||||||
id: meta_falcoctl
|
id: meta_falcoctl
|
||||||
uses: docker/metadata-action@507c2f2dc502c992ad446e3d7a5dfbe311567a96 # v4.3.0
|
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||||
with:
|
with:
|
||||||
# list of Docker images to use as base name for tags
|
# list of Docker images to use as base name for tags
|
||||||
images: |
|
images: |
|
||||||
docker.io/falcosecurity/falcoctl
|
docker.io/falcosecurity/falcoctl
|
||||||
|
public.ecr.aws/falcosecurity/falcoctl
|
||||||
tags: |
|
tags: |
|
||||||
type=ref,event=branch
|
type=ref,event=branch
|
||||||
type=semver,pattern={{ version }}
|
type=semver,pattern={{ version }}
|
||||||
|
@ -61,7 +78,7 @@ jobs:
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
id: build-and-push
|
id: build-and-push
|
||||||
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 # v4.0.0
|
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
|
@ -75,7 +92,7 @@ jobs:
|
||||||
|
|
||||||
- name: Install Cosign
|
- name: Install Cosign
|
||||||
if: ${{ inputs.sign }}
|
if: ${{ inputs.sign }}
|
||||||
uses: sigstore/cosign-installer@9614fae9e5c5eddabb09f90a270fcb487c9f7149 # v3.3.0
|
uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 # v3.9.2
|
||||||
|
|
||||||
- name: Sign the images with GitHub OIDC Token
|
- name: Sign the images with GitHub OIDC Token
|
||||||
if: ${{ inputs.sign }}
|
if: ${{ inputs.sign }}
|
||||||
|
|
|
@ -23,14 +23,14 @@ jobs:
|
||||||
goos: windows
|
goos: windows
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout commit
|
- name: Checkout commit
|
||||||
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version-file: 'go.mod'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Build Falcoctl
|
- name: Build Falcoctl
|
||||||
|
@ -47,14 +47,14 @@ jobs:
|
||||||
tar -czvf falcoctl-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz falcoctl LICENSE
|
tar -czvf falcoctl-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz falcoctl LICENSE
|
||||||
|
|
||||||
- name: Upload falcoctl artifacts
|
- name: Upload falcoctl artifacts
|
||||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: falcoctl-${{ matrix.goos }}-${{ matrix.goarch }}
|
name: falcoctl-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||||
path: ./falcoctl-${{ matrix.goos }}-${{ matrix.goarch }}
|
path: ./falcoctl-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
- name: Upload falcoctl archives
|
- name: Upload falcoctl archives
|
||||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: falcoctl-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz
|
name: falcoctl-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz
|
||||||
path: ./falcoctl-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz
|
path: ./falcoctl-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz
|
||||||
|
@ -80,22 +80,86 @@ jobs:
|
||||||
needs: docker-configure
|
needs: docker-configure
|
||||||
uses: ./.github/workflows/docker-image.yaml
|
uses: ./.github/workflows/docker-image.yaml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
with:
|
with:
|
||||||
release: ${{ needs.docker-configure.outputs.release }}
|
release: ${{ needs.docker-configure.outputs.release }}
|
||||||
commit: ${{ needs.docker-configure.outputs.commit }}
|
commit: ${{ needs.docker-configure.outputs.commit }}
|
||||||
build_date: ${{ needs.docker-configure.outputs.build_date }}
|
build_date: ${{ needs.docker-configure.outputs.build_date }}
|
||||||
|
sign: true
|
||||||
|
|
||||||
|
provenance-for-images-docker:
|
||||||
|
if: ${{ github.event_name == 'push' }}
|
||||||
|
needs: [docker-configure, docker-image]
|
||||||
|
permissions:
|
||||||
|
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
|
||||||
|
with:
|
||||||
|
image: docker.io/falcosecurity/falcoctl
|
||||||
|
# The image digest is used to prevent TOCTOU issues.
|
||||||
|
# This is an output of the docker/build-push-action
|
||||||
|
# See: https://github.com/slsa-framework/slsa-verifier#toctou-attacks
|
||||||
|
digest: ${{ needs.docker-image.outputs.digest }}
|
||||||
|
secrets:
|
||||||
|
registry-username: ${{ secrets.DOCKERHUB_USER }}
|
||||||
|
registry-password: ${{ secrets.DOCKERHUB_SECRET }}
|
||||||
|
|
||||||
|
login-to-amazon-ecr:
|
||||||
|
if: ${{ github.event_name == 'push' }}
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- name: Configure AWS credentials
|
||||||
|
uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1
|
||||||
|
with:
|
||||||
|
role-to-assume: arn:aws:iam::292999226676:role/github_actions-falcoctl-ecr
|
||||||
|
aws-region: us-east-1
|
||||||
|
|
||||||
|
- name: Login to Amazon ECR
|
||||||
|
id: login-ecr-public
|
||||||
|
uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2.0.1
|
||||||
|
with:
|
||||||
|
registry-type: public
|
||||||
|
mask-password: 'false'
|
||||||
|
outputs:
|
||||||
|
registry: ${{ steps.login-ecr-public.outputs.registry }}
|
||||||
|
docker_username: ${{ steps.login-ecr-public.outputs.docker_username_public_ecr_aws }}
|
||||||
|
docker_password: ${{ steps.login-ecr-public.outputs.docker_password_public_ecr_aws }}
|
||||||
|
|
||||||
|
provenance-for-images-aws-ecr:
|
||||||
|
if: ${{ github.event_name == 'push' }}
|
||||||
|
needs: [docker-configure, docker-image, login-to-amazon-ecr]
|
||||||
|
permissions:
|
||||||
|
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
|
||||||
|
with:
|
||||||
|
image: public.ecr.aws/falcosecurity/falcoctl
|
||||||
|
# The image digest is used to prevent TOCTOU issues.
|
||||||
|
# This is an output of the docker/build-push-action
|
||||||
|
# See: https://github.com/slsa-framework/slsa-verifier#toctou-attacks
|
||||||
|
digest: ${{ needs.docker-image.outputs.digest }}
|
||||||
|
secrets:
|
||||||
|
registry-username: ${{ needs.login-to-amazon-ecr.outputs.docker_username }}
|
||||||
|
registry-password: ${{ needs.login-to-amazon-ecr.outputs.docker_password }}
|
||||||
|
|
||||||
test:
|
test:
|
||||||
needs: build
|
needs: build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout commit
|
- name: Checkout commit
|
||||||
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version-file: 'go.mod'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
|
|
|
@ -8,24 +8,25 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: "^1.24.3"
|
||||||
|
go-version-file: "go.mod"
|
||||||
check-latest: true
|
check-latest: true
|
||||||
cache: 'false'
|
cache: "false"
|
||||||
|
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0
|
uses: golangci/golangci-lint-action@55c2c1448f86e01eaae002a5a3a9624417608d84 # v6.5.2
|
||||||
with:
|
with:
|
||||||
only-new-issues: true
|
only-new-issues: true
|
||||||
version: v1.54.2
|
version: v1.64.7
|
||||||
args: --timeout=900s
|
args: --timeout=900s
|
||||||
|
|
||||||
gomodtidy:
|
gomodtidy:
|
||||||
|
@ -34,16 +35,16 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
ref: "${{ github.event.pull_request.head.sha }}"
|
ref: "${{ github.event.pull_request.head.sha }}"
|
||||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version-file: "go.mod"
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Execute go mod tidy and check the outcome
|
- name: Execute go mod tidy and check the outcome
|
||||||
|
|
|
@ -14,7 +14,7 @@ jobs:
|
||||||
hashes: ${{ steps.hash.outputs.hashes }}
|
hashes: ${{ steps.hash.outputs.hashes }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
@ -22,21 +22,21 @@ jobs:
|
||||||
run: git fetch --force --tags
|
run: git fetch --force --tags
|
||||||
|
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version-file: 'go.mod'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
id: run-goreleaser
|
id: run-goreleaser
|
||||||
uses: goreleaser/goreleaser-action@f82d6c1c344bcacabba2c841718984797f664a6b # v4.2.0
|
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0
|
||||||
with:
|
with:
|
||||||
distribution: goreleaser
|
distribution: goreleaser
|
||||||
version: latest
|
version: latest
|
||||||
args: release --clean
|
args: release --clean
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Generate subject
|
- name: Generate subject
|
||||||
id: hash
|
id: hash
|
||||||
env:
|
env:
|
||||||
|
@ -46,25 +46,25 @@ jobs:
|
||||||
|
|
||||||
checksum_file=$(echo "$ARTIFACTS" | jq -r '.[] | select (.type=="Checksum") | .path')
|
checksum_file=$(echo "$ARTIFACTS" | jq -r '.[] | select (.type=="Checksum") | .path')
|
||||||
echo "hashes=$(cat $checksum_file | base64 -w0)" >> "$GITHUB_OUTPUT"
|
echo "hashes=$(cat $checksum_file | base64 -w0)" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
provenance-for-binaries:
|
provenance-for-binaries:
|
||||||
needs: [goreleaser]
|
needs: [goreleaser]
|
||||||
permissions:
|
permissions:
|
||||||
actions: read # To read the workflow path.
|
actions: read # To read the workflow path.
|
||||||
id-token: write # To sign the provenance.
|
id-token: write # To sign the provenance.
|
||||||
contents: write # To add assets to a release.
|
contents: write # To add assets to a release.
|
||||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.6.0
|
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
|
||||||
with:
|
with:
|
||||||
base64-subjects: "${{ needs.goreleaser.outputs.hashes }}"
|
base64-subjects: "${{ needs.goreleaser.outputs.hashes }}"
|
||||||
upload-assets: true # upload to a new release
|
upload-assets: true # upload to a new release
|
||||||
|
|
||||||
verification:
|
verification:
|
||||||
needs: [goreleaser, provenance-for-binaries]
|
needs: [goreleaser, provenance-for-binaries]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions: read-all
|
permissions: read-all
|
||||||
steps:
|
steps:
|
||||||
- name: Install the verifier
|
- name: Install the verifier
|
||||||
uses: slsa-framework/slsa-verifier/actions/installer@v2.3.0
|
uses: slsa-framework/slsa-verifier/actions/installer@v2.7.1
|
||||||
|
|
||||||
- name: Download assets
|
- name: Download assets
|
||||||
env:
|
env:
|
||||||
|
@ -75,7 +75,7 @@ jobs:
|
||||||
gh -R "$GITHUB_REPOSITORY" release download "$GITHUB_REF_NAME" -p "*.tar.gz"
|
gh -R "$GITHUB_REPOSITORY" release download "$GITHUB_REF_NAME" -p "*.tar.gz"
|
||||||
gh -R "$GITHUB_REPOSITORY" release download "$GITHUB_REF_NAME" -p "*.zip"
|
gh -R "$GITHUB_REPOSITORY" release download "$GITHUB_REF_NAME" -p "*.zip"
|
||||||
gh -R "$GITHUB_REPOSITORY" release download "$GITHUB_REF_NAME" -p "$PROVENANCE"
|
gh -R "$GITHUB_REPOSITORY" release download "$GITHUB_REF_NAME" -p "$PROVENANCE"
|
||||||
|
|
||||||
- name: Verify assets
|
- name: Verify assets
|
||||||
env:
|
env:
|
||||||
CHECKSUMS: ${{ needs.goreleaser.outputs.hashes }}
|
CHECKSUMS: ${{ needs.goreleaser.outputs.hashes }}
|
||||||
|
@ -105,7 +105,7 @@ jobs:
|
||||||
echo "release=$(echo $GITHUB_REF | cut -d / -f 3 | sed 's/^v//')" >> $GITHUB_OUTPUT
|
echo "release=$(echo $GITHUB_REF | cut -d / -f 3 | sed 's/^v//')" >> $GITHUB_OUTPUT
|
||||||
echo "commit=${{ github.sha }}" >> $GITHUB_OUTPUT
|
echo "commit=${{ github.sha }}" >> $GITHUB_OUTPUT
|
||||||
echo "build_date=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
echo "build_date=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
docker-image:
|
docker-image:
|
||||||
needs: docker-configure
|
needs: docker-configure
|
||||||
uses: ./.github/workflows/docker-image.yaml
|
uses: ./.github/workflows/docker-image.yaml
|
||||||
|
@ -120,13 +120,13 @@ jobs:
|
||||||
build_date: ${{ needs.docker-configure.outputs.build_date }}
|
build_date: ${{ needs.docker-configure.outputs.build_date }}
|
||||||
sign: true
|
sign: true
|
||||||
|
|
||||||
provenance-for-images:
|
provenance-for-images-docker:
|
||||||
needs: [docker-configure, docker-image]
|
needs: [docker-configure, docker-image]
|
||||||
permissions:
|
permissions:
|
||||||
actions: read # for detecting the Github Actions environment.
|
actions: read # for detecting the Github Actions environment.
|
||||||
id-token: write # for creating OIDC tokens for signing.
|
id-token: write # for creating OIDC tokens for signing.
|
||||||
packages: write # for uploading attestations.
|
packages: write # for uploading attestations.
|
||||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.9.0
|
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
|
||||||
with:
|
with:
|
||||||
image: docker.io/falcosecurity/falcoctl
|
image: docker.io/falcosecurity/falcoctl
|
||||||
# The image digest is used to prevent TOCTOU issues.
|
# The image digest is used to prevent TOCTOU issues.
|
||||||
|
@ -136,3 +136,43 @@ jobs:
|
||||||
secrets:
|
secrets:
|
||||||
registry-username: ${{ secrets.DOCKERHUB_USER }}
|
registry-username: ${{ secrets.DOCKERHUB_USER }}
|
||||||
registry-password: ${{ secrets.DOCKERHUB_SECRET }}
|
registry-password: ${{ secrets.DOCKERHUB_SECRET }}
|
||||||
|
|
||||||
|
login-to-amazon-ecr:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- name: Configure AWS credentials
|
||||||
|
uses: aws-actions/configure-aws-credentials@b47578312673ae6fa5b5096b330d9fbac3d116df # v4.2.1
|
||||||
|
with:
|
||||||
|
role-to-assume: arn:aws:iam::292999226676:role/github_actions-falcoctl-ecr
|
||||||
|
aws-region: us-east-1
|
||||||
|
|
||||||
|
- name: Login to Amazon ECR
|
||||||
|
id: login-ecr-public
|
||||||
|
uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2.0.1
|
||||||
|
with:
|
||||||
|
registry-type: public
|
||||||
|
mask-password: 'false'
|
||||||
|
outputs:
|
||||||
|
registry: ${{ steps.login-ecr-public.outputs.registry }}
|
||||||
|
docker_username: ${{ steps.login-ecr-public.outputs.docker_username_public_ecr_aws }}
|
||||||
|
docker_password: ${{ steps.login-ecr-public.outputs.docker_password_public_ecr_aws }}
|
||||||
|
|
||||||
|
provenance-for-images-aws-ecr:
|
||||||
|
needs: [docker-configure, docker-image, login-to-amazon-ecr]
|
||||||
|
permissions:
|
||||||
|
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
|
||||||
|
with:
|
||||||
|
image: public.ecr.aws/falcosecurity/falcoctl
|
||||||
|
# The image digest is used to prevent TOCTOU issues.
|
||||||
|
# This is an output of the docker/build-push-action
|
||||||
|
# See: https://github.com/slsa-framework/slsa-verifier#toctou-attacks
|
||||||
|
digest: ${{ needs.docker-image.outputs.digest }}
|
||||||
|
secrets:
|
||||||
|
registry-username: ${{ needs.login-to-amazon-ecr.outputs.docker_username }}
|
||||||
|
registry-password: ${{ needs.login-to-amazon-ecr.outputs.docker_password }}
|
||||||
|
|
|
@ -44,17 +44,11 @@ linters-settings:
|
||||||
- opinionated
|
- opinionated
|
||||||
- performance
|
- performance
|
||||||
- style
|
- style
|
||||||
disabled-checks:
|
|
||||||
# Conflicts with govet check-shadowing
|
|
||||||
- sloppyReassign
|
|
||||||
goimports:
|
goimports:
|
||||||
local-prefixes: github.com/falcosecurity/falcoctl
|
local-prefixes: github.com/falcosecurity/falcoctl
|
||||||
govet:
|
|
||||||
check-shadowing: true
|
|
||||||
misspell:
|
misspell:
|
||||||
locale: US
|
locale: US
|
||||||
nolintlint:
|
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
|
allow-unused: false # report any unused nolint directives
|
||||||
require-explanation: true # require an explanation for 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
|
require-specific: true # require nolint directives to be specific about which linter is being skipped
|
||||||
|
@ -71,7 +65,7 @@ linters:
|
||||||
- errcheck
|
- errcheck
|
||||||
- errorlint
|
- errorlint
|
||||||
- exhaustive
|
- exhaustive
|
||||||
- exportloopref
|
- copyloopvar
|
||||||
# - funlen
|
# - funlen
|
||||||
# - gochecknoglobals
|
# - gochecknoglobals
|
||||||
# - gochecknoinits
|
# - gochecknoinits
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
version: 2
|
||||||
|
|
||||||
project_name: falcoctl
|
project_name: falcoctl
|
||||||
before:
|
before:
|
||||||
hooks:
|
hooks:
|
||||||
|
@ -16,6 +18,8 @@ builds:
|
||||||
ignore:
|
ignore:
|
||||||
- goos: darwin
|
- goos: darwin
|
||||||
goarch: 386
|
goarch: 386
|
||||||
|
- goos: windows
|
||||||
|
goarch: 386
|
||||||
|
|
||||||
ldflags: |
|
ldflags: |
|
||||||
-X github.com/falcosecurity/falcoctl/cmd/version.buildDate={{ .Date }}
|
-X github.com/falcosecurity/falcoctl/cmd/version.buildDate={{ .Date }}
|
||||||
|
@ -43,3 +47,6 @@ release:
|
||||||
|
|
||||||
changelog:
|
changelog:
|
||||||
use: github-native
|
use: github-native
|
||||||
|
|
||||||
|
git:
|
||||||
|
tag_sort: -version:creatordate
|
||||||
|
|
3
Makefile
3
Makefile
|
@ -18,6 +18,7 @@ PROJECT?=github.com/falcosecurity/falcoctl
|
||||||
# todo(leogr): re-enable race when CLI tests can run with race enabled
|
# todo(leogr): re-enable race when CLI tests can run with race enabled
|
||||||
TEST_FLAGS ?= -v -cover# -race
|
TEST_FLAGS ?= -v -cover# -race
|
||||||
|
|
||||||
|
.PHONY: falcoctl
|
||||||
falcoctl:
|
falcoctl:
|
||||||
$(GO) build -ldflags \
|
$(GO) build -ldflags \
|
||||||
"-X '${PROJECT}/cmd/version.semVersion=${RELEASE}' \
|
"-X '${PROJECT}/cmd/version.semVersion=${RELEASE}' \
|
||||||
|
@ -62,7 +63,7 @@ fmt: gci addlicense
|
||||||
.PHONY: golangci-lint
|
.PHONY: golangci-lint
|
||||||
golangci-lint:
|
golangci-lint:
|
||||||
ifeq (, $(shell which golangci-lint))
|
ifeq (, $(shell which golangci-lint))
|
||||||
@go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.52.2
|
@go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2
|
||||||
GOLANGCILINT=$(GOBIN)/golangci-lint
|
GOLANGCILINT=$(GOBIN)/golangci-lint
|
||||||
else
|
else
|
||||||
GOLANGCILINT=$(shell which golangci-lint)
|
GOLANGCILINT=$(shell which golangci-lint)
|
||||||
|
|
1
OWNERS
1
OWNERS
|
@ -4,7 +4,6 @@ approvers:
|
||||||
- maxgio92
|
- maxgio92
|
||||||
- fededp
|
- fededp
|
||||||
- cpanato
|
- cpanato
|
||||||
reviewers:
|
|
||||||
- alacuku
|
- alacuku
|
||||||
- loresuso
|
- loresuso
|
||||||
emeritus_approvers:
|
emeritus_approvers:
|
||||||
|
|
10
README.md
10
README.md
|
@ -23,6 +23,13 @@ sudo install -o root -g root -m 0755 falcoctl /usr/local/bin/falcoctl
|
||||||
> NOTE: Make sure */usr/local/bin* is in your PATH environment variable.
|
> NOTE: Make sure */usr/local/bin* is in your PATH environment variable.
|
||||||
|
|
||||||
#### MacOS
|
#### MacOS
|
||||||
|
The easiest way to install on MacOS is via `Homebrew`:
|
||||||
|
```bash
|
||||||
|
brew install falcoctl
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can download directly from the source:
|
||||||
|
|
||||||
##### Intel
|
##### Intel
|
||||||
```bash
|
```bash
|
||||||
LATEST=$(curl -sI https://github.com/falcosecurity/falcoctl/releases/latest | awk '/location: /{gsub("\r","",$2);split($2,v,"/");print substr(v[8],2)}')
|
LATEST=$(curl -sI https://github.com/falcosecurity/falcoctl/releases/latest | awk '/location: /{gsub("\r","",$2);split($2,v,"/");print substr(v[8],2)}')
|
||||||
|
@ -209,6 +216,8 @@ 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. |
|
| http | http:// | Can be used to retrieve indices via simple HTTP GET requests. |
|
||||||
| https | https:// | Convenience alias for the HTTP backend. |
|
| https | https:// | Convenience alias for the HTTP backend. |
|
||||||
| gcs | gs:// | For indices stored as Google Cloud Storage objects. Supports application default credentials. |
|
| 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
|
#### falcoctl index add
|
||||||
|
@ -335,6 +344,7 @@ $ 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**.
|
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:
|
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;
|
* `--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`
|
* `--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
|
* `--tag`: additional artifact tag. Can be repeated multiple time
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.21 as builder
|
FROM cgr.dev/chainguard/go AS builder
|
||||||
WORKDIR /tmp/builder
|
WORKDIR /tmp/builder
|
||||||
|
|
||||||
ARG RELEASE
|
ARG RELEASE
|
||||||
|
@ -29,14 +29,8 @@ RUN CGO_ENABLED=0 \
|
||||||
|
|
||||||
RUN echo ${RELEASE}
|
RUN echo ${RELEASE}
|
||||||
|
|
||||||
FROM alpine:3.18.4
|
FROM cgr.dev/chainguard/static:latest
|
||||||
|
|
||||||
RUN apk update --no-cache && \
|
COPY --from=builder /tmp/builder/falcoctl /usr/bin/falcoctl
|
||||||
apk add --upgrade --no-cache libssl3 libcrypto3
|
|
||||||
RUN rm -rf /var/cache/apk/*
|
|
||||||
|
|
||||||
ARG BIN_NAME="falcoctl"
|
ENTRYPOINT [ "/usr/bin/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" ]
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ func (o *artifactInfoOptions) RunArtifactInfo(ctx context.Context, args []string
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
joinedTags := strings.Join(tags, ", ")
|
joinedTags := strings.Join(filterOutSigTags(tags), ", ")
|
||||||
data = append(data, []string{ref, joinedTags})
|
data = append(data, []string{ref, joinedTags})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,3 +110,14 @@ func (o *artifactInfoOptions) RunArtifactInfo(ctx context.Context, args []string
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func filterOutSigTags(tags []string) []string {
|
||||||
|
// Iterate the slice in reverse to avoid index shifting when deleting
|
||||||
|
for i := len(tags) - 1; i >= 0; i-- {
|
||||||
|
if strings.HasSuffix(tags[i], ".sig") {
|
||||||
|
// Remove the element at index i by slicing the slice
|
||||||
|
tags = append(tags[:i], tags[i+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tags
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,9 @@ const (
|
||||||
// FlagAllowedTypes is the name of the flag to specify allowed artifact types.
|
// FlagAllowedTypes is the name of the flag to specify allowed artifact types.
|
||||||
FlagAllowedTypes = "allowed-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 is the name of the flag to enable artifact dependencies resolution.
|
||||||
FlagResolveDeps = "resolve-deps"
|
FlagResolveDeps = "resolve-deps"
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
@ -51,11 +52,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
|
with "FALCOCTL_" and be followed by the hierarchical keys used in the configuration file separated by
|
||||||
an underscore "_".
|
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).
|
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
|
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
|
the configured index files, and if found, it will use the registry and repository specified
|
||||||
in the indexes.
|
in the indexes.
|
||||||
|
|
||||||
Example - Install "latest" tag of "k8saudit-rules" artifact by relying on index metadata:
|
Example - Install "latest" tag of "k8saudit-rules" artifact by relying on index metadata:
|
||||||
|
@ -74,6 +75,9 @@ type artifactInstallOptions struct {
|
||||||
*options.Registry
|
*options.Registry
|
||||||
*options.Directory
|
*options.Directory
|
||||||
allowedTypes oci.ArtifactTypeSlice
|
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
|
resolveDeps bool
|
||||||
noVerify bool
|
noVerify bool
|
||||||
}
|
}
|
||||||
|
@ -165,6 +169,15 @@ 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
|
return nil
|
||||||
},
|
},
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
@ -177,9 +190,11 @@ func NewArtifactInstallCmd(ctx context.Context, opt *options.Common) *cobra.Comm
|
||||||
cmd.Flags().Var(&o.allowedTypes, FlagAllowedTypes,
|
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.
|
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.
|
It accepts comma separated values or it can be repeated multiple times.
|
||||||
Examples:
|
Examples:
|
||||||
--%s="rulesfile,plugin"
|
--%s="rulesfile,plugin"
|
||||||
--%s=rulesfile --%s=plugin`, FlagAllowedTypes, FlagAllowedTypes, FlagAllowedTypes))
|
--%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,
|
cmd.Flags().BoolVar(&o.resolveDeps, FlagResolveDeps, true,
|
||||||
"whether this command should resolve dependencies or not")
|
"whether this command should resolve dependencies or not")
|
||||||
cmd.Flags().BoolVar(&o.noVerify, FlagNoVerify, false,
|
cmd.Flags().BoolVar(&o.noVerify, FlagNoVerify, false,
|
||||||
|
@ -225,7 +240,7 @@ func (o *artifactInstallOptions) RunArtifactInstall(ctx context.Context, args []
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
artifactConfig, err := puller.ArtifactConfig(ctx, ref, runtime.GOOS, runtime.GOARCH)
|
artifactConfig, err := puller.ArtifactConfig(ctx, ref, o.platformOS, o.platformArch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -264,31 +279,33 @@ func (o *artifactInstallOptions) RunArtifactInstall(ctx context.Context, args []
|
||||||
logger.Info("Installing artifacts", logger.Args("refs", refs))
|
logger.Info("Installing artifacts", logger.Args("refs", refs))
|
||||||
|
|
||||||
for _, ref := range refs {
|
for _, ref := range refs {
|
||||||
ref, err = o.IndexCache.ResolveReference(ref)
|
resolvedRef, err := o.IndexCache.ResolveReference(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info("Preparing to pull artifact", logger.Args("ref", ref))
|
if signatures[resolvedRef] == nil {
|
||||||
|
if sig := o.IndexCache.SignatureForIndexRef(ref); sig != nil {
|
||||||
|
signatures[resolvedRef] = sig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := puller.CheckAllowedType(ctx, ref, runtime.GOOS, runtime.GOARCH, o.allowedTypes.Types); err != nil {
|
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 {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install will always install artifact for the current OS and architecture
|
// Install will always install artifact for the current OS and architecture
|
||||||
result, err := puller.Pull(ctx, ref, tmpDir, runtime.GOOS, runtime.GOARCH)
|
result, err := puller.Pull(ctx, resolvedRef, tmpDir, o.platformOS, o.platformArch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
sig, ok := signatures[ref]
|
sig := signatures[resolvedRef]
|
||||||
if !ok {
|
|
||||||
// try to get the signature from the index
|
|
||||||
sig = o.IndexCache.SignatureForIndexRef(ref)
|
|
||||||
}
|
|
||||||
|
|
||||||
if sig != nil && !o.noVerify {
|
if sig != nil && !o.noVerify {
|
||||||
repo, err := utils.RepositoryFromRef(ref)
|
repo, err := utils.RepositoryFromRef(resolvedRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -337,7 +354,7 @@ func (o *artifactInstallOptions) RunArtifactInstall(ctx context.Context, args []
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Extract artifact and move it to its destination directory
|
// Extract artifact and move it to its destination directory
|
||||||
_, err = utils.ExtractTarGz(f, destDir, 0)
|
_, err = utils.ExtractTarGz(ctx, f, destDir, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot extract %q to %q: %w", result.Filename, destDir, err)
|
return fmt.Errorf("cannot extract %q to %q: %w", result.Filename, destDir, err)
|
||||||
}
|
}
|
||||||
|
@ -350,7 +367,7 @@ func (o *artifactInstallOptions) RunArtifactInstall(ctx context.Context, args []
|
||||||
if o.Printer.Spinner != nil {
|
if o.Printer.Spinner != nil {
|
||||||
_ = o.Printer.Spinner.Stop()
|
_ = o.Printer.Spinner.Stop()
|
||||||
}
|
}
|
||||||
logger.Info("Artifact successfully installed", logger.Args("name", ref, "type", result.Type, "digest", result.Digest, "directory", destDir))
|
logger.Info("Artifact successfully installed", logger.Args("name", resolvedRef, "type", result.Type, "digest", result.Digest, "directory", destDir))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -45,6 +45,7 @@ Flags:
|
||||||
--allowed-types=rulesfile --allowed-types=plugin
|
--allowed-types=rulesfile --allowed-types=plugin
|
||||||
-h, --help help for install
|
-h, --help help for install
|
||||||
--plain-http allows interacting with remote registry via plain http requests
|
--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")
|
--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)
|
--resolve-deps whether this command should resolve dependencies or not (default true)
|
||||||
--rulesfiles-dir string directory where to install rules. (default "/etc/falco")
|
--rulesfiles-dir string directory where to install rules. (default "/etc/falco")
|
||||||
|
@ -73,11 +74,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
|
with "FALCOCTL_" and be followed by the hierarchical keys used in the configuration file separated by
|
||||||
an underscore "_".
|
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).
|
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
|
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
|
the configured index files, and if found, it will use the registry and repository specified
|
||||||
in the indexes.
|
in the indexes.
|
||||||
|
|
||||||
Example - Install "latest" tag of "k8saudit-rules" artifact by relying on index metadata:
|
Example - Install "latest" tag of "k8saudit-rules" artifact by relying on index metadata:
|
||||||
|
@ -218,7 +219,7 @@ var artifactInstallTests = Describe("install", func() {
|
||||||
Expect(result).ToNot(BeNil())
|
Expect(result).ToNot(BeNil())
|
||||||
ref = registry + repoAndTag
|
ref = registry + repoAndTag
|
||||||
Expect(err).To(BeNil())
|
Expect(err).To(BeNil())
|
||||||
args = []string{artifactCmd, installCmd, ref, "--plain-http",
|
args = []string{artifactCmd, installCmd, ref, "--plain-http", "--platform", testPluginPlatform1,
|
||||||
"--config", configFilePath, "--allowed-types", "rulesfile"}
|
"--config", configFilePath, "--allowed-types", "rulesfile"}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -310,7 +311,7 @@ var artifactInstallTests = Describe("install", func() {
|
||||||
Expect(result).ToNot(BeNil())
|
Expect(result).ToNot(BeNil())
|
||||||
ref = registry + repoAndTag
|
ref = registry + repoAndTag
|
||||||
Expect(err).To(BeNil())
|
Expect(err).To(BeNil())
|
||||||
args = []string{artifactCmd, installCmd, ref, "--plain-http",
|
args = []string{artifactCmd, installCmd, ref, "--plain-http", "--platform", testPluginPlatform1,
|
||||||
"--config", configFilePath, "--plugins-dir", destDir}
|
"--config", configFilePath, "--plugins-dir", destDir}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -348,7 +349,7 @@ var artifactInstallTests = Describe("install", func() {
|
||||||
Expect(result).ToNot(BeNil())
|
Expect(result).ToNot(BeNil())
|
||||||
ref = registry + repoAndTag
|
ref = registry + repoAndTag
|
||||||
Expect(err).To(BeNil())
|
Expect(err).To(BeNil())
|
||||||
args = []string{artifactCmd, installCmd, ref, "--plain-http",
|
args = []string{artifactCmd, installCmd, ref, "--plain-http", "--platform", testPluginPlatform1,
|
||||||
"--config", configFilePath, "--plugins-dir", destDir}
|
"--config", configFilePath, "--plugins-dir", destDir}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -437,6 +438,28 @@ 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)))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -41,9 +41,8 @@ func NewDriverCleanupCmd(ctx context.Context, opt *options.Common, driver *optio
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "cleanup [flags]",
|
Use: "cleanup [flags]",
|
||||||
DisableFlagsInUseLine: true,
|
DisableFlagsInUseLine: true,
|
||||||
Short: "[Preview] Cleanup a driver",
|
Short: "Cleanup a driver",
|
||||||
Long: `[Preview] 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 {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return o.RunDriverCleanup(ctx)
|
return o.RunDriverCleanup(ctx)
|
||||||
},
|
},
|
||||||
|
@ -66,7 +65,7 @@ func (o *driverCleanupOptions) RunDriverCleanup(_ context.Context) error {
|
||||||
if o.Printer.Logger.Formatter == pterm.LogFormatterJSON {
|
if o.Printer.Logger.Formatter == pterm.LogFormatterJSON {
|
||||||
// Only print formatted text if we are formatting to json
|
// Only print formatted text if we are formatting to json
|
||||||
out := strings.ReplaceAll(buf.String(), "\n", ";")
|
out := strings.ReplaceAll(buf.String(), "\n", ";")
|
||||||
o.Printer.Logger.Info("Driver build", o.Printer.Logger.Args("output", out))
|
o.Printer.Logger.Info("Driver cleanup", o.Printer.Logger.Args("output", out))
|
||||||
} else {
|
} else {
|
||||||
// Print much more readable output as-is
|
// Print much more readable output as-is
|
||||||
o.Printer.DefaultText.Print(buf.String())
|
o.Printer.DefaultText.Print(buf.String())
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -25,8 +25,8 @@ import (
|
||||||
"github.com/falcosecurity/falcoctl/cmd"
|
"github.com/falcosecurity/falcoctl/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
var driverCleanupHelp = `[Preview] Cleans a driver up, eg for kmod, by removing it from dkms.
|
//nolint:lll // no need to check for line length.
|
||||||
** This command is in preview and under development. **
|
var driverCleanupHelp = `Cleans a driver up, eg for kmod, by removing it from dkms.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
falcoctl driver cleanup [flags]
|
falcoctl driver cleanup [flags]
|
||||||
|
@ -35,14 +35,16 @@ Flags:
|
||||||
-h, --help help for cleanup
|
-h, --help help for cleanup
|
||||||
|
|
||||||
Global Flags:
|
Global Flags:
|
||||||
--config string config file to be used for falcoctl (default "/etc/falcoctl/falcoctl.yaml")
|
--config string config file to be used for falcoctl (default "/etc/falcoctl/falcoctl.yaml")
|
||||||
--host-root string Driver host root to be used. (default "/")
|
--host-root string Driver host root to be used. (default "/")
|
||||||
--log-format string Set formatting for logs (color, text, json) (default "color")
|
--kernelrelease string Specify the kernel release for which to download/build the driver in the same format used by 'uname -r' (e.g. '6.1.0-10-cloud-amd64')
|
||||||
--log-level string Set level for logs (info, warn, debug, trace) (default "info")
|
--kernelversion string Specify the kernel version for which to download/build the driver in the same format used by 'uname -v' (e.g. '#1 SMP PREEMPT_DYNAMIC Debian 6.1.38-2 (2023-07-27)')
|
||||||
--name string Driver name to be used. (default "falco")
|
--log-format string Set formatting for logs (color, text, json) (default "color")
|
||||||
--repo strings Driver repo to be used. (default [https://download.falco.org/driver])
|
--log-level string Set level for logs (info, warn, debug, trace) (default "info")
|
||||||
--type string Driver type to be used (auto, ebpf, kmod, modern_ebpf) (default "kmod")
|
--name string Driver name to be used. (default "falco")
|
||||||
--version string Driver version to be used.
|
--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])
|
||||||
|
--version string Driver version to be used.
|
||||||
`
|
`
|
||||||
|
|
||||||
var addAssertFailedBehavior = func(specificError string) {
|
var addAssertFailedBehavior = func(specificError string) {
|
||||||
|
@ -93,8 +95,7 @@ var _ = Describe("cleanup", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
args = []string{driverCmd, cleanupCmd, "--config", configFile, "--type", "foo"}
|
args = []string{driverCmd, cleanupCmd, "--config", configFile, "--type", "foo"}
|
||||||
})
|
})
|
||||||
addAssertFailedBehavior(`ERROR invalid argument "foo" for "--type" flag: invalid argument "foo",` +
|
addAssertFailedBehavior(`ERROR unsupported driver type specified: foo`)
|
||||||
` please provide one of (auto, ebpf, kmod, modern_ebpf)`)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,326 @@
|
||||||
|
// 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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -16,44 +16,50 @@
|
||||||
package driverconfig
|
package driverconfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
|
|
||||||
"github.com/falcosecurity/falcoctl/internal/config"
|
"github.com/falcosecurity/falcoctl/internal/config"
|
||||||
"github.com/falcosecurity/falcoctl/internal/utils"
|
|
||||||
drivertype "github.com/falcosecurity/falcoctl/pkg/driver/type"
|
drivertype "github.com/falcosecurity/falcoctl/pkg/driver/type"
|
||||||
"github.com/falcosecurity/falcoctl/pkg/options"
|
"github.com/falcosecurity/falcoctl/pkg/options"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
configMapEngineKindKey = "engine.kind"
|
longConfig = `Configure a driver for future usages with other driver subcommands.
|
||||||
longConfig = `[Preview] 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.
|
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.
|
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.
|
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 {
|
type driverConfigOptions struct {
|
||||||
*options.Common
|
*options.Common
|
||||||
*options.Driver
|
*options.Driver
|
||||||
Update bool
|
update bool
|
||||||
Namespace string
|
namespace string
|
||||||
KubeConfig string
|
kubeconfig string
|
||||||
|
configmap string
|
||||||
|
configDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
type engineCfg struct {
|
||||||
|
Kind string `yaml:"kind"`
|
||||||
|
}
|
||||||
|
type falcoCfg struct {
|
||||||
|
Engine engineCfg `yaml:"engine"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDriverConfigCmd configures a driver and stores it in config.
|
// NewDriverConfigCmd configures a driver and stores it in config.
|
||||||
|
@ -66,16 +72,36 @@ func NewDriverConfigCmd(ctx context.Context, opt *options.Common, driver *option
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "config [flags]",
|
Use: "config [flags]",
|
||||||
DisableFlagsInUseLine: true,
|
DisableFlagsInUseLine: true,
|
||||||
Short: "[Preview] Configure a driver",
|
Short: "Configure a driver",
|
||||||
Long: longConfig,
|
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 {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return o.RunDriverConfig(ctx)
|
return o.RunDriverConfig(ctx)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flags().BoolVar(&o.Update, "update-falco", true, "Whether to update Falco config/configmap.")
|
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.namespace, "namespace", "", "Kubernetes namespace.")
|
||||||
cmd.Flags().StringVar(&o.KubeConfig, "kubeconfig", "", "Kubernetes config.")
|
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.")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,115 +114,149 @@ func (o *driverConfigOptions) RunDriverConfig(ctx context.Context) error {
|
||||||
"host-root", o.Driver.HostRoot,
|
"host-root", o.Driver.HostRoot,
|
||||||
"repos", strings.Join(o.Driver.Repos, ",")))
|
"repos", strings.Join(o.Driver.Repos, ",")))
|
||||||
|
|
||||||
if o.Update {
|
if o.update {
|
||||||
if err := o.commit(ctx, o.Driver.Type); err != nil {
|
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 {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
o.Printer.Logger.Info("Storing falcoctl driver config")
|
||||||
return config.StoreDriver(o.Driver.ToDriverConfig(), o.ConfigFile)
|
return config.StoreDriver(o.Driver.ToDriverConfig(), o.ConfigFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkFalcoRunsWithDrivers(engineKind string) error {
|
func checkFalcoRunsWithDrivers(engineKind string) bool {
|
||||||
// Modify the data in the ConfigMap/Falco config file ONLY if engine.kind is set to a known driver type.
|
// 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.
|
// 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,
|
// 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.
|
// other running with plugins. We must only touch the one running with driver.
|
||||||
if _, err := drivertype.Parse(engineKind); err != nil {
|
if _, err := drivertype.Parse(engineKind); err != nil {
|
||||||
return fmt.Errorf("engine.kind is not driver driven: %s", engineKind)
|
return false
|
||||||
}
|
}
|
||||||
return nil
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *driverConfigOptions) replaceDriverTypeInFalcoConfig(driverType drivertype.DriverType) error {
|
func (o *driverConfigOptions) IsRunningInDriverModeHost() (bool, error) {
|
||||||
falcoCfgFile := filepath.Clean(filepath.Join(string(os.PathSeparator), "etc", "falco", "falco.yaml"))
|
o.Printer.Logger.Debug("Checking if Falco is running in driver mode on host system")
|
||||||
type engineCfg struct {
|
|
||||||
Kind string `yaml:"kind"`
|
falcoCfgFile := filepath.Join(o.configDir, falcoConfigFile)
|
||||||
}
|
|
||||||
type falcoCfg struct {
|
|
||||||
Engine engineCfg `yaml:"engine"`
|
|
||||||
}
|
|
||||||
yamlFile, err := os.ReadFile(filepath.Clean(falcoCfgFile))
|
yamlFile, err := os.ReadFile(filepath.Clean(falcoCfgFile))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return false, err
|
||||||
}
|
}
|
||||||
cfg := falcoCfg{}
|
cfg := falcoCfg{}
|
||||||
if err = yaml.Unmarshal(yamlFile, &cfg); err != nil {
|
if err = yaml.Unmarshal(yamlFile, &cfg); err != nil {
|
||||||
return err
|
return false, fmt.Errorf("unable to unmarshal falco.yaml to falcoCfg struct: %w", err)
|
||||||
}
|
}
|
||||||
if err = checkFalcoRunsWithDrivers(cfg.Engine.Kind); err != nil {
|
|
||||||
o.Printer.Logger.Warn("Avoid updating Falco configuration",
|
return checkFalcoRunsWithDrivers(cfg.Engine.Kind), nil
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *driverConfigOptions) replaceDriverTypeInK8SConfigMap(ctx context.Context, driverType drivertype.DriverType) error {
|
func (o *driverConfigOptions) IsRunningInDriverModeK8S(ctx context.Context, cl kubernetes.Interface) (bool, error) {
|
||||||
var (
|
o.Printer.Logger.Debug("Checking if Falco is running in driver mode in Kubernetes")
|
||||||
err error
|
|
||||||
cfg *rest.Config
|
|
||||||
)
|
|
||||||
|
|
||||||
if o.KubeConfig != "" {
|
configMap, err := cl.CoreV1().ConfigMaps(o.namespace).Get(ctx, o.configmap, metav1.GetOptions{})
|
||||||
cfg, err = clientcmd.BuildConfigFromFlags("", o.KubeConfig)
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// Create the rest config.
|
||||||
|
if kubeconfig != "" {
|
||||||
|
cfg, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
|
||||||
} else {
|
} else {
|
||||||
cfg, err = rest.InClusterConfig()
|
cfg, err = rest.InClusterConfig()
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cl, err := kubernetes.NewForConfig(cfg)
|
// Create the clientset.
|
||||||
if err != nil {
|
return kubernetes.NewForConfig(cfg)
|
||||||
return err
|
}
|
||||||
}
|
|
||||||
|
|
||||||
configMapList, err := cl.CoreV1().ConfigMaps(o.Namespace).List(ctx, metav1.ListOptions{
|
func overwriteDriverType(configDir string, driverType drivertype.DriverType) error {
|
||||||
LabelSelector: "app.kubernetes.io/instance: falco",
|
var falcoConfig falcoCfg
|
||||||
})
|
|
||||||
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 {
|
configDir = filepath.Join(configDir, "config.d")
|
||||||
Op string `json:"op"`
|
// First thing, check if config.d folder exists in the configuration directory.
|
||||||
Path string `json:"path"`
|
_, err := os.Stat(configDir)
|
||||||
Value string `json:"value"`
|
if os.IsNotExist(err) {
|
||||||
}
|
// Create it.
|
||||||
payload := []patchDriverTypeValue{{
|
// #nosec G301 -- under /etc we want 755 permissions
|
||||||
Op: "replace",
|
if err := os.MkdirAll(configDir, 0o755); err != nil {
|
||||||
Path: "/data/" + configMapEngineKindKey,
|
return fmt.Errorf("unable to create directory %s: %w", configDir, err)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
} else if err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
falcoConfig.Engine.Kind = driverType.String()
|
||||||
|
engineKind, err := yaml.Marshal(falcoConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to marshal falco config: %w", 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)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -25,30 +25,34 @@ import (
|
||||||
"github.com/falcosecurity/falcoctl/cmd"
|
"github.com/falcosecurity/falcoctl/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
var driverConfigHelp = `[Preview] Configure a driver for future usages with other driver subcommands.
|
//nolint:lll // no need to check for line length.
|
||||||
|
var driverConfigHelp = `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.
|
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.
|
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.
|
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:
|
Usage:
|
||||||
falcoctl driver config [flags]
|
falcoctl driver config [flags]
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
-h, --help help for config
|
--configmap string Falco configmap name.
|
||||||
--kubeconfig string Kubernetes config.
|
--falco-config-dir string Falco configuration directory. (default "/etc/falco")
|
||||||
--namespace string Kubernetes namespace.
|
-h, --help help for config
|
||||||
--update-falco Whether to update Falco config/configmap. (default true)
|
--kubeconfig string Kubernetes config.
|
||||||
|
--namespace string Kubernetes namespace.
|
||||||
|
--update-falco Whether to overwrite Falco configuration (default true)
|
||||||
|
|
||||||
Global Flags:
|
Global Flags:
|
||||||
--config string config file to be used for falcoctl (default "/etc/falcoctl/falcoctl.yaml")
|
--config string config file to be used for falcoctl (default "/etc/falcoctl/falcoctl.yaml")
|
||||||
--host-root string Driver host root to be used. (default "/")
|
--host-root string Driver host root to be used. (default "/")
|
||||||
--log-format string Set formatting for logs (color, text, json) (default "color")
|
--kernelrelease string Specify the kernel release for which to download/build the driver in the same format used by 'uname -r' (e.g. '6.1.0-10-cloud-amd64')
|
||||||
--log-level string Set level for logs (info, warn, debug, trace) (default "info")
|
--kernelversion string Specify the kernel version for which to download/build the driver in the same format used by 'uname -v' (e.g. '#1 SMP PREEMPT_DYNAMIC Debian 6.1.38-2 (2023-07-27)')
|
||||||
--name string Driver name to be used. (default "falco")
|
--log-format string Set formatting for logs (color, text, json) (default "color")
|
||||||
--repo strings Driver repo to be used. (default [https://download.falco.org/driver])
|
--log-level string Set level for logs (info, warn, debug, trace) (default "info")
|
||||||
--type string Driver type to be used (auto, ebpf, kmod, modern_ebpf) (default "kmod")
|
--name string Driver name to be used. (default "falco")
|
||||||
--version string Driver version to be used.
|
--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])
|
||||||
|
--version string Driver version to be used.
|
||||||
`
|
`
|
||||||
|
|
||||||
var addAssertFailedBehavior = func(specificError string) {
|
var addAssertFailedBehavior = func(specificError string) {
|
||||||
|
@ -99,8 +103,7 @@ var _ = Describe("config", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
args = []string{driverCmd, configCmd, "--config", configFile, "--type", "foo"}
|
args = []string{driverCmd, configCmd, "--config", configFile, "--type", "foo"}
|
||||||
})
|
})
|
||||||
addAssertFailedBehavior(`ERROR invalid argument "foo" for "--type" flag: invalid argument "foo",` +
|
addAssertFailedBehavior(`ERROR unsupported driver type specified: foo`)
|
||||||
` please provide one of (auto, ebpf, kmod, modern_ebpf)`)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -44,14 +44,18 @@ import (
|
||||||
// NewDriverCmd returns the driver command.
|
// NewDriverCmd returns the driver command.
|
||||||
func NewDriverCmd(ctx context.Context, opt *options.Common) *cobra.Command {
|
func NewDriverCmd(ctx context.Context, opt *options.Common) *cobra.Command {
|
||||||
driver := &options.Driver{}
|
driver := &options.Driver{}
|
||||||
driverTypes := options.NewDriverTypes()
|
driverTypesEnum := options.NewDriverTypes()
|
||||||
|
var (
|
||||||
|
driverTypesStr []string
|
||||||
|
driverKernelRelease string
|
||||||
|
driverKernelVersion string
|
||||||
|
)
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "driver",
|
Use: "driver",
|
||||||
DisableFlagsInUseLine: true,
|
DisableFlagsInUseLine: true,
|
||||||
Short: "[Preview] Interact with falcosecurity driver",
|
Short: "Interact with falcosecurity driver",
|
||||||
Long: `[Preview] 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 {
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
opt.Initialize()
|
opt.Initialize()
|
||||||
if err := config.Load(opt.ConfigFile); err != nil {
|
if err := config.Load(opt.ConfigFile); err != nil {
|
||||||
|
@ -115,44 +119,62 @@ func NewDriverCmd(ctx context.Context, opt *options.Common) *cobra.Command {
|
||||||
// should never happen
|
// should never happen
|
||||||
return fmt.Errorf("unable to retrieve flag type")
|
return fmt.Errorf("unable to retrieve flag type")
|
||||||
} else if !f.Changed && viper.IsSet(config.DriverTypeKey) {
|
} else if !f.Changed && viper.IsSet(config.DriverTypeKey) {
|
||||||
val := viper.Get(config.DriverTypeKey)
|
val, err := config.DriverTypes()
|
||||||
if err := cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val)); err != nil {
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := cmd.Flags().Set(f.Name, strings.Join(val, ",")); err != nil {
|
||||||
return fmt.Errorf("unable to overwrite \"type\" flag: %w", err)
|
return fmt.Errorf("unable to overwrite \"type\" flag: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if driverTypes.String() != drivertype.TypeAuto {
|
// Logic to discover correct driver to be used
|
||||||
var err error
|
// Step 1: build up allowed driver types
|
||||||
|
allowedDriverTypes := make([]drivertype.DriverType, 0)
|
||||||
|
for _, dTypeStr := range driverTypesStr {
|
||||||
// Ok driver type was enforced by the user
|
// Ok driver type was enforced by the user
|
||||||
driver.Type, err = drivertype.Parse(driverTypes.String())
|
drvType, err := drivertype.Parse(dTypeStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
allowedDriverTypes = append(allowedDriverTypes, drvType)
|
||||||
// automatic logic
|
opt.Printer.Logger.Debug("Allowed driver",
|
||||||
info, err := driverkernel.FetchInfo("", "")
|
opt.Printer.Logger.Args("type", drvType))
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
opt.Printer.Logger.Debug("Fetched kernel info", opt.Printer.Logger.Args(
|
|
||||||
"arch", info.Architecture.ToNonDeb(),
|
|
||||||
"kernel release", info.String(),
|
|
||||||
"kernel version", info.KernelVersion))
|
|
||||||
|
|
||||||
d, err := driverdistro.Discover(info, driver.HostRoot)
|
|
||||||
if err != nil {
|
|
||||||
if !errors.Is(err, driverdistro.ErrUnsupported) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
opt.Printer.Logger.Info("Detected an unsupported target system; falling back at generic logic.")
|
|
||||||
}
|
|
||||||
opt.Printer.Logger.Debug("Discovered distro", opt.Printer.Logger.Args("target", d))
|
|
||||||
|
|
||||||
driver.Type = d.PreferredDriver(info)
|
|
||||||
if driver.Type == nil {
|
|
||||||
return fmt.Errorf("automatic driver selection failed")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Step 2: fetch system info (kernel release/version and distro)
|
||||||
|
var err error
|
||||||
|
driver.Kr, err = driverkernel.FetchInfo(driverKernelRelease, driverKernelVersion)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
opt.Printer.Logger.Debug("Fetched kernel info", opt.Printer.Logger.Args(
|
||||||
|
"arch", driver.Kr.Architecture.ToNonDeb(),
|
||||||
|
"kernel release", driver.Kr.String(),
|
||||||
|
"kernel version", driver.Kr.KernelVersion))
|
||||||
|
|
||||||
|
driver.Distro, err = driverdistro.Discover(driver.Kr, driver.HostRoot)
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Is(err, driverdistro.ErrUnsupported) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
opt.Printer.Logger.Debug("Detected an unsupported target system; falling back at generic logic.")
|
||||||
|
}
|
||||||
|
opt.Printer.Logger.Debug("Discovered distro", opt.Printer.Logger.Args("target", driver.Distro))
|
||||||
|
|
||||||
|
driver.Type = driver.Distro.PreferredDriver(driver.Kr, allowedDriverTypes)
|
||||||
|
if driver.Type == nil {
|
||||||
|
return fmt.Errorf("no supported driver found for distro: %s, "+
|
||||||
|
"kernelrelease %s, "+
|
||||||
|
"kernelversion %s, "+
|
||||||
|
"arch %s",
|
||||||
|
driver.Distro.String(),
|
||||||
|
driver.Kr.String(),
|
||||||
|
driver.Kr.KernelVersion,
|
||||||
|
driver.Kr.Architecture.ToNonDeb())
|
||||||
|
}
|
||||||
|
opt.Printer.Logger.Debug("Detected supported driver", opt.Printer.Logger.Args("type", driver.Type.String()))
|
||||||
|
|
||||||
// If empty, try to load it automatically from /usr/src sub folders,
|
// If empty, try to load it automatically from /usr/src sub folders,
|
||||||
// using the most recent (ie: the one with greatest semver) driver version.
|
// using the most recent (ie: the one with greatest semver) driver version.
|
||||||
if driver.Version == "" {
|
if driver.Version == "" {
|
||||||
|
@ -162,11 +184,22 @@ func NewDriverCmd(ctx context.Context, opt *options.Common) *cobra.Command {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.PersistentFlags().Var(driverTypes, "type", "Driver type to be used "+driverTypes.Allowed())
|
cmd.PersistentFlags().StringSliceVar(&driverTypesStr, "type", config.DefaultDriver.Type,
|
||||||
|
"Driver types allowed in descending priority order "+driverTypesEnum.Allowed())
|
||||||
cmd.PersistentFlags().StringVar(&driver.Version, "version", config.DefaultDriver.Version, "Driver version to be used.")
|
cmd.PersistentFlags().StringVar(&driver.Version, "version", config.DefaultDriver.Version, "Driver version to be used.")
|
||||||
cmd.PersistentFlags().StringSliceVar(&driver.Repos, "repo", config.DefaultDriver.Repos, "Driver repo to be used.")
|
cmd.PersistentFlags().StringSliceVar(&driver.Repos, "repo", config.DefaultDriver.Repos, "Driver repo to be used.")
|
||||||
cmd.PersistentFlags().StringVar(&driver.Name, "name", config.DefaultDriver.Name, "Driver name to be used.")
|
cmd.PersistentFlags().StringVar(&driver.Name, "name", config.DefaultDriver.Name, "Driver name to be used.")
|
||||||
cmd.PersistentFlags().StringVar(&driver.HostRoot, "host-root", config.DefaultDriver.HostRoot, "Driver host root to be used.")
|
cmd.PersistentFlags().StringVar(&driver.HostRoot, "host-root", config.DefaultDriver.HostRoot, "Driver host root to be used.")
|
||||||
|
cmd.PersistentFlags().StringVar(&driverKernelRelease,
|
||||||
|
"kernelrelease",
|
||||||
|
"",
|
||||||
|
"Specify the kernel release for which to download/build the driver in the same format used by 'uname -r' "+
|
||||||
|
"(e.g. '6.1.0-10-cloud-amd64')")
|
||||||
|
cmd.PersistentFlags().StringVar(&driverKernelVersion,
|
||||||
|
"kernelversion",
|
||||||
|
"",
|
||||||
|
"Specify the kernel version for which to download/build the driver in the same format used by 'uname -v' "+
|
||||||
|
"(e.g. '#1 SMP PREEMPT_DYNAMIC Debian 6.1.38-2 (2023-07-27)')")
|
||||||
|
|
||||||
cmd.AddCommand(driverinstall.NewDriverInstallCmd(ctx, opt, driver))
|
cmd.AddCommand(driverinstall.NewDriverInstallCmd(ctx, opt, driver))
|
||||||
cmd.AddCommand(driverconfig.NewDriverConfigCmd(ctx, opt, driver))
|
cmd.AddCommand(driverconfig.NewDriverConfigCmd(ctx, opt, driver))
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -29,22 +29,21 @@ import (
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
driverdistro "github.com/falcosecurity/falcoctl/pkg/driver/distro"
|
driverdistro "github.com/falcosecurity/falcoctl/pkg/driver/distro"
|
||||||
driverkernel "github.com/falcosecurity/falcoctl/pkg/driver/kernel"
|
|
||||||
"github.com/falcosecurity/falcoctl/pkg/options"
|
"github.com/falcosecurity/falcoctl/pkg/options"
|
||||||
)
|
)
|
||||||
|
|
||||||
type driverDownloadOptions struct {
|
type driverDownloadOptions struct {
|
||||||
InsecureDownload bool
|
InsecureDownload bool
|
||||||
HTTPTimeout time.Duration
|
HTTPTimeout time.Duration
|
||||||
|
HTTPHeaders string
|
||||||
}
|
}
|
||||||
|
|
||||||
type driverInstallOptions struct {
|
type driverInstallOptions struct {
|
||||||
*options.Common
|
*options.Common
|
||||||
*options.Driver
|
*options.Driver
|
||||||
Download bool
|
Download bool
|
||||||
Compile bool
|
Compile bool
|
||||||
DriverKernelRelease string
|
DownloadHeaders bool
|
||||||
DriverKernelVersion string
|
|
||||||
driverDownloadOptions
|
driverDownloadOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,9 +60,8 @@ func NewDriverInstallCmd(ctx context.Context, opt *options.Common, driver *optio
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "install [flags]",
|
Use: "install [flags]",
|
||||||
DisableFlagsInUseLine: true,
|
DisableFlagsInUseLine: true,
|
||||||
Short: "[Preview] Install previously configured driver",
|
Short: "Install previously configured driver",
|
||||||
Long: `[Preview] 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 {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
dest, err := o.RunDriverInstall(ctx)
|
dest, err := o.RunDriverInstall(ctx)
|
||||||
if dest != "" {
|
if dest != "" {
|
||||||
|
@ -80,18 +78,13 @@ 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.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.Compile, "compile", true, "Whether to enable local compilation of drivers")
|
||||||
cmd.Flags().StringVar(&o.DriverKernelRelease,
|
cmd.Flags().BoolVar(&o.DownloadHeaders, "download-headers", true, "Whether to enable automatic kernel headers download where supported")
|
||||||
"kernelrelease",
|
|
||||||
"",
|
|
||||||
"Specify the kernel release for which to download/build the driver in the same format used by 'uname -r' "+
|
|
||||||
"(e.g. '6.1.0-10-cloud-amd64')")
|
|
||||||
cmd.Flags().StringVar(&o.DriverKernelVersion,
|
|
||||||
"kernelversion",
|
|
||||||
"",
|
|
||||||
"Specify the kernel version for which to download/build the driver in the same format used by 'uname -v' "+
|
|
||||||
"(e.g. '#1 SMP PREEMPT_DYNAMIC Debian 6.1.38-2 (2023-07-27)')")
|
|
||||||
cmd.Flags().BoolVar(&o.InsecureDownload, "http-insecure", false, "Whether you want to allow insecure downloads or not")
|
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().DurationVar(&o.HTTPTimeout, "http-timeout", 60*time.Second, "Timeout for each http try")
|
||||||
|
cmd.Flags().StringVar(&o.HTTPHeaders, "http-headers",
|
||||||
|
"",
|
||||||
|
"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")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,20 +99,16 @@ func setDefaultHTTPClientOpts(downloadOptions driverDownloadOptions) {
|
||||||
|
|
||||||
// RunDriverInstall implements the driver install command.
|
// RunDriverInstall implements the driver install command.
|
||||||
func (o *driverInstallOptions) RunDriverInstall(ctx context.Context) (string, error) {
|
func (o *driverInstallOptions) RunDriverInstall(ctx context.Context) (string, error) {
|
||||||
kr, err := driverkernel.FetchInfo(o.DriverKernelRelease, o.DriverKernelVersion)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
o.Printer.Logger.Info("Running falcoctl driver install", o.Printer.Logger.Args(
|
o.Printer.Logger.Info("Running falcoctl driver install", o.Printer.Logger.Args(
|
||||||
"driver version", o.Driver.Version,
|
"driver version", o.Driver.Version,
|
||||||
"driver type", o.Driver.Type,
|
"driver type", o.Driver.Type,
|
||||||
"driver name", o.Driver.Name,
|
"driver name", o.Driver.Name,
|
||||||
"compile", o.Compile,
|
"compile", o.Compile,
|
||||||
"download", o.Download,
|
"download", o.Download,
|
||||||
"arch", kr.Architecture.ToNonDeb(),
|
"target", o.Distro.String(),
|
||||||
"kernel release", kr.String(),
|
"arch", o.Kr.Architecture.ToNonDeb(),
|
||||||
"kernel version", kr.KernelVersion))
|
"kernel release", o.Kr.String(),
|
||||||
|
"kernel version", o.Kr.KernelVersion))
|
||||||
|
|
||||||
if !o.Driver.Type.HasArtifacts() {
|
if !o.Driver.Type.HasArtifacts() {
|
||||||
o.Printer.Logger.Info("No artifacts needed for the selected driver.")
|
o.Printer.Logger.Info("No artifacts needed for the selected driver.")
|
||||||
|
@ -131,9 +120,8 @@ func (o *driverInstallOptions) RunDriverInstall(ctx context.Context) (string, er
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
d, err := driverdistro.Discover(kr, o.Driver.HostRoot)
|
if o.Distro.String() == driverdistro.UndeterminedDistro {
|
||||||
if err != nil {
|
if o.Compile {
|
||||||
if errors.Is(err, driverdistro.ErrUnsupported) && o.Compile {
|
|
||||||
o.Download = false
|
o.Download = false
|
||||||
o.Printer.Logger.Info(
|
o.Printer.Logger.Info(
|
||||||
"Detected an unsupported target system, please get in touch with the Falco community. Trying to compile anyway.")
|
"Detected an unsupported target system, please get in touch with the Falco community. Trying to compile anyway.")
|
||||||
|
@ -141,7 +129,6 @@ func (o *driverInstallOptions) RunDriverInstall(ctx context.Context) (string, er
|
||||||
return "", fmt.Errorf("detected an unsupported target system, please get in touch with the Falco community")
|
return "", fmt.Errorf("detected an unsupported target system, please get in touch with the Falco community")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
o.Printer.Logger.Info("Found distro", o.Printer.Logger.Args("target", d))
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
dest string
|
dest string
|
||||||
|
@ -151,14 +138,14 @@ func (o *driverInstallOptions) RunDriverInstall(ctx context.Context) (string, er
|
||||||
if !o.Printer.DisableStyling {
|
if !o.Printer.DisableStyling {
|
||||||
o.Printer.Spinner, _ = o.Printer.Spinner.Start("Cleaning up existing drivers")
|
o.Printer.Spinner, _ = o.Printer.Spinner.Start("Cleaning up existing drivers")
|
||||||
}
|
}
|
||||||
err = o.Driver.Type.Cleanup(o.Printer.WithWriter(&buf), o.Driver.Name)
|
err := o.Driver.Type.Cleanup(o.Printer.WithWriter(&buf), o.Driver.Name)
|
||||||
if o.Printer.Spinner != nil {
|
if o.Printer.Spinner != nil {
|
||||||
_ = o.Printer.Spinner.Stop()
|
_ = o.Printer.Spinner.Stop()
|
||||||
}
|
}
|
||||||
if o.Printer.Logger.Formatter == pterm.LogFormatterJSON {
|
if o.Printer.Logger.Formatter == pterm.LogFormatterJSON {
|
||||||
// Only print formatted text if we are formatting to json
|
// Only print formatted text if we are formatting to json
|
||||||
out := strings.ReplaceAll(buf.String(), "\n", ";")
|
out := strings.ReplaceAll(buf.String(), "\n", ";")
|
||||||
o.Printer.Logger.Info("Driver build", o.Printer.Logger.Args("output", out))
|
o.Printer.Logger.Info("Driver cleanup", o.Printer.Logger.Args("output", out))
|
||||||
} else {
|
} else {
|
||||||
// Print much more readable output as-is
|
// Print much more readable output as-is
|
||||||
o.Printer.DefaultText.Print(buf.String())
|
o.Printer.DefaultText.Print(buf.String())
|
||||||
|
@ -173,14 +160,15 @@ func (o *driverInstallOptions) RunDriverInstall(ctx context.Context) (string, er
|
||||||
if !o.Printer.DisableStyling {
|
if !o.Printer.DisableStyling {
|
||||||
o.Printer.Spinner, _ = o.Printer.Spinner.Start("Trying to download the driver")
|
o.Printer.Spinner, _ = o.Printer.Spinner.Start("Trying to download the driver")
|
||||||
}
|
}
|
||||||
dest, err = driverdistro.Download(ctx, d, o.Printer.WithWriter(&buf), kr, o.Driver.Name, o.Driver.Type, o.Driver.Version, o.Driver.Repos)
|
dest, err = driverdistro.Download(ctx, o.Distro, o.Printer.WithWriter(&buf), o.Kr, o.Driver.Name,
|
||||||
|
o.Driver.Type, o.Driver.Version, o.Driver.Repos, o.HTTPHeaders)
|
||||||
if o.Printer.Spinner != nil {
|
if o.Printer.Spinner != nil {
|
||||||
_ = o.Printer.Spinner.Stop()
|
_ = o.Printer.Spinner.Stop()
|
||||||
}
|
}
|
||||||
if o.Printer.Logger.Formatter == pterm.LogFormatterJSON {
|
if o.Printer.Logger.Formatter == pterm.LogFormatterJSON {
|
||||||
// Only print formatted text if we are formatting to json
|
// Only print formatted text if we are formatting to json
|
||||||
out := strings.ReplaceAll(buf.String(), "\n", ";")
|
out := strings.ReplaceAll(buf.String(), "\n", ";")
|
||||||
o.Printer.Logger.Info("Driver build", o.Printer.Logger.Args("output", out))
|
o.Printer.Logger.Info("Driver download", o.Printer.Logger.Args("output", out))
|
||||||
} else {
|
} else {
|
||||||
// Print much more readable output as-is
|
// Print much more readable output as-is
|
||||||
o.Printer.DefaultText.Print(buf.String())
|
o.Printer.DefaultText.Print(buf.String())
|
||||||
|
@ -205,7 +193,7 @@ func (o *driverInstallOptions) RunDriverInstall(ctx context.Context) (string, er
|
||||||
if !o.Printer.DisableStyling {
|
if !o.Printer.DisableStyling {
|
||||||
o.Printer.Spinner, _ = o.Printer.Spinner.Start("Trying to build the driver")
|
o.Printer.Spinner, _ = o.Printer.Spinner.Start("Trying to build the driver")
|
||||||
}
|
}
|
||||||
dest, err = driverdistro.Build(ctx, d, o.Printer.WithWriter(&buf), kr, o.Driver.Name, o.Driver.Type, o.Driver.Version)
|
dest, err = driverdistro.Build(ctx, o.Distro, o.Printer.WithWriter(&buf), o.Kr, o.Driver.Name, o.Driver.Type, o.Driver.Version, o.DownloadHeaders)
|
||||||
if o.Printer.Spinner != nil {
|
if o.Printer.Spinner != nil {
|
||||||
_ = o.Printer.Spinner.Stop()
|
_ = o.Printer.Spinner.Stop()
|
||||||
}
|
}
|
||||||
|
@ -219,7 +207,6 @@ func (o *driverInstallOptions) RunDriverInstall(ctx context.Context) (string, er
|
||||||
}
|
}
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
o.Printer.Logger.Info("Driver built.", o.Printer.Logger.Args("path", dest))
|
|
||||||
return dest, nil
|
return dest, nil
|
||||||
}
|
}
|
||||||
if errors.Is(err, driverdistro.ErrAlreadyPresent) {
|
if errors.Is(err, driverdistro.ErrAlreadyPresent) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -26,8 +26,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
//nolint:lll // no need to check for line length.
|
//nolint:lll // no need to check for line length.
|
||||||
var driverInstallHelp = `[Preview] Install previously configured driver, either downloading it or attempting a build.
|
var driverInstallHelp = `Install previously configured driver, either downloading it or attempting a build.
|
||||||
** This command is in preview and under development. **
|
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
falcoctl driver install [flags]
|
falcoctl driver install [flags]
|
||||||
|
@ -35,21 +34,23 @@ Usage:
|
||||||
Flags:
|
Flags:
|
||||||
--compile Whether to enable local compilation of drivers (default true)
|
--compile Whether to enable local compilation of drivers (default true)
|
||||||
--download Whether to enable download of prebuilt 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
|
-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
|
--http-insecure Whether you want to allow insecure downloads or not
|
||||||
--http-timeout duration Timeout for each http try (default 1m0s)
|
--http-timeout duration Timeout for each http try (default 1m0s)
|
||||||
--kernelrelease string Specify the kernel release for which to download/build the driver in the same format used by 'uname -r' (e.g. '6.1.0-10-cloud-amd64')
|
|
||||||
--kernelversion string Specify the kernel version for which to download/build the driver in the same format used by 'uname -v' (e.g. '#1 SMP PREEMPT_DYNAMIC Debian 6.1.38-2 (2023-07-27)')
|
|
||||||
|
|
||||||
Global Flags:
|
Global Flags:
|
||||||
--config string config file to be used for falcoctl (default "/etc/falcoctl/falcoctl.yaml")
|
--config string config file to be used for falcoctl (default "/etc/falcoctl/falcoctl.yaml")
|
||||||
--host-root string Driver host root to be used. (default "/")
|
--host-root string Driver host root to be used. (default "/")
|
||||||
--log-format string Set formatting for logs (color, text, json) (default "color")
|
--kernelrelease string Specify the kernel release for which to download/build the driver in the same format used by 'uname -r' (e.g. '6.1.0-10-cloud-amd64')
|
||||||
--log-level string Set level for logs (info, warn, debug, trace) (default "info")
|
--kernelversion string Specify the kernel version for which to download/build the driver in the same format used by 'uname -v' (e.g. '#1 SMP PREEMPT_DYNAMIC Debian 6.1.38-2 (2023-07-27)')
|
||||||
--name string Driver name to be used. (default "falco")
|
--log-format string Set formatting for logs (color, text, json) (default "color")
|
||||||
--repo strings Driver repo to be used. (default [https://download.falco.org/driver])
|
--log-level string Set level for logs (info, warn, debug, trace) (default "info")
|
||||||
--type string Driver type to be used (auto, ebpf, kmod, modern_ebpf) (default "kmod")
|
--name string Driver name to be used. (default "falco")
|
||||||
--version string Driver version to be used.
|
--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])
|
||||||
|
--version string Driver version to be used.
|
||||||
`
|
`
|
||||||
|
|
||||||
var addAssertFailedBehavior = func(specificError string) {
|
var addAssertFailedBehavior = func(specificError string) {
|
||||||
|
@ -114,8 +115,7 @@ var _ = Describe("install", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
args = []string{driverCmd, installCmd, "--config", configFile, "--type", "foo", "--version", "1.0.0+driver"}
|
args = []string{driverCmd, installCmd, "--config", configFile, "--type", "foo", "--version", "1.0.0+driver"}
|
||||||
})
|
})
|
||||||
addAssertFailedBehavior(`ERROR invalid argument "foo" for "--type" flag: invalid argument "foo",` +
|
addAssertFailedBehavior(`ERROR unsupported driver type specified: foo`)
|
||||||
` please provide one of (auto, ebpf, kmod, modern_ebpf)`)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -16,14 +16,11 @@
|
||||||
package driverprintenv
|
package driverprintenv
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
driverdistro "github.com/falcosecurity/falcoctl/pkg/driver/distro"
|
|
||||||
driverkernel "github.com/falcosecurity/falcoctl/pkg/driver/kernel"
|
|
||||||
"github.com/falcosecurity/falcoctl/pkg/options"
|
"github.com/falcosecurity/falcoctl/pkg/options"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -42,10 +39,9 @@ func NewDriverPrintenvCmd(ctx context.Context, opt *options.Common, driver *opti
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "printenv [flags]",
|
Use: "printenv [flags]",
|
||||||
DisableFlagsInUseLine: true,
|
DisableFlagsInUseLine: true,
|
||||||
Short: "[Preview] Print env vars",
|
Short: "Print env vars",
|
||||||
Long: `[Preview] 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 {
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
return o.RunDriverPrintenv(ctx)
|
return o.RunDriverPrintenv(ctx)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -58,27 +54,12 @@ func (o *driverPrintenvOptions) RunDriverPrintenv(_ context.Context) error {
|
||||||
o.Printer.DefaultText.Printf("DRIVER_VERSION=%q\n", o.Driver.Version)
|
o.Printer.DefaultText.Printf("DRIVER_VERSION=%q\n", o.Driver.Version)
|
||||||
o.Printer.DefaultText.Printf("DRIVER_NAME=%q\n", o.Driver.Name)
|
o.Printer.DefaultText.Printf("DRIVER_NAME=%q\n", o.Driver.Name)
|
||||||
o.Printer.DefaultText.Printf("HOST_ROOT=%q\n", o.Driver.HostRoot)
|
o.Printer.DefaultText.Printf("HOST_ROOT=%q\n", o.Driver.HostRoot)
|
||||||
|
o.Printer.DefaultText.Printf("TARGET_ID=%q\n", o.Distro.String())
|
||||||
kr, err := driverkernel.FetchInfo("", "")
|
o.Printer.DefaultText.Printf("ARCH=%q\n", o.Kr.Architecture.ToNonDeb())
|
||||||
if err != nil {
|
o.Printer.DefaultText.Printf("KERNEL_RELEASE=%q\n", o.Kr.String())
|
||||||
return err
|
o.Printer.DefaultText.Printf("KERNEL_VERSION=%q\n", o.Kr.KernelVersion)
|
||||||
}
|
fixedKr := o.Distro.FixupKernel(o.Kr)
|
||||||
|
|
||||||
d, err := driverdistro.Discover(kr, o.Driver.HostRoot)
|
|
||||||
if err != nil {
|
|
||||||
if !errors.Is(err, driverdistro.ErrUnsupported) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
o.Printer.DefaultText.Printf("TARGET_ID=%q\n", d.String())
|
|
||||||
|
|
||||||
o.Printer.DefaultText.Printf("ARCH=%q\n", kr.Architecture.ToNonDeb())
|
|
||||||
o.Printer.DefaultText.Printf("KERNEL_RELEASE=%q\n", kr.String())
|
|
||||||
o.Printer.DefaultText.Printf("KERNEL_VERSION=%q\n", kr.KernelVersion)
|
|
||||||
|
|
||||||
fixedKr := d.FixupKernel(kr)
|
|
||||||
o.Printer.DefaultText.Printf("FIXED_KERNEL_RELEASE=%q\n", fixedKr.String())
|
o.Printer.DefaultText.Printf("FIXED_KERNEL_RELEASE=%q\n", fixedKr.String())
|
||||||
o.Printer.DefaultText.Printf("FIXED_KERNEL_VERSION=%q\n", fixedKr.KernelVersion)
|
o.Printer.DefaultText.Printf("FIXED_KERNEL_VERSION=%q\n", fixedKr.KernelVersion)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -28,8 +28,8 @@ import (
|
||||||
"github.com/falcosecurity/falcoctl/cmd"
|
"github.com/falcosecurity/falcoctl/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
var driverPrintenvHelp = `[Preview] Print variables used by driver as env vars.
|
//nolint:lll // no need to check for line length.
|
||||||
** This command is in preview and under development. **
|
var driverPrintenvHelp = `Print variables used by driver as env vars.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
falcoctl driver printenv [flags]
|
falcoctl driver printenv [flags]
|
||||||
|
@ -38,17 +38,19 @@ Flags:
|
||||||
-h, --help help for printenv
|
-h, --help help for printenv
|
||||||
|
|
||||||
Global Flags:
|
Global Flags:
|
||||||
--config string config file to be used for falcoctl (default "/etc/falcoctl/falcoctl.yaml")
|
--config string config file to be used for falcoctl (default "/etc/falcoctl/falcoctl.yaml")
|
||||||
--host-root string Driver host root to be used. (default "/")
|
--host-root string Driver host root to be used. (default "/")
|
||||||
--log-format string Set formatting for logs (color, text, json) (default "color")
|
--kernelrelease string Specify the kernel release for which to download/build the driver in the same format used by 'uname -r' (e.g. '6.1.0-10-cloud-amd64')
|
||||||
--log-level string Set level for logs (info, warn, debug, trace) (default "info")
|
--kernelversion string Specify the kernel version for which to download/build the driver in the same format used by 'uname -v' (e.g. '#1 SMP PREEMPT_DYNAMIC Debian 6.1.38-2 (2023-07-27)')
|
||||||
--name string Driver name to be used. (default "falco")
|
--log-format string Set formatting for logs (color, text, json) (default "color")
|
||||||
--repo strings Driver repo to be used. (default [https://download.falco.org/driver])
|
--log-level string Set level for logs (info, warn, debug, trace) (default "info")
|
||||||
--type string Driver type to be used (auto, ebpf, kmod, modern_ebpf) (default "kmod")
|
--name string Driver name to be used. (default "falco")
|
||||||
--version string Driver version to be used.
|
--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])
|
||||||
|
--version string Driver version to be used.
|
||||||
`
|
`
|
||||||
|
|
||||||
var driverPrintenvDefaultConfig = `DRIVER="kmod"
|
var driverPrintenvDefaultConfig = `DRIVER=".*"
|
||||||
DRIVERS_REPO="https:\/\/download\.falco\.org\/driver"
|
DRIVERS_REPO="https:\/\/download\.falco\.org\/driver"
|
||||||
DRIVER_VERSION="1.0.0\+driver"
|
DRIVER_VERSION="1.0.0\+driver"
|
||||||
DRIVER_NAME="falco"
|
DRIVER_NAME="falco"
|
||||||
|
@ -116,8 +118,7 @@ var _ = Describe("printenv", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
args = []string{driverCmd, printenvCmd, "--config", configFile, "--type", "foo", "--version", "1.0.0+driver"}
|
args = []string{driverCmd, printenvCmd, "--config", configFile, "--type", "foo", "--version", "1.0.0+driver"}
|
||||||
})
|
})
|
||||||
addAssertFailedBehavior(`ERROR invalid argument "foo" for "--type" flag: invalid argument "foo",` +
|
addAssertFailedBehavior(`unsupported driver type specified: foo`)
|
||||||
` please provide one of (auto, ebpf, kmod, modern_ebpf)`)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -16,21 +16,31 @@
|
||||||
package basic
|
package basic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
credentials "github.com/oras-project/oras-credentials-go"
|
|
||||||
"github.com/spf13/cobra"
|
"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"
|
"github.com/falcosecurity/falcoctl/internal/config"
|
||||||
"github.com/falcosecurity/falcoctl/internal/login/basic"
|
"github.com/falcosecurity/falcoctl/internal/login/basic"
|
||||||
"github.com/falcosecurity/falcoctl/internal/utils"
|
"github.com/falcosecurity/falcoctl/internal/utils"
|
||||||
"github.com/falcosecurity/falcoctl/pkg/oci/authn"
|
"github.com/falcosecurity/falcoctl/pkg/oci/authn"
|
||||||
"github.com/falcosecurity/falcoctl/pkg/options"
|
"github.com/falcosecurity/falcoctl/pkg/options"
|
||||||
|
"github.com/falcosecurity/falcoctl/pkg/output"
|
||||||
)
|
)
|
||||||
|
|
||||||
type loginOptions struct {
|
type loginOptions struct {
|
||||||
*options.Common
|
*options.Common
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
passwordFromStdin bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBasicCmd returns the basic command.
|
// NewBasicCmd returns the basic command.
|
||||||
|
@ -43,22 +53,56 @@ func NewBasicCmd(ctx context.Context, opt *options.Common) *cobra.Command {
|
||||||
Use: "basic [hostname]",
|
Use: "basic [hostname]",
|
||||||
DisableFlagsInUseLine: true,
|
DisableFlagsInUseLine: true,
|
||||||
Short: "Login to an OCI registry",
|
Short: "Login to an OCI registry",
|
||||||
Long: "Login to an OCI registry to push and pull artifacts",
|
Long: `Login to an OCI registry
|
||||||
Args: cobra.ExactArgs(1),
|
|
||||||
|
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
|
||||||
|
},
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return o.RunBasic(ctx, args)
|
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
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunBasic executes the business logic for the basic command.
|
// RunBasic executes the business logic for the basic command.
|
||||||
func (o *loginOptions) RunBasic(ctx context.Context, args []string) error {
|
func (o *loginOptions) RunBasic(ctx context.Context, args []string) error {
|
||||||
reg := args[0]
|
var reg string
|
||||||
logger := o.Printer.Logger
|
logger := o.Printer.Logger
|
||||||
user, token, err := utils.GetCredentials(o.Printer)
|
|
||||||
|
// Allow to have the registry expressed as a ref, but actually extract it.
|
||||||
|
reg, err := utils.GetRegistryFromRef(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
reg = args[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := getCredentials(o.Printer, o); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,11 +117,46 @@ func (o *loginOptions) RunBasic(ctx context.Context, args []string) error {
|
||||||
return fmt.Errorf("unable to create new store: %w", err)
|
return fmt.Errorf("unable to create new store: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := basic.Login(ctx, client, credentialStore, reg, user, token); err != nil {
|
if err := basic.Login(ctx, client, credentialStore, reg, o.username, o.password); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Debug("Credentials added", logger.Args("credential store", config.RegistryCredentialConfPath()))
|
logger.Debug("Credentials added", logger.Args("credential store", config.RegistryCredentialConfPath()))
|
||||||
logger.Info("Login succeeded", logger.Args("registry", reg, "user", user))
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,34 @@ Global Flags:
|
||||||
`
|
`
|
||||||
|
|
||||||
//nolint:unused // false positive
|
//nolint:unused // false positive
|
||||||
var registryAuthBasicHelp = `Login to an OCI registry to push and pull artifacts`
|
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")
|
||||||
|
`
|
||||||
|
|
||||||
//nolint:unused // false positive
|
//nolint:unused // false positive
|
||||||
var registryAuthBasicAssertFailedBehavior = func(usage, specificError string) {
|
var registryAuthBasicAssertFailedBehavior = func(usage, specificError string) {
|
||||||
|
|
|
@ -297,7 +297,7 @@ var registryPullTests = Describe("pull", func() {
|
||||||
args = []string{registryCmd, pullCmd, newReg, "--plain-http", "--config", configFile}
|
args = []string{registryCmd, pullCmd, newReg, "--plain-http", "--config", configFile}
|
||||||
})
|
})
|
||||||
pullAssertFailedBehavior(registryPullUsage, fmt.Sprintf("ERROR unable to create new repository with ref %s: "+
|
pullAssertFailedBehavior(registryPullUsage, fmt.Sprintf("ERROR unable to create new repository with ref %s: "+
|
||||||
"invalid reference: invalid digest; invalid checksum digest format\n", newReg))
|
"invalid reference: invalid digest %q: invalid checksum digest format\n", newReg, "something"))
|
||||||
})
|
})
|
||||||
|
|
||||||
When("invalid platform", func() {
|
When("invalid platform", func() {
|
||||||
|
|
|
@ -22,7 +22,10 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/blang/semver/v4"
|
||||||
|
"github.com/pterm/pterm"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
"github.com/falcosecurity/falcoctl/internal/utils"
|
"github.com/falcosecurity/falcoctl/internal/utils"
|
||||||
"github.com/falcosecurity/falcoctl/pkg/oci"
|
"github.com/falcosecurity/falcoctl/pkg/oci"
|
||||||
|
@ -49,6 +52,10 @@ Example - Push artifact "myplugin.tar.gz" of type "plugin" for multiple platform
|
||||||
Example - Push artifact "myrulesfile.tar.gz" of type "rulesfile":
|
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
|
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:
|
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
|
falcoctl registry push --type rulesfile --version "0.1.2" --plain-http localhost:5000/myrulesfile:latest myrulesfile.tar.gz
|
||||||
|
|
||||||
|
@ -73,7 +80,7 @@ type pushOptions struct {
|
||||||
*options.Registry
|
*options.Registry
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o pushOptions) validate() error {
|
func (o *pushOptions) validate() error {
|
||||||
return o.Artifact.Validate()
|
return o.Artifact.Validate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,8 +127,8 @@ func (o *pushOptions) runPush(ctx context.Context, args []string) error {
|
||||||
ref := args[0]
|
ref := args[0]
|
||||||
paths := args[1:]
|
paths := args[1:]
|
||||||
// When creating the tar.gz archives we need to remove them after we are done.
|
// When creating the tar.gz archives we need to remove them after we are done.
|
||||||
// We save the temporary dir where they live here.
|
// Holds the path for each temporary dir.
|
||||||
var toBeDeleted string
|
var toBeDeletedTmpDirs []string
|
||||||
logger := o.Printer.Logger
|
logger := o.Printer.Logger
|
||||||
|
|
||||||
registry, err := utils.GetRegistryFromRef(ref)
|
registry, err := utils.GetRegistryFromRef(ref)
|
||||||
|
@ -141,35 +148,41 @@ func (o *pushOptions) runPush(ctx context.Context, args []string) error {
|
||||||
|
|
||||||
logger.Info("Preparing to push artifact", o.Printer.Logger.Args("name", args[0], "type", o.ArtifactType))
|
logger.Info("Preparing to push artifact", o.Printer.Logger.Args("name", args[0], "type", o.ArtifactType))
|
||||||
|
|
||||||
// Make sure to remove temporary working dir.
|
// Make sure to remove temporary working dirs.
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := os.RemoveAll(toBeDeleted); err != nil {
|
for _, dir := range toBeDeletedTmpDirs {
|
||||||
logger.Warn("Unable to remove temporary dir", logger.Args("name", toBeDeleted, "error", err.Error()))
|
logger.Debug("Removing temporary dir", logger.Args("name", dir))
|
||||||
|
if err := os.RemoveAll(dir); err != nil {
|
||||||
|
logger.Warn("Unable to remove temporary dir", logger.Args("name", dir, "error", err.Error()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
config := &oci.ArtifactConfig{
|
||||||
|
Name: o.Name,
|
||||||
|
Version: o.Version,
|
||||||
|
}
|
||||||
|
|
||||||
for i, p := range paths {
|
for i, p := range paths {
|
||||||
if err = utils.IsTarGz(filepath.Clean(p)); err != nil && !errors.Is(err, utils.ErrNotTarGz) {
|
if err = utils.IsTarGz(filepath.Clean(p)); err != nil && !errors.Is(err, utils.ErrNotTarGz) {
|
||||||
return err
|
return err
|
||||||
} else if err == nil {
|
} else if err == nil {
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
path, err := utils.CreateTarGzArchive(p)
|
if o.ArtifactType == oci.Rulesfile {
|
||||||
|
if config, err = rulesConfigLayer(o.Printer.Logger, p, o.Artifact); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
path, err := utils.CreateTarGzArchive("", p, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
paths[i] = path
|
paths[i] = path
|
||||||
if toBeDeleted == "" {
|
toBeDeletedTmpDirs = append(toBeDeletedTmpDirs, filepath.Dir(path))
|
||||||
toBeDeleted = filepath.Dir(path)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup OCI artifact configuration
|
|
||||||
config := oci.ArtifactConfig{
|
|
||||||
Name: o.Name,
|
|
||||||
Version: o.Version,
|
|
||||||
}
|
|
||||||
if config.Name == "" {
|
if config.Name == "" {
|
||||||
// extract artifact name from ref, if not provided by the user
|
// extract artifact name from ref, if not provided by the user
|
||||||
if config.Name, err = utils.NameFromRef(ref); err != nil {
|
if config.Name, err = utils.NameFromRef(ref); err != nil {
|
||||||
|
@ -183,10 +196,18 @@ func (o *pushOptions) runPush(ctx context.Context, args []string) error {
|
||||||
return err
|
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{
|
opts := ocipusher.Options{
|
||||||
ocipusher.WithTags(o.Tags...),
|
ocipusher.WithTags(o.Tags...),
|
||||||
ocipusher.WithAnnotationSource(o.AnnotationSource),
|
ocipusher.WithAnnotationSource(o.AnnotationSource),
|
||||||
ocipusher.WithArtifactConfig(config),
|
ocipusher.WithArtifactConfig(*config),
|
||||||
}
|
}
|
||||||
|
|
||||||
switch o.ArtifactType {
|
switch o.ArtifactType {
|
||||||
|
@ -203,7 +224,120 @@ func (o *pushOptions) runPush(ctx context.Context, args []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info("Artifact pushed", logger.Args("name", args[0], "type", res.Type, "digest", res.Digest))
|
logger.Info("Artifact pushed", logger.Args("name", args[0], "type", res.Type, "digest", res.RootDigest))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// depsKey is the key for deps in the rulesfiles.
|
||||||
|
depsKey = "required_plugin_versions"
|
||||||
|
// engineKey is the key in the rulesfiles.
|
||||||
|
engineKey = "required_engine_version"
|
||||||
|
// engineRequirementKey is used as name for the engine requirement in the config layer for the rulesfile artifacts.
|
||||||
|
engineRequirementKey = "engine_version_semver"
|
||||||
|
)
|
||||||
|
|
||||||
|
func rulesConfigLayer(logger *pterm.Logger, filePath string, artifactOptions *options.Artifact) (*oci.ArtifactConfig, error) {
|
||||||
|
var data []map[string]interface{}
|
||||||
|
|
||||||
|
// Setup OCI artifact configuration
|
||||||
|
config := oci.ArtifactConfig{
|
||||||
|
Name: artifactOptions.Name,
|
||||||
|
Version: artifactOptions.Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
yamlFile, err := os.ReadFile(filepath.Clean(filePath))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to open rulesfile %s: %w", filePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := yaml.Unmarshal(yamlFile, &data); err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to unmarshal rulesfile %s: %w", filePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the artifact dependencies.
|
||||||
|
// Check if the user has provided any.
|
||||||
|
if len(artifactOptions.Dependencies) != 0 {
|
||||||
|
logger.Info("Dependencies provided by user", logger.Args("rulesfile", filePath))
|
||||||
|
if err = config.ParseDependencies(artifactOptions.Dependencies...); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If no user provided then try to parse them from the rulesfile.
|
||||||
|
var found bool
|
||||||
|
logger.Info("Parsing dependencies from: ", logger.Args("rulesfile", filePath))
|
||||||
|
var requiredPluginVersionsEntry interface{}
|
||||||
|
var ok bool
|
||||||
|
for _, entry := range data {
|
||||||
|
if requiredPluginVersionsEntry, ok = entry[depsKey]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var deps []oci.ArtifactDependency
|
||||||
|
byteData, err := yaml.Marshal(requiredPluginVersionsEntry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse dependencies from rulesfile: %w", err)
|
||||||
|
}
|
||||||
|
err = yaml.Unmarshal(byteData, &deps)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse dependencies from rulesfile: %w", err)
|
||||||
|
}
|
||||||
|
logger.Info("Dependencies correctly parsed from rulesfile")
|
||||||
|
// Set the deps.
|
||||||
|
config.Dependencies = deps
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
logger.Warn("No dependencies were provided by the user and none were found in the rulesfile.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the requirements.
|
||||||
|
// Check if the user has provided any.
|
||||||
|
if len(artifactOptions.Requirements) != 0 {
|
||||||
|
logger.Info("Requirements provided by user")
|
||||||
|
if err = config.ParseRequirements(artifactOptions.Requirements...); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var found bool
|
||||||
|
var engineVersion string
|
||||||
|
logger.Info("Parsing requirements from: ", logger.Args("rulesfile", filePath))
|
||||||
|
// If no user provided requirements then try to parse them from the rulesfile.
|
||||||
|
for _, entry := range data {
|
||||||
|
if requiredEngineVersionEntry, ok := entry[engineKey]; ok {
|
||||||
|
// Check if the version is an int. This is for backward compatibility. The engine version used to be an
|
||||||
|
// int but internally used by falco as a semver minor version.
|
||||||
|
// 15 -> 0.15.0
|
||||||
|
if engVersionInt, ok := requiredEngineVersionEntry.(int); ok {
|
||||||
|
engineVersion = fmt.Sprintf("0.%d.0", engVersionInt)
|
||||||
|
} else {
|
||||||
|
engineVersion, ok = requiredEngineVersionEntry.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("%s must be an int or a string respecting the semver specification, got type %T", engineKey, requiredEngineVersionEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it is in semver format.
|
||||||
|
if _, err := semver.Parse(engineVersion); err != nil {
|
||||||
|
return nil, fmt.Errorf("%s must be in semver format: %w", engineVersion, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the requirements.
|
||||||
|
config.Requirements = []oci.ArtifactRequirement{{
|
||||||
|
Name: engineRequirementKey,
|
||||||
|
Version: engineVersion,
|
||||||
|
}}
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
logger.Warn("No requirements were provided by the user and none were found in the rulesfile.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &config, nil
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,217 @@
|
||||||
|
// 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 push_test
|
||||||
|
|
||||||
|
// revive:disable
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
"github.com/onsi/gomega/gbytes"
|
||||||
|
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
|
||||||
|
"github.com/falcosecurity/falcoctl/cmd"
|
||||||
|
"github.com/falcosecurity/falcoctl/internal/utils"
|
||||||
|
"github.com/falcosecurity/falcoctl/pkg/oci"
|
||||||
|
testutils "github.com/falcosecurity/falcoctl/pkg/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
// revive:enable
|
||||||
|
var _ = Describe("pushing plugins", func() {
|
||||||
|
var (
|
||||||
|
registryCmd = "registry"
|
||||||
|
pushCmd = "push"
|
||||||
|
version = "1.1.1"
|
||||||
|
// fullRepoName is set each time before each test.
|
||||||
|
fullRepoName string
|
||||||
|
// repoName same as fullRepoName.
|
||||||
|
repoName string
|
||||||
|
// It is set in the config layer.
|
||||||
|
artifactNameInConfigLayer = "test-push-plugins"
|
||||||
|
pushedTags = []string{"tag1", "tag2", "latest"}
|
||||||
|
|
||||||
|
// Plugin's platforms.
|
||||||
|
platformARM64 = "linux/arm64"
|
||||||
|
platformAMD64 = "linux/amd64"
|
||||||
|
|
||||||
|
// Paths pointing to plugins that will be pushed.
|
||||||
|
// Some of the functions expect these two variable to be set to valid paths.
|
||||||
|
// They are set in beforeEach blocks by tests that need them.
|
||||||
|
pluginOne string
|
||||||
|
pluginTwo string
|
||||||
|
// Data fetched from registry and used for assertions.
|
||||||
|
pluginData *testutils.PluginArtifact
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Used as flags for all the test cases.
|
||||||
|
requirement = "plugin_api_version:3.2.1"
|
||||||
|
anSource = "myrepo.com/rules.git"
|
||||||
|
pluginsRepoBaseName = "push-plugins-tests"
|
||||||
|
)
|
||||||
|
|
||||||
|
var AssertSuccessBehaviour = func(deps []oci.ArtifactDependency, reqs []oci.ArtifactRequirement, annotations map[string]string, platforms []string) {
|
||||||
|
It("should succeed", func() {
|
||||||
|
// We do not check the error here since we are checking it after
|
||||||
|
// pushing the artifact.
|
||||||
|
By("checking no error in output")
|
||||||
|
Expect(output).ShouldNot(gbytes.Say("ERROR"))
|
||||||
|
Expect(output).ShouldNot(gbytes.Say("Unable to remove temporary dir"))
|
||||||
|
|
||||||
|
By("checking descriptor")
|
||||||
|
Expect(pluginData.Descriptor.MediaType).Should(Equal(v1.MediaTypeImageIndex))
|
||||||
|
Expect(output).Should(gbytes.Say(regexp.QuoteMeta(pluginData.Descriptor.Digest.String())))
|
||||||
|
|
||||||
|
By("checking index")
|
||||||
|
Expect(pluginData.Index.Manifests).Should(HaveLen(len(platforms)))
|
||||||
|
|
||||||
|
By("checking platforms")
|
||||||
|
for _, p := range platforms {
|
||||||
|
Expect(pluginData.Platforms).Should(HaveKey(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
By("checking config layers")
|
||||||
|
for plat, p := range pluginData.Platforms {
|
||||||
|
By(fmt.Sprintf("platform %s", plat))
|
||||||
|
Expect(p.Config.Version).Should(Equal(version))
|
||||||
|
Expect(p.Config.Name).Should(Equal(artifactNameInConfigLayer))
|
||||||
|
|
||||||
|
By("checking dependencies")
|
||||||
|
Expect(p.Config.Dependencies).Should(HaveLen(len(deps)))
|
||||||
|
for _, dep := range deps {
|
||||||
|
Expect(p.Config.Dependencies).Should(ContainElement(dep))
|
||||||
|
}
|
||||||
|
|
||||||
|
By("checking requirements")
|
||||||
|
Expect(p.Config.Requirements).Should(HaveLen(len(reqs)))
|
||||||
|
for _, req := range reqs {
|
||||||
|
Expect(p.Config.Requirements).Should(ContainElement(req))
|
||||||
|
}
|
||||||
|
|
||||||
|
By("checking annotations")
|
||||||
|
// The creation timestamp is always present.
|
||||||
|
Expect(p.Manifest.Annotations).Should(HaveLen(len(annotations) + 1))
|
||||||
|
for key, val := range annotations {
|
||||||
|
Expect(p.Manifest.Annotations).Should(HaveKeyWithValue(key, val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
By("checking tags")
|
||||||
|
Expect(pluginData.Tags).Should(HaveLen(len(pushedTags)))
|
||||||
|
Expect(pluginData.Tags).Should(ContainElements(pushedTags))
|
||||||
|
|
||||||
|
By("checking that temporary dirs have been removed")
|
||||||
|
|
||||||
|
Eventually(func() bool {
|
||||||
|
entries, err := os.ReadDir("/tmp")
|
||||||
|
Expect(err).ShouldNot(HaveOccurred())
|
||||||
|
for _, e := range entries {
|
||||||
|
if e.IsDir() {
|
||||||
|
matched, err := filepath.Match(utils.TmpDirPrefix+"*", regexp.QuoteMeta(e.Name()))
|
||||||
|
Expect(err).ShouldNot(HaveOccurred())
|
||||||
|
if matched {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}).WithTimeout(5 * time.Second).Should(BeFalse())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each test gets its own root command and runs it.
|
||||||
|
// The err variable is asserted by each test.
|
||||||
|
JustBeforeEach(func() {
|
||||||
|
rootCmd = cmd.New(ctx, opt)
|
||||||
|
err = executeRoot(args)
|
||||||
|
})
|
||||||
|
|
||||||
|
JustAfterEach(func() {
|
||||||
|
// Reset the status after each test.
|
||||||
|
// This variable could be changed by single tests.
|
||||||
|
// Make sure to set them at their default values.
|
||||||
|
Expect(output.Clear()).ShouldNot(HaveOccurred())
|
||||||
|
artifactNameInConfigLayer = "test-plugin"
|
||||||
|
pushedTags = []string{"tag1", "tag2", "latest"}
|
||||||
|
pluginOne = ""
|
||||||
|
pluginTwo = ""
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("success", func() {
|
||||||
|
JustBeforeEach(func() {
|
||||||
|
// Check the returned error before proceeding.
|
||||||
|
Expect(err).ShouldNot(HaveOccurred())
|
||||||
|
pluginData, err = testutils.FetchPluginFromRegistry(ctx, repoName, pushedTags[0], orasRegistry)
|
||||||
|
Expect(err).ShouldNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
When("two platforms, with reqs and deps", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
repoName, fullRepoName = randomRulesRepoName(registry, pluginsRepoBaseName)
|
||||||
|
pluginOne = rulesfileyaml
|
||||||
|
pluginTwo = plugintgz
|
||||||
|
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, pluginOne, pluginTwo, "--type", "plugin", "--platform",
|
||||||
|
platformAMD64, "--platform", platformARM64, "--version", version, "--config", configFile,
|
||||||
|
"--plain-http", "--depends-on", "my-test:4.3.2", "--requires", requirement, "--annotation-source", anSource,
|
||||||
|
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
||||||
|
})
|
||||||
|
|
||||||
|
AssertSuccessBehaviour([]oci.ArtifactDependency{{
|
||||||
|
Name: "my-test",
|
||||||
|
Version: "4.3.2",
|
||||||
|
Alternatives: nil,
|
||||||
|
}}, []oci.ArtifactRequirement{
|
||||||
|
{
|
||||||
|
Name: "plugin_api_version",
|
||||||
|
Version: "3.2.1",
|
||||||
|
},
|
||||||
|
}, map[string]string{
|
||||||
|
"org.opencontainers.image.source": anSource,
|
||||||
|
}, []string{
|
||||||
|
platformAMD64, platformARM64,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("one platform, no reqs", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
repoName, fullRepoName = randomRulesRepoName(registry, pluginsRepoBaseName)
|
||||||
|
pluginOne = plugintgz
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, pluginOne, "--type", "plugin", "--platform",
|
||||||
|
platformAMD64, "--version", version, "--config", configFile,
|
||||||
|
"--plain-http", "--depends-on", "my-test:4.3.2", "--annotation-source", anSource,
|
||||||
|
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
||||||
|
})
|
||||||
|
// We expect to succeed and that the requirement is empty.
|
||||||
|
AssertSuccessBehaviour([]oci.ArtifactDependency{{
|
||||||
|
Name: "my-test",
|
||||||
|
Version: "4.3.2",
|
||||||
|
Alternatives: nil,
|
||||||
|
}}, []oci.ArtifactRequirement{}, map[string]string{
|
||||||
|
"org.opencontainers.image.source": anSource,
|
||||||
|
}, []string{
|
||||||
|
platformAMD64,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,655 @@
|
||||||
|
// 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 push_test
|
||||||
|
|
||||||
|
// revive:disable
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
"github.com/onsi/gomega/gbytes"
|
||||||
|
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
|
||||||
|
"github.com/falcosecurity/falcoctl/cmd"
|
||||||
|
"github.com/falcosecurity/falcoctl/internal/utils"
|
||||||
|
"github.com/falcosecurity/falcoctl/pkg/oci"
|
||||||
|
testutils "github.com/falcosecurity/falcoctl/pkg/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
// revive:enable
|
||||||
|
|
||||||
|
var _ = Describe("pushing rulesfiles", func() {
|
||||||
|
var (
|
||||||
|
registryCmd = "registry"
|
||||||
|
pushCmd = "push"
|
||||||
|
version = "1.1.1"
|
||||||
|
// registry/rulesRepoBaseName-randomInt
|
||||||
|
fullRepoName string
|
||||||
|
// rulesRepoBaseName-randomInt
|
||||||
|
repoName string
|
||||||
|
// It is set in the config layer.
|
||||||
|
artifactNameInConfigLayer = "test-rulesfile"
|
||||||
|
pushedTags = []string{"tag1", "tag2", "latest"}
|
||||||
|
|
||||||
|
// Variables passed as arguments to the push command. Each test case updates them
|
||||||
|
// to point to the file on disk living in pkg/test/data.
|
||||||
|
rulesfile string
|
||||||
|
|
||||||
|
// Data fetched from registry and used for assertions.
|
||||||
|
rulesfileData *testutils.RulesfileArtifact
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Used as flags for all the test cases.
|
||||||
|
dep1 = "myplugin:1.2.3"
|
||||||
|
dep2 = "myplugin1:1.2.3|otherplugin:3.2.1"
|
||||||
|
req = "engine_version_semver:0.37.0"
|
||||||
|
anSource = "myrepo.com/rules.git"
|
||||||
|
rulesRepoBaseName = "push-rulesfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
// We keep it inside the success context since need the variables of this context.
|
||||||
|
var AssertSuccesBehaviour = func(deps []oci.ArtifactDependency, reqs []oci.ArtifactRequirement, annotations map[string]string) {
|
||||||
|
It("should succeed", func() {
|
||||||
|
// We do not check the error here since we are checking it after
|
||||||
|
// pushing the artifact.
|
||||||
|
By("checking no error in output")
|
||||||
|
Expect(output).ShouldNot(gbytes.Say("ERROR"))
|
||||||
|
Expect(output).ShouldNot(gbytes.Say("Unable to remove temporary dir"))
|
||||||
|
|
||||||
|
By("checking descriptor")
|
||||||
|
Expect(rulesfileData.Descriptor.MediaType).Should(Equal(v1.MediaTypeImageManifest))
|
||||||
|
Expect(output).Should(gbytes.Say(regexp.QuoteMeta(rulesfileData.Descriptor.Digest.String())))
|
||||||
|
|
||||||
|
By("checking manifest")
|
||||||
|
Expect(rulesfileData.Layer.Manifest.Layers).Should(HaveLen(1))
|
||||||
|
|
||||||
|
By("checking platforms")
|
||||||
|
Expect(rulesfileData.Descriptor.Platform).Should(BeNil())
|
||||||
|
|
||||||
|
By("checking config layer")
|
||||||
|
Expect(rulesfileData.Layer.Config.Version).Should(Equal(version))
|
||||||
|
Expect(rulesfileData.Layer.Config.Name).Should(Equal(artifactNameInConfigLayer))
|
||||||
|
|
||||||
|
By("checking dependencies")
|
||||||
|
Expect(rulesfileData.Layer.Config.Dependencies).Should(HaveLen(len(deps)))
|
||||||
|
for _, dep := range deps {
|
||||||
|
Expect(rulesfileData.Layer.Config.Dependencies).Should(ContainElement(dep))
|
||||||
|
}
|
||||||
|
|
||||||
|
By("checking requirements")
|
||||||
|
Expect(rulesfileData.Layer.Config.Requirements).Should(HaveLen(len(reqs)))
|
||||||
|
for _, req := range reqs {
|
||||||
|
Expect(rulesfileData.Layer.Config.Requirements).Should(ContainElement(req))
|
||||||
|
}
|
||||||
|
|
||||||
|
By("checking annotations")
|
||||||
|
// The creation timestamp is always present.
|
||||||
|
Expect(rulesfileData.Layer.Manifest.Annotations).Should(HaveLen(len(annotations) + 1))
|
||||||
|
for key, val := range annotations {
|
||||||
|
Expect(rulesfileData.Layer.Manifest.Annotations).Should(HaveKeyWithValue(key, val))
|
||||||
|
}
|
||||||
|
|
||||||
|
By("checking tags")
|
||||||
|
Expect(rulesfileData.Tags).Should(HaveLen(len(pushedTags)))
|
||||||
|
Expect(rulesfileData.Tags).Should(ContainElements(pushedTags))
|
||||||
|
|
||||||
|
By("checking that temporary dirs have been removed")
|
||||||
|
Eventually(func() bool {
|
||||||
|
entries, err := os.ReadDir("/tmp")
|
||||||
|
Expect(err).ShouldNot(HaveOccurred())
|
||||||
|
for _, e := range entries {
|
||||||
|
if e.IsDir() {
|
||||||
|
matched, err := filepath.Match(utils.TmpDirPrefix+"*", regexp.QuoteMeta(e.Name()))
|
||||||
|
Expect(err).ShouldNot(HaveOccurred())
|
||||||
|
if matched {
|
||||||
|
fmt.Println(e.Name())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}).WithTimeout(5 * time.Second).Should(BeFalse())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each test gets its own root command and runs it.
|
||||||
|
// The err variable is asserted by each test.
|
||||||
|
JustBeforeEach(func() {
|
||||||
|
rootCmd = cmd.New(ctx, opt)
|
||||||
|
err = executeRoot(args)
|
||||||
|
})
|
||||||
|
|
||||||
|
JustAfterEach(func() {
|
||||||
|
Expect(output.Clear()).ShouldNot(HaveOccurred())
|
||||||
|
// This variable could be changed by single tests.
|
||||||
|
// Make sure to set them at their default values.
|
||||||
|
artifactNameInConfigLayer = "test-rulesfile"
|
||||||
|
pushedTags = []string{"tag1", "tag2", "latest"}
|
||||||
|
rulesfile = ""
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("success", func() {
|
||||||
|
// Here we are testing all the success cases for the push command. The artifact type used here is of type
|
||||||
|
// rulesfile. Keep in mind that here we are testing also the common flags that could be used by the plugin
|
||||||
|
// artifacts. So we are testing that common logic only once, and are doing it here.
|
||||||
|
|
||||||
|
JustBeforeEach(func() {
|
||||||
|
// This runs after the push command, so check the returned error before proceeding.
|
||||||
|
Expect(err).ShouldNot(HaveOccurred())
|
||||||
|
rulesfileData, err = testutils.FetchRulesfileFromRegistry(ctx, repoName, pushedTags[0], orasRegistry)
|
||||||
|
Expect(err).ShouldNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
repoName, fullRepoName = randomRulesRepoName(registry, rulesRepoBaseName)
|
||||||
|
})
|
||||||
|
|
||||||
|
When("with full flags and args", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
rulesfile = rulesfileyaml
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
||||||
|
"--plain-http", "--depends-on", dep1, "--depends-on", dep2, "--requires", req, "--annotation-source", anSource,
|
||||||
|
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
||||||
|
})
|
||||||
|
AssertSuccesBehaviour([]oci.ArtifactDependency{
|
||||||
|
{
|
||||||
|
Name: "myplugin",
|
||||||
|
Version: "1.2.3",
|
||||||
|
Alternatives: nil,
|
||||||
|
}, {
|
||||||
|
Name: "myplugin1",
|
||||||
|
Version: "1.2.3",
|
||||||
|
Alternatives: []oci.Dependency{{
|
||||||
|
Name: "otherplugin",
|
||||||
|
Version: "3.2.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, []oci.ArtifactRequirement{
|
||||||
|
{
|
||||||
|
Name: "engine_version_semver",
|
||||||
|
Version: "0.37.0",
|
||||||
|
},
|
||||||
|
}, map[string]string{
|
||||||
|
"org.opencontainers.image.source": anSource,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("no --name flag provided", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
rulesfile = rulesfileyaml
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
||||||
|
"--plain-http", "--depends-on", dep1, "--depends-on", dep2, "--requires", req, "--annotation-source", anSource,
|
||||||
|
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2]}
|
||||||
|
// Set name to the expected one.
|
||||||
|
artifactNameInConfigLayer = repoName
|
||||||
|
})
|
||||||
|
|
||||||
|
AssertSuccesBehaviour([]oci.ArtifactDependency{
|
||||||
|
{
|
||||||
|
Name: "myplugin",
|
||||||
|
Version: "1.2.3",
|
||||||
|
Alternatives: nil,
|
||||||
|
}, {
|
||||||
|
Name: "myplugin1",
|
||||||
|
Version: "1.2.3",
|
||||||
|
Alternatives: []oci.Dependency{{
|
||||||
|
Name: "otherplugin",
|
||||||
|
Version: "3.2.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, []oci.ArtifactRequirement{
|
||||||
|
{
|
||||||
|
Name: "engine_version_semver",
|
||||||
|
Version: "0.37.0",
|
||||||
|
},
|
||||||
|
}, map[string]string{
|
||||||
|
"org.opencontainers.image.source": anSource,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("no --annotation-source provided", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
rulesfile = rulesfileyaml
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
||||||
|
"--plain-http", "--depends-on", dep1, "--depends-on", dep2, "--requires", req,
|
||||||
|
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
||||||
|
})
|
||||||
|
AssertSuccesBehaviour([]oci.ArtifactDependency{
|
||||||
|
{
|
||||||
|
Name: "myplugin",
|
||||||
|
Version: "1.2.3",
|
||||||
|
Alternatives: nil,
|
||||||
|
}, {
|
||||||
|
Name: "myplugin1",
|
||||||
|
Version: "1.2.3",
|
||||||
|
Alternatives: []oci.Dependency{{
|
||||||
|
Name: "otherplugin",
|
||||||
|
Version: "3.2.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, []oci.ArtifactRequirement{
|
||||||
|
{
|
||||||
|
Name: "engine_version_semver",
|
||||||
|
Version: "0.37.0",
|
||||||
|
},
|
||||||
|
}, map[string]string{})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("no --tags provided", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
rulesfile = rulesfileyaml
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
||||||
|
"--plain-http", "--depends-on", dep1, "--depends-on", dep2, "--requires", req, "--annotation-source", anSource,
|
||||||
|
"--name", artifactNameInConfigLayer}
|
||||||
|
// We expect that latest tag is pushed, so set it in the pushed tags.
|
||||||
|
pushedTags = []string{"latest"}
|
||||||
|
})
|
||||||
|
AssertSuccesBehaviour([]oci.ArtifactDependency{
|
||||||
|
{
|
||||||
|
Name: "myplugin",
|
||||||
|
Version: "1.2.3",
|
||||||
|
Alternatives: nil,
|
||||||
|
}, {
|
||||||
|
Name: "myplugin1",
|
||||||
|
Version: "1.2.3",
|
||||||
|
Alternatives: []oci.Dependency{{
|
||||||
|
Name: "otherplugin",
|
||||||
|
Version: "3.2.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, []oci.ArtifactRequirement{
|
||||||
|
{
|
||||||
|
Name: "engine_version_semver",
|
||||||
|
Version: "0.37.0",
|
||||||
|
},
|
||||||
|
}, map[string]string{
|
||||||
|
"org.opencontainers.image.source": anSource,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("no --depends-on flag provided", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
rulesfile = rulesfileyaml
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
||||||
|
"--plain-http", "--requires", req, "--annotation-source", anSource,
|
||||||
|
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
||||||
|
})
|
||||||
|
AssertSuccesBehaviour([]oci.ArtifactDependency{},
|
||||||
|
[]oci.ArtifactRequirement{
|
||||||
|
{
|
||||||
|
Name: "engine_version_semver",
|
||||||
|
Version: "0.37.0",
|
||||||
|
},
|
||||||
|
}, map[string]string{
|
||||||
|
"org.opencontainers.image.source": anSource,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("no --requires flag provided", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
rulesfile = rulesfileyaml
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
||||||
|
"--plain-http", "--depends-on", dep1, "--depends-on", dep2, "--annotation-source", anSource,
|
||||||
|
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
||||||
|
})
|
||||||
|
AssertSuccesBehaviour([]oci.ArtifactDependency{
|
||||||
|
{
|
||||||
|
Name: "myplugin",
|
||||||
|
Version: "1.2.3",
|
||||||
|
Alternatives: nil,
|
||||||
|
}, {
|
||||||
|
Name: "myplugin1",
|
||||||
|
Version: "1.2.3",
|
||||||
|
Alternatives: []oci.Dependency{{
|
||||||
|
Name: "otherplugin",
|
||||||
|
Version: "3.2.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, []oci.ArtifactRequirement{}, map[string]string{
|
||||||
|
"org.opencontainers.image.source": anSource,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("only required flags", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
rulesfile = rulesfileyaml
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
||||||
|
"--plain-http"}
|
||||||
|
// Set name to the expected one.
|
||||||
|
artifactNameInConfigLayer = repoName
|
||||||
|
// We expect that latest tag is pushed, so set it in the pushed tags.
|
||||||
|
pushedTags = []string{"latest"}
|
||||||
|
})
|
||||||
|
AssertSuccesBehaviour([]oci.ArtifactDependency{},
|
||||||
|
[]oci.ArtifactRequirement{},
|
||||||
|
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
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
||||||
|
"--plain-http", "--depends-on", dep1, "--depends-on", dep2, "--requires", req, "--annotation-source", anSource,
|
||||||
|
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
||||||
|
})
|
||||||
|
AssertSuccesBehaviour([]oci.ArtifactDependency{
|
||||||
|
{
|
||||||
|
Name: "myplugin",
|
||||||
|
Version: "1.2.3",
|
||||||
|
Alternatives: nil,
|
||||||
|
}, {
|
||||||
|
Name: "myplugin1",
|
||||||
|
Version: "1.2.3",
|
||||||
|
Alternatives: []oci.Dependency{{
|
||||||
|
Name: "otherplugin",
|
||||||
|
Version: "3.2.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, []oci.ArtifactRequirement{
|
||||||
|
{
|
||||||
|
Name: "engine_version_semver",
|
||||||
|
Version: "0.37.0",
|
||||||
|
},
|
||||||
|
}, map[string]string{
|
||||||
|
"org.opencontainers.image.source": anSource,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("rulesfile deps and requirements", func() {
|
||||||
|
When("user provided deps", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
repoName, fullRepoName = randomRulesRepoName(registry, rulesRepoBaseName)
|
||||||
|
rulesfile = rulesFileWithDepsAndReq
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
||||||
|
"--plain-http", "--depends-on", dep1, "--depends-on", dep2, "--requires", req, "--annotation-source", anSource,
|
||||||
|
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
||||||
|
})
|
||||||
|
|
||||||
|
AssertSuccesBehaviour([]oci.ArtifactDependency{
|
||||||
|
{
|
||||||
|
Name: "myplugin",
|
||||||
|
Version: "1.2.3",
|
||||||
|
Alternatives: nil,
|
||||||
|
}, {
|
||||||
|
Name: "myplugin1",
|
||||||
|
Version: "1.2.3",
|
||||||
|
Alternatives: []oci.Dependency{{
|
||||||
|
Name: "otherplugin",
|
||||||
|
Version: "3.2.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, []oci.ArtifactRequirement{
|
||||||
|
{
|
||||||
|
Name: "engine_version_semver",
|
||||||
|
Version: "0.37.0",
|
||||||
|
},
|
||||||
|
}, map[string]string{
|
||||||
|
"org.opencontainers.image.source": anSource,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("parsed from file deps", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
repoName, fullRepoName = randomRulesRepoName(registry, rulesRepoBaseName)
|
||||||
|
rulesfile = rulesFileWithDepsAndReq
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
||||||
|
"--plain-http", "--annotation-source", anSource,
|
||||||
|
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
||||||
|
})
|
||||||
|
|
||||||
|
AssertSuccesBehaviour([]oci.ArtifactDependency{
|
||||||
|
{
|
||||||
|
Name: "cloudtrail",
|
||||||
|
Version: "0.2.3",
|
||||||
|
Alternatives: nil,
|
||||||
|
}, {
|
||||||
|
Name: "json",
|
||||||
|
Version: "0.2.2",
|
||||||
|
Alternatives: nil,
|
||||||
|
},
|
||||||
|
}, []oci.ArtifactRequirement{
|
||||||
|
{
|
||||||
|
Name: "engine_version_semver",
|
||||||
|
Version: "0.10.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
map[string]string{
|
||||||
|
"org.opencontainers.image.source": anSource,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("parsed from file deps with alternatives", func() {
|
||||||
|
var data = `
|
||||||
|
- required_plugin_versions:
|
||||||
|
- name: k8saudit
|
||||||
|
version: 0.7.0
|
||||||
|
alternatives:
|
||||||
|
- name: k8saudit-eks
|
||||||
|
version: 0.4.0
|
||||||
|
- name: json
|
||||||
|
version: 0.7.0
|
||||||
|
`
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
repoName, fullRepoName = randomRulesRepoName(registry, rulesRepoBaseName)
|
||||||
|
tmpDir := GinkgoT().TempDir()
|
||||||
|
rulesfile, err = testutils.WriteToTmpFile(data, tmpDir)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
||||||
|
"--plain-http", "--annotation-source", anSource,
|
||||||
|
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
||||||
|
})
|
||||||
|
|
||||||
|
AssertSuccesBehaviour([]oci.ArtifactDependency{
|
||||||
|
{
|
||||||
|
Name: "json",
|
||||||
|
Version: "0.7.0",
|
||||||
|
Alternatives: nil,
|
||||||
|
}, {
|
||||||
|
Name: "k8saudit",
|
||||||
|
Version: "0.7.0",
|
||||||
|
Alternatives: []oci.Dependency{{
|
||||||
|
Name: "k8saudit-eks",
|
||||||
|
Version: "0.4.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, []oci.ArtifactRequirement{},
|
||||||
|
map[string]string{
|
||||||
|
"org.opencontainers.image.source": anSource,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("no deps at all", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
repoName, fullRepoName = randomRulesRepoName(registry, rulesRepoBaseName)
|
||||||
|
rulesfile = rulesfileyaml
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
||||||
|
"--plain-http", "--annotation-source", anSource,
|
||||||
|
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
||||||
|
})
|
||||||
|
|
||||||
|
AssertSuccesBehaviour([]oci.ArtifactDependency{}, []oci.ArtifactRequirement{},
|
||||||
|
map[string]string{
|
||||||
|
"org.opencontainers.image.source": anSource,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("user provided requirement", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
repoName, fullRepoName = randomRulesRepoName(registry, rulesRepoBaseName)
|
||||||
|
rulesfile = rulesFileWithDepsAndReq
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
||||||
|
"--plain-http", "--requires", req, "--annotation-source", anSource,
|
||||||
|
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
||||||
|
})
|
||||||
|
|
||||||
|
AssertSuccesBehaviour([]oci.ArtifactDependency{
|
||||||
|
{
|
||||||
|
Name: "json",
|
||||||
|
Version: "0.2.2",
|
||||||
|
Alternatives: nil,
|
||||||
|
}, {
|
||||||
|
Name: "cloudtrail",
|
||||||
|
Version: "0.2.3",
|
||||||
|
Alternatives: nil,
|
||||||
|
},
|
||||||
|
}, []oci.ArtifactRequirement{
|
||||||
|
{
|
||||||
|
Name: "engine_version_semver",
|
||||||
|
Version: "0.37.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
map[string]string{
|
||||||
|
"org.opencontainers.image.source": anSource,
|
||||||
|
})
|
||||||
|
It("reqs should be the ones provided by the user", func() {
|
||||||
|
Expect(fmt.Sprintf("%s:%s", rulesfileData.Layer.Config.Requirements[0].Name,
|
||||||
|
rulesfileData.Layer.Config.Requirements[0].Version)).Should(Equal(req))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("requirement parsed from file in semver format", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
repoName, fullRepoName = randomRulesRepoName(registry, rulesRepoBaseName)
|
||||||
|
rulesfile = rulesFileWithDepsAndReq
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
||||||
|
"--plain-http", "--annotation-source", anSource,
|
||||||
|
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
||||||
|
})
|
||||||
|
|
||||||
|
AssertSuccesBehaviour([]oci.ArtifactDependency{
|
||||||
|
{
|
||||||
|
Name: "json",
|
||||||
|
Version: "0.2.2",
|
||||||
|
Alternatives: nil,
|
||||||
|
}, {
|
||||||
|
Name: "cloudtrail",
|
||||||
|
Version: "0.2.3",
|
||||||
|
Alternatives: nil,
|
||||||
|
},
|
||||||
|
}, []oci.ArtifactRequirement{
|
||||||
|
{
|
||||||
|
Name: "engine_version_semver",
|
||||||
|
Version: "0.10.0",
|
||||||
|
},
|
||||||
|
}, map[string]string{
|
||||||
|
"org.opencontainers.image.source": anSource,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("requirement parsed from file in int format", func() {
|
||||||
|
var rulesfileContent = `
|
||||||
|
- required_engine_version: 10
|
||||||
|
`
|
||||||
|
BeforeEach(func() {
|
||||||
|
repoName, fullRepoName = randomRulesRepoName(registry, rulesRepoBaseName)
|
||||||
|
tmpDir := GinkgoT().TempDir()
|
||||||
|
rulesfile, err = testutils.WriteToTmpFile(rulesfileContent, tmpDir)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
||||||
|
"--plain-http", "--annotation-source", anSource,
|
||||||
|
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
||||||
|
})
|
||||||
|
|
||||||
|
AssertSuccesBehaviour([]oci.ArtifactDependency{}, []oci.ArtifactRequirement{
|
||||||
|
{
|
||||||
|
Name: "engine_version_semver",
|
||||||
|
Version: "0.10.0",
|
||||||
|
},
|
||||||
|
}, map[string]string{
|
||||||
|
"org.opencontainers.image.source": anSource,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("failure", func() {
|
||||||
|
When("requirement parsed from file -- invalid format (float)", func() {
|
||||||
|
var rulesFile = `
|
||||||
|
- required_engine_version: 10.0
|
||||||
|
`
|
||||||
|
BeforeEach(func() {
|
||||||
|
repoName, fullRepoName = randomRulesRepoName(registry, rulesRepoBaseName)
|
||||||
|
tmpDir := GinkgoT().TempDir()
|
||||||
|
rulesfile, err = testutils.WriteToTmpFile(rulesFile, tmpDir)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
||||||
|
"--plain-http", "--annotation-source", anSource,
|
||||||
|
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should fail", func() {
|
||||||
|
Expect(err).Should(HaveOccurred())
|
||||||
|
Expect(output).Should(gbytes.Say(regexp.QuoteMeta("required_engine_version must be an int or a string respecting " +
|
||||||
|
"the semver specification, got type float64")))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("requirement parsed from file -- invalid format (not semver)", func() {
|
||||||
|
var rulesFile = `
|
||||||
|
- required_engine_version: 10.0notsemver
|
||||||
|
`
|
||||||
|
BeforeEach(func() {
|
||||||
|
repoName, fullRepoName = randomRulesRepoName(registry, rulesRepoBaseName)
|
||||||
|
tmpDir := GinkgoT().TempDir()
|
||||||
|
rulesfile, err = testutils.WriteToTmpFile(rulesFile, tmpDir)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
||||||
|
"--plain-http", "--annotation-source", anSource,
|
||||||
|
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
||||||
|
// Set name to the expected one.
|
||||||
|
artifactNameInConfigLayer = repoName
|
||||||
|
// We expect that latest tag is pushed, so set it in the pushed tags.
|
||||||
|
pushedTags = []string{"latest"}
|
||||||
|
})
|
||||||
|
|
||||||
|
It("reqs should be the ones provided by the user", func() {
|
||||||
|
Expect(err).Should(HaveOccurred())
|
||||||
|
Expect(output).Should(gbytes.Say(regexp.QuoteMeta("10.0notsemver must be in semver format: No Major.Minor.Patch elements found")))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -37,14 +37,13 @@ import (
|
||||||
testutils "github.com/falcosecurity/falcoctl/pkg/test"
|
testutils "github.com/falcosecurity/falcoctl/pkg/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
//nolint:unused // false positive
|
|
||||||
const (
|
const (
|
||||||
rulesfiletgz = "../../../pkg/test/data/rules.tar.gz"
|
rulesfiletgz = "../../../pkg/test/data/rules.tar.gz"
|
||||||
rulesfileyaml = "../../../pkg/test/data/rules.yaml"
|
rulesfileyaml = "../../../pkg/test/data/rulesWithoutReqAndDeps.yaml"
|
||||||
plugintgz = "../../../pkg/test/data/plugin.tar.gz"
|
rulesFileWithDepsAndReq = "../../../pkg/test/data/rules.yaml"
|
||||||
|
plugintgz = "../../../pkg/test/data/plugin.tar.gz"
|
||||||
)
|
)
|
||||||
|
|
||||||
//nolint:unused // false positive
|
|
||||||
var (
|
var (
|
||||||
registry string
|
registry string
|
||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
|
@ -102,7 +101,6 @@ var _ = AfterSuite(func() {
|
||||||
Expect(os.RemoveAll(configDir)).Should(Succeed())
|
Expect(os.RemoveAll(configDir)).Should(Succeed())
|
||||||
})
|
})
|
||||||
|
|
||||||
//nolint:unused // false positive
|
|
||||||
func executeRoot(args []string) error {
|
func executeRoot(args []string) error {
|
||||||
rootCmd.SetArgs(args)
|
rootCmd.SetArgs(args)
|
||||||
rootCmd.SetOut(output)
|
rootCmd.SetOut(output)
|
||||||
|
|
|
@ -18,25 +18,20 @@ package push_test
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo/v2"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"github.com/onsi/gomega/gbytes"
|
"github.com/onsi/gomega/gbytes"
|
||||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
|
||||||
|
|
||||||
"github.com/falcosecurity/falcoctl/cmd"
|
"github.com/falcosecurity/falcoctl/cmd"
|
||||||
"github.com/falcosecurity/falcoctl/internal/utils"
|
|
||||||
testutils "github.com/falcosecurity/falcoctl/pkg/test"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//nolint:lll,unused // no need to check for line length.
|
|
||||||
var registryPushUsage = `Usage:
|
var registryPushUsage = `Usage:
|
||||||
falcoctl registry push hostname/repo[:tag|@digest] file [flags]
|
falcoctl registry push hostname/repo[:tag|@digest] file [flags]
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
|
--add-floating-tags add the floating tags for the major and minor versions
|
||||||
--annotation-source string set annotation source for the artifact
|
--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"
|
-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
|
-h, --help help for push
|
||||||
|
@ -71,6 +66,10 @@ Example - Push artifact "myplugin.tar.gz" of type "plugin" for multiple platform
|
||||||
Example - Push artifact "myrulesfile.tar.gz" of type "rulesfile":
|
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
|
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:
|
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
|
falcoctl registry push --type rulesfile --version "0.1.2" --plain-http localhost:5000/myrulesfile:latest myrulesfile.tar.gz
|
||||||
|
|
||||||
|
@ -91,6 +90,7 @@ Usage:
|
||||||
falcoctl registry push hostname/repo[:tag|@digest] file [flags]
|
falcoctl registry push hostname/repo[:tag|@digest] file [flags]
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
|
--add-floating-tags add the floating tags for the major and minor versions
|
||||||
--annotation-source string set annotation source for the artifact
|
--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"
|
-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
|
-h, --help help for push
|
||||||
|
@ -108,7 +108,6 @@ Global Flags:
|
||||||
--log-level string Set level for logs (info, warn, debug, trace) (default "info")
|
--log-level string Set level for logs (info, warn, debug, trace) (default "info")
|
||||||
`
|
`
|
||||||
|
|
||||||
//nolint:unused // false positive
|
|
||||||
var pushAssertFailedBehavior = func(usage, specificError string) {
|
var pushAssertFailedBehavior = func(usage, specificError string) {
|
||||||
It("check that fails and the usage is not printed", func() {
|
It("check that fails and the usage is not printed", func() {
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
|
@ -117,27 +116,17 @@ var pushAssertFailedBehavior = func(usage, specificError string) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:unused // false positive
|
|
||||||
var randomRulesRepoName = func(registry, repo string) (string, string) {
|
var randomRulesRepoName = func(registry, repo string) (string, string) {
|
||||||
rName := fmt.Sprintf("%s-%d", repo, rand.Int())
|
rName := fmt.Sprintf("%s-%d", repo, rand.Int())
|
||||||
return rName, fmt.Sprintf("%s/%s", registry, rName)
|
return rName, fmt.Sprintf("%s/%s", registry, rName)
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:unused // false positive
|
var _ = Describe("push", func() {
|
||||||
var registryPushTests = Describe("push", func() {
|
|
||||||
var (
|
var (
|
||||||
registryCmd = "registry"
|
registryCmd = "registry"
|
||||||
pushCmd = "push"
|
pushCmd = "push"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// Used as flags for all the test cases.
|
|
||||||
dep1 = "myplugin:1.2.3"
|
|
||||||
dep2 = "myplugin1:1.2.3|otherplugin:3.2.1"
|
|
||||||
req = "engine_version:15"
|
|
||||||
anSource = "myrepo.com/rules.git"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Each test gets its own root command and runs it.
|
// Each test gets its own root command and runs it.
|
||||||
// The err variable is asserted by each test.
|
// The err variable is asserted by each test.
|
||||||
JustBeforeEach(func() {
|
JustBeforeEach(func() {
|
||||||
|
@ -207,6 +196,22 @@ var registryPushTests = Describe("push", func() {
|
||||||
"registry \"noregistry\": Get \"http://noregistry/v2/\": dial tcp: lookup noregistry")
|
"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() {
|
When("missing repository", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
args = []string{registryCmd, pushCmd, registry, rulesfiletgz, "--config", configFile, "--type", "rulesfile", "--version", "1.1.1", "--plain-http"}
|
args = []string{registryCmd, pushCmd, registry, rulesfiletgz, "--config", configFile, "--type", "rulesfile", "--version", "1.1.1", "--plain-http"}
|
||||||
|
@ -220,7 +225,7 @@ var registryPushTests = Describe("push", func() {
|
||||||
args = []string{registryCmd, pushCmd, newReg, rulesfiletgz, "--config", configFile, "--type", "rulesfile", "--version", "1.1.1", "--plain-http"}
|
args = []string{registryCmd, pushCmd, newReg, rulesfiletgz, "--config", configFile, "--type", "rulesfile", "--version", "1.1.1", "--plain-http"}
|
||||||
})
|
})
|
||||||
pushAssertFailedBehavior(registryPushUsage, fmt.Sprintf("ERROR unable to create new repository with ref %s: "+
|
pushAssertFailedBehavior(registryPushUsage, fmt.Sprintf("ERROR unable to create new repository with ref %s: "+
|
||||||
"invalid reference: invalid digest; invalid checksum digest format\n", newReg))
|
"invalid reference: invalid digest %q: invalid checksum digest format\n", newReg, "something"))
|
||||||
})
|
})
|
||||||
|
|
||||||
When("invalid requirement", func() {
|
When("invalid requirement", func() {
|
||||||
|
@ -257,303 +262,4 @@ var registryPushTests = Describe("push", func() {
|
||||||
"flag: must be one of \"rulesfile\", \"plugin\", \"asset")
|
"flag: must be one of \"rulesfile\", \"plugin\", \"asset")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("success", func() {
|
|
||||||
const (
|
|
||||||
rulesRepoBaseName = "push-rulesfile"
|
|
||||||
pluginsRepoBaseName = "push-plugins"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
version = "1.1.1"
|
|
||||||
// registry/rulesRepoBaseName-randomInt
|
|
||||||
fullRepoName string
|
|
||||||
// rulesRepoBaseName-randomInt
|
|
||||||
repoName string
|
|
||||||
// It is set in the config layer.
|
|
||||||
artifactNameInConfigLayer = "test-rulesfile"
|
|
||||||
pushedTags = []string{"tag1", "tag2", "latest"}
|
|
||||||
|
|
||||||
// Variables passed as arguments to the push command. Each test case updates them
|
|
||||||
// to point to the file on disk living in pkg/test/data.
|
|
||||||
rulesfile string
|
|
||||||
plugin string
|
|
||||||
pluginRaw string
|
|
||||||
|
|
||||||
// Plugin's platforms.
|
|
||||||
platformARM64 = "linux/arm64"
|
|
||||||
platformAMD64 = "linux/amd64"
|
|
||||||
|
|
||||||
// Data fetched from registry and used for assertions.
|
|
||||||
pluginData *testutils.PluginArtifact
|
|
||||||
rulesfileData *testutils.RulesfileArtifact
|
|
||||||
)
|
|
||||||
|
|
||||||
// We keep it inside the success context since need the variables of this context.
|
|
||||||
var AssertSuccesBehaviour = func(dependencies, requirements, annotation bool) {
|
|
||||||
It("should succeed", func() {
|
|
||||||
// We do not check the error here since we are checking it before
|
|
||||||
// pulling the artifact.
|
|
||||||
By("checking no error in output")
|
|
||||||
Expect(output).ShouldNot(gbytes.Say("ERROR"))
|
|
||||||
Expect(output).ShouldNot(gbytes.Say("Unable to remove temporary dir"))
|
|
||||||
|
|
||||||
By("checking descriptor")
|
|
||||||
Expect(rulesfileData.Descriptor.MediaType).Should(Equal(v1.MediaTypeImageManifest))
|
|
||||||
Expect(output).Should(gbytes.Say(regexp.QuoteMeta(rulesfileData.Descriptor.Digest.String())))
|
|
||||||
|
|
||||||
By("checking manifest")
|
|
||||||
Expect(rulesfileData.Layer.Manifest.Layers).Should(HaveLen(1))
|
|
||||||
if annotation {
|
|
||||||
Expect(rulesfileData.Layer.Manifest.Annotations).Should(HaveKeyWithValue("org.opencontainers.image.source", anSource))
|
|
||||||
} else {
|
|
||||||
Expect(rulesfileData.Layer.Manifest.Annotations).ShouldNot(HaveKeyWithValue("org.opencontainers.image.source", anSource))
|
|
||||||
}
|
|
||||||
|
|
||||||
By("checking config layer")
|
|
||||||
Expect(rulesfileData.Layer.Config.Version).Should(Equal(version))
|
|
||||||
Expect(rulesfileData.Layer.Config.Name).Should(Equal(artifactNameInConfigLayer))
|
|
||||||
if dependencies {
|
|
||||||
Expect(fmt.Sprintf("%s:%s", rulesfileData.Layer.Config.Dependencies[0].Name,
|
|
||||||
rulesfileData.Layer.Config.Dependencies[0].Version)).Should(Equal(dep1))
|
|
||||||
Expect(fmt.Sprintf("%s:%s|%s:%s", rulesfileData.Layer.Config.Dependencies[1].Name,
|
|
||||||
rulesfileData.Layer.Config.Dependencies[1].Version, rulesfileData.Layer.Config.Dependencies[1].Alternatives[0].Name,
|
|
||||||
rulesfileData.Layer.Config.Dependencies[1].Alternatives[0].Version)).Should(Equal(dep2))
|
|
||||||
} else {
|
|
||||||
Expect(rulesfileData.Layer.Config.Dependencies).Should(HaveLen(0))
|
|
||||||
}
|
|
||||||
if requirements {
|
|
||||||
Expect(fmt.Sprintf("%s:%s", rulesfileData.Layer.Config.Requirements[0].Name,
|
|
||||||
rulesfileData.Layer.Config.Requirements[0].Version)).Should(Equal(req))
|
|
||||||
} else {
|
|
||||||
Expect(rulesfileData.Layer.Config.Requirements).Should(HaveLen(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
By("checking tags")
|
|
||||||
Expect(rulesfileData.Tags).Should(HaveLen(len(pushedTags)))
|
|
||||||
Expect(rulesfileData.Tags).Should(ContainElements(pushedTags))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here we are testing all the success cases for the push command. The artifact type used here is of type
|
|
||||||
// rulesfile. Keep in mind that here we are testing also the common flags that could be used by the plugin
|
|
||||||
// artifacts. So we are testing that common logic only once, and are doing it here.
|
|
||||||
commonFlagsAndRulesfileSpecificFlags := Context("rulesfiles and common flags", func() {
|
|
||||||
JustBeforeEach(func() {
|
|
||||||
// This runs after the push command, so check the returned error before proceeding.
|
|
||||||
Expect(err).ShouldNot(HaveOccurred())
|
|
||||||
rulesfileData, err = testutils.FetchRulesfileFromRegistry(ctx, repoName, pushedTags[0], orasRegistry)
|
|
||||||
Expect(err).ShouldNot(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
JustAfterEach(func() {
|
|
||||||
// This variable could be changed by single tests.
|
|
||||||
// Make sure to set them at their default values.
|
|
||||||
artifactNameInConfigLayer = "test-rulesfile"
|
|
||||||
pushedTags = []string{"tag1", "tag2", "latest"}
|
|
||||||
})
|
|
||||||
|
|
||||||
BeforeEach(func() {
|
|
||||||
repoName, fullRepoName = randomRulesRepoName(registry, rulesRepoBaseName)
|
|
||||||
})
|
|
||||||
When("with full flags and args", func() {
|
|
||||||
BeforeEach(func() {
|
|
||||||
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
|
||||||
"--plain-http", "--depends-on", dep1, "--depends-on", dep2, "--requires", req, "--annotation-source", anSource,
|
|
||||||
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
|
||||||
})
|
|
||||||
AssertSuccesBehaviour(true, true, true)
|
|
||||||
})
|
|
||||||
|
|
||||||
When("no --name flag provided", func() {
|
|
||||||
BeforeEach(func() {
|
|
||||||
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
|
||||||
"--plain-http", "--depends-on", dep1, "--depends-on", dep2, "--requires", req, "--annotation-source", anSource,
|
|
||||||
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2]}
|
|
||||||
// Set name to the expected one.
|
|
||||||
artifactNameInConfigLayer = repoName
|
|
||||||
})
|
|
||||||
AssertSuccesBehaviour(true, true, true)
|
|
||||||
})
|
|
||||||
|
|
||||||
When("no --annotation-source provided", func() {
|
|
||||||
BeforeEach(func() {
|
|
||||||
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
|
||||||
"--plain-http", "--depends-on", dep1, "--depends-on", dep2, "--requires", req,
|
|
||||||
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
|
||||||
})
|
|
||||||
AssertSuccesBehaviour(true, true, false)
|
|
||||||
})
|
|
||||||
|
|
||||||
When("no --tags provided", func() {
|
|
||||||
BeforeEach(func() {
|
|
||||||
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
|
||||||
"--plain-http", "--depends-on", dep1, "--depends-on", dep2, "--requires", req, "--annotation-source", anSource,
|
|
||||||
"--name", artifactNameInConfigLayer}
|
|
||||||
// We expect that latest tag is pushed, so set it in the pushed tags.
|
|
||||||
pushedTags = []string{"latest"}
|
|
||||||
})
|
|
||||||
AssertSuccesBehaviour(true, true, true)
|
|
||||||
})
|
|
||||||
|
|
||||||
When("no --depends-on flag provided", func() {
|
|
||||||
BeforeEach(func() {
|
|
||||||
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
|
||||||
"--plain-http", "--requires", req, "--annotation-source", anSource,
|
|
||||||
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
|
||||||
})
|
|
||||||
AssertSuccesBehaviour(false, true, true)
|
|
||||||
})
|
|
||||||
|
|
||||||
When("no --requires flag provided", func() {
|
|
||||||
BeforeEach(func() {
|
|
||||||
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
|
||||||
"--plain-http", "--depends-on", dep1, "--depends-on", dep2, "--annotation-source", anSource,
|
|
||||||
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
|
||||||
})
|
|
||||||
AssertSuccesBehaviour(true, false, true)
|
|
||||||
})
|
|
||||||
|
|
||||||
When("only required flags", func() {
|
|
||||||
BeforeEach(func() {
|
|
||||||
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
|
||||||
"--plain-http"}
|
|
||||||
// Set name to the expected one.
|
|
||||||
artifactNameInConfigLayer = repoName
|
|
||||||
// We expect that latest tag is pushed, so set it in the pushed tags.
|
|
||||||
pushedTags = []string{"latest"}
|
|
||||||
})
|
|
||||||
AssertSuccesBehaviour(false, false, false)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("rulesfile", func() {
|
|
||||||
Context("tar.gz format", func() {
|
|
||||||
rulesfile = rulesfiletgz
|
|
||||||
var _ = commonFlagsAndRulesfileSpecificFlags
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("raw format", func() {
|
|
||||||
rulesfile = rulesfileyaml
|
|
||||||
|
|
||||||
// Push a raw rulesfiles using all the flags combinations.
|
|
||||||
var _ = commonFlagsAndRulesfileSpecificFlags
|
|
||||||
|
|
||||||
Context("filesystem cleanup", func() {
|
|
||||||
// Push a raw rulesfile.
|
|
||||||
BeforeEach(func() {
|
|
||||||
// Some values such as fullRepoName is the last one set by the other tests or the default one.
|
|
||||||
// Anyway we do not really care since the tar.gz is created before.
|
|
||||||
args = []string{registryCmd, pushCmd, fullRepoName, rulesfile, "--config", configFile, "--type", "rulesfile", "--version", version,
|
|
||||||
"--plain-http"}
|
|
||||||
})
|
|
||||||
|
|
||||||
It("temp dir should not exist", func() {
|
|
||||||
Expect(err).ShouldNot(HaveOccurred())
|
|
||||||
entries, err := os.ReadDir("/tmp")
|
|
||||||
Expect(err).ShouldNot(HaveOccurred())
|
|
||||||
for _, e := range entries {
|
|
||||||
if e.IsDir() {
|
|
||||||
matched, err := filepath.Match(utils.TmpDirPrefix+"*", regexp.QuoteMeta(e.Name()))
|
|
||||||
Expect(err).ShouldNot(HaveOccurred())
|
|
||||||
Expect(matched).ShouldNot(BeTrue())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// We keep it inside the success context since need the variables of this context.
|
|
||||||
var AssertSuccessBehaviourPlugins = func(dependencies, requirements, annotation bool) {
|
|
||||||
It("should succeed", func() {
|
|
||||||
// We do not check the error here since we are checking it before
|
|
||||||
// pulling the artifact.
|
|
||||||
By("checking no error in output")
|
|
||||||
Expect(output).ShouldNot(gbytes.Say("ERROR"))
|
|
||||||
Expect(output).ShouldNot(gbytes.Say("Unable to remove temporary dir"))
|
|
||||||
|
|
||||||
By("checking descriptor")
|
|
||||||
Expect(pluginData.Descriptor.MediaType).Should(Equal(v1.MediaTypeImageIndex))
|
|
||||||
Expect(output).Should(gbytes.Say(regexp.QuoteMeta(pluginData.Descriptor.Digest.String())))
|
|
||||||
|
|
||||||
By("checking index")
|
|
||||||
Expect(pluginData.Index.Manifests).Should(HaveLen(2))
|
|
||||||
|
|
||||||
if annotation {
|
|
||||||
Expect(pluginData.Index.Annotations).Should(HaveKeyWithValue("org.opencontainers.image.source", anSource))
|
|
||||||
} else {
|
|
||||||
Expect(pluginData.Descriptor.Annotations).ShouldNot(HaveKeyWithValue("org.opencontainers.image.source", anSource))
|
|
||||||
}
|
|
||||||
|
|
||||||
By("checking platforms")
|
|
||||||
Expect(pluginData.Platforms).Should(HaveKey(platformARM64))
|
|
||||||
Expect(pluginData.Platforms).Should(HaveKey(platformAMD64))
|
|
||||||
|
|
||||||
By("checking config layer")
|
|
||||||
for _, p := range pluginData.Platforms {
|
|
||||||
Expect(p.Config.Version).Should(Equal(version))
|
|
||||||
Expect(p.Config.Name).Should(Equal(artifactNameInConfigLayer))
|
|
||||||
if dependencies {
|
|
||||||
Expect(fmt.Sprintf("%s:%s", p.Config.Dependencies[0].Name, p.Config.Dependencies[0].Version)).Should(Equal(dep1))
|
|
||||||
Expect(fmt.Sprintf("%s:%s|%s:%s", p.Config.Dependencies[1].Name, p.Config.Dependencies[1].Version,
|
|
||||||
p.Config.Dependencies[1].Alternatives[0].Name, p.Config.Dependencies[1].Alternatives[0].Version)).Should(Equal(dep2))
|
|
||||||
} else {
|
|
||||||
Expect(p.Config.Dependencies).Should(HaveLen(0))
|
|
||||||
}
|
|
||||||
if requirements {
|
|
||||||
Expect(fmt.Sprintf("%s:%s", p.Config.Requirements[0].Name, p.Config.Requirements[0].Version)).Should(Equal(req))
|
|
||||||
} else {
|
|
||||||
Expect(p.Config.Requirements).Should(HaveLen(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
By("checking tags")
|
|
||||||
Expect(pluginData.Tags).Should(HaveLen(len(pushedTags)))
|
|
||||||
Expect(pluginData.Tags).Should(ContainElements(pushedTags))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here we are testing the success cases for the push command using a plugin artifact and its related flags.
|
|
||||||
// Other flags related to the plugin artifacts are tested in the rulesfile artifact section.
|
|
||||||
PluginsSpecificFlags := Context("plugins specific flags", func() {
|
|
||||||
JustBeforeEach(func() {
|
|
||||||
// This runs after the push command, so check the returned error before proceeding.
|
|
||||||
Expect(err).ShouldNot(HaveOccurred())
|
|
||||||
pluginData, err = testutils.FetchPluginFromRegistry(ctx, repoName, pushedTags[0], orasRegistry)
|
|
||||||
Expect(err).ShouldNot(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
JustAfterEach(func() {
|
|
||||||
// This variable could be changed by single tests.
|
|
||||||
// Make sure to set them at their default values.
|
|
||||||
artifactNameInConfigLayer = "test-plugin"
|
|
||||||
pushedTags = []string{"tag1", "tag2", "latest"}
|
|
||||||
})
|
|
||||||
|
|
||||||
BeforeEach(func() {
|
|
||||||
repoName, fullRepoName = randomRulesRepoName(registry, pluginsRepoBaseName)
|
|
||||||
})
|
|
||||||
When("with full flags and args", func() {
|
|
||||||
BeforeEach(func() {
|
|
||||||
args = []string{registryCmd, pushCmd, fullRepoName, plugin, pluginRaw, "--type", "plugin", "--platform",
|
|
||||||
platformAMD64, "--platform", platformARM64, "--version", version, "--config", configFile,
|
|
||||||
"--plain-http", "--depends-on", dep1, "--depends-on", dep2, "--requires", req, "--annotation-source", anSource,
|
|
||||||
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
|
|
||||||
})
|
|
||||||
AssertSuccessBehaviourPlugins(true, true, true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
Context("plugin", func() {
|
|
||||||
Context("tar.gz + raw format format", func() {
|
|
||||||
plugin = plugintgz
|
|
||||||
// We do not really care what the file is.
|
|
||||||
pluginRaw = rulesfileyaml
|
|
||||||
var _ = PluginsSpecificFlags
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -44,7 +44,7 @@ Usage:
|
||||||
Available Commands:
|
Available Commands:
|
||||||
artifact Interact with Falco artifacts
|
artifact Interact with Falco artifacts
|
||||||
completion Generate the autocompletion script for the specified shell
|
completion Generate the autocompletion script for the specified shell
|
||||||
driver [Preview] Interact with falcosecurity driver
|
driver Interact with falcosecurity driver
|
||||||
help Help about any command
|
help Help about any command
|
||||||
index Interact with index
|
index Interact with index
|
||||||
registry Interact with OCI registries
|
registry Interact with OCI registries
|
||||||
|
|
483
go.mod
483
go.mod
|
@ -1,59 +1,76 @@
|
||||||
module github.com/falcosecurity/falcoctl
|
module github.com/falcosecurity/falcoctl
|
||||||
|
|
||||||
go 1.21
|
go 1.24.3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/storage v1.33.0
|
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
|
||||||
github.com/blang/semver v3.5.1+incompatible
|
github.com/blang/semver v3.5.1+incompatible
|
||||||
github.com/distribution/distribution/v3 v3.0.0-20230608105614-4501a6e06d3b
|
github.com/blang/semver/v4 v4.0.0
|
||||||
github.com/docker/cli v24.0.7+incompatible
|
github.com/cilium/ebpf v0.17.3
|
||||||
github.com/docker/docker v24.0.7+incompatible
|
github.com/distribution/distribution/v3 v3.0.0
|
||||||
github.com/falcosecurity/driverkit v0.16.0
|
github.com/docker/cli v28.3.2+incompatible
|
||||||
github.com/go-oauth2/oauth2/v4 v4.5.2
|
github.com/docker/docker v28.3.3+incompatible
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
github.com/falcosecurity/driverkit v0.21.2
|
||||||
github.com/google/go-containerregistry v0.16.1
|
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/gookit/color v1.5.4
|
github.com/gookit/color v1.5.4
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mattn/go-isatty v0.0.20
|
||||||
github.com/onsi/ginkgo/v2 v2.10.0
|
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c
|
||||||
github.com/onsi/gomega v1.27.8
|
github.com/onsi/ginkgo/v2 v2.23.3
|
||||||
github.com/opencontainers/image-spec v1.1.0-rc5
|
github.com/onsi/gomega v1.36.3
|
||||||
github.com/oras-project/oras-credentials-go v0.3.0
|
github.com/opencontainers/image-spec v1.1.1
|
||||||
github.com/pterm/pterm v0.12.67
|
github.com/pterm/pterm v0.12.80
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/sigstore/cosign/v2 v2.2.1
|
github.com/sigstore/cosign/v2 v2.4.3
|
||||||
github.com/sigstore/sigstore v1.7.5
|
github.com/sigstore/sigstore v1.9.1
|
||||||
github.com/spf13/cobra v1.8.0
|
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.9.1
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/sigstore/sigstore/pkg/signature/kms/azure v1.9.1
|
||||||
github.com/spf13/viper v1.17.0
|
github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.9.1
|
||||||
golang.org/x/crypto v0.17.0
|
github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.1
|
||||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
|
github.com/spf13/cobra v1.9.1
|
||||||
google.golang.org/api v0.149.0
|
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
|
||||||
gopkg.in/ini.v1 v1.67.0
|
gopkg.in/ini.v1 v1.67.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
k8s.io/apimachinery v0.28.3
|
k8s.io/api v0.32.3
|
||||||
k8s.io/client-go v0.28.3
|
k8s.io/apimachinery v0.32.3
|
||||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
|
k8s.io/client-go v0.32.3
|
||||||
oras.land/oras-go/v2 v2.3.0
|
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.5.0 // indirect
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
atomicgo.dev/cursor v0.2.0 // indirect
|
atomicgo.dev/cursor v0.2.0 // indirect
|
||||||
atomicgo.dev/keyboard v0.2.9 // indirect
|
atomicgo.dev/keyboard v0.2.9 // indirect
|
||||||
atomicgo.dev/schedule v0.1.0 // indirect
|
atomicgo.dev/schedule v0.1.0 // indirect
|
||||||
cloud.google.com/go v0.110.9 // indirect
|
cel.dev/expr v0.19.2 // indirect
|
||||||
cloud.google.com/go/compute v1.23.2 // indirect
|
cloud.google.com/go v0.118.3 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
cloud.google.com/go/auth v0.15.0 // indirect
|
||||||
cloud.google.com/go/iam v1.1.4 // indirect
|
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
|
||||||
filippo.io/edwards25519 v1.0.0 // indirect
|
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||||
github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.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
|
||||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // 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/go-autorest v14.2.0+incompatible // 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 v0.11.29 // indirect
|
||||||
github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect
|
github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect
|
||||||
|
@ -62,16 +79,13 @@ require (
|
||||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
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/logger v0.2.1 // indirect
|
||||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||||
github.com/DataDog/appsec-internal-go v1.0.0 // indirect
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 // indirect
|
||||||
github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.1 // indirect
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect
|
||||||
github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.49.0-devel // indirect
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect
|
||||||
github.com/DataDog/datadog-go/v5 v5.3.0 // indirect
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect
|
||||||
github.com/DataDog/go-libddwaf v1.5.0 // indirect
|
github.com/MakeNowJust/heredoc v1.0.0 // indirect
|
||||||
github.com/DataDog/go-tuf v1.0.2-0.5.2 // indirect
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
github.com/DataDog/sketches-go v1.4.3 // indirect
|
github.com/ProtonMail/go-crypto v1.0.0 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c // indirect
|
|
||||||
github.com/Shopify/logrus-bugsnag v0.0.0-20230117174420-439a4b8ba167 // indirect
|
|
||||||
github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
|
github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
|
||||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect
|
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect
|
||||||
github.com/alibabacloud-go/cr-20160607 v1.0.1 // indirect
|
github.com/alibabacloud-go/cr-20160607 v1.0.1 // indirect
|
||||||
|
@ -80,163 +94,211 @@ require (
|
||||||
github.com/alibabacloud-go/debug v1.0.0 // indirect
|
github.com/alibabacloud-go/debug v1.0.0 // indirect
|
||||||
github.com/alibabacloud-go/endpoint-util v1.1.1 // 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/openapi-util v0.1.0 // indirect
|
||||||
github.com/alibabacloud-go/tea v1.2.1 // indirect
|
github.com/alibabacloud-go/tea v1.2.2 // indirect
|
||||||
github.com/alibabacloud-go/tea-utils v1.4.5 // indirect
|
github.com/alibabacloud-go/tea-utils v1.4.5 // indirect
|
||||||
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
|
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
|
||||||
github.com/aliyun/credentials-go v1.3.1 // indirect
|
github.com/aliyun/credentials-go v1.3.3 // indirect
|
||||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2 v1.21.2 // indirect
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.19.1 // indirect
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.62 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.43 // indirect
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.43 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.37 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/ecr v1.20.2 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ecr v1.40.3 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.18.2 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.31.2 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.15.2 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect
|
||||||
github.com/aws/smithy-go v1.15.0 // indirect
|
github.com/aws/aws-sdk-go-v2/service/kms v1.38.1 // indirect
|
||||||
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8 // 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/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bshuster-repo/logrus-logstash-hook v1.1.0 // indirect
|
github.com/bshuster-repo/logrus-logstash-hook v1.1.0 // indirect
|
||||||
github.com/bugsnag/bugsnag-go v2.2.0+incompatible // indirect
|
github.com/buildkite/agent/v3 v3.92.1 // indirect
|
||||||
github.com/bugsnag/bugsnag-go/v2 v2.2.0 // indirect
|
github.com/buildkite/go-pipeline v0.13.3 // indirect
|
||||||
github.com/bugsnag/panicwrap v1.3.4 // indirect
|
github.com/buildkite/interpolate v0.1.5 // indirect
|
||||||
github.com/buildkite/agent/v3 v3.58.0 // indirect
|
github.com/buildkite/roko v1.3.1 // indirect
|
||||||
github.com/buildkite/interpolate v0.0.0-20200526001904-07f35b4ae251 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/chai2010/gettext-go v1.0.3 // indirect
|
||||||
github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 // 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/clbanning/mxj/v2 v2.7.0 // indirect
|
||||||
github.com/cloudflare/circl v1.3.5 // indirect
|
github.com/cloudflare/circl v1.6.1 // indirect
|
||||||
|
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect
|
||||||
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect
|
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect
|
||||||
github.com/containerd/console v1.0.3 // indirect
|
github.com/containerd/console v1.0.4 // indirect
|
||||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
|
github.com/containerd/errdefs v1.0.0 // indirect
|
||||||
github.com/coreos/go-oidc/v3 v3.7.0 // indirect
|
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||||
github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 // 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/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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect
|
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect
|
||||||
github.com/digitorus/timestamp v0.0.0-20230902153158-687734543647 // indirect
|
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect
|
||||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
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/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-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
||||||
github.com/docker/go-metrics v0.0.1 // indirect
|
github.com/docker/go-metrics v0.0.1 // indirect
|
||||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/ebitengine/purego v0.5.0 // indirect
|
github.com/emicklei/go-restful/v3 v3.12.0 // indirect
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 // 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-chi/chi v4.1.2+incompatible // indirect
|
||||||
github.com/go-jose/go-jose/v3 v3.0.1 // indirect
|
github.com/go-errors/errors v1.5.1 // indirect
|
||||||
github.com/go-logr/logr v1.3.0 // 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-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-openapi/analysis v0.21.4 // indirect
|
github.com/go-openapi/analysis v0.23.0 // indirect
|
||||||
github.com/go-openapi/errors v0.20.4 // indirect
|
github.com/go-openapi/errors v0.22.0 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.20.0 // indirect
|
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||||
github.com/go-openapi/loads v0.21.2 // indirect
|
github.com/go-openapi/loads v0.22.0 // indirect
|
||||||
github.com/go-openapi/runtime v0.26.0 // indirect
|
github.com/go-openapi/runtime v0.28.0 // indirect
|
||||||
github.com/go-openapi/spec v0.20.9 // indirect
|
github.com/go-openapi/spec v0.21.0 // indirect
|
||||||
github.com/go-openapi/strfmt v0.21.7 // indirect
|
github.com/go-openapi/strfmt v0.23.0 // indirect
|
||||||
github.com/go-openapi/swag v0.22.4 // indirect
|
github.com/go-openapi/swag v0.23.0 // indirect
|
||||||
github.com/go-openapi/validate v0.22.1 // indirect
|
github.com/go-openapi/validate v0.24.0 // indirect
|
||||||
github.com/go-piv/piv-go v1.11.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/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.15.5 // indirect
|
github.com/go-playground/validator/v10 v10.24.0 // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
github.com/gomodule/redigo v1.8.9 // indirect
|
github.com/google/btree v1.1.3 // indirect
|
||||||
github.com/google/certificate-transparency-go v1.1.7 // indirect
|
github.com/google/certificate-transparency-go v1.3.1 // indirect
|
||||||
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
|
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
|
||||||
github.com/google/go-cmp v0.6.0 // indirect
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
github.com/google/go-github/v55 v55.0.0 // indirect
|
github.com/google/go-github/v55 v55.0.0 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/google/gofuzz v1.2.0 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b // indirect
|
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
|
||||||
github.com/google/s2a-go v0.1.7 // indirect
|
github.com/google/s2a-go v0.1.9 // indirect
|
||||||
github.com/google/uuid v1.4.0 // indirect
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||||
github.com/gorilla/handlers v1.5.1 // indirect
|
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||||
github.com/gorilla/mux v1.8.0 // indirect
|
github.com/gorilla/handlers v1.5.2 // indirect
|
||||||
github.com/gowebpki/jcs v1.0.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/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
github.com/hashicorp/go-retryablehttp v0.7.7 // 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/strutil v0.1.2 // indirect
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.6 // 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/hcl v1.0.1-vault-5 // indirect
|
||||||
github.com/imdario/mergo v0.3.16 // indirect
|
github.com/hashicorp/vault/api v1.16.0 // indirect
|
||||||
|
github.com/in-toto/attestation v1.1.0 // indirect
|
||||||
github.com/in-toto/in-toto-golang v0.9.0 // 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/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jellydator/ttlcache/v3 v3.3.0 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
github.com/klauspost/compress v1.17.2 // indirect
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
github.com/leodido/go-urn v1.2.4 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
|
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect
|
||||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
||||||
github.com/lestrrat-go/httprc v1.0.4 // indirect
|
|
||||||
github.com/lestrrat-go/iter v1.0.2 // indirect
|
|
||||||
github.com/lestrrat-go/jwx/v2 v2.0.16 // indirect
|
|
||||||
github.com/lestrrat-go/option v1.0.1 // indirect
|
|
||||||
github.com/letsencrypt/boulder v0.0.0-20231026200631-000cd05d5491 // indirect
|
|
||||||
github.com/lithammer/fuzzysearch v1.1.8 // 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/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
|
||||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // 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/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/mozillazg/docker-credential-acr-helper v0.3.0 // 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // 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/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect
|
||||||
github.com/oklog/ulid v1.3.1 // indirect
|
github.com/oklog/ulid v1.3.1 // indirect
|
||||||
github.com/oleiade/reflections v1.0.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/opentracing/opentracing-go v1.2.0 // indirect
|
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||||
github.com/outcaste-io/ristretto v0.2.3 // indirect
|
|
||||||
github.com/pborman/uuid v1.2.1 // indirect
|
github.com/pborman/uuid v1.2.1 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||||
github.com/philhofer/fwd v1.1.2 // indirect
|
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||||
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||||
github.com/pkg/errors v0.9.1 // 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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/prometheus/client_golang v1.17.0 // indirect
|
github.com/prometheus/client_golang v1.20.5 // indirect
|
||||||
github.com/prometheus/client_model v0.5.0 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
github.com/prometheus/common v0.45.0 // indirect
|
github.com/prometheus/common v0.62.0 // indirect
|
||||||
github.com/prometheus/procfs v0.12.0 // indirect
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect
|
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 // indirect
|
||||||
github.com/rivo/uniseg v0.4.4 // indirect
|
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.3.0 // indirect
|
github.com/redis/go-redis/v9 v9.7.3 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.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/sassoftware/relic v7.2.1+incompatible // indirect
|
github.com/sassoftware/relic v7.2.1+incompatible // indirect
|
||||||
github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect
|
github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect
|
||||||
github.com/segmentio/asm v1.2.0 // indirect
|
|
||||||
github.com/segmentio/ksuid v1.0.4 // indirect
|
github.com/segmentio/ksuid v1.0.4 // indirect
|
||||||
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
||||||
github.com/sigstore/fulcio v1.4.3 // indirect
|
github.com/sigstore/fulcio v1.6.6 // indirect
|
||||||
github.com/sigstore/rekor v1.3.3 // indirect
|
github.com/sigstore/protobuf-specs v0.4.0 // indirect
|
||||||
github.com/sigstore/timestamp-authority v1.2.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/sirupsen/logrus v1.9.3 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/afero v1.10.0 // indirect
|
github.com/spf13/afero v1.12.0 // indirect
|
||||||
github.com/spf13/cast v1.5.1 // indirect
|
github.com/spf13/cast v1.7.1 // indirect
|
||||||
github.com/spiffe/go-spiffe/v2 v2.1.6 // indirect
|
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
||||||
github.com/thales-e-security/pool v0.0.2 // indirect
|
github.com/thales-e-security/pool v0.0.2 // indirect
|
||||||
github.com/theupdateframework/go-tuf v0.6.1 // 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/btree v1.6.0 // indirect
|
||||||
github.com/tidwall/buntdb v1.3.0 // indirect
|
github.com/tidwall/buntdb v1.3.0 // indirect
|
||||||
github.com/tidwall/gjson v1.16.0 // indirect
|
github.com/tidwall/gjson v1.16.0 // indirect
|
||||||
|
@ -245,60 +307,75 @@ require (
|
||||||
github.com/tidwall/pretty v1.2.1 // indirect
|
github.com/tidwall/pretty v1.2.1 // indirect
|
||||||
github.com/tidwall/rtred v0.1.2 // indirect
|
github.com/tidwall/rtred v0.1.2 // indirect
|
||||||
github.com/tidwall/tinyqueue v0.1.1 // indirect
|
github.com/tidwall/tinyqueue v0.1.1 // indirect
|
||||||
github.com/tinylib/msgp v1.1.8 // indirect
|
|
||||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
|
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
|
||||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||||
github.com/transparency-dev/merkle v0.0.2 // indirect
|
github.com/transparency-dev/merkle v0.0.2 // indirect
|
||||||
github.com/vbatts/tar-split v0.11.5 // indirect
|
github.com/valyala/fasthttp v1.50.0 // indirect
|
||||||
github.com/xanzy/go-gitlab v0.93.2 // 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/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940 // indirect
|
github.com/zeebo/errs v1.4.0 // indirect
|
||||||
github.com/yvasiyarov/gorelic v0.0.7 // indirect
|
gitlab.com/gitlab-org/api/client-go v0.123.0 // indirect
|
||||||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20160601141957-9c099fbc30e9 // indirect
|
go.mongodb.org/mongo-driver v1.15.0 // indirect
|
||||||
github.com/zeebo/errs v1.3.0 // indirect
|
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||||
go.mongodb.org/mongo-driver v1.12.1 // indirect
|
go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.19.0 // indirect
|
go.opentelemetry.io/contrib/exporters/autoexport v0.57.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.19.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.19.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
|
||||||
go.step.sm/crypto v0.36.1 // indirect
|
go.opentelemetry.io/otel v1.34.0 // indirect
|
||||||
go.uber.org/atomic v1.11.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
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/zap v1.26.0 // indirect
|
go.uber.org/zap v1.27.0 // indirect
|
||||||
go4.org/intern v0.0.0-20230525184215-6c62f75575cb // indirect
|
golang.org/x/mod v0.23.0 // indirect
|
||||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect
|
golang.org/x/sync v0.14.0 // indirect
|
||||||
golang.org/x/mod v0.13.0 // indirect
|
golang.org/x/text v0.25.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/time v0.11.0 // indirect
|
||||||
golang.org/x/time v0.3.0 // indirect
|
golang.org/x/tools v0.30.0 // indirect
|
||||||
golang.org/x/tools v0.14.0 // indirect
|
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||||
google.golang.org/appengine v1.6.8 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
google.golang.org/grpc v1.71.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
google.golang.org/protobuf v1.36.5 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||||
google.golang.org/grpc v1.59.0 // indirect
|
|
||||||
google.golang.org/protobuf v1.31.0 // indirect
|
|
||||||
gopkg.in/DataDog/dd-trace-go.v1 v1.56.1 // indirect
|
|
||||||
gopkg.in/go-jose/go-jose.v2 v2.6.1 // indirect
|
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a // indirect
|
k8s.io/cli-runtime v0.30.0 // indirect
|
||||||
k8s.io/api v0.28.3 // indirect
|
k8s.io/component-base v0.30.0 // indirect
|
||||||
k8s.io/klog/v2 v2.100.1 // indirect
|
k8s.io/klog/v2 v2.130.1 // indirect
|
||||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
|
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
|
||||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
k8s.io/kubectl v0.30.0 // indirect
|
||||||
sigs.k8s.io/release-utils v0.7.6 // indirect
|
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect
|
modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b // indirect
|
||||||
|
modernc.org/libc v1.50.5 // indirect
|
||||||
|
modernc.org/mathutil v1.6.0 // indirect
|
||||||
|
modernc.org/memory v1.8.0 // indirect
|
||||||
|
modernc.org/sqlite v1.29.9 // 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/yaml v1.4.0 // 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.8.4
|
|
||||||
golang.org/x/net v0.17.0
|
|
||||||
golang.org/x/oauth2 v0.13.0
|
|
||||||
golang.org/x/sys v0.15.0
|
|
||||||
golang.org/x/term v0.15.0
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -30,6 +30,7 @@ import (
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
"github.com/falcosecurity/falcoctl/internal/utils"
|
||||||
drivertype "github.com/falcosecurity/falcoctl/pkg/driver/type"
|
drivertype "github.com/falcosecurity/falcoctl/pkg/driver/type"
|
||||||
"github.com/falcosecurity/falcoctl/pkg/oci"
|
"github.com/falcosecurity/falcoctl/pkg/oci"
|
||||||
)
|
)
|
||||||
|
@ -189,7 +190,7 @@ type Install struct {
|
||||||
|
|
||||||
// Driver represents the internal driver configuration (with Type string).
|
// Driver represents the internal driver configuration (with Type string).
|
||||||
type Driver struct {
|
type Driver struct {
|
||||||
Type string `mapstructure:"type"`
|
Type []string `mapstructure:"type"`
|
||||||
Name string `mapstructure:"name"`
|
Name string `mapstructure:"name"`
|
||||||
Repos []string `mapstructure:"repos"`
|
Repos []string `mapstructure:"repos"`
|
||||||
Version string `mapstructure:"version"`
|
Version string `mapstructure:"version"`
|
||||||
|
@ -203,11 +204,12 @@ func init() {
|
||||||
IndexesDir = filepath.Join(FalcoctlPath, "indexes")
|
IndexesDir = filepath.Join(FalcoctlPath, "indexes")
|
||||||
ClientCredentialsFile = filepath.Join(FalcoctlPath, "clientcredentials.json")
|
ClientCredentialsFile = filepath.Join(FalcoctlPath, "clientcredentials.json")
|
||||||
DefaultIndex = Index{
|
DefaultIndex = Index{
|
||||||
Name: "falcosecurity",
|
Name: "falcosecurity",
|
||||||
URL: "https://falcosecurity.github.io/falcoctl/index.yaml",
|
URL: "https://falcosecurity.github.io/falcoctl/index.yaml",
|
||||||
|
Backend: "https",
|
||||||
}
|
}
|
||||||
DefaultDriver = Driver{
|
DefaultDriver = Driver{
|
||||||
Type: drivertype.TypeKmod,
|
Type: []string{drivertype.TypeModernBpf, drivertype.TypeKmod, drivertype.TypeBpf},
|
||||||
Name: "falco",
|
Name: "falco",
|
||||||
Repos: []string{"https://download.falco.org/driver"},
|
Repos: []string{"https://download.falco.org/driver"},
|
||||||
Version: "",
|
Version: "",
|
||||||
|
@ -390,8 +392,14 @@ func basicAuthListHookFunc() mapstructure.DecodeHookFuncType {
|
||||||
return data, fmt.Errorf("not valid token %q", token)
|
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{
|
auths[i] = BasicAuth{
|
||||||
Registry: values[0],
|
Registry: registry,
|
||||||
User: values[1],
|
User: values[1],
|
||||||
Password: values[2],
|
Password: values[2],
|
||||||
}
|
}
|
||||||
|
@ -554,7 +562,20 @@ func Installer() (Install, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DriverRepos retrieves the driver section of the config file.
|
// DriverTypes retrieves the driver types of the config file.
|
||||||
|
func DriverTypes() ([]string, error) {
|
||||||
|
// manage driver.Type as ";" separated list.
|
||||||
|
types := viper.GetStringSlice(DriverTypeKey)
|
||||||
|
if len(types) == 1 { // in this case it might come from the env
|
||||||
|
if !SemicolonSeparatedRegexp.MatchString(types[0]) {
|
||||||
|
return types, fmt.Errorf("env variable not correctly set, should match %q, got %q", SemicolonSeparatedRegexp.String(), types[0])
|
||||||
|
}
|
||||||
|
types = strings.Split(types[0], ";")
|
||||||
|
}
|
||||||
|
return types, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DriverRepos retrieves the driver repos of the config file.
|
||||||
func DriverRepos() ([]string, error) {
|
func DriverRepos() ([]string, error) {
|
||||||
// manage driver.Repos as ";" separated list.
|
// manage driver.Repos as ";" separated list.
|
||||||
repos := viper.GetStringSlice(DriverReposKey)
|
repos := viper.GetStringSlice(DriverReposKey)
|
||||||
|
|
|
@ -42,6 +42,11 @@ import (
|
||||||
sigs "github.com/sigstore/cosign/v2/pkg/signature"
|
sigs "github.com/sigstore/cosign/v2/pkg/signature"
|
||||||
"github.com/sigstore/sigstore/pkg/cryptoutils"
|
"github.com/sigstore/sigstore/pkg/cryptoutils"
|
||||||
"github.com/sigstore/sigstore/pkg/signature"
|
"github.com/sigstore/sigstore/pkg/signature"
|
||||||
|
// Register the provider-specific plugins.
|
||||||
|
_ "github.com/sigstore/sigstore/pkg/signature/kms/aws"
|
||||||
|
_ "github.com/sigstore/sigstore/pkg/signature/kms/azure"
|
||||||
|
_ "github.com/sigstore/sigstore/pkg/signature/kms/gcp"
|
||||||
|
_ "github.com/sigstore/sigstore/pkg/signature/kms/hashivault"
|
||||||
)
|
)
|
||||||
|
|
||||||
// VerifyCommand verifies a signature on a supplied container image.
|
// VerifyCommand verifies a signature on a supplied container image.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2025 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -203,52 +203,83 @@ func (f *Follower) follow(ctx context.Context) {
|
||||||
return
|
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.
|
// Install the artifacts if necessary.
|
||||||
for _, path := range filePaths {
|
for _, path := range filePaths {
|
||||||
baseName := filepath.Base(path)
|
// Get the relative path from the temporary directory to preserve directory structure
|
||||||
f.logger.Debug("Installing file", f.logger.Args("followerName", f.ref, "fileName", baseName))
|
relPath, err := filepath.Rel(f.tmpDir, path)
|
||||||
dstPath := filepath.Join(dstDir, baseName)
|
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))
|
||||||
// Check if the file exists.
|
// Check if the file exists.
|
||||||
f.logger.Debug("Checking if file already exists", f.logger.Args("followerName", f.ref, "fileName", baseName, "directory", dstDir))
|
f.logger.Debug("Checking if file already exists", f.logger.Args("followerName", f.ref, "path", relPath, "directory", dstDir))
|
||||||
exists, err := utils.FileExists(dstPath)
|
exists, err := utils.FileExists(dstPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.logger.Error("Unable to check existence for file", f.logger.Args("followerName", f.ref, "fileName", baseName, "reason", err.Error()))
|
f.logger.Error("Unable to check existence for file", f.logger.Args("followerName", f.ref, "path", relPath, "reason", err.Error()))
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
f.logger.Debug("Moving file", f.logger.Args("followerName", f.ref, "fileName", baseName, "destDirectory", dstDir))
|
f.logger.Debug("Moving file", f.logger.Args("followerName", f.ref, "path", relPath, "destDirectory", dstDir))
|
||||||
if err = utils.Move(path, dstPath); err != nil {
|
if err = utils.Move(path, dstPath); err != nil {
|
||||||
f.logger.Error("Unable to move file", f.logger.Args("followerName", f.ref, "fileName", baseName, "destDirectory", dstDir, "reason", err.Error()))
|
f.logger.Error("Unable to move file", f.logger.Args("followerName", f.ref, "path", relPath, "destDirectory", dstDir, "reason", err.Error()))
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
f.logger.Debug("File correctly installed", f.logger.Args("followerName", f.ref, "path", path))
|
f.logger.Debug("File correctly installed", f.logger.Args("followerName", f.ref, "path", path))
|
||||||
// It's done, move to the next file.
|
// It's done, move to the next file.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
f.logger.Debug(fmt.Sprintf("file %q already exists in %q, checking if it is equal to the existing one", baseName, dstDir),
|
|
||||||
|
f.logger.Debug(fmt.Sprintf("file %q already exists in %q, checking if it is equal to the existing one", relPath, dstDir),
|
||||||
f.logger.Args("followerName", f.ref))
|
f.logger.Args("followerName", f.ref))
|
||||||
|
|
||||||
// Check if the files are equal.
|
// Check if the files are equal.
|
||||||
eq, err := equal([]string{path, dstPath})
|
eq, err := equal([]string{path, dstPath})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.logger.Error("Unable to compare files", f.logger.Args("followerName", f.ref, "newFile", path, "existingFile", dstPath, "reason", err.Error()))
|
f.logger.Error("Unable to compare files", f.logger.Args("followerName", f.ref, "existingFile", dstPath, "reason", err.Error()))
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !eq {
|
if !eq {
|
||||||
f.logger.Debug(fmt.Sprintf("Overwriting file %q with file %q", dstPath, path), f.logger.Args("followerName", f.ref))
|
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 {
|
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()))
|
f.logger.Error("Unable to overwrite file", f.logger.Args("followerName", f.ref, "existingFile", dstPath, "reason", err.Error()))
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
f.logger.Debug("The two file are equal, nothing to be done")
|
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.
|
// pull downloads, extracts, and installs the artifact.
|
||||||
|
@ -291,7 +322,7 @@ func (f *Follower) pull(ctx context.Context) (filePaths []string, res *oci.Regis
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract artifact and move it to its destination directory
|
// Extract artifact and move it to its destination directory
|
||||||
filePaths, err = utils.ExtractTarGz(file, f.tmpDir, 0)
|
filePaths, err = utils.ExtractTarGz(ctx, file, f.tmpDir, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return filePaths, res, fmt.Errorf("unable to extract %q to %q: %w", res.Filename, f.tmpDir, err)
|
return filePaths, res, fmt.Errorf("unable to extract %q to %q: %w", res.Filename, f.tmpDir, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,305 @@
|
||||||
|
// 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 follower
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pterm/pterm"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/falcosecurity/falcoctl/pkg/oci"
|
||||||
|
"github.com/falcosecurity/falcoctl/pkg/output"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCheckRequirements(t *testing.T) {
|
||||||
|
printer := output.NewPrinter(pterm.LogLevelDebug, pterm.LogFormatterJSON, os.Stdout)
|
||||||
|
|
||||||
|
type testArtifact struct {
|
||||||
|
conf *oci.ArtifactConfig
|
||||||
|
falcoVersions map[string]string
|
||||||
|
expectErr bool
|
||||||
|
testName string
|
||||||
|
}
|
||||||
|
var testArtifactConfigs = []testArtifact{
|
||||||
|
{
|
||||||
|
conf: &oci.ArtifactConfig{
|
||||||
|
Name: "my_rule",
|
||||||
|
Version: "0.1.0",
|
||||||
|
Requirements: []oci.ArtifactRequirement{{Name: "engine_version_semver", Version: "0.26.0"}},
|
||||||
|
},
|
||||||
|
falcoVersions: map[string]string{"engine_version_semver": "0.26.0", "engine_version": "26"},
|
||||||
|
expectErr: false,
|
||||||
|
testName: "New Falco with new rules with new semver engine version",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
conf: &oci.ArtifactConfig{
|
||||||
|
Name: "my_rule",
|
||||||
|
Version: "0.1.0",
|
||||||
|
Requirements: []oci.ArtifactRequirement{{Name: "engine_version_semver", Version: "26"}},
|
||||||
|
},
|
||||||
|
falcoVersions: map[string]string{"engine_version_semver": "0.26.0", "engine_version": "26"},
|
||||||
|
expectErr: true,
|
||||||
|
testName: "New Falco with new rules with old int engine version",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
conf: &oci.ArtifactConfig{
|
||||||
|
Name: "my_rule",
|
||||||
|
Version: "0.1.0",
|
||||||
|
Requirements: []oci.ArtifactRequirement{{Name: "engine_version_semver", Version: "0.26.0"}},
|
||||||
|
},
|
||||||
|
falcoVersions: map[string]string{"engine_version": "26"},
|
||||||
|
expectErr: true,
|
||||||
|
testName: "Old Falco with new rules with new semver engine version",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
conf: &oci.ArtifactConfig{
|
||||||
|
Name: "my_rule",
|
||||||
|
Version: "0.1.0",
|
||||||
|
Requirements: []oci.ArtifactRequirement{{Name: "engine_version_semver", Version: "26"}},
|
||||||
|
},
|
||||||
|
falcoVersions: map[string]string{"engine_version": "26"},
|
||||||
|
expectErr: true,
|
||||||
|
testName: "Old Falco with new new rules with old int engine version",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
conf: &oci.ArtifactConfig{
|
||||||
|
Name: "my_rule",
|
||||||
|
Version: "0.1.0",
|
||||||
|
Requirements: []oci.ArtifactRequirement{{Name: "engine_version", Version: "26"}},
|
||||||
|
},
|
||||||
|
falcoVersions: map[string]string{"engine_version_semver": "0.26.0", "engine_version": "26"},
|
||||||
|
expectErr: false,
|
||||||
|
testName: "New Falco with old rules with old int engine version",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
conf: &oci.ArtifactConfig{
|
||||||
|
Name: "my_rule",
|
||||||
|
Version: "0.1.0",
|
||||||
|
Requirements: []oci.ArtifactRequirement{{Name: "engine_version", Version: "0.26.0"}},
|
||||||
|
},
|
||||||
|
falcoVersions: map[string]string{"engine_version_semver": "0.26.0", "engine_version": "26"},
|
||||||
|
expectErr: true,
|
||||||
|
testName: "New Falco with old rules with new semver engine version",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
conf: &oci.ArtifactConfig{
|
||||||
|
Name: "my_rule",
|
||||||
|
Version: "0.1.0",
|
||||||
|
Requirements: []oci.ArtifactRequirement{{Name: "engine_version", Version: "26"}},
|
||||||
|
},
|
||||||
|
falcoVersions: map[string]string{"engine_version": "26"},
|
||||||
|
expectErr: false,
|
||||||
|
testName: "Old Falco with old rules with old int engine version",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
conf: &oci.ArtifactConfig{
|
||||||
|
Name: "my_rule",
|
||||||
|
Version: "0.1.0",
|
||||||
|
Requirements: []oci.ArtifactRequirement{{Name: "engine_version", Version: "0.26.0"}},
|
||||||
|
},
|
||||||
|
falcoVersions: map[string]string{"engine_version": "26"},
|
||||||
|
expectErr: true,
|
||||||
|
testName: "Old Falco with old rules with new semver engine version",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, artConf := range testArtifactConfigs {
|
||||||
|
t.Run(artConf.testName, func(t *testing.T) {
|
||||||
|
config := Config{
|
||||||
|
FalcoVersions: artConf.falcoVersions,
|
||||||
|
}
|
||||||
|
f, err := New("ghcr.io/falcosecurity/rules/my_rule:0.1.0", printer, &config)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = f.checkRequirements(artConf.conf)
|
||||||
|
if artConf.expectErr {
|
||||||
|
assert.Error(t, err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,8 +19,8 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
credentials "github.com/oras-project/oras-credentials-go"
|
|
||||||
"oras.land/oras-go/v2/registry/remote/auth"
|
"oras.land/oras-go/v2/registry/remote/auth"
|
||||||
|
"oras.land/oras-go/v2/registry/remote/credentials"
|
||||||
|
|
||||||
"github.com/falcosecurity/falcoctl/pkg/oci/registry"
|
"github.com/falcosecurity/falcoctl/pkg/oci/registry"
|
||||||
)
|
)
|
||||||
|
|
|
@ -28,7 +28,7 @@ import (
|
||||||
// Login checks if passed gcp credentials are correct.
|
// Login checks if passed gcp credentials are correct.
|
||||||
func Login(ctx context.Context, reg string) error {
|
func Login(ctx context.Context, reg string) error {
|
||||||
// Check that we can find a valid token source using GCE or ApplicationDefault.
|
// Check that we can find a valid token source using GCE or ApplicationDefault.
|
||||||
ts, err := google.DefaultTokenSource(ctx)
|
ts, err := google.DefaultTokenSource(ctx, "https://www.googleapis.com/auth/cloud-platform")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("wrong GCP token source, unable to find a valid source: %w", err)
|
return fmt.Errorf("wrong GCP token source, unable to find a valid source: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,9 @@ package login
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
credentials "github.com/oras-project/oras-credentials-go"
|
|
||||||
"golang.org/x/oauth2/clientcredentials"
|
"golang.org/x/oauth2/clientcredentials"
|
||||||
"oras.land/oras-go/v2/registry/remote/auth"
|
"oras.land/oras-go/v2/registry/remote/auth"
|
||||||
|
"oras.land/oras-go/v2/registry/remote/credentials"
|
||||||
|
|
||||||
"github.com/falcosecurity/falcoctl/internal/config"
|
"github.com/falcosecurity/falcoctl/internal/config"
|
||||||
"github.com/falcosecurity/falcoctl/internal/login/basic"
|
"github.com/falcosecurity/falcoctl/internal/login/basic"
|
||||||
|
|
|
@ -43,6 +43,8 @@ func Verify(ctx context.Context, ref string, signature *index.Signature) error {
|
||||||
CertOidcIssuer: signature.Cosign.CertificateOidcIssuer,
|
CertOidcIssuer: signature.Cosign.CertificateOidcIssuer,
|
||||||
CertOidcIssuerRegexp: signature.Cosign.CertificateOidcIssuerRegexp,
|
CertOidcIssuerRegexp: signature.Cosign.CertificateOidcIssuerRegexp,
|
||||||
},
|
},
|
||||||
|
KeyRef: signature.Cosign.KeyRef,
|
||||||
|
IgnoreTlog: signature.Cosign.IgnoreTlog,
|
||||||
}
|
}
|
||||||
return v.DoVerify(ctx, []string{ref})
|
return v.DoVerify(ctx, []string{ref})
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,13 +28,16 @@ import (
|
||||||
|
|
||||||
// TmpDirPrefix prefix used for the temporary directory where the tar.gz archives live before pushing
|
// TmpDirPrefix prefix used for the temporary directory where the tar.gz archives live before pushing
|
||||||
// to the OCI registry.
|
// to the OCI registry.
|
||||||
const TmpDirPrefix = "falcoctl-registry-push"
|
const TmpDirPrefix = "falcoctl-registry-push-"
|
||||||
|
|
||||||
// CreateTarGzArchive compresses and saves in a tar archive the passed file.
|
// CreateTarGzArchive compresses and saves in a tar archive the passed file.
|
||||||
func CreateTarGzArchive(path string) (file string, err error) {
|
func CreateTarGzArchive(dir, path string, stripComponents bool) (file string, err error) {
|
||||||
cleanedPath := filepath.Clean(path)
|
cleanedPath := filepath.Clean(path)
|
||||||
|
if dir == "" {
|
||||||
|
dir = TmpDirPrefix
|
||||||
|
}
|
||||||
// Create output file.
|
// Create output file.
|
||||||
tmpDir, err := os.MkdirTemp("", TmpDirPrefix)
|
tmpDir, err := os.MkdirTemp("", dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -93,13 +96,13 @@ func CreateTarGzArchive(path string) (file string, err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return copyToTarGz(path, tw, info)
|
return copyToTarGz(path, tw, info, stripComponents)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err = copyToTarGz(path, tw, fInfo); err != nil {
|
if err = copyToTarGz(path, tw, fInfo, stripComponents); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,9 +110,17 @@ func CreateTarGzArchive(path string) (file string, err error) {
|
||||||
return outFile.Name(), err
|
return outFile.Name(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyToTarGz(path string, tw *tar.Writer, info fs.FileInfo) error {
|
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
|
||||||
|
}
|
||||||
|
|
||||||
header := &tar.Header{
|
header := &tar.Header{
|
||||||
Name: path,
|
Name: headerName,
|
||||||
Size: info.Size(),
|
Size: info.Size(),
|
||||||
Mode: int64(info.Mode()),
|
Mode: int64(info.Mode()),
|
||||||
Typeflag: tar.TypeReg,
|
Typeflag: tar.TypeReg,
|
||||||
|
|
|
@ -29,31 +29,32 @@ import (
|
||||||
const (
|
const (
|
||||||
filename1 = "file1"
|
filename1 = "file1"
|
||||||
filename2 = "file2"
|
filename2 = "file2"
|
||||||
|
tmpPrefix = "testCreateTarGzArchiveFile"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateTarGzArchiveFile(t *testing.T) {
|
func TestCreateTarGzArchiveFile(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
f1, err := os.Create(filepath.Join(dir, filename1))
|
f1, err := os.Create(filepath.Join(dir, filename1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
defer f1.Close()
|
defer f1.Close()
|
||||||
|
|
||||||
tarball, err := CreateTarGzArchive(filepath.Join(dir, filename1))
|
tarball, err := CreateTarGzArchive(tmpPrefix, filepath.Join(dir, filename1), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
defer os.Remove(tarball)
|
defer os.RemoveAll(filepath.Dir(tarball))
|
||||||
|
|
||||||
file, err := os.Open(tarball)
|
file, err := os.Open(tarball)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
paths, err := listHeaders(file)
|
paths, err := listHeaders(file)
|
||||||
fmt.Println(paths)
|
fmt.Println(paths)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(paths) != 1 {
|
if len(paths) != 1 {
|
||||||
|
@ -66,6 +67,41 @@ 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) {
|
func TestCreateTarGzArchiveDir(t *testing.T) {
|
||||||
// Test that we can compress directories
|
// Test that we can compress directories
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
|
@ -73,30 +109,30 @@ func TestCreateTarGzArchiveDir(t *testing.T) {
|
||||||
// add some files
|
// add some files
|
||||||
f1, err := os.Create(filepath.Join(dir, filename1))
|
f1, err := os.Create(filepath.Join(dir, filename1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
defer f1.Close()
|
defer f1.Close()
|
||||||
f2, err := os.Create(filepath.Join(dir, filename2))
|
f2, err := os.Create(filepath.Join(dir, filename2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
defer f2.Close()
|
defer f2.Close()
|
||||||
|
|
||||||
tarball, err := CreateTarGzArchive(dir)
|
tarball, err := CreateTarGzArchive(tmpPrefix, dir, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
defer os.Remove(tarball)
|
defer os.RemoveAll(filepath.Dir(tarball))
|
||||||
|
|
||||||
file, err := os.Open(tarball)
|
file, err := os.Open(tarball)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
paths, err := listHeaders(file)
|
paths, err := listHeaders(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(paths) != 3 {
|
if len(paths) != 3 {
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
// 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
|
|
||||||
}
|
|
|
@ -24,12 +24,30 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type link struct {
|
||||||
|
Name string
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
// ExtractTarGz extracts a *.tar.gz compressed archive and moves its content to destDir.
|
// ExtractTarGz extracts a *.tar.gz compressed archive and moves its content to destDir.
|
||||||
// Returns a slice containing the full path of the extracted files.
|
// Returns a slice containing the full path of the extracted files.
|
||||||
func ExtractTarGz(gzipStream io.Reader, destDir string, stripPathComponents int) ([]string, error) {
|
func ExtractTarGz(ctx context.Context, gzipStream io.Reader, destDir string, stripPathComponents int) ([]string, error) {
|
||||||
var files []string
|
var (
|
||||||
|
files []string
|
||||||
|
links []link
|
||||||
|
symlinks []link
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
// We need an absolute path
|
||||||
|
destDir, err = filepath.Abs(destDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
uncompressedStream, err := gzip.NewReader(gzipStream)
|
uncompressedStream, err := gzip.NewReader(gzipStream)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -37,34 +55,45 @@ func ExtractTarGz(gzipStream io.Reader, destDir string, stripPathComponents int)
|
||||||
}
|
}
|
||||||
|
|
||||||
tarReader := tar.NewReader(uncompressedStream)
|
tarReader := tar.NewReader(uncompressedStream)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
header, err := tarReader.Next()
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, errors.New("interrupted")
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
header, err := tarReader.Next()
|
||||||
if errors.Is(err, io.EOF) {
|
if errors.Is(err, io.EOF) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.Contains(header.Name, "..") {
|
if strings.Contains(header.Name, "..") {
|
||||||
return nil, fmt.Errorf("not allowed relative path in tar archive")
|
return nil, fmt.Errorf("not allowed relative path in tar archive")
|
||||||
}
|
}
|
||||||
|
|
||||||
strippedName := stripComponents(header.Name, stripPathComponents)
|
path := header.Name
|
||||||
|
if stripPathComponents > 0 {
|
||||||
|
path = stripComponents(path, stripPathComponents)
|
||||||
|
}
|
||||||
|
if path == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if path, err = safeConcat(destDir, filepath.Clean(path)); err != nil {
|
||||||
|
// Skip paths that would escape destDir
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
info := header.FileInfo()
|
||||||
|
|
||||||
switch header.Typeflag {
|
switch header.Typeflag {
|
||||||
case tar.TypeDir:
|
case tar.TypeDir:
|
||||||
d := filepath.Join(destDir, strippedName)
|
if err = os.MkdirAll(path, info.Mode()); err != nil {
|
||||||
if err = os.Mkdir(filepath.Clean(d), 0o750); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
files = append(files, d)
|
|
||||||
case tar.TypeReg:
|
case tar.TypeReg:
|
||||||
f := filepath.Join(destDir, strippedName)
|
outFile, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, info.Mode())
|
||||||
outFile, err := os.Create(filepath.Clean(f))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -76,13 +105,47 @@ func ExtractTarGz(gzipStream io.Reader, destDir string, stripPathComponents int)
|
||||||
if err = outFile.Close(); err != nil {
|
if err = outFile.Close(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
files = append(files, f)
|
files = append(files, path)
|
||||||
|
case tar.TypeLink:
|
||||||
|
name := header.Linkname
|
||||||
|
if stripPathComponents > 0 {
|
||||||
|
name = stripComponents(name, stripPathComponents)
|
||||||
|
}
|
||||||
|
if name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
name = filepath.Join(destDir, filepath.Clean(name))
|
||||||
|
links = append(links, link{Path: path, Name: name})
|
||||||
|
case tar.TypeSymlink:
|
||||||
|
symlinks = append(symlinks, link{Path: path, Name: header.Linkname})
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("extractTarGz: uknown type: %b in %s", header.Typeflag, header.Name)
|
return nil, fmt.Errorf("extractTarGz: uknown type: %b in %s", header.Typeflag, header.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now we make another pass creating the links
|
||||||
|
for i := range links {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, errors.New("interrupted")
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
if err = os.Link(links[i].Name, links[i].Path); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range symlinks {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, errors.New("interrupted")
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
if err = os.Symlink(symlinks[i].Name, symlinks[i].Path); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,11 +153,22 @@ func stripComponents(headerName string, stripComponents int) string {
|
||||||
if stripComponents == 0 {
|
if stripComponents == 0 {
|
||||||
return headerName
|
return headerName
|
||||||
}
|
}
|
||||||
names := strings.FieldsFunc(headerName, func(r rune) bool {
|
names := strings.Split(headerName, string(filepath.Separator))
|
||||||
return r == os.PathSeparator
|
|
||||||
})
|
|
||||||
if len(names) < stripComponents {
|
if len(names) < stripComponents {
|
||||||
return headerName
|
return headerName
|
||||||
}
|
}
|
||||||
return filepath.Clean(strings.Join(names[stripComponents:], "/"))
|
return filepath.Clean(strings.Join(names[stripComponents:], string(filepath.Separator)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// safeConcat concatenates destDir and name
|
||||||
|
// but returns an error if the resulting path points outside 'destDir'.
|
||||||
|
func safeConcat(destDir, name string) (string, error) {
|
||||||
|
res := filepath.Join(destDir, name)
|
||||||
|
if !strings.HasSuffix(destDir, string(os.PathSeparator)) {
|
||||||
|
destDir += string(os.PathSeparator)
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(res, destDir) {
|
||||||
|
return res, fmt.Errorf("unsafe path concatenation: '%s' with '%s'", destDir, name)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,210 @@
|
||||||
|
// 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 utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"compress/gzip"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
srcDir = "./foo"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
files = []string{srcDir + "/example.txt", srcDir + "/test.txt", srcDir + "/bar/baz.txt"}
|
||||||
|
)
|
||||||
|
|
||||||
|
func createTarball(t *testing.T, tarballFilePath, srcDir string) {
|
||||||
|
file, err := os.Create(tarballFilePath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
gzipWriter := gzip.NewWriter(file)
|
||||||
|
defer gzipWriter.Close()
|
||||||
|
|
||||||
|
tarWriter := tar.NewWriter(gzipWriter)
|
||||||
|
defer tarWriter.Close()
|
||||||
|
|
||||||
|
err = filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
return addToArchive(tarWriter, path, info)
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addToArchive(tw *tar.Writer, fullName string, info os.FileInfo) error {
|
||||||
|
// Open the file which will be written into the archive
|
||||||
|
file, err := os.Open(fullName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// Create a tar Header from the FileInfo data
|
||||||
|
header, err := tar.FileInfoHeader(info, info.Name())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use full path as name (FileInfoHeader only takes the basename)
|
||||||
|
// If we don't do this the directory strucuture would
|
||||||
|
// not be preserved
|
||||||
|
// https://golang.org/src/archive/tar/common.go?#L626
|
||||||
|
header.Name = fullName
|
||||||
|
|
||||||
|
// Write file header to the tar archive
|
||||||
|
err = tw.WriteHeader(header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !info.IsDir() {
|
||||||
|
// Copy file content to tar archive
|
||||||
|
_, err = io.Copy(tw, file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractTarGz(t *testing.T) {
|
||||||
|
// Create src dir
|
||||||
|
err := os.MkdirAll(srcDir, 0o750)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
_ = os.RemoveAll(srcDir)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Generate files to be tarballed
|
||||||
|
for _, f := range files {
|
||||||
|
err := os.MkdirAll(filepath.Dir(f), 0o755)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
_, err = os.Create(f)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create tarball
|
||||||
|
createTarball(t, "./test.tgz", srcDir)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
_ = os.RemoveAll("./test.tgz")
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create dest folder
|
||||||
|
destDir := "./test"
|
||||||
|
err = os.MkdirAll(destDir, 0o750)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
_ = os.RemoveAll(destDir)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Extract tarball
|
||||||
|
f, err := os.Open("./test.tgz")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
f.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
list, err := ExtractTarGz(context.TODO(), f, destDir, 0)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Final checks
|
||||||
|
assert.NotEmpty(t, list)
|
||||||
|
|
||||||
|
// All extracted files are ok
|
||||||
|
for _, f := range list {
|
||||||
|
_, err := os.Stat(f)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extracted folder contains all source files (plus folders)
|
||||||
|
absDestDir, err := filepath.Abs(destDir)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
for _, f := range files {
|
||||||
|
path := filepath.Join(absDestDir, f)
|
||||||
|
assert.Contains(t, list, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractTarGzStripComponents(t *testing.T) {
|
||||||
|
// Create src dir
|
||||||
|
srcDir := "./foo"
|
||||||
|
err := os.MkdirAll(srcDir, 0o750)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
_ = os.RemoveAll(srcDir)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Generate files to be tarballed
|
||||||
|
for _, f := range files {
|
||||||
|
err := os.MkdirAll(filepath.Dir(f), 0o755)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
_, err = os.Create(f)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create tarball
|
||||||
|
createTarball(t, "./test.tgz", srcDir)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
_ = os.RemoveAll("./test.tgz")
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create dest folder
|
||||||
|
destdirStrip := "./test_strip"
|
||||||
|
err = os.MkdirAll(destdirStrip, 0o750)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
_ = os.RemoveAll(destdirStrip)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Extract tarball
|
||||||
|
f, err := os.Open("./test.tgz")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
f.Close()
|
||||||
|
})
|
||||||
|
// NOTE that here we strip first component
|
||||||
|
list, err := ExtractTarGz(context.TODO(), f, destdirStrip, 1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Final checks
|
||||||
|
assert.NotEmpty(t, list)
|
||||||
|
|
||||||
|
// All extracted files are ok
|
||||||
|
for _, f := range list {
|
||||||
|
_, err := os.Stat(f)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extracted folder contains all source files (plus folders)
|
||||||
|
absDestDirStrip, err := filepath.Abs(destdirStrip)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
for _, f := range files {
|
||||||
|
// We stripped first component (ie: srcDir)
|
||||||
|
ff := strings.TrimPrefix(f, srcDir)
|
||||||
|
path := filepath.Join(absDestDirStrip, ff)
|
||||||
|
assert.Contains(t, list, path)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -18,6 +18,7 @@ package driverdistro
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/blang/semver"
|
"github.com/blang/semver"
|
||||||
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
|
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
|
||||||
|
@ -68,15 +69,15 @@ func (c *cos) customizeBuild(ctx context.Context,
|
||||||
}
|
}
|
||||||
printer.Logger.Info("COS detected, using COS kernel headers.", printer.Logger.Args("build ID", c.buildID))
|
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)
|
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)
|
env, err := downloadKernelSrc(ctx, printer, &kr, bpfKernelSrcURL, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
currKernelDir := env[kernelDirEnv]
|
currKernelDir := env[drivertype.KernelDirEnv]
|
||||||
|
|
||||||
cosKernelDir := currKernelDir + "usr/src/"
|
cosKernelDir := filepath.Join(currKernelDir, "usr", "src")
|
||||||
entries, err := os.ReadDir(cosKernelDir)
|
entries, err := os.ReadDir(cosKernelDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -84,9 +85,9 @@ func (c *cos) customizeBuild(ctx context.Context,
|
||||||
if len(entries) == 0 {
|
if len(entries) == 0 {
|
||||||
return nil, fmt.Errorf("no COS kernel src found")
|
return nil, fmt.Errorf("no COS kernel src found")
|
||||||
}
|
}
|
||||||
cosKernelDir = entries[0].Name()
|
cosKernelDir = filepath.Join(cosKernelDir, entries[0].Name())
|
||||||
// Override env key
|
// Override env key
|
||||||
env[kernelDirEnv] = cosKernelDir
|
env[drivertype.KernelDirEnv] = cosKernelDir
|
||||||
|
|
||||||
clangCompilerHeader := fmt.Sprintf("%s/include/linux/compiler-clang.h", cosKernelDir)
|
clangCompilerHeader := fmt.Sprintf("%s/include/linux/compiler-clang.h", cosKernelDir)
|
||||||
err = utils.ReplaceLineInFile(clangCompilerHeader, "#define randomized_struct_fields_start", "", 1)
|
err = utils.ReplaceLineInFile(clangCompilerHeader, "#define randomized_struct_fields_start", "", 1)
|
||||||
|
@ -111,3 +112,18 @@ func (c *cos) customizeBuild(ctx context.Context,
|
||||||
}
|
}
|
||||||
return env, nil
|
return env, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PreferredDriver is reimplemented since COS does not support kmod
|
||||||
|
//
|
||||||
|
//nolint:gocritic // the method shall not be able to modify kr
|
||||||
|
func (c *cos) PreferredDriver(kr kernelrelease.KernelRelease, allowedDriverTypes []drivertype.DriverType) drivertype.DriverType {
|
||||||
|
for _, allowedDrvType := range allowedDriverTypes {
|
||||||
|
if allowedDrvType.String() == drivertype.TypeKmod {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if allowedDrvType.Supported(kr) {
|
||||||
|
return allowedDrvType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
package driverdistro
|
package driverdistro
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/tar"
|
"compress/gzip"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -29,6 +29,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/homedir"
|
"github.com/docker/docker/pkg/homedir"
|
||||||
|
"github.com/falcosecurity/driverkit/cmd"
|
||||||
|
"github.com/falcosecurity/driverkit/pkg/driverbuilder"
|
||||||
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
|
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
|
@ -39,9 +41,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// DefaultFalcoRepo is the default repository provided by falcosecurity to download driver artifacts from.
|
|
||||||
kernelDirEnv = "KERNELDIR"
|
|
||||||
kernelSrcDownloadFolder = "kernel-sources"
|
kernelSrcDownloadFolder = "kernel-sources"
|
||||||
|
// UndeterminedDistro is the string used for the generic distro object returned when we cannot determine the distro.
|
||||||
|
UndeterminedDistro = "undetermined"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -60,7 +62,7 @@ type Distro interface {
|
||||||
FixupKernel(kr kernelrelease.KernelRelease) kernelrelease.KernelRelease // private
|
FixupKernel(kr kernelrelease.KernelRelease) kernelrelease.KernelRelease // private
|
||||||
customizeBuild(ctx context.Context, printer *output.Printer, driverType drivertype.DriverType,
|
customizeBuild(ctx context.Context, printer *output.Printer, driverType drivertype.DriverType,
|
||||||
kr kernelrelease.KernelRelease) (map[string]string, error)
|
kr kernelrelease.KernelRelease) (map[string]string, error)
|
||||||
PreferredDriver(kr kernelrelease.KernelRelease) drivertype.DriverType
|
PreferredDriver(kr kernelrelease.KernelRelease, allowedDriverTypes []drivertype.DriverType) drivertype.DriverType
|
||||||
fmt.Stringer
|
fmt.Stringer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +95,7 @@ func Discover(kr kernelrelease.KernelRelease, hostroot string) (Distro, error) {
|
||||||
|
|
||||||
// Return a generic distro to try the build
|
// Return a generic distro to try the build
|
||||||
distro = &generic{}
|
distro = &generic{}
|
||||||
if err = distro.init(kr, "undetermined", nil); err != nil {
|
if err = distro.init(kr, UndeterminedDistro, nil); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return distro, ErrUnsupported
|
return distro, ErrUnsupported
|
||||||
|
@ -106,7 +108,7 @@ func getOSReleaseDistro(kr *kernelrelease.KernelRelease) (Distro, error) {
|
||||||
}
|
}
|
||||||
idKey, err := cfg.Section("").GetKey("ID")
|
idKey, err := cfg.Section("").GetKey("ID")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil
|
return nil, err
|
||||||
}
|
}
|
||||||
id := strings.ToLower(idKey.String())
|
id := strings.ToLower(idKey.String())
|
||||||
|
|
||||||
|
@ -167,30 +169,68 @@ func Build(ctx context.Context,
|
||||||
driverName string,
|
driverName string,
|
||||||
driverType drivertype.DriverType,
|
driverType drivertype.DriverType,
|
||||||
driverVer string,
|
driverVer string,
|
||||||
|
downloadHeaders bool,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
|
printer.Logger.Info("Trying to compile the requested driver")
|
||||||
driverFileName := toFilename(d, &kr, driverName, driverType)
|
driverFileName := toFilename(d, &kr, driverName, driverType)
|
||||||
destination := toLocalPath(driverVer, driverFileName, kr.Architecture.ToNonDeb())
|
destPath := toLocalPath(driverVer, driverFileName, kr.Architecture.ToNonDeb())
|
||||||
if exist, _ := utils.FileExists(destination); exist {
|
if exist, _ := utils.FileExists(destPath); exist {
|
||||||
return destination, ErrAlreadyPresent
|
return destPath, ErrAlreadyPresent
|
||||||
}
|
}
|
||||||
|
|
||||||
env, err := d.customizeBuild(ctx, printer, driverType, kr)
|
env, err := d.customizeBuild(ctx, printer, driverType, kr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
path, err := driverType.Build(ctx, printer, kr, driverName, driverVer, env)
|
|
||||||
|
ro, err := getDKRootOptions(d, kr, driverType, driverVer, driverName, destPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
// Copy the path to the expected location.
|
|
||||||
// NOTE: for kmod, this is not useful since the driver will
|
// Disable automatic kernel headers fetching
|
||||||
// be loaded directly by dkms.
|
// if customizeBuild already retrieved kernel headers for us
|
||||||
printer.Logger.Info("Copying built driver to its destination.", printer.Logger.Args("src", path, "dst", destination))
|
// (and has set the KernelDirEnv key)
|
||||||
f, err := os.Open(filepath.Clean(path))
|
if _, ok := env[drivertype.KernelDirEnv]; ok {
|
||||||
if err != nil {
|
downloadHeaders = false
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
return destination, copyDataToLocalPath(destination, f)
|
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()
|
||||||
|
if err != nil {
|
||||||
|
return nil, 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// Download will try to download drivers for a distro trying specified repos.
|
// Download will try to download drivers for a distro trying specified repos.
|
||||||
|
@ -203,6 +243,7 @@ func Download(ctx context.Context,
|
||||||
driverName string,
|
driverName string,
|
||||||
driverType drivertype.DriverType,
|
driverType drivertype.DriverType,
|
||||||
driverVer string, repos []string,
|
driverVer string, repos []string,
|
||||||
|
httpHeaders string,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
driverFileName := toFilename(d, &kr, driverName, driverType)
|
driverFileName := toFilename(d, &kr, driverName, driverType)
|
||||||
// Skip if existent
|
// Skip if existent
|
||||||
|
@ -214,14 +255,25 @@ func Download(ctx context.Context,
|
||||||
// Try to download from any specified repository,
|
// Try to download from any specified repository,
|
||||||
// stopping at first successful http GET.
|
// stopping at first successful http GET.
|
||||||
for _, repo := range repos {
|
for _, repo := range repos {
|
||||||
url := toURL(repo, driverVer, driverFileName, kr.Architecture.ToNonDeb())
|
driverURL := toURL(repo, driverVer, driverFileName, kr.Architecture.ToNonDeb())
|
||||||
printer.Logger.Info("Trying to download a driver.", printer.Logger.Args("url", url))
|
printer.Logger.Info("Trying to download a driver.", printer.Logger.Args("url", driverURL))
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, driverURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printer.Logger.Warn("Error creating http request.", printer.Logger.Args("err", err))
|
printer.Logger.Warn("Error creating http request.", printer.Logger.Args("err", err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if httpHeaders != "" {
|
||||||
|
header := http.Header{}
|
||||||
|
for _, h := range strings.Split(httpHeaders, ",") {
|
||||||
|
key, value := func() (string, string) {
|
||||||
|
x := strings.Split(h, ":")
|
||||||
|
return x[0], x[1]
|
||||||
|
}()
|
||||||
|
header.Add(key, value)
|
||||||
|
}
|
||||||
|
req.Header = header
|
||||||
|
}
|
||||||
resp, err := http.DefaultClient.Do(req)
|
resp, err := http.DefaultClient.Do(req)
|
||||||
if err != nil || resp.StatusCode != 200 {
|
if err != nil || resp.StatusCode != 200 {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -319,7 +371,7 @@ func downloadKernelSrc(ctx context.Context,
|
||||||
return env, err
|
return env, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = utils.ExtractTarGz(resp.Body, fullKernelDir, stripComponents)
|
_, err = utils.ExtractTarGz(ctx, resp.Body, fullKernelDir, stripComponents)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return env, err
|
return env, err
|
||||||
}
|
}
|
||||||
|
@ -336,12 +388,17 @@ func downloadKernelSrc(ctx context.Context,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var src io.Reader
|
var src io.ReadCloser
|
||||||
if strings.HasSuffix(kernelConfigPath, ".gz") {
|
if strings.HasSuffix(kernelConfigPath, ".gz") {
|
||||||
src = tar.NewReader(f)
|
src, err = gzip.NewReader(f)
|
||||||
|
if err != nil {
|
||||||
|
return env, err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
src = f
|
src = f
|
||||||
}
|
}
|
||||||
|
defer src.Close()
|
||||||
|
|
||||||
fStat, err := f.Stat()
|
fStat, err := f.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -350,6 +407,6 @@ func downloadKernelSrc(ctx context.Context,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
env[kernelDirEnv] = fullKernelDir
|
env[drivertype.KernelDirEnv] = fullKernelDir
|
||||||
return env, nil
|
return env, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ func TestDiscoverDistro(t *testing.T) {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
postFn: func() {},
|
postFn: func() {},
|
||||||
distroExpected: nil,
|
distroExpected: &generic{},
|
||||||
errExpected: true,
|
errExpected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -63,8 +63,8 @@ func TestDiscoverDistro(t *testing.T) {
|
||||||
postFn: func() {
|
postFn: func() {
|
||||||
_ = os.Remove(osReleaseFile)
|
_ = os.Remove(osReleaseFile)
|
||||||
},
|
},
|
||||||
distroExpected: nil,
|
distroExpected: &generic{},
|
||||||
errExpected: false,
|
errExpected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// os-release ID "foo" mapped to generic
|
// os-release ID "foo" mapped to generic
|
||||||
|
@ -167,6 +167,28 @@ func TestDiscoverDistro(t *testing.T) {
|
||||||
distroExpected: &bottlerocket{},
|
distroExpected: &bottlerocket{},
|
||||||
errExpected: false,
|
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
|
// No os-release but "centos-release" file present maps to centos
|
||||||
krInput: "5.10.0",
|
krInput: "5.10.0",
|
||||||
|
@ -206,9 +228,8 @@ func TestDiscoverDistro(t *testing.T) {
|
||||||
d, err := Discover(kr, localHostRoot)
|
d, err := Discover(kr, localHostRoot)
|
||||||
if tCase.errExpected {
|
if tCase.errExpected {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
} else {
|
|
||||||
assert.IsType(t, tCase.distroExpected, d)
|
|
||||||
}
|
}
|
||||||
|
assert.IsType(t, tCase.distroExpected, d)
|
||||||
tCase.postFn()
|
tCase.postFn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,29 +28,57 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const flatcarRelocateScript = `
|
const flatcarRelocateScript = `
|
||||||
local -a tools=(
|
set -euo pipefail
|
||||||
scripts/basic/fixdep
|
|
||||||
scripts/mod/modpost
|
shopt -s nullglob
|
||||||
tools/objtool/objtool
|
|
||||||
)
|
hostlds=( /host/usr/lib64/ld-linux-*.so.* )
|
||||||
local -r hostld=$(ls /host/usr/lib64/ld-linux-*.so.*)
|
if [[ ${#hostlds[@]} -eq 0 ]]; then
|
||||||
local -r kdir=/lib/modules/$(ls /lib/modules/)/build
|
echo "** no dynamic loaders found"
|
||||||
echo "** Found host dl interpreter: ${hostld}"
|
exit 1
|
||||||
for host_tool in ${tools[@]}; do
|
fi
|
||||||
t=${host_tool}
|
if [[ ${#hostlds[@]} -gt 1 ]]; then
|
||||||
tool=$(basename $t)
|
echo "** more than one fitting dynamic loader found, picking first"
|
||||||
tool_dir=$(dirname $t)
|
fi
|
||||||
host_tool=${kdir}/${host_tool}
|
hostld=${hostlds[0]}
|
||||||
if [ ! -f ${host_tool} ]; then
|
echo "** Found host dynamic loader: ${hostld}"
|
||||||
|
|
||||||
|
kdirs=( /host/lib/modules/*/build )
|
||||||
|
if [[ ${#kdirs[@]} -eq 0 ]]; then
|
||||||
|
echo "** no kernel module tools directories found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ ${#kdirs[@]} -gt 1 ]]; then
|
||||||
|
echo "** more than one fitting kernel module tools directory found, picking first"
|
||||||
|
fi
|
||||||
|
kdir=${kdirs[0]}
|
||||||
|
echo "** Found kernel tools directory: ${kdir}"
|
||||||
|
|
||||||
|
tools=(
|
||||||
|
scripts/basic/fixdep
|
||||||
|
scripts/mod/modpost
|
||||||
|
tools/objtool/objtool
|
||||||
|
)
|
||||||
|
|
||||||
|
tmp_dir=$(mktemp -d)
|
||||||
|
for tool in "${tools[@]}"; do
|
||||||
|
host_tool=${kdir}/${tool}
|
||||||
|
if [[ ! -f ${host_tool} ]]; then
|
||||||
|
echo "${tool@Q} not found in ${kdir@Q}, not patching"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
umount ${host_tool} 2>/dev/null || true
|
umount "${host_tool}" 2>/dev/null || true
|
||||||
mkdir -p /tmp/${tool_dir}/
|
tmp_tool=${tmp_dir}/${tool}
|
||||||
cp -a ${host_tool} /tmp/${tool_dir}/
|
mkdir -p "$(dirname "${tmp_tool}")"
|
||||||
echo "** Setting host dl interpreter for $host_tool"
|
cp -a "${host_tool}" "${tmp_tool}"
|
||||||
patchelf --set-interpreter ${hostld} --set-rpath /host/usr/lib64 /tmp/${tool_dir}/${tool}
|
echo "** Setting host dynamic loader for ${tool@Q}"
|
||||||
mount -o bind /tmp/${tool_dir}/${tool} ${host_tool}
|
patchelf \
|
||||||
|
--set-interpreter "${hostld}" \
|
||||||
|
--set-rpath /host/usr/lib64 \
|
||||||
|
"${tmp_tool}"
|
||||||
|
mount -o bind "${tmp_tool}" "${host_tool}"
|
||||||
done
|
done
|
||||||
|
rm -rf "${tmp_dir}"
|
||||||
`
|
`
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -94,7 +122,7 @@ func (f *flatcar) customizeBuild(ctx context.Context,
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
printer.Logger.Info("Flatcar detected; relocating kernel tools.", printer.Logger.Args("version", f.versionID))
|
printer.Logger.Info("Flatcar detected; relocating kernel tools.", printer.Logger.Args("version", f.versionID))
|
||||||
out, err := exec.CommandContext(ctx, "/bin/bash", "-c", flatcarRelocateScript).Output()
|
out, err := exec.CommandContext(ctx, "/bin/bash", "-c", flatcarRelocateScript).CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printer.DefaultText.Print(string(out))
|
printer.DefaultText.Print(string(out))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -16,9 +16,8 @@
|
||||||
package driverdistro
|
package driverdistro
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"regexp"
|
||||||
|
|
||||||
"github.com/blang/semver"
|
|
||||||
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
|
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
|
@ -27,6 +26,14 @@ import (
|
||||||
"github.com/falcosecurity/falcoctl/pkg/output"
|
"github.com/falcosecurity/falcoctl/pkg/output"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Parse start of string as "#NUMBER":
|
||||||
|
// eg1: "#1 SMP PREEMPT_DYNAMIC Tue, 10 Oct 2023 21:10:21 +0000" -> "1".
|
||||||
|
// eg2: #1-photon -> "1"
|
||||||
|
// Old falco-driver-loader method did:
|
||||||
|
// echo "${DRIVER_KERNEL_VERSION}" | sed 's/#\([[:digit:]]\+\).*/\1/'
|
||||||
|
// The regex does the same thing.
|
||||||
|
var genericKernelVersionRegex = regexp.MustCompile(`#(\d+).*`)
|
||||||
|
|
||||||
type generic struct {
|
type generic struct {
|
||||||
targetID string
|
targetID string
|
||||||
}
|
}
|
||||||
|
@ -43,10 +50,10 @@ func (g *generic) String() string {
|
||||||
|
|
||||||
//nolint:gocritic // the method shall not be able to modify kr
|
//nolint:gocritic // the method shall not be able to modify kr
|
||||||
func (g *generic) FixupKernel(kr kernelrelease.KernelRelease) kernelrelease.KernelRelease {
|
func (g *generic) FixupKernel(kr kernelrelease.KernelRelease) kernelrelease.KernelRelease {
|
||||||
// Take eg: "#1 SMP PREEMPT_DYNAMIC Tue, 10 Oct 2023 21:10:21 +0000" and return "1".
|
matches := genericKernelVersionRegex.FindStringSubmatch(kr.KernelVersion)
|
||||||
kv := strings.TrimLeft(kr.KernelVersion, "#")
|
if len(matches) == 2 {
|
||||||
kv = strings.Split(kv, " ")[0]
|
kr.KernelVersion = matches[1]
|
||||||
kr.KernelVersion = kv
|
}
|
||||||
return kr
|
return kr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,16 +67,11 @@ func (g *generic) customizeBuild(_ context.Context,
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:gocritic // the method shall not be able to modify kr
|
//nolint:gocritic // the method shall not be able to modify kr
|
||||||
func (g *generic) PreferredDriver(kr kernelrelease.KernelRelease) drivertype.DriverType {
|
func (g *generic) PreferredDriver(kr kernelrelease.KernelRelease, allowedDriverTypes []drivertype.DriverType) drivertype.DriverType {
|
||||||
// Deadly simple default automatic selection
|
for _, allowedDrvType := range allowedDriverTypes {
|
||||||
var dType drivertype.DriverType
|
if allowedDrvType.Supported(kr) {
|
||||||
switch {
|
return allowedDrvType
|
||||||
case kr.GTE(semver.MustParse("5.8.0")):
|
}
|
||||||
dType, _ = drivertype.Parse(drivertype.TypeModernBpf)
|
|
||||||
case kr.SupportsProbe():
|
|
||||||
dType, _ = drivertype.Parse(drivertype.TypeBpf)
|
|
||||||
default:
|
|
||||||
dType, _ = drivertype.Parse(drivertype.TypeKmod)
|
|
||||||
}
|
}
|
||||||
return dType
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
// 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 (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDistroGeneric(t *testing.T) {
|
||||||
|
type testCase struct {
|
||||||
|
krInput string
|
||||||
|
kvInput string
|
||||||
|
kvExpected string
|
||||||
|
}
|
||||||
|
testCases := []testCase{
|
||||||
|
{
|
||||||
|
krInput: "4.19.283-3.ph3",
|
||||||
|
kvInput: "#1-photon SMP Fri Jun 16 02:25:27 UTC 2023",
|
||||||
|
kvExpected: "1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
krInput: "6.7.2-arch1-2",
|
||||||
|
kvInput: "#1 SMP PREEMPT_DYNAMIC Wed, 31 Jan 2024 09:22:15 +0000",
|
||||||
|
kvExpected: "1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
krInput: "6.7.2-arch1-2",
|
||||||
|
kvInput: "#231asfa #rf3f",
|
||||||
|
kvExpected: "231",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
krInput: "6.7.2-arch1-2",
|
||||||
|
kvInput: "#231asfa234",
|
||||||
|
kvExpected: "231",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
g := &generic{}
|
||||||
|
for _, tCase := range testCases {
|
||||||
|
kr := kernelrelease.FromString(tCase.krInput)
|
||||||
|
kr.KernelVersion = tCase.kvInput
|
||||||
|
fixedKr := g.FixupKernel(kr)
|
||||||
|
assert.Equal(t, tCase.kvExpected, fixedKr.KernelVersion)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
// 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
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -54,12 +54,9 @@ func (u *ubuntu) FixupKernel(kr kernelrelease.KernelRelease) kernelrelease.Kerne
|
||||||
// from the following `uname -v` result
|
// from the following `uname -v` result
|
||||||
// `#26~22.04.1-Ubuntu SMP Mon Apr 24 01:58:15 UTC 2023`
|
// `#26~22.04.1-Ubuntu SMP Mon Apr 24 01:58:15 UTC 2023`
|
||||||
// we obtain the kernelversion`26~22.04.1`.
|
// we obtain the kernelversion`26~22.04.1`.
|
||||||
// NOTE: driverkernel.FetchInfo() already trims leading "#"
|
// Another example: "#1 SMP PREEMPT_DYNAMIC Tue, 10 Oct 2023 21:10:21 +0000" and return "1".
|
||||||
// and everything starting from the first whitespace,
|
kv := strings.TrimLeft(kr.KernelVersion, "#")
|
||||||
// so that eg: we receive "26~22.04.1-Ubuntu",
|
kv = strings.Split(kv, " ")[0]
|
||||||
// therefore we only need to drop "-Ubuntu" suffix
|
kr.KernelVersion = strings.TrimSuffix(kv, "-Ubuntu")
|
||||||
// Take eg: "#1 SMP PREEMPT_DYNAMIC Tue, 10 Oct 2023 21:10:21 +0000" and return "1".
|
|
||||||
kr = u.generic.FixupKernel(kr)
|
|
||||||
kr.KernelVersion = strings.TrimSuffix(kr.KernelVersion, "-Ubuntu")
|
|
||||||
return kr
|
return kr
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -13,6 +13,8 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build linux
|
||||||
|
|
||||||
// Package driverkernel implements the kernel info fetching helpers.
|
// Package driverkernel implements the kernel info fetching helpers.
|
||||||
package driverkernel
|
package driverkernel
|
||||||
|
|
||||||
|
@ -38,8 +40,11 @@ func FetchInfo(enforcedKR, enforcedKV string) (kernelrelease.KernelRelease, erro
|
||||||
|
|
||||||
kr = string(bytes.Trim(u.Release[:], "\x00"))
|
kr = string(bytes.Trim(u.Release[:], "\x00"))
|
||||||
kv = string(bytes.Trim(u.Version[:], "\x00"))
|
kv = string(bytes.Trim(u.Version[:], "\x00"))
|
||||||
} else {
|
}
|
||||||
|
if enforcedKR != "" {
|
||||||
kr = enforcedKR
|
kr = enforcedKR
|
||||||
|
}
|
||||||
|
if enforcedKV != "" {
|
||||||
kv = enforcedKV
|
kv = enforcedKV
|
||||||
}
|
}
|
||||||
kernelRel := kernelrelease.FromString(kr)
|
kernelRel := kernelrelease.FromString(kr)
|
|
@ -0,0 +1,29 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//go:build !linux
|
||||||
|
|
||||||
|
package driverkernel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FetchInfo returns information about currently running kernel.
|
||||||
|
func FetchInfo(_, _ string) (kernelrelease.KernelRelease, error) {
|
||||||
|
return kernelrelease.KernelRelease{}, errors.New("unsupported")
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -18,21 +18,15 @@ package drivertype
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/homedir"
|
"github.com/docker/docker/pkg/homedir"
|
||||||
|
"github.com/falcosecurity/driverkit/cmd"
|
||||||
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
|
"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"
|
"github.com/falcosecurity/falcoctl/pkg/output"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TypeBpf is the string for the bpf driver type.
|
|
||||||
const TypeBpf = "ebpf"
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
driverTypes[TypeBpf] = &bpf{}
|
driverTypes[TypeBpf] = &bpf{}
|
||||||
}
|
}
|
||||||
|
@ -74,44 +68,12 @@ func (b *bpf) HasArtifacts() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:gocritic // the method shall not be able to modify kr
|
//nolint:gocritic // the method shall not be able to modify kr
|
||||||
func (b *bpf) Build(ctx context.Context,
|
func (b *bpf) Supported(kr kernelrelease.KernelRelease) bool {
|
||||||
printer *output.Printer,
|
return kr.SupportsProbe()
|
||||||
_ 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
|
|
||||||
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 {
|
func (b *bpf) ToOutput(destPath string) cmd.OutputOptions {
|
||||||
// Mount /sys/kernel/debug that is needed on old (pre 4.17) kernel releases,
|
return cmd.OutputOptions{
|
||||||
// since these releases still did not support raw tracepoints.
|
Probe: destPath,
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
// 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"
|
||||||
|
)
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -19,33 +19,21 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/falcosecurity/driverkit/cmd"
|
||||||
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
|
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
|
||||||
"golang.org/x/net/context"
|
|
||||||
|
|
||||||
"github.com/falcosecurity/falcoctl/pkg/output"
|
"github.com/falcosecurity/falcoctl/pkg/output"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// TypeKmod is the string for the bpf driver type.
|
|
||||||
TypeKmod = "kmod"
|
|
||||||
maxRmmodWait = 10
|
maxRmmodWait = 10
|
||||||
rmmodWaitTime = 5 * time.Second
|
rmmodWaitTime = 5 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
type errMissingDep struct {
|
|
||||||
program string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *errMissingDep) Error() string {
|
|
||||||
return fmt.Sprintf("This program requires %s.", e.program)
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
driverTypes[TypeKmod] = &kmod{}
|
driverTypes[TypeKmod] = &kmod{}
|
||||||
}
|
}
|
||||||
|
@ -61,18 +49,18 @@ func (k *kmod) String() string {
|
||||||
// Then, using dkms, it tries to fetch all
|
// Then, using dkms, it tries to fetch all
|
||||||
// dkms-installed versions of the module to clean them up.
|
// dkms-installed versions of the module to clean them up.
|
||||||
func (k *kmod) Cleanup(printer *output.Printer, driverName string) error {
|
func (k *kmod) Cleanup(printer *output.Printer, driverName string) error {
|
||||||
_, err := exec.Command("bash", "-c", "hash lsmod").Output()
|
lsmod, err := exec.LookPath("lsmod")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &errMissingDep{program: "lsmod"}
|
return err
|
||||||
}
|
}
|
||||||
_, err = exec.Command("bash", "-c", "hash rmmod").Output()
|
rmmod, err := exec.LookPath("rmmod")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &errMissingDep{program: "rmmod"}
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
kmodName := strings.ReplaceAll(driverName, "-", "_")
|
kmodName := strings.ReplaceAll(driverName, "-", "_")
|
||||||
printer.Logger.Info("Check if kernel module is still loaded.")
|
printer.Logger.Info("Check if kernel module is still loaded.")
|
||||||
lsmodCmdArgs := fmt.Sprintf(`lsmod | cut -d' ' -f1 | grep -qx %q`, kmodName)
|
lsmodCmdArgs := fmt.Sprintf(`%s | cut -d' ' -f1 | grep -qx %q`, lsmod, kmodName)
|
||||||
_, err = exec.Command("bash", "-c", lsmodCmdArgs).Output() //nolint:gosec // false positive
|
_, err = exec.Command("bash", "-c", lsmodCmdArgs).Output() //nolint:gosec // false positive
|
||||||
if err == nil {
|
if err == nil {
|
||||||
unloaded := false
|
unloaded := false
|
||||||
|
@ -80,7 +68,7 @@ func (k *kmod) Cleanup(printer *output.Printer, driverName string) error {
|
||||||
for i := 0; i < maxRmmodWait; i++ {
|
for i := 0; i < maxRmmodWait; i++ {
|
||||||
printer.Logger.Info("Kernel module is still loaded.")
|
printer.Logger.Info("Kernel module is still loaded.")
|
||||||
printer.Logger.Info("Trying to unload it with 'rmmod'.")
|
printer.Logger.Info("Trying to unload it with 'rmmod'.")
|
||||||
if _, err = exec.Command("rmmod", kmodName).Output(); err == nil { //nolint:gosec // false positive
|
if _, err = exec.Command(rmmod, kmodName).Output(); err == nil { //nolint:gosec // false positive
|
||||||
printer.Logger.Info("OK! Unloading module succeeded.")
|
printer.Logger.Info("OK! Unloading module succeeded.")
|
||||||
unloaded = true
|
unloaded = true
|
||||||
break
|
break
|
||||||
|
@ -97,14 +85,14 @@ func (k *kmod) Cleanup(printer *output.Printer, driverName string) error {
|
||||||
printer.Logger.Info("OK! There is no module loaded.")
|
printer.Logger.Info("OK! There is no module loaded.")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = exec.Command("bash", "-c", "hash dkms").Output()
|
dkms, err := exec.LookPath("dkms")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printer.Logger.Info("Skipping dkms remove (dkms not found).")
|
printer.Logger.Info("Skipping dkms remove (dkms not found).")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
printer.Logger.Info("Check all versions of kernel module in dkms.")
|
printer.Logger.Info("Check all versions of kernel module in dkms.")
|
||||||
dkmsLsCmdArgs := fmt.Sprintf(`dkms status -m %q | tr -d "," | tr -d ":" | tr "/" " " | cut -d' ' -f2`, kmodName)
|
dkmsLsCmdArgs := fmt.Sprintf(`%s status -m %q | tr -d "," | tr -d ":" | tr "/" " " | cut -d' ' -f2`, dkms, kmodName)
|
||||||
out, err := exec.Command("bash", "-c", dkmsLsCmdArgs).Output() //nolint:gosec // false positive
|
out, err := exec.Command("bash", "-c", dkmsLsCmdArgs).Output() //nolint:gosec // false positive
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printer.Logger.Warn("Listing kernel module versions failed.", printer.Logger.Args("reason", err))
|
printer.Logger.Warn("Listing kernel module versions failed.", printer.Logger.Args("reason", err))
|
||||||
|
@ -118,7 +106,7 @@ func (k *kmod) Cleanup(printer *output.Printer, driverName string) error {
|
||||||
scanner := bufio.NewScanner(outBuffer)
|
scanner := bufio.NewScanner(outBuffer)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
dVer := scanner.Text()
|
dVer := scanner.Text()
|
||||||
dkmsRmCmdArgs := fmt.Sprintf(`dkms remove -m %s -v %q --all`, kmodName, dVer)
|
dkmsRmCmdArgs := fmt.Sprintf(`%s remove -m %s -v %q --all`, dkms, kmodName, dVer)
|
||||||
_, err = exec.Command("bash", "-c", dkmsRmCmdArgs).Output() //nolint:gosec // false positive
|
_, err = exec.Command("bash", "-c", dkmsRmCmdArgs).Output() //nolint:gosec // false positive
|
||||||
if err == nil {
|
if err == nil {
|
||||||
printer.Logger.Info("OK! Removing succeeded.", printer.Logger.Args("version", dVer))
|
printer.Logger.Info("OK! Removing succeeded.", printer.Logger.Args("version", dVer))
|
||||||
|
@ -165,90 +153,13 @@ func (k *kmod) HasArtifacts() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
//nolint:gocritic // the method shall not be able to modify kr
|
||||||
func (k *kmod) Build(ctx context.Context,
|
func (k *kmod) Supported(kr kernelrelease.KernelRelease) bool {
|
||||||
printer *output.Printer,
|
return kr.SupportsModule()
|
||||||
kr kernelrelease.KernelRelease,
|
}
|
||||||
driverName, driverVersion string,
|
|
||||||
_ map[string]string,
|
func (k *kmod) ToOutput(destPath string) cmd.OutputOptions {
|
||||||
) (string, error) {
|
return cmd.OutputOptions{
|
||||||
// Skip dkms on UEK hosts because it will always fail
|
Module: destPath,
|
||||||
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
|
|
||||||
}
|
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -13,18 +13,21 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build linux
|
||||||
|
|
||||||
package drivertype
|
package drivertype
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "unsafe" // Needed for go:linkname to be able to access a private function from cilium/ebpf/features.
|
||||||
|
|
||||||
|
"github.com/cilium/ebpf"
|
||||||
|
"github.com/cilium/ebpf/features"
|
||||||
|
"github.com/falcosecurity/driverkit/cmd"
|
||||||
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
|
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
|
||||||
"golang.org/x/net/context"
|
|
||||||
|
|
||||||
"github.com/falcosecurity/falcoctl/pkg/output"
|
"github.com/falcosecurity/falcoctl/pkg/output"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TypeModernBpf is the string for the bpf driver type.
|
|
||||||
const TypeModernBpf = "modern_ebpf"
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
driverTypes[TypeModernBpf] = &modernBpf{}
|
driverTypes[TypeModernBpf] = &modernBpf{}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +54,31 @@ func (m *modernBpf) HasArtifacts() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the private symbol `probeProgram` that will be used to test for
|
||||||
|
// type Tracing, attachType AttachTraceRawTp program availability.
|
||||||
|
//
|
||||||
|
//go:linkname probeProgram github.com/cilium/ebpf/features.probeProgram
|
||||||
|
func probeProgram(spec *ebpf.ProgramSpec) error
|
||||||
|
|
||||||
//nolint:gocritic // the method shall not be able to modify kr
|
//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) {
|
func (m *modernBpf) Supported(_ kernelrelease.KernelRelease) bool {
|
||||||
return "", nil
|
// We can't directly use this because it uses the wrong attachtype.
|
||||||
|
// err := features.HaveProgramType(ebpf.Tracing)
|
||||||
|
// Therefore, we need to manually build a feature test.
|
||||||
|
// Empty tracing program that just returns 0
|
||||||
|
progSpec := &ebpf.ProgramSpec{
|
||||||
|
Type: ebpf.Tracing,
|
||||||
|
AttachType: ebpf.AttachTraceRawTp,
|
||||||
|
AttachTo: "sys_enter",
|
||||||
|
}
|
||||||
|
err := probeProgram(progSpec)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return features.HaveMapType(ebpf.RingBuf) == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *modernBpf) ToOutput(_ string) cmd.OutputOptions {
|
||||||
|
return cmd.OutputOptions{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -19,14 +19,14 @@ package drivertype
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/falcosecurity/driverkit/cmd"
|
||||||
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
|
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
|
||||||
"golang.org/x/net/context"
|
|
||||||
|
|
||||||
"github.com/falcosecurity/falcoctl/pkg/output"
|
"github.com/falcosecurity/falcoctl/pkg/output"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TypeAuto enables a smart automatic driver selection logic instead of using a fixed driver type.
|
// KernelDirEnv is the env variable set to kernel headers extraction paths.
|
||||||
const TypeAuto = "auto"
|
const KernelDirEnv = "KERNELDIR"
|
||||||
|
|
||||||
var driverTypes = map[string]DriverType{}
|
var driverTypes = map[string]DriverType{}
|
||||||
|
|
||||||
|
@ -37,8 +37,8 @@ type DriverType interface {
|
||||||
Load(printer *output.Printer, src, driverName string, fallback bool) error
|
Load(printer *output.Printer, src, driverName string, fallback bool) error
|
||||||
Extension() string
|
Extension() string
|
||||||
HasArtifacts() bool
|
HasArtifacts() bool
|
||||||
Build(ctx context.Context, printer *output.Printer, kr kernelrelease.KernelRelease,
|
ToOutput(destPath string) cmd.OutputOptions
|
||||||
driverName, driverVersion string, env map[string]string) (string, error)
|
Supported(kr kernelrelease.KernelRelease) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTypes return the list of supported driver types.
|
// GetTypes return the list of supported driver types.
|
||||||
|
@ -47,9 +47,6 @@ func GetTypes() []string {
|
||||||
for key := range driverTypes {
|
for key := range driverTypes {
|
||||||
driverTypesSlice = append(driverTypesSlice, key)
|
driverTypesSlice = append(driverTypesSlice, key)
|
||||||
}
|
}
|
||||||
// auto is a sentinel value to enable automatic driver selection logic,
|
|
||||||
// but it is not mapped to any DriverType
|
|
||||||
driverTypesSlice = append(driverTypesSlice, TypeAuto)
|
|
||||||
return driverTypesSlice
|
return driverTypesSlice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,5 +55,5 @@ func Parse(driverType string) (DriverType, error) {
|
||||||
if dType, ok := driverTypes[driverType]; ok {
|
if dType, ok := driverTypes[driverType]; ok {
|
||||||
return dType, nil
|
return dType, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("wrong driver type specified: %s", driverType)
|
return nil, fmt.Errorf("unsupported driver type specified: %s", driverType)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
// 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
|
|
@ -13,7 +13,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package options
|
package enum
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -26,21 +26,21 @@ import (
|
||||||
// can have a limited set of values.
|
// can have a limited set of values.
|
||||||
type Enum struct {
|
type Enum struct {
|
||||||
allowed []string
|
allowed []string
|
||||||
value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEnum returns an enum struct. The firs argument is a set of values allowed for the flag.
|
// 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 {
|
func NewEnum(allowed []string, d string) *Enum {
|
||||||
return &Enum{
|
return &Enum{
|
||||||
allowed: allowed,
|
allowed: allowed,
|
||||||
value: d,
|
Value: d,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns the value.
|
// String returns the Value.
|
||||||
func (e *Enum) String() string {
|
func (e *Enum) String() string {
|
||||||
return e.value
|
return e.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allowed returns the list of allowed values enclosed in parenthesis.
|
// 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, ", "))
|
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 {
|
func (e *Enum) Set(p string) error {
|
||||||
if !slices.Contains(e.allowed, p) {
|
if !slices.Contains(e.allowed, p) {
|
||||||
return fmt.Errorf("invalid argument %q, please provide one of (%s)", p, strings.Join(e.allowed, ", "))
|
return fmt.Errorf("invalid argument %q, please provide one of (%s)", p, strings.Join(e.allowed, ", "))
|
||||||
}
|
}
|
||||||
e.value = p
|
e.Value = p
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package options
|
package enum
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/onsi/ginkgo/v2"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
@ -41,7 +41,7 @@ var _ = Describe("Enum", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should set the default values", 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() {
|
It("should set the allowed values", func() {
|
||||||
|
@ -64,9 +64,9 @@ var _ = Describe("Enum", func() {
|
||||||
val = newVal
|
val = newVal
|
||||||
})
|
})
|
||||||
|
|
||||||
It("Should set the correct val", func() {
|
It("Should set the correct value", func() {
|
||||||
Expect(err).ShouldNot(HaveOccurred())
|
Expect(err).ShouldNot(HaveOccurred())
|
||||||
Expect(enum.value).Should(Equal(newVal))
|
Expect(enum.Value).Should(Equal(newVal))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -22,8 +22,10 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/falcosecurity/falcoctl/pkg/index/config"
|
"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/gcs"
|
||||||
"github.com/falcosecurity/falcoctl/pkg/index/fetch/http"
|
"github.com/falcosecurity/falcoctl/pkg/index/fetch/http"
|
||||||
|
"github.com/falcosecurity/falcoctl/pkg/index/fetch/s3"
|
||||||
"github.com/falcosecurity/falcoctl/pkg/index/index"
|
"github.com/falcosecurity/falcoctl/pkg/index/index"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -46,11 +48,15 @@ func NewFetcher() *Fetcher {
|
||||||
"http": http.Fetch,
|
"http": http.Fetch,
|
||||||
"https": http.Fetch,
|
"https": http.Fetch,
|
||||||
"gcs": gcs.Fetch,
|
"gcs": gcs.Fetch,
|
||||||
|
"file": file.Fetch,
|
||||||
|
"s3": s3.Fetch,
|
||||||
},
|
},
|
||||||
schemeDefaultBackends: map[string]string{
|
schemeDefaultBackends: map[string]string{
|
||||||
"http": "http",
|
"http": "http",
|
||||||
"https": "https",
|
"https": "https",
|
||||||
"gs": "gcs",
|
"gs": "gcs",
|
||||||
|
"file": "file",
|
||||||
|
"s3": "s3",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
// 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
|
|
@ -0,0 +1,41 @@
|
||||||
|
// 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
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
// 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)
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
// 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
|
|
@ -0,0 +1,63 @@
|
||||||
|
// 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
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
// 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
|
||||||
|
}
|
|
@ -63,6 +63,8 @@ type CosignSignature struct {
|
||||||
CertificateIdentity string `yaml:"certificate-identity"`
|
CertificateIdentity string `yaml:"certificate-identity"`
|
||||||
CertificateIdentityRegexp string `yaml:"certificate-identity-regexp"`
|
CertificateIdentityRegexp string `yaml:"certificate-identity-regexp"`
|
||||||
CertificateGithubWorkflow string `yaml:"certificate-github-workflow"`
|
CertificateGithubWorkflow string `yaml:"certificate-github-workflow"`
|
||||||
|
KeyRef string `yaml:"key"`
|
||||||
|
IgnoreTlog bool `yaml:"ignore-tlog"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signature represents all the metadata necessary to perform signature verification.
|
// Signature represents all the metadata necessary to perform signature verification.
|
||||||
|
|
|
@ -18,8 +18,8 @@ package authn
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
credentials "github.com/oras-project/oras-credentials-go"
|
|
||||||
"oras.land/oras-go/v2/registry/remote/auth"
|
"oras.land/oras-go/v2/registry/remote/auth"
|
||||||
|
"oras.land/oras-go/v2/registry/remote/credentials"
|
||||||
|
|
||||||
"github.com/falcosecurity/falcoctl/internal/login"
|
"github.com/falcosecurity/falcoctl/internal/login"
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,8 +21,8 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
credentials "github.com/oras-project/oras-credentials-go"
|
|
||||||
"oras.land/oras-go/v2/registry/remote/auth"
|
"oras.land/oras-go/v2/registry/remote/auth"
|
||||||
|
"oras.land/oras-go/v2/registry/remote/credentials"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -55,7 +55,7 @@ func GCPCredential(ctx context.Context, reg string) (auth.Credential, error) {
|
||||||
|
|
||||||
// load saved tokenSource or saves it
|
// load saved tokenSource or saves it
|
||||||
if SavedTokenSource == nil {
|
if SavedTokenSource == nil {
|
||||||
tokenSource, err = google.DefaultTokenSource(ctx)
|
tokenSource, err = google.DefaultTokenSource(ctx, "https://www.googleapis.com/auth/cloud-platform")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return auth.EmptyCredential, fmt.Errorf("error while trying to identify a GCP TokenSource %w", err)
|
return auth.EmptyCredential, fmt.Errorf("error while trying to identify a GCP TokenSource %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -203,7 +203,8 @@ func (p *Pusher) Push(ctx context.Context, artifactType oci.ArtifactType,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &oci.RegistryResult{
|
return &oci.RegistryResult{
|
||||||
Digest: string(rootDesc.Digest),
|
RootDigest: string(rootDesc.Digest),
|
||||||
|
Type: artifactType,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -138,7 +138,7 @@ var _ = Describe("Pusher", func() {
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
// Being the artifact of type plugin we expect that the retrieved descriptor is of type image index.
|
// Being the artifact of type plugin we expect that the retrieved descriptor is of type image index.
|
||||||
Expect(d.MediaType).To(Equal(v1.MediaTypeImageIndex))
|
Expect(d.MediaType).To(Equal(v1.MediaTypeImageIndex))
|
||||||
Expect(d.Digest.String()).To(Equal(result.Digest))
|
Expect(d.Digest.String()).To(Equal(result.RootDigest))
|
||||||
Expect(index.Manifests).To(HaveLen(1))
|
Expect(index.Manifests).To(HaveLen(1))
|
||||||
Expect(fmt.Sprintf("%s/%s", index.Manifests[0].Platform.OS, index.Manifests[0].Platform.Architecture)).To(Equal(testPluginPlatform1))
|
Expect(fmt.Sprintf("%s/%s", index.Manifests[0].Platform.OS, index.Manifests[0].Platform.Architecture)).To(Equal(testPluginPlatform1))
|
||||||
// Check layer and config media types.
|
// Check layer and config media types.
|
||||||
|
@ -188,7 +188,7 @@ var _ = Describe("Pusher", func() {
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
// Being the artifact of type plugin we expect that the retrieved descriptor is of type image index.
|
// Being the artifact of type plugin we expect that the retrieved descriptor is of type image index.
|
||||||
Expect(d.MediaType).To(Equal(v1.MediaTypeImageIndex))
|
Expect(d.MediaType).To(Equal(v1.MediaTypeImageIndex))
|
||||||
Expect(d.Digest.String()).To(Equal(result.Digest))
|
Expect(d.Digest.String()).To(Equal(result.RootDigest))
|
||||||
Expect(index.Manifests).To(HaveLen(3))
|
Expect(index.Manifests).To(HaveLen(3))
|
||||||
Expect(fmt.Sprintf("%s/%s", index.Manifests[0].Platform.OS, index.Manifests[0].Platform.Architecture)).To(Equal(testPluginPlatform1))
|
Expect(fmt.Sprintf("%s/%s", index.Manifests[0].Platform.OS, index.Manifests[0].Platform.Architecture)).To(Equal(testPluginPlatform1))
|
||||||
Expect(fmt.Sprintf("%s/%s", index.Manifests[1].Platform.OS, index.Manifests[1].Platform.Architecture)).To(Equal(testPluginPlatform2))
|
Expect(fmt.Sprintf("%s/%s", index.Manifests[1].Platform.OS, index.Manifests[1].Platform.Architecture)).To(Equal(testPluginPlatform2))
|
||||||
|
@ -225,7 +225,7 @@ var _ = Describe("Pusher", func() {
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
// Being the artifact of type rulesfile we expect that the retrieved descriptor is of type manifest.
|
// Being the artifact of type rulesfile we expect that the retrieved descriptor is of type manifest.
|
||||||
Expect(d.MediaType).To(Equal(v1.MediaTypeImageManifest))
|
Expect(d.MediaType).To(Equal(v1.MediaTypeImageManifest))
|
||||||
Expect(d.Digest.String()).To(Equal(result.Digest))
|
Expect(d.Digest.String()).To(Equal(result.RootDigest))
|
||||||
// It must have only one layer since no config layer is configured.
|
// It must have only one layer since no config layer is configured.
|
||||||
Expect(manifest.Layers).To(HaveLen(1))
|
Expect(manifest.Layers).To(HaveLen(1))
|
||||||
// It must have the rulesfile's layer mediatype.
|
// It must have the rulesfile's layer mediatype.
|
||||||
|
@ -276,7 +276,7 @@ var _ = Describe("Pusher", func() {
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
// Being the artifact of type rulesfile we expect that the retrieved descriptor is of type manifest.
|
// Being the artifact of type rulesfile we expect that the retrieved descriptor is of type manifest.
|
||||||
Expect(d.MediaType).To(Equal(v1.MediaTypeImageManifest))
|
Expect(d.MediaType).To(Equal(v1.MediaTypeImageManifest))
|
||||||
Expect(d.Digest.String()).To(Equal(result.Digest))
|
Expect(d.Digest.String()).To(Equal(result.RootDigest))
|
||||||
// It must have only one layer since no config layer is configured.
|
// It must have only one layer since no config layer is configured.
|
||||||
Expect(manifest.Layers).To(HaveLen(1))
|
Expect(manifest.Layers).To(HaveLen(1))
|
||||||
// It must have the rulesfile's layer mediatype.
|
// It must have the rulesfile's layer mediatype.
|
||||||
|
@ -348,7 +348,7 @@ var _ = Describe("Pusher", func() {
|
||||||
// Being the artifact of type asset we expect that the retrieved descriptor is of type manifest.
|
// Being the artifact of type asset we expect that the retrieved descriptor is of type manifest.
|
||||||
Expect(d.MediaType).To(Equal(v1.MediaTypeImageManifest))
|
Expect(d.MediaType).To(Equal(v1.MediaTypeImageManifest))
|
||||||
// Checking the digest is correct.
|
// Checking the digest is correct.
|
||||||
Expect(d.Digest.String()).To(Equal(result.Digest))
|
Expect(d.Digest.String()).To(Equal(result.RootDigest))
|
||||||
// It must have only one layer since no config layer is configured.
|
// It must have only one layer since no config layer is configured.
|
||||||
Expect(manifest.Layers).To(HaveLen(1))
|
Expect(manifest.Layers).To(HaveLen(1))
|
||||||
// The layer has to be of type asset.
|
// The layer has to be of type asset.
|
||||||
|
|
|
@ -64,7 +64,7 @@ func WithPlainHTTP(plainHTTP bool) func(r *Repository) {
|
||||||
func (r *Repository) Tags(ctx context.Context) ([]string, error) {
|
func (r *Repository) Tags(ctx context.Context) ([]string, error) {
|
||||||
var result []string
|
var result []string
|
||||||
var tagRetriever = func(tags []string) error {
|
var tagRetriever = func(tags []string) error {
|
||||||
result = tags
|
result = append(result, tags...)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,8 +88,8 @@ func HumanReadableMediaType(s string) string {
|
||||||
return string(Asset)
|
return string(Asset)
|
||||||
}
|
}
|
||||||
|
|
||||||
// should never happen
|
// If we do not have a match for a well known mediaType then we return the original mediaType.
|
||||||
return ""
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// ArtifactTypeSlice is a slice of ArtifactType, can be passed as comma separated values.
|
// ArtifactTypeSlice is a slice of ArtifactType, can be passed as comma separated values.
|
||||||
|
|
|
@ -19,9 +19,9 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
credentials "github.com/oras-project/oras-credentials-go"
|
|
||||||
"oras.land/oras-go/v2/registry/remote"
|
"oras.land/oras-go/v2/registry/remote"
|
||||||
"oras.land/oras-go/v2/registry/remote/auth"
|
"oras.land/oras-go/v2/registry/remote/auth"
|
||||||
|
"oras.land/oras-go/v2/registry/remote/credentials"
|
||||||
|
|
||||||
"github.com/falcosecurity/falcoctl/internal/config"
|
"github.com/falcosecurity/falcoctl/internal/config"
|
||||||
"github.com/falcosecurity/falcoctl/pkg/oci/authn"
|
"github.com/falcosecurity/falcoctl/pkg/oci/authn"
|
||||||
|
|
|
@ -35,6 +35,7 @@ type Artifact struct {
|
||||||
Dependencies []string
|
Dependencies []string
|
||||||
Requirements []string
|
Requirements []string
|
||||||
Tags []string
|
Tags []string
|
||||||
|
AutoFloatingTags bool
|
||||||
AnnotationSource string
|
AnnotationSource string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +65,9 @@ func (art *Artifact) AddFlags(cmd *cobra.Command) error {
|
||||||
cmd.Flags().StringArrayVarP(&art.Tags, "tag", "t", nil,
|
cmd.Flags().StringArrayVarP(&art.Tags, "tag", "t", nil,
|
||||||
"additional artifact tag. Can be repeated multiple times")
|
"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",
|
cmd.Flags().Var(&art.ArtifactType, "type",
|
||||||
`type of artifact to be pushed. Allowed values: "rulesfile", "plugin", "asset"`)
|
`type of artifact to be pushed. Allowed values: "rulesfile", "plugin", "asset"`)
|
||||||
if err := cmd.MarkFlagRequired("type"); err != nil {
|
if err := cmd.MarkFlagRequired("type"); err != nil {
|
||||||
|
|
|
@ -46,15 +46,15 @@ type Common struct {
|
||||||
// IndexCache caches the entries for the configured indexes.
|
// IndexCache caches the entries for the configured indexes.
|
||||||
IndexCache *cache.Cache
|
IndexCache *cache.Cache
|
||||||
|
|
||||||
logLevel *LogLevel
|
logLevel *output.LogLevel
|
||||||
logFormat *LogFormat
|
logFormat *output.LogFormat
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions returns a new Common struct.
|
// NewOptions returns a new Common struct.
|
||||||
func NewOptions() *Common {
|
func NewOptions() *Common {
|
||||||
return &Common{
|
return &Common{
|
||||||
logLevel: NewLogLevel(),
|
logLevel: output.NewLogLevel(),
|
||||||
logFormat: NewLogFormat(),
|
logFormat: output.NewLogFormat(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (C) 2023 The Falco Authors
|
// Copyright (C) 2024 The Falco Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -22,13 +22,17 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/falcosecurity/driverkit/pkg/kernelrelease"
|
||||||
|
|
||||||
"github.com/falcosecurity/falcoctl/internal/config"
|
"github.com/falcosecurity/falcoctl/internal/config"
|
||||||
|
driverdistro "github.com/falcosecurity/falcoctl/pkg/driver/distro"
|
||||||
drivertype "github.com/falcosecurity/falcoctl/pkg/driver/type"
|
drivertype "github.com/falcosecurity/falcoctl/pkg/driver/type"
|
||||||
|
"github.com/falcosecurity/falcoctl/pkg/enum"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DriverTypes data structure for driver types.
|
// DriverTypes data structure for driver types.
|
||||||
type DriverTypes struct {
|
type DriverTypes struct {
|
||||||
*Enum
|
*enum.Enum
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDriverTypes returns a new Enum configured for the driver types.
|
// NewDriverTypes returns a new Enum configured for the driver types.
|
||||||
|
@ -36,7 +40,9 @@ func NewDriverTypes() *DriverTypes {
|
||||||
types := drivertype.GetTypes()
|
types := drivertype.GetTypes()
|
||||||
sort.Strings(types)
|
sort.Strings(types)
|
||||||
return &DriverTypes{
|
return &DriverTypes{
|
||||||
Enum: NewEnum(types, drivertype.TypeKmod),
|
// Default value is not used.
|
||||||
|
// This enum is only used to print allowed values.
|
||||||
|
Enum: enum.NewEnum(types, drivertype.TypeModernBpf),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,12 +53,14 @@ type Driver struct {
|
||||||
Repos []string
|
Repos []string
|
||||||
Version string
|
Version string
|
||||||
HostRoot string
|
HostRoot string
|
||||||
|
Distro driverdistro.Distro
|
||||||
|
Kr kernelrelease.KernelRelease
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToDriverConfig maps a Driver options to Driver config struct.
|
// ToDriverConfig maps a Driver options to Driver config struct.
|
||||||
func (d *Driver) ToDriverConfig() *config.Driver {
|
func (d *Driver) ToDriverConfig() *config.Driver {
|
||||||
return &config.Driver{
|
return &config.Driver{
|
||||||
Type: d.Type.String(),
|
Type: []string{d.Type.String()},
|
||||||
Name: d.Name,
|
Name: d.Name,
|
||||||
Repos: d.Repos,
|
Repos: d.Repos,
|
||||||
Version: d.Version,
|
Version: d.Version,
|
||||||
|
|
|
@ -13,10 +13,12 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package options
|
package output
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pterm/pterm"
|
"github.com/pterm/pterm"
|
||||||
|
|
||||||
|
"github.com/falcosecurity/falcoctl/pkg/enum"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -32,13 +34,13 @@ var logFormats = []string{LogFormatColor, LogFormatText, LogFormatJSON}
|
||||||
|
|
||||||
// LogFormat data structure for log-format flag.
|
// LogFormat data structure for log-format flag.
|
||||||
type LogFormat struct {
|
type LogFormat struct {
|
||||||
*Enum
|
*enum.Enum
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLogFormat returns a new Enum configured for the log formats flag.
|
// NewLogFormat returns a new Enum configured for the log formats flag.
|
||||||
func NewLogFormat() *LogFormat {
|
func NewLogFormat() *LogFormat {
|
||||||
return &LogFormat{
|
return &LogFormat{
|
||||||
Enum: NewEnum(logFormats, LogFormatColor),
|
Enum: enum.NewEnum(logFormats, LogFormatColor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,8 +48,9 @@ func NewLogFormat() *LogFormat {
|
||||||
func (lg *LogFormat) ToPtermFormatter() pterm.LogFormatter {
|
func (lg *LogFormat) ToPtermFormatter() pterm.LogFormatter {
|
||||||
var formatter pterm.LogFormatter
|
var formatter pterm.LogFormatter
|
||||||
|
|
||||||
switch lg.value {
|
switch lg.Value {
|
||||||
case LogFormatColor:
|
case LogFormatColor:
|
||||||
|
pterm.EnableColor()
|
||||||
formatter = pterm.LogFormatterColorful
|
formatter = pterm.LogFormatterColorful
|
||||||
case LogFormatText:
|
case LogFormatText:
|
||||||
pterm.DisableColor()
|
pterm.DisableColor()
|
|
@ -13,7 +13,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package options
|
package output
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gookit/color"
|
"github.com/gookit/color"
|
||||||
|
@ -33,8 +33,8 @@ var _ = Describe("LogFormat", func() {
|
||||||
Context("NewLogFormat Func", func() {
|
Context("NewLogFormat Func", func() {
|
||||||
It("should return a new logFormatter", func() {
|
It("should return a new logFormatter", func() {
|
||||||
Expect(logFormatter).ShouldNot(BeNil())
|
Expect(logFormatter).ShouldNot(BeNil())
|
||||||
Expect(logFormatter.value).Should(Equal(LogFormatColor))
|
Expect(logFormatter.Value).Should(Equal(LogFormatColor))
|
||||||
Expect(logFormatter.allowed).Should(Equal(logFormats))
|
Expect(logFormatter.Allowed()).Should(Equal("(color, text, json)"))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -13,10 +13,12 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package options
|
package output
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pterm/pterm"
|
"github.com/pterm/pterm"
|
||||||
|
|
||||||
|
"github.com/falcosecurity/falcoctl/pkg/enum"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -34,20 +36,20 @@ var logLevels = []string{LogLevelInfo, LogLevelWarn, LogLevelDebug, LogLevelTrac
|
||||||
|
|
||||||
// LogLevel data structure for log-level flag.
|
// LogLevel data structure for log-level flag.
|
||||||
type LogLevel struct {
|
type LogLevel struct {
|
||||||
*Enum
|
*enum.Enum
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLogLevel returns a new Enum configured for the log level flag.
|
// NewLogLevel returns a new Enum configured for the log level flag.
|
||||||
func NewLogLevel() *LogLevel {
|
func NewLogLevel() *LogLevel {
|
||||||
return &LogLevel{
|
return &LogLevel{
|
||||||
Enum: NewEnum(logLevels, LogLevelInfo),
|
Enum: enum.NewEnum(logLevels, LogLevelInfo),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToPtermLogLevel converts the current log level to pterm.LogLevel.
|
// ToPtermLogLevel converts the current log level to pterm.LogLevel.
|
||||||
func (ll *LogLevel) ToPtermLogLevel() pterm.LogLevel {
|
func (ll *LogLevel) ToPtermLogLevel() pterm.LogLevel {
|
||||||
var level pterm.LogLevel
|
var level pterm.LogLevel
|
||||||
switch ll.value {
|
switch ll.Value {
|
||||||
case LogLevelInfo:
|
case LogLevelInfo:
|
||||||
level = pterm.LogLevelInfo
|
level = pterm.LogLevelInfo
|
||||||
case LogLevelWarn:
|
case LogLevelWarn:
|
|
@ -13,7 +13,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package options
|
package output
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/onsi/ginkgo/v2"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
@ -32,8 +32,8 @@ var _ = Describe("LogLevel", func() {
|
||||||
Context("NewLogLevel Func", func() {
|
Context("NewLogLevel Func", func() {
|
||||||
It("should return a new LogLevel", func() {
|
It("should return a new LogLevel", func() {
|
||||||
Expect(logLevel).ShouldNot(BeNil())
|
Expect(logLevel).ShouldNot(BeNil())
|
||||||
Expect(logLevel.value).Should(Equal(LogLevelInfo))
|
Expect(logLevel.Value).Should(Equal(LogLevelInfo))
|
||||||
Expect(logLevel.allowed).Should(Equal(logLevels))
|
Expect(logLevel.Allowed()).Should(Equal("(info, warn, debug, trace)"))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
# All rules files related to plugins should require at least engine version 10
|
# All rules files related to plugins should require at least engine version 10
|
||||||
- required_engine_version: 10
|
- required_engine_version: 0.10.0
|
||||||
|
|
||||||
- required_plugin_versions:
|
- required_plugin_versions:
|
||||||
- name: cloudtrail
|
- name: cloudtrail
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
- rule: All Cloudtrail Events
|
||||||
|
desc: Match all cloudtrail events.
|
||||||
|
condition:
|
||||||
|
evt.num > 0
|
||||||
|
output: Some Cloudtrail Event (evtnum=%evt.num info=%evt.plugininfo ts=%evt.time.iso8601 id=%ct.id error=%ct.error)
|
||||||
|
priority: DEBUG
|
||||||
|
tags:
|
||||||
|
- cloud
|
||||||
|
- aws
|
||||||
|
source: aws_cloudtrail
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
- rule: Console Login Through Assume Role
|
||||||
|
desc: Detect a console login through Assume Role.
|
||||||
|
condition:
|
||||||
|
ct.name="ConsoleLogin" and not ct.error exists
|
||||||
|
and ct.user.identitytype="AssumedRole"
|
||||||
|
and json.value[/responseElements/ConsoleLogin]="Success"
|
||||||
|
output:
|
||||||
|
Detected a console login through Assume Role
|
||||||
|
(principal=%ct.user.principalid,
|
||||||
|
assumedRole=%ct.user.arn,
|
||||||
|
requesting IP=%ct.srcip,
|
||||||
|
AWS region=%ct.region)
|
||||||
|
priority: WARNING
|
||||||
|
tags:
|
||||||
|
- cloud
|
||||||
|
- aws
|
||||||
|
- aws_console
|
||||||
|
- aws_iam
|
||||||
|
source: aws_cloudtrail
|
|
@ -36,3 +36,21 @@ func CreateEmptyFile(name string) (string, error) {
|
||||||
|
|
||||||
return configFile, nil
|
return configFile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteToTmpFile writes data to a temporary file in the specified path.
|
||||||
|
func WriteToTmpFile(data, dirPath string) (string, error) {
|
||||||
|
tmpFile, err := os.CreateTemp(dirPath, "rulesfiles-test")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if _, err = tmpFile.WriteString(data); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get path.
|
||||||
|
info, err := tmpFile.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.Join(dirPath, info.Name()), tmpFile.Close()
|
||||||
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ import (
|
||||||
"github.com/go-oauth2/oauth2/v4/models"
|
"github.com/go-oauth2/oauth2/v4/models"
|
||||||
"github.com/go-oauth2/oauth2/v4/server"
|
"github.com/go-oauth2/oauth2/v4/server"
|
||||||
"github.com/go-oauth2/oauth2/v4/store"
|
"github.com/go-oauth2/oauth2/v4/store"
|
||||||
"github.com/golang-jwt/jwt"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RegistryTLSConfig maintains all certificate informations.
|
// RegistryTLSConfig maintains all certificate informations.
|
||||||
|
|
Loading…
Reference in New Issue