Update comments

This commit is contained in:
Steven Polley 2020-05-30 14:10:56 -06:00
parent 489d5ba9cc
commit 6673f69913
3 changed files with 32 additions and 19 deletions

View File

@ -6,26 +6,28 @@ import (
) )
// config accepts a slice of type Redirect and returns an error // config accepts a slice of type Redirect and returns an error
// it organizes the data from the input redirects in a way that makes sense for FortiOS objects // it organizes the input data in a way that makes sense for FortiOS objects
// then outputs the configuration to stdout // then outputs the configuration to stdout
func config(redirects []Redirect) error { func config(redirects []Redirect) error {
// A map which contains the name of a redirect policy as the key, and a slice of strings as a value. // A map which contains the name of a redirect policy as the key, and a slice of strings as a value.
// The value of the string is the name of the url-rewrite-rule which needs to be referenced by the redirect policy object in FortiOS // The value of the string is the name of the url-rewrite-rule which needs to be referenced by the redirect policy object in FortiOS
redirectPolicies := make(map[string][]string) redirectPolicies := make(map[string][]string)
// Iterate over all redirects and output url-rewrite-rules // Iterate over all redirects and output url-rewrite-rules
fmt.Printf("\n\n------------------------\n\n\n") fmt.Printf("\n\n------------------------ COPY BELOW THIS LINE ------------------------\n\n\n")
fmt.Println("config waf url-rewrite url-rewrite-rule") fmt.Println("config waf url-rewrite url-rewrite-rule")
for _, redirect := range redirects { for _, redirect := range redirects {
// Parse the source URL string into a URL object so we can extract features
sourceURL, err := url.Parse(redirect.sourceURL) sourceURL, err := url.Parse(redirect.sourceURL)
if err != nil { if err != nil {
return fmt.Errorf("unable to parse source URL '%s': %v", redirect.sourceURL, err) return fmt.Errorf("unable to parse source URL '%s': %v", redirect.sourceURL, err)
} }
var ruleName, action string var ruleName, action string
if len(redirect.sourceURL) > 63 { // FortiOS only allows up to 63 characters if len(redirect.sourceURL) > 63 { // FortiOS only allows up to 63 characters. There is potention for collission here. TBD: Reduce and replace characters in the middle of string with '...' and show characters at the end of the URL to make differentiation easier for operators of the WAF
ruleName = redirect.sourceURL[:62] ruleName = redirect.sourceURL[:62]
} else { } else {
ruleName = redirect.sourceURL ruleName = redirect.sourceURL
@ -37,6 +39,7 @@ func config(redirects []Redirect) error {
action = "redirect" action = "redirect"
} }
// Output FortiOS configuration syntax
fmt.Printf("edit \"%s\"\n", ruleName) fmt.Printf("edit \"%s\"\n", ruleName)
fmt.Printf("set location %s\n", redirect.destinationURL) fmt.Printf("set location %s\n", redirect.destinationURL)
fmt.Printf("set action %s\n", action) fmt.Printf("set action %s\n", action)
@ -50,11 +53,12 @@ func config(redirects []Redirect) error {
fmt.Println("end") fmt.Println("end")
fmt.Println("next") fmt.Println("next")
// Add this rule to the policy map // Add this rule to the slice inside the policy map. This is used to organize data for creating the url-rewrite-policy configuration below
redirectPolicies[sourceURL.Host] = append(redirectPolicies[sourceURL.Host], ruleName) redirectPolicies[sourceURL.Host] = append(redirectPolicies[sourceURL.Host], ruleName)
} }
fmt.Println("end") fmt.Println("end")
// Output url-rewrite-policy configuration.
// Iterate over values in the policy map and output the policy configuration // Iterate over values in the policy map and output the policy configuration
fmt.Println("config waf url-rewrite url-rewrite-policy") fmt.Println("config waf url-rewrite url-rewrite-policy")
for policyName, policyRules := range redirectPolicies { for policyName, policyRules := range redirectPolicies {
@ -68,7 +72,7 @@ func config(redirects []Redirect) error {
fmt.Println("end") fmt.Println("end")
fmt.Println("next") fmt.Println("next")
} }
fmt.Println("end") fmt.Printf("end\n\n\n")
return nil return nil
} }

View File

@ -64,12 +64,12 @@ func main() {
// Determine the next steps // Determine the next steps
if *action == "config" { if *action == "config" {
err := config(redirects) err := config(redirects) // Function defined in config.go
if err != nil { if err != nil {
log.Fatalf("failed to build configuration: %v", err) log.Fatalf("failed to build configuration: %v", err)
} }
} else if *action == "test" { } else if *action == "test" {
err := test(redirects) err := test(redirects) // Function defined in test.go
if err != nil { if err != nil {
log.Fatalf("failed to test redirects: %v", err) log.Fatalf("failed to test redirects: %v", err)
} }

31
test.go
View File

@ -12,21 +12,28 @@ var sem chan Empty // semaphore to limit requess in flight
// test accepts a slice of type Redirect and returns an error // test accepts a slice of type Redirect and returns an error
// it performs actual HTTP GET requests on each source URL and validates that a redirect occurs // it performs actual HTTP GET requests on each source URL and validates that a redirect occurs
// to the destination URL and that the redirect type/status code is correct // to the destination URL and that the redirect type/status code is correct
// This function makes use of concurrent requests to speed up testing.
func test(redirects []Redirect) error { func test(redirects []Redirect) error {
wg := &sync.WaitGroup{} // This string will hold output for any failed tests. It's displayed when all tests have completed
mu := &sync.Mutex{}
var summaryOutput string var summaryOutput string
sem = make(Semaphore, *maxConcurrentRequests) // Set up some tools to handle concurrency
wg := &sync.WaitGroup{} // Used to wait for all tests to finish
mu := &sync.Mutex{} // Used to lock shared memory in critical sections
sem = make(Semaphore, *maxConcurrentRequests) // Used to limit resources while having all requests queued up
// Loop through all redirects and queue them up
for _, redirect := range redirects { for _, redirect := range redirects {
wg.Add(1) wg.Add(1) // Add 1 resource to waitgroup
P(1) P(1) // Take 1 resource from semaphore
// This anonymous function executes in a separate go routine and can run concurrently
go func(redirect Redirect) { go func(redirect Redirect) {
defer V(1) defer V(1) // If function exits (error or otherwise), put 1 resource back into semaphore
defer wg.Done() defer wg.Done() // If function exits (error or otherwise), subtract 1 resource from waitgroup
// Create an HTTP client and override CheckRedirect to return the last response error so we can check the redirect type
client := &http.Client{ client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error { CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse return http.ErrUseLastResponse
@ -46,33 +53,35 @@ func test(redirects []Redirect) error {
// Check the status code // Check the status code
if resp.StatusCode != redirect.statusCode { if resp.StatusCode != redirect.statusCode {
// Modifying output is critical section // Modifying summaryOutput is critical section
mu.Lock() mu.Lock()
summaryOutput += fmt.Sprintf("redirect for source URL '%s': expected status code'%d': got '%d\n", redirect.sourceURL, redirect.statusCode, resp.StatusCode) summaryOutput += fmt.Sprintf("redirect for source URL '%s': expected status code'%d': got '%d\n", redirect.sourceURL, redirect.statusCode, resp.StatusCode)
mu.Unlock() mu.Unlock()
return return
} }
// Check that the redirect went to the correct location // Parse response location URL from header into URL object
destURL, err := resp.Location() destURL, err := resp.Location()
if err != nil { if err != nil {
log.Printf("failed to parse response location to URL: %v", err) log.Printf("failed to parse response location to URL: %v", err)
} }
// Check that the redirect went to the correct location
if destURL.String() != redirect.destinationURL { if destURL.String() != redirect.destinationURL {
// Modifying output is critical section // Modifying summyarOutput is critical section
mu.Lock() mu.Lock()
summaryOutput += fmt.Sprintf("redirect for source URL '%s': expected '%s': got '%s\n", redirect.sourceURL, redirect.destinationURL, destURL.String()) summaryOutput += fmt.Sprintf("redirect for source URL '%s': expected '%s': got '%s\n", redirect.sourceURL, redirect.destinationURL, destURL.String())
mu.Unlock() mu.Unlock()
return return
} }
}(redirect) }(redirect)
} }
// Wait for all tests to complete
wg.Wait() wg.Wait()
fmt.Printf("\ndone tests.\n---------------------------------------------\n") fmt.Printf("\ndone tests.\n---------------------------------------------\n")
// Display summaryOutput if any tests failed
if len(summaryOutput) > 0 { if len(summaryOutput) > 0 {
fmt.Printf("Summary:\n\n%s", summaryOutput) fmt.Printf("Summary:\n\n%s", summaryOutput)
} else { } else {