feat: Migrate TLS backend from rustls to native-tls (#3807)

## Fixes

- #1142
- #2528
- #570
- #3369
- #968
- #3196
This commit is contained in:
Marko Vejnovic
2026-07-30 16:44:11 -07:00
committed by GitHub
parent 6c9f57a7b5
commit 3aac1b3f5e
24 changed files with 180 additions and 241 deletions
+1 -1
View File
@@ -42,4 +42,4 @@ jobs:
# Run `nix fmt` to fix any failures here # Run `nix fmt` to fix any failures here
- name: Check nix formatting - name: Check nix formatting
run: nix fmt -- --check $(git ls-files '*.nix') run: nix fmt -- --ci
+27 -3
View File
@@ -23,6 +23,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- name: Install OpenSSL dev libraries (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y libssl-dev pkg-config
- name: Install rust - name: Install rust
uses: dtolnay/rust-toolchain@master uses: dtolnay/rust-toolchain@master
with: with:
@@ -76,14 +80,14 @@ jobs:
run: cross build -p atuin-common --locked --target ${{ matrix.target }} run: cross build -p atuin-common --locked --target ${{ matrix.target }}
- name: Run cross build client - name: Run cross build client
run: cross build -p atuin-client --locked --target ${{ matrix.target }} run: cross build -p atuin-client --locked --target ${{ matrix.target }} --features vendored-tls
- name: Run cross build server - name: Run cross build server
run: cross build -p atuin-server --locked --target ${{ matrix.target }} run: cross build -p atuin-server --locked --target ${{ matrix.target }} --features vendored-tls
- name: Run cross build main - name: Run cross build main
run: | run: |
cross build --all --locked --target ${{ matrix.target }} cross build --all --locked --target ${{ matrix.target }} --features vendored-tls
unit-test: unit-test:
strategy: strategy:
@@ -94,6 +98,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- name: Install OpenSSL dev libraries (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y libssl-dev pkg-config
- name: Install rust - name: Install rust
uses: dtolnay/rust-toolchain@master uses: dtolnay/rust-toolchain@master
with: with:
@@ -124,6 +132,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- name: Install OpenSSL dev libraries (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y libssl-dev pkg-config
- name: Install rust - name: Install rust
uses: dtolnay/rust-toolchain@master uses: dtolnay/rust-toolchain@master
with: with:
@@ -168,6 +180,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- name: Install OpenSSL dev libraries (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y libssl-dev pkg-config
- name: Install rust - name: Install rust
uses: dtolnay/rust-toolchain@master uses: dtolnay/rust-toolchain@master
with: with:
@@ -197,6 +213,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- name: Install OpenSSL dev libraries (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y libssl-dev pkg-config
- name: Install latest rust - name: Install latest rust
uses: dtolnay/rust-toolchain@master uses: dtolnay/rust-toolchain@master
with: with:
@@ -223,6 +243,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- name: Install OpenSSL dev libraries (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y libssl-dev pkg-config
- name: Install rust - name: Install rust
uses: dtolnay/rust-toolchain@master uses: dtolnay/rust-toolchain@master
with: with:
Generated
+103 -183
View File
@@ -406,7 +406,6 @@ dependencies = [
"pretty_assertions", "pretty_assertions",
"proptest", "proptest",
"rstest", "rstest",
"rustls",
"serde", "serde",
"serde_json", "serde_json",
"serde_with", "serde_with",
@@ -1115,16 +1114,6 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "combine"
version = "4.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
dependencies = [
"bytes",
"memchr",
]
[[package]] [[package]]
name = "compact_str" name = "compact_str"
version = "0.9.0" version = "0.9.0"
@@ -1878,6 +1867,21 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
version = "1.2.2" version = "1.2.2"
@@ -2293,21 +2297,6 @@ dependencies = [
"want", "want",
] ]
[[package]]
name = "hyper-rustls"
version = "0.27.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
dependencies = [
"http",
"hyper",
"hyper-util",
"rustls",
"tokio",
"tokio-rustls",
"tower-service",
]
[[package]] [[package]]
name = "hyper-timeout" name = "hyper-timeout"
version = "0.5.2" version = "0.5.2"
@@ -2321,6 +2310,22 @@ dependencies = [
"tower-service", "tower-service",
] ]
[[package]]
name = "hyper-tls"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
dependencies = [
"bytes",
"http-body-util",
"hyper",
"hyper-util",
"native-tls",
"tokio",
"tokio-native-tls",
"tower-service",
]
[[package]] [[package]]
name = "hyper-util" name = "hyper-util"
version = "0.1.20" version = "0.1.20"
@@ -2645,55 +2650,6 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "jni"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498"
dependencies = [
"cfg-if",
"combine",
"jni-macros",
"jni-sys",
"log",
"simd_cesu8",
"thiserror 2.0.18",
"walkdir",
"windows-link",
]
[[package]]
name = "jni-macros"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3"
dependencies = [
"proc-macro2",
"quote",
"rustc_version",
"simd_cesu8",
"syn 2.0.119",
]
[[package]]
name = "jni-sys"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2"
dependencies = [
"jni-sys-macros",
]
[[package]]
name = "jni-sys-macros"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
dependencies = [
"quote",
"syn 2.0.119",
]
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.100" version = "0.3.100"
@@ -3050,6 +3006,23 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084"
[[package]]
name = "native-tls"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2"
dependencies = [
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]] [[package]]
name = "nix" name = "nix"
version = "0.28.0" version = "0.28.0"
@@ -3306,12 +3279,59 @@ dependencies = [
"pathdiff", "pathdiff",
] ]
[[package]]
name = "openssl"
version = "0.10.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45"
dependencies = [
"bitflags 2.13.0",
"cfg-if",
"foreign-types",
"libc",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]] [[package]]
name = "openssl-probe" name = "openssl-probe"
version = "0.2.1" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
[[package]]
name = "openssl-src"
version = "300.6.1+3.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46eb8fb9fb3b61ce1c0f8a026c4c1a0714d3a9e138e7fbde78753ce2babc3846"
dependencies = [
"cc",
]
[[package]]
name = "openssl-sys"
version = "0.9.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695"
dependencies = [
"cc",
"libc",
"openssl-src",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "option-ext" name = "option-ext"
version = "0.2.0" version = "0.2.0"
@@ -4171,20 +4191,19 @@ dependencies = [
"http-body", "http-body",
"http-body-util", "http-body-util",
"hyper", "hyper",
"hyper-rustls", "hyper-tls",
"hyper-util", "hyper-util",
"js-sys", "js-sys",
"log", "log",
"native-tls",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"rustls",
"rustls-pki-types", "rustls-pki-types",
"rustls-platform-verifier",
"serde", "serde",
"serde_json", "serde_json",
"sync_wrapper", "sync_wrapper",
"tokio", "tokio",
"tokio-rustls", "tokio-native-tls",
"tokio-util", "tokio-util",
"tower", "tower",
"tower-http", "tower-http",
@@ -4326,32 +4345,6 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "rustls"
version = "0.23.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138"
dependencies = [
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-native-certs"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d"
dependencies = [
"openssl-probe",
"rustls-pki-types",
"schannel",
"security-framework",
]
[[package]] [[package]]
name = "rustls-pki-types" name = "rustls-pki-types"
version = "1.14.1" version = "1.14.1"
@@ -4361,44 +4354,6 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "rustls-platform-verifier"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0"
dependencies = [
"core-foundation",
"core-foundation-sys",
"jni",
"log",
"once_cell",
"rustls",
"rustls-native-certs",
"rustls-platform-verifier-android",
"rustls-webpki",
"security-framework",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.61.2",
]
[[package]]
name = "rustls-platform-verifier-android"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
[[package]]
name = "rustls-webpki"
version = "0.103.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.22" version = "1.0.22"
@@ -4826,22 +4781,6 @@ dependencies = [
"rand_core 0.6.4", "rand_core 0.6.4",
] ]
[[package]]
name = "simd_cesu8"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33"
dependencies = [
"rustc_version",
"simdutf8",
]
[[package]]
name = "simdutf8"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]] [[package]]
name = "siphasher" name = "siphasher"
version = "1.0.3" version = "1.0.3"
@@ -4949,8 +4888,8 @@ dependencies = [
"indexmap 2.14.0", "indexmap 2.14.0",
"log", "log",
"memchr", "memchr",
"native-tls",
"percent-encoding", "percent-encoding",
"rustls",
"serde", "serde",
"serde_json", "serde_json",
"sha2 0.10.9", "sha2 0.10.9",
@@ -4962,7 +4901,6 @@ dependencies = [
"tracing", "tracing",
"url", "url",
"uuid", "uuid",
"webpki-roots",
] ]
[[package]] [[package]]
@@ -5506,12 +5444,12 @@ dependencies = [
] ]
[[package]] [[package]]
name = "tokio-rustls" name = "tokio-native-tls"
version = "0.26.4" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
dependencies = [ dependencies = [
"rustls", "native-tls",
"tokio", "tokio",
] ]
@@ -6324,24 +6262,6 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "webpki-root-certs"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "webpki-roots"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d"
dependencies = [
"rustls-pki-types",
]
[[package]] [[package]]
name = "wezterm-bidi" name = "wezterm-bidi"
version = "0.2.3" version = "0.2.3"
+1 -6
View File
@@ -71,11 +71,6 @@ ratatui = "0.30.0"
sql-builder = "3" sql-builder = "3"
tempfile = { version = "3.19" } tempfile = { version = "3.19" }
minijinja = "2.9.0" minijinja = "2.9.0"
rustls = { version = "0.23", default-features = false, features = [
"ring",
"std",
"tls12",
] }
glob-match = "0.2.1" glob-match = "0.2.1"
imara-diff = "0.2" imara-diff = "0.2"
xxhash-rust = { version = "0.8", features = ["xxh3"] } xxhash-rust = { version = "0.8", features = ["xxh3"] }
@@ -90,7 +85,7 @@ features = ["ansi", "fmt", "registry", "env-filter", "json"]
[workspace.dependencies.reqwest] [workspace.dependencies.reqwest]
version = "0.13" version = "0.13"
features = ["json", "rustls-no-provider", "stream"] features = ["json", "native-tls", "stream"]
default-features = false default-features = false
[workspace.dependencies.sqlx] [workspace.dependencies.sqlx]
+12
View File
@@ -0,0 +1,12 @@
[target.x86_64-unknown-illumos]
pre-build = [
# perl+make build the vendored (target) OpenSSL. pkg-config+libssl-dev satisfy the
# HOST openssl-sys that the sqlx-macros proc-macro links: resolver v2 resolves the
# proc-macro graph separately, so `vendored-tls` never reaches it and it needs a
# real system OpenSSL on the build host.
"apt-get update && apt-get install --assume-yes --no-install-recommends perl make pkg-config libssl-dev",
# openssl-src builds vendored OpenSSL with the solaris64 target, whose Makefile
# calls `granlib` (the Solaris name for GNU ranlib). The cross image ships the
# toolchain's ranlib as x86_64-unknown-illumos-ranlib but no `granlib`, so provide it.
"ln -sf \"$(command -v x86_64-unknown-illumos-ranlib)\" /usr/local/bin/granlib",
]
+2 -2
View File
@@ -8,7 +8,7 @@ RUN cargo chef prepare --recipe-path recipe.json
FROM chef AS builder FROM chef AS builder
# Ensure working C compile setup (not installed by default in arm64 images) # Ensure working C compile setup (not installed by default in arm64 images)
RUN apt update && apt install build-essential -y RUN apt update && apt install build-essential libssl-dev pkg-config -y
COPY --from=planner /app/recipe.json recipe.json COPY --from=planner /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json RUN cargo chef cook --release --recipe-path recipe.json
@@ -23,7 +23,7 @@ LABEL org.opencontainers.image.source="https://github.com/atuinsh/atuin" \
RUN useradd -c 'atuin user' atuin && mkdir /config && chown atuin:atuin /config RUN useradd -c 'atuin user' atuin && mkdir /config && chown atuin:atuin /config
# ca-certificates for webhooks to work, curl for the healthcheck # ca-certificates for webhooks to work, curl for the healthcheck
RUN apt update && apt install --no-install-recommends ca-certificates curl -y && rm -rf /var/lib/apt/lists/* RUN apt update && apt install --no-install-recommends ca-certificates curl libssl3 -y && rm -rf /var/lib/apt/lists/*
WORKDIR app WORKDIR app
USER atuin USER atuin
+16 -2
View File
@@ -10,6 +10,8 @@
installShellFiles, installShellFiles,
rustPlatform, rustPlatform,
libiconv, libiconv,
pkg-config,
openssl,
}: }:
rustPlatform.buildRustPackage { rustPlatform.buildRustPackage {
name = "atuin"; name = "atuin";
@@ -22,9 +24,21 @@ rustPlatform.buildRustPackage {
allowBuiltinFetchGit = true; allowBuiltinFetchGit = true;
}; };
nativeBuildInputs = [ installShellFiles ]; nativeBuildInputs = [
installShellFiles
pkg-config
];
buildInputs = lib.optionals stdenv.isDarwin [ libiconv ]; buildInputs = [ openssl ] ++ lib.optionals stdenv.isDarwin [ libiconv ];
OPENSSL_NO_VENDOR = 1;
# native-tls pulls OpenSSL into the sqlx-macros proc-macro, which rustc dlopens at
# build time. With OPENSSL_NO_VENDOR it links libssl.so.3 dynamically, so the loader
# needs it on LD_LIBRARY_PATH while the proc-macro is loaded.
preBuild = ''
export LD_LIBRARY_PATH="${lib.makeLibraryPath [ openssl ]}''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
'';
postInstall = '' postInstall = ''
installShellCompletion --cmd atuin \ installShellCompletion --cmd atuin \
-1
View File
@@ -28,7 +28,6 @@ pub(crate) struct ModelList {
/// Fetch the models available to this user. Sent authenticated because the /// Fetch the models available to this user. Sent authenticated because the
/// server includes feature-flag-gated models only for entitled users. /// server includes feature-flag-gated models only for entitled users.
pub(crate) async fn fetch_models(endpoint: &reqwest::Url, token: &str) -> Result<ModelList> { pub(crate) async fn fetch_models(endpoint: &reqwest::Url, token: &str) -> Result<ModelList> {
atuin_common::tls::ensure_crypto_provider();
let url = endpoint.append_path("api/cli/models")?; let url = endpoint.append_path("api/cli/models")?;
let mut request = reqwest::Client::new() let mut request = reqwest::Client::new()
-2
View File
@@ -6,7 +6,6 @@ use atuin_client::history::History;
use atuin_client::settings::AiCapabilities; use atuin_client::settings::AiCapabilities;
use crate::context::history_output_capability_available; use crate::context::history_output_capability_available;
use atuin_common::tls::ensure_crypto_provider;
use atuin_common::url::UrlAppendExt; use atuin_common::url::UrlAppendExt;
use eventsource_stream::Eventsource; use eventsource_stream::Eventsource;
@@ -130,7 +129,6 @@ pub(crate) fn create_chat_stream(
skill_overflow: Option<String>, skill_overflow: Option<String>,
) -> std::pin::Pin<Box<dyn futures::Stream<Item = Result<StreamFrame>> + Send>> { ) -> std::pin::Pin<Box<dyn futures::Stream<Item = Result<StreamFrame>> + Send>> {
Box::pin(async_stream::stream! { Box::pin(async_stream::stream! {
ensure_crypto_provider();
let endpoint = match hub_address.append_path("api/cli/chat") { let endpoint = match hub_address.append_path("api/cli/chat") {
Ok(url) => url, Ok(url) => url,
Err(e) => { Err(e) => {
-1
View File
@@ -90,7 +90,6 @@ pub(crate) fn cache_key(token: &str) -> String {
/// Fetch current usage from the hub. Mirrors the `credits` object on the /// Fetch current usage from the hub. Mirrors the `credits` object on the
/// chat `done` event, for refreshing without starting a chat. /// chat `done` event, for refreshing without starting a chat.
pub(crate) async fn fetch_usage(endpoint: &reqwest::Url, token: &str) -> Result<UsageSnapshot> { pub(crate) async fn fetch_usage(endpoint: &reqwest::Url, token: &str) -> Result<UsageSnapshot> {
atuin_common::tls::ensure_crypto_provider();
let url = endpoint.append_path("api/cli/usage")?; let url = endpoint.append_path("api/cli/usage")?;
let response = reqwest::Client::new() let response = reqwest::Client::new()
+3
View File
@@ -19,6 +19,9 @@ sync = ["reqwest"]
hub = ["reqwest"] hub = ["reqwest"]
daemon = [] daemon = []
check-update = [] check-update = []
# Statically link (vendor) OpenSSL for native-tls instead of using the system
# libssl. Used for portable release artifacts; a no-op on macOS/Windows.
vendored-tls = ["reqwest?/native-tls-vendored"]
[dependencies] [dependencies]
derive_more = { workspace = true } derive_more = { workspace = true }
+1 -7
View File
@@ -8,7 +8,7 @@ use reqwest::{
header::{AUTHORIZATION, HeaderMap, HeaderName, HeaderValue, USER_AGENT}, header::{AUTHORIZATION, HeaderMap, HeaderName, HeaderValue, USER_AGENT},
}; };
use atuin_common::{tls::ensure_crypto_provider, url::UrlAppendExt}; use atuin_common::url::UrlAppendExt;
use atuin_domain::api::{ use atuin_domain::api::{
ATUIN_CARGO_VERSION, ATUIN_HEADER_VERSION, ATUIN_VERSION, ChangePasswordRequest, ErrorResponse, ATUIN_CARGO_VERSION, ATUIN_HEADER_VERSION, ATUIN_VERSION, ChangePasswordRequest, ErrorResponse,
LoginRequest, LoginResponse, MeResponse, RegisterResponse, LoginRequest, LoginResponse, MeResponse, RegisterResponse,
@@ -105,7 +105,6 @@ pub async fn register(
password: &str, password: &str,
extra_headers: &HashMap<String, String>, extra_headers: &HashMap<String, String>,
) -> Result<RegisterResponse> { ) -> Result<RegisterResponse> {
ensure_crypto_provider();
let mut map = HashMap::new(); let mut map = HashMap::new();
map.insert("username", username); map.insert("username", username);
map.insert("email", email); map.insert("email", email);
@@ -141,7 +140,6 @@ pub async fn login(
req: LoginRequest, req: LoginRequest,
extra_headers: &HashMap<String, String>, extra_headers: &HashMap<String, String>,
) -> Result<LoginResponse> { ) -> Result<LoginResponse> {
ensure_crypto_provider();
let url = address.append(["login"])?; let url = address.append(["login"])?;
let client = client_builder(extra_headers).build()?; let client = client_builder(extra_headers).build()?;
@@ -163,7 +161,6 @@ pub async fn login(
pub async fn latest_version() -> Result<Version> { pub async fn latest_version() -> Result<Version> {
use atuin_domain::api::IndexResponse; use atuin_domain::api::IndexResponse;
ensure_crypto_provider();
let url = crate::settings::DEFAULT_SYNC_URL.clone(); let url = crate::settings::DEFAULT_SYNC_URL.clone();
let client = reqwest::Client::new(); let client = reqwest::Client::new();
@@ -251,7 +248,6 @@ impl<'a> Client<'a> {
timeout: u64, timeout: u64,
extra_headers: &HashMap<String, String>, extra_headers: &HashMap<String, String>,
) -> Result<Self> { ) -> Result<Self> {
ensure_crypto_provider();
let mut headers = extra_headers_map(extra_headers)?; let mut headers = extra_headers_map(extra_headers)?;
headers.insert(AUTHORIZATION, auth.to_header_value().parse()?); headers.insert(AUTHORIZATION, auth.to_header_value().parse()?);
headers.insert(USER_AGENT, APP_USER_AGENT.parse()?); headers.insert(USER_AGENT, APP_USER_AGENT.parse()?);
@@ -452,7 +448,6 @@ mod tests {
.await; .await;
}); });
ensure_crypto_provider();
let client = client_builder(&extra_headers).build().unwrap(); let client = client_builder(&extra_headers).build().unwrap();
let err = client let err = client
.get(format!("http://127.0.0.1:{port}/")) .get(format!("http://127.0.0.1:{port}/"))
@@ -489,7 +484,6 @@ mod tests {
.await; .await;
}); });
ensure_crypto_provider();
let client = client_builder(&extra_headers).build().unwrap(); let client = client_builder(&extra_headers).build().unwrap();
let resp = client let resp = client
.get(format!("http://127.0.0.1:{port}/")) .get(format!("http://127.0.0.1:{port}/"))
+1 -6
View File
@@ -5,7 +5,7 @@ use eyre::{Context, Result, bail};
use reqwest::{StatusCode, Url, header::USER_AGENT}; use reqwest::{StatusCode, Url, header::USER_AGENT};
use serde::Deserialize; use serde::Deserialize;
use atuin_common::{tls::ensure_crypto_provider, url::UrlAppendExt}; use atuin_common::url::UrlAppendExt;
use atuin_domain::api::{ use atuin_domain::api::{
ATUIN_CARGO_VERSION, ATUIN_HEADER_VERSION, ChangePasswordRequest, LoginRequest, LoginResponse, ATUIN_CARGO_VERSION, ATUIN_HEADER_VERSION, ChangePasswordRequest, LoginRequest, LoginResponse,
RegisterResponse, RegisterResponse,
@@ -126,7 +126,6 @@ impl LegacyAuthClient {
.as_deref() .as_deref()
.ok_or_else(|| eyre::eyre!("Not logged in"))?; .ok_or_else(|| eyre::eyre!("Not logged in"))?;
ensure_crypto_provider();
let mut headers = crate::api_client::extra_headers_map(&self.extra_headers)?; let mut headers = crate::api_client::extra_headers_map(&self.extra_headers)?;
headers.insert( headers.insert(
reqwest::header::AUTHORIZATION, reqwest::header::AUTHORIZATION,
@@ -278,7 +277,6 @@ impl AuthClient for HubAuthClient {
password: &str, password: &str,
totp_code: Option<&str>, totp_code: Option<&str>,
) -> Result<AuthResponse> { ) -> Result<AuthResponse> {
ensure_crypto_provider();
let url = self.address.append_path("api/v0/login")?; let url = self.address.append_path("api/v0/login")?;
let client = reqwest::Client::new(); let client = reqwest::Client::new();
@@ -326,7 +324,6 @@ impl AuthClient for HubAuthClient {
} }
async fn register(&self, username: &str, email: &str, password: &str) -> Result<AuthResponse> { async fn register(&self, username: &str, email: &str, password: &str) -> Result<AuthResponse> {
ensure_crypto_provider();
let url = self.address.append_path("api/v0/register")?; let url = self.address.append_path("api/v0/register")?;
let client = reqwest::Client::new(); let client = reqwest::Client::new();
@@ -380,7 +377,6 @@ impl AuthClient for HubAuthClient {
); );
} }
ensure_crypto_provider();
let url = self.address.append_path("api/v0/account/password")?; let url = self.address.append_path("api/v0/account/password")?;
let client = reqwest::Client::new(); let client = reqwest::Client::new();
@@ -452,7 +448,6 @@ impl AuthClient for HubAuthClient {
); );
} }
ensure_crypto_provider();
let url = self.address.append_path("api/v0/account")?; let url = self.address.append_path("api/v0/account")?;
let client = reqwest::Client::new(); let client = reqwest::Client::new();
+1 -4
View File
@@ -13,7 +13,7 @@ use std::time::Duration;
use eyre::{Context, Result, bail}; use eyre::{Context, Result, bail};
use reqwest::{StatusCode, Url, header::USER_AGENT}; use reqwest::{StatusCode, Url, header::USER_AGENT};
use atuin_common::{tls::ensure_crypto_provider, url::UrlAppendExt}; use atuin_common::url::UrlAppendExt;
use atuin_domain::api::{ use atuin_domain::api::{
ATUIN_CARGO_VERSION, ATUIN_HEADER_VERSION, CliCodeResponse, CliVerifyResponse, ErrorResponse, ATUIN_CARGO_VERSION, ATUIN_HEADER_VERSION, CliCodeResponse, CliVerifyResponse, ErrorResponse,
}; };
@@ -191,7 +191,6 @@ pub async fn link_account(hub_address: &Url, cli_token: &str) -> Result<()> {
debug!("Linking CLI account to Hub at {}", hub_address); debug!("Linking CLI account to Hub at {}", hub_address);
ensure_crypto_provider();
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let resp = client let resp = client
@@ -246,7 +245,6 @@ async fn handle_resp_error(resp: reqwest::Response) -> Result<reqwest::Response>
/// Request a CLI auth code from the Atuin Hub /// Request a CLI auth code from the Atuin Hub
async fn request_code(address: &Url) -> Result<CliCodeResponse> { async fn request_code(address: &Url) -> Result<CliCodeResponse> {
ensure_crypto_provider();
let url = address.append_path("auth/cli/code")?; let url = address.append_path("auth/cli/code")?;
let client = reqwest::Client::new(); let client = reqwest::Client::new();
@@ -266,7 +264,6 @@ async fn request_code(address: &Url) -> Result<CliCodeResponse> {
/// Poll to verify the CLI auth code and get the session token /// Poll to verify the CLI auth code and get the session token
async fn verify_code(address: &Url, code: &str) -> Result<CliVerifyResponse> { async fn verify_code(address: &Url, code: &str) -> Result<CliVerifyResponse> {
ensure_crypto_provider();
let mut url = address.append_path("auth/cli/verify")?; let mut url = address.append_path("auth/cli/verify")?;
let client = reqwest::Client::new(); let client = reqwest::Client::new();
-1
View File
@@ -36,7 +36,6 @@ directories = { workspace = true }
sysinfo = "0.30.7" sysinfo = "0.30.7"
base64 = { workspace = true } base64 = { workspace = true }
getrandom = "0.4" getrandom = "0.4"
rustls = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
tracing-subscriber = { workspace = true, optional = true } tracing-subscriber = { workspace = true, optional = true }
unicode-width = { version = "0.2", optional = true } unicode-width = { version = "0.2", optional = true }
-1
View File
@@ -12,6 +12,5 @@ pub mod string;
#[cfg(feature = "test-utils")] #[cfg(feature = "test-utils")]
pub mod test_utils; pub mod test_utils;
pub mod time; pub mod time;
pub mod tls;
pub mod url; pub mod url;
pub mod utils; pub mod utils;
-15
View File
@@ -1,15 +0,0 @@
use std::sync::Once;
static INIT: Once = Once::new();
/// Ensure the rustls crypto provider (ring) is installed.
///
/// Must be called before creating any reqwest clients. Safe to call
/// multiple times — only the first call installs the provider.
pub fn ensure_crypto_provider() {
INIT.call_once(|| {
rustls::crypto::ring::default_provider()
.install_default()
.expect("Failed to install rustls crypto provider");
});
}
+1 -1
View File
@@ -19,7 +19,7 @@ eyre = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
time = { workspace = true } time = { workspace = true }
serde = { workspace = true } serde = { workspace = true }
sqlx = { workspace = true, features = ["postgres", "tls-rustls"] } sqlx = { workspace = true, features = ["postgres", "tls-native-tls"] }
async-trait = { workspace = true } async-trait = { workspace = true }
uuid = { workspace = true } uuid = { workspace = true }
metrics = "0.24" metrics = "0.24"
+5
View File
@@ -18,6 +18,11 @@ path = "src/lib.rs"
name = "atuin-server" name = "atuin-server"
path = "src/bin/main.rs" path = "src/bin/main.rs"
[features]
# Statically link (vendor) OpenSSL for native-tls instead of using the system
# libssl. Used for portable release artifacts; a no-op on macOS/Windows.
vendored-tls = ["reqwest/native-tls-vendored"]
[dependencies] [dependencies]
atuin-common = { workspace = true } atuin-common = { workspace = true }
atuin-domain = { workspace = true } atuin-domain = { workspace = true }
-3
View File
@@ -16,8 +16,6 @@ use metrics::counter;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use tracing::{debug, error, info, instrument}; use tracing::{debug, error, info, instrument};
use atuin_common::tls::ensure_crypto_provider;
use super::{ErrorResponse, ErrorResponseStatus, RespExt}; use super::{ErrorResponse, ErrorResponseStatus, RespExt};
use crate::router::{AppState, UserAuth}; use crate::router::{AppState, UserAuth};
use atuin_server_database::{ use atuin_server_database::{
@@ -41,7 +39,6 @@ pub fn verify_str(hash: &str, password: &str) -> bool {
// Try to send a Discord webhook once - if it fails, we don't retry. "At most once", and best effort. // Try to send a Discord webhook once - if it fails, we don't retry. "At most once", and best effort.
// Don't return the status because if this fails, we don't really care. // Don't return the status because if this fails, we don't really care.
async fn send_register_hook(url: &url::Url, username: String, registered: String) { async fn send_register_hook(url: &url::Url, username: String, registered: String) {
ensure_crypto_provider();
let hook = HashMap::from([ let hook = HashMap::from([
("username", username), ("username", username),
("content", format!("{registered} has just signed up!")), ("content", format!("{registered} has just signed up!")),
+1
View File
@@ -49,6 +49,7 @@ pty-proxy = ["dep:atuin-pty-proxy"]
hex = ["pty-proxy"] hex = ["pty-proxy"]
clipboard = ["arboard"] clipboard = ["arboard"]
check-update = ["atuin-client/check-update"] check-update = ["atuin-client/check-update"]
vendored-tls = ["atuin-client?/vendored-tls"]
[dependencies] [dependencies]
atuin-ai = { path = "../atuin-ai", version = "18.19.0-beta.2", optional = true, default-features = false } atuin-ai = { path = "../atuin-ai", version = "18.19.0-beta.2", optional = true, default-features = false }
+1 -1
View File
@@ -21,7 +21,7 @@ tokio = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
time = { workspace = true } time = { workspace = true }
serde = { workspace = true } serde = { workspace = true }
sqlx = { workspace = true, features = ["sqlite", "regexp", "postgres", "tls-rustls"] } sqlx = { workspace = true, features = ["sqlite", "regexp", "postgres", "tls-native-tls"] }
async-trait = { workspace = true } async-trait = { workspace = true }
uuid = { workspace = true } uuid = { workspace = true }
metrics = "0.24" metrics = "0.24"
+3
View File
@@ -21,6 +21,9 @@ install-updater = true
unix-archive = ".tar.gz" unix-archive = ".tar.gz"
# Whether to enable GitHub Attestations # Whether to enable GitHub Attestations
github-attestations = true github-attestations = true
# Vendor (statically link) OpenSSL so the downloadable binaries are portable and
# don't depend on the host's libssl version. No-op on macOS/Windows.
features = ["vendored-tls"]
[dist.github-custom-runners] [dist.github-custom-runners]
aarch64-unknown-linux-gnu = "depot-ubuntu-24.04-arm-8" aarch64-unknown-linux-gnu = "depot-ubuntu-24.04-arm-8"
+1 -1
View File
@@ -40,7 +40,7 @@
default = atuin; default = atuin;
}; };
formatter = pkgs.nixfmt; formatter = pkgs.nixfmt-tree;
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
inputsFrom = [ atuin ]; inputsFrom = [ atuin ];