自签证书
This commit is contained in:
@@ -1,8 +1,18 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
"mail_go/config"
|
"mail_go/config"
|
||||||
"mail_go/internal/db"
|
"mail_go/internal/db"
|
||||||
@@ -22,28 +32,118 @@ func applyDomainTLSConfig(stores *store.Stores, cfg *config.Config) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
applied := false
|
applied := applyTLSCertPaths(cfg, domain.TlsCertPath, domain.TlsKeyPath)
|
||||||
if cfg.SMTP.TLSCert == "" && cfg.SMTP.TLSKey == "" {
|
|
||||||
cfg.SMTP.TLSCert = domain.TlsCertPath
|
|
||||||
cfg.SMTP.TLSKey = domain.TlsKeyPath
|
|
||||||
applied = true
|
|
||||||
}
|
|
||||||
if cfg.IMAP.TLSCert == "" && cfg.IMAP.TLSKey == "" {
|
|
||||||
cfg.IMAP.TLSCert = domain.TlsCertPath
|
|
||||||
cfg.IMAP.TLSKey = domain.TlsKeyPath
|
|
||||||
applied = true
|
|
||||||
}
|
|
||||||
if cfg.POP3.TLSCert == "" && cfg.POP3.TLSKey == "" {
|
|
||||||
cfg.POP3.TLSCert = domain.TlsCertPath
|
|
||||||
cfg.POP3.TLSKey = domain.TlsKeyPath
|
|
||||||
applied = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if applied {
|
if applied {
|
||||||
log.Printf("使用域名 %s 的 TLS 证书;更新证书后需重启服务生效", domain.Name)
|
log.Printf("使用域名 %s 的 TLS 证书;更新证书后需重启服务生效", domain.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applyTLSCertPaths(cfg *config.Config, certPath, keyPath string) bool {
|
||||||
|
applied := false
|
||||||
|
if cfg.SMTP.TLSCert == "" && cfg.SMTP.TLSKey == "" {
|
||||||
|
cfg.SMTP.TLSCert = certPath
|
||||||
|
cfg.SMTP.TLSKey = keyPath
|
||||||
|
applied = true
|
||||||
|
}
|
||||||
|
if cfg.IMAP.TLSCert == "" && cfg.IMAP.TLSKey == "" {
|
||||||
|
cfg.IMAP.TLSCert = certPath
|
||||||
|
cfg.IMAP.TLSKey = keyPath
|
||||||
|
applied = true
|
||||||
|
}
|
||||||
|
if cfg.POP3.TLSCert == "" && cfg.POP3.TLSKey == "" {
|
||||||
|
cfg.POP3.TLSCert = certPath
|
||||||
|
cfg.POP3.TLSKey = keyPath
|
||||||
|
applied = true
|
||||||
|
}
|
||||||
|
return applied
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureSelfSignedTLSConfig(cfg *config.Config) {
|
||||||
|
if cfg.SMTP.TLSCert != "" && cfg.SMTP.TLSKey != "" && cfg.IMAP.TLSCert != "" && cfg.IMAP.TLSKey != "" && cfg.POP3.TLSCert != "" && cfg.POP3.TLSKey != "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
certPath := filepath.Join(cfg.Storage.BaseDir, "tls", "self-signed", "cert.pem")
|
||||||
|
keyPath := filepath.Join(cfg.Storage.BaseDir, "tls", "self-signed", "key.pem")
|
||||||
|
if err := ensureSelfSignedCert(certPath, keyPath, cfg.SMTP.Domain); err != nil {
|
||||||
|
log.Printf("生成自签名 TLS 证书失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if applyTLSCertPaths(cfg, certPath, keyPath) {
|
||||||
|
log.Printf("未配置 TLS 证书,已使用自签名证书启动 TLS 端口;正式使用请在后台上传受信任证书")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureSelfSignedCert(certPath, keyPath, domain string) error {
|
||||||
|
if _, certErr := os.Stat(certPath); certErr == nil {
|
||||||
|
if _, keyErr := os.Stat(keyPath); keyErr == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(filepath.Dir(certPath), 0700); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
notBefore := time.Now()
|
||||||
|
serialLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||||
|
serialNumber, err := rand.Int(rand.Reader, serialLimit)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if domain == "" {
|
||||||
|
domain = "localhost"
|
||||||
|
}
|
||||||
|
template := x509.Certificate{
|
||||||
|
SerialNumber: serialNumber,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
Organization: []string{"MailGo Self-Signed"},
|
||||||
|
CommonName: domain,
|
||||||
|
},
|
||||||
|
NotBefore: notBefore,
|
||||||
|
NotAfter: notBefore.AddDate(10, 0, 0),
|
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
DNSNames: []string{domain, "localhost"},
|
||||||
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")},
|
||||||
|
}
|
||||||
|
|
||||||
|
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
certFile, err := os.OpenFile(certPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: certDER}); err != nil {
|
||||||
|
certFile.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := certFile.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
keyFile, err := os.OpenFile(keyPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := pem.Encode(keyFile, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)}); err != nil {
|
||||||
|
keyFile.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return keyFile.Close()
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// 1. Load configuration
|
// 1. Load configuration
|
||||||
cfg, err := config.LoadConfig()
|
cfg, err := config.LoadConfig()
|
||||||
@@ -68,6 +168,7 @@ func main() {
|
|||||||
// 5. Initialize attachment storage
|
// 5. Initialize attachment storage
|
||||||
attStorage := storage.NewAttachmentStorage(cfg.Storage.AttachDir)
|
attStorage := storage.NewAttachmentStorage(cfg.Storage.AttachDir)
|
||||||
applyDomainTLSConfig(stores, cfg)
|
applyDomainTLSConfig(stores, cfg)
|
||||||
|
ensureSelfSignedTLSConfig(cfg)
|
||||||
|
|
||||||
// 6. Start SMTP server
|
// 6. Start SMTP server
|
||||||
smtpSrv := smtp_server.NewSMTPServer(cfg.SMTP, stores, attStorage)
|
smtpSrv := smtp_server.NewSMTPServer(cfg.SMTP, stores, attStorage)
|
||||||
|
|||||||
Reference in New Issue
Block a user