initial commit
This commit is contained in:
parent
a7047b2f7a
commit
dba0fd14c5
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
redirects.csv
|
||||||
|
wafredir.exe
|
||||||
|
wafredir
|
63
config.go
Normal file
63
config.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// then outputs the configuration to stdout
|
||||||
|
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.
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
// Iterate over all redirects and output url-rewrite-rules
|
||||||
|
|
||||||
|
fmt.Printf("\n\n------------------------\n\n\n")
|
||||||
|
fmt.Println("config waf url-rewrite url-rewrite-rule")
|
||||||
|
for _, redirect := range redirects {
|
||||||
|
sourceURL, err := url.Parse(redirect.sourceURL)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("skipping redirect: unable to parse source URL '%s': %v", redirect.sourceURL, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("edit \"%s\"\n", redirect.sourceURL)
|
||||||
|
fmt.Println("set action redirect")
|
||||||
|
fmt.Printf("set location %s\n", redirect.destinationURL)
|
||||||
|
fmt.Println("set action redirect")
|
||||||
|
fmt.Println("config match-condition")
|
||||||
|
fmt.Println("edit 0")
|
||||||
|
fmt.Println("set object http-url")
|
||||||
|
fmt.Printf("set reg-exp %s\n", sourceURL.Path)
|
||||||
|
fmt.Println("set protocol-filter enable")
|
||||||
|
fmt.Printf("set HTTP-protocol %s\n", sourceURL.Scheme)
|
||||||
|
fmt.Println("next")
|
||||||
|
fmt.Println("end")
|
||||||
|
fmt.Println("next")
|
||||||
|
|
||||||
|
// Add this rule to the policy map
|
||||||
|
redirectPolicies[sourceURL.Host] = append(redirectPolicies[sourceURL.Host], redirect.sourceURL)
|
||||||
|
}
|
||||||
|
fmt.Println("end")
|
||||||
|
|
||||||
|
// Iterate over values in the policy map and output the policy configuration
|
||||||
|
fmt.Println("config waf url-rewrite url-rewrite-policy")
|
||||||
|
for policyName, policyRules := range redirectPolicies {
|
||||||
|
fmt.Printf("edit \"%s\"\n", policyName)
|
||||||
|
for _, ruleName := range policyRules {
|
||||||
|
fmt.Println("edit 0")
|
||||||
|
fmt.Printf("set url-rewrite-rule-name %s-redirects\n", ruleName)
|
||||||
|
fmt.Println("next")
|
||||||
|
fmt.Println("end")
|
||||||
|
}
|
||||||
|
fmt.Println("next")
|
||||||
|
}
|
||||||
|
fmt.Println("end")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
79
main.go
Normal file
79
main.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"flag"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
maxConcurrentRequests *int // the maximum number of allowed in flight HTTP requests. Only used with testing mode.
|
||||||
|
)
|
||||||
|
|
||||||
|
// Redirect is an instance of a single row in the csv file. It contains a value from each of the columns of the input csv file
|
||||||
|
type Redirect struct {
|
||||||
|
sourceURL string // the source URL we are redirecting from
|
||||||
|
destinationURL string // the target URL we are redirecting to
|
||||||
|
statusCode int // 301 or 302
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// Parse flags and determine actions
|
||||||
|
action := flag.String("action", "config", "action can be either 'config' or 'test'. 'config' will read the input csv file and generate FortiOS compliant configuration to create redirection policies. 'test' will read the input csv file and validate that the redirects are actually working by making requests at the source URL and validating a redirect to the destination URL actually occurs.")
|
||||||
|
csvFilePath := flag.String("csvfile", "redirects.csv", "path to an input csv file. The first column of the file should be the source URL, the second column of the file should be the destination URL, and the third column should be the status code (for example 301 or 302).")
|
||||||
|
maxConcurrentRequests = flag.Int("concurrentReq", 8, "only used with the action 'test'. Determines the maximum number concurrent HTTP GET requests which can be in flight at any given time.")
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// Open and parse the CSV file and set up decoding
|
||||||
|
csvFile, err := os.Open(*csvFilePath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to open input csvFile '%s': %v", *csvFilePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rows := csv.NewReader(csvFile)
|
||||||
|
|
||||||
|
// Loop through each row until we reach the end and populate a slice of type Redirect
|
||||||
|
var redirects []Redirect
|
||||||
|
|
||||||
|
for {
|
||||||
|
row, err := rows.Read()
|
||||||
|
if err == io.EOF {
|
||||||
|
break // We've hit the end of the file
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to read row in CSV file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new instance of type Redirect and populate with data from CSV file
|
||||||
|
redirect := Redirect{sourceURL: row[0], destinationURL: row[1]}
|
||||||
|
redirect.statusCode, err = strconv.Atoi(strings.TrimSpace(row[2]))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("skipping row: failed to convert status code '%s' to integer: %v", row[2], err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the new redirect to the list of all redirects
|
||||||
|
redirects = append(redirects, redirect)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the next steps
|
||||||
|
if *action == "config" {
|
||||||
|
err := config(redirects)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to build configuration: %v", err)
|
||||||
|
}
|
||||||
|
} else if *action == "test" {
|
||||||
|
err := test(redirects)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to test redirects: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Fatalf("invalid action %s specified", *action)
|
||||||
|
}
|
||||||
|
}
|
9
test.go
Normal file
9
test.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// to the destination URL and that the redirect type/status code is correct
|
||||||
|
func test(redirects []Redirect) error { //TBD: implement
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user