Feat/ldap certificate connexion (#92)

* Give the way to connect against LDAP server with certificate and key

* fix(ldap) Update cert variable name

In order to be more explicit

Co-authored-by: Alexis Aurin <alexis@so6.pw>
This commit is contained in:
Alexis 2022-03-15 22:46:00 +01:00 committed by GitHub
parent 5d4d06db81
commit cc50fcf8e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 75 additions and 16 deletions

View File

@ -8,9 +8,9 @@
![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/h44z/wg-portal) ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/h44z/wg-portal)
[![Docker Pulls](https://img.shields.io/docker/pulls/h44z/wg-portal.svg)](https://hub.docker.com/r/h44z/wg-portal/) [![Docker Pulls](https://img.shields.io/docker/pulls/h44z/wg-portal.svg)](https://hub.docker.com/r/h44z/wg-portal/)
A simple, web based configuration portal for [WireGuard](https://wireguard.com). 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 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 interfaces. This allows for seamless activation or deactivation of new users, without disturbing existing VPN
connections. connections.
The configuration portal currently supports using SQLite and MySQL as a user source for authentication and profile data. The configuration portal currently supports using SQLite and MySQL as a user source for authentication and profile data.
@ -31,11 +31,11 @@ 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
![Screenshot](screenshot.png) ![Screenshot](screenshot.png)
## Setup ## Setup
Make sure that your host system has at least one WireGuard interface (for example wg0) available. Make sure that your host system has at least one WireGuard interface (for example wg0) available.
If you did not start up a WireGuard interface yet, take a look at [wg-quick](https://manpages.debian.org/unstable/wireguard-tools/wg-quick.8.en.html) in order to get started. If you did not start up a WireGuard interface yet, take a look at [wg-quick](https://manpages.debian.org/unstable/wireguard-tools/wg-quick.8.en.html) in order to get started.
### Docker ### Docker
@ -156,6 +156,9 @@ The following configuration options are available:
| LDAP_ATTR_LASTNAME | attrLastname | ldap | sn | User lastname attribute. | | LDAP_ATTR_LASTNAME | attrLastname | ldap | sn | User lastname attribute. |
| LDAP_ATTR_PHONE | attrPhone | ldap | telephoneNumber | User phone number attribute. | | LDAP_ATTR_PHONE | attrPhone | ldap | telephoneNumber | User phone number attribute. |
| LDAP_ATTR_GROUPS | attrGroups | ldap | memberOf | User groups attribute. | | LDAP_ATTR_GROUPS | attrGroups | ldap | memberOf | User groups attribute. |
| LDAP_CERT_CONN | ldapCertConn | ldap | false | Allow connection with certificate against LDAP server without user/password |
| LDAPTLS_CERT | ldapTlsCert | ldap | | The LDAP cert's path |
| LDAPTLS_KEY | ldapTlsKey | ldap | | The LDAP key's path |
| LOG_LEVEL | | | debug | Specify log level, one of: trace, debug, info, off. | | LOG_LEVEL | | | debug | Specify log level, one of: trace, debug, info, off. |
| LOG_JSON | | | false | Format log output as JSON. | | LOG_JSON | | | false | Format log output as JSON. |
| LOG_COLOR | | | true | Colorize log output. | | LOG_COLOR | | | true | Colorize log output. |
@ -190,7 +193,7 @@ email:
user: test@gmail.com user: test@gmail.com
pass: topsecret pass: topsecret
wg: wg:
devices: devices:
- wg0 - wg0
- wg1 - wg1
defaultDevice: wg0 defaultDevice: wg0
@ -199,8 +202,8 @@ wg:
``` ```
### RESTful API ### RESTful API
WireGuard Portal offers a RESTful API to interact with. WireGuard Portal offers a RESTful API to interact with.
The API is documented using OpenAPI 2.0, the Swagger UI can be found The API is documented using OpenAPI 2.0, the Swagger UI can be found
under the URL `http://<your wg-portal ip/domain>/swagger/index.html?displayOperationId=true`. under the URL `http://<your wg-portal ip/domain>/swagger/index.html?displayOperationId=true`.
The [API's unittesting](tests/test_API.py) may serve as an example how to make use of the API with python3 & pyswagger. The [API's unittesting](tests/test_API.py) may serve as an example how to make use of the API with python3 & pyswagger.
@ -210,7 +213,7 @@ The [API's unittesting](tests/test_API.py) may serve as an example how to make u
* Generation or application of any `iptables` or `nftables` rules. * 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. * Setting up or changing IP-addresses of the WireGuard interface on operating systems other than linux.
* Importing private keys of an existing WireGuard setup. * Importing private keys of an existing WireGuard setup.
## Application stack ## Application stack
* [Gin, HTTP web framework written in Go](https://github.com/gin-gonic/gin) * [Gin, HTTP web framework written in Go](https://github.com/gin-gonic/gin)
@ -221,6 +224,6 @@ The [API's unittesting](tests/test_API.py) may serve as an example how to make u
## License ## License
* MIT License. [MIT](LICENSE.txt) or https://opensource.org/licenses/MIT * MIT License. [MIT](LICENSE.txt) or https://opensource.org/licenses/MIT
This project was inspired by [wg-gen-web](https://github.com/vx3r/wg-gen-web). This project was inspired by [wg-gen-web](https://github.com/vx3r/wg-gen-web).

View File

@ -2,6 +2,7 @@ package ldap
import ( import (
"crypto/tls" "crypto/tls"
"io/ioutil"
"strings" "strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -154,7 +155,33 @@ func (provider Provider) GetUserModel(ctx *authentication.AuthContext) (*authent
} }
func (provider Provider) open() (*ldap.Conn, error) { func (provider Provider) open() (*ldap.Conn, error) {
tlsConfig := &tls.Config{InsecureSkipVerify: !provider.config.CertValidation} var tlsConfig *tls.Config
if provider.config.LdapCertConn {
cert_plain, err := ioutil.ReadFile(provider.config.LdapTlsCert)
if err != nil {
return nil, errors.WithMessage(err, "failed to load the certificate")
}
key, err := ioutil.ReadFile(provider.config.LdapTlsKey)
if err != nil {
return nil, errors.WithMessage(err, "failed to load the key")
}
cert_x509, err := tls.X509KeyPair(cert_plain, key)
if err != nil {
return nil, errors.WithMessage(err, "failed X509")
}
tlsConfig = &tls.Config{Certificates: []tls.Certificate{cert_x509}}
} else {
tlsConfig = &tls.Config{InsecureSkipVerify: !provider.config.CertValidation}
}
conn, err := ldap.DialURL(provider.config.URL, ldap.DialWithTLSConfig(tlsConfig)) conn, err := ldap.DialURL(provider.config.URL, ldap.DialWithTLSConfig(tlsConfig))
if err != nil { if err != nil {
return nil, errors.WithMessage(err, "failed to connect to LDAP") return nil, errors.WithMessage(err, "failed to connect to LDAP")

View File

@ -4,7 +4,6 @@ import (
gldap "github.com/go-ldap/ldap/v3" gldap "github.com/go-ldap/ldap/v3"
) )
type Type string type Type string
const ( const (
@ -26,8 +25,11 @@ type Config struct {
PhoneAttribute string `yaml:"attrPhone" envconfig:"LDAP_ATTR_PHONE"` PhoneAttribute string `yaml:"attrPhone" envconfig:"LDAP_ATTR_PHONE"`
GroupMemberAttribute string `yaml:"attrGroups" envconfig:"LDAP_ATTR_GROUPS"` GroupMemberAttribute string `yaml:"attrGroups" envconfig:"LDAP_ATTR_GROUPS"`
LoginFilter string `yaml:"loginFilter" envconfig:"LDAP_LOGIN_FILTER"` // {{login_identifier}} gets replaced with the login email address LoginFilter string `yaml:"loginFilter" envconfig:"LDAP_LOGIN_FILTER"` // {{login_identifier}} gets replaced with the login email address
SyncFilter string `yaml:"syncFilter" envconfig:"LDAP_SYNC_FILTER"` SyncFilter string `yaml:"syncFilter" envconfig:"LDAP_SYNC_FILTER"`
AdminLdapGroup string `yaml:"adminGroup" envconfig:"LDAP_ADMIN_GROUP"` // Members of this group receive admin rights in WG-Portal AdminLdapGroup string `yaml:"adminGroup" envconfig:"LDAP_ADMIN_GROUP"` // Members of this group receive admin rights in WG-Portal
AdminLdapGroup_ *gldap.DN `yaml:"-"` AdminLdapGroup_ *gldap.DN `yaml:"-"`
LdapCertConn bool `yaml:"ldapCertConn" envconfig:"LDAP_CERT_CONN"`
LdapTlsCert string `yaml:"ldapTlsCert" envconfig:"LDAPTLS_CERT"`
LdapTlsKey string `yaml:"ldapTlsKey" envconfig:"LDAPTLS_KEY"`
} }

View File

@ -2,6 +2,7 @@ package ldap
import ( import (
"crypto/tls" "crypto/tls"
"io/ioutil"
"github.com/go-ldap/ldap/v3" "github.com/go-ldap/ldap/v3"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -14,7 +15,33 @@ type RawLdapData struct {
} }
func Open(cfg *Config) (*ldap.Conn, error) { func Open(cfg *Config) (*ldap.Conn, error) {
tlsConfig := &tls.Config{InsecureSkipVerify: !cfg.CertValidation} var tlsConfig *tls.Config
if cfg.LdapCertConn {
cert_plain, err := ioutil.ReadFile(cfg.LdapTlsCert)
if err != nil {
return nil, errors.WithMessage(err, "failed to load the certificate")
}
key, err := ioutil.ReadFile(cfg.LdapTlsKey)
if err != nil {
return nil, errors.WithMessage(err, "failed to load the key")
}
cert_x509, err := tls.X509KeyPair(cert_plain, key)
if err != nil {
return nil, errors.WithMessage(err, "failed X509")
}
tlsConfig = &tls.Config{Certificates: []tls.Certificate{cert_x509}}
} else {
tlsConfig = &tls.Config{InsecureSkipVerify: !cfg.CertValidation}
}
conn, err := ldap.DialURL(cfg.URL, ldap.DialWithTLSConfig(tlsConfig)) conn, err := ldap.DialURL(cfg.URL, ldap.DialWithTLSConfig(tlsConfig))
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to connect to LDAP") return nil, errors.Wrap(err, "failed to connect to LDAP")