Add Docker Registry AIO with Nuxt Token Auth and admin UI.
Compose brings up registry:3 plus a Prisma/SQLite Nuxt app for docker login, ACL management, and image browsing with shadcn-vue. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
commit
0fd00bfa37
120 files changed
+17094
No files matched your search
@@ -0,0 +1,87 @@
|
||||
import {
|
||||
createPrivateKey,
|
||||
randomUUID,
|
||||
X509Certificate,
|
||||
} from 'node:crypto'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { SignJWT, importPKCS8 } from 'jose'
|
||||
import { ensureTokenCerts } from './certs'
|
||||
|
||||
export type RegistryAccess = {
|
||||
type: string
|
||||
name: string
|
||||
actions: string[]
|
||||
}
|
||||
|
||||
type TokenMaterial = {
|
||||
privateKeyPem: string
|
||||
x5c: string
|
||||
}
|
||||
|
||||
let cached: TokenMaterial | null = null
|
||||
|
||||
function toPkcs8Pem(privateKeyPem: string): string {
|
||||
const key = createPrivateKey(privateKeyPem)
|
||||
return key.export({ type: 'pkcs8', format: 'pem' }).toString()
|
||||
}
|
||||
|
||||
function pemToX5C(certPem: string): string {
|
||||
return certPem
|
||||
.replace(/-----BEGIN CERTIFICATE-----/g, '')
|
||||
.replace(/-----END CERTIFICATE-----/g, '')
|
||||
.replace(/\s+/g, '')
|
||||
}
|
||||
|
||||
function loadTokenMaterial(): TokenMaterial {
|
||||
if (cached) return cached
|
||||
|
||||
const { keyPath, certPath } = ensureTokenCerts()
|
||||
|
||||
const rawPrivateKeyPem = readFileSync(keyPath, 'utf8')
|
||||
const privateKeyPem = toPkcs8Pem(rawPrivateKeyPem)
|
||||
const certPem = readFileSync(certPath, 'utf8')
|
||||
|
||||
// Validate certificate parses
|
||||
void new X509Certificate(certPem)
|
||||
|
||||
cached = {
|
||||
privateKeyPem,
|
||||
x5c: pemToX5C(certPem),
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
export async function signRegistryToken(options: {
|
||||
subject: string
|
||||
access: RegistryAccess[]
|
||||
expiresInSeconds?: number
|
||||
}): Promise<{ token: string; expiresIn: number; issuedAt: string }> {
|
||||
const { privateKeyPem, x5c } = loadTokenMaterial()
|
||||
const privateKey = await importPKCS8(privateKeyPem, 'RS256')
|
||||
|
||||
const issuer = process.env.REGISTRY_AUTH_ISSUER || 'registry-aio'
|
||||
const audience = process.env.REGISTRY_AUTH_SERVICE || 'registry'
|
||||
const expiresIn = options.expiresInSeconds ?? 600
|
||||
const now = Math.floor(Date.now() / 1000)
|
||||
const issuedAt = new Date(now * 1000).toISOString()
|
||||
|
||||
const token = await new SignJWT({
|
||||
access: options.access,
|
||||
})
|
||||
.setProtectedHeader({
|
||||
alg: 'RS256',
|
||||
typ: 'JWT',
|
||||
// Registry v3 trusts tokens via x5c against rootcertbundle
|
||||
x5c: [x5c],
|
||||
})
|
||||
.setIssuer(issuer)
|
||||
.setSubject(options.subject)
|
||||
.setAudience(audience)
|
||||
.setIssuedAt(now)
|
||||
.setNotBefore(now - 10)
|
||||
.setExpirationTime(now + expiresIn)
|
||||
.setJti(randomUUID())
|
||||
.sign(privateKey)
|
||||
|
||||
return { token, expiresIn, issuedAt }
|
||||
}
|
||||
Reference in new issue
Block a user