Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
265 changes: 110 additions & 155 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ md4 = "0.10"
parse_link_header = "0.4"
paste = "1.0.15"
pgp = "0.15"
prost = "0.13"
pulldown-cmark = "0.13"
# match version used by sqlx
rand = "0.8"
Expand Down Expand Up @@ -89,8 +88,8 @@ tokio = { version = "1", features = [
] }
tokio-stream = "0.1"
tokio-util = "0.7"
tonic = { version = "0.12", features = ["gzip", "tls-native-roots"] }
tonic-health = "0.12"
tonic = { version = "0.14", features = ["gzip", "tls-native-roots"] }
tonic-health = "0.14"
totp-lite = { version = "2.0" }
tower-http = { version = "0.6", features = ["fs", "trace"] }
tracing = "0.1"
Expand Down
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM node:24-alpine AS web

WORKDIR /app
COPY web/package.json web/pnpm-lock.yaml web/.npmrc .
COPY web/package.json web/pnpm-lock.yaml web/.npmrc ./
RUN npm i -g pnpm
RUN pnpm install --ignore-scripts --frozen-lockfile
COPY web/ .
Expand Down Expand Up @@ -45,8 +45,8 @@ RUN cargo install --locked --bin defguard --path ./crates/defguard --root /build
# run
FROM debian:bookworm-slim
RUN apt-get update -y && \
apt-get install --no-install-recommends -y ca-certificates libssl-dev && \
rm -rf /var/lib/apt/lists/*
apt-get install --no-install-recommends -y ca-certificates libssl-dev && \
rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /build/bin/defguard .
ENTRYPOINT ["./defguard"]
2 changes: 1 addition & 1 deletion crates/defguard/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ defguard_event_logger = { workspace = true }

# external dependencies
anyhow = { workspace = true }
bytes = { workspace = true }
dotenvy = "0.15"
secrecy = { workspace = true }
tokio = { workspace = true }
tracing = { workspace = true }
bytes = { workspace = true }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
6 changes: 3 additions & 3 deletions crates/defguard_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ openidconnect = { version = "4.0", default-features = false, optional = true, fe
parse_link_header = { workspace = true }
paste = { workspace = true }
pgp = { workspace = true }
prost = { workspace = true }
prost = "0.14"
pulldown-cmark = { workspace = true }
# match version used by sqlx
rand = { workspace = true }
Expand Down Expand Up @@ -66,6 +66,7 @@ tokio-stream = { workspace = true }
tokio-util = { workspace = true }
tonic = { workspace = true }
tonic-health = { workspace = true }
tonic-prost = "0.14"
totp-lite = { workspace = true }
tower-http = { workspace = true }
tracing = { workspace = true }
Expand Down Expand Up @@ -105,8 +106,7 @@ serde_qs = "0.13"
webauthn-authenticator-rs = { version = "0.5", features = ["softpasskey"] }

[build-dependencies]
prost-build = "0.13"
tonic-build = "0.12"
tonic-prost-build = "0.14"
vergen-git2 = { version = "1.0", features = ["build"] }

[features]
Expand Down
47 changes: 23 additions & 24 deletions crates/defguard_core/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,29 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let git2 = Git2Builder::default().branch(true).sha(true).build()?;
Emitter::default().add_instructions(&git2)?.emit()?;

let mut config = prost_build::Config::new();
config.protoc_arg("--experimental_allow_proto3_optional");
config.type_attribute(
"license.LicenseLimits",
"#[derive(serde::Serialize, serde::Deserialize)]",
);
tonic_build::configure().compile_protos_with_config(
config,
&[
"../../proto/core/auth.proto",
"../../proto/core/proxy.proto",
"../../proto/worker/worker.proto",
"../../proto/wireguard/gateway.proto",
"../../proto/enterprise/firewall/firewall.proto",
"src/enterprise/proto/license.proto",
],
&[
"../../proto/core",
"../../proto/worker",
"../../proto/wireguard",
"../../proto/enterprise/firewall",
"src/enterprise/proto",
],
)?;
tonic_prost_build::configure()
.protoc_arg("--experimental_allow_proto3_optional")
.type_attribute(
"license.LicenseLimits",
"#[derive(serde::Serialize, serde::Deserialize)]",
)
.compile_protos(
&[
"../../proto/core/auth.proto",
"../../proto/core/proxy.proto",
"../../proto/worker/worker.proto",
"../../proto/wireguard/gateway.proto",
"../../proto/enterprise/firewall/firewall.proto",
"src/enterprise/proto/license.proto",
],
&[
"../../proto/core",
"../../proto/worker",
"../../proto/wireguard",
"../../proto/enterprise/firewall",
"src/enterprise/proto",
],
)?;
println!("cargo:rerun-if-changed=../../migrations");
println!("cargo:rerun-if-changed=../../proto");
println!("cargo:rerun-if-changed=src/enterprise");
Expand Down
15 changes: 7 additions & 8 deletions crates/defguard_core/src/db/models/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -893,14 +893,13 @@ impl User<Id> {
username_or_email: &str,
) -> Result<Option<Self>, SqlxError> {
let maybe_user = Self::find_by_username(&mut *conn, username_or_email).await?;
match maybe_user {
Some(user) => Ok(Some(user)),
None => {
debug!(
"Failed to find user by username {username_or_email}. Attempting to find by email"
);
Ok(Self::find_by_email(&mut *conn, username_or_email).await?)
}
if let Some(user) = maybe_user {
Ok(Some(user))
} else {
debug!(
"Failed to find user by username {username_or_email}. Attempting to find by email"
);
Ok(Self::find_by_email(&mut *conn, username_or_email).await?)
}
}
pub(crate) async fn find_many_by_emails<'e, E>(
Expand Down
1 change: 1 addition & 0 deletions crates/defguard_core/src/db/models/wireguard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ pub enum NetworkAddressError {
}

impl WireguardNetwork {
#[must_use]
pub fn new(
name: String,
address: Vec<IpNetwork>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub struct ApiToken<I = NoId> {
}

impl ApiToken {
#[must_use]
pub fn new(user_id: Id, created_at: NaiveDateTime, name: String, token_string: &str) -> Self {
let token_hash = Self::hash_token(token_string);
Self {
Expand Down
1 change: 1 addition & 0 deletions crates/defguard_core/src/enterprise/db/models/snat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub struct UserSnatBinding<I = NoId> {
}

impl UserSnatBinding {
#[must_use]
pub fn new(user_id: Id, location_id: Id, public_ip: IpAddr) -> Self {
Self {
id: NoId,
Expand Down
59 changes: 30 additions & 29 deletions crates/defguard_core/src/enterprise/directory_sync/microsoft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,10 @@ impl From<GroupsResponse> for Vec<DirectoryGroup> {
response
.value
.into_iter()
.filter_map(|group| match group.display_name {
Some(name) => Some(DirectoryGroup { id: group.id, name }),
None => {
.filter_map(|group| {
if let Some(name) = group.display_name {
Some(DirectoryGroup { id: group.id, name })
} else {
warn!(
"Group with ID {} doesn't have a display name set, skipping it.",
group.id
Expand Down Expand Up @@ -177,7 +178,7 @@ impl MicrosoftDirectorySync {
self.url
)))?;
debug!("Tenant ID extracted successfully: {tenant_id}",);
Ok(tenant_id.to_string())
Ok((*tenant_id).to_string())
}

async fn refresh_access_token(&mut self) -> Result<(), DirectorySyncError> {
Expand Down Expand Up @@ -252,7 +253,30 @@ impl MicrosoftDirectorySync {
let mut combined_response = GroupsResponse::default();
let mut url = GROUPS_URL.to_string();

if !self.group_filter.is_empty() {
if self.group_filter.is_empty() {
debug!("No group filter defined, all groups will be synced.");
let params = vec![("$top", MAX_RESULTS)];
let mut query = Some(params.as_slice());

for _ in 0..MAX_REQUESTS {
let response = make_get_request(&url, access_token, query).await?;
let response: GroupsResponse =
parse_response(response, "Failed to query Microsoft groups.").await?;
combined_response.value.extend(response.value);

if let Some(next_page) = response.next_page {
url = next_page;
// Set `query` to `None` as the next page URL already contains query parameters from the preceding request.
query = None;
debug!("Found next page of results, querying it: {url}");
} else {
debug!("No more pages of results found, finishing query.");
break;
}

sleep(REQUEST_PAGINATION_SLOWDOWN).await;
}
} else {
info!(
"Applying defined group filter to user group query, only the following groups will be synced: {:?}",
self.group_filter
Expand All @@ -261,7 +285,7 @@ impl MicrosoftDirectorySync {
let groups = self
.group_filter
.iter()
.map(|group| group.replace("'", "''"))
.map(|group| group.replace('\'', "''"))
.collect::<Vec<_>>();

// Microsoft has a limit of about 15 OR conditions per request, so batch it first.
Expand All @@ -284,29 +308,6 @@ impl MicrosoftDirectorySync {
parse_response(response, "Failed to query Microsoft groups.").await?;
combined_response.value.extend(response.value);

sleep(REQUEST_PAGINATION_SLOWDOWN).await;
}
} else {
debug!("No group filter defined, all groups will be synced.");
let params = vec![("$top", MAX_RESULTS)];
let mut query = Some(params.as_slice());

for _ in 0..MAX_REQUESTS {
let response = make_get_request(&url, access_token, query).await?;
let response: GroupsResponse =
parse_response(response, "Failed to query Microsoft groups.").await?;
combined_response.value.extend(response.value);

if let Some(next_page) = response.next_page {
url = next_page;
// Set `query` to `None` as the next page URL already contains query parameters from the preceding request.
query = None;
debug!("Found next page of results, querying it: {url}");
} else {
debug!("No more pages of results found, finishing query.");
break;
}

sleep(REQUEST_PAGINATION_SLOWDOWN).await;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ impl ClientMfaServer {
})?;
return Err(Status::unauthenticated("unauthorized"));
}
};
}

self.sessions.insert(
pubkey.clone(),
Expand Down
2 changes: 1 addition & 1 deletion crates/defguard_core/src/enterprise/handlers/api_tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ pub async fn fetch_api_tokens(
let tokens_info: Vec<ApiTokenInfo> = ApiToken::find_by_user_id(&appstate.pool, user.id)
.await?
.into_iter()
.map(|token| token.into())
.map(Into::into)
.collect();

Ok(ApiResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ use crate::{
/// - starts with non-special character
/// - only special characters allowed: . - _
/// - no whitespaces
#[must_use]
pub fn prune_username(username: &str, handling: OpenidUsernameHandling) -> String {
let mut result = username.to_string();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ pub async fn delete_openid_provider(
let locations = WireguardNetwork::all_using_external_mfa(&mut *transaction).await?;
if locations.is_empty() {
debug!("No locations are using OIDC provider for external MFA");
};
}
// fall back to internal MFA in all relevant locations
for mut location in locations {
debug!(
Expand Down
12 changes: 6 additions & 6 deletions crates/defguard_core/src/enterprise/ldap/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ impl super::LDAPConnection {
.map(|u| (self.config.user_dn_from_user(u).to_lowercase(), u))
.collect::<HashMap<_, _>>();

for entry in membership_entries.iter_mut() {
for entry in &mut membership_entries {
let groupname = entry
.attrs
.remove(&self.config.ldap_groupname_attr)
Expand Down Expand Up @@ -335,13 +335,16 @@ impl super::LDAPConnection {
}

pub(super) async fn list_users(&mut self) -> Result<Vec<SearchEntry>, LdapError> {
let filter = if !self.config.ldap_sync_groups.is_empty() {
let filter = if self.config.ldap_sync_groups.is_empty() {
debug!("No LDAP sync groups defined, searching for all users in the base DN");
format!("(objectClass={})", self.config.ldap_user_obj_class)
} else {
debug!(
"LDAP sync groups are defined, filtering users by those groups: {:?}",
self.config.ldap_sync_groups
);
let mut group_filters = vec![];
for group in self.config.ldap_sync_groups.iter() {
for group in &self.config.ldap_sync_groups {
let group_dn = self.config.group_dn(group);
let group_dn_escaped = ldap_escape(&group_dn);
group_filters.push(format!(
Expand All @@ -358,9 +361,6 @@ impl super::LDAPConnection {
self.config.ldap_user_obj_class,
group_filters.join("")
)
} else {
debug!("No LDAP sync groups defined, searching for all users in the base DN");
format!("(objectClass={})", self.config.ldap_user_obj_class)
};

debug!(
Expand Down
5 changes: 1 addition & 4 deletions crates/defguard_core/src/enterprise/ldap/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,7 @@ pub fn nthash(password: &str) -> String {
#[must_use]
pub fn unicode_pwd(password: &str) -> Vec<u8> {
let quoted = format!("\"{password}\"");
let utf16_bytes: Vec<u8> = quoted
.encode_utf16()
.flat_map(|c| c.to_le_bytes())
.collect();
let utf16_bytes: Vec<u8> = quoted.encode_utf16().flat_map(u16::to_le_bytes).collect();

utf16_bytes
}
Expand Down
Loading