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: add Notation task #19228

Merged
merged 42 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
ccb7298
feat: add Notation task
JeyJeyGao Nov 6, 2023
c1f7d65
fix: update id
JeyJeyGao Nov 6, 2023
9908793
fix: update
JeyJeyGao Nov 6, 2023
cd0d7ef
fix: restore file
JeyJeyGao Nov 6, 2023
48a9816
fix: update
JeyJeyGao Nov 6, 2023
3511d2a
feat: add notation v1.0.1
JeyJeyGao Nov 7, 2023
4f85a86
fix: update code
JeyJeyGao Nov 7, 2023
6984b6f
fix: update version
JeyJeyGao Nov 7, 2023
be910a2
fix: update files
JeyJeyGao Nov 8, 2023
8cebd62
fix: update code
JeyJeyGao Nov 8, 2023
a210586
fix: update code owners and update node version
JeyJeyGao Nov 8, 2023
be60ba4
fix: update node version to 20
JeyJeyGao Nov 8, 2023
558cfdf
fix: update maintainer
JeyJeyGao Nov 8, 2023
9a6f465
fix: update test
JeyJeyGao Nov 8, 2023
4cd6540
fix: update test timeout
JeyJeyGao Nov 8, 2023
9131cd0
fix: update package-lock.json
JeyJeyGao Nov 8, 2023
c8f3506
fix: update package-lock.json file
JeyJeyGao Nov 8, 2023
4fa11c6
fix: update task.json
JeyJeyGao Nov 8, 2023
0d9888d
fix: update package-lock.json
JeyJeyGao Nov 8, 2023
5bffa0d
fix: update package.json
JeyJeyGao Nov 8, 2023
0f98153
fix: update test
JeyJeyGao Nov 9, 2023
5e76de7
fix: update test
JeyJeyGao Nov 9, 2023
ab948ea
fix: update test timeout
JeyJeyGao Nov 9, 2023
f17aef1
fix: update code and readme file
JeyJeyGao Nov 9, 2023
60ad932
fix: update codeowners
JeyJeyGao Nov 9, 2023
6b34094
fix: update test
JeyJeyGao Nov 9, 2023
8df3612
fix: sort import
JeyJeyGao Nov 9, 2023
97e9ab6
doc: update readme
JeyJeyGao Nov 9, 2023
af755f0
Merge branch 'master' into users/junjie/feat_notation_task
JeyJeyGao Nov 14, 2023
5a575d5
Merge branch 'master' into users/junjie/feat_notation_task
JeyJeyGao Nov 16, 2023
bcec628
Merge branch 'master' into users/junjie/feat_notation_task
JeyJeyGao Nov 17, 2023
f1502b8
Merge branch 'master' into users/junjie/feat_notation_task
JeyJeyGao Nov 18, 2023
68f4ab6
Merge branch 'master' into users/junjie/feat_notation_task
JeyJeyGao Nov 21, 2023
19d2bb0
Merge branch 'master' into users/junjie/feat_notation_task
kirill-ivlev Nov 21, 2023
6119004
fix: update Node20 to be Node20_1
JeyJeyGao Nov 21, 2023
79d4f5d
Updated typescript and @types/node, since task are using node20
DmitriiBobreshev Nov 21, 2023
fbdb4d8
Merge branch 'master' into users/junjie/feat_notation_task
DmitriiBobreshev Nov 21, 2023
fdfd068
Revert "Updated typescript and @types/node, since task are using node20"
DmitriiBobreshev Nov 21, 2023
4a6867f
Merge branch 'master' into users/junjie/feat_notation_task
JeyJeyGao Nov 22, 2023
1062a91
Merge branch 'master' into users/junjie/feat_notation_task
JeyJeyGao Nov 22, 2023
a724979
fix: update test timeout
JeyJeyGao Nov 22, 2023
e6fb414
Merge branch 'master' into users/junjie/feat_notation_task
JeyJeyGao Nov 22, 2023
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
3 changes: 3 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,9 @@ Tasks/NodeTaskRunnerInstallerV0/ @microsoft/akvelon-build-task-team

