sslly-nginx

module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 30, 2026 License: MIT

README ΒΆ

sslly-nginx

A smart Nginx SSL reverse proxy manager that automatically configures SSL certificates and proxies traffic to your local applications.

logo

Features

  • πŸ”„ Automatic Configuration: Watches for configuration and SSL certificate changes and automatically reloads Nginx
  • πŸ”’ SSL Management: Automatically scans and maps SSL certificates to domains
  • πŸ” Hot Reload: Updates Nginx configuration without downtime when files change
  • πŸ›‘οΈ Error Recovery: Maintains the last working configuration and rolls back on failures
  • 🐳 Docker Ready: Runs as a containerized service with Docker Compose
  • 🌐 FRP Integration: Easy integration with FRP for secure remote access to local services
  • πŸš€ CI/CD Pipeline: Includes GitHub Actions workflows for testing, building, and releasing

How It Works

sslly-nginx is a Go application that runs inside an Nginx Alpine container and manages the Nginx configuration dynamically:

  1. Configuration Monitoring: Watches ./configs/config.yaml for changes
  2. Certificate Scanning: Recursively scans ./ssl directory for certificate files
  3. Nginx Generation: Generates Nginx configuration based on port-to-domain mappings
  4. Health Checks: Verifies Nginx health after each reload
  5. Rollback Protection: Maintains last working configuration for automatic recovery

Quick Start

For a quick start & deployment guide, see docs/QUICKSTART.md.

Documentation

Configuration

Application Configuration

The configuration file (configs/config.yaml) maps upstream addresses to domain names. It supports multiple formats and advanced features.

CORS Configuration (Optional)

Configure CORS (Cross-Origin Resource Sharing) settings globally or per-domain.

# You can also configure CORS for specific domains:
cors:
  'api.example.com':
    allow_origin: 'https://app.example.com'
    allow_methods: [GET, POST, PUT, DELETE, OPTIONS]
    allow_headers: [Content-Type, Authorization]
    allow_credentials: true

For more please check CORS Configuration for comprehensive CORS setup guide and best practices examples

Basic Port Mapping Formats
Format 1: Port Only (Default to localhost)
# Proxies to 127.0.0.1:port
1234:
  - example.com
  - www.example.com
5678:
  - api.example.com
Format 2: IP:Port (Proxy to specific IP)
# Proxies to 192.168.31.6:1234
192.168.31.6:1234:
  - lan.example.com
  - local.example.com
Format 3: Hostname:Port
# Proxies to example-server.local:8080
example-server.local:8080:
  - remote.example.com
Format 4: IPv6 with Brackets
# Proxies to IPv6 address 2001:db8::1 port 3000
'[2001:db8::1]:3000':
  - ipv6.example.com
Format 5: HTTPS Backend (Use [https] prefix)
# Proxies to HTTPS backend (prevents "plain HTTP to HTTPS port" errors)
'[https]192.168.50.2:8443':
  - secure-backend.example.com

Note: By default, sslly-nginx forwards requests to upstream servers using HTTP. Use the [https] prefix when your upstream server expects HTTPS connections to avoid "400 Bad Request - The plain HTTP request was sent to HTTPS port" errors.

Advanced: Path-based Routing

Route different paths of the same domain to different backends:

# Main site on port 9012
9012:
  - shared.example.com

# API endpoints on different server
192.168.50.2:5678/api:
  - shared.example.com/api

This generates Nginx configuration with location-based routing:

  • shared.example.com/api β†’ 192.168.50.2:5678/api
  • shared.example.com/ β†’ 127.0.0.1:9012

Configuration Key Formats Summary:

  • port β†’ Proxies to 127.0.0.1:port via HTTP (default)
  • ip:port β†’ Proxies to ip:port via HTTP
  • hostname:port β†’ Proxies to hostname:port via HTTP
  • [ipv6]:port β†’ Proxies to IPv6 address with brackets via HTTP
  • [https]upstream β†’ Proxies to upstream via HTTPS (prevents "plain HTTP to HTTPS port" errors)
    • Example: [https]192.168.50.2:8443 forwards requests using HTTPS
  • upstream/path β†’ Adds path routing to the upstream
    • Example: 192.168.50.2:5678/api proxies /api requests to that backend
