inspec 1.31.0 → 1.45.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +2599 -1
- data/Gemfile +0 -1
- data/README.md +24 -34
- data/Rakefile +0 -31
- data/docs/dsl_inspec.md +10 -0
- data/docs/matchers.md +29 -0
- data/docs/plugin_kitchen_inspec.md +18 -24
- data/docs/profiles.md +101 -81
- data/docs/resources/aide_conf.md.erb +71 -0
- data/docs/resources/apache_conf.md.erb +19 -33
- data/docs/resources/apt.md.erb +22 -36
- data/docs/resources/audit_policy.md.erb +9 -24
- data/docs/resources/auditd.md.erb +78 -0
- data/docs/resources/auditd_conf.md.erb +20 -34
- data/docs/resources/auditd_rules.md.erb +8 -24
- data/docs/resources/bash.md.erb +4 -26
- data/docs/resources/bond.md.erb +25 -40
- data/docs/resources/bridge.md.erb +5 -25
- data/docs/resources/bsd_service.md.erb +5 -25
- data/docs/resources/command.md.erb +35 -50
- data/docs/resources/cpan.md.erb +62 -0
- data/docs/resources/cran.md.erb +54 -0
- data/docs/resources/crontab.md.erb +9 -23
- data/docs/resources/csv.md.erb +12 -27
- data/docs/resources/dh_params.md +1 -0
- data/docs/resources/directory.md.erb +5 -25
- data/docs/resources/docker.md.erb +61 -58
- data/docs/resources/docker_container.md.erb +23 -19
- data/docs/resources/docker_image.md.erb +20 -16
- data/docs/resources/elasticsearch.md.erb +245 -0
- data/docs/resources/etc_fstab.md.erb +120 -0
- data/docs/resources/etc_group.md.erb +29 -45
- data/docs/resources/etc_hosts.md.erb +68 -0
- data/docs/resources/etc_hosts_allow.md.erb +68 -0
- data/docs/resources/etc_hosts_deny.md.erb +68 -0
- data/docs/resources/file.md.erb +227 -216
- data/docs/resources/firewalld.md.erb +104 -0
- data/docs/resources/gem.md.erb +21 -35
- data/docs/resources/group.md.erb +16 -30
- data/docs/resources/grub_conf.md.erb +9 -24
- data/docs/resources/host.md.erb +32 -49
- data/docs/resources/http.md.erb +38 -44
- data/docs/resources/iis_app.md.erb +25 -35
- data/docs/resources/iis_site.md.erb +26 -40
- data/docs/resources/inetd_conf.md.erb +27 -42
- data/docs/resources/ini.md.erb +9 -23
- data/docs/resources/interface.md.erb +5 -25
- data/docs/resources/iptables.md.erb +15 -29
- data/docs/resources/json.md.erb +12 -27
- data/docs/resources/kernel_module.md.erb +47 -61
- data/docs/resources/kernel_parameter.md.erb +15 -29
- data/docs/resources/key_rsa.md.erb +3 -0
- data/docs/resources/launchd_service.md.erb +5 -25
- data/docs/resources/limits_conf.md.erb +15 -29
- data/docs/resources/login_def.md.erb +15 -30
- data/docs/resources/mount.md.erb +18 -33
- data/docs/resources/mssql_session.md.erb +9 -12
- data/docs/resources/mysql_conf.md.erb +28 -32
- data/docs/resources/mysql_session.md.erb +15 -29
- data/docs/resources/nginx.md.erb +78 -0
- data/docs/resources/nginx_conf.md.erb +127 -0
- data/docs/resources/npm.md.erb +19 -35
- data/docs/resources/ntp_conf.md.erb +20 -37
- data/docs/resources/oneget.md.erb +15 -30
- data/docs/resources/oracledb_session.md.erb +9 -11
- data/docs/resources/os.md.erb +29 -43
- data/docs/resources/os_env.md.erb +23 -44
- data/docs/resources/package.md.erb +34 -36
- data/docs/resources/parse_config.md.erb +5 -25
- data/docs/resources/parse_config_file.md.erb +31 -43
- data/docs/resources/{etc_passwd.md.erb → passwd.md.erb} +24 -39
- data/docs/resources/pip.md.erb +20 -35
- data/docs/resources/port.md.erb +43 -57
- data/docs/resources/postgres_conf.md.erb +17 -31
- data/docs/resources/postgres_hba_conf.md.erb +26 -38
- data/docs/resources/postgres_ident_conf.md.erb +25 -37
- data/docs/resources/postgres_session.md.erb +15 -29
- data/docs/resources/powershell.md.erb +27 -42
- data/docs/resources/processes.md.erb +17 -33
- data/docs/resources/rabbitmq_config.md.erb +9 -24
- data/docs/resources/registry_key.md.erb +27 -42
- data/docs/resources/runit_service.md.erb +5 -25
- data/docs/resources/security_policy.md.erb +12 -27
- data/docs/resources/service.md.erb +27 -42
- data/docs/resources/{etc_shadow.md.erb → shadow.md.erb} +41 -47
- data/docs/resources/ssh_config.md.erb +19 -34
- data/docs/resources/sshd_config.md.erb +19 -34
- data/docs/resources/ssl.md.erb +39 -54
- data/docs/resources/sys_info.md.erb +12 -26
- data/docs/resources/systemd_service.md.erb +5 -25
- data/docs/resources/sysv_service.md.erb +5 -25
- data/docs/resources/upstart_service.md.erb +5 -25
- data/docs/resources/user.md.erb +29 -44
- data/docs/resources/users.md.erb +12 -26
- data/docs/resources/vbscript.md.erb +9 -24
- data/docs/resources/virtualization.md.erb +8 -23
- data/docs/resources/windows_feature.md.erb +15 -30
- data/docs/resources/windows_hotfix.md.erb +50 -0
- data/docs/resources/windows_task.md.erb +12 -26
- data/docs/resources/wmi.md.erb +9 -24
- data/docs/resources/x509_certificate.md.erb +4 -0
- data/docs/resources/xinetd_conf.md.erb +65 -80
- data/docs/resources/xml.md.erb +61 -0
- data/docs/resources/yaml.md.erb +12 -27
- data/docs/resources/yum.md.erb +37 -51
- data/docs/resources/zfs_dataset.md.erb +15 -26
- data/docs/resources/zfs_pool.md.erb +9 -20
- data/docs/ruby_usage.md +0 -1
- data/examples/meta-profile/README.md +1 -1
- data/examples/profile-sensitive/README.md +29 -0
- data/examples/profile-sensitive/controls/sensitive-failures.rb +9 -0
- data/examples/profile-sensitive/controls/sensitive.rb +9 -0
- data/examples/profile-sensitive/inspec.yml +8 -0
- data/inspec.gemspec +3 -3
- data/lib/bundles/inspec-artifact/cli.rb +5 -5
- data/lib/bundles/inspec-compliance/README.md +24 -22
- data/lib/bundles/inspec-compliance/api/login.rb +152 -0
- data/lib/bundles/inspec-compliance/api.rb +46 -16
- data/lib/bundles/inspec-compliance/cli.rb +58 -165
- data/lib/bundles/inspec-compliance/target.rb +2 -3
- data/lib/bundles/inspec-habitat/cli.rb +7 -2
- data/lib/bundles/inspec-habitat/profile.rb +44 -20
- data/lib/bundles/inspec-init/templates/profile/README.md +1 -1
- data/lib/bundles/inspec-init/templates/profile/controls/example.rb +1 -1
- data/lib/bundles/inspec-supermarket/api.rb +1 -1
- data/lib/inspec/backend.rb +8 -0
- data/lib/inspec/cli.rb +1 -1
- data/lib/inspec/control_eval_context.rb +16 -6
- data/lib/inspec/dependencies/requirement.rb +22 -8
- data/lib/inspec/dependencies/resolver.rb +1 -1
- data/lib/inspec/exceptions.rb +4 -0
- data/lib/inspec/file_provider.rb +38 -9
- data/lib/inspec/metadata.rb +17 -3
- data/lib/inspec/objects/attribute.rb +16 -7
- data/lib/inspec/objects/control.rb +10 -2
- data/lib/inspec/plugins/resource.rb +34 -5
- data/lib/inspec/profile.rb +10 -1
- data/lib/inspec/profile_context.rb +3 -1
- data/lib/inspec/resource.rb +17 -5
- data/lib/inspec/rspec_json_formatter.rb +48 -14
- data/lib/inspec/rule.rb +4 -0
- data/lib/inspec/runner.rb +72 -30
- data/lib/inspec/shell.rb +24 -19
- data/lib/inspec/version.rb +1 -1
- data/lib/matchers/matchers.rb +40 -0
- data/lib/resources/aide_conf.rb +162 -0
- data/lib/resources/auditd.rb +231 -0
- data/lib/resources/auditd_rules.rb +3 -1
- data/lib/resources/command.rb +1 -1
- data/lib/resources/cpan.rb +60 -0
- data/lib/resources/cran.rb +66 -0
- data/lib/resources/docker.rb +8 -1
- data/lib/resources/docker_container.rb +19 -2
- data/lib/resources/elasticsearch.rb +172 -0
- data/lib/resources/etc_fstab.rb +102 -0
- data/lib/resources/etc_group.rb +1 -1
- data/lib/resources/etc_hosts.rb +81 -0
- data/lib/resources/etc_hosts_allow_deny.rb +122 -0
- data/lib/resources/file.rb +87 -10
- data/lib/resources/firewalld.rb +144 -0
- data/lib/resources/groups.rb +0 -1
- data/lib/resources/host.rb +30 -15
- data/lib/resources/http.rb +178 -22
- data/lib/resources/mssql_session.rb +3 -3
- data/lib/resources/mysql_conf.rb +10 -0
- data/lib/resources/mysql_session.rb +1 -1
- data/lib/resources/nginx.rb +97 -0
- data/lib/resources/nginx_conf.rb +134 -1
- data/lib/resources/oracledb_session.rb +9 -8
- data/lib/resources/os_env.rb +7 -4
- data/lib/resources/package.rb +14 -2
- data/lib/resources/packages.rb +1 -1
- data/lib/resources/pip.rb +28 -20
- data/lib/resources/port.rb +251 -3
- data/lib/resources/postgres.rb +1 -1
- data/lib/resources/postgres_session.rb +1 -1
- data/lib/resources/processes.rb +106 -20
- data/lib/resources/registry_key.rb +11 -2
- data/lib/resources/service.rb +5 -24
- data/lib/resources/ssh_conf.rb +1 -1
- data/lib/resources/ssl.rb +4 -3
- data/lib/resources/toml.rb +2 -2
- data/lib/resources/windows_hotfix.rb +35 -0
- data/lib/resources/xinetd.rb +4 -6
- data/lib/resources/xml.rb +27 -0
- data/lib/source_readers/inspec.rb +3 -3
- data/lib/utils/find_files.rb +2 -1
- data/lib/utils/nginx_parser.rb +10 -1
- data/lib/utils/object_traversal.rb +35 -10
- data/lib/utils/parser.rb +21 -3
- metadata +48 -18
- data/docs/cli.md +0 -392
data/lib/resources/port.rb
CHANGED
|
@@ -59,9 +59,11 @@ module Inspec::Resources
|
|
|
59
59
|
os = inspec.os
|
|
60
60
|
if os.linux?
|
|
61
61
|
LinuxPorts.new(inspec)
|
|
62
|
-
elsif
|
|
62
|
+
elsif os.aix?
|
|
63
63
|
# AIX: see http://www.ibm.com/developerworks/aix/library/au-lsof.html#resources
|
|
64
64
|
# and https://www-01.ibm.com/marketing/iwm/iwm/web/reg/pick.do?source=aixbp
|
|
65
|
+
AixPorts.new(inspec)
|
|
66
|
+
elsif os.darwin?
|
|
65
67
|
# Darwin: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man8/lsof.8.html
|
|
66
68
|
LsofPorts.new(inspec)
|
|
67
69
|
elsif os.windows?
|
|
@@ -263,11 +265,150 @@ module Inspec::Resources
|
|
|
263
265
|
end
|
|
264
266
|
end
|
|
265
267
|
|
|
268
|
+
class AixPorts < PortsInfo
|
|
269
|
+
def info
|
|
270
|
+
ports_via_netstat || ports_via_lsof
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def ports_via_lsof
|
|
274
|
+
return nil unless inspec.command('lsof').exist?
|
|
275
|
+
LsofPorts.new(inspec).info
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def ports_via_netstat
|
|
279
|
+
return nil unless inspec.command('netstat').exist?
|
|
280
|
+
|
|
281
|
+
cmd = inspec.command('netstat -Aan | grep LISTEN')
|
|
282
|
+
return nil unless cmd.exit_status.to_i.zero?
|
|
283
|
+
|
|
284
|
+
ports = []
|
|
285
|
+
# parse all lines
|
|
286
|
+
cmd.stdout.each_line do |line|
|
|
287
|
+
port_info = parse_netstat_line(line)
|
|
288
|
+
|
|
289
|
+
# only push protocols we are interested in
|
|
290
|
+
next unless %w{tcp tcp6 udp udp6}.include?(port_info['protocol'])
|
|
291
|
+
ports.push(port_info)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
ports
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def parse_netstat_line(line)
|
|
298
|
+
# parse each line
|
|
299
|
+
# 1 - Socket, 2 - Proto, 3 - Receive-Q, 4 - Send-Q, 5 - Local address, 6 - Foreign Address, 7 - State
|
|
300
|
+
parsed = /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)?\s+(\S+)/.match(line)
|
|
301
|
+
return {} if parsed.nil?
|
|
302
|
+
|
|
303
|
+
# parse ip4 and ip6 addresses
|
|
304
|
+
protocol = parsed[2].downcase
|
|
305
|
+
|
|
306
|
+
# detect protocol if not provided
|
|
307
|
+
protocol += '6' if parsed[5].count(':') > 1 && %w{tcp udp}.include?(protocol)
|
|
308
|
+
protocol.chop! if %w{tcp4 upd4}.include?(protocol)
|
|
309
|
+
|
|
310
|
+
# extract host and port information
|
|
311
|
+
host, port = parse_net_address(parsed[5], protocol)
|
|
312
|
+
return {} if host.nil?
|
|
313
|
+
|
|
314
|
+
# extract PID
|
|
315
|
+
cmd = inspec.command("rmsock #{parsed[1]} tcpcb")
|
|
316
|
+
parsed_pid = /^The socket (\S+) is being held by proccess (\d+) \((\S+)\)/.match(cmd.stdout)
|
|
317
|
+
return {} if parsed_pid.nil?
|
|
318
|
+
process = parsed_pid[3]
|
|
319
|
+
pid = parsed_pid[2]
|
|
320
|
+
pid = pid.to_i if pid =~ /^\d+$/
|
|
321
|
+
|
|
322
|
+
{
|
|
323
|
+
'port' => port,
|
|
324
|
+
'address' => host,
|
|
325
|
+
'protocol' => protocol,
|
|
326
|
+
'process' => process,
|
|
327
|
+
'pid' => pid,
|
|
328
|
+
}
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def parse_net_address(net_addr, protocol)
|
|
332
|
+
# local/foreign addresses on AIX use a '.' to separate the addresss
|
|
333
|
+
# from the port
|
|
334
|
+
address, _sep, port = net_addr.rpartition('.')
|
|
335
|
+
if protocol.eql?('tcp6') || protocol.eql?('udp6')
|
|
336
|
+
ip6addr = address
|
|
337
|
+
# AIX uses the wildcard character for ipv6 addresses listening on
|
|
338
|
+
# all interfaces.
|
|
339
|
+
ip6addr = '::' if ip6addr =~ /^\*$/
|
|
340
|
+
|
|
341
|
+
# v6 addresses need to end in a double-colon when using
|
|
342
|
+
# shorthand notation. netstat ends with a single colon.
|
|
343
|
+
# IPAddr will fail to properly parse an address unless it
|
|
344
|
+
# uses a double-colon for short-hand notation.
|
|
345
|
+
ip6addr += ':' if ip6addr =~ /\w:$/
|
|
346
|
+
|
|
347
|
+
begin
|
|
348
|
+
ip_parser = IPAddr.new(ip6addr)
|
|
349
|
+
rescue IPAddr::InvalidAddressError
|
|
350
|
+
# This IP is not parsable. There appears to be a bug in netstat
|
|
351
|
+
# output that truncates link-local IP addresses:
|
|
352
|
+
# example: udp6 0 0 fe80::42:acff:fe11::123 :::* 0 54550 3335/ntpd
|
|
353
|
+
# actual link address: inet6 fe80::42:acff:fe11:5/64 scope link
|
|
354
|
+
#
|
|
355
|
+
# in this example, the "5" is truncated making the netstat output
|
|
356
|
+
# an invalid IP address.
|
|
357
|
+
return [nil, nil]
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
# Check to see if this is a IPv4 address in a tcp6/udp6 line.
|
|
361
|
+
# If so, don't put brackets around the IP or URI won't know how
|
|
362
|
+
# to properly handle it.
|
|
363
|
+
# example: f000000000000000 tcp6 0 0 127.0.0.1.8005 *.* LISTEN
|
|
364
|
+
if ip_parser.ipv4?
|
|
365
|
+
ip_addr = URI("addr://#{ip6addr}:#{port}")
|
|
366
|
+
host = ip_addr.host
|
|
367
|
+
else
|
|
368
|
+
ip_addr = URI("addr://[#{ip6addr}]:#{port}")
|
|
369
|
+
host = ip_addr.host[1..ip_addr.host.size-2]
|
|
370
|
+
end
|
|
371
|
+
else
|
|
372
|
+
ip4addr = address
|
|
373
|
+
# In AIX the wildcard character is used to match all interfaces
|
|
374
|
+
ip4addr = '0.0.0.0' if ip4addr =~ /^\*$/
|
|
375
|
+
ip_addr = URI("addr://#{ip4addr}:#{port}")
|
|
376
|
+
host = ip_addr.host
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
[host, port.to_i]
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
|
|
266
383
|
# extract port information from netstat
|
|
267
|
-
class LinuxPorts < PortsInfo
|
|
384
|
+
class LinuxPorts < PortsInfo # rubocop:disable Metrics/ClassLength
|
|
385
|
+
ALLOWED_PROTOCOLS = %w{tcp tcp6 udp udp6}.freeze
|
|
386
|
+
|
|
268
387
|
def info
|
|
388
|
+
ports_via_ss || ports_via_netstat
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def ports_via_ss
|
|
392
|
+
return nil unless inspec.command('ss').exist?
|
|
393
|
+
|
|
394
|
+
cmd = inspec.command('ss -tulpen')
|
|
395
|
+
return nil unless cmd.exit_status.to_i.zero?
|
|
396
|
+
|
|
397
|
+
ports = []
|
|
398
|
+
|
|
399
|
+
cmd.stdout.each_line do |line|
|
|
400
|
+
parsed_line = parse_ss_line(line)
|
|
401
|
+
ports << parsed_line unless parsed_line.nil?
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
ports
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def ports_via_netstat
|
|
408
|
+
return nil unless inspec.command('netstat').exist?
|
|
409
|
+
|
|
269
410
|
cmd = inspec.command('netstat -tulpen')
|
|
270
|
-
return nil
|
|
411
|
+
return nil unless cmd.exit_status.to_i.zero?
|
|
271
412
|
|
|
272
413
|
ports = []
|
|
273
414
|
# parse all lines
|
|
@@ -362,6 +503,113 @@ module Inspec::Resources
|
|
|
362
503
|
'pid' => pid,
|
|
363
504
|
}
|
|
364
505
|
end
|
|
506
|
+
|
|
507
|
+
def tokenize_ss_line(line)
|
|
508
|
+
# iproute-2.6.32-54.el6 output:
|
|
509
|
+
# Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port
|
|
510
|
+
# udp UNCONN 0 0 *:111 *:* users:(("rpcbind",1123,6)) ino=8680 sk=ffff8801390cf7c0
|
|
511
|
+
# tcp LISTEN 0 128 *:22 *:* users:(("sshd",3965,3)) ino:11604 sk:ffff88013a3b5800
|
|
512
|
+
#
|
|
513
|
+
# iproute-2.6.32-20.el6 output:
|
|
514
|
+
# Netid Recv-Q Send-Q Local Address:Port Peer Address:Port
|
|
515
|
+
# udp 0 0 *:111 *:* users:(("rpcbind",1123,6)) ino=8680 sk=ffff8801390cf7c0
|
|
516
|
+
# tcp 0 128 *:22 *:* users:(("sshd",3965,3)) ino:11604 sk:ffff88013a3b5800
|
|
517
|
+
tokens = line.split(/\s+/, 7)
|
|
518
|
+
if tokens[1] =~ /^\d+$/ # iproute-2.6.32-20
|
|
519
|
+
{
|
|
520
|
+
netid: tokens[0],
|
|
521
|
+
local_addr: tokens[3],
|
|
522
|
+
process_info: tokens[5],
|
|
523
|
+
}
|
|
524
|
+
else # iproute-2.6.32-54
|
|
525
|
+
{
|
|
526
|
+
netid: tokens[0],
|
|
527
|
+
local_addr: tokens[4],
|
|
528
|
+
process_info: tokens[6],
|
|
529
|
+
}
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
def parse_ss_line(line)
|
|
534
|
+
# parsed = line.split(/\s+/, 7)
|
|
535
|
+
parsed = tokenize_ss_line(line)
|
|
536
|
+
|
|
537
|
+
# ss only returns "tcp" and "udp" as the protocol. However, netstat would return
|
|
538
|
+
# "tcp6" and "udp6" as necessary. In order to maintain backward compatibility, we
|
|
539
|
+
# will manually modify the protocol value if the line we're parsing is an IPv6
|
|
540
|
+
# entry.
|
|
541
|
+
process_info = parsed[:process_info]
|
|
542
|
+
protocol = parsed[:netid]
|
|
543
|
+
protocol += '6' if process_info.include?('v6only:1')
|
|
544
|
+
return nil unless ALLOWED_PROTOCOLS.include?(protocol)
|
|
545
|
+
|
|
546
|
+
# parse the Local Address:Port
|
|
547
|
+
# examples:
|
|
548
|
+
# *:22
|
|
549
|
+
# :::22
|
|
550
|
+
# 10.0.2.15:1234
|
|
551
|
+
# ::ffff:10.0.2.15:9300
|
|
552
|
+
# fe80::a00:27ff:fe32:ed09%enp0s3:9200
|
|
553
|
+
parsed_net_address = parsed[:local_addr].match(/(\S+):(\*|\d+)$/)
|
|
554
|
+
return nil if parsed_net_address.nil?
|
|
555
|
+
host = parsed_net_address[1]
|
|
556
|
+
port = parsed_net_address[2]
|
|
557
|
+
return nil if host.nil? && port.nil?
|
|
558
|
+
|
|
559
|
+
# For backward compatibility with the netstat output, ensure the
|
|
560
|
+
# port is stored as an integer
|
|
561
|
+
port = port.to_i
|
|
562
|
+
|
|
563
|
+
# for those "v4-but-listed-in-v6" entries, strip off the
|
|
564
|
+
# leading IPv6 value at the beginning
|
|
565
|
+
# example: ::ffff:10.0.2.15:9200
|
|
566
|
+
host.delete!('::ffff:') if host.start_with?('::ffff:')
|
|
567
|
+
|
|
568
|
+
# if there's an interface name in the local address, which is common for
|
|
569
|
+
# IPv6 listeners, strip that out too.
|
|
570
|
+
# example: fe80::a00:27ff:fe32:ed09%enp0s3
|
|
571
|
+
host = host.split('%').first
|
|
572
|
+
|
|
573
|
+
# if host is "*", replace with "0.0.0.0" to maintain backward compatibility with
|
|
574
|
+
# the netstat-provided data
|
|
575
|
+
host = '0.0.0.0' if host == '*'
|
|
576
|
+
|
|
577
|
+
# in case process list parsing is not successfull
|
|
578
|
+
process = nil
|
|
579
|
+
pid = nil
|
|
580
|
+
|
|
581
|
+
# parse process and pid from the process list
|
|
582
|
+
#
|
|
583
|
+
# remove the "users:((" and "))" parts
|
|
584
|
+
# input: users:((\"nginx\",pid=583,fd=8),(\"nginx\",pid=582,fd=8),(\"nginx\",pid=580,fd=8),(\"nginx\",pid=579,fd=8))
|
|
585
|
+
# res: \"nginx\",pid=583,fd=8),(\"nginx\",pid=582,fd=8),(\"nginx\",pid=580,fd=8),(\"nginx\",pid=579,fd=8
|
|
586
|
+
process_list_match = parsed[:process_info].match(/users:\(\((.+)\)\)/)
|
|
587
|
+
if process_list_match
|
|
588
|
+
# list entires are seperated by "," the braces can also be removed
|
|
589
|
+
# input: \"nginx\",pid=583,fd=8),(\"nginx\",pid=582,fd=8),(\"nginx\",pid=580,fd=8),(\"nginx\",pid=579,fd=8
|
|
590
|
+
# res: ["\"nginx\",pid=583,fd=8", "\"nginx\",pid=582,fd=8", "\"nginx\",pid=580,fd=8", "\"nginx\",pid=579,fd=8"]
|
|
591
|
+
process_list = process_list_match[1].split('),(')
|
|
592
|
+
# To stay backwards compatible with netstat we need to select
|
|
593
|
+
# the last element in the resulting array.
|
|
594
|
+
# res: "\"nginx\",pid=579,fd=8"
|
|
595
|
+
|
|
596
|
+
# parse the process name from the process list
|
|
597
|
+
process_match = process_list.last.match(/^\"(\S+)\"/)
|
|
598
|
+
process = process_match.nil? ? nil : process_match[1]
|
|
599
|
+
|
|
600
|
+
# parse the PID from the process list
|
|
601
|
+
pid_match = process_list.last.match(/pid=(\d+)/)
|
|
602
|
+
pid = pid_match.nil? ? nil : pid_match[1].to_i
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
{
|
|
606
|
+
'port' => port,
|
|
607
|
+
'address' => host,
|
|
608
|
+
'protocol' => protocol,
|
|
609
|
+
'process' => process,
|
|
610
|
+
'pid' => pid,
|
|
611
|
+
}
|
|
612
|
+
end
|
|
365
613
|
end
|
|
366
614
|
|
|
367
615
|
# extracts information from sockstat
|
data/lib/resources/postgres.rb
CHANGED
|
@@ -86,7 +86,7 @@ module Inspec::Resources
|
|
|
86
86
|
if data_dir_loc.nil?
|
|
87
87
|
warn 'Unable to find the PostgreSQL data_dir in expected location(s), please
|
|
88
88
|
execute "psql -t -A -p <port> -h <host> -c "show hba_file";" as the PostgreSQL
|
|
89
|
-
DBA to find the non-
|
|
89
|
+
DBA to find the non-standard data_dir location.'
|
|
90
90
|
end
|
|
91
91
|
data_dir_loc
|
|
92
92
|
end
|
|
@@ -52,7 +52,7 @@ module Inspec::Resources
|
|
|
52
52
|
cmd = inspec.command(psql_cmd)
|
|
53
53
|
out = cmd.stdout + "\n" + cmd.stderr
|
|
54
54
|
if cmd.exit_status != 0 || out =~ /could not connect to .*/ || out.downcase =~ /^error:.*/
|
|
55
|
-
|
|
55
|
+
Lines.new(out, "PostgreSQL query with errors: #{query}")
|
|
56
56
|
else
|
|
57
57
|
Lines.new(cmd.stdout.strip, "PostgreSQL query: #{query}")
|
|
58
58
|
end
|
data/lib/resources/processes.rb
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
# author: Christoph Hartmann
|
|
5
5
|
|
|
6
6
|
require 'utils/filter'
|
|
7
|
+
require 'ostruct'
|
|
7
8
|
|
|
8
9
|
module Inspec::Resources
|
|
9
|
-
class Processes < Inspec.resource(1)
|
|
10
|
+
class Processes < Inspec.resource(1) # rubocop:disable Metrics/ClassLength
|
|
10
11
|
name 'processes'
|
|
11
12
|
desc 'Use the processes InSpec audit resource to test properties for programs that are running on the system.'
|
|
12
13
|
example "
|
|
@@ -15,12 +16,18 @@ module Inspec::Resources
|
|
|
15
16
|
its('users') { should eq ['mysql'] }
|
|
16
17
|
its('states') { should include 'S' }
|
|
17
18
|
end
|
|
19
|
+
|
|
18
20
|
describe processes(/.+/).where { label != 'unconfined' && pid < 1000 } do
|
|
19
21
|
its('users') { should cmp [] }
|
|
20
22
|
end
|
|
23
|
+
|
|
24
|
+
# work with all processes
|
|
25
|
+
describe processes do
|
|
26
|
+
its('entries.length') { should be <= 100 }
|
|
27
|
+
end
|
|
21
28
|
"
|
|
22
29
|
|
|
23
|
-
def initialize(grep)
|
|
30
|
+
def initialize(grep = /.*/)
|
|
24
31
|
@grep = grep
|
|
25
32
|
# turn into a regexp if it isn't one yet
|
|
26
33
|
if grep.class == String
|
|
@@ -80,39 +87,118 @@ module Inspec::Resources
|
|
|
80
87
|
os = inspec.os
|
|
81
88
|
|
|
82
89
|
if os.linux?
|
|
83
|
-
command
|
|
84
|
-
regex = /^([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+(\w{3} \d{2}|\d{2}:\d{2}:\d{2})\s+([^ ]+)\s+([^ ]+)\s+(.*)$/
|
|
90
|
+
command, regex, field_map = ps_configuration_for_linux
|
|
85
91
|
elsif os.windows?
|
|
86
92
|
command = '$Proc = Get-Process -IncludeUserName | Where-Object {$_.Path -ne $null } | Select-Object PriorityClass,Id,CPU,PM,VirtualMemorySize,NPM,SessionId,Responding,StartTime,TotalProcessorTime,UserName,Path | ConvertTo-Csv -NoTypeInformation;$Proc.Replace("""","").Replace("`r`n","`n")'
|
|
87
93
|
# Wanted to use /(?:^|,)([^,]*)/; works on rubular.com not sure why here?
|
|
88
94
|
regex = /^(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+),(.+)$/
|
|
95
|
+
field_map = {
|
|
96
|
+
pid: 2,
|
|
97
|
+
cpu: 3,
|
|
98
|
+
mem: 4,
|
|
99
|
+
vsz: 5,
|
|
100
|
+
rss: 6,
|
|
101
|
+
tty: 7,
|
|
102
|
+
stat: 8,
|
|
103
|
+
start: 9,
|
|
104
|
+
time: 10,
|
|
105
|
+
user: 11,
|
|
106
|
+
command: 12,
|
|
107
|
+
}
|
|
89
108
|
else
|
|
90
109
|
command = 'ps axo pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user,command'
|
|
91
110
|
regex = /^\s*([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+(.*)$/
|
|
111
|
+
field_map = {
|
|
112
|
+
pid: 1,
|
|
113
|
+
cpu: 2,
|
|
114
|
+
mem: 3,
|
|
115
|
+
vsz: 4,
|
|
116
|
+
rss: 5,
|
|
117
|
+
tty: 6,
|
|
118
|
+
stat: 7,
|
|
119
|
+
start: 8,
|
|
120
|
+
time: 9,
|
|
121
|
+
user: 10,
|
|
122
|
+
command: 11,
|
|
123
|
+
}
|
|
92
124
|
end
|
|
93
|
-
build_process_list(command, regex,
|
|
125
|
+
build_process_list(command, regex, field_map)
|
|
94
126
|
end
|
|
95
127
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
128
|
+
def ps_configuration_for_linux
|
|
129
|
+
if busybox_ps?
|
|
130
|
+
command = 'ps -o pid,vsz,rss,tty,stat,time,ruser,args'
|
|
131
|
+
regex = /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/
|
|
132
|
+
field_map = {
|
|
133
|
+
pid: 1,
|
|
134
|
+
vsz: 2,
|
|
135
|
+
rss: 3,
|
|
136
|
+
tty: 4,
|
|
137
|
+
stat: 5,
|
|
138
|
+
time: 6,
|
|
139
|
+
user: 7,
|
|
140
|
+
command: 8,
|
|
141
|
+
}
|
|
142
|
+
else
|
|
143
|
+
command = 'ps axo label,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user:32,command'
|
|
144
|
+
regex = /^(.+?)\s+(\d+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+(\w{3} \d{2}|\d{2}:\d{2}:\d{2})\s+([^ ]+)\s+([^ ]+)\s+(.*)$/
|
|
145
|
+
field_map = {
|
|
146
|
+
label: 1,
|
|
147
|
+
pid: 2,
|
|
148
|
+
cpu: 3,
|
|
149
|
+
mem: 4,
|
|
150
|
+
vsz: 5,
|
|
151
|
+
rss: 6,
|
|
152
|
+
tty: 7,
|
|
153
|
+
stat: 8,
|
|
154
|
+
start: 9,
|
|
155
|
+
time: 10,
|
|
156
|
+
user: 11,
|
|
157
|
+
command: 12,
|
|
158
|
+
}
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
[command, regex, field_map]
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def busybox_ps?
|
|
165
|
+
@busybox_ps ||= inspec.command('ps --help').stderr.include?('BusyBox')
|
|
166
|
+
end
|
|
100
167
|
|
|
101
|
-
def build_process_list(command, regex,
|
|
168
|
+
def build_process_list(command, regex, field_map)
|
|
102
169
|
cmd = inspec.command(command)
|
|
103
170
|
all = cmd.stdout.split("\n")[1..-1]
|
|
104
171
|
return [] if all.nil?
|
|
105
|
-
|
|
106
|
-
|
|
172
|
+
|
|
173
|
+
# map all the process lines into match objects, fetch the available fields,
|
|
174
|
+
# and then build an OpenStruct of the process data for each process
|
|
175
|
+
all.map do |line|
|
|
176
|
+
line = line.match(regex)
|
|
177
|
+
|
|
178
|
+
# skip this line if we couldn't match the regular expression
|
|
179
|
+
next if line.nil?
|
|
180
|
+
|
|
181
|
+
# skip this entry if there's no command for this line
|
|
182
|
+
next if line[field_map[:command]].nil?
|
|
183
|
+
|
|
184
|
+
# build a hash of process data that we'll turn into a struct for FilterTable
|
|
185
|
+
process_data = {}
|
|
186
|
+
[:label, :pid, :cpu, :mem, :vsz, :rss, :tty, :stat, :start, :time, :user, :command].each do |param|
|
|
187
|
+
# not all operating systems support all fields, so skip the field if we don't have it
|
|
188
|
+
process_data[param] = line[field_map[param]] if field_map.key?(param)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# ensure pid, vsz, and rss are integers for backward compatibility
|
|
192
|
+
[:pid, :vsz, :rss].each do |int_param|
|
|
193
|
+
process_data[int_param] = process_data[int_param].to_i if process_data.key?(int_param)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# strip any newlines off the command
|
|
197
|
+
process_data[:command].strip!
|
|
198
|
+
|
|
199
|
+
# return an OpenStruct of the process for future use by FilterTable
|
|
200
|
+
OpenStruct.new(process_data)
|
|
107
201
|
end.compact
|
|
108
|
-
lines.map do |m|
|
|
109
|
-
a = m.to_a[1..-1] # grab all matching groups
|
|
110
|
-
a.unshift(nil) unless os.linux? || os.windows?
|
|
111
|
-
a[1] = a[1].to_i
|
|
112
|
-
a[4] = a[4].to_i
|
|
113
|
-
a[5] = a[5].to_i
|
|
114
|
-
Process.new(*a)
|
|
115
|
-
end
|
|
116
202
|
end
|
|
117
203
|
end
|
|
118
204
|
end
|
|
@@ -106,10 +106,19 @@ module Inspec::Resources
|
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
# returns nil, if not existant or value
|
|
109
|
-
def method_missing(
|
|
109
|
+
def method_missing(*keys)
|
|
110
|
+
# allow the use of array syntax in an `its` block so that users
|
|
111
|
+
# can use it to query for keys with . characters in them
|
|
112
|
+
if keys.is_a?(Array)
|
|
113
|
+
keys.shift if keys[0] == :[]
|
|
114
|
+
key = keys.first
|
|
115
|
+
else
|
|
116
|
+
key = keys
|
|
117
|
+
end
|
|
118
|
+
|
|
110
119
|
# get data
|
|
111
120
|
val = registry_key(@options[:path])
|
|
112
|
-
registry_property_value(val,
|
|
121
|
+
registry_property_value(val, key)
|
|
113
122
|
end
|
|
114
123
|
|
|
115
124
|
def to_s
|
data/lib/resources/service.rb
CHANGED
|
@@ -349,40 +349,21 @@ module Inspec::Resources
|
|
|
349
349
|
description: nil,
|
|
350
350
|
installed: true,
|
|
351
351
|
running: running,
|
|
352
|
-
enabled: info_enabled(
|
|
352
|
+
enabled: info_enabled(service_name),
|
|
353
353
|
type: 'upstart',
|
|
354
354
|
}
|
|
355
355
|
end
|
|
356
356
|
|
|
357
357
|
private
|
|
358
358
|
|
|
359
|
-
def info_enabled(
|
|
359
|
+
def info_enabled(service_name)
|
|
360
360
|
# check if a service is enabled
|
|
361
|
-
|
|
362
|
-
# $ initctl show-config $job | grep -q "^ start on" && echo enabled || echo disabled
|
|
363
|
-
# Ubuntu 10.04 show-config is not supported
|
|
364
|
-
# @see http://manpages.ubuntu.com/manpages/maverick/man8/initctl.8.html
|
|
365
|
-
support_for_show_config = Gem::Version.new('1.3')
|
|
366
|
-
|
|
367
|
-
if version >= support_for_show_config
|
|
368
|
-
config = inspec.command("#{service_ctl} show-config #{service_name}").stdout
|
|
369
|
-
else # use config file as fallback
|
|
370
|
-
config = inspec.file("/etc/init/#{service_name}.conf").content
|
|
371
|
-
end
|
|
361
|
+
config = inspec.file("/etc/init/#{service_name}.conf").content
|
|
372
362
|
|
|
373
363
|
# disregard if the config does not exist
|
|
374
364
|
return nil if config.nil?
|
|
375
|
-
enabled = !config[/^\s*start on/].nil?
|
|
376
|
-
|
|
377
|
-
# implement fallback for Ubuntu 10.04
|
|
378
|
-
if inspec.os[:name] == 'ubuntu' &&
|
|
379
|
-
inspec.os[:release].to_f >= 10.04 &&
|
|
380
|
-
inspec.os[:release].to_f < 12.04 &&
|
|
381
|
-
status.exit_status == 0
|
|
382
|
-
enabled = true
|
|
383
|
-
end
|
|
384
365
|
|
|
385
|
-
|
|
366
|
+
!config.match(/^\s*start on/).nil?
|
|
386
367
|
end
|
|
387
368
|
|
|
388
369
|
def version
|
|
@@ -416,7 +397,7 @@ module Inspec::Resources
|
|
|
416
397
|
# read all enabled services from runlevel
|
|
417
398
|
# on rhel via: 'chkconfig --list', is not installed by default
|
|
418
399
|
# bash: for i in `find /etc/rc*.d -name S*`; do basename $i | sed -r 's/^S[0-9]+//'; done | sort | uniq
|
|
419
|
-
enabled_services_cmd = inspec.command('find /etc/rc*.d /etc/init.d/rc*.d -name S*').stdout
|
|
400
|
+
enabled_services_cmd = inspec.command('find /etc/rc*.d /etc/init.d/rc*.d -name "S*"').stdout
|
|
420
401
|
service_line = %r{rc(?<runlevel>[0-6])\.d/S[^/]*?#{Regexp.escape service_name}$}
|
|
421
402
|
all_services = enabled_services_cmd.split("\n").map { |line|
|
|
422
403
|
service_line.match(line)
|
data/lib/resources/ssh_conf.rb
CHANGED
data/lib/resources/ssl.rb
CHANGED
|
@@ -50,8 +50,6 @@ class SSL < Inspec.resource(1)
|
|
|
50
50
|
@host = inspec.backend.hostname
|
|
51
51
|
elsif inspec.backend.class.to_s == 'Train::Transports::Local::Connection'
|
|
52
52
|
@host = 'localhost'
|
|
53
|
-
else
|
|
54
|
-
raise 'Cannot determine host for SSL test. Please specify it or use a different target.'
|
|
55
53
|
end
|
|
56
54
|
end
|
|
57
55
|
@port = opts[:port] || 443
|
|
@@ -60,11 +58,14 @@ class SSL < Inspec.resource(1)
|
|
|
60
58
|
end
|
|
61
59
|
|
|
62
60
|
filter = FilterTable.create
|
|
61
|
+
filter.add(:enabled?) do |x|
|
|
62
|
+
raise 'Cannot determine host for SSL test. Please specify it or use a different target.' if x.resource.host.nil?
|
|
63
|
+
x.handshake.values.any? { |i| i['success'] }
|
|
64
|
+
end
|
|
63
65
|
filter.add_accessor(:where)
|
|
64
66
|
.add_accessor(:entries)
|
|
65
67
|
.add(:ciphers, field: 'cipher')
|
|
66
68
|
.add(:protocols, field: 'protocol')
|
|
67
|
-
.add(:enabled?) { |x| x.handshake.values.any? { |i| i['success'] } }
|
|
68
69
|
.add(:handshake) { |x|
|
|
69
70
|
groups = x.entries.group_by(&:protocol)
|
|
70
71
|
res = Parallel.map(groups, in_threads: 8) do |proto, e|
|
data/lib/resources/toml.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
# author: Nolan Davidson
|
|
3
3
|
|
|
4
|
-
require '
|
|
4
|
+
require 'tomlrb'
|
|
5
5
|
|
|
6
6
|
module Inspec::Resources
|
|
7
7
|
class TomlConfig < JsonConfig
|
|
@@ -16,7 +16,7 @@ module Inspec::Resources
|
|
|
16
16
|
"
|
|
17
17
|
|
|
18
18
|
def parse(content)
|
|
19
|
-
|
|
19
|
+
Tomlrb.parse(content)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def to_s
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# author: Matt Ray
|
|
3
|
+
|
|
4
|
+
module Inspec::Resources
|
|
5
|
+
class WindowsHotfix < Inspec.resource(1)
|
|
6
|
+
name 'windows_hotfix'
|
|
7
|
+
desc 'Use the windows_hotfix InSpec audit resource to test if the hotfix has been installed on the Windows system.'
|
|
8
|
+
example "
|
|
9
|
+
describe windows_hotfix('KB4012212') do
|
|
10
|
+
it { should be_installed }
|
|
11
|
+
end
|
|
12
|
+
"
|
|
13
|
+
|
|
14
|
+
attr_accessor :content
|
|
15
|
+
|
|
16
|
+
def initialize(hotfix_id = nil)
|
|
17
|
+
@id = hotfix_id.upcase
|
|
18
|
+
@content = nil
|
|
19
|
+
os = inspec.os
|
|
20
|
+
return skip_resource 'The `windows_hotfix` resource is not a feature of your OS.' unless os.windows?
|
|
21
|
+
query = "get-hotfix -id #{@id}"
|
|
22
|
+
cmd = inspec.powershell(query)
|
|
23
|
+
@content = cmd.stdout
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_s
|
|
27
|
+
"Windows Hotfix #{@id}"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def installed?
|
|
31
|
+
return false if @content.nil?
|
|
32
|
+
@content.include?(@id)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/resources/xinetd.rb
CHANGED
|
@@ -53,15 +53,14 @@ module Inspec::Resources
|
|
|
53
53
|
return @contents[path] if @contents.key?(path)
|
|
54
54
|
file = inspec.file(path)
|
|
55
55
|
if !file.file?
|
|
56
|
-
|
|
56
|
+
raise Inspec::Exceptions::ResourceSkipped, "Can't find file: #{path}"
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return skip_resource "Can't read file \"#{path}\""
|
|
59
|
+
if file.content.nil? || file.content.empty?
|
|
60
|
+
raise Inspec::Exceptions::ResourceSkipped, "Can't read file: #{path}"
|
|
62
61
|
end
|
|
63
62
|
|
|
64
|
-
@contents[path]
|
|
63
|
+
@contents[path] = file.content
|
|
65
64
|
end
|
|
66
65
|
|
|
67
66
|
def read_params
|
|
@@ -69,7 +68,6 @@ module Inspec::Resources
|
|
|
69
68
|
flat_params = parse_xinetd(read_content)
|
|
70
69
|
# we need to map service data in order to use it with filtertable
|
|
71
70
|
params = { 'services' => {} }
|
|
72
|
-
|
|
73
71
|
# map services that were defined and map it to the service hash
|
|
74
72
|
flat_params.each do |k, v|
|
|
75
73
|
name = k[/^service (.+)$/, 1]
|