diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index ac9dca566..d0244eec5 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -499,7 +499,7 @@ Lint/Void: # Offense count: 60 Metrics/AbcSize: - Max: 84 + Max: 88 # Offense count: 15 # Configuration parameters: CountComments, ExcludedMethods. diff --git a/docs/Configuration.md b/docs/Configuration.md index 6bbbb61f6..73b7055e9 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -18,6 +18,29 @@ input: secure: false ``` +## Password Masking in Debug Logs + +By default Oxidized will no longer log cleartext passwords in debug logs. This is designed to prevent Oxidized debug logs from containing all of your node authentication information. This makes submitting [GitHub issues for oxidized](https://github.com/ytti/oxidized/issues) and sharing debug logs easier and safer for everyone. + +NOTE - This only applies to the configuration of Oxidized itself - NOT any fetched device configurations. Model-specific code is used for model-specific configuration sanitization (see Removing secrets below). + +The value for `password` will instead be logged as `********`. + +These settings can be controlled by the global section in the Oxidized configuration file. + +```yaml +..(other global configuration)... +mask_value: "" +``` + +If you need to log authentication information in your debug logs you can make the following adjustments: + +```yaml +..(other global configuration)... +mask_sensitive: false +``` + + ## Privileged mode To start privileged mode before pulling the configuration, Oxidized needs to send the enable command. You can globally enable this, by adding the following snippet to the global section of the configuration file. diff --git a/lib/oxidized/config.rb b/lib/oxidized/config.rb index 47544fbb5..e5c60fc1b 100644 --- a/lib/oxidized/config.rb +++ b/lib/oxidized/config.rb @@ -28,7 +28,10 @@ def self.load(cmd_opts={}) asetus.default.retries = 3 asetus.default.prompt = /^([\w.@-]+[#>]\s?)$/ asetus.default.rest = '127.0.0.1:8888' # or false to disable - asetus.default.next_adds_job = false # if true, /next adds job, so device is fetched immmeiately + asetus.default.next_adds_job = false # if true, /next adds job, so device is fetched immediately + asetus.default.mask_sensitive= true # True to mask sensitive configuration fields from log output/display + asetus.default.mask_fields = /^(password)$/i # Our default regexp of configuration fields to mask + asetus.default.mask_value = '*' * 8 # What we mask the sensitive configuration fields with asetus.default.vars = {} # could be 'enable'=>'enablePW' asetus.default.groups = {} # group level configuration asetus.default.models = {} # model level configuration diff --git a/lib/oxidized/node.rb b/lib/oxidized/node.rb index 2b15d4e5a..db721148f 100644 --- a/lib/oxidized/node.rb +++ b/lib/oxidized/node.rb @@ -201,19 +201,37 @@ def resolve_key key, opt, global=nil key_sym = key.to_sym key_str = key.to_s value = global - Oxidized.logger.debug "node.rb: resolving node key '#{key}', with passed global value of '#{value}' and node value '#{opt[key_sym]}'" + + # Implement basic configuration field masking + # The goal is to prevent logging of sensitive information unless the user positively activates logging of sensitive information + # This helps to keep authentication credentials out of logs, pastebins, GitHub issues, etc. + # Authentication failures to devices are typically easy to spot in other ways. + # NOTE - This does NOT control any masking for device configurations - that is covered in model-specific code + # Controllable with the configuration file via the following parameters: + # mask_sensitive: true || false (default is true) + # mask_fields: A regular expression of what is masked. Defaults to /^(password)$/i + # mask_value: A String value of what to log/display instead. Defaults to '*' * 8 + is_key_masked = false + if Oxidized.config.mask_sensitive + if key_str =~ Oxidized.config.mask_fields + is_key_masked = true + masked_value = Oxidized.config.mask_value + end + end + + Oxidized.logger.debug "node.rb: resolving node key '#{key}', with passed global value of '" + (is_key_masked ? masked_value.to_s : value.to_s) + "' and node value '" + (is_key_masked ? masked_value.to_s : opt[key_sym].to_s) + "'" #global if not value and Oxidized.config.has_key?(key_str) value = Oxidized.config[key_str] - Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from global" + Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '" + (is_key_masked ? masked_value.to_s : value.to_s) + "' from global" end #group if Oxidized.config.groups.has_key?(@group) if Oxidized.config.groups[@group].has_key?(key_str) value = Oxidized.config.groups[@group][key_str] - Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from group" + Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '" + (is_key_masked ? masked_value.to_s : value.to_s) + "' from group" end end @@ -222,13 +240,13 @@ def resolve_key key, opt, global=nil if Oxidized.config.models.has_key?(@model.class.name.to_s.downcase) if Oxidized.config.models[@model.class.name.to_s.downcase].has_key?(key_str) value = Oxidized.config.models[@model.class.name.to_s.downcase][key_str] - Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from model" + Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '" + (is_key_masked ? masked_value.to_s : value.to_s) + "' from model" end end #node value = opt[key_sym] || value - Oxidized.logger.debug "node.rb: returning node key '#{key}' with value '#{value}'" + Oxidized.logger.debug "node.rb: returning node key '#{key}' with value '" + (is_key_masked ? masked_value.to_s : value.to_s) + "'" value end