feat: Implement Docker registry token endpoint and enhance registry request handling
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user