|
| 1 | +# Common Patterns For Generators |
| 2 | + |
| 3 | +<!--* # copybara:strip_begin(internal linter) |
| 4 | +# LINT.IfChange |
| 5 | +# copybara:strip_end *--> |
| 6 | + |
| 7 | +## Objective |
| 8 | + |
| 9 | +The purpose of this document is to describe common patterns for new Capirca |
| 10 | +Generators. |
| 11 | + |
| 12 | +## Security based requirements: |
| 13 | + |
| 14 | +### Inet_version ‘Mixed’ Platform Support |
| 15 | + |
| 16 | +#### When the platform does not support “mixed” in a single access-list |
| 17 | + |
| 18 | +##### Problem |
| 19 | + |
| 20 | +When the inet_version is set to ‘mixed’ it implies that the resultant policy |
| 21 | +should contain addresses from both families. Some platforms do not support both |
| 22 | +address families to exist in the same filter therefore leading to Capirca |
| 23 | +generators needing to handle the output differently for those platforms. Cisco |
| 24 | +is an example platform that does not support a mixed filter being generated, and |
| 25 | +therefore it requires two separate *access-list* filters. |
| 26 | + |
| 27 | +Platforms that support mixed family filters will simply generate filters that |
| 28 | +contain both address families. |
| 29 | + |
| 30 | +##### Desired Approach |
| 31 | + |
| 32 | +**The desired approach will be to have Capirca output two filters, one for each |
| 33 | +address family, for platforms that do not support ‘mixed’.** This currently |
| 34 | +occurs already with |
| 35 | +[cisco.py](https://github.com/google/capirca/blob/master/capirca/lib/cisco.py) |
| 36 | +which outputs two access-lists one that contains IPv4 and another that contains |
| 37 | +IPv6 addresses. This solves a problem of having to potentially maintain two |
| 38 | +different .pol so that in cases where vendor syntax of filter name is derived |
| 39 | +from .pol a syncing between v4 and v6 .pol do not need to be maintained. |
| 40 | + |
| 41 | +This may be misleading at first because when using Capirca the user expects that |
| 42 | +the output will be a single policy, but it is actually their lack of |
| 43 | +understanding about the vendor syntax that causes this belief. |
| 44 | + |
| 45 | +If the user does not want this output, then the user can simply issue two |
| 46 | +headers to Capirca one for IPv4 and one for IPv6. |
| 47 | + |
| 48 | +#### When the platform does supports “mixed” in a single access-list |
| 49 | + |
| 50 | +This will require the policy to be generated correctly for the |
| 51 | +[following permutations](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/tests/lib/nsxv_test.py#L236-L322) |
| 52 | +of address family, and when “mixed” is supported, and with valid tests: |
| 53 | + |
| 54 | +1. [MIXED_TO_V4](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/tests/lib/nsxv_test.py#L236) |
| 55 | +1. [V4_TO_MIXED](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/tests/lib/nsxv_test.py#L245) |
| 56 | +1. [MIXED_TO_V6](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/tests/lib/nsxv_test.py#L254) |
| 57 | +1. [V6_TO_MIXED](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/tests/lib/nsxv_test.py#L262) |
| 58 | +1. [MIXED_TO_MIXED](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/tests/lib/nsxv_test.py#L270) |
| 59 | +1. [MIXED_TO_ANY](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/tests/lib/nsxv_test.py#L278) |
| 60 | +1. [ANY_TO_MIXED](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/tests/lib/nsxv_test.py#L285) |
| 61 | +1. [V4_TO_V4](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/tests/lib/nsxv_test.py#L92) |
| 62 | +1. [V6_TO_V6](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/tests/lib/nsxv_test.py#L300) |
| 63 | +1. [V4_TO_V6](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/tests/lib/nsxv_test.py#L308) |
| 64 | +1. [V6_TO_V4](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/tests/lib/nsxv_test.py#L316) |
| 65 | + |
| 66 | +### noverbose option is supported correctly |
| 67 | + |
| 68 | +noverberse must be supported if it makes sense for the platform. Noverbose |
| 69 | +removes all comments from the ACE terms, policies, etc. For example see the |
| 70 | +logic implemented in |
| 71 | +[juniper.py](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/capirca/lib/juniper.py#L219). |
| 72 | + |
| 73 | +### Sample.pol file is present |
| 74 | + |
| 75 | +A sample pol file should exist for that generator. Some examples are |
| 76 | +[here](https://github.com/google/capirca/tree/master/policies/pol). The |
| 77 | +sample.pol file for the new generator should have examples for all the filter |
| 78 | +option types used, with all supported IP types, along with any custom fields for |
| 79 | +that generator. |
| 80 | + |
| 81 | +### Perform truncating of names for term name or comment based on max length |
| 82 | + |
| 83 | +This is to ensure that truncation of terms and comments that exceed a max_width, |
| 84 | +is supported by the generator. Not all platforms have length limitations, if the |
| 85 | +generator’s platform has none, then this requirement can be skipped. This |
| 86 | +applies only when the term name and comments are being incorporated into the |
| 87 | +policy. If they are actual # comments (which are not applied to the policy), |
| 88 | +then this requirement does not apply. This could be done using the existing |
| 89 | +[WrapWords()](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/capirca/lib/aclgenerator.py#L549) |
| 90 | +or it may be done by a truncate function within the generator using a custom |
| 91 | +function such as in |
| 92 | +[juniper.py](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/capirca/lib/juniper.py#L715). |
| 93 | +This wrapping should also be present for the term name, and should be using |
| 94 | +Capirca’s |
| 95 | +[FixTermLength()](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/capirca/lib/aclgenerator.py#L463). |
| 96 | + |
| 97 | +### Logging is supported correctly for different types of logging |
| 98 | + |
| 99 | +There are different values of logging already created in |
| 100 | +[policy.py](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/capirca/lib/policy.py#L49). |
| 101 | +Not all are supported by every platform. |
| 102 | + |
| 103 | +* LOG_BOTH is for logging session-init as well as session-close, which is |
| 104 | + currently supported by JuniperSRX. |
| 105 | + |
| 106 | +* DISABLE is a negative logging action. |
| 107 | + |
| 108 | +* Every other option are considered positive actions. |
| 109 | + |
| 110 | +General rules: |
| 111 | + |
| 112 | +* The generator should support all the types of logging it can. |
| 113 | + |
| 114 | +* For all unsupported positive logging actions, the generator should enable |
| 115 | + logging. |
| 116 | + |
| 117 | +* For DISABLE, if the platform can turn off logging, it must; if the platform |
| 118 | + does not support it, then it does nothing. Key part is DISABLE must not |
| 119 | + enable logging. |
| 120 | + |
| 121 | +### DSMO Support |
| 122 | + |
| 123 | +DSMO is Discontinuous Subnet Masks and is used to save on TCAM space. This is |
| 124 | +supported by certain platforms such as Cisco, but is not supported by most |
| 125 | +platforms. If DSMO is not supported by a platform, this requirement can be |
| 126 | +safely ignored. If DSMO is supported by a platform it must be fully implemented |
| 127 | +and carefully unit tested. |
| 128 | + |
| 129 | +### The usage of good and meta Unified Direction names |
| 130 | + |
| 131 | +Good unified direction names for the ACE terms, that are meta and not specific |
| 132 | +to that platform, are preferred. This is only for platforms that require |
| 133 | +direction. The meta directions |
| 134 | +[supported by Capirca](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/capirca/lib/packetfilter.py#L77) |
| 135 | +are “in”, “out” and “”, but these should not be used as is. Different platforms |
| 136 | +use “ingress”, “egress” or “both” such as GCE. Do not rely on platform specific |
| 137 | +names for directions. “ingress” and “egress” are the preferred directions to be |
| 138 | +used, which can then be converted to the platform’s required specific names for |
| 139 | +directions. |
| 140 | + |
| 141 | +### Term Expirations are handled correctly |
| 142 | + |
| 143 | +Term expirations need to be handled correctly in code. An |
| 144 | +[example from Juniper](https://github.com/google/capirca/blob/master/capirca/lib/juniper.py#L968-L974) |
| 145 | +is that when the term is close to |
| 146 | +[expiration](https://github.com/google/capirca/blob/c0ca9d9a3a34d3dab0b41510571448f5d82c033d/capirca/utils/config.py#L17), |
| 147 | +an INFO message is logged; and when it is expired, a WARNING message is logged |
| 148 | +and the term is not generated. This is also done similarly across other |
| 149 | +platforms such as Cisco/GCE. |
| 150 | + |
| 151 | +### ICMP and ICMPv6 handling |
| 152 | + |
| 153 | +The generator needs to handle ICMP and ICMPv6 correctly. This is a broad |
| 154 | +requirement, but ICMP and ICMPv6 requires careful handling to **avoid rendering |
| 155 | +icmp terms under inet6, and icmpv6 under inet**. One commit that implements this |
| 156 | +for gcp_hf is |
| 157 | +[here](https://github.com/google/capirca/commit/b4af15a36b70593b7bbf043559405558e82c81bc). |
| 158 | +Some generators do not support icmp and icmp6 when the address family is mixed, |
| 159 | +such as |
| 160 | +[nftables.py](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/capirca/lib/nftables.py#L103-L111). |
| 161 | +The expected correct behavior is that when “mixed” is specified in the |
| 162 | +inet_version, the rule for ICMP should only contain IPv4 addresses, and the rule |
| 163 | +for ICMPv6 should contain only IPv6 addresses. Tests must ensure that types and |
| 164 | +codes used are valid for the given address family. |
| 165 | + |
| 166 | +In the future, we hope to refactor the code to allow for general ICMP support, |
| 167 | +but for now this functionality is implemented per-platform in each generator. |
| 168 | + |
| 169 | +### Makes an explicit determination about statefulness |
| 170 | + |
| 171 | +The generator author should check for “Am I stateful?”. It should clearly state |
| 172 | +in generator the result of this as a comment somewhere If it is, it should make |
| 173 | +sure that it is doing the right thing for terms. For example, for Juniper SRX, |
| 174 | +it is |
| 175 | +[possible to skip TCP-established](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/capirca/lib/junipersrx.py#L450-L453) |
| 176 | +because it is stateful. You would also want to do the stateful check probably |
| 177 | +early in the code rather than later, since this may impact efficiency by being |
| 178 | +able to skip further code/ checks. A pro of checking early would be being able |
| 179 | +to skip any processing of terms not necessary for a stateful firewall such as |
| 180 | +skipping TCP-established. In contrast, in iptables.py, the check is made later, |
| 181 | +while formatting the terms to modify the term to |
| 182 | +[allow established and related terms](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/capirca/lib/iptables.py#L474-L485). |
| 183 | + |
| 184 | +### Syntax of the config from the generator and on-device should match when cryptographically verified |
| 185 | + |
| 186 | +The ACL that is generated from the generator, and the ACL that is obtained from |
| 187 | +the device when a show configuration command is used, should match bit-by-bit, |
| 188 | +such that it should be possible to run a hashing function (such as SHA-1) and |
| 189 | +obtain the same hash for both the ACL configurations. Another variant of this |
| 190 | +requirement is that the `diff` between these two policies should be empty. |
| 191 | + |
| 192 | +There can be certain exceptions, such as if there is a policy header comment |
| 193 | +that cannot be handled by Cisco devices and is thus not present in the Cisco |
| 194 | +ACL. This can be handled by checking the diff between them and skipping over the |
| 195 | +known mismatches that are acceptable because of the device’s incapability. |
| 196 | +Another example of an exception is the Juniper |
| 197 | +[control sequence such as ‘replace’,](https://github.com/google/capirca/blob/b3e605a54f12efa1e6b0b1cfd179ee6078313c9d/capirca/lib/juniper.py#L993) |
| 198 | +which indicates the device to replace, rather than merge the contents of the |
| 199 | +ACL, which does not show up on the device ACL. |
| 200 | + |
| 201 | +If there is a mismatch in the syntax of the 2 ACLs that cannot be fixed, then |
| 202 | +these mismatches and the technical reasoning behind the lack of a workaround or |
| 203 | +a fix should be listed in the associated Github issue for this generator. |
| 204 | + |
| 205 | +### Apply priority as described in .pol files |
| 206 | + |
| 207 | +If a priority or order exists for the platform, and an input pol file doesn't |
| 208 | +set priority for ACEs, then autogenerate priorities in top down order based on |
| 209 | +the ACE order in the .pol file. |
| 210 | + |
| 211 | +### Protocol support |
| 212 | + |
| 213 | +#### Call out support |
| 214 | + |
| 215 | +Make explicit which protocols the platform supports, and support them in the |
| 216 | +generator. If protocols are not supported by the platform, ensure that the |
| 217 | +generator explicitly does not support them, and gracefully handles these errors. |
| 218 | + |
| 219 | +#### Names vs numbers |
| 220 | + |
| 221 | +Names or numbers can be used to represent protocols within a generator. |
| 222 | +Throughout a given generators use only names or numbers, not a mix of both. The |
| 223 | +choice should be made based on the default representation for the device |
| 224 | +platform. (I.e. if the policy once applied to the device will show names in a |
| 225 | +"show config" command output, then use names within the generator. If the output |
| 226 | +contains numbers, use numbers within the generator.) |
| 227 | + |
| 228 | +#### Port support |
| 229 | + |
| 230 | +The following is a list of which IP protocols support ports. When supporting a |
| 231 | +protocol, make sure that the handling of port or lack thereof is correct. |
| 232 | + |
| 233 | +* HOPOPT = No |
| 234 | + |
| 235 | +* ICMP = No |
| 236 | + |
| 237 | +* IGMP = No |
| 238 | + |
| 239 | +* GGP = No |
| 240 | + |
| 241 | +* IPIP = No |
| 242 | + |
| 243 | +* TCP = Yes |
| 244 | + |
| 245 | +* EGP = No |
| 246 | + |
| 247 | +* IGP = No |
| 248 | + |
| 249 | +* UDP = *Yes* |
| 250 | + |
| 251 | +* RDP = *Yes (Uses different port ranges though, check RFC)* |
| 252 | + |
| 253 | +* IPV6 = No |
| 254 | + |
| 255 | +* IPV6_ROUTE = No |
| 256 | + |
| 257 | +* FRAGMENT = No |
| 258 | + |
| 259 | +* RSVP = No |
| 260 | + |
| 261 | +* GRE = No |
| 262 | + |
| 263 | +* ESP = No |
| 264 | + |
| 265 | +* AH = No |
| 266 | + |
| 267 | +* ICMPV6 = No |
| 268 | + |
| 269 | +* IPV6_NONXT = No |
| 270 | + |
| 271 | +* IPV6_OPTS = No |
| 272 | + |
| 273 | +* OSPF = No |
| 274 | + |
| 275 | +* PIM = No |
| 276 | + |
| 277 | +* VRRP = No |
| 278 | + |
| 279 | +* L2TP = No. (only uses UDP 1701) |
| 280 | + |
| 281 | +* SCTP = *Yes* |
| 282 | + |
| 283 | +* UDPLITE = *Yes* |
| 284 | + |
| 285 | +Note: DCCP also uses ports, but this is not currently supported. Source: |
| 286 | +https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml |
| 287 | + |
| 288 | +### Zone based firewall support |
| 289 | + |
| 290 | +Zone based firewalls should be implemented if supported by the platform. When |
| 291 | +implementing zone based firewalls, all combinations of zone types must be tested |
| 292 | +fully. Test for invalid and reserved zone names and illegal combinations of |
| 293 | +policies (i.e. any-> specific, or any->any). |
| 294 | + |
| 295 | +### Address book support |
| 296 | + |
| 297 | +Address books should be implemented if they are present in a platform. The |
| 298 | +generator should explicitly state whether it is implementing a global or zone |
| 299 | +based address book. If both are available for the platform, both must be |
| 300 | +implemented, and an option must be added to choose between the two. When |
| 301 | +implementing address books, always filter for address family before building the |
| 302 | +book. Tests should ensure that address books are filtered by address family |
| 303 | +properly along with their relevant rules. |
| 304 | + |
| 305 | +## Coding Style Requirements: |
| 306 | + |
| 307 | +### Use builtin libraries |
| 308 | + |
| 309 | +Use only Python standard builtin libraries wherever possible. External |
| 310 | +dependencies are discouraged and must be justified. |
| 311 | + |
| 312 | +### Structure generators for inheritance |
| 313 | + |
| 314 | +See Cisco and Juniper generators for examples. Wherever possible, use base |
| 315 | +classes, inheritance, etc to allow for common functions between generators in a |
| 316 | +"family". |
| 317 | + |
| 318 | +### Reuse common functions |
| 319 | + |
| 320 | +Use functions from aclgenerator.py, policy.py, nacaddr.py, etc wherever possible |
| 321 | +instead of implementing your own. |
| 322 | + |
| 323 | +### If output will be in a common exchange format, use a standard library for rendering |
| 324 | + |
| 325 | +This applies to common standards such as JSON, XML, YAML, protocol buffers, etc. |
| 326 | +Instead of building up such serialized output using string ops, use a standard |
| 327 | +library to produce the rendered output instead. (For example, to render JSON, |
| 328 | +use [json.dumps](https://docs.python.org/3/library/json.html#json.dumps). For |
| 329 | +XML, use some combination of the |
| 330 | +[standard libraries](https://docs.python.org/3/library/xml.html), such as |
| 331 | +xml.etree and xml.dom.) This should allow most generator code to interact with |
| 332 | +objects only, and serialize to a buffer at the end. Unit tests should operate on |
| 333 | +the object structures. Additional small unit tests should sanity check that the |
| 334 | +rendering library is producing valid output as expected. If a (JSON, XML, etc.) |
| 335 | +schema is available this should also be used to validate output in a test. |
| 336 | + |
| 337 | +### Check various limits when rendering output |
| 338 | + |
| 339 | +Make sure that line, identifier, full output, etc. limits are applied when |
| 340 | +rendering final output. Some of these may be specific to a given platform. These |
| 341 | +should always include but are not limited to: |
| 342 | + |
| 343 | +* Maximum value of addresses and ports allowed in single rule. Generator must |
| 344 | + support automatically splitting into new rule when exceeded. |
| 345 | +* Maximum values allowed across entire policy for rule count, address, ports |
| 346 | +* Maximum length for comments, and support splitting across lines the correct |
| 347 | + way when over. Also check for max per-rule limit if one exists and truncate |
| 348 | + using the common Capirca functions if needed. |
| 349 | +* Max term length supported must be 24 or greater, in order to allow for |
| 350 | + meaningful term names. |
| 351 | + |
| 352 | +### Test coverage |
| 353 | + |
| 354 | +#### General coverage |
| 355 | + |
| 356 | +Aim for as close to 100% test coverage as you can. Tests should cover a wide |
| 357 | +span of the vendor syntax, not just a single keyword. |
| 358 | + |
| 359 | +#### Custom exceptions |
| 360 | + |
| 361 | +All custom exceptions types added must be unit tested. |
| 362 | + |
| 363 | +<!--* # copybara:strip_begin(internal linter) |
| 364 | +# LINT.ThenChange( |
| 365 | +# //depot/google3/ops/security/miracl/g3doc/capirca_generator_patterns.md |
| 366 | +# ) |
| 367 | +# copybara:strip_end *--> |
0 commit comments