mirror of
https://github.com/alireza0/s-ui.git
synced 2026-08-04 15:28:45 +00:00
fix certificate conflict #1153
Some checks are pending
Release S-UI / build-frontend (push) Waiting to run
Release S-UI / build-386 (push) Blocked by required conditions
Release S-UI / build-amd64 (push) Blocked by required conditions
Release S-UI / build-armv5 (push) Blocked by required conditions
Release S-UI / build-armv6 (push) Blocked by required conditions
Release S-UI / build-armv7 (push) Blocked by required conditions
Release S-UI / build-arm64 (push) Blocked by required conditions
Release S-UI / build-s390x (push) Blocked by required conditions
Build S-UI for Windows / build-frontend (push) Waiting to run
Build S-UI for Windows / build-windows-amd64 (push) Blocked by required conditions
Build S-UI for Windows / build-windows-arm64 (push) Blocked by required conditions
Some checks are pending
Release S-UI / build-frontend (push) Waiting to run
Release S-UI / build-386 (push) Blocked by required conditions
Release S-UI / build-amd64 (push) Blocked by required conditions
Release S-UI / build-armv5 (push) Blocked by required conditions
Release S-UI / build-armv6 (push) Blocked by required conditions
Release S-UI / build-armv7 (push) Blocked by required conditions
Release S-UI / build-arm64 (push) Blocked by required conditions
Release S-UI / build-s390x (push) Blocked by required conditions
Build S-UI for Windows / build-frontend (push) Waiting to run
Build S-UI for Windows / build-windows-amd64 (push) Blocked by required conditions
Build S-UI for Windows / build-windows-arm64 (push) Blocked by required conditions
This commit is contained in:
parent
3b81e7656f
commit
b58de20ce4
5 changed files with 147 additions and 3 deletions
117
cmd/migration/1_5.go
Normal file
117
cmd/migration/1_5.go
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
package migration
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/alireza0/s-ui/util"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func to1_5_1(tx *gorm.DB) error {
|
||||
type tlsRow struct {
|
||||
Id uint
|
||||
Server []byte
|
||||
Client []byte
|
||||
}
|
||||
var tlsRows []tlsRow
|
||||
if err := tx.Raw("SELECT id, server, client FROM tls").Scan(&tlsRows).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pinByTlsId := make(map[uint]string, len(tlsRows))
|
||||
for _, row := range tlsRows {
|
||||
var server map[string]interface{}
|
||||
if len(row.Server) > 0 {
|
||||
_ = json.Unmarshal(row.Server, &server)
|
||||
}
|
||||
|
||||
pin := ""
|
||||
isReality := false
|
||||
if r, ok := server["reality"].(map[string]interface{}); ok {
|
||||
isReality, _ = r["enabled"].(bool)
|
||||
}
|
||||
// Only self-signed certificates are pinned.
|
||||
if !isReality {
|
||||
if certPEM := util.CertPEMFromTLS(server); util.CertIsSelfSigned(certPEM) {
|
||||
pin = util.CertPublicKeySha256(certPEM)
|
||||
}
|
||||
}
|
||||
pinByTlsId[row.Id] = pin
|
||||
|
||||
var client map[string]interface{}
|
||||
if len(row.Client) > 0 {
|
||||
if err := json.Unmarshal(row.Client, &client); err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if client == nil {
|
||||
client = map[string]interface{}{}
|
||||
}
|
||||
if applyTlsPin(client, pin) {
|
||||
newClient, err := json.MarshalIndent(client, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Exec("UPDATE tls SET client = ? WHERE id = ?", newClient, row.Id).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type inboundRow struct {
|
||||
Id uint
|
||||
TlsId uint
|
||||
OutJson []byte
|
||||
}
|
||||
var inbounds []inboundRow
|
||||
if err := tx.Raw("SELECT id, tls_id, out_json FROM inbounds WHERE tls_id > 0").Scan(&inbounds).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
for _, in := range inbounds {
|
||||
if len(in.OutJson) == 0 {
|
||||
continue
|
||||
}
|
||||
var out map[string]interface{}
|
||||
if err := json.Unmarshal(in.OutJson, &out); err != nil {
|
||||
continue
|
||||
}
|
||||
tlsM, ok := out["tls"].(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if applyTlsPin(tlsM, pinByTlsId[in.TlsId]) {
|
||||
out["tls"] = tlsM
|
||||
newOut, err := json.MarshalIndent(out, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Exec("UPDATE inbounds SET out_json = ? WHERE id = ?", newOut, in.Id).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyTlsPin(tls map[string]interface{}, pin string) bool {
|
||||
changed := false
|
||||
if pin != "" {
|
||||
if cur, _ := tls["certificate_public_key_sha256"].([]interface{}); len(cur) != 1 || cur[0] != pin {
|
||||
tls["certificate_public_key_sha256"] = []string{pin}
|
||||
changed = true
|
||||
}
|
||||
if _, ok := tls["certificate"]; ok {
|
||||
delete(tls, "certificate")
|
||||
changed = true
|
||||
}
|
||||
if _, ok := tls["certificate_path"]; ok {
|
||||
delete(tls, "certificate_path")
|
||||
changed = true
|
||||
}
|
||||
} else if _, ok := tls["certificate_public_key_sha256"]; ok {
|
||||
delete(tls, "certificate_public_key_sha256")
|
||||
changed = true
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
|
@ -74,6 +74,15 @@ func MigrateDb() {
|
|||
}
|
||||
}
|
||||
|
||||
// Before 1.5.1: back-fill self-signed TLS public-key pins and rewrite OutJson
|
||||
if dbVersion < "1.5.1" {
|
||||
err = to1_5_1(tx)
|
||||
if err != nil {
|
||||
log.Fatal("Migration to 1.5.1 failed: ", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Set version
|
||||
err = tx.Exec("UPDATE settings SET value = ? WHERE key = ?", currentVersion, "version").Error
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -118,7 +118,9 @@ func setCertFingerprint(t *model.Tls) {
|
|||
isReality, _ = r["enabled"].(bool)
|
||||
}
|
||||
if !isReality {
|
||||
pin = util.CertPublicKeySha256(util.CertPEMFromTLS(server))
|
||||
if certPEM := util.CertPEMFromTLS(server); util.CertIsSelfSigned(certPEM) {
|
||||
pin = util.CertPublicKeySha256(certPEM)
|
||||
}
|
||||
}
|
||||
|
||||
var client map[string]interface{}
|
||||
|
|
@ -133,6 +135,8 @@ func setCertFingerprint(t *model.Tls) {
|
|||
|
||||
if pin != "" {
|
||||
client["certificate_public_key_sha256"] = []string{pin}
|
||||
delete(client, "certificate")
|
||||
delete(client, "certificate_path")
|
||||
} else {
|
||||
delete(client, "certificate_public_key_sha256")
|
||||
}
|
||||
|
|
|
|||
12
util/cert.go
12
util/cert.go
|
|
@ -56,6 +56,18 @@ func parseLeafCert(pemData string) *x509.Certificate {
|
|||
}
|
||||
}
|
||||
|
||||
// CertIsSelfSigned reports whether the leaf certificate in pemData is
|
||||
// self-signed, i.e. its signature verifies against its own public key. Only
|
||||
// self-signed certificates should be pinned via certificate_public_key_sha256;
|
||||
// CA-signed certificates are validated normally.
|
||||
func CertIsSelfSigned(pemData string) bool {
|
||||
cert := parseLeafCert(pemData)
|
||||
if cert == nil {
|
||||
return false
|
||||
}
|
||||
return cert.CheckSignature(cert.SignatureAlgorithm, cert.RawTBSCertificate, cert.Signature) == nil
|
||||
}
|
||||
|
||||
// CertPublicKeySha256 returns the base64-encoded SHA256 of the certificate's
|
||||
// SubjectPublicKeyInfo (sing-box `certificate_public_key_sha256` / link pinSHA256).
|
||||
func CertPublicKeySha256(pemData string) string {
|
||||
|
|
|
|||
|
|
@ -101,8 +101,10 @@ func addTls(out *map[string]interface{}, tls *model.Tls) {
|
|||
if maxVersion, ok := tlsServer["max_version"]; ok {
|
||||
tlsConfig["max_version"] = maxVersion
|
||||
}
|
||||
if certificate, ok := tlsServer["certificate"]; ok {
|
||||
tlsConfig["certificate"] = certificate
|
||||
if _, pinned := tlsConfig["certificate_public_key_sha256"]; !pinned {
|
||||
if certificate, ok := tlsServer["certificate"]; ok {
|
||||
tlsConfig["certificate"] = certificate
|
||||
}
|
||||
}
|
||||
if cipherSuites, ok := tlsServer["cipher_suites"]; ok {
|
||||
tlsConfig["cipher_suites"] = cipherSuites
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue