Template
fix: ACME HTTP-01 via nginx config without HTTPS redirect
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
package nginx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/panelhosting/panel/internal/ssl"
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
sitesDir string
|
||||
reloadCmd string
|
||||
sslStorage string
|
||||
}
|
||||
|
||||
func NewManager(sitesDir, reloadCmd, sslStorage string) *Manager {
|
||||
return &Manager{sitesDir: sitesDir, reloadCmd: reloadCmd, sslStorage: sslStorage}
|
||||
}
|
||||
|
||||
func (m *Manager) Enabled() bool {
|
||||
return m.sitesDir != ""
|
||||
}
|
||||
|
||||
func (m *Manager) PrepareHTTPChallenge(domain, documentRoot string) error {
|
||||
if !m.Enabled() {
|
||||
return nil
|
||||
}
|
||||
if err := os.MkdirAll(m.sitesDir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Join(documentRoot, ".well-known", "acme-challenge"), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg := fmt.Sprintf(`# PanelHosting — HTTP challenge for %s
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name %s;
|
||||
|
||||
location ^~ /.well-known/acme-challenge/ {
|
||||
root %s;
|
||||
default_type "text/plain";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 404;
|
||||
}
|
||||
}
|
||||
`, domain, domain, documentRoot)
|
||||
|
||||
path := filepath.Join(m.sitesDir, sanitize(domain)+".conf")
|
||||
if err := os.WriteFile(path, []byte(cfg), 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
return m.reload()
|
||||
}
|
||||
|
||||
func (m *Manager) ApplyHTTPS(domain, documentRoot string) error {
|
||||
if !m.Enabled() {
|
||||
return nil
|
||||
}
|
||||
certPath, keyPath := ssl.CertPaths(m.sslStorage, domain)
|
||||
cfg := fmt.Sprintf(`# PanelHosting — %s
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name %s;
|
||||
|
||||
location ^~ /.well-known/acme-challenge/ {
|
||||
root %s;
|
||||
default_type "text/plain";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
http2 on;
|
||||
server_name %s;
|
||||
|
||||
ssl_certificate %s;
|
||||
ssl_certificate_key %s;
|
||||
|
||||
root %s;
|
||||
index index.php index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
}
|
||||
`, domain, domain, documentRoot, domain, domain, certPath, keyPath, documentRoot)
|
||||
|
||||
path := filepath.Join(m.sitesDir, sanitize(domain)+".conf")
|
||||
if err := os.WriteFile(path, []byte(cfg), 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
return m.reload()
|
||||
}
|
||||
|
||||
func (m *Manager) reload() error {
|
||||
cmd := strings.TrimSpace(m.reloadCmd)
|
||||
if cmd == "" {
|
||||
return nil
|
||||
}
|
||||
parts := strings.Fields(cmd)
|
||||
if len(parts) == 0 {
|
||||
return nil
|
||||
}
|
||||
c := exec.Command(parts[0], parts[1:]...)
|
||||
if out, err := c.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("nginx reload: %w: %s", err, strings.TrimSpace(string(out)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func sanitize(domain string) string {
|
||||
return strings.NewReplacer(".", "_", ":", "_").Replace(domain)
|
||||
}
|
||||
Reference in New Issue
Block a user