console1984 0.1.4 → 0.1.5
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 +6 -2
- data/lib/console1984/commands.rb +2 -2
- data/lib/console1984/config.rb +4 -4
- data/lib/console1984/engine.rb +3 -7
- data/lib/console1984/errors.rb +1 -1
- data/lib/console1984/freezeable.rb +54 -0
- data/lib/console1984/protected_auditable_tables.rb +23 -22
- data/lib/console1984/protected_context.rb +3 -1
- data/lib/console1984/protected_object.rb +15 -0
- data/lib/console1984/protected_tcp_socket.rb +3 -1
- data/lib/console1984/sessions_logger/database.rb +3 -1
- data/lib/console1984/supervisor/accesses/protected.rb +2 -0
- data/lib/console1984/supervisor/accesses/unprotected.rb +2 -0
- data/lib/console1984/supervisor/accesses.rb +1 -1
- data/lib/console1984/supervisor/executor.rb +28 -4
- data/lib/console1984/supervisor/input_output.rb +1 -1
- data/lib/console1984/supervisor/protector.rb +18 -0
- data/lib/console1984/supervisor.rb +18 -4
- data/lib/console1984/username/env_resolver.rb +2 -0
- data/lib/console1984/version.rb +1 -1
- data/lib/console1984.rb +7 -4
- metadata +4 -3
- data/lib/console1984/frozen_methods.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 44d77add0193cc1bb27955cb70d0d3e58153a17524e6d3b93a15179700322c58
|
4
|
+
data.tar.gz: b36e25eb75d6b1d113ae3653118115da747122e10af23d38ea00ebd72f3427cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07d029ed6dcd845cbc30c035ccdb2a9879983f1660c028530dfc8873673d212f9230ffc9ad8870b1806a981317dad06d48c95b53cdebe728a37bc585bd182e29
|
7
|
+
data.tar.gz: c9dfd6cd41b27c000b0f37a22ee9b1d4fbbdd32081b22735636d90c62175c2cb8f55b3ef94f90c9ac706a72838b67e8a8130e813943fe908f3860bf631365c8f
|
data/README.md
CHANGED
@@ -10,7 +10,7 @@ A Rails console extension that protects sensitive accesses and makes them audita
|
|
10
10
|
|
11
11
|
If you are looking for the auditing tool, check [`audits1984`](https://github.com/basecamp/audits1984).
|
12
12
|
|
13
|
-

|
14
14
|
|
15
15
|
## Installation
|
16
16
|
|
@@ -69,7 +69,7 @@ To decrypt data, enter the command `decrypt!`. It will ask for a justification,
|
|
69
69
|
irb(main)> Topic.last.name
|
70
70
|
Topic Load (1.4ms) SELECT `topics`.* FROM `topics` ORDER BY `topics`.`id` DESC LIMIT 1
|
71
71
|
=> "{\"p\":\"iu6+LfnNlurC6sL++JyOIDvedjNSz/AvnZQ=\",\"h\":{\"iv\":\"BYa86+JNM/LdkC18\",\"at\":\"r4sQNoSyIlAjJdZEKHVMow==\",\"k\":{\"p\":\"7L1l/5UiYsFQqqo4jfMZtLwp90KqcrIgS7HqgteVjuM=\",\"h\":{\"iv\":\"ItwRYxZAerKIoSZ8\",\"at\":\"ZUSNVfvtm4wAYWLBKRAx/g==\",\"e\":\"QVNDSUktOEJJVA==\"}},\"i\":\"OTdiOQ==\"}}"
|
72
|
-
irb(main)
|
72
|
+
irb(main)> decrypt!
|
73
73
|
```
|
74
74
|
|
75
75
|
```
|
@@ -128,6 +128,10 @@ This will work for systems that use Ruby sockets as the underlying communication
|
|
128
128
|
|
129
129
|
By default, sessions will be incinerated with a job 30 days after they are created. You can configure this period by setting `config.console1984.incinerate_after = 1.year` and you can disable incineration completely by setting `config.console1984.incinerate = false`.
|
130
130
|
|
131
|
+
### Eager loading
|
132
|
+
|
133
|
+
When starting a console session, `console1984` will eager load all the application classes if necessary. In practice, production environments already load classes eagerly, so this won't represent any change for those.
|
134
|
+
|
131
135
|
## Configuration
|
132
136
|
|
133
137
|
These config options are namespaced in `config.console1984`:
|
data/lib/console1984/commands.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
module Console1984::Commands
|
2
|
+
include Console1984::Freezeable
|
3
|
+
|
2
4
|
def decrypt!
|
3
5
|
supervisor.enable_access_to_encrypted_content
|
4
6
|
end
|
@@ -11,6 +13,4 @@ module Console1984::Commands
|
|
11
13
|
def supervisor
|
12
14
|
Console1984.supervisor
|
13
15
|
end
|
14
|
-
|
15
|
-
include Console1984::FrozenMethods
|
16
16
|
end
|
data/lib/console1984/config.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# Container for config options.
|
2
2
|
class Console1984::Config
|
3
|
-
include Console1984::Messages
|
3
|
+
include Console1984::Freezeable, Console1984::Messages
|
4
4
|
|
5
5
|
PROPERTIES = %i[
|
6
6
|
session_logger username_resolver
|
7
7
|
protected_environments protected_urls
|
8
8
|
production_data_warning enter_unprotected_encryption_mode_warning enter_protected_mode_warning
|
9
9
|
incinerate incinerate_after incineration_queue
|
10
|
-
debug
|
10
|
+
debug test_mode
|
11
11
|
]
|
12
12
|
|
13
13
|
attr_accessor(*PROPERTIES)
|
@@ -23,7 +23,7 @@ class Console1984::Config
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def freeze
|
26
|
-
super
|
26
|
+
super
|
27
27
|
protected_urls.freeze
|
28
28
|
end
|
29
29
|
|
@@ -44,6 +44,6 @@ class Console1984::Config
|
|
44
44
|
self.incineration_queue = "console1984_incineration"
|
45
45
|
|
46
46
|
self.debug = false
|
47
|
-
self.
|
47
|
+
self.test_mode = false
|
48
48
|
end
|
49
49
|
end
|
data/lib/console1984/engine.rb
CHANGED
@@ -17,13 +17,9 @@ module Console1984
|
|
17
17
|
console do
|
18
18
|
Console1984.config.set_from(config.console1984)
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
# Make it serve remote address as TCPSocket so that our extension works for it
|
24
|
-
def remote_address
|
25
|
-
Addrinfo.getaddrinfo(hostname, 443).first
|
26
|
-
end
|
20
|
+
if Console1984.running_protected_environment?
|
21
|
+
Console1984.supervisor.install
|
22
|
+
Console1984.supervisor.start
|
27
23
|
end
|
28
24
|
end
|
29
25
|
end
|
data/lib/console1984/errors.rb
CHANGED
@@ -0,0 +1,54 @@
|
|
1
|
+
# Prevents adding new methods to classes.
|
2
|
+
#
|
3
|
+
# This prevents manipulating certain Console1984 classes
|
4
|
+
# during a console session.
|
5
|
+
module Console1984::Freezeable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
mattr_reader :to_freeze, default: Set.new
|
9
|
+
|
10
|
+
included do
|
11
|
+
Console1984::Freezeable.to_freeze << self
|
12
|
+
end
|
13
|
+
|
14
|
+
class_methods do
|
15
|
+
SENSITIVE_INSTANCE_METHODS = %i[ instance_variable_set ]
|
16
|
+
|
17
|
+
def prevent_sensitive_overrides
|
18
|
+
SENSITIVE_INSTANCE_METHODS.each do |method|
|
19
|
+
prevent_sensitive_method method
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def prevent_sensitive_method(method_name)
|
25
|
+
define_method method_name do |*arguments|
|
26
|
+
raise Console1984::Errors::ForbiddenCodeManipulation, "You can't invoke #{method_name} on #{self}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class << self
|
32
|
+
def freeze_all
|
33
|
+
class_and_modules_to_freeze.each do |class_or_module|
|
34
|
+
freeze_class_or_module(class_or_module)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def class_and_modules_to_freeze
|
40
|
+
with_descendants(to_freeze)
|
41
|
+
end
|
42
|
+
|
43
|
+
def freeze_class_or_module(class_or_module)
|
44
|
+
class_or_module.prevent_sensitive_overrides
|
45
|
+
class_or_module.freeze
|
46
|
+
end
|
47
|
+
|
48
|
+
def with_descendants(classes_and_modules)
|
49
|
+
classes_and_modules + classes_and_modules.grep(Class).flat_map(&:descendants)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
freeze
|
54
|
+
end
|
@@ -1,29 +1,30 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
1
|
+
# Prevents accessing trail model tables when executing console commands.
|
2
|
+
module Console1984::ProtectedAuditableTables
|
3
|
+
include Console1984::Freezeable
|
4
|
+
|
5
|
+
%i[ execute exec_query exec_insert exec_delete exec_update exec_insert_all ].each do |method|
|
6
|
+
define_method method do |*args|
|
7
|
+
sql = args.first
|
8
|
+
if Console1984.supervisor.executing_user_command? && sql =~ auditable_tables_regexp
|
9
|
+
raise Console1984::Errors::ForbiddenCommand, "#{sql}"
|
10
|
+
else
|
11
|
+
super(*args)
|
12
12
|
end
|
13
13
|
end
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
16
|
+
private
|
17
|
+
def auditable_tables_regexp
|
18
|
+
@auditable_tables_regexp ||= Regexp.new("#{auditable_tables.join("|")}")
|
19
|
+
end
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
+
def auditable_tables
|
22
|
+
@auditable_tables ||= auditable_models.collect(&:table_name)
|
23
|
+
end
|
21
24
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
26
|
-
end
|
25
|
+
def auditable_models
|
26
|
+
@auditable_models ||= Console1984::Base.descendants
|
27
|
+
end
|
27
28
|
|
28
|
-
|
29
|
+
include Console1984::Freezeable
|
29
30
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
module Console1984::ProtectedContext
|
2
|
+
include Console1984::Freezeable
|
3
|
+
|
2
4
|
# This method is invoked for showing returned objects in the console
|
3
5
|
# Overridden to make sure their evaluation is supervised.
|
4
6
|
def inspect_last_value
|
@@ -14,5 +16,5 @@ module Console1984::ProtectedContext
|
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
17
|
-
include Console1984::
|
19
|
+
include Console1984::Freezeable
|
18
20
|
end
|
@@ -0,0 +1,15 @@
|
|
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,5 +1,7 @@
|
|
1
1
|
# Wraps socket methods to execute supervised.
|
2
2
|
module Console1984::ProtectedTcpSocket
|
3
|
+
include Console1984::Freezeable
|
4
|
+
|
3
5
|
def write(*args)
|
4
6
|
protecting do
|
5
7
|
super
|
@@ -55,5 +57,5 @@ module Console1984::ProtectedTcpSocket
|
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
58
|
-
include Console1984::
|
60
|
+
include Console1984::Freezeable
|
59
61
|
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
# A session logger that saves audit trails in the database.
|
2
2
|
class Console1984::SessionsLogger::Database
|
3
|
+
include Console1984::Freezeable
|
4
|
+
|
3
5
|
attr_reader :current_session, :current_sensitive_access
|
4
6
|
|
5
7
|
def start_session(username, reason)
|
6
8
|
silence_logging do
|
7
9
|
user = Console1984::User.find_or_create_by!(username: username)
|
8
|
-
@current_session = user.sessions.create!
|
10
|
+
@current_session = user.sessions.create!(reason: reason)
|
9
11
|
end
|
10
12
|
end
|
11
13
|
|
@@ -1,13 +1,16 @@
|
|
1
1
|
module Console1984::Supervisor::Executor
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
|
+
include Console1984::Freezeable
|
5
|
+
|
4
6
|
def execute_supervised(commands, &block)
|
5
7
|
run_system_command { session_logger.before_executing commands }
|
8
|
+
validate_commands(commands)
|
6
9
|
execute(&block)
|
7
|
-
rescue Console1984::Errors::ForbiddenCommand, Console1984::Errors::
|
8
|
-
|
9
|
-
|
10
|
-
|
10
|
+
rescue Console1984::Errors::ForbiddenCommand, Console1984::Errors::ForbiddenCodeManipulation, FrozenError
|
11
|
+
flag_forbidden(commands)
|
12
|
+
rescue FrozenError
|
13
|
+
flag_forbidden(commands)
|
11
14
|
ensure
|
12
15
|
run_system_command { session_logger.after_executing commands }
|
13
16
|
end
|
@@ -23,6 +26,12 @@ module Console1984::Supervisor::Executor
|
|
23
26
|
end
|
24
27
|
|
25
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
|
+
|
26
35
|
def run_user_command(&block)
|
27
36
|
run_command true, &block
|
28
37
|
end
|
@@ -31,6 +40,21 @@ module Console1984::Supervisor::Executor
|
|
31
40
|
run_command false, &block
|
32
41
|
end
|
33
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
|
+
|
34
58
|
def run_command(run_by_user, &block)
|
35
59
|
original_value = @executing_user_command
|
36
60
|
@executing_user_command = run_by_user
|
@@ -1,13 +1,20 @@
|
|
1
1
|
module Console1984::Supervisor::Protector
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
|
+
include Console1984::Freezeable
|
5
|
+
|
4
6
|
private
|
5
7
|
def extend_protected_systems
|
8
|
+
extend_object
|
6
9
|
extend_irb
|
7
10
|
extend_active_record
|
8
11
|
extend_socket_classes
|
9
12
|
end
|
10
13
|
|
14
|
+
def extend_object
|
15
|
+
Object.prepend Console1984::ProtectedObject
|
16
|
+
end
|
17
|
+
|
11
18
|
def extend_irb
|
12
19
|
IRB::Context.prepend(Console1984::ProtectedContext)
|
13
20
|
Rails::ConsoleMethods.include(Console1984::Commands)
|
@@ -20,18 +27,29 @@ module Console1984::Supervisor::Protector
|
|
20
27
|
if Object.const_defined?(class_string)
|
21
28
|
klass = class_string.constantize
|
22
29
|
klass.prepend(Console1984::ProtectedAuditableTables)
|
30
|
+
klass.include(Console1984::Freezeable)
|
23
31
|
end
|
24
32
|
end
|
25
33
|
end
|
26
34
|
|
27
35
|
def extend_socket_classes
|
28
36
|
socket_classes = [TCPSocket, OpenSSL::SSL::SSLSocket]
|
37
|
+
OpenSSL::SSL::SSLSocket.include(SSLSocketRemoteAddress)
|
38
|
+
|
29
39
|
if defined?(Redis::Connection)
|
30
40
|
socket_classes.push(*[Redis::Connection::TCPSocket, Redis::Connection::SSLSocket])
|
31
41
|
end
|
32
42
|
|
33
43
|
socket_classes.compact.each do |socket_klass|
|
34
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
|
35
53
|
end
|
36
54
|
end
|
37
55
|
end
|
@@ -3,14 +3,17 @@ require 'rails/console/app'
|
|
3
3
|
|
4
4
|
# Protects console sessions and executes code in supervised mode.
|
5
5
|
class Console1984::Supervisor
|
6
|
-
include Accesses,
|
6
|
+
include Accesses, Console1984::Freezeable, Executor, InputOutput, Protector
|
7
|
+
|
8
|
+
def install
|
9
|
+
extend_protected_systems
|
10
|
+
freeze_all
|
11
|
+
end
|
7
12
|
|
8
13
|
# Starts a console session extending IRB and several systems to inject
|
9
14
|
# the protection logic, and notifies the session logger to record the
|
10
15
|
# session.
|
11
16
|
def start
|
12
|
-
Console1984.config.freeze
|
13
|
-
extend_protected_systems
|
14
17
|
disable_access_to_encrypted_content(silent: true)
|
15
18
|
|
16
19
|
show_welcome_message
|
@@ -31,6 +34,17 @@ class Console1984::Supervisor
|
|
31
34
|
session_logger.finish_session
|
32
35
|
end
|
33
36
|
|
37
|
+
def freeze_all
|
38
|
+
eager_load_all_classes
|
39
|
+
Console1984.config.freeze unless Console1984.config.test_mode
|
40
|
+
Console1984::Freezeable.freeze_all
|
41
|
+
end
|
42
|
+
|
43
|
+
def eager_load_all_classes
|
44
|
+
Rails.application.eager_load! unless Rails.application.config.eager_load
|
45
|
+
Console1984.class_loader.eager_load
|
46
|
+
end
|
47
|
+
|
34
48
|
def session_logger
|
35
49
|
Console1984.session_logger
|
36
50
|
end
|
@@ -43,5 +57,5 @@ class Console1984::Supervisor
|
|
43
57
|
Console1984.username_resolver
|
44
58
|
end
|
45
59
|
|
46
|
-
include Console1984::
|
60
|
+
include Console1984::Freezeable
|
47
61
|
end
|
data/lib/console1984/version.rb
CHANGED
data/lib/console1984.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
require 'console1984/engine'
|
2
2
|
|
3
3
|
require "zeitwerk"
|
4
|
-
|
5
|
-
|
4
|
+
class_loader = Zeitwerk::Loader.for_gem
|
5
|
+
class_loader.setup
|
6
6
|
|
7
7
|
module Console1984
|
8
|
-
include Messages
|
8
|
+
include Messages, Freezeable
|
9
9
|
|
10
|
-
|
10
|
+
mattr_accessor :supervisor, default: Supervisor.new
|
11
11
|
mattr_reader :config, default: Config.new
|
12
|
+
mattr_accessor :class_loader
|
12
13
|
|
13
14
|
thread_mattr_accessor :currently_protected_urls, default: []
|
14
15
|
|
@@ -37,3 +38,5 @@ module Console1984
|
|
37
38
|
end
|
38
39
|
end
|
39
40
|
end
|
41
|
+
|
42
|
+
Console1984.class_loader = class_loader
|
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.5
|
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-08-
|
11
|
+
date: 2021-08-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -160,10 +160,11 @@ files:
|
|
160
160
|
- lib/console1984/config.rb
|
161
161
|
- lib/console1984/engine.rb
|
162
162
|
- lib/console1984/errors.rb
|
163
|
-
- lib/console1984/
|
163
|
+
- lib/console1984/freezeable.rb
|
164
164
|
- lib/console1984/messages.rb
|
165
165
|
- lib/console1984/protected_auditable_tables.rb
|
166
166
|
- lib/console1984/protected_context.rb
|
167
|
+
- lib/console1984/protected_object.rb
|
167
168
|
- lib/console1984/protected_tcp_socket.rb
|
168
169
|
- lib/console1984/sessions_logger/database.rb
|
169
170
|
- lib/console1984/supervisor.rb
|
@@ -1,17 +0,0 @@
|
|
1
|
-
# Prevents adding new methods to classes.
|
2
|
-
#
|
3
|
-
# This prevents manipulating certain Console1984 classes
|
4
|
-
# during a console session.
|
5
|
-
module Console1984::FrozenMethods
|
6
|
-
extend ActiveSupport::Concern
|
7
|
-
|
8
|
-
module ClassMethods
|
9
|
-
def method_added(method_name)
|
10
|
-
raise Console1984::Errors::ForbiddenClassManipulation, "Can't override #{name}##{method_name}"
|
11
|
-
end
|
12
|
-
|
13
|
-
def singleton_method_added(method_name)
|
14
|
-
raise Console1984::Errors::ForbiddenClassManipulation, "Can't override #{name}.#{method_name}"
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|