48 Commits

Author SHA1 Message Date
039cbcdfc4 incorporate keystore change from golgi 2022-05-13 13:09:03 +02:00
4cd2692b9a fix probes dependency version 2022-05-13 13:07:37 +02:00
3e5e7e0f7c update wpactrl dependency api usage 2022-05-13 13:07:21 +02:00
e6fd9a48cf Merge pull request 'Update peach-config to use configured paths' (#111) from change-paths2 into main
Reviewed-on: #111
2022-05-12 20:25:13 +00:00
8960df6635 Fix cargo fmt
All checks were successful
continuous-integration/drone/pr Build is passing
2022-05-12 21:58:15 +02:00
781af460ae Fix clippy warning
Some checks failed
continuous-integration/drone/pr Build is failing
2022-05-12 13:32:40 +02:00
4a08e4ed6d Merge pull request 'Use get_config_value for Rouille configurations' (#109) from get-config into main
Reviewed-on: #109
2022-05-12 11:31:18 +00:00
908d265de6 Bump version numbers
Some checks failed
continuous-integration/drone/pr Build is failing
2022-05-12 12:53:06 +02:00
8202d4af5f Update peach-config to use configured paths
Some checks failed
continuous-integration/drone/pr Build is failing
2022-05-12 12:49:47 +02:00
5ea6a86700 Fix clippy error
All checks were successful
continuous-integration/drone/pr Build is passing
2022-05-12 12:24:17 +02:00
99fd3be4ad Change RouilleConfig to ServerConfig
Some checks failed
continuous-integration/drone/pr Build is failing
2022-05-12 12:21:05 +02:00
e041e1c7f9 Change defaults to only be defined in config_manager.rs
All checks were successful
continuous-integration/drone/pr Build is passing
2022-05-12 11:55:59 +02:00
e10777a5a5 Merge pull request 'Enable nanorand featureflag for cross-compilation on mac os' (#108) from cross-compilation into main
Reviewed-on: #108
2022-05-12 09:27:00 +00:00
288941e8a3 Change Rouille to use get_config_value
Some checks failed
continuous-integration/drone/pr Build is failing
2022-05-12 11:26:23 +02:00
f96c950aa6 Enable nanorand featureflag for cross-compilation on mac os
All checks were successful
continuous-integration/drone/pr Build is passing
2022-05-11 17:06:12 +02:00
bab33b602a Merge pull request 'Remove unecessary .to_string' (#106) from to-string into main
Reviewed-on: #106
2022-05-11 09:48:10 +00:00
b84e470a42 Remove unecessary .to_string
All checks were successful
continuous-integration/drone/pr Build is passing
2022-05-11 11:24:33 +02:00
97680f9010 Merge branch 'main' of https://git.coopcloud.tech/PeachCloud/peach-workspace into main5 2022-05-11 11:05:56 +02:00
ab0401e555 Change lazy_static to 1.4 2022-05-11 11:05:49 +02:00
810a97db8a Merge pull request 'Update config_manager to check from three sources of configuration' (#105) from config into main
Reviewed-on: #105
2022-05-11 09:03:57 +00:00
610d60d989 Fix typo
All checks were successful
continuous-integration/drone/pr Build is passing
2022-05-11 10:34:53 +02:00
f4c1bc1169 Fix cargo fmt in peach-web
All checks were successful
continuous-integration/drone/pr Build is passing
2022-05-10 13:25:53 +02:00
3ae182caa9 Fix clippy warnings in peach-web
Some checks failed
continuous-integration/drone/pr Build is failing
2022-05-10 13:23:39 +02:00
8a6ad4ad61 Fix example
Some checks failed
continuous-integration/drone/pr Build is failing
2022-05-10 13:08:32 +02:00
600f9c58bf Use &str instead of String in save_config_value
Some checks failed
continuous-integration/drone/pr Build is failing
2022-05-10 13:07:50 +02:00
2540a77af1 Working 3-way configuration
Some checks failed
continuous-integration/drone/pr Build is failing
2022-05-10 12:59:36 +02:00
5b86f754f4 Working on refactor to use hashmaps
Some checks failed
continuous-integration/drone/pr Build is failing
2022-05-09 15:53:03 +02:00
29804b0dce Get CONFIG_PATH from env_variable 2022-05-03 15:50:45 +02:00
e2ac5de6e4 Merge pull request 'Fix peach-config manifest' (#103) from fix-manifest into main
Reviewed-on: #103
2022-04-20 18:27:43 +00:00
d03de8cf5d Remove extraneous quote
All checks were successful
continuous-integration/drone/pr Build is passing
2022-04-20 13:55:26 -04:00
03720a7338 Remove unused import
All checks were successful
continuous-integration/drone/pr Build is passing
2022-04-20 12:19:15 -04:00
cf9c0c7eca Add doc comment 2022-04-20 12:18:31 -04:00
f764acc2df More concise transforms 2022-04-20 12:15:38 -04:00
a347e4726d Fix manifest 2022-04-20 12:07:32 -04:00
3d3006049b Merge pull request 'Change peach-web to use systemd system calls' (#102) from disc-image into main
Reviewed-on: #102
2022-04-19 18:41:22 +00:00
2adb3006fe Cargo fmt
All checks were successful
continuous-integration/drone/pr Build is passing
2022-04-19 12:57:09 -04:00
64b5929e5c Update peach-config to set file permissions correctly
Some checks failed
continuous-integration/drone/pr Build is failing
2022-04-19 12:50:29 -04:00
5629a048a1 Fix conflict 2022-04-18 16:45:28 -04:00
713c3da4cc fix formatting 2022-04-15 11:08:55 +02:00
92c7d7daa9 Merge pull request 'Drone CI build pipeline' (#101) from drone_ci_test into main
Reviewed-on: #101
2022-04-15 09:05:02 +00:00
5a95ade8b9 only run ci on pull request and add docs to readme [CI SKIP]
All checks were successful
continuous-integration/drone/pr Build is passing
2022-04-15 10:38:12 +02:00
315b04a63e Update permissions for peach home dir in peach-config 2022-04-14 16:29:57 -04:00
1866e289a6 Fix clippy warning in update.rs 2022-04-14 16:11:20 -04:00
bff86a490b Bump version number 2022-04-14 15:53:09 -04:00
65d5352c85 Bump version number of peach-config 2022-04-14 14:51:43 -04:00
df3b4b8858 Update peach-config to install go-sbot instead of peach-go-sbot 2022-04-14 14:51:27 -04:00
2f1535fbee Update peach-web to use systemd system service 2022-04-14 14:47:43 -04:00
b75aadd62d Working on peachcloud disc image 2022-04-11 10:31:45 -04:00
30 changed files with 705 additions and 669 deletions

View File

@ -30,5 +30,4 @@ steps:
trigger:
event:
- push
- pull_request

576
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -45,6 +45,19 @@ _Better [Scuttlebutt](https://scuttlebutt.nz) cloud infrastructure as a hardware
- [peach-patterns](https://github.com/peachcloud/peach-patterns) - Pattern library for the PeachCloud UI design system
- [peach-web](https://github.com/peachcloud/peach-web) - A web interface for monitoring and interacting with the PeachCloud device
## Continuous Integration
[Drone CI](https://docs.drone.io/) is used to provide continuous integration for this workspace. The configuration file can be found in `.drone.yml` in the root of this repository. It is currently configured to run `cargo fmt`, `cargo clippy`, `cargo test` and `cargo build` on every `pull request` event. The pipeline runs on the AMD64 Debian Buster image from the official Rust Docker image repository.
The status of the current and previous CI builds can be viewed via the [Drone CI Build UI](https://build.coopcloud.tech/PeachCloud/peach-workspace) (kindly hosted by Co-op Cloud).
Adding `[CI SKIP]` to the end of a commit message results in the CI checks being skipped for the next event. For example:
```
git commit -m "update readme [CI SKIP]"
git push origin main
```
## Developer Diaries
- [@ahdinosaur](https://github.com/ahdinosaur): `@6ilZq3kN0F+dXFHAPjAwMm87JEb/VdB+LC9eIMW3sa0=.ed25519`

View File

@ -1,6 +1,6 @@
[package]
name = "peach-config"
version = "0.1.17"
version = "0.1.25"
authors = ["Andrew Reid <gnomad@cryptolab.net>", "Max Fowler <max@mfowler.info>"]
edition = "2018"
description = "Command line tool for installing, updating and configuring PeachCloud"

View File

@ -10,7 +10,7 @@ pub const SERVICES: [&str; 8] = [
"peach-buttons",
"peach-oled",
"peach-dyndns-updater",
"peach-go-sbot",
"go-sbot",
"peach-config",
];

View File

@ -1,40 +1,32 @@
use regex::Regex;
use serde::{Deserialize, Serialize};
use snafu::ResultExt;
use std::collections::HashMap;
use std::fs;
use crate::constants::HARDWARE_CONFIG_FILE;
use crate::constants::{HARDWARE_CONFIG_FILE, SERVICES};
use crate::error::{FileReadError, FileWriteError, PeachConfigError};
use crate::utils::get_output;
use crate::RtcOption;
/// Helper function which returns the version of a package currently installed,
/// as an Ok(String) if found, and as an Err if not found
pub fn get_package_version_number(package: &str) -> Result<String, PeachConfigError> {
let version = get_output(&["dpkg-query", "--showformat=${Version}", "--show", package])?;
Ok(version)
}
/// Returns a HashMap<String, String> of all the peach-packages which are currently installed
/// mapped to their version number e.g. { "peach-probe": "1.2.0", "peach-network": "1.4.0" }
pub fn get_currently_installed_microservices() -> Result<HashMap<String, String>, PeachConfigError>
{
// gets a list of all packages currently installed with dpkg
let packages = get_output(&["dpkg", "-l"])?;
// this regex matches packages which contain the word peach in them
// and has two match groups
// 1. the first match group gets the package name
// 2. the second match group gets the version number of the package
let re: Regex = Regex::new(r"\S+\s+(\S*peach\S+)\s+(\S+).*\n").unwrap();
// the following iterator, iterates through the captures matched via the regex
// and for each capture, creates a value in the hash map,
// which maps the name of the package, to its version number
// e.g. { "peach-probe": "1.2.0", "peach-network": "1.4.0" }
let peach_packages: HashMap<String, String> = re
.captures_iter(&packages)
.filter_map(|cap| {
let groups = (cap.get(1), cap.get(2));
match groups {
(Some(package), Some(version)) => {
Some((package.as_str().to_string(), version.as_str().to_string()))
}
_ => None,
// gets a list of all packages currently installed with dpkg-query
let peach_packages: HashMap<String, String> = SERVICES
.iter()
.filter_map(|service| {
let version = get_package_version_number(service);
match version {
Ok(v) => Some((service.to_string(), v)),
Err(_) => None,
}
})
.collect();

View File

@ -1,21 +1,30 @@
use lazy_static::lazy_static;
use peach_lib::config_manager::get_config_value;
use crate::error::PeachConfigError;
use crate::utils::cmd;
/// All configs are stored in this folder, and should be read/writeable by peach group
/// so they can be read and written by all PeachCloud services.
pub const CONFIGS_DIR: &str = "/var/lib/peachcloud";
pub const PEACH_WEB_DIR: &str = "/usr/share/peach-web";
lazy_static! {
pub static ref PEACH_CONFIGDIR: String = get_config_value("PEACH_CONFIGDIR")
.expect("Failed to load config value for PEACH_CONFIGDIR");
pub static ref PEACH_WEBDIR: String =
get_config_value("PEACH_WEBDIR").expect("Failed to load config value for PEACH_WEBDIR");
pub static ref PEACH_HOMEDIR: String =
get_config_value("PEACH_HOMEDIR").expect("Failed to load config value for PEACH_HOMEDIR");
}
/// Utility function to set correct file permissions on the PeachCloud device.
/// Accidentally changing file permissions is a fairly common thing to happen,
/// so this is a useful CLI function for quickly correcting anything that may be out of order.
pub fn set_permissions() -> Result<(), PeachConfigError> {
println!("[ UPDATING FILE PERMISSIONS ON PEACHCLOUD DEVICE ]");
cmd(&["chmod", "-R", "u+rwX,g+rwX", CONFIGS_DIR])?;
cmd(&["chown", "-R", "peach", CONFIGS_DIR])?;
cmd(&["chgrp", "-R", "peach", CONFIGS_DIR])?;
cmd(&["chmod", "-R", "u+rwX,g+rwX", PEACH_WEB_DIR])?;
cmd(&["chown", "-R", "peach-web:peach", PEACH_WEB_DIR])?;
cmd(&["chmod", "-R", "u+rwX,g+rwX", &PEACH_CONFIGDIR])?;
cmd(&["chown", "-R", "peach:peach", &PEACH_CONFIGDIR])?;
cmd(&["chmod", "-R", "u+rwX,g+rwX", &PEACH_WEBDIR])?;
cmd(&["chown", "-R", "peach:peach", &PEACH_WEBDIR])?;
cmd(&["chmod", "-R", "u+rwX,g+rwX", &PEACH_HOMEDIR])?;
cmd(&["chown", "-R", "peach:peach", &PEACH_HOMEDIR])?;
println!("[ PERMISSIONS SUCCESSFULLY UPDATED ]");
Ok(())
}

View File

@ -4,6 +4,7 @@ use std::fs;
use crate::error::{FileWriteError, PeachConfigError};
use crate::generate_manifest::save_hardware_config;
use crate::set_permissions::set_permissions;
use crate::setup_networking::configure_networking;
use crate::setup_peach_deb::setup_peach_deb;
use crate::update::update_microservices;
@ -239,6 +240,9 @@ pub fn setup_peach(
info!("[ SAVING LOG OF HARDWARE CONFIGURATIONS ]");
save_hardware_config(i2c, rtc)?;
info!("[ SETTING FILE PERMISSIONS ]");
set_permissions()?;
info!("[ PEACHCLOUD SETUP COMPLETE ]");
info!("[ ------------------------- ]");
info!("[ please reboot your device ]");

View File

@ -1,6 +1,6 @@
[package]
name = "peach-lib"
version = "1.3.2"
version = "1.3.3"
authors = ["Andrew Reid <glyph@mycelial.technology>"]
edition = "2018"
@ -14,10 +14,11 @@ jsonrpc-client-core = "0.5"
jsonrpc-client-http = "0.5"
jsonrpc-core = "8.0"
log = "0.4"
nanorand = "0.6"
nanorand = { version = "0.6", features = ["getrandom"] }
regex = "1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_yaml = "0.8"
toml = "0.5"
sha3 = "0.10"
lazy_static = "1.4"

View File

@ -1,4 +0,0 @@
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
objcopy = { path ="aarch64-linux-gnu-objcopy" }
strip = { path ="aarch64-linux-gnu-strip" }

View File

@ -1,12 +0,0 @@
[package]
name = "debug"
version = "0.1.0"
authors = ["notplants <mfowler.email@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
peach-lib = { path = "../" }
env_logger = "0.6"
chrono = "0.4.19"

View File

@ -1,65 +0,0 @@
use peach_lib::dyndns_client::{dyndns_update_ip, register_domain, is_dns_updater_online, log_successful_nsupdate, get_num_seconds_since_successful_dns_update };
use peach_lib::password_utils::{verify_password, set_new_password, verify_temporary_password, set_new_temporary_password, send_password_reset};
use peach_lib::config_manager::{add_ssb_admin_id, delete_ssb_admin_id};
use peach_lib::sbot_client;
use std::process;
use chrono::prelude::*;
fn main() {
// initalize the logger
env_logger::init();
//
// println!("Hello, world its debug!");
// let result = set_new_password("password3");
// println!("result: {:?}", result);
//
// let result = verify_password("password1");
// println!("result should be error: {:?}", result);
//
// let result = verify_password("password3");
// println!("result should be ok: {:?}", result);
//
//
// println!("Testing temporary passwords");
// let result = set_new_temporary_password("abcd");
// println!("result: {:?}", result);
//
// let result = verify_temporary_password("password1");
// println!("result should be error: {:?}", result);
//
// let result = verify_temporary_password("abcd");
// println!("result should be ok: {:?}", result);
//
let result = send_password_reset();
println!("send password reset result should be ok: {:?}", result);
// sbot_client::post("hi cat");
// let result = sbot_client::whoami();
// let result = sbot_client::create_invite(50);
// let result = sbot_client::post("is this working");
// println!("result: {:?}", result);
// let result = sbot_client::post("nice we have contact");
// let result = sbot_client::update_pub_name("vermont-pub");
// let result = sbot_client::private_message("this is a private message", "@LZx+HP6/fcjUm7vef2eaBKAQ9gAKfzmrMVGzzdJiQtA=.ed25519");
// println!("result: {:?}", result);
// let result = send_password_reset();
// let result = add_ssb_admin_id("xyzdab");
// println!("result: {:?}", result);
// let result = delete_ssb_admin_id("xyzdab");
// println!("result: {:?}", result);
// let result = delete_ssb_admin_id("ab");
// println!("result: {:?}", result);
//// let result = log_successful_nsupdate();
//// let result = get_num_seconds_since_successful_dns_update();
// let is_online = is_dns_updater_online();
// println!("is online: {:?}", is_online);
//
//// let result = get_last_successful_dns_update();
//// println!("result: {:?}", result);
//// register_domain("newquarter299.dyn.peachcloud.org");
// let result = dyndns_update_ip();
// println!("result: {:?}", result);
}

View File

@ -0,0 +1,11 @@
use peach_lib::config_manager::{get_config_value, save_config_value};
fn main() {
println!("Running example of PeachCloud configuration management");
let v = get_config_value("ADDR").unwrap();
println!("ADDR: {}", v);
save_config_value("ADDR", "1.1.1.1");
let v = get_config_value("ADDR").unwrap();
println!("ADDR: {}", v);
}

View File

@ -3,170 +3,274 @@
//! Different PeachCloud microservices import peach-lib, so that they can share
//! this interface.
//!
//! Config values are looked up from three locations in this order by key name:
//! 1. from environmental variables
//! 2. from a configuration file
//! 3. from default values
//!
//! The configuration file is located at: "/var/lib/peachcloud/config.yml"
//! unless its path is configured by setting PEACH_CONFIG_PATH env variable.
use std::fs;
use std::collections::{BTreeMap, HashMap};
use std::{env, fs};
use fslock::LockFile;
use lazy_static::lazy_static;
use log::debug;
use serde::{Deserialize, Serialize};
use crate::error::PeachError;
// main configuration file
pub const YAML_PATH: &str = "/var/lib/peachcloud/config.yml";
// lock file (used to avoid race conditions during config reading & writing)
pub const LOCK_FILE_PATH: &str = "/var/lib/peachcloud/config.lock";
// default values
pub const DEFAULT_DYN_SERVER_ADDRESS: &str = "http://dynserver.dyn.peachcloud.org";
pub const DEFAULT_DYN_NAMESERVER: &str = "ns.peachcloud.org";
// we make use of Serde default values in order to make PeachCloud
// robust and keep running even with a not fully complete config.yml
// main type which represents all peachcloud configurations
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct PeachConfig {
#[serde(default)]
pub external_domain: String,
#[serde(default)]
pub dyn_domain: String,
#[serde(default)]
pub dyn_dns_server_address: String,
#[serde(default)]
pub dyn_use_custom_server: bool,
#[serde(default)]
pub dyn_nameserver: String,
#[serde(default)]
pub dyn_tsig_key_path: String,
#[serde(default)] // default is false
pub dyn_enabled: bool,
#[serde(default)] // default is empty vector
pub ssb_admin_ids: Vec<String>,
#[serde(default)]
pub admin_password_hash: String,
#[serde(default)]
pub temporary_password_hash: String,
// load path to main configuration file
// from PEACH_CONFIG_PATH if that environment variable is set
// or using the default value if not set
pub const DEFAULT_YAML_PATH: &str = "/var/lib/peachcloud/config.yml";
lazy_static! {
static ref CONFIG_PATH: String = {
if let Ok(val) = env::var("PEACH_CONFIG_PATH") {
val
}
else {
DEFAULT_YAML_PATH.to_string()
}
};
// lock file (used to avoid race conditions during config reading & writing)
// the lock file path is the config file path + ".lock"
static ref LOCK_FILE_PATH: String = format!("{}.lock", *CONFIG_PATH);
}
// helper functions for serializing and deserializing PeachConfig from disc
pub fn save_peach_config(peach_config: PeachConfig) -> Result<PeachConfig, PeachError> {
// Default values for PeachCloud configs which are used for any key which is not set
// via an environment variable or in a saved configuration file.
pub fn get_peach_config_defaults() -> HashMap<String, String> {
let peach_config_defaults: HashMap<&str, &str> = HashMap::from([
("STANDALONE_MODE", "true"),
("DISABLE_AUTH", "false"),
("ADDR", "127.0.0.1"),
("PORT", "8000"),
("EXTERNAL_DOMAIN", ""),
("DYN_DOMAIN", ""),
(
"DYN_DNS_SERVER_ADDRESS",
"http://dynserver.dyn.peachcloud.org",
),
("DYN_USE_CUSTOM_SERVER", "true"),
("DYN_TSIG_KEY_PATH", ""),
("DYN_NAMESERVER", "ns.peachcloud.org"),
("DYN_ENABLED", "false"),
("SSB_ADMIN_IDS", ""),
("ADMIN_PASSWORD_HASH", "47"),
("TEMPORARY_PASSWORD_HASH", ""),
("GO_SBOT_DATADIR", "/home/peach/.ssb-go"),
("PEACH_CONFIGDIR", "/var/lib/peachcloud"),
("PEACH_HOMEDIR", "/home/peach"),
("PEACH_WEBDIR", "/usr/share/peach-web"),
]);
// convert HashMap<&str, &str> to HashMap<String, String> and return
let pc_defaults: HashMap<String, String> = peach_config_defaults
.iter()
.map(|(key, val)| (key.to_string(), val.to_string()))
.collect();
pc_defaults
}
// primary interface for getting config values
// Config values are looked up from three locations in this order by key name:
// 1. from environmental variables
// 2. from a configuration file
// 3. from default values
pub fn get_config_value(key: &str) -> Result<String, PeachError> {
// first check if there is an environmental variable set
if let Ok(val) = env::var(key) {
Ok(val)
} else {
// then check if a value is set in the config file
let peach_config_on_disc = load_peach_config_from_disc()?;
let val = peach_config_on_disc.get(key);
// if no value is found in the config file, then get the default value
match val {
// return config value
Some(v) => Ok(v.to_string()),
// get default value
None => {
match get_peach_config_defaults().get(key) {
Some(v) => Ok(v.to_string()),
// if this key was not found in the defaults, then it was an invalid key
None => Err(PeachError::InvalidKey {
key: key.to_string(),
}),
}
}
}
}
}
// helper function to load PeachCloud configuration file saved to disc
pub fn load_peach_config_from_disc() -> Result<HashMap<String, String>, PeachError> {
let peach_config_exists = std::path::Path::new(CONFIG_PATH.as_str()).exists();
// if config file does not exist, return an emtpy HashMap
if !peach_config_exists {
let peach_config: HashMap<String, String> = HashMap::new();
Ok(peach_config)
}
// otherwise we load peach config from disk
else {
debug!("Loading peach config: {} exists", CONFIG_PATH.as_str());
let contents =
fs::read_to_string(CONFIG_PATH.as_str()).map_err(|source| PeachError::Read {
source,
path: CONFIG_PATH.to_string(),
})?;
let peach_config: HashMap<String, String> = serde_yaml::from_str(&contents)?;
Ok(peach_config)
}
}
// helper function to save PeachCloud configuration file to disc
// takes in a Hashmap<String, String> and saves the whole HashMap as a yaml file
// with the keys in alphabetical order
pub fn save_peach_config_to_disc(
peach_config: HashMap<String, String>,
) -> Result<HashMap<String, String>, PeachError> {
// use a file lock to avoid race conditions while saving config
let mut lock = LockFile::open(LOCK_FILE_PATH)?;
let mut lock = LockFile::open(&*LOCK_FILE_PATH)?;
lock.lock()?;
let yaml_str = serde_yaml::to_string(&peach_config)?;
// first convert Hashmap to BTreeMap (so that keys are saved in deterministic alphabetical order)
let ordered: BTreeMap<_, _> = peach_config.iter().collect();
// then serialize BTreeMap as yaml
let yaml_str = serde_yaml::to_string(&ordered)?;
fs::write(YAML_PATH, yaml_str).map_err(|source| PeachError::Write {
// write yaml to file
fs::write(CONFIG_PATH.as_str(), yaml_str).map_err(|source| PeachError::Write {
source,
path: YAML_PATH.to_string(),
path: CONFIG_PATH.to_string(),
})?;
// unlock file lock
lock.unlock()?;
// return peach_config
// return modified HashMap
Ok(peach_config)
}
pub fn load_peach_config() -> Result<PeachConfig, PeachError> {
let peach_config_exists = std::path::Path::new(YAML_PATH).exists();
// helper functions for serializing and deserializing PeachConfig values from disc
pub fn save_config_value(key: &str, value: &str) -> Result<HashMap<String, String>, PeachError> {
// get current config from disc
let mut peach_config = load_peach_config_from_disc()?;
let peach_config: PeachConfig = if !peach_config_exists {
debug!("Loading peach config: {} does not exist", YAML_PATH);
PeachConfig {
external_domain: "".to_string(),
dyn_domain: "".to_string(),
dyn_dns_server_address: DEFAULT_DYN_SERVER_ADDRESS.to_string(),
dyn_use_custom_server: false,
dyn_nameserver: DEFAULT_DYN_NAMESERVER.to_string(),
dyn_tsig_key_path: "".to_string(),
dyn_enabled: false,
ssb_admin_ids: Vec::new(),
// default password is `peach`
admin_password_hash: "146".to_string(),
temporary_password_hash: "".to_string(),
}
}
// otherwise we load peach config from disk
else {
debug!("Loading peach config: {} exists", YAML_PATH);
let contents = fs::read_to_string(YAML_PATH).map_err(|source| PeachError::Read {
source,
path: YAML_PATH.to_string(),
})?;
serde_yaml::from_str(&contents)?
};
// insert new key/value
peach_config.insert(key.to_string(), value.to_string());
Ok(peach_config)
// save the modified hashmap to disc
save_peach_config_to_disc(peach_config)
}
// interfaces for setting specific config values
// set all dyn configuration values at once
pub fn set_peach_dyndns_config(
dyn_domain: &str,
dyn_dns_server_address: &str,
dyn_tsig_key_path: &str,
dyn_enabled: bool,
) -> Result<PeachConfig, PeachError> {
let mut peach_config = load_peach_config()?;
peach_config.dyn_domain = dyn_domain.to_string();
peach_config.dyn_dns_server_address = dyn_dns_server_address.to_string();
peach_config.dyn_tsig_key_path = dyn_tsig_key_path.to_string();
peach_config.dyn_enabled = dyn_enabled;
save_peach_config(peach_config)
) -> Result<HashMap<String, String>, PeachError> {
let mut peach_config = load_peach_config_from_disc()?;
let dyn_enabled_str = match dyn_enabled {
true => "true",
false => "false",
};
peach_config.insert("DYN_DOMAIN".to_string(), dyn_domain.to_string());
peach_config.insert(
"DYN_DNS_SERVER_ADDRESS".to_string(),
dyn_dns_server_address.to_string(),
);
peach_config.insert(
"DYN_TSIG_KEY_PATH".to_string(),
dyn_tsig_key_path.to_string(),
);
peach_config.insert("DYN_ENABLED".to_string(), dyn_enabled_str.to_string());
save_peach_config_to_disc(peach_config)
}
pub fn set_external_domain(new_external_domain: &str) -> Result<PeachConfig, PeachError> {
let mut peach_config = load_peach_config()?;
peach_config.external_domain = new_external_domain.to_string();
save_peach_config(peach_config)
pub fn set_external_domain(
new_external_domain: &str,
) -> Result<HashMap<String, String>, PeachError> {
save_config_value("EXTERNAL_DOMAIN", new_external_domain)
}
pub fn get_peachcloud_domain() -> Result<Option<String>, PeachError> {
let peach_config = load_peach_config()?;
if !peach_config.external_domain.is_empty() {
Ok(Some(peach_config.external_domain))
} else if !peach_config.dyn_domain.is_empty() {
Ok(Some(peach_config.dyn_domain))
let external_domain = get_config_value("EXTERNAL_DOMAIN")?;
let dyn_domain = get_config_value("DYN_DOMAIN")?;
if !external_domain.is_empty() {
Ok(Some(external_domain))
} else if !dyn_domain.is_empty() {
Ok(Some(dyn_domain))
} else {
Ok(None)
}
}
pub fn get_dyndns_server_address() -> Result<String, PeachError> {
let peach_config = load_peach_config()?;
// if the user is using a custom dyn server then load the address from the config
if peach_config.dyn_use_custom_server {
Ok(peach_config.dyn_dns_server_address)
}
// otherwise hardcode the address
else {
Ok(DEFAULT_DYN_SERVER_ADDRESS.to_string())
get_config_value("DYN_DNS_SERVER_ADDRESS")
}
pub fn set_dyndns_enabled_value(
enabled_value: bool,
) -> Result<HashMap<String, String>, PeachError> {
match enabled_value {
true => save_config_value("DYN_ENABLED", "true"),
false => save_config_value("DYN_ENABLED", "false"),
}
}
pub fn set_dyndns_enabled_value(enabled_value: bool) -> Result<PeachConfig, PeachError> {
let mut peach_config = load_peach_config()?;
peach_config.dyn_enabled = enabled_value;
save_peach_config(peach_config)
pub fn get_dyndns_enabled_value() -> Result<bool, PeachError> {
let val = get_config_value("DYN_ENABLED")?;
Ok(val == "true")
}
pub fn add_ssb_admin_id(ssb_id: &str) -> Result<PeachConfig, PeachError> {
let mut peach_config = load_peach_config()?;
peach_config.ssb_admin_ids.push(ssb_id.to_string());
save_peach_config(peach_config)
pub fn set_admin_password_hash(
password_hash: String,
) -> Result<HashMap<String, String>, PeachError> {
save_config_value("ADMIN_PASSWORD_HASH", &password_hash)
}
pub fn delete_ssb_admin_id(ssb_id: &str) -> Result<PeachConfig, PeachError> {
let mut peach_config = load_peach_config()?;
let mut ssb_admin_ids = peach_config.ssb_admin_ids;
pub fn get_admin_password_hash() -> Result<String, PeachError> {
let admin_password_hash = get_config_value("ADMIN_PASSWORD_HASH")?;
if !admin_password_hash.is_empty() {
Ok(admin_password_hash)
} else {
Err(PeachError::PasswordNotSet)
}
}
pub fn set_temporary_password_hash(
password_hash: String,
) -> Result<HashMap<String, String>, PeachError> {
save_config_value("TEMPORARY_PASSWORD_HASH", &password_hash)
}
pub fn get_temporary_password_hash() -> Result<String, PeachError> {
let admin_password_hash = get_config_value("TEMPORARY_PASSWORD_HASH")?;
if !admin_password_hash.is_empty() {
Ok(admin_password_hash)
} else {
Err(PeachError::PasswordNotSet)
}
}
// add ssb_id to vector of admin ids and save new value for SSB_ADMIN_IDS
pub fn add_ssb_admin_id(ssb_id: &str) -> Result<Vec<String>, PeachError> {
let mut ssb_admin_ids = get_ssb_admin_ids()?;
ssb_admin_ids.push(ssb_id.to_string());
save_ssb_admin_ids(ssb_admin_ids)
}
// remove ssb_id from vector of admin ids if found and save new value for SSB_ADMIN_IDS
// if value is not found then return an error
pub fn delete_ssb_admin_id(ssb_id: &str) -> Result<Vec<String>, PeachError> {
let mut ssb_admin_ids = get_ssb_admin_ids()?;
let index_result = ssb_admin_ids.iter().position(|x| *x == ssb_id);
match index_result {
Some(index) => {
ssb_admin_ids.remove(index);
peach_config.ssb_admin_ids = ssb_admin_ids;
save_peach_config(peach_config)
save_ssb_admin_ids(ssb_admin_ids)
}
None => Err(PeachError::SsbAdminIdNotFound {
id: ssb_id.to_string(),
@ -174,32 +278,16 @@ pub fn delete_ssb_admin_id(ssb_id: &str) -> Result<PeachConfig, PeachError> {
}
}
pub fn set_admin_password_hash(password_hash: &str) -> Result<PeachConfig, PeachError> {
let mut peach_config = load_peach_config()?;
peach_config.admin_password_hash = password_hash.to_string();
save_peach_config(peach_config)
// looks up the String value for SSB_ADMIN_IDS and converts it into a Vec<String>
pub fn get_ssb_admin_ids() -> Result<Vec<String>, PeachError> {
let ssb_admin_ids_str = get_config_value("SSB_ADMIN_IDS")?;
let ssb_admin_ids: Vec<String> = serde_json::from_str(&ssb_admin_ids_str)?;
Ok(ssb_admin_ids)
}
pub fn get_admin_password_hash() -> Result<String, PeachError> {
let peach_config = load_peach_config()?;
if !peach_config.admin_password_hash.is_empty() {
Ok(peach_config.admin_password_hash)
} else {
Err(PeachError::PasswordNotSet)
}
}
pub fn set_temporary_password_hash(password_hash: &str) -> Result<PeachConfig, PeachError> {
let mut peach_config = load_peach_config()?;
peach_config.temporary_password_hash = password_hash.to_string();
save_peach_config(peach_config)
}
pub fn get_temporary_password_hash() -> Result<String, PeachError> {
let peach_config = load_peach_config()?;
if !peach_config.temporary_password_hash.is_empty() {
Ok(peach_config.temporary_password_hash)
} else {
Err(PeachError::PasswordNotSet)
}
// takes in a Vec<String> and saves SSB_ADMIN_IDS as a json string representation of this vec
pub fn save_ssb_admin_ids(ssb_admin_ids: Vec<String>) -> Result<Vec<String>, PeachError> {
let ssb_admin_ids_as_json_str = serde_json::to_string(&ssb_admin_ids)?;
save_config_value("SSB_ADMIN_IDS", &ssb_admin_ids_as_json_str)?;
Ok(ssb_admin_ids)
}

View File

@ -18,7 +18,9 @@ use jsonrpc_client_http::HttpTransport;
use log::{debug, info};
use regex::Regex;
use crate::config_manager::get_dyndns_server_address;
use crate::config_manager::{
get_config_value, get_dyndns_enabled_value, get_dyndns_server_address,
};
use crate::{config_manager, error::PeachError};
/// constants for dyndns configuration
@ -107,7 +109,11 @@ fn get_public_ip_address() -> Result<String, PeachError> {
/// Reads dyndns configurations from config.yml
/// and then uses nsupdate to update the IP address for the configured domain
pub fn dyndns_update_ip() -> Result<bool, PeachError> {
let peach_config = config_manager::load_peach_config()?;
let dyn_tsig_key_path = get_config_value("DYN_TSIG_KEY_PATH")?;
let dyn_enabled = get_dyndns_enabled_value()?;
let dyn_domain = get_config_value("DYN_DOMAIN")?;
let dyn_dns_server_address = get_config_value("DYN_DNS_SERVER_ADDRESS")?;
let dyn_nameserver = get_config_value("DYN_NAMESERVER")?;
info!(
"Using config:
dyn_tsig_key_path: {:?}
@ -116,22 +122,15 @@ pub fn dyndns_update_ip() -> Result<bool, PeachError> {
dyn_enabled: {:?}
dyn_nameserver: {:?}
",
peach_config.dyn_tsig_key_path,
peach_config.dyn_domain,
peach_config.dyn_dns_server_address,
peach_config.dyn_enabled,
peach_config.dyn_nameserver,
dyn_tsig_key_path, dyn_domain, dyn_dns_server_address, dyn_enabled, dyn_nameserver,
);
if !peach_config.dyn_enabled {
if !dyn_enabled {
info!("dyndns is not enabled, not updating");
Ok(false)
} else {
// call nsupdate passing appropriate configs
let mut nsupdate_command = Command::new("nsupdate");
nsupdate_command
.arg("-k")
.arg(&peach_config.dyn_tsig_key_path)
.arg("-v");
nsupdate_command.arg("-k").arg(&dyn_tsig_key_path).arg("-v");
// pass nsupdate commands via stdin
let public_ip_address = get_public_ip_address()?;
info!("found public ip address: {}", public_ip_address);
@ -142,9 +141,9 @@ pub fn dyndns_update_ip() -> Result<bool, PeachError> {
update delete {DOMAIN} A
update add {DOMAIN} 30 A {PUBLIC_IP_ADDRESS}
send",
NAMESERVER = peach_config.dyn_nameserver,
ZONE = peach_config.dyn_domain,
DOMAIN = peach_config.dyn_domain,
NAMESERVER = dyn_nameserver,
ZONE = dyn_domain,
DOMAIN = dyn_domain,
PUBLIC_IP_ADDRESS = public_ip_address,
);
info!("ns_commands: {:?}", ns_commands);
@ -217,8 +216,7 @@ pub fn get_num_seconds_since_successful_dns_update() -> Result<Option<i64>, Peac
/// and has successfully run recently (in the last six minutes)
pub fn is_dns_updater_online() -> Result<bool, PeachError> {
// first check if it is enabled in peach-config
let peach_config = config_manager::load_peach_config()?;
let is_enabled = peach_config.dyn_enabled;
let is_enabled = get_dyndns_enabled_value()?;
// then check if it has successfully run within the last 6 minutes (60*6 seconds)
let num_seconds_since_successful_update = get_num_seconds_since_successful_dns_update()?;
let ran_recently: bool = match num_seconds_since_successful_update {
@ -248,8 +246,7 @@ pub fn get_dyndns_subdomain(dyndns_full_domain: &str) -> Option<String> {
// helper function which checks if a dyndns domain is new
pub fn check_is_new_dyndns_domain(dyndns_full_domain: &str) -> Result<bool, PeachError> {
let peach_config = config_manager::load_peach_config()?;
let previous_dyndns_domain = peach_config.dyn_domain;
let previous_dyndns_domain = get_config_value("DYN_DOMAIN")?;
Ok(dyndns_full_domain != previous_dyndns_domain)
}

View File

@ -7,6 +7,12 @@ use std::{io, str, string};
/// This type represents all possible errors that can occur when interacting with the PeachCloud library.
#[derive(Debug)]
pub enum PeachError {
/// Represents looking up a Config value with a non-existent key
InvalidKey {
/// the key value which was invalid
key: String,
},
/// Represents a failure to determine the path of the user's home directory.
HomeDir,
@ -102,6 +108,7 @@ impl std::error::Error for PeachError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self {
PeachError::HomeDir => None,
PeachError::InvalidKey { .. } => None,
PeachError::Io(_) => None,
PeachError::JsonRpcClientCore(_) => None,
PeachError::JsonRpcCore(_) => None,
@ -130,6 +137,9 @@ impl std::error::Error for PeachError {
impl std::fmt::Display for PeachError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
PeachError::InvalidKey { ref key } => {
write!(f, "Invalid key in config lookup for key: {}", key)
}
PeachError::HomeDir => {
write!(
f,

View File

@ -1,5 +1,5 @@
use async_std::task;
use golgi::Sbot;
use golgi::{sbot::Keystore, Sbot};
use log::debug;
use nanorand::{Rng, WyRand};
use sha3::{Digest, Sha3_256};
@ -33,7 +33,7 @@ pub fn validate_new_passwords(new_password1: &str, new_password2: &str) -> Resul
/// Sets a new password for the admin user
pub fn set_new_password(new_password: &str) -> Result<(), PeachError> {
let new_password_hash = hash_password(new_password);
config_manager::set_admin_password_hash(&new_password_hash)?;
config_manager::set_admin_password_hash(new_password_hash)?;
Ok(())
}
@ -53,7 +53,7 @@ pub fn hash_password(password: &str) -> String {
/// which can be used to reset the permanent password
pub fn set_new_temporary_password(new_password: &str) -> Result<(), PeachError> {
let new_password_hash = hash_password(new_password);
config_manager::set_temporary_password_hash(&new_password_hash)?;
config_manager::set_temporary_password_hash(new_password_hash)?;
Ok(())
}
@ -103,8 +103,8 @@ using this link: http://peach.local/auth/reset",
};
msg += &remote_link;
// finally send the message to the admins
let peach_config = config_manager::load_peach_config()?;
for ssb_admin_id in peach_config.ssb_admin_ids {
let ssb_admin_ids = config_manager::get_ssb_admin_ids()?;
for ssb_admin_id in ssb_admin_ids {
// use golgi to send a private message on scuttlebutt
match task::block_on(publish_private_msg(&msg, &ssb_admin_id)) {
Ok(_) => (),
@ -126,11 +126,13 @@ async fn publish_private_msg(msg: &str, recipient: &str) -> Result<(), String> {
// TODO: panics if we pass `Some(conf.shscap)` as second arg
Some(conf) => {
let ip_port = conf.lis.clone();
Sbot::init(Some(ip_port), None)
Sbot::init(Keystore::GoSbot, Some(ip_port), None)
.await
.map_err(|e| e.to_string())?
}
None => Sbot::init(None, None).await.map_err(|e| e.to_string())?,
None => Sbot::init(Keystore::GoSbot, None, None)
.await
.map_err(|e| e.to_string())?,
};
debug!("Publishing a Scuttlebutt private message with temporary password");

View File

@ -2,6 +2,7 @@
use std::{fs, fs::File, io, io::Write, path::PathBuf, process::Command, str};
use crate::config_manager::get_config_value;
use serde::{Deserialize, Serialize};
use crate::error::PeachError;
@ -62,8 +63,9 @@ impl SbotStatus {
pub fn read() -> Result<Self, PeachError> {
let mut status = SbotStatus::default();
// note this command does not need to be run as sudo
// because non-privileged users are able to run systemctl show
let info_output = Command::new("systemctl")
.arg("--user")
.arg("show")
.arg("go-sbot.service")
.arg("--no-page")
@ -83,8 +85,9 @@ impl SbotStatus {
}
}
// note this command does not need to be run as sudo
// because non-privileged users are able to run systemctl status
let status_output = Command::new("systemctl")
.arg("--user")
.arg("status")
.arg("go-sbot.service")
.output()?;
@ -124,11 +127,8 @@ impl SbotStatus {
}
}
// determine path of user's home directory
let mut blobstore_path = dirs::home_dir().ok_or(PeachError::HomeDir)?;
// append the blobstore path
blobstore_path.push(".ssb-go/blobs/sha256");
// get path to blobstore
let blobstore_path = format!("{}/blobs/sha256", get_config_value("GO_SBOT_DATADIR")?);
// determine the size of the blobstore directory in bytes
status.blobstore = dir_size(blobstore_path).ok();
@ -218,8 +218,7 @@ impl SbotConfig {
let config_string = toml::to_string(&config)?;
// determine path of user's home directory
let mut config_path = dirs::home_dir().ok_or(PeachError::HomeDir)?;
config_path.push(".ssb-go/config.toml");
let config_path = format!("{}/config.toml", get_config_value("GO_SBOT_DATADIR")?);
// open config file for writing
let mut file = File::create(config_path)?;

View File

@ -6,7 +6,7 @@ use std::num::ParseIntError;
use io::Error as IoError;
use probes::ProbeError;
use regex::Error as RegexError;
use wpactrl::WpaError;
use wpactrl::Error as WpaError;
/// Custom error type encapsulating all possible errors when querying
/// network interfaces and modifying their state.

View File

@ -22,6 +22,7 @@ use std::{
};
use probes::network;
use wpactrl::Client as WpaClient;
#[cfg(feature = "miniserde_support")]
use miniserde::{Deserialize, Serialize};
@ -121,7 +122,7 @@ pub struct Traffic {
/// In the event of an error, a `NetworkError` is returned in the `Result`.
pub fn available_networks(iface: &str) -> Result<Option<Vec<Scan>>, NetworkError> {
let wpa_path: String = format!("/var/run/wpa_supplicant/{}", iface);
let mut wpa = wpactrl::WpaCtrl::builder().ctrl_path(wpa_path).open()?;
let mut wpa = WpaClient::builder().ctrl_path(wpa_path).open()?;
wpa.request("SCAN")?;
let networks = wpa.request("SCAN_RESULTS")?;
let mut scan = Vec::new();
@ -173,7 +174,7 @@ pub fn available_networks(iface: &str) -> Result<Option<Vec<Scan>>, NetworkError
/// event of an error, a `NetworkError` is returned in the `Result`.
pub fn id(iface: &str, ssid: &str) -> Result<Option<String>, NetworkError> {
let wpa_path: String = format!("/var/run/wpa_supplicant/{}", iface);
let mut wpa = wpactrl::WpaCtrl::builder().ctrl_path(wpa_path).open()?;
let mut wpa = WpaClient::builder().ctrl_path(wpa_path).open()?;
let networks = wpa.request("LIST_NETWORKS")?;
let mut id = Vec::new();
for network in networks.lines() {
@ -232,7 +233,7 @@ pub fn ip(iface: &str) -> Result<Option<String>, NetworkError> {
/// `Result`.
pub fn rssi(iface: &str) -> Result<Option<String>, NetworkError> {
let wpa_path: String = format!("/var/run/wpa_supplicant/{}", iface);
let mut wpa = wpactrl::WpaCtrl::builder().ctrl_path(wpa_path).open()?;
let mut wpa = WpaClient::builder().ctrl_path(wpa_path).open()?;
let status = wpa.request("SIGNAL_POLL")?;
let rssi = utils::regex_finder(r"RSSI=(.*)\n", &status)?;
@ -259,7 +260,7 @@ pub fn rssi(iface: &str) -> Result<Option<String>, NetworkError> {
/// the `Result`.
pub fn rssi_percent(iface: &str) -> Result<Option<String>, NetworkError> {
let wpa_path: String = format!("/var/run/wpa_supplicant/{}", iface);
let mut wpa = wpactrl::WpaCtrl::builder().ctrl_path(wpa_path).open()?;
let mut wpa = WpaClient::builder().ctrl_path(wpa_path).open()?;
let status = wpa.request("SIGNAL_POLL")?;
let rssi = utils::regex_finder(r"RSSI=(.*)\n", &status)?;
@ -291,7 +292,7 @@ pub fn rssi_percent(iface: &str) -> Result<Option<String>, NetworkError> {
/// is returned in the `Result`. In the event of an error, a `NetworkError` is
/// returned in the `Result`.
pub fn saved_networks() -> Result<Option<Vec<String>>, NetworkError> {
let mut wpa = wpactrl::WpaCtrl::builder().open()?;
let mut wpa = WpaClient::builder().open()?;
let networks = wpa.request("LIST_NETWORKS")?;
let mut ssids = Vec::new();
for network in networks.lines() {
@ -323,7 +324,7 @@ pub fn saved_networks() -> Result<Option<Vec<String>>, NetworkError> {
/// returned in the `Result`.
pub fn ssid(iface: &str) -> Result<Option<String>, NetworkError> {
let wpa_path: String = format!("/var/run/wpa_supplicant/{}", iface);
let mut wpa = wpactrl::WpaCtrl::builder().ctrl_path(wpa_path).open()?;
let mut wpa = WpaClient::builder().ctrl_path(wpa_path).open()?;
let status = wpa.request("STATUS")?;
// pass the regex pattern and status output to the regex finder
@ -379,7 +380,7 @@ pub fn state(iface: &str) -> Result<Option<String>, NetworkError> {
/// a `NetworkError` is returned in the `Result`.
pub fn status(iface: &str) -> Result<Option<Status>, NetworkError> {
let wpa_path: String = format!("/var/run/wpa_supplicant/{}", iface);
let mut wpa = wpactrl::WpaCtrl::builder().ctrl_path(wpa_path).open()?;
let mut wpa = WpaClient::builder().ctrl_path(wpa_path).open()?;
let wpa_status = wpa.request("STATUS")?;
// pass the regex pattern and status output to the regex finder
@ -579,7 +580,7 @@ pub fn check_iface(wlan_iface: &str, ap_iface: &str) -> Result<(), NetworkError>
/// is returned in the `Result`.
pub fn connect(id: &str, iface: &str) -> Result<(), NetworkError> {
let wpa_path: String = format!("/var/run/wpa_supplicant/{}", iface);
let mut wpa = wpactrl::WpaCtrl::builder().ctrl_path(wpa_path).open()?;
let mut wpa = WpaClient::builder().ctrl_path(wpa_path).open()?;
let select = format!("SELECT {}", id);
wpa.request(&select)?;
Ok(())
@ -598,7 +599,7 @@ pub fn connect(id: &str, iface: &str) -> Result<(), NetworkError> {
/// returned in the `Result`.
pub fn delete(id: &str, iface: &str) -> Result<(), NetworkError> {
let wpa_path: String = format!("/var/run/wpa_supplicant/{}", iface);
let mut wpa = wpactrl::WpaCtrl::builder().ctrl_path(wpa_path).open()?;
let mut wpa = WpaClient::builder().ctrl_path(wpa_path).open()?;
let remove = format!("REMOVE_NETWORK {}", id);
wpa.request(&remove)?;
Ok(())
@ -617,7 +618,7 @@ pub fn delete(id: &str, iface: &str) -> Result<(), NetworkError> {
/// `Result`.
pub fn disable(id: &str, iface: &str) -> Result<(), NetworkError> {
let wpa_path: String = format!("/var/run/wpa_supplicant/{}", iface);
let mut wpa = wpactrl::WpaCtrl::builder().ctrl_path(wpa_path).open()?;
let mut wpa = WpaClient::builder().ctrl_path(wpa_path).open()?;
let disable = format!("DISABLE_NETWORK {}", id);
wpa.request(&disable)?;
Ok(())
@ -634,7 +635,7 @@ pub fn disable(id: &str, iface: &str) -> Result<(), NetworkError> {
/// error, a `NetworkError` is returned in the `Result`.
pub fn disconnect(iface: &str) -> Result<(), NetworkError> {
let wpa_path: String = format!("/var/run/wpa_supplicant/{}", iface);
let mut wpa = wpactrl::WpaCtrl::builder().ctrl_path(wpa_path).open()?;
let mut wpa = WpaClient::builder().ctrl_path(wpa_path).open()?;
let disconnect = "DISCONNECT".to_string();
wpa.request(&disconnect)?;
Ok(())
@ -685,7 +686,7 @@ pub fn forget(iface: &str, ssid: &str) -> Result<(), NetworkError> {
/// event of an error, a `NetworkError` is returned in the `Result`.
pub fn modify(id: &str, iface: &str, pass: &str) -> Result<(), NetworkError> {
let wpa_path: String = format!("/var/run/wpa_supplicant/{}", iface);
let mut wpa = wpactrl::WpaCtrl::builder().ctrl_path(wpa_path).open()?;
let mut wpa = WpaClient::builder().ctrl_path(wpa_path).open()?;
let new_pass = format!("NEW_PASSWORD {} {}", id, pass);
wpa.request(&new_pass)?;
Ok(())
@ -702,7 +703,7 @@ pub fn modify(id: &str, iface: &str, pass: &str) -> Result<(), NetworkError> {
/// error, a `NetworkError` is returned in the `Result`.
pub fn reassociate(iface: &str) -> Result<(), NetworkError> {
let wpa_path: String = format!("/var/run/wpa_supplicant/{}", iface);
let mut wpa = wpactrl::WpaCtrl::builder().ctrl_path(wpa_path).open()?;
let mut wpa = WpaClient::builder().ctrl_path(wpa_path).open()?;
wpa.request("REASSOCIATE")?;
Ok(())
}
@ -714,7 +715,7 @@ pub fn reassociate(iface: &str) -> Result<(), NetworkError> {
/// `Result` type is returned. In the event of an error, a `NetworkError` is
/// returned in the `Result`.
pub fn reconfigure() -> Result<(), NetworkError> {
let mut wpa = wpactrl::WpaCtrl::builder().open()?;
let mut wpa = WpaClient::builder().open()?;
wpa.request("RECONFIGURE")?;
Ok(())
}
@ -730,7 +731,7 @@ pub fn reconfigure() -> Result<(), NetworkError> {
/// event of an error, a `NetworkError` is returned in the `Result`.
pub fn reconnect(iface: &str) -> Result<(), NetworkError> {
let wpa_path: String = format!("/var/run/wpa_supplicant/{}", iface);
let mut wpa = wpactrl::WpaCtrl::builder().ctrl_path(wpa_path).open()?;
let mut wpa = WpaClient::builder().ctrl_path(wpa_path).open()?;
wpa.request("DISCONNECT")?;
wpa.request("RECONNECT")?;
Ok(())
@ -742,7 +743,7 @@ pub fn reconnect(iface: &str) -> Result<(), NetworkError> {
/// `wpa_supplicant.conf` file, an `Ok` `Result` type is returned. In the
/// event of an error, a `NetworkError` is returned in the `Result`.
pub fn save() -> Result<(), NetworkError> {
let mut wpa = wpactrl::WpaCtrl::builder().open()?;
let mut wpa = WpaClient::builder().open()?;
wpa.request("SAVE_CONFIG")?;
Ok(())
}

View File

@ -1,6 +1,6 @@
[package]
name = "peach-web"
version = "0.6.0"
version = "0.6.13"
authors = ["Andrew Reid <gnomad@cryptolab.net>"]
edition = "2018"
description = "peach-web is a web application which provides a web interface for monitoring and interacting with the PeachCloud device. This allows administration of the single-board computer (ie. Raspberry Pi) running PeachCloud, as well as the ssb-server and related plugins."

View File

@ -1,14 +1,10 @@
[Unit]
Description=Rocket web application for serving the PeachCloud web interface.
Description=Rouille web application for serving the PeachCloud web interface.
[Service]
User=peach-web
Group=www-data
User=peach
Group=peach
WorkingDirectory=/usr/share/peach-web
Environment="ROCKET_ENV=prod"
Environment="ROCKET_ADDRESS=127.0.0.1"
Environment="ROCKET_PORT=3000"
Environment="ROCKET_LOG=critical"
Environment="RUST_LOG=info"
ExecStart=/usr/bin/peach-web
Restart=always

View File

@ -2,8 +2,7 @@
set -e
# create user which peach-web runs as
adduser --quiet --system peach-web
usermod -g peach peach-web
id -u peach &>/dev/null || adduser --quiet peach
# create nginx config
cat <<EOF > /etc/nginx/sites-enabled/default
@ -15,16 +14,25 @@ server {
rewrite ^/(.*)/$ /$1 permanent;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_pass http://127.0.0.1:8000;
}
}
EOF
cat <<EOF > /etc/sudoers.d/peach-web
# allow peach-web to run commands as peach-go-sbot without a password
peach-web ALL=(peach-go-sbot) NOPASSWD:ALL
# update sudoers to allow peach-web to stop and restart go-sbot.service
mkdir -p /etc/sudoers.d/
SYSTEMCTL=/bin/systemctl
START="${SYSTEMCTL} start go-sbot.service"
RESTART="${SYSTEMCTL} restart go-sbot.service"
STOP="${SYSTEMCTL} stop go-sbot.service"
ENABLE="${SYSTEMCTL} enable go-sbot.service"
DISABLE="${SYSTEMCTL} disable go-sbot.service"
cat <<EOF > /etc/sudoers.d/peach-web
peach ALL=(ALL) NOPASSWD: $START, $STOP, $RESTART, $ENABLE, $DISABLE
EOF
chmod 0440 /etc/sudoers.d/peach-web
# cargo deb automatically replaces this token below, see https://github.com/mmstick/cargo-deb/blob/master/systemd.md
#DEBHELPER#

View File

@ -1,53 +1,31 @@
//! Define the configuration parameters for the web application.
//!
//! Sets default values and updates them if the corresponding environment
//! variables have been set.
//! These configs are loaded using peach-lib::config_manager which checks config keys from
//! three sources:
//! 1. from environmental variables
//! 2. from a configuration file
//! 3. from default values
use std::env;
use crate::error::PeachWebError;
use peach_lib::config_manager::get_config_value;
// environment variable keys to check for
const ENV_VARS: [&str; 4] = ["STANDALONE_MODE", "DISABLE_AUTH", "ADDR", "PORT"];
pub struct Config {
pub struct ServerConfig {
pub standalone_mode: bool,
pub disable_auth: bool,
pub addr: String,
pub port: String,
}
impl Default for Config {
fn default() -> Self {
Self {
standalone_mode: true,
disable_auth: false,
addr: "127.0.0.1".to_string(),
port: "8000".to_string(),
}
}
}
impl Config {
pub fn new() -> Config {
impl ServerConfig {
pub fn new() -> Result<ServerConfig, PeachWebError> {
// define default config values
let mut config = Config::default();
let config = ServerConfig {
standalone_mode: get_config_value("STANDALONE_MODE")?.as_str() == "true",
disable_auth: get_config_value("DISABLE_AUTH")?.as_str() == "true",
addr: get_config_value("ADDR")?,
port: get_config_value("PORT")?,
};
// check for the environment variables in our config
for key in ENV_VARS {
// if a variable (key) has been set, check the value
if let Ok(val) = env::var(key) {
// if the value is of the correct type, update the config value
match key {
"STANDALONE_MODE" if val.as_str() == "true" => config.standalone_mode = true,
"STANDALONE_MODE" if val.as_str() == "false" => config.standalone_mode = false,
"DISABLE_AUTH" if val.as_str() == "true" => config.disable_auth = true,
"DISABLE_AUTH" if val.as_str() == "false" => config.disable_auth = false,
"ADDR" => config.addr = val,
"PORT" => config.port = val,
_ => (),
}
}
}
config
Ok(config)
}
}

View File

@ -26,16 +26,16 @@ use std::{
};
use lazy_static::lazy_static;
use log::{debug, info};
use peach_lib::{config_manager, config_manager::YAML_PATH as PEACH_CONFIG};
use log::info;
// crate-local dependencies
use config::Config;
use config::ServerConfig;
use utils::theme::Theme;
// load the application configuration and create the theme switcher
lazy_static! {
static ref CONFIG: Config = Config::new();
static ref SERVER_CONFIG: ServerConfig =
ServerConfig::new().expect("Failed to load rouille configuration values on server startup");
static ref THEME: RwLock<Theme> = RwLock::new(Theme::Light);
}
@ -50,21 +50,9 @@ fn main() {
// initialize logger
env_logger::init();
// check if /var/lib/peachcloud/config.yml exists
if !std::path::Path::new(PEACH_CONFIG).exists() {
debug!("PeachCloud configuration file not found; loading default values");
// since we're in the intialisation phase, panic if the loading fails
let config =
config_manager::load_peach_config().expect("peachcloud configuration loading failed");
debug!("Saving default PeachCloud configuration values to file");
// this ensures a config file is created if it does not already exist
config_manager::save_peach_config(config).expect("peachcloud configuration saving failed");
}
// set ip address / hostname and port for the webserver
// defaults to "127.0.0.1:8000"
let addr_and_port = format!("{}:{}", CONFIG.addr, CONFIG.port);
let addr_and_port = format!("{}:{}", SERVER_CONFIG.addr, SERVER_CONFIG.port);
// store the session data for each session and a hashmap that associates
// each session id with the data
@ -80,7 +68,7 @@ fn main() {
// with a name of "SID" and a duration of one hour (3600 seconds)
rouille::session::session(request, "SID", 3600, |session| {
// if the "DISABLE_AUTH" env var is true, authenticate the session
let mut session_data = if CONFIG.disable_auth {
let mut session_data = if SERVER_CONFIG.disable_auth {
Some(SessionData {
_login: "success".to_string(),
})

View File

@ -29,7 +29,7 @@ pub fn build_template() -> PreEscaped<String> {
}
}
" to start the sbot. If the server starts successfully, you will see a green smiley face on the home page. If the face is orange and sleeping, that means the sbot is still inactive (ie. the process is not running). If the face is red and dead, that means the sbot failed to start - indicated an error. For now, the best way to gain insight into the problem is to check the systemd log. Open a terminal and enter: "
code { "systemctl --user status go-sbot.service" }
code { "systemctl status go-sbot.service" }
". The log output may give some clues about the source of the error."
}
}

View File

@ -13,8 +13,8 @@ pub fn build_template(request: &Request) -> PreEscaped<String> {
let (mut flash_name, mut flash_msg) = request.retrieve_flash();
// attempt to load peachcloud config file
let ssb_admins = match config_manager::load_peach_config() {
Ok(config) => Some(config.ssb_admin_ids),
let ssb_admins = match config_manager::get_ssb_admin_ids() {
Ok(ssb_admin_ids) => Some(ssb_admin_ids),
// note: this will overwrite any received flash cookie values
// TODO: find a way to include the `err` in the flash_msg
// currently produces an error because we end up with Some(String)

View File

@ -1,6 +1,6 @@
use maud::{html, PreEscaped};
use crate::{templates, utils::theme, CONFIG};
use crate::{templates, utils::theme, SERVER_CONFIG};
// ROUTE: /settings
@ -12,7 +12,7 @@ pub fn build_template() -> PreEscaped<String> {
(PreEscaped("<!-- BUTTONS -->"))
div id="settingsButtons" {
// render the network settings button if we're not in standalone mode
@if !CONFIG.standalone_mode {
@if !SERVER_CONFIG.standalone_mode {
a id="network" class="button button-primary center" href="/settings/network" title="Network Settings" { "Network" }
}
a id="scuttlebutt" class="button button-primary center" href="/settings/scuttlebutt" title="Scuttlebutt Settings" { "Scuttlebutt" }

View File

@ -53,7 +53,6 @@ fn run_on_startup_element(boot_state: &Option<String>) -> Markup {
fn database_element(state: &str) -> Markup {
// retrieve the sequence number of the latest message in the sbot database
let sequence_num = sbot::latest_sequence_number();
match (state, sequence_num) {
// if the state is "active" and latest_sequence_number() was successful
("active", Ok(number)) => {
@ -62,7 +61,9 @@ fn database_element(state: &str) -> Markup {
label class="label-small font-gray" { "MESSAGES IN LOCAL DATABASE" }
}
}
(_, _) => html! { label class="label-small font-gray" { "DATABASE UNAVAILABLE" } },
(_, _) => {
html! { label class="label-small font-gray" { "DATABASE UNAVAILABLE" } }
}
}
}

View File

@ -12,7 +12,9 @@ use std::{
use async_std::task;
use dirs;
use futures::stream::TryStreamExt;
use golgi::{api::friends::RelationshipQuery, blobs, messages::SsbMessageValue, Sbot};
use golgi::{
api::friends::RelationshipQuery, blobs, messages::SsbMessageValue, sbot::Keystore, Sbot,
};
use log::debug;
use peach_lib::sbot::SbotConfig;
use rouille::input::post::BufferedFile;
@ -24,8 +26,8 @@ use crate::{error::PeachWebError, utils::sbot};
/// Executes a systemctl command for the go-sbot.service process.
pub fn systemctl_sbot_cmd(cmd: &str) -> io::Result<Output> {
Command::new("systemctl")
.arg("--user")
Command::new("sudo")
.arg("systemctl")
.arg(cmd)
.arg("go-sbot.service")
.output()
@ -70,9 +72,9 @@ pub async fn init_sbot_with_config(
// TODO: panics if we pass `Some(conf.shscap)` as second arg
Some(conf) => {
let ip_port = conf.lis.clone();
Sbot::init(Some(ip_port), None).await?
Sbot::init(Keystore::GoSbot, Some(ip_port), None).await?
}
None => Sbot::init(None, None).await?,
None => Sbot::init(Keystore::GoSbot, None, None).await?,
};
Ok(sbot_client)