Skip to content

Commit 58bfad7

Browse files
authored
feat: [CI-18308]: Add Cosign Image Signing Support (#494)
* Add signing support via cosign * Updated docker.go * Add signing support via cosign * Updated docker.go * Updated docker.go * Updated docker.go * Updated docker.go * Updated docker.go * Updated dockerfiles
1 parent 0493478 commit 58bfad7

File tree

9 files changed

+364
-11
lines changed

9 files changed

+364
-11
lines changed

COSIGN_USAGE.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# Cosign Integration for Drone-Docker
2+
3+
This document describes how to use the cosign container image signing feature in drone-docker.
4+
5+
## Overview
6+
7+
The drone-docker plugin now supports automatic container image signing using cosign after each successful push. This provides cryptographic verification that images haven't been tampered with.
8+
9+
## Environment Variables
10+
11+
The plugin accepts three cosign-related environment variables:
12+
13+
### `PLUGIN_COSIGN_PRIVATE_KEY` (Required for signing)
14+
- **Description**: Private key for signing (PEM format content or file path)
15+
- **Format**: Either PEM content or file path to private key
16+
- **Usage**: Should be provided via secrets
17+
18+
### `PLUGIN_COSIGN_PASSWORD` (Optional)
19+
- **Description**: Password for encrypted private keys
20+
- **Usage**: Only needed if your private key is password-protected
21+
22+
### `PLUGIN_COSIGN_PARAMS` (Optional)
23+
- **Description**: Additional cosign parameters
24+
- **Examples**:
25+
- `-a build_id=123` (add annotations)
26+
- `--tlog-upload=false` (disable transparency log)
27+
- `--rekor-url=https://custom-rekor.example.com` (custom rekor instance)
28+
29+
## Usage Examples
30+
31+
### 1. Basic Signing (Drone)
32+
33+
```yaml
34+
kind: pipeline
35+
type: docker
36+
name: default
37+
38+
steps:
39+
- name: docker
40+
image: plugins/docker
41+
settings:
42+
repo: myregistry/myapp
43+
tags: latest
44+
cosign_private_key:
45+
from_secret: cosign_private_key
46+
cosign_password:
47+
from_secret: cosign_password
48+
```
49+
50+
### 2. Advanced Signing with Annotations (Drone)
51+
52+
```yaml
53+
steps:
54+
- name: docker
55+
image: plugins/docker
56+
settings:
57+
repo: myregistry/myapp
58+
tags:
59+
- latest
60+
- ${DRONE_BUILD_NUMBER}
61+
cosign_private_key:
62+
from_secret: cosign_private_key
63+
cosign_params: "-a build_id=${DRONE_BUILD_NUMBER} -a commit_sha=${DRONE_COMMIT_SHA} -a branch=${DRONE_BRANCH}"
64+
```
65+
66+
### 3. Harness CI/CD Usage
67+
68+
```yaml
69+
- step:
70+
type: Plugin
71+
name: Build and Sign
72+
identifier: build_and_sign
73+
spec:
74+
connectorRef: account.harnessImage
75+
image: plugins/docker
76+
settings:
77+
repo: myregistry/myapp
78+
tags: <+pipeline.sequenceId>
79+
cosign_private_key: <+secrets.getValue("cosign_private_key")>
80+
cosign_password: <+secrets.getValue("cosign_password")>
81+
cosign_params: "-a harness_build=<+pipeline.sequenceId> -a harness_project=<+project.name>"
82+
```
83+
84+
## Key Management
85+
86+
### Generating Cosign Keys
87+
88+
```bash
89+
# Generate a new key pair
90+
cosign generate-key-pair
91+
92+
# This creates:
93+
# - cosign.key (private key)
94+
# - cosign.pub (public key)
95+
```
96+
97+
### Storing Keys Securely
98+
**Harness Secrets:**
99+
1. Go to Project Settings → Secrets
100+
2. Create new secret with type "File" for private key
101+
3. Create new secret with type "Text" for password
102+
103+
## Security Features
104+
105+
### Automatic Validation
106+
-**Private key format validation**: Ensures PEM format is correct
107+
-**Password requirement detection**: Warns if encrypted key needs password
108+
-**Keyless signing prevention**: Warns that OIDC keyless signing isn't supported
109+
110+
### Error Handling
111+
- **Invalid private key**: `❌ Invalid private key format. Expected PEM format`
112+
- **Missing password**: `🔐 Encrypted private key requires password. Set PLUGIN_COSIGN_PASSWORD`
113+
- **Keyless signing**: `⚠️ WARNING: Keyless signing (OIDC) isn't supported yet in this plugin`
114+
115+
## Signing Behavior
116+
117+
### When Signing Occurs
118+
-**After each successful push**: Images are signed immediately after push
119+
-**Multiple tags**: Each tag gets signed individually
120+
-**Push-only mode**: Works with existing images
121+
-**Dry-run respect**: Skips signing in dry-run mode
122+
123+
### Image References
124+
- **Preferred**: Signs by digest (e.g., `image@sha256:abc123...`) for security
125+
- **Fallback**: Signs by tag if digest unavailable
126+
127+
### Authentication
128+
- **Registry auth**: Automatically uses existing Docker registry credentials
129+
130+
## Verification
131+
132+
To verify a signed image:
133+
134+
```bash
135+
# Verify with public key
136+
cosign verify --key cosign.pub myregistry/myapp:latest
137+
138+
# Verify with annotations
139+
cosign verify --key cosign.pub \
140+
-a build_id=123 \
141+
myregistry/myapp:latest
142+
```
143+
144+
## Troubleshooting
145+
146+
### Common Issues
147+
148+
1. **"cosign: command not found"**
149+
- The container image includes cosign binary
150+
- Use the latest plugin image: `plugins/docker:latest`
151+
152+
2. **"keyless signing not supported"**
153+
- This plugin only supports private key signing
154+
- Don't use `--oidc` or `--identity-token` in `cosign_params`
155+
156+
3. **"encrypted private key requires password"**
157+
- Set `PLUGIN_COSIGN_PASSWORD` environment variable
158+
- Or use an unencrypted private key
159+
160+
4. **Registry authentication issues**
161+
- Cosign uses the same Docker registry credentials
162+
- Ensure Docker login is working first

cmd/drone-docker/main.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,22 @@ func main() {
323323
Usage: "access token",
324324
EnvVar: "ACCESS_TOKEN",
325325
},
326+
// Cosign signing configuration
327+
cli.StringFlag{
328+
Name: "cosign.private-key",
329+
Usage: "cosign private key content or file path for signing",
330+
EnvVar: "PLUGIN_COSIGN_PRIVATE_KEY",
331+
},
332+
cli.StringFlag{
333+
Name: "cosign.password",
334+
Usage: "password for encrypted cosign private key",
335+
EnvVar: "PLUGIN_COSIGN_PASSWORD",
336+
},
337+
cli.StringFlag{
338+
Name: "cosign.params",
339+
Usage: "additional cosign parameters (e.g., annotations, flags)",
340+
EnvVar: "PLUGIN_COSIGN_PARAMS",
341+
},
326342
}
327343

