feat: Implement Docker registry token endpoint and enhance registry request handling

This commit is contained in:
2025-11-25 19:46:18 +00:00
parent 76793d512b
commit 5cf9c72dd4
4 changed files with 201 additions and 4 deletions

View File

@@ -83,6 +83,11 @@ export class OneboxHttpServer {
return this.handleLogStreamUpgrade(req, serviceName);
}
// Docker Registry v2 Token endpoint (for OCI authentication)
if (path === '/v2/token') {
return await this.handleRegistryTokenRequest(req, url);
}
// Docker Registry v2 API (no auth required - registry handles it)
if (path.startsWith('/v2/')) {
return await this.oneboxRef.registry.handleRequest(req);
@@ -1234,6 +1239,79 @@ export class OneboxHttpServer {
// ============ Registry Endpoints ============
/**
* Handle Docker registry token request (OCI token authentication)
* Docker calls this endpoint to get a bearer token for registry operations
*
* Query params:
* - service: The registry service name
* - scope: Permission scope (e.g., "repository:hello-world:push,pull")
* - account: Optional account name (for basic auth)
*/
private async handleRegistryTokenRequest(req: Request, url: URL): Promise<Response> {
try {
const service = url.searchParams.get('service') || 'onebox-registry';
const scope = url.searchParams.get('scope');
const account = url.searchParams.get('account') || 'anonymous';
logger.info(`Registry token request: service=${service}, scope=${scope}, account=${account}`);
// Parse scope to extract repository and actions
// Format: repository:name:action1,action2 (e.g., "repository:hello-world:push,pull")
let scopes: string[] = [];
if (scope) {
const scopeParts = scope.split(':');
if (scopeParts.length >= 3 && scopeParts[0] === 'repository') {
const repository = scopeParts[1];
// For now, grant both push and pull for any repository request
// This allows anonymous push to the local registry
// TODO: Add authentication and authorization to restrict access
scopes = [
`oci:repository:${repository}:push`,
`oci:repository:${repository}:pull`,
];
}
}
// If no scope specified, grant basic access
if (scopes.length === 0) {
scopes = ['oci:repository:*:pull'];
}
logger.info(`Creating OCI token with scopes: ${scopes.join(', ')}`);
// Use the registry's auth manager to create a token
// smartregistry v2.0.0 returns proper JWT format (header.payload.signature)
const authManager = this.oneboxRef.registry.getAuthManager();
const token = await authManager.createOciToken(account, scopes, 3600);
logger.info(`Token created (JWT length: ${token.length})`);
// Return in Docker-expected format
return new Response(JSON.stringify({
token,
access_token: token,
expires_in: 3600,
issued_at: new Date().toISOString(),
}), {
status: 200,
headers: {
'Content-Type': 'application/json',
},
});
} catch (error) {
logger.error(`Registry token error: ${getErrorMessage(error)}`);
return new Response(JSON.stringify({
error: 'token_error',
error_description: getErrorMessage(error),
}), {
status: 500,
headers: { 'Content-Type': 'application/json' },
});
}
}
private async handleGetRegistryTagsRequest(serviceName: string): Promise<Response> {
try {
const tags = await this.oneboxRef.registry.getImageTags(serviceName);