diff --git a/hieradata/node/vpn01.cp.lsst.org.yaml b/hieradata/node/vpn01.cp.lsst.org.yaml new file mode 100644 index 0000000000..f7ecde1968 --- /dev/null +++ b/hieradata/node/vpn01.cp.lsst.org.yaml @@ -0,0 +1,44 @@ +--- +nm::connections: + enp65s0f0: + content: + connection: + id: "enp65s0f0" + uuid: "682a815d-eedf-a30b-774c-aae04c2d5ccb" + type: "ethernet" + interface-name: "enp65s0f0" + ethernet: {} + ipv4: + address1: "139.229.160.82/24,139.229.160.254" + dns: "139.229.160.53;139.229.160.54;139.229.160.55;" + dns-search: "cp.lsst.org;" + method: "manual" + ipv6: + method: "disabled" + proxy: {} + enp65s0f1: + content: + connection: + id: "enp65s0f1" + uuid: "de9904c8-9577-1a17-36b1-34b94132f06a" + type: "ethernet" + autoconnect: "false" + interface-name: "enp65s0f1" + ethernet: {} + ipv4: + method: "disabled" + ipv6: + method: "disabled" + enp12s0f4u1u2c2: + content: + connection: + id: "enp12s0f4u1u2c2" + uuid: "de9904c8-9577-1a17-36b1-34b94132f06a" + type: "ethernet" + autoconnect: "false" + interface-name: "enp12s0f4u1u2c2" + ethernet: {} + ipv4: + method: "disabled" + ipv6: + method: "disabled" diff --git a/hieradata/node/vpn01.dev.lsst.org.yaml b/hieradata/node/vpn01.dev.lsst.org.yaml new file mode 100644 index 0000000000..5d7994128f --- /dev/null +++ b/hieradata/node/vpn01.dev.lsst.org.yaml @@ -0,0 +1,18 @@ +--- +nm::connections: + ens192: + content: + connection: + id: "ens192" + uuid: "03da7500-2101-c722-2438-d0d006c28c73" + type: "ethernet" + interface-name: "ens192" + ethernet: {} + ipv4: + address1: "139.229.134.64/24,139.229.134.254" + dns: "139.229.134.53;139.229.134.54;139.229.134.55;" + dns-search: "dev.lsst.org;" + method: "manual" + ipv6: + method: "disabled" + proxy: {} diff --git a/hieradata/node/vpn01.ls.lsst.org.yaml b/hieradata/node/vpn01.ls.lsst.org.yaml new file mode 100644 index 0000000000..5b0ed4f73d --- /dev/null +++ b/hieradata/node/vpn01.ls.lsst.org.yaml @@ -0,0 +1,70 @@ +--- +nm::connections: + enp129s0f1: + content: + connection: + id: "enp129s0f1" + uuid: "46d19ce1-bcab-7e77-6fc7-b730b26c54b1" + type: "ethernet" + interface-name: "enp129s0f1" + ethernet: {} + ipv4: + address1: "139.229.138.20/24,139.229.138.254" + dns: "139.229.135.53;139.229.135.54;139.229.135.55;" + dns-search: "ls.lsst.org;" + method: "manual" + ipv6: + method: "disabled" + proxy: {} + enp129s0f0: + content: + connection: + id: "enp129s0f0" + uuid: "de9904c8-9577-1a17-36b1-34b94132f06a" + type: "ethernet" + autoconnect: "false" + interface-name: "enp129s0f0" + ethernet: {} + ipv4: + method: "disabled" + ipv6: + method: "disabled" + eno1np0: + content: + connection: + id: "eno1np0" + uuid: "de9904c8-9577-1a17-36b1-34b94132f06a" + type: "ethernet" + autoconnect: "false" + interface-name: "eno1np0" + ethernet: {} + ipv4: + method: "disabled" + ipv6: + method: "disabled" + eno1np1: + content: + connection: + id: "eno1np1" + uuid: "de9904c8-9577-1a17-36b1-34b94132f06a" + type: "ethernet" + autoconnect: "false" + interface-name: "eno1np1" + ethernet: {} + ipv4: + method: "disabled" + ipv6: + method: "disabled" + enp4s0f3u2u2c2: + content: + connection: + id: "enp4s0f3u2u2c2" + uuid: "de9904c8-9577-1a17-36b1-34b94132f06a" + type: "ethernet" + autoconnect: "false" + interface-name: "enp4s0f3u2u2c2" + ethernet: {} + ipv4: + method: "disabled" + ipv6: + method: "disabled" diff --git a/hieradata/node/vpn02.cp.lsst.org.yaml b/hieradata/node/vpn02.cp.lsst.org.yaml new file mode 100644 index 0000000000..d376d4b109 --- /dev/null +++ b/hieradata/node/vpn02.cp.lsst.org.yaml @@ -0,0 +1,18 @@ +--- +nm::connections: + ens192: + content: + connection: + id: "ens192" + uuid: "51e4ccf4-1dd1-3081-9b28-0aa1291b79ac" + type: "ethernet" + interface-name: "ens192" + ethernet: {} + ipv4: + address1: "139.229.160.83/24,139.229.160.254" + dns: "139.229.160.53;139.229.160.54;139.229.160.55;" + dns-search: "cp.lsst.org;" + method: "manual" + ipv6: + method: "disabled" + proxy: {} diff --git a/hieradata/node/vpn02.dev.lsst.org.yaml b/hieradata/node/vpn02.dev.lsst.org.yaml new file mode 100644 index 0000000000..b1c483c1ac --- /dev/null +++ b/hieradata/node/vpn02.dev.lsst.org.yaml @@ -0,0 +1,18 @@ +--- +nm::connections: + ens192: + content: + connection: + id: "ens192" + uuid: "03da7500-2101-c722-2438-d0d006c28c73" + type: "ethernet" + interface-name: "ens192" + ethernet: {} + ipv4: + address1: "139.229.134.99/24,139.229.134.254" + dns: "139.229.134.53;139.229.134.54;139.229.134.55;" + dns-search: "dev.lsst.org;" + method: "manual" + ipv6: + method: "disabled" + proxy: {} diff --git a/hieradata/node/vpn02.ls.lsst.org.yaml b/hieradata/node/vpn02.ls.lsst.org.yaml new file mode 100644 index 0000000000..a950becde4 --- /dev/null +++ b/hieradata/node/vpn02.ls.lsst.org.yaml @@ -0,0 +1,18 @@ +--- +nm::connections: + ens192: + content: + connection: + id: "ens192" + uuid: "36e2d4c8-3d89-49c8-95f0-048af8c5fe28" + type: "ethernet" + interface-name: "ens192" + ethernet: {} + ipv4: + address1: "139.229.138.21/24,139.229.138.254" + dns: "139.229.135.53;139.229.135.54;139.229.135.55;" + dns-search: "ls.lsst.org;" + method: "manual" + ipv6: + method: "disabled" + proxy: {} diff --git a/hieradata/role/openvpn.yaml b/hieradata/role/openvpn.yaml new file mode 100644 index 0000000000..c6b93cfc38 --- /dev/null +++ b/hieradata/role/openvpn.yaml @@ -0,0 +1,7 @@ +--- +classes: + - "profile::core::common" + - "profile::core::openvpn" + +profile::core::openvpn::version: "3.0.0_2b84043e" +profile::core::openvpn::cluster: "vpn.%{::site}.lsst.org" diff --git a/site/profile/manifests/core/openvpn.pp b/site/profile/manifests/core/openvpn.pp new file mode 100644 index 0000000000..ba24403e35 --- /dev/null +++ b/site/profile/manifests/core/openvpn.pp @@ -0,0 +1,86 @@ +# @summary +# Installs openvpn connect server. +# +# @param version +# Sets version lock for openvpn package. +# +# @param cluster +# Generates an additional certificate for vpn round robin setup. +# +class profile::core::openvpn ( + String[1] $version, + String[1] $cluster, +) { + include yum::plugin::versionlock + include profile::core::letsencrypt + + yumrepo { 'as-repo-rhel9': + ensure => 'present', + name => 'openvpn-access-server', + descr => 'OpenVPN Access Server', + baseurl => 'http://as-repository.openvpn.net/as/yum/rhel9/', + gpgkey => 'https://as-repository.openvpn.net/as-repo-public.gpg', + gpgcheck => '1', + enabled => '1', + } + + package { 'openvpn-as': + ensure => $version, + require => Yumrepo['as-repo-rhel9'], + notify => Yum::Versionlock['openvpn-as'], + } + + yum::versionlock { 'openvpn-as': + ensure => 'present', + version => $version, + release => '1.el9', + arch => 'x86_64', + } + + # Host FQDN + $fqdn = fact('networking.fqdn') + + # Signed Certificate Location + $le_root = "/etc/letsencrypt/live/${fqdn}" + + # Generate and sign certificate + letsencrypt::certonly { $fqdn: + plugin => 'dns-route53', + manage_cron => true, + } + + # Generate an additional certificate for round robin setup. + letsencrypt::certonly { $cluster: + plugin => 'dns-route53', + manage_cron => true, + } + + # Create symbolic links for certificates + file { '/usr/local/openvpn_as/etc/web-ssl/server.crt': + ensure => 'link', + target => "${le_root}/cert.pem", + force => true, + require => Letsencrypt::Certonly[$fqdn], + } + + file { '/usr/local/openvpn_as/etc/web-ssl/server.key': + ensure => 'link', + target => "${le_root}/privkey.pem", + force => true, + require => Letsencrypt::Certonly[$fqdn], + } + + file { '/usr/local/openvpn_as/etc/web-ssl/ca.crt': + ensure => 'link', + target => "${le_root}/fullchain.pem", + force => true, + require => Letsencrypt::Certonly[$fqdn], + } + + # Manage OpenVPN Access Server service + service { 'openvpnas': + ensure => 'running', + enable => true, + require => Package['openvpn-as'], + } +} diff --git a/spec/classes/core/openvpn_spec.rb b/spec/classes/core/openvpn_spec.rb new file mode 100644 index 0000000000..f8a87024b5 --- /dev/null +++ b/spec/classes/core/openvpn_spec.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'profile::core::openvpn' do + on_supported_os.each do |os, os_facts| + next unless os =~ %r{almalinux-9-x86_64} + + context "on #{os}" do + let(:facts) { os_facts } + + context 'with default parameters' do + let(:params) do + { + version: '3.0.0_2b84043e', + cluster: 'vpn.%{::site}.lsst.org', + } + end + + it { is_expected.to compile.with_all_deps } + + it { is_expected.to contain_class('profile::core::letsencrypt') } + + it do + is_expected.to contain_package('openvpn-as').with( + ensure: '3.0.0_2b84043e', + require: 'Yumrepo[as-repo-rhel9]' + ) + end + + it do + is_expected.to contain_letsencrypt__certonly('vpn.%{::site}.lsst.org').with( + plugin: 'dns-route53', + manage_cron: true + ) + end + + it do + is_expected.to contain_yumrepo('as-repo-rhel9').with( + ensure: 'present', + name: 'openvpn-access-server', + descr: 'OpenVPN Access Server', + baseurl: 'http://as-repository.openvpn.net/as/yum/rhel9/', + gpgkey: 'https://as-repository.openvpn.net/as-repo-public.gpg', + gpgcheck: '1', + enabled: '1' + ) + end + + it do + is_expected.to contain_file('/usr/local/openvpn_as/etc/web-ssl/server.crt').with( + ensure: 'link' + ) + end + + it do + is_expected.to contain_file('/usr/local/openvpn_as/etc/web-ssl/server.key').with( + ensure: 'link' + ) + end + + it do + is_expected.to contain_file('/usr/local/openvpn_as/etc/web-ssl/ca.crt').with( + ensure: 'link' + ) + end + + it do + is_expected.to contain_service('openvpnas').with( + ensure: 'running', + enable: true, + require: 'Package[openvpn-as]' + ) + end + end + end + end +end diff --git a/spec/hosts/nodes/vpn01.cp.lsst.org_spec.rb b/spec/hosts/nodes/vpn01.cp.lsst.org_spec.rb new file mode 100644 index 0000000000..0ccee814eb --- /dev/null +++ b/spec/hosts/nodes/vpn01.cp.lsst.org_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'vpn01.cp.lsst.org', :sitepp do + on_supported_os.each do |os, os_facts| + next unless os =~ %r{almalinux-9-x86_64} + + context "on #{os}" do + let(:facts) do + lsst_override_facts(os_facts, + is_virtual: false, + virtual: 'physical', + dmi: { + 'product' => { + 'name' => 'AS -1115HS-TNR', + }, + }) + end + let(:node_params) do + { + role: 'openvpn', + site: 'cp', + } + end + + it { is_expected.to compile.with_all_deps } + + include_examples 'baremetal' + include_context 'with nm interface' + it { is_expected.to have_nm__connection_resource_count(3) } + + %w[ + enp12s0f4u1u2c2 + enp65s0f1 + ].each do |i| + context "with #{i}" do + let(:interface) { i } + + it_behaves_like 'nm disabled interface' + end + end + + context 'with enp65s0f0' do + let(:interface) { 'enp65s0f0' } + + it_behaves_like 'nm enabled interface' + it_behaves_like 'nm ethernet interface' + it { expect(nm_keyfile['ipv4']['address1']).to eq('139.229.160.82/24,139.229.160.254') } + it { expect(nm_keyfile['ipv4']['dns']).to eq('139.229.160.53;139.229.160.54;139.229.160.55;') } + it { expect(nm_keyfile['ipv4']['dns-search']).to eq('cp.lsst.org;') } + it { expect(nm_keyfile['ipv4']['method']).to eq('manual') } + end + end # on os + end # on_supported_os +end diff --git a/spec/hosts/nodes/vpn01.dev.lsst.org_spec.rb b/spec/hosts/nodes/vpn01.dev.lsst.org_spec.rb new file mode 100644 index 0000000000..df7b981791 --- /dev/null +++ b/spec/hosts/nodes/vpn01.dev.lsst.org_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'vpn01.dev.lsst.org', :sitepp do + on_supported_os.each do |os, os_facts| + next unless os =~ %r{almalinux-9-x86_64} + + context "on #{os}" do + let(:facts) do + lsst_override_facts(os_facts, + is_virtual: true, + virtual: 'vmware', + dmi: { + 'product' => { + 'name' => 'VMware7,1', + }, + }) + end + let(:node_params) do + { + role: 'openvpn', + site: 'dev', + } + end + + it { is_expected.to compile.with_all_deps } + + include_context 'with nm interface' + it { is_expected.to have_nm__connection_resource_count(1) } + + context 'with ens192' do + let(:interface) { 'ens192' } + + it_behaves_like 'nm enabled interface' + it_behaves_like 'nm ethernet interface' + it { expect(nm_keyfile['ipv4']['address1']).to eq('139.229.134.64/24,139.229.134.254') } + it { expect(nm_keyfile['ipv4']['dns']).to eq('139.229.134.53;139.229.134.54;139.229.134.55;') } + it { expect(nm_keyfile['ipv4']['dns-search']).to eq('dev.lsst.org;') } + it { expect(nm_keyfile['ipv4']['method']).to eq('manual') } + end + end # on os + end # on_supported_os +end diff --git a/spec/hosts/nodes/vpn01.ls.lsst.org_spec.rb b/spec/hosts/nodes/vpn01.ls.lsst.org_spec.rb new file mode 100644 index 0000000000..d691431e8c --- /dev/null +++ b/spec/hosts/nodes/vpn01.ls.lsst.org_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'vpn01.ls.lsst.org', :sitepp do + on_supported_os.each do |os, os_facts| + next unless os =~ %r{almalinux-9-x86_64} + + context "on #{os}" do + let(:facts) do + lsst_override_facts(os_facts, + is_virtual: false, + virtual: 'physical', + dmi: { + 'product' => { + 'name' => 'AS -1114S-WN10RT', + }, + }) + end + let(:node_params) do + { + role: 'openvpn', + site: 'ls', + } + end + + it { is_expected.to compile.with_all_deps } + + include_examples 'baremetal' + include_context 'with nm interface' + it { is_expected.to have_nm__connection_resource_count(5) } + + %w[ + enp129s0f0 + eno1np0 + eno1np1 + enp4s0f3u2u2c2 + ].each do |i| + context "with #{i}" do + let(:interface) { i } + + it_behaves_like 'nm disabled interface' + end + end + + context 'with enp129s0f1' do + let(:interface) { 'enp129s0f1' } + + it_behaves_like 'nm enabled interface' + it_behaves_like 'nm ethernet interface' + it { expect(nm_keyfile['ipv4']['address1']).to eq('139.229.138.20/24,139.229.138.254') } + it { expect(nm_keyfile['ipv4']['dns']).to eq('139.229.135.53;139.229.135.54;139.229.135.55;') } + it { expect(nm_keyfile['ipv4']['dns-search']).to eq('ls.lsst.org;') } + it { expect(nm_keyfile['ipv4']['method']).to eq('manual') } + end + end # on os + end # on_supported_os +end diff --git a/spec/hosts/nodes/vpn02.cp.lsst.org_spec.rb b/spec/hosts/nodes/vpn02.cp.lsst.org_spec.rb new file mode 100644 index 0000000000..77bcc42879 --- /dev/null +++ b/spec/hosts/nodes/vpn02.cp.lsst.org_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'vpn02.cp.lsst.org', :sitepp do + on_supported_os.each do |os, os_facts| + next unless os =~ %r{almalinux-9-x86_64} + + context "on #{os}" do + let(:facts) do + lsst_override_facts(os_facts, + is_virtual: true, + virtual: 'vmware', + dmi: { + 'product' => { + 'name' => 'VMware7,1', + }, + }) + end + let(:node_params) do + { + role: 'openvpn', + site: 'cp', + } + end + + it { is_expected.to compile.with_all_deps } + + include_context 'with nm interface' + it { is_expected.to have_nm__connection_resource_count(1) } + + context 'with ens192' do + let(:interface) { 'ens192' } + + it_behaves_like 'nm enabled interface' + it_behaves_like 'nm ethernet interface' + it { expect(nm_keyfile['ipv4']['address1']).to eq('139.229.160.83/24,139.229.160.254') } + it { expect(nm_keyfile['ipv4']['dns']).to eq('139.229.160.53;139.229.160.54;139.229.160.55;') } + it { expect(nm_keyfile['ipv4']['dns-search']).to eq('cp.lsst.org;') } + it { expect(nm_keyfile['ipv4']['method']).to eq('manual') } + end + end # on os + end # on_supported_os +end diff --git a/spec/hosts/nodes/vpn02.dev.lsst.org_spec.rb b/spec/hosts/nodes/vpn02.dev.lsst.org_spec.rb new file mode 100644 index 0000000000..ea6d669170 --- /dev/null +++ b/spec/hosts/nodes/vpn02.dev.lsst.org_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'vpn02.dev.lsst.org', :sitepp do + on_supported_os.each do |os, os_facts| + next unless os =~ %r{almalinux-9-x86_64} + + context "on #{os}" do + let(:facts) do + lsst_override_facts(os_facts, + is_virtual: true, + virtual: 'vmware', + dmi: { + 'product' => { + 'name' => 'VMware7,1', + }, + }) + end + let(:node_params) do + { + role: 'openvpn', + site: 'dev', + } + end + + it { is_expected.to compile.with_all_deps } + + include_context 'with nm interface' + it { is_expected.to have_nm__connection_resource_count(1) } + + context 'with ens192' do + let(:interface) { 'ens192' } + + it_behaves_like 'nm enabled interface' + it_behaves_like 'nm ethernet interface' + it { expect(nm_keyfile['ipv4']['address1']).to eq('139.229.134.99/24,139.229.134.254') } + it { expect(nm_keyfile['ipv4']['dns']).to eq('139.229.134.53;139.229.134.54;139.229.134.55;') } + it { expect(nm_keyfile['ipv4']['dns-search']).to eq('dev.lsst.org;') } + it { expect(nm_keyfile['ipv4']['method']).to eq('manual') } + end + end # on os + end # on_supported_os +end diff --git a/spec/hosts/nodes/vpn02.ls.lsst.org_spec.rb b/spec/hosts/nodes/vpn02.ls.lsst.org_spec.rb new file mode 100644 index 0000000000..ffc7c7d703 --- /dev/null +++ b/spec/hosts/nodes/vpn02.ls.lsst.org_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'vpn02.ls.lsst.org', :sitepp do + on_supported_os.each do |os, os_facts| + next unless os =~ %r{almalinux-9-x86_64} + + context "on #{os}" do + let(:facts) do + lsst_override_facts(os_facts, + is_virtual: true, + virtual: 'vmware', + dmi: { + 'product' => { + 'name' => 'VMware7,1', + }, + }) + end + let(:node_params) do + { + role: 'openvpn', + site: 'ls', + } + end + + it { is_expected.to compile.with_all_deps } + + include_context 'with nm interface' + it { is_expected.to have_nm__connection_resource_count(1) } + + context 'with ens192' do + let(:interface) { 'ens192' } + + it_behaves_like 'nm enabled interface' + it_behaves_like 'nm ethernet interface' + it { expect(nm_keyfile['ipv4']['address1']).to eq('139.229.138.21/24,139.229.138.254') } + it { expect(nm_keyfile['ipv4']['dns']).to eq('139.229.135.53;139.229.135.54;139.229.135.55;') } + it { expect(nm_keyfile['ipv4']['dns-search']).to eq('ls.lsst.org;') } + it { expect(nm_keyfile['ipv4']['method']).to eq('manual') } + end + end # on os + end # on_supported_os +end diff --git a/spec/hosts/roles/openvpn_spec.rb b/spec/hosts/roles/openvpn_spec.rb new file mode 100644 index 0000000000..66bc7d3220 --- /dev/null +++ b/spec/hosts/roles/openvpn_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'spec_helper' + +role = 'openvpn' + +describe "#{role} role" do + on_supported_os.each do |os, os_facts| + next unless os =~ %r{almalinux-9-x86_64} + + context "on #{os}" do + lsst_sites.each do |site| + describe "#{role}.#{site}.lsst.org", :sitepp do + let(:node_params) do + { + role:, + site:, + } + end + let(:facts) { lsst_override_facts(os_facts) } + + it { is_expected.to compile.with_all_deps } + + include_examples('common', os_facts:, site:) + end # host + end # lsst_sites + end # on os + end # on_supported_os +end # role