Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 24 additions & 22 deletions agent/app/api/v2/firewall.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func (b *BaseApi) SearchForwardingRules(c *gin.Context) {
// @Summary Operate forwarding rules
// @Accept json
// @Param request body dto.ForwardRuleOperate true "request"
// @Success 200
// @Success 200 {object} dto.FilterChainOperationResponse
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/firewall/forward/operate [post]
Expand All @@ -132,11 +132,12 @@ func (b *BaseApi) OperateForwardingRules(c *gin.Context) {
return
}

if err := forwardingService.OperateRules(request); err != nil {
result, err := forwardingService.OperateRules(request)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
helper.SuccessWithData(c, result)
}

// @Tags Firewall
Expand Down Expand Up @@ -259,29 +260,30 @@ func (b *BaseApi) LoadFirewallNativeDetail(c *gin.Context) {
}

// @Tags Firewall
// @Summary Check unified firewall v2 rules for duplicates and conflicts
// @Summary Adopt an external firewall rule
// @Accept json
// @Param request body dto.FirewallRuleCheck true "request"
// @Success 200 {object} dto.FirewallRuleCheckResponse
// @Param request body dto.FirewallRuleAdopt true "request"
// @Success 200
// @Failure 400 {object} dto.Response
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/firewall/rules/check [post]
func (b *BaseApi) CheckFirewallRules(c *gin.Context) {
var request dto.FirewallRuleCheck
// @Router /hosts/firewall/rules/adopt [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"纳管防火墙规则","formatEN":"adopt firewall rule"}
func (b *BaseApi) AdoptFirewallRule(c *gin.Context) {
var request dto.FirewallRuleAdopt
if err := helper.CheckBindAndValidate(&request, c); err != nil {
return
}
result, err := firewallService.Check(c.Request.Context(), c.ClientIP(), request)
if err != nil {
if err := firewallService.Adopt(c.Request.Context(), request); err != nil {
handleFirewallRuleError(c, err)
return
}
helper.SuccessWithData(c, result)
helper.Success(c)
}

// @Tags Firewall
// @Summary Create unified firewall v2 rules
// @Summary Queue firewall rule creation
// @Description Creation and import return a taskID immediately; validation and execution results are written to the task log.
// @Accept json
// @Param request body dto.FirewallRuleCreate true "request"
// @Success 200 {object} dto.FirewallRuleCreateResponse
Expand Down Expand Up @@ -458,8 +460,6 @@ func handleFirewallRuleError(c *gin.Context, err error) {
helper.ErrorWithBusinessCode(c, http.StatusConflict, "FW_RULE_STALE", "ErrInvalidParams", err)
case errors.Is(err, repo.ErrFirewallRuleRevisionConflict):
helper.ErrorWithBusinessCode(c, http.StatusConflict, "FW_RULE_REVISION_CONFLICT", "ErrInvalidParams", err)
case errors.Is(err, filter.ErrRuleCheckRequired):
helper.ErrorWithBusinessCode(c, http.StatusConflict, "FW_RULE_CHECK_REQUIRED", "ErrInvalidParams", err)
case errors.Is(err, filter.ErrUnsupportedScope), errors.Is(err, filter.ErrInvalidScope),
errors.Is(err, filter.ErrProviderUnavailable), errors.Is(err, filter.ErrAdapterUnavailable):
helper.ErrorWithBusinessCode(c, http.StatusBadRequest, "FW_SCOPE_UNSUPPORTED", "ErrInvalidParams", err)
Expand Down Expand Up @@ -598,7 +598,7 @@ func (b *BaseApi) OperateDockerPortGuard(c *gin.Context) {
// @Summary Delete Docker port guard policies
// @Accept json
// @Param request body dto.DockerPortGuardPolicyBatchDelete true "request"
// @Success 200
// @Success 200 {object} dto.FilterChainOperationResponse
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/firewall/docker/policies/delete/batch [post]
Expand All @@ -608,32 +608,34 @@ func (b *BaseApi) DeleteDockerPortGuardPolicies(c *gin.Context) {
if err := helper.CheckBindAndValidate(&request, c); err != nil {
return
}
if err := dockerPortGuardService.DeletePolicies(c.Request.Context(), request); err != nil {
result, err := dockerPortGuardService.DeletePolicies(request)
if err != nil {
handleDockerPortGuardError(c, err)
return
}
helper.Success(c)
helper.SuccessWithData(c, result)
}

// @Tags Firewall
// @Summary Batch upsert Docker port guard policies
// @Accept json
// @Param request body dto.DockerPortGuardPolicyBatch true "request"
// @Success 200
// @Success 200 {object} dto.FilterChainOperationResponse
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/firewall/docker/policies/batch [post]
// @x-panel-log {"bodyKeys":["mode"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"批量更新 Docker 端口防护策略 [mode]","formatEN":"batch update Docker port guard policies [mode]"}
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"批量更新 Docker 端口防护策略","formatEN":"batch update Docker port guard policies"}
func (b *BaseApi) UpsertDockerPortGuardPolicies(c *gin.Context) {
var request dto.DockerPortGuardPolicyBatch
if err := helper.CheckBindAndValidate(&request, c); err != nil {
return
}
if err := dockerPortGuardService.UpsertPolicies(c.Request.Context(), request); err != nil {
result, err := dockerPortGuardService.UpsertPolicies(request)
if err != nil {
handleDockerPortGuardError(c, err)
return
}
helper.Success(c)
helper.SuccessWithData(c, result)
}

func handleDockerPortGuardError(c *gin.Context, err error) {
Expand Down
75 changes: 38 additions & 37 deletions agent/app/dto/firewall.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,18 +109,6 @@ type FirewallRuleReset struct {
WithDockerRestart bool `json:"withDockerRestart"`
}

type FirewallRuleCheckResult struct {
Decision filter.CheckDecision `json:"decision"`
Classification filter.CheckClassification `json:"classification"`
Reason string `json:"reason"`
RequestedRule filter.FirewallRule `json:"requestedRule"`
RequestedRuleKey string `json:"requestedRuleKey"`
ExistingRuleUUID string `json:"existingRuleUUID,omitempty"`
Candidates []filter.ObservedRule `json:"candidates,omitempty"`
AllowedActions []filter.CheckAction `json:"allowedActions,omitempty"`
CheckFlag string `json:"checkFlag"`
}

type FirewallRuleInventory struct {
PageInfo
Scope filter.Scope `json:"scope,omitempty"`
Expand Down Expand Up @@ -213,49 +201,43 @@ type DockerPortGuardEndpointIdentity struct {
}

type DockerPortGuardPolicyBatch struct {
Endpoints []DockerPortGuardEndpointIdentity `json:"endpoints" validate:"required,min=1,max=256,dive"`
Mode string `json:"mode" validate:"required,oneof=deny_sources allow_sources deny_all"`
Sources []string `json:"sources" validate:"max=256,dive,required,max=64"`
Description string `json:"description" validate:"max=256"`
Policies []DockerPortGuardPolicy `json:"policies" validate:"required,min=1,dive"`
}

type DockerPortGuardPolicyBatchDelete struct {
UUIDs []string `json:"uuids" validate:"required,min=1,max=256,dive,required,max=64"`
UUIDs []string `json:"uuids" validate:"required,min=1,dive,required,max=64"`
}

type DockerPortGuardPolicy struct {
DockerPortGuardEndpointIdentity
Mode string `json:"mode" validate:"required,oneof=deny_sources allow_sources deny_all"`
Sources []string `json:"sources" validate:"dive,required,max=64"`
Description string `json:"description" validate:"max=256"`
}

type DockerPortGuardOperation struct {
Operation string `json:"operation" validate:"required,oneof=initialize bind unbind"`
TaskID string `json:"taskID,omitempty" validate:"omitempty,max=64"`
}

type FirewallRuleCheckItem struct {
AdoptLocator *filter.Locator `json:"adoptLocator,omitempty" validate:"excluded_with=UUID"`
UUID string `json:"uuid" validate:"omitempty,max=64"`
Rule filter.FirewallRule `json:"rule" validate:"required"`
}

type FirewallRuleCheck struct {
Items []FirewallRuleCheckItem `json:"items" validate:"required,min=1,max=256,dive"`
}

type FirewallRuleCheckResponse struct {
Items []FirewallRuleCheckResult `json:"items"`
type FirewallRuleAdopt struct {
Scope filter.Scope `json:"scope" validate:"required"`
InstanceKey string `json:"instanceKey" validate:"required,max=128"`
}

type FirewallRuleCreateItem struct {
Rule filter.FirewallRule `json:"rule" validate:"required"`
CheckFlag string `json:"checkFlag"`
Action filter.CheckAction `json:"action"`
AdoptInstanceKey string `json:"adoptInstanceKey"`
SourceKind string `json:"sourceKind" validate:"omitempty,oneof=user imported"`
SourceID string `json:"sourceID"`
Rule filter.FirewallRule `json:"rule" validate:"required"`
SourceKind string `json:"sourceKind" validate:"omitempty,oneof=user imported"`
SourceID string `json:"sourceID"`
}

type FirewallRuleCreate struct {
Items []FirewallRuleCreateItem `json:"items" validate:"required,min=1,max=256,dive"`
Items []FirewallRuleCreateItem `json:"items" validate:"required,min=1,dive"`
}

type FirewallRuleCreateResponse struct {
TaskID string `json:"taskID,omitempty"`
Queued bool `json:"queued,omitempty"`
Succeeded int `json:"succeeded"`
Failed int `json:"failed"`
Skipped int `json:"skipped"`
Expand Down Expand Up @@ -327,7 +309,7 @@ type FirewallRuleSyncFailure struct {
}

type FirewallRuleDelete struct {
UUIDs []string `json:"uuids" validate:"required,min=1,max=256,dive,required,max=64"`
UUIDs []string `json:"uuids" validate:"required,min=1,dive,required,max=64"`
}

type FirewallRuleDeleteResponse struct {
Expand Down Expand Up @@ -355,3 +337,22 @@ type FirewallRuleReorder struct {
TargetPosition *int64 `json:"targetPosition"`
Priority *int `json:"priority"`
}

func (p *FirewallRuleSyncPreview) Add(item FirewallRuleSyncItem) {
p.Items = append(p.Items, item)
switch item.Status {
case firewallsync.StatusReady:
p.Ready++
p.Total++
case firewallsync.StatusExisting:
p.Existing++
p.Total++
case firewallsync.StatusRemove:
p.Removed++
case firewallsync.StatusBlocked:
p.Blocked++
if item.ReasonCode != firewallsync.ReasonReadOnlyRule {
p.Total++
}
}
}
4 changes: 1 addition & 3 deletions agent/app/dto/forwarding.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ type ForwardRuleSearch struct {
Strategy string `json:"strategy"`
}

// ForwardRule preserves the existing firewall search response shape while
// keeping forwarding data separate from the filter client model.
type ForwardRule struct {
ID uint `json:"id"`
Chain string `json:"chain"`
Expand All @@ -34,7 +32,7 @@ type ForwardRule struct {

type ForwardRuleOperate struct {
ForceDelete bool `json:"forceDelete"`
Rules []ForwardRuleOperation `json:"rules"`
Rules []ForwardRuleOperation `json:"rules" validate:"required,min=1,dive"`
}

type ForwardRuleOperation struct {
Expand Down
13 changes: 0 additions & 13 deletions agent/app/model/firewall.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,19 +72,6 @@ func FirewallRuleOwner(sourceKind, sourceID string) string {
return sourceKind + ":" + sourceID
}

func FirewallRulesRevision(rules []FirewallRule) (string, error) {
ordered := append([]FirewallRule(nil), rules...)
sort.Slice(ordered, func(i, j int) bool {
return ordered[i].UUID < ordered[j].UUID
})
payload, err := json.Marshal(ordered)
if err != nil {
return "", err
}
sum := sha256.Sum256(payload)
return hex.EncodeToString(sum[:]), nil
}

func FirewallRuleFromDomain(rule filter.FirewallRule) (FirewallRule, error) {
normalized, err := filter.NormalizeRule(rule)
if err != nil {
Expand Down
Loading
Loading