Class: Mt::Wall::DSL::DeviceBuilder

Inherits:
Object
  • Object
show all
Includes:
PolicyScope
Defined in:
lib/mt/wall/dsl/device_builder.rb

Overview

Block context for the ‘device` verb. Configures the box’s OWN firewall (Layer B), decoupled from the abstract host/group/rule model (Layer A). The abstract grants are device-agnostic and compiled into this device’s forward chain by the Compiler — they are NOT declared here.

Verbs:

* policy <chain>, <action>      — override the global chain default
* input / output / forward { } — open a ChainBuilder for that chain
  (the box's own input/output rules and the forward baseline, e.g.
  established/related/invalid handling)
* nat { }                       — open a NatBuilder (the box's
  `/ip firewall nat` table; IPv4-only for v1)
* management src:, service:, port: — explicitly declare the mgmt
  traffic the safe-chain must protect against lockout (else inferred
  from the transport). REPEATABLE: each call adds another protected
  path; the compiler emits the union of all of them.

device "edge-1", host: "10.0.0.1", transport: :rest_api do
  policy :input,   :drop
  policy :forward, :drop
  management src: "admin", service: "ssh"   # optional; inferred if omitted

  input do
    allow_established
    drop_invalid
    accept protocol: :icmp
    accept protocol: :tcp, dst_port: 22, src: "admin"   # mgmt access
  end

  forward do
    allow_established
    drop_invalid
    # global Layer-A grants are injected here, then the default policy
  end

  nat do
    masquerade out_interface: "ether1-wan"
    dst_nat protocol: :tcp, dst_port: 443, in_interface: "ether1-wan",
            to_addresses: "10.0.0.5", to_ports: 8443
  end
end

Instance Method Summary collapse

Methods included from PolicyScope

#policy

Constructor Details

#initialize(name, host:, transport: :rest_api, **options) ⇒ DeviceBuilder

Returns a new instance of DeviceBuilder.



50
51
52
53
54
55
56
57
58
59
# File 'lib/mt/wall/dsl/device_builder.rb', line 50

def initialize(name, host:, transport: :rest_api, **options)
  @name = name
  @host = host
  @transport = transport
  @options = options
  @policies = []
  @filter_rules = []
  @nat_rules = []
  @management = []
end

Instance Method Details

#forward(&block) ⇒ void

This method returns an undefined value.



74
75
76
# File 'lib/mt/wall/dsl/device_builder.rb', line 74

def forward(&block)
  collect_chain(:forward, &block)
end

#input { ... } ⇒ void

This method returns an undefined value.

Open a chain context; collected FilterRules are tagged with the chain.

Yields:

  • a ChainBuilder context



64
65
66
# File 'lib/mt/wall/dsl/device_builder.rb', line 64

def input(&block)
  collect_chain(:input, &block)
end

#management(src: nil, service: nil, port: nil) ⇒ void

This method returns an undefined value.

Explicitly declare the management traffic the safe-chain must keep open (INPUT chain only), so an apply can never lock the operator out. EXPLICIT is PRIMARY; ‘src` references a Layer-A host/group, `service` references a Service by name, `port` is a raw port. Stored on Model::Device#management.

REPEATABLE: each call ADDS one protected management path; the Compiler emits the UNION of all of them (so a device can protect, e.g., an SSH admin AND a REST/CI apply channel from different sources). A second call no longer overwrites the first.

This declaration becomes REQUIRED whenever the device locks its input chain (‘policy :input, :drop`): the Compiler FAILS FAST (ConfigurationError) if NO management path is declared. Transport inference is only a best-effort BACKSTOP (full mgmt service set —winbox 8291, ssh 22, api 8728, rest 80/443 — both families) used ONLY when no explicit management path exists, and is NEVER treated as the authoritative human mgmt source when the input chain is locked; the apply-connection source in particular is not authoritative.

management src: "admin", service: "ssh"
management src: "ci",    port: 443        # adds a second path

Parameters:

  • src (String, nil) (defaults to: nil)

    host/group name -> src-address-list

  • service (String, nil) (defaults to: nil)

    Service name (protocol/port)

  • port (Integer, Array, nil) (defaults to: nil)

    raw port(s) if no Service is used



114
115
116
117
118
119
120
121
# File 'lib/mt/wall/dsl/device_builder.rb', line 114

def management(src: nil, service: nil, port: nil)
  Validators.validate_name!(src, label: "management src") if src
  Validators.validate_name!(service, label: "management service") if service
  Validators.validate_ports!(port) unless port.nil?

  spec = { src: src, service: service, port: port }.compact
  @management << spec unless spec.empty?
end

#nat { ... } ⇒ void

This method returns an undefined value.

Open a NatBuilder context; collected NatRules are stored on the device.

Yields:

  • a NatBuilder context



81
82
83
84
85
# File 'lib/mt/wall/dsl/device_builder.rb', line 81

def nat(&block)
  builder = NatBuilder.new
  builder.instance_eval(&block) if block
  @nat_rules.concat(builder.rules)
end

#output(&block) ⇒ void

This method returns an undefined value.



69
70
71
# File 'lib/mt/wall/dsl/device_builder.rb', line 69

def output(&block)
  collect_chain(:output, &block)
end

#record_policy(policy) ⇒ void

This method returns an undefined value.

PolicyScope storage hook.



125
126
127
# File 'lib/mt/wall/dsl/device_builder.rb', line 125

def record_policy(policy)
  @policies << policy
end

#to_deviceModel::Device

Materialize the collected policies / filter rules / nat rules / management spec into a Model::Device.

Returns:



132
133
134
135
136
137
# File 'lib/mt/wall/dsl/device_builder.rb', line 132

def to_device
  Model::Device.new(name: Validators.validate_name!(@name, label: "device"),
                    host: @host, transport: @transport, policies: @policies,
                    filter_rules: @filter_rules, nat_rules: @nat_rules,
                    management: @management, options: @options)
end