Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / spec / classes / nftables_spec.rb @ e0bb7852

Historique | Voir | Annoter | Télécharger (11,6 ko)

1
# frozen_string_literal: true
2

    
3
require 'spec_helper'
4

    
5
describe 'nftables' do
6
  let(:pre_condition) { 'Exec{path => "/bin"}' }
7

    
8
  on_supported_os.each do |os, os_facts|
9
    context "on #{os}" do
10
      let(:facts) { os_facts }
11

    
12
      nft_path = case os_facts[:os]['family']
13
                 when 'Archlinux'
14
                   '/usr/bin/nft'
15
                 else
16
                   '/usr/sbin/nft'
17
                 end
18
      nft_config = case os_facts[:os]['family']
19
                   when 'RedHat'
20
                     '/etc/sysconfig/nftables.conf'
21
                   else
22
                     '/etc/nftables.conf'
23
                   end
24

    
25
      nft_mode = case os_facts[:os]['family']
26
                 when 'RedHat'
27
                   '0600'
28
                 else
29
                   '0640'
30
                 end
31

    
32
      it { is_expected.to compile.with_all_deps }
33

    
34
      it { is_expected.to contain_package('nftables') }
35

    
36
      context 'with clobber_default_config false' do
37
        let(:params) do
38
          { clobber_default_config: false }
39
        end
40

    
41
        it {
42
          is_expected.to contain_file_line('enable_nftables').with(
43
            line: 'include "/etc/nftables/puppet.nft"',
44
            path: nft_config
45
          )
46
        }
47

    
48
        it { is_expected.not_to contain_file(nft_config) }
49
      end
50

    
51
      context 'with clobber_default_config true' do
52
        let(:params) do
53
          { clobber_default_config: true }
54
        end
55

    
56
        it {
57
          is_expected.to contain_file(nft_config).with(
58
            ensure: 'file',
59
            content: %r{^include "/etc/nftables/puppet.nft"$},
60
            owner: 'root',
61
            group: 'root'
62
          )
63
        }
64

    
65
        it { is_expected.not_to contain_file_line('enable_nftables') }
66
      end
67

    
68
      it {
69
        is_expected.to contain_file('/etc/nftables').with(
70
          ensure: 'directory',
71
          owner: 'root',
72
          group: 'root',
73
          mode: nft_mode
74
        )
75
      }
76

    
77
      it {
78
        expect(subject).to contain_file('/etc/nftables/puppet.nft').with(
79
          ensure: 'file',
80
          owner: 'root',
81
          group: 'root',
82
          mode: nft_mode,
83
          content: %r{flush ruleset}
84
        )
85
      }
86

    
87
      it {
88
        expect(subject).to contain_file('/etc/nftables/puppet.nft').with(
89
          content: %r{^include "file-\*\.nft"$}
90
        )
91
      }
92

    
93
      it {
94
        expect(subject).to contain_file('/etc/nftables/puppet').with(
95
          ensure: 'directory',
96
          owner: 'root',
97
          group: 'root',
98
          mode: nft_mode,
99
          purge: true,
100
          force: true,
101
          recurse: true
102
        )
103
      }
104

    
105
      it {
106
        expect(subject).to contain_file('/etc/nftables/puppet-preflight.nft').with(
107
          ensure: 'file',
108
          owner: 'root',
109
          group: 'root',
110
          mode: nft_mode,
111
          content: %r{flush ruleset}
112
        )
113
      }
114

    
115
      it {
116
        expect(subject).to contain_file('/etc/nftables/puppet-preflight.nft').with(
117
          content: %r{^include "file-\*\.nft"$}
118
        )
119
      }
120

    
121
      it {
122
        expect(subject).to contain_file('/etc/nftables/puppet-preflight').with(
123
          ensure: 'directory',
124
          owner: 'root',
125
          group: 'root',
126
          mode: nft_mode,
127
          purge: true,
128
          force: true,
129
          recurse: true
130
        )
131
      }
132

    
133
      it {
134
        expect(subject).not_to contain_exec('nftables_memory_state_check')
135
      }
136

    
137
      it {
138
        expect(subject).not_to contain_exec('nftables_generate_hash')
139
      }
140

    
141
      it {
142
        expect(subject).not_to contain_file('/var/tmp/puppet-nft-memhash')
143
      }
