Projet

Général

Profil

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

root / spec / classes / nftables_spec.rb @ 5dedf86c

Historique | Voir | Annoter | Télécharger (11,8 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
      case [os_facts[:os]['name'], os_facts[:os]['release']['major']]
37
      when ['Ubuntu', '20.04']
38
        it { is_expected.to contain_package('netbase') }
39
      else
40
        it { is_expected.not_to contain_package('netbase') }
41
      end
42

    
43
      context 'with clobber_default_config false' do
44
        let(:params) do
45
          { clobber_default_config: false }
46
        end
47

    
48
        it {
49
          is_expected.to contain_file_line('enable_nftables').with(
50
            line: 'include "/etc/nftables/puppet.nft"',
51
            path: nft_config
52
          )
53
        }
54

    
55
        it { is_expected.not_to contain_file(nft_config) }
56
      end
57

    
58
      context 'with clobber_default_config true' do
59
        let(:params) do
60
          { clobber_default_config: true }
61
        end
62

    
63
        it {
64
          is_expected.to contain_file(nft_config).with(
65
            ensure: 'file',
66
            content: %r{^include "/etc/nftables/puppet.nft"$},
67
            owner: 'root',
68
            group: 'root'
69
          )
70
        }
71

    
72
        it { is_expected.not_to contain_file_line('enable_nftables') }
73
      end
74

    
75
      it {
76
        is_expected.to contain_file('/etc/nftables').with(
77
          ensure: 'directory',
78
          owner: 'root',
79
          group: 'root',
80
          mode: nft_mode
81
        )
82
      }
83

    
84
      it {
85
        expect(subject).to contain_file('/etc/nftables/puppet.nft').with(
86
          ensure: 'file',
87
          owner: 'root',
88
          group: 'root',
89
          mode: nft_mode,
90
          content: %r{flush ruleset}
91
        )
92
      }
93

    
94
      it {
95
        expect(subject).to contain_file('/etc/nftables/puppet.nft').with(
96
          content: %r{^include "file-\*\.nft"$}
97
        )
98
      }
99

    
100
      it {
101
        expect(subject).to contain_file('/etc/nftables/puppet').with(
102
          ensure: 'directory',
103
          owner: 'root',
104
          group: 'root',
105
          mode: nft_mode,
106
          purge: true,
107
          force: true,
108
          recurse: true
109
        )
110
      }
111

    
112
      it {
113
        expect(subject).to contain_file('/etc/nftables/puppet-preflight.nft').with(
114
          ensure: 'file',
115
          owner: 'root',
116
          group: 'root',
117
          mode: nft_mode,
118
          content: %r{flush ruleset}
119
        )
120
      }
121

    
122
      it {
123
        expect(subject).to contain_file('/etc/nftables/puppet-preflight.nft').with(
124
          content: %r{^include "file-\*\.nft"$}
125
        )
126
      }
127

    
128
      it {
129
        expect(subject).to contain_file('/etc/nftables/puppet-preflight').with(
130
          ensure: 'directory',
131
          owner: 'root',
132
          group: 'root',
133
          mode: nft_mode,
134
          purge: true,
135
          force: true,
136
          recurse: true
137
        )
138
      }
139

    
140
      it {
141
        expect(subject).not_to contain_exec('nftables_memory_state_check')
142
      }
143

    
144
      it {
145
        expect(subject).not_to contain_exec('nftables_generate_hash')
146
      }
147

    
148
      it {
149
        expect(subject).not_to contain_file('/var/tmp/puppet-nft-memhash')
150
      }
151

    
152
      it {
153
        expect(subject).to contain_exec('nft validate').with(
154
          refreshonly: true,
155
          command: %r{^#{nft_path} -I /etc/nftables/puppet-preflight -c -f /etc/nftables/puppet-preflight.nft.*}
156
        )
157
      }
158

    
159
      it {
160
        expect(subject).to contain_service('nftables').with(
161
          ensure: 'running',
162
          enable: true,
163
          hasrestart: true,
164
          restart: %r{PATH=/usr/bin:/bin systemctl reload nft.*}
165
        )
166
      }
167

    
168
      it {
169
        expect(subject).to contain_systemd__dropin_file('puppet_nft.conf').with(
170
          content: %r{^ExecReload=#{nft_path} -I /etc/nftables/puppet -f #{nft_config}$}
171
        )
172
      }
173

    
174
      case os_facts[:os]['family']
175
      when 'Archlinux'
176

    
177
        it {
178
          expect(subject).to contain_service('firewalld').with(
179
            ensure: 'stopped',
180
            enable: false
181
          )
182
        }
183
      when 'Debian'
184
        it {
185
          is_expected.to contain_service('firewalld').with(
186
            ensure: 'stopped',
187
            enable: false
188
          )
189
        }
190
      else
191
        it {
192
          expect(subject).to contain_service('firewalld').with(
193
            ensure: 'stopped',
194
            enable: 'mask'
195
          )
196
        }
197
      end
198

    
199
      it { is_expected.to contain_class('nftables::inet_filter') }
200
      it { is_expected.to contain_class('nftables::ip_nat') }
201
      it { is_expected.to contain_class('nftables::rules::out::http') }
202
      it { is_expected.to contain_class('nftables::rules::out::https') }
203
      it { is_expected.to contain_class('nftables::rules::out::dns') }
204
      it { is_expected.to contain_class('nftables::rules::out::chrony') }
205
      it { is_expected.not_to contain_class('nftables::rules::out::all') }
206
      it { is_expected.not_to contain_nftables__rule('default_out-all') }
207

    
208
      context 'with out_all set true' do
209
        let(:params) do
210
          {
211
            out_all: true,
212
          }
213
        end
214

    
215
        it { is_expected.to contain_class('nftables::rules::out::all') }
216
        it { is_expected.not_to contain_class('nftables::rules::out::http') }
217
        it { is_expected.not_to contain_class('nftables::rules::out::https') }
218
        it { is_expected.not_to contain_class('nftables::rules::out::dns') }
219
        it { is_expected.not_to contain_class('nftables::rules::out::chrony') }
220
        it { is_expected.to contain_nftables__rule('default_out-all').with_content('accept') }
221
        it { is_expected.to contain_nftables__rule('default_out-all').with_order('90') }
222
      end
223

    
224
      context 'with custom rules' do
225
        let(:params) do
226
          {
227
            rules: {
228
              'INPUT-web_accept' => {
229
                order: '50',
230
                content: 'iifname eth0 tcp dport { 80, 443 } accept',
231
              },
232
            },
233
          }
234
        end
235

    
236
        it {
237
          expect(subject).to contain_concat__fragment('nftables-inet-filter-chain-INPUT-rule-web_accept').with(
238
            target: 'nftables-inet-filter-chain-INPUT',
239
            content: %r{^  iifname eth0 tcp dport \{ 80, 443 \} accept$},
240
            order: '50-nftables-inet-filter-chain-INPUT-rule-web_accept-b'
241
          )
242
        }
243
      end
244

    
245
      context 'with custom sets' do
246
        let(:params) do
247
          {
248
            sets: {
249
              'testset1' => {
250
                type: 'ipv4_addr',
251
                gc_interval: 2,
252
              },
253
              'testset2' => {
254
                type: 'ipv6_addr',
255
                elements: ['2a02:62:c601::dead:beef'],
256
              },
257
            },
258
          }
259
        end
260

    
261
        it {
262
          expect(subject).to contain_nftables__set('testset1').with(
263
            type: 'ipv4_addr',
264
            gc_interval: 2,
265
            table: 'inet-filter'
266
          )
267
        }
268

    
269
        it {
270
          expect(subject).to contain_nftables__set('testset2').with(
271
            type: 'ipv6_addr',
272
            elements: ['2a02:62:c601::dead:beef'],
273
            table: 'inet-filter'
274
          )
275
        }
276
      end
277

    
278
      context 'without masking firewalld' do
279
        let(:params) do
280
          {
281
            'firewalld_enable' => false,
282
          }
283
        end
284

    
285
        it {
286
          expect(subject).to contain_service('firewalld').with(
287
            ensure: 'stopped',
288
            enable: false
289
          )
290
        }
291
      end
292

    
293
      context 'with no default filtering rules' do
294
        let(:params) do
295
          {
296
            'inet_filter' => false,
297
          }
298
        end
299

    
300
        it { is_expected.to contain_class('nftables::ip_nat') }
301
        it { is_expected.not_to contain_class('nftables::inet_filter') }
302
      end
303

    
304
      context 'with no default tables, chains or rules' do
305
        let(:params) do
306
          {
307
            'inet_filter' => false,
308
            'nat' => false,
309
          }
310
        end
311

    
312
        it { is_expected.not_to contain_class('nftables::ip_nat') }
313
        it { is_expected.not_to contain_class('nftables::inet_filter') }
314
        it { is_expected.to have_nftables__config_resource_count(0) }
315
        it { is_expected.to have_nftables__chain_resource_count(0) }
316
        it { is_expected.to have_nftables__rule_resource_count(0) }
317
        it { is_expected.to have_nftables__set_resource_count(0) }
318
      end
319

    
320
      context 'when purging unmanaged rules' do
321
        let(:params) do
322
          {
323
            'purge_unmanaged_rules' => true,
324
            'inmem_rules_hash_file' => '/foo/bar',
325
          }
326
        end
327

    
328
        it {
329
          is_expected.to contain_exec('nftables_memory_state_check').with(
330
            command: %w[echo reloading_nftables],
331
            notify: 'Service[nftables]',
332
            unless: ['test -s /foo/bar -a "$(nft -s list ruleset | sha1sum)" = "$(cat /foo/bar)"']
333
          )
334
        }
335

    
336
        it {
337
          is_expected.to contain_exec('nftables_generate_hash').with(
338
            command: ['nft -s list ruleset | sha1sum > /foo/bar'],
339
            subscribe: 'Service[nftables]',
340
            refreshonly: true
341
          )
342
        }
343
      end
344

    
345
      %w[ip ip6 inet arp bridge netdev].each do |family|
346
        context "with noflush_tables parameter set to valid family #{family}" do
347
          let(:params) do
348
            {
349
              noflush_tables: ["#{family}-f2b-table"],
350
            }
351
          end
352

    
353
          context 'with no nftables fact' do
354
            it { is_expected.to contain_file('/etc/nftables/puppet-preflight.nft').with_content(%r{^flush ruleset$}) }
355
          end
356

    
357
          context 'with nftables fact matching' do
358
            let(:facts) do
359
              super().merge(nftables: { tables: %W[#{family}-abc #{family}-f2b-table] })
360
            end
361

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

    
367
            it {
368
              expect(subject).to contain_file('/etc/nftables/puppet-preflight.nft').
369
                with_content(%r{^flush table #{family} abc$})
370
            }
371
          end
372

    
373
          context 'with nftables fact not matching' do
374
            let(:facts) do
375
              super().merge(nftables: { tables: %W[#{family}-abc #{family}-ijk] })
376
            end
377

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

    
383
            it {
384
              expect(subject).to contain_file('/etc/nftables/puppet-preflight.nft').
385
                with_content(%r{^flush table #{family} abc$})
386
            }
387

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

    
393
            it {
394
              expect(subject).to contain_file('/etc/nftables/puppet-preflight.nft').
395
                with_content(%r{^flush table #{family} ijk$})
396
            }
397
          end
398
        end
399
      end
400

    
401
      %w[it ip7 inter arpa brid netdevs].each do |family|
402
        context "with noflush_tables parameter set to invalid family #{family}" do
403
          let(:params) do
404
            {
405
              noflush_tables: ["#{family}-f2b-table"],
406
            }
407
          end
408

    
409
          it { is_expected.not_to compile }
410
        end
411
      end
412
    end
413
  end
414
end