console1984 0.1.6 → 0.1.7
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/README.md +14 -1
- data/config/command_protections.yml +17 -0
- data/lib/console1984/command_executor.rb +94 -0
- data/lib/console1984/command_validator/forbidden_constant_reference_validation.rb +31 -0
- data/lib/console1984/command_validator/forbidden_reopening_validation.rb +29 -0
- data/lib/console1984/command_validator/parsed_command.rb +64 -0
- data/lib/console1984/command_validator/suspicious_terms_validation.rb +22 -0
- data/lib/console1984/command_validator.rb +71 -0
- data/lib/console1984/config.rb +9 -5
- data/lib/console1984/engine.rb +2 -0
- data/lib/console1984/errors.rb +10 -1
- data/lib/console1984/{protected_auditable_tables.rb → ext/active_record/protected_auditable_tables.rb} +2 -4
- data/lib/console1984/ext/core/object.rb +42 -0
- data/lib/console1984/ext/irb/commands.rb +16 -0
- data/lib/console1984/{protected_context.rb → ext/irb/context.rb} +5 -5
- data/lib/console1984/{protected_tcp_socket.rb → ext/socket/tcp_socket.rb} +3 -3
- data/lib/console1984/freezeable.rb +15 -5
- data/lib/console1984/{supervisor/input_output.rb → input_output.rb} +8 -2
- data/lib/console1984/messages.rb +0 -10
- data/lib/console1984/shield/modes/protected.rb +27 -0
- data/lib/console1984/shield/modes/unprotected.rb +8 -0
- data/lib/console1984/shield/modes.rb +60 -0
- data/lib/console1984/shield.rb +86 -0
- data/lib/console1984/supervisor.rb +24 -33
- data/lib/console1984/version.rb +1 -1
- data/lib/console1984.rb +36 -17
- metadata +61 -14
- data/config/routes.rb +0 -9
- data/lib/console1984/commands.rb +0 -16
- data/lib/console1984/protected_object.rb +0 -15
- data/lib/console1984/supervisor/accesses/protected.rb +0 -12
- data/lib/console1984/supervisor/accesses/unprotected.rb +0 -7
- data/lib/console1984/supervisor/accesses.rb +0 -41
- data/lib/console1984/supervisor/executor.rb +0 -65
- data/lib/console1984/supervisor/protector.rb +0 -55
data/lib/console1984/version.rb
CHANGED
data/lib/console1984.rb
CHANGED
@@ -4,38 +4,57 @@ require "zeitwerk"
|
|
4
4
|
class_loader = Zeitwerk::Loader.for_gem
|
5
5
|
class_loader.setup
|
6
6
|
|
7
|
+
# = Console 1984
|
8
|
+
#
|
9
|
+
# Console1984 is an IRB-based Rails console extension that does
|
10
|
+
# three things:
|
11
|
+
#
|
12
|
+
# * Record console sessions with their user, reason and commands.
|
13
|
+
# * Protect encrypted data by showing the ciphertexts when you visualize it.
|
14
|
+
# * Protect access to external systems that contain sensitive information (such as Redis
|
15
|
+
# or Elasticsearch).
|
16
|
+
#
|
17
|
+
# == Session logging
|
18
|
+
#
|
19
|
+
# The console will record the session, its user and the commands entered. The logic to
|
20
|
+
# persist sessions is handled by the configured session logger, which is
|
21
|
+
# Console1984::SessionsLogger::Database by default.
|
22
|
+
#
|
23
|
+
# == Execution of commands
|
24
|
+
#
|
25
|
+
# The console will work in two modes:
|
26
|
+
#
|
27
|
+
# * Protected: It won't show encrypted information (it will show the ciphertexts instead)
|
28
|
+
# and it won't allow connections to protected urls.
|
29
|
+
# * Unprotected: it allows access to encrypted information and protected urls. The commands
|
30
|
+
# executed in this mode as flagged as sensitive.
|
31
|
+
#
|
32
|
+
# Console1984::CommandExecutor handles the execution of commands applying the corresponding
|
33
|
+
# protection mechanisms.´
|
34
|
+
#
|
35
|
+
# == Internal tampering prevention
|
36
|
+
#
|
37
|
+
# Finally, console1984 includes protection mechanisms against internal tampering while using
|
38
|
+
# the console. For example, to prevent the user from deleting audit trails. See
|
39
|
+
# Console1984::Shield and Console1984::CommandValidator to learn more.
|
7
40
|
module Console1984
|
8
41
|
include Messages, Freezeable
|
9
42
|
|
10
43
|
mattr_accessor :supervisor, default: Supervisor.new
|
44
|
+
|
11
45
|
mattr_reader :config, default: Config.new
|
12
|
-
mattr_accessor :class_loader
|
13
46
|
|
14
|
-
|
47
|
+
mattr_accessor :class_loader
|
15
48
|
|
16
49
|
class << self
|
17
50
|
Config::PROPERTIES.each do |property|
|
18
51
|
delegate property, to: :config
|
19
52
|
end
|
20
53
|
|
54
|
+
# Returns whether the console is currently running in protected mode or not.
|
21
55
|
def running_protected_environment?
|
22
56
|
protected_environments.collect(&:to_sym).include?(Rails.env.to_sym)
|
23
57
|
end
|
24
|
-
|
25
|
-
def protecting(&block)
|
26
|
-
protecting_connections do
|
27
|
-
ActiveRecord::Encryption.protecting_encrypted_data(&block)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
def protecting_connections
|
33
|
-
old_currently_protected_urls = self.currently_protected_urls
|
34
|
-
self.currently_protected_urls = protected_urls
|
35
|
-
yield
|
36
|
-
ensure
|
37
|
-
self.currently_protected_urls = old_currently_protected_urls
|
38
|
-
end
|
39
58
|
end
|
40
59
|
end
|
41
60
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: console1984
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jorge Manrubia
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-09-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: parser
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: benchmark-ips
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,6 +150,34 @@ dependencies:
|
|
136
150
|
- - ">="
|
137
151
|
- !ruby/object:Gem::Version
|
138
152
|
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: pg
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: mysql2
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
139
181
|
description:
|
140
182
|
email:
|
141
183
|
- jorge@basecamp.com
|
@@ -153,27 +195,32 @@ files:
|
|
153
195
|
- app/models/console1984/session.rb
|
154
196
|
- app/models/console1984/session/incineratable.rb
|
155
197
|
- app/models/console1984/user.rb
|
156
|
-
- config/
|
198
|
+
- config/command_protections.yml
|
157
199
|
- db/migrate/20210517203931_create_console1984_tables.rb
|
158
200
|
- lib/console1984.rb
|
159
|
-
- lib/console1984/
|
201
|
+
- lib/console1984/command_executor.rb
|
202
|
+
- lib/console1984/command_validator.rb
|
203
|
+
- lib/console1984/command_validator/forbidden_constant_reference_validation.rb
|
204
|
+
- lib/console1984/command_validator/forbidden_reopening_validation.rb
|
205
|
+
- lib/console1984/command_validator/parsed_command.rb
|
206
|
+
- lib/console1984/command_validator/suspicious_terms_validation.rb
|
160
207
|
- lib/console1984/config.rb
|
161
208
|
- lib/console1984/engine.rb
|
162
209
|
- lib/console1984/errors.rb
|
210
|
+
- lib/console1984/ext/active_record/protected_auditable_tables.rb
|
211
|
+
- lib/console1984/ext/core/object.rb
|
212
|
+
- lib/console1984/ext/irb/commands.rb
|
213
|
+
- lib/console1984/ext/irb/context.rb
|
214
|
+
- lib/console1984/ext/socket/tcp_socket.rb
|
163
215
|
- lib/console1984/freezeable.rb
|
216
|
+
- lib/console1984/input_output.rb
|
164
217
|
- lib/console1984/messages.rb
|
165
|
-
- lib/console1984/protected_auditable_tables.rb
|
166
|
-
- lib/console1984/protected_context.rb
|
167
|
-
- lib/console1984/protected_object.rb
|
168
|
-
- lib/console1984/protected_tcp_socket.rb
|
169
218
|
- lib/console1984/sessions_logger/database.rb
|
219
|
+
- lib/console1984/shield.rb
|
220
|
+
- lib/console1984/shield/modes.rb
|
221
|
+
- lib/console1984/shield/modes/protected.rb
|
222
|
+
- lib/console1984/shield/modes/unprotected.rb
|
170
223
|
- lib/console1984/supervisor.rb
|
171
|
-
- lib/console1984/supervisor/accesses.rb
|
172
|
-
- lib/console1984/supervisor/accesses/protected.rb
|
173
|
-
- lib/console1984/supervisor/accesses/unprotected.rb
|
174
|
-
- lib/console1984/supervisor/executor.rb
|
175
|
-
- lib/console1984/supervisor/input_output.rb
|
176
|
-
- lib/console1984/supervisor/protector.rb
|
177
224
|
- lib/console1984/username/env_resolver.rb
|
178
225
|
- lib/console1984/version.rb
|
179
226
|
- test/fixtures/console1984/commands.yml
|
data/config/routes.rb
DELETED
data/lib/console1984/commands.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
module Console1984::Commands
|
2
|
-
include Console1984::Freezeable
|
3
|
-
|
4
|
-
def decrypt!
|
5
|
-
supervisor.enable_access_to_encrypted_content
|
6
|
-
end
|
7
|
-
|
8
|
-
def encrypt!
|
9
|
-
supervisor.disable_access_to_encrypted_content
|
10
|
-
end
|
11
|
-
|
12
|
-
private
|
13
|
-
def supervisor
|
14
|
-
Console1984.supervisor
|
15
|
-
end
|
16
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module Console1984::ProtectedObject
|
2
|
-
extend ActiveSupport::Concern
|
3
|
-
|
4
|
-
include Console1984::Freezeable
|
5
|
-
|
6
|
-
class_methods do
|
7
|
-
def const_get(*arguments)
|
8
|
-
if Console1984.supervisor.executing_user_command? && arguments.first.to_s =~ /Console1984|ActiveRecord/
|
9
|
-
raise Console1984::Errors::ForbiddenCommand
|
10
|
-
else
|
11
|
-
super
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
class Console1984::Supervisor::Accesses::Protected
|
2
|
-
include Console1984::Freezeable
|
3
|
-
|
4
|
-
def execute(&block)
|
5
|
-
Console1984.protecting(&block)
|
6
|
-
end
|
7
|
-
|
8
|
-
private
|
9
|
-
def null_encryptor
|
10
|
-
@null_encryptor ||= ActiveRecord::Encryption::NullEncryptor.new
|
11
|
-
end
|
12
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
module Console1984::Supervisor::Accesses
|
2
|
-
include Console1984::Messages, Console1984::Freezeable
|
3
|
-
|
4
|
-
PROTECTED_ACCESS = Protected.new
|
5
|
-
UNPROTECTED_ACCESS = Unprotected.new
|
6
|
-
|
7
|
-
def enable_access_to_encrypted_content(silent: false)
|
8
|
-
run_system_command do
|
9
|
-
show_warning Console1984.enter_unprotected_encryption_mode_warning if !silent && protected_mode?
|
10
|
-
justification = ask_for_value "\nBefore you can access personal information, you need to ask for and get explicit consent from the user(s). #{current_username}, where can we find this consent (a URL would be great)?"
|
11
|
-
session_logger.start_sensitive_access justification
|
12
|
-
nil
|
13
|
-
end
|
14
|
-
ensure
|
15
|
-
@access = UNPROTECTED_ACCESS
|
16
|
-
nil
|
17
|
-
end
|
18
|
-
|
19
|
-
def disable_access_to_encrypted_content(silent: false)
|
20
|
-
run_system_command do
|
21
|
-
show_warning Console1984.enter_protected_mode_warning if !silent && unprotected_mode?
|
22
|
-
session_logger.end_sensitive_access
|
23
|
-
nil
|
24
|
-
end
|
25
|
-
ensure
|
26
|
-
@access = PROTECTED_ACCESS
|
27
|
-
nil
|
28
|
-
end
|
29
|
-
|
30
|
-
def with_encryption_mode(&block)
|
31
|
-
@access.execute(&block)
|
32
|
-
end
|
33
|
-
|
34
|
-
def unprotected_mode?
|
35
|
-
@access.is_a?(Unprotected)
|
36
|
-
end
|
37
|
-
|
38
|
-
def protected_mode?
|
39
|
-
!unprotected_mode?
|
40
|
-
end
|
41
|
-
end
|
@@ -1,65 +0,0 @@
|
|
1
|
-
module Console1984::Supervisor::Executor
|
2
|
-
extend ActiveSupport::Concern
|
3
|
-
|
4
|
-
include Console1984::Freezeable
|
5
|
-
|
6
|
-
def execute_supervised(commands, &block)
|
7
|
-
run_system_command { session_logger.before_executing commands }
|
8
|
-
validate_commands(commands)
|
9
|
-
execute(&block)
|
10
|
-
rescue Console1984::Errors::ForbiddenCommand, Console1984::Errors::ForbiddenCodeManipulation, FrozenError
|
11
|
-
flag_forbidden(commands)
|
12
|
-
rescue FrozenError
|
13
|
-
flag_forbidden(commands)
|
14
|
-
ensure
|
15
|
-
run_system_command { session_logger.after_executing commands }
|
16
|
-
end
|
17
|
-
|
18
|
-
def execute(&block)
|
19
|
-
run_user_command do
|
20
|
-
with_encryption_mode(&block)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def executing_user_command?
|
25
|
-
@executing_user_command
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
def flag_forbidden(commands)
|
30
|
-
puts "Forbidden command attempted: #{commands.join("\n")}"
|
31
|
-
run_system_command { session_logger.suspicious_commands_attempted commands }
|
32
|
-
nil
|
33
|
-
end
|
34
|
-
|
35
|
-
def run_user_command(&block)
|
36
|
-
run_command true, &block
|
37
|
-
end
|
38
|
-
|
39
|
-
def run_system_command(&block)
|
40
|
-
run_command false, &block
|
41
|
-
end
|
42
|
-
|
43
|
-
def validate_commands(commands)
|
44
|
-
if Array(commands).find { |command| forbidden_command?(command) }
|
45
|
-
raise Console1984::Errors::ForbiddenCommand
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def forbidden_command?(command)
|
50
|
-
# This is a first protection layer. Very simple for now. We'll likely make this
|
51
|
-
# more sophisticated and configurable in future versions.
|
52
|
-
#
|
53
|
-
# We can't use our +Freezable+ concern in ActiveRecord since it relies on code
|
54
|
-
# generation on the fly.
|
55
|
-
command =~ /Console1984|console_1984|(class|module)\s+ActiveRecord::/
|
56
|
-
end
|
57
|
-
|
58
|
-
def run_command(run_by_user, &block)
|
59
|
-
original_value = @executing_user_command
|
60
|
-
@executing_user_command = run_by_user
|
61
|
-
block.call
|
62
|
-
ensure
|
63
|
-
@executing_user_command = original_value
|
64
|
-
end
|
65
|
-
end
|
@@ -1,55 +0,0 @@
|
|
1
|
-
module Console1984::Supervisor::Protector
|
2
|
-
extend ActiveSupport::Concern
|
3
|
-
|
4
|
-
include Console1984::Freezeable
|
5
|
-
|
6
|
-
private
|
7
|
-
def extend_protected_systems
|
8
|
-
extend_object
|
9
|
-
extend_irb
|
10
|
-
extend_active_record
|
11
|
-
extend_socket_classes
|
12
|
-
end
|
13
|
-
|
14
|
-
def extend_object
|
15
|
-
Object.prepend Console1984::ProtectedObject
|
16
|
-
end
|
17
|
-
|
18
|
-
def extend_irb
|
19
|
-
IRB::Context.prepend(Console1984::ProtectedContext)
|
20
|
-
Rails::ConsoleMethods.include(Console1984::Commands)
|
21
|
-
end
|
22
|
-
|
23
|
-
ACTIVE_RECORD_CONNECTION_ADAPTERS = %w[ActiveRecord::ConnectionAdapters::Mysql2Adapter ActiveRecord::ConnectionAdapters::PostgreSQLAdapter ActiveRecord::ConnectionAdapters::SQLite3Adapter]
|
24
|
-
|
25
|
-
def extend_active_record
|
26
|
-
ACTIVE_RECORD_CONNECTION_ADAPTERS.each do |class_string|
|
27
|
-
if Object.const_defined?(class_string)
|
28
|
-
klass = class_string.constantize
|
29
|
-
klass.prepend(Console1984::ProtectedAuditableTables)
|
30
|
-
klass.include(Console1984::Freezeable)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def extend_socket_classes
|
36
|
-
socket_classes = [TCPSocket, OpenSSL::SSL::SSLSocket]
|
37
|
-
OpenSSL::SSL::SSLSocket.include(SSLSocketRemoteAddress)
|
38
|
-
|
39
|
-
if defined?(Redis::Connection)
|
40
|
-
socket_classes.push(*[Redis::Connection::TCPSocket, Redis::Connection::SSLSocket])
|
41
|
-
end
|
42
|
-
|
43
|
-
socket_classes.compact.each do |socket_klass|
|
44
|
-
socket_klass.prepend Console1984::ProtectedTcpSocket
|
45
|
-
socket_klass.freeze
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
module SSLSocketRemoteAddress
|
50
|
-
# Make it serve remote address as TCPSocket so that our extension works for it
|
51
|
-
def remote_address
|
52
|
-
Addrinfo.getaddrinfo(hostname, 443).first
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|