mirror of
https://github.com/phishingclub/phishingclub.git
synced 2026-07-05 11:57:54 +02:00
Add support for custom certificates in proxy configuration
This commit is contained in:
@@ -61,7 +61,9 @@ func setupCertMagic(
|
||||
if conf.TLSAuto() && conf.TLSHost() == name {
|
||||
return nil
|
||||
}
|
||||
// check phishing host with managed TLS
|
||||
// allow on-demand ACME only for domains that use managed TLS (let's encrypt / acme).
|
||||
// own_managed_tls and self_signed_tls domains must never trigger ACME acquisition —
|
||||
// their certificates are provided manually or generated internally.
|
||||
res := db.
|
||||
Select("id").
|
||||
Where("name = ?", name).
|
||||
|
||||
@@ -20,6 +20,10 @@ type Proxy struct {
|
||||
Description nullable.Nullable[vo.OptionalString1024] `json:"description"`
|
||||
StartURL nullable.Nullable[vo.String1024] `json:"startURL"`
|
||||
ProxyConfig nullable.Nullable[vo.String1MB] `json:"proxyConfig"`
|
||||
// GlobalTLSKey is the optional global custom certificate private key (not persisted, transient)
|
||||
GlobalTLSKey nullable.Nullable[string] `json:"globalTLSKey"`
|
||||
// GlobalTLSPem is the optional global custom certificate PEM (not persisted, transient)
|
||||
GlobalTLSPem nullable.Nullable[string] `json:"globalTLSPem"`
|
||||
|
||||
Company *Company `json:"-"`
|
||||
}
|
||||
|
||||
@@ -903,6 +903,19 @@ func (d *Domain) handleOwnManagedTLS(
|
||||
)
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
// evict any previously cached certs for this domain so the stale cert is not served
|
||||
existingCerts := d.CertMagicCache.AllMatchingCertificates(name)
|
||||
if len(existingCerts) > 0 {
|
||||
hashes := make([]string, 0, len(existingCerts))
|
||||
for _, c := range existingCerts {
|
||||
hashes = append(hashes, c.Hash())
|
||||
}
|
||||
d.CertMagicCache.Remove(hashes)
|
||||
d.Logger.Debugw("evicted stale cached certs before replacing",
|
||||
"domain", name,
|
||||
"count", len(hashes),
|
||||
)
|
||||
}
|
||||
// Create fresh buffers for caching since upload consumed the original buffers
|
||||
keyBufferForCache := bytes.NewBufferString(key)
|
||||
pemBufferForCache := bytes.NewBufferString(pem)
|
||||
@@ -910,7 +923,7 @@ func (d *Domain) handleOwnManagedTLS(
|
||||
ctx,
|
||||
pemBufferForCache.Bytes(),
|
||||
keyBufferForCache.Bytes(),
|
||||
[]string{name},
|
||||
[]string{},
|
||||
)
|
||||
if err != nil {
|
||||
d.Logger.Errorw(
|
||||
@@ -1033,7 +1046,7 @@ func (d *Domain) handleSelfSignedTLS(
|
||||
ctx,
|
||||
pemBytes,
|
||||
keyBytes,
|
||||
[]string{name},
|
||||
[]string{},
|
||||
)
|
||||
if err != nil {
|
||||
d.Logger.Errorw(
|
||||
|
||||
+134
-3
@@ -68,6 +68,7 @@ type ProxyServiceRules struct {
|
||||
// TLS modes:
|
||||
// - "managed": Use Let's Encrypt for automatic certificate management (DEFAULT)
|
||||
// - "self-signed": Use automatically generated self-signed certificates
|
||||
// - "custom": Use a manually supplied certificate (key + pem)
|
||||
//
|
||||
// Configuration can be set globally and overridden per-host:
|
||||
//
|
||||
@@ -79,7 +80,7 @@ type ProxyServiceRules struct {
|
||||
// tls:
|
||||
// mode: "self-signed" # override global setting
|
||||
type ProxyServiceTLSConfig struct {
|
||||
Mode string `yaml:"mode"` // "managed" | "self-signed"
|
||||
Mode string `yaml:"mode"` // "managed" | "self-signed" | "custom"
|
||||
}
|
||||
|
||||
// ProxyServiceImpersonateConfig represents client impersonation configuration
|
||||
@@ -756,6 +757,13 @@ func (m *Proxy) UpdateByID(
|
||||
if v, err := proxy.ProxyConfig.Get(); err == nil {
|
||||
current.ProxyConfig.Set(v)
|
||||
}
|
||||
// copy transient cert fields — not persisted to db, but needed by syncProxyDomains
|
||||
if v, err := proxy.GlobalTLSKey.Get(); err == nil {
|
||||
current.GlobalTLSKey.Set(v)
|
||||
}
|
||||
if v, err := proxy.GlobalTLSPem.Get(); err == nil {
|
||||
current.GlobalTLSPem.Set(v)
|
||||
}
|
||||
|
||||
// validate updated Proxy configuration
|
||||
if err := m.validateProxyConfigForUpdate(ctx, current, id); err != nil {
|
||||
@@ -890,6 +898,11 @@ func (m *Proxy) validateProxyConfigForUpdate(ctx context.Context, proxy *model.P
|
||||
)
|
||||
}
|
||||
|
||||
// validate domain-specific TLS config
|
||||
if err := m.validateTLSConfig(domainConfig.TLS); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// validate domain-specific access control
|
||||
if err := m.validateAccessControl(domainConfig.Access); err != nil {
|
||||
return err
|
||||
@@ -911,6 +924,9 @@ func (m *Proxy) validateProxyConfigForUpdate(ctx context.Context, proxy *model.P
|
||||
|
||||
// validate global rules
|
||||
if config.Global != nil {
|
||||
if err := m.validateTLSConfig(config.Global.TLS); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := m.validateAccessControl(config.Global.Access); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -930,6 +946,52 @@ func (m *Proxy) validateProxyConfigForUpdate(ctx context.Context, proxy *model.P
|
||||
}
|
||||
}
|
||||
|
||||
// validate custom TLS domains have a cert — either a new one is supplied or the domain already has one
|
||||
newCertProvided := false
|
||||
if globalKey, keyErr := proxy.GlobalTLSKey.Get(); keyErr == nil && len(globalKey) > 0 {
|
||||
if globalPem, pemErr := proxy.GlobalTLSPem.Get(); pemErr == nil && len(globalPem) > 0 {
|
||||
newCertProvided = true
|
||||
}
|
||||
}
|
||||
if !newCertProvided {
|
||||
// resolve the effective TLS mode for each host and check if any require a cert we don't have
|
||||
for _, domainConfig := range config.Hosts {
|
||||
if domainConfig == nil {
|
||||
continue
|
||||
}
|
||||
// determine effective TLS mode for this host
|
||||
effectiveTLS := ""
|
||||
if domainConfig.TLS != nil && domainConfig.TLS.Mode != "" {
|
||||
effectiveTLS = domainConfig.TLS.Mode
|
||||
} else if config.Global != nil && config.Global.TLS != nil {
|
||||
effectiveTLS = config.Global.TLS.Mode
|
||||
}
|
||||
if effectiveTLS != "custom" {
|
||||
continue
|
||||
}
|
||||
// check if the phishing domain already has a custom cert in the db
|
||||
phishingDomain, err := vo.NewString255(domainConfig.To)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
existingDomain, err := m.DomainRepository.GetByName(ctx, phishingDomain, &repository.DomainOption{})
|
||||
if err != nil || existingDomain == nil {
|
||||
// new domain — no existing cert possible
|
||||
return validate.WrapErrorWithField(
|
||||
fmt.Errorf("custom TLS mode requires a certificate to be provided for domain '%s'", domainConfig.To),
|
||||
"proxyConfig",
|
||||
)
|
||||
}
|
||||
if !existingDomain.OwnManagedTLS.MustGet() {
|
||||
// existing domain but no custom cert yet
|
||||
return validate.WrapErrorWithField(
|
||||
fmt.Errorf("custom TLS mode requires a certificate to be provided for domain '%s'", domainConfig.To),
|
||||
"proxyConfig",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1546,9 +1608,9 @@ func (m *Proxy) validateTLSConfig(tlsConfig *ProxyServiceTLSConfig) error {
|
||||
}
|
||||
|
||||
// validate TLS mode
|
||||
if tlsConfig.Mode != "" && tlsConfig.Mode != "managed" && tlsConfig.Mode != "self-signed" {
|
||||
if tlsConfig.Mode != "" && tlsConfig.Mode != "managed" && tlsConfig.Mode != "self-signed" && tlsConfig.Mode != "custom" {
|
||||
return validate.WrapErrorWithField(
|
||||
errors.New("tls.mode must be either 'managed' or 'self-signed'"),
|
||||
errors.New("tls.mode must be either 'managed', 'self-signed', or 'custom'"),
|
||||
"proxyConfig",
|
||||
)
|
||||
}
|
||||
@@ -2399,6 +2461,28 @@ func (m *Proxy) createProxyDomains(ctx context.Context, session *model.Session,
|
||||
domain.ManagedTLS.Set(false)
|
||||
domain.OwnManagedTLS.Set(false)
|
||||
domain.SelfSignedTLS.Set(true)
|
||||
} else if tlsMode == "custom" {
|
||||
// check if a global cert is provided on the proxy model
|
||||
globalKey, keyErr := proxy.GlobalTLSKey.Get()
|
||||
globalPem, pemErr := proxy.GlobalTLSPem.Get()
|
||||
if keyErr == nil && pemErr == nil && len(globalKey) > 0 && len(globalPem) > 0 {
|
||||
// apply the global cert to this domain
|
||||
domain.ManagedTLS.Set(false)
|
||||
domain.OwnManagedTLS.Set(true)
|
||||
domain.SelfSignedTLS.Set(false)
|
||||
domain.OwnManagedTLSKey.Set(globalKey)
|
||||
domain.OwnManagedTLSPem.Set(globalPem)
|
||||
} else {
|
||||
// no cert provided for a new domain — cannot configure custom TLS without a certificate
|
||||
m.Logger.Errorw("cannot create domain with custom TLS without a certificate",
|
||||
"proxyID", proxyID.String(),
|
||||
"domain", domainConfig.To,
|
||||
)
|
||||
m.rollbackCreatedDomains(ctx, session, createdDomains)
|
||||
return errs.NewValidationError(
|
||||
fmt.Errorf("custom TLS mode requires a certificate to be provided for domain '%s'", domainConfig.To),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// default to managed
|
||||
domain.ManagedTLS.Set(true)
|
||||
@@ -2661,18 +2745,44 @@ func (m *Proxy) syncProxyDomains(ctx context.Context, session *model.Session, pr
|
||||
// check current TLS settings
|
||||
currentManagedTLS := existingDomain.ManagedTLS.MustGet()
|
||||
currentSelfSignedTLS := existingDomain.SelfSignedTLS.MustGet()
|
||||
currentOwnManagedTLS := existingDomain.OwnManagedTLS.MustGet()
|
||||
|
||||
// determine if TLS settings need updating
|
||||
if tlsMode == "self-signed" && !currentSelfSignedTLS {
|
||||
// switching to self-signed — clear any existing custom cert flag so updateDomain cleans it up
|
||||
existingDomain.ManagedTLS.Set(false)
|
||||
existingDomain.OwnManagedTLS.Set(false)
|
||||
existingDomain.SelfSignedTLS.Set(true)
|
||||
needsUpdate = true
|
||||
} else if tlsMode == "managed" && !currentManagedTLS {
|
||||
// switching to managed — clear any existing custom cert flag so updateDomain cleans it up
|
||||
existingDomain.ManagedTLS.Set(true)
|
||||
existingDomain.OwnManagedTLS.Set(false)
|
||||
existingDomain.SelfSignedTLS.Set(false)
|
||||
needsUpdate = true
|
||||
} else if tlsMode == "custom" {
|
||||
// check if a new global cert is being pushed
|
||||
globalKey, keyErr := proxy.GlobalTLSKey.Get()
|
||||
globalPem, pemErr := proxy.GlobalTLSPem.Get()
|
||||
if keyErr == nil && pemErr == nil && len(globalKey) > 0 && len(globalPem) > 0 {
|
||||
// apply the new global cert — always update when a cert is explicitly provided
|
||||
existingDomain.ManagedTLS.Set(false)
|
||||
existingDomain.OwnManagedTLS.Set(true)
|
||||
existingDomain.SelfSignedTLS.Set(false)
|
||||
existingDomain.OwnManagedTLSKey.Set(globalKey)
|
||||
existingDomain.OwnManagedTLSPem.Set(globalPem)
|
||||
needsUpdate = true
|
||||
} else if !currentOwnManagedTLS {
|
||||
// no new cert provided and domain doesn't already have a custom cert — cannot switch to custom
|
||||
m.Logger.Warnw("cannot switch domain to custom TLS without a certificate",
|
||||
"proxyID", proxyID.String(),
|
||||
"domain", phishingDomain,
|
||||
)
|
||||
syncErrors = append(syncErrors, fmt.Sprintf("cannot switch domain '%s' to custom TLS without providing a certificate", phishingDomain))
|
||||
errorCount++
|
||||
continue
|
||||
}
|
||||
// if currentOwnManagedTLS is true and no new cert provided — preserve existing cert, no update needed
|
||||
}
|
||||
|
||||
if needsUpdate {
|
||||
@@ -2735,6 +2845,27 @@ func (m *Proxy) syncProxyDomains(ctx context.Context, session *model.Session, pr
|
||||
domain.ManagedTLS.Set(false)
|
||||
domain.OwnManagedTLS.Set(false)
|
||||
domain.SelfSignedTLS.Set(true)
|
||||
} else if tlsMode == "custom" {
|
||||
// check if a global cert is provided on the proxy model
|
||||
globalKey, keyErr := proxy.GlobalTLSKey.Get()
|
||||
globalPem, pemErr := proxy.GlobalTLSPem.Get()
|
||||
if keyErr == nil && pemErr == nil && len(globalKey) > 0 && len(globalPem) > 0 {
|
||||
// apply the global cert to this domain
|
||||
domain.ManagedTLS.Set(false)
|
||||
domain.OwnManagedTLS.Set(true)
|
||||
domain.SelfSignedTLS.Set(false)
|
||||
domain.OwnManagedTLSKey.Set(globalKey)
|
||||
domain.OwnManagedTLSPem.Set(globalPem)
|
||||
} else {
|
||||
// no cert provided for a new domain — cannot configure custom TLS without a certificate
|
||||
m.Logger.Warnw("cannot add domain with custom TLS without a certificate",
|
||||
"proxyID", proxyID.String(),
|
||||
"domain", phishingDomain,
|
||||
)
|
||||
syncErrors = append(syncErrors, fmt.Sprintf("cannot add domain '%s' with custom TLS without providing a certificate", phishingDomain))
|
||||
errorCount++
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// default to managed
|
||||
domain.ManagedTLS.Set(true)
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCixyMt5qK2ulK3
|
||||
t7dS1BkNaxbuZNexYLCmWSWUN9tdboAKIapLPcV3/jwqZwP04kaU/ObUp+1vXjK9
|
||||
2dJXASZQ5HuZO3438oCsgeKGe8FRsbuK/2gfXbWrLxDhHW7hTlwhuuC/SRe2u+n1
|
||||
Qjrz67iQQ1QdfdE3HhLzllU9SFPcIW+l1T7oILJt0NLdEbjBoecxP1tE7DDw7E87
|
||||
y0ipPDBz5Q9ZwS95vaJG013MvM7Ly5gg6+4E108JKw5Rl0NoUZVhi1bNJcrg1n2K
|
||||
R7aqX+URtcA8sTMEKqXzTGUvXxvGsXESwlttb5tHjwKaRxLRBqSi0IEoad0h+Vlx
|
||||
o/z+IPQdAgMBAAECggEADBEbtdCvbQ2779wB9MIKlDJe0hzBc2gTc4l2ETPg1av8
|
||||
eIPNX+PvSkY1B8ze/+pxFaT320Z4zs6aj8HHGrI+bAG8FEk5dQR+1T4qekWDUiVZ
|
||||
6n0+wHbnodrzDK64/dSbkAtbLNYwy75h1hyEsHj5eh+CBU7cUXjJXXheaK5+ocaH
|
||||
swmkXMigidMkfzAURhwhJ4ce6A3F9wVe8+SJlOHCx5m5usIXthg9gTYgq6EymALt
|
||||
C1zRlO2xteB6qzvHu1FgDKWkHmX1PgiKr1L3gBNjJutuY8TgnbiaQ9pRMlaeE850
|
||||
ctxGL8Hg11llA6sBGdUuUEA2UlUMJEIadJjVqcEjKwKBgQDN+OyGiOimY+bjjBW6
|
||||
NLq8/f/T2T549DGsSNt+EiGj+gJlif1jZqVtgtjRta/kbwF5f6Zx3Q9I4J69iw8c
|
||||
wvmS1ip4UXhrJKua+Wvk6mEeTAejazH46paOgX97TUfmsSQC2+R2iM8xKNxbpxCD
|
||||
k4wNPt1rNeTCA/SBF3dKDlqrPwKBgQDKUHDM+/YIGuXa8DAe34E/Tl2Onq/R9yA4
|
||||
WNXnjKsOk/aK/fshzVIS7wcGpXzK74i9ds1z19ojUgYI4qV7XcZ9DiXewqP8tXtI
|
||||
llNU8DXUOBrFX65d//1XiwU5b8bplXwiOB1Jd5hue0HPscsaic0EYS/j2ByXD+0l
|
||||
8XJ3j/ZVowKBgQCwxBSZUR34znv0hOCQsXghggrwEN0giNGofc6BX6YnSASOh+JC
|
||||
UHFgjo7tSvPtI6csUnTR+1mGvd795D3P/TSa49oG8ERcD1iG48/I4az/h1h20yRL
|
||||
72fOXSy+8Q/n19aD7Zsgb0EBe4PB1JrDkPj81RrJS7NLHoHT2AO0NqVxmQKBgDtw
|
||||
ApPWemPLMzhtVFXdqCUnKslZyaHQDsE/KCjM5Px1b/tJvtwhbDlvzAqh19XvJac0
|
||||
HgwooEe8M1Ws8J0b4dKfs3SMjo0R7FRZBcZwhAADM6pE//9R0+ZCS5iiRDgf2MZc
|
||||
4g3RexEKWT1hqJ/1WCwvOVihB1VCMpPxKYYC34YtAoGAexR7JhjgjztmP65HzyCv
|
||||
hff37H89MomRWUyaAlQ8Sqpj9bTMfcpsAmZ2qMGWKgUCfwQc1n3jRuSrWhotGWKZ
|
||||
1n3xQ5q3IgBIOWBJHJ1Ez4HF9F0xVrrwPjYH/OoFBPQKU6QGkIr68YsQq+Yc+0BF
|
||||
ReemAd9XD2vSRrBh1/WJKaI=
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDHzCCAgegAwIBAgIUNv0gRyo+x0RDbBFBxmP/T3CfVowwDQYJKoZIhvcNAQEL
|
||||
BQAwFDESMBAGA1UEAwwJY2VydC50ZXN0MB4XDTI2MDQxOTEzMDQyOFoXDTM2MDQx
|
||||
NjEzMDQyOFowFDESMBAGA1UEAwwJY2VydC50ZXN0MIIBIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAQ8AMIIBCgKCAQEAoscjLeaitrpSt7e3UtQZDWsW7mTXsWCwplkllDfbXW6A
|
||||
CiGqSz3Fd/48KmcD9OJGlPzm1Kftb14yvdnSVwEmUOR7mTt+N/KArIHihnvBUbG7
|
||||
iv9oH121qy8Q4R1u4U5cIbrgv0kXtrvp9UI68+u4kENUHX3RNx4S85ZVPUhT3CFv
|
||||
pdU+6CCybdDS3RG4waHnMT9bROww8OxPO8tIqTwwc+UPWcEveb2iRtNdzLzOy8uY
|
||||
IOvuBNdPCSsOUZdDaFGVYYtWzSXK4NZ9ike2ql/lEbXAPLEzBCql80xlL18bxrFx
|
||||
EsJbbW+bR48CmkcS0QakotCBKGndIflZcaP8/iD0HQIDAQABo2kwZzAdBgNVHQ4E
|
||||
FgQUWtHAW8LyyJpevjePLzgRSQBTP9wwHwYDVR0jBBgwFoAUWtHAW8LyyJpevjeP
|
||||
LzgRSQBTP9wwDwYDVR0TAQH/BAUwAwEB/zAUBgNVHREEDTALggljZXJ0LnRlc3Qw
|
||||
DQYJKoZIhvcNAQELBQADggEBADf3QqqNrG97rxTe+QmCltKRD/4Rj9tekoXKPi/c
|
||||
E1343ZwfH2WWnhZFQnZnWeck8BhXqEjvxmRP7IHOj+WyeE0l+rANR3YvmqkSrqs3
|
||||
N0JF47OfabyNUYJIO5wKochCUqHiM1yyET3RD4Mcz435SNRCdk6G9ytLt+19zrRx
|
||||
7h+vvh7Bmw3mhCgcPwdBcmtAwoVlVmdwzwpVZ7GBpr9SlA7WxQo4HYEd66XJK7Pk
|
||||
p58qWkDHHBPIOO4DvuuvBwU4VMl4TynDecAK6qbIDghUaSDeWDHhN8rpLsuOjYwI
|
||||
6ZzoKb4FiZjYn8QhKcTSJjf082s66ovfml0JglZp6AScYq8=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,28 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEszCCApsCFD5xluzzeuIZbCYcshr+3OfR3e0eMA0GCSqGSIb3DQEBDQUAMBYx
|
||||
FDASBgNVBAMMC215Y2VydC50ZXN0MB4XDTI2MDQxNjIxMDg1MloXDTM2MDQxMzIx
|
||||
MDg1MlowFjEUMBIGA1UEAwwLbXljZXJ0LnRlc3QwggIiMA0GCSqGSIb3DQEBAQUA
|
||||
A4ICDwAwggIKAoICAQC0qRS0tUDPwLVNEuB6j8ADS9RQQIZb7jUULHzpi9t1z8QZ
|
||||
u0mDI21s/GU1lHER+S4f13Df1Dvp7bSl/nR6Pkq9PYMR+h2ExHqHhQII4kY2cxaC
|
||||
6fZdIeyxIGaYnlIRH05fqG8NbKGhhAJ34APVp8nptA+NSwwk9upiJzbUfMb8yxBQ
|
||||
YaT9Q194J5LlQK5Rge9bJEDrATSTqOVBq6227yQso1nr5nEpbwHfDmU44py/OLKf
|
||||
byYvjEI9PGUBJE6rjC5iS23RKHh/mBSeGLuEF8XnXqDV2EfWkozpTBri0RdTVbNP
|
||||
aYIpgueYsMTGoRXJ/I0amGaju0uqGbqbObkqFeSZOyPg3zssmqQv7bMXSzJjt10X
|
||||
ZcXKu62Ba064o8gKJ4wQ/lg5aJp1ysjVZViigJbnbm1KFuKpBUq5WFN0MrViVsWL
|
||||
ACwETw1MjA8bLjHfcwSG/8dB3vnHLgulvWZut0g6nJaX2fPxmEPbSboh9wbdB5o4
|
||||
iCcYx1X3bc0cx6r80caCnICvLjYcf+i89TUPpXW7UqlZngjoohIfhhb6fKPWxT6v
|
||||
w4dG2SUtRhoRzLP9GWbawKnlrEuctVTbAYnN6BtluqGX1DfEMp0j+wZl6o2YMyQ7
|
||||
6bQRa6LT9Ddmq1QHXgvVn81+d2pJDOefTVDAacWij6wIrpjWg/z8qx1kFJPW5wID
|
||||
AQABMA0GCSqGSIb3DQEBDQUAA4ICAQCVRRmMY8zliozIA4s5O2dYXaPM6wnGErmx
|
||||
QzEryE6hQT6gIkPrJExHefokrKAMZy1+waslugIYbtURDzqu9O+zJaf8WOzhYSxL
|
||||
+3NLFemfBVvP68c3g6y2Hy3gSZ2ZZb4yeiG8aHB74rl5yxaUmdJ690zDBaJ7NC5G
|
||||
ZS84iSWZMCjAcqViLqQ1RvxybcAduW5vPVPej4U3HIdUQ6YvOo+7x+BNW5Ost4xv
|
||||
ytR1IZBRdxdmXQEOp049EENzqLPq9hvwoUgKZqj9QZT5CEO985Cvo0U9BPpsGWg6
|
||||
YcecWXues/TmacJA55rCdJ279wiQKTmaLvI1D3vpdbYBdpAwH6l4eMmb7XOppa8s
|
||||
50hJpPoxtQ5aqU6Twyp0mhf2/ie70oRoS0UqrLMlR/dArw3qDZqYt7p/frsIHMgu
|
||||
dhbwdFhczXTQTl+Fz+k96nFYN8r9CYZrxzOi2fZCUdN42MqAfSeeqm6EwfCOHM6G
|
||||
Mhccf3ZfnSKLXkfL/jiyPtZRmNaO+zAJSjP7VKMyeqk8M4XYQnWBci1JrWcBy7mV
|
||||
dD9WUb51yUKmRIggOKzNuXp9splFP9yZTUFhtz4ztU6HExTwnADQRQFB7crLaoTU
|
||||
CuhJo3vxZGds7FDcw3/NYRxWsjXcKwcE3nBewVSfdJCZBkcA4KztuCuqTB23rO6E
|
||||
Ew+AoWp2AQ==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,52 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
4lF4+z9bxHS9QBHIkTOjBYGeVGkSpQ==
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -0,0 +1,80 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEszCCApsCFD5xluzzeuIZbCYcshr+3OfR3e0eMA0GCSqGSIb3DQEBDQUAMBYx
|
||||
FDASBgNVBAMMC215Y2VydC50ZXN0MB4XDTI2MDQxNjIxMDg1MloXDTM2MDQxMzIx
|
||||
MDg1MlowFjEUMBIGA1UEAwwLbXljZXJ0LnRlc3QwggIiMA0GCSqGSIb3DQEBAQUA
|
||||
A4ICDwAwggIKAoICAQC0qRS0tUDPwLVNEuB6j8ADS9RQQIZb7jUULHzpi9t1z8QZ
|
||||
u0mDI21s/GU1lHER+S4f13Df1Dvp7bSl/nR6Pkq9PYMR+h2ExHqHhQII4kY2cxaC
|
||||
6fZdIeyxIGaYnlIRH05fqG8NbKGhhAJ34APVp8nptA+NSwwk9upiJzbUfMb8yxBQ
|
||||
YaT9Q194J5LlQK5Rge9bJEDrATSTqOVBq6227yQso1nr5nEpbwHfDmU44py/OLKf
|
||||
byYvjEI9PGUBJE6rjC5iS23RKHh/mBSeGLuEF8XnXqDV2EfWkozpTBri0RdTVbNP
|
||||
aYIpgueYsMTGoRXJ/I0amGaju0uqGbqbObkqFeSZOyPg3zssmqQv7bMXSzJjt10X
|
||||
ZcXKu62Ba064o8gKJ4wQ/lg5aJp1ysjVZViigJbnbm1KFuKpBUq5WFN0MrViVsWL
|
||||
ACwETw1MjA8bLjHfcwSG/8dB3vnHLgulvWZut0g6nJaX2fPxmEPbSboh9wbdB5o4
|
||||
iCcYx1X3bc0cx6r80caCnICvLjYcf+i89TUPpXW7UqlZngjoohIfhhb6fKPWxT6v
|
||||
w4dG2SUtRhoRzLP9GWbawKnlrEuctVTbAYnN6BtluqGX1DfEMp0j+wZl6o2YMyQ7
|
||||
6bQRa6LT9Ddmq1QHXgvVn81+d2pJDOefTVDAacWij6wIrpjWg/z8qx1kFJPW5wID
|
||||
AQABMA0GCSqGSIb3DQEBDQUAA4ICAQCVRRmMY8zliozIA4s5O2dYXaPM6wnGErmx
|
||||
QzEryE6hQT6gIkPrJExHefokrKAMZy1+waslugIYbtURDzqu9O+zJaf8WOzhYSxL
|
||||
+3NLFemfBVvP68c3g6y2Hy3gSZ2ZZb4yeiG8aHB74rl5yxaUmdJ690zDBaJ7NC5G
|
||||
ZS84iSWZMCjAcqViLqQ1RvxybcAduW5vPVPej4U3HIdUQ6YvOo+7x+BNW5Ost4xv
|
||||
ytR1IZBRdxdmXQEOp049EENzqLPq9hvwoUgKZqj9QZT5CEO985Cvo0U9BPpsGWg6
|
||||
YcecWXues/TmacJA55rCdJ279wiQKTmaLvI1D3vpdbYBdpAwH6l4eMmb7XOppa8s
|
||||
50hJpPoxtQ5aqU6Twyp0mhf2/ie70oRoS0UqrLMlR/dArw3qDZqYt7p/frsIHMgu
|
||||
dhbwdFhczXTQTl+Fz+k96nFYN8r9CYZrxzOi2fZCUdN42MqAfSeeqm6EwfCOHM6G
|
||||
Mhccf3ZfnSKLXkfL/jiyPtZRmNaO+zAJSjP7VKMyeqk8M4XYQnWBci1JrWcBy7mV
|
||||
dD9WUb51yUKmRIggOKzNuXp9splFP9yZTUFhtz4ztU6HExTwnADQRQFB7crLaoTU
|
||||
CuhJo3vxZGds7FDcw3/NYRxWsjXcKwcE3nBewVSfdJCZBkcA4KztuCuqTB23rO6E
|
||||
Ew+AoWp2AQ==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
4lF4+z9bxHS9QBHIkTOjBYGeVGkSpQ==
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC0kJclAABh6Ocf
|
||||
QcCj9+Dh6fUSyefnfqcVG3CYa0F/TENIZLAdQXGSXfprkQnhUQPfPfSOGiJ27Vme
|
||||
sQiUTfLidqRgFXEF4JQgR6oO5au6llBBKU79fkw9UJ4OR2AtE136gGx9aUF4iLzT
|
||||
UU019KNMnY7ICnAnd8dL69hY6pg9rCGiZmiiWAZ4XkxNkN3L5GMHMDNbcEWMCalp
|
||||
6rP75rAvlXlhOUKomEZN1zLMu3FSWuMMCIbc2Anaq5Z2NYmf61YPb48/lklo5Klz
|
||||
s0dN1ktBv7QMybOIP0YZnPNa76SDZ5bi1YYaCQybs6l845vfzEseAew+23RXXkam
|
||||
QLSw2ZldAgMBAAECggEABk9rSNAS3gPCrVVMmIOy0z0BjeeKeDef5TKb9mer1qQL
|
||||
/JKwPkV+5OuYiHvSyZIIPZwFA5ZRkAXEH7H2J8tVlVPUyMWBuVUw4rPpHd4pkzq/
|
||||
kUISpZ9CpiiUGCb8ayGFzkRWBkf7EW2jPmCqqcZj5+s1sIiqL2brNccHPOGYnRxr
|
||||
eN5P+JGNN3Gn+LRy26xGgdaRdGDuWHQJOStQUFQvmLswjPui5hhNtJVvUmvQ/XlP
|
||||
peWSL3mV/07fhBv7PcBxyeUJx+zk8NHSSNDXKT5fkGKAS4FAqtQziXKqqn3G8nKk
|
||||
8jaBPaJli6Ezw6NLlUrUax0DbmQYopHLQgMm6vJ2zQKBgQDpHgKKNxlqha3xf8Y0
|
||||
NCD4T3Ju3cF2T/U/lDqRwZo97wZVnwHIfiG19pfhHbEshYvolUb3Frnro1ba60ed
|
||||
9ePGGATxi7zPETkh15fZF2TJcW1GMBYeD5vTBkkyhdTnPFOBEcArVBQgTUm/XRaR
|
||||
AZRefXM73siXIwdYiKHtIIdPHwKBgQDGSf5bzjfQoaEudrW8K8ZNiDEMZ6hR96so
|
||||
iSMh7q73AwhiEnMacVezAN4WDLqHH8knCd0WC/b7R6ocRDuG8sFOzS9peIiklnn3
|
||||
A768LzFex+6AantOE2LEE5Cx1kjQ2aj2KdGkrXtBZpXMOVDpRSdNZIYPCj8pNgFn
|
||||
rE9abILUAwKBgQCyV60lxIWDQwYSDei6o27dyRoIy0pokz9TBrnQLMctvqGf+2fH
|
||||
1QdBSIhlRuv23axtoVaLTi2qommeTgWaSTWapWGS0Y7+83Q7+c5H3WfT3Rz2Z29k
|
||||
TBiwVszFBDIfPb28rrHP9CD5nWdgKX1MLmMt7ter5AKd7cR+7PjEivA5jQKBgQCC
|
||||
PXuqhUq36FHcGPDJhd8cccX1pegy3oA3gcvnr8SQThelgwTDa4r08i7tQLMLqd8P
|
||||
mzTyFC3HYozjQBXxT2WVAsSPfDIUGRpHGtie9khxPtTy1/3hjG4k58z0YhE1zKFj
|
||||
/pfKmIAKtvzRRRxV+6wS82HyYwKVaPmHRPBiLj/ITQKBgAOG6wa2ZMClX9MtNqMD
|
||||
w0voX4xo+x+4l4v8GgtLQns1wyhW8BG8CYEzogI/Dx5jT9Q787F+gRctjzQPHvm1
|
||||
a4E7MwKDgklAJFaOwNmX7DEqkAmfszLjRZFTleOK594/FEjbWTi5CWqW55pZ/C3S
|
||||
61VYVaqIn84aGCbIaLI5vULr
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDQDCCAiigAwIBAgIUAp5wJoElDOa8IzoIcfUj917Y0MMwDQYJKoZIhvcNAQEL
|
||||
BQAwGjEYMBYGA1UEAwwPKi53aWxkY2FyZC50ZXN0MB4XDTI2MDQxOTEzMTAwNloX
|
||||
DTM2MDQxNjEzMTAwNlowGjEYMBYGA1UEAwwPKi53aWxkY2FyZC50ZXN0MIIBIjAN
|
||||
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtJCXJQAAYejnH0HAo/fg4en1Esnn
|
||||
536nFRtwmGtBf0xDSGSwHUFxkl36a5EJ4VED3z30jhoidu1ZnrEIlE3y4nakYBVx
|
||||
BeCUIEeqDuWrupZQQSlO/X5MPVCeDkdgLRNd+oBsfWlBeIi801FNNfSjTJ2OyApw
|
||||
J3fHS+vYWOqYPawhomZoolgGeF5MTZDdy+RjBzAzW3BFjAmpaeqz++awL5V5YTlC
|
||||
qJhGTdcyzLtxUlrjDAiG3NgJ2quWdjWJn+tWD2+PP5ZJaOSpc7NHTdZLQb+0DMmz
|
||||
iD9GGZzzWu+kg2eW4tWGGgkMm7OpfOOb38xLHgHsPtt0V15GpkC0sNmZXQIDAQAB
|
||||
o34wfDAdBgNVHQ4EFgQU62UOXUjmffiNy6BJm8Qv+3qKJq0wHwYDVR0jBBgwFoAU
|
||||
62UOXUjmffiNy6BJm8Qv+3qKJq0wDwYDVR0TAQH/BAUwAwEB/zApBgNVHREEIjAg
|
||||
gg8qLndpbGRjYXJkLnRlc3SCDXdpbGRjYXJkLnRlc3QwDQYJKoZIhvcNAQELBQAD
|
||||
ggEBAFu8H73GimARkhT9kW/lgVxs8/cvBOjskdUOtmndhLi7p1ile1Md+E59tcm0
|
||||
lp7zMlCsOZGhU0eS70Y/rTL02sSS4pWPARxQr8KqjtqS2+3PBnBuyb8lwh00h1yA
|
||||
G+YnF4fNom7XhBjXhxtKvajrrywyuxOTga4LQ9aqs4qc+ZCH30LYl0ev93bEEMf/
|
||||
MKjEenlfw4KDrxDmBJzMPG8T4npeoigz9olOQFWW1nd+4GfLIpENCzkIhiuvq2JA
|
||||
KVGB3kj7OyECkNk9WA6eeUUu8QMFxv/G9j3BW8Uh6ENLHnEBd1CthSUXv0kTSwQ0
|
||||
ZLygXMZfBxrhaBO/TgUDOZmyYW8=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -3226,15 +3226,27 @@ export class API {
|
||||
* @param {string} proxy.startURL
|
||||
* @param {string} proxy.proxyConfig
|
||||
* @param {string} proxy.companyID
|
||||
* @param {string} [proxy.globalTLSKey]
|
||||
* @param {string} [proxy.globalTLSPem]
|
||||
* @returns {Promise<ApiResponse>}
|
||||
*/
|
||||
create: async ({ name, description, startURL, proxyConfig, companyID }) => {
|
||||
create: async ({
|
||||
name,
|
||||
description,
|
||||
startURL,
|
||||
proxyConfig,
|
||||
companyID,
|
||||
globalTLSKey,
|
||||
globalTLSPem
|
||||
}) => {
|
||||
return await postJSON(this.getPath('/proxy'), {
|
||||
name: name,
|
||||
description: description,
|
||||
startURL: startURL,
|
||||
proxyConfig: proxyConfig,
|
||||
companyID: companyID
|
||||
companyID: companyID,
|
||||
...(globalTLSKey ? { globalTLSKey } : {}),
|
||||
...(globalTLSPem ? { globalTLSPem } : {})
|
||||
});
|
||||
},
|
||||
|
||||
@@ -3247,6 +3259,8 @@ export class API {
|
||||
* @param {string} proxy.description
|
||||
* @param {string} proxy.startURL
|
||||
* @param {string} proxy.proxyConfig
|
||||
* @param {string} [proxy.globalTLSKey]
|
||||
* @param {string} [proxy.globalTLSPem]
|
||||
* @returns {Promise<ApiResponse>}
|
||||
*/
|
||||
update: async (id, proxy) => {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -853,9 +853,9 @@
|
||||
<FileField
|
||||
bind:bindTo={ownManagedPemKeyElement}
|
||||
name="certPem"
|
||||
accept=".pem"
|
||||
accept=".pem,.crt"
|
||||
on:change={(e) => onSetFile(e, 'ownManagedTLSPem')}
|
||||
>Certificate (.pem)</FileField
|
||||
>Certificate (.pem, .crt)</FileField
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
import AutoRefresh from '$lib/components/AutoRefresh.svelte';
|
||||
import TableCellScope from '$lib/components/table/TableCellScope.svelte';
|
||||
import ProxyConfigBuilder from '$lib/components/proxy/ProxyConfigBuilder.svelte';
|
||||
import FileField from '$lib/components/FileField.svelte';
|
||||
|
||||
// services
|
||||
const appStateService = AppStateService.instance;
|
||||
@@ -46,6 +47,24 @@
|
||||
};
|
||||
let isSubmitting = false;
|
||||
|
||||
// cert state for custom TLS mode — populated by certChange event (visual mode) or yaml cert upload fields
|
||||
let globalTLSKey = '';
|
||||
let globalTLSPem = '';
|
||||
|
||||
// detect if the current yaml config uses tls.mode: "custom" anywhere so we can show upload fields in yaml mode
|
||||
$: yamlHasCustomTLS = (() => {
|
||||
if (!formValues.proxyConfig || editorMode !== 'yaml') return false;
|
||||
try {
|
||||
// simple string check first to avoid parsing on every keystroke
|
||||
if (!formValues.proxyConfig.includes('custom')) return false;
|
||||
// dynamic import not available in reactive block — use a regex check on the yaml string
|
||||
// matches: mode: "custom" or mode: 'custom' or mode: custom
|
||||
return /mode\s*:\s*['"]?custom['"]?/.test(formValues.proxyConfig);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
})();
|
||||
|
||||
// data
|
||||
const tableURLParams = newTableURLParams();
|
||||
let contextCompanyID = null;
|
||||
@@ -212,7 +231,7 @@
|
||||
# global configuration (applies to all hosts unless overridden)
|
||||
global:
|
||||
tls:
|
||||
mode: "managed" # "managed" (Let's Encrypt) or "self-signed"
|
||||
mode: "managed" # "managed" (Let's Encrypt), "self-signed", or "custom" (upload cert below)
|
||||
# template variables allow recipient data in rewrite rules
|
||||
# variables:
|
||||
# enabled: true
|
||||
@@ -225,7 +244,7 @@ portal.example.com:
|
||||
# scheme: "https" # use https:// when connecting to target
|
||||
# optional: override global TLS config for this specific host
|
||||
# tls:
|
||||
# mode: "self-signed"
|
||||
# mode: "self-signed" # or "custom" for a manually supplied certificate
|
||||
response:
|
||||
- path: "^/api/health$"
|
||||
headers:
|
||||
@@ -393,8 +412,13 @@ portal.example.com:
|
||||
};
|
||||
|
||||
const res = await api.proxy.create({
|
||||
...proxyData,
|
||||
companyID: contextCompanyID
|
||||
name: proxyData.name,
|
||||
description: proxyData.description,
|
||||
startURL: proxyData.startURL,
|
||||
proxyConfig: proxyData.proxyConfig,
|
||||
companyID: contextCompanyID,
|
||||
globalTLSKey: globalTLSKey || undefined,
|
||||
globalTLSPem: globalTLSPem || undefined
|
||||
});
|
||||
if (!res.success) {
|
||||
formError = res.error;
|
||||
@@ -416,7 +440,9 @@ portal.example.com:
|
||||
name: formValues.name,
|
||||
description: formValues.description,
|
||||
startURL: formValues.startURL,
|
||||
proxyConfig: formValues.proxyConfig
|
||||
proxyConfig: formValues.proxyConfig,
|
||||
globalTLSKey: globalTLSKey || undefined,
|
||||
globalTLSPem: globalTLSPem || undefined
|
||||
};
|
||||
|
||||
const res = await api.proxy.update(formValues.id, updateData);
|
||||
@@ -469,6 +495,26 @@ portal.example.com:
|
||||
formValues.id = '';
|
||||
form.reset();
|
||||
formError = '';
|
||||
globalTLSKey = '';
|
||||
globalTLSPem = '';
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Event} event
|
||||
* @param {'key'|'pem'} target
|
||||
*/
|
||||
const onYamlCertFile = (event, target) => {
|
||||
const file = /** @type {HTMLInputElement} */ (event.target).files?.[0];
|
||||
if (!file) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
if (target === 'key') {
|
||||
globalTLSKey = e.target?.result?.toString() ?? '';
|
||||
} else {
|
||||
globalTLSPem = e.target?.result?.toString() ?? '';
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
};
|
||||
|
||||
/** @param {string} id */
|
||||
@@ -719,6 +765,22 @@ portal.example.com:
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{#if yamlHasCustomTLS}
|
||||
<div class="grid grid-cols-1 md:grid-cols-[1fr_1fr] gap-4 mt-2">
|
||||
<FileField
|
||||
name="yamlGlobalCertKey"
|
||||
accept=".key"
|
||||
optional
|
||||
on:change={(e) => onYamlCertFile(e, 'key')}>Private Key (.key)</FileField
|
||||
>
|
||||
<FileField
|
||||
name="yamlGlobalCertPem"
|
||||
accept=".pem,.crt"
|
||||
optional
|
||||
on:change={(e) => onYamlCertFile(e, 'pem')}>Certificate (.pem, .crt)</FileField
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -830,6 +892,10 @@ portal.example.com:
|
||||
on:nameChange={(e) => (formValues.name = e.detail)}
|
||||
on:descriptionChange={(e) => (formValues.description = e.detail)}
|
||||
on:startURLChange={(e) => (formValues.startURL = e.detail)}
|
||||
on:certChange={(e) => {
|
||||
globalTLSKey = e.detail.globalTLSKey || '';
|
||||
globalTLSPem = e.detail.globalTLSPem || '';
|
||||
}}
|
||||
/>
|
||||
{/key}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user