Tasks/NodeToolV0/ @microsoft/akvelon-build-task-team

# DRI rotation: Azure Container Registry / Triage
Tasks/NotationV0/ @Azure/azure-container-registry @JeyJeyGao @Two-Hearts @shizhMSFT

Tasks/NpmV0/ @microsoft/azure-artifacts-packages

Tasks/NpmV1/ @microsoft/azure-artifacts-packages
Expand Down
59 changes: 59 additions & 0 deletions Tasks/NotationV0/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Notation for Azure DevOps Task

Install Notation CLI, sign or verify container registry artifact.

# Technical Design
The Notation ADO task calls upon the Notation CLI to execute signing and verification operations. Notation CLI is a tool used to sign and verify Docker container artifacts or images. When signing an artifact, Notation signs the artifact's unique manifest descriptor and attaches the signature to the same repository. When verifying an artifact, Notation retrieves the signature from the repository and validates it against the certificate in the trust store.

## notation install command
The command detects the current operating system and architecture to download the corresponding Notation CLI from GitHub releases. It also verifies the checksum of the downloaded file against the golden file in the `./data` folder and adds Notation to the PATH.

## notation sign command
This command downloads the selected Notation plugin, validates its checksum, and then calls on the Notation CLI to sign.

## notation verfy command
It transfers the trust store and trust policy from the user's code repository to the Notation configuration folder, as required by Notation CLI. It then invokes the Notation CLI to perform verification.

## Requirements
- public network access for downloading Notation CLI and Notation Azure Key Vault plugin from Github releases.
- Supported OS: Linux x64/ARM64, Windows x64, macOS x64/ARM64

# User Documents
- [Notation sign on ADO pipeline](./docs/sign-images-pipeline.md)

## Inputs
`command` - Command
`string`. Required. Allowed values: `install`, `sign` and `verify`.

`artifactRefs` - Artifact References
`string`. The container artifact reference with digest. If multiple references are used, please use comma to separate them. If it was not specified, the task will automatically detect it from previous Docker task.

`plugin` - Plugin
`string`. Required for sign command. Allowed values: `azureKeyVault`.

