Skip to content

Commit

Permalink
Merge pull request #3155 from fluent/handle-linux-capability
Browse files Browse the repository at this point in the history
Handle linux capability if available
  • Loading branch information
cosmo0920 authored Nov 13, 2020
2 parents 644ac50 + 9a4ad43 commit 7e88922
Show file tree
Hide file tree
Showing 6 changed files with 241 additions and 3 deletions.
24 changes: 22 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,44 +9,63 @@ matrix:
include:
- rvm: 2.4.9
os: linux
env: USE_CAPNG=false
- rvm: 2.4.9
os: linux-ppc64le
env: USE_CAPNG=false
- rvm: 2.5.7
os: linux
env: USE_CAPNG=false
- rvm: 2.5.7
os: linux
arch: s390x
dist: xenial
env: USE_CAPNG=false
- rvm: 2.6.5
os: linux
env: USE_CAPNG=false
- rvm: 2.6.6
os: linux
env: USE_CAPNG=true
- rvm: 2.7.0
os: linux
env: USE_CAPNG=false
- rvm: ruby-head
os: linux
env: USE_CAPNG=false
- rvm: ruby-head
os: linux-ppc64le
env: USE_CAPNG=false
- rvm: 2.4.6
os: osx
osx_image: xcode8.3 # OSX 10.12
env: USE_CAPNG=false
- rvm: ruby-head
os: osx
osx_image: xcode8.3 # OSX 10.12
env: USE_CAPNG=false
allow_failures:
- rvm: 2.4.6
os: osx
osx_image: xcode8.3
env: USE_CAPNG=false
- rvm: 2.5.7
os: linux
arch: s390x
dist: xenial
env: USE_CAPNG=false
- rvm: ruby-head
env: USE_CAPNG=false

branches:
only:
- master

before_install:
- gem update --system=3.1.2
before_install: |
gem update --system=3.1.2
if [[ x"${USE_CAPNG}" == "xtrue" ]]; then
echo 'gem "capng_c"' >> Gemfile.local
fi
sudo: false
dist: trusty # for TLSv1.2 support
Expand All @@ -55,3 +74,4 @@ addons:
apt:
packages:
- libgmp3-dev
- libcap-ng-dev
87 changes: 87 additions & 0 deletions lib/fluent/capability.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#
# Fluent
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require "fluent/env"

if Fluent.linux?
begin
require 'capng'
rescue LoadError
end
end

module Fluent
if defined?(CapNG)
class Capability
def initialize(target = nil, pid = nil)
@capng = CapNG.new(target, pid)
end

def usable?
true
end

def apply(select_set)
@capng.apply(select_set)
end

def clear(select_set)
@capng.clear(select_set)
end

def have_capability?(type, capability)
@capng.have_capability?(type, capability)
end

def update(action, type, capability_or_capability_array)
@capng.update(action, type, capability_or_capability_array)
end

def have_capabilities?(select_set)
@capng.have_capabilities?(select_set)
end
end
else
class Capability
def initialize(target = nil, pid = nil)
end

def usable?
false
end

def apply(select_set)
false
end

def clear(select_set)
false
end

def have_capability?(type, capability)
false
end

def update(action, type, capability_or_capability_array)
false
end

def have_capabilities?(select_set)
false
end
end
end
end
4 changes: 4 additions & 0 deletions lib/fluent/env.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,8 @@ module Fluent
def self.windows?
ServerEngine.windows?
end

def self.linux?
/linux/ === RUBY_PLATFORM
end
end
9 changes: 8 additions & 1 deletion lib/fluent/plugin/in_tail.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
require 'fluent/plugin/buffer'
require 'fluent/plugin/parser_multiline'
require 'fluent/variable_store'
require 'fluent/capability'
require 'fluent/plugin/in_tail/position_file'

if Fluent.windows?
Expand Down Expand Up @@ -171,6 +172,7 @@ def configure(conf)
@dir_perm = system_config.dir_permission || Fluent::DEFAULT_DIR_PERMISSION
# parser is already created by parser helper
@parser = parser_create(usage: parser_config['usage'] || @parser_configs.first.usage)
@capability = Fluent::Capability.new(:current_process)
end

def configure_tag
Expand Down Expand Up @@ -250,6 +252,11 @@ def close
close_watcher_handles
end

def have_read_capability?
@capability.have_capability?(:effective, :dac_read_search) ||
@capability.have_capability?(:effective, :dac_override)
end

def expand_paths
date = Fluent::EventTime.now
paths = []
Expand All @@ -263,7 +270,7 @@ def expand_paths
paths += Dir.glob(path).select { |p|
begin
is_file = !File.directory?(p)
if File.readable?(p) && is_file
if (File.readable?(p) || have_read_capability?) && is_file
if @limit_recently_modified && File.mtime(p) < (date.to_time - @limit_recently_modified)
false
else
Expand Down
46 changes: 46 additions & 0 deletions test/plugin/test_in_tail.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ def create_driver(conf = SINGLE_LINE_CONFIG, use_common_conf = true)
assert_equal "#{TMP_DIR}/tail.pos", d.instance.pos_file
assert_equal 1000, d.instance.read_lines_limit
assert_equal false, d.instance.ignore_repeated_permission_error
assert_nothing_raised do
d.instance.have_read_capability?
end
end

data("empty" => config_element,
Expand Down Expand Up @@ -1112,6 +1115,49 @@ def count_timer_object
end
end

sub_test_case "path w/ Linux capability" do
def capability_enabled?
if Fluent.linux?
begin
require 'capng'
true
rescue LoadError
false
end
else
false
end
end

setup do
omit "This environment is not enabled Linux capability handling feature" unless capability_enabled?

@capng = CapNG.new(:current_process)
flexstub(Fluent::Capability) do |klass|
klass.should_receive(:new).with(:current_process).and_return(@capng)
end
end

data("dac_read_search" => [:dac_read_search, true, 1],
"dac_override" => [:dac_override, true, 1],
"chown" => [:chown, false, 0],
)
test "with partially elevated privileges" do |data|
cap, result, readable_paths = data
@capng.update(:add, :effective, cap)

d = create_driver(
config_element("ROOT", "", {
"path" => "/var/log/ker*.log", # Use /var/log/kern.log
"tag" => "t1",
"rotate_wait" => "2s"
}) + PARSE_SINGLE_LINE_CONFIG, false)

assert_equal readable_paths, d.instance.expand_paths.length
assert_equal result, d.instance.have_read_capability?
end
end

def test_pos_file_dir_creation
config = config_element("", "", {
"tag" => "tail",
Expand Down
74 changes: 74 additions & 0 deletions test/test_capability.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
require_relative 'helper'
require 'fluent/test'
require 'fluent/capability'

class FluentCapabilityTest < ::Test::Unit::TestCase
setup do
@capability = Fluent::Capability.new(:current_process)
omit "Fluent::Capability class is not usable on this environment" unless @capability.usable?
end

sub_test_case "check capability" do
test "effective" do
@capability.clear(:both)
assert_true @capability.update(:add, :effective, :dac_read_search)
assert_equal CapNG::Result::PARTIAL, @capability.have_capabilities?(:caps)
assert_nothing_raised do
@capability.apply(:caps)
end
assert_equal CapNG::Result::NONE, @capability.have_capabilities?(:bounds)
assert_true @capability.have_capability?(:effective, :dac_read_search)
assert_false @capability.have_capability?(:inheritable, :dac_read_search)
assert_false @capability.have_capability?(:permitted, :dac_read_search)
end

test "inheritable" do
@capability.clear(:both)
capabilities = [:chown, :dac_override]
assert_equal [true, true], @capability.update(:add, :inheritable, capabilities)
assert_equal CapNG::Result::NONE, @capability.have_capabilities?(:caps)
assert_nothing_raised do
@capability.apply(:caps)
end
assert_equal CapNG::Result::NONE, @capability.have_capabilities?(:bounds)
capabilities.each do |capability|
assert_false @capability.have_capability?(:effective, capability)
assert_true @capability.have_capability?(:inheritable, capability)
assert_false @capability.have_capability?(:permitted, capability)
end
end

test "permitted" do
@capability.clear(:both)
capabilities = [:fowner, :fsetid, :kill]
assert_equal [true, true, true], @capability.update(:add, :permitted, capabilities)
assert_equal CapNG::Result::NONE, @capability.have_capabilities?(:caps)
assert_nothing_raised do
@capability.apply(:caps)
end
assert_equal CapNG::Result::NONE, @capability.have_capabilities?(:bounds)
capabilities.each do |capability|
assert_false @capability.have_capability?(:effective, capability)
assert_false @capability.have_capability?(:inheritable, capability)
assert_true @capability.have_capability?(:permitted, capability)
end
end

test "effective/inheritable/permitted" do
@capability.clear(:both)
capabilities = [:setpcap, :net_admin, :net_raw, :sys_boot, :sys_time]
update_type = CapNG::Type::EFFECTIVE | CapNG::Type::INHERITABLE | CapNG::Type::PERMITTED
assert_equal [true, true, true, true, true], @capability.update(:add, update_type, capabilities)
assert_equal CapNG::Result::PARTIAL, @capability.have_capabilities?(:caps)
assert_nothing_raised do
@capability.apply(:caps)
end
assert_equal CapNG::Result::NONE, @capability.have_capabilities?(:bounds)
capabilities.each do |capability|
assert_true @capability.have_capability?(:effective, capability)
assert_true @capability.have_capability?(:inheritable, capability)
assert_true @capability.have_capability?(:permitted, capability)
end
end
end
end

0 comments on commit 7e88922

Please sign in to comment.