mirror of
				https://github.com/h44z/wg-portal.git
				synced 2025-11-04 08:06:18 +00:00 
			
		
		
		
	
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							@@ -52,7 +52,7 @@ docker-push:
 | 
				
			|||||||
	docker push $(IMAGE)
 | 
						docker push $(IMAGE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
api-docs:
 | 
					api-docs:
 | 
				
			||||||
	cd internal/server; swag init --propertyStrategy pascalcase --parseDependency --parseInternal --generalInfo api.go
 | 
						cd internal; swag init --propertyStrategy pascalcase --parseInternal --generalInfo server/api.go --output server/docs/
 | 
				
			||||||
	$(GOCMD) fmt internal/server/docs/docs.go
 | 
						$(GOCMD) fmt internal/server/docs/docs.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(BUILDDIR)/%-amd64: cmd/%/main.go dep phony
 | 
					$(BUILDDIR)/%-amd64: cmd/%/main.go dep phony
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										113
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										113
									
								
								README.md
									
									
									
									
									
								
							@@ -31,6 +31,7 @@ It also supports LDAP (Active Directory or OpenLDAP) as authentication provider.
 | 
				
			|||||||
 * Can be used with existing WireGuard setups
 | 
					 * Can be used with existing WireGuard setups
 | 
				
			||||||
 * Support for multiple WireGuard interfaces
 | 
					 * Support for multiple WireGuard interfaces
 | 
				
			||||||
 * REST API for management and client deployment
 | 
					 * REST API for management and client deployment
 | 
				
			||||||
 | 
					 * Peer Expiry Feature
 | 
				
			||||||
 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -108,61 +109,63 @@ For example: `CONFIG_FILE=/home/test/config.yml ./wg-portal-amd64`.
 | 
				
			|||||||
### Configuration Options
 | 
					### Configuration Options
 | 
				
			||||||
The following configuration options are available:
 | 
					The following configuration options are available:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| environment                | yaml                    | yaml_parent | default_value                                   | description                                                                                |
 | 
					| environment                | yaml                    | yaml_parent | default_value                                                                                                   | description                                                                                                                                       |
 | 
				
			||||||
|----------------------------|-------------------------|-------------|-------------------------------------------------|-------------------------------------------------------------------------------------------|
 | 
					|----------------------------|-------------------------|-------------|-----------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------|
 | 
				
			||||||
| LISTENING_ADDRESS          | listeningAddress        | core        | :8123                                           | The address on which the web server is listening. Optional IP address and port, e.g.: 127.0.0.1:8080.                                                    |
 | 
					| LISTENING_ADDRESS          | listeningAddress        | core        | :8123                                                                                                           | The address on which the web server is listening. Optional IP address and port, e.g.: 127.0.0.1:8080.                                             |
 | 
				
			||||||
| EXTERNAL_URL               | externalUrl             | core        | http://localhost:8123                           | The external URL where the web server is reachable. This link is used in emails that are created by the WireGuard Portal.                                |
 | 
					| EXTERNAL_URL               | externalUrl             | core        | http://localhost:8123                                                                                           | The external URL where the web server is reachable. This link is used in emails that are created by the WireGuard Portal.                         |
 | 
				
			||||||
| WEBSITE_TITLE              | title                   | core        | WireGuard VPN                                   | The website title.                                                                                     |
 | 
					| WEBSITE_TITLE              | title                   | core        | WireGuard VPN                                                                                                   | The website title.                                                                                                                                |
 | 
				
			||||||
| COMPANY_NAME               | company                 | core        | WireGuard Portal                                | The company name (for branding).                                                                                          |
 | 
					| COMPANY_NAME               | company                 | core        | WireGuard Portal                                                                                                | The company name (for branding).                                                                                                                  |
 | 
				
			||||||
| MAIL_FROM                  | mailFrom                | core        | WireGuard VPN <noreply@company.com>             | The email address from which emails are sent.                                                                                      |
 | 
					| MAIL_FROM                  | mailFrom                | core        | WireGuard VPN <noreply@company.com>                                                                             | The email address from which emails are sent.                                                                                                     |
 | 
				
			||||||
| LOGO_URL                   | logoUrl                 | core        | /img/header-logo.png                            | The logo displayed in the page's header.                                                                                    |
 | 
					| LOGO_URL                   | logoUrl                 | core        | /img/header-logo.png                                                                                            | The logo displayed in the page's header.                                                                                                          |
 | 
				
			||||||
| ADMIN_USER                 | adminUser               | core        | admin@wgportal.local                            | The administrator user. Must be a valid email address.                                                                                   |
 | 
					| ADMIN_USER                 | adminUser               | core        | admin@wgportal.local                                                                                            | The administrator user. Must be a valid email address.                                                                                            |
 | 
				
			||||||
| ADMIN_PASS                 | adminPass               | core        | wgportal                                        | The administrator password. If unchanged, a random password will be set on first startup.                                                              |
 | 
					| ADMIN_PASS                 | adminPass               | core        | wgportal                                                                                                        | The administrator password. If unchanged, a random password will be set on first startup.                                                         |
 | 
				
			||||||
| EDITABLE_KEYS              | editableKeys            | core        | true                                            | Allow to edit key-pairs in the UI.                                                                                        |
 | 
					| EDITABLE_KEYS              | editableKeys            | core        | true                                                                                                            | Allow to edit key-pairs in the UI.                                                                                                                |
 | 
				
			||||||
| CREATE_DEFAULT_PEER        | createDefaultPeer       | core        | false                                           | If an LDAP user logs in for the first time, a new WireGuard peer will be created on the WG_DEFAULT_DEVICE if this option is enabled.                   |
 | 
					| CREATE_DEFAULT_PEER        | createDefaultPeer       | core        | false                                                                                                           | If an LDAP user logs in for the first time, a new WireGuard peer will be created on the WG_DEFAULT_DEVICE if this option is enabled.              |
 | 
				
			||||||
| SELF_PROVISIONING          | selfProvisioning        | core        | false                                           | Allow registered users to automatically create peers via the RESTful API.                                                                               |
 | 
					| SELF_PROVISIONING          | selfProvisioning        | core        | false                                                                                                           | Allow registered users to automatically create peers via the RESTful API.                                                                         |
 | 
				
			||||||
| WG_EXPORTER_FRIENDLY_NAMES | wgExporterFriendlyNames | core        | false                                           | Enable integration with [prometheus_wireguard_exporter friendly name](https://github.com/MindFlavor/prometheus_wireguard_exporter#friendly-tags). |
 | 
					| WG_EXPORTER_FRIENDLY_NAMES | wgExporterFriendlyNames | core        | false                                                                                                           | Enable integration with [prometheus_wireguard_exporter friendly name](https://github.com/MindFlavor/prometheus_wireguard_exporter#friendly-tags). |
 | 
				
			||||||
| LDAP_ENABLED               | ldapEnabled             | core        | false                                           | Enable or disable the LDAP backend.                                                                                   |
 | 
					| LDAP_ENABLED               | ldapEnabled             | core        | false                                                                                                           | Enable or disable the LDAP backend.                                                                                                               |
 | 
				
			||||||
| SESSION_SECRET             | sessionSecret           | core        | secret                                          | Use a custom secret to encrypt session data.                                                                                      |
 | 
					| SESSION_SECRET             | sessionSecret           | core        | secret                                                                                                          | Use a custom secret to encrypt session data.                                                                                                      |
 | 
				
			||||||
| DATABASE_TYPE              | typ                     | database    | sqlite                                          | Either mysql or sqlite.                                                                                    |
 | 
					| BACKGROUND_TASK_INTERVAL   | backgroundTaskInterval  | core        | 900                                                                                                             | The interval (in seconds) for the background tasks (like peer expiry check).                                                                      |
 | 
				
			||||||
| DATABASE_HOST              | host                    | database    |                                                 | The mysql server address.                                                                                   |
 | 
					| DATABASE_TYPE              | typ                     | database    | sqlite                                                                                                          | Either mysql or sqlite.                                                                                                                           |
 | 
				
			||||||
| DATABASE_PORT              | port                    | database    |                                                 | The mysql server port.                                                                                      |
 | 
					| DATABASE_HOST              | host                    | database    |                                                                                                                 | The mysql server address.                                                                                                                         |
 | 
				
			||||||
| DATABASE_NAME              | database                | database    | data/wg_portal.db                               | For sqlite database: the database file-path, otherwise the database name.                                                                             |
 | 
					| DATABASE_PORT              | port                    | database    |                                                                                                                 | The mysql server port.                                                                                                                            |
 | 
				
			||||||
| DATABASE_USERNAME          | user                    | database    |                                                 | The mysql user.                                                                                      |
 | 
					| DATABASE_NAME              | database                | database    | data/wg_portal.db                                                                                               | For sqlite database: the database file-path, otherwise the database name.                                                                         |
 | 
				
			||||||
| DATABASE_PASSWORD          | password                | database    |                                                 | The mysql password.                                                                                  |
 | 
					| DATABASE_USERNAME          | user                    | database    |                                                                                                                 | The mysql user.                                                                                                                                   |
 | 
				
			||||||
| EMAIL_HOST                 | host                    | email       | 127.0.0.1                                       | The email server address.                                                                                   |
 | 
					| DATABASE_PASSWORD          | password                | database    |                                                                                                                 | The mysql password.                                                                                                                               |
 | 
				
			||||||
| EMAIL_PORT                 | port                    | email       | 25                                              | The email server port.                                                                                      |
 | 
					| EMAIL_HOST                 | host                    | email       | 127.0.0.1                                                                                                       | The email server address.                                                                                                                         |
 | 
				
			||||||
| EMAIL_TLS                  | tls                     | email       | false                                           | Use STARTTLS. DEPRECATED: use EMAIL_ENCRYPTION instead.                                                                                   |
 | 
					| EMAIL_PORT                 | port                    | email       | 25                                                                                                              | The email server port.                                                                                                                            |
 | 
				
			||||||
| EMAIL_ENCRYPTION           | encryption              | email       | none                                            | Either none, tls or starttls.                                                                                  |
 | 
					| EMAIL_TLS                  | tls                     | email       | false                                                                                                           | Use STARTTLS. DEPRECATED: use EMAIL_ENCRYPTION instead.                                                                                           |
 | 
				
			||||||
| EMAIL_CERT_VALIDATION      | certcheck               | email       | false                                           | Validate the email server certificate.                                                                               |
 | 
					| EMAIL_ENCRYPTION           | encryption              | email       | none                                                                                                            | Either none, tls or starttls.                                                                                                                     |
 | 
				
			||||||
| EMAIL_USERNAME             | user                    | email       |                                                 | An optional username for SMTP authentication.                                                                            |
 | 
					| EMAIL_CERT_VALIDATION      | certcheck               | email       | false                                                                                                           | Validate the email server certificate.                                                                                                            |
 | 
				
			||||||
| EMAIL_PASSWORD             | pass                    | email       |                                                 | An optional password for SMTP authentication.                                                                            |
 | 
					| EMAIL_USERNAME             | user                    | email       |                                                                                                                 | An optional username for SMTP authentication.                                                                                                     |
 | 
				
			||||||
| EMAIL_AUTHTYPE             | auth                    | email       | plain                                           | Either plain, login or crammd5. If username and password are empty, this value is ignored.                                                              |
 | 
					| EMAIL_PASSWORD             | pass                    | email       |                                                                                                                 | An optional password for SMTP authentication.                                                                                                     |
 | 
				
			||||||
| WG_DEVICES                 | devices                 | wg          | wg0                                             | A comma separated list of WireGuard devices.                                                                                   |
 | 
					| EMAIL_AUTHTYPE             | auth                    | email       | plain                                                                                                           | Either plain, login or crammd5. If username and password are empty, this value is ignored.                                                        |
 | 
				
			||||||
| WG_DEFAULT_DEVICE          | defaultDevice           | wg          | wg0                                             | This device is used for auto-created peers (if CREATE_DEFAULT_PEER is enabled).                                                           |
 | 
					| WG_DEVICES                 | devices                 | wg          | wg0                                                                                                             | A comma separated list of WireGuard devices.                                                                                                      |
 | 
				
			||||||
| WG_CONFIG_PATH             | configDirectory         | wg          | /etc/wireguard                                  | If set, interface configuration updates will be written to this path, filename: <devicename>.conf.                                                    |
 | 
					| WG_DEFAULT_DEVICE          | defaultDevice           | wg          | wg0                                                                                                             | This device is used for auto-created peers (if CREATE_DEFAULT_PEER is enabled).                                                                   |
 | 
				
			||||||
| MANAGE_IPS                 | manageIPAddresses       | wg          | true                                            | Handle IP address setup of interface, only available on linux.                                                                                     |
 | 
					| WG_CONFIG_PATH             | configDirectory         | wg          | /etc/wireguard                                                                                                  | If set, interface configuration updates will be written to this path, filename: <devicename>.conf.                                                |
 | 
				
			||||||
| LDAP_URL                   | url                     | ldap        | ldap://srv-ad01.company.local:389               | The LDAP server url.                                                                                       |
 | 
					| MANAGE_IPS                 | manageIPAddresses       | wg          | true                                                                                                            | Handle IP address setup of interface, only available on linux.                                                                                    |
 | 
				
			||||||
| LDAP_STARTTLS              | startTLS                | ldap        | true                                            | Use STARTTLS.                                                                                  |
 | 
					| USER_MANAGE_PEERS          | userManagePeers         | wg          | false                                                                                                           | Logged in user can create or update peers (partially).                                                                                            |
 | 
				
			||||||
| LDAP_CERT_VALIDATION       | certcheck               | ldap        | false                                           | Validate the LDAP server certificate.                                                                               |
 | 
					| LDAP_URL                   | url                     | ldap        | ldap://srv-ad01.company.local:389                                                                               | The LDAP server url.                                                                                                                              |
 | 
				
			||||||
| LDAP_BASEDN                | dn                      | ldap        | DC=COMPANY,DC=LOCAL                             | The base DN for searching users.                                                                                     |
 | 
					| LDAP_STARTTLS              | startTLS                | ldap        | true                                                                                                            | Use STARTTLS.                                                                                                                                     |
 | 
				
			||||||
| LDAP_USER                  | user                    | ldap        | company\\\\ldap_wireguard                       | The bind user.                                                                                      |
 | 
					| LDAP_CERT_VALIDATION       | certcheck               | ldap        | false                                                                                                           | Validate the LDAP server certificate.                                                                                                             |
 | 
				
			||||||
| LDAP_PASSWORD              | pass                    | ldap        | SuperSecret                                     | The bind password.                                                                                  |
 | 
					| LDAP_BASEDN                | dn                      | ldap        | DC=COMPANY,DC=LOCAL                                                                                             | The base DN for searching users.                                                                                                                  |
 | 
				
			||||||
| LDAP_LOGIN_FILTER          | loginFilter             | ldap        | (&(objectClass=organizationalPerson)(mail={{login_identifier}})(!userAccountControl:1.2.840.113556.1.4.803:=2)) | {{login_identifier}} will be replaced with the login email address.                      |
 | 
					| LDAP_USER                  | user                    | ldap        | company\\\\ldap_wireguard                                                                                       | The bind user.                                                                                                                                    |
 | 
				
			||||||
| LDAP_SYNC_FILTER           | syncFilter              | ldap        | (&(objectClass=organizationalPerson)(!userAccountControl:1.2.840.113556.1.4.803:=2)(mail=*))                    | The filter string for the LDAP synchronization service.                                  |
 | 
					| LDAP_PASSWORD              | pass                    | ldap        | SuperSecret                                                                                                     | The bind password.                                                                                                                                |
 | 
				
			||||||
| LDAP_ADMIN_GROUP           | adminGroup              | ldap        | CN=WireGuardAdmins,OU=_O_IT,DC=COMPANY,DC=LOCAL | Users in this group are marked as administrators.                                                                            |
 | 
					| LDAP_LOGIN_FILTER          | loginFilter             | ldap        | (&(objectClass=organizationalPerson)(mail={{login_identifier}})(!userAccountControl:1.2.840.113556.1.4.803:=2)) | {{login_identifier}} will be replaced with the login email address.                                                                               |
 | 
				
			||||||
| LDAP_ATTR_EMAIL            | attrEmail               | ldap        | mail                                            | User email attribute.                                                                                 |
 | 
					| LDAP_SYNC_FILTER           | syncFilter              | ldap        | (&(objectClass=organizationalPerson)(!userAccountControl:1.2.840.113556.1.4.803:=2)(mail=*))                    | The filter string for the LDAP synchronization service.                                                                                           |
 | 
				
			||||||
| LDAP_ATTR_FIRSTNAME        | attrFirstname           | ldap        | givenName                                       | User firstname attribute.                                                                                 |
 | 
					| LDAP_ADMIN_GROUP           | adminGroup              | ldap        | CN=WireGuardAdmins,OU=_O_IT,DC=COMPANY,DC=LOCAL                                                                 | Users in this group are marked as administrators.                                                                                                 |
 | 
				
			||||||
| LDAP_ATTR_LASTNAME         | attrLastname            | ldap        | sn                                              | User lastname attribute.                                                                                 |
 | 
					| LDAP_ATTR_EMAIL            | attrEmail               | ldap        | mail                                                                                                            | User email attribute.                                                                                                                             |
 | 
				
			||||||
| LDAP_ATTR_PHONE            | attrPhone               | ldap        | telephoneNumber                                 | User phone number attribute.                                                                                 |
 | 
					| LDAP_ATTR_FIRSTNAME        | attrFirstname           | ldap        | givenName                                                                                                       | User firstname attribute.                                                                                                                         |
 | 
				
			||||||
| LDAP_ATTR_GROUPS           | attrGroups              | ldap        | memberOf                                        | User groups attribute.                                                                                 |
 | 
					| LDAP_ATTR_LASTNAME         | attrLastname            | ldap        | sn                                                                                                              | User lastname attribute.                                                                                                                          |
 | 
				
			||||||
| LDAP_CERT_CONN             | ldapCertConn            | ldap        | false                                           | Allow connection with certificate against LDAP server without user/password                            |
 | 
					| LDAP_ATTR_PHONE            | attrPhone               | ldap        | telephoneNumber                                                                                                 | User phone number attribute.                                                                                                                      |
 | 
				
			||||||
| LDAPTLS_CERT               | ldapTlsCert             | ldap        |                                                 | The LDAP cert's path                                                                                   |
 | 
					| LDAP_ATTR_GROUPS           | attrGroups              | ldap        | memberOf                                                                                                        | User groups attribute.                                                                                                                            |
 | 
				
			||||||
| LDAPTLS_KEY                | ldapTlsKey              | ldap        |                                                 | The LDAP key's path                                                                                    |
 | 
					| LDAP_CERT_CONN             | ldapCertConn            | ldap        | false                                                                                                           | Allow connection with certificate against LDAP server without user/password                                                                       |
 | 
				
			||||||
| LOG_LEVEL                  |                         |             | debug                                           | Specify log level, one of: trace, debug, info, off.                                                                                       |
 | 
					| LDAPTLS_CERT               | ldapTlsCert             | ldap        |                                                                                                                 | The LDAP cert's path                                                                                                                              |
 | 
				
			||||||
| LOG_JSON                   |                         |             | false                                           | Format log output as JSON.                                                                                      |
 | 
					| LDAPTLS_KEY                | ldapTlsKey              | ldap        |                                                                                                                 | The LDAP key's path                                                                                                                               |
 | 
				
			||||||
| LOG_COLOR                  |                         |             | true                                            | Colorize log output.                                                                                    |
 | 
					| LOG_LEVEL                  |                         |             | debug                                                                                                           | Specify log level, one of: trace, debug, info, off.                                                                                               |
 | 
				
			||||||
| CONFIG_FILE                |                         |             | config.yml                                      | The config file path.                                                                                      |
 | 
					| LOG_JSON                   |                         |             | false                                                                                                           | Format log output as JSON.                                                                                                                        |
 | 
				
			||||||
 | 
					| LOG_COLOR                  |                         |             | true                                                                                                            | Colorize log output.                                                                                                                              |
 | 
				
			||||||
 | 
					| CONFIG_FILE                |                         |             | config.yml                                                                                                      | The config file path.                                                                                                                             |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Sample yaml configuration
 | 
					### Sample yaml configuration
 | 
				
			||||||
config.yml:
 | 
					config.yml:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,6 +73,10 @@ pre{background:#f7f7f9}iframe{overflow:hidden;border:none}@media (min-width: 768
 | 
				
			|||||||
    color: #d03131;
 | 
					    color: #d03131;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.expiring-peer {
 | 
				
			||||||
 | 
					    color: #d09d12;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.tokenfield .token {
 | 
					.tokenfield .token {
 | 
				
			||||||
    border-radius: 0px;
 | 
					    border-radius: 0px;
 | 
				
			||||||
    border: 1px solid #1a1a1a;
 | 
					    border: 1px solid #1a1a1a;
 | 
				
			||||||
@@ -106,3 +110,9 @@ a.advanced-settings.collapsed:before {
 | 
				
			|||||||
.text-blue {
 | 
					.text-blue {
 | 
				
			||||||
    color: #0057bb;
 | 
					    color: #0057bb;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media (min-width: 992px) {
 | 
				
			||||||
 | 
					    .pull-right-lg {
 | 
				
			||||||
 | 
					        float: right;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -106,7 +106,7 @@
 | 
				
			|||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <div class="form-row">
 | 
					            <div class="form-row">
 | 
				
			||||||
                <div class="form-group col-md-12">
 | 
					                <div class="form-group col-md-6">
 | 
				
			||||||
                    <div class="custom-control custom-switch">
 | 
					                    <div class="custom-control custom-switch">
 | 
				
			||||||
                        <input class="custom-control-input" name="isdisabled" type="checkbox" value="true" id="server_Disabled" {{if .Peer.DeactivatedAt}}checked{{end}}>
 | 
					                        <input class="custom-control-input" name="isdisabled" type="checkbox" value="true" id="server_Disabled" {{if .Peer.DeactivatedAt}}checked{{end}}>
 | 
				
			||||||
                        <label class="custom-control-label" for="server_Disabled">
 | 
					                        <label class="custom-control-label" for="server_Disabled">
 | 
				
			||||||
@@ -120,6 +120,10 @@
 | 
				
			|||||||
                        </label>
 | 
					                        </label>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div class="form-group col-md-6">
 | 
				
			||||||
 | 
					                    <label for="expires_at">Expires At</label>
 | 
				
			||||||
 | 
					                    <input type="date" name="expires_at" pattern="\d{4}-\d{2}-\d{2}" class="form-control" id="expires_at" placeholder="" value="{{formatDate .Peer.ExpiresAt}}" min="2022-01-01">
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -185,7 +189,7 @@
 | 
				
			|||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <div class="form-row">
 | 
					            <div class="form-row">
 | 
				
			||||||
                <div class="form-group col-md-12">
 | 
					                <div class="form-group col-md-6">
 | 
				
			||||||
                    <div class="custom-control custom-switch">
 | 
					                    <div class="custom-control custom-switch">
 | 
				
			||||||
                        <input class="custom-control-input" name="isdisabled" type="checkbox" value="true" id="client_Disabled" {{if .Peer.DeactivatedAt}}checked{{end}}>
 | 
					                        <input class="custom-control-input" name="isdisabled" type="checkbox" value="true" id="client_Disabled" {{if .Peer.DeactivatedAt}}checked{{end}}>
 | 
				
			||||||
                        <label class="custom-control-label" for="client_Disabled">
 | 
					                        <label class="custom-control-label" for="client_Disabled">
 | 
				
			||||||
@@ -193,6 +197,10 @@
 | 
				
			|||||||
                        </label>
 | 
					                        </label>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div class="form-group col-md-6">
 | 
				
			||||||
 | 
					                    <label for="expires_at">Expires At</label>
 | 
				
			||||||
 | 
					                    <input type="date" name="expires_at" pattern="\d{4}-\d{2}-\d{2}" class="form-control" id="expires_at" placeholder="" value="{{formatDate .Peer.ExpiresAt}}" min="2022-01-01">
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -170,7 +170,7 @@
 | 
				
			|||||||
                            <!-- online check -->
 | 
					                            <!-- online check -->
 | 
				
			||||||
                            <span title="Online status" class="online-status" id="online-{{$p.UID}}" data-pkey="{{$p.PublicKey}}"><i class="fas fa-unlink"></i></span>
 | 
					                            <span title="Online status" class="online-status" id="online-{{$p.UID}}" data-pkey="{{$p.PublicKey}}"><i class="fas fa-unlink"></i></span>
 | 
				
			||||||
                        </th>
 | 
					                        </th>
 | 
				
			||||||
                        <td>{{$p.Identifier}}</td>
 | 
					                        <td>{{$p.Identifier}}{{if $p.WillExpire}} <i class="fas fa-hourglass-end expiring-peer" data-toggle="tooltip" data-placement="right" title="" data-original-title="Expires at: {{formatDate $p.ExpiresAt}}"></i>{{end}}</td>
 | 
				
			||||||
                        <td>{{$p.PublicKey}}</td>
 | 
					                        <td>{{$p.PublicKey}}</td>
 | 
				
			||||||
                        {{if eq $.Device.Type "server"}}
 | 
					                        {{if eq $.Device.Type "server"}}
 | 
				
			||||||
                        <td>{{$p.Email}}</td>
 | 
					                        <td>{{$p.Email}}</td>
 | 
				
			||||||
@@ -239,12 +239,18 @@
 | 
				
			|||||||
                                    </div>
 | 
					                                    </div>
 | 
				
			||||||
                                    <div class="col-md-3">
 | 
					                                    <div class="col-md-3">
 | 
				
			||||||
                                        {{if eq $.Device.Type "server"}}
 | 
					                                        {{if eq $.Device.Type "server"}}
 | 
				
			||||||
                                        <img class="list-image-large" src="/user/qrcode?pkey={{$p.PublicKey}}"/>
 | 
					                                        <img class="list-image-large" loading="lazy" alt="Configuration QR Code" src="/user/qrcode?pkey={{$p.PublicKey}}"/>
 | 
				
			||||||
                                        {{end}}
 | 
					                                        {{end}}
 | 
				
			||||||
                                    </div>
 | 
					                                    </div>
 | 
				
			||||||
                                    <div class="col-md-3">
 | 
					                                    <div class="col-md-3">
 | 
				
			||||||
 | 
					                                        {{if $p.DeactivatedAt}}
 | 
				
			||||||
 | 
					                                        <div class="pull-right-lg mt-lg-5 disabled-peer">Peer is disabled! <i class="fas fa-comment-dots" data-toggle="tooltip" data-placement="left" title="" data-original-title="Reason: {{$p.DeactivatedReason}}"></i></div>
 | 
				
			||||||
 | 
					                                        {{end}}
 | 
				
			||||||
 | 
					                                        {{if $p.WillExpire}}
 | 
				
			||||||
 | 
					                                        <div class="pull-right-lg mt-lg-5 expiring-peer"><i class="fas fa-exclamation-triangle"></i> Peer will expire on {{ formatDate $p.ExpiresAt}}</div>
 | 
				
			||||||
 | 
					                                        {{end}}
 | 
				
			||||||
                                        {{if eq $.Device.Type "server"}}
 | 
					                                        {{if eq $.Device.Type "server"}}
 | 
				
			||||||
                                        <div class="float-right mt-5">
 | 
					                                        <div class="pull-right-lg mt-lg-5 mt-md-3">
 | 
				
			||||||
                                        <a href="/admin/peer/download?pkey={{$p.PublicKey}}" class="btn btn-primary" title="Download configuration">Download</a>
 | 
					                                        <a href="/admin/peer/download?pkey={{$p.PublicKey}}" class="btn btn-primary" title="Download configuration">Download</a>
 | 
				
			||||||
                                        <a href="/admin/peer/email?pkey={{$p.PublicKey}}" class="btn btn-primary" title="Send configuration via Email">Email</a>
 | 
					                                        <a href="/admin/peer/email?pkey={{$p.PublicKey}}" class="btn btn-primary" title="Send configuration via Email">Email</a>
 | 
				
			||||||
                                        </div>
 | 
					                                        </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,7 +49,7 @@
 | 
				
			|||||||
                            <!-- online check -->
 | 
					                            <!-- online check -->
 | 
				
			||||||
                            <span class="online-status" id="online-{{$p.UID}}" data-pkey="{{$p.PublicKey}}"><i class="fas fa-unlink"></i></span>
 | 
					                            <span class="online-status" id="online-{{$p.UID}}" data-pkey="{{$p.PublicKey}}"><i class="fas fa-unlink"></i></span>
 | 
				
			||||||
                        </th>
 | 
					                        </th>
 | 
				
			||||||
                        <td>{{$p.Identifier}}</td>
 | 
					                        <td>{{$p.Identifier}}{{if $p.WillExpire}} <i class="fas fa-hourglass-end expiring-peer" data-toggle="tooltip" data-placement="right" title="" data-original-title="Expires at: {{formatDate $p.ExpiresAt}}"></i>{{end}}</td>
 | 
				
			||||||
                        <td>{{$p.PublicKey}}</td>
 | 
					                        <td>{{$p.PublicKey}}</td>
 | 
				
			||||||
                        <td>{{$p.Email}}</td>
 | 
					                        <td>{{$p.Email}}</td>
 | 
				
			||||||
                        <td>{{$p.IPsStr}}</td>
 | 
					                        <td>{{$p.IPsStr}}</td>
 | 
				
			||||||
@@ -102,7 +102,13 @@
 | 
				
			|||||||
                                        <img class="list-image-large" src="/user/qrcode?pkey={{$p.PublicKey}}"/>
 | 
					                                        <img class="list-image-large" src="/user/qrcode?pkey={{$p.PublicKey}}"/>
 | 
				
			||||||
                                    </div>
 | 
					                                    </div>
 | 
				
			||||||
                                    <div class="col-md-3">
 | 
					                                    <div class="col-md-3">
 | 
				
			||||||
                                        <div class="float-right mt-5">
 | 
					                                        {{if $p.DeactivatedAt}}
 | 
				
			||||||
 | 
					                                        <div class="pull-right-lg mt-lg-5 disabled-peer">Peer is disabled! <i class="fas fa-comment-dots" data-toggle="tooltip" data-placement="left" title="" data-original-title="Reason: {{$p.DeactivatedReason}}"></i></div>
 | 
				
			||||||
 | 
					                                        {{end}}
 | 
				
			||||||
 | 
					                                        {{if $p.WillExpire}}
 | 
				
			||||||
 | 
					                                        <div class="pull-right-lg mt-lg-5 expiring-peer"><i class="fas fa-exclamation-triangle"></i> Profile expires on {{ formatDate $p.ExpiresAt}}</div>
 | 
				
			||||||
 | 
					                                        {{end}}
 | 
				
			||||||
 | 
					                                        <div class="pull-right-lg mt-lg-5 mt-md-3">
 | 
				
			||||||
                                        <a href="/user/download?pkey={{$p.PublicKey}}" class="btn btn-primary" title="Download configuration">Download</a>
 | 
					                                        <a href="/user/download?pkey={{$p.PublicKey}}" class="btn btn-primary" title="Download configuration">Download</a>
 | 
				
			||||||
                                        <a href="/user/email?pkey={{$p.PublicKey}}" class="btn btn-primary" title="Send configuration via Email">Email</a>
 | 
					                                        <a href="/user/email?pkey={{$p.PublicKey}}" class="btn btn-primary" title="Send configuration via Email">Email</a>
 | 
				
			||||||
                                        </div>
 | 
					                                        </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,6 +36,14 @@ func init() {
 | 
				
			|||||||
			return nil
 | 
								return nil
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						migrations = append(migrations, Migration{
 | 
				
			||||||
 | 
							version: "1.0.9",
 | 
				
			||||||
 | 
							migrateFn: func(db *gorm.DB) error {
 | 
				
			||||||
 | 
								logrus.Infof("upgraded database format to version 1.0.9")
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type SupportedDatabase string
 | 
					type SupportedDatabase string
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// BroadcastAddr returns the last address in the given network, or the broadcast address.
 | 
					// BroadcastAddr returns the last address in the given network, or the broadcast address.
 | 
				
			||||||
@@ -21,7 +22,7 @@ func BroadcastAddr(n *net.IPNet) net.IP {
 | 
				
			|||||||
	return broadcast
 | 
						return broadcast
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//  http://play.golang.org/p/m8TNTtygK0
 | 
					// http://play.golang.org/p/m8TNTtygK0
 | 
				
			||||||
func IncreaseIP(ip net.IP) {
 | 
					func IncreaseIP(ip net.IP) {
 | 
				
			||||||
	for j := len(ip) - 1; j >= 0; j-- {
 | 
						for j := len(ip) - 1; j >= 0; j-- {
 | 
				
			||||||
		ip[j]++
 | 
							ip[j]++
 | 
				
			||||||
@@ -84,3 +85,11 @@ func ByteCountSI(b int64) string {
 | 
				
			|||||||
	return fmt.Sprintf("%.1f %cB",
 | 
						return fmt.Sprintf("%.1f %cB",
 | 
				
			||||||
		float64(b)/float64(div), "kMGTPE"[exp])
 | 
							float64(b)/float64(div), "kMGTPE"[exp])
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func FormatDateHTML(t *time.Time) string {
 | 
				
			||||||
 | 
						if t == nil {
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return t.Format("2006-01-02")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -439,6 +439,7 @@ func (s *ApiServer) PutPeer(c *gin.Context) {
 | 
				
			|||||||
	now := time.Now()
 | 
						now := time.Now()
 | 
				
			||||||
	if updatePeer.DeactivatedAt != nil {
 | 
						if updatePeer.DeactivatedAt != nil {
 | 
				
			||||||
		updatePeer.DeactivatedAt = &now
 | 
							updatePeer.DeactivatedAt = &now
 | 
				
			||||||
 | 
							updatePeer.DeactivatedReason = wireguard.DeactivatedReasonApiEdit
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err := s.s.UpdatePeer(updatePeer, now); err != nil {
 | 
						if err := s.s.UpdatePeer(updatePeer, now); err != nil {
 | 
				
			||||||
		c.JSON(http.StatusInternalServerError, ApiError{Message: err.Error()})
 | 
							c.JSON(http.StatusInternalServerError, ApiError{Message: err.Error()})
 | 
				
			||||||
@@ -516,6 +517,7 @@ func (s *ApiServer) PatchPeer(c *gin.Context) {
 | 
				
			|||||||
	now := time.Now()
 | 
						now := time.Now()
 | 
				
			||||||
	if mergedPeer.DeactivatedAt != nil {
 | 
						if mergedPeer.DeactivatedAt != nil {
 | 
				
			||||||
		mergedPeer.DeactivatedAt = &now
 | 
							mergedPeer.DeactivatedAt = &now
 | 
				
			||||||
 | 
							mergedPeer.DeactivatedReason = wireguard.DeactivatedReasonApiEdit
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err := s.s.UpdatePeer(mergedPeer, now); err != nil {
 | 
						if err := s.s.UpdatePeer(mergedPeer, now); err != nil {
 | 
				
			||||||
		c.JSON(http.StatusInternalServerError, ApiError{Message: err.Error()})
 | 
							c.JSON(http.StatusInternalServerError, ApiError{Message: err.Error()})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -67,10 +67,11 @@ type Config struct {
 | 
				
			|||||||
		EditableKeys            bool   `yaml:"editableKeys" envconfig:"EDITABLE_KEYS"`
 | 
							EditableKeys            bool   `yaml:"editableKeys" envconfig:"EDITABLE_KEYS"`
 | 
				
			||||||
		CreateDefaultPeer       bool   `yaml:"createDefaultPeer" envconfig:"CREATE_DEFAULT_PEER"`
 | 
							CreateDefaultPeer       bool   `yaml:"createDefaultPeer" envconfig:"CREATE_DEFAULT_PEER"`
 | 
				
			||||||
		SelfProvisioningAllowed bool   `yaml:"selfProvisioning" envconfig:"SELF_PROVISIONING"`
 | 
							SelfProvisioningAllowed bool   `yaml:"selfProvisioning" envconfig:"SELF_PROVISIONING"`
 | 
				
			||||||
		WGExoprterFriendlyNames bool   `yaml:"wgExporterFriendlyNames" envconfig:"WG_EXPORTER_FRIENDLY_NAMES"`
 | 
							WGExporterFriendlyNames bool   `yaml:"wgExporterFriendlyNames" envconfig:"WG_EXPORTER_FRIENDLY_NAMES"`
 | 
				
			||||||
		LdapEnabled             bool   `yaml:"ldapEnabled" envconfig:"LDAP_ENABLED"`
 | 
							LdapEnabled             bool   `yaml:"ldapEnabled" envconfig:"LDAP_ENABLED"`
 | 
				
			||||||
		SessionSecret           string `yaml:"sessionSecret" envconfig:"SESSION_SECRET"`
 | 
							SessionSecret           string `yaml:"sessionSecret" envconfig:"SESSION_SECRET"`
 | 
				
			||||||
		LogoUrl                 string `yaml:"logoUrl" envconfig:"LOGO_URL"`
 | 
							LogoUrl                 string `yaml:"logoUrl" envconfig:"LOGO_URL"`
 | 
				
			||||||
 | 
							BackgroundTaskInterval  int    `yaml:"backgroundTaskInterval" envconfig:"BACKGROUND_TASK_INTERVAL"` // in seconds
 | 
				
			||||||
	} `yaml:"core"`
 | 
						} `yaml:"core"`
 | 
				
			||||||
	Database common.DatabaseConfig `yaml:"database"`
 | 
						Database common.DatabaseConfig `yaml:"database"`
 | 
				
			||||||
	Email    common.MailConfig     `yaml:"email"`
 | 
						Email    common.MailConfig     `yaml:"email"`
 | 
				
			||||||
@@ -92,8 +93,9 @@ func NewConfig() *Config {
 | 
				
			|||||||
	cfg.Core.AdminPassword = "wgportal"
 | 
						cfg.Core.AdminPassword = "wgportal"
 | 
				
			||||||
	cfg.Core.LdapEnabled = false
 | 
						cfg.Core.LdapEnabled = false
 | 
				
			||||||
	cfg.Core.EditableKeys = true
 | 
						cfg.Core.EditableKeys = true
 | 
				
			||||||
	cfg.Core.WGExoprterFriendlyNames = false
 | 
						cfg.Core.WGExporterFriendlyNames = false
 | 
				
			||||||
	cfg.Core.SessionSecret = "secret"
 | 
						cfg.Core.SessionSecret = "secret"
 | 
				
			||||||
 | 
						cfg.Core.BackgroundTaskInterval = 15 * 60 // 15 minutes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cfg.Database.Typ = "sqlite"
 | 
						cfg.Database.Typ = "sqlite"
 | 
				
			||||||
	cfg.Database.Database = "data/wg_portal.db"
 | 
						cfg.Database.Database = "data/wg_portal.db"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,17 +1,10 @@
 | 
				
			|||||||
// Package docs GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 | 
					// Package docs GENERATED BY SWAG; DO NOT EDIT
 | 
				
			||||||
// This file was generated by swaggo/swag
 | 
					// This file was generated by swaggo/swag
 | 
				
			||||||
package docs
 | 
					package docs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import "github.com/swaggo/swag"
 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"text/template"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/swaggo/swag"
 | 
					const docTemplate = `{
 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var doc = `{
 | 
					 | 
				
			||||||
    "schemes": {{ marshal .Schemes }},
 | 
					    "schemes": {{ marshal .Schemes }},
 | 
				
			||||||
    "swagger": "2.0",
 | 
					    "swagger": "2.0",
 | 
				
			||||||
    "info": {
 | 
					    "info": {
 | 
				
			||||||
@@ -1267,10 +1260,13 @@ var doc = `{
 | 
				
			|||||||
                    "type": "string"
 | 
					                    "type": "string"
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "Mtu": {
 | 
					                "Mtu": {
 | 
				
			||||||
                    "type": "integer"
 | 
					                    "type": "integer",
 | 
				
			||||||
 | 
					                    "maximum": 1500,
 | 
				
			||||||
 | 
					                    "minimum": 0
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "PersistentKeepalive": {
 | 
					                "PersistentKeepalive": {
 | 
				
			||||||
                    "type": "integer"
 | 
					                    "type": "integer",
 | 
				
			||||||
 | 
					                    "minimum": 0
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@@ -1344,16 +1340,19 @@ var doc = `{
 | 
				
			|||||||
                    "type": "string"
 | 
					                    "type": "string"
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "DefaultPersistentKeepalive": {
 | 
					                "DefaultPersistentKeepalive": {
 | 
				
			||||||
                    "type": "integer"
 | 
					                    "type": "integer",
 | 
				
			||||||
 | 
					                    "minimum": 0
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "DeviceName": {
 | 
					                "DeviceName": {
 | 
				
			||||||
                    "type": "string"
 | 
					                    "type": "string"
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "DisplayName": {
 | 
					                "DisplayName": {
 | 
				
			||||||
                    "type": "string"
 | 
					                    "type": "string",
 | 
				
			||||||
 | 
					                    "maxLength": 200
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "FirewallMark": {
 | 
					                "FirewallMark": {
 | 
				
			||||||
                    "type": "integer"
 | 
					                    "type": "integer",
 | 
				
			||||||
 | 
					                    "minimum": 0
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "IPsStr": {
 | 
					                "IPsStr": {
 | 
				
			||||||
                    "description": "comma separated list of the IPs of the client, wg-quick addition",
 | 
					                    "description": "comma separated list of the IPs of the client, wg-quick addition",
 | 
				
			||||||
@@ -1364,7 +1363,9 @@ var doc = `{
 | 
				
			|||||||
                },
 | 
					                },
 | 
				
			||||||
                "Mtu": {
 | 
					                "Mtu": {
 | 
				
			||||||
                    "description": "the interface MTU, wg-quick addition",
 | 
					                    "description": "the interface MTU, wg-quick addition",
 | 
				
			||||||
                    "type": "integer"
 | 
					                    "type": "integer",
 | 
				
			||||||
 | 
					                    "maximum": 1500,
 | 
				
			||||||
 | 
					                    "minimum": 0
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "PostDown": {
 | 
					                "PostDown": {
 | 
				
			||||||
                    "description": "post down script, wg-quick addition",
 | 
					                    "description": "post down script, wg-quick addition",
 | 
				
			||||||
@@ -1399,7 +1400,11 @@ var doc = `{
 | 
				
			|||||||
                    "type": "boolean"
 | 
					                    "type": "boolean"
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "Type": {
 | 
					                "Type": {
 | 
				
			||||||
                    "type": "string"
 | 
					                    "type": "string",
 | 
				
			||||||
 | 
					                    "enum": [
 | 
				
			||||||
 | 
					                        "client",
 | 
				
			||||||
 | 
					                        "server"
 | 
				
			||||||
 | 
					                    ]
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "UpdatedAt": {
 | 
					                "UpdatedAt": {
 | 
				
			||||||
                    "type": "string"
 | 
					                    "type": "string"
 | 
				
			||||||
@@ -1438,11 +1443,18 @@ var doc = `{
 | 
				
			|||||||
                "DeactivatedAt": {
 | 
					                "DeactivatedAt": {
 | 
				
			||||||
                    "type": "string"
 | 
					                    "type": "string"
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					                "DeactivatedReason": {
 | 
				
			||||||
 | 
					                    "type": "string"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
                "DeviceName": {
 | 
					                "DeviceName": {
 | 
				
			||||||
                    "type": "string"
 | 
					                    "type": "string"
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "DeviceType": {
 | 
					                "DeviceType": {
 | 
				
			||||||
                    "type": "string"
 | 
					                    "type": "string",
 | 
				
			||||||
 | 
					                    "enum": [
 | 
				
			||||||
 | 
					                        "client",
 | 
				
			||||||
 | 
					                        "server"
 | 
				
			||||||
 | 
					                    ]
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "Email": {
 | 
					                "Email": {
 | 
				
			||||||
                    "type": "string"
 | 
					                    "type": "string"
 | 
				
			||||||
@@ -1450,23 +1462,30 @@ var doc = `{
 | 
				
			|||||||
                "Endpoint": {
 | 
					                "Endpoint": {
 | 
				
			||||||
                    "type": "string"
 | 
					                    "type": "string"
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					                "ExpiresAt": {
 | 
				
			||||||
 | 
					                    "type": "string"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
                "IPsStr": {
 | 
					                "IPsStr": {
 | 
				
			||||||
                    "description": "a comma separated list of IPs of the client",
 | 
					                    "description": "a comma separated list of IPs of the client",
 | 
				
			||||||
                    "type": "string"
 | 
					                    "type": "string"
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "Identifier": {
 | 
					                "Identifier": {
 | 
				
			||||||
                    "description": "Identifier AND Email make a WireGuard peer unique",
 | 
					                    "description": "Identifier AND Email make a WireGuard peer unique",
 | 
				
			||||||
                    "type": "string"
 | 
					                    "type": "string",
 | 
				
			||||||
 | 
					                    "maxLength": 64
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "IgnoreGlobalSettings": {
 | 
					                "IgnoreGlobalSettings": {
 | 
				
			||||||
                    "type": "boolean"
 | 
					                    "type": "boolean"
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "Mtu": {
 | 
					                "Mtu": {
 | 
				
			||||||
                    "description": "Global Device Settings (can be ignored, only make sense if device is in server mode)",
 | 
					                    "description": "Global Device Settings (can be ignored, only make sense if device is in server mode)",
 | 
				
			||||||
                    "type": "integer"
 | 
					                    "type": "integer",
 | 
				
			||||||
 | 
					                    "maximum": 1500,
 | 
				
			||||||
 | 
					                    "minimum": 0
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "PersistentKeepalive": {
 | 
					                "PersistentKeepalive": {
 | 
				
			||||||
                    "type": "integer"
 | 
					                    "type": "integer",
 | 
				
			||||||
 | 
					                    "minimum": 0
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "PresharedKey": {
 | 
					                "PresharedKey": {
 | 
				
			||||||
                    "type": "string"
 | 
					                    "type": "string"
 | 
				
			||||||
@@ -1502,56 +1521,18 @@ var doc = `{
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}`
 | 
					}`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type swaggerInfo struct {
 | 
					 | 
				
			||||||
	Version     string
 | 
					 | 
				
			||||||
	Host        string
 | 
					 | 
				
			||||||
	BasePath    string
 | 
					 | 
				
			||||||
	Schemes     []string
 | 
					 | 
				
			||||||
	Title       string
 | 
					 | 
				
			||||||
	Description string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
 | 
					// SwaggerInfo holds exported Swagger Info so clients can modify it
 | 
				
			||||||
var SwaggerInfo = swaggerInfo{
 | 
					var SwaggerInfo = &swag.Spec{
 | 
				
			||||||
	Version:     "1.0",
 | 
						Version:          "1.0",
 | 
				
			||||||
	Host:        "",
 | 
						Host:             "",
 | 
				
			||||||
	BasePath:    "/api/v1",
 | 
						BasePath:         "/api/v1",
 | 
				
			||||||
	Schemes:     []string{},
 | 
						Schemes:          []string{},
 | 
				
			||||||
	Title:       "WireGuard Portal API",
 | 
						Title:            "WireGuard Portal API",
 | 
				
			||||||
	Description: "WireGuard Portal API for managing users and peers.",
 | 
						Description:      "WireGuard Portal API for managing users and peers.",
 | 
				
			||||||
}
 | 
						InfoInstanceName: "swagger",
 | 
				
			||||||
 | 
						SwaggerTemplate:  docTemplate,
 | 
				
			||||||
type s struct{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s *s) ReadDoc() string {
 | 
					 | 
				
			||||||
	sInfo := SwaggerInfo
 | 
					 | 
				
			||||||
	sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	t, err := template.New("swagger_info").Funcs(template.FuncMap{
 | 
					 | 
				
			||||||
		"marshal": func(v interface{}) string {
 | 
					 | 
				
			||||||
			a, _ := json.Marshal(v)
 | 
					 | 
				
			||||||
			return string(a)
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"escape": func(v interface{}) string {
 | 
					 | 
				
			||||||
			// escape tabs
 | 
					 | 
				
			||||||
			str := strings.Replace(v.(string), "\t", "\\t", -1)
 | 
					 | 
				
			||||||
			// replace " with \", and if that results in \\", replace that with \\\"
 | 
					 | 
				
			||||||
			str = strings.Replace(str, "\"", "\\\"", -1)
 | 
					 | 
				
			||||||
			return strings.Replace(str, "\\\\\"", "\\\\\\\"", -1)
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}).Parse(doc)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return doc
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var tpl bytes.Buffer
 | 
					 | 
				
			||||||
	if err := t.Execute(&tpl, sInfo); err != nil {
 | 
					 | 
				
			||||||
		return doc
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return tpl.String()
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	swag.Register(swag.Name, &s{})
 | 
						swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -112,7 +112,7 @@ func (s *Server) GetInterfaceConfig(c *gin.Context) {
 | 
				
			|||||||
	currentSession := GetSessionData(c)
 | 
						currentSession := GetSessionData(c)
 | 
				
			||||||
	device := s.peers.GetDevice(currentSession.DeviceName)
 | 
						device := s.peers.GetDevice(currentSession.DeviceName)
 | 
				
			||||||
	peers := s.peers.GetActivePeers(device.DeviceName)
 | 
						peers := s.peers.GetActivePeers(device.DeviceName)
 | 
				
			||||||
	cfg, err := device.GetConfigFile(peers, s.config.Core.WGExoprterFriendlyNames)
 | 
						cfg, err := device.GetConfigFile(peers, s.config.Core.WGExporterFriendlyNames)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		s.GetHandleError(c, http.StatusInternalServerError, "ConfigFile error", err.Error())
 | 
							s.GetHandleError(c, http.StatusInternalServerError, "ConfigFile error", err.Error())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -71,8 +71,18 @@ func (s *Server) PostAdminEditPeer(c *gin.Context) {
 | 
				
			|||||||
	now := time.Now()
 | 
						now := time.Now()
 | 
				
			||||||
	if disabled && currentPeer.DeactivatedAt == nil {
 | 
						if disabled && currentPeer.DeactivatedAt == nil {
 | 
				
			||||||
		formPeer.DeactivatedAt = &now
 | 
							formPeer.DeactivatedAt = &now
 | 
				
			||||||
 | 
							formPeer.DeactivatedReason = wireguard.DeactivatedReasonAdminEdit
 | 
				
			||||||
	} else if !disabled {
 | 
						} else if !disabled {
 | 
				
			||||||
		formPeer.DeactivatedAt = nil
 | 
							formPeer.DeactivatedAt = nil
 | 
				
			||||||
 | 
							formPeer.DeactivatedReason = ""
 | 
				
			||||||
 | 
							// If a peer was deactivated due to expiry, remove the expires-at date to avoid
 | 
				
			||||||
 | 
							// unwanted re-expiry.
 | 
				
			||||||
 | 
							if currentPeer.DeactivatedReason == wireguard.DeactivatedReasonExpired {
 | 
				
			||||||
 | 
								formPeer.ExpiresAt = nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if formPeer.ExpiresAt != nil && formPeer.ExpiresAt.IsZero() { // convert 01-01-0001 to nil
 | 
				
			||||||
 | 
							formPeer.ExpiresAt = nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Update in database
 | 
						// Update in database
 | 
				
			||||||
@@ -129,6 +139,7 @@ func (s *Server) PostAdminCreatePeer(c *gin.Context) {
 | 
				
			|||||||
	now := time.Now()
 | 
						now := time.Now()
 | 
				
			||||||
	if disabled {
 | 
						if disabled {
 | 
				
			||||||
		formPeer.DeactivatedAt = &now
 | 
							formPeer.DeactivatedAt = &now
 | 
				
			||||||
 | 
							formPeer.DeactivatedReason = wireguard.DeactivatedReasonAdminCreate
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := s.CreatePeer(currentSession.DeviceName, formPeer); err != nil {
 | 
						if err := s.CreatePeer(currentSession.DeviceName, formPeer); err != nil {
 | 
				
			||||||
@@ -189,7 +200,7 @@ func (s *Server) PostAdminCreateLdapPeers(c *gin.Context) {
 | 
				
			|||||||
	logrus.Infof("creating %d ldap peers", len(emails))
 | 
						logrus.Infof("creating %d ldap peers", len(emails))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i := range emails {
 | 
						for i := range emails {
 | 
				
			||||||
		if err := s.CreatePeerByEmail(currentSession.DeviceName, emails[i], formData.Identifier, false); err != nil {
 | 
							if err := s.CreatePeerByEmail(currentSession.DeviceName, emails[i], formData.Identifier); err != nil {
 | 
				
			||||||
			_ = s.updateFormInSession(c, formData)
 | 
								_ = s.updateFormInSession(c, formData)
 | 
				
			||||||
			SetFlashMessage(c, "failed to add user: "+err.Error(), "danger")
 | 
								SetFlashMessage(c, "failed to add user: "+err.Error(), "danger")
 | 
				
			||||||
			c.Redirect(http.StatusSeeOther, "/admin/peer/createldap?formerr=create")
 | 
								c.Redirect(http.StatusSeeOther, "/admin/peer/createldap?formerr=create")
 | 
				
			||||||
@@ -440,6 +451,7 @@ func (s *Server) PostUserCreatePeer(c *gin.Context) {
 | 
				
			|||||||
	now := time.Now()
 | 
						now := time.Now()
 | 
				
			||||||
	if disabled {
 | 
						if disabled {
 | 
				
			||||||
		formPeer.DeactivatedAt = &now
 | 
							formPeer.DeactivatedAt = &now
 | 
				
			||||||
 | 
							formPeer.DeactivatedReason = wireguard.DeactivatedReasonUserCreate
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := s.CreatePeer(currentSession.DeviceName, formPeer); err != nil {
 | 
						if err := s.CreatePeer(currentSession.DeviceName, formPeer); err != nil {
 | 
				
			||||||
@@ -496,6 +508,7 @@ func (s *Server) PostUserEditPeer(c *gin.Context) {
 | 
				
			|||||||
	now := time.Now()
 | 
						now := time.Now()
 | 
				
			||||||
	if disabled && currentPeer.DeactivatedAt == nil {
 | 
						if disabled && currentPeer.DeactivatedAt == nil {
 | 
				
			||||||
		currentPeer.DeactivatedAt = &now
 | 
							currentPeer.DeactivatedAt = &now
 | 
				
			||||||
 | 
							currentPeer.DeactivatedReason = wireguard.DeactivatedReasonUserEdit
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Update in database
 | 
						// Update in database
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,8 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/h44z/wg-portal/internal/wireguard"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/h44z/wg-portal/internal/ldap"
 | 
						"github.com/h44z/wg-portal/internal/ldap"
 | 
				
			||||||
	"github.com/h44z/wg-portal/internal/users"
 | 
						"github.com/h44z/wg-portal/internal/users"
 | 
				
			||||||
	"github.com/sirupsen/logrus"
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
@@ -112,6 +114,7 @@ func (s *Server) disableMissingLdapUsers(ldapUsers []ldap.RawLdapData) {
 | 
				
			|||||||
		for _, peer := range s.peers.GetPeersByMail(activeUsers[i].Email) {
 | 
							for _, peer := range s.peers.GetPeersByMail(activeUsers[i].Email) {
 | 
				
			||||||
			now := time.Now()
 | 
								now := time.Now()
 | 
				
			||||||
			peer.DeactivatedAt = &now
 | 
								peer.DeactivatedAt = &now
 | 
				
			||||||
 | 
								peer.DeactivatedReason = wireguard.DeactivatedReasonLdapMissing
 | 
				
			||||||
			if err := s.UpdatePeer(peer, now); err != nil {
 | 
								if err := s.UpdatePeer(peer, now); err != nil {
 | 
				
			||||||
				logrus.Errorf("failed to update deactivated peer %s: %v", peer.PublicKey, err)
 | 
									logrus.Errorf("failed to update deactivated peer %s: %v", peer.PublicKey, err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -141,6 +144,7 @@ func (s *Server) updateLdapUsers(ldapUsers []ldap.RawLdapData) {
 | 
				
			|||||||
			for _, peer := range s.peers.GetPeersByMail(user.Email) {
 | 
								for _, peer := range s.peers.GetPeersByMail(user.Email) {
 | 
				
			||||||
				now := time.Now()
 | 
									now := time.Now()
 | 
				
			||||||
				peer.DeactivatedAt = nil
 | 
									peer.DeactivatedAt = nil
 | 
				
			||||||
 | 
									peer.DeactivatedReason = ""
 | 
				
			||||||
				if err = s.UpdatePeer(peer, now); err != nil {
 | 
									if err = s.UpdatePeer(peer, now); err != nil {
 | 
				
			||||||
					logrus.Errorf("failed to update activated peer %s: %v", peer.PublicKey, err)
 | 
										logrus.Errorf("failed to update activated peer %s: %v", peer.PublicKey, err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -127,6 +127,7 @@ func (s *Server) Setup(ctx context.Context) error {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
	s.server.Use(sessions.Sessions("authsession", cookieStore))
 | 
						s.server.Use(sessions.Sessions("authsession", cookieStore))
 | 
				
			||||||
	s.server.SetFuncMap(template.FuncMap{
 | 
						s.server.SetFuncMap(template.FuncMap{
 | 
				
			||||||
 | 
							"formatDate":  common.FormatDateHTML,
 | 
				
			||||||
		"formatBytes": common.ByteCountSI,
 | 
							"formatBytes": common.ByteCountSI,
 | 
				
			||||||
		"urlEncode":   url.QueryEscape,
 | 
							"urlEncode":   url.QueryEscape,
 | 
				
			||||||
		"startsWith":  strings.HasPrefix,
 | 
							"startsWith":  strings.HasPrefix,
 | 
				
			||||||
@@ -215,6 +216,8 @@ func (s *Server) Run() {
 | 
				
			|||||||
		go s.SyncLdapWithUserDatabase()
 | 
							go s.SyncLdapWithUserDatabase()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						go s.RunBackgroundTasks(s.ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Run web service
 | 
						// Run web service
 | 
				
			||||||
	srv := &http.Server{
 | 
						srv := &http.Server{
 | 
				
			||||||
		Addr:    s.config.Core.ListeningAddress,
 | 
							Addr:    s.config.Core.ListeningAddress,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
package server
 | 
					package server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
	"crypto/md5"
 | 
						"crypto/md5"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
@@ -62,7 +63,7 @@ func (s *Server) PrepareNewPeer(device string) (wireguard.Peer, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CreatePeerByEmail creates a new peer for the given email.
 | 
					// CreatePeerByEmail creates a new peer for the given email.
 | 
				
			||||||
func (s *Server) CreatePeerByEmail(device, email, identifierSuffix string, disabled bool) error {
 | 
					func (s *Server) CreatePeerByEmail(device, email, identifierSuffix string) error {
 | 
				
			||||||
	user := s.users.GetUser(email)
 | 
						user := s.users.GetUser(email)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	peer, err := s.PrepareNewPeer(device)
 | 
						peer, err := s.PrepareNewPeer(device)
 | 
				
			||||||
@@ -75,10 +76,6 @@ func (s *Server) CreatePeerByEmail(device, email, identifierSuffix string, disab
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		peer.Identifier = fmt.Sprintf("%s (%s)", email, identifierSuffix)
 | 
							peer.Identifier = fmt.Sprintf("%s (%s)", email, identifierSuffix)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	now := time.Now()
 | 
					 | 
				
			||||||
	if disabled {
 | 
					 | 
				
			||||||
		peer.DeactivatedAt = &now
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return s.CreatePeer(device, peer)
 | 
						return s.CreatePeer(device, peer)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -209,7 +206,7 @@ func (s *Server) WriteWireGuardConfigFile(device string) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dev := s.peers.GetDevice(device)
 | 
						dev := s.peers.GetDevice(device)
 | 
				
			||||||
	cfg, err := dev.GetConfigFile(s.peers.GetActivePeers(device), s.config.Core.WGExoprterFriendlyNames)
 | 
						cfg, err := dev.GetConfigFile(s.peers.GetActivePeers(device), s.config.Core.WGExporterFriendlyNames)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return errors.WithMessage(err, "failed to get config file")
 | 
							return errors.WithMessage(err, "failed to get config file")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -281,6 +278,7 @@ func (s *Server) UpdateUser(user users.User) error {
 | 
				
			|||||||
		for _, peer := range s.peers.GetPeersByMail(user.Email) {
 | 
							for _, peer := range s.peers.GetPeersByMail(user.Email) {
 | 
				
			||||||
			now := time.Now()
 | 
								now := time.Now()
 | 
				
			||||||
			peer.DeactivatedAt = nil
 | 
								peer.DeactivatedAt = nil
 | 
				
			||||||
 | 
								peer.DeactivatedReason = ""
 | 
				
			||||||
			if err := s.UpdatePeer(peer, now); err != nil {
 | 
								if err := s.UpdatePeer(peer, now); err != nil {
 | 
				
			||||||
				logrus.Errorf("failed to update (re)activated peer %s for %s: %v", peer.PublicKey, user.Email, err)
 | 
									logrus.Errorf("failed to update (re)activated peer %s for %s: %v", peer.PublicKey, user.Email, err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -302,6 +300,7 @@ func (s *Server) DeleteUser(user users.User) error {
 | 
				
			|||||||
	for _, peer := range s.peers.GetPeersByMail(user.Email) {
 | 
						for _, peer := range s.peers.GetPeersByMail(user.Email) {
 | 
				
			||||||
		now := time.Now()
 | 
							now := time.Now()
 | 
				
			||||||
		peer.DeactivatedAt = &now
 | 
							peer.DeactivatedAt = &now
 | 
				
			||||||
 | 
							peer.DeactivatedReason = wireguard.DeactivatedReasonUserMissing
 | 
				
			||||||
		if err := s.UpdatePeer(peer, now); err != nil {
 | 
							if err := s.UpdatePeer(peer, now); err != nil {
 | 
				
			||||||
			logrus.Errorf("failed to update deactivated peer %s for %s: %v", peer.PublicKey, user.Email, err)
 | 
								logrus.Errorf("failed to update deactivated peer %s for %s: %v", peer.PublicKey, user.Email, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -376,3 +375,60 @@ func (s *Server) GetDeviceNames() map[string]string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return devNames
 | 
						return devNames
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *Server) RunBackgroundTasks(ctx context.Context) {
 | 
				
			||||||
 | 
						running := true
 | 
				
			||||||
 | 
						for running {
 | 
				
			||||||
 | 
							select {
 | 
				
			||||||
 | 
							case <-ctx.Done():
 | 
				
			||||||
 | 
								running = false
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							case <-time.After(time.Duration(s.config.Core.BackgroundTaskInterval) * time.Second):
 | 
				
			||||||
 | 
								// sleep completed, select will stop blocking
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							logrus.Debug("running periodic background tasks...")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err := s.checkExpiredPeers()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								logrus.Errorf("failed to check expired peers: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *Server) checkExpiredPeers() error {
 | 
				
			||||||
 | 
						now := time.Now()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, devName := range s.wg.Cfg.DeviceNames {
 | 
				
			||||||
 | 
							changed := false
 | 
				
			||||||
 | 
							peers := s.peers.GetAllPeers(devName)
 | 
				
			||||||
 | 
							for _, peer := range peers {
 | 
				
			||||||
 | 
								if peer.IsExpired() && !peer.IsDeactivated() {
 | 
				
			||||||
 | 
									changed = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									peer.UpdatedAt = now
 | 
				
			||||||
 | 
									peer.DeactivatedAt = &now
 | 
				
			||||||
 | 
									peer.DeactivatedReason = wireguard.DeactivatedReasonExpired
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									res := s.db.Save(&peer)
 | 
				
			||||||
 | 
									if res.Error != nil {
 | 
				
			||||||
 | 
										return fmt.Errorf("failed save expired peer %s: %w", peer.PublicKey, res.Error)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									err := s.wg.RemovePeer(peer.DeviceName, peer.PublicKey)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return fmt.Errorf("failed to expire peer %s: %w", peer.PublicKey, err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if changed {
 | 
				
			||||||
 | 
								err := s.WriteWireGuardConfigFile(devName)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return fmt.Errorf("failed to persist config for interface %s: %w", devName, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
package server
 | 
					package server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var Version = "testbuild"
 | 
					var Version = "testbuild"
 | 
				
			||||||
var DatabaseVersion = "1.0.8"
 | 
					var DatabaseVersion = "1.0.9"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,18 @@ import (
 | 
				
			|||||||
	"gorm.io/gorm"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						DeactivatedReasonExpired     = "expired"
 | 
				
			||||||
 | 
						DeactivatedReasonUserEdit    = "user edit action"
 | 
				
			||||||
 | 
						DeactivatedReasonUserCreate  = "user create action"
 | 
				
			||||||
 | 
						DeactivatedReasonAdminEdit   = "admin edit action"
 | 
				
			||||||
 | 
						DeactivatedReasonAdminCreate = "admin create action"
 | 
				
			||||||
 | 
						DeactivatedReasonApiEdit     = "api edit action"
 | 
				
			||||||
 | 
						DeactivatedReasonApiCreate   = "api create action"
 | 
				
			||||||
 | 
						DeactivatedReasonLdapMissing = "missing in ldap"
 | 
				
			||||||
 | 
						DeactivatedReasonUserMissing = "missing user"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CUSTOM VALIDATORS ----------------------------------------------------------------------------
 | 
					// CUSTOM VALIDATORS ----------------------------------------------------------------------------
 | 
				
			||||||
var cidrList validator.Func = func(fl validator.FieldLevel) bool {
 | 
					var cidrList validator.Func = func(fl validator.FieldLevel) bool {
 | 
				
			||||||
	cidrListStr := fl.Field().String()
 | 
						cidrListStr := fl.Field().String()
 | 
				
			||||||
@@ -108,11 +120,15 @@ type Peer struct {
 | 
				
			|||||||
	// Global Device Settings (can be ignored, only make sense if device is in server mode)
 | 
						// Global Device Settings (can be ignored, only make sense if device is in server mode)
 | 
				
			||||||
	Mtu int `form:"mtu" binding:"gte=0,lte=1500"`
 | 
						Mtu int `form:"mtu" binding:"gte=0,lte=1500"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	DeactivatedAt *time.Time `json:",omitempty"`
 | 
						DeactivatedAt     *time.Time `json:",omitempty"`
 | 
				
			||||||
	CreatedBy     string
 | 
						DeactivatedReason string     `json:",omitempty"`
 | 
				
			||||||
	UpdatedBy     string
 | 
					
 | 
				
			||||||
	CreatedAt     time.Time
 | 
						ExpiresAt *time.Time `json:",omitempty" form:"expires_at" binding:"omitempty" time_format:"2006-01-02"`
 | 
				
			||||||
	UpdatedAt     time.Time
 | 
					
 | 
				
			||||||
 | 
						CreatedBy string
 | 
				
			||||||
 | 
						UpdatedBy string
 | 
				
			||||||
 | 
						CreatedAt time.Time
 | 
				
			||||||
 | 
						UpdatedAt time.Time
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *Peer) SetIPAddresses(addresses ...string) {
 | 
					func (p *Peer) SetIPAddresses(addresses ...string) {
 | 
				
			||||||
@@ -238,6 +254,33 @@ func (p Peer) IsValid() bool {
 | 
				
			|||||||
	return true
 | 
						return true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p Peer) WillExpire() bool {
 | 
				
			||||||
 | 
						if p.ExpiresAt == nil {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if p.DeactivatedAt != nil {
 | 
				
			||||||
 | 
							return false // already deactivated...
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if p.ExpiresAt.After(time.Now()) {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p Peer) IsExpired() bool {
 | 
				
			||||||
 | 
						if p.ExpiresAt == nil {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if p.ExpiresAt.Before(time.Now()) {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p Peer) IsDeactivated() bool {
 | 
				
			||||||
 | 
						return p.DeactivatedAt != nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p Peer) GetConfigFileName() string {
 | 
					func (p Peer) GetConfigFileName() string {
 | 
				
			||||||
	reg := regexp.MustCompile("[^a-zA-Z0-9_-]+")
 | 
						reg := regexp.MustCompile("[^a-zA-Z0-9_-]+")
 | 
				
			||||||
	return reg.ReplaceAllString(strings.ReplaceAll(p.Identifier, " ", "-"), "") + ".conf"
 | 
						return reg.ReplaceAllString(strings.ReplaceAll(p.Identifier, " ", "-"), "") + ".conf"
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user