2 커밋 21dc55fe50 ... 08c738228e

작성자 SHA1 메시지 날짜
  Michael Buesch 08c738228e Split the daemon into network and firewall part 4 달 전
  Michael Buesch 21dc55fe50 Split the daemon into network and firewall part 4 달 전
4개의 변경된 파일70개의 추가작업 그리고 74개의 파일을 삭제
  1. 9 71
      letmein-seccomp/src/lib.rs
  2. 1 1
      letmeind/letmeind.service
  3. 57 2
      letmeind/src/main.rs
  4. 3 0
      letmeinfwd/src/server.rs

+ 9 - 71
letmein-seccomp/src/lib.rs

@@ -18,25 +18,12 @@ pub enum Allow {
     Mprotect,
     UnixConnect,
     UnixListen,
-    Open,
+    TcpAccept,
     Read,
     Write,
-    Stat,
-    Listdir,
     Recv,
     Send,
-    Sendfile,
-    Futex,
-    Signal,
-    SignalMask,
     SignalReturn,
-    Threading,
-    Inotify,
-    Prctl,
-    Timer,
-    ClockGet,
-    ClockSet,
-    Sleep,
 }
 
 #[derive(Clone, Debug)]
@@ -69,6 +56,8 @@ pub fn seccomp_compile_for_arch(
         (libc::SYS_munmap, vec![]),
         (libc::SYS_sched_getaffinity, vec![]),
         (libc::SYS_sigaltstack, vec![]),
+        (libc::SYS_nanosleep, vec![]),
+        (libc::SYS_clock_nanosleep, vec![]),
     ]
     .into();
 