`akvPluginVersion` - Azure Key Vault Plugin Version
`string`. Required for `azureKeyVault` plugin. The version for Azure Key Vault plugin. Please visit the [release page](https://github.com/Azure/notation-azure-kv/releases) to choose a released version.

`azurekvServiceConnection` - Azure Key Vault Service Connection
`string`. Required for `azure-kv` plugin. Select the The Azure Resource Manager service connection for the key vault if prefer to use service connection for authentication.

`keyid` - Key ID
`string`. Required for `azure-kv` plugin. The key identifier of an Azure Key Vault certificate.

`selfSigned` - Self signed
`boolean`. Whether the certficate is self-signed certificate.

`caCertBundle` - Certificate Bundle File Path
`string`. The certificate bundle file containing intermidiate certificates and root certificate.

`trustPolicy` - Trust Policy File Path
`string`. Required for `verify` command. The trust policy file path.

`trustStore` - Trust Store Folder Path
`string`. Requried for `verify` command. The trust store folder path.

`signatureFormat` - Signature Format
`string`. Signature envelope format. Allowed values: `jws`, `cose`.

`allowReferrersAPI` - [Experimental] Allow Referrers API
`boolean`. Use the Referrers API to sign signatures, if not supported (returns 404), fallback to the Referrers tag schema.
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"loc.friendlyName": "Notation",
"loc.helpMarkDown": "",
"loc.description": "Azure Pipepine Task for setting up Notation CLI, sign and verify with Notation",
"loc.instanceNameFormat": "$(command)",
"loc.group.displayName.commandConfig": "Command Configuration",
"loc.group.displayName.pluginConfig": "Plugin Configuration",
"loc.group.displayName.advancedConfig": "Advanced Configuration",
"loc.input.label.command": "Command to run",
"loc.input.help.command": "example: install",
"loc.input.label.isCustomVersion": "Custom Version",
"loc.input.help.isCustomVersion": "If checked, you can provide a custom version for the task",
"loc.input.label.version": "Version",
"loc.input.help.version": "The version of Notation to install. Example: 1.0.0, 1, 1.0, 1.0.0",
"loc.input.label.url": "Download URL",
"loc.input.help.url": "example: https://github.com/notaryproject/notation/releases/download/v1.0.0/notation_1.0.0_linux_amd64.tar.gz",
"loc.input.label.checksum": "Checksum",
"loc.input.help.checksum": "The SHA-256 checksum of the downloaded file",
"loc.input.label.artifactRefs": "Artifact references",
"loc.input.help.artifactRefs": "Container artifact references for signing. If it was not specified, use the artifact reference from previous Docker push task. Example: <registry name>/<repository name>@<digest> If multiple, separate by comma.",
"loc.input.label.signatureFormat": "Signature Format",
"loc.input.help.signatureFormat": "Signature envelope format, options: \"jws\", \"cose\" (default \"jws\")",
"loc.input.label.allowReferrersAPI": "[Experimental] Allow Referrers API",
"loc.input.help.allowReferrersAPI": "Use the Referrers API to sign signatures, if not supported (returns 404), fallback to the Referrers tag schema.",
"loc.input.label.plugin": "Plugin",
"loc.input.label.akvPluginVersion": "Plugin Version",
"loc.input.help.akvPluginVersion": "The version of the Azure Key Vault plugin to be installed. please visit the [release page](https://github.com/Azure/notation-azure-kv/releases) for the available versions.",
"loc.input.label.azurekvServiceConection": "Azure Key Vault service connection",
"loc.input.help.azurekvServiceConection": "Select the Azure subscription for the key vault if prefer to use service connection for authentication.",
"loc.input.label.keyid": "Key ID",
"loc.input.help.keyid": "The Key ID is the key or certificate identifier for Azure Key Vault.",
"loc.input.label.caCertBundle": "Certificate Bundle File Path",
"loc.input.help.caCertBundle": "A file with root and all intermediate certificates, starting from the root certificate, following the order in the certificate chain.",
"loc.input.label.selfSigned": "Self-signed Certificate",
"loc.input.label.trustPolicy": "Trust Policy File Path",
"loc.input.help.trustPolicy": "The path to the [trust policy](https://github.com/notaryproject/specifications/blob/v1.0.0/specs/trust-store-trust-policy.md#trust-policy) file relative to the repository. Example: ./path/to/trust-policy.json",
"loc.input.label.trustStore": "Trust Store Folder Path",
"loc.input.help.trustStore": "The path to the directory containing the [trust store](https://github.com/notaryproject/specifications/blob/v1.0.0/specs/trust-store-trust-policy.md#trust-store) relative to the repository. Example: ./path/to/truststore/",
"loc.messages.ArtifactRefs": "Artifact references: %s.",
"loc.messages.ArtifactRefsNotSpecified": "Artifact references are not specified.",
"loc.messages.APPDATANotSet": "APPDATA is not set.",
"loc.messages.AzureKVPluginAlreadyInstalled": "Azure Key Vault plugin is already installed.",
"loc.messages.CannotFindTrustStore": "Cannot find trust store directory: %s.",
"loc.messages.ChecksumValidated": "Checksum validated: %s.",
"loc.messages.ChecksumValidationFailed": "Checksum validation failed. Expected: %s, Actual: %s.",
"loc.messages.FailedArtifacts": "Failed artifacts: %s.",
"loc.messages.FailedToAddCertToTrustStore": "Failed to add a certificate to trust store: %s.",
"loc.messages.FailedToImportTrustPolicy": "Failed to import trust policy: %s.",
"loc.messages.FailedToListTrustStore": "Failed to list the trust store.",
"loc.messages.FailedToShowTrustPolicy": "Failed to show trust policy.",
"loc.messages.TryToGetArtifactRefsFromDockerTask": "Try to get artifact references from Docker task.",
"loc.messages.InvalidResourceURI": "Invalid resource URI: %s.",
"loc.messages.InvalidVersionPrefix": "Invalid version prefix: %s.",
"loc.messages.ResultSummary": "Total artifacts: %s, succeeded: %s, failed: %s.",
"loc.messages.RepeatedArtifactRef": "Repeated artifact reference: %s.",
"loc.messages.SucceededArtifacts": "Succeeded artifacts: %s.",
"loc.messages.TempDirectoryNotSet": "Agent.TempDirectory is not set.",
"loc.messages.TempDirectoryOrWorkingDirectoryNotSet": "Agent.TempDirectory or system.DefaultWorkingDirectory is not set",
"loc.messages.NoAuthScheme": "No authentication scheme is specified.",
"loc.messages.NotationAlreadyInstalled": "Notation is already installed, please do not install it again.",
"loc.messages.NotationInstalledFromURL": "Notation is installed from %s.",
"loc.messages.NotationInstalledFromVersion": "Notation v%s is installed.",
"loc.messages.NoServiceConnection": "No Azure Key Vault service connection endpoint is specified.",
"loc.messages.UnsupportedArchitecture": "Unsupported architecture: %s.",
"loc.messages.UnsupportedFileExtension": "Unsupported file extension: %s.",
"loc.messages.UnsupportedPlatform": "Unsupported platform: %s.",
"loc.messages.UnsupportedVersion": "Unsupported version: %s.",
"loc.messages.UseManagedIdentity": "Use managed identity to access Azure Key Vault.",
"loc.messages.UnknownCommand": "Unknown command: %s.",
"loc.messages.UnknownError": "An unknown error occurred.",
"loc.messages.UnknownPlugin": "Unknown plugin: %s.",
"loc.messages.UnsupportedAuthScheme": "Unsupported authentication scheme: %s."
}
35 changes: 35 additions & 0 deletions Tasks/NotationV0/Tests/L0.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as ttm from 'azure-pipelines-task-lib/mock-test';
import * as path from 'path';
import assert = require('assert');

