ardecy 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/README.md +6 -2
- data/lib/ardecy.rb +5 -4
- data/lib/ardecy/harden.rb +55 -59
- data/lib/ardecy/harden/cmdline.rb +222 -0
- data/lib/ardecy/harden/modules.rb +290 -0
- data/lib/ardecy/harden/mountpoint.rb +149 -0
- data/lib/ardecy/harden/perms.rb +110 -0
- data/lib/ardecy/harden/sysctl.rb +7 -5
- data/lib/ardecy/harden/sysctl/kernel.rb +67 -58
- data/lib/ardecy/harden/sysctl/network.rb +52 -66
- data/lib/ardecy/options.rb +12 -0
- data/lib/ardecy/privacy.rb +2 -0
- data/lib/ardecy/version.rb +1 -1
- data/lib/display.rb +13 -3
- data/lib/nito.rb +30 -0
- metadata +7 -2
- metadata.gz.sig +0 -0
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'display'
|
4
|
+
require 'nito'
|
5
|
+
|
6
|
+
module Ardecy
|
7
|
+
module Harden
|
8
|
+
module Mountpoint
|
9
|
+
def self.exec(args)
|
10
|
+
ProcHidepid.new(args).x
|
11
|
+
puts " ===> Mountpoint Corrected." if args[:fix]
|
12
|
+
end
|
13
|
+
|
14
|
+
class MountInc
|
15
|
+
include Display
|
16
|
+
include NiTo
|
17
|
+
|
18
|
+
def initialize(args)
|
19
|
+
@res = 'FAIL'
|
20
|
+
@args = args
|
21
|
+
@tab = 2
|
22
|
+
end
|
23
|
+
|
24
|
+
def x
|
25
|
+
scan
|
26
|
+
add_group
|
27
|
+
build_args
|
28
|
+
fix
|
29
|
+
systemd_case
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_group
|
33
|
+
return unless @args[:fix] && @group
|
34
|
+
|
35
|
+
has_group = group_search
|
36
|
+
unless has_group
|
37
|
+
if File.exists? '/usr/sbin/groupadd'
|
38
|
+
puts " => Group #{@group} added." if system("/usr/sbin/groupadd #{@group}")
|
39
|
+
elsif File.exists? '/usr/bin/groupadd'
|
40
|
+
puts " => Group #{@group} added." if system("/usr/bin/groupadd #{@group}")
|
41
|
+
else
|
42
|
+
puts '[-] Can\'t find command groupadd'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def group_search
|
48
|
+
if File.readable? '/etc/group'
|
49
|
+
etc_group = File.readlines('/etc/group')
|
50
|
+
etc_group.each { |l| return true if l =~ /#{@group}/ }
|
51
|
+
else
|
52
|
+
puts " [-] /etc/group is not readable"
|
53
|
+
end
|
54
|
+
false
|
55
|
+
end
|
56
|
+
|
57
|
+
def scan
|
58
|
+
return unless mount_match('/proc/mounts')
|
59
|
+
|
60
|
+
print " - Checking #{@name} contain " + @ensure.join(',') if @args[:audit]
|
61
|
+
res_a = []
|
62
|
+
@ensure.each do |v|
|
63
|
+
o = v.split('=')
|
64
|
+
res_a << true if @val =~ /#{o[0]}=[a-z0-9]+/
|
65
|
+
end
|
66
|
+
@res = 'OK' if res_a.length == @ensure.length
|
67
|
+
|
68
|
+
@tab ? result(@res, @tab) : result(@res) if @args[:audit]
|
69
|
+
end
|
70
|
+
|
71
|
+
def build_args
|
72
|
+
return unless @args[:fix]
|
73
|
+
return if @res =~ /OK/
|
74
|
+
|
75
|
+
v = @val.split ' '
|
76
|
+
@ensure.each do |e|
|
77
|
+
o = e.split('=')
|
78
|
+
v[3] += ",#{e}" unless v[3] =~ /#{o[0]}=[a-z0-9]+/
|
79
|
+
end
|
80
|
+
@new = v.join(' ')
|
81
|
+
end
|
82
|
+
|
83
|
+
def fix
|
84
|
+
return unless @args[:fix]
|
85
|
+
return if @res =~ /OK/
|
86
|
+
|
87
|
+
if mount_match('/etc/fstab')
|
88
|
+
edit_fstab
|
89
|
+
else
|
90
|
+
File.write('/etc/fstab', "\n#{@new}\n", mode: 'a')
|
91
|
+
end
|
92
|
+
|
93
|
+
puts "old -> " + @val
|
94
|
+
puts "new -> " + @new
|
95
|
+
puts
|
96
|
+
end
|
97
|
+
|
98
|
+
def mount_match(file)
|
99
|
+
File.readlines(file).each do |l|
|
100
|
+
if l =~ /^#{@name}/
|
101
|
+
@val = l
|
102
|
+
return true
|
103
|
+
end
|
104
|
+
end
|
105
|
+
false
|
106
|
+
end
|
107
|
+
|
108
|
+
def edit_fstab
|
109
|
+
sed(/^#{@name}/, @new, '/etc/fstab')
|
110
|
+
end
|
111
|
+
|
112
|
+
def systemd_case
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class ProcHidepid < Mountpoint::MountInc
|
117
|
+
def initialize(args)
|
118
|
+
super
|
119
|
+
@name = 'proc'
|
120
|
+
@ensure = [ 'hidepid=2', 'gid=proc' ]
|
121
|
+
@group = 'proc'
|
122
|
+
end
|
123
|
+
|
124
|
+
# man logind.conf check under:
|
125
|
+
# > /etc/systemd/logind.conf.d/*.conf
|
126
|
+
# > /run/systemd/logind.conf.d/*.conf
|
127
|
+
# > /usr/lib/systemd/logind.conf.d/*.conf
|
128
|
+
def systemd_case
|
129
|
+
return unless @args[:fix]
|
130
|
+
|
131
|
+
if File.exist? '/etc/systemd/logind.conf'
|
132
|
+
create_content '/etc/systemd/logind.conf.d'
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def create_content(in_dir)
|
137
|
+
content = [
|
138
|
+
'[Service]',
|
139
|
+
'SupplementaryGroups=proc',
|
140
|
+
''
|
141
|
+
]
|
142
|
+
Dir.mkdir in_dir, 0700 unless Dir.exists? in_dir
|
143
|
+
File.write("#{in_dir}/hidepid.conf", content.join("\n"), mode: 'w')
|
144
|
+
puts " > Creating file #{in_dir}/hidepid.conf"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'display'
|
4
|
+
|
5
|
+
module Ardecy
|
6
|
+
module Harden
|
7
|
+
module Perms
|
8
|
+
class DirCheck
|
9
|
+
include Display
|
10
|
+
|
11
|
+
def initialize(args)
|
12
|
+
@args = args
|
13
|
+
@res = 'OK'
|
14
|
+
@exp = 0755
|
15
|
+
@tab = 2
|
16
|
+
end
|
17
|
+
|
18
|
+
def x
|
19
|
+
scan
|
20
|
+
fix
|
21
|
+
end
|
22
|
+
|
23
|
+
def scan
|
24
|
+
return unless Dir.exist? @name
|
25
|
+
|
26
|
+
perm = File.stat(@name).mode & 07777
|
27
|
+
@line = "Permission on #{@name}"
|
28
|
+
|
29
|
+
perm_show(@line, @exp) if @args[:audit]
|
30
|
+
@res = 'FAIL' if perm > @exp
|
31
|
+
@tab ? result(@res, @tab) : result(@res) if @args[:audit]
|
32
|
+
end
|
33
|
+
|
34
|
+
def fix
|
35
|
+
return unless @args[:fix]
|
36
|
+
|
37
|
+
File.chmod @exp, @name unless @res =~ /OK/
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module Directory
|
42
|
+
def self.exec(args)
|
43
|
+
Directory::Home.new(args).x
|
44
|
+
Directory::CronDaily.new(args).x
|
45
|
+
Directory::Boot.new(args).x
|
46
|
+
Directory::UsrSrc.new(args).x
|
47
|
+
Directory::LibMod.new(args).x
|
48
|
+
Directory::UsrLibMod.new(args).x
|
49
|
+
puts " ===> Permission Corrected." if args[:fix]
|
50
|
+
end
|
51
|
+
|
52
|
+
class Home < Perms::DirCheck
|
53
|
+
def initialize(args)
|
54
|
+
super
|
55
|
+
@exp = 0700
|
56
|
+
end
|
57
|
+
|
58
|
+
def x
|
59
|
+
Dir.glob('/home/*').each { |d|
|
60
|
+
@name = d
|
61
|
+
scan
|
62
|
+
fix
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class CronDaily < Perms::DirCheck
|
68
|
+
def initialize(args)
|
69
|
+
super
|
70
|
+
@name = '/etc/cron.daily'
|
71
|
+
@exp = 0700
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Boot < Perms::DirCheck
|
76
|
+
def initialize(args)
|
77
|
+
super
|
78
|
+
@name = '/boot'
|
79
|
+
@exp = 0700
|
80
|
+
@tab = 3
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class UsrSrc < Perms::DirCheck
|
85
|
+
def initialize(args)
|
86
|
+
super
|
87
|
+
@name = '/usr/src'
|
88
|
+
@exp = 0700
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class LibMod < Perms::DirCheck
|
93
|
+
def initialize(args)
|
94
|
+
super
|
95
|
+
@name = '/lib/modules'
|
96
|
+
@exp = 0700
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class UsrLibMod < Perms::DirCheck
|
101
|
+
def initialize(args)
|
102
|
+
super
|
103
|
+
@name = '/usr/lib/modules'
|
104
|
+
@exp = 0700
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/lib/ardecy/harden/sysctl.rb
CHANGED
@@ -11,6 +11,12 @@ module Ardecy
|
|
11
11
|
class SysKern
|
12
12
|
include Display
|
13
13
|
|
14
|
+
def initialize(args)
|
15
|
+
@res = 'FALSE'
|
16
|
+
@args = args
|
17
|
+
@exp = '0'
|
18
|
+
end
|
19
|
+
|
14
20
|
def scan
|
15
21
|
kernel_show(@line, @exp) if @args[:audit]
|
16
22
|
if File.exist? @file
|
@@ -23,11 +29,7 @@ module Ardecy
|
|
23
29
|
else
|
24
30
|
@res = 'NO FOUND'
|
25
31
|
end
|
26
|
-
if @
|
27
|
-
kernel_res(@res, @tab) if @args[:audit]
|
28
|
-
elsif @args[:audit]
|
29
|
-
kernel_res(@res)
|
30
|
-
end
|
32
|
+
@tab ? result(@res, @tab) : result(@res) if @args[:audit]
|
31
33
|
end
|
32
34
|
|
33
35
|
def fix
|
@@ -4,205 +4,214 @@ module Ardecy
|
|
4
4
|
module Harden
|
5
5
|
module Sysctl
|
6
6
|
module Kernel
|
7
|
+
def self.exec(args)
|
8
|
+
Kernel::KPointer.new(args).x
|
9
|
+
Kernel::Dmesg.new(args).x
|
10
|
+
Kernel::Printk.new(args).x
|
11
|
+
Kernel::BpfDisabled.new(args).x
|
12
|
+
Kernel::BpfJitHarden.new(args).x
|
13
|
+
Kernel::LdiskAutoload.new(args).x
|
14
|
+
Kernel::UserFaultFd.new(args).x
|
15
|
+
Kernel::KExecLoadDisabled.new(args).x
|
16
|
+
Kernel::SysRQ.new(args).x
|
17
|
+
Kernel::UsernsClone.new(args).x
|
18
|
+
Kernel::MaxUserNameSpace.new(args).x
|
19
|
+
Kernel::PerfEventParanoid.new(args).x
|
20
|
+
Kernel::YamaPtrace.new(args).x
|
21
|
+
Kernel::VmMmapRndBits.new(args).x
|
22
|
+
Kernel::VmMmapRndCompatBits.new(args).x
|
23
|
+
Kernel::FsProtectedSymlinks.new(args).x
|
24
|
+
Kernel::FsProtectedHardlinks.new(args).x
|
25
|
+
Kernel::FsProtectedFifos.new(args).x
|
26
|
+
Kernel::FsProtectedRegular.new(args).x
|
27
|
+
Kernel::FsSuidDumpable.new(args).x
|
28
|
+
end
|
29
|
+
|
7
30
|
class KPointer < Sysctl::SysKern
|
8
31
|
def initialize(args)
|
9
32
|
@file = '/proc/sys/kernel/kptr_restrict'
|
10
|
-
@exp = '2'
|
11
|
-
@res = 'FALSE'
|
12
33
|
@line = 'kernel.kptr_restrict'
|
13
|
-
|
34
|
+
super
|
35
|
+
@exp = '2'
|
14
36
|
end
|
15
37
|
end
|
16
38
|
|
17
39
|
class Dmesg < Sysctl::SysKern
|
18
40
|
def initialize(args)
|
19
41
|
@file = '/proc/sys/kernel/dmesg_restrict'
|
20
|
-
@exp = '1'
|
21
|
-
@res = 'FALSE'
|
22
42
|
@line = 'kernel.dmesg_restrict'
|
23
|
-
|
43
|
+
super
|
44
|
+
@exp = '1'
|
24
45
|
end
|
25
46
|
end
|
26
47
|
|
27
48
|
class Printk < Sysctl::SysKern
|
28
49
|
def initialize(args)
|
29
50
|
@file = '/proc/sys/kernel/printk'
|
30
|
-
@exp = '3 3 3 3'
|
31
|
-
@res = 'FALSE'
|
32
51
|
@line = 'kernel.printk'
|
33
|
-
@
|
52
|
+
@tab = 6
|
53
|
+
super
|
54
|
+
@exp = '3 3 3 3'
|
34
55
|
end
|
35
56
|
|
36
57
|
def scan
|
37
58
|
kernel_show(@line, @exp) if @args[:audit]
|
38
59
|
value = File.read(@file).chomp
|
39
60
|
@res = 'OK' if value =~ /3\s+3\s+3\s+3/
|
40
|
-
|
61
|
+
result(@res) if @args[:audit]
|
41
62
|
end
|
42
63
|
end
|
43
64
|
|
44
65
|
class BpfDisabled < Sysctl::SysKern
|
45
66
|
def initialize(args)
|
46
67
|
@file = '/proc/sys/kernel/unprivileged_bpf_disabled'
|
47
|
-
@exp = '1'
|
48
|
-
@res = 'FALSE'
|
49
68
|
@line = 'kernel.unprivileged_bpf_disabled'
|
50
69
|
@tab = 2
|
51
|
-
|
70
|
+
super
|
71
|
+
@exp = '1'
|
52
72
|
end
|
53
73
|
end
|
54
74
|
|
55
75
|
class BpfJitHarden < Sysctl::SysKern
|
56
76
|
def initialize(args)
|
57
77
|
@file = '/proc/sys/net/core/bpf_jit_harden'
|
58
|
-
@exp = '2'
|
59
|
-
@res = 'FALSE'
|
60
78
|
@line = 'net.core.bpf_jit_harden'
|
61
|
-
|
79
|
+
super
|
80
|
+
@exp = '2'
|
62
81
|
end
|
63
82
|
end
|
64
83
|
|
65
84
|
class LdiskAutoload < Sysctl::SysKern
|
66
85
|
def initialize(args)
|
67
86
|
@file = '/proc/sys/dev/tty/ldisc_autoload'
|
68
|
-
@exp = '0'
|
69
|
-
@res = 'FALSE'
|
70
87
|
@line = 'dev.tty.ldisc_autoload'
|
71
|
-
|
88
|
+
super
|
72
89
|
end
|
73
90
|
end
|
74
91
|
|
75
92
|
class UserFaultFd < Sysctl::SysKern
|
76
93
|
def initialize(args)
|
77
94
|
@file = '/proc/sys/vm/unprivileged_userfaultfd'
|
78
|
-
@exp = '0'
|
79
|
-
@res = 'FALSE'
|
80
95
|
@line = 'vm.unprivileged_userfaultfd'
|
81
|
-
@args = args
|
82
96
|
@tab = 2
|
97
|
+
super
|
83
98
|
end
|
84
99
|
end
|
85
100
|
|
86
101
|
class KExecLoadDisabled < Sysctl::SysKern
|
87
102
|
def initialize(args)
|
88
103
|
@file = '/proc/sys/kernel/kexec_load_disabled'
|
89
|
-
@exp = '1'
|
90
|
-
@res = 'FALSE'
|
91
104
|
@line = 'kernel.kexec_load_disabled'
|
92
|
-
|
105
|
+
super
|
106
|
+
@exp = '1'
|
93
107
|
end
|
94
108
|
end
|
95
109
|
|
96
110
|
class SysRQ < Sysctl::SysKern
|
97
111
|
def initialize(args)
|
98
112
|
@file = '/proc/sys/kernel/sysrq'
|
99
|
-
@exp = '0'
|
100
|
-
@res = 'FALSE'
|
101
113
|
@line = 'kernel.sysrq'
|
102
|
-
@args = args
|
103
114
|
@tab = 4
|
115
|
+
super
|
104
116
|
end
|
105
117
|
end
|
106
118
|
|
107
119
|
class UsernsClone < Sysctl::SysKern
|
108
120
|
def initialize(args)
|
109
121
|
@file = '/proc/sys/kernel/unprivileged_userns_clone'
|
110
|
-
@exp = '0'
|
111
|
-
@res = 'FALSE'
|
112
122
|
@line = 'unprivileged_userns_clone'
|
113
|
-
|
123
|
+
super
|
114
124
|
end
|
115
125
|
end
|
116
126
|
|
117
127
|
class MaxUserNameSpace < Sysctl::SysKern
|
118
128
|
def initialize(args)
|
119
129
|
@file = '/proc/sys/user/max_user_namespaces'
|
120
|
-
@exp = '0'
|
121
|
-
@res = 'FALSE'
|
122
130
|
@line = 'user.max_user_namespaces'
|
123
|
-
|
131
|
+
super
|
124
132
|
end
|
125
133
|
end
|
126
134
|
|
127
135
|
class PerfEventParanoid < Sysctl::SysKern
|
128
136
|
def initialize(args)
|
129
137
|
@file = '/proc/sys/kernel/perf_event_paranoid'
|
130
|
-
@exp = '3'
|
131
|
-
@res = 'FALSE'
|
132
138
|
@line = 'kernel.perf_event_paranoid'
|
133
|
-
|
139
|
+
super
|
140
|
+
@exp = '3'
|
134
141
|
end
|
135
142
|
end
|
136
143
|
|
137
144
|
class YamaPtrace < Sysctl::SysKern
|
138
145
|
def initialize(args)
|
139
146
|
@file = '/proc/sys/kernel/yama/ptrace_scope'
|
140
|
-
@exp = '2'
|
141
|
-
@res = 'FALSE'
|
142
147
|
@line = 'kernel.yama.ptrace_scope'
|
143
|
-
|
148
|
+
super
|
149
|
+
@exp = '2'
|
144
150
|
end
|
145
151
|
end
|
146
152
|
|
147
153
|
class VmMmapRndBits < Sysctl::SysKern
|
148
154
|
def initialize(args)
|
149
155
|
@file = '/proc/sys/vm/mmap_rnd_bits'
|
150
|
-
@exp = '32'
|
151
|
-
@res = 'FALSE'
|
152
156
|
@line = 'vm.mmap_rnd_bits'
|
153
|
-
@args = args
|
154
157
|
@tab = 4
|
158
|
+
super
|
159
|
+
@exp = '32'
|
155
160
|
end
|
156
161
|
end
|
157
162
|
|
158
163
|
class VmMmapRndCompatBits < Sysctl::SysKern
|
159
164
|
def initialize(args)
|
160
165
|
@file = '/proc/sys/vm/mmap_rnd_compat_bits'
|
161
|
-
@exp = '16'
|
162
|
-
@res = 'FALSE'
|
163
166
|
@line = 'vm.mmap_rnd_compat_bits'
|
164
|
-
|
167
|
+
super
|
168
|
+
@exp = '16'
|
165
169
|
end
|
166
170
|
end
|
167
171
|
|
168
172
|
class FsProtectedSymlinks < Sysctl::SysKern
|
169
173
|
def initialize(args)
|
170
174
|
@file = '/proc/sys/fs/protected_symlinks'
|
171
|
-
@exp = '1'
|
172
|
-
@res = 'FALSE'
|
173
175
|
@line = 'fs.protected_symlinks'
|
174
|
-
|
176
|
+
super
|
177
|
+
@exp = '1'
|
175
178
|
end
|
176
179
|
end
|
177
180
|
|
178
181
|
class FsProtectedHardlinks < Sysctl::SysKern
|
179
182
|
def initialize(args)
|
180
183
|
@file = '/proc/sys/fs/protected_hardlinks'
|
181
|
-
@exp = '1'
|
182
|
-
@res = 'FALSE'
|
183
184
|
@line = 'fs.protected_hardlinks'
|
184
|
-
|
185
|
+
super
|
186
|
+
@exp = '1'
|
185
187
|
end
|
186
188
|
end
|
187
189
|
|
188
190
|
class FsProtectedFifos < Sysctl::SysKern
|
189
191
|
def initialize(args)
|
190
192
|
@file = '/proc/sys/fs/protected_fifos'
|
191
|
-
@exp = '2'
|
192
|
-
@res = 'FALSE'
|
193
193
|
@line = 'fs.protected_fifos'
|
194
|
-
@args = args
|
195
194
|
@tab = 4
|
195
|
+
super
|
196
|
+
@exp = '2'
|
196
197
|
end
|
197
198
|
end
|
198
199
|
|
199
200
|
class FsProtectedRegular < Sysctl::SysKern
|
200
201
|
def initialize(args)
|
201
202
|
@file = '/proc/sys/fs/protected_regular'
|
202
|
-
@exp = '2'
|
203
|
-
@res = 'FALSE'
|
204
203
|
@line = 'fs.protected_regular'
|
205
|
-
|
204
|
+
super
|
205
|
+
@exp = '2'
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
class FsSuidDumpable < Sysctl::SysKern
|
210
|
+
def initialize(args)
|
211
|
+
@file = '/proc/sys/fs/suid_dumpable'
|
212
|
+
@line = 'fs.suid_dumpable'
|
213
|
+
super
|
214
|
+
@tab = 4
|
206
215
|
end
|
207
216
|
end
|
208
217
|
end
|