19 KiB
Key/Info Manager
A robust and secure Key/Info Management system designed for applications requiring controlled access to sensitive data. It features a WebSocket API for client applications and an HTTP admin panel for comprehensive management tasks. All stored secrets are encrypted at rest using AES-256-GCM.
Overview
The Key/Info Manager provides a centralized solution for storing, managing, and securely distributing secrets or configuration data to authorized client applications. It emphasizes security through data encryption, role-based access (via admin approval and group association), and secure communication protocols (when deployed with HTTPS/WSS).
Key Features
- Secure Storage: Secrets are encrypted using AES-256-GCM. The master encryption key is derived from a user-provided password at server startup.
- Admin UI Panel: A comprehensive web-based interface for:
- Managing secret groups (create, rename, delete).
- Managing secrets within groups (create, view, update, delete).
- Managing client applications:
- Viewing pending client registrations.
- Approving or rejecting client registration requests.
- Associating approved clients with specific secret groups to grant access.
- Revoking client access.
- Configuring WebSocket client auto-approval (for debugging/development).
- WebSocket API: A secure and efficient API for client applications to:
- Register themselves with the server.
- Request secrets they are authorized to access (based on group association).
- List all secret keys they are authorized to access.
- Password Protection: Admin UI login and initial server setup (for encryption key) are protected by a master password.
- CSRF Protection: Admin UI operations are protected against Cross-Site Request Forgery.
- Rate Limiting: Both HTTP admin endpoints and WebSocket communications are rate-limited to mitigate abuse and brute-force attempts.
- Configuration Management: Core settings like JWT secret and port configurations are manageable.
- Data Persistence: Stores data (encrypted secrets, client info, group info) in local files.
Tech Stack
- Backend: Node.js, Express.js
- WebSocket Server:
wslibrary - Templating: EJS for Admin UI
- Core Libraries:
jsonwebtokenfor JWT generation (Admin sessions)csurffor CSRF protectionhelmetfor security headersexpress-rate-limitfor rate limitingexpress-sessionfor session managementdotenvfor environment variable management
- Cryptography: Node.js
cryptomodule (AES-256-GCM for data encryption) - Development: TypeScript, Jest (for testing), Nodemon
Prerequisites
- Node.js (v16.x or later recommended)
- npm (Node Package Manager, typically included with Node.js)
Getting Started & Installation
-
Clone the Repository (if applicable):
git clone <repository_url> cd key_manager # Or your project directory name -
Install Dependencies: Navigate to the project root directory and run:
npm install -
Configure Environment Variables: Create a
.envfile in the project root directory. This file is used to store sensitive information and configurations. It is crucial for security that this file is NOT committed to version control. Add it to your.gitignoreif it's not already there.Essential Security Variables (add these to your
.envfile):# Master Password for data encryption and initial admin login # Choose a strong, unique password. This will be used to derive the data encryption key. # If not set, you will be prompted for it in the console on first run. MASTER_PASSWORD=your_very_strong_master_password_here # JWT Secret for signing authentication tokens for the Admin UI # Should be a long, random, and unique string. JWT_SECRET=your_super_random_jwt_secret_string_here_at_least_32_chars # Session Secret for Express sessions (used by CSRF protection) # Should also be a long, random, and unique string. SESSION_SECRET=another_super_random_session_secret_string_hereOptional Configuration Variables (defaults are generally suitable for local development):
# HTTP Port for the Admin UI (default: 3000, can also be set in data/runtime-config.json) HTTP_PORT=3000 # WebSocket Port for client applications (default: 3001, can also be set in data/runtime-config.json) WS_PORT=3001 # Data files path (defaults to 'data' directory in project root) # DATA_DIR=./data # Example, usually not needed to change # Config file path (defaults to 'data/runtime-config.json') # CONFIG_FILE_PATH=./data/runtime-config.json # ExampleRefer to
src/lib/configManager.tsfor howjwtSecret,httpPort, andwsPortcan also be managed viadata/runtime-config.json(which is auto-generated on first run if it doesn't exist). For production, setting secrets likeJWT_SECRETandSESSION_SECRETvia environment variables is generally recommended. -
Build TypeScript Code: Compile the TypeScript source files to JavaScript:
npm run buildThis will create a
distdirectory with the compiled code.
Running the Application
-
Start the Server: After building the code, run:
npm startThis command executes the compiled
dist/main.jsfile. -
Development Mode (Optional): For development, you can use
npm run dev. This script typically watches for TypeScript file changes, recompiles, and restarts the server automatically usingnodemon.npm run dev -
First Run & Master Password:
- If the
MASTER_PASSWORDenvironment variable was not set in your.envfile, the server will prompt you to enter and confirm a master password in the console during its first startup. - This password is critical: it's used to derive the encryption key for your
secrets.json.encdata file. Choose a strong password and store it securely. Losing this password means losing access to all encrypted data. - A salt for deriving the master key will be generated and stored in
data/masterkey.salt. This salt file should be backed up along with your encrypted data. - The encrypted data itself is stored in
data/secrets.json.enc. - The application runtime configuration (like JWT secret if not overridden by env var, ports, etc.) is stored in
data/runtime-config.json.
Important: The
datadirectory (containingsecrets.json.enc,masterkey.salt, andruntime-config.json) should be backed up regularly and secured appropriately. It is gitignored by default. - If the
Usage
Admin UI
The Admin UI provides a web interface for managing secrets, secret groups, and client applications.
-
Access: Open your web browser and navigate to
http://localhost:<HTTP_PORT>/admin(e.g.,http://localhost:3000/adminif using the default port). -
Login: You will be prompted to log in. Use the
MASTER_PASSWORDthat you either set as an environment variable or entered in the console during the server's first startup. -
Key Management Areas:
-
Manage Secrets (Main Page / Secrets Tab):
- Create Secret Groups: Define logical groups to organize your secrets.
- Manage Existing Groups: View, rename, or delete secret groups. Deleting a group also deletes all secrets within it.
- Add New Secrets: Create new secrets (key-value pairs) and assign them to an existing group. Values can be simple strings or complex JSON objects/arrays.
- View/Manage Secrets within a Group: Drill down into a group to see its secrets, edit their values, or delete them.
- Edit/Delete Individual Secrets: Modify the value of existing secrets or delete them from the system (this also removes them from their group).
-
Manage Clients (Clients Tab):
- Pending Client Registrations: View client applications that have registered via the WebSocket API and are awaiting administrative approval.
- Approve: Approves the client's registration. The client can then proceed to use the WebSocket API with its approved status.
- Reject: Rejects the client's registration request.
- Approved Clients: View already approved client applications.
- Manage Groups (for Client): For each approved client, you can associate them with one or more secret groups. This grants the client access to all secrets within the associated groups.
- Revoke Access: Deletes the client from the system, effectively revoking all their access.
- WebSocket Settings (Debug):
- Toggle the "Automatically Approve New WebSocket Registrations" setting. This is primarily for development or debugging and should generally be disabled in production environments.
- Pending Client Registrations: View client applications that have registered via the WebSocket API and are awaiting administrative approval.
-
WebSocket API
Client applications connect to the Key/Info Manager via WebSocket to register and request authorized secrets.
- Connection URL:
ws://localhost:<WS_PORT>(e.g.,ws://localhost:3001) - Message Format: All messages (client-to-server and server-to-client) are JSON objects. A typical structure includes:
type: A string indicating the message type (e.g.,REGISTER_CLIENT,ERROR).payload: An object containing the data relevant to the message type.code(Server-to-Client): A numerical response code (seeWsResponseCodesbelow).requestId(Optional, Client-to-Server & echoed in Server-to-Client): A client-generated ID to correlate requests and responses.
Common Server Response Codes (WsResponseCodes):
2000 OK: General success.2001 REGISTRATION_SUBMITTED: Client registration successfully submitted, awaiting approval.4000 BAD_REQUEST: The request was malformed or missing required parameters.4001 UNAUTHORIZED: Client is not authorized for the requested action (e.g., not approved, wrong credentials, insufficient permissions).4004 NOT_FOUND: Requested resource (e.g., a specific secret) was not found.4005 CLIENT_NOT_REGISTERED: Client attempted an action before completingREGISTER_CLIENT.4006 CLIENT_REGISTRATION_EXPIRED: A pending client registration has expired.4029 RATE_LIMIT_EXCEEDED: Client has sent too many requests in a given timeframe.5000 INTERNAL_SERVER_ERROR: An unexpected error occurred on the server.
Key Message Flows:
-
Server Welcome Message (Server to Client): Upon successful WebSocket connection, the server sends:
{ "type": "WELCOME", "code": 2000, "payload": { "detail": "Welcome! Please register your client using REGISTER_CLIENT message." } } -
Client Registration (Client to Server):
- Purpose: New client applications must register to be recognized by the server.
- Request:
{ "type": "REGISTER_CLIENT", "payload": { "clientName": "My Awesome Application", // "requestedSecretKeys": ["key1", "key2"] // Legacy field, group association is now preferred }, "requestId": "client-req-001" } - Server Response (Success - Awaiting Approval):
The client must then be approved in the Admin UI.
{ "type": "REGISTRATION_ACK", "code": 2001, // REGISTRATION_SUBMITTED "payload": { "clientId": "server_generated_client_id", // For admin tracking "detail": "Registration for 'My Awesome Application' submitted. Awaiting admin approval. Your Client ID is server_generated_client_id." }, "requestId": "client-req-001" }
-
Status Update Notification (Server to Client):
- Purpose: After admin action (approval/rejection), the server notifies the specific client if connected.
- Example (Approved):
{ "type": "STATUS_UPDATE", "code": 2000, // Or a more specific code if defined "payload": { "newStatus": "approved", "detail": "Client registration automatically approved (debug mode)." // Or admin approved } } - Example (Rejected):
{ "type": "STATUS_UPDATE", "code": 4001, // UNAUTHORIZED (or a specific "REJECTED" code) "payload": { "newStatus": "rejected", "detail": "Client registration was rejected by an administrator." } }
-
Request Secret (Client to Server - Approved Clients Only):
- Purpose: To request the value of a secret the client is authorized (via group association) to access.
- Request:
{ "type": "REQUEST_SECRET", "payload": { "secretKey": "database_connection_string" }, "requestId": "client-req-002" } - Server Response (Success):
{ "type": "SECRET_DATA", "code": 2000, // OK "payload": { "secretKey": "database_connection_string", "value": "the_actual_secret_value_here" }, "requestId": "client-req-002" } - Server Response (Unauthorized):
{ "type": "ERROR", "code": 4001, // UNAUTHORIZED "payload": { "detail": "You are not authorized to access the secret key \"database_connection_string\"." }, "requestId": "client-req-002" } - Server Response (Not Found):
{ "type": "ERROR", "code": 4004, // NOT_FOUND "payload": { "detail": "Secret key \"some_other_key\" not found on server, though client is authorized. Please contact admin." }, "requestId": "client-req-003" }
-
List Authorized Secrets (Client to Server - Approved Clients Only):
- Purpose: To get a list of all secret keys the client is currently authorized to access.
- Request:
{ "type": "LIST_AUTHORIZED_SECRETS", "requestId": "client-req-004" } - Server Response (Success):
{ "type": "AUTHORIZED_SECRETS_LIST", "code": 2000, // OK "payload": { "authorizedSecretKeys": ["key1", "key2", "database_connection_string"] }, "requestId": "client-req-004" }
General Error Response (Server to Client): If a request fails for other reasons (e.g., malformed JSON, internal server error), a generic error is sent:
{
"type": "ERROR",
"code": "<appropriate_error_code>", // e.g., 4000, 5000
"payload": { "detail": "A descriptive error message." },
"requestId": "<client_provided_requestId_if_any>"
}
Security Considerations
Security is a primary focus of the Key/Info Manager. Please be aware of the following:
- Master Password: The security of all encrypted data relies heavily on the strength and secrecy of your
MASTER_PASSWORD. Choose a strong, unique password and protect it diligently. This password is used to derive the data encryption key. - JWT Secret (
JWT_SECRET): This secret is used to sign JSON Web Tokens for Admin UI sessions. It must be kept confidential. If compromised, attackers could forge admin session tokens. Ensure it's a long, random string. - Session Secret (
SESSION_SECRET): Used by Express session middleware, which is crucial for CSRF protection. This also needs to be a long, random, and unique string kept confidential. - Data Encryption: Secrets are encrypted at rest using AES-256-GCM. The salt used for key derivation is stored in
data/masterkey.salt, and the encrypted data is indata/secrets.json.enc. Protect these files appropriately. - HTTPS/WSS: For any production deployment, it is critical to run both the HTTP Admin UI and the WebSocket server over HTTPS and WSS respectively. This protects data in transit, including the master password during admin login, session cookies, and any secrets transmitted over WebSockets. This application setup does not include HTTPS/WSS by default; you will need to configure this using a reverse proxy (like Nginx or Caddy) or other methods.
- CSRF Protection: The Admin UI uses
csurfto protect against Cross-Site Request Forgery attacks on state-changing POST requests. - Security Headers:
helmetis used to set various HTTP security headers, providing an additional layer of defense against common web vulnerabilities. - Rate Limiting: Both the HTTP admin interface and the WebSocket server implement rate limiting to protect against brute-force attacks and denial-of-service attempts. These limits are configurable via environment variables (see "Getting Started & Installation").
- Input Validation: While the application includes input validation, continuously reviewing and hardening validation logic is good practice, especially for all data received from clients or the Admin UI.
- Principle of Least Privilege: Clients should only be associated with secret groups containing the specific secrets they absolutely need. Avoid overly broad permissions.
- Environment Variables: Never commit your
.envfile (or any file containing actual secrets) to version control. Use environment variables for deploying sensitive configurations in production.
Testing
Basic unit tests for some core components (like DataManager) might be available.
- Run tests:
npm test - Watch mode for tests:
npm run test:watch
For end-to-end testing, manual testing of the Admin UI is recommended. The client-example.html file (if provided in the repository) can be used for basic WebSocket client interaction testing.
Project Structure Overview
key_manager/
├── data/ # Encrypted data, salt, runtime config (gitignored)
├── dist/ # Compiled JavaScript output from TypeScript
├── node_modules/ # Project dependencies (gitignored)
├── public/ # Static assets (e.g., CSS)
│ └── css/
│ └── admin_styles.css
├── src/ # TypeScript source files
│ ├── http/ # HTTP server (Express) and Admin UI logic
│ ├── lib/ # Core libraries (dataManager, configManager, encryption)
│ ├── websocket/ # WebSocket server logic
│ └── main.ts # Main application entry point
├── views/ # EJS templates for Admin UI
├── .env # Local environment variables (gitignored)
├── .gitignore
├── client-example.html # Example WebSocket client for testing (if present)
├── jest.config.js
├── package-lock.json
├── package.json
└── tsconfig.json
Contributing
Contributions are welcome! Please feel free to open an issue or submit a pull request. (Further details can be added here, e.g., coding standards, branch strategy, if the project grows.)
License
This project is licensed under the ISC License. See the LICENSE file (if one exists) or package.json for more details.