Skip to content

Commit 564decf

Browse files
authored
add dockerRegistrysAuth (#1193)
* add dockerRegistrysAuth
1 parent 3885e44 commit 564decf

18 files changed

Lines changed: 1596 additions & 41 deletions

docs/API.md

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,20 +1448,21 @@ starts a free compute job and returns jobId if succesfull
14481448

14491449
#### Parameters
14501450

1451-
| name | type | required | description |
1452-
| ----------------- | ------ | -------- | ----------------------------------------------------------------------------- |
1453-
| command | string | v | command name |
1454-
| node | string | | if not present it means current node |
1455-
| consumerAddress | string | v | consumer address |
1456-
| signature | string | v | signature (msg=String(nonce) ) |
1457-
| nonce | string | v | nonce for the request |
1458-
| datasets | object | | list of ComputeAsset to be used as inputs |
1459-
| algorithm | object | | ComputeAlgorithm definition |
1460-
| environment | string | v | compute environment to use |
1461-
| resources | object | | optional list of required resources |
1462-
| metadata | object | | optional metadata for the job, data provided by the user |
1463-
| additionalViewers | object | | optional array of addresses that are allowed to fetch the result |
1464-
| queueMaxWaitTime | number | | optional max time in seconds a job can wait in the queue before being started |
1451+
| name | type | required | description |
1452+
| --------------------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1453+
| command | string | v | command name |
1454+
| node | string | | if not present it means current node |
1455+
| consumerAddress | string | v | consumer address |
1456+
| signature | string | v | signature (msg=String(nonce) ) |
1457+
| nonce | string | v | nonce for the request |
1458+
| datasets | object | | list of ComputeAsset to be used as inputs |
1459+
| algorithm | object | | ComputeAlgorithm definition |
1460+
| environment | string | v | compute environment to use |
1461+
| resources | object | | optional list of required resources |
1462+
| metadata | object | | optional metadata for the job, data provided by the user |
1463+
| additionalViewers | object | | optional array of addresses that are allowed to fetch the result |
1464+
| queueMaxWaitTime | number | | optional max time in seconds a job can wait in the queue before being started |
1465+
| encryptedDockerRegistryAuth | string | | Ecies encrypted docker auth schema for image (see [Private Docker Registries with Per-Job Authentication](../env.md#private-docker-registries-with-per-job-authentication)) |
14651466

14661467
#### Request
14671468

docs/env.md

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,205 @@ The `DOCKER_COMPUTE_ENVIRONMENTS` environment variable should be a JSON array of
226226
- **total**: Total number of the resource available.
227227
- **min**: Minimum number of the resource needed for a job.
228228
- **max**: Maximum number of the resource for a job.
229+
230+
### Docker Registry Authentication
231+
232+
- `DOCKER_REGISTRY_AUTHS`: JSON object mapping Docker registry URLs to authentication credentials. Used for accessing private Docker/OCI registries when validating and pulling Docker images. Each registry entry must provide either `username`+`password` or `auth`. Example:
233+
234+
```json
235+
{
236+
"https://registry-1.docker.io": {
237+
"username": "myuser",
238+
"password": "mypassword"
239+
},
240+
"https://ghcr.io": {
241+
"username": "myuser",
242+
"password": "ghp_..."
243+
},
244+
"https://registry.gitlab.com": {
245+
"auth": "glpat-..."
246+
}
247+
}
248+
```
249+
250+
**Configuration Options:**
251+
252+
- **Registry URL** (key): The full registry URL including protocol (e.g., `https://registry-1.docker.io`, `https://ghcr.io`, `https://registry.gitlab.com`)
253+
- **username** (optional): Username for registry authentication. Required if using password-based auth.
254+
- **password** (optional): Password or personal access token for registry authentication. Required if using username-based auth.
255+
- **auth** (optional): Authentication token (alternative to username+password). Required if not using username+password.
256+
257+
**Notes:**
258+
259+
- For Docker Hub (`registry-1.docker.io`), you can use your Docker Hub username and password, or a personal access token (PAT) as the password.
260+
- For GitHub Container Registry (GHCR), use your GitHub username with a personal access token (PAT) as the password, or use a token directly.
261+
- For GitLab Container Registry, use a personal access token (PAT) or deploy token.
262+
- The registry URL must match exactly (including protocol) with the registry used in the Docker image reference.
263+
- If no credentials are configured for a registry, the node will attempt unauthenticated access (works for public images only).
264+
265+
---
266+
267+
## Private Docker Registries with Per-Job Authentication
268+
269+
In addition to node-level registry authentication via `DOCKER_REGISTRY_AUTHS`, you can provide encrypted Docker registry authentication credentials on a per-job basis. This allows different users to use different private registries or credentials for their compute jobs.
270+
271+
### Overview
272+
273+
The `encryptedDockerRegistryAuth` parameter allows you to securely provide Docker registry credentials that are:
274+
275+
- Encrypted using ECIES (Elliptic Curve Integrated Encryption Scheme) with the node's public key
276+
- Validated to ensure proper format (either `auth` string OR `username`+`password`)
277+
- Used only for the specific compute job, overriding node-level configuration if provided
278+
279+
### Encryption Format
280+
281+
The `encryptedDockerRegistryAuth` must be:
282+
283+
1. A JSON object matching the Docker registry auth schema (see below)
284+
2. Encrypted using ECIES with the node's public key
285+
3. Hex-encoded as a string
286+
287+
**Auth Schema Format:**
288+
289+
The decrypted JSON must follow this structure:
290+
291+
```json
292+
{
293+
"username": "myuser",
294+
"password": "mypassword"
295+
}
296+
```
297+
298+
OR
299+
300+
```json
301+
{
302+
"auth": "base64-encoded-username:password"
303+
}
304+
```
305+
306+
OR (all fields present)
307+
308+
```json
309+
{
310+
"username": "myuser",
311+
"password": "mypassword",
312+
"auth": "base64-encoded-username:password"
313+
}
314+
```
315+
316+
**Validation Rules:**
317+
318+
- Either `auth` string must be provided (non-empty), OR
319+
- Both `username` AND `password` must be provided (both non-empty)
320+
- Empty strings are not accepted
321+
322+
### Usage Examples
323+
324+
#### 1. Paid Compute Start (`POST /api/services/compute`)
325+
326+
```json
327+
{
328+
"command": "startCompute",
329+
"consumerAddress": "0x...",
330+
"signature": "...",
331+
"nonce": "123",
332+
"environment": "0x...",
333+
"algorithm": {
334+
"meta": {
335+
"container": {
336+
"image": "registry.example.com/myorg/myimage:latest"
337+
}
338+
}
339+
},
340+
"datasets": [],
341+
"payment": { ... },
342+
"encryptedDockerRegistryAuth": "0xdeadbeef..." // ECIES encrypted hex string
343+
}
344+
```
345+
346+
#### 2. Free Compute Start (`POST /api/services/freeCompute`)
347+
348+
```json
349+
{
350+
"command": "freeStartCompute",
351+
"consumerAddress": "0x...",
352+
"signature": "...",
353+
"nonce": "123",
354+
"environment": "0x...",
355+
"algorithm": {
356+
"meta": {
357+
"container": {
358+
"image": "ghcr.io/myorg/myimage:latest"
359+
}
360+
}
361+
},
362+
"datasets": [],
363+
"encryptedDockerRegistryAuth": "0xdeadbeef..." // ECIES encrypted hex string
364+
}
365+
```
366+
367+
#### 3. Initialize Compute
368+
369+
The `initialize` command accepts `encryptedDockerRegistryAuth` as part of the command payload, as it validates the image
370+
371+
```json
372+
{
373+
"command": "initialize",
374+
"datasets": [...],
375+
"algorithm": {
376+
"meta": {
377+
"container": {
378+
"image": "registry.gitlab.com/myorg/myimage:latest"
379+
}
380+
}
381+
},
382+
"environment": "0x...",
383+
"payment": { ... },
384+
"consumerAddress": "0x...",
385+
"maxJobDuration": 3600,
386+
"encryptedDockerRegistryAuth": "0xdeadbeef..." // ECIES encrypted hex string
387+
}
388+
```
389+
390+
### Encryption Process
391+
392+
To create `encryptedDockerRegistryAuth`, you need to:
393+
394+
1. **Prepare the auth JSON object:**
395+
396+
```json
397+
{
398+
"username": "myuser",
399+
"password": "mypassword"
400+
}
401+
```
402+
403+
2. **Get the node's public key** (available via the node's API or P2P interface)
404+
405+
3. **Encrypt the JSON string** using ECIES with the node's public key
406+
407+
4. **Hex-encode the encrypted result**
408+
409+
### Behavior
410+
411+
- **Priority**: If `encryptedDockerRegistryAuth` is provided, it takes precedence over node-level `DOCKER_REGISTRY_AUTHS` configuration for that specific job
412+
- **Validation**: The encrypted auth is decrypted and validated before the job starts. Invalid formats will result in an error
413+
- **Scope**: The credentials are used for:
414+
- Validating the Docker image exists (during initialize)
415+
- Pulling the Docker image (during job execution)
416+
- **Security**: Credentials are encrypted and only decrypted by the node using its private key
417+
418+
### Error Handling
419+
420+
If `encryptedDockerRegistryAuth` is invalid, you'll receive an error:
421+
422+
- **Decryption failure**: `Invalid encryptedDockerRegistryAuth: failed to parse JSON - [error message]`
423+
- **Schema validation failure**: `Invalid encryptedDockerRegistryAuth: Either 'auth' must be provided, or both 'username' and 'password' must be provided`
424+
425+
### Notes
426+
427+
- The `encryptedDockerRegistryAuth` parameter is optional. If not provided, the node will use `DOCKER_REGISTRY_AUTHS` configuration or attempt unauthenticated access
428+
- The registry URL in the Docker image reference must match the registry you're authenticating to
429+
- For Docker Hub, use `registry-1.docker.io` as the registry URL
430+
- Credentials are stored encrypted in the job record and decrypted only when needed for image operations

src/@types/C2D/C2D.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ export interface DBComputeJob extends ComputeJob {
264264
metadata?: DBComputeJobMetadata
265265
additionalViewers?: string[] // addresses of additional addresses that can get results
266266
algoDuration: number // duration of the job in seconds
267+
encryptedDockerRegistryAuth?: string
267268
}
268269

269270
// make sure we keep them both in sync

src/@types/OceanNode.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,18 @@ export interface AccessListContract {
9393
[chainId: string]: string[]
9494
}
9595

96+
export interface dockerRegistryAuth {
97+
username?: string
98+
password?: string
99+
auth?: string
100+
}
101+
export interface dockerRegistrysAuth {
102+
[registry: string]: dockerRegistryAuth
103+
}
104+
96105
export interface OceanNodeConfig {
97106
dockerComputeEnvironments: C2DDockerConfig[]
107+
dockerRegistrysAuth: dockerRegistrysAuth
98108
authorizedDecrypters: string[]
99109
authorizedDecryptersList: AccessListContract | null
100110
allowedValidators: string[]

src/@types/commands.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ export interface ComputeInitializeCommand extends Command {
207207
maxJobDuration: number
208208
policyServer?: any // object to pass to policy server
209209
queueMaxWaitTime?: number // max time in seconds a job can wait in the queue before being started
210+
encryptedDockerRegistryAuth?: string
210211
}
211212

212213
export interface FreeComputeStartCommand extends Command {
@@ -223,6 +224,7 @@ export interface FreeComputeStartCommand extends Command {
223224
metadata?: DBComputeJobMetadata
224225
additionalViewers?: string[] // addresses of additional addresses that can get results
225226
queueMaxWaitTime?: number // max time in seconds a job can wait in the queue before being started
227+
encryptedDockerRegistryAuth?: string
226228
}
227229
export interface PaidComputeStartCommand extends FreeComputeStartCommand {
228230
payment: ComputePayment

0 commit comments

Comments
 (0)