Skip to content

Commit dd14e92

Browse files
feat(openvpn) refactor to allow hieradata network setup (#2517)
* chore(tests) use different hieradata configurations for unit tests and vagrant Signed-off-by: Damien Duportal <[email protected]> * chore(updatecli) track puppetlabs-firewall module and bump it to 4.0.0 Signed-off-by: Damien Duportal <[email protected]> * chore(vagrant) add a 2nd NIC, which requires ubuntu 20.04 and iptables package to avoid iptables errors - robbertkl/docker-ipv6nat#47 Signed-off-by: Damien Duportal <[email protected]> * fix(openvpn) correct bootstrap from scratch by fixing recursive directories issue Signed-off-by: Damien Duportal <[email protected]> * fix(openvpn) correct boostrap by ensuring that required CLI are installe (route CLI) Signed-off-by: Damien Duportal <[email protected]> * chore(openvpn) refactorize role to allow defining CIDRs, peering and VPN networks through hieradata Signed-off-by: Damien Duportal <[email protected]> * Apply suggestions from code review Co-authored-by: Hervé Le Meur <[email protected]> Signed-off-by: Damien Duportal <[email protected]> Co-authored-by: Hervé Le Meur <[email protected]>
1 parent f4c274e commit dd14e92

File tree

14 files changed

+640
-78
lines changed

14 files changed

+640
-78
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
*.sw*
22
.vagrant*
33
.ruby-*
4-
spec/fixtures/
4+
spec/fixtures/manifests
5+
spec/fixtures/modules
56
.bundle
67
modules
78
modules/

Puppetfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ mod 'puppetlabs-cron_core', '1.1.0'
2525
mod 'saz-sudo', '5.0.0'
2626

2727
# Needed for managing firewall rules
28-
mod 'puppetlabs-firewall', '3.0.1'
28+
mod 'puppetlabs-firewall', '4.0.0'
2929

3030
# Needed for managing .yaml files from within Puppet
3131
mod 'reidmv-yamlfile'

Vagrantfile

+4-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ Vagrant.configure("2") do |config|
1313
d.has_ssh = true
1414
end
1515

16+
# Add a secondary NIC ("private" network simulation for roles such as openvpn)
17+
config.vm.network "private_network", ip: "192.168.0.10", netmask: 24, docker_network__internal: true, docker_network__gateway: "192.168.0.1"
18+
1619
config.vm.network "forwarded_port", guest: 80, host: 80
1720
config.vm.network "forwarded_port", guest: 443, host: 443
1821
config.vm.network "forwarded_port", guest: 8080, host: 8080
@@ -45,7 +48,7 @@ Vagrant.configure("2") do |config|
4548
puppet.working_directory = "/vagrant"
4649
puppet.manifests_path = "manifests"
4750
puppet.manifest_file = "site.pp"
48-
puppet.options = "--hiera_config=/vagrant/spec/fixtures/hiera.yaml --execute 'require profile::vagrant\n include role::#{veggie}'"
51+
puppet.options = "--hiera_config=/vagrant/vagrant-docker/hiera.yaml --execute 'require profile::vagrant\n include role::#{veggie}'"
4952
end
5053
end
5154
end

dist/profile/manifests/openvpn.pp

+75-67
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,21 @@
4242
require => [Docker::Image[$image]],
4343
}
4444

45+
file { '/etc/cloud':
46+
ensure => 'directory',
47+
owner => 'root',
48+
group => 'root',
49+
recurse => true,
50+
}
51+
4552
file { '/etc/cloud/cloud.cfg.d':
4653
ensure => 'directory',
4754
owner => 'root',
4855
group => 'root',
4956
recurse => true,
57+
require => [
58+
File['/etc/cloud'],
59+
],
5060
}
5161

5262
# Ensure cloud-init doesn't manage network (netplan config + netplan apply + systemd, in Ubuntu Bionic)
@@ -79,79 +89,77 @@
7989
content => template("${module_name}/openvpn/90-network-config.yaml.erb"),
8090
}
8191

