Skip to content

Commit

Permalink
Merge pull request #41719 from drewtul/f-add-ja34-rate-limits
Browse files Browse the repository at this point in the history
[Enhancement] Add support for JA3 and JA4 Fingerprint in WebACL rate limits and JA4 support for matching
  • Loading branch information
ewbankkit authored Mar 10, 2025
2 parents cf90ded + 5801cd2 commit eb9ee3b
Show file tree
Hide file tree
Showing 6 changed files with 526 additions and 3 deletions.
15 changes: 15 additions & 0 deletions .changelog/41719.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
```release-note:enhancement
resource/aws_wafv2_web_acl: Add `ja3_fingerprint` and `ja4_fingerprint` to `custom_key` configuration blocks
```

```release-note:enhancement
resource/aws_wafv2_rule_group: Add `ja3_fingerprint` and `ja4_fingerprint` to `custom_key` configuration blocks
```

```release-note:enhancement
resource/aws_wafv2_web_acl: Add `ja4_fingerprint` to `field_to_match` configuration blocks
```

```release-note:enhancement
resource/aws_wafv2_rule_group: Add `ja4_fingerprint` to `field_to_match` configuration blocks
```
88 changes: 88 additions & 0 deletions internal/service/wafv2/flex.go
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,10 @@ func expandFieldToMatch(l []interface{}) *awstypes.FieldToMatch {
f.JA3Fingerprint = expandJA3Fingerprint(v.([]interface{}))
}

if v, ok := m["ja4_fingerprint"]; ok && len(v.([]interface{})) > 0 {
f.JA4Fingerprint = expandJA4Fingerprint(v.([]interface{}))
}

if v, ok := m["single_query_argument"]; ok && len(v.([]interface{})) > 0 {
f.SingleQueryArgument = expandSingleQueryArgument(m["single_query_argument"].([]interface{}))
}
Expand Down Expand Up @@ -716,6 +720,20 @@ func expandJA3Fingerprint(l []interface{}) *awstypes.JA3Fingerprint {
return ja3fingerprint
}

func expandJA4Fingerprint(l []interface{}) *awstypes.JA4Fingerprint {
if len(l) == 0 || l[0] == nil {
return nil
}

m := l[0].(map[string]interface{})

ja4fingerprint := &awstypes.JA4Fingerprint{
FallbackBehavior: awstypes.FallbackBehavior(m["fallback_behavior"].(string)),
}

return ja4fingerprint
}

