Reload config on HUP signal; change and synchronize default config

This commit is contained in:
Mike Dilger 2024-03-16 12:57:53 +13:00
parent a701da6dda
commit 54bb5450f6
7 changed files with 83 additions and 53 deletions

View File

@ -6,9 +6,10 @@ use std::sync::atomic::AtomicUsize;
use std::sync::OnceLock;
use tokio::sync::broadcast::Sender as BroadcastSender;
use tokio::sync::watch::Sender as WatchSender;
use tokio::sync::RwLock;
pub struct Globals {
pub config: OnceLock<Config>,
pub config: RwLock<Config>,
pub store: OnceLock<Store>,
pub http_server: Http,
pub rid: OnceLock<String>,
@ -33,7 +34,7 @@ lazy_static! {
let (shutting_down, _) = tokio::sync::watch::channel(false);
Globals {
config: OnceLock::new(),
config: RwLock::new(Default::default()),
store: OnceLock::new(),
http_server,
rid: OnceLock::new(),

View File

@ -45,7 +45,7 @@ async fn main() -> Result<(), Error> {
let config_path = args.next().unwrap();
// Read config file
let mut file = OpenOptions::new().read(true).open(config_path)?;
let mut file = OpenOptions::new().read(true).open(config_path.clone())?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
let friendly_config: FriendlyConfig = toml::from_str(&contents)?;
@ -80,11 +80,12 @@ async fn main() -> Result<(), Error> {
log::info!(target: "Server", "Running on {}:{}", config.ip_address, config.port);
// Store config into GLOBALS
let _ = GLOBALS.config.set(config);
*GLOBALS.config.write().await = config;
let mut interrupt_signal = signal(SignalKind::interrupt())?;
let mut quit_signal = signal(SignalKind::quit())?;
let mut terminate_signal = signal(SignalKind::terminate())?;
let mut hup_signal = signal(SignalKind::hangup())?;
loop {
tokio::select! {
@ -102,6 +103,20 @@ async fn main() -> Result<(), Error> {
break;
},
// Reload config on HUP
v = hup_signal.recv() => if v.is_some() {
log::info!(target: "Server", "SIGHUP: Reloading configuration");
// Reload the config file
let mut file = OpenOptions::new().read(true).open(config_path.clone())?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
let friendly_config: FriendlyConfig = toml::from_str(&contents)?;
let config: Config = friendly_config.into_config()?;
*GLOBALS.config.write().await = config;
},
// Accepts network connections and spawn a task to serve each one
v = listener.accept() => {
let (tcp_stream, peer_addr) = v?;

View File

@ -82,7 +82,7 @@ impl WebSocketService {
if let Err(e) = self.req_inner(&subid, filters).await {
let reply = match e.inner {
ChorusError::TooManySubscriptions => {
let max_subscriptions = GLOBALS.config.get().unwrap().max_subscriptions;
let max_subscriptions = GLOBALS.config.read().await.max_subscriptions;
NostrReply::Closed(
&subid,
NostrReplyPrefix::Blocked,
@ -105,7 +105,7 @@ impl WebSocketService {
}
async fn req_inner(&mut self, subid: &String, filters: Vec<OwnedFilter>) -> Result<(), Error> {
let max_subscriptions = GLOBALS.config.get().unwrap().max_subscriptions;
let max_subscriptions = GLOBALS.config.read().await.max_subscriptions;
if self.subscriptions.len() >= max_subscriptions {
return Err(ChorusError::TooManySubscriptions.into());
}
@ -233,8 +233,7 @@ impl WebSocketService {
let event_flags = event_flags(&event, &user);
if !event_flags.author_is_an_authorized_user || GLOBALS.config.get().unwrap().verify_events
{
if !event_flags.author_is_an_authorized_user || GLOBALS.config.read().await.verify_events {
// Verify the event is valid (id is hash, signature is valid)
if let Err(e) = event.verify() {
return Err(ChorusError::EventIsInvalid(format!("{}", e)).into());
@ -345,7 +344,7 @@ impl WebSocketService {
};
if let Some(h) = url.host() {
let theirhost = h.to_owned();
if theirhost == GLOBALS.config.get().unwrap().hostname {
if theirhost == GLOBALS.config.read().await.hostname {
relay_ok = true;
}
}
@ -402,7 +401,7 @@ async fn screen_incoming_event(
}
// Accept if an open relay
if GLOBALS.config.get().unwrap().open_relay {
if GLOBALS.config.read().await.open_relay {
return Ok(true);
}
@ -412,20 +411,20 @@ async fn screen_incoming_event(
}
// Accept relay lists from anybody
if event.kind() == Kind(10002) && GLOBALS.config.get().unwrap().serve_relay_lists {
if event.kind() == Kind(10002) && GLOBALS.config.read().await.serve_relay_lists {
return Ok(true);
}
// Allow if event kind ephemeral
if event.kind().is_ephemeral() && GLOBALS.config.get().unwrap().serve_ephemeral {
if event.kind().is_ephemeral() && GLOBALS.config.read().await.serve_ephemeral {
return Ok(true);
}
// If the author is one of our users, always accept it
if GLOBALS
.config
.get()
.unwrap()
.read()
.await
.user_keys
.contains(&event.pubkey())
{
@ -436,7 +435,7 @@ async fn screen_incoming_event(
for mut tag in event.tags()?.iter() {
if tag.next() == Some(b"p") {
if let Some(value) = tag.next() {
for ukhex in &GLOBALS.config.get().unwrap().user_hex_keys {
for ukhex in &GLOBALS.config.read().await.user_hex_keys {
if value == ukhex.as_bytes() {
return Ok(true);
}
@ -460,17 +459,17 @@ pub fn screen_outgoing_event(
}
// Allow if an open relay
if GLOBALS.config.get().unwrap().open_relay {
if GLOBALS.config.blocking_read().open_relay {
return true;
}
// Allow Relay Lists
if event.kind() == Kind(10002) && GLOBALS.config.get().unwrap().serve_relay_lists {
if event.kind() == Kind(10002) && GLOBALS.config.blocking_read().serve_relay_lists {
return true;
}
// Allow if event kind ephemeral
if event.kind().is_ephemeral() && GLOBALS.config.get().unwrap().serve_ephemeral {
if event.kind().is_ephemeral() && GLOBALS.config.blocking_read().serve_ephemeral {
return true;
}
@ -491,7 +490,7 @@ pub fn screen_outgoing_event(
pub async fn authorized_user(user: &Option<Pubkey>) -> bool {
match user {
None => false,
Some(pk) => GLOBALS.config.get().unwrap().user_keys.contains(pk),
Some(pk) => GLOBALS.config.read().await.user_keys.contains(pk),
}
}
@ -505,8 +504,7 @@ pub struct EventFlags {
pub fn event_flags(event: &Event<'_>, user: &Option<Pubkey>) -> EventFlags {
let author_is_an_authorized_user = GLOBALS
.config
.get()
.unwrap()
.blocking_read()
.user_keys
.contains(&event.pubkey());
@ -529,7 +527,12 @@ pub fn event_flags(event: &Event<'_>, user: &Option<Pubkey>) -> EventFlags {
}
}
if GLOBALS.config.get().unwrap().user_keys.contains(&tagged_pk) {
if GLOBALS
.config
.blocking_read()
.user_keys
.contains(&tagged_pk)
{
tags_an_authorized_user = true;
}
}

View File

@ -18,7 +18,7 @@ pub async fn serve_http(peer: SocketAddr, request: Request<Body>) -> Result<Resp
pub async fn serve_nip11(peer: SocketAddr) -> Result<Response<Body>, Error> {
log::debug!(target: "Client", "{}: sent NIP-11", peer);
let rid = {
let config = GLOBALS.config.get().unwrap();
let config = &*GLOBALS.config.read().await;
GLOBALS.rid.get_or_init(|| build_rid(config))
};

View File

@ -35,15 +35,15 @@ pub struct FriendlyConfig {
impl Default for FriendlyConfig {
fn default() -> FriendlyConfig {
FriendlyConfig {
data_directory: "/tmp".to_string(),
data_directory: "/opt/chorus/var/chorus".to_string(),
ip_address: "127.0.0.1".to_string(),
port: 80,
port: 443,
hostname: "localhost".to_string(),
use_tls: false,
certchain_pem_path: "./tls/fullchain.pem".to_string(),
key_pem_path: "./tls/privkey.pem".to_string(),
name: None,
description: None,
use_tls: true,
certchain_pem_path: "/opt/chorus/etc/tls/fullchain.pem".to_string(),
key_pem_path: "/opt/chorus/etc/tls/privkey.pem".to_string(),
name: Some("Chorus Default".to_string()),
description: Some("A default config of the Chorus relay".to_string()),
contact: None,
public_key_hex: None,
open_relay: false,
@ -56,8 +56,8 @@ impl Default for FriendlyConfig {
serve_ephemeral: true,
serve_relay_lists: true,
server_log_level: "Info".to_string(),
library_log_level: "Warn".to_string(),
client_log_level: "Error".to_string(),
library_log_level: "Info".to_string(),
client_log_level: "Info".to_string(),
}
}
}
@ -103,9 +103,9 @@ impl FriendlyConfig {
let hostname = Host::parse(&hostname)?;
let server_log_level =
log::LevelFilter::from_str(&server_log_level).unwrap_or(log::LevelFilter::Error);
log::LevelFilter::from_str(&server_log_level).unwrap_or(log::LevelFilter::Info);
let library_log_level =
log::LevelFilter::from_str(&library_log_level).unwrap_or(log::LevelFilter::Warn);
log::LevelFilter::from_str(&library_log_level).unwrap_or(log::LevelFilter::Info);
let client_log_level =
log::LevelFilter::from_str(&client_log_level).unwrap_or(log::LevelFilter::Info);
@ -165,3 +165,12 @@ pub struct Config {
pub library_log_level: log::LevelFilter,
pub client_log_level: log::LevelFilter,
}
impl Default for Config {
fn default() -> Config {
let friendly = FriendlyConfig::default();
// We know the default config passes into_config without error:
friendly.into_config().unwrap()
}
}

View File

@ -67,16 +67,16 @@ certchain_pem_path = "/opt/chorus/etc/tls/fullchain.pem"
key_pem_path = "/opt/chorus/etc/tls/privkey.pem"
# This is an optional name for your relay, displayed in the NIP-11 response.
# This is a name for your relay, displayed in the NIP-11 response.
#
# Default is not set
# Default is "Chorus Default"
#
# name = "Chorus Default"
# This is an optional description for your relay, displayed in the NIP-11 response.
# This is a description for your relay, displayed in the NIP-11 response.
#
# Default is not set
# Default is "A default config of the Chorus relay"
#
# description = "A default config of the Chorus relay"
@ -203,15 +203,15 @@ server_log_level = "Info"
#
# Possible values are: Trace, Debug, Info, Warn, Error
#
# Default is Warn
# Default is Info
#
library_log_level = "Warn"
library_log_level = "Info"
# How verbose to log issues with client requests
#
# Possible values are: Trace, Debug, Info, Warn, Error
#
# Default is Error
# Default is Info
#
client_log_level = "Error"
client_log_level = "Info"

View File

@ -10,7 +10,7 @@ The config file must be in TOML format. See the [TOML documentation](https://git
This is the directory where chorus stores data.
Default is "/tmp".
Default is "/opt/chorus/var/chorus".
If deployed according to [docs/DEPLOYING.md](docs/DEPLOYING.md), is "/opt/chorus/var/chorus".
@ -34,6 +34,8 @@ Default is 443.
This is the DNS hostname of your relay. This is used for verifying AUTH events, which specify
your relay host name.
Default is localhost
### use_tls
If true, chorus will handle TLS, running over HTTPS. If false, chorus run over HTTP.
@ -41,17 +43,18 @@ If true, chorus will handle TLS, running over HTTPS. If false, chorus run over
If you are proxying via nginx, normally you will set this to false and allow nginx to handle
TLS.
Default is true
### certchain_pem_path
This is the path to your TLS certificate chain file.
If `use_tls` is false, this value is irrelevant.
Default is "./tls/fullchain.pem".
Default is "/opt/chorus/etc/tls/fullchain.pem"
If deployed according to [docs/DEPLOYING.md](docs/DEPLOYING.md) using the direct method,
this is set to "/opt/chorus/etc/tls/fullchain.pem" and the systemd service copies letsencrypt
TLS certificates into this position on start.
systemd service copies letsencrypt TLS certificates into this position on start.
### key_pem_path
@ -59,23 +62,22 @@ This is the path to yoru TLS private key file.
If `use_tls` is false, this value is irrelevant.
Default is "./tls/privkey.pem".
Default is "/opt/chorus/etc/tls/privkey.pem"
If deployed according to [docs/DEPLOYING.md](docs/DEPLOYING.md) using the direct method,
this is set to "/opt/chorus/etc/tls/privkey.pem" and the systemd service copies letsencrypt
TLS certificates into this position on start.
systemd service copies letsencrypt TLS certificates into this position on start.
### name
This is an optional name for your relay, displayed in the NIP-11 response.
Default is None.
Default is "Chorus Default"
### description
This is an optional description for your relay, displayed in the NIP-11 response.
Default is None.
Default is "A default config of the Chorus relay".
### contact
@ -177,7 +179,7 @@ How verbose to log library issues and other general issues
Possible values are: Trace, Debug, Info, Warn, Error
Default is Warn
Default is Info
### client_log_level
@ -185,5 +187,5 @@ How verbose to log issues with client requests
Possible values are: Trace, Debug, Info, Warn, Error
Default is Error
Default is Info