-
Notifications
You must be signed in to change notification settings - Fork 0
Add certificate and key management system #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
2944999
93bfad0
da3fa8d
88dd7c0
297d00b
75175f3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,11 +6,16 @@ Self-hosted secrets management with web UI and REST API. | |
|
|
||
| ## Features | ||
|
|
||
| - Web UI with dark mode | ||
| - SQLite storage (single binary, no dependencies) | ||
| - Multi-user with JWT authentication | ||
| - API tokens with pattern-based permissions | ||
| - Audit logging | ||
| - 🔐 Web UI with dark mode | ||
| - 💾 SQLite storage (single binary, no dependencies) | ||
| - 👥 Multi-user with JWT authentication | ||
| - 🔑 API tokens with pattern-based permissions | ||
| - 📜 Audit logging | ||
| - 🔏 Certificate & Key Management (RSA, ECDSA, ED25519) | ||
| - Generate key pairs | ||
| - Import/Export certificates (PEM format) | ||
| - Create self-signed & CA-signed certificates | ||
| - Certificate verification & validation | ||
|
|
||
| ## Screenshots | ||
|
|
||
|
|
@@ -109,9 +114,138 @@ Then use `Authorization: Bearer <jwt>` for: | |
| | POST | `/api/secrets` | Create secret | | ||
| | PUT | `/api/secrets?key=` | Update secret | | ||
| | DELETE | `/api/secrets?key=` | Delete secret | | ||
| | GET/POST/PUT/DELETE | `/api/users` | Manage users | | ||
| | GET/POST/PUT/DELETE | `/api/tokens` | Manage tokens | | ||
| | GET/POST/PUT/DELETE | `/api/permissions` | Manage permissions | | ||
| | GET/POST/PUT/DELETE | `/api/users` | Manage users | | ||
| | GET/POST/PUT/DELETE | `/api/tokens` | Manage tokens | | ||
| | GET/POST/PUT/DELETE | `/api/permissions` | Manage permissions | | ||
| | GET/POST/DELETE | `/api/certificates` | Manage certificates | | ||
|
|
||
| ## Certificate Management | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot Certificate management should be much shorter description and preferably at the bottom. It's a niche feature
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will move Certificate Management section to the bottom of README with a much shorter description (1-2 paragraphs max). |
||
|
|
||
| The secrets manager includes comprehensive certificate and key management capabilities: | ||
|
|
||
| ### Generate Key Pairs | ||
|
|
||
| Generate RSA, ECDSA, or ED25519 key pairs: | ||
|
|
||
| ```bash | ||
| POST /api/certificates/generate-keypair | ||
| Authorization: Bearer <jwt> | ||
| Content-Type: application/json | ||
|
|
||
| { | ||
| "name": "my-keypair", | ||
| "algorithm": "RSA", // "RSA", "ECDSA", or "ED25519" | ||
| "key_size": 2048 // RSA: 2048, 3072, 4096; ECDSA: 256, 384, 521 | ||
| } | ||
| ``` | ||
|
|
||
| ### Generate Certificates | ||
|
|
||
| Create self-signed or CA-signed X.509 certificates: | ||
|
|
||
| ```bash | ||
| POST /api/certificates/generate-certificate | ||
| Authorization: Bearer <jwt> | ||
| Content-Type: application/json | ||
|
|
||
| { | ||
| "name": "my-cert", | ||
| "private_key_name": "my-keypair-private", | ||
| "subject": { | ||
| "common_name": "example.com", | ||
| "organization": "My Organization", | ||
| "country": "US" | ||
| }, | ||
| "validity_days": 365, | ||
| "is_ca": false, | ||
| "dns_names": ["example.com", "www.example.com"], | ||
| "signing_cert_name": "ca-cert" // Optional: for CA-signed certs | ||
| } | ||
| ``` | ||
|
|
||
| ### Import/Export Certificates | ||
|
|
||
| ```bash | ||
| # Import a certificate | ||
| POST /api/certificates/import | ||
| Authorization: Bearer <jwt> | ||
| Content-Type: application/json | ||
|
|
||
| { | ||
| "name": "imported-cert", | ||
| "cert_type": "certificate", // "private_key", "public_key", "certificate", "ca_certificate" | ||
| "pem_data": "-----BEGIN CERTIFICATE-----\n..." | ||
| } | ||
|
|
||
| # Export a certificate | ||
| GET /api/certificates/{name}/export | ||
| Authorization: Bearer <jwt> | ||
| ``` | ||
|
|
||
| ### Verify Certificates | ||
|
|
||
| Verify certificate validity and signatures: | ||
|
|
||
| ```bash | ||
| POST /api/certificates/verify | ||
| Authorization: Bearer <jwt> | ||
| Content-Type: application/json | ||
|
|
||
| { | ||
| "certificate_name": "my-cert", | ||
| "ca_cert_name": "ca-cert" // Optional: verify against specific CA | ||
| } | ||
| ``` | ||
|
|
||
| ### Go SDK - Certificate Management | ||
|
|
||
| ```go | ||
| // Generate RSA key pair | ||
| keyPair, err := client.GenerateKeyPair(secretssdk.GenerateKeyPairRequest{ | ||
| Name: "my-rsa-key", | ||
| Algorithm: "RSA", | ||
| KeySize: ptrInt(2048), | ||
| }) | ||
|
|
||
| // Generate self-signed certificate | ||
| cert, err := client.GenerateCertificate(secretssdk.GenerateCertificateRequest{ | ||
| Name: "my-cert", | ||
| PrivateKeyName: "my-rsa-key-private", | ||
| Subject: secretssdk.Subject{ | ||
| CommonName: "example.com", | ||
| Organization: "My Org", | ||
| Country: "US", | ||
| }, | ||
| ValidityDays: 365, | ||
| IsCA: false, | ||
| DNSNames: []string{"example.com", "*.example.com"}, | ||
| }) | ||
|
|
||
| // Verify certificate | ||
| result, err := client.VerifyCertificate(secretssdk.VerifyCertificateRequest{ | ||
| CertificateName: "my-cert", | ||
| }) | ||
|
|
||
| // Export certificate | ||
| exported, err := client.ExportCertificate("my-cert") | ||
| fmt.Println(exported.PemData) | ||
| ``` | ||
|
|
||
| ### List & Manage Certificates | ||
|
|
||
| ```bash | ||
| # List all certificates | ||
| GET /api/certificates | ||
| Authorization: Bearer <jwt> | ||
|
|
||
| # Get specific certificate | ||
| GET /api/certificates/{name} | ||
| Authorization: Bearer <jwt> | ||
|
|
||
| # Delete certificate | ||
| DELETE /api/certificates/{name} | ||
| Authorization: Bearer <jwt> | ||
| ``` | ||
|
|
||
| ## Pattern Matching | ||
|
|
||
|
|
@@ -138,5 +272,9 @@ cd web && yarn dev # Runs on localhost:5173, proxies API to :7770 | |
| Integration tests (requires [Bruno CLI](https://www.usebruno.com/)): | ||
|
|
||
| ```bash | ||
| # Run all test collections | ||
| cd bruno/auth_integration_tests && bru run --env local | ||
| cd bruno/secrets_integration_tests && bru run --env local | ||
| cd bruno/users_tokens_integration_tests && bru run --env local | ||
| cd bruno/certificates_integration_tests && bru run --env local | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| meta { | ||
| name: 01 - Login as admin | ||
| type: http | ||
| seq: 1 | ||
| } | ||
|
|
||
| post { | ||
| url: {{burl}}/login | ||
| body: json | ||
| auth: none | ||
| } | ||
|
|
||
| body:json { | ||
| { | ||
| "username": "admin", | ||
| "password": "{{admin_password}}" | ||
| } | ||
| } | ||
|
|
||
| script:post-response { | ||
| if (res.body.data && res.body.data.token) { | ||
| bru.setEnvVar("admin_token", res.body.data.token); | ||
| } | ||
| } | ||
|
|
||
| assert { | ||
| res.status: eq 200 | ||
| res.body.success: eq true | ||
| res.body.data.token: isDefined | ||
| } | ||
|
|
||
| tests { | ||
| test("Login should succeed with correct credentials", function() { | ||
| expect(res.getStatus()).to.equal(200); | ||
| expect(res.getBody().success).to.be.true; | ||
| expect(res.getBody().data.token).to.be.a('string'); | ||
| }); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| meta { | ||
| name: 02 - Generate RSA key pair | ||
| type: http | ||
| seq: 2 | ||
| } | ||
|
|
||
| post { | ||
| url: {{burl}}/api/certificates/generate-keypair | ||
| body: json | ||
| auth: inherit | ||
| } | ||
|
|
||
| headers { | ||
| Authorization: Bearer {{admin_token}} | ||
| Content-Type: application/json | ||
| } | ||
|
|
||
| body:json { | ||
| { | ||
| "name": "test-rsa", | ||
| "algorithm": "RSA", | ||
| "key_size": 2048 | ||
| } | ||
| } | ||
|
|
||
| script:post-response { | ||
| if (res.body.data && res.body.data.private_key) { | ||
| bru.setEnvVar("rsa_private_key_name", res.body.data.private_key.name); | ||
| bru.setEnvVar("rsa_public_key_name", res.body.data.public_key.name); | ||
| } | ||
| } | ||
|
|
||
| assert { | ||
| res.status: eq 200 | ||
| res.body.success: eq true | ||
| res.body.data.private_key.name: eq test-rsa-private | ||
| res.body.data.public_key.name: eq test-rsa-public | ||
| res.body.data.private_key.algorithm: eq RSA | ||
| res.body.data.private_key.cert_type: eq private_key | ||
| res.body.data.public_key.cert_type: eq public_key | ||
| } | ||
|
|
||
| tests { | ||
| test("RSA key pair generation should succeed", function() { | ||
| expect(res.getStatus()).to.equal(200); | ||
| expect(res.getBody().data.private_key.pem_data).to.contain("RSA PRIVATE KEY"); | ||
| expect(res.getBody().data.public_key.pem_data).to.contain("PUBLIC KEY"); | ||
| }); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| meta { | ||
| name: 03 - Generate ECDSA key pair | ||
| type: http | ||
| seq: 3 | ||
| } | ||
|
|
||
| post { | ||
| url: {{burl}}/api/certificates/generate-keypair | ||
| body: json | ||
| auth: inherit | ||
| } | ||
|
|
||
| headers { | ||
| Authorization: Bearer {{admin_token}} | ||
| Content-Type: application/json | ||
| } | ||
|
|
||
| body:json { | ||
| { | ||
| "name": "test-ecdsa", | ||
| "algorithm": "ECDSA", | ||
| "key_size": 256 | ||
| } | ||
| } | ||
|
|
||
| script:post-response { | ||
| if (res.body.data && res.body.data.private_key) { | ||
| bru.setEnvVar("ecdsa_private_key_name", res.body.data.private_key.name); | ||
| bru.setEnvVar("ecdsa_public_key_name", res.body.data.public_key.name); | ||
| } | ||
| } | ||
|
|
||
| assert { | ||
| res.status: eq 200 | ||
| res.body.success: eq true | ||
| res.body.data.private_key.name: eq test-ecdsa-private | ||
| res.body.data.public_key.name: eq test-ecdsa-public | ||
| res.body.data.private_key.algorithm: eq ECDSA | ||
| } | ||
|
|
||
| tests { | ||
| test("ECDSA key pair generation should succeed", function() { | ||
| expect(res.getStatus()).to.equal(200); | ||
| expect(res.getBody().data.private_key.pem_data).to.contain("EC PRIVATE KEY"); | ||
| }); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| meta { | ||
| name: 04 - Generate ED25519 key pair | ||
| type: http | ||
| seq: 4 | ||
| } | ||
|
|
||
| post { | ||
| url: {{burl}}/api/certificates/generate-keypair | ||
| body: json | ||
| auth: inherit | ||
| } | ||
|
|
||
| headers { | ||
| Authorization: Bearer {{admin_token}} | ||
| Content-Type: application/json | ||
| } | ||
|
|
||
| body:json { | ||
| { | ||
| "name": "test-ed25519", | ||
| "algorithm": "ED25519" | ||
| } | ||
| } | ||
|
|
||
| script:post-response { | ||
| if (res.body.data && res.body.data.private_key) { | ||
| bru.setEnvVar("ed25519_private_key_name", res.body.data.private_key.name); | ||
| } | ||
| } | ||
|
|
||
| assert { | ||
| res.status: eq 200 | ||
| res.body.success: eq true | ||
| res.body.data.private_key.name: eq test-ed25519-private | ||
| res.body.data.private_key.algorithm: eq ED25519 | ||
| } | ||
|
|
||
| tests { | ||
| test("ED25519 key pair generation should succeed", function() { | ||
| expect(res.getStatus()).to.equal(200); | ||
| expect(res.getBody().data.private_key.pem_data).to.contain("PRIVATE KEY"); | ||
| }); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| meta { | ||
| name: 05 - List all certificates | ||
| type: http | ||
| seq: 5 | ||
| } | ||
|
|
||
| get { | ||
| url: {{burl}}/api/certificates | ||
| body: none | ||
| auth: inherit | ||
| } | ||
|
|
||
| headers { | ||
| Authorization: Bearer {{admin_token}} | ||
| } | ||
|
|
||
| assert { | ||
| res.status: eq 200 | ||
| res.body.success: eq true | ||
| } | ||
|
|
||
| tests { | ||
| test("Should list all certificates", function() { | ||
| expect(res.getStatus()).to.equal(200); | ||
| expect(res.getBody().data).to.be.an('array'); | ||
| expect(res.getBody().data.length).to.be.at.least(6); | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot remove emojis. Keep it professional.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will remove all emojis from README Features section.