diff --git a/Documentation/EXAMPLES.md b/Documentation/EXAMPLES.md index 45b0e01..d88ef32 100644 --- a/Documentation/EXAMPLES.md +++ b/Documentation/EXAMPLES.md @@ -31,4 +31,5 @@ ## Disable WAF in case of an emergency `./waflyctl -apikey $FASTLY_TOKEN -serviceid 7YCnicdpjTvxR2JdzNAKCj -status disable` - +## Customer with shielding +`./waflyctl -apikey $FASTLY_TOKEN -serviceid 2mvVrPMLDWSaZzCZIpbcUI -enable-logs-only -with-shielding` diff --git a/Documentation/USAGE.md b/Documentation/USAGE.md index 4a86e49..7337c8f 100644 --- a/Documentation/USAGE.md +++ b/Documentation/USAGE.md @@ -3,7 +3,7 @@ waflyctl configuration file contains the default parameters to build and also ma If needed please adjust them in waflyctl.toml, and or pass them via command line. ``` Usage of waflyctl: - -action string + -action string Select what action to take on the rules list and rule tags. Also overwrites action defined in config file, choices are: disabled, block, log. -apiendpoint string Fastly API endpoint to use. (default "https://api.fastly.com") @@ -41,4 +41,6 @@ Usage of waflyctl: Which rules tags to add to the ruleset in a comma delimited fashion, overwrites tags defined in config file, example: wordpress,language-php,drupal -with-perimeterx Enable if the customer has perimeterX enabled on the service as well as WAF. Helps fix null value logging. + -with-shielding + Enable if the customer has shielding enabled on the service. Helps fix multiple events with duplicate request IDs. ``` diff --git a/waflyctl.go b/waflyctl.go index 3b8e194..b9704ff 100644 --- a/waflyctl.go +++ b/waflyctl.go @@ -1332,6 +1332,73 @@ func WithPXCondition(client fastly.Client, serviceID string, version int, config } +// WithShieldingCondition adds a condition if shielding is present to avoid multiple events with duplicate request ID logging +func WithShieldingCondition(client fastly.Client, serviceID string, version int, config TOMLConfig) bool { + + conditions, err := client.ListConditions(&fastly.ListConditionsInput{ + Service: serviceID, + Version: version, + }) + + if err != nil { + Error.Fatal(err) + return false + } + for _, condition := range conditions { + //do we have a condition name waf-soc-with-shielding, if not create one + //iterate through returned conditions check if any say waf-soc-with-shielding if not lets configure the service + if strings.EqualFold(condition.Name, "waf-soc-with-shielding") { + Error.Println("WAF with shielding logging condition already exists with name: " + condition.Name + "..skipping creating conditions") + return false + } + } + + Info.Printf("WAF enabled with shielding, creating logging condition: waf-soc-with-shielding") + _, err = client.CreateCondition(&fastly.CreateConditionInput{ + Service: serviceID, + Version: version, + Name: "waf-soc-with-shielding", + Statement: "!req.backend.is_shield", + Type: "RESPONSE", + Priority: 10, + }) + + if err != nil { + Error.Fatal(err) + return false + } + + //update syslog endpoints + Info.Printf("WAF enabled with shielding, applying condition 'waf-soc-with-shielding' to web logs %s", config.Weblog.Name) + _, err = client.UpdateSyslog(&fastly.UpdateSyslogInput{ + Service: serviceID, + Version: version, + Name: config.Weblog.Name, + ResponseCondition: "waf-soc-with-shielding", + }) + + if err != nil { + Error.Fatal(err) + return false + } + + Info.Printf("WAF enabled with shielding, applying condition 'waf-soc-with-shielding' to waf logs %s", config.Waflog.Name) + _, err = client.UpdateSyslog(&fastly.UpdateSyslogInput{ + Service: serviceID, + Version: version, + Name: config.Waflog.Name, + ResponseCondition: "waf-soc-with-shielding", + }) + + if err != nil { + Error.Fatal(err) + return false + } + + return true + +} + // PatchRules function patches a rule set after a status of a rule has been changed func PatchRules(serviceID, wafID string, client fastly.Client) bool { @@ -1967,6 +2034,7 @@ func main() { DeleteLogs := flag.Bool("delete-logs", false, "When set removes WAF logging configuration.") Backup := flag.Bool("backup", false, "When set stores a copy of the WAF configuration in "+HOMEDIRECTORY+"/.waflyctl-.rules") WithPX := flag.Bool("with-perimeterx", false, "Enable if the customer has perimeterX enabled on the service as well as WAF. Helps fix null value logging.") + WithShielding := flag.Bool("with-shielding", false, "Enable if the customer has shielding enabled on the service. Helps fix multiple events with duplicate request IDs.") Status := flag.String("status", "", "Disable or Enable the WAF. A disabled WAF will not block any traffic, also disabling a WAF does not change rule statuses on its configure policy. ") flag.Parse() @@ -2139,6 +2207,11 @@ func main() { os.Exit(1) } + if *WithShielding { + Info.Printf("WAF enabled with Shielding, adding logging condition") + WithShieldingCondition(*client, *serviceID, version, config) + } + if *WithPX { Info.Printf("WAF enabled with PerimterX, adding logging condition") WithPXCondition(*client, *serviceID, version, config) @@ -2302,6 +2375,14 @@ func main() { WithPXCondition(*client, *serviceID, version, config) + case *WithShielding: + Info.Printf("WAF enabled with shielding, adding logging condition") + + //clone current version + _, version := cloneVersion(*client, *serviceID, *apiKey, config, activeVersion) + + WithShieldingCondition(*client, *serviceID, version, config) + //back up WAF rules locally case *Backup: Info.Printf("WAF Backups enabled") @@ -2312,6 +2393,7 @@ func main() { backupConfig(*apiEndpoint, *apiKey, *serviceID, waf.ID, *client) //WithPXCondition(*client, *serviceID, version, config) + default: //tags management tagsConfig(config.APIEndpoint, *apiKey, *serviceID, waf.ID, *client, config) @@ -2358,11 +2440,15 @@ func main() { //Default Disabled DefaultRuleDisabled(config.APIEndpoint, *apiKey, *serviceID, wafID, *client, config) + Info.Printf("WAF enabled with shielding, adding logging condition") + WithShieldingCondition(*client, *serviceID, version, config) + if *WithPX { Info.Printf("WAF enabled with PerimterX, adding logging condition") WithPXCondition(*client, *serviceID, version, config) } + latest, err := client.LatestVersion(&fastly.LatestVersionInput{ Service: *serviceID, })