From 51cd3b1c52246e4c6a0576a20ee9ec47e46863f5 Mon Sep 17 00:00:00 2001 From: packgron Date: Wed, 18 Dec 2024 18:25:27 +0200 Subject: [PATCH] teliod cgi: Prepare teliod for cgi Prepared teliod structure to make cgi development easier. --- .unreleased/LLT-5865_cgi_skeleton | 0 Cargo.lock | 12 ++- Justfile | 6 ++ ci/build_libtelio.py | 1 + clis/teliod/Cargo.toml | 5 + clis/teliod/src/cgi/mod.rs | 13 +++ clis/teliod/src/configure_interface.rs | 5 +- clis/teliod/src/main.rs | 10 ++ contrib/http_root/.gitignore | 1 + contrib/http_root/cgi-bin/teliod.cgi | 17 ++++ qnap/package_routines | 8 +- qnap/shared/NordSecMeshnet.sh | 3 +- qnap/shared/teliod.cfg | 1 - qnap/shared/teliod.cgi | 135 ++----------------------- 14 files changed, 79 insertions(+), 138 deletions(-) create mode 100644 .unreleased/LLT-5865_cgi_skeleton create mode 100644 clis/teliod/src/cgi/mod.rs create mode 100644 contrib/http_root/.gitignore create mode 100755 contrib/http_root/cgi-bin/teliod.cgi diff --git a/.unreleased/LLT-5865_cgi_skeleton b/.unreleased/LLT-5865_cgi_skeleton new file mode 100644 index 000000000..e69de29bb diff --git a/Cargo.lock b/Cargo.lock index f3b4fd61a..f7e673f7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3600,6 +3600,15 @@ dependencies = [ "tokio-rustls 0.25.0", ] +[[package]] +name = "rust-cgi" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f429f64b3929e733331d856e47ac00afb9dd8cda39f0a740c5622c886754acfa" +dependencies = [ + "http 1.1.0", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -5097,6 +5106,7 @@ dependencies = [ "regex", "reqwest 0.12.9", "rumqttc", + "rust-cgi", "rustls-native-certs 0.8.0", "serde", "serde_json", @@ -5926,7 +5936,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Justfile b/Justfile index d4cefcd69..256cb6877 100644 --- a/Justfile +++ b/Justfile @@ -67,6 +67,12 @@ black fix="false": pylint: docker run --rm -t -v$(pwd):/code 'ubuntu:22.04' sh -c "apt-get update && apt-get -y install python3-pip && cd code && pip3 install --no-deps -r requirements.txt && pipenv install --system && cd nat-lab && pipenv install --system && pylint -f colorized . --ignore telio_bindings.py" +# Start a dev web cgi server, for local teliod cgi development +web: + @echo "Go to http://127.0.0.1:8080/cgi-bin/teliod.cgi" + python3 -m http.server --cgi -d $(pwd)/contrib/http_root/ -b 127.0.0.1 8080 + + _udeps-install: _nightly-install cargo +{{ nightly }} install cargo-udeps@0.1.47 --locked diff --git a/ci/build_libtelio.py b/ci/build_libtelio.py index b0feb98b6..38958ad32 100755 --- a/ci/build_libtelio.py +++ b/ci/build_libtelio.py @@ -296,6 +296,7 @@ def post_qnap_build_wrap_binary_on_qpkg(config, args): "packages": { "teliod": {"teliod": "teliod"}, }, + "build_args": ("--features", "qnap"), }, "macos": { "packages": { diff --git a/clis/teliod/Cargo.toml b/clis/teliod/Cargo.toml index 7bf49854f..270bb147b 100644 --- a/clis/teliod/Cargo.toml +++ b/clis/teliod/Cargo.toml @@ -34,6 +34,11 @@ anyhow.workspace = true smart-default = "0.7.1" base64 = "0.22.1" dirs = "4.0.0" +rust-cgi = { version = "0.7.1", optional = true } [dev-dependencies] rand = "0.8.5" + +[features] +cgi = ["rust-cgi"] +qnap = ["cgi"] diff --git a/clis/teliod/src/cgi/mod.rs b/clis/teliod/src/cgi/mod.rs new file mode 100644 index 000000000..0d4d8ad12 --- /dev/null +++ b/clis/teliod/src/cgi/mod.rs @@ -0,0 +1,13 @@ +use rust_cgi::{http::StatusCode, text_response, Request, Response}; + +const CHAIN: &[fn(&Request) -> Option] = &[]; + +pub(crate) fn handle_request(request: Request) -> Response { + for handler in CHAIN.iter() { + if let Some(response) = handler(&request) { + return response; + } + } + + text_response(StatusCode::BAD_REQUEST, "Invalid request.") +} diff --git a/clis/teliod/src/configure_interface.rs b/clis/teliod/src/configure_interface.rs index ed4181adc..c15b974c9 100644 --- a/clis/teliod/src/configure_interface.rs +++ b/clis/teliod/src/configure_interface.rs @@ -1,5 +1,5 @@ use crate::TeliodError; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use std::net::IpAddr; use std::process::Command; use tracing::{error, info}; @@ -24,9 +24,10 @@ fn execute(command: &mut Command) -> Result<(), TeliodError> { } } -#[derive(Debug, Deserialize, PartialEq, Eq)] +#[derive(Default, Debug, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "lowercase")] pub enum InterfaceConfigurationProvider { + #[default] Manual, Ifconfig, Iproute, diff --git a/clis/teliod/src/main.rs b/clis/teliod/src/main.rs index ca3827270..62398adcd 100644 --- a/clis/teliod/src/main.rs +++ b/clis/teliod/src/main.rs @@ -12,6 +12,8 @@ use tokio::{ }; use tracing::{debug, error}; +#[cfg(feature = "cgi")] +mod cgi; mod command_listener; mod comms; mod config; @@ -44,6 +46,9 @@ enum Cmd { Daemon { config_path: String }, #[clap(flatten)] Client(ClientCmd), + #[cfg(feature = "cgi")] + #[clap(about = "Receive and parse http requests")] + Cgi, } #[derive(Debug, ThisError)] @@ -134,5 +139,10 @@ async fn main() -> Result<(), TeliodError> { Err(TeliodError::DaemonIsNotRunning) } } + #[cfg(feature = "cgi")] + Cmd::Cgi => { + rust_cgi::handle(cgi::handle_request); + Ok(()) + } } } diff --git a/contrib/http_root/.gitignore b/contrib/http_root/.gitignore new file mode 100644 index 000000000..a98849924 --- /dev/null +++ b/contrib/http_root/.gitignore @@ -0,0 +1 @@ +**/*.log diff --git a/contrib/http_root/cgi-bin/teliod.cgi b/contrib/http_root/cgi-bin/teliod.cgi new file mode 100755 index 000000000..ab7f09ad4 --- /dev/null +++ b/contrib/http_root/cgi-bin/teliod.cgi @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -eu -o pipefail + +fail() { + cat $DEV_DIR/cargo.log + exit 0 +} + +DEV_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +BIN_DIR="$(realpath $DEV_DIR/../../../target/debug/)" + +cargo build -F cgi -p teliod >"$DEV_DIR/cargo.log" 2>&1 || fail + +export PATH=$BIN_DIR:$PATH + +exec teliod cgi diff --git a/qnap/package_routines b/qnap/package_routines index 7ace22297..04dd53e1b 100644 --- a/qnap/package_routines +++ b/qnap/package_routines @@ -110,10 +110,10 @@ #}" # PKG_POST_REMOVE="{ - rm -rf /tmp/nordsecmeshnet - rm -f /home/Qhttpd/Web/NordSecurityMeshnet - rm -f /home/httpd/cgi-bin/qpkg/teliod.cgi - rm -f /var/log/teliod.log + $CMD_RM -rf /tmp/nordsecuritymeshnet + $CMD_RM -f /home/Qhttpd/Web/NordSecurityMeshnet + $CMD_RM -f /home/httpd/cgi-bin/qpkg/teliod.cgi + $CMD_RM -f /var/log/teliod.log }" # ###################################################################### diff --git a/qnap/shared/NordSecMeshnet.sh b/qnap/shared/NordSecMeshnet.sh index 74a6dc704..90377e8ed 100755 --- a/qnap/shared/NordSecMeshnet.sh +++ b/qnap/shared/NordSecMeshnet.sh @@ -1,8 +1,9 @@ #!/bin/sh +set -eu + CONF=/etc/config/qpkg.conf QPKG_NAME="NordSecurityMeshnet" - QPKG_ROOT=`/sbin/getcfg $QPKG_NAME Install_Path -f ${CONF}` APACHE_ROOT=`/sbin/getcfg SHARE_DEF defWeb -d Qweb -f /etc/config/def_share.info` export QNAP_QPKG=$QPKG_NAME diff --git a/qnap/shared/teliod.cfg b/qnap/shared/teliod.cfg index 7ab7db276..15c3ffe42 100644 --- a/qnap/shared/teliod.cfg +++ b/qnap/shared/teliod.cfg @@ -8,4 +8,3 @@ "config_provider": "manual" } } - diff --git a/qnap/shared/teliod.cgi b/qnap/shared/teliod.cgi index f6a9b4d9c..74abddd09 100755 --- a/qnap/shared/teliod.cgi +++ b/qnap/shared/teliod.cgi @@ -1,131 +1,8 @@ -#!/bin/bash +#!/bin/sh +set -eu -echo "Content-Type: application/json" -echo "" +CONF=/etc/config/qpkg.conf +QPKG_NAME="NordSecurityMeshnet" +QPKG_ROOT=`/sbin/getcfg $QPKG_NAME Install_Path -f ${CONF}` -# Varibles set by the HTTP web server -# -# $REQUEST_METHOD -# $QUERY_STRING -# $CONTENT_LENGTH - -POST_DATA="" - -if [[ "$REQUEST_METHOD" == "POST" || "$REQUEST_METHOD" == "PATCH" ]]; then - read -n $CONTENT_LENGTH POST_DATA -fi - -QPKG_DIR="/share/CACHEDEV1_DATA/.qpkg/NordSecurityMeshnet" -APP_TMP_DIR="/tmp/nordsecuritymeshnet" -APP_CMD="$QPKG_DIR/teliod" -CONFIG_FILE="$QPKG_DIR/teliod.cfg" -LOG_FILE="/var/log/teliod.log" -PID_FILE="$APP_TMP_DIR/teliod.pid" - -is_application_running() { - if [ -f ${PID_FILE} ]; then - PID=$(cat ${PID_FILE}) - if [ -d /proc/${PID}/ ]; then - # application is running - return 0 - fi - fi - # application is NOT running - return 1 -} - -send_response() { - local code=$1 - local message=$2 - echo "{\"code\": $code, \"message\": \"$message\"}" - exit $code -} - -start_daemon() { - if is_application_running; then - send_response 400 "Application is already running." - fi - $APP_CMD daemon $CONFIG_FILE > $LOG_FILE 2>&1 & - if [[ $? -eq 0 ]]; then - echo $! > ${PID_FILE} - send_response 200 "Application started successfully." - else - echo $! > ${PID_FILE} - send_response 500 "Failed to start the application." - fi -} - -stop_daemon() { - if [ -f ${PID_FILE} ]; then - PID=$(cat ${PID_FILE}) - kill ${PID} || true - rm -f ${PID_FILE} - send_response 200 "Application stopped successfully." - else - send_response 400 "Application PID not found." - fi -} - -update_config() { - NEW_CONFIG=$(echo "$POST_DATA" | jq -r '.config') - if [[ -z "$NEW_CONFIG" ]]; then - send_response 400 "No configuration provided." - fi - echo "$NEW_CONFIG" > $CONFIG_FILE - if [[ $? -eq 0 ]]; then - send_response 200 "Configuration updated successfully." - else - send_response 500 "Failed to update configuration." - fi -} - -get_status() { - if is_application_running; then - STATUS=$($APP_CMD get-status) - echo "{\"code\": 200, \"status-report\": $STATUS}" - exit 0 - else - send_response 400 "Application is not running." - fi -} - -get_logs() { - if [[ -f $LOG_FILE ]]; then - LOGS=$(tail -n 100 $LOG_FILE | jq -R -s '.') - echo "{\"code\": 200, \"logs\": $LOGS}" - exit 0 - else - send_response 404 "Log file not found." - fi -} - -case "$REQUEST_METHOD" in - POST) - if [[ "$QUERY_STRING" == "action=start" ]]; then - start_daemon - elif [[ "$QUERY_STRING" == "action=stop" ]]; then - stop_daemon - else - send_response 400 "Invalid action for POST." - fi - ;; - PATCH) - if [[ "$QUERY_STRING" == "action=update-config" ]]; then - update_config - else - send_response 400 "Invalid action for PATCH." - fi - ;; - GET) - if [[ "$QUERY_STRING" == "action=get-status" ]]; then - get_status - elif [[ "$QUERY_STRING" == "action=get-logs" ]]; then - get_logs - else - send_response 400 "Invalid action for GET." - fi - ;; - *) - send_response 405 "Method not allowed." - ;; -esac \ No newline at end of file +exec "${QPKG_ROOT}/teliod" cgi