Compare commits

...

7 Commits

Author SHA1 Message Date
Aravinth Manivannan b0d94f91dc
feat: run conductor as root
ci/woodpecker/push/woodpecker Pipeline was successful Details
2022-12-30 05:18:43 +05:30
Aravinth Manivannan d40e8642de
feat: publish systemd service file
ci/woodpecker/push/woodpecker Pipeline was successful Details
2022-12-30 04:44:39 +05:30
Aravinth Manivannan 5851b686b4
feat: read config from /etc/librepages/conductor/ 2022-12-30 04:44:30 +05:30
Aravinth Manivannan db9115b90b
feat: read token from env var
ci/woodpecker/push/woodpecker Pipeline was successful Details
2022-12-29 18:34:39 +05:30
Aravinth Manivannan b15c72ef30
feat: read token from env var
ci/woodpecker/push/woodpecker Pipeline was successful Details
2022-12-29 17:48:30 +05:30
Aravinth Manivannan cd0589fb2e
feat: replace http auth with bearer auth
ci/woodpecker/push/woodpecker Pipeline failed Details
2022-12-29 17:29:07 +05:30
Aravinth Manivannan 58eef6b3fa
feat: add prometheus instrumentation
ci/woodpecker/push/woodpecker Pipeline was successful Details
2022-12-25 13:14:00 +05:30
9 changed files with 85 additions and 26 deletions

27
Cargo.lock generated
View File