func expandJSONMatchPattern(l []interface{}) *awstypes.JsonMatchPattern {
if len(l) == 0 || l[0] == nil {
return nil
Expand Down Expand Up @@ -1561,6 +1579,26 @@ func expandRateLimitHeader(l []interface{}) *awstypes.RateLimitHeader {
}
}

func expandRateLimitJa3Fingerprint(l []interface{}) *awstypes.RateLimitJA3Fingerprint {
if len(l) == 0 || l[0] == nil {
return nil
}
m := l[0].(map[string]interface{})
return &awstypes.RateLimitJA3Fingerprint{
FallbackBehavior: awstypes.FallbackBehavior(m["fallback_behavior"].(string)),
}
}

func expandRateLimitJa4Fingerprint(l []interface{}) *awstypes.RateLimitJA4Fingerprint {
if len(l) == 0 || l[0] == nil {
return nil
}
m := l[0].(map[string]interface{})
return &awstypes.RateLimitJA4Fingerprint{
FallbackBehavior: awstypes.FallbackBehavior(m["fallback_behavior"].(string)),
}
}

func expandRateLimitLabelNamespace(l []interface{}) *awstypes.RateLimitLabelNamespace {
if len(l) == 0 || l[0] == nil {
return nil
Expand Down Expand Up @@ -1626,6 +1664,12 @@ func expandRateBasedStatementCustomKeys(l []interface{}) []awstypes.RateBasedSta
if v, ok := m["ip"]; ok && len(v.([]interface{})) > 0 {
r.IP = &awstypes.RateLimitIP{}
}
if v, ok := m["ja3_fingerprint"]; ok && len(v.([]interface{})) > 0 {
r.JA3Fingerprint = expandRateLimitJa3Fingerprint(v.([]interface{}))
}
if v, ok := m["ja4_fingerprint"]; ok && len(v.([]interface{})) > 0 {
r.JA4Fingerprint = expandRateLimitJa4Fingerprint(v.([]interface{}))
}
if v, ok := m["label_namespace"]; ok {
r.LabelNamespace = expandRateLimitLabelNamespace(v.([]interface{}))
}
Expand Down Expand Up @@ -2124,6 +2168,10 @@ func flattenFieldToMatch(f *awstypes.FieldToMatch) interface{} {
m["ja3_fingerprint"] = flattenJA3Fingerprint(f.JA3Fingerprint)
}

if f.JA4Fingerprint != nil {
m["ja4_fingerprint"] = flattenJA4Fingerprint(f.JA4Fingerprint)
}

if f.JsonBody != nil {
m["json_body"] = flattenJSONBody(f.JsonBody)
}
Expand Down Expand Up @@ -2221,6 +2269,18 @@ func flattenJA3Fingerprint(j *awstypes.JA3Fingerprint) interface{} {
return []interface{}{m}
}

func flattenJA4Fingerprint(j *awstypes.JA4Fingerprint) interface{} {
if j == nil {
return []interface{}{}
}

m := map[string]interface{}{
"fallback_behavior": j.FallbackBehavior,
}

return []interface{}{m}
}

func flattenJSONBody(b *awstypes.JsonBody) interface{} {
if b == nil {
return []interface{}{}
Expand Down Expand Up @@ -2949,6 +3009,28 @@ func flattenRateLimitHeader(apiObject *awstypes.RateLimitHeader) []interface{} {
}
}

func flattenRateLimitJa3Fingerprint(apiObject *awstypes.RateLimitJA3Fingerprint) []interface{} {
if apiObject == nil {
return nil
}
return []interface{}{
map[string]interface{}{
"fallback_behavior": apiObject.FallbackBehavior,
},
}
}

func flattenRateLimitJa4Fingerprint(apiObject *awstypes.RateLimitJA4Fingerprint) []interface{} {
if apiObject == nil {
return nil
}
return []interface{}{
map[string]interface{}{
"fallback_behavior": apiObject.FallbackBehavior,
},
}
}

func flattenRateLimitLabelNamespace(apiObject *awstypes.RateLimitLabelNamespace) []interface{} {
if apiObject == nil {
return nil
Expand Down Expand Up @@ -3024,6 +3106,12 @@ func flattenRateBasedStatementCustomKeys(apiObject []awstypes.RateBasedStatement
map[string]interface{}{},
}
}
if o.JA3Fingerprint != nil {
tfMap["ja3_fingerprint"] = flattenRateLimitJa3Fingerprint(o.JA3Fingerprint)
}
if o.JA4Fingerprint != nil {
tfMap["ja4_fingerprint"] = flattenRateLimitJa4Fingerprint(o.JA4Fingerprint)
}
if o.LabelNamespace != nil {
tfMap["label_namespace"] = flattenRateLimitLabelNamespace(o.LabelNamespace)
}
Expand Down
26 changes: 23 additions & 3 deletions internal/service/wafv2/schemas.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,8 @@ var fieldToMatchBaseSchema = sync.OnceValue(func() *schema.Resource {
"cookies": cookiesSchema(),
"header_order": headerOrderSchema(),
"headers": headersSchema(),
"ja3_fingerprint": ja3fingerprintSchema(),
"ja3_fingerprint": jaFingerprintSchema(),
"ja4_fingerprint": jaFingerprintSchema(),
"json_body": jsonBodySchema(),
"method": emptySchema(),
"query_string": emptySchema(),
Expand Down Expand Up @@ -504,6 +505,23 @@ var forwardedIPConfigSchema = sync.OnceValue(func() *schema.Schema {
}
})

var rateLimitJAFingerprintConfigSchema = sync.OnceValue(func() *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"fallback_behavior": {
Type: schema.TypeString,
Required: true,
ValidateDiagFunc: enum.Validate[awstypes.FallbackBehavior](),
},
},
},
}
})

var textTransformationSchema = sync.OnceValue(func() *schema.Schema {
return &schema.Schema{
Type: schema.TypeSet,
Expand Down Expand Up @@ -860,7 +878,7 @@ func cookiesMatchPatternSchema() *schema.Schema {
}
}

func ja3fingerprintSchema() *schema.Schema {
func jaFingerprintSchema() *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
Optional: true,
Expand Down Expand Up @@ -1078,7 +1096,9 @@ func rateBasedStatementSchema(level int) *schema.Schema {
},
},
},
"ip": emptySchema(),
"ip": emptySchema(),
"ja3_fingerprint": rateLimitJAFingerprintConfigSchema(),
"ja4_fingerprint": rateLimitJAFingerprintConfigSchema(),
"label_namespace": {
Type: schema.TypeList,
Optional: true,
Expand Down
Loading

0 comments on commit eb9ee3b

Please sign in to comment.