Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions agent/app/api/v2/website_ssl.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package v2

import (
"github.com/1Panel-dev/1Panel/agent/app/model"
"net/http"
"net/url"
"reflect"
Expand Down Expand Up @@ -245,3 +246,15 @@ func (b *BaseApi) DownloadWebsiteSSL(c *gin.Context) {
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(info.Name()))
http.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), file)
}

func (b *BaseApi) ImportMasterSSL(c *gin.Context) {
var req model.WebsiteSSL
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteSSLService.ImportMasterSSL(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
38 changes: 20 additions & 18 deletions agent/app/dto/request/website_ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,26 @@ type WebsiteSSLSearch struct {
}

type WebsiteSSLCreate struct {
PrimaryDomain string `json:"primaryDomain" validate:"required"`
OtherDomains string `json:"otherDomains"`
Provider string `json:"provider" validate:"required"`
AcmeAccountID uint `json:"acmeAccountId" validate:"required"`
DnsAccountID uint `json:"dnsAccountId"`
AutoRenew bool `json:"autoRenew"`
KeyType string `json:"keyType"`
Apply bool `json:"apply"`
PushDir bool `json:"pushDir"`
Dir string `json:"dir"`
ID uint `json:"id"`
Description string `json:"description"`
DisableCNAME bool `json:"disableCNAME"`
SkipDNS bool `json:"skipDNS"`
Nameserver1 string `json:"nameserver1"`
Nameserver2 string `json:"nameserver2"`
ExecShell bool `json:"execShell"`
Shell string `json:"shell"`
PrimaryDomain string `json:"primaryDomain" validate:"required"`
OtherDomains string `json:"otherDomains"`
Provider string `json:"provider" validate:"required"`
AcmeAccountID uint `json:"acmeAccountId" validate:"required"`
DnsAccountID uint `json:"dnsAccountId"`
AutoRenew bool `json:"autoRenew"`
KeyType string `json:"keyType"`
Apply bool `json:"apply"`
PushDir bool `json:"pushDir"`
Dir string `json:"dir"`
ID uint `json:"id"`
Description string `json:"description"`
DisableCNAME bool `json:"disableCNAME"`
SkipDNS bool `json:"skipDNS"`
Nameserver1 string `json:"nameserver1"`
Nameserver2 string `json:"nameserver2"`
ExecShell bool `json:"execShell"`
Shell string `json:"shell"`
PushNode bool `json:"pushNode"`
Nodes []string `json:"nodes"`
}

type WebsiteDNSReq struct {
Expand Down
3 changes: 3 additions & 0 deletions agent/app/model/website_ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ type WebsiteSSL struct {
DisableCNAME bool `json:"disableCNAME"`
ExecShell bool `json:"execShell"`
Shell string `json:"shell"`
MasterSSLID uint `json:"masterSslId"`
Nodes string `json:"nodes"`
PushNode bool `json:"pushNode"`

AcmeAccount WebsiteAcmeAccount `json:"acmeAccount" gorm:"-:migration"`
DnsAccount WebsiteDnsAccount `json:"dnsAccount" gorm:"-:migration"`
Expand Down
8 changes: 8 additions & 0 deletions agent/app/repo/website_ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type ISSLRepo interface {
WithByDnsAccountId(dnsAccountId uint) DBOption
WithByCAID(caID uint) DBOption
WithByDomain(domain string) DBOption
WithByMasterSSLID(sslID uint) DBOption
Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error)
GetFirst(opts ...DBOption) (*model.WebsiteSSL, error)
List(opts ...DBOption) ([]model.WebsiteSSL, error)
Expand Down Expand Up @@ -52,12 +53,19 @@ func (w WebsiteSSLRepo) WithByCAID(caID uint) DBOption {
return db.Where("ca_id = ?", caID)
}
}

func (w WebsiteSSLRepo) WithByDomain(domain string) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("primary_domain Like ? or domains Like ?", "%"+domain+"%", "%"+domain+"%")
}
}

func (w WebsiteSSLRepo) WithByMasterSSLID(sslID uint) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("master_ssl_id = ?", sslID)
}
}

