From 7822434b9d19a0d3b102e4a708c4e0f5df954acc Mon Sep 17 00:00:00 2001 From: Kamil Chudy Date: Tue, 2 Jun 2026 18:00:29 +0200 Subject: [PATCH 01/18] Updated README file (#3032) --- README.md | 233 +++++++---------------------- docs/cover-image_smaller-logo.png | Bin 0 -> 114241 bytes docs/new_defguard-architecture.png | Bin 0 -> 108188 bytes 3 files changed, 50 insertions(+), 183 deletions(-) create mode 100644 docs/cover-image_smaller-logo.png create mode 100644 docs/new_defguard-architecture.png diff --git a/README.md b/README.md index 2816b702b3..6ae3c448c3 100644 --- a/README.md +++ b/README.md @@ -1,220 +1,87 @@ -

- defguard -

- -
-

- Defguard is an enterprise-grade open-source VPN solution built with the highest security standards in mind. It provides the world’s only multi-factor authentication (MFA) for WireGuard VPN connections, using either its built-in SSO (with TOTP, biometrics, etc.) or external SSO providers such as Google, Microsoft, Active Directory/LDAP, Okta, JumpCloud or any other OpenID Connect Provider. -

- -[Website](https://defguard.net) | [Getting Started](https://docs.defguard.net/#what-is-defguard) | [Features](https://github.com/defguard/defguard#features) | [Roadmap](https://github.com/orgs/defguard/projects/5) | [Support ❤](https://github.com/defguard/defguard#support) -
- -### Open, transparent, verifiable and inspectable - -- Our security approach: https://defguard.net/security/ -- Our public penetration tests reports: https://defguard.net/pentesting/ -- Daily SBOM CVE scan: https://defguard.net/sbom/ -- Our detailed roadmap: https://github.com/orgs/DefGuard/projects/5 -- Our Architecture Decision Records: https://app.gitbook.com/o/Z3mGSAbEj9iLdZ7cNFlL/s/kHPDOBrb5X1TB8O3GsjW/~/changes/86/in-depth/architecture-decision-records - -### Defguard provides Comprehensive Access Control (a complete security platform): - -- **[WireGuard® VPN with 2FA/MFA](https://docs.defguard.net/in-depth/architecture/architecture)** - not 2FA to "access application" like most solutions - - The only solution with [automatic and real-time synchronization](https://docs.defguard.net/features/remote-user-enrollment/automatic-real-time-desktop-client-configuration) for users' desktop client settings (including all VPNs/locations). - - Control users [ability to manage devices and VPN options](https://docs.defguard.net/features/wireguard/behavior-customization) -- [ACLs/Firewall Management](https://docs.defguard.net/features/access-control-list) for Linux and FreeBSD/OPNSense -- [Integrated SSO based on OpenID Connect](https://docs.defguard.net/features/openid-connect): - - significant cost saving, simplifying deployment and maintenance - - enabling features unavailable to VPN platforms relying upon 3rd party SSO integration -- Already using Google/Microsoft or other OpenID Provider? - [external OpenID provider support](https://docs.defguard.net/features/external-openid-providers) -- [Two way Active Directory/LDAP synchronization](https://docs.defguard.net/features/ldap-and-active-directory-integration/two-way-ldap-and-active-directory-synchronization) -- Only solution with [secure remote user Enrollment & Onboarding](https://docs.defguard.net/using-defguard-for-end-users/enrollment) -- Yubico YubiKey Hardware [security key management and provisioning](https://docs.defguard.net/features/yubikey-provisioning) -- Secure and robust architecture, featuring components and micro-services seamlessly deployable in diverse network setups (eg. utilizing network segments like Demilitarized Zones, Intranet with no external access, etc), ensuring a secure environment. -- Enterprise ready (multiple Locations/Gateways/Kubernetes deployment, etc..) -- Built on WireGuard® protocol which is faster than IPSec, and significantly faster than OpenVPN -- Built with Rust for speed and security - -See: -- [full list of features](https://github.com/defguard/defguard#features) -- [enterprise only features](https://docs.defguard.net/enterprise/enterprise-features) - -### Defguard makes it easy to manage complex VPN networks in a secure way +

+ defguard +

-locations-connections +Defguard is a self-hosted secure remote access platform that combines WireGuard VPN, identity and access management, multi-factor authentication, and network access control in a single solution. -#### Video introduction +Built with a security-first architecture, Defguard helps organizations securely manage access to infrastructure, applications, and private networks while maintaining full control over their environment. -Bear in in mind we are no youtubers - just engineers - here is a video introduction to defguard: +## Why Defguard? -
-

- -[![Introduction to defguard](https://img.youtube.com/vi/4PF7edMGBwk/hqdefault.jpg)](https://www.youtube.com/watch?v=4PF7edMGBwk) +Modern organizations often rely on multiple disconnected tools to manage identity, VPN access, authentication, and network permissions. Defguard brings these capabilities together into a unified platform designed for security, transparency, and operational simplicity. -

-
+Key principles behind Defguard: -### Control plane management (this video is few versions behind... - a lot has changed!) +- 📖 Open-source core (AGPL), open-code Enterprise components +- 🏠 Fully self-hosted — no external dependencies or data leaving your infrastructure +- 🔒 Security-first: [Zero-Trust VPN](https://docs.defguard.net/features/wireguard) with connection-level MFA, [architecture](https://docs.defguard.net/in-depth/architecture) designed to minimize attack surface +- 🔍 Transparency: [published SBOMs](https://defguard.net/sbom/), [penetration test reports](https://defguard.net/pentesting/), [architecture decision records](https://docs.defguard.net/in-depth/architecture-decision-records) -![](https://defguard.net/images/product/core/hero-image.png) +For detailed security information see the [secure-by-design documentation](https://docs.defguard.net/in-depth/secure-by-design). -![](https://github.com/DefGuard/docs/blob/docs/screencasts/defguard.gif?raw=true) +## Core Capabilities -Better quality video can [be viewed here](https://github.com/DefGuard/docs/raw/docs/screencasts/defguard-screencast.mkv) +- 🌐 **WireGuard VPN** — multiple locations with per-location access control, MFA per connection, self-service device setup, kernel and userspace support +- 👥 **Identity & Access Management** — internal OIDC provider for SSO, external OIDC (Google, Microsoft, custom), LDAP/AD sync, remote enrollment, user self-service +- 🔑 **Multi-Factor Authentication** — TOTP, WebAuthn/FIDO2, email tokens, biometric via mobile app +- 🛡️ **Firewall** — allow/deny rules per VPN location by user or group, applied in real time +- 📋 **Activity Log** — audit log with filtering and search; real-time SIEM streaming (Enterprise) +- 🔗 **Integrations** — webhooks and REST API -### Desktop Client with 2FA / MFA (Multi-Factor Authentication) +## Clients -#### Light +- 🖥️ **Desktop** (Linux, macOS, Windows) — VPN management with MFA, multi-instance and multi-location support, and real-time connection statistics. [Download](https://defguard.net/download/) +- 📱 **Mobile** (Android, iOS) — VPN management with MFA, QR code onboarding. [Android](https://play.google.com/store/apps/details?id=net.defguard.mobile) · [iOS](https://apps.apple.com/us/app/defguard-vpn-client/id6748068630) -![defguard desktop client](https://defguard.net/images/product/client/main-screen.png) +## Architecture -#### Dark +Defguard follows a component-based architecture designed to reduce attack surface and support secure deployments. -![defguard WireGuard MFA](https://github.com/DefGuard/docs/blob/docs/releases/0.9/mfa.png?raw=true) +

+ architecture +

-[Desktop client](https://github.com/DefGuard/client): +Strict division of responsibilities and network segmentation: +- **Core** — central management plane: identity, authentication, authorization, and policy +- **Edge** — public-facing entry point, exposes selected Defguard services [GitHub repo](https://github.com/DefGuard/proxy) +- **Gateway** — enforces network access policies for protected resources [GitHub repo](https://github.com/DefGuard/gateway) -- **2FA / Multi-Factor Authentication** with TOTP or email based tokens & WireGuard PSK -- [automatic and real-time synchronization](https://docs.defguard.net/features/remote-user-enrollment/automatic-real-time-desktop-client-configuration) for users' desktop client settings (including all VPNs/locations). -- Control users [ability to manage devices and VPN options](https://docs.defguard.net/features/wireguard/behavior-customization) -- Defguard instances as well as **any WireGuard tunnel** - just import your tunnels - one client for all WireGuard connections -- Secure and remote user enrollment - setting up password, automatically configuring the client for all VPN Locations/Networks -- Onboarding - displaying custom onboarding messages, with templates, links ... -- Ability to route predefined VPN traffic or all traffic (server needs to have NAT configured - in gateway example) -- Live & real-time network charts -- live VPN logs -- light/dark theme +For details refer to the [architecture documentation](https://docs.defguard.net/in-depth/architecture). -## Quick start +## Quick Start -The easiest way to run your own defguard instance is to use Docker and our [one-line install script](https://docs.defguard.net/getting-started/one-line-install). -Just run the command below in your shell and follow the prompts: +The fastest way to evaluate Defguard is with the [one-line installer](https://docs.defguard.net/getting-started/one-line-install): ```bash -curl --proto '=https' --tlsv1.2 -sSf -L https://raw.githubusercontent.com/DefGuard/deployment/main/docker-compose/setup.sh -O && bash setup.sh +bash <(curl -sSL https://raw.githubusercontent.com/defguard/deployment/main/docker-compose2.0/setup.sh) ``` -Here is a step-by-step video about this process: - -
-

- -[![Quickly deploy defguard](https://img.youtube.com/vi/MqlE6ZTn0bg/hqdefault.jpg)](https://www.youtube.com/watch?v=MqlE6ZTn0bg) - -

-
- -To learn more about the script and available options please see the [documentation](https://docs.defguard.net/getting-started/one-line-install). - -### Setup a VPN server in under 5 minutes !? - -Just follow [this tutorial](http://bit.ly/defguard-setup) - -## Manual deployment examples - -- [Standalone system package based install](https://docs.defguard.net/deployment-strategies/standalone-package-based-installation) -- Using [Docker Compose](https://docs.defguard.net/deployment-strategies/docker-compose) -- Using [Kubernetes](https://docs.defguard.net/deployment-strategies/kubernetes) - -## Roadmap & Development backlog - -[A detailed product roadmap and development status can be found here](https://github.com/orgs/DefGuard/projects/5/views/1) - -### ⛑️ Want to help? ⛑️ - -Here is a [dedicated view for **good first bugs**](https://github.com/orgs/DefGuard/projects/5/views/5) - -## Features - -* Remote Access: [WireGuard® VPN](https://www.wireguard.com/) server with: - - [Multi-Factor Authentication](https://docs.defguard.net/features/wireguard/multi-factor-authentication-mfa-2fa) with TOTP/Email & Pre-Shared Session Keys - - multiple VPN Locations (networks/sites) - with defined access (all users or only Admin group) - - multiple [Gateways](https://github.com/DefGuard/gateway) for each VPN Location (**high availability/failover**) - supported on a cluster of routers/firewalls for Linux, FreeBSD/PFSense/OPNSense - - **import your current WireGuard® server configuration (with a wizard!)** - - **most beautiful [Desktop Client!](https://github.com/defguard/client)** (in our opinion ;-)) - - automatic IP allocation - - [automatic and real-time synchronization](https://docs.defguard.net/features/remote-user-enrollment/automatic-real-time-desktop-client-configuration) for users' desktop client settings (including all VPNs/locations). - - control users [ability to manage devices and VPN options](https://docs.defguard.net/features/wireguard/behavior-customization) - - kernel (Linux, FreeBSD/OPNSense/PFSense) & userspace WireGuard® support with [our Rust library](https://github.com/defguard/wireguard-rs) - - dashboard and statistics overview of connected users/devices for admins - - *defguard is not an official WireGuard® project, and WireGuard is a registered trademark of Jason A. Donenfeld.* -* Identity & Account Management: - - SSO based on OpenID Connect](https://openid.net/developers/how-connect-works/) - - External SSO: [external OpenID provider support](https://docs.defguard.net/features/external-openid-providers) - - [Multi-Factor/2FA](https://en.wikipedia.org/wiki/Multi-factor_authentication) Authentication: - - [Time-based One-Time Password Algorithm](https://en.wikipedia.org/wiki/Time-based_one-time_password) (TOTP - e.g. Google Authenticator) - - WebAuthn / FIDO2 - for hardware key authentication support (eg. YubiKey, FaceID, TouchID, ...) - - Email based TOTP - - LDAP (tested on [OpenLDAP](https://www.openldap.org/)) synchronization - - [forward auth](https://docs.defguard.net/features/forward-auth) for reverse proxies (tested with Traefik and Caddy) - - nice UI to manage users - - Users **self-service** (besides typical data management, users can revoke access to granted apps, MFA, WireGuard®, etc.) -* Account Lifecycle Management: - - Secure remote (over the Internet) [user enrollment](https://docs.defguard.net/features/remote-user-enrollment) - on public web / Desktop Client - - User [onboarding after enrollment](https://docs.defguard.net/features/remote-user-enrollment/user-onboarding-after-enrollment) -* SSH & GPG public key management in user profile - with [SSH keys authentication for servers](https://docs.defguard.net/features/ssh-authentication) -* [Yubikey hardware keys](https://www.yubico.com/) provisioning for users by *one click* -* [Email/SMTP support](https://docs.defguard.net/features/notifications/setting-up-smtp-for-email-notifications) for notifications, remote enrollment and onboarding -* Easy support with [sending debug/support information](https://docs.defguard.net/support-1/troubleshooting/sending-support-info) -* Webhooks & REST API -* Built with [Rust](https://www.rust-lang.org/) for portability, security, and speed -* [UI Library](https://github.com/defguard/ui) - our beautiful React/TypeScript UI is a collection of React components: - - a set of custom and beautiful components for the layout - - Responsive Web Design (supporting mobile phones, tablets, etc..) - - [iOS Web App](https://www.macrumors.com/how-to/use-web-apps-iphone-ipad/) -* **Checked by professional security researchers** (see [comprehensive security report](https://defguard.net/pdf/isec-defguard.pdf)) -* End2End tests +⚠️ Warning! This installation method is intended for testing, demonstrations, and evaluation purposes only. It is not recommended for production deployments. See the [deployment documentation](https://docs.defguard.net/deployment-strategies/overview) for production deployment guidance, architecture recommendations, and high-availability configurations. ## Documentation -See the [documentation](https://docs.defguard.net/) for more information. +Comprehensive documentation is available at: https://docs.defguard.net -## Community and Support +## Video guides -Find us on Matrix: [#defguard:teonite.com](https://matrix.to/#/#defguard:teonite.com) +Visit out YouTube channel to see our [video guides](https://www.youtube.com/playlist?list=PLVR33X0CUHUcoyLshs9S8VbsGgggouCAW). -## License +## Community -The code in this repository is available under a dual licensing model: +We want to get as much feedback as possible, so we encourage you to: -1. Open Source License: The code, except for the contents of the "crates/defguard_core/src/enterprise" directory, is licensed under the AGPL license (see file LICENSE.md in this repository). This applies to the open core components of the software. -2. Enterprise License: All code in this repository (including within the "crates/defguard_core/src/enterprise" directory) is licensed under a separate Enterprise License (see file crates/defguard_core/src/enterprise/LICENSE.md). +- 💬 open a [GitHub discussion](https://github.com/DefGuard/defguard/discussions/new/choose) +- 🪲 report any missing [features](https://github.com/DefGuard/defguard/issues/new?assignees=&labels=feature&projects=&template=feature_request.md&title=) or [bugs](https://github.com/DefGuard/defguard/issues/new?assignees=&labels=bug&projects=&template=bug_report.md&title=) as issues ## Contributions Please review the [Contributing guide](https://docs.defguard.net/for-developers/contributing) for information on how to get started contributing to the project. You might also find our [environment setup guide](https://docs.defguard.net/for-developers/dev-env-setup) handy. -## Verifiability of releases - -We provide following ways to verify the authenticity and integrity of official releases: - -### Docker Image Verification with Cosign - -All official Docker images are signed using [Cosign](https://docs.sigstore.dev/cosign/overview/). To verify a Docker image: - -1. [Install](https://github.com/sigstore/cosign?tab=readme-ov-file#installation) cosign CLI - -2. Verify the image signature (replace with the tag you want to verify): - ```bash - cosign verify --certificate-identity-regexp="https://github.com/DefGuard/defguard" \ - --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \ - ghcr.io/defguard/defguard: - ``` - -### Release Asset Verification - -All release assets (binaries, packages, etc.) include SHA256 checksums that are automatically generated and published with each GitHub release: - -1. Download the release asset and copy its corresponding checksum from the [releases page](https://github.com/DefGuard/defguard/releases) - -2. Verify the checksum: - ```bash - # Linux/macOS - echo known_sha256_checksum_of_the_file path/to/file | sha256sum --check - ``` +## License +The code in this repository is available under a dual licensing model: -# Legal +- Open Source License: The code, except for the contents of the "crates/defguard_core/src/enterprise" directory, is licensed under the AGPL license (see file LICENSE.md in this repository). This applies to the open core components of the software. +- Enterprise License: All code in this repository (including within the "crates/defguard_core/src/enterprise" directory) is licensed under a separate Enterprise License (see file crates/defguard_core/src/enterprise/LICENSE.md). -WireGuard® is [registered trademarks](https://www.wireguard.com/trademark-policy/) of Jason A. Donenfeld. +## Legal +WireGuard® is [registered trademarks](https://www.wireguard.com/trademark-policy/) of Jason A. Donenfeld. \ No newline at end of file diff --git a/docs/cover-image_smaller-logo.png b/docs/cover-image_smaller-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..83ee1e2261b8a2e1eafabb0735839fd2509cbca8 GIT binary patch literal 114241 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 Date: Fri, 5 Jun 2026 13:35:25 +0200 Subject: [PATCH 02/18] Show enrollment modal after group assignment (#3049) --- .../modals/AddUserModal/AddUserModal.tsx | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/web/src/pages/UsersOverviewPage/modals/AddUserModal/AddUserModal.tsx b/web/src/pages/UsersOverviewPage/modals/AddUserModal/AddUserModal.tsx index 6deae95cc0..d6b9df429d 100644 --- a/web/src/pages/UsersOverviewPage/modals/AddUserModal/AddUserModal.tsx +++ b/web/src/pages/UsersOverviewPage/modals/AddUserModal/AddUserModal.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import './style.scss'; import { useStore } from '@tanstack/react-form'; import { useMutation } from '@tanstack/react-query'; @@ -516,22 +516,24 @@ const AddUserGroupsSelectionStep = () => { const user = useAddUserModal((s) => s.user as User); const [selected, setSelected] = useState(new Set()); + const handleStepFinish = useCallback(() => { + if (enrollEnabled) { + useAddUserModal.setState({ + step: 'enrollment', + }); + } else { + useAddUserModal.setState({ + isOpen: false, + }); + } + }, [enrollEnabled]); + const { mutate, isPending } = useMutation({ mutationFn: api.group.addUsersToGroups, meta: { invalidate: [['group'], ['group-info'], ['user']], }, - onSuccess: () => { - if (enrollEnabled) { - useAddUserModal.setState({ - step: 'enrollment', - }); - } else { - useAddUserModal.setState({ - isOpen: false, - }); - } - }, + onSuccess: handleStepFinish, }); const options = useMemo( @@ -566,9 +568,7 @@ const AddUserGroupsSelectionStep = () => { groups: groups, }); } else { - useAddUserModal.setState({ - isOpen: false, - }); + handleStepFinish(); } }, }} From 447106c47b629b2cb805a6689e78a0dd22fd7b03 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Wed, 10 Jun 2026 10:15:32 +0200 Subject: [PATCH 03/18] fix (#3065) --- .../src/servers/enrollment.rs | 6 +- .../tests/proxy_manager/handler/enrollment.rs | 74 +++++++++++++++++++ 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/crates/defguard_proxy_manager/src/servers/enrollment.rs b/crates/defguard_proxy_manager/src/servers/enrollment.rs index c5b61ef975..f9be882ce7 100644 --- a/crates/defguard_proxy_manager/src/servers/enrollment.rs +++ b/crates/defguard_proxy_manager/src/servers/enrollment.rs @@ -422,9 +422,9 @@ impl EnrollmentServer { "Fetching user {} data to check if the user already has a password.", user.username ); - if user.has_password() { - error!("User {} already activated", user.username); - return Err(Status::invalid_argument("user already activated")); + if user.is_enrolled() { + error!("User {} already enrolled", user.username); + return Err(Status::invalid_argument("user already enrolled")); } debug!("User doesn't have a password yet. Continue user activation process..."); diff --git a/crates/defguard_proxy_manager/src/tests/proxy_manager/handler/enrollment.rs b/crates/defguard_proxy_manager/src/tests/proxy_manager/handler/enrollment.rs index 4b04d3ac24..ce7d90d6e9 100644 --- a/crates/defguard_proxy_manager/src/tests/proxy_manager/handler/enrollment.rs +++ b/crates/defguard_proxy_manager/src/tests/proxy_manager/handler/enrollment.rs @@ -248,6 +248,80 @@ async fn test_activate_user_already_activated_returns_error( context.finish().await.expect_server_finished().await; } +/// Regression: a user with a password whose enrollment was retriggered +/// (`enrollment_pending == true`) must be able to enroll again, because the +/// activation gate checks `is_enrolled()` rather than `has_password()`. +#[sqlx::test] +async fn test_activate_user_with_password_but_enrollment_pending_can_reenroll( + _: PgPoolOptions, + options: PgConnectOptions, +) { + let mut context = HandlerTestContext::new(options).await; + complete_proxy_handshake(&mut context).await; + + let mut user = create_user(&context.pool).await; + user.set_password("OldPassw0rd!"); + user.enrollment_pending = true; + user.save(&context.pool) + .await + .expect("failed to save user with pending enrollment"); + assert!(!user.is_enrolled()); + + let token = create_enrollment_token(&context.pool, user.id, Some(user.id)).await; + start_enrollment_session(&mut context, &token.id).await; + let _ = timeout(TEST_TIMEOUT, context.bidi_events_rx.recv()).await; + + let response = send_activate_user(&mut context, &token.id, STRONG_PASSWORD, None).await; + match &response.payload { + Some(core_response::Payload::Empty(())) => {} + other => panic!( + "expected Empty on re-enrollment of pending user, got: {:?}", + other.as_ref().map(std::mem::discriminant) + ), + } + + let updated = User::find_by_username(&context.pool, &user.username) + .await + .expect("db query failed") + .expect("user not found"); + assert!(!updated.enrollment_pending); + assert!(updated.is_enrolled()); + + context.finish().await.expect_server_finished().await; +} + +/// Regression: a user who is already enrolled (password set, not +/// enrollment-pending) must be rejected with `InvalidArgument` when enrolling. +#[sqlx::test] +async fn test_activate_user_already_enrolled_cannot_reenroll( + _: PgPoolOptions, + options: PgConnectOptions, +) { + let mut context = HandlerTestContext::new(options).await; + complete_proxy_handshake(&mut context).await; + + let mut user = create_user(&context.pool).await; + user.set_password("OldPassw0rd!"); + user.enrollment_pending = false; + user.save(&context.pool) + .await + .expect("failed to save already-enrolled user"); + assert!(user.is_enrolled()); + + let token = create_enrollment_token(&context.pool, user.id, Some(user.id)).await; + start_enrollment_session(&mut context, &token.id).await; + + let response = send_activate_user(&mut context, &token.id, STRONG_PASSWORD, None).await; + let code = assert_error_response(&response); + assert_eq!( + code, + tonic::Code::InvalidArgument, + "activating an already-enrolled user must return InvalidArgument" + ); + + context.finish().await.expect_server_finished().await; +} + #[sqlx::test] async fn test_new_device_sends_gateway_device_created_event( _: PgPoolOptions, From 5cc8b4027d7241f3cf088c6250257ddd1226ce95 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Thu, 11 Jun 2026 11:43:57 +0200 Subject: [PATCH 04/18] Adjust APT repo testing pipeline to our new release model, other enhancements (#3069) * possibly fix * change branch * test 1.x also * test after uploa * increase cron schedule * cleanup * cleanup 2 --- .github/workflows/test-apt-repo.yml | 67 +++++++++++++++++------ .github/workflows/update-repositories.yml | 51 +++++++++++++++++ 2 files changed, 100 insertions(+), 18 deletions(-) diff --git a/.github/workflows/test-apt-repo.yml b/.github/workflows/test-apt-repo.yml index 1757369181..0d58dc7111 100644 --- a/.github/workflows/test-apt-repo.yml +++ b/.github/workflows/test-apt-repo.yml @@ -2,10 +2,11 @@ name: Test APT repository "on": schedule: - - cron: "0 6 * * *" + - cron: "0 */6 * * *" workflow_dispatch: - release: - types: [published] + workflow_run: + workflows: ["Update repositories with packages"] + types: [completed] jobs: test-apt-install: @@ -20,7 +21,7 @@ jobs: fail-fast: false matrix: package: [defguard, defguard-proxy, defguard-gateway] - component: [release, pre-release] + component: [release, pre-release, release-2.0, pre-release-2.0] include: - package: defguard github_repo: DefGuard/defguard @@ -47,35 +48,65 @@ jobs: - name: Update APT cache run: apt-get update -y + - name: Check package availability in component + run: | + CANDIDATE=$(apt-cache policy ${{ matrix.package }} | awk '/Candidate:/ {print $2}') + if [ -z "$CANDIDATE" ] || [ "$CANDIDATE" = "(none)" ]; then + echo "::notice::${{ matrix.package }} not available in component ${{ matrix.component }}, skipping" + echo "SKIP=true" >> $GITHUB_ENV + else + echo "Candidate version: $CANDIDATE" + echo "SKIP=false" >> $GITHUB_ENV + fi + - name: Get expected version from GitHub + if: env.SKIP != 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - if [ "${{ matrix.component }}" = "release" ]; then - VERSION=$(curl -sf \ - -H "Authorization: Bearer $GH_TOKEN" \ - https://api.github.com/repos/${{ matrix.github_repo }}/releases/latest \ - | jq -r '.tag_name') - else - VERSION=$(curl -sf \ - -H "Authorization: Bearer $GH_TOKEN" \ - https://api.github.com/repos/${{ matrix.github_repo }}/releases \ - | jq -r '[.[] | select(.prerelease == true)][0].tag_name') + case "${{ matrix.component }}" in + release-2.0) PRERELEASE=false; MAJOR=v2. ;; + pre-release-2.0) PRERELEASE=true; MAJOR=v2. ;; + release) PRERELEASE=false; MAJOR=v1. ;; + pre-release) PRERELEASE=true; MAJOR=v1. ;; + esac + VERSION=$(curl -sf \ + -H "Authorization: Bearer $GH_TOKEN" \ + https://api.github.com/repos/${{ matrix.github_repo }}/releases \ + | jq -r --argjson pre "$PRERELEASE" --arg major "$MAJOR" \ + '[.[] | select(.prerelease == $pre and (.tag_name | startswith($major)))][0].tag_name // empty') + if [ -z "$VERSION" ]; then + echo "::notice::no $MAJOR release (prerelease=$PRERELEASE) of ${{ matrix.package }} on GitHub, skipping" + echo "SKIP=true" >> $GITHUB_ENV + exit 0 fi VERSION="${VERSION#v}" + + # legacy pre-release still holds 2.0 betas published before the + # component split; accept them instead of expecting the latest v1.x + CANDIDATE=$(apt-cache policy ${{ matrix.package }} | awk '/Candidate:/ {print $2}') + if [ "${{ matrix.component }}" = "pre-release" ] && [ "${CANDIDATE%%.*}" != "1" ]; then + echo "::notice::candidate $CANDIDATE is from before the component split, skipping version comparison" + VERSION="" + fi + echo "Expected version: $VERSION" echo "EXPECTED_VERSION=$VERSION" >> $GITHUB_ENV - name: Install ${{ matrix.package }} + if: env.SKIP != 'true' run: apt-get install -y ${{ matrix.package }} - name: Verify ${{ matrix.package }} version + if: env.SKIP != 'true' run: | INSTALLED=$(dpkg -s ${{ matrix.package }} | grep '^Version:' | awk '{print $2}') echo "Installed version: $INSTALLED" - echo "Expected version: $EXPECTED_VERSION" - if [ "$INSTALLED" != "$EXPECTED_VERSION" ]; then - echo "Version mismatch!" - exit 1 + if [ -n "$EXPECTED_VERSION" ]; then + echo "Expected version: $EXPECTED_VERSION" + if [ "$INSTALLED" != "$EXPECTED_VERSION" ]; then + echo "Version mismatch!" + exit 1 + fi fi ${{ matrix.package }} -V diff --git a/.github/workflows/update-repositories.yml b/.github/workflows/update-repositories.yml index 603b831751..7fa6af2e1c 100644 --- a/.github/workflows/update-repositories.yml +++ b/.github/workflows/update-repositories.yml @@ -96,3 +96,54 @@ jobs: done (aws s3 ls s3://apt.defguard.net/dists/ --recursive; aws s3 ls s3://apt.defguard.net/pool/ --recursive) | awk '{print ""$4"
"}' > index.html aws s3 cp index.html s3://apt.defguard.net/ --acl public-read + + verify-apt-repo: + needs: + - apt-sign + runs-on: + - self-hosted + - Linux + - X64 + steps: + - name: Verify published repository signatures and metadata + run: | + set -euo pipefail + sudo apt update -y + sudo apt install -y curl gpg + + WORKDIR=$(mktemp -d) + trap 'rm -rf "$WORKDIR"' EXIT + cd "$WORKDIR" + + curl -fsSL https://apt.defguard.net/defguard.asc | gpg --dearmor -o keyring.gpg + + for DIST in trixie bookworm; do + echo "=== Verifying $DIST ===" + curl -fsSL "https://apt.defguard.net/dists/${DIST}/Release" -o Release + curl -fsSL "https://apt.defguard.net/dists/${DIST}/Release.gpg" -o Release.gpg + curl -fsSL "https://apt.defguard.net/dists/${DIST}/InRelease" -o InRelease + + gpgv --keyring "$WORKDIR/keyring.gpg" Release.gpg Release + gpgv --keyring "$WORKDIR/keyring.gpg" InRelease + + for COMPONENT in $(awk '/^Components:/ {for (i=2; i<=NF; i++) print $i}' Release); do + PACKAGES_PATH="${COMPONENT}/binary-amd64/Packages" + EXPECTED_SHA=$(awk -v p="$PACKAGES_PATH" '/^SHA256:/{s=1; next} /^[A-Za-z]/{s=0} s && $3 == p {print $1; exit}' Release) + # InRelease must describe the same metadata as the detached-signed + # Release, otherwise apt clients see a signature/content mismatch + INRELEASE_SHA=$(awk -v p="$PACKAGES_PATH" '/^SHA256:/{s=1; next} /^[A-Za-z-]/{s=0} s && $3 == p {print $1; exit}' InRelease) + if [ -z "$EXPECTED_SHA" ] || [ "$EXPECTED_SHA" != "$INRELEASE_SHA" ]; then + echo "SHA256 entry for $PACKAGES_PATH missing or differs between Release and InRelease in $DIST" + exit 1 + fi + curl -fsSL "https://apt.defguard.net/dists/${DIST}/${PACKAGES_PATH}" -o Packages + ACTUAL_SHA=$(sha256sum Packages | awk '{print $1}') + if [ "$ACTUAL_SHA" != "$EXPECTED_SHA" ]; then + echo "Checksum mismatch for $DIST/$PACKAGES_PATH" + echo "expected: $EXPECTED_SHA" + echo "actual: $ACTUAL_SHA" + exit 1 + fi + echo "$DIST/$PACKAGES_PATH OK" + done + done From cabdc4008b7e974a02ded4514f3a8768b4cb2891 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Wed, 17 Jun 2026 13:01:34 +0200 Subject: [PATCH 05/18] Fix ldap tests (#3093) * try fixing tests\ * bump dependency --- .github/workflows/ci.yml | 11 ++++++++--- docker-compose.ldap-test.yaml | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f32d55870f..f03fa6cc87 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,7 @@ on: permissions: contents: read + packages: read jobs: lint: @@ -231,9 +232,6 @@ jobs: --partition hash:${{ matrix.partition }}/8 test-ldap: - # binami ldap image is no longer served by ecr - # disable this job for now to avoid CI fails - if: false needs: build runs-on: @@ -255,6 +253,13 @@ jobs: - name: Download cargo-nextest binary run: curl -LsSf https://get.nexte.st/latest/linux | tar zxf - + - name: Log in to ghcr.io + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Start Postgres and OpenLDAP run: docker compose -p defguard-ldap -f docker-compose.ldap-test.yaml up -d --wait db openldap diff --git a/docker-compose.ldap-test.yaml b/docker-compose.ldap-test.yaml index 42b6a5fb5a..0c5f55527d 100644 --- a/docker-compose.ldap-test.yaml +++ b/docker-compose.ldap-test.yaml @@ -17,7 +17,7 @@ services: retries: 5 openldap: - image: public.ecr.aws/bitnamilegacy/openldap:2.6 + image: ghcr.io/defguard/openldap:2.6.8 user: root environment: LDAP_ADMIN_PASSWORD: "pass123" From cfe6d53db85a4e65659423e8f0cd720263d76732 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Tue, 23 Jun 2026 13:43:08 +0200 Subject: [PATCH 06/18] Fix path in ova example (#3119) --- web/src/pages/GatewaySetupPage/steps/SetupDeployGatewayStep.tsx | 2 +- .../wizard-steps/SetupEdgeDeployStep/SetupEdgeDeployStep.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/pages/GatewaySetupPage/steps/SetupDeployGatewayStep.tsx b/web/src/pages/GatewaySetupPage/steps/SetupDeployGatewayStep.tsx index e996440c64..56b28ea134 100644 --- a/web/src/pages/GatewaySetupPage/steps/SetupDeployGatewayStep.tsx +++ b/web/src/pages/GatewaySetupPage/steps/SetupDeployGatewayStep.tsx @@ -215,7 +215,7 @@ const VirtualImageTab = () => { { Date: Thu, 25 Jun 2026 10:39:13 +0200 Subject: [PATCH 07/18] bump version --- .github/workflows/release.yml | 8 ++++++-- Cargo.lock | 2 +- Cargo.toml | 2 +- crates/defguard_common/Cargo.toml | 2 +- freebsd/post-install.sh | 6 ++++++ 5 files changed, 15 insertions(+), 5 deletions(-) create mode 100755 freebsd/post-install.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5333efe64b..56140dd538 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -79,6 +79,9 @@ jobs: - X64 env: SQLX_OFFLINE: "1" + # Force the installed stable toolchain. RUSTUP_TOOLCHAIN takes precedence + # over any stale rustup directory override left on the self-hosted runner. + RUSTUP_TOOLCHAIN: "stable" # sccache SCCACHE_GHA_ENABLED: "true" RUSTC_WRAPPER: "sccache" @@ -221,14 +224,15 @@ jobs: fpm_args: "defguard-${{ env.VERSION }}-x86_64-unknown-freebsd=/usr/local/bin/defguard freebsd/defguard=/usr/local/etc/rc.d/defguard - .env.example=/etc/defguard/core.conf" + .env.example=/etc/defguard/core.conf.sample" fpm_opts: "--architecture amd64 --output-type freebsd --version ${{ env.VERSION }} --package defguard-${{ env.VERSION }}_x86_64-unknown-freebsd.pkg --freebsd-osversion '*' - --depends openssl" + --depends openssl + --after-install freebsd/post-install.sh" - name: Upload Linux x86_64 archive uses: shogo82148/actions-upload-release-asset@394b3c11c3cfc038b5396ad265c074065cf875c3 # v1.10.2 diff --git a/Cargo.lock b/Cargo.lock index 922d6b2c84..1847fe104a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1387,7 +1387,7 @@ dependencies = [ [[package]] name = "defguard_common" -version = "2.0.1" +version = "2.1.0" dependencies = [ "anyhow", "argon2", diff --git a/Cargo.toml b/Cargo.toml index dbd43ccae3..bedd41b8d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ redundant_closure = "warn" # internal crates defguard_setup = { path = "./crates/defguard_setup", version = "0.0.0" } -defguard_common = { path = "./crates/defguard_common", version = "2.0.1" } +defguard_common = { path = "./crates/defguard_common", version = "2.1.0" } defguard_static_ip = { path = "./crates/defguard_static_ip", version = "0.0.0" } defguard_core = { path = "./crates/defguard_core", version = "0.0.0" } defguard_event_logger = { path = "./crates/defguard_event_logger", version = "0.0.0" } diff --git a/crates/defguard_common/Cargo.toml b/crates/defguard_common/Cargo.toml index ad15490168..b8c30d0577 100644 --- a/crates/defguard_common/Cargo.toml +++ b/crates/defguard_common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "defguard_common" -version = "2.0.1" +version = "2.1.0" edition.workspace = true license-file.workspace = true homepage.workspace = true diff --git a/freebsd/post-install.sh b/freebsd/post-install.sh new file mode 100755 index 0000000000..38fe967f1b --- /dev/null +++ b/freebsd/post-install.sh @@ -0,0 +1,6 @@ +#!/bin/sh +CONFIG=/etc/defguard/core.conf + +if [ ! -f "${CONFIG}" ]; then + cp "${CONFIG}.sample" "${CONFIG}" +fi From 10e79181421cb27652376618ea761a3f0180389e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Thu, 25 Jun 2026 14:04:38 +0200 Subject: [PATCH 08/18] migrate xoauth2 backend --- ...72929d6a612846acf4a5489c36a04cb57de59.json | 22 --- ...ab52c14293fa64d260db812c89ed43a5a7cc8.json | 15 -- ...68ebe8d0b64fe8bf6dafb9d92768397fbcecd.json | 15 -- ...4f06bc8e8fce23ee336d64f8608f0e7eefc8c.json | 14 -- ...b4e6bb1502935468d1459e1693513c40821b.json} | 7 +- ...1a2f9a2abcb8d044ca90a536c6e567e320d69.json | 15 -- Cargo.lock | 33 +--- Cargo.toml | 1 - .../src/db/models/settings/mod.rs | 6 +- .../src/db/models/settings/smtp.rs | 38 ++++ crates/defguard_core/Cargo.toml | 9 +- .../defguard_core/src/db/models/enrollment.rs | 2 +- .../src/enrollment_management.rs | 2 +- crates/defguard_core/src/enterprise/mod.rs | 4 + .../src/enterprise/oauth2/microsoft.rs | 67 +++++++ .../src/enterprise/oauth2/mod.rs | 124 ++++++++++++ crates/defguard_core/src/error.rs | 2 +- .../src/grpc/proxy/client_mfa.rs | 2 +- crates/defguard_core/src/handlers/auth.rs | 2 +- crates/defguard_core/src/handlers/mail.rs | 10 +- .../src/handlers/network_devices.rs | 2 +- .../defguard_core/src/handlers/openid_flow.rs | 2 +- crates/defguard_core/src/handlers/user.rs | 2 +- .../defguard_core/src/handlers/wireguard.rs | 2 +- crates/defguard_core/src/headers.rs | 2 +- crates/defguard_core/src/letsencrypt.rs | 2 +- crates/defguard_core/src/lib.rs | 1 + .../src/mail}/assets/apple.png | Bin .../src/mail}/assets/date.png | Bin .../src/mail}/assets/defguard.png | Bin .../src/mail}/assets/github.png | Bin .../src/mail}/assets/google_play.png | Bin .../src/mail}/assets/mastodon.png | Bin .../src/mail}/assets/new_account_1.png | Bin .../src/mail}/assets/new_account_2.png | Bin .../src/mail}/assets/otp.png | Bin .../src/mail}/assets/x.png | Bin .../src/mail}/mail_context.rs | 0 .../mail.rs => defguard_core/src/mail/mod.rs} | 180 +++++++++++------- .../src => defguard_core/src/mail}/qr.rs | 0 .../src/mail}/templates.rs | 6 +- .../src/mail}/templates/base.mjml | 0 .../templates/certificate-expiration.mjml | 0 .../templates/certificate-expiration.text | 0 .../src/mail}/templates/desktop-start.mjml | 0 .../src/mail}/templates/desktop-start.text | 0 .../enrollment-admin-notification.mjml | 0 .../enrollment-admin-notification.text | 0 .../mail}/templates/enrollment-welcome.mjml | 0 .../mail}/templates/enrollment-welcome.text | 0 .../mail}/templates/gateway-disconnected.mjml | 0 .../mail}/templates/gateway-disconnected.text | 0 .../mail}/templates/gateway-reconnected.mjml | 0 .../mail}/templates/gateway-reconnected.text | 0 .../letsencrypt-cert-refresh-failed.mjml | 0 .../letsencrypt-cert-refresh-failed.text | 0 .../src/mail}/templates/macros.mjml | 0 .../src/mail}/templates/mfa-activation.mjml | 0 .../src/mail}/templates/mfa-activation.text | 0 .../src/mail}/templates/mfa-code.mjml | 0 .../src/mail}/templates/mfa-code.text | 0 .../src/mail}/templates/mfa-configured.mjml | 0 .../src/mail}/templates/mfa-configured.text | 0 .../src/mail}/templates/new-account.mjml | 0 .../src/mail}/templates/new-account.text | 0 .../src/mail}/templates/new-device-login.mjml | 0 .../src/mail}/templates/new-device-login.text | 0 .../templates/new-device-oidc-login.mjml | 0 .../templates/new-device-oidc-login.text | 0 .../src/mail}/templates/new-device.mjml | 0 .../src/mail}/templates/new-device.text | 0 .../mail}/templates/password-reset-done.mjml | 0 .../mail}/templates/password-reset-done.text | 0 .../src/mail}/templates/password-reset.mjml | 0 .../src/mail}/templates/password-reset.text | 0 .../mail}/templates/plain-notification.mjml | 0 .../mail}/templates/plain-notification.text | 0 .../src/mail}/templates/support-data.mjml | 0 .../src/mail}/templates/support-data.text | 0 .../src/mail}/templates/test.mjml | 0 .../src/mail}/templates/test.text | 0 .../src => defguard_core/src/mail}/tests.rs | 5 +- crates/defguard_core/src/utility_thread.rs | 2 +- crates/defguard_mail/Cargo.toml | 33 ---- crates/defguard_mail/src/lib.rs | 49 ----- crates/defguard_mail/src/xoauth2.rs | 64 ------- crates/defguard_proxy_manager/Cargo.toml | 1 - .../src/servers/enrollment.rs | 8 +- .../src/servers/password_reset.rs | 2 +- ...0000_[2.1.0]_smtp_oauth_tenant_id.down.sql | 2 + ...000000_[2.1.0]_smtp_oauth_tenant_id.up.sql | 2 + 91 files changed, 395 insertions(+), 362 deletions(-) delete mode 100644 .sqlx/query-0ad599ef120ecc02b030bd1276172929d6a612846acf4a5489c36a04cb57de59.json delete mode 100644 .sqlx/query-1bb3c8ecbd6500717d639678e08ab52c14293fa64d260db812c89ed43a5a7cc8.json delete mode 100644 .sqlx/query-8662e89e31b58def986e1a155cd68ebe8d0b64fe8bf6dafb9d92768397fbcecd.json delete mode 100644 .sqlx/query-8e70e158b41d640eba1333c0aea4f06bc8e8fce23ee336d64f8608f0e7eefc8c.json rename .sqlx/{query-46fb95097c4cf79cdea7ef38c10cf8ca71f37bd60944f1b133a1ccd3a5fef98b.json => query-93ac6465cd4f3100d8e3b6723d9fb4e6bb1502935468d1459e1693513c40821b.json} (96%) delete mode 100644 .sqlx/query-e0a5e5060afa2628112ae29b8a51a2f9a2abcb8d044ca90a536c6e567e320d69.json create mode 100644 crates/defguard_core/src/enterprise/oauth2/microsoft.rs create mode 100644 crates/defguard_core/src/enterprise/oauth2/mod.rs rename crates/{defguard_mail => defguard_core/src/mail}/assets/apple.png (100%) rename crates/{defguard_mail => defguard_core/src/mail}/assets/date.png (100%) rename crates/{defguard_mail => defguard_core/src/mail}/assets/defguard.png (100%) rename crates/{defguard_mail => defguard_core/src/mail}/assets/github.png (100%) rename crates/{defguard_mail => defguard_core/src/mail}/assets/google_play.png (100%) rename crates/{defguard_mail => defguard_core/src/mail}/assets/mastodon.png (100%) rename crates/{defguard_mail => defguard_core/src/mail}/assets/new_account_1.png (100%) rename crates/{defguard_mail => defguard_core/src/mail}/assets/new_account_2.png (100%) rename crates/{defguard_mail => defguard_core/src/mail}/assets/otp.png (100%) rename crates/{defguard_mail => defguard_core/src/mail}/assets/x.png (100%) rename crates/{defguard_mail/src => defguard_core/src/mail}/mail_context.rs (100%) rename crates/{defguard_mail/src/mail.rs => defguard_core/src/mail/mod.rs} (73%) rename crates/{defguard_mail/src => defguard_core/src/mail}/qr.rs (100%) rename crates/{defguard_mail/src => defguard_core/src/mail}/templates.rs (99%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/base.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/certificate-expiration.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/certificate-expiration.text (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/desktop-start.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/desktop-start.text (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/enrollment-admin-notification.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/enrollment-admin-notification.text (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/enrollment-welcome.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/enrollment-welcome.text (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/gateway-disconnected.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/gateway-disconnected.text (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/gateway-reconnected.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/gateway-reconnected.text (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/letsencrypt-cert-refresh-failed.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/letsencrypt-cert-refresh-failed.text (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/macros.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/mfa-activation.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/mfa-activation.text (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/mfa-code.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/mfa-code.text (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/mfa-configured.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/mfa-configured.text (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/new-account.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/new-account.text (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/new-device-login.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/new-device-login.text (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/new-device-oidc-login.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/new-device-oidc-login.text (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/new-device.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/new-device.text (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/password-reset-done.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/password-reset-done.text (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/password-reset.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/password-reset.text (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/plain-notification.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/plain-notification.text (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/support-data.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/support-data.text (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/test.mjml (100%) rename crates/{defguard_mail => defguard_core/src/mail}/templates/test.text (100%) rename crates/{defguard_mail/src => defguard_core/src/mail}/tests.rs (99%) delete mode 100644 crates/defguard_mail/Cargo.toml delete mode 100644 crates/defguard_mail/src/lib.rs delete mode 100644 crates/defguard_mail/src/xoauth2.rs create mode 100644 migrations/20260625000000_[2.1.0]_smtp_oauth_tenant_id.down.sql create mode 100644 migrations/20260625000000_[2.1.0]_smtp_oauth_tenant_id.up.sql diff --git a/.sqlx/query-0ad599ef120ecc02b030bd1276172929d6a612846acf4a5489c36a04cb57de59.json b/.sqlx/query-0ad599ef120ecc02b030bd1276172929d6a612846acf4a5489c36a04cb57de59.json deleted file mode 100644 index ce67e42930..0000000000 --- a/.sqlx/query-0ad599ef120ecc02b030bd1276172929d6a612846acf4a5489c36a04cb57de59.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT recovery_codes FROM \"user\" WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "recovery_codes", - "type_info": "TextArray" - } - ], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [ - false - ] - }, - "hash": "0ad599ef120ecc02b030bd1276172929d6a612846acf4a5489c36a04cb57de59" -} diff --git a/.sqlx/query-1bb3c8ecbd6500717d639678e08ab52c14293fa64d260db812c89ed43a5a7cc8.json b/.sqlx/query-1bb3c8ecbd6500717d639678e08ab52c14293fa64d260db812c89ed43a5a7cc8.json deleted file mode 100644 index 8d314a5faa..0000000000 --- a/.sqlx/query-1bb3c8ecbd6500717d639678e08ab52c14293fa64d260db812c89ed43a5a7cc8.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE session SET expires = $1 WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Timestamp", - "Text" - ] - }, - "nullable": [] - }, - "hash": "1bb3c8ecbd6500717d639678e08ab52c14293fa64d260db812c89ed43a5a7cc8" -} diff --git a/.sqlx/query-8662e89e31b58def986e1a155cd68ebe8d0b64fe8bf6dafb9d92768397fbcecd.json b/.sqlx/query-8662e89e31b58def986e1a155cd68ebe8d0b64fe8bf6dafb9d92768397fbcecd.json deleted file mode 100644 index 82b7e023c7..0000000000 --- a/.sqlx/query-8662e89e31b58def986e1a155cd68ebe8d0b64fe8bf6dafb9d92768397fbcecd.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO group_user (user_id, group_id) VALUES ($1, $2)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8", - "Int8" - ] - }, - "nullable": [] - }, - "hash": "8662e89e31b58def986e1a155cd68ebe8d0b64fe8bf6dafb9d92768397fbcecd" -} diff --git a/.sqlx/query-8e70e158b41d640eba1333c0aea4f06bc8e8fce23ee336d64f8608f0e7eefc8c.json b/.sqlx/query-8e70e158b41d640eba1333c0aea4f06bc8e8fce23ee336d64f8608f0e7eefc8c.json deleted file mode 100644 index d62bfc64a5..0000000000 --- a/.sqlx/query-8e70e158b41d640eba1333c0aea4f06bc8e8fce23ee336d64f8608f0e7eefc8c.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO group_user (group_id, user_id) VALUES (1, $1)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [] - }, - "hash": "8e70e158b41d640eba1333c0aea4f06bc8e8fce23ee336d64f8608f0e7eefc8c" -} diff --git a/.sqlx/query-46fb95097c4cf79cdea7ef38c10cf8ca71f37bd60944f1b133a1ccd3a5fef98b.json b/.sqlx/query-93ac6465cd4f3100d8e3b6723d9fb4e6bb1502935468d1459e1693513c40821b.json similarity index 96% rename from .sqlx/query-46fb95097c4cf79cdea7ef38c10cf8ca71f37bd60944f1b133a1ccd3a5fef98b.json rename to .sqlx/query-93ac6465cd4f3100d8e3b6723d9fb4e6bb1502935468d1459e1693513c40821b.json index 0a6f59c7c3..76a93fcc47 100644 --- a/.sqlx/query-46fb95097c4cf79cdea7ef38c10cf8ca71f37bd60944f1b133a1ccd3a5fef98b.json +++ b/.sqlx/query-93ac6465cd4f3100d8e3b6723d9fb4e6bb1502935468d1459e1693513c40821b.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE \"settings\" SET openid_enabled = $1, wireguard_enabled = $2, webhooks_enabled = $3, worker_enabled = $4, challenge_template = $5, instance_name = $6, main_logo_url = $7, nav_logo_url = $8, smtp_server = $9, smtp_port = $10, smtp_encryption = $11, smtp_user = $12, smtp_password = $13, smtp_sender = $14, smtp_authentication = $15, smtp_oauth_issuer_url = $16, smtp_oauth_client_id = $17, smtp_oauth_client_secret = $18, smtp_oauth_refresh_token = $19, enrollment_vpn_step_optional = $20, enrollment_welcome_message = $21, enrollment_welcome_email = $22, enrollment_welcome_email_subject = $23, enrollment_use_welcome_message_as_email = $24, enrollment_send_welcome_email = $25, uuid = $26, ldap_url = $27, ldap_bind_username = $28, ldap_bind_password = $29, ldap_group_search_base = $30, ldap_user_search_base = $31, ldap_user_obj_class = $32, ldap_group_obj_class = $33, ldap_username_attr = $34, ldap_groupname_attr = $35, ldap_group_member_attr = $36, ldap_member_attr = $37, ldap_use_starttls = $38, ldap_tls_verify_cert = $39, openid_create_account = $40, license = $41, gateway_disconnect_notifications_enabled = $42, gateway_disconnect_notifications_inactivity_threshold = $43, gateway_disconnect_notifications_reconnect_notification_enabled = $44, ldap_sync_status = $45, ldap_enabled = $46, ldap_sync_enabled = $47, ldap_is_authoritative = $48, ldap_sync_interval = $49, ldap_user_auxiliary_obj_classes = $50, ldap_uses_ad = $51, ldap_user_rdn_attr = $52, ldap_sync_groups = $53, ldap_remote_enrollment_enabled = $54, ldap_remote_enrollment_send_invite = $55, openid_username_handling = $56, defguard_url = $57, default_admin_group_name = $58, authentication_period_days = $59, mfa_code_timeout_seconds = $60, public_proxy_url = $61, default_admin_id = $62, secret_key = $63, openid_signing_key_der = $64, enable_stats_purge = $65, stats_purge_frequency_hours = $66, stats_purge_threshold_days = $67, enrollment_token_timeout_hours = $68, password_reset_token_timeout_hours = $69, enrollment_session_timeout_minutes = $70, password_reset_session_timeout_minutes = $71, ldap_sync_account_status = $72 WHERE id = 1", + "query": "UPDATE \"settings\" SET openid_enabled = $1, wireguard_enabled = $2, webhooks_enabled = $3, worker_enabled = $4, challenge_template = $5, instance_name = $6, main_logo_url = $7, nav_logo_url = $8, smtp_server = $9, smtp_port = $10, smtp_encryption = $11, smtp_user = $12, smtp_password = $13, smtp_sender = $14, smtp_authentication = $15, smtp_oauth_issuer_url = $16, smtp_oauth_client_id = $17, smtp_oauth_client_secret = $18, smtp_oauth_refresh_token = $19, enrollment_vpn_step_optional = $20, enrollment_welcome_message = $21, enrollment_welcome_email = $22, enrollment_welcome_email_subject = $23, enrollment_use_welcome_message_as_email = $24, enrollment_send_welcome_email = $25, uuid = $26, ldap_url = $27, ldap_bind_username = $28, ldap_bind_password = $29, ldap_group_search_base = $30, ldap_user_search_base = $31, ldap_user_obj_class = $32, ldap_group_obj_class = $33, ldap_username_attr = $34, ldap_groupname_attr = $35, ldap_group_member_attr = $36, ldap_member_attr = $37, ldap_use_starttls = $38, ldap_tls_verify_cert = $39, openid_create_account = $40, license = $41, gateway_disconnect_notifications_enabled = $42, gateway_disconnect_notifications_inactivity_threshold = $43, gateway_disconnect_notifications_reconnect_notification_enabled = $44, ldap_sync_status = $45, ldap_enabled = $46, ldap_sync_enabled = $47, ldap_is_authoritative = $48, ldap_sync_interval = $49, ldap_user_auxiliary_obj_classes = $50, ldap_uses_ad = $51, ldap_user_rdn_attr = $52, ldap_sync_groups = $53, ldap_remote_enrollment_enabled = $54, ldap_remote_enrollment_send_invite = $55, openid_username_handling = $56, defguard_url = $57, default_admin_group_name = $58, authentication_period_days = $59, mfa_code_timeout_seconds = $60, public_proxy_url = $61, default_admin_id = $62, secret_key = $63, openid_signing_key_der = $64, enable_stats_purge = $65, stats_purge_frequency_hours = $66, stats_purge_threshold_days = $67, enrollment_token_timeout_hours = $68, password_reset_token_timeout_hours = $69, enrollment_session_timeout_minutes = $70, password_reset_session_timeout_minutes = $71, ldap_sync_account_status = $72, smtp_oauth_tenant_id = $73 WHERE id = 1", "describe": { "columns": [], "parameters": { @@ -119,10 +119,11 @@ "Int4", "Int4", "Int4", - "Bool" + "Bool", + "Text" ] }, "nullable": [] }, - "hash": "46fb95097c4cf79cdea7ef38c10cf8ca71f37bd60944f1b133a1ccd3a5fef98b" + "hash": "93ac6465cd4f3100d8e3b6723d9fb4e6bb1502935468d1459e1693513c40821b" } diff --git a/.sqlx/query-e0a5e5060afa2628112ae29b8a51a2f9a2abcb8d044ca90a536c6e567e320d69.json b/.sqlx/query-e0a5e5060afa2628112ae29b8a51a2f9a2abcb8d044ca90a536c6e567e320d69.json deleted file mode 100644 index 6fff00d5e4..0000000000 --- a/.sqlx/query-e0a5e5060afa2628112ae29b8a51a2f9a2abcb8d044ca90a536c6e567e320d69.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO group_user (group_id, user_id) VALUES (1, $1), (1, $2)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8", - "Int8" - ] - }, - "nullable": [] - }, - "hash": "e0a5e5060afa2628112ae29b8a51a2f9a2abcb8d044ca90a536c6e567e320d69" -} diff --git a/Cargo.lock b/Cargo.lock index 1847fe104a..e9efe6b9cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1442,10 +1442,10 @@ dependencies = [ "bytes", "chrono", "claims", + "css-inline", "defguard_certs", "defguard_common", "defguard_grpc_tls", - "defguard_mail", "defguard_proto", "defguard_static_ip", "defguard_version", @@ -1453,18 +1453,23 @@ dependencies = [ "futures", "humantime", "hyper-util", + "image", "ipnetwork", "jsonwebkey", "jsonwebtoken", "ldap3", + "lettre", "matches", "md4", "model_derive", + "mrml", "openidconnect", "parse_link_header", "paste", "pgp", "prost", + "pulldown-cmark", + "qrforge", "rand 0.8.6", "regex", "reqwest", @@ -1588,31 +1593,6 @@ dependencies = [ "x509-parser 0.18.1", ] -[[package]] -name = "defguard_mail" -version = "0.0.0" -dependencies = [ - "chrono", - "claims", - "css-inline", - "defguard_common", - "humantime", - "image", - "lettre", - "mrml", - "openidconnect", - "pulldown-cmark", - "qrforge", - "reqwest", - "serde", - "serde_json", - "sqlx", - "tera", - "thiserror 2.0.18", - "tokio", - "tracing", -] - [[package]] name = "defguard_proto" version = "0.0.0" @@ -1639,7 +1619,6 @@ dependencies = [ "defguard_common", "defguard_core", "defguard_grpc_tls", - "defguard_mail", "defguard_proto", "defguard_version", "hyper-rustls", diff --git a/Cargo.toml b/Cargo.toml index bedd41b8d7..1fea50c014 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,6 @@ defguard_static_ip = { path = "./crates/defguard_static_ip", version = "0.0.0" } defguard_core = { path = "./crates/defguard_core", version = "0.0.0" } defguard_event_logger = { path = "./crates/defguard_event_logger", version = "0.0.0" } defguard_gateway_manager = { path = "./crates/defguard_gateway_manager", version = "0.0.0" } -defguard_mail = { path = "./crates/defguard_mail", version = "0.0.0" } defguard_proto = { path = "./crates/defguard_proto", version = "0.0.0" } defguard_proxy_manager = { path = "./crates/defguard_proxy_manager", version = "0.0.0" } defguard_session_manager = { path = "./crates/defguard_session_manager", version = "0.0.0" } diff --git a/crates/defguard_common/src/db/models/settings/mod.rs b/crates/defguard_common/src/db/models/settings/mod.rs index e42df9cbe1..6c5e303c42 100644 --- a/crates/defguard_common/src/db/models/settings/mod.rs +++ b/crates/defguard_common/src/db/models/settings/mod.rs @@ -481,7 +481,7 @@ impl Settings { challenge_template, instance_name, main_logo_url, nav_logo_url, smtp_server, \ smtp_port, smtp_encryption, smtp_user, smtp_password, smtp_sender, \ smtp_authentication, smtp_oauth_issuer_url, smtp_oauth_client_id, \ - smtp_oauth_client_secret, smtp_oauth_refresh_token, \ + smtp_oauth_client_secret, smtp_oauth_refresh_token, smtp_oauth_tenant_id, \ enrollment_vpn_step_optional, enrollment_welcome_message, \ enrollment_welcome_email, enrollment_welcome_email_subject, \ enrollment_use_welcome_message_as_email, enrollment_send_welcome_email, \ @@ -627,7 +627,8 @@ impl Settings { password_reset_token_timeout_hours = $69, \ enrollment_session_timeout_minutes = $70, \ password_reset_session_timeout_minutes = $71, \ - ldap_sync_account_status = $72 \ + ldap_sync_account_status = $72, \ + smtp_oauth_tenant_id = $73 \ WHERE id = 1", self.openid_enabled, self.wireguard_enabled, @@ -701,6 +702,7 @@ impl Settings { self.enrollment_session_timeout_minutes, self.password_reset_session_timeout_minutes, self.ldap_sync_account_status, + self.smtp.oauth_tenant_id, ) .execute(executor) .await?; diff --git a/crates/defguard_common/src/db/models/settings/smtp.rs b/crates/defguard_common/src/db/models/settings/smtp.rs index c8e5982acb..6223400017 100644 --- a/crates/defguard_common/src/db/models/settings/smtp.rs +++ b/crates/defguard_common/src/db/models/settings/smtp.rs @@ -82,6 +82,10 @@ pub struct SmtpSettings { #[sqlx(rename = "smtp_oauth_refresh_token")] #[patch(attribute(serde(rename = "smtp_oauth_refresh_token")))] pub oauth_refresh_token: Option, + #[serde(rename = "smtp_oauth_tenant_id")] + #[sqlx(rename = "smtp_oauth_tenant_id")] + #[patch(attribute(serde(rename = "smtp_oauth_tenant_id")))] + pub oauth_tenant_id: Option, } impl SmtpSettings { @@ -105,6 +109,39 @@ impl SmtpSettings { Ok(()) } + + /// Check if all required options are properly configured. + /// This is meant to be used to check if sending emails is enabled in current instance. + #[must_use] + pub fn is_configured(&self) -> bool { + let string_not_empty = |string: &String| !string.is_empty(); + let secret_not_empty = |secret: &SecretStringWrapper| !secret.expose_secret().is_empty(); + + self.port.is_some() + && self.server.as_ref().is_some_and(string_not_empty) + && self.sender.as_ref().is_some_and(string_not_empty) + && match self.authentication { + SmtpAuthentication::None => true, + SmtpAuthentication::Login => { + self.user.as_ref().is_some_and(string_not_empty) + && self.password.as_ref().is_some_and(secret_not_empty) + } + SmtpAuthentication::XOAuth2 => { + self.oauth_issuer_url.as_ref().is_some_and(string_not_empty) + && self.oauth_client_id.as_ref().is_some_and(string_not_empty) + && self + .oauth_client_secret + .as_ref() + .is_some_and(secret_not_empty) + } + } + } + + /// Returns `true` is SMTP authentication is using XOAUTH2. + #[must_use] + pub fn is_xoauth2(&self) -> bool { + matches!(self.authentication, SmtpAuthentication::XOAuth2) + } } // Implement manually to avoid exposing secrets. @@ -119,6 +156,7 @@ impl fmt::Debug for SmtpSettings { .field("authentication", &self.authentication) .field("oauth_issuer_url", &self.oauth_issuer_url) .field("oauth_client_id", &self.oauth_client_id) + .field("oauth_tenant_id", &self.oauth_tenant_id) .finish_non_exhaustive() } } diff --git a/crates/defguard_core/Cargo.toml b/crates/defguard_core/Cargo.toml index 71005e69bb..8512c6d24d 100644 --- a/crates/defguard_core/Cargo.toml +++ b/crates/defguard_core/Cargo.toml @@ -10,7 +10,6 @@ rust-version.workspace = true [dependencies] # internal crates defguard_common = { workspace = true } -defguard_mail = { workspace = true } defguard_proto = { workspace = true } defguard_web_ui = { workspace = true } defguard_version = { workspace = true } @@ -19,6 +18,14 @@ defguard_certs = { workspace = true } defguard_grpc_tls = { workspace = true } defguard_static_ip = { workspace = true } +# mail (moved in-crate from the former defguard_mail crate) +lettre.workspace = true +pulldown-cmark.workspace = true +css-inline = "0.20" +image = "0.25" # match with qrforge +mrml = "6.0" +qrforge = { version = "0.1", default-features = false, features = ["image"] } + # external dependencies anyhow = { workspace = true } axum = { workspace = true } diff --git a/crates/defguard_core/src/db/models/enrollment.rs b/crates/defguard_core/src/db/models/enrollment.rs index a7608c4292..75a9e65e69 100644 --- a/crates/defguard_core/src/db/models/enrollment.rs +++ b/crates/defguard_core/src/db/models/enrollment.rs @@ -1,3 +1,4 @@ +use crate::mail::templates; use chrono::{NaiveDateTime, TimeDelta, Utc}; use defguard_common::{ VERSION, @@ -8,7 +9,6 @@ use defguard_common::{ random::gen_alphanumeric, types::UrlParseError, }; -use defguard_mail::templates; use sqlx::{PgConnection, PgExecutor, PgPool, query, query_as}; use tera::Context; use thiserror::Error; diff --git a/crates/defguard_core/src/enrollment_management.rs b/crates/defguard_core/src/enrollment_management.rs index 6da9611a60..e125ed247f 100644 --- a/crates/defguard_core/src/enrollment_management.rs +++ b/crates/defguard_core/src/enrollment_management.rs @@ -1,8 +1,8 @@ +use crate::mail::templates::{desktop_start_mail, new_account_mail}; use defguard_common::db::{ Id, models::{Settings, user::User}, }; -use defguard_mail::templates::{desktop_start_mail, new_account_mail}; use reqwest::Url; use sqlx::{PgConnection, PgExecutor, PgPool}; diff --git a/crates/defguard_core/src/enterprise/mod.rs b/crates/defguard_core/src/enterprise/mod.rs index ea1618dd26..a3be740fad 100644 --- a/crates/defguard_core/src/enterprise/mod.rs +++ b/crates/defguard_core/src/enterprise/mod.rs @@ -8,6 +8,7 @@ pub mod handlers; pub mod ldap; pub mod license; pub mod limits; +pub mod oauth2; pub mod posture; pub mod snat; mod utils; @@ -17,6 +18,9 @@ use limits::get_counts; use crate::enterprise::license::LicenseTier; +/// Shared HTTP request timeout for enterprise outbound calls (e.g. OAuth2 token endpoints). +const REQUEST_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10); + /// Helper function to gate features which require a base license (Team or Business tier) #[must_use] pub fn is_business_license_active() -> bool { diff --git a/crates/defguard_core/src/enterprise/oauth2/microsoft.rs b/crates/defguard_core/src/enterprise/oauth2/microsoft.rs new file mode 100644 index 0000000000..b72b864fa3 --- /dev/null +++ b/crates/defguard_core/src/enterprise/oauth2/microsoft.rs @@ -0,0 +1,67 @@ +use std::time::{Duration, SystemTime}; + +use serde::Deserialize; + +use super::super::REQUEST_TIMEOUT; + +const TOKEN_URL: &str = "https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"; +const GRANT_TYPE: &str = "client_credentials"; + +#[derive(Deserialize)] +pub struct TokenResponse { + // token_type: String, + access_token: String, + expires_in: u64, +} + +impl TokenResponse { + #[must_use] + pub fn access_token(&self) -> String { + self.access_token.clone() + } + + #[must_use] + pub fn expires_in(&self) -> SystemTime { + SystemTime::UNIX_EPOCH + Duration::from_secs(self.expires_in) + } +} + +pub struct MicrosoftOAuth2 { + client_id: String, + client_secret: String, + tenant_id: String, + scope: String, +} + +impl MicrosoftOAuth2 { + #[must_use] + pub fn new(client_id: String, client_secret: String, tenant_id: String, scope: String) -> Self { + Self { + client_id, + client_secret, + tenant_id, + scope, + } + } + + pub async fn fetch_access_token(&self) -> Result { + debug!("Querying Microsoft for OAuth2 access token."); + let token_url = TOKEN_URL.replace("{tenant_id}", &self.tenant_id); + let client = reqwest::Client::new(); + let response = client + .post(&token_url) + .form(&[ + ("client_id", self.client_id.as_str()), + ("client_secret", self.client_secret.as_str()), + ("scope", self.scope.as_str()), + ("grant_type", GRANT_TYPE), + ]) + .timeout(REQUEST_TIMEOUT) + .send() + .await?; + let token_response = response.json::().await?; + debug!("Fetched Microsoft OAuth2 access token"); + + Ok(token_response) + } +} diff --git a/crates/defguard_core/src/enterprise/oauth2/mod.rs b/crates/defguard_core/src/enterprise/oauth2/mod.rs new file mode 100644 index 0000000000..590d854798 --- /dev/null +++ b/crates/defguard_core/src/enterprise/oauth2/mod.rs @@ -0,0 +1,124 @@ +pub mod microsoft; + +use defguard_common::db::models::settings::smtp::SmtpSettings; +use openidconnect::{ + ClientId, ClientSecret, IssuerUrl, OAuth2TokenResponse, RefreshToken, + core::{CoreClient, CoreProviderMetadata}, + reqwest::{ClientBuilder, redirect::Policy}, +}; +use tracing::{debug, error}; + +use self::microsoft::MicrosoftOAuth2; +use super::is_business_license_active; + +const OUTLOOK_DEFAULT_SCOPE: &str = "https://outlook.office365.com/.default"; + +#[derive(Debug, thiserror::Error)] +pub enum OAuth2Error { + #[error("OAuth2 not configured")] + NotConfigured, + #[error(transparent)] + Configuration(#[from] openidconnect::ConfigurationError), + #[error("Open ID discovery")] + OpenIDDiscovery, + #[error("Refresh token exchange")] + RefreshTokenExchange, + #[error(transparent)] + Reqwest(#[from] openidconnect::reqwest::Error), + #[error(transparent)] + Url(#[from] openidconnect::url::ParseError), +} + +/// Obtain access token from Google. +async fn google_access_token(smtp_settings: &mut SmtpSettings) -> Result { + let (Some(issuer_url), Some(client_id), Some(client_secret), Some(refresh_token)) = ( + &smtp_settings.oauth_issuer_url, + &smtp_settings.oauth_client_id, + &smtp_settings.oauth_client_secret, + &smtp_settings.oauth_refresh_token, + ) else { + error!("Google SMTP XOAUTH2 requires: issuer URL, client ID, client secret, refresh token"); + return Err(OAuth2Error::NotConfigured); + }; + let issuer_url = IssuerUrl::new(issuer_url.into())?; + let client_id = ClientId::new(client_id.into()); + let client_secret = ClientSecret::new(client_secret.expose_secret().into()); + let refresh_token = RefreshToken::new(refresh_token.into()); + + let http_client = ClientBuilder::new() + // Following redirects opens the client up to SSRF vulnerabilities. + .redirect(Policy::none()) + .build()?; + + let provider_metadata = CoreProviderMetadata::discover_async(issuer_url, &http_client) + .await + .map_err(|err| { + error!("Failed OpenID Connect Discovery: {err}"); + OAuth2Error::OpenIDDiscovery + })?; + + let client = + CoreClient::from_provider_metadata(provider_metadata, client_id, Some(client_secret)); + + let token_response = client + .exchange_refresh_token(&refresh_token)? + .request_async(&http_client) + .await + .map_err(|err| { + error!("Failed to fetch token: {err}"); + OAuth2Error::RefreshTokenExchange + })?; + + let access_token = token_response.access_token().secret(); + debug!("Got access token"); + if let Some(expires_in) = token_response.expires_in() { + debug!("Access token expires in {expires_in:?}"); + } + if let Some(refresh_token) = token_response.refresh_token() { + debug!("Got refresh token"); + // TODO: use `self.set_oauth_refresh_token` + smtp_settings.oauth_refresh_token = Some(refresh_token.secret().into()); + } + Ok(access_token.clone()) +} + +/// Obtain access token from Microsoft. +async fn microsoft_access_token(smtp_settings: &mut SmtpSettings) -> Result { + let (Some(client_id), Some(client_secret), Some(tenant_id)) = ( + &smtp_settings.oauth_client_id, + &smtp_settings.oauth_client_secret, + &smtp_settings.oauth_tenant_id, + ) else { + error!("Microsoft SMTP XOAUTH2 requires: tenant ID, client ID, client secret"); + return Err(OAuth2Error::NotConfigured); + }; + + let oauth2 = MicrosoftOAuth2::new( + client_id.clone(), + client_secret.expose_secret().to_string(), + tenant_id.clone(), + OUTLOOK_DEFAULT_SCOPE.into(), + ); + let token = oauth2.fetch_access_token().await?; + + Ok(token.access_token()) +} + +/// Obtain access token for XOAUTH2 authentication. +pub async fn xoauth2_access_token(smtp_settings: &mut SmtpSettings) -> Result { + if !is_business_license_active() { + error!("SMTP XOAUTH2 requires business license"); + } else if let Some(issuer_url) = &smtp_settings.oauth_issuer_url { + // FIXME: baked URLs + if issuer_url == "https://login.microsoftonline.com/common" { + return microsoft_access_token(smtp_settings).await; + } + if issuer_url == "https://accounts.google.com" { + return google_access_token(smtp_settings).await; + } + } else { + error!("SMTP XOAUTH2 requires: issuer URL"); + } + + Err(OAuth2Error::NotConfigured) +} diff --git a/crates/defguard_core/src/error.rs b/crates/defguard_core/src/error.rs index fb90765672..33fd5148d2 100644 --- a/crates/defguard_core/src/error.rs +++ b/crates/defguard_core/src/error.rs @@ -1,3 +1,4 @@ +use crate::mail::templates::TemplateError; use axum::http::StatusCode; use defguard_common::{ db::models::{ @@ -7,7 +8,6 @@ use defguard_common::{ }, types::UrlParseError, }; -use defguard_mail::templates::TemplateError; use defguard_static_ip::error::StaticIpError; use thiserror::Error; use tokio::sync::mpsc::error::SendError; diff --git a/crates/defguard_core/src/grpc/proxy/client_mfa.rs b/crates/defguard_core/src/grpc/proxy/client_mfa.rs index fc12c9dd56..2f5e55bf2c 100644 --- a/crates/defguard_core/src/grpc/proxy/client_mfa.rs +++ b/crates/defguard_core/src/grpc/proxy/client_mfa.rs @@ -4,6 +4,7 @@ use std::{ time::Duration, }; +use crate::mail::templates::mfa_code_mail; use chrono::Utc; use defguard_common::{ auth::claims::{Claims, ClaimsType}, @@ -18,7 +19,6 @@ use defguard_common::{ }, types::user_info::UserInfo, }; -use defguard_mail::templates::mfa_code_mail; use defguard_proto::{ client_types::{ ClientMfaFinishRequest, ClientMfaFinishResponse, ClientMfaStartRequest, diff --git a/crates/defguard_core/src/handlers/auth.rs b/crates/defguard_core/src/handlers/auth.rs index 696ad58945..021c5f46b8 100644 --- a/crates/defguard_core/src/handlers/auth.rs +++ b/crates/defguard_core/src/handlers/auth.rs @@ -1,5 +1,6 @@ use std::net::IpAddr; +use crate::mail::templates::{mfa_activation_mail, mfa_code_mail, mfa_configured_mail}; use axum::{ extract::{Json, Path, State}, http::StatusCode, @@ -19,7 +20,6 @@ use defguard_common::{ }, types::user_info::UserInfo, }; -use defguard_mail::templates::{mfa_activation_mail, mfa_code_mail, mfa_configured_mail}; use sqlx::{PgPool, types::Uuid}; use time::Duration; use uaparser::Parser; diff --git a/crates/defguard_core/src/handlers/mail.rs b/crates/defguard_core/src/handlers/mail.rs index 9a0f35c67e..6be9f63abe 100644 --- a/crates/defguard_core/src/handlers/mail.rs +++ b/crates/defguard_core/src/handlers/mail.rs @@ -1,13 +1,13 @@ +use crate::mail::{ + Attachment, + templates::{self, SUPPORT_EMAIL_ADDRESS}, +}; use axum::{ extract::{Json, State}, http::StatusCode, }; use chrono::Utc; use defguard_common::db::models::{User, gateway::Gateway, proxy::Proxy}; -use defguard_mail::{ - mail::Attachment, - templates::{self, SUPPORT_EMAIL_ADDRESS}, -}; use serde_json::json; use sqlx::query_scalar; use tera::Context; @@ -134,7 +134,7 @@ pub enum MailError { #[error("Database error: {0}")] Db(#[from] sqlx::Error), #[error("Template error: {0}")] - Template(#[from] defguard_mail::templates::TemplateError), + Template(#[from] crate::mail::templates::TemplateError), } pub async fn send_gateway_disconnected_email( diff --git a/crates/defguard_core/src/handlers/network_devices.rs b/crates/defguard_core/src/handlers/network_devices.rs index 1b9d343ba5..c23499de69 100644 --- a/crates/defguard_core/src/handlers/network_devices.rs +++ b/crates/defguard_core/src/handlers/network_devices.rs @@ -3,6 +3,7 @@ use std::{ str::FromStr, }; +use crate::mail::templates::{TemplateLocation, new_device_added_mail}; use axum::{ extract::{Json, Path, Query, State}, http::StatusCode, @@ -20,7 +21,6 @@ use defguard_common::{ }, utils::{SplitIp, split_ip}, }; -use defguard_mail::templates::{TemplateLocation, new_device_added_mail}; use serde_json::json; use sqlx::PgConnection; diff --git a/crates/defguard_core/src/handlers/openid_flow.rs b/crates/defguard_core/src/handlers/openid_flow.rs index 1fe4334450..453b5aa218 100644 --- a/crates/defguard_core/src/handlers/openid_flow.rs +++ b/crates/defguard_core/src/handlers/openid_flow.rs @@ -3,6 +3,7 @@ use std::{ ops::{Deref, DerefMut}, }; +use crate::mail::templates::new_device_oidc_login_mail; use axum::{ Form, extract::{FromRef, FromRequestParts, Query, State}, @@ -22,7 +23,6 @@ use defguard_common::db::{ oauth2client::OAuth2Client, }, }; -use defguard_mail::templates::new_device_oidc_login_mail; use openidconnect::{ AccessToken, AdditionalClaims, Audience, AuthUrl, AuthorizationCode, EmptyAdditionalProviderMetadata, EmptyExtraTokenFields, EndUserEmail, EndUserFamilyName, diff --git a/crates/defguard_core/src/handlers/user.rs b/crates/defguard_core/src/handlers/user.rs index e01df85447..bf34f7f051 100644 --- a/crates/defguard_core/src/handlers/user.rs +++ b/crates/defguard_core/src/handlers/user.rs @@ -1,5 +1,6 @@ use std::{collections::HashSet, fmt}; +use crate::mail::templates; use axum::{ extract::{Json, Path, State}, http::StatusCode, @@ -12,7 +13,6 @@ use defguard_common::{ }, types::{group_diff::GroupDiff, user_info::UserInfo}, }; -use defguard_mail::templates; use humantime::parse_duration; use serde::{Deserialize, Serialize}; use serde_json::json; diff --git a/crates/defguard_core/src/handlers/wireguard.rs b/crates/defguard_core/src/handlers/wireguard.rs index 15396f6568..a4caee7376 100644 --- a/crates/defguard_core/src/handlers/wireguard.rs +++ b/crates/defguard_core/src/handlers/wireguard.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; +use crate::mail::templates::{TemplateLocation, new_device_added_mail}; use axum::{ extract::{Json, Path, State}, http::StatusCode, @@ -16,7 +17,6 @@ use defguard_common::{ }, utils::parse_network_address_list, }; -use defguard_mail::templates::{TemplateLocation, new_device_added_mail}; use ipnetwork::IpNetwork; use serde_json::{Value, json}; use sqlx::PgPool; diff --git a/crates/defguard_core/src/headers.rs b/crates/defguard_core/src/headers.rs index 24b9757714..e9404e48f1 100644 --- a/crates/defguard_core/src/headers.rs +++ b/crates/defguard_core/src/headers.rs @@ -6,6 +6,7 @@ use std::{ }, }; +use crate::mail::templates::{SessionContext, TemplateError, new_device_login_mail}; use axum::{ body::Body, http::{HeaderName, HeaderValue, Request, header}, @@ -16,7 +17,6 @@ use defguard_common::db::{ Id, models::{DeviceLoginEvent, User}, }; -use defguard_mail::templates::{SessionContext, TemplateError, new_device_login_mail}; use sqlx::PgPool; use uaparser::{Client, Parser, UserAgentParser}; diff --git a/crates/defguard_core/src/letsencrypt.rs b/crates/defguard_core/src/letsencrypt.rs index ef1a00b8f3..f52c4f830f 100644 --- a/crates/defguard_core/src/letsencrypt.rs +++ b/crates/defguard_core/src/letsencrypt.rs @@ -1,5 +1,6 @@ use std::{collections::HashMap, sync::Arc, time::Duration}; +use crate::mail::templates; use chrono::{NaiveDateTime, TimeDelta, Utc}; use defguard_common::{ VERSION, @@ -10,7 +11,6 @@ use defguard_common::{ types::proxy::ProxyControlMessage, }; use defguard_grpc_tls::certs::proxy_mtls_channel; -use defguard_mail::templates; use defguard_proto::proxy::{ AcmeChallenge, AcmeLogs, AcmeStep, acme_issue_event, proxy_client::ProxyClient, }; diff --git a/crates/defguard_core/src/lib.rs b/crates/defguard_core/src/lib.rs index eebad7afff..fb77cf53cd 100644 --- a/crates/defguard_core/src/lib.rs +++ b/crates/defguard_core/src/lib.rs @@ -208,6 +208,7 @@ pub mod handlers; pub mod headers; pub mod letsencrypt; pub mod location_management; +pub mod mail; pub mod setup_logs; pub mod support; pub mod updates; diff --git a/crates/defguard_mail/assets/apple.png b/crates/defguard_core/src/mail/assets/apple.png similarity index 100% rename from crates/defguard_mail/assets/apple.png rename to crates/defguard_core/src/mail/assets/apple.png diff --git a/crates/defguard_mail/assets/date.png b/crates/defguard_core/src/mail/assets/date.png similarity index 100% rename from crates/defguard_mail/assets/date.png rename to crates/defguard_core/src/mail/assets/date.png diff --git a/crates/defguard_mail/assets/defguard.png b/crates/defguard_core/src/mail/assets/defguard.png similarity index 100% rename from crates/defguard_mail/assets/defguard.png rename to crates/defguard_core/src/mail/assets/defguard.png diff --git a/crates/defguard_mail/assets/github.png b/crates/defguard_core/src/mail/assets/github.png similarity index 100% rename from crates/defguard_mail/assets/github.png rename to crates/defguard_core/src/mail/assets/github.png diff --git a/crates/defguard_mail/assets/google_play.png b/crates/defguard_core/src/mail/assets/google_play.png similarity index 100% rename from crates/defguard_mail/assets/google_play.png rename to crates/defguard_core/src/mail/assets/google_play.png diff --git a/crates/defguard_mail/assets/mastodon.png b/crates/defguard_core/src/mail/assets/mastodon.png similarity index 100% rename from crates/defguard_mail/assets/mastodon.png rename to crates/defguard_core/src/mail/assets/mastodon.png diff --git a/crates/defguard_mail/assets/new_account_1.png b/crates/defguard_core/src/mail/assets/new_account_1.png similarity index 100% rename from crates/defguard_mail/assets/new_account_1.png rename to crates/defguard_core/src/mail/assets/new_account_1.png diff --git a/crates/defguard_mail/assets/new_account_2.png b/crates/defguard_core/src/mail/assets/new_account_2.png similarity index 100% rename from crates/defguard_mail/assets/new_account_2.png rename to crates/defguard_core/src/mail/assets/new_account_2.png diff --git a/crates/defguard_mail/assets/otp.png b/crates/defguard_core/src/mail/assets/otp.png similarity index 100% rename from crates/defguard_mail/assets/otp.png rename to crates/defguard_core/src/mail/assets/otp.png diff --git a/crates/defguard_mail/assets/x.png b/crates/defguard_core/src/mail/assets/x.png similarity index 100% rename from crates/defguard_mail/assets/x.png rename to crates/defguard_core/src/mail/assets/x.png diff --git a/crates/defguard_mail/src/mail_context.rs b/crates/defguard_core/src/mail/mail_context.rs similarity index 100% rename from crates/defguard_mail/src/mail_context.rs rename to crates/defguard_core/src/mail/mail_context.rs diff --git a/crates/defguard_mail/src/mail.rs b/crates/defguard_core/src/mail/mod.rs similarity index 73% rename from crates/defguard_mail/src/mail.rs rename to crates/defguard_core/src/mail/mod.rs index e4a319961f..b181bd1492 100644 --- a/crates/defguard_mail/src/mail.rs +++ b/crates/defguard_core/src/mail/mod.rs @@ -1,3 +1,15 @@ +//! Handle email messages. +//! +//! Refer to: +//! - [RFC 2557](https://datatracker.ietf.org/doc/html/rfc2557) +//! - [Meaning of mulitpart](https://www.codestudy.net/blog/mail-multipart-alternative-vs-multipart-mixed/) + +pub(crate) mod mail_context; +mod qr; +pub mod templates; +#[cfg(test)] +mod tests; + use std::{str::FromStr, time::Duration}; use defguard_common::db::models::{ @@ -17,12 +29,36 @@ use sqlx::PgConnection; use tera::{Context, Tera, Value}; use tracing::{debug, error, info, warn}; -use super::{ - MailError, +use crate::enterprise::oauth2::xoauth2_access_token; + +#[derive(Debug, thiserror::Error)] +pub enum MailError { + #[error(transparent)] + Lettre(#[from] lettre::error::Error), + + #[error(transparent)] + Address(#[from] lettre::address::AddressError), + + #[error(transparent)] + Smtp(#[from] lettre::transport::smtp::Error), + + #[error(transparent)] + Sqlx(#[from] sqlx::Error), + + #[error("SMTP not configured")] + SmtpNotConfigured, + + #[error("Invalid port: {0}")] + InvalidPort(i32), + + #[error(transparent)] + OAuth2(#[from] crate::enterprise::oauth2::OAuth2Error), +} + +use self::{ mail_context::MailContext, qr::qr_png, templates::{DEFAULT_LANG, TemplateError}, - xoauth2::obtain_access_token, }; #[derive(Debug)] @@ -48,18 +84,18 @@ impl From for SinglePart { const SMTP_TIMEOUT: Duration = Duration::from_secs(15); // Template images. -static DEFGUARD_LOGO: &[u8] = include_bytes!("../assets/defguard.png"); -static GITHUB_LOGO: &[u8] = include_bytes!("../assets/github.png"); -static MASTODON_LOGO: &[u8] = include_bytes!("../assets/mastodon.png"); -static X_LOGO: &[u8] = include_bytes!("../assets/x.png"); +static DEFGUARD_LOGO: &[u8] = include_bytes!("assets/defguard.png"); +static GITHUB_LOGO: &[u8] = include_bytes!("assets/github.png"); +static MASTODON_LOGO: &[u8] = include_bytes!("assets/mastodon.png"); +static X_LOGO: &[u8] = include_bytes!("assets/x.png"); // MFA code -static DATE_ICON: &[u8] = include_bytes!("../assets/date.png"); -static OTP_ICON: &[u8] = include_bytes!("../assets/otp.png"); +static DATE_ICON: &[u8] = include_bytes!("assets/date.png"); +static OTP_ICON: &[u8] = include_bytes!("assets/otp.png"); // New account -static NEW_ACCOUNT_1: &[u8] = include_bytes!("../assets/new_account_1.png"); -static NEW_ACCOUNT_2: &[u8] = include_bytes!("../assets/new_account_2.png"); -static GOOGLE_PLAY: &[u8] = include_bytes!("../assets/google_play.png"); -static APPLE: &[u8] = include_bytes!("../assets/apple.png"); +static NEW_ACCOUNT_1: &[u8] = include_bytes!("assets/new_account_1.png"); +static NEW_ACCOUNT_2: &[u8] = include_bytes!("assets/new_account_2.png"); +static GOOGLE_PLAY: &[u8] = include_bytes!("assets/google_play.png"); +static APPLE: &[u8] = include_bytes!("assets/apple.png"); /// Mail message #[derive(Debug)] @@ -78,7 +114,7 @@ pub struct Mail { impl Mail { /// Create new [`Mail`]. #[must_use] - pub fn new(to: T, subject: String, html: String, text: String) -> Self + pub fn new(to: T, subject: String, html: String, text: String) -> Mail where T: Into, { @@ -257,7 +293,7 @@ impl Mail { builder.credentials(Credentials::new(user, password.expose_secret().into())); } SmtpAuthentication::XOAuth2 => { - let code = obtain_access_token(&mut smtp_settings).await?; + let code = xoauth2_access_token(&mut smtp_settings).await?; let Some(sender) = smtp_settings.sender else { error!("XOAUTH2 requires sender email address"); return Err(MailError::SmtpNotConfigured); @@ -318,30 +354,30 @@ impl MailMessage { } } match self { - Self::Test => "Defguard: Test message".to_owned(), - Self::Welcome => WELCOME_EMAIL_SUBJECT.to_owned(), - Self::SupportData => "Defguard: Support data".to_owned(), - Self::DesktopStart => "Defguard: Desktop client configuration".to_owned(), - Self::NewAccount => "Defguard: User enrollment".to_owned(), - Self::NewDevice => "Defguard: new device added to your account".to_owned(), - Self::NewDeviceLogin => "Defguard: New device logged in to your account".to_owned(), - Self::NewDeviceOIDCLogin => "New login to OIDC application".to_owned(), - Self::GatewayDisconnect => "Defguard: Gateway disconnected".to_owned(), - Self::GatewayReconnect => "Defguard: Gateway reconnected".to_owned(), - Self::MFAActivation => "Multi-Factor Authentication activation".to_owned(), + Self::Test => "Defguard: Test message".to_string(), + Self::Welcome => WELCOME_EMAIL_SUBJECT.to_string(), + Self::SupportData => "Defguard: Support data".to_string(), + Self::DesktopStart => "Defguard: Desktop client configuration".to_string(), + Self::NewAccount => "Defguard: User enrollment".to_string(), + Self::NewDevice => "Defguard: new device added to your account".to_string(), + Self::NewDeviceLogin => "Defguard: New device logged in to your account".to_string(), + Self::NewDeviceOIDCLogin => "New login to OIDC application".to_string(), + Self::GatewayDisconnect => "Defguard: Gateway disconnected".to_string(), + Self::GatewayReconnect => "Defguard: Gateway reconnected".to_string(), + Self::MFAActivation => "Multi-Factor Authentication activation".to_string(), Self::MFAConfigured { method } => { format!("Multi-Factor Authentication {method} has been activated") } - Self::MFACode => "Defguard: Multi-Factor Authentication code for login".to_owned(), - Self::PasswordReset => "Defguard: Password reset".to_owned(), - Self::PasswordResetDone => "Defguard: Password reset success".to_owned(), - Self::UserImportBlocked => "User import blocked".to_owned(), - Self::EnrollmentNotification => "Defguard: User enrollment completed".to_owned(), + Self::MFACode => "Defguard: Multi-Factor Authentication code for login".to_string(), + Self::PasswordReset => "Defguard: Password reset".to_string(), + Self::PasswordResetDone => "Defguard: Password reset success".to_string(), + Self::UserImportBlocked => "User import blocked".to_string(), + Self::EnrollmentNotification => "Defguard: User enrollment completed".to_string(), Self::LetsencryptCertRefreshFailed => { - "Defguard: automatic Let's Encrypt certificate refresh failed".to_owned() + "Defguard: automatic Let's Encrypt certificate refresh failed".to_string() } - Self::CertificateExpiration => "Defguard: Certificate expiration".to_owned(), - Self::CertificateExpired => "Defguard: Certificate has expired".to_owned(), + Self::CertificateExpiration => "Defguard: Certificate expiration".to_string(), + Self::CertificateExpired => "Defguard: Certificate has expired".to_string(), } } @@ -372,60 +408,60 @@ impl MailMessage { pub(crate) const fn mjml_template(&self) -> &str { match self { - Self::Test => include_str!("../templates/test.mjml"), - Self::Welcome => include_str!("../templates/enrollment-welcome.mjml"), - Self::SupportData => include_str!("../templates/support-data.mjml"), - Self::DesktopStart => include_str!("../templates/desktop-start.mjml"), - Self::NewAccount => include_str!("../templates/new-account.mjml"), - Self::NewDevice => include_str!("../templates/new-device.mjml"), - Self::NewDeviceLogin => include_str!("../templates/new-device-login.mjml"), - Self::NewDeviceOIDCLogin => include_str!("../templates/new-device-oidc-login.mjml"), - Self::GatewayDisconnect => include_str!("../templates/gateway-disconnected.mjml"), - Self::GatewayReconnect => include_str!("../templates/gateway-reconnected.mjml"), - Self::MFAActivation => include_str!("../templates/mfa-activation.mjml"), - Self::MFAConfigured { method: _ } => include_str!("../templates/mfa-configured.mjml"), - Self::MFACode => include_str!("../templates/mfa-code.mjml"), - Self::PasswordReset => include_str!("../templates/password-reset.mjml"), - Self::PasswordResetDone => include_str!("../templates/password-reset-done.mjml"), - Self::UserImportBlocked => include_str!("../templates/plain-notification.mjml"), + Self::Test => include_str!("templates/test.mjml"), + Self::Welcome => include_str!("templates/enrollment-welcome.mjml"), + Self::SupportData => include_str!("templates/support-data.mjml"), + Self::DesktopStart => include_str!("templates/desktop-start.mjml"), + Self::NewAccount => include_str!("templates/new-account.mjml"), + Self::NewDevice => include_str!("templates/new-device.mjml"), + Self::NewDeviceLogin => include_str!("templates/new-device-login.mjml"), + Self::NewDeviceOIDCLogin => include_str!("templates/new-device-oidc-login.mjml"), + Self::GatewayDisconnect => include_str!("templates/gateway-disconnected.mjml"), + Self::GatewayReconnect => include_str!("templates/gateway-reconnected.mjml"), + Self::MFAActivation => include_str!("templates/mfa-activation.mjml"), + Self::MFAConfigured { method: _ } => include_str!("templates/mfa-configured.mjml"), + Self::MFACode => include_str!("templates/mfa-code.mjml"), + Self::PasswordReset => include_str!("templates/password-reset.mjml"), + Self::PasswordResetDone => include_str!("templates/password-reset-done.mjml"), + Self::UserImportBlocked => include_str!("templates/plain-notification.mjml"), Self::EnrollmentNotification => { - include_str!("../templates/enrollment-admin-notification.mjml") + include_str!("templates/enrollment-admin-notification.mjml") } Self::LetsencryptCertRefreshFailed => { - include_str!("../templates/letsencrypt-cert-refresh-failed.mjml") + include_str!("templates/letsencrypt-cert-refresh-failed.mjml") } Self::CertificateExpiration | Self::CertificateExpired => { - include_str!("../templates/certificate-expiration.mjml") + include_str!("templates/certificate-expiration.mjml") } } } pub(crate) const fn text_template(&self) -> &str { match self { - Self::Test => include_str!("../templates/test.text"), - Self::Welcome => include_str!("../templates/enrollment-welcome.text"), - Self::SupportData => include_str!("../templates/support-data.text"), - Self::DesktopStart => include_str!("../templates/desktop-start.text"), - Self::NewAccount => include_str!("../templates/new-account.text"), - Self::NewDevice => include_str!("../templates/new-device.text"), - Self::NewDeviceLogin => include_str!("../templates/new-device-login.text"), - Self::NewDeviceOIDCLogin => include_str!("../templates/new-device-oidc-login.text"), - Self::GatewayDisconnect => include_str!("../templates/gateway-disconnected.text"), - Self::GatewayReconnect => include_str!("../templates/gateway-reconnected.text"), - Self::MFAActivation => include_str!("../templates/mfa-activation.text"), - Self::MFAConfigured { method: _ } => include_str!("../templates/mfa-configured.text"), - Self::MFACode => include_str!("../templates/mfa-code.text"), - Self::PasswordReset => include_str!("../templates/password-reset.text"), - Self::PasswordResetDone => include_str!("../templates/password-reset-done.text"), - Self::UserImportBlocked => include_str!("../templates/plain-notification.text"), + Self::Test => include_str!("templates/test.text"), + Self::Welcome => include_str!("templates/enrollment-welcome.text"), + Self::SupportData => include_str!("templates/support-data.text"), + Self::DesktopStart => include_str!("templates/desktop-start.text"), + Self::NewAccount => include_str!("templates/new-account.text"), + Self::NewDevice => include_str!("templates/new-device.text"), + Self::NewDeviceLogin => include_str!("templates/new-device-login.text"), + Self::NewDeviceOIDCLogin => include_str!("templates/new-device-oidc-login.text"), + Self::GatewayDisconnect => include_str!("templates/gateway-disconnected.text"), + Self::GatewayReconnect => include_str!("templates/gateway-reconnected.text"), + Self::MFAActivation => include_str!("templates/mfa-activation.text"), + Self::MFAConfigured { method: _ } => include_str!("templates/mfa-configured.text"), + Self::MFACode => include_str!("templates/mfa-code.text"), + Self::PasswordReset => include_str!("templates/password-reset.text"), + Self::PasswordResetDone => include_str!("templates/password-reset-done.text"), + Self::UserImportBlocked => include_str!("templates/plain-notification.text"), Self::EnrollmentNotification => { - include_str!("../templates/enrollment-admin-notification.text") + include_str!("templates/enrollment-admin-notification.text") } Self::LetsencryptCertRefreshFailed => { - include_str!("../templates/letsencrypt-cert-refresh-failed.text") + include_str!("templates/letsencrypt-cert-refresh-failed.text") } Self::CertificateExpiration | Self::CertificateExpired => { - include_str!("../templates/certificate-expiration.text") + include_str!("templates/certificate-expiration.text") } } } diff --git a/crates/defguard_mail/src/qr.rs b/crates/defguard_core/src/mail/qr.rs similarity index 100% rename from crates/defguard_mail/src/qr.rs rename to crates/defguard_core/src/mail/qr.rs diff --git a/crates/defguard_mail/src/templates.rs b/crates/defguard_core/src/mail/templates.rs similarity index 99% rename from crates/defguard_mail/src/templates.rs rename to crates/defguard_core/src/mail/templates.rs index 68c324fb1e..b38af35baf 100644 --- a/crates/defguard_mail/src/templates.rs +++ b/crates/defguard_core/src/mail/templates.rs @@ -15,14 +15,14 @@ use tera::{Context, Function, Tera}; use thiserror::Error; use tracing::{debug, warn}; -use crate::mail::{Attachment, MailMessage}; +use super::{Attachment, MailMessage}; pub(crate) const DEFAULT_LANG: &str = "en_US"; pub static SUPPORT_EMAIL_ADDRESS: &str = "support@defguard.net"; -static BASE_MJML: &str = include_str!("../templates/base.mjml"); -static MACROS_MJML: &str = include_str!("../templates/macros.mjml"); +static BASE_MJML: &str = include_str!("templates/base.mjml"); +static MACROS_MJML: &str = include_str!("templates/macros.mjml"); static MAIL_DATETIME_FORMAT: &str = "%A, %B %d, %Y at %r"; #[derive(Debug, Error)] diff --git a/crates/defguard_mail/templates/base.mjml b/crates/defguard_core/src/mail/templates/base.mjml similarity index 100% rename from crates/defguard_mail/templates/base.mjml rename to crates/defguard_core/src/mail/templates/base.mjml diff --git a/crates/defguard_mail/templates/certificate-expiration.mjml b/crates/defguard_core/src/mail/templates/certificate-expiration.mjml similarity index 100% rename from crates/defguard_mail/templates/certificate-expiration.mjml rename to crates/defguard_core/src/mail/templates/certificate-expiration.mjml diff --git a/crates/defguard_mail/templates/certificate-expiration.text b/crates/defguard_core/src/mail/templates/certificate-expiration.text similarity index 100% rename from crates/defguard_mail/templates/certificate-expiration.text rename to crates/defguard_core/src/mail/templates/certificate-expiration.text diff --git a/crates/defguard_mail/templates/desktop-start.mjml b/crates/defguard_core/src/mail/templates/desktop-start.mjml similarity index 100% rename from crates/defguard_mail/templates/desktop-start.mjml rename to crates/defguard_core/src/mail/templates/desktop-start.mjml diff --git a/crates/defguard_mail/templates/desktop-start.text b/crates/defguard_core/src/mail/templates/desktop-start.text similarity index 100% rename from crates/defguard_mail/templates/desktop-start.text rename to crates/defguard_core/src/mail/templates/desktop-start.text diff --git a/crates/defguard_mail/templates/enrollment-admin-notification.mjml b/crates/defguard_core/src/mail/templates/enrollment-admin-notification.mjml similarity index 100% rename from crates/defguard_mail/templates/enrollment-admin-notification.mjml rename to crates/defguard_core/src/mail/templates/enrollment-admin-notification.mjml diff --git a/crates/defguard_mail/templates/enrollment-admin-notification.text b/crates/defguard_core/src/mail/templates/enrollment-admin-notification.text similarity index 100% rename from crates/defguard_mail/templates/enrollment-admin-notification.text rename to crates/defguard_core/src/mail/templates/enrollment-admin-notification.text diff --git a/crates/defguard_mail/templates/enrollment-welcome.mjml b/crates/defguard_core/src/mail/templates/enrollment-welcome.mjml similarity index 100% rename from crates/defguard_mail/templates/enrollment-welcome.mjml rename to crates/defguard_core/src/mail/templates/enrollment-welcome.mjml diff --git a/crates/defguard_mail/templates/enrollment-welcome.text b/crates/defguard_core/src/mail/templates/enrollment-welcome.text similarity index 100% rename from crates/defguard_mail/templates/enrollment-welcome.text rename to crates/defguard_core/src/mail/templates/enrollment-welcome.text diff --git a/crates/defguard_mail/templates/gateway-disconnected.mjml b/crates/defguard_core/src/mail/templates/gateway-disconnected.mjml similarity index 100% rename from crates/defguard_mail/templates/gateway-disconnected.mjml rename to crates/defguard_core/src/mail/templates/gateway-disconnected.mjml diff --git a/crates/defguard_mail/templates/gateway-disconnected.text b/crates/defguard_core/src/mail/templates/gateway-disconnected.text similarity index 100% rename from crates/defguard_mail/templates/gateway-disconnected.text rename to crates/defguard_core/src/mail/templates/gateway-disconnected.text diff --git a/crates/defguard_mail/templates/gateway-reconnected.mjml b/crates/defguard_core/src/mail/templates/gateway-reconnected.mjml similarity index 100% rename from crates/defguard_mail/templates/gateway-reconnected.mjml rename to crates/defguard_core/src/mail/templates/gateway-reconnected.mjml diff --git a/crates/defguard_mail/templates/gateway-reconnected.text b/crates/defguard_core/src/mail/templates/gateway-reconnected.text similarity index 100% rename from crates/defguard_mail/templates/gateway-reconnected.text rename to crates/defguard_core/src/mail/templates/gateway-reconnected.text diff --git a/crates/defguard_mail/templates/letsencrypt-cert-refresh-failed.mjml b/crates/defguard_core/src/mail/templates/letsencrypt-cert-refresh-failed.mjml similarity index 100% rename from crates/defguard_mail/templates/letsencrypt-cert-refresh-failed.mjml rename to crates/defguard_core/src/mail/templates/letsencrypt-cert-refresh-failed.mjml diff --git a/crates/defguard_mail/templates/letsencrypt-cert-refresh-failed.text b/crates/defguard_core/src/mail/templates/letsencrypt-cert-refresh-failed.text similarity index 100% rename from crates/defguard_mail/templates/letsencrypt-cert-refresh-failed.text rename to crates/defguard_core/src/mail/templates/letsencrypt-cert-refresh-failed.text diff --git a/crates/defguard_mail/templates/macros.mjml b/crates/defguard_core/src/mail/templates/macros.mjml similarity index 100% rename from crates/defguard_mail/templates/macros.mjml rename to crates/defguard_core/src/mail/templates/macros.mjml diff --git a/crates/defguard_mail/templates/mfa-activation.mjml b/crates/defguard_core/src/mail/templates/mfa-activation.mjml similarity index 100% rename from crates/defguard_mail/templates/mfa-activation.mjml rename to crates/defguard_core/src/mail/templates/mfa-activation.mjml diff --git a/crates/defguard_mail/templates/mfa-activation.text b/crates/defguard_core/src/mail/templates/mfa-activation.text similarity index 100% rename from crates/defguard_mail/templates/mfa-activation.text rename to crates/defguard_core/src/mail/templates/mfa-activation.text diff --git a/crates/defguard_mail/templates/mfa-code.mjml b/crates/defguard_core/src/mail/templates/mfa-code.mjml similarity index 100% rename from crates/defguard_mail/templates/mfa-code.mjml rename to crates/defguard_core/src/mail/templates/mfa-code.mjml diff --git a/crates/defguard_mail/templates/mfa-code.text b/crates/defguard_core/src/mail/templates/mfa-code.text similarity index 100% rename from crates/defguard_mail/templates/mfa-code.text rename to crates/defguard_core/src/mail/templates/mfa-code.text diff --git a/crates/defguard_mail/templates/mfa-configured.mjml b/crates/defguard_core/src/mail/templates/mfa-configured.mjml similarity index 100% rename from crates/defguard_mail/templates/mfa-configured.mjml rename to crates/defguard_core/src/mail/templates/mfa-configured.mjml diff --git a/crates/defguard_mail/templates/mfa-configured.text b/crates/defguard_core/src/mail/templates/mfa-configured.text similarity index 100% rename from crates/defguard_mail/templates/mfa-configured.text rename to crates/defguard_core/src/mail/templates/mfa-configured.text diff --git a/crates/defguard_mail/templates/new-account.mjml b/crates/defguard_core/src/mail/templates/new-account.mjml similarity index 100% rename from crates/defguard_mail/templates/new-account.mjml rename to crates/defguard_core/src/mail/templates/new-account.mjml diff --git a/crates/defguard_mail/templates/new-account.text b/crates/defguard_core/src/mail/templates/new-account.text similarity index 100% rename from crates/defguard_mail/templates/new-account.text rename to crates/defguard_core/src/mail/templates/new-account.text diff --git a/crates/defguard_mail/templates/new-device-login.mjml b/crates/defguard_core/src/mail/templates/new-device-login.mjml similarity index 100% rename from crates/defguard_mail/templates/new-device-login.mjml rename to crates/defguard_core/src/mail/templates/new-device-login.mjml diff --git a/crates/defguard_mail/templates/new-device-login.text b/crates/defguard_core/src/mail/templates/new-device-login.text similarity index 100% rename from crates/defguard_mail/templates/new-device-login.text rename to crates/defguard_core/src/mail/templates/new-device-login.text diff --git a/crates/defguard_mail/templates/new-device-oidc-login.mjml b/crates/defguard_core/src/mail/templates/new-device-oidc-login.mjml similarity index 100% rename from crates/defguard_mail/templates/new-device-oidc-login.mjml rename to crates/defguard_core/src/mail/templates/new-device-oidc-login.mjml diff --git a/crates/defguard_mail/templates/new-device-oidc-login.text b/crates/defguard_core/src/mail/templates/new-device-oidc-login.text similarity index 100% rename from crates/defguard_mail/templates/new-device-oidc-login.text rename to crates/defguard_core/src/mail/templates/new-device-oidc-login.text diff --git a/crates/defguard_mail/templates/new-device.mjml b/crates/defguard_core/src/mail/templates/new-device.mjml similarity index 100% rename from crates/defguard_mail/templates/new-device.mjml rename to crates/defguard_core/src/mail/templates/new-device.mjml diff --git a/crates/defguard_mail/templates/new-device.text b/crates/defguard_core/src/mail/templates/new-device.text similarity index 100% rename from crates/defguard_mail/templates/new-device.text rename to crates/defguard_core/src/mail/templates/new-device.text diff --git a/crates/defguard_mail/templates/password-reset-done.mjml b/crates/defguard_core/src/mail/templates/password-reset-done.mjml similarity index 100% rename from crates/defguard_mail/templates/password-reset-done.mjml rename to crates/defguard_core/src/mail/templates/password-reset-done.mjml diff --git a/crates/defguard_mail/templates/password-reset-done.text b/crates/defguard_core/src/mail/templates/password-reset-done.text similarity index 100% rename from crates/defguard_mail/templates/password-reset-done.text rename to crates/defguard_core/src/mail/templates/password-reset-done.text diff --git a/crates/defguard_mail/templates/password-reset.mjml b/crates/defguard_core/src/mail/templates/password-reset.mjml similarity index 100% rename from crates/defguard_mail/templates/password-reset.mjml rename to crates/defguard_core/src/mail/templates/password-reset.mjml diff --git a/crates/defguard_mail/templates/password-reset.text b/crates/defguard_core/src/mail/templates/password-reset.text similarity index 100% rename from crates/defguard_mail/templates/password-reset.text rename to crates/defguard_core/src/mail/templates/password-reset.text diff --git a/crates/defguard_mail/templates/plain-notification.mjml b/crates/defguard_core/src/mail/templates/plain-notification.mjml similarity index 100% rename from crates/defguard_mail/templates/plain-notification.mjml rename to crates/defguard_core/src/mail/templates/plain-notification.mjml diff --git a/crates/defguard_mail/templates/plain-notification.text b/crates/defguard_core/src/mail/templates/plain-notification.text similarity index 100% rename from crates/defguard_mail/templates/plain-notification.text rename to crates/defguard_core/src/mail/templates/plain-notification.text diff --git a/crates/defguard_mail/templates/support-data.mjml b/crates/defguard_core/src/mail/templates/support-data.mjml similarity index 100% rename from crates/defguard_mail/templates/support-data.mjml rename to crates/defguard_core/src/mail/templates/support-data.mjml diff --git a/crates/defguard_mail/templates/support-data.text b/crates/defguard_core/src/mail/templates/support-data.text similarity index 100% rename from crates/defguard_mail/templates/support-data.text rename to crates/defguard_core/src/mail/templates/support-data.text diff --git a/crates/defguard_mail/templates/test.mjml b/crates/defguard_core/src/mail/templates/test.mjml similarity index 100% rename from crates/defguard_mail/templates/test.mjml rename to crates/defguard_core/src/mail/templates/test.mjml diff --git a/crates/defguard_mail/templates/test.text b/crates/defguard_core/src/mail/templates/test.text similarity index 100% rename from crates/defguard_mail/templates/test.text rename to crates/defguard_core/src/mail/templates/test.text diff --git a/crates/defguard_mail/src/tests.rs b/crates/defguard_core/src/mail/tests.rs similarity index 99% rename from crates/defguard_mail/src/tests.rs rename to crates/defguard_core/src/mail/tests.rs index 7eadb4dc9f..80042d6642 100644 --- a/crates/defguard_mail/src/tests.rs +++ b/crates/defguard_core/src/mail/tests.rs @@ -24,8 +24,7 @@ use tera::Context; use tokio::time::sleep; use super::{ - mail::{Attachment, MailMessage}, - templates, + templates, {Attachment, MailMessage}, }; #[test] @@ -444,7 +443,7 @@ fn send_certificate_expired(_: PgPoolOptions, options: PgConnectOptions) { } mod markdown_to_html { - use crate::templates::markdown_to_html; + use super::templates::markdown_to_html; fn has_tag(html: &str, tag: &str) -> bool { html.contains(&format!("<{tag}")) diff --git a/crates/defguard_core/src/utility_thread.rs b/crates/defguard_core/src/utility_thread.rs index 501e3265a6..d069daa635 100644 --- a/crates/defguard_core/src/utility_thread.rs +++ b/crates/defguard_core/src/utility_thread.rs @@ -1,5 +1,6 @@ use std::{collections::HashSet, time::Duration}; +use crate::mail::templates; use chrono::{NaiveDateTime, TimeDelta, Utc}; use defguard_common::{ db::models::{ @@ -8,7 +9,6 @@ use defguard_common::{ }, types::proxy::ProxyControlMessage, }; -use defguard_mail::templates; use sqlx::{PgConnection, PgPool, query_as}; use tokio::{ sync::{broadcast, mpsc}, diff --git a/crates/defguard_mail/Cargo.toml b/crates/defguard_mail/Cargo.toml deleted file mode 100644 index 4cf337077e..0000000000 --- a/crates/defguard_mail/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "defguard_mail" -version = "0.0.0" -edition.workspace = true -license-file.workspace = true -homepage.workspace = true -repository.workspace = true -rust-version.workspace = true - -[dependencies] -defguard_common.workspace = true - -chrono.workspace = true -lettre.workspace = true -openidconnect.workspace = true -pulldown-cmark.workspace = true -reqwest.workspace = true -serde.workspace = true -serde_json.workspace = true -sqlx.workspace = true -tera.workspace = true -thiserror.workspace = true -tokio.workspace = true -tracing.workspace = true -humantime.workspace = true - -css-inline = "0.20" -image = "0.25" # match with qrforge -mrml = "6.0" -qrforge = {version = "0.1", default-features = false, features = ["image"]} - -[dev-dependencies] -claims.workspace = true diff --git a/crates/defguard_mail/src/lib.rs b/crates/defguard_mail/src/lib.rs deleted file mode 100644 index 2dc9e329e0..0000000000 --- a/crates/defguard_mail/src/lib.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! Handle email messages. -//! -//! Refer to: -//! - [RFC 2557](https://datatracker.ietf.org/doc/html/rfc2557) -//! - [Meaning of mulitpart](https://www.codestudy.net/blog/mail-multipart-alternative-vs-multipart-mixed/) - -pub mod mail; -pub(crate) mod mail_context; -mod qr; -pub mod templates; -#[cfg(test)] -mod tests; -mod xoauth2; - -#[derive(Debug, thiserror::Error)] -pub enum MailError { - #[error(transparent)] - LettreError(#[from] lettre::error::Error), - - #[error(transparent)] - AddressError(#[from] lettre::address::AddressError), - - #[error(transparent)] - SmtpError(#[from] lettre::transport::smtp::Error), - - #[error(transparent)] - SqlxError(#[from] sqlx::Error), - - #[error("SMTP not configured")] - SmtpNotConfigured, - - #[error("Invalid port: {0}")] - InvalidPort(i32), - - #[error(transparent)] - ReqwestError(#[from] openidconnect::reqwest::Error), - - #[error(transparent)] - UrlError(#[from] openidconnect::url::ParseError), - - #[error(transparent)] - OAuth2Error(#[from] openidconnect::ConfigurationError), - - #[error("Open ID discovery")] - OpenIDDiscovery, - - #[error("Refresh token exchange")] - RefreshTokenExchange, -} diff --git a/crates/defguard_mail/src/xoauth2.rs b/crates/defguard_mail/src/xoauth2.rs deleted file mode 100644 index b9b7a943a6..0000000000 --- a/crates/defguard_mail/src/xoauth2.rs +++ /dev/null @@ -1,64 +0,0 @@ -use defguard_common::db::models::settings::smtp::SmtpSettings; -use openidconnect::{ - ClientId, ClientSecret, IssuerUrl, OAuth2TokenResponse, RefreshToken, - core::{CoreClient, CoreProviderMetadata}, - reqwest::{ClientBuilder, redirect::Policy}, -}; -use tracing::{debug, error}; - -use super::MailError; - -/// Obtain access token for XOAUTH2 authentication. -pub(super) async fn obtain_access_token( - smtp_settings: &mut SmtpSettings, -) -> Result { - let (Some(issuer_url), Some(client_id), Some(client_secret), Some(refresh_token)) = ( - &smtp_settings.oauth_issuer_url, - &smtp_settings.oauth_client_id, - &smtp_settings.oauth_client_secret, - &smtp_settings.oauth_refresh_token, - ) else { - error!("SMTP XOAUTH requires: issuer URL, client ID, client secret, and refresh token"); - return Err(MailError::SmtpNotConfigured); - }; - let issuer_url = IssuerUrl::new(issuer_url.into())?; - let client_id = ClientId::new(client_id.into()); - let client_secret = ClientSecret::new(client_secret.expose_secret().into()); - let refresh_token = RefreshToken::new(refresh_token.into()); - - let http_client = ClientBuilder::new() - // Following redirects opens the client up to SSRF vulnerabilities. - .redirect(Policy::none()) - .build()?; - - let provider_metadata = CoreProviderMetadata::discover_async(issuer_url, &http_client) - .await - .map_err(|err| { - error!("Failed OpenID Connect Discovery: {err}"); - MailError::OpenIDDiscovery - })?; - - let client = - CoreClient::from_provider_metadata(provider_metadata, client_id, Some(client_secret)); - - let token_response = client - .exchange_refresh_token(&refresh_token)? - .request_async(&http_client) - .await - .map_err(|err| { - error!("Failed to fetch token: {err}"); - MailError::RefreshTokenExchange - })?; - - let access_token = token_response.access_token().secret(); - debug!("Got access token"); - if let Some(expires_in) = token_response.expires_in() { - debug!("Access token expires in:\n{expires_in:?}\n"); - } - if let Some(refresh_token) = token_response.refresh_token() { - debug!("Got refresh token"); - // TODO: use `smtp_settings.set_oauth_refresh_token` - smtp_settings.oauth_refresh_token = Some(refresh_token.secret().into()); - } - Ok(access_token.clone()) -} diff --git a/crates/defguard_proxy_manager/Cargo.toml b/crates/defguard_proxy_manager/Cargo.toml index ba61a76275..fec3973898 100644 --- a/crates/defguard_proxy_manager/Cargo.toml +++ b/crates/defguard_proxy_manager/Cargo.toml @@ -13,7 +13,6 @@ defguard_certs.workspace = true defguard_common.workspace = true defguard_core.workspace = true defguard_grpc_tls.workspace = true -defguard_mail.workspace = true defguard_proto.workspace = true defguard_version.workspace = true diff --git a/crates/defguard_proxy_manager/src/servers/enrollment.rs b/crates/defguard_proxy_manager/src/servers/enrollment.rs index f9be882ce7..a54093f5af 100644 --- a/crates/defguard_proxy_manager/src/servers/enrollment.rs +++ b/crates/defguard_proxy_manager/src/servers/enrollment.rs @@ -13,6 +13,10 @@ use defguard_common::{ }, }, }; +use defguard_core::mail::templates::{ + TemplateLocation, enrollment_admin_notification, mfa_activation_mail, mfa_configured_mail, + new_device_added_mail, +}; use defguard_core::{ db::models::enrollment::{ENROLLMENT_TOKEN_TYPE, Token}, device_access::{build_device_config, join_device_to_all_networks}, @@ -32,10 +36,6 @@ use defguard_core::{ headers::get_device_info, is_valid_phone_number, }; -use defguard_mail::templates::{ - TemplateLocation, enrollment_admin_notification, mfa_activation_mail, mfa_configured_mail, - new_device_added_mail, -}; use defguard_proto::client_types::{ ActivateUserRequest, AdminInfo, CodeMfaSetupFinishRequest, CodeMfaSetupFinishResponse, CodeMfaSetupStartRequest, CodeMfaSetupStartResponse, DeviceConfigResponse, diff --git a/crates/defguard_proxy_manager/src/servers/password_reset.rs b/crates/defguard_proxy_manager/src/servers/password_reset.rs index 55ef1967e9..3a4c2eea6f 100644 --- a/crates/defguard_proxy_manager/src/servers/password_reset.rs +++ b/crates/defguard_proxy_manager/src/servers/password_reset.rs @@ -1,4 +1,5 @@ use defguard_common::db::models::{Settings, User}; +use defguard_core::mail::templates::{password_reset_mail, password_reset_success_mail}; use defguard_core::{ db::models::enrollment::{PASSWORD_RESET_TOKEN_TYPE, Token}, enterprise::{ @@ -9,7 +10,6 @@ use defguard_core::{ handlers::user::check_password_strength, headers::get_device_info, }; -use defguard_mail::templates::{password_reset_mail, password_reset_success_mail}; use defguard_proto::proxy::{ DeviceInfo, PasswordResetInitializeRequest, PasswordResetRequest, PasswordResetStartRequest, PasswordResetStartResponse, diff --git a/migrations/20260625000000_[2.1.0]_smtp_oauth_tenant_id.down.sql b/migrations/20260625000000_[2.1.0]_smtp_oauth_tenant_id.down.sql new file mode 100644 index 0000000000..b8922ea8b2 --- /dev/null +++ b/migrations/20260625000000_[2.1.0]_smtp_oauth_tenant_id.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE settings + DROP COLUMN smtp_oauth_tenant_id; diff --git a/migrations/20260625000000_[2.1.0]_smtp_oauth_tenant_id.up.sql b/migrations/20260625000000_[2.1.0]_smtp_oauth_tenant_id.up.sql new file mode 100644 index 0000000000..0decb10ef3 --- /dev/null +++ b/migrations/20260625000000_[2.1.0]_smtp_oauth_tenant_id.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE settings + ADD COLUMN smtp_oauth_tenant_id text NULL; From 12cadbef0b5000e868e394173f42b975c1f53ed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Thu, 25 Jun 2026 15:35:07 +0200 Subject: [PATCH 09/18] port xoauth2 frontend --- web/messages/en/common.json | 3 +- web/messages/en/settings.json | 31 +- web/public/smtp-oauth-relay.worker.js | 19 + .../SettingsSmtpPage/SendTestEmailModal.tsx | 1 - .../SettingsSmtpPage/SettingsSmtpPage.tsx | 477 ++++++++++-------- .../SmtpAuthConfigModal/BasicAuthForm.tsx | 147 ++++++ .../SmtpAuthConfigModal/CustomAuthForm.tsx | 190 +++++++ .../SmtpAuthConfigModal/GoogleAuthForm.tsx | 160 ++++++ .../SmtpAuthConfigModal/MicrosoftAuthForm.tsx | 129 +++++ .../SmtpAuthConfigModal/NoneAuthForm.tsx | 119 +++++ .../SmtpAuthConfigModal/encryptionOptions.ts | 19 + .../SmtpAuthConfigModal/index.tsx | 63 +++ .../SmtpAuthConfigModal/oauthFlow.ts | 157 ++++++ .../SmtpAuthConfigModal/types.ts | 39 ++ .../SmtpAuthConfigModal/useOAuthSubmit.ts | 27 + .../SmtpAuthMethodCard/SmtpAuthMethodCard.tsx | 131 +++++ .../components/SmtpAuthMethodCard/style.scss | 62 +++ .../SettingsSmtpPage/smtpAuthUtils.ts | 19 + .../settings/SettingsSmtpPage/style.scss | 22 + web/src/routeTree.gen.ts | 21 + web/src/routes/smtp-oauth-callback.tsx | 40 ++ web/src/shared/api/types.ts | 15 + 22 files changed, 1685 insertions(+), 206 deletions(-) create mode 100644 web/public/smtp-oauth-relay.worker.js create mode 100644 web/src/pages/settings/SettingsSmtpPage/SmtpAuthConfigModal/BasicAuthForm.tsx create mode 100644 web/src/pages/settings/SettingsSmtpPage/SmtpAuthConfigModal/CustomAuthForm.tsx create mode 100644 web/src/pages/settings/SettingsSmtpPage/SmtpAuthConfigModal/GoogleAuthForm.tsx create mode 100644 web/src/pages/settings/SettingsSmtpPage/SmtpAuthConfigModal/MicrosoftAuthForm.tsx create mode 100644 web/src/pages/settings/SettingsSmtpPage/SmtpAuthConfigModal/NoneAuthForm.tsx create mode 100644 web/src/pages/settings/SettingsSmtpPage/SmtpAuthConfigModal/encryptionOptions.ts create mode 100644 web/src/pages/settings/SettingsSmtpPage/SmtpAuthConfigModal/index.tsx create mode 100644 web/src/pages/settings/SettingsSmtpPage/SmtpAuthConfigModal/oauthFlow.ts create mode 100644 web/src/pages/settings/SettingsSmtpPage/SmtpAuthConfigModal/types.ts create mode 100644 web/src/pages/settings/SettingsSmtpPage/SmtpAuthConfigModal/useOAuthSubmit.ts create mode 100644 web/src/pages/settings/SettingsSmtpPage/components/SmtpAuthMethodCard/SmtpAuthMethodCard.tsx create mode 100644 web/src/pages/settings/SettingsSmtpPage/components/SmtpAuthMethodCard/style.scss create mode 100644 web/src/pages/settings/SettingsSmtpPage/smtpAuthUtils.ts create mode 100644 web/src/pages/settings/SettingsSmtpPage/style.scss create mode 100644 web/src/routes/smtp-oauth-callback.tsx diff --git a/web/messages/en/common.json b/web/messages/en/common.json index 811cabefb0..41df4c62c6 100644 --- a/web/messages/en/common.json +++ b/web/messages/en/common.json @@ -87,5 +87,6 @@ "failed_to_start_enrollment": "Failed to start enrollment", "misc_recommended": "Recommended", "footer_copyright": "Copyright © {year} Defguard Sp. z o.o.", - "error_unknown": "An unknown error occurred" + "error_unknown": "An unknown error occurred", + "controls_configure": "Configure" } diff --git a/web/messages/en/settings.json b/web/messages/en/settings.json index c9d25da992..aef232acd1 100644 --- a/web/messages/en/settings.json +++ b/web/messages/en/settings.json @@ -327,5 +327,34 @@ "settings_gateway_notifications_reconnect_title": "Gateway reconnect notifications", "settings_gateway_notifications_reconnect_content": "Send an email notification to admin users when a gateway reconnects.", "settings_gateway_notifications_inactivity_threshold_label": "Inactivity threshold for disconnect notifications (minutes)", - "settings_gateway_notifications_inactivity_threshold_helper": "" + "settings_gateway_notifications_inactivity_threshold_helper": "", + "settings_smtp_activate_confirm_body": "Activating a new SMTP method will automatically disable the currently active configuration. Do you want to continue?", + "settings_smtp_activate_confirm_title": "Activate new SMTP method", + "settings_smtp_active_config_label": "Active SMTP configuration", + "settings_smtp_auth_card_active_method": "Applied", + "settings_smtp_auth_card_basic_description": "Authenticate with a username and password for secure email delivery.", + "settings_smtp_auth_card_basic_name": "Username and password", + "settings_smtp_auth_card_custom_description": "Authenticate SMTP using your organization’s custom provider.", + "settings_smtp_auth_card_custom_name": "Custom OAuth2 provider", + "settings_smtp_auth_card_google_description": "Connect to Gmail using a Google Client ID and Client Secret.", + "settings_smtp_auth_card_google_name": "Google", + "settings_smtp_auth_card_microsoft_description": "Connect to Microsoft 365 or Outlook using a Client ID and Client Secret.", + "settings_smtp_auth_card_microsoft_name": "Microsoft", + "settings_smtp_auth_card_none_description": "Connect using only the server address, port, encryption type, and sender email.", + "settings_smtp_auth_card_none_name": "No Authentication", + "settings_smtp_auth_oauth_error": "Authorization failed", + "settings_smtp_auth_oauth_info": "Clicking Apply will open a browser window to authorize access. You will be asked to sign in and grant permission. Make sure /smtp-oauth-callback is authorized as a redirect URL.", + "settings_smtp_auth_oauth_popup_blocked": "Authorization popup was blocked. Please allow popups for this site.", + "settings_smtp_auth_oauth_popup_closed": "Authorization popup was closed before completing.", + "settings_smtp_helper_oauth_client_id": "", + "settings_smtp_helper_oauth_client_secret": "", + "settings_smtp_helper_oauth_issuer_url": "", + "settings_smtp_helper_oauth_scope": "OAuth2 scopes to request, space-separated.", + "settings_smtp_helper_oauth_tenant_id": "", + "settings_smtp_label_oauth_client_id": "Client ID", + "settings_smtp_label_oauth_client_secret": "Client Secret", + "settings_smtp_label_oauth_issuer_url": "Issuer URL", + "settings_smtp_label_oauth_scope": "Scope", + "settings_smtp_label_oauth_tenant_id": "Tenant ID", + "settings_smtp_other_methods_label": "Other configuration methods" } diff --git a/web/public/smtp-oauth-relay.worker.js b/web/public/smtp-oauth-relay.worker.js new file mode 100644 index 0000000000..fc420e7ade --- /dev/null +++ b/web/public/smtp-oauth-relay.worker.js @@ -0,0 +1,19 @@ +// SharedWorker relay for SMTP OAuth code delivery. +// Relays messages between the parent window and the OAuth callback popup +// when COOP has severed window.opener (e.g. accounts.google.com sets +// Cross-Origin-Opener-Policy: same-origin). SharedWorkers cross browsing-context- +// group boundaries, unlike postMessage and BroadcastChannel. +const ports = new Set(); + +self.addEventListener('connect', (e) => { + const port = e.ports[0]; + ports.add(port); + + port.addEventListener('message', (msg) => { + for (const p of ports) { + if (p !== port) p.postMessage(msg.data); + } + }); + + port.start(); +}); diff --git a/web/src/pages/settings/SettingsSmtpPage/SendTestEmailModal.tsx b/web/src/pages/settings/SettingsSmtpPage/SendTestEmailModal.tsx index b181dc35ed..bbaebc7e2e 100644 --- a/web/src/pages/settings/SettingsSmtpPage/SendTestEmailModal.tsx +++ b/web/src/pages/settings/SettingsSmtpPage/SendTestEmailModal.tsx @@ -109,7 +109,6 @@ const ModalContent = () => { /> )} - ({ isDefault: s.isDefaultValue || s.isPristine, diff --git a/web/src/pages/settings/SettingsSmtpPage/SettingsSmtpPage.tsx b/web/src/pages/settings/SettingsSmtpPage/SettingsSmtpPage.tsx index 76201c1a5f..eeaff4abe0 100644 --- a/web/src/pages/settings/SettingsSmtpPage/SettingsSmtpPage.tsx +++ b/web/src/pages/settings/SettingsSmtpPage/SettingsSmtpPage.tsx @@ -1,11 +1,13 @@ +import './style.scss'; import { useMutation, useQuery } from '@tanstack/react-query'; import { Link } from '@tanstack/react-router'; -import { useMemo } from 'react'; +import { useMemo, useRef, useState } from 'react'; import z from 'zod'; import { m } from '../../../paraglide/messages'; import api from '../../../shared/api/api'; import { type Settings, + SmtpAuthentication, SmtpEncryption, type SmtpEncryptionValue, } from '../../../shared/api/types'; @@ -14,15 +16,9 @@ import { ContextualHelpKey, ContextualHelpSidebar, } from '../../../shared/components/ContextualHelp'; -import { Controls } from '../../../shared/components/Controls/Controls'; -import { DescriptionBlock } from '../../../shared/components/DescriptionBlock/DescriptionBlock'; import { Page } from '../../../shared/components/Page/Page'; -import { SettingsCard } from '../../../shared/components/SettingsCard/SettingsCard'; import { SettingsHeader } from '../../../shared/components/SettingsHeader/SettingsHeader'; import { SettingsLayout } from '../../../shared/components/SettingsLayout/SettingsLayout'; -import { Button } from '../../../shared/defguard-ui/components/Button/Button'; -import { EvenSplit } from '../../../shared/defguard-ui/components/EvenSplit/EvenSplit'; -import type { SelectOption } from '../../../shared/defguard-ui/components/Select/types'; import { SizedBox } from '../../../shared/defguard-ui/components/SizedBox/SizedBox'; import { Snackbar } from '../../../shared/defguard-ui/providers/snackbar/snackbar'; import { ThemeSpacing } from '../../../shared/defguard-ui/types'; @@ -33,10 +29,24 @@ import { openModal } from '../../../shared/hooks/modalControls/modalsSubjects'; import { ModalName } from '../../../shared/hooks/modalControls/modalTypes'; import { useApp } from '../../../shared/hooks/useApp'; import { patternValidEmail } from '../../../shared/patterns'; -import { getSettingsQueryOptions } from '../../../shared/query'; +import { + getLicenseInfoQueryOptions, + getSettingsQueryOptions, +} from '../../../shared/query'; +import { canUseBusinessFeature, licenseActionCheck } from '../../../shared/utils/license'; import { Validate } from '../../../shared/validate'; import { getConfiguredBadge, getNotConfiguredBadge } from '../SettingsIndexPage/types'; +import { + type SmtpAuthCardVariant, + SmtpAuthMethodCard, +} from './components/SmtpAuthMethodCard/SmtpAuthMethodCard'; import { SendTestEmailModal } from './SendTestEmailModal'; +import { + type SmtpAuthApplyResult, + SmtpAuthConfigModal, + type SmtpAuthModalValues, +} from './SmtpAuthConfigModal'; +import { isGoogleIssuerUrl, isMicrosoftIssuerUrl } from './smtpAuthUtils'; const breadcrumbsLinks = [ { icon="mail" badgeProps={smtp ? getConfiguredBadge() : getNotConfiguredBadge()} /> - {isPresent(settings) && ( - - -

{m.settings_smtp_section_server_description()}

-
- - -
- )} + {isPresent(settings) && } ); }; -const encryptionValueToLabel = (value: SmtpEncryptionValue): string => { - switch (value) { - case 'ImplicitTls': - return m.settings_smtp_encryption_implicit_tls(); - case 'StartTls': - return m.settings_smtp_encryption_start_tls(); - case 'None': - return m.settings_smtp_encryption_none(); +const detectActiveCard = ( + authentication: string, + issuerUrl: string | null, + smtpServer: string, +): SmtpAuthCardVariant | null => { + if (!smtpServer) return null; + if (authentication === SmtpAuthentication.None) return 'none'; + if (authentication === SmtpAuthentication.Login) return 'basic'; + if (authentication === SmtpAuthentication.XOAuth2) { + if (isGoogleIssuerUrl(issuerUrl)) return 'google'; + if (isMicrosoftIssuerUrl(issuerUrl)) return 'microsoft'; + return 'custom'; } + return null; }; -const encryptionSelectOptions: SelectOption[] = Object.values( - SmtpEncryption, -).map((e) => ({ - key: e, - label: encryptionValueToLabel(e), - value: e, -})); +const AUTH_CARDS: SmtpAuthCardVariant[] = ['none', 'basic', 'google', 'microsoft']; -const Content = ({ settings }: { settings: Settings }) => { - const smtpConfigured = useApp((s) => s.appInfo.smtp_enabled); - const formSchema = useMemo( - () => - z.object({ - smtp_server: z - .string() - .trim() - .min(1, m.form_error_required()) - .refine((val) => - !val - ? true - : Validate.any( - val, - [Validate.IPv4, Validate.IPv6, Validate.Domain, Validate.Hostname], - false, - ), +const formSchema = z.object({ + smtp_server: z + .string() + .trim() + .min(1, m.form_error_required()) + .refine((val) => + !val + ? true + : Validate.any( + val, + [Validate.IPv4, Validate.IPv6, Validate.Domain, Validate.Hostname], + false, ), - smtp_port: z.number(m.form_error_required()).max(65535, m.form_error_port_max()), - smtp_password: z.string().trim().nullable(), - smtp_user: z.string().trim().nullable(), - smtp_sender: z - .string() - .trim() - .min(1, m.form_error_required()) - .regex(patternValidEmail, m.form_error_email()), - smtp_encryption: z.enum(SmtpEncryption), - }), - [], - ); + ), + smtp_port: z.number(m.form_error_required()).max(65535, m.form_error_port_max()), + smtp_password: z.string().trim().nullable(), + smtp_user: z.string().trim().nullable(), + smtp_sender: z + .string() + .trim() + .min(1, m.form_error_required()) + .regex(patternValidEmail, m.form_error_email()), + smtp_encryption: z.enum(SmtpEncryption), + smtp_authentication: z.enum(SmtpAuthentication), + smtp_oauth_issuer_url: z.string().trim().nullable(), + smtp_oauth_client_id: z.string().trim().nullable(), + smtp_oauth_client_secret: z.string().trim().nullable(), + smtp_oauth_refresh_token: z.string().trim().nullable(), + smtp_oauth_tenant_id: z.string().trim().nullable(), +}); - type FormFields = z.infer; +type FormFields = z.infer; - const emptyValues = useMemo( - (): FormFields => ({ - smtp_encryption: SmtpEncryption.StartTls, - smtp_password: null, - smtp_port: 587, - smtp_sender: '', - smtp_server: '', - smtp_user: null, - }), - [], - ); +const emptyValues: FormFields = { + smtp_encryption: SmtpEncryption.StartTls, + smtp_password: null, + smtp_port: 587, + smtp_sender: '', + smtp_server: '', + smtp_user: null, + smtp_authentication: SmtpAuthentication.None, + smtp_oauth_issuer_url: null, + smtp_oauth_client_id: null, + smtp_oauth_client_secret: null, + smtp_oauth_refresh_token: null, + smtp_oauth_tenant_id: null, +}; + +const Content = ({ + settings, + smtpEnabled, +}: { + settings: Settings; + smtpEnabled: boolean; +}) => { + const [modalVariant, setModalVariant] = useState(null); + const modalInitialValuesRef = useRef({ + smtp_server: '', + smtp_port: 587, + smtp_sender: '', + smtp_encryption: SmtpEncryption.StartTls, + smtp_user: null, + smtp_password: null, + smtp_oauth_issuer_url: null, + smtp_oauth_client_id: null, + smtp_oauth_client_secret: null, + smtp_oauth_refresh_token: null, + smtp_oauth_tenant_id: null, + }); const defaultValues = useMemo( (): FormFields => ({ @@ -156,10 +182,20 @@ const Content = ({ settings }: { settings: Settings }) => { smtp_sender: settings.smtp_sender ?? '', smtp_server: settings.smtp_server ?? '', smtp_user: settings.smtp_user ?? null, + smtp_authentication: settings.smtp_authentication, + smtp_oauth_issuer_url: settings.smtp_oauth_issuer_url ?? null, + smtp_oauth_client_id: settings.smtp_oauth_client_id ?? null, + smtp_oauth_client_secret: settings.smtp_oauth_client_secret ?? null, + smtp_oauth_refresh_token: settings.smtp_oauth_refresh_token ?? null, + smtp_oauth_tenant_id: settings.smtp_oauth_tenant_id ?? null, }), [settings], ); + const { data: licenseInfo } = useQuery(getLicenseInfoQueryOptions); + const oauthLocked = + licenseInfo !== undefined && !canUseBusinessFeature(licenseInfo).result; + const { mutateAsync: editSettings } = useMutation({ mutationFn: api.settings.patchSettings, meta: { @@ -177,140 +213,175 @@ const Content = ({ settings }: { settings: Settings }) => { defaultValues, validationLogic: formChangeLogic, validators: { - onSubmit: formSchema, onChange: formSchema, }, - onSubmit: async ({ value }) => { - await editSettings(value); - form.reset(value); - }, + onSubmit: async () => {}, }); + const openConfigModal = (variant: SmtpAuthCardVariant) => { + modalInitialValuesRef.current = { + smtp_server: form.state.values.smtp_server, + smtp_port: form.state.values.smtp_port, + smtp_sender: form.state.values.smtp_sender, + smtp_encryption: form.state.values.smtp_encryption as SmtpEncryptionValue, + smtp_user: form.state.values.smtp_user, + smtp_password: form.state.values.smtp_password, + smtp_oauth_issuer_url: form.state.values.smtp_oauth_issuer_url, + smtp_oauth_client_id: form.state.values.smtp_oauth_client_id, + smtp_oauth_client_secret: form.state.values.smtp_oauth_client_secret, + smtp_oauth_refresh_token: form.state.values.smtp_oauth_refresh_token, + smtp_oauth_tenant_id: form.state.values.smtp_oauth_tenant_id, + }; + setModalVariant(variant); + }; + + const openConfigModalGated = (variant: SmtpAuthCardVariant) => { + if (variant === 'google' || variant === 'microsoft') { + if (licenseInfo === undefined) return; + licenseActionCheck(canUseBusinessFeature(licenseInfo), () => + openConfigModal(variant), + ); + } else { + openConfigModal(variant); + } + }; + + const handleDelete = () => { + openModal(ModalName.ConfirmAction, { + title: m.settings_smtp_reset_confirm_title(), + contentMd: m.settings_smtp_reset_confirm_body(), + actionPromise: () => { + return api.settings.patchSettings(emptyValues); + }, + invalidateKeys: [['settings'], ['info']], + submitProps: { text: m.controls_delete(), variant: 'critical' }, + onSuccess: () => { + form.reset(emptyValues); + Snackbar.default(m.settings_smtp_reset_success()); + }, + onError: () => Snackbar.error(m.settings_smtp_reset_failed()), + }); + }; + return ( -
{ - e.stopPropagation(); - e.preventDefault(); - form.handleSubmit(); - }} - > - - - - {(field) => ( - - )} - - - {(field) => ( - - )} - - - - - - {(field) => ( - - )} - - - {(field) => ( - - )} - - - - - - {(field) => ( - - )} - - - {(field) => ( - - )} - - - ({ - isDefaultValue: s.isDefaultValue || s.isPristine, - isSubmitting: s.isSubmitting, - })} - > - {({ isDefaultValue, isSubmitting }) => ( - - {smtpConfigured && ( -