Skip to content

Commit 2ad75db

Browse files
Improve Documentation (#60)
2 parents ba5b431 + ea5910d commit 2ad75db

File tree

11 files changed

+252
-114
lines changed

11 files changed

+252
-114
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ paranoia
33
.paranoia.yaml
44

55
/.idea
6+
man/

README.md

Lines changed: 60 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -2,142 +2,100 @@
22

33
_Who do you trust?_
44

5-
Paranoia is a tool to analyse and export trust bundles (e.g., "ca-certificates") from container images. These certificates identify the certificate authorites that your container trusts when establishing TLS connections. The design of TLS is that any certificate authority that your container trusts can issue a certificate for any domain. This means that a malicious or compromised certificate authority could issue a certificate to impersonate any other service, including your internal infrastructure.
5+
Paranoia is a tool to analyse and export trust bundles (e.g., "ca-certificates") from container images.
6+
These certificates identify the certificate authorities that your container trusts when establishing TLS connections.
7+
The design of TLS is that any certificate authority that your container trusts can issue a certificate for any domain.
8+
This means that a malicious or compromised certificate authority could issue a certificate to impersonate any other service, including your internal infrastructure.
69

7-
Paranoia can be used to inspect and validate the certificates within your container images. This gives you visibility into which certificate authorities your container images are trusting; allows you to forbid or require certificates at build-time in CI; and help you decide _who to trust_ in your container images.
10+
Paranoia can be used to inspect and validate the certificates within your container images.
11+
This gives you visibility into which certificate authorities your container images are trusting; allows you to forbid or require certificates at build-time in CI; and help you decide _who to trust_ in your container images.
812

913
Paranoia is built by [Jetstack](https://jetstack.io) and made available under the Apache 2.0 license, see [LICENSE.txt](LICENSE.txt).
1014

11-
## Limitations
12-
13-
Paranoia will detect certificate authorities in most cases, and is especially useful at finding accidental inclusion or for conducting a certificate authority inventory. However there are some limitations to bear in mind while using Paranoia:
14-
- Paranoia only functions on container images, not running containers. Anything added into the container at runtime is not seen.
15-
- If a certificate is found, that doesn’t guarantee that the container will trust it as a certificate authority. It could, for example, be an unused leftover file.
16-
- It’s possible for an attacker to ‘hide’ a certificate authority from Paranoia (e.g., by encoding it in a format Paranoia doesn’t understand). In general Paranoia isn’t designed to defend against an adversary with supply chain write access intentionally sneaking obfuscated certificate authorities into container images.
17-
18-
## Command Line Usage
19-
20-
### Inspect
21-
22-
Checks found certificates and reports on whether they're valid, expired, due to expire in the next 6 months or are on
23-
[Mozilla's Removed CA Certificate Report](https://ccadb-public.secure.force.com/mozilla/RemovedCACertificateReportCSVFormat).
24-
25-
```shell
26-
$ paranoia inspect alpine:latest
27-
Certificate CN=Hellenic Academic and Research Institutions RootCA 2011,O=Hellenic Academic and Research Institutions Cert. Authority,C=GR
28-
┗ 🚨 removed from Mozilla trust store, no reason given
29-
Certificate CN=Staat der Nederlanden EV Root CA,O=Staat der Nederlanden,C=NL
30-
┗ ⚠️️ expires soon ( expires on 2022-12-08T11:10:28Z, 20 weeks 6 days until expiry)
31-
Found 132 certificates total, of which 2 had issues
32-
```
33-
34-
### Validate
15+
## Installation
3516

36-
Compares found certificates against a given configuration file (`.paranoia.yaml` by default) and reports on any
37-
conflicts.
17+
### Binaries
3818

39-
**Flags:**
19+
Binaries for common platforms and architectures are provided on the [releases](https://github.com/jetstack/paranoia/releases/latest).
4020

41-
`--permissive`: Enables permissive mode, allowing all certificates unless explicitly forbidden. When not
42-
enabled `validate` defaults to `strict` mode where all certificates are forbidden unless explicitly allowed.
21+
### Go Install
4322

44-
`--quiet`: Forces the process to end with exit code 0 regardless of whether conflicts are found. Output remains the same.
45-
46-
`-c --config`: Takes a file path and allows the use of a specified config file rather than the default, `.paranoia.yaml`.
47-
48-
Example config:
49-
```yaml
50-
version: "1"
51-
allow:
52-
- fingerprints:
53-
sha256: 30FBBA2C32238E2A98547AF97931E550428B9B3F1C8EEB6633DCFA86C5B27DD3
54-
comment: "A certificate we're okay with but don't explicitly need"
55-
forbid:
56-
- fingerprints:
57-
sha256: 4348A0E9444C78CB265E058D5E8944B4D84F9662BD26DB257F8934A443C70161
58-
comment: "A certificate we definitely don't want"
59-
require:
60-
- fingerprints:
61-
sha1: a7c36ea226e1adc60c4aa7866b79ed9e7831103c
62-
comment: "A certificate that must be present"
63-
```
23+
If you have [Go](https://go.dev/) installed you can install Paranoia using Go directly.
6424

6525
```shell
66-
$ paranoia validate some-image:latest --config some_config.yaml
67-
Validating certificates with 1 allowed, 1 forbidden, and 1 required certificates, in strict mode
68-
Scanned 3 certificates in image some-image:latest, found issues.
69-
Certificate with SHA256 fingerprint 4348A0E9444C78CB265E058D5E8944B4D84F9662BD26DB257F8934A443C70161 in location etc/ssl/certs/ca-certificates.crt was forbidden Comment: A certificate we definitely don't want
70-
Certificate with SHA256 fingerprint 30FBBA2C32238E2A98547AF97931E550428B9B3F1C8EEB6633DCFA86C5B27DD3 in location etc/ssl/certs/ca-certificates.crt was not allowed
71-
Certificate with SHA1 a7c36ea226e1adc60c4aa7866b79ed9e7831103c was required, but was not found Comment: A certificate that must be present
72-
exit status 1
26+
go install github.com/jetstack/paranoia@latest
7327
```
74-
**Note:** Comments on allowed certificate fingerprints will never be displayed in the output as we don't report on
75-
allowances. However, they can be very helpful for anyone who needs to maintain the file.
7628

77-
### Export
29+
## Examples
7830

79-
Outputs data on all found certificates, including the file location, owner, valid from and valid to dates and the SHA256
80-
fingerprint (useful for populating a config file for use with the `validate` command).
31+
Paranoia can be used to list out the certificates in a container image:
8132

8233
```shell
8334
$ paranoia export alpine:latest
8435
File Location Subject
8536
/etc/ssl/certs/ca-certificates.crt CN=ACCVRAIZ1,OU=PKIACCV,O=ACCV,C=ES
8637
/etc/ssl/certs/ca-certificates.crt OU=AC RAIZ FNMT-RCM,O=FNMT-RCM,C=ES
8738
/etc/ssl/certs/ca-certificates.crt CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS,OU=Ceres,O=FNMT-RCM,C=ES,2.5.4.97=#130f56415445532d51323832363030344a
88-
/etc/ssl/certs/ca-certificates.crt SERIALNUMBER=G63287510,CN=ANF Secure Server Root CA,OU=ANF CA Raiz,O=ANF Autoridad de Certificacion,C=ES
89-
/etc/ssl/certs/ca-certificates.crt CN=Actalis Authentication Root CA,O=Actalis S.p.A./03358520967,L=Milan,C=IT
90-
/etc/ssl/certs/ca-certificates.crt CN=AffirmTrust Commercial,O=AffirmTrust,C=US
91-
/etc/ssl/certs/ca-certificates.crt CN=AffirmTrust Networking,O=AffirmTrust,C=US
92-
/etc/ssl/certs/ca-certificates.crt CN=AffirmTrust Premium,O=AffirmTrust,C=US
93-
/etc/ssl/certs/ca-certificates.crt CN=AffirmTrust Premium ECC,O=AffirmTrust,C=US
9439
9540
/etc/ssl/certs/ca-certificates.crt CN=vTrus ECC Root CA,O=iTrusChina Co.\,Ltd.,C=CN
9641
/etc/ssl/certs/ca-certificates.crt CN=vTrus Root CA,O=iTrusChina Co.\,Ltd.,C=CN
9742
Found 140 certificates
9843
```
9944

100-
The `--output` (or `-o`) flag allows specifying the output mode for export.
45+
Export them for further audit:
10146

102-
- `--output pretty` is the default, and gives a table view of the data.
103-
- `--output wide` also uses a table layout, but includes more infomation.
104-
- `--output json` emits JSON.
105-
- `--output pem` emits all full certificates in PEM format.
47+
```shell
48+
paranoia export --output json python:3 | jq '.certificates[].fingerprintSHA256' | head -n 5
10649

107-
### Global flags
50+
"ebd41040e4bb3ec742c9e381d31ef2a41a48b6685c96e7cef3c1df6cd4331c99"
51+
"6dc47172e01cbcb0bf62580d895fe2b8ac9ad4f873801e0c10b9c837d21eb177"
52+
"16af57a9f676b0ab126095aa5ebadef22ab31119d644ac95cd4b93dbf3f26aeb"
53+
"73c176434f1bc6d5adf45b0e76e727287c8de57616c1e6e6141a2b2cbc7d8e4c"
54+
"d7a7a0fb5d7e2731d771e9484ebcdef71d5f0c3e0a2948782bc83ee0ea699ef4"
55+
```
10856

109-
`--platform`: Specifies the platform in the form `os/arch[/variant][:osversion]` (e.g. `linux/amd64`)
57+
Detect internal certificates left over from internal testing:
11058

111-
## CI Usage
59+
```shell
60+
cat << EOF > .paranoia.yaml
61+
version: "1"
62+
forbid:
63+
- comment: "An internal-only cert"
64+
fingerprints:
65+
sha256: bd40be0eccfce513ab318882f03962e4e2ec3799b51392e82805d9249e426d28
66+
EOF
67+
paranoia validate my-image
68+
```
11269

113-
The functionality of Paranoia is well suited to running in CI pipelines, either producing reports on a schedule or
114-
as a check before or after the release of a new container image.
70+
Find certificates inside binaries:
11571

116-
Below are some examples:
72+
```shell
73+
paranoia export -o json consul:latest | jq '.certificates[] | select(.fileLocation == "/bin/consul")'
74+
{
75+
"fileLocation": "/bin/consul",
76+
"owner": "CN=Circonus Certificate Authority,OU=Circonus,O=Circonus\\, Inc.,L=Columbia,ST=Maryland,C=US,1.2.840.113549.1.9.1=#0c0f636140636972636f6e75732e6e6574",
77+
"parser": "pem",
78+
"signature": "01C1B65D790706D2CAAD1D30406911D41884789A9D4FEBBCE31EE7B7628019A8C7B6643C46C1FDB684B18272B33880DAB68EB51C5546D731B9948C8A3D918890EC2F1CC8A751FAD1786BF2599FEEA17A63EB1997B577E8A65B9F67B368EA11B6C425F5D86A10C7BCCE02FBEA9F5867913AF409749A08A27D3B5EC8D8E332E216",
79+
"notBefore": "2009-12-23T19:17:06Z",
80+
"notAfter": "2019-12-21T19:17:06Z",
81+
"fingerprintSHA1": "063ff657e055b0036d794cda892c85417c07739a",
82+
"fingerprintSHA256": "0c97e0898343c5b1973c6568a15c8c853dd663d363020071e34f789859ece19f"
83+
}
84+
```
11785

118-
### GitHub Actions
86+
## Limitations
11987

120-
Paranoia is used on itself after container image build to confirm that it only contains the certificates that we expect.
121-
The full workflow can be found [here](.github/workflows/publish.yaml).
88+
Paranoia will detect certificate authorities in most cases, and is especially useful at finding accidental inclusion or for conducting a certificate authority inventory.
89+
However, there are some limitations to bear in mind while using Paranoia:
12290

123-
In it we use our [paranoia action](action.yml) to run the validation, using the `file://` prefix to read the container
124-
image from a local file, as opposed to pulling it from a container registry:
91+
- Paranoia only functions on container images, not running containers.
92+
Anything added into the container at runtime is not seen.
93+
- If a certificate is found, that doesn’t guarantee that the container will trust it as a certificate authority.
94+
It could, for example, be an unused leftover file.
95+
- It’s possible for an attacker to ‘hide’ a certificate authority from Paranoia (e.g., by encoding it in a format Paranoia doesn’t understand).
96+
In general Paranoia isn’t designed to defend against an adversary with supply chain write access intentionally sneaking obfuscated certificate authorities into container images.
12597

126-
```yaml
127-
...
128-
- name: Build and export to Docker
129-
uses: docker/build-push-action@v3
130-
with:
131-
context: .
132-
load: true
133-
cache-from: type=gha
134-
cache-to: type=gha,mode=max
135-
outputs: type=docker,dest=${{ env.CONTAINER_TAR }}
98+
## Usage
13699

137-
- name: "Run Paranoia container"
138-
uses: ./
139-
with:
140-
action: validate
141-
target_tar: file://${{ env.CONTAINER_TAR }}
142-
...
143-
```
100+
The usage documentation for Paranoia is included in the help text.
101+
Invoke a command with `--help` for usage instructions, or see the manual pages.

cmd/export.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,22 @@ func newExport(ctx context.Context) *cobra.Command {
2929

3030
cmd := &cobra.Command{
3131
Use: "export [flags] image",
32-
Short: "Export all certificate data for later use",
32+
Short: "Export all certificate authorities in the given container image",
33+
Long: `
34+
Exports all certificates found in the container image.
35+
The detail available depends on the output mode used
36+
37+
In most output modes, partial certificates are also included after the main output.
38+
`,
39+
Example: `
40+
Export certificates for an image:
41+
42+
$ paranoia export alpine:latest
43+
44+
Pipe certificate information into jq:
45+
46+
$ paranoia export --output json alpine:latest | jq '.certificates[].fingerprintSHA256'
47+
`,
3348
PreRunE: func(_ *cobra.Command, args []string) error {
3449
if err := options.MustSingleImageArgs(args); err != nil {
3550
return err

cmd/inspect.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,16 @@ func newInspect(ctx context.Context) *cobra.Command {
2020

2121
cmd := &cobra.Command{
2222
Use: "inspect [flags] image",
23-
Short: "Inspect a container image for root certificates",
23+
Short: "Summarise potential issues with certificates",
24+
Long: `
25+
Inspect prints out certificates that have one or more of the following faults:
26+
27+
- Expired (based on current system time).
28+
- Close to expiry (based on current system time).
29+
- Removed by Mozilla from their certificate authority bundle.
30+
31+
Partial certificates are also all printed for further inspection.
32+
`,
2433
PreRunE: func(_ *cobra.Command, args []string) error {
2534
return options.MustSingleImageArgs(args)
2635
},

cmd/options/outputs.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,26 @@ type Output struct {
3131

3232
func RegisterOutputs(cmd *cobra.Command) *Output {
3333
var opts Output
34-
cmd.Flags().StringVarP(&opts.Mode, "output", "o", "pretty", "Output mode. Supported modes: "+strings.Join(outputModes, ", "))
34+
cmd.Flags().StringVarP(&opts.Mode, "output", "o", "pretty", `
35+
The output mode controls how Paranoia displays the data, and what data is shown.
36+
Supported modes are *pretty*, *wide*, *json*, and *pem*.
37+
38+
*pretty*: Both certificates and partial certificates are output using a table to the terminal.
39+
This includes the file location (in the container) and the subject line of the certificate.
40+
41+
*wide*: Like pretty mode, this uses a table to format data.
42+
Wide includes additional columns including the SHA256 and other information.
43+
44+
*json*: The JSON output mode emits only JSON to STDOUT.
45+
Therefore, it is suitable for piping either to file or into programs that consume JSON text.
46+
The output format will include a "certificates" key containing an array of certificate objects.
47+
Each certificate object will have keys for "fileLocation", "owner", "parser", "signature", "notBefore", "notAfter", "fingerprintSHA1", and "fingerprintSHA256".
48+
Optionally, the output will include a "partials" key containing an array of partial certificate objects.
49+
Partial certificate objects will have keys for "fileLocation", "reason", and "parser".
50+
51+
*pem*: Emits every certificate found in PEM format.
52+
In this output mode, partial certificates are omitted.
53+
`)
3554
return &opts
3655
}
3756

cmd/options/validation.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ type Validation struct {
1717

1818
func RegisterValidation(cmd *cobra.Command) *Validation {
1919
var opts Validation
20-
cmd.PersistentFlags().StringVarP(&opts.Config, "config", "c", ".paranoia.yaml", "Configuration file for Paranoia")
20+
cmd.PersistentFlags().StringVarP(&opts.Config, "config", "c", ".paranoia.yaml", "Path to configuration file for Paranoia's validate mode.")
2121
cmd.PersistentFlags().BoolVar(&opts.Quiet, "quiet", false, "Suppress nonzero exit code on validation failures.")
2222
cmd.PersistentFlags().BoolVar(&opts.Permissive, "permissive", false, "Allow any certificate that is not otherwise forbidden. This overrides the config's allow list.")
2323
return &opts

cmd/root.go

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,52 @@ import (
1111
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
1212
)
1313

14-
func newRoot(ctx context.Context) *cobra.Command {
14+
func NewRoot(ctx context.Context) *cobra.Command {
1515
root := &cobra.Command{
16-
Use: "paranoia",
17-
Short: "Inspect container image trust bundles",
18-
Long: `Paranoia is a CLI tool to inspect the trust bundles present in a container image.`,
16+
Use: "paranoia subcommand",
17+
Short: "Inspect certificate authorities in container images ",
18+
Long: `
19+
Paranoia is a command-line tool to inspect the certificate authorities present in a container image.
20+
It is capable of scanning not only well-known locations (such as PEM-encoded files under /etc/ssl/certs/), but finding
21+
certificates embedded in text files and even inside of binaries.
22+
23+
## LIMITATIONS
24+
25+
Paranoia will detect certificate authorities in most cases, and is especially useful at finding accidental inclusion or for conducting a certificate authority inventory. However there are some limitations to bear in mind while using Paranoia:
26+
27+
- Paranoia only functions on container images, not running containers.
28+
Anything added into the container at runtime is not seen.
29+
- If a certificate is found, that doesn’t guarantee that the container will trust it as a certificate authority.
30+
It could, for example, be an unused leftover file.
31+
- It’s possible for an attacker to ‘hide’ a certificate authority from Paranoia (e.g., by encoding it in a format Paranoia doesn’t understand).
32+
In general Paranoia isn’t designed to defend against an adversary with supply chain write access intentionally sneaking obfuscated certificate authorities into container images.
33+
34+
## CERTIFICATE DETECTION
35+
36+
Paranoia runs a number of parsers over the data contained within a container image.
37+
This includes searching through files for strings, including binary files.
38+
39+
Container images are comprised of layers.
40+
Each layer may remove or replace files from previous layers.
41+
Paranoia only considers the final state of the image, available to the application at runtime.
42+
Certificates in intermediate layers which are removed or replaced in later layers are not detected by Paranoia.
43+
44+
### Partial Certificates
45+
46+
Paranoia can also detect "partial" certificates.
47+
A partial certificate is where Paranoia has detected data that appears to be a certificate but is incomplete or invalid.
48+
These can be false-positives, but are often worthy of further investigation.
49+
50+
## LOCAL IMAGES
51+
52+
Paranoia can be invoked on any container image by name.
53+
This can include a tag, or a SHA256 fingerprint.
54+
Paranoia can also read from STDIN to handle local images that are exported as tar files.
55+
56+
To enable this behaviour, use "-" as the image name.
57+
58+
$ docker save my-local-image:sometag | paranoia export -
59+
`,
1960
}
2061

2162
root.AddCommand(newExport(ctx))
@@ -27,7 +68,7 @@ func newRoot(ctx context.Context) *cobra.Command {
2768

2869
func Execute() {
2970
ctx := signals.SetupSignalHandler()
30-
if err := newRoot(ctx).Execute(); err != nil {
71+
if err := NewRoot(ctx).Execute(); err != nil {
3172
fmt.Fprintf(os.Stderr, "error: %v\n", err)
3273
os.Exit(1)
3374
}

0 commit comments

Comments
 (0)