Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added kyverno-json-policies for dockerfile-best-practices #113

Merged
merged 3 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions dockerfile-best-practices/detect-multiple-instructions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Detect if there are any multiple instructions in a single line

This policy is implemented to ensure that container images are built with minimal cached layers. It specifically focuses on detecting and preventing the use of multiple instructions in a single line within Dockerfiles. An example of such a scenario is using '&&' to combine two commands.

## Policy Details:

- **Policy Name:** detect-multiple-instructions
- **Check Description:** This policy ensures that Dockerfile Container Image Should Be Built with Minimal Cached Layers
- **Policy Category:** Dockerfile Best Practices

## How It Works:

### Validation Criteria:

**Key** : CmdLine[0]
**Valid Values** : executables of the command
**Type** : string

- **Condition:** `CmdLine[0]` contains ` && `
- **Result:** FAIL
- **Reason:** Multiple instructions in a single line detected.


- **Condition:** `CmdLine[0]` doesn't contains ` && `
- **Result:** PASS
- **Reason:** Single instruction per line, complying with best practices.

### Policy Validation Testing Instructions

To evaluate and test the policy ensuring dockerfile meets poliy requirements:

For testing this policy you will need to:
- Make sure you have `kyverno-json` installed on the machine
- Make sure you have [nctl `v3.4.0`](https://downloads.nirmata.io/nctl/downloads/) or above.


1. **Extract JSON equivalent of the dockerfile:**
```bash
nctl scan dockerfile -r test/good-test/Dockerfile --show-json > payload.json
```

2. **Test the Policy with Kyverno:**
```bash
kyverno-json scan --payload payload.json --policy detect-multiple-instructions.yaml
```

a. **Test Policy Against Valid Payload:**
```bash
kyverno-json scan --policy detect-multiple-instructions.yaml --payload test/good-test/good-payload.json
```

This produces the output:
```
Loading policies ...
Loading payload ...
Pre processing ...
Running ( evaluating 1 resource against 1 policy ) ...
- detect-multiple-instructions / detect-multiple-instructions / PASSED
Done
```

b. **Test Against Invalid Payload:**
```bash
kyverno-json scan --policy detect-multiple-instructions.yaml --payload test/bad-test/bad-payload.json
```

This produces the output:
```
Loading policies ...
Loading payload ...
Pre processing ...
Running ( evaluating 1 resource against 1 policy ) ...
- detect-multiple-instructions / detect-multiple-instructions / FAILED: Found multiple instructions in a single line: any[0].check.~.(Stages[].Commands[].CmdLine[0].contains(@, ' && '))[0]: Invalid value: true: Expected value: false
Done
```

---

This way you can ensure that your dockerfile adhere to policy.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
apiVersion: json.kyverno.io/v1alpha1
kind: ValidatingPolicy
metadata:
name: detect-multiple-instructions
annotations:
policies.kyverno.io/title: Detect Multiple Instructions in Single Line
policies.kyverno.io/category: Dockerfile Best Practices
policies.kyverno.io/severity: medium
policies.kyverno.io/description: >-
This policy ensures that Dockerfile Container Image Should Be Built with Minimal Cached Layers
spec:
rules:
- name: detect-multiple-instructions
match:
any:
- (Stages[].Commands[?Name=='RUN'].CmdLine[][] | length(@) > `0`): true
assert:
all:
- message: Found multiple instructions in a single line
check:
~.(Stages[].Commands[?Name=='RUN'].CmdLine[][]):
(contains(@, ' && ')): false
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM ubuntu:latest

# Update the package repository
RUN apt-get update && apt-get install -y nano

WORKDIR /app

COPY . /app

EXPOSE 8080

# Example: Run a command when the container starts
CMD ["echo", "Container is running!"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"MetaArgs": null,
"Stages": [
{
"Name": "",
"BaseName": "ubuntu:latest",
"Platform": "",
"Comment": "",
"SourceCode": "FROM ubuntu:latest",
"Location": [
{
"Start": {
"Line": 1,
"Character": 0
},
"End": {
"Line": 1,
"Character": 0
}
}
],
"From": {
"Image": "ubuntu:latest"
},
"Commands": [
{
"CmdLine": [
"apt-get update && apt-get install -y nano"
],
"Files": null,
"FlagsUsed": [],
"Name": "RUN",
"PrependShell": true
},
{
"Name": "WORKDIR",
"Path": "/app"
},
{
"Chmod": "",
"Chown": "",
"DestPath": "/app",
"From": "",
"Link": false,
"Name": "COPY",
"SourceContents": null,
"SourcePaths": [
"."
]
},
{
"Name": "EXPOSE",
"Ports": [
"8080"
]
},
{
"CmdLine": [
"echo",
"Container is running!"
],
"Files": null,
"Name": "CMD",
"PrependShell": false
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM ubuntu:latest

# Update the package repository
RUN apt-get update

WORKDIR /app

COPY . /app

EXPOSE 8080

# Example: Run a command when the container starts
CMD ["echo", "&& is not present"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"MetaArgs": null,
"Stages": [
{
"Name": "",
"BaseName": "ubuntu:latest",
"Platform": "",
"Comment": "",
"SourceCode": "FROM ubuntu:latest",
"Location": [
{
"Start": {
"Line": 1,
"Character": 0
},
"End": {
"Line": 1,
"Character": 0
}
}
],
"From": {
"Image": "ubuntu:latest"
},
"Commands": [
{
"CmdLine": [
"apt-get update"
],
"Files": null,
"FlagsUsed": [

],
"Name": "RUN",
"PrependShell": true
},
{
"Name": "WORKDIR",
"Path": "/app"
},
{
"Chmod": "",
"Chown": "",
"DestPath": "/app",
"From": "",
"Link": false,
"Name": "COPY",
"SourceContents": null,
"SourcePaths": [
"."
]
},
{
"Name": "EXPOSE",
"Ports": [
"8080"
]
},
{
"CmdLine": [
"echo",
"&& is not present"
],
"Files": null,
"Name": "CMD",
"PrependShell": false
}
]
}
]
}
76 changes: 76 additions & 0 deletions dockerfile-best-practices/prefer-copy-over-add/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Prefer COPY over ADD Instructions in Dockerfile

This policy ensures that container images are built using commands that result in known outcomes. Specifically, it advocates for the preference of using the `COPY` instruction over `ADD` in Dockerfiles. By adhering to this policy, you enhance the predictability and transparency of the image-building process.

## Policy Details:

- **Policy Name:** prefer-copy-over-add
- **Check Description:** This policy checks whether Dockerfiles use the ADD instructions for copying files into the container image.
- **Policy Category:** Dockerfile Best Practices

## How It Works:

### Validation Criteria:

**Key** : ADD

- **Condition:** If `ADD` exists
- **Result:** FAIL
- **Reason:** Using `ADD` can introduce unnecessary complexity and ambiguity, potentially leading to unintended consequences. The policy recommends preferring COPY for explicit file copying.

- **Condition:** If `ADD` doesn't exist
- **Result:** PASS
- **Reason:** This indicates compliance with the policy, as the absence of ADD aligns with the best practice of using COPY for file operations.

### Policy Validation Testing Instructions

To evaluate and test the policy ensuring dockerfile meets poliy requirements:

For testing this policy you will need to:
- Make sure you have `kyverno-json` installed on the machine
- Make sure you have [nctl `v3.4.0`](https://downloads.nirmata.io/nctl/downloads/) or above.


1. **Extract JSON equivalent of the dockerfile:**
```bash
nctl scan dockerfile -r test/good-test/Dockerfile --show-json > payload.json
```

2. **Test the Policy with Kyverno:**
```bash
kyverno-json scan --payload payload.json --policy prefer-copy-over-add.yaml
```

a. **Test Policy Against Valid Payload:**
```bash
kyverno-json scan --policy prefer-copy-over-add.yaml --payload test/good-test/good-payload.json
```

This produces the output:
```
Loading policies ...
Loading payload ...
Pre processing ...
Running ( evaluating 1 resource against 1 policy ) ...
- prefer-copy-over-add / prefer-copy-over-add / PASSED
Done
```

b. **Test Against Invalid Payload:**
```bash
kyverno-json scan --policy prefer-copy-over-add.yaml --payload test/bad-test/bad-payload.json
```

This produces the output:
```
Loading policies ...
Loading payload ...
Pre processing ...
Running ( evaluating 1 resource against 1 policy ) ...
- prefer-copy-over-add / prefer-copy-over-add / FAILED: Avoid the use of ADD instructions in Dockerfiles: any[0].check.(Stages[].Commands[?Name=='ADD'].Link[] | length(@) > `0`): Invalid value: true: Expected value: false
Done
```

---

By following this policy, you can ensure that your Dockerfile adopts a more straightforward and transparent approach to file copying, promoting better image building practices.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: json.kyverno.io/v1alpha1
kind: ValidatingPolicy
metadata:
name: prefer-copy-over-add
annotations:
policies.kyverno.io/title: Prefer COPY over ADD in Dockerfile
policies.kyverno.io/category: Dockerfile Best Practices
policies.kyverno.io/severity: medium
policies.kyverno.io/description: >-
This policy ensures that COPY instructions are used instead of ADD instructions in Dockerfiles.
spec:
rules:
- name: prefer-copy-over-add
match:
any:
- (Stages[].Commands[] | length(@) > `0`): true
assert:
any:
- message: Avoid the use of ADD instructions in Dockerfiles
check:
(Stages[].Commands[?Name=='ADD'].Link[] | length(@) > `0`): false
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM ubuntu:latest

WORKDIR /app

ADD . /app/

RUN apt-get update && apt-get install -y nano

COPY . /app

EXPOSE 8080

# Example: Run a command when the container starts
CMD ["echo", "ADD is present in the Dockerfile"]
Loading
Loading