mirror of
https://github.com/google/sandboxed-api.git
synced 2024-03-22 13:11:30 +08:00
Now network proxy server supports IP filtering. API to policybuilder is added to make a list of allowed pairs of allowed IP, mask and port where mask and port are optional.
PiperOrigin-RevId: 296206385 Change-Id: I53b23122abece1fe318ed4c6a7e37bf3228c8f5f
This commit is contained in:
parent
5d81c822d8
commit
5a4e3f3d29
|
@ -89,7 +89,7 @@ cc_library(
|
|||
":syscall",
|
||||
":util",
|
||||
"//sandboxed_api/util:status",
|
||||
"@com_google_absl//absl/base",
|
||||
"//sandboxed_api/util:statusor",
|
||||
"@com_google_absl//absl/memory",
|
||||
"@com_google_absl//absl/strings",
|
||||
],
|
||||
|
@ -127,36 +127,6 @@ cc_library(
|
|||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "network_proxy_server",
|
||||
srcs = ["network_proxy_server.cc"],
|
||||
hdrs = ["network_proxy_server.h"],
|
||||
copts = sapi_platform_copts(),
|
||||
deps = [
|
||||
":comms",
|
||||
"//sandboxed_api/sandbox2/util:fileops",
|
||||
"@com_google_absl//absl/memory",
|
||||
"@com_google_glog//:glog",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "network_proxy_client",
|
||||
srcs = ["network_proxy_client.cc"],
|
||||
hdrs = ["network_proxy_client.h"],
|
||||
copts = sapi_platform_copts(),
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":comms",
|
||||
"//sandboxed_api/sandbox2/util:strerror",
|
||||
"//sandboxed_api/util:status",
|
||||
"@com_google_absl//absl/memory",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
"@com_google_glog//:glog",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "ipc",
|
||||
srcs = ["ipc.cc"],
|
||||
|
@ -166,8 +136,6 @@ cc_library(
|
|||
":comms",
|
||||
":logserver",
|
||||
":logsink",
|
||||
":network_proxy_client",
|
||||
":network_proxy_server",
|
||||
"@com_google_absl//absl/base:core_headers",
|
||||
"@com_google_absl//absl/memory",
|
||||
"@com_google_absl//absl/strings",
|
||||
|
@ -186,6 +154,7 @@ cc_library(
|
|||
":regs",
|
||||
":syscall",
|
||||
":violation_cc_proto",
|
||||
"//sandboxed_api/sandbox2/network_proxy:filtering",
|
||||
"//sandboxed_api/sandbox2/util:bpf_helper",
|
||||
"//sandboxed_api/util:flags",
|
||||
"@com_google_absl//absl/base:core_headers",
|
||||
|
@ -306,10 +275,10 @@ cc_library(
|
|||
":client",
|
||||
":executor",
|
||||
":comms",
|
||||
":violation_cc_proto",
|
||||
":forkserver",
|
||||
":forkserver_cc_proto",
|
||||
":global_forkserver",
|
||||
":violation_cc_proto",
|
||||
":forkserver",
|
||||
":ipc",
|
||||
":limits",
|
||||
":logsink",
|
||||
|
@ -321,7 +290,6 @@ cc_library(
|
|||
":result",
|
||||
":syscall",
|
||||
":util",
|
||||
":network_proxy_client",
|
||||
"@com_google_absl//absl/base:core_headers",
|
||||
"@com_google_absl//absl/container:flat_hash_map",
|
||||
"@com_google_absl//absl/container:flat_hash_set",
|
||||
|
@ -333,11 +301,15 @@ cc_library(
|
|||
"@com_google_absl//absl/time",
|
||||
"@com_google_absl//absl/types:optional",
|
||||
"@org_kernel_libcap//:libcap",
|
||||
"//sandboxed_api/sandbox2/network_proxy:client",
|
||||
"//sandboxed_api/sandbox2/network_proxy:filtering",
|
||||
"//sandboxed_api/sandbox2/network_proxy:server",
|
||||
"//sandboxed_api/sandbox2/unwind",
|
||||
"//sandboxed_api/sandbox2/unwind:unwind_cc_proto",
|
||||
"//sandboxed_api/sandbox2/util:bpf_helper",
|
||||
"//sandboxed_api/sandbox2/util:file_base",
|
||||
"//sandboxed_api/sandbox2/util:fileops",
|
||||
"//sandboxed_api/sandbox2/util:strerror",
|
||||
"//sandboxed_api/util:raw_logging",
|
||||
"//sandboxed_api/util:status",
|
||||
"//sandboxed_api/util:statusor",
|
||||
|
@ -364,7 +336,7 @@ cc_library(
|
|||
deps = [
|
||||
":comms",
|
||||
":logsink",
|
||||
":network_proxy_client",
|
||||
"//sandboxed_api/sandbox2/network_proxy:client",
|
||||
"//sandboxed_api/sandbox2/util:file_helpers",
|
||||
"//sandboxed_api/sandbox2/util:fileops",
|
||||
"//sandboxed_api/sandbox2/util:strerror",
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
add_subdirectory(examples)
|
||||
add_subdirectory(unwind)
|
||||
add_subdirectory(util)
|
||||
add_subdirectory(network_proxy)
|
||||
|
||||
# sandboxed_api/sandbox2:bpfdisassembler
|
||||
add_library(sandbox2_bpfdisassembler STATIC
|
||||
|
@ -121,36 +122,6 @@ target_link_libraries(sandbox2_logsink
|
|||
PUBLIC glog::glog
|
||||
)
|
||||
|
||||
# sandboxed_api/sandbox2:network_proxy_server
|
||||
add_library(sandbox2_network_proxy_server STATIC
|
||||
network_proxy_server.cc
|
||||
network_proxy_server.h
|
||||
)
|
||||
add_library(sandbox2::network_proxy_server ALIAS sandbox2_network_proxy_server)
|
||||
target_link_libraries(sandbox2_network_proxy_server PRIVATE
|
||||
absl::memory
|
||||
glog::glog
|
||||
sandbox2::comms
|
||||
sandbox2::fileops
|
||||
sapi::base
|
||||
)
|
||||
|
||||
# sandboxed_api/sandbox2:network_proxy_client
|
||||
add_library(sandbox2_network_proxy_client STATIC
|
||||
network_proxy_client.cc
|
||||
network_proxy_client.h
|
||||
)
|
||||
add_library(sandbox2::network_proxy_client ALIAS sandbox2_network_proxy_client)
|
||||
target_link_libraries(sandbox2_network_proxy_client PRIVATE
|
||||
absl::strings
|
||||
absl::synchronization
|
||||
glog::glog
|
||||
sandbox2::comms
|
||||
sandbox2::strerror
|
||||
sapi::base
|
||||
sapi::status
|
||||
)
|
||||
|
||||
# sandboxed_api/sandbox2:ipc
|
||||
add_library(sandbox2_ipc STATIC
|
||||
ipc.cc
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
#include "absl/strings/str_join.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "sandboxed_api/sandbox2/comms.h"
|
||||
#include "sandboxed_api/sandbox2/network_proxy_client.h"
|
||||
#include "sandboxed_api/sandbox2/sanitizer.h"
|
||||
#include "sandboxed_api/sandbox2/util/strerror.h"
|
||||
#include "sandboxed_api/util/raw_logging.h"
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
#include "sandboxed_api/sandbox2/comms.h"
|
||||
#include "sandboxed_api/sandbox2/logsink.h"
|
||||
#include "sandboxed_api/sandbox2/network_proxy_client.h"
|
||||
#include "sandboxed_api/sandbox2/network_proxy/client.h"
|
||||
|
||||
namespace sandbox2 {
|
||||
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
|
||||
# The 'network proxy' example demonstrates how to use network proxy server.
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load("//sandboxed_api/bazel:build_defs.bzl", "sapi_platform_copts")
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
# Executor
|
||||
cc_binary(
|
||||
name = "networkproxy_sandbox",
|
||||
|
@ -27,6 +27,7 @@ cc_binary(
|
|||
deps = [
|
||||
"//sandboxed_api/sandbox2",
|
||||
"//sandboxed_api/sandbox2:comms",
|
||||
"//sandboxed_api/sandbox2/network_proxy:filtering",
|
||||
"//sandboxed_api/sandbox2/util:bpf_helper",
|
||||
"//sandboxed_api/sandbox2/util:fileops",
|
||||
"//sandboxed_api/sandbox2/util:runfiles",
|
||||
|
@ -42,7 +43,7 @@ cc_binary(
|
|||
deps = [
|
||||
"//sandboxed_api/sandbox2:client",
|
||||
"//sandboxed_api/sandbox2:comms",
|
||||
"//sandboxed_api/sandbox2:network_proxy_client",
|
||||
"//sandboxed_api/sandbox2/network_proxy:client",
|
||||
"//sandboxed_api/sandbox2/util:fileops",
|
||||
"@com_google_absl//absl/strings:str_format",
|
||||
],
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include "absl/strings/str_format.h"
|
||||
#include "sandboxed_api/sandbox2/client.h"
|
||||
#include "sandboxed_api/sandbox2/comms.h"
|
||||
#include "sandboxed_api/sandbox2/network_proxy_client.h"
|
||||
#include "sandboxed_api/sandbox2/network_proxy/client.h"
|
||||
#include "sandboxed_api/sandbox2/util/fileops.h"
|
||||
|
||||
static ssize_t ReadFromFd(int fd, uint8_t* buf, size_t size) {
|
||||
|
@ -113,5 +113,6 @@ int main(int argc, char** argv) {
|
|||
if (!CommunicationTest(client.get())) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ std::unique_ptr<sandbox2::Policy> GetPolicy(absl::string_view sandboxee_path) {
|
|||
.AllowSyscall(__NR_munmap)
|
||||
.AllowSyscall(__NR_getpid)
|
||||
.AddNetworkProxyHandlerPolicy()
|
||||
.AllowTcMalloc()
|
||||
.AllowIPv6("::1")
|
||||
.AddLibrariesForBinary(sandboxee_path)
|
||||
.BuildOrDie();
|
||||
}
|
||||
|
@ -129,10 +131,7 @@ int main(int argc, char** argv) {
|
|||
// is capable of enabling sandboxing on its own).
|
||||
->set_enable_sandbox_before_exec(false)
|
||||
// Set cwd to / to get rids of warnings connected with file namespace.
|
||||
.set_cwd("/")
|
||||
// Enable built-in network proxy.
|
||||
.ipc()
|
||||
->EnableNetworkProxyServer();
|
||||
.set_cwd("/");
|
||||
executor
|
||||
->limits()
|
||||
// Remove restrictions on the size of address-space of sandboxed
|
||||
|
|
|
@ -17,16 +17,12 @@
|
|||
#include "sandboxed_api/sandbox2/ipc.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <thread> // NOLINT(build/c++11)
|
||||
#include <thread>
|
||||
|
||||
#include <glog/logging.h>
|
||||
#include "absl/memory/memory.h"
|
||||
#include "sandboxed_api/sandbox2/logserver.h"
|
||||
#include "sandboxed_api/sandbox2/logsink.h"
|
||||
#include "sandboxed_api/sandbox2/network_proxy_client.h"
|
||||
#include "sandboxed_api/sandbox2/network_proxy_server.h"
|
||||
|
||||
namespace sandbox2 {
|
||||
|
||||
|
@ -103,16 +99,4 @@ void IPC::EnableLogServer() {
|
|||
log_thread.detach();
|
||||
}
|
||||
|
||||
void IPC::EnableNetworkProxyServer() {
|
||||
int fd = ReceiveFd(NetworkProxyClient::kFDName);
|
||||
|
||||
auto proxy_server = [fd]() {
|
||||
NetworkProxyServer network_proxy_server(fd);
|
||||
network_proxy_server.Run();
|
||||
};
|
||||
|
||||
std::thread proxy_thread{proxy_server};
|
||||
proxy_thread.detach();
|
||||
}
|
||||
|
||||
} // namespace sandbox2
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/macros.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "sandboxed_api/sandbox2/comms.h"
|
||||
|
||||
|
@ -60,10 +59,6 @@ class IPC final {
|
|||
// Client::SendLogsToSupervisor in the sandboxee.
|
||||
void EnableLogServer();
|
||||
|
||||
// Enable network proxy server, this will start a thread in the sandbox
|
||||
// that waits for connection requests from the sandboxee.
|
||||
void EnableNetworkProxyServer();
|
||||
|
||||
private:
|
||||
friend class Executor;
|
||||
friend class Monitor;
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
#include "sandboxed_api/sandbox2/limits.h"
|
||||
#include "sandboxed_api/sandbox2/mounts.h"
|
||||
#include "sandboxed_api/sandbox2/namespace.h"
|
||||
#include "sandboxed_api/sandbox2/network_proxy/server.h"
|
||||
#include "sandboxed_api/sandbox2/policy.h"
|
||||
#include "sandboxed_api/sandbox2/regs.h"
|
||||
#include "sandboxed_api/sandbox2/result.h"
|
||||
|
@ -140,6 +141,9 @@ Monitor::~Monitor() {
|
|||
if (log_file_) {
|
||||
std::fclose(log_file_);
|
||||
}
|
||||
if (network_proxy_server_) {
|
||||
network_proxy_thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -198,6 +202,10 @@ void Monitor::Run() {
|
|||
// sandbox master/monitor, which ptrace_attach'es to the child.
|
||||
int clone_flags = CLONE_UNTRACED;
|
||||
|
||||
if (policy_->allowed_hosts_) {
|
||||
EnableNetworkProxyServer();
|
||||
}
|
||||
|
||||
// Get PID of the sandboxee.
|
||||
pid_t init_pid = 0;
|
||||
Namespace* ns = policy_->GetNamespace();
|
||||
|
@ -349,6 +357,14 @@ void Monitor::MainLoop(sigset_t* sset) {
|
|||
KillSandboxee();
|
||||
}
|
||||
|
||||
if (network_proxy_server_ &&
|
||||
network_proxy_server_->violation_occurred_.load(
|
||||
std::memory_order_acquire) &&
|
||||
!network_violation_) {
|
||||
network_violation_ = true;
|
||||
KillSandboxee();
|
||||
}
|
||||
|
||||
// It should be a non-blocking operation (hence WNOHANG), so this function
|
||||
// returns quickly if there are no events to be processed.
|
||||
// Prioritize main pid to avoid resource starvation
|
||||
|
@ -401,7 +417,10 @@ void Monitor::MainLoop(sigset_t* sset) {
|
|||
VLOG(1) << "PID: " << ret << " terminated with signal: "
|
||||
<< util::GetSignalName(WTERMSIG(status));
|
||||
if (ret == pid_) {
|
||||
if (external_kill_) {
|
||||
if (network_violation_) {
|
||||
SetExitStatusCode(Result::VIOLATION, Result::VIOLATION_NETWORK);
|
||||
result_.SetNetworkViolation(network_proxy_server_->violation_msg_);
|
||||
} else if (external_kill_) {
|
||||
SetExitStatusCode(Result::EXTERNAL_KILL, 0);
|
||||
} else if (timed_out_) {
|
||||
SetExitStatusCode(Result::TIMEOUT, 0);
|
||||
|
@ -794,7 +813,10 @@ void Monitor::EventPtraceExit(pid_t pid, int event_msg) {
|
|||
// 3) Regular signal/other exit cause.
|
||||
if (pid == pid_) {
|
||||
VLOG(1) << "PID: " << pid << " main special exit";
|
||||
if (external_kill_) {
|
||||
if (network_violation_) {
|
||||
SetExitStatusCode(Result::VIOLATION, Result::VIOLATION_NETWORK);
|
||||
result_.SetNetworkViolation(network_proxy_server_->violation_msg_);
|
||||
} else if (external_kill_) {
|
||||
SetExitStatusCode(Result::EXTERNAL_KILL, 0);
|
||||
} else if (timed_out_) {
|
||||
SetExitStatusCode(Result::TIMEOUT, 0);
|
||||
|
@ -926,4 +948,14 @@ void Monitor::LogSyscallViolationExplanation(const Syscall& syscall) const {
|
|||
}
|
||||
}
|
||||
|
||||
void Monitor::EnableNetworkProxyServer() {
|
||||
int fd = ipc_->ReceiveFd(NetworkProxyClient::kFDName);
|
||||
|
||||
network_proxy_server_ = absl::make_unique<NetworkProxyServer>(
|
||||
fd, &policy_->allowed_hosts_.value(), pthread_self());
|
||||
|
||||
network_proxy_thread_ = std::thread(&NetworkProxyServer::Run,
|
||||
network_proxy_server_.get());
|
||||
}
|
||||
|
||||
} // namespace sandbox2
|
||||
|
|
|
@ -26,11 +26,13 @@
|
|||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "absl/synchronization/notification.h"
|
||||
#include "sandboxed_api/sandbox2/comms.h"
|
||||
#include "sandboxed_api/sandbox2/executor.h"
|
||||
#include "sandboxed_api/sandbox2/ipc.h"
|
||||
#include "sandboxed_api/sandbox2/network_proxy/server.h"
|
||||
#include "sandboxed_api/sandbox2/notify.h"
|
||||
#include "sandboxed_api/sandbox2/policy.h"
|
||||
#include "sandboxed_api/sandbox2/regs.h"
|
||||
|
@ -139,6 +141,10 @@ class Monitor final {
|
|||
// Processes stop path.
|
||||
void EventPtraceStop(pid_t pid, int stopsig);
|
||||
|
||||
// Enable network proxy server, this will start a thread in the sandbox
|
||||
// that waits for connection requests from the sandboxee.
|
||||
void EnableNetworkProxyServer();
|
||||
|
||||
// Internal objects, owned by the Sandbox2 object.
|
||||
Executor* executor_;
|
||||
Notify* notify_;
|
||||
|
@ -168,6 +174,8 @@ class Monitor final {
|
|||
std::atomic<int64_t> deadline_millis_{0};
|
||||
// Was external kill sent to the sandboxee
|
||||
bool external_kill_ = false;
|
||||
// Network violation occurred and process of killing sandboxee started
|
||||
bool network_violation_ = false;
|
||||
// Is the sandboxee timed out
|
||||
bool timed_out_ = false;
|
||||
// Should we dump the main sandboxed PID's stack?
|
||||
|
@ -178,6 +186,12 @@ class Monitor final {
|
|||
// Log file specified by
|
||||
// --sandbox_danger_danger_permit_all_and_log flag.
|
||||
FILE* log_file_ = nullptr;
|
||||
|
||||
// Handle to the class responsible for proxying and validating connect()
|
||||
// requests.
|
||||
std::unique_ptr<NetworkProxyServer> network_proxy_server_;
|
||||
|
||||
std::thread network_proxy_thread_;
|
||||
};
|
||||
|
||||
} // namespace sandbox2
|
||||
|
|
80
sandboxed_api/sandbox2/network_proxy/BUILD.bazel
Normal file
80
sandboxed_api/sandbox2/network_proxy/BUILD.bazel
Normal file
|
@ -0,0 +1,80 @@
|
|||
# Copyright 2019 Google LLC. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
load("//sandboxed_api/bazel:build_defs.bzl", "sapi_platform_copts")
|
||||
|
||||
package(default_visibility = [
|
||||
"//sandboxed_api/sandbox2:__subpackages__",
|
||||
])
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
|
||||
cc_library(
|
||||
name = "server",
|
||||
srcs = ["server.cc"],
|
||||
hdrs = ["server.h"],
|
||||
copts = sapi_platform_copts(),
|
||||
deps = [
|
||||
":filtering",
|
||||
"//sandboxed_api/sandbox2:comms",
|
||||
"//sandboxed_api/sandbox2/util:fileops",
|
||||
"@com_google_absl//absl/memory",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_glog//:glog",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "client",
|
||||
srcs = ["client.cc"],
|
||||
hdrs = ["client.h"],
|
||||
copts = sapi_platform_copts(),
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//sandboxed_api/sandbox2:comms",
|
||||
"//sandboxed_api/sandbox2/util:strerror",
|
||||
"//sandboxed_api/util:status",
|
||||
"@com_google_absl//absl/memory",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
"@com_google_glog//:glog",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "filtering",
|
||||
srcs = ["filtering.cc"],
|
||||
hdrs = ["filtering.h"],
|
||||
copts = sapi_platform_copts(),
|
||||
deps = [
|
||||
"//sandboxed_api/sandbox2:comms",
|
||||
"//sandboxed_api/sandbox2/util:strerror",
|
||||
"//sandboxed_api/util:status",
|
||||
"//sandboxed_api/util:statusor",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_glog//:glog",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "filtering_test",
|
||||
srcs = ["filtering_test.cc"],
|
||||
copts = sapi_platform_copts(),
|
||||
deps = [
|
||||
":filtering",
|
||||
"//sandboxed_api/sandbox2:testing",
|
||||
"//sandboxed_api/util:status_matchers",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
78
sandboxed_api/sandbox2/network_proxy/CMakeLists.txt
Normal file
78
sandboxed_api/sandbox2/network_proxy/CMakeLists.txt
Normal file
|
@ -0,0 +1,78 @@
|
|||
# Copyright 2019 Google LLC. All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
# sandboxed_api/sandbox2/network_proxy:server
|
||||
add_library(sandbox2_network_proxy_server STATIC
|
||||
server.cc
|
||||
server.h
|
||||
)
|
||||
add_library(sandbox2::network_proxy_server ALIAS sandbox2_network_proxy_server)
|
||||
target_link_libraries(sandbox2_network_proxy_server PRIVATE
|
||||
absl::memory
|
||||
glog::glog
|
||||
sandbox2::comms
|
||||
sandbox2::fileops
|
||||
sandbox2::network_proxy_filtering
|
||||
sapi::base
|
||||
)
|
||||
|
||||
# sandboxed_api/sandbox2/network_proxy:filtering
|
||||
add_library(sandbox2_network_proxy_filtering STATIC
|
||||
filtering.cc
|
||||
filtering.h
|
||||
)
|
||||
add_library(sandbox2::network_proxy_filtering ALIAS sandbox2_network_proxy_filtering)
|
||||
target_link_libraries(sandbox2_network_proxy_filtering PRIVATE
|
||||
absl::memory
|
||||
glog::glog
|
||||
sandbox2::comms
|
||||
sandbox2::fileops
|
||||
sapi::base
|
||||
PUBLIC sapi::status
|
||||
sapi::statusor
|
||||
)
|
||||
|
||||
# sandboxed_api/sandbox2/network_proxy:client
|
||||
add_library(sandbox2_network_proxy_client STATIC
|
||||
client.cc
|
||||
client.h
|
||||
)
|
||||
add_library(sandbox2::network_proxy_client ALIAS sandbox2_network_proxy_client)
|
||||
target_link_libraries(sandbox2_network_proxy_client PRIVATE
|
||||
absl::strings
|
||||
absl::synchronization
|
||||
glog::glog
|
||||
sandbox2::comms
|
||||
sandbox2::strerror
|
||||
sapi::base
|
||||
sapi::status
|
||||
)
|
||||
|
||||
if(SAPI_ENABLE_TESTS)
|
||||
# sandboxed_api/sandbox2/network_proxy:filtering_test
|
||||
add_executable(filtering_test
|
||||
filtering_test.cc
|
||||
)
|
||||
target_link_libraries(filtering_test PRIVATE
|
||||
absl::strings
|
||||
glog::glog
|
||||
gflags::gflags
|
||||
sandbox2::network_proxy_filtering
|
||||
sandbox2::testing
|
||||
sapi::base
|
||||
sapi::status_matchers
|
||||
sapi::test_main
|
||||
)
|
||||
gtest_discover_tests(filtering_test)
|
||||
endif()
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "sandboxed_api/sandbox2/network_proxy_client.h"
|
||||
#include "sandboxed_api/sandbox2/network_proxy/client.h"
|
||||
|
||||
#include <linux/net.h>
|
||||
#include <linux/seccomp.h>
|
278
sandboxed_api/sandbox2/network_proxy/filtering.cc
Normal file
278
sandboxed_api/sandbox2/network_proxy/filtering.cc
Normal file
|
@ -0,0 +1,278 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "sandboxed_api/sandbox2/network_proxy/filtering.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <glog/logging.h>
|
||||
#include "absl/strings/numbers.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "sandboxed_api/sandbox2/util/strerror.h"
|
||||
#include "sandboxed_api/util/status.h"
|
||||
#include "sandboxed_api/util/status_macros.h"
|
||||
|
||||
namespace sandbox2 {
|
||||
|
||||
static sapi::StatusOr<std::string> Addr6ToString(
|
||||
const struct sockaddr_in6* saddr) {
|
||||
char addr[INET6_ADDRSTRLEN];
|
||||
int port = htons(saddr->sin6_port);
|
||||
if (!inet_ntop(AF_INET6, &saddr->sin6_addr, addr, sizeof addr)) {
|
||||
return sapi::InternalError(
|
||||
"Error in converting sockaddr_in6 addres to string");
|
||||
}
|
||||
return absl::StrCat("IP: ", addr, ", port: ", port);
|
||||
}
|
||||
|
||||
// Converts sockaddr_in structure into a string IPv4 representation.
|
||||
static sapi::StatusOr<std::string> Addr4ToString(
|
||||
const struct sockaddr_in* saddr) {
|
||||
char addr[INET_ADDRSTRLEN];
|
||||
int port = htons(saddr->sin_port);
|
||||
if (!inet_ntop(AF_INET, &saddr->sin_addr, addr, sizeof addr)) {
|
||||
return sapi::InternalError(
|
||||
"Error in converting sockaddr_in addres to string");
|
||||
}
|
||||
return absl::StrCat("IP: ", addr, ", port: ", port);
|
||||
}
|
||||
|
||||
// Converts sockaddr_in6 structure into a string IPv6 representation.
|
||||
sapi::StatusOr<std::string> AddrToString(const struct sockaddr* saddr) {
|
||||
switch (saddr->sa_family) {
|
||||
case AF_INET:
|
||||
return Addr4ToString(reinterpret_cast<const struct sockaddr_in*>(saddr));
|
||||
case AF_INET6:
|
||||
return Addr6ToString(reinterpret_cast<const struct sockaddr_in6*>(saddr));
|
||||
default:
|
||||
return sapi::InternalError(
|
||||
absl::StrCat("Unexpected sa_family value: ", saddr->sa_family));
|
||||
}
|
||||
}
|
||||
|
||||
static sapi::Status IPStringToAddr(const std::string& ip, int address_family,
|
||||
void* addr) {
|
||||
int err = inet_pton(address_family, ip.c_str(), addr);
|
||||
if (err == 0) {
|
||||
return sapi::InvalidArgumentError(absl::StrCat("Invalid address: ", ip));
|
||||
}
|
||||
if (err == -1) {
|
||||
return sapi::InternalError(
|
||||
absl::StrCat("inet_pton() failed for ", ip, ": ", StrError(errno)));
|
||||
}
|
||||
|
||||
return sapi::OkStatus();
|
||||
}
|
||||
|
||||
// Parses a string of type IP or IP/mask or IP/cidr and saves appropriate
|
||||
// values in output arguments.
|
||||
static sapi::Status ParseIpAndMask(const std::string& ip_and_mask,
|
||||
std::string* ip, std::string* mask,
|
||||
uint32_t* cidr) {
|
||||
// mask is checked later because only IPv4 format supports mask
|
||||
if (ip == nullptr || cidr == nullptr) {
|
||||
return sapi::InvalidArgumentError(
|
||||
"ip and cidr arguments of ParseIpAndMask cannot be nullptr");
|
||||
}
|
||||
*cidr = 0;
|
||||
|
||||
std::vector<std::string> ip_and_mask_split =
|
||||
absl::StrSplit(ip_and_mask, absl::MaxSplits('/', 1));
|
||||
|
||||
*ip = ip_and_mask_split[0];
|
||||
if (ip_and_mask_split.size() == 1) {
|
||||
return sapi::OkStatus();
|
||||
}
|
||||
std::string mask_or_cidr = ip_and_mask_split[1];
|
||||
|
||||
const bool has_dot = mask_or_cidr.find(".") == absl::string_view::npos;
|
||||
if (has_dot) { // mask_or_cidr is cidr
|
||||
bool res = absl::SimpleAtoi<uint32_t>(mask_or_cidr, cidr);
|
||||
if (!res || !*cidr) {
|
||||
return sapi::InvalidArgumentError(
|
||||
absl::StrCat(mask_or_cidr, " is not a correct cidr"));
|
||||
}
|
||||
} else {
|
||||
if (mask == nullptr) {
|
||||
return sapi::InvalidArgumentError(
|
||||
"mask argument of ParseIpAndMask cannot be NULL in this case");
|
||||
}
|
||||
*mask = std::string(mask_or_cidr);
|
||||
}
|
||||
return sapi::OkStatus();
|
||||
}
|
||||
|
||||
static sapi::Status CidrToIn6Addr(uint32_t cidr, in6_addr* addr) {
|
||||
if (cidr > 128) {
|
||||
return sapi::InvalidArgumentError(
|
||||
absl::StrCat(cidr, " is not a correct cidr"));
|
||||
}
|
||||
|
||||
memset(addr, 0, sizeof(*addr));
|
||||
|
||||
int i = 0;
|
||||
while (cidr >= 8) {
|
||||
addr->s6_addr[i++] = 0xff;
|
||||
cidr -= 8;
|
||||
}
|
||||
if (cidr) {
|
||||
uint8_t tmp = 0x0;
|
||||
while (cidr--) {
|
||||
tmp >>= 1;
|
||||
tmp |= 0x80;
|
||||
}
|
||||
addr->s6_addr[i] = tmp;
|
||||
}
|
||||
return sapi::OkStatus();
|
||||
}
|
||||
|
||||
static sapi::Status CidrToInAddr(uint32_t cidr, in_addr* addr) {
|
||||
if (cidr > 32) {
|
||||
return sapi::InvalidArgumentError(
|
||||
absl::StrCat(cidr, " is not a correct cidr"));
|
||||
}
|
||||
|
||||
memset(addr, 0, sizeof(*addr));
|
||||
|
||||
uint32_t tmp = 0x0;
|
||||
while (cidr--) {
|
||||
tmp >>= 1;
|
||||
tmp |= 0x80000000;
|
||||
}
|
||||
addr->s_addr = htonl(tmp);
|
||||
return sapi::OkStatus();
|
||||
}
|
||||
|
||||
static bool IsIPv4MaskCorrect(in_addr_t m) {
|
||||
m = ntohl(m);
|
||||
if (m == 0) {
|
||||
return false;
|
||||
}
|
||||
m = ~m + 1;
|
||||
return !(m & (m - 1));
|
||||
}
|
||||
|
||||
sapi::Status AllowedHosts::AllowIPv4(const std::string& ip_and_mask,
|
||||
uint32_t port) {
|
||||
std::string ip, mask;
|
||||
uint32_t cidr;
|
||||
SAPI_RETURN_IF_ERROR(ParseIpAndMask(ip_and_mask, &ip, &mask, &cidr));
|
||||
SAPI_RETURN_IF_ERROR(AllowIPv4(ip, mask, cidr, port));
|
||||
|
||||
return sapi::OkStatus();
|
||||
}
|
||||
|
||||
sapi::Status AllowedHosts::AllowIPv6(const std::string& ip_and_mask,
|
||||
uint32_t port) {
|
||||
std::string ip;
|
||||
uint32_t cidr;
|
||||
SAPI_RETURN_IF_ERROR(ParseIpAndMask(ip_and_mask, &ip, NULL, &cidr));
|
||||
SAPI_RETURN_IF_ERROR(AllowIPv6(ip, cidr, port));
|
||||
return sapi::OkStatus();
|
||||
}
|
||||
|
||||
sapi::Status AllowedHosts::AllowIPv4(const std::string& ip,
|
||||
const std::string& mask, uint32_t cidr,
|
||||
uint32_t port) {
|
||||
in_addr addr{};
|
||||
in_addr m{};
|
||||
|
||||
if (mask.length()) {
|
||||
SAPI_RETURN_IF_ERROR(IPStringToAddr(mask, AF_INET, &m));
|
||||
|
||||
if (!IsIPv4MaskCorrect(m.s_addr)) {
|
||||
return sapi::InvalidArgumentError(
|
||||
absl::StrCat(mask, " is not a correct mask"));
|
||||
}
|
||||
|
||||
} else {
|
||||
if (cidr > 32) {
|
||||
return sapi::InvalidArgumentError(
|
||||
absl::StrCat(cidr, " is not a correct cidr"));
|
||||
}
|
||||
if (!cidr) {
|
||||
cidr = 32;
|
||||
}
|
||||
|
||||
SAPI_RETURN_IF_ERROR(CidrToInAddr(cidr, &m));
|
||||
}
|
||||
|
||||
SAPI_RETURN_IF_ERROR(IPStringToAddr(ip, AF_INET, &addr));
|
||||
allowed_IPv4_.emplace_back(addr.s_addr, m.s_addr, htons(port));
|
||||
|
||||
return sapi::OkStatus();
|
||||
}
|
||||
|
||||
sapi::Status AllowedHosts::AllowIPv6(const std::string& ip, uint32_t cidr,
|
||||
uint32_t port) {
|
||||
if (cidr == 0) {
|
||||
cidr = 128;
|
||||
}
|
||||
|
||||
in6_addr addr{};
|
||||
SAPI_RETURN_IF_ERROR(IPStringToAddr(ip, AF_INET6, &addr));
|
||||
|
||||
in6_addr m;
|
||||
SAPI_RETURN_IF_ERROR(CidrToIn6Addr(cidr, &m));
|
||||
|
||||
allowed_IPv6_.emplace_back(addr, m, htons(port));
|
||||
return sapi::OkStatus();
|
||||
}
|
||||
|
||||
bool AllowedHosts::IsHostAllowed(const struct sockaddr* saddr) const {
|
||||
switch (saddr->sa_family) {
|
||||
case AF_INET:
|
||||
return IsIPv4Allowed(reinterpret_cast<const struct sockaddr_in*>(saddr));
|
||||
case AF_INET6:
|
||||
return IsIPv6Allowed(reinterpret_cast<const struct sockaddr_in6*>(saddr));
|
||||
default:
|
||||
LOG(FATAL) << absl::StrCat("Unexpected sa_family value: ",
|
||||
saddr->sa_family);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AllowedHosts::IsIPv6Allowed(const struct sockaddr_in6* saddr) const {
|
||||
auto result = std::find_if(
|
||||
allowed_IPv6_.begin(), allowed_IPv6_.end(), [saddr](const IPv6& entry) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if ((entry.ip.__in6_u.__u6_addr32[i] &
|
||||
entry.mask.__in6_u.__u6_addr32[i]) !=
|
||||
(saddr->sin6_addr.__in6_u.__u6_addr32[i] &
|
||||
entry.mask.__in6_u.__u6_addr32[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!entry.port || (entry.port == saddr->sin6_port)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
return result != allowed_IPv6_.end();
|
||||
}
|
||||
|
||||
bool AllowedHosts::IsIPv4Allowed(const struct sockaddr_in* saddr) const {
|
||||
auto result = std::find_if(
|
||||
allowed_IPv4_.begin(), allowed_IPv4_.end(), [saddr](const IPv4& entry) {
|
||||
return ((entry.ip & entry.mask) ==
|
||||
(saddr->sin_addr.s_addr & entry.mask)) &&
|
||||
(!entry.port || (entry.port == saddr->sin_port));
|
||||
});
|
||||
|
||||
return result != allowed_IPv4_.end();
|
||||
}
|
||||
|
||||
} // namespace sandbox2
|
71
sandboxed_api/sandbox2/network_proxy/filtering.h
Normal file
71
sandboxed_api/sandbox2/network_proxy/filtering.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef SANDBOXED_API_SANDBOX2_NETWORK_PROXY_FILTERING_H_
|
||||
#define SANDBOXED_API_SANDBOX2_NETWORK_PROXY_FILTERING_H_
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "sandboxed_api/sandbox2/comms.h"
|
||||
#include "sandboxed_api/util/statusor.h"
|
||||
|
||||
namespace sandbox2 {
|
||||
|
||||
// Converts sockaddr_in or sockaddr_in6 structure into a string
|
||||
// representation.
|
||||
sapi::StatusOr<std::string> AddrToString(const struct sockaddr* saddr);
|
||||
|
||||
struct IPv4 {
|
||||
in_addr_t ip;
|
||||
in_addr_t mask;
|
||||
uint32_t port;
|
||||
IPv4(in_addr_t IP, in_addr_t mask, uint32_t port)
|
||||
: ip(IP), mask(mask), port(port) {}
|
||||
};
|
||||
|
||||
struct IPv6 {
|
||||
in6_addr ip;
|
||||
in6_addr mask;
|
||||
uint32_t port;
|
||||
IPv6(in6_addr IP, in6_addr mask, uint32_t port)
|
||||
: ip(IP), mask(mask), port(port) {}
|
||||
};
|
||||
|
||||
// Keeps a list of allowed pairs of IP, mask and port. Port equal to 0 means
|
||||
// that all ports are allowed.
|
||||
class AllowedHosts {
|
||||
public:
|
||||
// ip_and_mask should have one of following formats: IP, IP/mask, IP/cidr.
|
||||
sapi::Status AllowIPv4(const std::string& ip_and_mask, uint32_t port = 0);
|
||||
// ip_and_mask should have following format: IP or IP/cidr.
|
||||
sapi::Status AllowIPv6(const std::string& ip_and_mask, uint32_t port = 0);
|
||||
// Checks if this host is allowed.
|
||||
bool IsHostAllowed(const struct sockaddr* saddr) const;
|
||||
|
||||
private:
|
||||
sapi::Status AllowIPv4(const std::string& ip, const std::string& mask,
|
||||
uint32_t cidr, uint32_t port);
|
||||
sapi::Status AllowIPv6(const std::string& ip, uint32_t cidr, uint32_t port);
|
||||
bool IsIPv4Allowed(const struct sockaddr_in* saddr) const;
|
||||
bool IsIPv6Allowed(const struct sockaddr_in6* saddr) const;
|
||||
|
||||
std::vector<IPv4> allowed_IPv4_;
|
||||
std::vector<IPv6> allowed_IPv6_;
|
||||
};
|
||||
|
||||
} // namespace sandbox2
|
||||
|
||||
#endif // SANDBOXED_API_SANDBOX2_NETWORK_PROXY_FILTERING_H_
|
130
sandboxed_api/sandbox2/network_proxy/filtering_test.cc
Normal file
130
sandboxed_api/sandbox2/network_proxy/filtering_test.cc
Normal file
|
@ -0,0 +1,130 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "sandboxed_api/sandbox2/network_proxy/filtering.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <glog/logging.h>
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "sandboxed_api/sandbox2/testing.h"
|
||||
#include "sandboxed_api/util/status_matchers.h"
|
||||
|
||||
using ::sapi::IsOk;
|
||||
using ::testing::IsFalse;
|
||||
using ::testing::IsTrue;
|
||||
|
||||
namespace sandbox2 {
|
||||
namespace {
|
||||
|
||||
static struct sockaddr* PrepareIpv6(const std::string& ip, uint32_t port = 80) {
|
||||
static struct sockaddr_in6 saddr {};
|
||||
memset(&saddr, 0, sizeof(saddr));
|
||||
|
||||
saddr.sin6_family = AF_INET6;
|
||||
saddr.sin6_port = htons(port);
|
||||
|
||||
int err = inet_pton(AF_INET6, ip.c_str(), &saddr.sin6_addr);
|
||||
CHECK_GE(err, -1);
|
||||
|
||||
return reinterpret_cast<struct sockaddr*>(&saddr);
|
||||
}
|
||||
|
||||
static struct sockaddr* PrepareIpv4(const std::string& ip, uint32_t port = 80) {
|
||||
static struct sockaddr_in saddr {};
|
||||
memset(&saddr, 0, sizeof(saddr));
|
||||
|
||||
saddr.sin_family = AF_INET;
|
||||
saddr.sin_port = htons(port);
|
||||
|
||||
int err = inet_pton(AF_INET, ip.c_str(), &saddr.sin_addr);
|
||||
CHECK_GE(err, -1);
|
||||
|
||||
return reinterpret_cast<struct sockaddr*>(&saddr);
|
||||
}
|
||||
|
||||
TEST(FilteringTest, Basic) {
|
||||
sandbox2::AllowedHosts allowed_hosts;
|
||||
|
||||
// Create rules
|
||||
EXPECT_THAT(allowed_hosts.AllowIPv4("127.0.0.1"), IsOk());
|
||||
EXPECT_THAT(allowed_hosts.AllowIPv4("127.0.0.2", 33), IsOk());
|
||||
EXPECT_THAT(allowed_hosts.AllowIPv4("120.120.120.120/255.255.255.0"), IsOk());
|
||||
EXPECT_THAT(allowed_hosts.AllowIPv4("130.130.130.130/255.255.252.0", 1000),
|
||||
IsOk());
|
||||
EXPECT_THAT(allowed_hosts.AllowIPv4("140.140.140.140/8"), IsOk());
|
||||
EXPECT_THAT(allowed_hosts.AllowIPv4("150.150.150.150/10", 123), IsOk());
|
||||
|
||||
EXPECT_THAT(allowed_hosts.AllowIPv6("::2"), IsOk());
|
||||
EXPECT_THAT(allowed_hosts.AllowIPv6("::1", 80), IsOk());
|
||||
EXPECT_THAT(allowed_hosts.AllowIPv6("0:1234:0:0:0:0:0:0/32"), IsOk());
|
||||
EXPECT_THAT(allowed_hosts.AllowIPv6("0:5678:0:0:0:0:0:0/46", 70), IsOk());
|
||||
|
||||
// IPv4 tests
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv4("130.0.0.3")),
|
||||
testing::IsFalse());
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv4("127.0.0.1")),
|
||||
testing::IsTrue());
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv4("127.0.0.2")),
|
||||
testing::IsFalse());
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv4("127.0.0.2", 33)),
|
||||
testing::IsTrue());
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv4("120.120.120.255")),
|
||||
testing::IsTrue());
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv4("120.120.121.120")),
|
||||
testing::IsFalse());
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv4("130.130.128.130", 1000)),
|
||||
testing::IsTrue());
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv4("130.130.132.134", 1000)),
|
||||
testing::IsFalse());
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv4("130.130.128.130", 1001)),
|
||||
testing::IsFalse());
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv4("140.0.140.140")),
|
||||
testing::IsTrue());
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv4("141.140.140.140")),
|
||||
testing::IsFalse());
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv4("150.182.150.150", 123)),
|
||||
testing::IsTrue());
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv4("150.214.150.150", 123)),
|
||||
testing::IsFalse());
|
||||
|
||||
// IPv6 tests
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv6("::3")),
|
||||
testing::IsFalse());
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv6("::2")),
|
||||
testing::IsTrue());
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv6("::1")),
|
||||
testing::IsTrue());
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv6("::1", 81)),
|
||||
testing::IsFalse());
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv6("0:1234:ffff:0:0:0:0:0")),
|
||||
testing::IsTrue());
|
||||
EXPECT_THAT(allowed_hosts.IsHostAllowed(PrepareIpv6("0:1233:0000:0:0:0:0:0")),
|
||||
testing::IsFalse());
|
||||
EXPECT_THAT(
|
||||
allowed_hosts.IsHostAllowed(PrepareIpv6("0:5678:0002:0:0:0:0:0", 70)),
|
||||
testing::IsTrue());
|
||||
EXPECT_THAT(
|
||||
allowed_hosts.IsHostAllowed(PrepareIpv6("0:5678:0004:0:0:0:0:0", 70)),
|
||||
testing::IsFalse());
|
||||
EXPECT_THAT(
|
||||
allowed_hosts.IsHostAllowed(PrepareIpv6("0:5678:0000:0:0:0:0:0", 2222)),
|
||||
testing::IsFalse());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace sandbox2
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "sandboxed_api/sandbox2/network_proxy_server.h"
|
||||
#include "sandboxed_api/sandbox2/network_proxy/server.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
@ -20,6 +20,7 @@
|
|||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <syscall.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
|
@ -29,8 +30,13 @@
|
|||
|
||||
namespace sandbox2 {
|
||||
|
||||
NetworkProxyServer::NetworkProxyServer(int fd)
|
||||
: comms_{absl::make_unique<Comms>(fd)}, fatal_error_{false} {}
|
||||
NetworkProxyServer::NetworkProxyServer(int fd, AllowedHosts* allowed_hosts,
|
||||
pthread_t monitor_thread_id)
|
||||
: violation_occurred_(false),
|
||||
comms_{absl::make_unique<Comms>(fd)},
|
||||
fatal_error_(false),
|
||||
monitor_thread_id_(monitor_thread_id),
|
||||
allowed_hosts_(allowed_hosts) {}
|
||||
|
||||
void NetworkProxyServer::ProcessConnectRequest() {
|
||||
std::vector<uint8_t> addr;
|
||||
|
@ -39,18 +45,22 @@ void NetworkProxyServer::ProcessConnectRequest() {
|
|||
return;
|
||||
}
|
||||
|
||||
const struct sockaddr_in* saddr =
|
||||
reinterpret_cast<const sockaddr_in*>(addr.data());
|
||||
const struct sockaddr* saddr = reinterpret_cast<const sockaddr*>(addr.data());
|
||||
|
||||
// Only IPv4 TCP and IPv6 TCP are supported.
|
||||
if (!((addr.size() == sizeof(sockaddr_in) && saddr->sin_family == AF_INET) ||
|
||||
if (!((addr.size() == sizeof(sockaddr_in) && saddr->sa_family == AF_INET) ||
|
||||
(addr.size() == sizeof(sockaddr_in6) &&
|
||||
saddr->sin_family == AF_INET6))) {
|
||||
saddr->sa_family == AF_INET6))) {
|
||||
SendError(EINVAL);
|
||||
return;
|
||||
}
|
||||
|
||||
int new_socket = socket(saddr->sin_family, SOCK_STREAM, 0);
|
||||
if (!allowed_hosts_->IsHostAllowed(saddr)) {
|
||||
NotifyViolation(saddr);
|
||||
return;
|
||||
}
|
||||
|
||||
int new_socket = socket(saddr->sa_family, SOCK_STREAM, 0);
|
||||
if (new_socket < 0) {
|
||||
SendError(errno);
|
||||
return;
|
||||
|
@ -73,7 +83,8 @@ void NetworkProxyServer::ProcessConnectRequest() {
|
|||
}
|
||||
|
||||
void NetworkProxyServer::Run() {
|
||||
while (!fatal_error_) {
|
||||
while (!fatal_error_ &&
|
||||
!violation_occurred_.load(std::memory_order_relaxed)) {
|
||||
ProcessConnectRequest();
|
||||
}
|
||||
LOG(INFO)
|
||||
|
@ -92,4 +103,14 @@ void NetworkProxyServer::NotifySuccess() {
|
|||
}
|
||||
}
|
||||
|
||||
void NetworkProxyServer::NotifyViolation(const struct sockaddr* saddr) {
|
||||
sapi::StatusOr<std::string> result = AddrToString(saddr);
|
||||
if (result.ok()) {
|
||||
violation_msg_ = result.ValueOrDie();
|
||||
} else {
|
||||
violation_msg_ = std::string(result.status().message());
|
||||
}
|
||||
violation_occurred_.store(true, std::memory_order_release);
|
||||
pthread_kill(monitor_thread_id_, SIGCHLD);
|
||||
}
|
||||
} // namespace sandbox2
|
|
@ -18,15 +18,18 @@
|
|||
#include <memory>
|
||||
|
||||
#include "sandboxed_api/sandbox2/comms.h"
|
||||
#include "sandboxed_api/sandbox2/network_proxy/filtering.h"
|
||||
|
||||
namespace sandbox2 {
|
||||
|
||||
// This is a proxy server that spawns connected sockets on requests.
|
||||
// Then it sends the file descriptor to the requestor. It is used to get around
|
||||
// limitations created by network namespaces.
|
||||
// limitations created by network namespaces. It also contains a set of rules
|
||||
// of allowed hosts.
|
||||
class NetworkProxyServer {
|
||||
public:
|
||||
explicit NetworkProxyServer(int fd);
|
||||
NetworkProxyServer(int fd, AllowedHosts* allowed_hosts,
|
||||
pthread_t monitor_thread_id);
|
||||
|
||||
NetworkProxyServer(const NetworkProxyServer&) = delete;
|
||||
NetworkProxyServer& operator=(const NetworkProxyServer&) = delete;
|
||||
|
@ -34,6 +37,11 @@ class NetworkProxyServer {
|
|||
// Starts handling incoming connection requests.
|
||||
void Run();
|
||||
|
||||
// When the network rules were violated violation_occurred_ is set and
|
||||
// violation_msg_ contains details about the host.
|
||||
std::atomic<bool> violation_occurred_;
|
||||
std::string violation_msg_;
|
||||
|
||||
private:
|
||||
// Notifies the network proxy client about the error and sends its code.
|
||||
void SendError(int saved_errno);
|
||||
|
@ -44,8 +52,15 @@ class NetworkProxyServer {
|
|||
// Serves connection requests from the network proxy client.
|
||||
void ProcessConnectRequest();
|
||||
|
||||
// Throw a violation when the network rules are subverted.
|
||||
void NotifyViolation(const struct sockaddr* saddr);
|
||||
|
||||
std::unique_ptr<Comms> comms_;
|
||||
bool fatal_error_;
|
||||
pthread_t monitor_thread_id_;
|
||||
|
||||
// Contains list of allowed to connect hosts.
|
||||
AllowedHosts* allowed_hosts_;
|
||||
};
|
||||
|
||||
} // namespace sandbox2
|
|
@ -31,6 +31,7 @@
|
|||
#include "absl/base/macros.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "sandboxed_api/sandbox2/namespace.h"
|
||||
#include "sandboxed_api/sandbox2/network_proxy/filtering.h"
|
||||
#include "sandboxed_api/sandbox2/syscall.h"
|
||||
#include "sandboxed_api/sandbox2/violation.pb.h"
|
||||
|
||||
|
@ -104,6 +105,9 @@ class Policy final {
|
|||
// Get a policy which would allow the Monitor module to track all syscalls.
|
||||
std::vector<sock_filter> GetTrackingPolicy() const;
|
||||
|
||||
// Contains a list of hosts the sandboxee is allowed to connect to.
|
||||
absl::optional<AllowedHosts> allowed_hosts_;
|
||||
|
||||
friend class Monitor;
|
||||
friend class PolicyBuilder;
|
||||
friend class PolicyBuilderPeer; // For testing
|
||||
|
|
|
@ -699,6 +699,7 @@ sapi::StatusOr<std::unique_ptr<Policy>> PolicyBuilder::TryBuild() {
|
|||
|
||||
StoreDescription(pb_description.get());
|
||||
output->policy_builder_description_ = std::move(pb_description);
|
||||
output->allowed_hosts_ = std::move(allowed_hosts_);
|
||||
already_built_ = true;
|
||||
return std::move(output);
|
||||
}
|
||||
|
@ -847,6 +848,15 @@ PolicyBuilder& PolicyBuilder::CollectStacktracesOnKill(bool enable) {
|
|||
}
|
||||
|
||||
PolicyBuilder& PolicyBuilder::AddNetworkProxyPolicy() {
|
||||
if (allowed_hosts_) {
|
||||
SetError(sapi::FailedPreconditionError(
|
||||
"AddNetworkProxyPolicy or AddNetworkProxyHandlerPolicy can be called "
|
||||
"at most once"));
|
||||
return *this;
|
||||
}
|
||||
|
||||
allowed_hosts_ = AllowedHosts();
|
||||
|
||||
AllowFutexOp(FUTEX_WAKE);
|
||||
AllowFutexOp(FUTEX_WAIT);
|
||||
AllowFutexOp(FUTEX_WAIT_BITSET);
|
||||
|
@ -919,4 +929,36 @@ void PolicyBuilder::StoreDescription(PolicyBuilderDescription* pb_description) {
|
|||
}
|
||||
}
|
||||
|
||||
PolicyBuilder& PolicyBuilder::AllowIPv4(const std::string& ip_and_mask,
|
||||
uint32_t port) {
|
||||
if (!allowed_hosts_) {
|
||||
SetError(sapi::FailedPreconditionError(
|
||||
"AddNetworkProxyPolicy or AddNetworkProxyHandlerPolicy must be called "
|
||||
"before adding IP rules"));
|
||||
return *this;
|
||||
}
|
||||
|
||||
sapi::Status status = allowed_hosts_->AllowIPv4(ip_and_mask, port);
|
||||
if (!status.ok()) {
|
||||
SetError(status);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
PolicyBuilder& PolicyBuilder::AllowIPv6(const std::string& ip_and_mask,
|
||||
uint32_t port) {
|
||||
if (!allowed_hosts_) {
|
||||
SetError(sapi::FailedPreconditionError(
|
||||
"AddNetworkProxyPolicy or AddNetworkProxyHandlerPolicy must be called "
|
||||
"before adding IP rules"));
|
||||
return *this;
|
||||
}
|
||||
|
||||
sapi::Status status = allowed_hosts_->AllowIPv6(ip_and_mask, port);
|
||||
if (!status.ok()) {
|
||||
SetError(status);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace sandbox2
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "sandboxed_api/sandbox2/mounts.h"
|
||||
#include "sandboxed_api/sandbox2/network_proxy/filtering.h"
|
||||
#include "sandboxed_api/sandbox2/policy.h"
|
||||
#include "sandboxed_api/util/statusor.h"
|
||||
|
||||
|
@ -501,6 +502,10 @@ class PolicyBuilder final {
|
|||
// Not recommended
|
||||
PolicyBuilder& SetRootWritable();
|
||||
|
||||
// Allows connections to this IP.
|
||||
PolicyBuilder& AllowIPv4(const std::string& ip_and_mask, uint32_t port = 0);
|
||||
PolicyBuilder& AllowIPv6(const std::string& ip_and_mask, uint32_t port = 0);
|
||||
|
||||
private:
|
||||
friend class PolicyBuilderPeer; // For testing
|
||||
friend class StackTracePeer;
|
||||
|
@ -542,6 +547,9 @@ class PolicyBuilder final {
|
|||
// This function returns a PolicyBuilder so that we can use it in the status
|
||||
// macros
|
||||
PolicyBuilder& SetError(const sapi::Status& status);
|
||||
|
||||
// Contains list of allowed hosts.
|
||||
absl::optional<AllowedHosts> allowed_hosts_;
|
||||
};
|
||||
|
||||
} // namespace sandbox2
|
||||
|
|
|
@ -72,11 +72,15 @@ std::string Result::ToString() const {
|
|||
ReasonCodeEnumToString(static_cast<ReasonCodeEnum>(reason_code())));
|
||||
break;
|
||||
case sandbox2::Result::VIOLATION:
|
||||
result = absl::StrCat("SYSCALL VIOLATION - Violating Syscall ",
|
||||
Syscall::GetArchDescription(GetSyscallArch()), "[",
|
||||
reason_code(), "/",
|
||||
Syscall(GetSyscallArch(), reason_code()).GetName(),
|
||||
if (reason_code() == sandbox2::Result::VIOLATION_NETWORK) {
|
||||
result = absl::StrCat("NETWORK VIOLATION: ", GetNetworkViolation());
|
||||
} else {
|
||||
result = absl::StrCat(
|
||||
"SYSCALL VIOLATION - Violating Syscall ",
|
||||
Syscall::GetArchDescription(GetSyscallArch()), "[", reason_code(),
|
||||
"/", Syscall(GetSyscallArch(), reason_code()).GetName(),
|
||||
"] Stack: ", GetStackTrace());
|
||||
}
|
||||
break;
|
||||
case sandbox2::Result::SIGNALED:
|
||||
result = absl::StrCat("Process terminated with a SIGNAL - Signal: ",
|
||||
|
@ -184,6 +188,8 @@ std::string Result::ReasonCodeEnumToString(ReasonCodeEnum value) {
|
|||
return "VIOLATION_SYSCALL";
|
||||
case sandbox2::Result::VIOLATION_ARCH:
|
||||
return "VIOLATION_ARCH";
|
||||
case sandbox2::Result::VIOLATION_NETWORK:
|
||||
return "VIOLATION_NETWORK";
|
||||
}
|
||||
return absl::StrCat("UNKNOWN: ", value);
|
||||
}
|
||||
|
|
|
@ -85,6 +85,8 @@ class Result {
|
|||
// Codes used by status=`VIOLATION`:
|
||||
VIOLATION_SYSCALL,
|
||||
VIOLATION_ARCH,
|
||||
VIOLATION_NETWORK = 0x10000000, // TODO(eternalred): temporary value, needs
|
||||
// to be big until it's fixed
|
||||
};
|
||||
|
||||
Result() = default;
|
||||
|
@ -119,6 +121,10 @@ class Result {
|
|||
syscall_ = std::move(syscall);
|
||||
}
|
||||
|
||||
void SetNetworkViolation(std::string network_violation) {
|
||||
network_violation_ = std::move(network_violation);
|
||||
}
|
||||
|
||||
StatusEnum final_status() const { return final_status_; }
|
||||
uintptr_t reason_code() const { return reason_code_; }
|
||||
|
||||
|
@ -137,6 +143,8 @@ class Result {
|
|||
|
||||
const std::string& GetProgName() const { return prog_name_; }
|
||||
|
||||
const std::string& GetNetworkViolation() const { return network_violation_; }
|
||||
|
||||
void SetProgName(const std::string& name) { prog_name_ = name; }
|
||||
|
||||
const std::string& GetProcMaps() const { return proc_maps_; }
|
||||
|
@ -179,6 +187,8 @@ class Result {
|
|||
std::string prog_name_;
|
||||
// /proc/pid/maps of the main process.
|
||||
std::string proc_maps_;
|
||||
// IP and port if network violation occurred
|
||||
std::string network_violation_;
|
||||
// Final resource usage as defined in <sys/resource.h> (man getrusage), for
|
||||
// the Monitor thread.
|
||||
rusage rusage_monitor_;
|
||||
|
|
Loading…
Reference in New Issue
Block a user