diff --git a/fluent.conf b/fluent.conf
index 98d7a38856..aa6b02541d 100644
--- a/fluent.conf
+++ b/fluent.conf
@@ -1,6 +1,11 @@
# In v1 configuration, type and id are @ prefix parameters.
# @type and @id are recommended. type and id are still available for backward compatibility
+## System-wide configurations
+
+ umask 0027
+
+
## built-in TCP input
## $ echo | fluent-cat
@@ -12,7 +17,6 @@
#
# @type unix
#
-
# HTTP input
# http://localhost:8888/?json=
diff --git a/lib/fluent/root_agent.rb b/lib/fluent/root_agent.rb
index 552819e4bf..8185166977 100644
--- a/lib/fluent/root_agent.rb
+++ b/lib/fluent/root_agent.rb
@@ -180,6 +180,33 @@ def configure(conf)
label_configs.each { |name, e| @labels[name].configure(e) }
setup_error_label(error_label_config) if error_label_config
+ # Get umask value from system config
+ system_conf = conf.elements.find { |e| e.name == "system" }
+ if system_conf
+ if system_conf.has_key?("umask")
+ umask_str = system_conf.to_h["umask"]
+ begin
+ umask_value = Integer(umask_str, 8) # ArgumentError for invalid octal
+ if umask_value > 0
+ File.umask(umask_value)
+ log.info "Applied umask: #{sprintf("%04o", File.umask)}"
+ end
+ rescue ArgumentError
+ raise Fluent::ConfigError, "Invalid umask value: #{umask_str} (must be valid octal)"
+ end
+ else
+ # No umask found in system config, set default
+ default_umask = 0022
+ File.umask(default_umask)
+ log.info "No umask specified in config, using default: #{sprintf("%04o", File.umask)}"
+ end
+ else
+ # No system section found, set default
+ default_umask = 0022
+ File.umask(default_umask)
+ log.info "No system section found in config, using default umask: #{sprintf("%04o", File.umask)}"
+ end
+
super
setup_source_only_buffer_agent if @source_only_mode.enabled?
diff --git a/test/test_config.rb b/test/test_config.rb
index 7533e6e16b..cc367c4479 100644
--- a/test/test_config.rb
+++ b/test/test_config.rb
@@ -372,4 +372,4 @@ def write_config(path, data, encoding: 'utf-8')
assert_equal('value2', c['key2'])
end
end
-end
+end
\ No newline at end of file
diff --git a/test/test_root_agent.rb b/test/test_root_agent.rb
index 8b44b68699..cc69ce70b4 100644
--- a/test/test_root_agent.rb
+++ b/test/test_root_agent.rb
@@ -1167,4 +1167,86 @@ def setup
@root_agent.shutdown
end
end
+
+ test 'configure with umask should set proper value' do
+ conf = <<-EOC
+
+ umask 0027
+
+ EOC
+
+ config = Fluent::Config.parse(conf, "(test)", "(test_dir)", true)
+ ra = Fluent::RootAgent.new(log: $log)
+
+ original_umask = File.umask
+
+ filename = "test_umask_file"
+
+ begin
+ ra.configure(config)
+
+ File.open(filename, "w") do |f|
+ f.write("Test data")
+ end
+
+ file_mode = File.stat(filename).mode & 0777
+
+ # 0666 & ~0027 => 0640 (octal)
+ expected_mode = 0640
+
+ assert_equal(expected_mode, file_mode,
+ "Expected file mode to be #{sprintf('%o', expected_mode)}, but got #{sprintf('%o', file_mode)}")
+ ensure
+ File.umask(original_umask)
+ File.delete(filename) if File.exist?(filename)
+ end
+ end
+
+
+ test 'configure with invalid umask should raise error' do
+ conf = <<-EOC
+
+ umask 0999 # invalid octal
+
+ EOC
+
+ config = Fluent::Config.parse(conf, "(test)", "(test_dir)", true)
+ ra = Fluent::RootAgent.new(log: $log)
+ original_umask = File.umask
+ begin
+ assert_raise(Fluent::ConfigError, "Expected configuration with invalid umask to raise Fluent::ConfigError") do
+ ra.configure(config)
+ end
+ ensure
+ File.umask(original_umask)
+ end
+ end
+
+ test 'configure without umask should use default umask and affect file permissions' do
+ conf = <<-EOC
+
+
+ EOC
+
+ config = Fluent::Config.parse(conf, "(test)", "(test_dir)", true)
+ ra = Fluent::RootAgent.new(log: $log)
+ original_umask = File.umask
+ filename = "test_umask_default_file"
+
+ begin
+ ra.configure(config)
+ assert_equal 0022, File.umask, "Expected umask to be 0022 after configuration"
+ # 0666 & ~0022 => 0644 (rw-r--r--)
+ expected_mode = 0644
+
+ File.open(filename, "w") { |f| f.write("Test content") }
+ file_mode = File.stat(filename).mode & 0777
+
+ assert_equal expected_mode, file_mode,
+ "Expected file mode to be #{sprintf('%o', expected_mode)}, but got #{sprintf('%o', file_mode)}"
+ ensure
+ File.umask(original_umask)
+ File.delete(filename) if File.exist?(filename)
+ end
+ end
end