mirror of
https://github.com/jeffcaldwellca/mkcertWeb.git
synced 2026-04-29 02:29:10 -05:00
revert docker details for now, add root ca gen
This commit is contained in:
@@ -18,12 +18,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Health check configuration for container monitoring
|
||||
- Non-root user security implementation in containers
|
||||
- Environment variable support for all configuration options
|
||||
- Automatic Root CA generation when none exists
|
||||
- Manual Root CA generation option with user-friendly interface
|
||||
- Visual indicators for auto-generated Root CAs
|
||||
- New API endpoint `/api/generate-ca` for manual CA creation
|
||||
|
||||
### Changed
|
||||
- Updated .gitignore to exclude Docker-related build files
|
||||
- Enhanced package.json with Docker-related scripts
|
||||
- Optimized .dockerignore for efficient Docker builds
|
||||
- Cleaned up unused backup and development files
|
||||
- **Docker**: Added OpenSSL to container for full certificate functionality
|
||||
|
||||
### Fixed
|
||||
- **Docker**: OpenSSL now included in container for certificate analysis and operations
|
||||
|
||||
### Removed
|
||||
- Unused backup files
|
||||
|
||||
@@ -4,36 +4,91 @@ This document provides comprehensive instructions for running mkcert Web UI usin
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Option 1: Docker Run (Simple)
|
||||
### Recommended Method: Docker Compose
|
||||
|
||||
Run the application with default settings:
|
||||
The repository includes a pre-configured `docker-compose.yml` file for easy deployment:
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/jeffcaldwellca/mkcertWeb.git
|
||||
cd mkcertWeb
|
||||
|
||||
# Start the application using the included docker-compose.yml
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
That's it! The application will be available at:
|
||||
- **HTTP**: http://localhost:3000
|
||||
- **HTTPS**: http://localhost:3443 (if enabled)
|
||||
|
||||
### Alternative: Manual Docker Run
|
||||
|
||||
If you prefer to run Docker commands manually:
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/jeffcaldwellca/mkcertWeb.git
|
||||
cd mkcertWeb
|
||||
|
||||
# Build the image
|
||||
docker build -t mkcert-web-ui .
|
||||
|
||||
# Run the application
|
||||
docker run -d \
|
||||
--name mkcert-web-ui \
|
||||
-p 3000:3000 \
|
||||
-v mkcert_certificates:/app/certificates \
|
||||
-v mkcert_data:/app/data \
|
||||
jeffcaldwellca/mkcert-web-ui:latest
|
||||
```
|
||||
|
||||
### Option 2: Docker Compose (Recommended)
|
||||
|
||||
1. Download the docker-compose.yml file:
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/jeffcaldwellca/mkcertWeb/main/docker-compose.yml
|
||||
```
|
||||
|
||||
2. Start the application:
|
||||
```bash
|
||||
docker-compose up -d
|
||||
mkcert-web-ui
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### Environment Variables
|
||||
The included `docker-compose.yml` file provides sensible defaults and can be customized for your needs.
|
||||
|
||||
You can customize the application behavior using environment variables:
|
||||
### Using the Included Docker Compose File
|
||||
|
||||
#### Default Configuration
|
||||
The included configuration provides:
|
||||
- HTTP server on port 3000
|
||||
- HTTPS server on port 3443 (disabled by default)
|
||||
- Dark theme as default
|
||||
- Authentication disabled (for easy development)
|
||||
- Persistent volumes for certificates and data
|
||||
- Automatic restart on failure
|
||||
- Health monitoring
|
||||
|
||||
#### Customizing Environment Variables
|
||||
|
||||
You can override any environment variable by creating a `.env` file in the repository root:
|
||||
|
||||
```bash
|
||||
# Create a .env file to customize settings
|
||||
cat > .env << EOF
|
||||
ENABLE_AUTH=true
|
||||
AUTH_USERNAME=myuser
|
||||
AUTH_PASSWORD=mysecurepassword
|
||||
DEFAULT_THEME=light
|
||||
ENABLE_HTTPS=true
|
||||
SSL_DOMAIN=myapp.local
|
||||
EOF
|
||||
|
||||
# Start with custom configuration
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
#### Direct Environment Variable Override
|
||||
|
||||
You can also override environment variables directly:
|
||||
|
||||
```bash
|
||||
# Override specific settings
|
||||
ENABLE_AUTH=true AUTH_USERNAME=admin docker-compose up -d
|
||||
```
|
||||
|
||||
### Manual Docker Run Examples
|
||||
|
||||
If you prefer not to use the included docker-compose.yml file:
|
||||
|
||||
#### Basic Configuration
|
||||
```bash
|
||||
@@ -43,7 +98,7 @@ docker run -d \
|
||||
-e "DEFAULT_THEME=light" \
|
||||
-e "NODE_ENV=production" \
|
||||
-v mkcert_certificates:/app/certificates \
|
||||
jeffcaldwellca/mkcert-web-ui:latest
|
||||
mkcert-web-ui
|
||||
```
|
||||
|
||||
#### With Authentication
|
||||
@@ -56,7 +111,7 @@ docker run -d \
|
||||
-e "AUTH_PASSWORD=mysecurepassword" \
|
||||
-e "SESSION_SECRET=your-very-long-random-secret-key" \
|
||||
-v mkcert_certificates:/app/certificates \
|
||||
jeffcaldwellca/mkcert-web-ui:latest
|
||||
mkcert-web-ui
|
||||
```
|
||||
|
||||
#### With HTTPS
|
||||
@@ -68,7 +123,7 @@ docker run -d \
|
||||
-e "ENABLE_HTTPS=true" \
|
||||
-e "SSL_DOMAIN=your-domain.com" \
|
||||
-v mkcert_certificates:/app/certificates \
|
||||
jeffcaldwellca/mkcert-web-ui:latest
|
||||
mkcert-web-ui
|
||||
```
|
||||
|
||||
### Available Environment Variables
|
||||
@@ -87,75 +142,97 @@ docker run -d \
|
||||
| `AUTH_PASSWORD` | `admin` | Password for authentication |
|
||||
| `SESSION_SECRET` | `mkcert-web-ui-secret-key-change-in-production` | Session secret |
|
||||
|
||||
## Volume Mounts
|
||||
## Docker Compose Management
|
||||
|
||||
### Required Volumes
|
||||
### Basic Commands
|
||||
|
||||
- **Certificates**: `/app/certificates` - Stores generated SSL certificates
|
||||
- **Data**: `/app/data` - Stores application data and configuration
|
||||
|
||||
### Example with Custom Directories
|
||||
```bash
|
||||
docker run -d \
|
||||
--name mkcert-web-ui \
|
||||
-p 3000:3000 \
|
||||
-v /host/path/to/certificates:/app/certificates \
|
||||
-v /host/path/to/data:/app/data \
|
||||
jeffcaldwellca/mkcert-web-ui:latest
|
||||
# Start the application
|
||||
docker-compose up -d
|
||||
|
||||
# View logs
|
||||
docker-compose logs -f
|
||||
|
||||
# Stop the application
|
||||
docker-compose down
|
||||
|
||||
# Restart the application
|
||||
docker-compose restart
|
||||
|
||||
# Update and rebuild
|
||||
docker-compose down
|
||||
docker-compose up -d --build
|
||||
|
||||
# View status
|
||||
docker-compose ps
|
||||
```
|
||||
|
||||
### Data Persistence
|
||||
|
||||
The included docker-compose.yml automatically creates and manages:
|
||||
- **mkcert_certificates**: Stores all generated SSL certificates
|
||||
- **mkcert_data**: Stores application data and configuration
|
||||
|
||||
```bash
|
||||
# View volume information
|
||||
docker volume ls | grep mkcert
|
||||
|
||||
# Backup certificates
|
||||
docker run --rm -v mkcert_certificates:/data -v $(pwd):/backup alpine tar czf /backup/certificates-backup.tar.gz -C /data .
|
||||
|
||||
# Restore certificates
|
||||
docker run --rm -v mkcert_certificates:/data -v $(pwd):/backup alpine tar xzf /backup/certificates-backup.tar.gz -C /data
|
||||
```
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### Recommended Production Setup
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name mkcert-web-ui \
|
||||
--restart unless-stopped \
|
||||
-p 3000:3000 \
|
||||
-p 3443:3443 \
|
||||
-e "NODE_ENV=production" \
|
||||
-e "ENABLE_HTTPS=true" \
|
||||
-e "FORCE_HTTPS=true" \
|
||||
-e "SSL_DOMAIN=your-domain.com" \
|
||||
-e "ENABLE_AUTH=true" \
|
||||
-e "AUTH_USERNAME=yourusername" \
|
||||
-e "AUTH_PASSWORD=yoursecurepassword" \
|
||||
-e "SESSION_SECRET=$(openssl rand -base64 32)" \
|
||||
-e "DEFAULT_THEME=light" \
|
||||
-v mkcert_certificates:/app/certificates \
|
||||
-v mkcert_data:/app/data \
|
||||
jeffcaldwellca/mkcert-web-ui:latest
|
||||
```
|
||||
|
||||
### Using Docker Compose for Production
|
||||
|
||||
Create a `.env` file:
|
||||
The included docker-compose.yml can be easily configured for production:
|
||||
|
||||
```bash
|
||||
# Production Configuration
|
||||
# Create a production .env file
|
||||
cat > .env << EOF
|
||||
NODE_ENV=production
|
||||
ENABLE_HTTPS=true
|
||||
FORCE_HTTPS=true
|
||||
SSL_DOMAIN=your-domain.com
|
||||
|
||||
# Authentication
|
||||
ENABLE_AUTH=true
|
||||
AUTH_USERNAME=yourusername
|
||||
AUTH_PASSWORD=yoursecurepassword
|
||||
SESSION_SECRET=your-very-long-random-secret-key
|
||||
|
||||
# Theme
|
||||
SESSION_SECRET=$(openssl rand -base64 32)
|
||||
DEFAULT_THEME=light
|
||||
EOF
|
||||
|
||||
# Deploy to production
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
Then run:
|
||||
### Production Checklist
|
||||
|
||||
- ✅ Set strong `AUTH_USERNAME` and `AUTH_PASSWORD`
|
||||
- ✅ Generate secure `SESSION_SECRET`
|
||||
- ✅ Enable HTTPS with your domain
|
||||
- ✅ Configure proper SSL_DOMAIN
|
||||
- ✅ Set NODE_ENV=production
|
||||
- ✅ Enable authentication
|
||||
- ✅ Configure reverse proxy if needed
|
||||
|
||||
## Building and Running
|
||||
|
||||
### Using the Included Docker Compose (Recommended)
|
||||
|
||||
The repository includes everything needed:
|
||||
|
||||
```bash
|
||||
docker-compose --env-file .env up -d
|
||||
git clone https://github.com/jeffcaldwellca/mkcertWeb.git
|
||||
cd mkcertWeb
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## Building from Source
|
||||
### Manual Build Process
|
||||
|
||||
If you want to build the Docker image yourself:
|
||||
If you need to build manually:
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
@@ -165,7 +242,7 @@ cd mkcertWeb
|
||||
# Build the image
|
||||
docker build -t mkcert-web-ui .
|
||||
|
||||
# Run your custom build
|
||||
# Run your build
|
||||
docker run -d \
|
||||
--name mkcert-web-ui \
|
||||
-p 3000:3000 \
|
||||
@@ -175,39 +252,97 @@ docker run -d \
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Check Container Logs
|
||||
### Docker Compose Commands
|
||||
|
||||
```bash
|
||||
docker logs mkcert-web-ui
|
||||
# Check container status
|
||||
docker-compose ps
|
||||
|
||||
# View application logs
|
||||
docker-compose logs -f mkcert-web-ui
|
||||
|
||||
# Access container shell
|
||||
docker-compose exec mkcert-web-ui /bin/sh
|
||||
|
||||
# Restart the service
|
||||
docker-compose restart mkcert-web-ui
|
||||
|
||||
# Stop and remove everything
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
### Access Container Shell
|
||||
### Manual Docker Commands
|
||||
|
||||
```bash
|
||||
# Check container logs
|
||||
docker logs mkcert-web-ui
|
||||
|
||||
# Access container shell
|
||||
docker exec -it mkcert-web-ui /bin/sh
|
||||
```
|
||||
|
||||
### Health Check
|
||||
The container includes a health check that verifies the application is responding:
|
||||
The included docker-compose.yml has health monitoring built-in:
|
||||
```bash
|
||||
docker inspect --format='{{.State.Health.Status}}' mkcert-web-ui
|
||||
# Check health status
|
||||
docker-compose ps
|
||||
|
||||
# View detailed health information
|
||||
docker inspect mkcertWeb_mkcert-web-ui_1 | grep -A 5 Health
|
||||
```
|
||||
|
||||
### Port Conflicts
|
||||
If port 3000 is already in use:
|
||||
If port 3000 is already in use, modify the docker-compose.yml or use environment override:
|
||||
```bash
|
||||
# Edit docker-compose.yml ports section, or:
|
||||
# Override ports with environment variable
|
||||
PORT=8080 docker-compose up -d
|
||||
|
||||
# Or edit the docker-compose.yml file to change:
|
||||
# ports:
|
||||
# - "8080:3000" # HTTP port
|
||||
```
|
||||
|
||||
For manual docker run:
|
||||
```bash
|
||||
docker run -d \
|
||||
--name mkcert-web-ui \
|
||||
-p 8080:3000 \
|
||||
jeffcaldwellca/mkcert-web-ui:latest
|
||||
mkcert-web-ui
|
||||
```
|
||||
|
||||
### Persistence Issues
|
||||
Ensure volumes are properly mounted to persist certificates and data:
|
||||
The docker-compose.yml automatically handles volume persistence. To verify:
|
||||
```bash
|
||||
# Check volume mounts
|
||||
docker inspect mkcert-web-ui | grep -A 10 "Mounts"
|
||||
docker-compose config
|
||||
|
||||
# List volumes
|
||||
docker volume ls | grep mkcert
|
||||
|
||||
# Inspect volume details
|
||||
docker volume inspect mkcertWeb_mkcert_certificates
|
||||
docker volume inspect mkcertWeb_mkcert_data
|
||||
```
|
||||
|
||||
### Missing Dependencies
|
||||
The Docker image includes all required dependencies:
|
||||
- **mkcert**: Pre-installed for certificate generation
|
||||
- **OpenSSL**: Included for certificate analysis and operations
|
||||
- **Node.js**: Runtime environment
|
||||
- **Alpine Linux**: Minimal base image
|
||||
|
||||
If you encounter issues, verify the container has the required tools:
|
||||
```bash
|
||||
# Check mkcert (using docker-compose)
|
||||
docker-compose exec mkcert-web-ui mkcert -help
|
||||
|
||||
# Check OpenSSL (using docker-compose)
|
||||
docker-compose exec mkcert-web-ui openssl version
|
||||
|
||||
# For manual docker run:
|
||||
docker exec mkcert-web-ui mkcert -help
|
||||
docker exec mkcert-web-ui openssl version
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
@@ -220,26 +355,55 @@ docker volume ls | grep mkcert
|
||||
|
||||
## Examples
|
||||
|
||||
### Development Setup
|
||||
### Quick Development Setup
|
||||
```bash
|
||||
docker run -d \
|
||||
--name mkcert-web-ui-dev \
|
||||
-p 3000:3000 \
|
||||
-e "NODE_ENV=development" \
|
||||
-e "DEFAULT_THEME=dark" \
|
||||
-v mkcert_certificates:/app/certificates \
|
||||
jeffcaldwellca/mkcert-web-ui:latest
|
||||
git clone https://github.com/jeffcaldwellca/mkcertWeb.git
|
||||
cd mkcertWeb
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### Production Setup with Authentication
|
||||
```bash
|
||||
git clone https://github.com/jeffcaldwellca/mkcertWeb.git
|
||||
cd mkcertWeb
|
||||
|
||||
# Create production configuration
|
||||
cat > .env << EOF
|
||||
ENABLE_AUTH=true
|
||||
AUTH_USERNAME=admin
|
||||
AUTH_PASSWORD=$(openssl rand -base64 12)
|
||||
SESSION_SECRET=$(openssl rand -base64 32)
|
||||
DEFAULT_THEME=light
|
||||
NODE_ENV=production
|
||||
EOF
|
||||
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### Development with Custom Theme
|
||||
```bash
|
||||
# Override just the theme
|
||||
DEFAULT_THEME=light docker-compose up -d
|
||||
```
|
||||
|
||||
### Reverse Proxy Setup (nginx)
|
||||
```bash
|
||||
docker run -d \
|
||||
--name mkcert-web-ui \
|
||||
--network nginx-proxy \
|
||||
-e "VIRTUAL_HOST=certs.yourdomain.com" \
|
||||
-e "LETSENCRYPT_HOST=certs.yourdomain.com" \
|
||||
-v mkcert_certificates:/app/certificates \
|
||||
jeffcaldwellca/mkcert-web-ui:latest
|
||||
# For use with nginx-proxy, modify docker-compose.yml or create override:
|
||||
version: '3.8'
|
||||
services:
|
||||
mkcert-web-ui:
|
||||
extends:
|
||||
file: docker-compose.yml
|
||||
service: mkcert-web-ui
|
||||
networks:
|
||||
- nginx-proxy
|
||||
environment:
|
||||
- VIRTUAL_HOST=certs.yourdomain.com
|
||||
- LETSENCRYPT_HOST=certs.yourdomain.com
|
||||
|
||||
networks:
|
||||
nginx-proxy:
|
||||
external: true
|
||||
```
|
||||
|
||||
For more information, see the main [README.md](README.md) file.
|
||||
|
||||
@@ -4,6 +4,7 @@ FROM node:18-alpine
|
||||
# Install mkcert and other required tools
|
||||
RUN apk add --no-cache \
|
||||
ca-certificates \
|
||||
openssl \
|
||||
wget \
|
||||
&& wget -O /usr/local/bin/mkcert https://github.com/FiloSottile/mkcert/releases/latest/download/mkcert-v1.4.4-linux-amd64 \
|
||||
&& chmod +x /usr/local/bin/mkcert
|
||||
|
||||
@@ -159,6 +159,16 @@ async function loadSystemStatus() {
|
||||
try {
|
||||
const status = await apiRequest('/status');
|
||||
|
||||
// Show notification if CA was auto-generated
|
||||
if (status.autoGenerated) {
|
||||
showAlert(
|
||||
'<strong>Root CA Auto-Generated!</strong><br>' +
|
||||
'A new Root Certificate Authority was automatically created and installed.<br>' +
|
||||
'<em>Location:</em> ' + (status.caRoot || 'Default location'),
|
||||
'success'
|
||||
);
|
||||
}
|
||||
|
||||
// Create status indicators HTML
|
||||
const statusHtml =
|
||||
'<div class="status-info">' +
|
||||
@@ -169,6 +179,7 @@ async function loadSystemStatus() {
|
||||
'<div class="status-item">' +
|
||||
'<i class="fas fa-' + (status.caExists ? 'shield-alt' : 'exclamation-triangle') + '"></i>' +
|
||||
'<span id="ca-status" class="status-indicator ' + (status.caExists ? 'status-success' : 'status-error') + '">Root CA ' + (status.caExists ? 'exists' : 'missing') + '</span>' +
|
||||
(status.autoGenerated ? '<span class="auto-generated-badge"><i class="fas fa-magic"></i> Auto-generated</span>' : '') +
|
||||
'</div>' +
|
||||
'<div class="status-item">' +
|
||||
'<i class="fas fa-' + (status.opensslAvailable ? 'key' : 'times-circle') + '"></i>' +
|
||||
@@ -190,6 +201,9 @@ async function loadSystemStatus() {
|
||||
// Load Root CA information if CA exists
|
||||
if (status.caExists) {
|
||||
await loadRootCAInfo();
|
||||
} else {
|
||||
// Show manual generation option if auto-generation failed
|
||||
showManualCAGeneration();
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
@@ -275,6 +289,11 @@ async function loadRootCAInfo() {
|
||||
const rootCAInfo = document.getElementById('rootca-info');
|
||||
if (rootCAInfo) {
|
||||
rootCAInfo.innerHTML = rootCAHtml;
|
||||
// Add highlight effect to show the section was updated
|
||||
rootCAInfo.classList.add('ca-updated');
|
||||
setTimeout(() => {
|
||||
rootCAInfo.classList.remove('ca-updated');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Show the Root CA section
|
||||
@@ -568,6 +587,96 @@ async function restoreCertificate(folder, certName) {
|
||||
}
|
||||
}
|
||||
|
||||
// Show manual CA generation option
|
||||
function showManualCAGeneration() {
|
||||
const rootCASection = document.getElementById('rootca-section');
|
||||
if (rootCASection) {
|
||||
rootCASection.style.display = 'block';
|
||||
|
||||
const rootCAInfo = document.getElementById('rootca-info');
|
||||
if (rootCAInfo) {
|
||||
rootCAInfo.innerHTML =
|
||||
'<div class="ca-missing-info">' +
|
||||
'<div class="warning-message">' +
|
||||
'<i class="fas fa-exclamation-triangle"></i>' +
|
||||
'<h3>Root CA Not Found</h3>' +
|
||||
'<p>A Root Certificate Authority (CA) is required to generate SSL certificates. You can generate one now.</p>' +
|
||||
'</div>' +
|
||||
'<div class="ca-generation-actions">' +
|
||||
'<button id="generate-ca-btn" class="btn btn-primary">' +
|
||||
'<i class="fas fa-magic"></i> Generate Root CA' +
|
||||
'</button>' +
|
||||
'<div class="ca-info-text">' +
|
||||
'<p><strong>What this does:</strong></p>' +
|
||||
'<ul>' +
|
||||
'<li>Creates a new Root Certificate Authority</li>' +
|
||||
'<li>Installs it in your system trust store</li>' +
|
||||
'<li>Enables certificate generation for local development</li>' +
|
||||
'</ul>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
|
||||
// Attach event listener for generate CA button
|
||||
const generateBtn = document.getElementById('generate-ca-btn');
|
||||
if (generateBtn) {
|
||||
generateBtn.addEventListener('click', handleGenerateCA);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle manual CA generation
|
||||
async function handleGenerateCA() {
|
||||
const generateBtn = document.getElementById('generate-ca-btn');
|
||||
if (generateBtn) {
|
||||
generateBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Generating Root CA...';
|
||||
generateBtn.disabled = true;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await apiRequest('/generate-ca', {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
// Show success notification
|
||||
showNotification(result.message, 'success');
|
||||
|
||||
// Add a small delay to ensure the CA is fully created
|
||||
setTimeout(async () => {
|
||||
// Force reload the entire system status and CA information
|
||||
await loadSystemStatus();
|
||||
|
||||
// Show a more detailed success message
|
||||
showAlert(
|
||||
'<strong>Root CA Generated Successfully!</strong><br>' +
|
||||
'Your new Root Certificate Authority is now ready to generate SSL certificates.<br>' +
|
||||
'<em>CA Root Path:</em> ' + (result.caRoot || 'Default location') +
|
||||
(result.caInfo && result.caInfo.expiry ? '<br><em>Valid Until:</em> ' + new Date(result.caInfo.expiry).toLocaleDateString() : ''),
|
||||
'success'
|
||||
);
|
||||
|
||||
// Scroll to the Root CA section to show the new information
|
||||
const rootCASection = document.getElementById('rootca-section');
|
||||
if (rootCASection) {
|
||||
rootCASection.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
}, 1000); // 1 second delay to ensure backend processing is complete
|
||||
|
||||
} else {
|
||||
showAlert('Failed to generate CA: ' + result.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showAlert('Failed to generate CA: ' + error.message, 'error');
|
||||
} finally {
|
||||
if (generateBtn) {
|
||||
generateBtn.innerHTML = '<i class="fas fa-magic"></i> Generate Root CA';
|
||||
generateBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CA Installation
|
||||
async function handleInstallCA() {
|
||||
installCaBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Installing...';
|
||||
|
||||
@@ -348,6 +348,118 @@ section h2::before {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.btn-group .btn {
|
||||
min-width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
/* CA Missing and Generation Styles */
|
||||
.ca-missing-info {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.warning-message {
|
||||
background: var(--card-bg-secondary);
|
||||
border: 2px solid var(--warning-color);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.warning-message i {
|
||||
color: var(--warning-color);
|
||||
font-size: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
text-shadow: 0 0 8px rgba(255, 193, 7, 0.4);
|
||||
}
|
||||
|
||||
.warning-message h3 {
|
||||
color: var(--warning-color);
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.warning-message p {
|
||||
color: var(--text-color);
|
||||
margin-bottom: 0;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.ca-generation-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.ca-info-text {
|
||||
max-width: 500px;
|
||||
text-align: left;
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.ca-info-text p {
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--success-color);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.ca-info-text ul {
|
||||
margin: 0;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
.ca-info-text li {
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--text-color);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Auto-generated badge */
|
||||
.auto-generated-badge {
|
||||
display: inline-block;
|
||||
background: linear-gradient(135deg, var(--success-color), var(--primary-color));
|
||||
color: var(--dark-bg);
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 12px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
margin-left: 0.5rem;
|
||||
text-shadow: none;
|
||||
box-shadow: 0 0 8px rgba(64, 224, 208, 0.3);
|
||||
animation: pulse-glow 2s ease-in-out;
|
||||
}
|
||||
|
||||
.auto-generated-badge i {
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
/* Animation for newly generated CA */
|
||||
@keyframes pulse-glow {
|
||||
0% { box-shadow: 0 0 8px rgba(64, 224, 208, 0.3); }
|
||||
50% { box-shadow: 0 0 20px rgba(64, 224, 208, 0.8); }
|
||||
100% { box-shadow: 0 0 8px rgba(64, 224, 208, 0.3); }
|
||||
}
|
||||
|
||||
/* Highlight effect for newly updated sections */
|
||||
.ca-updated {
|
||||
animation: section-highlight 3s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes section-highlight {
|
||||
0% { background: var(--card-bg); }
|
||||
20% { background: rgba(64, 224, 208, 0.1); border: 1px solid rgba(64, 224, 208, 0.3); }
|
||||
100% { background: var(--card-bg); }
|
||||
}
|
||||
|
||||
/* Forms */
|
||||
|
||||
@@ -220,7 +220,23 @@ app.get('/api/status', requireAuth, async (req, res) => {
|
||||
const caKeyPath = path.join(caRoot, 'rootCA-key.pem');
|
||||
const caCertPath = path.join(caRoot, 'rootCA.pem');
|
||||
|
||||
const caExists = await fs.pathExists(caKeyPath) && await fs.pathExists(caCertPath);
|
||||
let caExists = await fs.pathExists(caKeyPath) && await fs.pathExists(caCertPath);
|
||||
|
||||
// Auto-generate CA if it doesn't exist
|
||||
let autoGenerated = false;
|
||||
if (!caExists) {
|
||||
try {
|
||||
console.log('Root CA not found, attempting to generate...');
|
||||
await executeCommand('mkcert -install');
|
||||
caExists = await fs.pathExists(caKeyPath) && await fs.pathExists(caCertPath);
|
||||
autoGenerated = caExists;
|
||||
if (autoGenerated) {
|
||||
console.log('Root CA auto-generated successfully');
|
||||
}
|
||||
} catch (generateError) {
|
||||
console.error('Failed to auto-generate Root CA:', generateError.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if OpenSSL is available
|
||||
let opensslAvailable = false;
|
||||
@@ -237,7 +253,8 @@ app.get('/api/status', requireAuth, async (req, res) => {
|
||||
caExists,
|
||||
caCertPath: caExists ? caCertPath : null,
|
||||
mkcertInstalled: true,
|
||||
opensslAvailable
|
||||
opensslAvailable,
|
||||
autoGenerated
|
||||
});
|
||||
} catch (error) {
|
||||
res.json({
|
||||
@@ -266,6 +283,94 @@ app.post('/api/install-ca', requireAuth, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Generate new Root CA (mkcert -install creates a new CA if one doesn't exist)
|
||||
app.post('/api/generate-ca', requireAuth, async (req, res) => {
|
||||
try {
|
||||
// First check if mkcert is available
|
||||
try {
|
||||
await executeCommand('mkcert -help');
|
||||
} catch (helpError) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
error: 'mkcert is not installed or not available in PATH'
|
||||
});
|
||||
}
|
||||
|
||||
// Get current CA root directory
|
||||
let caRoot;
|
||||
try {
|
||||
const caRootResult = await executeCommand('mkcert -CAROOT');
|
||||
caRoot = caRootResult.stdout.trim();
|
||||
} catch (caRootError) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
error: 'Failed to get mkcert CA root directory'
|
||||
});
|
||||
}
|
||||
|
||||
// Check if CA already exists
|
||||
const caKeyPath = path.join(caRoot, 'rootCA-key.pem');
|
||||
const caCertPath = path.join(caRoot, 'rootCA.pem');
|
||||
const caExists = await fs.pathExists(caKeyPath) && await fs.pathExists(caCertPath);
|
||||
|
||||
if (caExists) {
|
||||
return res.json({
|
||||
success: true,
|
||||
message: 'Root CA already exists',
|
||||
caRoot,
|
||||
caExists: true,
|
||||
action: 'none'
|
||||
});
|
||||
}
|
||||
|
||||
// Generate new CA by running mkcert -install
|
||||
// This will create a new CA if one doesn't exist
|
||||
const installResult = await executeCommand('mkcert -install');
|
||||
|
||||
// Verify CA was created
|
||||
const newCaExists = await fs.pathExists(caKeyPath) && await fs.pathExists(caCertPath);
|
||||
|
||||
if (!newCaExists) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
error: 'Failed to generate Root CA - files not found after installation'
|
||||
});
|
||||
}
|
||||
|
||||
// Get CA information
|
||||
let caInfo = {};
|
||||
try {
|
||||
const caResult = await executeCommand(`openssl x509 -in "${caCertPath}" -noout -subject -issuer -dates`);
|
||||
caInfo.details = caResult.stdout;
|
||||
|
||||
// Extract expiry date
|
||||
const expiryMatch = caResult.stdout.match(/notAfter=(.+)/);
|
||||
if (expiryMatch) {
|
||||
caInfo.expiry = new Date(expiryMatch[1]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Could not read CA info with OpenSSL (this is optional)');
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Root CA generated and installed successfully',
|
||||
caRoot,
|
||||
caExists: true,
|
||||
caInfo,
|
||||
action: 'generated',
|
||||
output: installResult.stdout
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error.error || error.message,
|
||||
details: error.stderr
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Download Root CA certificate
|
||||
app.get('/api/download/rootca', requireAuth, async (req, res) => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user