@@ -95,17 +84,19 @@ pub fn seccomp_compile_for_arch(
             Allow::UnixConnect => {
                 rules.insert(libc::SYS_connect, vec![]);
                 rules.insert(libc::SYS_socket, vec![]); //TODO: Restrict to AF_UNIX
+                rules.insert(libc::SYS_getsockopt, vec![]);
             }
             Allow::UnixListen => {
                 rules.insert(libc::SYS_accept4, vec![]);
                 rules.insert(libc::SYS_bind, vec![]);
                 rules.insert(libc::SYS_listen, vec![]);
                 rules.insert(libc::SYS_socket, vec![]); //TODO: Restrict to AF_UNIX
+                rules.insert(libc::SYS_getsockopt, vec![]);
             }
-            Allow::Open => {
-                //TODO: This should be restricted
-                rules.insert(libc::SYS_open, vec![]);
-                rules.insert(libc::SYS_openat, vec![]);
+            Allow::TcpAccept => {
+                rules.insert(libc::SYS_accept4, vec![]);
+                rules.insert(libc::SYS_socket, vec![]); //TODO: Restrict to AF_UNIX
+                rules.insert(libc::SYS_getsockopt, vec![]);
             }
             Allow::Read => {
                 rules.insert(libc::SYS_pread64, vec![]);
@@ -123,13 +114,6 @@ pub fn seccomp_compile_for_arch(
                 rules.insert(libc::SYS_writev, vec![]);
                 add_read_write_rules(&mut rules);
             }
-            Allow::Stat => {
-                rules.insert(libc::SYS_statx, vec![]);
-                rules.insert(libc::SYS_newfstatat, vec![]);
-            }
-            Allow::Listdir => {
-                rules.insert(libc::SYS_getdents64, vec![]);
-            }
             Allow::Recv => {
                 rules.insert(libc::SYS_recvfrom, vec![]);
                 rules.insert(libc::SYS_recvmsg, vec![]);
@@ -140,55 +124,9 @@ pub fn seccomp_compile_for_arch(
                 rules.insert(libc::SYS_sendmsg, vec![]);
                 rules.insert(libc::SYS_sendmmsg, vec![]);
             }
-            Allow::Sendfile => {
-                rules.insert(libc::SYS_sendfile, vec![]);
-            }
-            Allow::Futex => {
-                rules.insert(libc::SYS_futex, vec![]);
-                rules.insert(libc::SYS_get_robust_list, vec![]);
-                rules.insert(libc::SYS_set_robust_list, vec![]);
-            }
-            Allow::Signal => {
-                rules.insert(libc::SYS_rt_sigaction, vec![]);
-                rules.insert(libc::SYS_rt_sigprocmask, vec![]);
-            }
-            Allow::SignalMask => {
-                rules.insert(libc::SYS_rt_sigprocmask, vec![]);
-            }
             Allow::SignalReturn => {
                 rules.insert(libc::SYS_rt_sigreturn, vec![]);
             }
-            Allow::Threading => {
-                rules.insert(libc::SYS_clone3, vec![]); //TODO restrict to threads
-                rules.insert(libc::SYS_rseq, vec![]);
-            }
-            Allow::Inotify => {
-                rules.insert(libc::SYS_inotify_init, vec![]);
-                rules.insert(libc::SYS_inotify_add_watch, vec![]);
-                rules.insert(libc::SYS_inotify_rm_watch, vec![]);
-            }
-            Allow::Prctl => {
-                //TODO: This should be restricted
-                rules.insert(libc::SYS_prctl, vec![]);
-            }
-            Allow::Timer => {
-                rules.insert(libc::SYS_timer_create, vec![]);
-                rules.insert(libc::SYS_timer_settime, vec![]);
-                rules.insert(libc::SYS_timer_gettime, vec![]);
-                rules.insert(libc::SYS_timer_getoverrun, vec![]);
-                rules.insert(libc::SYS_timer_delete, vec![]);
-            }
-            Allow::ClockGet => {
-                rules.insert(libc::SYS_clock_gettime, vec![]);
-                rules.insert(libc::SYS_clock_getres, vec![]);
-            }
-            Allow::ClockSet => {
-                rules.insert(libc::SYS_clock_settime, vec![]);
-            }
-            Allow::Sleep => {
-                rules.insert(libc::SYS_nanosleep, vec![]);
-                rules.insert(libc::SYS_clock_nanosleep, vec![]);
-            }
         }
     }
 

+ 1 - 1
letmeind/letmeind.service

@@ -7,7 +7,7 @@ StartLimitIntervalSec=0
 [Service]
 Type=notify
 NotifyAccess=main
-ExecStart=/opt/letmein/bin/letmeind
+ExecStart=/opt/letmein/bin/letmeind --seccomp=log
 ExecReload=/bin/kill -HUP $MAINPID
 StandardOutput=journal
 StandardError=journal

+ 57 - 2
letmeind/src/main.rs

@@ -14,8 +14,11 @@ mod server;
 
 use crate::{processor::Processor, server::Server};
 use anyhow::{self as ah, format_err as err, Context as _};
-use clap::Parser;
+use clap::{Parser, ValueEnum};
 use letmein_conf::{Config, ConfigVariant, INSTALL_PREFIX, SERVER_CONF_PATH};
+use letmein_seccomp::{
+    seccomp_compile, seccomp_install, Action as SeccompAction, Allow as SeccompAllow,
+};
 use std::{path::PathBuf, sync::Arc};
 use tokio::{
     signal::unix::{signal, SignalKind},
@@ -25,6 +28,32 @@ use tokio::{
 
 pub type ConfigRef<'a> = RwLockReadGuard<'a, Config>;
 
+#[derive(ValueEnum, Debug, Clone, PartialEq, Eq)]
+enum SeccompOpt {
+    /// Seccomp is disabled (default).
+    Off,
+
+    /// Seccomp is enabled with logging only.
+    ///
+    /// The event will be logged, if a syscall is called that is not allowed.
+    /// See the Linux kernel logs for seccomp audit messages.
+    Log,
+
+    /// Seccomp is enabled with killing (recommended).
+    ///
+    /// The process will be killed, if a syscall is called that is not allowed.
+    Kill,
+}
+
+impl From<SeccompOpt> for SeccompAction {
+    fn from(seccomp: SeccompOpt) -> SeccompAction {
+        match seccomp {
+            SeccompOpt::Off | SeccompOpt::Log => SeccompAction::Log,
+            SeccompOpt::Kill => SeccompAction::Kill,
+        }
+    }
+}
+
 #[derive(Parser, Debug, Clone)]
 struct Opts {
     /// Override the default path to the configuration file.
@@ -45,6 +74,10 @@ struct Opts {
     /// even if a systemd socket has been passed to the application.
     #[arg(long, default_value = "false")]
     no_systemd: bool,
+
+    /// Enable Linux 'seccomp' to security harden letmein.
+    #[arg(long, default_value = "off")]
+    seccomp: SeccompOpt,
 }
 
 impl Opts {
@@ -76,7 +109,29 @@ async fn main() -> ah::Result<()> {
         .await
         .context("Server init")?;
 
-    //TODO setup seccomp
+    match opts.seccomp {
+        SeccompOpt::Log | SeccompOpt::Kill => {
+            seccomp_install(
+                seccomp_compile(
+                    &[
+                        SeccompAllow::Mmap,
+                        SeccompAllow::Mprotect,
+                        SeccompAllow::Read,
+                        SeccompAllow::Write,
+                        SeccompAllow::Recv,
+                        SeccompAllow::Send,
+                        SeccompAllow::TcpAccept,
+                        SeccompAllow::UnixConnect,
+                        SeccompAllow::SignalReturn,
+                    ],
+                    opts.seccomp.clone().into(),
+                )
+                .context("Compile seccomp filter")?,
+            )
+            .context("Install seccomp filter")?;
+        }
+        SeccompOpt::Off => (),
+    }
 
     // Task: Socket handler.
     let conf_clone = Arc::clone(&conf);

+ 3 - 0
letmeinfwd/src/server.rs

@@ -18,6 +18,7 @@ use std::{
 use tokio::net::{UnixListener, UnixStream};
 use user_lookup::async_reader::{GroupReader, PasswdReader};
 
+/// Resolve a user name into a UID.
 async fn get_uid(user_name: &str) -> ah::Result<u32> {
     let Some(user) = PasswdReader::new(Duration::from_secs(0))
         .get_by_username(user_name)
@@ -29,6 +30,7 @@ async fn get_uid(user_name: &str) -> ah::Result<u32> {
     Ok(user.uid)
 }
 
+/// Resolve a group name into a GID.
 async fn get_gid(group_name: &str) -> ah::Result<u32> {
     let Some(group) = GroupReader::new(Duration::from_secs(0))
         .get_by_name(group_name)
@@ -111,6 +113,7 @@ impl FirewallServer {
         })
     }
 
+    /// Accept a connection on the Unix socket.
     pub async fn accept(&self) -> ah::Result<FirewallConnection> {
         let (stream, _addr) = self.listener.accept().await?;