mirror of
https://github.com/h44z/wg-portal.git
synced 2025-08-09 06:52:24 +00:00
more cleanup
This commit is contained in:
parent
4b3225e293
commit
93f8633b2b
@ -42,12 +42,9 @@ COPY --from=builder /etc/group /etc/group
|
||||
|
||||
# Copy binaries
|
||||
COPY --from=builder /build/dist/wg-portal /app/wg-portal
|
||||
COPY --from=builder /build/dist/hc /app/hc
|
||||
|
||||
# Set the Current Working Directory inside the container
|
||||
WORKDIR /app
|
||||
|
||||
# Command to run the executable
|
||||
CMD [ "/app/wg-portal" ]
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 CMD [ "/app/hc", "http://localhost:11223/health" ]
|
||||
CMD [ "/app/wg-portal" ]
|
222
README.md
222
README.md
@ -1,4 +1,4 @@
|
||||
# WireGuard Portal
|
||||
# WireGuard Portal (V2 - alpha testing)
|
||||
|
||||
[](https://travis-ci.com/h44z/wg-portal)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
@ -8,6 +8,9 @@
|
||||

|
||||
[](https://hub.docker.com/r/h44z/wg-portal/)
|
||||
|
||||
> :warning: **IMPORTANT** Version 2 is currently under development and may contain bugs. It is currently not advised to use this version
|
||||
in production. Use version [1.0.17](https://github.com/h44z/wg-portal/releases) instead.
|
||||
|
||||
A simple, web based configuration portal for [WireGuard](https://wireguard.com).
|
||||
The portal uses the WireGuard [wgctrl](https://github.com/WireGuard/wgctrl-go) library to manage existing VPN
|
||||
interfaces. This allows for seamless activation or deactivation of new users, without disturbing existing VPN
|
||||
@ -15,25 +18,27 @@ connections.
|
||||
|
||||
The configuration portal supports using a database (SQLite, MySQL, MsSQL or Postgres), OAuth or LDAP (Active Directory or OpenLDAP) as a user source for authentication and profile data.
|
||||
|
||||
|
||||
## Features
|
||||
* Self-hosted and web based
|
||||
* Self-hosted - the whole application is a single binary
|
||||
* Responsive web UI written in Vue.JS
|
||||
* Automatically select IP from the network pool assigned to client
|
||||
* QR-Code for convenient mobile client configuration
|
||||
* Sent email to client with QR-code and client config
|
||||
* Enable / Disable clients seamlessly
|
||||
* Generation of `wgX.conf` if required
|
||||
* Generation of wg-quick configuration file (`wgX.conf`) if required
|
||||
* User authentication (database, OAuth or LDAP)
|
||||
* IPv6 ready
|
||||
* User authentication (database, OAuth or LDAP)
|
||||
* Dockerized
|
||||
* Responsive web UI written in Vue.JS
|
||||
* One single binary
|
||||
* Docker ready
|
||||
* Can be used with existing WireGuard setups
|
||||
* Support for multiple WireGuard interfaces
|
||||
* Peer Expiry Feature
|
||||
* REST API for management and client deployment (coming soon)
|
||||
* Handle route and DNS settings like wg-quick does
|
||||
* ~~REST API for management and client deployment~~ (coming soon)
|
||||
|
||||

|
||||
|
||||
|
||||
## Configuration
|
||||
You can configure WireGuard Portal using a yaml configuration file.
|
||||
The filepath of the yaml configuration file defaults to **config.yml** in the working directory of the executable.
|
||||
@ -55,94 +60,129 @@ The following configuration options are available:
|
||||
| log_level | advanced | warn | The loglevel, can be one of: trace, debug, info, warn, error. |
|
||||
| log_pretty | advanced | false | Uses pretty, colorized log messages. |
|
||||
| log_json | advanced | false | Logs in JSON format. |
|
||||
| ldap_sync_interval | advanced | 15m | |
|
||||
| start_listen_port | advanced | 51820 | |
|
||||
| start_cidr_v4 | advanced | 10.11.12.0/24 | |
|
||||
| start_cidr_v6 | advanced | fdfd:d3ad:c0de:1234::0/64 | |
|
||||
| use_ip_v6 | advanced | true | |
|
||||
| config_storage_path | advanced | | |
|
||||
| expiry_check_interval | advanced | 15m | |
|
||||
| rule_prio_offset | advanced | 20000 | |
|
||||
| route_table_offset | advanced | 20000 | |
|
||||
| use_ping_checks | statistics | true | |
|
||||
| ping_check_workers | statistics | 10 | |
|
||||
| ping_unprivileged | statistics | false | |
|
||||
| ping_check_interval | statistics | 1m | |
|
||||
| data_collection_interval | statistics | 10m | |
|
||||
| collect_interface_data | statistics | true | |
|
||||
| collect_peer_data | statistics | true | |
|
||||
| collect_audit_data | statistics | true | |
|
||||
| host | mail | 127.0.0.1 | |
|
||||
| port | mail | 25 | |
|
||||
| encryption | mail | none | |
|
||||
| cert_validation | mail | false | |
|
||||
| username | mail | | |
|
||||
| password | mail | | |
|
||||
| auth_type | mail | plain | |
|
||||
| from | mail | Wireguard Portal <noreply@wireguard.local> | |
|
||||
| link_only | mail | false | |
|
||||
| callback_url_prefix | auth | /api/v0 | |
|
||||
| oidc | auth | Empty Array - no providers configured | |
|
||||
| oauth | auth | Empty Array - no providers configured | |
|
||||
| ldap | auth | Empty Array - no providers configured | |
|
||||
| provider_name | auth/oidc | | |
|
||||
| display_name | auth/oidc | | |
|
||||
| base_url | auth/oidc | | |
|
||||
| client_id | auth/oidc | | |
|
||||
| client_secret | auth/oidc | | |
|
||||
| extra_scopes | auth/oidc | | |
|
||||
| field_map | auth/oidc | | |
|
||||
| registration_enabled | auth/oidc | | |
|
||||
| provider_name | auth/oidc | | |
|
||||
| display_name | auth/oauth | | |
|
||||
| base_url | auth/oauth | | |
|
||||
| client_id | auth/oauth | | |
|
||||
| client_secret | auth/oauth | | |
|
||||
| auth_url | auth/oauth | | |
|
||||
| token_url | auth/oauth | | |
|
||||
| redirect_url | auth/oauth | | |
|
||||
| user_info_url | auth/oauth | | |
|
||||
| scopes | auth/oauth | | |
|
||||
| field_map | auth/oauth | | |
|
||||
| registration_enabled | auth/oauth | | |
|
||||
| url | auth/ldap | | |
|
||||
| start_tls | auth/ldap | | |
|
||||
| cert_validation | auth/ldap | | |
|
||||
| tls_certificate_path | auth/ldap | | |
|
||||
| tls_key_path | auth/ldap | | |
|
||||
| base_dn | auth/ldap | | |
|
||||
| bind_user | auth/ldap | | |
|
||||
| bind_pass | auth/ldap | | |
|
||||
| field_map | auth/ldap | | |
|
||||
| login_filter | auth/ldap | | |
|
||||
| admin_group | auth/ldap | | |
|
||||
| synchronize | auth/ldap | | |
|
||||
| disable_missing | auth/ldap | | |
|
||||
| sync_filter | auth/ldap | | |
|
||||
| registration_enabled | auth/ldap | | |
|
||||
| debug | database | false | |
|
||||
| slow_query_threshold | database | | |
|
||||
| type | database | sqlite | |
|
||||
| dsn | database | sqlite.db | |
|
||||
| request_logging | web | false | |
|
||||
| external_url | web | http://localhost:8888 | |
|
||||
| listening_address | web | :8888 | |
|
||||
| session_identifier | web | wgPortalSession | |
|
||||
| session_secret | web | very_secret | |
|
||||
| csrf_secret | web | extremely_secret | |
|
||||
| site_title | web | WireGuard Portal | |
|
||||
| site_company_name | web | WireGuard Portal | |
|
||||
| ldap_sync_interval | advanced | 15m | The time interval after which users will be synchronized from LDAP. |
|
||||
| start_listen_port | advanced | 51820 | The first port number that will be used as listening port for new interfaces. |
|
||||
| start_cidr_v4 | advanced | 10.11.12.0/24 | The first IPv4 subnet that will be used for new interfaces. |
|
||||
| start_cidr_v6 | advanced | fdfd:d3ad:c0de:1234::0/64 | The first IPv6 subnet that will be used for new interfaces. |
|
||||
| use_ip_v6 | advanced | true | Enable IPv6 support. |
|
||||
| config_storage_path | advanced | | If a wg-quick style configuration should be stored to the filesystem, specify a storage directory. |
|
||||
| expiry_check_interval | advanced | 15m | The interval after which existing peers will be checked if they expired. |
|
||||
| rule_prio_offset | advanced | 20000 | The default offset for ip route rule priorities. |
|
||||
| route_table_offset | advanced | 20000 | The default offset for ip route table id's. |
|
||||
| use_ping_checks | statistics | true | If enabled, peers will be pinged periodically to check if they are still connected. |
|
||||
| ping_check_workers | statistics | 10 | Number of parallel ping checks that will be executed. |
|
||||
| ping_unprivileged | statistics | false | If set to false, the ping checks will run without root permissions (BETA). |
|
||||
| ping_check_interval | statistics | 1m | The interval time between two ping check runs. |
|
||||
| data_collection_interval | statistics | 10m | The interval between the data collection cycles. |
|
||||
| collect_interface_data | statistics | true | A flag to enable interface data collection like bytes sent and received. |
|
||||
| collect_peer_data | statistics | true | A flag to enable peer data collection like bytes sent and received, last handshake and remote endpoint address. |
|
||||
| collect_audit_data | statistics | true | If enabled, some events, like portal logins, will be logged to the database. |
|
||||
| host | mail | 127.0.0.1 | The mail-server address. |
|
||||
| port | mail | 25 | The mail-server SMTP port. |
|
||||
| encryption | mail | none | SMTP encryption type, allowed values: none, tls, starttls. |
|
||||
| cert_validation | mail | false | Validate the mail server certificate (if encryption tls is used). |
|
||||
| username | mail | | The SMTP user name. |
|
||||
| password | mail | | The SMTP password. |
|
||||
| auth_type | mail | plain | SMTP authentication type, allowed values: plain, login, crammd5. |
|
||||
| from | mail | Wireguard Portal <noreply@wireguard.local> | The address that is used to send mails. |
|
||||
| link_only | mail | false | Only send links to WireGuard Portal instead of the full configuration. |
|
||||
| callback_url_prefix | auth | /api/v0 | OAuth callback URL prefix. The full callback URL will look like: https://wg.portal.local/callback_url_prefix/provider_name/callback |
|
||||
| oidc | auth | Empty Array - no providers configured | A list of OpenID Connect providers. See auth/oidc properties to setup a new provider. |
|
||||
| oauth | auth | Empty Array - no providers configured | A list of plain OAuth providers. See auth/oauth properties to setup a new provider. |
|
||||
| ldap | auth | Empty Array - no providers configured | A list of LDAP providers. See auth/ldap properties to setup a new provider. |
|
||||
| provider_name | auth/oidc | | A unique provider name. This name must be unique throughout all authentication providers (even other types). |
|
||||
| display_name | auth/oidc | | The display name is shown at the login page (the login button). |
|
||||
| base_url | auth/oidc | | The base_url is the URL identifier for the service. For example: "https://accounts.google.com". |
|
||||
| client_id | auth/oidc | | The OAuth client id. |
|
||||
| client_secret | auth/oidc | | The OAuth client secret. |
|
||||
| extra_scopes | auth/oidc | | Extra scopes that should be used in the OpenID Connect authentication flow. |
|
||||
| field_map | auth/oidc | | Mapping of user fields. Internal fields: user_identifier, email, firstname, lastname, phone, department and is_admin. |
|
||||
| registration_enabled | auth/oidc | | If registration is enabled, new user accounts will created in WireGuard Portal. |
|
||||
| provider_name | auth/oauth | | A unique provider name. This name must be unique throughout all authentication providers (even other types). |
|
||||
| display_name | auth/oauth | | The display name is shown at the login page (the login button). |
|
||||
| base_url | auth/oauth | | The base_url is the URL identifier for the service. For example: "https://accounts.google.com". |
|
||||
| client_id | auth/oauth | | The OAuth client id. |
|
||||
| client_secret | auth/oauth | | The OAuth client secret. |
|
||||
| auth_url | auth/oauth | | The URL for the authentication endpoint. |
|
||||
| token_url | auth/oauth | | The URL for the token endpoint. |
|
||||
| redirect_url | auth/oauth | | The redirect URL. |
|
||||
| user_info_url | auth/oauth | | The URL for the user information endpoint. |
|
||||
| scopes | auth/oauth | | OAuth scopes. |
|
||||
| field_map | auth/oauth | | Mapping of user fields. Internal fields: user_identifier, email, firstname, lastname, phone, department and is_admin. |
|
||||
| registration_enabled | auth/oauth | | If registration is enabled, new user accounts will created in WireGuard Portal. |
|
||||
| url | auth/ldap | | The LDAP server url. For example: ldap://srv-ad01.company.local:389 |
|
||||
| start_tls | auth/ldap | | Use STARTTLS to encrypt LDAP requests. |
|
||||
| cert_validation | auth/ldap | | Validate the LDAP server certificate. |
|
||||
| tls_certificate_path | auth/ldap | | A path to the TLS certificate. |
|
||||
| tls_key_path | auth/ldap | | A path to the TLS key. |
|
||||
| base_dn | auth/ldap | | The base DN for searching users. For example: DC=COMPANY,DC=LOCAL |
|
||||
| bind_user | auth/ldap | | The bind user. For example: company\\ldap_wireguard |
|
||||
| bind_pass | auth/ldap | | The bind password. |
|
||||
| field_map | auth/ldap | | Mapping of user fields. Internal fields: user_identifier, email, firstname, lastname, phone, department and memberof. |
|
||||
| login_filter | auth/ldap | | LDAP filters for users that should be allowed to log in. {{login_identifier}} will be replaced with the login username. |
|
||||
| admin_group | auth/ldap | | Users in this group are marked as administrators. |
|
||||
| synchronize | auth/ldap | | Periodically synchronize users (name, department, phone, status, ...) to the WireGuard Portal database. |
|
||||
| disable_missing | auth/ldap | | If synchronization is enabled, missing LDAP users will be disabled in WireGuard Portal. |
|
||||
| sync_filter | auth/ldap | | LDAP filters for users that should be synchronized to WireGuard Portal. |
|
||||
| registration_enabled | auth/ldap | | If registration is enabled, new user accounts will created in WireGuard Portal. |
|
||||
| debug | database | false | Debug database statements (log each statement). |
|
||||
| slow_query_threshold | database | | A threshold for slow database queries. If the threshold is exceeded, a warning message will be logged. |
|
||||
| type | database | sqlite | The database type. Allowed values: sqlite, mssql, mysql or postgres. |
|
||||
| dsn | database | sqlite.db | The database DSN. For example: user:pass@tcp(1.2.3.4:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local |
|
||||
| request_logging | web | false | Log all HTTP requests. |
|
||||
| external_url | web | http://localhost:8888 | The URL where a client can access WireGuard Portal. |
|
||||
| listening_address | web | :8888 | The listening port of the web server. |
|
||||
| session_identifier | web | wgPortalSession | The session identifier for the web frontend. |
|
||||
| session_secret | web | very_secret | The session secret for the web frontend. |
|
||||
| csrf_secret | web | extremely_secret | The CSRF secret. |
|
||||
| site_title | web | WireGuard Portal | The title that is shown in the web frontend. |
|
||||
| site_company_name | web | WireGuard Portal | The company name that is shown at the bottom of the web frontend. |
|
||||
|
||||
|
||||
## Upgrading from V1
|
||||
|
||||
> :warning: Before upgrading from V1, make sure that you have a backup of your currently working configuration files and database!
|
||||
|
||||
To start the upgrade process, start the wg-portal binary with the **-migrateFrom** parameter.
|
||||
The configuration (config.yml) for WireGuard Portal must be updated and valid before starting the upgrade.
|
||||
|
||||
To upgrade from a previous SQLite database, start wg-portal like:
|
||||
|
||||
```shell
|
||||
./wg-portal-amd64 -migrateFrom=old_wg_portal.db
|
||||
```
|
||||
|
||||
You can also specify the database type using the parameter **-migrateFromType**, supported types: mysql, mssql, postgres or sqlite.
|
||||
For example:
|
||||
|
||||
```shell
|
||||
./wg-portal-amd64 -migrateFromType=mysql -migrateFrom=user:pass@tcp(1.2.3.4:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local
|
||||
```
|
||||
|
||||
The upgrade will transform the old, existing database and store the values in the new database specified in config.yml.
|
||||
Ensure that the new database does not contain any data!
|
||||
|
||||
|
||||
## V2 TODOs
|
||||
* Public REST API
|
||||
* Translations
|
||||
* Documentation
|
||||
* Audit UI
|
||||
|
||||
|
||||
## What is out of scope
|
||||
* Generation or application of any `iptables` or `nftables` rules.
|
||||
* Setting up or changing IP-addresses of the WireGuard interface on operating systems other than linux.
|
||||
* Importing private keys of an existing WireGuard setup.
|
||||
* Automatic generation or application of any `iptables` or `nftables` rules.
|
||||
* Support for operating systems other than linux.
|
||||
* Automatic import of private keys of an existing WireGuard setup.
|
||||
|
||||
|
||||
## Application stack
|
||||
|
||||
* [Gin, HTTP web framework written in Go](https://github.com/gin-gonic/gin)
|
||||
* [Bootstrap, for the HTML templates](https://getbootstrap.com/)
|
||||
* [Vue.JS, for the frontend](https://vuejs.org/)
|
||||
* [wgctrl-go](https://github.com/WireGuard/wgctrl-go) and [netlink](https://github.com/vishvananda/netlink) for interface handling
|
||||
* [Gin](https://github.com/gin-gonic/gin), HTTP web framework written in Go
|
||||
* [Bootstrap](https://getbootstrap.com/), for the HTML templates
|
||||
* [Vue.JS](https://vuejs.org/), for the frontend
|
||||
|
||||
|
||||
## License
|
||||
|
||||
|
@ -28,7 +28,8 @@ import (
|
||||
func main() {
|
||||
ctx := internal.SignalAwareContext(context.Background(), syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
logrus.Infof("Starting WireGuard Portal...")
|
||||
logrus.Infof("Starting WireGuard Portal V2...")
|
||||
logrus.Infof("WireGuard Portal version: %s", internal.Version)
|
||||
|
||||
cfg, err := config.GetConfig()
|
||||
internal.AssertNoError(err)
|
||||
@ -77,7 +78,7 @@ func main() {
|
||||
statisticsCollector, err := wireguard.NewStatisticsCollector(cfg, database, wireGuard)
|
||||
internal.AssertNoError(err)
|
||||
|
||||
cfgFileManager, err := configfile.NewConfigFileManager(cfg, database, database, cfgFileSystem)
|
||||
cfgFileManager, err := configfile.NewConfigFileManager(cfg, eventBus, database, database, cfgFileSystem)
|
||||
internal.AssertNoError(err)
|
||||
|
||||
mailManager, err := mail.NewMailManager(cfg, mailer, cfgFileManager, database, database)
|
||||
|
@ -2,7 +2,7 @@
|
||||
version: '3.6'
|
||||
services:
|
||||
wg-portal:
|
||||
image: h44z/wg-portal:1.0.16
|
||||
image: h44z/wg-portal:2.0.0-alpha1
|
||||
container_name: wg-portal
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
@ -16,4 +16,4 @@ services:
|
||||
- /etc/wireguard:/etc/wireguard
|
||||
- ./data:/app/data
|
||||
environment:
|
||||
- EXTERNAL_URL=http://localhost:8123
|
||||
- EXTERNAL_URL=http://localhost:8888
|
||||
|
@ -33,9 +33,9 @@ const title = computed(() => {
|
||||
}
|
||||
|
||||
if (selectedInterface.value) {
|
||||
return t("interfaces.interface.edit") + ": " + selectedInterface.value.Identifier
|
||||
return t("modals.interface-edit.headline-edit") + " " + selectedInterface.value.Identifier
|
||||
}
|
||||
return t("interfaces.interface.new")
|
||||
return t("modals.interface-edit.headline-new")
|
||||
})
|
||||
|
||||
const formData = ref(freshInterface())
|
||||
@ -291,134 +291,135 @@ async function del() {
|
||||
<template #default>
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-bs-toggle="tab" href="#interface">Interface</a>
|
||||
<a class="nav-link active" data-bs-toggle="tab" href="#interface">{{ $t('modals.interface-edit.tab-interface') }}</a>
|
||||
</li>
|
||||
<li v-if="formData.Mode==='server'" class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="tab" href="#peerdefaults">Peer Defaults</a>
|
||||
<a class="nav-link" data-bs-toggle="tab" href="#peerdefaults">{{ $t('modals.interface-edit.tab-peerdef') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div id="interfaceTabs" class="tab-content">
|
||||
<div id="interface" class="tab-pane fade active show">
|
||||
<fieldset>
|
||||
<legend class="mt-4">General</legend>
|
||||
<legend class="mt-4">{{ $t('modals.interface-edit.header-general') }}</legend>
|
||||
<div v-if="props.interfaceId==='#NEW#'" class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.identifier') }}</label>
|
||||
<input v-model="formData.Identifier" class="form-control" placeholder="The device identifier" type="text">
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.identifier.label') }}</label>
|
||||
<input v-model="formData.Identifier" class="form-control" :placeholder="$t('modals.interface-edit.identifier.placeholder')" type="text">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.displayname') }}</label>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.mode.label') }}</label>
|
||||
<select v-model="formData.Mode" class="form-select">
|
||||
<option value="server">Server Mode</option>
|
||||
<option value="client">Client Mode</option>
|
||||
<option value="any">Custom Mode</option>
|
||||
<option value="server">{{ $t('modals.interface-edit.mode.server') }}</option>
|
||||
<option value="client">{{ $t('modals.interface-edit.mode.client') }}</option>
|
||||
<option value="any">{{ $t('modals.interface-edit.mode.any') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.displayname') }}</label>
|
||||
<input v-model="formData.DisplayName" class="form-control" placeholder="A descriptive name of the interface" type="text">
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.display-name.label') }}</label>
|
||||
<input v-model="formData.DisplayName" class="form-control" :placeholder="$t('modals.interface-edit.display-name.placeholder')" type="text">
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend class="mt-4">Cryptography</legend>
|
||||
<legend class="mt-4">{{ $t('modals.interface-edit.header-crypto') }}</legend>
|
||||
<div class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.privatekey') }}</label>
|
||||
<input v-model="formData.PrivateKey" class="form-control" placeholder="The private key" required type="email">
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.private-key.label') }}</label>
|
||||
<input v-model="formData.PrivateKey" class="form-control" :placeholder="$t('modals.interface-edit.private-key.placeholder')" required type="email">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.publickey') }}</label>
|
||||
<input v-model="formData.PublicKey" class="form-control" placeholder="The public key" required type="email">
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.public-key.label') }}</label>
|
||||
<input v-model="formData.PublicKey" class="form-control" :placeholder="$t('modals.interface-edit.public-key.placeholder')" required type="email">
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend class="mt-4">Networking</legend>
|
||||
<legend class="mt-4">{{ $t('modals.interface-edit.header-network') }}</legend>
|
||||
<div class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.ips') }}</label>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.ip.label') }}</label>
|
||||
<vue3-tags-input class="form-control" :tags="formData.Addresses"
|
||||
placeholder="IP Addresses (CIDR format)"
|
||||
:placeholder="$t('modals.interface-edit.ip.placeholder')"
|
||||
:add-tag-on-keys="[13, 188, 32, 9]"
|
||||
:validate="validateCIDR"
|
||||
@on-tags-changed="handleChangeAddresses"/>
|
||||
</div>
|
||||
<div v-if="formData.Mode==='server'" class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.listenport') }}</label>
|
||||
<input v-model="formData.ListenPort" class="form-control" placeholder="Listen Port" type="number">
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.listen-port.label') }}</label>
|
||||
<input v-model="formData.ListenPort" class="form-control" :placeholder="$t('modals.interface-edit.listen-port.placeholder')" type="number">
|
||||
</div>
|
||||
<div v-if="formData.Mode!=='server'" class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.dns') }}</label>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.dns.label') }}</label>
|
||||
<vue3-tags-input class="form-control" :tags="formData.Dns"
|
||||
placeholder="DNS Servers"
|
||||
:placeholder="$t('modals.interface-edit.dns.placeholder')"
|
||||
:add-tag-on-keys="[13, 188, 32, 9]"
|
||||
:validate="validateIP"
|
||||
@on-tags-changed="handleChangeDns"/>
|
||||
</div>
|
||||
<div v-if="formData.Mode!=='server'" class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.dnssearch') }}</label>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.dns-search.label') }}</label>
|
||||
<vue3-tags-input class="form-control" :tags="formData.DnsSearch"
|
||||
placeholder="DNS Search prefixes"
|
||||
:placeholder="$t('modals.interface-edit.dns-search.placeholder')"
|
||||
:add-tag-on-keys="[13, 188, 32, 9]"
|
||||
:validate="validateDomain"
|
||||
@on-tags-changed="handleChangeDnsSearch"/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group col-md-6">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.mtu') }}</label>
|
||||
<input v-model="formData.Mtu" class="form-control" placeholder="Client MTU (0 = default)" type="number">
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.mtu.label') }}</label>
|
||||
<input v-model="formData.Mtu" class="form-control" :placeholder="$t('modals.interface-edit.mtu.placeholder')" type="number">
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.firewallmark') }}</label>
|
||||
<input v-model="formData.FirewallMark" class="form-control" placeholder="Firewall Mark (0 = default)" type="number">
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.firewall-mark.label') }}</label>
|
||||
<input v-model="formData.FirewallMark" class="form-control" :placeholder="$t('modals.interface-edit.firewall-mark.placeholder')" type="number">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group col-md-6">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.routingtable') }}</label>
|
||||
<input v-model="formData.RoutingTable" class="form-control" placeholder="Routing Table (0 = default)" type="number">
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.routing-table.label') }}</label>
|
||||
<input v-model="formData.RoutingTable" aria-describedby="routingTableHelp" class="form-control" :placeholder="$t('modals.interface-edit.routing-table.placeholder')" type="text">
|
||||
<small id="routingTableHelp" class="form-text text-muted">{{ $t('modals.interface-edit.routing-table.description') }}</small>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend class="mt-4">Hooks</legend>
|
||||
<legend class="mt-4">{{ $t('modals.interface-edit.header-hooks') }}</legend>
|
||||
<div class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.preup') }}</label>
|
||||
<textarea v-model="formData.PreUp" class="form-control" rows="2"></textarea>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.pre-up.label') }}</label>
|
||||
<textarea v-model="formData.PreUp" class="form-control" rows="2" :placeholder="$t('modals.interface-edit.pre-up.placeholder')"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.postup') }}</label>
|
||||
<textarea v-model="formData.PostUp" class="form-control" rows="2"></textarea>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.post-up.label') }}</label>
|
||||
<textarea v-model="formData.PostUp" class="form-control" rows="2" :placeholder="$t('modals.interface-edit.post-up.placeholder')"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.predown') }}</label>
|
||||
<textarea v-model="formData.PreDown" class="form-control" rows="2"></textarea>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.pre-down.label') }}</label>
|
||||
<textarea v-model="formData.PreDown" class="form-control" rows="2" :placeholder="$t('modals.interface-edit.pre-down.placeholder')"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.postdown') }}</label>
|
||||
<textarea v-model="formData.PostDown" class="form-control" rows="2"></textarea>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.post-down.label') }}</label>
|
||||
<textarea v-model="formData.PostDown" class="form-control" rows="2" :placeholder="$t('modals.interface-edit.post-down.placeholder')"></textarea>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend class="mt-4">State</legend>
|
||||
<legend class="mt-4">{{ $t('modals.interface-edit.header-state') }}</legend>
|
||||
<div class="form-check form-switch">
|
||||
<input v-model="formData.Disabled" class="form-check-input" type="checkbox">
|
||||
<label class="form-check-label" >Disabled</label>
|
||||
<label class="form-check-label">{{ $t('modals.interface-edit.disabled.label') }}</label>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input v-model="formData.SaveConfig" checked="" class="form-check-input" type="checkbox">
|
||||
<label class="form-check-label">Save Config to File</label>
|
||||
<label class="form-check-label">{{ $t('modals.interface-edit.save-config.label') }}</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div id="peerdefaults" class="tab-pane fade">
|
||||
<fieldset>
|
||||
<legend class="mt-4">Networking</legend>
|
||||
<legend class="mt-4">{{ $t('modals.interface-edit.header-network') }}</legend>
|
||||
<div class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.defaults.endpoint') }}</label>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.defaults.endpoint') }}</label>
|
||||
<input v-model="formData.PeerDefEndpoint" class="form-control" placeholder="Endpoint Addresses" type="text">
|
||||
<small class="form-text text-muted">The endpoint address that peers will connect to.</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.defaults.networks') }}</label>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.defaults.networks') }}</label>
|
||||
<vue3-tags-input class="form-control" :tags="formData.PeerDefNetwork"
|
||||
placeholder="Network Addresses"
|
||||
:add-tag-on-keys="[13, 188, 32, 9]"
|
||||
@ -427,7 +428,7 @@ async function del() {
|
||||
<small class="form-text text-muted">Peers will get IP addresses from those subnets.</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.defaults.allowedips') }}</label>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.defaults.allowedips') }}</label>
|
||||
<vue3-tags-input class="form-control" :tags="formData.PeerDefAllowedIPs"
|
||||
placeholder="Default Allowed IP Addresses"
|
||||
:add-tag-on-keys="[13, 188, 32, 9]"
|
||||
@ -435,7 +436,7 @@ async function del() {
|
||||
@on-tags-changed="handleChangePeerDefAllowedIPs"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.defaults.dns') }}</label>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.defaults.dns') }}</label>
|
||||
<vue3-tags-input class="form-control" :tags="formData.PeerDefDns"
|
||||
placeholder="DNS Servers"
|
||||
:add-tag-on-keys="[13, 188, 32, 9]"
|
||||
@ -443,7 +444,7 @@ async function del() {
|
||||
@on-tags-changed="handleChangePeerDefDns"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.defaults.dnssearch') }}</label>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.defaults.dnssearch') }}</label>
|
||||
<vue3-tags-input class="form-control" :tags="formData.PeerDefDnsSearch"
|
||||
placeholder="DNS Search prefix"
|
||||
:add-tag-on-keys="[13, 188, 32, 9]"
|
||||
@ -452,41 +453,41 @@ async function del() {
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group col-md-6">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.defaults.mtu') }}</label>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.defaults.mtu') }}</label>
|
||||
<input v-model="formData.PeerDefMtu" class="form-control" placeholder="Client MTU (0 = default)" type="number">
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.defaults.firewallmark') }}</label>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.defaults.firewallmark') }}</label>
|
||||
<input v-model="formData.PeerDefFirewallMark" class="form-control" placeholder="Firewall Mark (0 = default)" type="number">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group col-md-6">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.defaults.routingtable') }}</label>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.defaults.routingtable') }}</label>
|
||||
<input v-model="formData.PeerDefRoutingTable" class="form-control" placeholder="Routing Table (0 = default)" type="number">
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.defaults.keepalive') }}</label>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.defaults.keepalive') }}</label>
|
||||
<input v-model="formData.PeerDefPersistentKeepalive" class="form-control" placeholder="Persistent Keepalive (0 = default)" type="number">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend class="mt-4">Hooks</legend>
|
||||
<legend class="mt-4">{{ $t('modals.interface-edit.header-peer-hooks') }}</legend>
|
||||
<div class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.defaults.preup') }}</label>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.defaults.preup') }}</label>
|
||||
<textarea v-model="formData.PeerDefPreUp" class="form-control" rows="2"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.defaults.postup') }}</label>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.defaults.postup') }}</label>
|
||||
<textarea v-model="formData.PeerDefPostUp" class="form-control" rows="2"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.defaults.predown') }}</label>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.defaults.predown') }}</label>
|
||||
<textarea v-model="formData.PeerDefPreDown" class="form-control" rows="2"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label mt-4">{{ $t('modals.interfaceedit.defaults.postdown') }}</label>
|
||||
<label class="form-label mt-4">{{ $t('modals.interface-edit.defaults.postdown') }}</label>
|
||||
<textarea v-model="formData.PeerDefPostDown" class="form-control" rows="2"></textarea>
|
||||
</div>
|
||||
</fieldset>
|
||||
@ -499,10 +500,10 @@ async function del() {
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="flex-fill text-start">
|
||||
<button v-if="props.interfaceId!=='#NEW#'" class="btn btn-danger me-1" type="button" @click.prevent="del">Delete</button>
|
||||
<button v-if="props.interfaceId!=='#NEW#'" class="btn btn-danger me-1" type="button" @click.prevent="del">{{ $t('general.delete') }}</button>
|
||||
</div>
|
||||
<button class="btn btn-primary me-1" type="button" @click.prevent="save">Save</button>
|
||||
<button class="btn btn-secondary" type="button" @click.prevent="close">Discard</button>
|
||||
<button class="btn btn-primary me-1" type="button" @click.prevent="save">{{ $t('general.save') }}</button>
|
||||
<button class="btn btn-secondary" type="button" @click.prevent="close">{{ $t('general.close') }}</button>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
@ -243,7 +243,89 @@
|
||||
"headline": "Config for Interface:"
|
||||
},
|
||||
"interface-edit": {
|
||||
"privatekey": "Private Key"
|
||||
"headline-edit": "Edit Interface:",
|
||||
"headline-new": "New Interface",
|
||||
"tab-interface": "Interface",
|
||||
"tab-peerdef": "Peer Defaults",
|
||||
"header-general": "General",
|
||||
"header-network": "Network",
|
||||
"header-crypto": "Cryptography",
|
||||
"header-hooks": "Interface Hooks",
|
||||
"header-peer-hooks": "Hooks",
|
||||
"header-state": "State",
|
||||
"identifier": {
|
||||
"label": "Identifier",
|
||||
"placeholder": "The unique interface identifier"
|
||||
},
|
||||
"mode": {
|
||||
"label": "Interface Mode",
|
||||
"server": "Server Mode",
|
||||
"client": "Client Mode",
|
||||
"any": "Unknown Mode"
|
||||
},
|
||||
"display-name": {
|
||||
"label": "Display Name",
|
||||
"placeholder": "The descriptive name for the interface"
|
||||
},
|
||||
"private-key": {
|
||||
"label": "Private Key",
|
||||
"placeholder": "The private key"
|
||||
},
|
||||
"public-key": {
|
||||
"label": "Public Key",
|
||||
"placeholder": "The public key"
|
||||
},
|
||||
"ip": {
|
||||
"label": "IP Addresses",
|
||||
"placeholder": "IP Addresses (CIDR format)"
|
||||
},
|
||||
"listen-port": {
|
||||
"label": "Listen Port",
|
||||
"placeholder": "The listening port"
|
||||
},
|
||||
"dns": {
|
||||
"label": "DNS Server",
|
||||
"placeholder": "The DNS servers that should be used"
|
||||
},
|
||||
"dns-search": {
|
||||
"label": "DNS Search Domains",
|
||||
"placeholder": "DNS search prefixes"
|
||||
},
|
||||
"mtu": {
|
||||
"label": "MTU",
|
||||
"placeholder": "The interface MTU (0 = keep default)"
|
||||
},
|
||||
"firewall-mark": {
|
||||
"label": "Firewall Mark",
|
||||
"placeholder": "Firewall mark that is applied to outgoing traffic. (0 = automatic)"
|
||||
},
|
||||
"routing-table": {
|
||||
"label": "Routing Table",
|
||||
"placeholder": "The routing table ID",
|
||||
"description": "Special cases: off = do not manage routes, 0 = automatic"
|
||||
},
|
||||
"pre-up": {
|
||||
"label": "Pre-Up",
|
||||
"placeholder": "One or multiple bash commands"
|
||||
},
|
||||
"post-up": {
|
||||
"label": "Post-Up",
|
||||
"placeholder": "One or multiple bash commands"
|
||||
},
|
||||
"pre-down": {
|
||||
"label": "Pre-Down",
|
||||
"placeholder": "One or multiple bash commands"
|
||||
},
|
||||
"post-down": {
|
||||
"label": "Post-Down",
|
||||
"placeholder": "One or multiple bash commands"
|
||||
},
|
||||
"disabled": {
|
||||
"label": "Interface Disabled"
|
||||
},
|
||||
"save-config": {
|
||||
"label": "Automatically save wg-quick config"
|
||||
}
|
||||
},
|
||||
"peer-view": {
|
||||
"headline-peer": "Peer:",
|
||||
|
12
go.mod
12
go.mod
@ -7,7 +7,7 @@ require (
|
||||
github.com/gin-contrib/cors v1.4.0
|
||||
github.com/gin-contrib/sessions v0.0.5
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/glebarez/sqlite v1.8.0
|
||||
github.com/glebarez/sqlite v1.9.0
|
||||
github.com/go-ldap/ldap/v3 v3.4.5
|
||||
github.com/prometheus-community/pro-bing v0.3.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
@ -18,9 +18,10 @@ require (
|
||||
github.com/vardius/message-bus v1.1.5
|
||||
github.com/vishvananda/netlink v1.1.0
|
||||
github.com/xhit/go-simple-mail/v2 v2.15.0
|
||||
github.com/yeqown/go-qrcode/v2 v2.2.1
|
||||
github.com/yeqown/go-qrcode/v2 v2.2.2
|
||||
golang.org/x/crypto v0.11.0
|
||||
golang.org/x/oauth2 v0.10.0
|
||||
golang.org/x/sys v0.10.0
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gorm.io/driver/mysql v1.5.1
|
||||
@ -39,7 +40,7 @@ require (
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.1 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
@ -91,16 +92,15 @@ require (
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/net v0.12.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
golang.org/x/text v0.11.0 // indirect
|
||||
golang.org/x/tools v0.9.3 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.22.3 // indirect
|
||||
modernc.org/libc v1.22.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/sqlite v1.21.1 // indirect
|
||||
modernc.org/sqlite v1.23.1 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
20
go.sum
20
go.sum
@ -45,10 +45,10 @@ github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6
|
||||
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/glebarez/go-sqlite v1.21.1 h1:7MZyUPh2XTrHS7xNEHQbrhfMZuPSzhkm2A1qgg0y5NY=
|
||||
github.com/glebarez/go-sqlite v1.21.1/go.mod h1:ISs8MF6yk5cL4n/43rSOmVMGJJjHYr7L2MbZZ5Q4E2E=
|
||||
github.com/glebarez/sqlite v1.8.0 h1:02X12E2I/4C1n+v90yTqrjRa8yuo7c3KeHI3FRznCvc=
|
||||
github.com/glebarez/sqlite v1.8.0/go.mod h1:bpET16h1za2KOOMb8+jCp6UBP/iahDpfPQqSaYLTLx8=
|
||||
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
||||
github.com/glebarez/sqlite v1.9.0 h1:Aj6bPA12ZEx5GbSF6XADmCkYXlljPNUY+Zf1EQxynXs=
|
||||
github.com/glebarez/sqlite v1.9.0/go.mod h1:YBYCoyupOao60lzp1MVBLEjZfgkq0tdB1voAQ09K9zw=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
@ -246,8 +246,8 @@ github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1Y
|
||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/xhit/go-simple-mail/v2 v2.15.0 h1:qMXeqcZErUW/Dw6EXxmPuxHzVI8MdxWnEnu2xcisohU=
|
||||
github.com/xhit/go-simple-mail/v2 v2.15.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98=
|
||||
github.com/yeqown/go-qrcode/v2 v2.2.1 h1:Jc1Q916fwC05R8C7mpWDbrT9tyLPaLLKDABoC5XBCe8=
|
||||
github.com/yeqown/go-qrcode/v2 v2.2.1/go.mod h1:2Qsk2APUCPne0TsRo40DIkI5MYnbzYKCnKGEFWrxd24=
|
||||
github.com/yeqown/go-qrcode/v2 v2.2.2 h1:0comk6jEwi0oWNhKEmzx4JI+Q7XIneAApmFSMKWmSVc=
|
||||
github.com/yeqown/go-qrcode/v2 v2.2.2/go.mod h1:2Qsk2APUCPne0TsRo40DIkI5MYnbzYKCnKGEFWrxd24=
|
||||
github.com/yeqown/reedsolomon v1.0.0 h1:x1h/Ej/uJnNu8jaX7GLHBWmZKCAWjEJTetkqaabr4B0=
|
||||
github.com/yeqown/reedsolomon v1.0.0/go.mod h1:P76zpcn2TCuL0ul1Fso373qHRc69LKwAw/Iy6g1WiiM=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
@ -368,14 +368,14 @@ gorm.io/driver/sqlserver v1.5.1/go.mod h1:AYHzzte2msKTmYBYsSIq8ZUsznLJwBdkB2wpI+
|
||||
gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho=
|
||||
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
modernc.org/libc v1.22.3 h1:D/g6O5ftAfavceqlLOFwaZuA5KYafKwmr30A6iSqoyY=
|
||||
modernc.org/libc v1.22.3/go.mod h1:MQrloYP209xa2zHome2a8HLiLm6k0UT8CoHpV74tOFw=
|
||||
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
||||
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/sqlite v1.21.1 h1:GyDFqNnESLOhwwDRaHGdp2jKLDzpyT/rNLglX3ZkMSU=
|
||||
modernc.org/sqlite v1.21.1/go.mod h1:XwQ0wZPIh1iKb5mkvCJ3szzbhk+tykC8ZWqTRTgYRwI=
|
||||
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
||||
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
|
@ -5,8 +5,11 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/h44z/wg-portal/internal/app"
|
||||
"github.com/h44z/wg-portal/internal/config"
|
||||
"github.com/h44z/wg-portal/internal/domain"
|
||||
"github.com/sirupsen/logrus"
|
||||
evbus "github.com/vardius/message-bus"
|
||||
"github.com/yeqown/go-qrcode/v2"
|
||||
"io"
|
||||
"os"
|
||||
@ -15,6 +18,7 @@ import (
|
||||
|
||||
type Manager struct {
|
||||
cfg *config.Config
|
||||
bus evbus.MessageBus
|
||||
tplHandler *TemplateHandler
|
||||
|
||||
fsRepo FileSystemRepo // can be nil if storing the configuration is disabled
|
||||
@ -22,7 +26,7 @@ type Manager struct {
|
||||
wg WireguardDatabaseRepo
|
||||
}
|
||||
|
||||
func NewConfigFileManager(cfg *config.Config, users UserDatabaseRepo, wg WireguardDatabaseRepo, fsRepo FileSystemRepo) (*Manager, error) {
|
||||
func NewConfigFileManager(cfg *config.Config, bus evbus.MessageBus, users UserDatabaseRepo, wg WireguardDatabaseRepo, fsRepo FileSystemRepo) (*Manager, error) {
|
||||
tplHandler, err := newTemplateHandler()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize template handler: %w", err)
|
||||
@ -30,6 +34,7 @@ func NewConfigFileManager(cfg *config.Config, users UserDatabaseRepo, wg Wiregua
|
||||
|
||||
m := &Manager{
|
||||
cfg: cfg,
|
||||
bus: bus,
|
||||
tplHandler: tplHandler,
|
||||
|
||||
fsRepo: fsRepo,
|
||||
@ -58,6 +63,51 @@ func (m Manager) createStorageDirectory() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m Manager) connectToMessageBus() {
|
||||
if m.fsRepo == nil {
|
||||
return // skip subscription
|
||||
}
|
||||
|
||||
_ = m.bus.Subscribe(app.TopicInterfaceUpdated, m.handleInterfaceUpdatedEvent)
|
||||
_ = m.bus.Subscribe(app.TopicPeerInterfaceUpdated, m.handleInterfaceUpdatedEvent)
|
||||
}
|
||||
|
||||
func (m Manager) handleInterfaceUpdatedEvent(iface *domain.Interface) {
|
||||
logrus.Errorf("handling interface updated event for %s", iface.Identifier)
|
||||
|
||||
if !iface.SaveConfig || m.fsRepo == nil {
|
||||
return
|
||||
}
|
||||
|
||||
err := m.PersistInterfaceConfig(context.Background(), iface.Identifier)
|
||||
if err != nil {
|
||||
logrus.Errorf("failed to automatically persist interface config for %s: %v", iface.Identifier, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (m Manager) handlePeerInterfaceUpdatedEvent(id domain.InterfaceIdentifier) {
|
||||
logrus.Errorf("handling interface updated event for %s", id)
|
||||
|
||||
if m.fsRepo == nil {
|
||||
return
|
||||
}
|
||||
|
||||
peerInterface, err := m.wg.GetInterface(context.Background(), id)
|
||||
if err != nil {
|
||||
logrus.Errorf("failed to load interface %s: %v", id, err)
|
||||
return
|
||||
}
|
||||
|
||||
if !peerInterface.SaveConfig {
|
||||
return
|
||||
}
|
||||
|
||||
err = m.PersistInterfaceConfig(context.Background(), peerInterface.Identifier)
|
||||
if err != nil {
|
||||
logrus.Errorf("failed to automatically persist interface config for %s: %v", peerInterface.Identifier, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (m Manager) GetInterfaceConfig(ctx context.Context, id domain.InterfaceIdentifier) (io.Reader, error) {
|
||||
iface, peers, err := m.wg.GetInterfaceAndPeers(ctx, id)
|
||||
if err != nil {
|
||||
|
@ -7,3 +7,5 @@ const TopicUserDeleted = "user:deleted"
|
||||
const TopicAuthLogin = "auth:login"
|
||||
const TopicRouteUpdate = "route:update"
|
||||
const TopicRouteRemove = "route:remove"
|
||||
const TopicInterfaceUpdated = "interface:updated"
|
||||
const TopicPeerInterfaceUpdated = "peer:interface:updated"
|
||||
|
@ -414,6 +414,8 @@ func (m Manager) saveInterface(ctx context.Context, iface *domain.Interface, pee
|
||||
return nil, fmt.Errorf("post-save hooks failed: %w", err)
|
||||
}
|
||||
|
||||
m.bus.Publish(app.TopicInterfaceUpdated, iface)
|
||||
|
||||
return iface, nil
|
||||
}
|
||||
|
||||
|
@ -284,6 +284,10 @@ func (m Manager) savePeers(ctx context.Context, peers ...*domain.Peer) error {
|
||||
m.bus.Publish(app.TopicRouteUpdate, "peers updated")
|
||||
}
|
||||
|
||||
for iface := range interfaces {
|
||||
m.bus.Publish(app.TopicPeerInterfaceUpdated, iface)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -122,13 +122,17 @@ func (c Cidr) BroadcastAddr() Cidr {
|
||||
a16[off+byteNum] |= 1 << uint(bitInByte)
|
||||
}
|
||||
if prefix.Addr().Is4() {
|
||||
addr := netip.AddrFrom16(a16).Unmap()
|
||||
return Cidr{
|
||||
Addr: netip.AddrFrom16(a16).Unmap().String(),
|
||||
Cidr: netip.PrefixFrom(addr, prefix.Bits()).String(),
|
||||
Addr: addr.String(),
|
||||
NetLength: prefix.Bits(),
|
||||
}
|
||||
} else {
|
||||
addr := netip.AddrFrom16(a16) // doesn't unmap
|
||||
return Cidr{
|
||||
Addr: netip.AddrFrom16(a16).String(), // doesn't unmap
|
||||
Cidr: netip.PrefixFrom(addr, prefix.Bits()).String(),
|
||||
Addr: addr.String(), // doesn't unmap
|
||||
NetLength: prefix.Bits(),
|
||||
}
|
||||
}
|
||||
|
BIN
screenshot.png
BIN
screenshot.png
Binary file not shown.
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 107 KiB |
Loading…
x
Reference in New Issue
Block a user