Phase 2 : tripoli-edge, service Rust interne derrière Caddy
- tripoli-edge (hyper/tokio bas niveau, pas de framework type axum) : /healthz, /metrics (Prometheus, compteurs à la main), reverse-proxy vers tripoli-api avec vrais 502 tant que ce service n'existe pas (Phase 3), logs JSON structurés par requête. - Écoute sur 127.0.0.1 + 172.17.0.1 (passerelle docker0), jamais sur l'interface publique - même pattern que le Nginx de VoltVision déjà sur ce serveur. - Déployé en prod : systemd (utilisateur système dédié, sans privilège), bloc Caddy ajouté pour tripoli.cloud/www.tripoli.cloud, règle ufw ajoutée (Ansible) pour que Caddy puisse joindre le port 9000 - sans quoi le chemin INPUT de l'hôte bloque le trafic inter-bridges même vers 172.17.0.1. - Vérifié bout-en-bout via https://tripoli.cloud, sans régression sur les 6 autres domaines déjà servis par ce Caddy. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
2fa16083c3
commit
8f70c2aae7
12 changed files with 1219 additions and 1 deletions
723
Cargo.lock
generated
Normal file
723
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,723 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic-waker"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.22.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "1.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-channel"
|
||||||
|
version = "0.3.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-core"
|
||||||
|
version = "0.3.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-sink"
|
||||||
|
version = "0.3.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-task"
|
||||||
|
version = "0.3.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util"
|
||||||
|
version = "0.3.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "h2"
|
||||||
|
version = "0.4.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-waker",
|
||||||
|
"bytes",
|
||||||
|
"fnv",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"http",
|
||||||
|
"indexmap",
|
||||||
|
"slab",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.17.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"itoa",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http-body"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"http",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http-body-util"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "httparse"
|
||||||
|
version = "1.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "httpdate"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper"
|
||||||
|
version = "1.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-waker",
|
||||||
|
"bytes",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"h2",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"httparse",
|
||||||
|
"httpdate",
|
||||||
|
"itoa",
|
||||||
|
"pin-project-lite",
|
||||||
|
"smallvec",
|
||||||
|
"tokio",
|
||||||
|
"want",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper-util"
|
||||||
|
version = "0.1.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"bytes",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"hyper",
|
||||||
|
"ipnet",
|
||||||
|
"libc",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project-lite",
|
||||||
|
"socket2",
|
||||||
|
"system-configuration",
|
||||||
|
"tokio",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
"windows-registry",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.14.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ipnet"
|
||||||
|
version = "2.12.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.189"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matchers"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||||
|
dependencies = [
|
||||||
|
"regex-automata",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "1.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nu-ansi-term"
|
||||||
|
version = "0.50.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "percent-encoding"
|
||||||
|
version = "2.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.107"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.47"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.229"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_core"
|
||||||
|
version = "1.0.229"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.229"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 3.0.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.151"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
"zmij",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
||||||
|
dependencies = [
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slab"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.16.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba467056f1b547ed52077911161fc86985becbc60e8e1857c8a144dab0def891"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.6.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.119"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "3.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "system-configuration"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"core-foundation",
|
||||||
|
"system-configuration-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "system-configuration-sys"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio"
|
||||||
|
version = "1.53.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"pin-project-lite",
|
||||||
|
"signal-hook-registry",
|
||||||
|
"socket2",
|
||||||
|
"tokio-macros",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-macros"
|
||||||
|
version = "2.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 3.0.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-util"
|
||||||
|
version = "0.7.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"libc",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-layer"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-service"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing"
|
||||||
|
version = "0.1.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project-lite",
|
||||||
|
"tracing-attributes",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-attributes"
|
||||||
|
version = "0.1.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-core"
|
||||||
|
version = "0.1.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"valuable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-log"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-serde"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
|
||||||
|
dependencies = [
|
||||||
|
"matchers",
|
||||||
|
"nu-ansi-term",
|
||||||
|
"once_cell",
|
||||||
|
"regex-automata",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sharded-slab",
|
||||||
|
"smallvec",
|
||||||
|
"thread_local",
|
||||||
|
"tracing",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-log",
|
||||||
|
"tracing-serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tripoli-edge"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"http-body-util",
|
||||||
|
"hyper",
|
||||||
|
"hyper-util",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "try-lock"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "valuable"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "want"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
|
||||||
|
dependencies = [
|
||||||
|
"try-lock",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.1+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-registry"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
"windows-result",
|
||||||
|
"windows-strings",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-strings"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zmij"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
|
||||||
3
Cargo.toml
Normal file
3
Cargo.toml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
|
members = ["tripoli-edge"]
|
||||||
|
|
@ -29,7 +29,7 @@ tenté, non simulé.
|
||||||
| SSH public | Toujours ouvert (choix explicite, pas encore restreint au VPN) |
|
| SSH public | Toujours ouvert (choix explicite, pas encore restreint au VPN) |
|
||||||
| Ansible (rôle de base) | Fait — `ansible/` |
|
| Ansible (rôle de base) | Fait — `ansible/` |
|
||||||
| OpenTofu (serveur + DNS) | Code écrit — `tofu/` ; serveur en donnée en lecture seule (jamais recréé/détruit), DNS géré via le provider OVH. `plan`/`apply` pas encore exécutés (nécessitent des identifiants OVH/Hetzner non fournis) |
|
| OpenTofu (serveur + DNS) | Code écrit — `tofu/` ; serveur en donnée en lecture seule (jamais recréé/détruit), DNS géré via le provider OVH. `plan`/`apply` pas encore exécutés (nécessitent des identifiants OVH/Hetzner non fournis) |
|
||||||
| `tripoli-edge` (Rust, Phase 2) | Pas commencé |
|
| `tripoli-edge` (Rust, Phase 2) | Fait et déployé en prod — `tripoli-edge/`, service systemd, derrière Caddy, vérifié via `https://tripoli.cloud/healthz` et `/metrics` |
|
||||||
| `tripoli-api` (Rust, Phase 3) | Pas commencé |
|
| `tripoli-api` (Rust, Phase 3) | Pas commencé |
|
||||||
| `tripoli-netctl` (Rust, Phase 4) | Pas commencé |
|
| `tripoli-netctl` (Rust, Phase 4) | Pas commencé |
|
||||||
| Observabilité dédiée (Phase 5) | Pas commencé (une stack Prometheus/Grafana existe déjà sur le serveur, partagée avec les autres projets) |
|
| Observabilité dédiée (Phase 5) | Pas commencé (une stack Prometheus/Grafana existe déjà sur le serveur, partagée avec les autres projets) |
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,19 @@
|
||||||
proto: udp
|
proto: udp
|
||||||
comment: "tripoli-cloud : WireGuard admin"
|
comment: "tripoli-cloud : WireGuard admin"
|
||||||
|
|
||||||
|
- name: Autoriser Caddy à joindre tripoli-edge (172.17.0.1:9000)
|
||||||
|
# Même contrainte que la règle 8090/tcp de voltvision déjà en place :
|
||||||
|
# une requête du conteneur Caddy (réseau "edge", 172.19.0.0/16) vers
|
||||||
|
# 172.17.0.1 (passerelle docker0) passe par la chaîne INPUT de l'hôte,
|
||||||
|
# pas FORWARD - ufw la bloque par défaut même si tripoli-edge écoute
|
||||||
|
# bien sur cette adresse.
|
||||||
|
community.general.ufw:
|
||||||
|
rule: allow
|
||||||
|
from_ip: "172.16.0.0/12"
|
||||||
|
port: "9000"
|
||||||
|
proto: tcp
|
||||||
|
comment: "tripoli-cloud : Caddy -> tripoli-edge"
|
||||||
|
|
||||||
- name: SSH restreint au tunnel WireGuard uniquement
|
- name: SSH restreint au tunnel WireGuard uniquement
|
||||||
when: ssh_restrict_to_wireguard
|
when: ssh_restrict_to_wireguard
|
||||||
block:
|
block:
|
||||||
|
|
|
||||||
14
tripoli-edge/Cargo.toml
Normal file
14
tripoli-edge/Cargo.toml
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "tripoli-edge"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tokio = { version = "1", features = ["rt-multi-thread", "net", "macros", "time", "signal"] }
|
||||||
|
hyper = { version = "1", features = ["full"] }
|
||||||
|
hyper-util = { version = "0.1", features = ["full"] }
|
||||||
|
http-body-util = "0.1"
|
||||||
|
bytes = "1"
|
||||||
|
tokio-util = "0.7"
|
||||||
|
tracing = "0.1"
|
||||||
|
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
|
||||||
60
tripoli-edge/README.md
Normal file
60
tripoli-edge/README.md
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
# tripoli-edge
|
||||||
|
|
||||||
|
Service Rust interne placé derrière le Caddy déjà en place sur le
|
||||||
|
serveur (voir `README.md` à la racine du dépôt pour le pourquoi : ce
|
||||||
|
Caddy tient déjà les ports 80/443 pour six autres domaines).
|
||||||
|
|
||||||
|
Ce que fait tripoli-edge, concrètement :
|
||||||
|
|
||||||
|
- `GET /healthz` : répond directement, sans toucher à tripoli-api.
|
||||||
|
- `GET /metrics` : métriques au format d'exposition Prometheus
|
||||||
|
(compteurs à la main, voir `src/metrics.rs`).
|
||||||
|
- Tout le reste : reverse-proxy vers `tripoli-api` (méthode, en-têtes et
|
||||||
|
corps conservés). Tant que `tripoli-api` n'existe pas (Phase 3), ça
|
||||||
|
répond un vrai `502 Bad Gateway`, pas une réponse simulée.
|
||||||
|
- Logs structurés JSON (`tracing` + `tracing-subscriber`) pour chaque
|
||||||
|
requête : méthode, chemin, host, statut, latence.
|
||||||
|
|
||||||
|
## Configuration (variables d'environnement)
|
||||||
|
|
||||||
|
| Variable | Défaut | Rôle |
|
||||||
|
|---|---|---|
|
||||||
|
| `TRIPOLI_EDGE_LISTEN` | `127.0.0.1:9000,172.17.0.1:9000` | Adresses d'écoute (jamais `0.0.0.0` — voir plus bas) |
|
||||||
|
| `TRIPOLI_API_UPSTREAM` | `127.0.0.1:8081` | Adresse de tripoli-api |
|
||||||
|
| `RUST_LOG` | — | Niveau de logs (`info`, `debug`, ...) |
|
||||||
|
|
||||||
|
`172.17.0.1` est la passerelle `docker0` du serveur : c'est ce qui permet
|
||||||
|
au conteneur Caddy (sur un réseau Docker différent) de joindre
|
||||||
|
tripoli-edge, qui tourne nativement sur l'hôte — même pattern que le
|
||||||
|
Nginx de VoltVision sur ce même serveur. tripoli-edge n'écoute jamais sur
|
||||||
|
l'interface publique.
|
||||||
|
|
||||||
|
## Déploiement (fait manuellement pour cette phase)
|
||||||
|
|
||||||
|
L'automatisation complète (build + déploiement via CI) est prévue dans
|
||||||
|
la section CI/CD du brief, pas encore faite. Pour l'instant, sur le
|
||||||
|
serveur :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# build (rustc/cargo installés via apt)
|
||||||
|
cd /opt/tripoli-edge/src && cargo build --release
|
||||||
|
install -m 755 target/release/tripoli-edge /usr/local/bin/tripoli-edge
|
||||||
|
systemctl restart tripoli-edge
|
||||||
|
```
|
||||||
|
|
||||||
|
Le service tourne sous l'utilisateur système `tripoli` (sans shell,
|
||||||
|
sans privilège) via `deploy/tripoli-edge.service`, déjà installé dans
|
||||||
|
`/etc/systemd/system/`.
|
||||||
|
|
||||||
|
Le bloc Caddy est dans `deploy/Caddyfile.snippet` — ajouté à
|
||||||
|
`/opt/edge/Caddyfile` sur le serveur (fichier partagé, donc pas commité
|
||||||
|
dans son intégralité ici). Une règle `ufw` supplémentaire était
|
||||||
|
nécessaire pour que Caddy puisse joindre `172.17.0.1:9000` (voir
|
||||||
|
`ansible/roles/base/tasks/main.yml`).
|
||||||
|
|
||||||
|
## Vérifié en production
|
||||||
|
|
||||||
|
```
|
||||||
|
curl https://tripoli.cloud/healthz -> 200 "ok"
|
||||||
|
curl https://tripoli.cloud/metrics -> métriques Prometheus
|
||||||
|
```
|
||||||
25
tripoli-edge/deploy/Caddyfile.snippet
Normal file
25
tripoli-edge/deploy/Caddyfile.snippet
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Bloc ajouté à /opt/edge/Caddyfile sur le serveur (fichier partagé avec
|
||||||
|
# les 6 autres projets hébergés, donc pas dans ce dépôt en entier — voir
|
||||||
|
# le README de ce dossier).
|
||||||
|
#
|
||||||
|
# Pourquoi Caddy et pas tripoli-edge lui-même sur 80/443 : les ports 80 et
|
||||||
|
# 443 appartiennent déjà à ce Caddy. tripoli-edge est un service interne
|
||||||
|
# (172.17.0.1:9000, passerelle docker0, jamais exposé publiquement) :
|
||||||
|
# Caddy fait TLS/ACME et le routage par Host, tripoli-edge fait le reste.
|
||||||
|
|
||||||
|
tripoli.cloud, www.tripoli.cloud {
|
||||||
|
reverse_proxy 172.17.0.1:9000
|
||||||
|
|
||||||
|
header {
|
||||||
|
Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
||||||
|
X-Content-Type-Options "nosniff"
|
||||||
|
X-Frame-Options "SAMEORIGIN"
|
||||||
|
Referrer-Policy "strict-origin-when-cross-origin"
|
||||||
|
-Server
|
||||||
|
}
|
||||||
|
|
||||||
|
log {
|
||||||
|
output file /var/log/caddy/tripoli-cloud.log
|
||||||
|
format json
|
||||||
|
}
|
||||||
|
}
|
||||||
25
tripoli-edge/deploy/tripoli-edge.service
Normal file
25
tripoli-edge/deploy/tripoli-edge.service
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
[Unit]
|
||||||
|
Description=tripoli-edge - service interne derrière Caddy pour tripoli.cloud
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=tripoli
|
||||||
|
Group=tripoli
|
||||||
|
ExecStart=/usr/local/bin/tripoli-edge
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=2
|
||||||
|
Environment=RUST_LOG=info
|
||||||
|
Environment=TRIPOLI_EDGE_LISTEN=127.0.0.1:9000,172.17.0.1:9000
|
||||||
|
Environment=TRIPOLI_API_UPSTREAM=127.0.0.1:8081
|
||||||
|
|
||||||
|
# Un utilisateur système dédié, sans shell, plutôt que root : ce service
|
||||||
|
# n'a besoin d'aucun privilège (pas de port <1024, pas d'accès disque
|
||||||
|
# hors ses propres logs via journald).
|
||||||
|
NoNewPrivileges=true
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=true
|
||||||
|
PrivateTmp=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
42
tripoli-edge/src/config.rs
Normal file
42
tripoli-edge/src/config.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
|
/// Configuration minimale, entièrement par variables d'environnement :
|
||||||
|
/// pas de fichier de config à ce stade, ce serait de la complexité pour
|
||||||
|
/// rien tant qu'il n'y a qu'un seul déploiement (derrière Caddy, sur ce
|
||||||
|
/// même serveur).
|
||||||
|
pub struct Config {
|
||||||
|
/// Adresses d'écoute internes. Toujours la boucle locale et/ou une
|
||||||
|
/// passerelle docker (jamais 0.0.0.0) : c'est le même choix que pour
|
||||||
|
/// le Nginx de VoltVision sur ce serveur (172.17.0.1, passerelle
|
||||||
|
/// docker0, atteignable depuis le conteneur Caddy). Une valeur reste
|
||||||
|
/// une adresse unique ; plusieurs sont séparées par une virgule.
|
||||||
|
pub listen_addrs: Vec<SocketAddr>,
|
||||||
|
/// Adresse de tripoli-api. Tant que ce service n'existe pas (Phase 3),
|
||||||
|
/// les requêtes proxyées échoueront en 502 - un vrai 502, pas une
|
||||||
|
/// réponse simulée.
|
||||||
|
pub upstream_addr: SocketAddr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn from_env() -> Self {
|
||||||
|
let listen_addrs = std::env::var("TRIPOLI_EDGE_LISTEN")
|
||||||
|
.unwrap_or_else(|_| "127.0.0.1:9000,172.17.0.1:9000".to_string())
|
||||||
|
.split(',')
|
||||||
|
.map(|addr| {
|
||||||
|
addr.trim()
|
||||||
|
.parse()
|
||||||
|
.expect("TRIPOLI_EDGE_LISTEN doit être une liste d'adresses host:port valides")
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let upstream_addr = std::env::var("TRIPOLI_API_UPSTREAM")
|
||||||
|
.unwrap_or_else(|_| "127.0.0.1:8081".to_string())
|
||||||
|
.parse()
|
||||||
|
.expect("TRIPOLI_API_UPSTREAM doit être une adresse host:port valide");
|
||||||
|
|
||||||
|
Self {
|
||||||
|
listen_addrs,
|
||||||
|
upstream_addr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
106
tripoli-edge/src/main.rs
Normal file
106
tripoli-edge/src/main.rs
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
mod config;
|
||||||
|
mod metrics;
|
||||||
|
mod proxy;
|
||||||
|
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use hyper::server::conn::http1;
|
||||||
|
use hyper::service::service_fn;
|
||||||
|
use hyper_util::rt::TokioIo;
|
||||||
|
use tokio::net::TcpListener;
|
||||||
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
|
use config::Config;
|
||||||
|
use metrics::Metrics;
|
||||||
|
use proxy::UpstreamClient;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.json()
|
||||||
|
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
|
||||||
|
.init();
|
||||||
|
|
||||||
|
let config = Config::from_env();
|
||||||
|
let metrics = Arc::new(Metrics::default());
|
||||||
|
let client = Arc::new(proxy::build_client());
|
||||||
|
let shutdown = CancellationToken::new();
|
||||||
|
|
||||||
|
let mut listeners = Vec::new();
|
||||||
|
for addr in &config.listen_addrs {
|
||||||
|
let listener = TcpListener::bind(addr)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|err| panic!("impossible d'écouter sur {addr}: {err}"));
|
||||||
|
listeners.push(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
listen_addrs = ?config.listen_addrs,
|
||||||
|
upstream_addr = %config.upstream_addr,
|
||||||
|
"tripoli-edge démarré"
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut tasks = Vec::new();
|
||||||
|
for listener in listeners {
|
||||||
|
let client = Arc::clone(&client);
|
||||||
|
let metrics = Arc::clone(&metrics);
|
||||||
|
let shutdown = shutdown.clone();
|
||||||
|
let upstream_addr = config.upstream_addr;
|
||||||
|
tasks.push(tokio::spawn(accept_loop(
|
||||||
|
listener,
|
||||||
|
client,
|
||||||
|
metrics,
|
||||||
|
upstream_addr,
|
||||||
|
shutdown,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
tokio::signal::ctrl_c()
|
||||||
|
.await
|
||||||
|
.expect("impossible d'installer le gestionnaire de signal");
|
||||||
|
tracing::info!("signal d'arrêt reçu, extinction de tripoli-edge");
|
||||||
|
shutdown.cancel();
|
||||||
|
|
||||||
|
for task in tasks {
|
||||||
|
let _ = task.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn accept_loop(
|
||||||
|
listener: TcpListener,
|
||||||
|
client: Arc<UpstreamClient>,
|
||||||
|
metrics: Arc<Metrics>,
|
||||||
|
upstream_addr: SocketAddr,
|
||||||
|
shutdown: CancellationToken,
|
||||||
|
) {
|
||||||
|
loop {
|
||||||
|
let (stream, peer_addr) = tokio::select! {
|
||||||
|
accepted = listener.accept() => match accepted {
|
||||||
|
Ok(pair) => pair,
|
||||||
|
Err(err) => {
|
||||||
|
tracing::warn!(error = %err, "échec d'acceptation d'une connexion");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ = shutdown.cancelled() => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let client = Arc::clone(&client);
|
||||||
|
let metrics = Arc::clone(&metrics);
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let io = TokioIo::new(stream);
|
||||||
|
let service = service_fn(move |req| {
|
||||||
|
proxy::handle(req, Arc::clone(&client), upstream_addr, Arc::clone(&metrics))
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Err(err) = http1::Builder::new()
|
||||||
|
.serve_connection(io, service)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
tracing::debug!(peer = %peer_addr, error = %err, "connexion terminée en erreur");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
84
tripoli-edge/src/metrics.rs
Normal file
84
tripoli-edge/src/metrics.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
|
||||||
|
|
||||||
|
/// Compteurs à la main plutôt qu'une crate de métriques : pour cinq
|
||||||
|
/// compteurs, une lib dédiée serait une abstraction de plus à défendre
|
||||||
|
/// sans bénéfice réel. `Ordering::Relaxed` suffit ici - on n'a besoin
|
||||||
|
/// d'aucune synchronisation entre compteurs, juste que chaque incrément
|
||||||
|
/// soit atomique.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Metrics {
|
||||||
|
requests_total: AtomicU64,
|
||||||
|
responses_1xx: AtomicU64,
|
||||||
|
responses_2xx: AtomicU64,
|
||||||
|
responses_3xx: AtomicU64,
|
||||||
|
responses_4xx: AtomicU64,
|
||||||
|
responses_5xx: AtomicU64,
|
||||||
|
in_flight: AtomicI64,
|
||||||
|
duration_ms_sum: AtomicU64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InFlightGuard<'a>(&'a Metrics);
|
||||||
|
|
||||||
|
impl<'a> InFlightGuard<'a> {
|
||||||
|
pub fn enter(metrics: &'a Metrics) -> Self {
|
||||||
|
metrics.in_flight.fetch_add(1, Ordering::Relaxed);
|
||||||
|
Self(metrics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for InFlightGuard<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.0.in_flight.fetch_sub(1, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Metrics {
|
||||||
|
pub fn record(&self, status: u16, duration_ms: u64) {
|
||||||
|
self.requests_total.fetch_add(1, Ordering::Relaxed);
|
||||||
|
self.duration_ms_sum.fetch_add(duration_ms, Ordering::Relaxed);
|
||||||
|
let bucket = match status {
|
||||||
|
100..=199 => &self.responses_1xx,
|
||||||
|
200..=299 => &self.responses_2xx,
|
||||||
|
300..=399 => &self.responses_3xx,
|
||||||
|
400..=499 => &self.responses_4xx,
|
||||||
|
_ => &self.responses_5xx,
|
||||||
|
};
|
||||||
|
bucket.fetch_add(1, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Format texte Prometheus (exposition format), à la main : pas de
|
||||||
|
/// registre global, pas de types de métriques (counter/gauge) au-delà
|
||||||
|
/// de ce qui est vraiment utile ici.
|
||||||
|
pub fn render(&self) -> String {
|
||||||
|
let total = self.requests_total.load(Ordering::Relaxed);
|
||||||
|
let sum_ms = self.duration_ms_sum.load(Ordering::Relaxed);
|
||||||
|
let avg_ms = if total > 0 { sum_ms as f64 / total as f64 } else { 0.0 };
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"# HELP tripoli_edge_requests_total Nombre total de requêtes traitées.\n\
|
||||||
|
# TYPE tripoli_edge_requests_total counter\n\
|
||||||
|
tripoli_edge_requests_total {total}\n\
|
||||||
|
# HELP tripoli_edge_responses_total Réponses par classe de statut HTTP.\n\
|
||||||
|
# TYPE tripoli_edge_responses_total counter\n\
|
||||||
|
tripoli_edge_responses_total{{class=\"1xx\"}} {c1}\n\
|
||||||
|
tripoli_edge_responses_total{{class=\"2xx\"}} {c2}\n\
|
||||||
|
tripoli_edge_responses_total{{class=\"3xx\"}} {c3}\n\
|
||||||
|
tripoli_edge_responses_total{{class=\"4xx\"}} {c4}\n\
|
||||||
|
tripoli_edge_responses_total{{class=\"5xx\"}} {c5}\n\
|
||||||
|
# HELP tripoli_edge_in_flight_requests Requêtes en cours de traitement.\n\
|
||||||
|
# TYPE tripoli_edge_in_flight_requests gauge\n\
|
||||||
|
tripoli_edge_in_flight_requests {in_flight}\n\
|
||||||
|
# HELP tripoli_edge_request_duration_ms_avg Latence moyenne, en millisecondes.\n\
|
||||||
|
# TYPE tripoli_edge_request_duration_ms_avg gauge\n\
|
||||||
|
tripoli_edge_request_duration_ms_avg {avg_ms}\n",
|
||||||
|
total = total,
|
||||||
|
c1 = self.responses_1xx.load(Ordering::Relaxed),
|
||||||
|
c2 = self.responses_2xx.load(Ordering::Relaxed),
|
||||||
|
c3 = self.responses_3xx.load(Ordering::Relaxed),
|
||||||
|
c4 = self.responses_4xx.load(Ordering::Relaxed),
|
||||||
|
c5 = self.responses_5xx.load(Ordering::Relaxed),
|
||||||
|
in_flight = self.in_flight.load(Ordering::Relaxed),
|
||||||
|
avg_ms = avg_ms,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
123
tripoli-edge/src/proxy.rs
Normal file
123
tripoli-edge/src/proxy.rs
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use bytes::Bytes;
|
||||||
|
use http_body_util::combinators::BoxBody;
|
||||||
|
use http_body_util::{BodyExt, Full};
|
||||||
|
use hyper::body::Incoming;
|
||||||
|
use hyper::{Request, Response, StatusCode, Uri};
|
||||||
|
use hyper_util::client::legacy::connect::HttpConnector;
|
||||||
|
use hyper_util::client::legacy::Client;
|
||||||
|
use hyper_util::rt::TokioExecutor;
|
||||||
|
|
||||||
|
use crate::metrics::{InFlightGuard, Metrics};
|
||||||
|
|
||||||
|
type BoxError = Box<dyn std::error::Error + Send + Sync>;
|
||||||
|
pub type EdgeBody = BoxBody<Bytes, BoxError>;
|
||||||
|
pub type UpstreamClient = Client<HttpConnector, Incoming>;
|
||||||
|
|
||||||
|
pub fn build_client() -> UpstreamClient {
|
||||||
|
Client::builder(TokioExecutor::new()).build(HttpConnector::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_body(text: impl Into<Bytes>) -> EdgeBody {
|
||||||
|
Full::new(text.into())
|
||||||
|
.map_err(|never| match never {})
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_response(status: StatusCode, body: impl Into<Bytes>) -> Response<EdgeBody> {
|
||||||
|
Response::builder()
|
||||||
|
.status(status)
|
||||||
|
.header("content-type", "text/plain; charset=utf-8")
|
||||||
|
.body(text_body(body))
|
||||||
|
.expect("réponse texte statique toujours valide")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Point d'entrée de chaque requête reçue de Caddy. Deux routes sont
|
||||||
|
/// gérées par tripoli-edge lui-même (`/healthz`, `/metrics`) ; tout le
|
||||||
|
/// reste est transmis tel quel à tripoli-api.
|
||||||
|
pub async fn handle(
|
||||||
|
req: Request<Incoming>,
|
||||||
|
client: Arc<UpstreamClient>,
|
||||||
|
upstream_addr: SocketAddr,
|
||||||
|
metrics: Arc<Metrics>,
|
||||||
|
) -> Result<Response<EdgeBody>, hyper::Error> {
|
||||||
|
let start = Instant::now();
|
||||||
|
let _in_flight = InFlightGuard::enter(&metrics);
|
||||||
|
|
||||||
|
let method = req.method().clone();
|
||||||
|
let path = req.uri().path().to_string();
|
||||||
|
let host_header = req
|
||||||
|
.headers()
|
||||||
|
.get("host")
|
||||||
|
.and_then(|h| h.to_str().ok())
|
||||||
|
.unwrap_or("-")
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let response = match path.as_str() {
|
||||||
|
"/healthz" => text_response(StatusCode::OK, "ok"),
|
||||||
|
"/metrics" => text_response(StatusCode::OK, metrics.render()),
|
||||||
|
_ => proxy_to_upstream(req, &client, upstream_addr).await,
|
||||||
|
};
|
||||||
|
|
||||||
|
let status = response.status().as_u16();
|
||||||
|
let duration_ms = start.elapsed().as_millis() as u64;
|
||||||
|
metrics.record(status, duration_ms);
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
method = %method,
|
||||||
|
path = %path,
|
||||||
|
host = %host_header,
|
||||||
|
status = status,
|
||||||
|
duration_ms = duration_ms,
|
||||||
|
"requête traitée"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reconstruit la requête vers tripoli-api en conservant méthode,
|
||||||
|
/// en-têtes et corps ; seule l'autorité de l'URI change, pour pointer
|
||||||
|
/// vers l'adresse interne au lieu de `tripoli.cloud`.
|
||||||
|
async fn proxy_to_upstream(
|
||||||
|
req: Request<Incoming>,
|
||||||
|
client: &UpstreamClient,
|
||||||
|
upstream_addr: SocketAddr,
|
||||||
|
) -> Response<EdgeBody> {
|
||||||
|
let (parts, body) = req.into_parts();
|
||||||
|
|
||||||
|
let path_and_query = parts
|
||||||
|
.uri
|
||||||
|
.path_and_query()
|
||||||
|
.map(|pq| pq.as_str())
|
||||||
|
.unwrap_or("/");
|
||||||
|
|
||||||
|
let upstream_uri: Uri = match format!("http://{upstream_addr}{path_and_query}").parse() {
|
||||||
|
Ok(uri) => uri,
|
||||||
|
Err(_) => return text_response(StatusCode::BAD_GATEWAY, "URI amont invalide"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut upstream_req = Request::builder()
|
||||||
|
.method(parts.method)
|
||||||
|
.uri(upstream_uri)
|
||||||
|
.body(body)
|
||||||
|
.expect("requête amont toujours valide : mêmes en-têtes/corps que l'entrante");
|
||||||
|
*upstream_req.headers_mut() = parts.headers;
|
||||||
|
|
||||||
|
match client.request(upstream_req).await {
|
||||||
|
Ok(resp) => {
|
||||||
|
let (parts, body) = resp.into_parts();
|
||||||
|
let boxed_body = body.map_err(|e| Box::new(e) as BoxError).boxed();
|
||||||
|
Response::from_parts(parts, boxed_body)
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
tracing::warn!(error = %err, "tripoli-api injoignable");
|
||||||
|
text_response(
|
||||||
|
StatusCode::BAD_GATEWAY,
|
||||||
|
format!("tripoli-api injoignable : {err}"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue