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:
2026-02-11 20:31:20 -05:00
commit dddbd30677
27 changed files with 21162 additions and 0 deletions
@@ -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;
}
}