Skip to content

Commit

Permalink
Allow running tests as root, without user-switching to fix lsof tests.
Browse files Browse the repository at this point in the history
With the changes in 2e595ce to change
the gid of the running process, this broke the ability to run "lsof"
tests in Docker test environments. This is mostly because lsof can't
grab information for processes belonging to different users/groups (even
when running lsof as root) inside a Docker container unless that Docker
container is run in privileged mode (which we can't do in our CircleCI
2.0 environment).

So make the necessary tweaks so that we can always run the tests as the
current user, even if the current user is root.

While in some ways it was nice to test user-switching in our test
environment, in other ways this simply reverts testing back to how we
tested in CircleCI 1.0, where the tests were run as the current "ubuntu"
user (the current user is just root now).

As part of this, also rework the lsof port check tests a bit to get rid
of shell piping so the errors are more obvious on failures, and also
better sanity check the output to ensure we're not missing expected
ports from lsof's output.
  • Loading branch information
GUI committed May 20, 2018
1 parent 624a0b4 commit e3a3e78
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 11 deletions.
24 changes: 24 additions & 0 deletions src/api-umbrella/cli/read_config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ local deep_defaults = require "api-umbrella.utils.deep_defaults"
local deep_merge_overwrite_arrays = require "api-umbrella.utils.deep_merge_overwrite_arrays"
local dir = require "pl.dir"
local file = require "pl.file"
local getgrgid = require("posix.grp").getgrgid
local getpwuid = require("posix.pwd").getpwuid
local host_normalize = require "api-umbrella.utils.host_normalize"
local lyaml = require "lyaml"
local nillify_yaml_nulls = require "api-umbrella.utils.nillify_yaml_nulls"
Expand Down Expand Up @@ -313,6 +315,28 @@ local function set_computed_config()
config["nginx"]["_initial_proxy_read_timeout"] = config["nginx"]["proxy_read_timeout"] + 2
config["nginx"]["_initial_proxy_send_timeout"] = config["nginx"]["proxy_send_timeout"] + 2

if not config["user"] then
local euid = unistd.geteuid()
if euid then
local user = getpwuid(euid)
if user then
config["_effective_user_id"] = user.pw_uid
config["_effective_user_name"] = user.pw_name
end
end
end

if not config["group"] then
local egid = unistd.getegid()
if egid then
local group = getgrgid(egid)
if group then
config["_effective_group_id"] = group.gr_gid
config["_effective_group_name"] = group.gr_name
end
end
end

deep_merge_overwrite_arrays(config, {
_embedded_root_dir = embedded_root_dir,
_src_root_dir = src_root_dir,
Expand Down
2 changes: 1 addition & 1 deletion src/api-umbrella/cli/setup.lua
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ local function permission_check()
end
end

if effective_uid == 0 then
if effective_uid == 0 and config["app_env"] ~= "test" then
if not config["user"] or not config["group"] then
print("Must define a user and group to run worker processes as when starting with with super-user privileges")
os.exit(1)
Expand Down
8 changes: 8 additions & 0 deletions templates/etc/nginx/router.conf.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ daemon off;
{{#user}}
user {{user}} {{group}};
{{/user}}
{{^user}}
{{#_effective_user_name}}
# Even if user switching isn't enabled, make sure nginx uses the current
# effective user and group for launching the child workers (so nginx's default
# usage of the "nobody" user isn't used).
user {{_effective_user_name}} {{_effective_group_name}};
{{/_effective_user_name}}
{{/user}}

pid {{run_dir}}/nginx.pid;

Expand Down
2 changes: 1 addition & 1 deletion templates/etc/perp/elasticsearch/rc.env.mustache
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
ES_JAVA_OPTS=-Des.default.path.conf={{etc_dir}}/elasticsearch -Dmapper.allow_dots_in_name=true -XX:-HeapDumpOnOutOfMemoryError {{elasticsearch.embedded_server_env.java_opts}}
ES_JAVA_OPTS=-Des.default.path.conf={{etc_dir}}/elasticsearch -Dmapper.allow_dots_in_name=true -XX:-HeapDumpOnOutOfMemoryError {{#_test_env?}}-Des.insecure.allow.root=true{{/_test_env?}} {{elasticsearch.embedded_server_env.java_opts}}
ES_HEAP_SIZE={{elasticsearch.embedded_server_env.heap_size}}
22 changes: 19 additions & 3 deletions test/processes/test_network_binds.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,23 @@ def setup
end

def test_binds_http_to_public_interface_other_services_to_localhost
pid_path = File.join($config["run_dir"], "perpboot.pid")
output, status = run_shell("lsof -n -P -l -R -p $(pstree -p $(cat #{pid_path}) | grep -o '([0-9]\\+)' | grep -o '[0-9]\\+' | tr '\\012' ',') | grep LISTEN")
assert_equal(0, status, output)
pid = File.read(File.join($config["run_dir"], "perpboot.pid")).strip
pstree_output, pstree_status = run_shell("pstree -p #{pid}")
assert_equal(0, pstree_status, pstree_output)
pids = pstree_output.scan(/\((\d+)\)/).flatten.sort.uniq
output, _status = run_shell("lsof -n -P -l -R -a -i TCP -s TCP:LISTEN -p #{pids.join(",")}")
# lsof may return an unsuccessful exit code (since there may not be
# anything to match for all the PIDs passed in), so just sanity check the
# output.
assert_match("COMMAND", output)

listening = {
:local => Set.new,
:public => Set.new,
}
output.strip.split("\n").each do |line|
next if(line.start_with?("COMMAND"))

ip_version = line.match(/(IPv4|IPv6)/)[1]
assert(ip_version, line)

Expand Down Expand Up @@ -49,6 +57,14 @@ def test_binds_http_to_public_interface_other_services_to_localhost
"9444:IPv4",
"9444:IPv6",
], listening[:public].sort)

# Ensure all other services are listening on localhost-only, and sanity
# check to ensure some of the expected services were present in the lsof
# output.
assert_operator(listening[:local].length, :>, 0)
assert_includes(listening[:local], "#{$config.fetch("elasticsearch").fetch("embedded_server_config").fetch("http").fetch("port")}:IPv4")
assert_includes(listening[:local], "#{$config.fetch("mongodb").fetch("embedded_server_config").fetch("net").fetch("port")}:IPv4")
assert_includes(listening[:local], "#{$config.fetch("mora").fetch("port")}:IPv4")
assert_includes(listening[:local], "#{$config.fetch("trafficserver").fetch("port")}:IPv4")
end
end
6 changes: 0 additions & 6 deletions test/support/api_umbrella_test_helpers/process.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,6 @@ def self.start
computed = {
"root_dir" => TEST_RUN_API_UMBRELLA_ROOT,
}
if(::Process.euid == 0)
# If tests are running as root (Docker environment), then add the
# user to run things as.
computed["user"] = "api-umbrella"
computed["group"] = "api-umbrella"
end
File.write(CONFIG_COMPUTED_PATH, YAML.dump(computed))
$config.deep_merge!(YAML.load_file(CONFIG_COMPUTED_PATH))

Expand Down

0 comments on commit e3a3e78

Please sign in to comment.