From f05ebf2ae754c3e59e6d68833819035fac462fae Mon Sep 17 00:00:00 2001 From: TolaMironcenko Date: Fri, 25 Jul 2025 18:48:12 +0700 Subject: [PATCH 1/2] updated certs endpoint and delete certs/reate certs/delete endpoints --- api/Certs.go | 62 ++++++---------------------------- api/CreateCert.go | 4 --- api/DeleteCert.go | 4 --- api/GetCerts.go | 57 +++++++++++++++++++++++++++++++ api/frontend/js/certs.js | 4 +-- api/frontend/js/create_cert.js | 2 +- server/Configure.go | 2 -- 7 files changed, 71 insertions(+), 64 deletions(-) create mode 100644 api/GetCerts.go diff --git a/api/Certs.go b/api/Certs.go index b06fa90..9a62fd2 100644 --- a/api/Certs.go +++ b/api/Certs.go @@ -1,61 +1,21 @@ package api import ( - "encoding/json" - "fmt" "net/http" - "os" - "scm/crt" - - "github.com/google/uuid" ) func Certs(w http.ResponseWriter, r *http.Request) { - if r.Method == "POST" { + switch r.Method { + case "GET": + GetCerts(w, r) + return + case "POST": + CreateCert(w, r) + return + case "DELETE": + DeleteCert(w, r) + return + default: http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return } - var certs_info []map[string]any - entires, err := os.ReadDir(os.Getenv("CERTS_PATH")) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - for _, entry := range entires { - if entry.IsDir() { - if entry.Name() == ".git" || entry.Name() == "root" { - continue - } - id, err := uuid.NewRandom() - if err != nil { - fmt.Fprintf(w, "{\"error\":\"%v\"}", err) - return - } - certdata, err := os.ReadFile(fmt.Sprintf("%s/%s/%s.crt", os.Getenv("CERTS_PATH"), entry.Name(), entry.Name())) - if err != nil { - fmt.Fprintf(w, "{\"error\":\"%v\"}", err) - return - } - keydata, err := os.ReadFile(fmt.Sprintf("%s/%s/%s.key", os.Getenv("CERTS_PATH"), entry.Name(), entry.Name())) - if err != nil { - fmt.Fprintf(w, "{\"error\":\"%v\"}", err) - return - } - new_cert := make(map[string]any) - new_cert["id"] = id.String() - new_cert["domain"] = entry.Name() - new_cert["href"] = fmt.Sprintf("https://%s", entry.Name()) - new_cert["cert"] = string(certdata) - new_cert["key"] = string(keydata) - new_cert["certinfo"] = crt.ParseCert(fmt.Sprintf("%s/%s/%s.crt", os.Getenv("CERTS_PATH"), entry.Name(), entry.Name())) - certs_info = append(certs_info, new_cert) - } - } - jsondata, err := json.Marshal(certs_info) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - w.Header().Set("Content-Type", "application/json") - w.Write(jsondata) } diff --git a/api/CreateCert.go b/api/CreateCert.go index 339d54a..91a7865 100644 --- a/api/CreateCert.go +++ b/api/CreateCert.go @@ -14,10 +14,6 @@ import ( ) func CreateCert(w http.ResponseWriter, r *http.Request) { - if r.Method == "GET" { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } var requestData model.CreateCertRequest if err := json.NewDecoder(r.Body).Decode(&requestData); err != nil { http.Error(w, "invalid JSON format", http.StatusBadRequest) diff --git a/api/DeleteCert.go b/api/DeleteCert.go index 4c7fe1a..e2b926a 100644 --- a/api/DeleteCert.go +++ b/api/DeleteCert.go @@ -9,10 +9,6 @@ import ( ) func DeleteCert(w http.ResponseWriter, r *http.Request) { - if r.Method == "GET" { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } var requestData model.DeleteCertRequest if err := json.NewDecoder(r.Body).Decode(&requestData); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) diff --git a/api/GetCerts.go b/api/GetCerts.go new file mode 100644 index 0000000..f8cb7ad --- /dev/null +++ b/api/GetCerts.go @@ -0,0 +1,57 @@ +package api + +import ( + "encoding/json" + "fmt" + "net/http" + "os" + "scm/crt" + + "github.com/google/uuid" +) + +func GetCerts(w http.ResponseWriter, r *http.Request) { + var certs_info []map[string]any + entires, err := os.ReadDir(os.Getenv("CERTS_PATH")) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + for _, entry := range entires { + if entry.IsDir() { + if entry.Name() == ".git" || entry.Name() == "root" { + continue + } + id, err := uuid.NewRandom() + if err != nil { + fmt.Fprintf(w, "{\"error\":\"%v\"}", err) + return + } + certdata, err := os.ReadFile(fmt.Sprintf("%s/%s/%s.crt", os.Getenv("CERTS_PATH"), entry.Name(), entry.Name())) + if err != nil { + fmt.Fprintf(w, "{\"error\":\"%v\"}", err) + return + } + keydata, err := os.ReadFile(fmt.Sprintf("%s/%s/%s.key", os.Getenv("CERTS_PATH"), entry.Name(), entry.Name())) + if err != nil { + fmt.Fprintf(w, "{\"error\":\"%v\"}", err) + return + } + new_cert := make(map[string]any) + new_cert["id"] = id.String() + new_cert["domain"] = entry.Name() + new_cert["href"] = fmt.Sprintf("https://%s", entry.Name()) + new_cert["cert"] = string(certdata) + new_cert["key"] = string(keydata) + new_cert["certinfo"] = crt.ParseCert(fmt.Sprintf("%s/%s/%s.crt", os.Getenv("CERTS_PATH"), entry.Name(), entry.Name())) + certs_info = append(certs_info, new_cert) + } + } + jsondata, err := json.Marshal(certs_info) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + w.Write(jsondata) +} diff --git a/api/frontend/js/certs.js b/api/frontend/js/certs.js index 8fdaa8e..de873aa 100644 --- a/api/frontend/js/certs.js +++ b/api/frontend/js/certs.js @@ -103,8 +103,8 @@ if (preview) { cards.addEventListener("click", (e) => { if (e.target.classList.contains("delcert")) { if (confirm("Are you sure you want to delete this certificate?")) { - fetch(window.location.origin + `/api/certs/delete`, { - method: "POST", + fetch(window.location.origin + `/api/certs`, { + method: "DELETE", headers: { "Content-Type": "application/json", }, diff --git a/api/frontend/js/create_cert.js b/api/frontend/js/create_cert.js index 434d486..d5e0e19 100644 --- a/api/frontend/js/create_cert.js +++ b/api/frontend/js/create_cert.js @@ -15,7 +15,7 @@ create_certificate_form.addEventListener("submit", (event) => { const passwordValue = password.value; const organizationunitValue = organizationunit.value; - fetch(window.location.origin + "/api/certs/create", { + fetch(window.location.origin + "/api/certs", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/server/Configure.go b/server/Configure.go index 59637b3..c26bf22 100644 --- a/server/Configure.go +++ b/server/Configure.go @@ -10,6 +10,4 @@ func Configure() { http.HandleFunc("/api/certs", api.Certs) http.HandleFunc("/api/certs/root", api.RootCert) http.HandleFunc("/api/certs/download/", api.CertKeyDownloadZip) - http.HandleFunc("/api/certs/create", api.CreateCert) - http.HandleFunc("/api/certs/delete", api.DeleteCert) } From af76bd57ba28d2fe2456d64afdeeca534343b235 Mon Sep 17 00:00:00 2001 From: TolaMironcenko Date: Fri, 25 Jul 2025 18:51:04 +0700 Subject: [PATCH 2/2] Update README.md --- README.md | 155 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 116 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 2465f47..326ae43 100644 --- a/README.md +++ b/README.md @@ -5,55 +5,72 @@ A Go-based SSL Certificate Manager that provides a REST API for managing SSL cer ## Features - **Root CA Management**: Create and manage root Certificate Authority certificates -- **Certificate Management**: Create, list, and delete SSL certificates +- **Certificate Management**: Create, list, delete, and download SSL certificates - **CA Integration**: Uses a root Certificate Authority to sign certificates - **REST API**: Simple HTTP endpoints for certificate operations - **Web Interface**: Frontend for certificate management operations +- **Certificate Download**: Download certificates and keys as ZIP archives - **TLS Support**: Configurable HTTPS server with certificate validation - **Certificate Parsing**: Detailed certificate information extraction - **File System Storage**: Organized certificate storage in directories +- **Docker Support**: Container deployment with Docker Compose ## Project Structure ``` scm/ ├── api/ # HTTP handlers and API endpoints -│ ├── Certs.go # List certificates endpoint +│ ├── CertDownload.go # Certificate download endpoint +│ ├── Certs.go # Certificate operations router │ ├── CreateCert.go # Create certificate endpoint │ ├── DeleteCert.go # Delete certificate endpoint +│ ├── GetCerts.go # List certificates endpoint │ ├── RootCert.go # Root certificate management endpoint │ ├── Spa.go # Single-page application handler │ └── frontend/ # Frontend assets │ ├── css/ # Stylesheets │ ├── js/ # JavaScript files │ └── index.html # Main web interface +├── certs/ # Certificate storage directory ├── crt/ # Certificate operations and utilities │ ├── CreateRootKeyCertificate.go # Root CA creation │ ├── CreateServerCert.go # Server certificate creation │ ├── LoadRootCertAndKey.go # CA certificate loading │ ├── ParseCert.go # Certificate parsing utilities │ └── SavePEMFile.go # PEM file operations +├── data/ # Data storage directory ├── model/ # Data structures and models │ ├── CreateCertRequest.go # Server certificate request model │ ├── CreateRootCertRequest.go # Root certificate request model │ └── DeleteCertRequest.go # Certificate deletion model ├── server/ # Server configuration and setup +│ ├── Configure.go # Route configuration +│ └── Run.go # Server startup ├── utils/ # General utility functions -│ ├── CheckExistsOrCreateDir.go # Directory operations +│ ├── AddFileToZipWithName.go # ZIP file utilities +│ ├── CheckExistsOrCreateDir.go # Directory operations +│ ├── CreateDomainZip.go # Certificate ZIP creation │ └── LoadEnv.go # Environment loading ├── main.go # Application entry point ├── go.mod # Go module definition ├── go.sum # Go module checksums -└── scm.conf # Configuration file +├── scm.conf # Configuration file +├── scm.conf.docker # Docker configuration file +├── Dockerfile # Docker build configuration +├── docker-compose.yml # Docker Compose setup +└── scm.sh # Shell script for operations ``` ## Requirements - Go 1.24.5 or higher - Linux/Unix environment (recommended) +- Docker (optional, for container deployment) ## Installation +### From Source + 1. Clone the repository: ```bash git clone @@ -70,6 +87,19 @@ go mod tidy go build -o scm ``` +### Docker Deployment + +1. Using Docker Compose: +```bash +docker-compose up -d +``` + +2. Using Docker directly: +```bash +docker build -t scm . +docker run -d -p 8777:8777 -v ./certs:/certs scm +``` + ## Configuration Create a configuration file `scm.conf` in the project root: @@ -160,7 +190,7 @@ List all managed certificates with details. ] ``` -### POST /api/certs/create +### POST /api/certs Create a new SSL certificate signed by the root CA. **Request Body:** @@ -182,7 +212,7 @@ Create a new SSL certificate signed by the root CA. } ``` -### POST /api/certs/delete +### DELETE /api/certs Delete an existing certificate. **Request Body:** @@ -192,6 +222,15 @@ Delete an existing certificate. } ``` +### GET /api/certs/download/?domain=example.com +Download certificate and private key as a ZIP archive. + +**Query Parameters:** +- `domain` (required): Domain name of the certificate to download + +**Response:** +- ZIP file containing `domain.crt` and `domain.key` + ### GET / Access the web interface for certificate management. @@ -211,7 +250,7 @@ Access the web interface for certificate management. Before creating server certificates, you need to create a root CA: ```bash -curl -X POST http://localhost:8777/api/rootcert \ +curl -X POST http://localhost:8777/api/certs/root \ -H "Content-Type: application/json" \ -d '{ "CommonName": "My Root CA", @@ -227,7 +266,7 @@ curl -X POST http://localhost:8777/api/rootcert \ 1. Create a certificate: ```bash -curl -X POST http://localhost:8777/api/certs/create \ +curl -X POST http://localhost:8777/api/certs \ -H "Content-Type: application/json" \ -d '{ "commonname": "example.com", @@ -243,9 +282,14 @@ curl -X POST http://localhost:8777/api/certs/create \ curl http://localhost:8777/api/certs ``` -3. Delete a certificate: +3. Download a certificate: ```bash -curl -X POST http://localhost:8777/api/certs/delete \ +curl -O "http://localhost:8777/api/certs/download/?domain=example.com" +``` + +4. Delete a certificate: +```bash +curl -X DELETE http://localhost:8777/api/certs \ -H "Content-Type: application/json" \ -d '{"domain": "example.com"}' ``` @@ -282,6 +326,35 @@ go test ./... go test -v ./... ``` +## Docker Deployment + +### Using Docker Compose + +```yaml +version: '3.8' +services: + scm: + build: . + ports: + - "8777:8777" + volumes: + - ./certs:/certs + - ./data:/data + environment: + - CERTS_PATH=/certs +``` + +### Environment Variables for Docker + +The Docker configuration uses `scm.conf.docker` which should contain: + +```bash +ENABLE_TLS=false +CERTS_PATH=/certs +SERVER_ADDRESS=0.0.0.0 +SERVER_PORT=8777 +``` + ## Security Considerations - **Root CA Security**: Store CA private keys securely and use strong passwords @@ -291,6 +364,7 @@ go test -v ./... - **Input Validation**: Validate input data to prevent injection attacks - **Monitoring**: Monitor certificate expiration dates - **Password Strength**: Use strong passwords for CA key encryption +- **File Permissions**: Ensure proper file system permissions for certificate directories ## File Organization @@ -311,35 +385,11 @@ CERTS_PATH/ ## Workflow 1. **Initial Setup**: Configure the application and create certificates directory -2. **Create Root CA**: Use `/api/rootcert` endpoint to create a root Certificate Authority -3. **Create Server Certificates**: Use `/api/certs/create` to generate server certificates signed by the CA -4. **Manage Certificates**: List, view, and delete certificates as needed +2. **Create Root CA**: Use `/api/certs/root` endpoint to create a root Certificate Authority +3. **Create Server Certificates**: Use `/api/certs` POST to generate server certificates signed by the CA +4. **Manage Certificates**: List, view, download, and delete certificates as needed 5. **Deploy Certificates**: Use the generated certificates in your applications -## Troubleshooting - -### Common Issues - -1. **"Certificate not found"** - Ensure the CERTS_PATH is correctly configured and accessible -2. **"Invalid CA password"** - Verify the CA private key password is correct -3. **"Permission denied"** - Check file system permissions for certificate directories -4. **"Port already in use"** - Change the SERVER_PORT in configuration -5. **"Root certificate not found"** - Create a root CA first using the `/api/rootcert` endpoint - -### Debugging Tips - -- Check application logs for detailed error messages -- Verify certificate directory permissions -- Ensure the configuration file is properly formatted -- Test API endpoints individually to isolate issues - -### Logs - -The application logs to stdout. For production, consider redirecting logs to a file: -```bash -./scm >> /var/log/scm.log 2>&1 -``` - ## Web Interface The application includes a web-based interface accessible at the root URL. The interface provides: @@ -348,8 +398,35 @@ The application includes a web-based interface accessible at the root URL. The i - Certificate creation with form validation - Certificate listing and viewing - Certificate deletion +- Certificate download functionality - Real-time status updates +## Troubleshooting + +### Common Issues + +1. **"Certificate not found"** - Ensure the CERTS_PATH is correctly configured and accessible +2. **"Invalid CA password"** - Verify the CA private key password is correct +3. **"Permission denied"** - Check file system permissions for certificate directories +4. **"Port already in use"** - Change the SERVER_PORT in configuration +5. **"Root certificate not found"** - Create a root CA first using the `/api/certs/root` endpoint +6. **"domain parameter is required"** - Ensure the domain parameter is provided for download requests + +### Debugging Tips + +- Check application logs for detailed error messages +- Verify certificate directory permissions +- Ensure the configuration file is properly formatted +- Test API endpoints individually to isolate issues +- Use the web interface for easier debugging + +### Logs + +The application logs to stdout. For production, consider redirecting logs to a file: +```bash +./scm >> /var/log/scm.log 2>&1 +``` + ## Contributing 1. Fork the repository @@ -365,5 +442,5 @@ This project is licensed under the MIT License. See LICENSE file for details. ## Version History -- **Latest**: Added root certificate management, web interface, and improved project structure -- **Previous**: Basic certificate management with REST API +- **v1.0.0**: Full-featured SSL Certificate Manager with web interface, REST API, and certificate download functionality +- **Previous**: Basic certificate management with REST API \ No newline at end of file