https://github.com/styliteag/ssm
WIP: Securely manage your SSH keys on multiple host from a web UI
https://github.com/styliteag/ssm
authorized-keys manager react rust ssh ssh-key ssh-key-management
Last synced: 5 months ago
JSON representation
WIP: Securely manage your SSH keys on multiple host from a web UI
- Host: GitHub
- URL: https://github.com/styliteag/ssm
- Owner: styliteag
- License: gpl-3.0
- Created: 2024-06-18T11:18:42.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2025-12-01T21:56:56.000Z (7 months ago)
- Last Synced: 2025-12-04T11:18:36.632Z (7 months ago)
- Topics: authorized-keys, manager, react, rust, ssh, ssh-key, ssh-key-management
- Language: TypeScript
- Homepage:
- Size: 2.15 MB
- Stars: 5
- Watchers: 3
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.txt
- Agents: AGENTS.md
Awesome Lists containing this project
README
# Secure SSH Manager (SSM)
> [!NOTE]
> This is pre-release software. Use at your own risk.
A modern web application for managing SSH keys across multiple hosts with a **React frontend** and **Rust API backend**.
## 🚀 Quick Start
### Development Environment
### Prerequisites
- **Rust** (1.75+) with Cargo
- **Node.js** (24+) with npm
- **Docker** (optional, for deployment)
- **htpasswd** utility (for authentication)
- **SSH** private key (for SSH connections)
- **just**
- install just: `cargo install just`
- **watch**
- install watch: `cargo install cargo-watch`
### Initial Setup
Start both frontend and backend development servers:
```bash
./start-dev.sh
```
- **Frontend**: http://localhost:5173 (React + Vite)
- **Backend API**: http://localhost:8000 (Rust + Actix Web)
### Production Deployment
Deploy with Docker:
```bash
docker-compose -f docker/compose.prod.yml up --build
```
- **Application**: http://localhost/ (nginx serves frontend, proxies API)
## 📋 Overview
SSH Key Manager provides a web interface for managing `authorized_keys` files on remote hosts via SSH connections. The application has been refactored from a monolithic Rust application to a modern distributed architecture:
- **Frontend**: React + TypeScript + Tailwind CSS
- **Backend**: Rust + Actix Web REST API
- **Database**: SQLite (with PostgreSQL/MySQL support)
- **Deployment**: Multi-stage Docker build with nginx proxy
- **Authentication**: Session-based with htpasswd integration
## 🏗️ Architecture
### Frontend (`frontend/`)
- **Framework**: React 19 with TypeScript
- **Styling**: Tailwind CSS with custom component library
- **State Management**: Zustand + React Context
- **Routing**: React Router with protected routes
- **Build Tool**: Vite for fast development and production builds
- **API Communication**: Axios with centralized service layer
### Backend (`backend/`)
- **Framework**: Rust + Actix Web
- **Database**: Diesel ORM (SQLite/PostgreSQL/MySQL)
- **Authentication**: Session-based with htpasswd files
- **SSH Client**: russh for remote host connections
- **API Design**: RESTful JSON endpoints with structured responses
### Deployment
- **Multi-stage Docker**: Frontend build → Backend build → Combined runtime
- **Web Server**: nginx serves React app and proxies API requests
- **Single Container**: Simplified deployment with internal service communication
- **Health Checks**: Built-in monitoring and health endpoints
## 🛠️ Development Setup
### Prerequisites
- **Rust** (1.75+) with Cargo
- **Node.js** (24+) with npm
- **Docker** (optional, for deployment)
- **htpasswd** utility (for authentication)
### Initial Setup
1. **Clone the repository**:
```bash
git clone
cd ssm
```
2. **Set up authentication**:
```bash
htpasswd -B -c .htpasswd admin
```
3. **Configure the application**:
Create `config.toml` (see [Configuration](#configuration) section)
4. **Set up the database** (from `backend/` directory):
```bash
cd backend
cargo install diesel_cli --no-default-features --features sqlite
diesel setup
diesel migration run
cd ..
```
5. **Install frontend dependencies**:
```bash
cd frontend
npm install
cd ..
```
### Development Workflow
#### Start Development Servers
```bash
# Start both frontend and backend
./start-dev.sh
# Or start individually:
# Backend (from backend/ directory)
cd backend && cargo run
# Frontend (from frontend/ directory)
cd frontend && npm run dev
```
#### Frontend Development
```bash
cd frontend
# Start dev server
npm run dev
# Build for production
npm run build
# Lint and type check
npm run lint
npm run type-check
```
#### Backend Development
```bash
cd backend
# Run with auto-reload
cargo watch -x run
# Run tests
cargo test
# Database operations
diesel migration run
diesel migration generate
```
## 🐳 Docker Deployment
### Production Deployment
```bash
# Build and start production stack
docker-compose -f docker/compose.prod.yml up --build
# Run in background
docker-compose -f docker/compose.prod.yml up -d --build
```
### Development with Docker
```bash
# Start development stack
docker-compose -f docker/compose.yml up --build
```
### Docker Architecture
The multi-stage build process:
1. **Frontend Build Stage**: Builds React application with Vite
2. **Backend Build Stage**: Compiles Rust application with optimizations
3. **Runtime Stage**: Combines built assets with nginx + Alpine Linux
**Container Structure**:
- nginx serves React frontend from `/usr/share/nginx/html`
- nginx proxies `/api/*` requests to Rust backend on port 8000
- Single container exposes port 80 for all web traffic
- Persistent volumes for database, configuration, and SSH keys
## ⚙️ Configuration
### Main Configuration (`config.toml`) - Optional
The `config.toml` file is optional. If it doesn't exist, the server will use environment variables and built-in defaults.
```toml
# Database URL (SQLite default) - can be overridden by DATABASE_URL env var
# database_url = "sqlite://ssm.db"
# API server configuration
listen = "127.0.0.1"
port = 8000
# Logging level
loglevel = "info"
# htpasswd path - can be overridden by HTPASSWD env var
# htpasswd_path = ".htpasswd"
[ssh]
# Path to private key for SSH connections - can be overridden by SSH_KEY env var
# private_key_file = "/path/to/your/ssh/private/key"
# Optional passphrase
# private_key_passphrase = "your_passphrase_here"
```
### Environment Variables
- `CONFIG` - Path to config file (default: `./config/config.toml`)
- `DATABASE_URL`, `HTPASSWD`, `SSH_KEY`, `SESSION_KEY` - Take precedence over config file settings
- `RUST_LOG` - Logging level (overrides config)
- `VITE_API_URL` - Frontend API URL (for production builds)
### SSH Key Setup
**🔑 IMPORTANT**: The server requires a valid SSH private key file to function. The default path is `keys/id_ssm`. If no `config.toml` exists, set the `SSH_KEY` environment variable:
```bash
SSH_KEY=/path/to/your/private/key cargo run
```
The server will provide detailed instructions for generating an SSH key pair if the file is missing.
**Generating SSH Keys:**
For ed25519 keys (recommended):
```bash
# Generate key pair in the default location
ssh-keygen -t ed25519 -f keys/id_ssm -C 'ssm-server'
# Set proper permissions
chmod 600 keys/id_ssm
chmod 644 keys/id_ssm.pub
```
For RSA keys (alternative):
```bash
ssh-keygen -t rsa -b 4096 -f keys/id_ssm -C 'ssm-server'
```
**Docker Setup:**
In Docker, the SSH key should be placed at `/app/keys/id_ssm` (mounted from `docker/data/keys/id_ssm`).
### Authentication Setup
**🔐 IMPORTANT**: Authentication is required for all API endpoints except login/logout.
**Auto-creation**: If no htpasswd file exists when the server starts, it will automatically create one with a default `admin` user and a randomly generated password (displayed on console).
**Manual creation**: You can also create the htpasswd file manually:
```bash
# Create htpasswd file with bcrypt encryption
htpasswd -cB .htpasswd admin
# Add additional users
htpasswd -B .htpasswd another_user
# Set secure session key for production
export SESSION_KEY="your-super-secret-session-key-change-in-production"
```
**Security Notes:**
- All API requests (except authentication) require session-based authentication
- Session cookies are `HttpOnly` and secure
- bcrypt encryption is used for password storage
- Unauthenticated requests return `401 Unauthorized`
### Docker Environment
When using Docker, place configuration files in `docker/data/`:
```
docker/data/
├── auth/.htpasswd # Authentication file
├── config/config.toml # Main configuration
├── ssh-keys/ # SSH private keys
├── db/ # Database files
└── logs/ # Application logs
```
## 🔧 API Documentation
For detailed API documentation including all endpoints, authentication, and examples, see [API_DOCUMENTATION.md](backend/API_DOCUMENTATION.md).
## 📁 Project Structure
```
ssm/
├── frontend/ # React frontend application
│ ├── src/
│ │ ├── components/ # Reusable React components
│ │ ├── pages/ # Route components
│ │ ├── services/ # API communication
│ │ ├── contexts/ # React contexts
│ │ └── types/ # TypeScript definitions
│ ├── package.json
│ └── vite.config.ts
├── backend/ # Rust API backend
│ ├── src/
│ │ ├── routes/ # API endpoint handlers
│ │ ├── db/ # Database models
│ │ ├── ssh/ # SSH client implementation
│ │ └── api_types.rs # API request/response types
│ ├── migrations/ # Database migrations
│ └── Cargo.toml
├── docker/ # Docker deployment configuration
│ ├── app/Dockerfile # Multi-stage build configuration
│ ├── compose.prod.yml # Production deployment
│ └── data/ # Persistent data volumes
├── start-dev.sh # Development environment startup
├── config.toml # Application configuration
└── README.md
```
## 🔐 Security Features
**🛡️ Comprehensive Security Implementation:**
- **🔒 Required Authentication**: All API endpoints (except login) require session-based authentication
- **🍪 Secure Sessions**: HttpOnly cookies with session signing keys for protection
- **🔐 bcrypt Encryption**: Industry-standard password hashing for user credentials
- **⚡ Session Validation**: Real-time authentication checks on every API request
- **🚫 Unauthorized Access**: 401 responses for unauthenticated requests
- **🔑 SSH Key Security**: Key-based authentication for all remote SSH connections
- **✅ Input Validation**: Comprehensive validation and sanitization of all API inputs
- **🗄️ Database Security**: Prepared statements and foreign key constraints
- **🌐 CORS Configuration**: Controlled cross-origin resource sharing for frontend integration
**Authentication Flow:**
1. Login with `.htpasswd` credentials → Session cookie issued
2. Include session cookie in subsequent API requests
3. Server validates session on every protected endpoint
4. Logout to invalidate session
## 🚫 SSH Key Management Controls
### Host Disabling
- **Disabled Hosts**: Mark hosts as `disabled` to prevent all SSH operations
- **Use Cases**: Maintenance windows, decommissioned servers, temporary disconnection
- **Effects**: No SSH connections, no polling, no diff operations, no syncing
### Readonly Controls
Prevent SSM from modifying keyfiles by creating control files:
- **`.ssh/system_readonly`**: Disables updates for all keyfiles on the host
- **`.ssh/user_readonly`**: Disables updates for specific user keyfiles
Optional: Include a reason in the file that will be displayed in the UI.
## 🤝 Contributing
### Development Guidelines
1. **API-First Development**: Design API endpoints before frontend implementation
2. **Type Safety**: Use TypeScript for frontend and structured types for backend
3. **Component Reusability**: Build modular React components
4. **Error Handling**: Implement comprehensive error handling and user feedback
5. **Testing**: Write tests for both frontend and backend components
### Code Style
- **Frontend**: ESLint + TypeScript for code quality
- **Backend**: `cargo fmt` and `cargo clippy` for Rust code
- **Consistent Naming**: Use clear, descriptive names for variables and functions
### Pull Request Process
1. Fork the repository and create a feature branch
2. Implement changes with appropriate tests
3. Ensure both frontend and backend build successfully
4. Update documentation if needed
5. Submit pull request with clear description
## 📄 License
This project is licensed under GPL-3.0. See LICENSE.txt for details.
## 🔗 Links
- **Repository**: https://github.com/styliteag/ssm
- **Issues**: Report bugs and feature requests
- **Documentation**: Additional documentation in `/docs` (coming soon)
---
For technical implementation details, see [CLAUDE.md](CLAUDE.md) for development guidance.