Implement a simple audit log

This commit is contained in:
Nadja Reitzenstein 2022-01-06 20:30:50 +01:00
parent 19abba371e
commit 4858a6a6fb
9 changed files with 102 additions and 12 deletions

33
Cargo.lock generated
View File

@ -401,7 +401,7 @@ dependencies = [
"libc",
"num-integer",
"num-traits",
"time",
"time 0.1.43",
"winapi",
]
@ -532,7 +532,7 @@ dependencies = [
[[package]]
name = "diflouroborane"
version = "0.3.1"
version = "0.3.2"
dependencies = [
"async-channel",
"async-rustls",
@ -561,12 +561,14 @@ dependencies = [
"rustls-pemfile",
"serde",
"serde_dhall",
"serde_json",
"signal-hook",
"slog",
"slog-async",
"slog-term",
"smol",
"tempfile",
"time 0.3.5",
"toml",
"uuid",
"walkdir",
@ -1077,6 +1079,12 @@ dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]]
name = "js-sys"
version = "0.3.55"
@ -1725,6 +1733,17 @@ dependencies = [
"url",
]
[[package]]
name = "serde_json"
version = "1.0.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "sha-1"
version = "0.8.2"
@ -1974,6 +1993,16 @@ dependencies = [
"winapi",
]
[[package]]
name = "time"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad"
dependencies = [
"libc",
"serde",
]
[[package]]
name = "tinyvec"
version = "1.3.1"

View File

@ -38,6 +38,9 @@ flexbuffers = "2.0.0"
bincode = "2.0.0-dev"
serde_dhall = { version = "0.10.1", default-features = false }
# Audit log uses serde_json for now
serde_json = "1.0.74"
time = { version = "0.3.5", features = ["serde"] }
uuid = { version = "0.8.2", features = ["serde", "v4"] }

View File

@ -38,6 +38,11 @@
-- access into them.
db_path = "/tmp/bffh",
-- Audit log path. Bffh will log state changes into this file, one per line.
-- Audit log entries are for now JSON:
-- {"timestamp":1641497361,"machine":"Testmachine","state":{"state":{"InUse":{"uid":"Testuser","subuid":null,"realm":null}}}}
auditlog_path = "/tmp/bffh.audit",
-- In dhall you can also easily import definitions from other files, e.g. you could write
-- roles = ./roles.dhall
roles = {
@ -192,14 +197,14 @@
-- Initiators are configured almost the same way as Actors, refer to actor documentation for more details
-- The below '{=}' is what you need if you want to define *no* initiators at all and only use the API with apps
-- to let people use machines.
initiators = {=},
-- initiators = {=},
-- The "Dummy" initiator will try to use and return a machine as the given user every few seconds. It's good to
-- test your system but will spam your log so is disabled by default.
--{ Initiator = { module = "Dummy", params = { uid = "Testuser" } } }
initiators = { Initiator = { module = "Dummy", params = { uid = "Testuser" } } },
-- Linking up machines to initiators. Similar to actors a machine can have several initiators assigned but an
-- initiator can only be assigned to one machine.
-- The below is once again how you have to define *no* initiators.
init_connections = [] : List { machine : Text, initiator : Text }
-- init_connections = [{ machine = "Testmachine", initiator = "Initiator" }]
--init_connections = [] : List { machine : Text, initiator : Text }
init_connections = [{ machine = "Testmachine", initiator = "Initiator" }]
}

43
src/audit.rs Normal file
View File

@ -0,0 +1,43 @@
use std::fs::{File, OpenOptions};
use std::io;
use std::io::{LineWriter, Write};
use std::sync::Mutex;
use std::time::Instant;
use crate::Config;
use serde::{Serialize, Deserialize};
use serde_json::Serializer;
use time::OffsetDateTime;
use crate::db::machine::{MachineIdentifier, MachineState};
#[derive(Debug)]
pub struct AuditLog {
writer: Mutex<LineWriter<File>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditLogLine {
timestamp: i64,
machine: MachineIdentifier,
state: MachineState,
}
impl AuditLog {
pub fn new(config: &Config) -> io::Result<Self> {
let fd = OpenOptions::new().create(true).append(true).open(&config.auditlog_path)?;
let writer = Mutex::new(LineWriter::new(fd));
Ok(Self { writer })
}
pub fn log(&self, machine: &MachineIdentifier, state: &MachineState) -> io::Result<()> {
let timestamp = OffsetDateTime::now_utc().unix_timestamp();
let line = AuditLogLine { timestamp, machine: machine.clone(), state: state.clone() };
let mut guard = self.writer.lock().unwrap();
let mut writer: &mut LineWriter<File> = &mut *guard;
let mut ser = Serializer::new(&mut writer);
line.serialize(&mut ser).expect("failed to serialize audit log line");
writer.write("\n".as_bytes())?;
Ok(())
}
}

View File

@ -50,6 +50,7 @@ pub struct Config {
pub init_connections: Box<[InitiatorConn]>,
pub db_path: PathBuf,
pub auditlog_path: PathBuf,
pub roles: HashMap<String, RoleConfig>,
@ -136,6 +137,7 @@ impl Default for Config {
},
]),
auditlog_path: PathBuf::from("/var/log/bffh/audit.log"),
db_path: PathBuf::from("/run/bffh/database"),
roles: HashMap::new(),

View File

@ -11,6 +11,7 @@ use crate::db::user::UserId;
pub mod internal;
use internal::Internal;
use crate::audit::AuditLog;
pub type MachineIdentifier = String;
pub type Priority = u64;
@ -73,11 +74,13 @@ impl MachineState {
}
}
pub fn init(log: Logger, _config: &Config, env: Arc<lmdb::Environment>) -> Result<Internal> {
pub fn init(log: Logger, config: &Config, env: Arc<lmdb::Environment>) -> Result<Internal> {
let mut flags = lmdb::DatabaseFlags::empty();
//flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true);
let machdb = env.create_db(Some("machines"), flags)?;
debug!(&log, "Opened machine db successfully.");
Ok(Internal::new(log, env, machdb))
let audit = AuditLog::new(config)?;
Ok(Internal::new(log, audit, env, machdb))
}

View File

@ -3,20 +3,22 @@ use std::sync::Arc;
use slog::Logger;
use lmdb::{Environment, Transaction, RwTransaction, Cursor, RoTransaction};
use crate::audit::AuditLog;
use super::{MachineIdentifier, MachineState};
use crate::error::Result;
#[derive(Clone, Debug)]
#[derive(Debug)]
pub struct Internal {
log: Logger,
audit: AuditLog,
env: Arc<Environment>,
db: lmdb::Database,
}
impl Internal {
pub fn new(log: Logger, env: Arc<Environment>, db: lmdb::Database) -> Self {
Self { log, env, db }
pub fn new(log: Logger, audit: AuditLog, env: Arc<Environment>, db: lmdb::Database) -> Self {
Self { log, audit, env, db }
}
pub fn get_with_txn<T: Transaction>(&self, txn: &T, id: &String)
@ -47,6 +49,7 @@ impl Internal {
}
pub fn put(&self, id: &MachineIdentifier, status: &MachineState) -> Result<()> {
self.audit.log(id, status)?;
let mut txn = self.env.begin_rw_txn()?;
self.put_with_txn(&mut txn, id, status)?;
txn.commit().map_err(Into::into)

View File

@ -295,7 +295,7 @@ impl Inner {
Box::pin(self.state.signal_cloned().dedupe_cloned())
}
fn replace_state(&mut self, new_state: MachineState) -> MachineState {
fn replace_state(&self, new_state: MachineState) -> MachineState {
self.db.put(&self.id, &new_state);
self.state.replace(new_state)
}

View File

@ -25,6 +25,8 @@ mod actor;
mod initiator;
mod space;
mod audit;
use clap::{App, Arg};
use std::io;