82-
## Custom routes for peered networks
83-
exec { 'addroute-10.240.0.0':
84-
command => 'ip route add 10.240.0.0/14 via 10.0.2.1 dev eth1',
85-
unless => 'route | grep 10.240.0.0',
86-
path => '/usr/bin:/usr/sbin:/bin:/sbin',
92+
# The CLI '/sbin/route' included in net-tools is required to create custom routes for peered networks
93+
package { 'net-tools':
94+
ensure => present,
8795
}
8896

97+
# Allow openvpn clients (incoming 443)
8998
firewall { '107 accept incoming 443 connections':
90-
proto => 'tcp',
91-
port => 443,
92-
action => 'accept',
93-
}
94-
95-
# Following firewall rules authorizes different network accesses as defined in https://github.com/jenkins-infra/openvpn
96-
firewall { '100 snat for network public data tier from default vpn network to port 80/443':
97-
chain => 'POSTROUTING',
98-
jump => 'MASQUERADE',
99-
proto => 'tcp',
100-
outiface => 'eth1',
101-
source => '10.8.0.0/24',
102-
dport => [80,443],
103-
destination => '10.0.2.0/24',
104-
table => 'nat',
105-
}
106-
107-
# Admin Rules
108-
firewall { '100 snat for network public dmz tier from admin vpn network':
109-
chain => 'POSTROUTING',
110-
jump => 'MASQUERADE',
111-
proto => 'all',
112-
outiface => 'eth0',
113-
source => '10.8.1.0/24',
114-
destination => '10.0.99.0/24',
115-
table => 'nat',
116-
}
117-
118-
firewall { '100 snat for network public data tier from admin vpn network':
119-
chain => 'POSTROUTING',
120-
jump => 'MASQUERADE',
121-
proto => 'all',
122-
outiface => 'eth1',
123-
source => '10.8.1.0/24',
124-
destination => '10.0.2.0/24',
125-
table => 'nat',
126-
}
127-
128-
firewall { '100 snat for network public app tier from admin vpn network':
129-
chain => 'POSTROUTING',
130-
jump => 'MASQUERADE',
131-
proto => 'all',
132-
outiface => 'eth2',
133-
source => '10.8.1.0/24',
134-
destination => '10.0.1.0/24',
135-
table => 'nat',
99+
proto => 'tcp',
100+
dport => 443,
101+
action => 'accept',
102+
iniface => 'eth0',
136103
}
137104

138-
firewall { '100 snat for network private-vnet default tier from default vpn network (peered with public network)':
139-
chain => 'POSTROUTING',
140-
jump => 'MASQUERADE',
141-
proto => 'all',
142-
outiface => 'eth1',
143-
source => '10.8.0.0/24',
144-
destination => '10.240.0.0/14',
145-
table => 'nat',
105+
# Allow SSH clients (incoming 2)
106+
firewall { '107 accept incoming 22 connections':
107+
proto => 'tcp',
108+
dport => 22,
109+
action => 'accept',
110+
iniface => 'eth0',
146111
}
147112

148-
firewall { '100 snat for network private-vnet default tier from admin vpn network (peered with public network)':
149-
chain => 'POSTROUTING',
150-
jump => 'MASQUERADE',
151-
proto => 'all',
152-
outiface => 'eth1',
153-
source => '10.8.1.0/24',
154-
destination => '10.240.0.0/14',
155-
table => 'nat',
113+
# Create firewall rules and route for each specified NIC to allow routing from VPN virtual networks to different networks
114+
lookup('profile::openvpn::networks').each |$network_nic, $network_setup| {
115+
# Remove the mask from CIDR to only keep the network Ipv4 (`10.0.0.0/24` returns `10.0.0.0`)
116+
$network_first_ip = split($network_setup['network_cidr'], '/')[1]
117+
# Only get the 3 first digits of the IPv4 (`10.0.0.0` returns `10.0.0`)
118+
$network_prefix = join(split($network_setup['network_cidr'], '[.]')[0,3], '.')
119+
120+
# A given NIC has a "main" CIDR (its network) but may also be used for routes to peered networks
121+
# If there are any peered network, then add a manual route
122+
if $network_setup['peered_network_cidrs'] and $network_setup['peered_network_cidrs'].length > 0 {
123+
$network_setup['peered_network_cidrs'].each | $peered_net_cidr | {
124+
# Remove the mask from CIDR to only keep the network Ipv4 (`10.0.0.0/24` returns `10.0.0.0`)
125+
$peered_network_ip = split($peered_net_cidr, '/')[0]
126+
# Only get the 3 first digits of the IPv4 (`10.0.0.0` returns `10.0.0`)
127+
$peered_network_prefix = join(split($peered_network_ip, '[.]')[0,3], '.')
128+
129+
## Custom routes for peered networks
130+
$gateway = "${network_prefix}.1"
131+
exec { "addroute ${peered_network_prefix}.0 through ${gateway} (NIC ${network_nic})":
132+
command => "ip route add ${peered_net_cidr} via ${gateway} dev ${network_nic}",
133+
unless => "route | grep ${peered_network_prefix}.0",
134+
require => [
135+
# The CLI command 'route' is needed
136+
Package['net-tools'],
137+
],
138+
path => '/usr/bin:/usr/sbin:/bin:/sbin',
139+
}
140+
}
141+
}
142+
143+
# The lambda filter is used to cleanup the array from empty element (when $network_setup['peered_network_cidrs'] is undefined)
144+
$destinations_cidrs = ([$network_setup['network_cidr']] + $network_setup['peered_network_cidrs']).filter |$item| {
145+
$item and $item.length > 0
146+
}
147+
148+
# For each VPN network, add all the destinations per interface
149+
lookup('profile::openvpn::vpn_networks_cidr').each |$vpn_network_cidr| {
150+
$destinations_cidrs.each |$destination_cidr| {
151+
# Then add firewall rules to allow routing through networks using masquerading
152+
firewall { "100 allow routing from ${vpn_network_cidr} to ${destination_cidr} on ports 80/443":
153+
chain => 'POSTROUTING',
154+
jump => 'MASQUERADE',
155+
proto => 'tcp',
156+
outiface => $network_nic,
157+
source => $vpn_network_cidr,
158+
dport => [80,443],
159+
destination => $destination_cidr,
160+
table => 'nat',
161+
}
162+
}
163+
}
156164
}
157165
}

hieradata/clients/vpn.jenkins.io.yaml

+12
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
11
---
22
profile::openvpn::networks:
33
eth0:
4+
name: Subnet 'dmz' of network 'prod-jenkins-public-prod'
45
route-metric: 100
56
macaddress: 00:0d:3a:0e:4b:1c
7+
network_cidr: 10.0.99.0/24
68
eth1:
9+
name: Subnet 'data-tier' of network 'prod-jenkins-public-prod'
710
route-metric: 200
811
macaddress: 00:0d:3a:0e:4f:89
12+
network_cidr: 10.0.2.0/24
13+
peered_network_cidrs:
14+
# Network 'prod-jenkins-private-prod-vnet' through peering
15+
- 10.240.0.0/14
916
eth2:
17+
name: Subnet 'app-tier' of network 'prod-jenkins-public-prod'
1018
route-metric: 300
1119
macaddress: 00:0d:3a:0e:4e:7e
20+
network_cidr: 10.0.1.0/24
21+
profile::openvpn::vpn_networks_cidr:
22+
- 10.8.0.0/24 # Normal VPN
23+
- 10.8.1.0/24 # Admin network (legacy: only used for KK openvpn client config) - https://github.com/jenkins-infra/docker-openvpn/blob/583f84af3fb7f0a60bfb0b100e56e1b905d713f2/config.yaml#L12-L19
1224
profile::openvpn::auth_ldap_binddn: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAUnLHzSYfhy9wQ622F9qg27p80NMo+5M1I0XJUM3vQ6+XhcmG8OeFN0lksZJMD1p2tp8vZTAbq+BtdCfy9e9iaZudQvL2XHqlGFSrssz25FfpfKny5/QvKRZJqOW7K/7gfwnj+kIO7kVMFkO0XhcqRKsCjO2hBIaWBw/3BtIpE0MB/pPw/vLI3aERnj/YBRg6iHQUuBiYOZc5WJ6CJiVPiiCcfz1YhzvsdjeE/6PLCXSWVzR6fXJaIv8AeLOJ/T/KrulWZbQ2C3fE/hWYCznIEzmsHHnd/td8/gTOyetl8CbPeeVWnl3z4cjD3dhpRc80hDB912Wpp7p3U6QeM2qlszBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBDTNSew0xsPvYG7UiPm3rC2gDBdODpfNj9Y+ihtpKsm67MmN88nbQZ2LPfg2ClMVFQOYtKs+3nQoT8LupqIDGMIRYo=]
1325
profile::openvpn::auth_ldap_password: ENC[PKCS7,MIIBiQYJKoZIhvcNAQcDoIIBejCCAXYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAHnkX82SuWKYuW/ROAxQX3fw+HrN/AQf3BpbeUdSwCe9+ljLni05W4ZypQkRNgEksUfD0bBgK2RA7PF+KYSnTpA5v+GkqjUn8iAZg9whEpQaK7wfidp8rSL+nlsJFNE8mPHLCoO+6xHMFmm6XCCsFnQ2qYNQAjWeecngdp3IIZi2Vs8NBYJbOEovsRgdksNu7gO516C9DBrWKaTOf7j9x28g22XFQ3kxefxq89QGquvwc3Lyl8iNInJNUQ8wmCk54+m2CvTKzbqnO53QgHbw2VUwN+6JnmN+HgxzSM9o+VO8x2S8o8dzS0v45fWwLM4sKuwUPeAxfVGpfb2J46ab3tzBMBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBAidRxKsLYgBugid4s3KGq+gCBhpSsRd85sDKxC3m6l5ys56qg96hfUV7BRqeygYAztiw==]
1426
# CA comes from https://github.com/jenkins-infra/docker-openvpn

0 commit comments

Comments
 (0)