[WAF]规则中增加请求Header长度限制和响应Header长度限制

This commit is contained in:
GoEdgeLab
2020-11-18 19:35:32 +08:00
parent 545ec75842
commit 60ba3e0a05
9 changed files with 192 additions and 66 deletions

View File

@@ -1,7 +1,7 @@
package teaconst package teaconst
const ( const (
Version = "0.0.1" Version = "0.0.2"
ProductName = "Edge Admin" ProductName = "Edge Admin"
ProcessName = "edge-admin" ProcessName = "edge-admin"

View File

@@ -22,15 +22,16 @@ func (this *CreateRulePopupAction) RunGet(params struct {
}) { }) {
// check points // check points
checkpointList := []maps.Map{} checkpointList := []maps.Map{}
for _, def := range firewallconfigs.AllCheckpoints { for _, checkpoint := range firewallconfigs.AllCheckpoints {
if (params.Type == "inbound" && def.IsRequest) || (params.Type == "outbound" && !def.IsRequest) { if (params.Type == "inbound" && checkpoint.IsRequest) || (params.Type == "outbound" && !checkpoint.IsRequest) {
checkpointList = append(checkpointList, maps.Map{ checkpointList = append(checkpointList, maps.Map{
"name": def.Name, "name": checkpoint.Name,
"prefix": def.Prefix, "prefix": checkpoint.Prefix,
"description": def.Description, "description": checkpoint.Description,
"hasParams": len(def.Params) > 0, "hasParams": len(checkpoint.Params) > 0,
"params": def.Params, "params": checkpoint.Params,
"options": def.Options, "options": checkpoint.Options,
"isComposed": checkpoint.IsComposed,
}) })
} }
} }
@@ -89,7 +90,7 @@ func (this *CreateRulePopupAction) RunPost(params struct {
rule.CheckpointOptions = map[string]interface{}{} rule.CheckpointOptions = map[string]interface{}{}
for _, option := range options { for _, option := range options {
rule.CheckpointOptions[option.GetString("code")] = option.GetString("value") rule.CheckpointOptions[option.GetString("code")] = option.Get("value")
} }
} }

View File

@@ -86,15 +86,15 @@ func (this *GroupAction) RunGet(params struct {
"name": set.Name, "name": set.Name,
"rules": lists.Map(set.Rules, func(k int, v interface{}) interface{} { "rules": lists.Map(set.Rules, func(k int, v interface{}) interface{} {
rule := v.(*firewallconfigs.HTTPFirewallRule) rule := v.(*firewallconfigs.HTTPFirewallRule)
return maps.Map{ return maps.Map{
"param": rule.Param, "param": rule.Param,
"operator": rule.Operator, "operator": rule.Operator,
"value": rule.Value, "value": rule.Value,
"isCaseInsensitive": rule.IsCaseInsensitive, "isCaseInsensitive": rule.IsCaseInsensitive,
"isComposed": firewallconfigs.CheckCheckpointIsComposed(rule.Prefix()),
} }
}), }),
"isOn": set.IsOn, "isOn": set.IsOn,
"action": strings.ToUpper(set.Action), "action": strings.ToUpper(set.Action),
"actionOptions": set.ActionOptions, "actionOptions": set.ActionOptions,
"actionName": firewallconfigs.FindActionName(set.Action), "actionName": firewallconfigs.FindActionName(set.Action),

View File

@@ -3,6 +3,7 @@ package waf
import ( import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/models" "github.com/TeaOSLab/EdgeAdmin/internal/web/models"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps" "github.com/iwind/TeaGo/maps"
) )
@@ -53,5 +54,20 @@ func (this *PolicyAction) RunGet(params struct {
"groups": internalGroups, "groups": internalGroups,
} }
// 正在使用此策略的服务
listServersResp, err := this.RPC().ServerRPC().FindAllEnabledServersWithHTTPFirewallPolicyId(this.AdminContext(), &pb.FindAllEnabledServersWithHTTPFirewallPolicyIdRequest{FirewallPolicyId: params.FirewallPolicyId})
if err != nil {
this.ErrorPage(err)
return
}
serverMaps := []maps.Map{}
for _, server := range listServersResp.Servers {
serverMaps = append(serverMaps, maps.Map{
"id": server.Id,
"name": server.Name,
})
}
this.Data["servers"] = serverMaps
this.Show() this.Show()
} }

View File

@@ -39,7 +39,7 @@ Vue.component("http-firewall-rules-box", {
<input type="hidden" name="rulesJSON" :value="JSON.stringify(rules)"/> <input type="hidden" name="rulesJSON" :value="JSON.stringify(rules)"/>
<div v-if="rules.length > 0"> <div v-if="rules.length > 0">
<div v-for="(rule, index) in rules" class="ui label tiny" style="margin-bottom: 0.5em"> <div v-for="(rule, index) in rules" class="ui label tiny" style="margin-bottom: 0.5em">
<span>{{rule.param}} <var>{{rule.operator}}</var> {{rule.value}}</span> <span>{{rule.param}} <var v-if="rule.value.length > 0">{{rule.operator}}</var> {{rule.value}}</span>
<a href="" title="修改" @click.prevent="updateRule(index, rule)"><i class="icon pencil small"></i></a> <a href="" title="修改" @click.prevent="updateRule(index, rule)"><i class="icon pencil small"></i></a>
<a href="" title="删除" @click.prevent="removeRule(index)"><i class="icon remove"></i></a> <a href="" title="删除" @click.prevent="removeRule(index)"><i class="icon remove"></i></a>
</div> </div>

View File

@@ -0,0 +1,86 @@
// 通用Header长度
let defaultGeneralHeaders = ["Cache-Control", "Connection", "Date", "Pragma", "Trailer", "Transfer-Encoding", "Upgrade", "Via", "Warning"]
Vue.component("http-cond-general-header-length", {
props: ["v-checkpoint"],
data: function () {
let headers = null
let length = null
if (window.parent.UPDATING_RULE != null) {
let options = window.parent.UPDATING_RULE.checkpointOptions
if (options.headers != null && Array.$isArray(options.headers)) {
headers = options.headers
}
if (options.length != null) {
length = options.length
}
}
if (headers == null) {
headers = defaultGeneralHeaders
}
if (length == null) {
length = 128
}
let that = this
setTimeout(function () {
that.change()
}, 100)
return {
headers: headers,
length: length
}
},
watch: {
length: function (v) {
let len = parseInt(v)
if (isNaN(len)) {
len = 0
}
if (len < 0) {
len = 0
}
this.length = len
this.change()
}
},
methods: {
change: function () {
this.vCheckpoint.options = [
{
code: "headers",
value: this.headers
},
{
code: "length",
value: this.length
}
]
}
},
template: `<div>
<table class="ui table">
<tr>
<td class="title">通用Header列表</td>
<td>
<values-box :values="headers" :placeholder="'Header'" @change="change"></values-box>
<p class="comment">需要检查的Header列表。</p>
</td>
</tr>
<tr>
<td>Header值超出长度</td>
<td>
<div class="ui input right labeled">
<input type="text" name="" style="width: 5em" v-model="length" maxlength="6"/>
<span class="ui label">字节</span>
</div>
<p class="comment">超出此长度认为匹配成功0表示不限制。</p>
</td>
</tr>
</table>
</div>`
})

View File

@@ -12,7 +12,10 @@
<td> <td>
<select name="prefix" class="ui dropdown auto-width" @change="changeCheckpoint()" v-model="rule.checkpointPrefix"> <select name="prefix" class="ui dropdown auto-width" @change="changeCheckpoint()" v-model="rule.checkpointPrefix">
<option value="">[选择参数]</option> <option value="">[选择参数]</option>
<option v-for="cp in checkpoints" :value="cp.prefix">{{cp.name}} - [ {{cp.prefix}}]</option> <optgroup label="特殊参数"></optgroup>
<option v-for="cp in checkpoints" v-if="cp.isComposed" :value="cp.prefix">{{cp.name}} - [ {{cp.prefix}}]</option>
<optgroup label="通用参数"></optgroup>
<option v-for="cp in checkpoints" v-if="!cp.isComposed" :value="cp.prefix">{{cp.name}} - [ {{cp.prefix}}]</option>
</select> </select>
<p class="comment" v-if="checkpoint != null"><span class="ui label tiny">${<em style="font-style: normal;">{{checkpoint.prefix}}</em>}</span>{{checkpoint.description}}</p> <p class="comment" v-if="checkpoint != null"><span class="ui label tiny">${<em style="font-style: normal;">{{checkpoint.prefix}}</em>}</span>{{checkpoint.description}}</p>
</td> </td>
@@ -27,59 +30,71 @@
</td> </td>
</tr> </tr>
<!-- 选项 --> <!-- 组合规则的选项 -->
<tbody v-if="checkpoint != null && checkpoint.options != null && checkpoint.options.length > 0"> <tbody v-if="checkpoint != null && checkpoint.isComposed">
<tr v-for="option in checkpoint.options"> <tr>
<td>{{option.name}}</td> <td>配置选项</td>
<td> <td>
<div class="ui fields inline" v-if="option.type == 'field'"> <http-cond-general-header-length v-if="checkpoint.prefix == 'requestGeneralHeaderLength' || checkpoint.prefix == 'responseGeneralHeaderLength'" :v-checkpoint="checkpoint"></http-cond-general-header-length>
<div class="ui field"> </td>
<input type="text" name="" :placeholder="option.placeholder" :maxlength="(option.maxLength > 0)?option.maxLength:1024" :size="option.size" v-model="option.value"/> </tr>
</div>
<div class="ui field">
{{option.rightLabel}}
</div>
</div>
<div class="ui fields inline" v-if="option.type == 'options'">
<div class="ui field">
<select class="ui dropdown" :style="'width:' + option.size + 'em'" name="" v-model="option.value">
<option v-for="opt in option.options" :value="opt.value">{{opt.name}}</option>
</select>
</div>
<div class="ui field">
{{option.rightLabel}}
</div>
</div>
<p class="comment" v-if="option.comment != null && option.comment.length > 0">{{option.comment}}</p>
</td>
</tr>
</tbody> </tbody>
<tr> <!-- 选项 -->
<td>操作符</td> <tbody v-if="checkpoint != null && !checkpoint.isComposed && checkpoint.options != null && checkpoint.options.length > 0">
<td> <tr v-for="option in checkpoint.options">
<select class="ui dropdown" name="operator" style="width:10em" v-model="rule.operator" @change="changeOperator()"> <td>{{option.name}}</td>
<option v-for="op in operators" :value="op.code">{{op.name}}</option> <td>
</select> <div class="ui fields inline" v-if="option.type == 'field'">
<p class="comment" v-if="operator != null" v-html="operator.description"></p> <div class="ui field">
</td> <input type="text" name="" :placeholder="option.placeholder" :maxlength="(option.maxLength > 0)?option.maxLength:1024" :size="option.size" v-model="option.value"/>
</tr> </div>
<tr v-if="operator.case != 'none'"> <div class="ui field">
<td>开启大小写不敏感</td> {{option.rightLabel}}
<td> </div>
<div class="ui checkbox"> </div>
<input name="case" type="checkbox" value="1" v-model="rule.isCaseInsensitive"/> <div class="ui fields inline" v-if="option.type == 'options'">
<label></label> <div class="ui field">
</div> <select class="ui dropdown" :style="'width:' + option.size + 'em'" name="" v-model="option.value">
<p class="comment">开启后忽略英文字母大小写</p> <option v-for="opt in option.options" :value="opt.value">{{opt.name}}</option>
</td> </select>
</tr> </div>
<tr> <div class="ui field">
<td>对比值</td> {{option.rightLabel}}
<td> </div>
<textarea rows="3" maxlength="4096" name="value" v-model="rule.value"></textarea> </div>
</td> <p class="comment" v-if="option.comment != null && option.comment.length > 0">{{option.comment}}</p>
</tr> </td>
</tr>
</tbody>
<tbody v-show="checkpoint != null && !checkpoint.isComposed">
<tr>
<td>操作符</td>
<td>
<select class="ui dropdown" name="operator" style="width:10em" v-model="rule.operator" @change="changeOperator()">
<option v-for="op in operators" :value="op.code">{{op.name}}</option>
</select>
<p class="comment" v-if="operator != null" v-html="operator.description"></p>
</td>
</tr>
<tr v-if="operator.case != 'none'">
<td>开启大小写不敏感</td>
<td>
<div class="ui checkbox">
<input name="case" type="checkbox" value="1" v-model="rule.isCaseInsensitive"/>
<label></label>
</div>
<p class="comment">开启后忽略英文字母大小写</p>
</td>
</tr>
<tr>
<td>对比值</td>
<td>
<textarea rows="3" maxlength="4096" name="value" v-model="rule.value"></textarea>
</td>
</tr>
</tbody>
</table> </table>
<submit-btn></submit-btn> <submit-btn></submit-btn>

View File

@@ -48,7 +48,7 @@
</td> </td>
<td class="rules-box"> <td class="rules-box">
<div v-for="rule in set.rules" style="margin-top: 0.4em;margin-bottom:0.4em"> <div v-for="rule in set.rules" style="margin-top: 0.4em;margin-bottom:0.4em">
<span class="ui label tiny">{{rule.name}}[{{rule.param}}] <var :class="{dash:rule.isCaseInsensitive}" :title="rule.isCaseInsensitive ? '大小写不敏感':''">{{rule.operator}}</var> {{rule.value}}</span> <span class="ui label tiny">{{rule.name}}[{{rule.param}}] <var :class="{dash:rule.isCaseInsensitive}" :title="rule.isCaseInsensitive ? '大小写不敏感':''" v-if="!rule.isComposed">{{rule.operator}}</var> {{rule.value}}</span>
</div> </div>
<span class="ui disabled" v-if="set.rules.length == 0">暂时还没有规则</span> <span class="ui disabled" v-if="set.rules.length == 0">暂时还没有规则</span>
</td> </td>

View File

@@ -29,4 +29,12 @@
</td> </td>
</tr> </tr>
</table> </table>
<h4>使用此策略的服务</h4>
<p class="comment" v-if="servers.length == 0">暂时还没有代理服务使用此策略。</p>
<table class="ui table selectable" v-if="servers.length > 0">
<tr v-for="server in servers">
<td>{{server.name}}<link-icon :href="'/servers/server?serverId=' + server.id"></link-icon></td>
</tr>
</table>
</div> </div>