github.com/aldelo/common@v1.5.1/wrapper/waf2/waf2.go (about) 1 package waf2 2 3 import ( 4 "fmt" 5 util "github.com/aldelo/common" 6 awshttp2 "github.com/aldelo/common/wrapper/aws" 7 "github.com/aldelo/common/wrapper/aws/awsregion" 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/aws/session" 10 "github.com/aws/aws-sdk-go/service/wafv2" 11 "net/http" 12 ) 13 14 /* 15 * Copyright 2020-2023 Aldelo, LP 16 * 17 * Licensed under the Apache License, Version 2.0 (the "License"); 18 * you may not use this file except in compliance with the License. 19 * You may obtain a copy of the License at 20 * 21 * http://www.apache.org/licenses/LICENSE-2.0 22 * 23 * Unless required by applicable law or agreed to in writing, software 24 * distributed under the License is distributed on an "AS IS" BASIS, 25 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26 * See the License for the specific language governing permissions and 27 * limitations under the License. 28 */ 29 30 // ================================================================================================================= 31 // AWS CREDENTIAL: 32 // use $> aws configure (to set aws access key and secret to target machine) 33 // Store AWS Access ID and Secret Key into Default Profile Using '$ aws configure' cli 34 // 35 // To Install & Setup AWS CLI on Host: 36 // 1) https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html 37 // On Ubuntu, if host does not have zip and unzip: 38 // $> sudo apt install zip 39 // $> sudo apt install unzip 40 // On Ubuntu, to install AWS CLI v2: 41 // $> curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" 42 // $> unzip awscliv2.zip 43 // $> sudo ./aws/install 44 // 2) $> aws configure set region awsRegionName --profile default 45 // 3) $> aws configure 46 // follow prompts to enter Access ID and Secret Key 47 // 48 // AWS Region Name Reference: 49 // us-west-2, us-east-1, ap-northeast-1, etc 50 // See: https://docs.aws.amazon.com/general/latest/gr/rande.html 51 // ================================================================================================================= 52 53 // ================================================================================================================ 54 // STRUCTS 55 // ================================================================================================================ 56 57 type WAF2 struct { 58 // define the AWS region that s3 is located at 59 AwsRegion awsregion.AWSRegion 60 61 // custom http2 client options 62 HttpOptions *awshttp2.HttpClientSettings 63 64 // store aws session object 65 sess *session.Session 66 67 // store waf2 object 68 waf2Obj *wafv2.WAFV2 69 } 70 71 // ================================================================================================================ 72 // STRUCTS FUNCTIONS 73 // ================================================================================================================ 74 75 // ---------------------------------------------------------------------------------------------------------------- 76 // utility functions 77 // ---------------------------------------------------------------------------------------------------------------- 78 79 // Connect will establish a connection to the WAF2 service 80 func (w *WAF2) Connect() error { 81 // clean up prior session reference 82 w.sess = nil 83 84 if !w.AwsRegion.Valid() || w.AwsRegion == awsregion.UNKNOWN { 85 return fmt.Errorf("Connect To WAF2 Failed: (AWS Session Error) " + "Region is Required") 86 } 87 88 // create custom http2 client if needed 89 var httpCli *http.Client 90 var httpErr error 91 92 if w.HttpOptions == nil { 93 w.HttpOptions = new(awshttp2.HttpClientSettings) 94 } 95 96 // use custom http2 client 97 h2 := &awshttp2.AwsHttp2Client{ 98 Options: w.HttpOptions, 99 } 100 101 if httpCli, httpErr = h2.NewHttp2Client(); httpErr != nil { 102 return fmt.Errorf("Connect to WAF2 Failed: (AWS Session Error) " + "Create Custom Http2 Client Errored = " + httpErr.Error()) 103 } 104 105 // establish aws session connection and keep session object in struct 106 if sess, err := session.NewSession( 107 &aws.Config{ 108 Region: aws.String(w.AwsRegion.Key()), 109 HTTPClient: httpCli, 110 }); err != nil { 111 // aws session error 112 return fmt.Errorf("Connect To WAF2 Failed: (AWS Session Error) " + err.Error()) 113 } else { 114 // aws session obtained 115 w.waf2Obj = wafv2.New(sess) 116 117 if w.waf2Obj == nil { 118 return fmt.Errorf("Connect To WAF2 Object Failed: (New WAF2 Connection) " + "Connection Object Nil") 119 } 120 121 // session stored to struct 122 return nil 123 } 124 } 125 126 // UpdateIPSet will update an existing IPSet with new addresses specified 127 // ipsetName = exact name from WAF2 IP Set already created 128 // ipsetId = exact id from WAF2 IP Set already created 129 // scope = 'REGIONAL' or other scope per aws WAF2 doc (defaults to REGIONAL if blank) 130 // newAddr = addresses to add to ip set 131 // 132 // note: aws limit is 10000 ip per ip set 133 func (w *WAF2) UpdateIPSet(ipsetName string, ipsetId string, scope string, newAddr []string) error { 134 if util.LenTrim(ipsetName) == 0 { 135 return fmt.Errorf("UpdateIPSet Failed: ipsetName is Required") 136 } 137 138 if util.LenTrim(ipsetId) == 0 { 139 return fmt.Errorf("UpdateIPSet Failed: ipsetId is Required") 140 } 141 142 if util.LenTrim(scope) == 0 { 143 scope = "REGIONAL" 144 } 145 146 if len(newAddr) == 0 { 147 return fmt.Errorf("UpdateIPSet Failed: New Address to Add is Required") 148 } 149 150 var lockToken *string 151 var addrList []string 152 153 if getOutput, err := w.waf2Obj.GetIPSet(&wafv2.GetIPSetInput{ 154 Name: aws.String(ipsetName), 155 Id: aws.String(ipsetId), 156 Scope: aws.String(scope), 157 }); err != nil { 158 // error 159 return fmt.Errorf("Get IP Set Failed: %s", err.Error()) 160 } else { 161 lockToken = getOutput.LockToken 162 addrList = aws.StringValueSlice(getOutput.IPSet.Addresses) 163 addrList = append(addrList, newAddr...) 164 165 if len(addrList) > 10000 { 166 addrList = addrList[len(addrList)-10000:] 167 } 168 } 169 170 if _, err := w.waf2Obj.UpdateIPSet(&wafv2.UpdateIPSetInput{ 171 Name: aws.String(ipsetName), 172 Id: aws.String(ipsetId), 173 Scope: aws.String(scope), 174 Addresses: aws.StringSlice(addrList), 175 LockToken: lockToken, 176 }); err != nil { 177 // error encountered 178 return fmt.Errorf("Update IP Set Failed: %s", err.Error()) 179 } else { 180 // update completed 181 return nil 182 } 183 } 184 185 // UpdateRegexPatternSet will update an existing RegexPatternSet with new regex patterns specified 186 // regexPatternSetName = exact name from WAF2 Regex Pattern Set already created 187 // regexPatternSetId = exact id from WAF2 Regex Pattern Set already created 188 // scope = 'REGIONAL' or other scope per aws WAF2 doc (defaults to REGIONAL if blank) 189 // newRegexPatterns = regex patterns to add to regex pattern set 190 // 191 // NOTE = AWS limits to 10 regex expressions per regex set, and max of 10 regex sets 192 // 193 // this method will take the newest regex pattern to replace the older patterns 194 func (w *WAF2) UpdateRegexPatternSet(regexPatternSetName string, regexPatternSetId string, scope string, newRegexPatterns []string) error { 195 if util.LenTrim(regexPatternSetName) == 0 { 196 return fmt.Errorf("UpdateRegexPatternSet Failed: regexPatternSetName is Required") 197 } 198 199 if util.LenTrim(regexPatternSetId) == 0 { 200 return fmt.Errorf("UpdateRegexPatternSet Failed: regexPatternSetId is Required") 201 } 202 203 if util.LenTrim(scope) == 0 { 204 scope = "REGIONAL" 205 } 206 207 if len(newRegexPatterns) == 0 { 208 return fmt.Errorf("UpdateRegexPatternSet Failed: New Regex Pattern to Add is Required") 209 } 210 211 var lockToken *string 212 var patternsList []*wafv2.Regex 213 214 if getOutput, err := w.waf2Obj.GetRegexPatternSet(&wafv2.GetRegexPatternSetInput{ 215 Name: aws.String(regexPatternSetName), 216 Id: aws.String(regexPatternSetId), 217 Scope: aws.String(scope), 218 }); err != nil { 219 // error 220 return fmt.Errorf("Get Regex Pattern Set Failed: %s", err.Error()) 221 } else { 222 lockToken = getOutput.LockToken 223 224 var oldList []string 225 226 patternsList = getOutput.RegexPatternSet.RegularExpressionList 227 newAddedCount := 0 228 229 if len(patternsList) > 0 { 230 for _, v := range patternsList { 231 oldList = append(oldList, aws.StringValue(v.RegexString)) 232 } 233 } 234 235 for _, v := range newRegexPatterns { 236 if !util.StringSliceContains(&oldList, v) { 237 patternsList = append(patternsList, &wafv2.Regex{ 238 RegexString: aws.String(v), 239 }) 240 241 oldList = append(oldList, v) 242 newAddedCount++ 243 } 244 } 245 246 if newAddedCount == 0 { 247 return nil 248 } 249 250 // take the last 10 251 if len(patternsList) > 10 { 252 patternsList = patternsList[len(patternsList)-10:] 253 } 254 } 255 256 if _, err := w.waf2Obj.UpdateRegexPatternSet(&wafv2.UpdateRegexPatternSetInput{ 257 Name: aws.String(regexPatternSetName), 258 Id: aws.String(regexPatternSetId), 259 Scope: aws.String(scope), 260 RegularExpressionList: patternsList, 261 LockToken: lockToken, 262 }); err != nil { 263 // error encountered 264 return fmt.Errorf("Update Regex Patterns Set Failed: %s", err.Error()) 265 } else { 266 // update completed 267 return nil 268 } 269 }