Introduction
Junos provides an oddly named but quite useful mechanism called “groups” for creating reusable configuration snippets. In this article I will describe what they are and how to use them to make your life a little easier.
The Basics: Creating and Applying a Group
Almost anything you can set under the main configuration hierarchy can also be set under the groups section:
wayne@vr1# set groups foo ? Possible completions: Execute this command > access Network access configuration > access-profile Access profile for this instance > accounting-options Accounting data configuration > applications Define applications by protocol characteristics [...]
Here’s a sample group:
groups {
foo {
system {
default-address-selection;
services {
netconf {
ssh;
}
}
}
}
}
By itself, that doesn’t do anything other than make your configuration larger. In order for those settings to take effect, you have to apply the group using the apply-groups setting:
wayne@vr1# set apply-groups foo
Even then, when you look at your configuration you won’t see your new settings:
wayne@vr1# show system
host-name vr1;
services {
ssh;
}
In order to see the effective settings, you need to pipe the show command through the “display inheritance” filter:
wayne@vr1# show system | display inheritance
host-name vr1;
##
## 'default-address-selection' was inherited from group 'foo'
##
default-address-selection;
services {
ssh;
##
## 'netconf' was inherited from group 'foo'
##
netconf {
##
## 'ssh' was inherited from group 'foo'
##
ssh;
}
}
Configuration groups do not need to be applied at the root of the configuration. Any settings that reside above where the group was applied will be ignored:
wayne@vr1# delete apply-groups foo
wayne@vr1# set sysetm services apply-groups foo
wayne@vr1# show system | display inheritance | except ##$
host-name vr1;
services {
## 'netconf' was inherited from group 'foo'
netconf {
## 'ssh' was inherited from group 'foo'
ssh;
}
}
Getting a Little Fancier: Patterns and Exceptions
A single group can be applied to multiple places (such as interfaces) by using a pattern (in UNIX-style glob format) for the name. In order to be treated as a pattern, it must be enclosed in <angle brackets>.
Here are a few sample groups using patterns:
/* matches all interfaces */
all-interfaces {
interfaces {
<*> {
mtu 1500;
}
}
}
/* matches all ge and xe interfaces */
all-gig-interfaces {
interfaces {
"<[gx]e-*>" {
mtu 9000;
}
}
}
/* matches all interfaces on fpc0 */
all-fpc0-interfaces {
interfaces {
<*-0/*> {
mtu 2000;
}
}
}
So, let’s say you apply all three of these groups at the root of the configuration (or under interfaces – it doesn’t really matter) – what happens if you want or need to exclude an interface? For instance, members of LAGs are not allowed to have MTU settings:
wayne@fw1# commit check
[edit interfaces fe-0/0/5 fastether-options]
'802.3ad'
ae child device mtu setting and vlan-tagging is not allowed
error: configuration check-out failed
This can be done with the help of apply-groups-except:
wayne@fw1# set interfaces fe-0/0/5 apply-groups-except all-interfaces wayne@fw1# commit check configuration check succeeds
Precedence and Stacking
One thing that I found to be poorly documented is the expected behavior when multiple groups try to apply conflicting values for the same setting. The groups in the previous section, for instance, have different MTUs and all 3 would apply to all ge and xe interfaces on fpc0. Well, from what I can tell, it’s a combination of best match and first match:
- Groups applied further down in the hierarchy override groups applied above them
- When applied at the same level, the first group in the apply-groups list trumps those that come after it
wayne@fw1# set interfaces apply-groups [ all-interfaces all-gig-interfaces all-fpc0-interfaces ]
wayne@fw1# set interfaces fe-0/0/3 apply-groups all-fpc0-interfaces
ge-0/0/0 {
## '9000' was inherited from group 'all-gig-interfaces'
mtu 9000;
unit 0 {
family ethernet-switching;
}
}
ge-0/0/1 {
## '9000' was inherited from group 'all-gig-interfaces'
mtu 9000;
unit 0 {
family ethernet-switching;
}
}
fe-0/0/2 {
## '1500' was inherited from group 'all-interfaces'
mtu 1500;
unit 0 {
family ethernet-switching;
}
}
fe-0/0/3 {
## '2000' was inherited from group 'all-fpc0-interfaces'
mtu 2000;
unit 0 {
family ethernet-switching;
}
}
Groups can also build upon one another – and in fact order does not always matter in the way you think it would. Consider the following groups:
foo {
security {
ike {
proposal <pre-*> {
authentication-method pre-shared-keys;
lifetime-seconds 86400;
}
proposal <*-g2-*> {
dh-group group2;
}
proposal <*-g5-*> {
dh-group group5;
}
proposal <*-aes192-*> {
encryption-algorithm aes-192-cbc;
}
proposal <*-aes256-*> {
encryption-algorithm aes-256-cbc;
}
proposal <*-sha1> {
authentication-algorithm sha1;
}
proposal <*-sha256> {
authentication-algorithm sha-256;
}
}
}
}
bar {
security {
ike {
proposal pre-g2-aes192-sha1;
proposal pre-g2-aes256-sha1;
proposal pre-g5-aes192-sha1;
proposal pre-g5-aes256-sha1;
proposal pre-g5-aes256-sha256;
}
}
}
Regardless of the order in which they are applied, the proposals from bar are filled in with the values from foo:
wayne@fw1# set security ike apply-groups [ foo bar ]
[edit]
wayne@fw1# show security ike | display inheritance | except ##$
## 'pre-g2-aes192-sha1' was inherited from group 'bar'
proposal pre-g2-aes192-sha1 {
## 'pre-shared-keys' was inherited from group 'foo'
authentication-method pre-shared-keys;
## 'group2' was inherited from group 'foo'
dh-group group2;
## 'sha1' was inherited from group 'foo'
authentication-algorithm sha1;
## 'aes-192-cbc' was inherited from group 'foo'
encryption-algorithm aes-192-cbc;
## '86400' was inherited from group 'foo'
lifetime-seconds 86400;
}
[...]
wayne@fw1# delete security ike apply-groups
[edit]
wayne@fw1# set security ike apply-groups [ bar foo ]
[edit]
wayne@fw1# show security ike | display inheritance | except ##$
## 'pre-g2-aes192-sha1' was inherited from group 'bar'
proposal pre-g2-aes192-sha1 {
## 'pre-shared-keys' was inherited from group 'foo'
authentication-method pre-shared-keys;
## 'group2' was inherited from group 'foo'
dh-group group2;
## 'sha1' was inherited from group 'foo'
authentication-algorithm sha1;
## 'aes-192-cbc' was inherited from group 'foo'
encryption-algorithm aes-192-cbc;
## '86400' was inherited from group 'foo'
lifetime-seconds 86400;
}
[...]
These settings also work fine if you put them in a single group – I broke them up only to show what can be done.
One More Feature – apply-path
The policy-options/prefix-list[name] section of the hierarchy has a nifty option called apply-path that can be used to pull IP addresses from other sections of the configuration:
wayne@fw1# show policy-options
prefix-list foo {
apply-path "interfaces <*> unit <*> family inet address <*>";
}
wayne@fw1# show policy-options | display inheritance
prefix-list foo {
##
## apply-path was expanded to:
## 192.168.0.0/24;
## 192.168.0.0/24;
## 192.168.128.0/24;
##
apply-path "interfaces <*> unit <*> family inet address <*>";
}
Note: This feature pulls from the configuration, so it doesn’t work with addresses from DHCP and the like.
For more examples using apply-path see “Day One: Securing the Routing Engine on M, MX, and T Series” – it’s free and well worth the read.
Conclusion
The groups mechanism in Junos provides a simple but powerful way to create configuration templates. There are a few small limitations, but overall they are more flexible than mechanisms such as interface macros yet not nearly as complex to implement as XSLT, SLAX, expect scripts, etc.
In a follow-up article I’m planning to write, I will talk more about a framework I used to centrally manage configuration templates at a previous job.