Initial release: Headlamp Sealed Secrets plugin v0.1.0
Features: - Complete SealedSecret CRD integration with Headlamp - Client-side encryption using controller's public key - Support for all three scoping modes (strict, namespace-wide, cluster-wide) - List and detail views for SealedSecrets - Encryption dialog for creating new SealedSecrets - Decryption support with RBAC awareness - Sealing keys management - Settings page for controller configuration - Integration with Secret detail view Technical: - Full TypeScript with strict mode - ~1,345 lines of code - Build size: 339.42 kB (93.21 kB gzipped) - Compatible with Headlamp v0.13.0+ - Apache 2.0 license Security: - All encryption performed client-side - RSA-OAEP + AES-256-GCM (kubeseal-compatible) - Auto-hide decrypted values after 30 seconds Closes: Initial implementation
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* SealedSecret Custom Resource Definition
|
||||
*/
|
||||
|
||||
import { apiFactoryWithNamespace } from '@kinvolk/headlamp-plugin/lib/lib/k8s/apiProxy';
|
||||
import { KubeObject } from '@kinvolk/headlamp-plugin/lib/lib/k8s/cluster';
|
||||
import {
|
||||
SealedSecretInterface,
|
||||
SealedSecretScope,
|
||||
SealedSecretSpec,
|
||||
SealedSecretStatus,
|
||||
} from '../types';
|
||||
|
||||
/**
|
||||
* SealedSecret CRD class
|
||||
* Represents a Bitnami Sealed Secret resource in the cluster
|
||||
*/
|
||||
export class SealedSecret extends KubeObject<SealedSecretInterface> {
|
||||
/**
|
||||
* API endpoint for SealedSecret resources
|
||||
* bitnami.com/v1alpha1/sealedsecrets
|
||||
*/
|
||||
static apiEndpoint = apiFactoryWithNamespace('bitnami.com', 'v1alpha1', 'sealedsecrets');
|
||||
|
||||
/**
|
||||
* Class name used for Headlamp registration
|
||||
*/
|
||||
static get className(): string {
|
||||
return 'SealedSecret';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SealedSecret spec
|
||||
*/
|
||||
get spec(): SealedSecretSpec {
|
||||
return this.jsonData.spec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SealedSecret status
|
||||
*/
|
||||
get status(): SealedSecretStatus | undefined {
|
||||
return this.jsonData.status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the scope of this SealedSecret (strict, namespace-wide, or cluster-wide)
|
||||
*/
|
||||
get scope(): SealedSecretScope {
|
||||
const annotations = this.metadata.annotations || {};
|
||||
|
||||
if (annotations['sealedsecrets.bitnami.com/cluster-wide'] === 'true') {
|
||||
return 'cluster-wide';
|
||||
}
|
||||
if (annotations['sealedsecrets.bitnami.com/namespace-wide'] === 'true') {
|
||||
return 'namespace-wide';
|
||||
}
|
||||
return 'strict';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of encrypted keys
|
||||
*/
|
||||
get encryptedKeysCount(): number {
|
||||
return Object.keys(this.spec.encryptedData || {}).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the SealedSecret is synced
|
||||
*/
|
||||
get isSynced(): boolean {
|
||||
const syncCondition = this.status?.conditions?.find(c => c.type === 'Synced');
|
||||
return syncCondition?.status === 'True';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sync status condition
|
||||
*/
|
||||
get syncCondition() {
|
||||
return this.status?.conditions?.find(c => c.type === 'Synced');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sync status message
|
||||
*/
|
||||
get syncMessage(): string {
|
||||
const condition = this.syncCondition;
|
||||
if (!condition) {
|
||||
return 'Unknown';
|
||||
}
|
||||
return condition.message || condition.reason || condition.status;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user