Co se v jednotlivých verzích změnilo: CHANGELOG.md.
Async Rust klient (a CLI cdctl) pro IBM Sterling Connect:Direct Web Services (CDWS) REST API.
Funkce: sign-on/sign-out se správou JWT + XSRF tokenu, automatický re-login při 401,
submit procesu (text nebo .cdp na serveru), generátor COPY procesů, select/delete process,
select statistics, čekání na dokončení procesu, verze CDWS a obecné call() pro ostatní
endpointy (netmap, user proxy, Secure+, …). TLS přes rustls, vlastní CA i certifikátová autentizace.
Hotová statická binárka cdctl (musl, bez závislostí na glibc/OpenSSL – běží na RHEL, SLES, Ubuntu, Alpine…)
je u každého release:
curl -LO https://github.com/qwerin/disconnnect-indirect/releases/latest/download/cdctl-x86_64-unknown-linux-musl.tar.gz
curl -LO https://github.com/qwerin/disconnnect-indirect/releases/latest/download/cdctl-x86_64-unknown-linux-musl.tar.gz.sha256
sha256sum -c cdctl-x86_64-unknown-linux-musl.tar.gz.sha256
tar xzf cdctl-x86_64-unknown-linux-musl.tar.gz && ./cdctl --versionBuild z každého commitu v main je také v záložce Actions jako artefakt cdctl-linux-amd64.
Vydání nové verze: v CHANGELOG.md přejmenovat sekci [Unreleased] na novou verzi s datem
(a založit novou prázdnou [Unreleased]), zvednout version v Cargo.toml, commit, pak git tag v0.4.0 && git push --tags.
Workflow ověří, že tag odpovídá verzi a changelog ji obsahuje, sestaví binárku a založí GitHub Release
s textem z changelogu.
[dependencies]
connect-direct = { git = "https://github.com/qwerin/disconnnect-indirect", default-features = false }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }use std::time::Duration;
use connect_direct::*;
#[tokio::main]
async fn main() -> Result<()> {
let mut cfg = Config::new("https://cdws:9443", "cdnode1", "cduser", "secret");
cfg.extra_root_certs_pem.push(std::fs::read("cdws-ca.pem").unwrap());
let cd = CdClient::new(cfg)?;
let text = CopyProcess::send("PAYOUT", "PARTNER01", "/data/out/pay.csv", "/in/pay.csv")
.disp(Disp::Rpl)
.render()?;
let resp = cd.submit_process(&SubmitProcess::new(text)).await?;
if let Some(pnum) = extract_process_number(&resp) {
let end = cd.wait_for_process_end(pnum, Duration::from_secs(5), Duration::from_secs(600)).await?;
println!("ccode = {:?}", completion_code(&end));
}
// jakýkoli jiný endpoint relativně k /cdwebconsole/svc/
let v = cd.call(Method::GET, "version", &Query::new(), None).await?;
println!("{v}");
cd.sign_out().await
}cargo install --git https://github.com/qwerin/disconnnect-indirect
export CDWS_URL=https://cdws:9443 CD_NODE_HOST=cdnode1 CD_USER=cduser CD_PASSWORD=secret
# volitelně: CD_NODE_PORT=1363 CD_PROTOCOL=TLS1.2 CDWS_CA_CERT=ca.pem CDWS_INSECURE=true
cdctl version
cdctl send --snode PARTNER01 --from /out/a.csv --to /in/a.csv --name PAYOUT --wait
cdctl receive --snode PARTNER01 --from /out/b.csv --to /in/b.csv --dry-run # jen vypíše proces
cdctl submit --file ./muj_proces.cdp --wait
cdctl submit --server-file /opt/cdunix/process/send.cdp
cdctl stats --process-number 126
cdctl processes
cdctl delete --process-number 126 --force
cdctl raw GET netmapnodes -q name=PARTNER01Exit kód: 0 OK, 2 proces skončil s nenulovým completion code, 1 chyba.
cdctl čte konfiguraci z /etc/cdctl/config.toml (jiný soubor: --config / CDCTL_CONFIG).
Priorita: přepínač > proměnná prostředí > konfigurační soubor > výchozí hodnota.
Kompletní ukázka s komentáři je v config.example.toml.
[connection]
url = "https://cdws.example.com:9443"
node_host = "cdnode1.example.com"
user = "cduser"
# password = "..." # raději CD_PASSWORD; se heslem v souboru: chmod 600
[log]
dir = "/var/log/cdctl" # výchozí
level = "info" # nebo "connect_direct=debug,info", "trace"
format = "text" # text | json
rotation = "daily" # minutely | hourly | daily | never
max_files = 14Každé spuštění se loguje do /var/log/cdctl/cdctl.<datum>.log: start (verze, příkaz, argumenty, PID, uživatel,
použitá konfigurace), přihlášení k uzlu, každé HTTP volání se stavem a dobou trvání (debug), opakované
přihlášení po 401, požadované přenosy (směr, SNODE, zdroj, cíl), číslo odeslaného procesu, výsledek
a completion code, chyby včetně celé příčiny (např. tcp connect error: Connection refused) a konec
s exit kódem. Řádky jednoho spuštění spojuje run=<id>; na úrovni trace jdou do logu i těla
požadavků a odpovědí.
Hesla, Basic autentizace, JWT a XSRF tokeny se do logu nikdy nezapisují (ověřeno testem na úrovni trace),
heslo zadané přepínačem --password se v logu maskuje.
Když /var/log/cdctl není zapisovatelný (běh bez roota), použije se ~/.local/state/cdctl/log
a vypíše se varování. Explicitně nastavený adresář se nenahrazuje – když do něj nejde zapisovat, cdctl skončí chybou.
Nasazení na server:
sudo install -d -o cduser -g cduser -m 750 /var/log/cdctl
sudo install -d -m 755 /etc/cdctl
sudo install -o cduser -g cduser -m 600 config.example.toml /etc/cdctl/config.toml
cdctl config # výsledná konfigurace a cesta k logu (heslo se nevypisuje)Knihovna sama loguje přes tracing (target connect_direct);
v aplikaci stačí nastavit libovolný tracing subscriber.
Projekt má vlastního záškodníka – scripts/saboteur/run.sh spouští 37 útoků na cdctl
(škodlivé argumenty, konfigurace, soubory jako /dev/zero a zlý CDWS server, který přesměrovává, posílá obří
odpovědi, padělá řádky logu nebo escape sekvence terminálu). Běží v CI při každém pushi, spolu s property testy
(proptest, 20 000 náhodných vstupů na test).
Co klient a CLI hlídají:
- žádná přesměrování – session token (
X-XSRF-TOKEN) nikdy neodejde na jiný server - cesty API jen pod
/cdwebconsole/svc/(../,%2e%2e,?,#se odmítnou) - limity velikosti: odpověď serveru (výchozí 64 MiB,
max_response_mb), tokeny 16 KiB, konfigurační soubor,.cdpsoubor a certifikát 1 MiB - generátor procesů nedovolí změnit strukturu procesu: odmítá řídicí znaky a bidi znaky v cestách, SYSOPTS musí být jeden token nebo korektně uzavřený řetězec
- logy a terminál: řídicí znaky z dat (nové řádky, ESC, bidi) se escapují – nejde padělat řádky logu ani poslat escape sekvence do terminálu; hesla a tokeny se do logu nezapisují
- konfigurace: neznámé klíče, nulový timeout/port a název souboru logu s cestou se odmítnou
- zavřená roura (
cdctl stats | head) není pád
Na ověření proti reálnému C:D zbývá: zda C:D rozbaluje symbolické proměnné (&VAR) i uvnitř uvozovek
v cestě souboru.
Pravidla jsou v deny.toml a hlídá je cargo deny check v CI (při každém pushi a jednou týdně):
žádné známé zranitelnosti (RustSec) ani neudržované či stažené (yanked) crates, jen permisivní licence,
jen crates.io (žádné git závislosti), žádný OpenSSL / native-tls – TLS je čistě rustls
(kryptografie aws-lc-rs, kořenové certifikáty ze systému přes rustls-platform-verifier).
GitHub Actions jsou připnuté na konkrétní commit (SHA), aby změna tagu u cizí akce nemohla podvrhnout build ani release.
Aktualizace crates i akcí navrhuje Dependabot (.github/dependabot.yml).
Release obsahuje THIRD-PARTY-LICENSES.txt s licencemi všech crates zkompilovaných do binárky
(generuje scripts/third_party_licenses.py).
Pozor při nasazení: od verze 0.3.1 se serverový certifikát CDWS ověřuje proti systémovému úložišti
certifikátů (dřív proti certifikátům zabudovaným v binárce). Firemní CA nainstalovaná v systému tak funguje
automaticky; na minimálním systému bez balíčku ca-certificates je potřeba ca_cert / --ca-cert.
- Sign-on:
POST /cdwebconsole/svc/signonsAuthorization: Basic …a fixnímX-XSRF-TOKEN: Y2hlY2tpdA==; tělo{"ipAddress", "port", "protocol"}. Z odpovědi se bere hlavičkaAuthorization(JWT) a_csrf(XSRF token pro další volání). - Pokud vaše verze CDWS čeká v sign-on jiný název pole pro port, upravte
SignonBodyvsrc/models.rs. - Tvar JSON odpovědí se mezi verzemi CDWS liší, proto API vrací
serde_json::Value.extract_process_numberawait_for_process_endhledají pole heuristicky (processNumber, záznamPRED,ccode) – ověřte protihttps://<cdws>/cdws-doc/vaší instalace. Spolehlivější alternativa k pollingu:SubmitProcess::new(..).max_delay("unlimited"). /versionendpoint existuje od CDWS 6.3.0.9.CDWS_INSECURE/accept_invalid_certsvypíná ověřování TLS – používejte jen v testu.
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features # unit + integrační + CLI + doc testy
cargo test --no-default-features # jen knihovna, bez CLIProperty testy (prop_*, proptest) generují náhodné vstupy – v CI 20 000 na test.
Testy nepotřebují reálný Connect:Direct – běží proti mock CDWS serveru (wiremock):
src/*– unit testy generátoru procesů, serializace a heuristik nad odpověďmitests/client.rs– knihovna: sign-on (Basic i certifikát), XSRF z hlavičky i cookie, re-login při 401, sdílená session při souběžných voláních, submit/select/delete, statistiky, polling a timeout, chybové stavytests/cli.rs– binárkacdctl: dry-run, validace vstupů, exit kódy, všechny subpříkazy end-to-end, konfigurační soubor a priority, logování (obsah, JSON formát, úrovně, žádné úniky hesel/tokenů, chybné cesty)
CI (GitHub Actions) spouští rustfmt, clippy, testy na Linuxu/macOS/Windows (stable + beta), kontrolu MSRV (Rust 1.88), build dokumentace, měření pokrytí (cargo-llvm-cov) a bezpečnostní audit závislostí (cargo-deny). Navíc jednou týdně.
Pull requesty a issues jsou vítány – hlavně zkušenosti s reálnými odpověďmi různých verzí CDWS,
ať jde heuristiku ve models.rs nahradit pevně typovanými strukturami.
Neoficiální projekt, není spojen s IBM. IBM, Sterling a Connect:Direct jsou ochranné známky společnosti International Business Machines Corporation.