describe('NotationV0 Suite', function () {
it('install notation', async function () {
this.timeout(10000);

let tp = path.join(__dirname, 'L0Install.js');
let tr = new ttm.MockTestRunner(tp);
await tr.runAsync()

assert(tr.succeeded, 'should have succeeded');
})

it('notation sign', async function () {
this.timeout(10000);

let tp = path.join(__dirname, 'L0Sign.js');
let tr = new ttm.MockTestRunner(tp);
await tr.runAsync()

assert(tr.succeeded, 'should have succeeded');
})

it('notation verify', async function () {
this.timeout(10000);

let tp = path.join(__dirname, 'L0Verify.js');
let tr = new ttm.MockTestRunner(tp);
await tr.runAsync()

assert(tr.succeeded, 'should have succeeded');
})
})
50 changes: 50 additions & 0 deletions Tasks/NotationV0/Tests/L0Install.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as tmrm from 'azure-pipelines-task-lib/mock-run';
import * as path from 'path';
import os = require('os');

let taskPath = path.join(__dirname, '..', 'src', 'index.js');
let tmr = new tmrm.TaskMockRunner(taskPath);

tmr.setAnswers({
"which": {
"notation": ""
},
"exec": {
"tar xC . -f notation_1.0.0_linux_amd64.tar.gz": {
"code": 0,
"stdout": "extracted"
}
}
})

tmr.registerMock('azure-pipelines-tool-lib/tool', {
downloadTool(url: string, filename: string): Promise<string> {
return Promise.resolve('notation_1.0.0_linux_amd64.tar.gz');
},
extractTar(file: string, destination?: string | undefined): Promise<string> {
return Promise.resolve('extracted');
},
prependPath(toolPath: string) {
return;
}
});

tmr.registerMock('./crypto', {
computeChecksum: (filePath: string) => {
return Promise.resolve('eceec8cb6d5cbaeb8f6f22399eb89317fe005a85206a5e780fdde1ef5bb64596');
}
})

os.platform = () => {
return 'linux' as NodeJS.Platform;
}
os.arch = () => {
return 'x64';
}
tmr.registerMock('os', os);

