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: