diff --git a/docs/configuration/system.md b/docs/configuration/system.md new file mode 100644 index 0000000000..22d00817e4 --- /dev/null +++ b/docs/configuration/system.md @@ -0,0 +1,45 @@ +# System Configuration + +The `` directive in Fluentd's configuration controls process-wide settings. + +## Configuration Parameters + +### `umask` + +The `umask` parameter sets the process umask (file permission mask). This affects the default permissions of files created by Fluentd. + +``` + + umask 0022 # Allows r/w for owner, read for group/others + +``` + +You can specify the umask value in octal format (e.g., `0022`, `0027`, `0077`). The meaning of umask values: +- `0022`: Allow read/write for owner, read for group/others +- `0027`: Allow read/write for owner, read for group, no access for others +- `0077`: Allow read/write for owner only, no access for group/others + +Note: If not specified, Fluentd will use the system default umask. + +### Examples + +Restrictive umask (owner only): +``` + + umask 0077 + +``` + +Standard umask: +``` + + umask 0022 + +``` + +Group readable: +``` + + umask 0027 + +``` \ No newline at end of file diff --git a/lib/fluent/system_config.rb b/lib/fluent/system_config.rb index c2a376f11d..0368eb4b95 100644 --- a/lib/fluent/system_config.rb +++ b/lib/fluent/system_config.rb @@ -30,7 +30,7 @@ class SystemConfig :file_permission, :dir_permission, :counter_server, :counter_client, :strict_config_value, :enable_msgpack_time_support, :disable_shared_socket, :metrics, :enable_input_metrics, :enable_size_metrics, :enable_jit, :source_only_buffer, - :config_include_dir + :config_include_dir, :umask ] config_param :workers, :integer, default: 1 @@ -61,6 +61,7 @@ class SystemConfig v.to_i(8) end config_param :config_include_dir, default: Fluent::DEFAULT_CONFIG_INCLUDE_DIR + config_param :umask, :string, default: nil # Keep as string for validation in apply() config_section :log, required: false, init: true, multi: false do config_param :path, :string, default: nil config_param :format, :enum, list: [:text, :json], default: :text @@ -151,6 +152,29 @@ def initialize(conf=nil, strict_config_value=false) configure(conf, strict_config_value) end + def apply(log) + return unless @umask + + begin + # Parse as octal integer and validate + new_mask = Integer(@umask, 8) + + if new_mask < 0 || new_mask > 0o777 + log.warn "umask value out of range (must be between 0000 and 0777): #{@umask}" + return + end + + # Only apply if valid + old_mask = File.umask(new_mask) + log.info "System umask changed from #{sprintf('0%03o', old_mask)} to #{sprintf('0%03o', new_mask)}" + rescue ArgumentError + log.warn "Invalid umask value '#{@umask}' - must be octal format (e.g., 0022)" + rescue => e + log.error "Failed to set umask", error: e + log.error_backtrace + end + end + def configure(conf, strict_config_value=false) strict = strict_config_value if !strict && conf && conf.has_key?("strict_config_value") diff --git a/test/config/test_system_config.rb b/test/config/test_system_config.rb index a06ee0bbcf..f340ea9596 100644 --- a/test/config/test_system_config.rb +++ b/test/config/test_system_config.rb @@ -101,7 +101,81 @@ def parse_text(text) 'enable_input_metrics' => ['enable_input_metrics', false], 'enable_size_metrics' => ['enable_size_metrics', true], 'enable_jit' => ['enable_jit', true], + 'umask' => ['umask', '0022'], ) + sub_test_case "umask configuration" do + setup do + @original_umask = File.umask + end + + teardown do + File.umask(@original_umask) # Restore original umask + end + + test 'valid umask is applied' do + conf = parse_text(<<-EOS) + + umask 0022 + + EOS + + log = Fluent::Test::TestLogger.new + sc = Fluent::SystemConfig.new(conf) + sc.apply(log) + + assert_equal '0022', sc.umask + assert_equal 0o022, File.umask + assert_true log.logs.any? { |msg| msg.include?('System umask changed') } + end + + test 'invalid umask format logs warning' do + conf = parse_text(<<-EOS) + + umask invalid + + EOS + + log = Fluent::Test::TestLogger.new + sc = Fluent::SystemConfig.new(conf) + old_mask = File.umask + sc.apply(log) + + assert_equal old_mask, File.umask # Should not change + assert_true log.logs.any? { |msg| msg.include?('Invalid umask value') } + end + + test 'out of range umask logs warning' do + conf = parse_text(<<-EOS) + + umask 0888 + + EOS + + log = Fluent::Test::TestLogger.new + sc = Fluent::SystemConfig.new(conf) + old_mask = File.umask + sc.apply(log) + + assert_equal old_mask, File.umask # Should not change + assert_true log.logs.any? { |msg| msg.include?('out of range') } + end + + test 'nil umask is ignored' do + conf = parse_text(<<-EOS) + + + EOS + + log = Fluent::Test::TestLogger.new + sc = Fluent::SystemConfig.new(conf) + old_mask = File.umask + sc.apply(log) + + assert_nil sc.umask + assert_equal old_mask, File.umask + end + end + test "accepts parameters" do |(k, v)| conf = parse_text(<<-EOS)