Skip to content

Commit

Permalink
switch to rustls, include CA cert in binary
Browse files Browse the repository at this point in the history
  • Loading branch information
fnschmidt committed Aug 7, 2024
1 parent 7bd93f5 commit 8c597aa
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 358 deletions.
393 changes: 70 additions & 323 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jsonwebtoken = "9.3.0"
serde_json = "1.0.120"
reqwest_cookie_store = { version = "0.8.0", features = ["serde"] }
scraper = "0.20.0"
reqwest = { version = "0.12.5", features = ["cookies", "json"] }
reqwest = { version = "0.12.5", features = ["cookies", "json", "rustls-tls"], default-features = false }
anyhow = "1.0.86"
cookie_store = "0.21.0"
tower-http = { version = "0.5.2", features = ["cors"] }
Expand All @@ -25,7 +25,6 @@ fnv = "1.0.7"
aes-gcm = "0.10.3"
base64 = "0.22.1"
rand = "0.8.5"
openssl = "0.10.66"

[profile.release]
strip = true
Expand Down
10 changes: 0 additions & 10 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,6 @@ COPY ./Cargo.toml .
RUN cargo build --release

FROM debian:bookworm-slim AS campus-api
RUN apt-get update && \
apt-get install -y \
libssl3 \
ca-certificates \
&& \
apt-get autoremove -y && \
apt-get clean -y && \
rm -rf /var/lib/apt/lists/*
COPY GEANT_OV_RSA_CA_4_tcs-cert3.pem /etc/ssl/certs/GEANT_OV_RSA_CA_4_tcs-cert3.pem
RUN c_rehash
COPY --from=build ./target/release/campus-api /app/campus-api
WORKDIR /app/data
EXPOSE 8080
Expand Down
File renamed without changes.
9 changes: 1 addition & 8 deletions src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,7 @@ pub async fn sign_in(
// Attempt CD login
let (cd_auth_data, user_basic_info) = match cdlogin_get_jcookie_and_meta(login_data).await {
Ok((cd_auth_data, user_basic_info)) => (cd_auth_data, user_basic_info),
Err(e) => {
if e.root_cause()
.downcast_ref::<openssl::error::ErrorStack>()
.is_some()
{
return Err(StatusCode::IM_A_TEAPOT);
}

Err(_) => {
return Err(StatusCode::UNAUTHORIZED); // CD login failed
}
};
Expand Down
6 changes: 5 additions & 1 deletion src/campus_backend/login.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ use reqwest::Client;
use reqwest_cookie_store::{CookieStore, CookieStoreMutex};
use scraper::{Html, Selector};

use crate::types::{CampusLoginData, CdAuthData, UserBasicInfo};
use crate::{
constants::CD_CERT_PEM,
types::{CampusLoginData, CdAuthData, UserBasicInfo},
};

pub async fn cdlogin_get_jcookie_and_meta(
login_data: CampusLoginData,
) -> Result<(CdAuthData, UserBasicInfo)> {
let cookie_store = Arc::new(CookieStoreMutex::new(CookieStore::new(None)));

let client = reqwest::Client::builder()
.add_root_certificate(CD_CERT_PEM.get().unwrap().clone())
.cookie_provider(cookie_store.clone())
.build()?;

Expand Down
18 changes: 15 additions & 3 deletions src/campus_backend/req_client_funcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,22 @@ use reqwest::Url;
use reqwest_cookie_store::CookieStoreMutex;
use scraper::{selectable::Selectable, Html, Selector};

use crate::types::{
CampusDualGrade, CampusDualSignupOption, CampusDualSubGrade, CampusDualVerfahrenOption,
ExamRegistrationMetadata,
use crate::{
constants::CD_CERT_PEM,
types::{
CampusDualGrade, CampusDualSignupOption, CampusDualSubGrade, CampusDualVerfahrenOption,
ExamRegistrationMetadata,
},
};

pub fn get_client_default() -> reqwest::Client {
reqwest::ClientBuilder::new()
.add_root_certificate(CD_CERT_PEM.get().unwrap().clone())
.use_rustls_tls()
.build()
.unwrap()
}

pub fn get_client_with_cd_cookie(j_cookie: String) -> Result<reqwest::Client> {
let cookie: cookie_store::Cookie = serde_json::from_str(&j_cookie)?;
let cookie_store = Arc::new(CookieStoreMutex::new(CookieStore::new(None)));
Expand All @@ -22,6 +33,7 @@ pub fn get_client_with_cd_cookie(j_cookie: String) -> Result<reqwest::Client> {
}

Ok(reqwest::Client::builder()
.add_root_certificate(CD_CERT_PEM.get().unwrap().clone())
.cookie_provider(cookie_store)
.build()?)
}
Expand Down
3 changes: 2 additions & 1 deletion src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::sync::OnceLock;

use jsonwebtoken::{DecodingKey, EncodingKey};
use reqwest::Certificate;

// pub static JWT_SECRET: OnceLock<String> = OnceLock::new();
pub static JWT_ENC_KEY: OnceLock<EncodingKey> = OnceLock::new();
pub static JWT_DEC_KEY: OnceLock<DecodingKey> = OnceLock::new();
pub static AES_KEY: OnceLock<[u8; 32]> = OnceLock::new();
pub static CD_CERT_PEM: OnceLock<Certificate> = OnceLock::new();
6 changes: 5 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use constants::{AES_KEY, JWT_DEC_KEY, JWT_ENC_KEY};
use constants::{AES_KEY, CD_CERT_PEM, JWT_DEC_KEY, JWT_ENC_KEY};
use encryption::{get_aes_from_env, get_jwt_keys_from_env};
use tokio::net::TcpListener;

Expand All @@ -22,6 +22,10 @@ async fn main() {
.set(jwt_dec_key)
.unwrap_or_else(|_| panic!("Unable to set JWT dec key"));

let buf = include_bytes!("GEANT_OV_RSA_CA_4_tcs-cert3.pem");
let cert = reqwest::Certificate::from_pem(buf).unwrap();
CD_CERT_PEM.set(cert).unwrap();

let listener = TcpListener::bind("0.0.0.0:8080")
.await
.expect("Unable to start the server");
Expand Down
18 changes: 9 additions & 9 deletions src/services.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
auth::sign_in,
campus_backend::req_client_funcs::{
extract_exam_signup_options, extract_exam_verfahren_options, extract_grades,
get_client_with_cd_cookie,
get_client_default, get_client_with_cd_cookie,
},
color_stuff::hex_to_luminance,
types::{
Expand Down Expand Up @@ -109,7 +109,7 @@ pub async fn post_registerexam(
Extension(cd_auth_data): Extension<CdAuthData>,
Json(examregist_meta): Json<ExamRegistrationMetadata>,
) -> Result<String, ResponseError> {
let client = reqwest::Client::new();
let client = get_client_default();
let exam_regist_resp = client
.get(format!(
"https://selfservice.campus-dual.de/acwork/registerexam?userid={}&assessment={}&peryr={}&perid={}&offerno={}&hash={}",
Expand All @@ -133,7 +133,7 @@ pub async fn post_cancelexam(
Extension(cd_auth_data): Extension<CdAuthData>,
Json(examregist_meta): Json<ExamRegistrationMetadata>,
) -> Result<String, ResponseError> {
let client = reqwest::Client::new();
let client = get_client_default();
let exam_regist_resp = client
.get(format!(
"https://selfservice.campus-dual.de/acwork/cancelexam?userid={}&objid={}&hash={}",
Expand Down Expand Up @@ -168,7 +168,7 @@ pub async fn get_examverfahren(
pub async fn get_ects(
Extension(cd_authdata): Extension<CdAuthData>,
) -> Result<String, ResponseError> {
let client = reqwest::Client::new();
let client = get_client_default();

let user = cd_authdata.user;
let hash = cd_authdata.hash;
Expand All @@ -190,7 +190,7 @@ pub async fn get_ects(
pub async fn get_fachsem(
Extension(cd_authdata): Extension<CdAuthData>,
) -> Result<String, ResponseError> {
let client = reqwest::Client::new();
let client = get_client_default();

let user = cd_authdata.user;
let hash = cd_authdata.hash;
Expand Down Expand Up @@ -225,7 +225,7 @@ pub async fn get_examstats(
// daten/partitionen: ['erfolgreich', 0], ['nicht bestanden', 0], ['gebucht', 0]
// farben: ["#0070a3", "#4297d7", "#fcbe04"]

let client = reqwest::Client::new();
let client = get_client_default();

let user = cd_authdata.user;
let hash = cd_authdata.hash;
Expand All @@ -246,7 +246,7 @@ pub async fn get_examstats(
pub async fn get_stundenplan(
Extension(cd_authdata): Extension<CdAuthData>,
) -> Result<Json<Vec<StundenplanItem>>, ResponseError> {
let client = reqwest::Client::new();
let client = get_client_default();

let user = cd_authdata.user;
let hash = cd_authdata.hash;
Expand Down Expand Up @@ -300,7 +300,7 @@ fn string_to_rgb(input: &str) -> String {
pub async fn get_reminders(
Extension(cd_authdata): Extension<CdAuthData>,
) -> Result<Json<CampusReminders>, ResponseError> {
let client = reqwest::Client::new();
let client = get_client_default();

let user = cd_authdata.user;
let hash = cd_authdata.hash;
Expand All @@ -321,7 +321,7 @@ pub async fn get_reminders(
pub async fn get_timeline(
Extension(cd_authdata): Extension<CdAuthData>,
) -> Result<Json<ExportTimelineEvents>, ResponseError> {
let client = reqwest::Client::new();
let client = get_client_default();
let resp: CampusTimeline = client
.get(format!(
"https://selfservice.campus-dual.de/dash/gettimeline?user={}",
Expand Down

0 comments on commit 8c597aa

Please sign in to comment.