diff --git a/README.md b/README.md index 20d0bacc..2a0daf01 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ The default functionality of Gitlab is limited at the project level. This can be of projects, potentially resulting in undetected failed pipelines. -## 👉 [Demo (main branch)](https://gitlab-ci-dashboard.larscom.nl) +## 👉 [Demo](https://gitlab-ci-dashboard.larscom.nl)
@@ -66,13 +66,22 @@ projects, potentially resulting in undetected failed pipelines. 2. Run docker with the required environment variables (GITLAB_BASE_URL, GITLAB_API_TOKEN) ```bash -docker run -p 8080:8080 -e GITLAB_BASE_URL=https://gitlab.com -e GITLAB_API_TOKEN=my_token larscom/gitlab-ci-dashboard:latest +docker run \ + -p 8080:8080 \ + -e GITLAB_BASE_URL=https://gitlab.com \ + -e GITLAB_API_TOKEN=my_token \ + larscom/gitlab-ci-dashboard:latest ``` Or you can run it with a TOML configration file ```bash -docker run -p 8080:8080 -v $(pwd)/config.toml:/app/config.toml larscom/gitlab-ci-dashboard:latest +docker run \ + -p 8080:8080 \ + -e GITLAB_BASE_URL=https://gitlab.com \ + -e GITLAB_API_TOKEN=my_token \ + -v ./config.toml:/app/config.toml \ + larscom/gitlab-ci-dashboard:latest ``` 3. Dashboard should be available at: http://localhost:8080/ showing (by default) all available groups and their @@ -103,13 +112,39 @@ A TOML file takes precedence over environment variables, except for the `RUST_LO > An example TOML file can be found inside the `./api` folder. -Mount the `config.toml` inside the container. +Mount the `config.toml` inside the container (`/app/config.toml`) ```bash -docker run -p 8080:8080 -v $(pwd)/config.toml:/app/config.toml larscom/gitlab-ci-dashboard:latest +docker run \ + -p 8080:8080 \ + -e GITLAB_BASE_URL=https://gitlab.com \ + -e GITLAB_API_TOKEN=my_token \ + -v ./config.toml:/app/config.toml \ + larscom/gitlab-ci-dashboard:latest ``` -### Environment variables +## 📜 Custom CA certificate +If you are running a gitlab instance that is using a TLS certificate signed with a private CA you are able to provide that CA as mount (PEM encoded) + +This is needed when the dashboard backend is unable to make a connection to the gitlab API over HTTPS. + +Mount the `ca.crt` inside the container (`/app/certs/ca.crt`) + +```bash +docker run \ + -p 8080:8080 \ + -e GITLAB_BASE_URL=https://gitlab.com \ + -e GITLAB_API_TOKEN=my_token \ + -v ./ca.crt:/app/certs/ca.crt \ + larscom/gitlab-ci-dashboard:latest +``` + +### Troubleshooting +If you are still unable to connect with a custom CA cert, be sure that the gitlab server certificate contains a valid SAN (Subject Alternative Name) + +If there is a mismatch the HTTP client is still unable to make a proper connection. + +## 🌍 Environment variables | Variable | Type | Description | Required | Default | |-----------------------------------|--------|------------------------------------------------------------------------------------------------------------------------------------|----------|--------------| diff --git a/api/.gitignore b/api/.gitignore index f567020a..0ca631b5 100644 --- a/api/.gitignore +++ b/api/.gitignore @@ -1,2 +1,3 @@ .env config.toml +certs/ diff --git a/api/Cargo.lock b/api/Cargo.lock index 89aff007..88df4fff 100644 --- a/api/Cargo.lock +++ b/api/Cargo.lock @@ -425,6 +425,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.42" @@ -871,8 +877,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -882,8 +890,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.13.3+wasi-0.2.2", + "wasm-bindgen", "windows-targets 0.52.6", ] @@ -1041,6 +1051,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", + "webpki-roots 0.26.11", ] [[package]] @@ -1438,6 +1449,12 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "memchr" version = "2.7.2" @@ -1722,6 +1739,61 @@ dependencies = [ "thiserror", ] +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2 0.5.10", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.1", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.5.10", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "quote" version = "1.0.40" @@ -1829,6 +1901,8 @@ dependencies = [ "native-tls", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", "rustls-pki-types", "serde", "serde_json", @@ -1836,6 +1910,7 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-native-tls", + "tokio-rustls", "tower", "tower-http", "tower-service", @@ -1843,6 +1918,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots 1.0.3", ] [[package]] @@ -1860,6 +1936,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustc_version" version = "0.4.0" @@ -1889,6 +1971,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebbbdb961df0ad3f2652da8f3fdc4b36122f568f968f45ad3316f26c025c677b" dependencies = [ "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -1901,6 +1984,7 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ + "web-time", "zeroize", ] @@ -2325,6 +2409,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.48.0" @@ -2684,6 +2783,34 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.3", +] + +[[package]] +name = "webpki-roots" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "windows-core" version = "0.52.0" diff --git a/api/Cargo.toml b/api/Cargo.toml index 086a39c6..848b2bc0 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -16,7 +16,7 @@ dotenv = "0.15.0" env_logger = "0.11.8" log = "0.4.28" moka = { version = "0.12.11", features = ["future"] } -reqwest = { version = "0.12.24", features = ["json"] } +reqwest = { version = "0.12.24", features = ["json", "rustls-tls"] } tokio = { version = "1.48.0", features = ["sync"] } async-trait = "0.1.89" futures = "0.3.31" diff --git a/api/src/gitlab.rs b/api/src/gitlab.rs index ac33d3cb..6da57bdb 100644 --- a/api/src/gitlab.rs +++ b/api/src/gitlab.rs @@ -11,6 +11,7 @@ use reqwest::{Client, Url}; use serde::de::DeserializeOwned; use serde_json::Value; use std::collections::HashMap; +use std::fs; use tokio::sync::mpsc; #[async_trait] @@ -79,14 +80,29 @@ struct Page { } impl GitlabClient { + fn get_ca_cert() -> Option { + match fs::read("./certs/ca.crt") { + Ok(cert) => { + let ca = String::from_utf8_lossy(&cert); + log::debug!("Found custom CA cert:\n{ca}"); + Some(reqwest::Certificate::from_pem(&cert).expect("invalid cert")) + } + Err(_) => None, + } + } + pub fn new(gitlab_url: &str, gitlab_token: &str) -> Self { - let http_client = Client::builder() - .default_headers(create_http_headers(gitlab_token)) - .build() - .expect("http client to be build"); + let mut client_builder = Client::builder() + .use_rustls_tls() + .default_headers(create_http_headers(gitlab_token)); + + if let Some(ca) = Self::get_ca_cert() { + client_builder = client_builder.add_root_certificate(ca); + } + Self { base_url: format!("{gitlab_url}/api/v4"), - http_client, + http_client: client_builder.build().expect("invalid client"), } } } diff --git a/docker-compose.yml b/docker-compose.yml index bae38cf0..12677f1a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,8 +7,9 @@ services: VERSION_ARG: docker env_file: - ./api/.env -# volumes: -# - ./api/config.toml:/app/config.toml + # volumes: + # - ./api/config.toml:/app/config.toml + # - ./api/certs/ca.crt:/app/certs/ca.crt environment: - 'TZ=Europe/Amsterdam' ports: