mirror of
https://github.com/phishingclub/phishingclub.git
synced 2026-02-12 16:12:44 +00:00
add proxy yaml tls directive
Signed-off-by: Ronni Skansing <rskansing@gmail.com>
This commit is contained in:
@@ -41,6 +41,7 @@ type ProxyServiceConfig struct {
|
||||
// ProxyServiceDomainConfig represents configuration for a specific domain mapping
|
||||
type ProxyServiceDomainConfig struct {
|
||||
To string `yaml:"to"`
|
||||
TLS *ProxyServiceTLSConfig `yaml:"tls,omitempty"`
|
||||
Access *ProxyServiceAccessControl `yaml:"access,omitempty"`
|
||||
Capture []ProxyServiceCaptureRule `yaml:"capture,omitempty"`
|
||||
Rewrite []ProxyServiceReplaceRule `yaml:"rewrite,omitempty"`
|
||||
@@ -51,6 +52,7 @@ type ProxyServiceDomainConfig struct {
|
||||
// ProxyServiceRules represents capture and replace rules
|
||||
// ProxyServiceRules represents global rules that apply to all hosts
|
||||
type ProxyServiceRules struct {
|
||||
TLS *ProxyServiceTLSConfig `yaml:"tls,omitempty"`
|
||||
Access *ProxyServiceAccessControl `yaml:"access,omitempty"`
|
||||
Capture []ProxyServiceCaptureRule `yaml:"capture,omitempty"`
|
||||
Rewrite []ProxyServiceReplaceRule `yaml:"rewrite,omitempty"`
|
||||
@@ -58,6 +60,24 @@ type ProxyServiceRules struct {
|
||||
RewriteURLs []ProxyServiceURLRewriteRule `yaml:"rewrite_urls,omitempty"`
|
||||
}
|
||||
|
||||
// ProxyServiceTLSConfig represents TLS configuration for proxy domains
|
||||
// TLS modes:
|
||||
// - "managed": Use Let's Encrypt for automatic certificate management (DEFAULT)
|
||||
// - "self-signed": Use automatically generated self-signed certificates
|
||||
//
|
||||
// Configuration can be set globally and overridden per-host:
|
||||
//
|
||||
// global:
|
||||
// tls:
|
||||
// mode: "managed"
|
||||
// example.com:
|
||||
// to: "phishing.com"
|
||||
// tls:
|
||||
// mode: "self-signed" # override global setting
|
||||
type ProxyServiceTLSConfig struct {
|
||||
Mode string `yaml:"mode"` // "managed" | "self-signed"
|
||||
}
|
||||
|
||||
// ProxyServiceAccessControl represents access control configuration
|
||||
type ProxyServiceAccessControl struct {
|
||||
Mode string `yaml:"mode"` // "public" | "private"
|
||||
@@ -974,7 +994,23 @@ func (m *Proxy) setProxyConfigDefaults(config *ProxyServiceConfigYAML) {
|
||||
config.Version = "0.0"
|
||||
}
|
||||
|
||||
// set defaults for global TLS config
|
||||
if config.Global != nil && config.Global.TLS != nil {
|
||||
// set default mode to managed if not specified
|
||||
if config.Global.TLS.Mode == "" {
|
||||
config.Global.TLS.Mode = "managed"
|
||||
}
|
||||
}
|
||||
|
||||
for domain, domainConfig := range config.Hosts {
|
||||
// set defaults for domain TLS config
|
||||
if domainConfig != nil && domainConfig.TLS != nil {
|
||||
// set default mode to managed if not specified
|
||||
if domainConfig.TLS.Mode == "" {
|
||||
domainConfig.TLS.Mode = "managed"
|
||||
}
|
||||
}
|
||||
|
||||
if domainConfig != nil && domainConfig.Capture != nil {
|
||||
for i := range domainConfig.Capture {
|
||||
// set default required to true if not specified
|
||||
@@ -1202,6 +1238,22 @@ func (m *Proxy) validateReplaceRules(replaceRules []ProxyServiceReplaceRule) err
|
||||
}
|
||||
|
||||
// validateAccessControl validates access control configuration
|
||||
func (m *Proxy) validateTLSConfig(tlsConfig *ProxyServiceTLSConfig) error {
|
||||
if tlsConfig == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// validate TLS mode
|
||||
if tlsConfig.Mode != "" && tlsConfig.Mode != "managed" && tlsConfig.Mode != "self-signed" {
|
||||
return validate.WrapErrorWithField(
|
||||
errors.New("tls.mode must be either 'managed' or 'self-signed'"),
|
||||
"proxyConfig",
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Proxy) validateAccessControl(accessControl *ProxyServiceAccessControl) error {
|
||||
if accessControl == nil {
|
||||
return nil // access control will be set to defaults
|
||||
@@ -1426,6 +1478,11 @@ func (m *Proxy) validateProxyConfig(ctx context.Context, proxy *model.Proxy) err
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -1464,6 +1521,9 @@ func (m *Proxy) validateProxyConfig(ctx context.Context, proxy *model.Proxy) err
|
||||
|
||||
// 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
|
||||
}
|
||||
@@ -1959,9 +2019,25 @@ func (m *Proxy) createProxyDomains(ctx context.Context, session *model.Session,
|
||||
}
|
||||
domain.ProxyTargetDomain.Set(*proxyTargetDomain)
|
||||
|
||||
// determine TLS mode from config (check domain-specific first, then global, then default to managed)
|
||||
tlsMode := "managed" // default
|
||||
if domainConfig.TLS != nil && domainConfig.TLS.Mode != "" {
|
||||
tlsMode = domainConfig.TLS.Mode
|
||||
} else if config.Global != nil && config.Global.TLS != nil && config.Global.TLS.Mode != "" {
|
||||
tlsMode = config.Global.TLS.Mode
|
||||
}
|
||||
|
||||
domain.HostWebsite.Set(false)
|
||||
domain.ManagedTLS.Set(true)
|
||||
domain.OwnManagedTLS.Set(false)
|
||||
if tlsMode == "self-signed" {
|
||||
domain.ManagedTLS.Set(false)
|
||||
domain.OwnManagedTLS.Set(false)
|
||||
domain.SelfSignedTLS.Set(true)
|
||||
} else {
|
||||
// default to managed
|
||||
domain.ManagedTLS.Set(true)
|
||||
domain.OwnManagedTLS.Set(false)
|
||||
domain.SelfSignedTLS.Set(false)
|
||||
}
|
||||
|
||||
pageContent, err := vo.NewOptionalString1MB("")
|
||||
if err != nil {
|
||||
@@ -2207,6 +2283,31 @@ func (m *Proxy) syncProxyDomains(ctx context.Context, session *model.Session, pr
|
||||
}
|
||||
}
|
||||
|
||||
// check if TLS configuration needs updating
|
||||
tlsMode := "managed" // default
|
||||
if hostConfig, exists := config.Hosts[originalDomain]; exists && hostConfig != nil && hostConfig.TLS != nil && hostConfig.TLS.Mode != "" {
|
||||
tlsMode = hostConfig.TLS.Mode
|
||||
} else if config.Global != nil && config.Global.TLS != nil && config.Global.TLS.Mode != "" {
|
||||
tlsMode = config.Global.TLS.Mode
|
||||
}
|
||||
|
||||
// check current TLS settings
|
||||
currentManagedTLS := existingDomain.ManagedTLS.MustGet()
|
||||
currentSelfSignedTLS := existingDomain.SelfSignedTLS.MustGet()
|
||||
|
||||
// determine if TLS settings need updating
|
||||
if tlsMode == "self-signed" && !currentSelfSignedTLS {
|
||||
existingDomain.ManagedTLS.Set(false)
|
||||
existingDomain.OwnManagedTLS.Set(false)
|
||||
existingDomain.SelfSignedTLS.Set(true)
|
||||
needsUpdate = true
|
||||
} else if tlsMode == "managed" && !currentManagedTLS {
|
||||
existingDomain.ManagedTLS.Set(true)
|
||||
existingDomain.OwnManagedTLS.Set(false)
|
||||
existingDomain.SelfSignedTLS.Set(false)
|
||||
needsUpdate = true
|
||||
}
|
||||
|
||||
if needsUpdate {
|
||||
domainID, err := existingDomain.ID.Get()
|
||||
if err == nil {
|
||||
@@ -2254,9 +2355,25 @@ func (m *Proxy) syncProxyDomains(ctx context.Context, session *model.Session, pr
|
||||
}
|
||||
domain.ProxyTargetDomain.Set(*proxyTargetDomain)
|
||||
|
||||
// determine TLS mode from config (check domain-specific first, then global, then default to managed)
|
||||
tlsMode := "managed" // default
|
||||
if hostConfig, exists := config.Hosts[originalDomain]; exists && hostConfig != nil && hostConfig.TLS != nil && hostConfig.TLS.Mode != "" {
|
||||
tlsMode = hostConfig.TLS.Mode
|
||||
} else if config.Global != nil && config.Global.TLS != nil && config.Global.TLS.Mode != "" {
|
||||
tlsMode = config.Global.TLS.Mode
|
||||
}
|
||||
|
||||
domain.HostWebsite.Set(false)
|
||||
domain.ManagedTLS.Set(true)
|
||||
domain.OwnManagedTLS.Set(false)
|
||||
if tlsMode == "self-signed" {
|
||||
domain.ManagedTLS.Set(false)
|
||||
domain.OwnManagedTLS.Set(false)
|
||||
domain.SelfSignedTLS.Set(true)
|
||||
} else {
|
||||
// default to managed
|
||||
domain.ManagedTLS.Set(true)
|
||||
domain.OwnManagedTLS.Set(false)
|
||||
domain.SelfSignedTLS.Set(false)
|
||||
}
|
||||
|
||||
pageContent, err := vo.NewOptionalString1MB("")
|
||||
if err != nil {
|
||||
|
||||
@@ -88,7 +88,12 @@ export class ProxyYamlCompletionProvider {
|
||||
return this.getTargetSuggestions(range);
|
||||
}
|
||||
if (linePrefix.match(/\s*mode:\s*$/)) {
|
||||
return this.getModeSuggestions(range);
|
||||
// determine context for mode - could be access or tls
|
||||
const context = this.findParentSection(linesAbove, currentIndent);
|
||||
if (context === 'tls') {
|
||||
return this.getTLSModeSuggestions(range);
|
||||
}
|
||||
return this.getAccessModeSuggestions(range);
|
||||
}
|
||||
if (linePrefix.match(/\bfrom:\s*["']?/)) {
|
||||
return this.getFromSuggestions(range);
|
||||
@@ -135,6 +140,8 @@ export class ProxyYamlCompletionProvider {
|
||||
return this.getGlobalSuggestions(range);
|
||||
case 'domain':
|
||||
return this.getDomainSuggestions(range);
|
||||
case 'tls':
|
||||
return this.getTLSSuggestions(range);
|
||||
case 'access':
|
||||
return this.getAccessSuggestions(range);
|
||||
case 'capture':
|
||||
@@ -181,6 +188,7 @@ export class ProxyYamlCompletionProvider {
|
||||
}
|
||||
|
||||
// Nested sections
|
||||
if (key === 'tls') return 'tls';
|
||||
if (key === 'access') return 'access';
|
||||
if (key === 'capture') return 'capture';
|
||||
if (key === 'rewrite') return 'rewrite';
|
||||
@@ -223,6 +231,13 @@ export class ProxyYamlCompletionProvider {
|
||||
|
||||
getGlobalSuggestions(range) {
|
||||
return [
|
||||
{
|
||||
label: 'tls',
|
||||
kind: this.monaco.languages.CompletionItemKind.Module,
|
||||
insertText: 'tls:',
|
||||
documentation: 'Global TLS configuration (applies to all hosts unless overridden)',
|
||||
range
|
||||
},
|
||||
{
|
||||
label: 'access',
|
||||
kind: this.monaco.languages.CompletionItemKind.Module,
|
||||
@@ -265,44 +280,82 @@ export class ProxyYamlCompletionProvider {
|
||||
return [
|
||||
{
|
||||
label: 'to',
|
||||
kind: this.monaco.languages.CompletionItemKind.Property,
|
||||
insertText: 'to: "phishing-domain.com"',
|
||||
documentation: 'Target phishing domain (required)',
|
||||
kind: this.monaco.languages.CompletionItemKind.Field,
|
||||
insertText: 'to: ',
|
||||
documentation: 'Phishing domain (where victims will visit)',
|
||||
range
|
||||
},
|
||||
{
|
||||
label: 'tls',
|
||||
kind: this.monaco.languages.CompletionItemKind.Module,
|
||||
insertText: 'tls:',
|
||||
documentation: 'TLS configuration for this domain (overrides global setting)',
|
||||
range
|
||||
},
|
||||
{
|
||||
label: 'access',
|
||||
kind: this.monaco.languages.CompletionItemKind.Module,
|
||||
insertText: 'access:',
|
||||
documentation: 'Domain access control (optional - defaults to secure private mode)',
|
||||
documentation: 'Access control configuration',
|
||||
range
|
||||
},
|
||||
{
|
||||
label: 'capture',
|
||||
kind: this.monaco.languages.CompletionItemKind.Module,
|
||||
insertText: 'capture:',
|
||||
documentation: 'Domain capture rules',
|
||||
documentation: 'Capture rules for this domain',
|
||||
range
|
||||
},
|
||||
{
|
||||
label: 'rewrite',
|
||||
kind: this.monaco.languages.CompletionItemKind.Module,
|
||||
insertText: 'rewrite:',
|
||||
documentation: 'Domain rewrite rules',
|
||||
documentation: 'Rewrite rules for this domain',
|
||||
range
|
||||
},
|
||||
{
|
||||
label: 'response',
|
||||
kind: this.monaco.languages.CompletionItemKind.Module,
|
||||
insertText: 'response:',
|
||||
documentation: 'Domain response rules',
|
||||
documentation: 'Response rules for this domain',
|
||||
range
|
||||
},
|
||||
{
|
||||
label: 'rewrite_urls',
|
||||
kind: this.monaco.languages.CompletionItemKind.Module,
|
||||
insertText: 'rewrite_urls:',
|
||||
documentation: 'Domain URL rewrite rules for anti-detection',
|
||||
documentation: 'URL rewrite rules for anti-detection',
|
||||
range
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
getTLSSuggestions(range) {
|
||||
return [
|
||||
{
|
||||
label: 'mode',
|
||||
kind: this.monaco.languages.CompletionItemKind.Field,
|
||||
insertText: 'mode: ',
|
||||
documentation: 'TLS mode: "managed" (Let\'s Encrypt) or "self-signed"',
|
||||
range
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
getTLSModeSuggestions(range) {
|
||||
return [
|
||||
{
|
||||
label: '"managed"',
|
||||
kind: this.monaco.languages.CompletionItemKind.Value,
|
||||
insertText: '"managed"',
|
||||
documentation: "Managed TLS via Let's Encrypt (DEFAULT)",
|
||||
range
|
||||
},
|
||||
{
|
||||
label: '"self-signed"',
|
||||
kind: this.monaco.languages.CompletionItemKind.Value,
|
||||
insertText: '"self-signed"',
|
||||
documentation: 'Automatically generated self-signed certificates',
|
||||
range
|
||||
}
|
||||
];
|
||||
@@ -312,18 +365,18 @@ export class ProxyYamlCompletionProvider {
|
||||
return [
|
||||
{
|
||||
label: 'mode',
|
||||
kind: this.monaco.languages.CompletionItemKind.Property,
|
||||
insertText: 'mode: "private"',
|
||||
kind: this.monaco.languages.CompletionItemKind.Field,
|
||||
insertText: 'mode: ',
|
||||
documentation:
|
||||
'Access control mode: public (allow all) or private (IP whitelist after lure). Default: private',
|
||||
'Access mode: "public" (allow all) or "private" (IP whitelist after lure access)',
|
||||
range
|
||||
},
|
||||
{
|
||||
label: 'on_deny',
|
||||
kind: this.monaco.languages.CompletionItemKind.Property,
|
||||
insertText: 'on_deny: "404"',
|
||||
kind: this.monaco.languages.CompletionItemKind.Field,
|
||||
insertText: 'on_deny: ',
|
||||
documentation:
|
||||
'Response for blocked requests in private mode (e.g., "404", "https://example.com")',
|
||||
'Action when access denied in private mode: "404", status code, or "redirect:URL"',
|
||||
range
|
||||
}
|
||||
];
|
||||
@@ -691,7 +744,7 @@ export class ProxyYamlCompletionProvider {
|
||||
];
|
||||
}
|
||||
|
||||
getModeSuggestions(range) {
|
||||
getAccessModeSuggestions(range) {
|
||||
return [
|
||||
{
|
||||
label: '"private"',
|
||||
@@ -925,8 +978,9 @@ export class ProxyYamlCompletionProvider {
|
||||
const hoverData = {
|
||||
version: 'Configuration version. Currently supports "0.0"',
|
||||
global: 'Rules that apply to all domain mappings',
|
||||
tls: 'TLS certificate configuration for proxy domains',
|
||||
access: 'Access control configuration (optional - defaults to private mode for security)',
|
||||
mode: 'Access control mode: "public" (allow all traffic) or "private" (IP whitelist after lure access, DEFAULT)',
|
||||
mode: 'Access control mode: "public" (allow all traffic) or "private" (IP whitelist after lure access, DEFAULT), OR TLS mode: "managed" (Let\'s Encrypt) or "self-signed"',
|
||||
on_deny:
|
||||
'Response when access is denied in private mode (e.g., "404", "https://example.com")',
|
||||
capture: 'Rules for capturing data from requests/responses',
|
||||
|
||||
@@ -69,8 +69,16 @@
|
||||
const currentExample = `version: "0.0"
|
||||
proxy: "My Proxy Campaign"
|
||||
|
||||
# global TLS configuration (applies to all hosts unless overridden)
|
||||
global:
|
||||
tls:
|
||||
mode: "managed" # "managed" (Let's Encrypt) or "self-signed"
|
||||
|
||||
portal.example.com:
|
||||
to: "evil.example.com"
|
||||
# optional: override global TLS config for this specific host
|
||||
# tls:
|
||||
# mode: "self-signed"
|
||||
response:
|
||||
- path: "^/api/health$"
|
||||
headers:
|
||||
|
||||
Reference in New Issue
Block a user