328344
if err := app.Run(os.Args); err != nil {
@@ -398,6 +414,11 @@ func run(c *cli.Context) error {
398414
BaseImageRegistry: c.String("docker.baseimageregistry"),
399415
BaseImageUsername: c.String("docker.baseimageusername"),
400416
BaseImagePassword: c.String("docker.baseimagepassword"),
417+
Cosign: docker.CosignConfig{
418+
PrivateKey: c.String("cosign.private-key"),
419+
Password: c.String("cosign.password"),
420+
Params: c.String("cosign.params"),
421+
},
401422
}
402423

403424
if c.Bool("tags.auto") {

daemon.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
const dockerExe = "/usr/local/bin/docker"
1212
const dockerdExe = "/usr/local/bin/dockerd"
1313
const dockerHome = "/root/.docker/"
14+
const cosignExe = "/usr/local/bin/cosign"
1415

1516
func (p Plugin) startDaemon() {
1617
cmd := commandDaemon(p.Daemon)

daemon_win.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
//go:build windows
12
// +build windows
23

34
package docker
45

56
const dockerExe = "C:\\bin\\docker.exe"
67
const dockerdExe = ""
78
const dockerHome = "C:\\ProgramData\\docker\\"
9+
const cosignExe = "C:\\bin\\cosign.exe"
810

911
func (p Plugin) startDaemon() {
1012
// this is a no-op on windows

0 commit comments

Comments
 (0)