process.env['AGENT_TEMPDIRECTORY'] = '.';
tmr.setInput('command', 'install');
tmr.setInput('version', '1.0.0');

tmr.run();
59 changes: 59 additions & 0 deletions Tasks/NotationV0/Tests/L0Sign.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as tmrm from 'azure-pipelines-task-lib/mock-run';
import * as path from 'path';
import os = require('os');

let taskPath = path.join(__dirname, '..', 'src', 'index.js');
let tmr = new tmrm.TaskMockRunner(taskPath);

tmr.setAnswers({
"which": {
"notation": "notation"
},
"checkPath": {
"notation": true
},
"exec": {
"notation plugin list": {
"code": 0,
"stdout": "extracted"
},
"notation sign localhost:5000/e2e@sha256:xxxxxx --plugin azure-kv --id https://xxx.vault.azure.net/keys/self-signed-cert/a12c1ba176df4476a9325ca48ff796ad --signature-format cose --plugin-config=self_signed=true": {
"code": 0,
"stdout": "extracted"
}
},
})

tmr.registerMock('azure-pipelines-tool-lib/tool', {
downloadTool(url: string, filename: string): Promise<string> {
return Promise.resolve('notation-azure-kv_1.0.0_linux_amd64.tar.gz');
},
extractTar(file: string, destination?: string | undefined): Promise<string> {
return Promise.resolve('extracted');
},
});

tmr.registerMock('./crypto', {
computeChecksum: (filePath: string) => {
return Promise.resolve('f8a75d9234db90069d9eb5660e5374820edf36d710bd063f4ef81e7063d3810b');
}
})

os.platform = () => {
return 'linux' as NodeJS.Platform;
}
os.arch = () => {
return 'x64';
}
tmr.registerMock('os', os);

process.env['AGENT_TEMPDIRECTORY'] = '.';
tmr.setInput('command', 'sign');
tmr.setInput('akvPluginVersion', '1.0.1');
tmr.setInput('artifactRefs', 'localhost:5000/e2e@sha256:xxxxxx');
tmr.setInput('plugin', 'azureKeyVault');
tmr.setInput('azurekvServiceConection', 'akv-service-connection');
tmr.setInput('keyid', 'https://xxx.vault.azure.net/keys/self-signed-cert/a12c1ba176df4476a9325ca48ff796ad')
tmr.setInput('selfSigned', 'true');

tmr.run();
62 changes: 62 additions & 0 deletions Tasks/NotationV0/Tests/L0Verify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as tmrm from 'azure-pipelines-task-lib/mock-run';
import * as path from 'path';
import os = require('os');

let taskPath = path.join(__dirname, '..', 'src', 'index.js');
let tmr = new tmrm.TaskMockRunner(taskPath);

tmr.setAnswers({
"which": {
"notation": "notation"
},
"checkPath": {
"notation": true
},
"exec": {
"notation policy show": {
"code": 0,
"stdout": "extracted"
},
"notation policy import --force ./policy.json": {
"code": 0,
"stdout": "extracted"
},
"notation cert list": {
"code": 0,
"stdout": "extracted"
},
"notation verify localhost:5000/e2e@sha256:xxxxxx --verbose": {
"code": 0,
"stdout": "extracted"
},
},
"exist": {
"truststore/x509": true,
"truststore\\x509": true
},
"rmRF": {
"/user/config/notation/truststore": {
"success": true
},
"\\user\\config\\notation\\truststore": {
"success": true
}
},
})

process.env['AGENT_TEMPDIRECTORY'] = '.';
process.env['XDG_CONFIG_HOME'] = '/user/config';
tmr.setInput('command', 'verify');
tmr.setInput('artifactRefs', 'localhost:5000/e2e@sha256:xxxxxx');
tmr.setInput('trustpolicy', './policy.json')
tmr.setInput('truststore', './truststore')

os.platform = () => {
return 'linux' as NodeJS.Platform;
}
os.arch = () => {
return 'x64';
}
tmr.registerMock('os', os);

tmr.run();
Loading