@ -208,6 +208,18 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "actix-web-prom"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9df3127d20a5d01c9fc9aceb969a38d31a6767e1b48a54d55a8f56c769a84923"
dependencies = [
"actix-web",
"futures-core",
"pin-project-lite",
"prometheus",
]
[[package]]
name = "adler"
version = "1.0.2"
@ -415,6 +427,7 @@ dependencies = [
"actix-web",
"actix-web-codegen-const-routes",
"actix-web-httpauth",
"actix-web-prom",
"base64",
"clap",
"config",
@ -1408,6 +1421,20 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "prometheus"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c"
dependencies = [
"cfg-if",
"fnv",
"lazy_static",
"memchr",
"parking_lot 0.12.1",
"thiserror",
]
[[package]]
name = "quick-error"
version = "1.2.3"

View File

@ -12,6 +12,7 @@ build = "build.rs"
[dependencies]
actix-web = "4"
actix-web-prom = "0.6.0"
futures-util = { version = "0.3.17", default-features = false, features = ["std"] }
lazy_static = "1.4.0"
log = "0.4.17"

View File

@ -3,8 +3,7 @@ source_code = "https://git.batsense.net/librepages/conductor"
conductor = "dummy"
[creds]
username = "librepages_api"
password="longrandomlygeneratedpassword"
token="longrandomlygeneratedpassword"
[server]
# Please set a unique value, your mCaptcha instance's security depends on this being

View File

@ -0,0 +1,24 @@
[Unit]
Description=LibrePages Conductor: Easiest way to deploy websites. Conductor component
[Service]
Type=simple
User=root
ExecStart=/usr/bin/conductor serve
Restart=on-failure
RestartSec=1
SuccessExitStatus=3 4
RestartForceExitStatus=3 4
SystemCallArchitectures=native
MemoryDenyWriteExecute=true
NoNewPrivileges=true
Environment="RUST_LOG=info"
[Unit]
Wants=network-online.target
Wants=network-online.target
Requires=postgresql.service
After=syslog.target
[Install]
WantedBy=multi-user.target

View File

@ -38,6 +38,8 @@ DOCKER_IMG="realaravinth/$NAME:$3"
get_bin(){
cp target/release/conductor $TARGET_DIR
cp -r config/ $TARGET_DIR
cp -r contrib/ $TARGET_DIR
}
copy() {

View File

@ -17,7 +17,7 @@
use actix_web::dev::ServiceRequest;
use actix_web::web;
use actix_web::Error;
use actix_web_httpauth::extractors::basic::BasicAuth;
use actix_web_httpauth::extractors::bearer::BearerAuth;
use crate::errors::*;
use crate::AppCtx;
@ -26,14 +26,13 @@ use crate::SETTINGS;
pub mod meta;
pub mod webhook;
pub async fn httpauth(
pub async fn bearerauth(
req: ServiceRequest,
credentials: BasicAuth,
credentials: BearerAuth,
) -> Result<ServiceRequest, (Error, ServiceRequest)> {
let _ctx: &AppCtx = req.app_data().unwrap();
let username = credentials.user_id();
let password = credentials.password().unwrap();
if SETTINGS.authenticate(username, password) {
let token = credentials.token();
if SETTINGS.authenticate(token) {
Ok(req)
} else {
let e = Error::from(ServiceError::Unauthorized);

View File

@ -24,7 +24,7 @@ use crate::errors::*;
use crate::AppCtx;
use crate::*;
use super::httpauth;
use super::bearerauth;
pub mod routes {
use super::*;
@ -47,7 +47,7 @@ pub fn services(cfg: &mut web::ServiceConfig) {
#[actix_web_codegen_const_routes::post(
path = "API_V1_ROUTES.webhook.post_event",
wrap = "HttpAuthentication::basic(httpauth)"
wrap = "HttpAuthentication::bearer(bearerauth)"
)]
async fn post_event(ctx: AppCtx, payload: web::Json<EventType>) -> ServiceResult<impl Responder> {
ctx.conductor.process(payload.into_inner()).await;
@ -71,10 +71,7 @@ pub mod tests {
.await;
let creds = settings.creds.clone();
let auth = format!(
"Basic {}",
base64::encode(format!("{}:{}", creds.username.clone(), creds.password))
);
let auth = format!("Bearer {}", creds.token,);
let msg = EventType::NewSite {
hostname: "demo.librepages.org".into(),

View File

@ -19,6 +19,7 @@ use std::env;
use actix_web::http::StatusCode;
use actix_web::web::JsonConfig;
use actix_web::{error::InternalError, middleware, App, HttpServer};
use actix_web_prom::PrometheusMetricsBuilder;
use clap::{Parser, Subcommand};
use log::info;
@ -112,6 +113,11 @@ async fn serve(settings: Settings, ctx: AppCtx) -> std::io::Result<()> {
let ip = settings.server.get_ip();
println!("Starting server on: http://{ip}");
let prometheus = PrometheusMetricsBuilder::new("api")
.endpoint("/metrics")
.build()
.unwrap();
HttpServer::new(move || {
App::new()
.wrap(middleware::Logger::default())
@ -124,6 +130,7 @@ async fn serve(settings: Settings, ctx: AppCtx) -> std::io::Result<()> {
middleware::TrailingSlash::Trim,
))
.app_data(get_json_err())
.wrap(prometheus.clone())
.configure(routes::services)
})
.bind(ip)?

View File

@ -54,8 +54,7 @@ pub enum ConductorType {
#[derive(Debug, Clone, Deserialize)]
pub struct Creds {
pub username: String,
pub password: String,
pub token: String,
}
#[derive(Debug, Clone, Deserialize)]
@ -69,15 +68,15 @@ pub struct Settings {
#[cfg(not(tarpaulin_include))]
impl Settings {
pub fn authenticate(&self, username: &str, password: &str) -> bool {
self.creds.username == username && self.creds.password == password
pub fn authenticate(&self, token: &str) -> bool {
self.creds.token == token
}
pub fn new() -> Result<Self, ConfigError> {
let mut s = Config::builder();
const CURRENT_DIR: &str = "./config/config.toml";
const ETC: &str = "/etc/lpconductor/config.toml";
const ETC: &str = "/etc/librepages/conductor/config.toml";
if let Ok(path) = env::var("LPCONDUCTOR_CONFIG") {
s = s.add_source(File::with_name(&path));
@ -138,6 +137,13 @@ fn set_separator_field(mut s: ConfigBuilder<DefaultState>) -> ConfigBuilder<Defa
&format!("{PREFIX}{SEPARATOR}SERVER{SEPARATOR}PROXY_HAS_TLS"),
"server.proxy_has_tls",
);
s = from_env(
s,
&format!("{PREFIX}{SEPARATOR}CREDS{SEPARATOR}TOKEN"),
"creds.token",
);
s
}
@ -148,16 +154,13 @@ mod tests {
#[test]
fn creds_works() {
let settings = Settings::new().unwrap();
let mut creds = settings.creds.clone();
let creds = settings.creds.clone();
assert!(settings.authenticate(&creds.username, &creds.password));
creds.username = "noexist".into();
assert!(!settings.authenticate(&creds.username, &creds.password));
assert!(settings.authenticate(&creds.token));
let mut creds = settings.creds.clone();
creds.password = "noexist".into();
assert!(!settings.authenticate(&creds.username, &creds.password));
creds.token = "noexist".into();
assert!(!settings.authenticate(&creds.token))
}
}