SSL Certificate Structure

Place SSL certificates in the ssl/ directory. The application automatically matches certificate files (.crt) with their corresponding private key files (.key) based on the domain information contained within the SSL certificates.

You can organize certificates in subdirectories:

ssl/
β”œβ”€β”€ production/
β”‚   β”œβ”€β”€ example.com_bundle.crt
β”‚   └── example.com_bundle.key
β”œβ”€β”€ staging/
β”‚   β”œβ”€β”€ staging.example.com.crt
β”‚   └── staging.example.com.key
└── api.example.com.crt
    └── api.example.com.key

Important Notes:

  • Duplicate certificates are allowed. For each domain, only certificate+private-key pairs are considered valid; if multiple pairs match, the certificate with the farthest expiration time is selected (ties prefer .pem over .crt)
  • Certificate and key files are optional (a domain without a matched cert/key will be served over HTTP)
  • The application reads the domain information from the certificate content itself and matches it with the corresponding key file
  • SSL certificates are optional: If no certificate is found for a domain, the service will proxy HTTP traffic directly
  • HTTPS to HTTP redirect: If HTTPS is accessed for domains without valid certificates, traffic is redirected to HTTP (301)
Backup & Crash Recovery

To make hot-reloads safer, sslly-nginx keeps a persistent on-disk snapshot of the last known-good configuration.

  • Backup folder: configs/.sslly-backups/
  • Snapshot content: configs/ + ssl/ + generated /etc/nginx/nginx.conf
  • Runtime cache: the currently used cert/key files are copied into configs/.sslly-runtime/current/ and nginx.conf only references that cache, so edits under ssl/ won't affect the running nginx process until a successful reload
  • Crash detection: if the previous run died mid-reload, the next start detects an unfinished reload and automatically restores the last known-good snapshot
HTTP-Only Mode

If you don't have SSL certificates yet or want to serve some domains over HTTP only:

  1. The application will automatically detect missing certificates
  2. Domains without certificates will be served over HTTP (no redirect)
  3. Domains with certificates will use HTTPS with automatic HTTP β†’ HTTPS redirect
  4. HTTPS fallback: If someone accesses HTTPS for a domain without a valid certificate, they'll be redirected to HTTP with a 301 status
  5. You can mix HTTP and HTTPS domains in the same configuration

Example scenario:

# config.yaml
1234:
  - secure.example.com # Has certificate β†’ HTTPS
  - dev.example.com # No certificate β†’ HTTP only

Features in Detail

Automatic HTTPS Redirect

When SSL certificates are detected:

  • All HTTP traffic for domains with certificates is automatically redirected to HTTPS
  • HTTPS traffic for domains without certificates is redirected to HTTP (301) to avoid certificate errors

If no certificates are found for any domain, HTTP traffic is proxied directly to your applications.

Hot Reload

The application watches for changes in:

  • Configuration files (./configs/config.yaml or ./configs/config.yml)
  • SSL certificates (./ssl/**/*)

Note: internal state folders under configs/ (like configs/.sslly-backups/ and configs/.sslly-runtime/) are ignored by the watcher to avoid feedback loops.

When changes are detected:

  1. New configuration is generated
  2. Nginx configuration is tested
  3. If valid, Nginx is reloaded
  4. If invalid, the previous working configuration is restored (including the on-disk configs/ + ssl/ contents)
Logs: Domain Summary

On startup and after every successful reload, the service prints a single domain summary instead of logging domain status one-by-one.

  • Matched: (INFO) domains with a valid certificate+key and the certificate is not expired
  • No-cert: (WARN) domains with no matched certificate+key (served over HTTP)
  • Expired: (WARN) domains with a matched certificate+key, but the certificate is expired
  • Multi-certs: (WARN) domains where multiple certificate candidates were found; the selected certificate path is shown along with the ignored count

Domains inside each block are sorted by comparing labels from TLD to left (e.g. compare de before abc in abc.de), and each label is compared by Unicode order. If all compared labels match, the shorter domain sorts first.

Each domain line also prints its upstream destination(s) in the form domain -> scheme://host:port[/path].

Example order:

