From 7e126e8d0363ed5e89a382ae95d4babe4deeeb94 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Wed, 1 Sep 2021 11:36:07 +0200 Subject: [PATCH] logging: new mode -l passthrough when passthrough is specified, the std stream files are passed directly to the container without conmon intercepting them. Signed-off-by: Giuseppe Scrivano --- runner/conmon/conmon.go | 2 + runner/conmon/options.go | 7 +++ runner/conmon_test/ctr_logs_test.go | 5 +++ runner/conmon_test/suite_test.go | 3 +- src/conmon.c | 67 ++++++++++++++++------------- src/ctr_logging.c | 14 ++++++ src/ctr_logging.h | 1 + 7 files changed, 69 insertions(+), 30 deletions(-) diff --git a/runner/conmon/conmon.go b/runner/conmon/conmon.go index 59bffc14..cb23d2e3 100644 --- a/runner/conmon/conmon.go +++ b/runner/conmon/conmon.go @@ -22,6 +22,7 @@ type ConmonInstance struct { pidFile string stdout io.Writer stderr io.Writer + stdin io.Reader parentStartPipe *os.File parentAttachPipe *os.File @@ -61,6 +62,7 @@ func NewConmonInstance(options ...ConmonOption) (*ConmonInstance, error) { ci.cmd.Stdout = ci.stdout ci.cmd.Stderr = ci.stderr + ci.cmd.Stdin = ci.stdin return ci, nil } diff --git a/runner/conmon/options.go b/runner/conmon/options.go index 74884942..a0b12d4b 100644 --- a/runner/conmon/options.go +++ b/runner/conmon/options.go @@ -30,6 +30,13 @@ func WithStderr(stderr io.Writer) ConmonOption { } } +func WithStdin(stdin io.Reader) ConmonOption { + return func(ci *ConmonInstance) error { + ci.stdin = stdin + return nil + } +} + func WithPath(path string) ConmonOption { return func(ci *ConmonInstance) error { ci.path = path diff --git a/runner/conmon_test/ctr_logs_test.go b/runner/conmon_test/ctr_logs_test.go index 1be9269b..1a0b9cc4 100644 --- a/runner/conmon_test/ctr_logs_test.go +++ b/runner/conmon_test/ctr_logs_test.go @@ -55,6 +55,11 @@ var _ = Describe("conmon ctr logs", func() { _, err := os.Stat(tmpLogPath) Expect(err).To(BeNil()) }) + It("log driver as passthrough should pass", func() { + stdout, stderr := getConmonOutputGivenLogOpts(conmon.WithLogDriver("passthrough", "")) + Expect(stdout).To(BeEmpty()) + Expect(stderr).To(BeEmpty()) + }) It("log driver as k8s-file with invalid path should fail", func() { _, stderr := getConmonOutputGivenLogOpts(conmon.WithLogDriver("k8s-file", invalidPath)) Expect(stderr).To(ContainSubstring("Failed to open log file")) diff --git a/runner/conmon_test/suite_test.go b/runner/conmon_test/suite_test.go index 360888f9..aef16b4b 100644 --- a/runner/conmon_test/suite_test.go +++ b/runner/conmon_test/suite_test.go @@ -37,8 +37,9 @@ func TestConmon(t *testing.T) { func getConmonOutputGivenOptions(options ...conmon.ConmonOption) (string, string) { var stdout bytes.Buffer var stderr bytes.Buffer + var stdin bytes.Buffer - options = append(options, conmon.WithStdout(&stdout), conmon.WithStderr(&stderr)) + options = append(options, conmon.WithStdout(&stdout), conmon.WithStderr(&stderr), conmon.WithStdin(&stdin)) ci, err := conmon.CreateAndExecConmon(options...) Expect(err).To(BeNil()) diff --git a/src/conmon.c b/src/conmon.c index 22564ec1..244cec9c 100644 --- a/src/conmon.c +++ b/src/conmon.c @@ -25,6 +25,16 @@ #include #include +static void disconnect_std_streams(int dev_null_r, int dev_null_w) +{ + if (dup2(dev_null_r, STDIN_FILENO) < 0) + pexit("Failed to dup over stdin"); + if (dup2(dev_null_w, STDOUT_FILENO) < 0) + pexit("Failed to dup over stdout"); + if (dup2(dev_null_w, STDERR_FILENO) < 0) + pexit("Failed to dup over stderr"); +} + int main(int argc, char *argv[]) { setlocale(LC_ALL, ""); @@ -114,13 +124,8 @@ int main(int argc, char *argv[]) /* Disconnect stdio from parent. We need to do this, because the parent is waiting for the stdout to end when the intermediate child dies */ - if (dup2(dev_null_r, STDIN_FILENO) < 0) - pexit("Failed to dup over stdin"); - if (dup2(dev_null_w, STDOUT_FILENO) < 0) - pexit("Failed to dup over stdout"); - if (dup2(dev_null_w, STDERR_FILENO) < 0) - pexit("Failed to dup over stderr"); - + if (!logging_is_passthrough()) + disconnect_std_streams(dev_null_r, dev_null_w); /* Create a new session group */ setsid(); @@ -193,7 +198,7 @@ int main(int argc, char *argv[]) /* Setup endpoint for attach */ _cleanup_free_ char *attach_symlink_dir_path = NULL; - if (opt_bundle_path != NULL) { + if (opt_bundle_path != NULL && !logging_is_passthrough()) { attach_symlink_dir_path = setup_attach_socket(); dummyfd = setup_terminal_control_fifo(); setup_console_fifo(); @@ -227,27 +232,28 @@ int main(int argc, char *argv[]) if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) _pexit("Failed to unblock signals"); - if (workerfd_stdin < 0) - workerfd_stdin = dev_null_r; - if (dup2(workerfd_stdin, STDIN_FILENO) < 0) - _pexit("Failed to dup over stdin"); - if (workerfd_stdin != dev_null_r && fchmod(STDIN_FILENO, 0777) < 0) - nwarn("Failed to chown stdin"); - - if (workerfd_stdout < 0) - workerfd_stdout = dev_null_w; - if (dup2(workerfd_stdout, STDOUT_FILENO) < 0) - _pexit("Failed to dup over stdout"); - if (workerfd_stdout != dev_null_w && fchmod(STDOUT_FILENO, 0777) < 0) - nwarn("Failed to chown stdout"); - - if (workerfd_stderr < 0) - workerfd_stderr = workerfd_stdout; - if (dup2(workerfd_stderr, STDERR_FILENO) < 0) - _pexit("Failed to dup over stderr"); - if (workerfd_stderr != dev_null_w && fchmod(STDERR_FILENO, 0777) < 0) - nwarn("Failed to chown stderr"); - + if (!logging_is_passthrough()) { + if (workerfd_stdin < 0) + workerfd_stdin = dev_null_r; + if (dup2(workerfd_stdin, STDIN_FILENO) < 0) + _pexit("Failed to dup over stdin"); + if (workerfd_stdin != dev_null_r && fchmod(STDIN_FILENO, 0777) < 0) + nwarn("Failed to chown stdin"); + + if (workerfd_stdout < 0) + workerfd_stdout = dev_null_w; + if (dup2(workerfd_stdout, STDOUT_FILENO) < 0) + _pexit("Failed to dup over stdout"); + if (workerfd_stdout != dev_null_w && fchmod(STDOUT_FILENO, 0777) < 0) + nwarn("Failed to chown stdout"); + + if (workerfd_stderr < 0) + workerfd_stderr = workerfd_stdout; + if (dup2(workerfd_stderr, STDERR_FILENO) < 0) + _pexit("Failed to dup over stderr"); + if (workerfd_stderr != dev_null_w && fchmod(STDERR_FILENO, 0777) < 0) + nwarn("Failed to chown stderr"); + } /* If LISTEN_PID env is set, we need to set the LISTEN_PID it to the new child process */ char *listenpid = getenv("LISTEN_PID"); @@ -287,6 +293,9 @@ int main(int argc, char *argv[]) exit(127); } + if (logging_is_passthrough()) + disconnect_std_streams(dev_null_r, dev_null_w); + if ((signal(SIGTERM, on_sig_exit) == SIG_ERR) || (signal(SIGQUIT, on_sig_exit) == SIG_ERR) || (signal(SIGINT, on_sig_exit) == SIG_ERR)) pexit("Failed to register the signal handler"); diff --git a/src/ctr_logging.c b/src/ctr_logging.c index c3fd5d25..71f1bce0 100644 --- a/src/ctr_logging.c +++ b/src/ctr_logging.c @@ -24,6 +24,7 @@ static inline int sd_journal_sendv(G_GNUC_UNUSED const struct iovec *iov, G_GNUC /* Different types of container logging */ static gboolean use_journald_logging = FALSE; static gboolean use_k8s_logging = FALSE; +static gboolean use_logging_passthrough = FALSE; /* Value the user must input for each log driver */ static const char *const K8S_FILE_STRING = "k8s-file"; @@ -71,6 +72,11 @@ static int set_k8s_timestamp(char *buf, ssize_t buflen, const char *pipename); static void reopen_k8s_file(void); +gboolean logging_is_passthrough(void) +{ + return use_logging_passthrough; +} + /* * configures container log specific information, such as the drivers the user * called with and the max log size for log file types. For the log file types @@ -160,6 +166,14 @@ static void parse_log_path(char *log_config) return; } + if (!strcmp(driver, "passthrough")) { + if (isatty(STDIN_FILENO) || isatty(STDOUT_FILENO) || isatty(STDERR_FILENO)) + nexitf("cannot use a tty with passthrough logging mode to prevent attacks via TIOCSTI"); + + use_logging_passthrough = TRUE; + return; + } + if (!strcmp(driver, JOURNALD_FILE_STRING)) { use_journald_logging = TRUE; return; diff --git a/src/ctr_logging.h b/src/ctr_logging.h index 1b63cd70..1a62396f 100644 --- a/src/ctr_logging.h +++ b/src/ctr_logging.h @@ -9,5 +9,6 @@ void reopen_log_files(void); bool write_to_logs(stdpipe_t pipe, char *buf, ssize_t num_read); void configure_log_drivers(gchar **log_drivers, int64_t log_size_max_, char *cuuid_, char *name_, char *tag); void sync_logs(void); +gboolean logging_is_passthrough(void); #endif /* !defined(CTR_LOGGING_H) */