func (w WebsiteSSLRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error) {
var sslList []model.WebsiteSSL
db := getDb(opts...).Model(&model.WebsiteSSL{})
Expand Down
68 changes: 67 additions & 1 deletion agent/app/service/website_ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
"github.com/go-acme/lego/v4/certificate"
"log"
"os"
Expand Down Expand Up @@ -46,6 +47,7 @@ type IWebsiteSSLService interface {
ObtainSSL(apply request.WebsiteSSLApply) error
SyncForRestart() error
DownloadFile(id uint) (*os.File, error)
ImportMasterSSL(create model.WebsiteSSL) error
}

func NewIWebsiteSSLService() IWebsiteSSLService {
Expand Down Expand Up @@ -146,6 +148,10 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
}
websiteSSL.Dir = create.Dir
}
if create.PushNode && global.IsMaster && len(create.Nodes) > 0 {
websiteSSL.PushNode = true
websiteSSL.Nodes = strings.Join(create.Nodes, ",")
}

var domains []string
if create.OtherDomains != "" {
Expand Down Expand Up @@ -207,7 +213,7 @@ func printSSLLog(logger *log.Logger, msgKey string, params map[string]interface{
}

func reloadSystemSSL(websiteSSL *model.WebsiteSSL, logger *log.Logger) {
if global.CoreDB == nil {
if !global.IsMaster {
return
}
systemSSLEnable, sslID := GetSystemSSL()
Expand Down Expand Up @@ -387,6 +393,14 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
printSSLLog(logger, "ApplyWebSiteSSLSuccess", nil, apply.DisableLog)
}
reloadSystemSSL(websiteSSL, logger)
if websiteSSL.PushNode {
printSSLLog(logger, "StartPushSSLToNode", nil, apply.DisableLog)
if err = xpack.PushSSLToNode(websiteSSL); err != nil {
printSSLLog(logger, "PushSSLToNodeFailed", map[string]interface{}{"err": err.Error()}, apply.DisableLog)
return
}
printSSLLog(logger, "PushSSLToNodeSuccess", nil, apply.DisableLog)
}
}()

return nil
Expand Down Expand Up @@ -712,3 +726,55 @@ func (w WebsiteSSLService) SyncForRestart() error {
}
return nil
}

func (w WebsiteSSLService) ImportMasterSSL(create model.WebsiteSSL) error {
websiteSSL, _ := websiteSSLRepo.GetFirst(websiteSSLRepo.WithByMasterSSLID(create.ID))
if websiteSSL == nil {
websiteSSL = &model.WebsiteSSL{
Status: constant.SSLReady,
Provider: constant.FromMaster,
PrimaryDomain: create.PrimaryDomain,
StartDate: create.StartDate,
ExpireDate: create.ExpireDate,
KeyType: create.KeyType,
Description: create.Description,
MasterSSLID: create.ID,
PrivateKey: create.PrivateKey,
Pem: create.Pem,
Type: create.Type,
Organization: create.Organization,
}
if err := websiteSSLRepo.Create(context.TODO(), websiteSSL); err != nil {
return err
}
} else {
websiteSSL.PrimaryDomain = create.PrimaryDomain
websiteSSL.StartDate = create.StartDate
websiteSSL.ExpireDate = create.ExpireDate
websiteSSL.KeyType = create.KeyType
websiteSSL.Description = create.Description
websiteSSL.PrivateKey = create.PrivateKey
websiteSSL.Pem = create.Pem
websiteSSL.Type = create.Type
websiteSSL.Organization = create.Organization
if err := websiteSSLRepo.Save(websiteSSL); err != nil {
return err
}
}
websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(websiteSSL.ID))
if len(websites) == 0 {
return nil
}
for _, website := range websites {
if err := createPemFile(website, *websiteSSL); err != nil {
continue
}
}
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err == nil {
if err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {
return err
}
}
return nil
}
1 change: 1 addition & 0 deletions agent/constant/website.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const (
Http = "http"
Manual = "manual"
SelfSigned = "selfSigned"
FromMaster = "fromMaster"

StartWeb = "start"
StopWeb = "stop"
Expand Down
3 changes: 3 additions & 0 deletions agent/i18n/lang/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ StartUpdateSystemSSL: 'Start updating system certificate'
UpdateSystemSSLSuccess: 'Update system certificate successfully'
ErrWildcardDomain: 'Unable to apply for wildcard domain name certificate in HTTP mode'
ErrApplySSLCanNotDelete: "The certificate {{.name}} being applied for cannot be deleted, please try again later."
StartPushSSLToNode: "Starting to push certificate to node"
PushSSLToNodeFailed: "Failed to push certificate to node: {{ .err }}"
PushSSLToNodeSuccess: "Successfully pushed certificate to node"

#mysql
ErrUserIsExist: 'The current user already exists, please re-enter'
Expand Down
3 changes: 3 additions & 0 deletions agent/i18n/lang/ja.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ StartUpdateSystemSSL: 'システム証明書の更新を開始します'
UpdateSystemSSLSuccess: 'システム証明書を正常に更新しました'
ErrWildcardDomain: 'HTTP モードでワイルドカード ドメイン名証明書を申請できません'
ErrApplySSLCanNotDelete: "申請中の証明書 {{.name}} は削除できません。しばらくしてからもう一度お試しください。"
StartPushSSLToNode: "証明書をノードにプッシュ開始"
PushSSLToNodeFailed: "ノードに証明書をプッシュ失敗: {{ .err }}"
PushSSLToNodeSuccess: "ノードに証明書をプッシュ成功"

#mysql
ErrUserIsExist: '現在のユーザーは既に存在します。再入力してください'
Expand Down
3 changes: 3 additions & 0 deletions agent/i18n/lang/ko.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ StartUpdateSystemSSL: '시스템 인증서 업데이트 시작'
UpdateSystemSSLSuccess: '시스템 인증서 업데이트가 성공적으로 완료되었습니다.'
ErrWildcardDomain: 'HTTP 모드에서 와일드카드 도메인 이름 인증서를 신청할 수 없습니다'
ErrApplySSLCanNotDelete: "신청 중인 인증서 {{.name}}는 삭제할 수 없습니다. 나중에 다시 시도해 주세요."
StartPushSSLToNode: "인증서를 노드로 푸시 시작"
PushSSLToNodeFailed: "노드로 인증서 푸시 실패: {{ .err }}"
PushSSLToNodeSuccess: "노드로 인증서 푸시 성공"

#마이SQL
ErrUserIsExist: '현재 사용자가 이미 존재합니다. 다시 입력하세요'
Expand Down
3 changes: 3 additions & 0 deletions agent/i18n/lang/ms.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ ErrApiConfigKeyInvalid: 'Ralat kunci antara muka API: {{ .detail }}'
ErrApiConfigIPInvalid: 'IP yang digunakan untuk memanggil antara muka API tiada dalam senarai putih: {{ .detail }}'
ErrApiConfigDisable: 'Antara muka ini melarang penggunaan panggilan antara muka API: {{ .detail }}'
ErrApiConfigKeyTimeInvalid: 'Ralat cap masa antara muka API: {{ .detail }}'
StartPushSSLToNode: "Mula menolak sijil ke nod"
PushSSLToNodeFailed: "Gagal menolak sijil ke nod: {{ .err }}"
PushSSLToNodeSuccess: "Berjaya menolak sijil ke nod"

#biasa
ErrUsernameIsExist: 'Nama pengguna sudah wujud'
Expand Down
3 changes: 3 additions & 0 deletions agent/i18n/lang/pt-BR.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ ErrApiConfigKeyInvalid: 'Erro de chave da interface da API: {{ .detail }}'
ErrApiConfigIPInvalid: 'O IP usado para chamar a interface da API não está na lista de permissões: {{ .detail }}'
ErrApiConfigDisable: 'Esta interface proíbe o uso de chamadas de interface de API: {{ .detail }}'
ErrApiConfigKeyTimeInvalid: 'Erro de registro de data e hora da interface da API: {{ .detail }}'
StartPushSSLToNode: "Iniciando o envio do certificado para o nó"
PushSSLToNodeFailed: "Falha ao enviar o certificado para o nó: {{ .err }}"
PushSSLToNodeSuccess: "Certificado enviado com sucesso para o nó"

#comum
ErrUsernameIsExist: 'Nome de usuário já existe'
Expand Down
3 changes: 3 additions & 0 deletions agent/i18n/lang/ru.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ ErrApiConfigKeyInvalid: 'Ошибка ключа интерфейса API: {{ .d
ErrApiConfigIPInvalid: 'IP-адрес, используемый для вызова интерфейса API, отсутствует в белом списке: {{ .detail }}'
ErrApiConfigDisable: 'Этот интерфейс запрещает использование вызовов интерфейса API: {{ .detail }}'
ErrApiConfigKeyTimeInvalid: 'Ошибка временной метки интерфейса API: {{ .detail }}'
StartPushSSLToNode: "Начало отправки сертификата на узел"
PushSSLToNodeFailed: "Не удалось отправить сертификат на узел: {{ .err }}"
PushSSLToNodeSuccess: "Сертификат успешно отправлен на узел"

#общий
ErrUsernameIsExist: 'Имя пользователя уже существует'
Expand Down
3 changes: 3 additions & 0 deletions agent/i18n/lang/tr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ ErrApiConfigKeyInvalid: 'API arayüz anahtarı hatası: {{ .detail }}'
ErrApiConfigIPInvalid: 'API arayüzünü çağırmak için kullanılan IP beyaz listede değil: {{ .detail }}'
ErrApiConfigDisable: 'Bu arayüz API arayüz çağrılarının kullanımını yasaklıyor: {{ .detail }}'
ErrApiConfigKeyTimeInvalid: 'API arayüz zaman damgası hatası: {{ .detail }}'
StartPushSSLToNode: "Sertifika düğüme gönderilmeye başlandı"
PushSSLToNodeFailed: "Sertifika düğüme gönderilemedi: {{ .err }}"
PushSSLToNodeSuccess: "Sertifika düğüme başarıyla gönderildi"

#common
ErrUsernameIsExist: 'Kullanıcı adı zaten mevcut'
Expand Down
3 changes: 3 additions & 0 deletions agent/i18n/lang/zh-Hant.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ StartUpdateSystemSSL: '開始更新系統憑證'
UpdateSystemSSLSuccess: '更新系統憑證成功'
ErrWildcardDomain: 'HTTP 模式無法申請泛網域憑證'
ErrApplySSLCanNotDelete: "正在申請的證書 {{.name}} 無法刪除,請稍後再試"
StartPushSSLToNode: "開始推送證書到節點"
PushSSLToNodeFailed: "推送證書到節點失敗: {{ .err }}"
PushSSLToNodeSuccess: "推送證書到節點成功"

#mysql
ErrUserIsExist: '目前使用者已存在,請重新輸入'
Expand Down
3 changes: 3 additions & 0 deletions agent/i18n/lang/zh.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ StartUpdateSystemSSL: "开始更新系统证书"
UpdateSystemSSLSuccess: "更新系统证书成功"
ErrWildcardDomain: "HTTP 模式无法申请泛域名证书"
ErrApplySSLCanNotDelete: "正在申请的证书{{.name}}无法删除,请稍后再试"
StartPushSSLToNode: "开始推送证书到节点"
PushSSLToNodeFailed: "推送证书到节点失败: {{ .err }}"
PushSSLToNodeSuccess: "推送证书到节点成功"

#mysql
ErrUserIsExist: "当前用户已存在,请重新输入"
Expand Down
1 change: 1 addition & 0 deletions agent/init/migration/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func InitAgentDB() {
migrations.UpdateMcpServer,
migrations.InitCronjobGroup,
migrations.AddColumnToAlert,
migrations.UpdateWebsiteSSL,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
Expand Down
10 changes: 10 additions & 0 deletions agent/init/migration/migrations/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,3 +461,13 @@ var AddColumnToAlert = &gormigrate.Migration{
return nil
},
}

var UpdateWebsiteSSL = &gormigrate.Migration{
ID: "20250819-update-website-ssl",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.WebsiteSSL{}); err != nil {
return err
}
return nil
},
}
1 change: 1 addition & 0 deletions agent/router/ro_website_ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ func (a *WebsiteSSLRouter) InitRouter(Router *gin.RouterGroup) {
groupRouter.POST("/upload", baseApi.UploadWebsiteSSL)
groupRouter.POST("/obtain", baseApi.ApplyWebsiteSSL)
groupRouter.POST("/download", baseApi.DownloadWebsiteSSL)
groupRouter.POST("/import", baseApi.ImportMasterSSL)
}
}
3 changes: 2 additions & 1 deletion agent/utils/ssl/acme.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ func getCaDirURL(accountType, customCaURL string) string {
var caDirURL string
switch accountType {
case "letsencrypt":
caDirURL = "https://acme-v02.api.letsencrypt.org/directory"
//caDirURL = "https://acme-v02.api.letsencrypt.org/directory"
caDirURL = "https://acme-staging-v02.api.letsencrypt.org/directory"
case "zerossl":
caDirURL = "https://acme.zerossl.com/v2/DV90"
case "buypass":
Expand Down
4 changes: 4 additions & 0 deletions agent/utils/xpack/xpack.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,7 @@ func LoadRequestTransport() *http.Transport {
func ValidateCertificate(c *gin.Context) bool {
return true
}

func PushSSLToNode(websiteSSL *model.WebsiteSSL) error {
return nil
}
5 changes: 4 additions & 1 deletion core/i18n/lang/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -227,4 +227,7 @@ ErrMasterDelete: "Unable to delete the master node, please delete the slave node
ClusterNameIsExist: "Cluster name already exists."
AppStatusUnHealthy: "Application status acquisition is abnormal, please check the installation node status in the node list."
MasterNodePortNotAvailable: "Node {{ .name }} port {{ .port }} connectivity verification failed, please check firewall/security group settings and master node status."
ClusterMasterNotExist: "The master node of the cluster is disconnected, please delete the child nodes."
ClusterMasterNotExist: "The master node of the cluster is disconnected, please delete the child nodes."

#ssl
ErrReqFailed: "{{.name}} request failed: {{ .err }}"
5 changes: 4 additions & 1 deletion core/i18n/lang/ja.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -228,4 +228,7 @@ ErrMasterDelete: "マスターノードを削除できません。スレーブ
ClusterNameIsExist: "クラスタ名は既に存在します。"
AppStatusUnHealthy: "アプリケーションのステータス取得が異常です。ノードリストでインストールノードのステータスを確認してください。"
MasterNodePortNotAvailable: "ノード {{ .name }} のポート {{ .port }} の接続性検証が失敗しました。ファイアウォール/セキュリティグループの設定とマスターノードのステータスを確認してください。"
ClusterMasterNotExist: "クラスタのマスターノードが切断されています。子ノードを削除してください。"
ClusterMasterNotExist: "クラスタのマスターノードが切断されています。子ノードを削除してください。"

#ssl
ErrReqFailed: "{{.name}} リクエスト失敗: {{ .err }}"
5 changes: 4 additions & 1 deletion core/i18n/lang/ko.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -227,4 +227,7 @@ ErrMasterDelete: "마스터 노드를 삭제할 수 없습니다. 슬레이브
ClusterNameIsExist: "클러스터 이름이 이미 존재합니다."
AppStatusUnHealthy: "애플리케이션 상태 획득이 비정상입니다. 노드 목록에서 설치 노드 상태를 확인하세요."
MasterNodePortNotAvailable: "노드 {{ .name }} 포트 {{ .port }} 연결성 검증에 실패했습니다. 방화벽/보안 그룹 설정 및 마스터 노드 상태를 확인하세요."
ClusterMasterNotExist: "클러스터의 마스터 노드가 연결이 끊어졌습니다. 자식 노드를 삭제하세요."
ClusterMasterNotExist: "클러스터의 마스터 노드가 연결이 끊어졌습니다. 자식 노드를 삭제하세요."

#ssl
ErrReqFailed: "{{.name}} 요청 실패: {{ .err }}"
Loading
Loading