mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-12-22 19:53:49 +01:00
Implement a simple audit log
This commit is contained in:
parent
19abba371e
commit
4858a6a6fb
33
Cargo.lock
generated
33
Cargo.lock
generated
@ -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"
|
||||
|
@ -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"] }
|
||||
|
||||
|
@ -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
43
src/audit.rs
Normal 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(())
|
||||
}
|
||||
}
|
@ -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(),
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ mod actor;
|
||||
mod initiator;
|
||||
mod space;
|
||||
|
||||
mod audit;
|
||||
|
||||
use clap::{App, Arg};
|
||||
|
||||
use std::io;
|
||||
|
Loading…
Reference in New Issue
Block a user