|SlyWare Home | Products | Downloads | Free Source Code | Current Projects | Articles and Papers||About|
|Page Hop [ 1 2 3 4 5 ]|
Variables and Service Constructs
I've also introduced the concept of a service construct. These are sections of configuration data for a service that provides some encapsulate functionality. They apply at the abstracted XML description level, and can be useful to group common behaviour into manageable sections. Another advantage they introduce is described in the following Variables section. A service construct can have it's own identity and description.
A concept of external service constructs has also been introduced. These are simply standard service constructs that are held in some central repository, available to all nodes and services within the system. These are specifically useful for providing common sets of behaviour to services, and saves the user having to explicitly include them in their service configurations. A good example of this is the behaviour used in firewalls to prevent IP address spoofing over interfaces. Typically, a set of ten or so rules are used, and these are generally the same over all firewall implementations. Therefore, in the XML description of a firewall, the user can simply reference the external spoof protection construct. At service translation level, the reference will be replaced by the actual configurations.
The following extract shows a service construct, for a set of firewall rules. Variables local to this construct have been defined and are explained later. Three rules are contained within it, with the aim to control the loopback device. Some variables within this example such as $interface_1 are defined within the node description itself.
<!-- Standard loopback processing --> <fw:FirewallConstruct name="Loopback Handeling" description="Does stuff with the loopback device"> <Mappings> <Variable name="block_rule_base" value="$rule_base+100"/> </Mappings> <fw:Rule Desc="Allow loopback" RuleID="$block_rule_base"> <fw:action perform="pass"/> <fw:protocol type="all"/> <fw:src type="any"/> <fw:dst type="any"/> <fw:interface via="lo0"/> </fw:Rule> <fw:Rule Desc="Prevent spoofing of loopback" RuleID="$block_rule_base+1"> <fw:action perform="deny"/> <fw:log logamount="0" value="true"/> <fw:protocol type="all"/> <fw:src type="any"/> <fw:dst mask="255.0.0.0" address="127.0.0.1" type="ip"/> <fw:interface/> </fw:Rule> <fw:Rule Desc="Prevent spoofing of internal private ip range" RuleID="$block_rule_base+2"> <fw:action perform="deny"/> <fw:protocol type="all"/> <fw:src type="ip" negate="true" address="$net_num" mask="$net_mask"/> <fw:dst type="any"/> <fw:interface direction="in" recv="$interface_1"/> </fw:Rule> </fw:FirewallConstruct>
Service Constructs represent the building blocks of the overall configuration. As explained in the following sections, they can be used for external references, variable scoping and function calls. From any point within the system, a node can reference any service construct within any service on another node. This adds a great deal of functionality, as configurations can be automatically generated depending on the specification of another.
Variable mappings play an important role within the system, and hence have been integrated into the application from the start. I identified early that there would be a large amount of data common to all services running on a node. This could be extended to the network as well, as nodes within a single subnet will share common data. An example of this is that each node would have it's subnet mask and network number, and each service within a node may want to know that nodes IP. Also, network interfaces need to be known by firewall services for example.
Variables also play an important role in relation to situations such as ID's for rules. Within a firewall configuration, the user may wish to give each rule a unique ID number, but may not know in advanced the ranges required. This can be easily solved by defining a variable within a service construct, such as $rule_id_base, then specifying offsets from this value. The package supports arithmetic within variables, so ID's could be mapped simply as $rule_id_base+1 for example. Along with this, recursive variable resolution occurs if part of one variable maps to another. A set of global variables are also provided, such as $this returning the current node name.
The service construct XML shown in the previous section demonstrates how these variables can be used. We have defined a local variable, initially taking on the value of $rule_base+100. This is a useful way to manage rule ID's, specifying the offset that this block of rules takes. Variables can be used within any string element, hence allowing for ease of configuration throughout the system.Goto Top
Construct Libraries and External Constructs
Service constructs can be defined externally with construct libraries. The idea is that service configurations can import the behaviour from such libraries which provide some encapsulated, logical chunk of information. An example is providing basic spoof prevention for firewalls. Typically a small set of rules can be used to prevent IP Packets with the source address of non-globally routable addresses from entering in via the un-trusted, global interface. As these rules will remain the same over many firewall configurations, it is advantageous to define them externally, and simply reference them.
<!-- Use externally defined loopback feature --> <dns:DNSConstruct name="Loopback" description="Pull in external loopback definitions"> <Mappings> <Variable name="primaryns" value="$NS"/> <Variable name="adminmail" value="admin.$root_domain"/> </Mappings> <!-- Pull in loopback definition. Use variables to define behaviour --> <ExternalConstruct name="DNS::ExternalConstruct:Loopback"/> </dns:DNSConstruct>
The XML extract above shows how a reference to an externally defined service construct can be made. The construct to be included is DNS::ExternalConstruct:Loopback, and various variable mappings are made to pass information onto this construct. When full XML generation is required, i.e. when passing through the translator, the whole dns:DNSConstruct element will be replaced by the construct within the external library.
External constructs are implemented using a substitution method. When the user wishes to generate fully expanded XML configuration, such as prior to running through the XSL translator, the external construct references are resolved. However, variable scope also plays an important role. The reference to include an external construct can itself include a set of variable mappings, enabling data to be passed through to the external construct itself. A simple example of this, using the spoof prevention scheme mentioned previously, is to define $ut_interface to be the untrusted interface of the machine. The external construct would then use this data in defining the actual rules.
<!-- This file holds all the different DNS constructs that can be use. Each DNS constuct is basically a set of forward and reverse start of authorities, and include external variables which must be defined when they are included. To use these, you must define the variables somewhere in the variable namespace. Hence, if a construct defines that $primaryns needs to be present, define it somewhere, either within the parent Service or Node. --> <ntman:ConstructLibrary xmlns="http://www.ecs.soton.ac.uk/~src299/xmlnetman/dns" xmlns:ntman="http://www.ecs.soton.ac.uk/~src299/xmlnetman" name="External DNS constructs" description="Various DNS external constructs"> <!-- DNS reverse start of authority entry for lookback resolution. User should define $primaryns to be the address of the primary name server and $adminmail to the administrators email. --> <DNSConstruct name="DNS::ExternalConstruct:Loopback" description="Lookback DNS reverse resolution"> <dns:ReverseSOA match="0.0.127.in-addr-arpa" primaryns="$primaryns" adminmail="$adminmail" file="db.127.0.0" serial="1" refresh="10800" retry="3600" expire="604800" min_ttl="86400"> <!-- Just define a name server and out loopback mappings. --> <dns:NS match="0.0.127.in-addr.arpa" target="$primaryns"/> <dns:PTR match="126.96.36.199.in-addr.arpa" target="localhost"/> </dns:ReverseSOA> </DNSConstruct> </ntman:ConstructLibrary>
The above XML shows the content of the library entry for DNS::ExternalConstruct:Loopback. The variables defined within the original reference, $primaryns and $adminmail, are used within the external definition. The use of variables to configure external constructs enables the user to include multiple copies of the construct within a single service, with different functionality each time. It also helps achieve the goal of making the definitions as generic as possible.Goto Top
The introduction of function calls adds a whole new level of scope to the abstracted configuration description. The idea is to add computational functionality to the description, before it is passed through the translator to generate the final files. Within a service, an element can be used to describe a function call. As input, this takes the name of the function to evaluate, the service construct to pass as input XML to the function, and a set of parameters.
Functions themselves are simply XSL files. When a function is evaluated, full XML is generated from the service construct specified as the input. Any service construct can be used as input, no matter where it is defined, or what service it may be part of. For example, you could call a function, using a section of configuration from another nodes Firewall description. The function itself will then carry out its computation, and is expected to generate valid XML configuration in the form of another service construct. This is then directly replaced for the original function call, hence completing the operation.
Reference to the input service construct is made using the standard naming method. However, you can indeed express that you want all service constructs from a particular service to be input to the function. For example, to request all service constructs from the DNS configuration on a node called Bob to be input to a function, use DNS::ServiceConstruct:Bob/*. This would allow for dynamic configuration generation, when the configuration of one service depends on that of another.
In addition, input to functions can be specified using explicit paths to element. These can also work cross nodes, and specify exactly the elements required as input, from the service level down to Rule level. Paths use indexes on elements to address positions within the XML configuration itself, which implies that they are more powerful but harder to maintain when extra configurations are added.
The first path specifies the second Rule within the third FirewallConstruct of the first Firewall service definition on the node called bill. The second similarly indexes the second FirewallConstruct within the first Firewall service running on bill.
It is proposed that as a future enhancement to this project, XML filter rules will be used to specify in detail the exact elements that get passed as input to function calls. This would use a similar syntax to the translation restriction filter described on page 3, enabling dynamic processing of Rule's to select specific combinations of elements and attributes.
The Need and Use of Function Calls
A prime example of where such function calls are useful is that with DNS configuration. Within the configuration, there are typically two sets of data, forward and reverse start of authorities. These map from name to IP address, and from IP address to name respectively. However, there is a great dependency between the two. The addition of a single node to the service would require two alterations to the configuration, both using a similar description.
The following XML output shows the configuration within the DNS service of a node. The first service construct is a definition of the forward name to address mappings. The second construct contains a CallFunction element. This takes as input the forward definitions (DNS::ServiceConstruct:Forward) and passes them through the function, to generate the required reverse rules. Note that $this is mapped to the name of the node this service is within. Function calls can be made using service constructs from any node and within any service.
<!-- All the forward resolution files. This construct will be used as input to the function. --> <dns:DNSConstruct name="Forward" description="Standard forward resolution"> <Mappings> <Variable name="file" value="db.slybase.homeip.net"/> <Variable name="network" value="192.168.2"/> </Mappings> <!-- Standard forward lookup --> <dns:ForwardSOA match="$root_domain" primaryns="$NS" adminmail="admin.$root_domain" file="db.slybase.homeip.net" serial="5" refresh="10800" retry="3600" expire="604800" min_ttl="86400"> <!-- Our name server, this is overriden when the function is called. --> <dns:NS match="@" target="$this.$root_domain"/> <!-- Just some name to address IPv4 mappings. --> <dns:A match="localhost" target="127.0.0.1"/> <dns:A match="bill" target="$network.100"/> <dns:A match="ben" target="$network.101"/> </dns:ForwardSOA> </dns:DNSConstruct> <!-- Use a function to generate the reverse resolution file (SOA) --> <dns:DNSConstruct name="Reverse" description="Standard reverse resolution"> <Mappings> <Variable name="NS" value="Lets Override it"/> </Mappings> <!-- Following will create a new function structure. When full XML is required, the whole CallFunction structure will be replaced by the result of feeding the XML generated by the ServiceConstruct 'onConstruct' through the XSL sheet representing the function 'name'. The parameter 'dbfile' is the name of the file we use to reference this reverse SOA --> <CallFunction name="DNS::Functions:GenReverseSOA" onConstruct="DNS::ServiceConstruct:$this/Forward"> <Parameter name="dbfile" value="db.192.168.2"/> <Parameter name="with_net" value="$network"/> </CallFunction> </dns:DNSConstruct>
The implemented design now introduces a function to automatically generate the reverse mappings from the forward ones. The user specifies the FowardSOA (forward start of authority) segment as usual. However, to generate the ReverseSOA, a function call is made, taking as input the contents of the ForwardSOA service construct. The function call used here is DNS::Function:GenReverseSOA, which in itself is an XSL stylesheet.
When evaluated (on full XML generation), the construct referenced as input to the function, DNS::ServiceConstruct:$this/Forward, is called to generate its XML content. This XML is then passed as input to the function call, using the XSLT processor. The function itself generates a PTR (IP address to name mapping) resource record for each node within the original ForwardSOA, and formats it as a ReverseSOA element. This is directly replaced with the original function call.
See here for an example of the final output it produces.
Expansion of Function Calls
As previously mentioned, basic wildcards can be specified to return a set of service constructs. One idea I plan to investigate is whether this could be used to set up another service within that node. A simple example is when a service requires network access via some standard ports to the outside world. If a function call was made from within the firewall configuration, it could effectively evaluate the service in question, and generate the rules needed to allow that service to function correctly.
On a higher level, I plan to see whether a function could in fact query its surroundings, and generate configurations to reflect them. For example, the firewall controlling access between two subnets could query a network to find out if any node was running a DNS service for example. If so, it could generate the rules to allow such access to traverse the firewall. As there are no boundaries between nodes in the sense of service constructs, detailed analysis of the overall network should be able to be achieved.
The following XML configuration shows how you could get a DNS service to be configured as an exact mirror of another, in this example the node called Ben serving foo.net. This is achieved simply by using the Global::Function:Duplication function call. This reads in the XML and spits it back out again. We use the address of DNS::ServiceConstruct:Ben/* to obtain every DNS service construct within the node Ben, hence inherit its behaviour. Some variables have also be redefined at the level of the CallFunction construct. This example simply demonstrates the dependencies that can be built up between nodes and their services, allowing for quick configuration.
<!-- DNS service configuration follows --> <dns:DNS> <!-- DNS wide global variable mappings --> <Mappings> <Variable name="root_domain" value="foo.net"/> <Variable name="NS" value="bill.$root_domain."/> <Variable name="network" value="192.168.2"/> </Mappings> <!-- Options - i.e. named.conf in BIND8 : Defines all our zones --> <dns:Bindings> <dns:Zone name="$root_domain" type="master" file="db.foo.net"/> <dns:Zone name="2.168.192.in-addr-arpa" type="master" file="db.192.168.2"/> <dns:Zone name="0.0.127.in-addr.arpa" type="master" file="db.127.0.0"/> </dns:Bindings> <!-- Use a function to get all DNS service constructs from the node Ben --> <dns:DNSConstruct name="Reverse" description="Standard reverse resolution"> <!-- Redefine some variables. --> <Mappings> <Variable name="name_server" value="$NS"/> <Variable name="use_domain" value="$root_domain"/> </Mappings> <!-- Execute the duplication function. grab all DNSConstructs from Ben. --> <CallFunction name="Global::Functions:Duplication" onConstruct="DNS::ServiceConstruct:Ben/*"> <Parameter name="dbfile" value="db.192.168.2"/> </CallFunction> </dns:DNSConstruct> <!-- Use externally defined loopback feature --> <dns:DNSConstruct name="Loopback" description="Pull in external loopback definitions"> <Mappings> <Variable name="primaryns" value="$NS"/> <Variable name="adminmail" value="admin.$root_domain"/> </Mappings> <!-- Pull in loopback definition. Use variables to define behaviour --> <ExternalConstruct name="DNS::ExternalConstruct:Loopback"/> </dns:DNSConstruct> </dns:DNS>Goto Top
|Last Updated /projects_xmlnetman_2.shtml (38639 bytes) on Dec 14 at 2004|