diff --git a/cmd/wg-portal/main.go b/cmd/wg-portal/main.go index 0592b82..327e327 100644 --- a/cmd/wg-portal/main.go +++ b/cmd/wg-portal/main.go @@ -80,7 +80,7 @@ func main() { internal.AssertNoError(err) auditRecorder.StartBackgroundJobs(ctx) - userManager, err := users.NewUserManager(cfg, eventBus, database, database) + userManager, err := users.NewUserManager(cfg, eventBus, database, database, database) internal.AssertNoError(err) userManager.StartBackgroundJobs(ctx) diff --git a/docs/documentation/configuration/examples.md b/docs/documentation/configuration/examples.md index bafc3df..7dfdb33 100644 --- a/docs/documentation/configuration/examples.md +++ b/docs/documentation/configuration/examples.md @@ -86,6 +86,9 @@ auth: memberof: memberOf admin_group: CN=WireGuardAdmins,OU=Some-OU,DC=COMPANY,DC=LOCAL registration_enabled: true + # Restrict interface access based on LDAP filters + interface_filter: + wg0: "(memberOf=CN=VPNUsers,OU=Groups,DC=COMPANY,DC=LOCAL)" log_user_info: true ``` diff --git a/docs/documentation/configuration/overview.md b/docs/documentation/configuration/overview.md index cad55c6..9fa1f84 100644 --- a/docs/documentation/configuration/overview.md +++ b/docs/documentation/configuration/overview.md @@ -742,6 +742,16 @@ Below are the properties for each LDAP provider entry inside `auth.ldap`: - **Important**: The `login_filter` must always be a valid LDAP filter. It should at most return one user. If the filter returns multiple or no users, the login will fail. +#### `interface_filter` +- **Default:** *(empty)* +- **Description:** A map of LDAP filters to restrict access to specific WireGuard interfaces. The map keys are the interface identifiers (e.g., `wg0`), and the values are LDAP filters. Only users matching the filter will be allowed to provision peers for the respective interface. + For example: + ```yaml + interface_filter: + wg0: "(memberOf=CN=VPNUsers,OU=Groups,DC=COMPANY,DC=LOCAL)" + wg1: "(description=special-access)" + ``` + #### `admin_group` - **Default:** *(empty)* - **Description:** A specific LDAP group whose members are considered administrators in WireGuard Portal. diff --git a/docs/documentation/usage/authentication.md b/docs/documentation/usage/authentication.md index 76a9d67..d02afeb 100644 --- a/docs/documentation/usage/authentication.md +++ b/docs/documentation/usage/authentication.md @@ -147,6 +147,26 @@ You can map users to admin roles based on their group membership in the LDAP ser The `admin_group` property defines the distinguished name of the group that is allowed to log in as admin. All groups that are listed in the `memberof` attribute of the user will be checked against this group. If one of the groups matches, the user is granted admin access. +### Interface-specific Provisioning Filters + +You can restrict which users are allowed to provision peers for specific WireGuard interfaces by setting the `interface_filter` property. +This property is a map where each key corresponds to a WireGuard interface identifier, and the value is an LDAP filter. +A user will only be able to see and provision peers for an interface if they match the specified LDAP filter for that interface. + +Example: +```yaml +auth: + ldap: + - provider_name: "ldap1" + # ... other settings + interface_filter: + wg0: "(memberOf=CN=VPNUsers,OU=Groups,DC=COMPANY,DC=LOCAL)" + wg1: "(department=IT)" +``` + +This feature works by materializing the list of authorized users for each interface during the periodic LDAP synchronization. +Even if a user bypasses the UI, the backend will enforce these restrictions at the service layer. + ## User Synchronization diff --git a/docs/documentation/usage/user-sync.md b/docs/documentation/usage/user-sync.md index b248ffb..e7c4e2a 100644 --- a/docs/documentation/usage/user-sync.md +++ b/docs/documentation/usage/user-sync.md @@ -43,4 +43,12 @@ If you set the `disable_missing` property to `true`, any user that is not found All peers associated with that user will also be disabled. If you want a user and its peers to be automatically re-enabled once they are found in LDAP again, set the `auto_re_enable` property to `true`. -This will only re-enable the user if they were disabled by the synchronization process. Manually disabled users will not be re-enabled. \ No newline at end of file +This will only re-enable the user if they were disabled by the synchronization process. Manually disabled users will not be re-enabled. + +##### Interface-specific Access Materialization + +If `interface_filter` is configured in the LDAP provider, the synchronization process will evaluate these filters for each enabled user. +The results are materialized in the `interfaces` table of the database in a hidden field. +This materialized list is used by the backend to quickly determine if a user has permission to provision peers for a specific interface, without having to query the LDAP server for every request. +The list is refreshed every time the LDAP synchronization runs. +For more details on how to configure these filters, see the [Authentication](./authentication.md#interface-specific-provisioning-filters) section. \ No newline at end of file diff --git a/frontend/src/stores/profile.js b/frontend/src/stores/profile.js index 8b5df8f..632e931 100644 --- a/frontend/src/stores/profile.js +++ b/frontend/src/stores/profile.js @@ -74,6 +74,7 @@ export const profileStore = defineStore('profile', { }, hasStatistics: (state) => state.statsEnabled, CountInterfaces: (state) => state.interfaces.length, + HasInterface: (state) => (id) => state.interfaces.some((i) => i.Identifier === id), }, actions: { afterPageSizeChange() { diff --git a/frontend/src/views/ProfileView.vue b/frontend/src/views/ProfileView.vue index 8f7b3e5..6c91c6c 100644 --- a/frontend/src/views/ProfileView.vue +++ b/frontend/src/views/ProfileView.vue @@ -80,6 +80,8 @@ onMounted(async () => {

{{ $t('profile.headline') }}

+
+
@@ -90,8 +92,8 @@ onMounted(async () => {
-
-
+
+