Skip to content

Commit

Permalink
Feature/switch interface acl (#8090)
Browse files Browse the repository at this point in the history
* First draft of interface ACL

* Fix isInterfaceMap not used

* Missing definition

* Added getInterfaceByName function

* Generate Interface hash

* Assign ACL on switchport through ansible

* Syntax fix

* Add a method for snapshot

* Pass the old config

* Add post_remove hook

* Update configstore

* Removed old configuration

* Remove acl on interfaces

* removed snapshot

* Detect duplicate interface defined for multiples roles

* Remove ACL on switch delete

* Delete switch configuration when the switch is deleted from PacketFence

* Fixed rebase issue

---------

Co-authored-by: James Rouzier <[email protected]>
  • Loading branch information
fdurand and jrouzierinverse authored Jul 15, 2024
1 parent dbb96f6 commit 8181c43
Show file tree
Hide file tree
Showing 14 changed files with 249 additions and 39 deletions.
36 changes: 34 additions & 2 deletions conf/pfsetacls/switch_acls.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@

- name: Load new acl into Cisco Switch
cisco.ios.ios_acls:
config: "{{ acls.parsed }}"
state: replaced
config: "{{ acls.parsed }}"[% IF delete >= 1 %]
state: deleted[% ELSE %]
state: replaced[% END %]
when: ansible_network_os == 'cisco.ios.ios'

- name: Load new acl into Cisco WLC
Expand All @@ -31,3 +32,34 @@
arubanetworks.aoscx.aoscx_config:
src: "{{ acl_config }}"
when: ansible_network_os == 'arubanetworks.aoscx.aoscx'

[% FOREACH role IN interfaces_delete.keys %]
[% FOREACH interface IN interfaces_delete.$role %]
- name: remove acl on interface
cisco.ios.ios_config:
lines:
- no ip access-group [% role %] in
parents: "{{ item }}"
with_items:
- interface [% interface %]
[% END %]
[% END %]

[% IF delete == 0 %]
[% FOREACH role IN interfaces.keys %]
[% FOREACH interface IN interfaces.$role %]
- name: Merge module attributes of given access-groups
cisco.ios.ios_acl_interfaces:
config:
- name: [% interface %]
access_groups:
- afi: ipv4
acls:
- name: [% role %]
direction: in[% IF delete >= 1 %]
state: deleted[% ELSE %]
state: merged[% END %]
when: ansible_network_os == 'cisco.ios.ios'
[% END %]
[% END %]
[% END %]
7 changes: 7 additions & 0 deletions html/pfappserver/lib/pfappserver/Form/Config/Switch.pm
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ has_field 'UrlMap' =>
label => 'Role by Web Auth URL',
default => undef,
);
has_field 'InterfaceMap' =>
(
type => 'Toggle',
label => 'Interface to apply Role ACL',
default => undef,
);
has_field 'cliAccess' =>
(
type => 'Toggle',
Expand Down Expand Up @@ -512,6 +518,7 @@ addRoleMapping("AccessListMapping", "accesslist");
addRoleMapping("VpnMapping", "vpn");
addRoleMapping("NetworkMapping", "network");
addRoleMapping("NetworkFromMapping", "networkfrom");
addRoleMapping("InterfaceMapping", "interface");

sub _validate_acl_switch {
my ($field) = @_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,17 @@
/>
</template>
</div>
<b-card-header>
<h4 class="mb-0" v-t="'Interface mapping to Access List'"></h4>
</b-card-header>
<div class="card-body pb-0">
<template v-if="isInterfaceMap">
<form-group-role-map-interface v-for="role in roles" :key="`${role}Interface`"
:namespace="`${role}Interface`"
:column-label="role"
/>
</template>
</div>
</b-card>
</base-form-tab>

Expand Down Expand Up @@ -559,6 +570,7 @@ import {
FormGroupRoleMapVpn,
FormGroupRoleMapUrl,
FormGroupRoleMapVlan,
FormGroupRoleMapInterface,
FormGroupSnmpAuthProtocolTrap,
FormGroupSnmpAuthPasswordTrap,
FormGroupSnmpCommunityRead,
Expand Down Expand Up @@ -587,6 +599,7 @@ import {
FormGroupToggleUrlMap,
FormGroupToggleVlanMap,
FormGroupToggleNetworkMap,
FormGroupToggleInterfaceMap,
FormGroupType,
FormGroupUplink,
FormGroupUplinkDynamic,
Expand Down Expand Up @@ -640,6 +653,7 @@ const components = {
FormGroupRoleMapVpn,
FormGroupRoleMapUrl,
FormGroupRoleMapVlan,
FormGroupRoleMapInterface,
FormGroupSnmpAuthProtocolTrap,
FormGroupSnmpAuthPasswordTrap,
FormGroupSnmpCommunityRead,
Expand Down Expand Up @@ -668,6 +682,7 @@ const components = {
FormGroupToggleUrlMap,
FormGroupToggleVlanMap,
FormGroupToggleNetworkMap,
FormGroupToggleInterfaceMap,
FormGroupType,
FormGroupUplink,
FormGroupUplinkDynamic,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export {
BaseFormGroupInput as FormGroupRoleMapVpn,
BaseFormGroupInput as FormGroupRoleMapUrl,
BaseFormGroupInput as FormGroupRoleMapVlan,
BaseFormGroupInput as FormGroupRoleMapInterface,
BaseFormGroupInput as FormGroupSnmpAuthProtocolTrap,
BaseFormGroupInputPassword as FormGroupSnmpAuthPasswordTrap,
BaseFormGroupInput as FormGroupSnmpCommunityRead,
Expand Down Expand Up @@ -83,6 +84,7 @@ export {
BaseFormGroupToggleNYDefault as FormGroupToggleUrlMap,
BaseFormGroupToggleNYDefault as FormGroupToggleVlanMap,
BaseFormGroupToggleNYDefault as FormGroupToggleNetworkMap,
BaseFormGroupToggleNYDefault as FormGroupToggleInterfaceMap,
BaseFormGroupToggleNYDefault as FormGroupVoipEnabled,
BaseFormGroupToggleNYDefault as FormGroupVoipLldpDetect,
BaseFormGroupToggleNYDefault as FormGroupVoipCdpDetect,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,21 @@
/>
</template>
</div>
<b-card-header>
<h4 class="mb-0" v-t="'Interface mapping by Access List'"></h4>
</b-card-header>
<div class="card-body pb-0">
<form-group-toggle-interface-map namespace="InterfaceMap"
:column-label="$i18n.t('Interface by Access List')"
:text="$i18n.t('Define the interface name where the acl associated to the role will be applied.')"
/>

<template v-if="isInterfaceMap">
<form-group-role-map-interface v-for="role in roles" :key="`${role}Interface`" :namespace="`${role}Interface`"
:column-label="role"
/>
</template>
</div>
</b-card>
</base-form-tab>

Expand Down Expand Up @@ -487,6 +502,7 @@ import {
FormGroupRoleMapVpn,
FormGroupRoleMapUrl,
FormGroupRoleMapVlan,
FormGroupRoleMapInterface,
FormGroupSnmpAuthProtocolTrap,
FormGroupSnmpAuthPasswordTrap,
FormGroupSnmpCommunityRead,
Expand Down Expand Up @@ -515,6 +531,7 @@ import {
FormGroupToggleUrlMap,
FormGroupToggleVlanMap,
FormGroupToggleNetworkMap,
FormGroupToggleInterfaceMap,
FormGroupType,
FormGroupUplink,
FormGroupUplinkDynamic,
Expand Down Expand Up @@ -567,6 +584,7 @@ const components = {
FormGroupRoleMapVpn,
FormGroupRoleMapUrl,
FormGroupRoleMapVlan,
FormGroupRoleMapInterface,
FormGroupSnmpAuthProtocolTrap,
FormGroupSnmpAuthPasswordTrap,
FormGroupSnmpCommunityRead,
Expand Down Expand Up @@ -595,6 +613,7 @@ const components = {
FormGroupToggleUrlMap,
FormGroupToggleVlanMap,
FormGroupToggleNetworkMap,
FormGroupToggleInterfaceMap,
FormGroupType,
FormGroupUplink,
FormGroupUplinkDynamic,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export {
BaseFormGroupInput as FormGroupRoleMapVpn,
BaseFormGroupInput as FormGroupRoleMapUrl,
BaseFormGroupInput as FormGroupRoleMapVlan,
BaseFormGroupInput as FormGroupRoleMapInterface,
BaseFormGroupInput as FormGroupSnmpAuthProtocolTrap,
BaseFormGroupInputPassword as FormGroupSnmpAuthPasswordTrap,
BaseFormGroupInput as FormGroupSnmpCommunityRead,
Expand Down Expand Up @@ -78,6 +79,7 @@ export {
BaseFormGroupInput as FormGroupDownloadableAclsLimit,
BaseFormGroupInput as FormGroupAclsLimit,
BaseFormGroupToggleNYDefault as FormGroupToggleAccessListMap,
BaseFormGroupToggleNYDefault as FormGroupToggleInterfaceMap,
BaseFormGroupSwitch as FormGroupDeauthOnPrevious,
BaseFormGroupToggleNYDefault as FormGroupToggleRoleMap,
BaseFormGroupToggleNYDefault as FormGroupToggleVpnMap,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,24 @@ const useForm = (props, context) => {
// inspect meta placeholder for `NetworkMap`
const { NetworkMap: { placeholder } = {} } = meta.value
return placeholder === 'Y'

const isInterfaceMap = computed(() => {
// inspect form value for `InterfaceMap`
const { InterfaceMap } = form.value
if (InterfaceMap !== null)
return InterfaceMap === 'Y'

// inspect meta placeholder for `InterfaceMap`
const { InterfaceMap: { placeholder } = {} } = meta.value
return placeholder === 'Y'
})

const roles = ref(baseRoles)
$store.dispatch('$_roles/all').then(allRoles => {
roles.value = [
...roles.value,
...allRoles.map(role => role.id)
]
})

const isUsePushACLs = computed(() => {
Expand Down Expand Up @@ -204,6 +222,7 @@ const useForm = (props, context) => {
isUrlMap,
isVlanMap,
isNetworkMap,
isInterfaceMap,
roles,

isUsePushACLs,
Expand Down
10 changes: 6 additions & 4 deletions lib/pf/ConfigStore/Switch.pm
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ our %MappingKey = (
VpnMapping => 'vpn',
NetworkMapping => 'network',
NetworkFromMapping => 'networkfrom',
InterfaceMapping => 'interface',
);

our %MappingKey2 = (
Expand All @@ -52,6 +53,7 @@ our %MappingKey2 = (
VpnMapping => 'Vpn',
NetworkMapping => 'Network',
NetworkFromMapping => 'NetworkFrom',
InterfaceMapping => 'Interface',
);

=head2 Methods
Expand Down Expand Up @@ -107,7 +109,7 @@ sub _expandMapping {
# We put it back as a string so it works in the admin UI
my $toset = {};
while (my ($attr, $val) = each %$switch) {
if ($attr =~ /(.*)(AccessList|Vlan|Url|Role|Vpn|Network|NetworkFrom)$/) {
if ($attr =~ /(.*)(AccessList|Vlan|Url|Role|Vpn|Interface|Network|NetworkFrom)$/) {
my $type = $2;
my $role = $1;
if ($type eq 'AccessList' && ref($val) eq 'ARRAY') {
Expand All @@ -131,7 +133,7 @@ sub _expandMapping {
$switch->{$attr} = $val;
}

for my $k (qw(AccessListMapping VlanMapping UrlMapping ControllerRoleMapping VpnMapping NetworkMapping NetworkFromMapping)) {
for my $k (qw(AccessListMapping VlanMapping UrlMapping ControllerRoleMapping VpnMapping InterfaceMapping NetworkMapping NetworkFromMapping)) {
next if !exists $switch->{$k};
$switch->{$k} = [sort { $a->{role} cmp $b->{role} } @{$switch->{$k} // []}]
}
Expand Down Expand Up @@ -160,7 +162,7 @@ sub cleanupBeforeCommit {

sub _flattenRoleMappings {
my ( $switch ) = @_;
for my $namespace (qw(AccessListMapping VlanMapping UrlMapping ControllerRoleMapping VpnMapping NetworkMapping NetworkFromMapping)) {
for my $namespace (qw(AccessListMapping VlanMapping UrlMapping ControllerRoleMapping VpnMapping InterfaceMapping NetworkMapping NetworkFromMapping)) {
my $list = $switch->{$namespace} // [];
for my $mapping (@$list) {
my $role = $mapping->{role};
Expand All @@ -171,7 +173,7 @@ sub _flattenRoleMappings {

sub _deleteRoleMappings {
my ( $switch ) = @_;
delete @{$switch}{qw(AccessListMapping VlanMapping UrlMapping ControllerRoleMapping VpnMapping NetworkMapping NetworkFromMapping)};
delete @{$switch}{qw(AccessListMapping VlanMapping UrlMapping ControllerRoleMapping VpnMapping InterfaceMapping NetworkMapping NetworkFromMapping)};
}

=head2 _normalizeUplink
Expand Down
Loading

0 comments on commit 8181c43

Please sign in to comment.