abc.az
abc.de
abc.abc.de
aad.def
abc.def
abc.abc.def
Error Handling
  • Initial Startup:
    • If configuration is invalid, the service stops
    • Missing SSL certificates are not an error - service runs in HTTP-only mode
  • Runtime Errors: If reload fails, the application:
    • Logs detailed error messages
    • Restores the last working configuration
    • Continues running with previous settings

Testing

Run unit tests:

go test ./...

Run tests with coverage:

go test ./... -coverprofile=coverage.out
go tool cover -func=coverage.out
go tool cover -html=coverage.out -o coverage.html
WebSocket Support

The generated Nginx configuration includes WebSocket support for all proxied applications.

Advanced Proxy Features

The reverse proxy includes optimized settings for various applications:

  • Large File Upload: Supports files up to 100MB by default
  • Correct Host Header: Uses $host to preserve the original request hostname (critical for apps like qBittorrent, OnlineJudge)
  • Proxy Headers: Includes all standard headers:
    • Host: Original request hostname (e.g., torrent.hnrobert.space)
    • X-Real-IP: Client's real IP address
    • X-Forwarded-For: Full proxy chain
    • X-Forwarded-Host: Original Host header
    • X-Forwarded-Proto: Original protocol (http/https)
  • Cookie Security: Automatically sets Secure flag for cookies when using HTTPS
  • Timeouts: Configured with 60s timeouts for connect/send/read operations
  • Proxy Buffering: Optimized buffer settings for better performance

These settings work well with applications like:

  • qBittorrent (WebUI)
  • Portainer (Docker management)
  • Jellyfin (Media streaming)
  • Home Assistant (Smart home)
  • OnlineJudge (Competitive programming)
  • And most other web applications

FRP Integration

sslly-nginx integrates seamlessly with FRP (Fast Reverse Proxy) to expose your local services through remote servers, enabling secure remote access to your applications from anywhere.

Key Benefits
  • Secure Remote Access: Access your local applications from anywhere via HTTPS
  • Custom Domains: Use your own domain names instead of IP addresses
  • SSL Management: SSL certificates configured locally for domain-based routing
  • Flexible Port Configuration: Change HTTP/HTTPS ports to avoid conflicts with FRP
Quick Setup
  1. Configure Ports: Modify docker-compose.yml to use non-standard ports:

    environment:
      - SSL_NGINX_HTTP_PORT=9980 # HTTP traffic
      - SSL_NGINX_HTTPS_PORT=9943 # HTTPS traffic
    
  2. Setup FRP Client: Create frpc.toml:

    serverAddr = "your-frp-server.com"
    serverPort = 7000
    auth.method = "token"
    auth.token = "your-secure-token"
    
    # HTTPS proxy - handles SSL/TLS traffic
    [[proxies]]
    name = "sslly-nginx-https"
    type = "https"
    localIP = "127.0.0.1"
    localPort = 9943
    customDomains = ["*.yourdomain.com", "yourdomain.com"]
    
    # HTTP proxy - handles plain HTTP and auto-redirects
    [[proxies]]
    name = "sslly-nginx-http"
    type = "http"
    localIP = "127.0.0.1"
    localPort = 9980
    customDomains = ["*.yourdomain.com", "yourdomain.com"]
    
  3. Start Services: Run both FRP client and sslly-nginx

For detailed FRP integration guide, see docs/FRP.md.

Development

Build Locally
# Build the binary
make build

# Run tests
make test

# Run tests with coverage
make test-coverage

# Format code
make fmt

# Run linter
make lint
Build Docker Image
# Build image
make docker-build

# Or use Docker directly
docker build -t sslly-nginx:latest .
Run Locally (without Docker)
# Note: Requires Nginx installed on your system
make run

CI/CD Workflows

The project includes three GitHub Actions workflows:

1. CI Workflow (ci.yml)
  • Triggers: All branch pushes and pull requests
  • Actions:
    • Build the application
    • Run tests
    • Run linter and format checks
  • No Docker image is built
2. Docker Build Workflow (docker-build.yml)
  • Triggers: Pushes to main and develop branches
  • Actions:
    • Run tests
    • Build Docker image
    • Push to ghcr.io
  • Tags:
    • main branch β†’ latest tag
    • develop branch β†’ develop tag