144

    
145
      it {
146
        expect(subject).to contain_exec('nft validate').with(
147
          refreshonly: true,
148
          command: %r{^#{nft_path} -I /etc/nftables/puppet-preflight -c -f /etc/nftables/puppet-preflight.nft.*}
149
        )
150
      }
151

    
152
      it {
153
        expect(subject).to contain_service('nftables').with(
154
          ensure: 'running',
155
          enable: true,
156
          hasrestart: true,
157
          restart: %r{PATH=/usr/bin:/bin systemctl reload nft.*}
158
        )
159
      }
160

    
161
      it {
162
        expect(subject).to contain_systemd__dropin_file('puppet_nft.conf').with(
163
          content: %r{^ExecReload=#{nft_path} -I /etc/nftables/puppet -f #{nft_config}$}
164
        )
165
      }
166

    
167
      case os_facts[:os]['family']
168
      when 'Archlinux'
169

    
170
        it {
171
          expect(subject).to contain_service('firewalld').with(
172
            ensure: 'stopped',
173
            enable: false
174
          )
175
        }
176
      when 'Debian'
177
        it {
178
          is_expected.to contain_service('firewalld').with(
179
            ensure: 'stopped',
180
            enable: false
181
          )
182
        }
183
      else
184
        it {
185
          expect(subject).to contain_service('firewalld').with(
186
            ensure: 'stopped',
187
            enable: 'mask'
188
          )
189
        }
190
      end
191

    
192
      it { is_expected.to contain_class('nftables::inet_filter') }
193
      it { is_expected.to contain_class('nftables::ip_nat') }
194
      it { is_expected.to contain_class('nftables::rules::out::http') }
195
      it { is_expected.to contain_class('nftables::rules::out::https') }
196
      it { is_expected.to contain_class('nftables::rules::out::dns') }
197
      it { is_expected.to contain_class('nftables::rules::out::chrony') }
198
      it { is_expected.not_to contain_class('nftables::rules::out::all') }
199
      it { is_expected.not_to contain_nftables__rule('default_out-all') }
200

    
201
      context 'with out_all set true' do
202
        let(:params) do
203
          {
204
            out_all: true,
205
          }
206
        end
207

    
208
        it { is_expected.to contain_class('nftables::rules::out::all') }
209
        it { is_expected.not_to contain_class('nftables::rules::out::http') }
210
        it { is_expected.not_to contain_class('nftables::rules::out::https') }
211
        it { is_expected.not_to contain_class('nftables::rules::out::dns') }
212
        it { is_expected.not_to contain_class('nftables::rules::out::chrony') }
213
        it { is_expected.to contain_nftables__rule('default_out-all').with_content('accept') }
214
        it { is_expected.to contain_nftables__rule('default_out-all').with_order('90') }
215
      end
216

    
217
      context 'with custom rules' do
218
        let(:params) do
219
          {
220
            rules: {
221
              'INPUT-web_accept' => {
222
                order: '50',
223
                content: 'iifname eth0 tcp dport { 80, 443 } accept',
224
              },
225
            },
226
          }
227
        end
228

    
229
        it {
230
          expect(subject).to contain_concat__fragment('nftables-inet-filter-chain-INPUT-rule-web_accept').with(
231
            target: 'nftables-inet-filter-chain-INPUT',
232
            content: %r{^  iifname eth0 tcp dport \{ 80, 443 \} accept$},
233
            order: '50-nftables-inet-filter-chain-INPUT-rule-web_accept-b'
234
          )
235
        }
236
      end
237

    
238
      context 'with custom sets' do
239
        let(:params) do
240
          {
241
            sets: {
242
              'testset1' => {
243
                type: 'ipv4_addr',
244
                gc_interval: 2,
245
              },
246
              'testset2' => {
247
                type: 'ipv6_addr',
248
                elements: ['2a02:62:c601::dead:beef'],
249
              },
250
            },
251
          }
252
        end
253

    
254
        it {
255
          expect(subject).to contain_nftables__set('testset1').with(
256
            type: 'ipv4_addr',
257
            gc_interval: 2,
258
            table: 'inet-filter'
259
          )
260
        }
261

    
262
        it {
263
          expect(subject).to contain_nftables__set('testset2').with(
264
            type: 'ipv6_addr',
265
            elements: ['2a02:62:c601::dead:beef'],
266
            table: 'inet-filter'
267
          )
268
        }
269
      end
270

    
271
      context 'without masking firewalld' do
272
        let(:params) do
273
          {
274
            'firewalld_enable' => false,
275
          }
276
        end
277

    
278
        it {
279
          expect(subject).to contain_service('firewalld').with(
280
            ensure: 'stopped',
281
            enable: false
282
          )
283
        }
284
      end
285

    
286
      context 'with no default filtering rules' do
287
        let(:params) do
288
          {
289
            'inet_filter' => false,
290
          }
291
        end
292

    
293
        it { is_expected.to contain_class('nftables::ip_nat') }
294
        it { is_expected.not_to contain_class('nftables::inet_filter') }
295
      end
296

    
297
      context 'with no default tables, chains or rules' do
298
        let(:params) do
299
          {
300
            'inet_filter' => false,
301
            'nat' => false,
302
          }
303
        end
304

    
305
        it { is_expected.not_to contain_class('nftables::ip_nat') }
306
        it { is_expected.not_to contain_class('nftables::inet_filter') }
307
        it { is_expected.to have_nftables__config_resource_count(0) }
308
        it { is_expected.to have_nftables__chain_resource_count(0) }
309
        it { is_expected.to have_nftables__rule_resource_count(0) }
310
        it { is_expected.to have_nftables__set_resource_count(0) }
311
      end
312

    
313
      context 'when purging unmanaged rules' do
314
        let(:params) do
315
          {
316
            'purge_unmanaged_rules' => true,
317
            'inmem_rules_hash_file' => '/foo/bar',
318
          }
319
        end
320

    
321
        it {
322
          is_expected.to contain_exec('nftables_memory_state_check').with(
323
            command: %w[echo reloading_nftables],
324
            notify: 'Service[nftables]',
325
            unless: ['test -s /foo/bar -a "$(nft -s list ruleset | sha1sum)" = "$(cat /foo/bar)"']
326
          )
327
        }
328

    
329
        it {
330
          is_expected.to contain_exec('nftables_generate_hash').with(
331
            command: ['nft -s list ruleset | sha1sum > /foo/bar'],
332
            subscribe: 'Service[nftables]',
333
            refreshonly: true
334
          )
335
        }
336
      end
337

    
338
      %w[ip ip6 inet arp bridge netdev].each do |family|
339
        context "with noflush_tables parameter set to valid family #{family}" do
340
          let(:params) do
341
            {
342
              noflush_tables: ["#{family}-f2b-table"],
343
            }
344
          end
345

    
346
          context 'with no nftables fact' do
347
            it { is_expected.to contain_file('/etc/nftables/puppet-preflight.nft').with_content(%r{^flush ruleset$}) }
348
          end
349

    
350
          context 'with nftables fact matching' do
351
            let(:facts) do
352
              super().merge(nftables: { tables: %W[#{family}-abc #{family}-f2b-table] })
353
            end
354

    
355
            it {
356
              expect(subject).to contain_file('/etc/nftables/puppet-preflight.nft').
357
                with_content(%r{^table #{family} abc \{\}$})
358
            }
359

    
360
            it {
361
              expect(subject).to contain_file('/etc/nftables/puppet-preflight.nft').
362
                with_content(%r{^flush table #{family} abc$})
363
            }
364
          end
365

    
366
          context 'with nftables fact not matching' do
367
            let(:facts) do
368
              super().merge(nftables: { tables: %W[#{family}-abc #{family}-ijk] })
369
            end
370

    
371
            it {
372
              expect(subject).to contain_file('/etc/nftables/puppet-preflight.nft').
373
                with_content(%r{^table #{family} abc \{\}$})
374
            }
375

    
376
            it {
377
              expect(subject).to contain_file('/etc/nftables/puppet-preflight.nft').
378
                with_content(%r{^flush table #{family} abc$})
379
            }
380

    
381
            it {
382
              expect(subject).to contain_file('/etc/nftables/puppet-preflight.nft').
383
                with_content(%r{^table #{family} ijk \{\}$})
384
            }
385

    
386
            it {
387
              expect(subject).to contain_file('/etc/nftables/puppet-preflight.nft').
388
                with_content(%r{^flush table #{family} ijk$})
389
            }
390
          end
391
        end
392
      end
393

    
394
      %w[it ip7 inter arpa brid netdevs].each do |family|
395
        context "with noflush_tables parameter set to invalid family #{family}" do
396
          let(:params) do
397
            {
398
              noflush_tables: ["#{family}-f2b-table"],
399
            }
400
          end
401

    
402
          it { is_expected.not_to compile }
403
        end
404
      end
405
    end
406
  end
407
end