3. Release Workflow (release.yml)
  • Triggers:
    • Git tag push (e.g., v1.0.0)
    • Manual workflow dispatch
  • Actions:
    • Create tag (if workflow_dispatch)
    • Run tests
    • Build and push Docker image with version tag
    • Create GitHub release

Docker Compose Configuration

The docker-compose.yml is configured with:

  • Network Mode: host - Uses host networking for direct port access
  • Restart Policy: on-failure - Stops on errors, auto-starts on system boot
  • Volumes:
    • ./configs:/app/configs:ro - Configuration (read-only)
    • ./ssl:/app/ssl:ro - SSL certificates (read-only)
Environment Variables
  • SSL_NGINX_HTTP_PORT (default: 80) β€” port Nginx listens for HTTP and redirect to HTTPS
  • SSL_NGINX_HTTPS_PORT (default: 443) β€” port Nginx listens for HTTPS
Viewing Logs

All logs (application + nginx access/error logs) are forwarded to Docker's log collector:

# View all logs
docker-compose logs -f

# View only application logs
docker-compose logs -f sslly-nginx

# View last 100 lines
docker-compose logs --tail=100 sslly-nginx

Nginx access and error logs are automatically forwarded to stdout/stderr and visible via docker logs

Project Structure

sslly-nginx/
β”œβ”€β”€ cmd/
β”‚   └── sslly-nginx/
β”‚       └── main.go              # Application entry point
β”œβ”€β”€ internal/
β”‚   β”œβ”€β”€ app/
β”‚   β”‚   └── app.go               # Application logic
β”‚   β”œβ”€β”€ config/
β”‚   β”‚   β”œβ”€β”€ config.go            # Configuration loader
β”‚   β”‚   └── config_test.go
β”‚   β”œβ”€β”€ nginx/
β”‚   β”‚   └── nginx.go             # Nginx management
β”‚   β”œβ”€β”€ ssl/
β”‚   β”‚   β”œβ”€β”€ ssl.go               # Certificate scanner
β”‚   β”‚   └── ssl_test.go
β”‚   └── watcher/
β”‚       └── watcher.go           # File system watcher
β”œβ”€β”€ .github/
β”‚   └── workflows/
β”‚       β”œβ”€β”€ ci.yml               # CI pipeline
β”‚       β”œβ”€β”€ docker-build.yml     # Docker build pipeline
β”‚       └── release.yml          # Release pipeline
β”œβ”€β”€ configs/
β”‚   └── config.example.yaml      # Example configuration
β”œβ”€β”€ ssl/
β”‚   └── README.md                # SSL certificate guide
β”œβ”€β”€ Dockerfile                   # Docker image definition
β”œβ”€β”€ docker-compose.yml           # Docker Compose configuration
β”œβ”€β”€ Makefile                     # Build automation
β”œβ”€β”€ go.mod                       # Go module definition
└── README.md                    # This file

Logging

The application logs important events:

  • Configuration changes detected
  • Certificate scanning results
  • Nginx reload success/failure
  • Error details with recovery actions

Logs can be viewed with:

docker-compose logs -f

Troubleshooting

Container Stops Immediately

Cause: Invalid configuration or missing certificates

Solution:

  1. Check logs: docker-compose logs
  2. Verify configs/config.yaml exists and is valid YAML
  3. Ensure all domains have matching certificates in ssl/
Certificate Not Found

Cause: Certificate file naming doesn't match expected patterns

Solution:

  1. Check certificate files follow naming pattern: domain.crt/key or domain_bundle.crt/key
  2. Ensure both .crt and .key files exist
  3. Check logs for certificate scanning results
Nginx Fails to Reload

Cause: Configuration error or certificate issues

Solution:

  1. Application automatically rolls back to last working configuration
  2. Check logs for specific error messages
  3. Fix the configuration or certificate issue
  4. Changes will be automatically detected and reloaded

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

See LICENSE file for details.

Support

For issues and questions, please use the GitHub Issues page. A nginx container with automatic ssl handling

Directories ΒΆ

Path Synopsis
cmd
sslly-nginx command
internal
app
ssl

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL