mirror of
				https://github.com/h44z/wg-portal.git
				synced 2025-11-03 23:56:18 +00:00 
			
		
		
		
	V2 alpha - initial version (#172)
Initial alpha codebase for version 2 of WireGuard Portal. This version is considered unstable and incomplete (for example, no public REST API)! Use with care! Fixes/Implements the following issues: - OAuth support #154, #1 - New Web UI with internationalisation support #98, #107, #89, #62 - Postgres Support #49 - Improved Email handling #47, #119 - DNS Search Domain support #46 - Bugfixes #94, #48 --------- Co-authored-by: Fabian Wechselberger <wechselbergerf@hotmail.com>
This commit is contained in:
		
							
								
								
									
										118
									
								
								internal/config/auth.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								internal/config/auth.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,118 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/go-ldap/ldap/v3"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Auth struct {
 | 
			
		||||
	OpenIDConnect     []OpenIDConnectProvider `yaml:"oidc"`
 | 
			
		||||
	OAuth             []OAuthProvider         `yaml:"oauth"`
 | 
			
		||||
	Ldap              []LdapProvider          `yaml:"ldap"`
 | 
			
		||||
	CallbackUrlPrefix string                  `yaml:"callback_url_prefix"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type BaseFields struct {
 | 
			
		||||
	UserIdentifier string `yaml:"user_identifier"`
 | 
			
		||||
	Email          string `yaml:"email"`
 | 
			
		||||
	Firstname      string `yaml:"firstname"`
 | 
			
		||||
	Lastname       string `yaml:"lastname"`
 | 
			
		||||
	Phone          string `yaml:"phone"`
 | 
			
		||||
	Department     string `yaml:"department"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type OauthFields struct {
 | 
			
		||||
	BaseFields `yaml:",inline"`
 | 
			
		||||
	IsAdmin    string `yaml:"is_admin"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type LdapFields struct {
 | 
			
		||||
	BaseFields      `yaml:",inline"`
 | 
			
		||||
	GroupMembership string `yaml:"memberof"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type LdapProvider struct {
 | 
			
		||||
	// ProviderName is an internal name that is used to distinguish LDAP servers. It must not contain spaces or special characters.
 | 
			
		||||
	ProviderName string `yaml:"provider_name"`
 | 
			
		||||
 | 
			
		||||
	URL                string `yaml:"url"`
 | 
			
		||||
	StartTLS           bool   `yaml:"start_tls"`
 | 
			
		||||
	CertValidation     bool   `yaml:"cert_validation"`
 | 
			
		||||
	TlsCertificatePath string `yaml:"tls_certificate_path"`
 | 
			
		||||
	TlsKeyPath         string `yaml:"tls_key_path"`
 | 
			
		||||
 | 
			
		||||
	BaseDN   string `yaml:"base_dn"`
 | 
			
		||||
	BindUser string `yaml:"bind_user"`
 | 
			
		||||
	BindPass string `yaml:"bind_pass"`
 | 
			
		||||
 | 
			
		||||
	FieldMap LdapFields `yaml:"field_map"`
 | 
			
		||||
 | 
			
		||||
	LoginFilter        string   `yaml:"login_filter"` // {{login_identifier}} gets replaced with the login email address / username
 | 
			
		||||
	AdminGroupDN       string   `yaml:"admin_group"`  // Members of this group receive admin rights in WG-Portal
 | 
			
		||||
	ParsedAdminGroupDN *ldap.DN `yaml:"-"`
 | 
			
		||||
 | 
			
		||||
	Synchronize bool `yaml:"synchronize"`
 | 
			
		||||
	// If DisableMissing is false, missing users will be deactivated
 | 
			
		||||
	DisableMissing bool   `yaml:"disable_missing"`
 | 
			
		||||
	SyncFilter     string `yaml:"sync_filter"`
 | 
			
		||||
 | 
			
		||||
	// If RegistrationEnabled is set to true, wg-portal will create new users that do not exist in the database.
 | 
			
		||||
	RegistrationEnabled bool `yaml:"registration_enabled"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type OpenIDConnectProvider struct {
 | 
			
		||||
	// ProviderName is an internal name that is used to distinguish oauth endpoints. It must not contain spaces or special characters.
 | 
			
		||||
	ProviderName string `yaml:"provider_name"`
 | 
			
		||||
 | 
			
		||||
	// DisplayName is shown to the user on the login page. If it is empty, ProviderName will be displayed.
 | 
			
		||||
	DisplayName string `yaml:"display_name"`
 | 
			
		||||
 | 
			
		||||
	BaseUrl string `yaml:"base_url"`
 | 
			
		||||
 | 
			
		||||
	// ClientID is the application's ID.
 | 
			
		||||
	ClientID string `yaml:"client_id"`
 | 
			
		||||
 | 
			
		||||
	// ClientSecret is the application's secret.
 | 
			
		||||
	ClientSecret string `yaml:"client_secret"`
 | 
			
		||||
 | 
			
		||||
	// ExtraScopes specifies optional requested permissions.
 | 
			
		||||
	ExtraScopes []string `yaml:"extra_scopes"`
 | 
			
		||||
 | 
			
		||||
	// FieldMap is used to map the names of the user-info endpoint fields to wg-portal fields
 | 
			
		||||
	FieldMap OauthFields `yaml:"field_map"`
 | 
			
		||||
 | 
			
		||||
	// If RegistrationEnabled is set to true, missing users will be created in the database
 | 
			
		||||
	RegistrationEnabled bool `yaml:"registration_enabled"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type OAuthProvider struct {
 | 
			
		||||
	// ProviderName is an internal name that is used to distinguish oauth endpoints. It must not contain spaces or special characters.
 | 
			
		||||
	ProviderName string `yaml:"provider_name"`
 | 
			
		||||
 | 
			
		||||
	// DisplayName is shown to the user on the login page. If it is empty, ProviderName will be displayed.
 | 
			
		||||
	DisplayName string `yaml:"display_name"`
 | 
			
		||||
 | 
			
		||||
	BaseUrl string `yaml:"base_url"`
 | 
			
		||||
 | 
			
		||||
	// ClientID is the application's ID.
 | 
			
		||||
	ClientID string `yaml:"client_id"`
 | 
			
		||||
 | 
			
		||||
	// ClientSecret is the application's secret.
 | 
			
		||||
	ClientSecret string `yaml:"client_secret"`
 | 
			
		||||
 | 
			
		||||
	AuthURL     string `yaml:"auth_url"`
 | 
			
		||||
	TokenURL    string `yaml:"token_url"`
 | 
			
		||||
	UserInfoURL string `yaml:"user_info_url"`
 | 
			
		||||
 | 
			
		||||
	// RedirectURL is the URL to redirect users going through
 | 
			
		||||
	// the OAuth flow, after the resource owner's URLs.
 | 
			
		||||
	RedirectURL string `yaml:"redirect_url"`
 | 
			
		||||
 | 
			
		||||
	// Scope specifies optional requested permissions.
 | 
			
		||||
	Scopes []string `yaml:"scopes"`
 | 
			
		||||
 | 
			
		||||
	// FieldMap is used to map the names of the user-info endpoint fields to wg-portal fields
 | 
			
		||||
	FieldMap OauthFields `yaml:"field_map"`
 | 
			
		||||
 | 
			
		||||
	// If RegistrationEnabled is set to true, wg-portal will create new users that do not exist in the database.
 | 
			
		||||
	RegistrationEnabled bool `yaml:"registration_enabled"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										173
									
								
								internal/config/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								internal/config/config.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,173 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
	"os"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"gopkg.in/yaml.v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Config struct {
 | 
			
		||||
	Core struct {
 | 
			
		||||
		// AdminUser defines the default administrator account that will be created
 | 
			
		||||
		AdminUser     string `yaml:"admin_user"`
 | 
			
		||||
		AdminPassword string `yaml:"admin_password"`
 | 
			
		||||
 | 
			
		||||
		EditableKeys            bool `yaml:"editable_keys"`
 | 
			
		||||
		CreateDefaultPeer       bool `yaml:"create_default_peer"`
 | 
			
		||||
		SelfProvisioningAllowed bool `yaml:"self_provisioning_allowed"`
 | 
			
		||||
		ImportExisting          bool `yaml:"import_existing"`
 | 
			
		||||
		RestoreState            bool `yaml:"restore_state"`
 | 
			
		||||
	} `yaml:"core"`
 | 
			
		||||
 | 
			
		||||
	Advanced struct {
 | 
			
		||||
		LogLevel            string        `yaml:"log_level"`
 | 
			
		||||
		LogPretty           bool          `yaml:"log_pretty"`
 | 
			
		||||
		LogJson             bool          `yaml:"log_json"`
 | 
			
		||||
		LdapSyncInterval    time.Duration `yaml:"ldap_sync_interval"`
 | 
			
		||||
		StartListenPort     int           `yaml:"start_listen_port"`
 | 
			
		||||
		StartCidrV4         string        `yaml:"start_cidr_v4"`
 | 
			
		||||
		StartCidrV6         string        `yaml:"start_cidr_v6"`
 | 
			
		||||
		UseIpV6             bool          `yaml:"use_ip_v6"`
 | 
			
		||||
		ConfigStoragePath   string        `yaml:"config_storage_path"` // keep empty to disable config export to file
 | 
			
		||||
		ExpiryCheckInterval time.Duration `yaml:"expiry_check_interval"`
 | 
			
		||||
		RulePrioOffset      int           `yaml:"rule_prio_offset"`
 | 
			
		||||
		RouteTableOffset    int           `yaml:"route_table_offset"`
 | 
			
		||||
	} `yaml:"advanced"`
 | 
			
		||||
 | 
			
		||||
	Statistics struct {
 | 
			
		||||
		UsePingChecks          bool          `yaml:"use_ping_checks"`
 | 
			
		||||
		PingCheckWorkers       int           `yaml:"ping_check_workers"`
 | 
			
		||||
		PingUnprivileged       bool          `yaml:"ping_unprivileged"`
 | 
			
		||||
		PingCheckInterval      time.Duration `yaml:"ping_check_interval"`
 | 
			
		||||
		DataCollectionInterval time.Duration `yaml:"data_collection_interval"`
 | 
			
		||||
		CollectInterfaceData   bool          `yaml:"collect_interface_data"`
 | 
			
		||||
		CollectPeerData        bool          `yaml:"collect_peer_data"`
 | 
			
		||||
		CollectAuditData       bool          `yaml:"collect_audit_data"`
 | 
			
		||||
	} `yaml:"statistics"`
 | 
			
		||||
 | 
			
		||||
	Mail MailConfig `yaml:"mail"`
 | 
			
		||||
 | 
			
		||||
	Auth Auth `yaml:"auth"`
 | 
			
		||||
 | 
			
		||||
	Database DatabaseConfig `yaml:"database"`
 | 
			
		||||
 | 
			
		||||
	Web WebConfig `yaml:"web"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Config) LogStartupValues() {
 | 
			
		||||
	logrus.Debug("WireGuard Portal Features:")
 | 
			
		||||
	logrus.Debugf("  - EditableKeys: %t", c.Core.EditableKeys)
 | 
			
		||||
	logrus.Debugf("  - CreateDefaultPeer: %t", c.Core.CreateDefaultPeer)
 | 
			
		||||
	logrus.Debugf("  - SelfProvisioningAllowed: %t", c.Core.SelfProvisioningAllowed)
 | 
			
		||||
	logrus.Debugf("  - ImportExisting: %t", c.Core.ImportExisting)
 | 
			
		||||
	logrus.Debugf("  - RestoreState: %t", c.Core.RestoreState)
 | 
			
		||||
	logrus.Debugf("  - UseIpV6: %t", c.Advanced.UseIpV6)
 | 
			
		||||
	logrus.Debugf("  - CollectInterfaceData: %t", c.Statistics.CollectInterfaceData)
 | 
			
		||||
	logrus.Debugf("  - CollectPeerData: %t", c.Statistics.CollectPeerData)
 | 
			
		||||
	logrus.Debugf("  - CollectAuditData: %t", c.Statistics.CollectAuditData)
 | 
			
		||||
 | 
			
		||||
	logrus.Debug("WireGuard Portal Settings:")
 | 
			
		||||
	logrus.Debugf("  - ConfigStoragePath: %s", c.Advanced.ConfigStoragePath)
 | 
			
		||||
	logrus.Debugf("  - ExternalUrl: %s", c.Web.ExternalUrl)
 | 
			
		||||
 | 
			
		||||
	logrus.Debug("WireGuard Portal Authentication:")
 | 
			
		||||
	logrus.Debugf("  - OIDC Providers: %d", len(c.Auth.OpenIDConnect))
 | 
			
		||||
	logrus.Debugf("  - OAuth Providers: %d", len(c.Auth.OAuth))
 | 
			
		||||
	logrus.Debugf("  - Ldap Providers: %d", len(c.Auth.Ldap))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func defaultConfig() *Config {
 | 
			
		||||
	cfg := &Config{}
 | 
			
		||||
 | 
			
		||||
	cfg.Core.ImportExisting = true
 | 
			
		||||
	cfg.Core.RestoreState = true
 | 
			
		||||
 | 
			
		||||
	cfg.Database = DatabaseConfig{
 | 
			
		||||
		Type: "sqlite",
 | 
			
		||||
		DSN:  "sqlite.db",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cfg.Web = WebConfig{
 | 
			
		||||
		RequestLogging:    false,
 | 
			
		||||
		ExternalUrl:       "http://localhost:8888",
 | 
			
		||||
		ListeningAddress:  ":8888",
 | 
			
		||||
		SessionIdentifier: "wgPortalSession",
 | 
			
		||||
		SessionSecret:     "very_secret",
 | 
			
		||||
		CsrfSecret:        "extremely_secret",
 | 
			
		||||
		SiteTitle:         "WireGuard Portal",
 | 
			
		||||
		SiteCompanyName:   "WireGuard Portal",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cfg.Auth.CallbackUrlPrefix = "/api/v0"
 | 
			
		||||
 | 
			
		||||
	cfg.Advanced.StartListenPort = 51820
 | 
			
		||||
	cfg.Advanced.StartCidrV4 = "10.11.12.0/24"
 | 
			
		||||
	cfg.Advanced.StartCidrV6 = "fdfd:d3ad:c0de:1234::0/64"
 | 
			
		||||
	cfg.Advanced.UseIpV6 = true
 | 
			
		||||
	cfg.Advanced.ExpiryCheckInterval = 15 * time.Minute
 | 
			
		||||
	cfg.Advanced.RulePrioOffset = 20000
 | 
			
		||||
	cfg.Advanced.RouteTableOffset = 20000
 | 
			
		||||
 | 
			
		||||
	cfg.Statistics.UsePingChecks = true
 | 
			
		||||
	cfg.Statistics.PingCheckWorkers = 10
 | 
			
		||||
	cfg.Statistics.PingUnprivileged = false
 | 
			
		||||
	cfg.Statistics.PingCheckInterval = 1 * time.Minute
 | 
			
		||||
	cfg.Statistics.DataCollectionInterval = 10 * time.Second
 | 
			
		||||
	cfg.Statistics.CollectInterfaceData = true
 | 
			
		||||
	cfg.Statistics.CollectPeerData = true
 | 
			
		||||
	cfg.Statistics.CollectAuditData = true
 | 
			
		||||
 | 
			
		||||
	cfg.Mail = MailConfig{
 | 
			
		||||
		Host:           "127.0.0.1",
 | 
			
		||||
		Port:           25,
 | 
			
		||||
		Encryption:     MailEncryptionNone,
 | 
			
		||||
		CertValidation: false,
 | 
			
		||||
		Username:       "",
 | 
			
		||||
		Password:       "",
 | 
			
		||||
		AuthType:       MailAuthPlain,
 | 
			
		||||
		From:           "Wireguard Portal <noreply@wireguard.local>",
 | 
			
		||||
		LinkOnly:       false,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cfg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetConfig() (*Config, error) {
 | 
			
		||||
	cfg := defaultConfig()
 | 
			
		||||
 | 
			
		||||
	// override config values from YAML file
 | 
			
		||||
 | 
			
		||||
	cfgFileName := "config.yml"
 | 
			
		||||
	if envCfgFileName := os.Getenv("WG_PORTAL_CONFIG"); envCfgFileName != "" {
 | 
			
		||||
		cfgFileName = envCfgFileName
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := loadConfigFile(cfg, cfgFileName); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to load config from yaml: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cfg, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func loadConfigFile(cfg any, filename string) error {
 | 
			
		||||
	f, err := os.Open(filename)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer func(f *os.File) {
 | 
			
		||||
		if err := f.Close(); err != nil {
 | 
			
		||||
			logrus.Errorf("failed to close configuration file %s: %v", filename, err)
 | 
			
		||||
		}
 | 
			
		||||
	}(f)
 | 
			
		||||
 | 
			
		||||
	decoder := yaml.NewDecoder(f)
 | 
			
		||||
	err = decoder.Decode(cfg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								internal/config/database.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								internal/config/database.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import "time"
 | 
			
		||||
 | 
			
		||||
type SupportedDatabase string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DatabaseMySQL    SupportedDatabase = "mysql"
 | 
			
		||||
	DatabaseMsSQL    SupportedDatabase = "mssql"
 | 
			
		||||
	DatabasePostgres SupportedDatabase = "postgres"
 | 
			
		||||
	DatabaseSQLite   SupportedDatabase = "sqlite"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type DatabaseConfig struct {
 | 
			
		||||
	Debug              bool              `yaml:"debug"`
 | 
			
		||||
	SlowQueryThreshold time.Duration     `yaml:"slow_query_threshold"` // 0 means no logging of slow queries
 | 
			
		||||
	Type               SupportedDatabase `yaml:"type"`
 | 
			
		||||
	DSN                string            `yaml:"dsn"` // On SQLite: the database file-path, otherwise the dsn (see: https://gorm.io/docs/connecting_to_the_database.html)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								internal/config/mail.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								internal/config/mail.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
type MailEncryption string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	MailEncryptionNone     MailEncryption = "none"
 | 
			
		||||
	MailEncryptionTLS      MailEncryption = "tls"
 | 
			
		||||
	MailEncryptionStartTLS MailEncryption = "starttls"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type MailAuthType string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	MailAuthPlain   MailAuthType = "plain"
 | 
			
		||||
	MailAuthLogin   MailAuthType = "login"
 | 
			
		||||
	MailAuthCramMD5 MailAuthType = "crammd5"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type MailConfig struct {
 | 
			
		||||
	Host           string         `yaml:"host"`
 | 
			
		||||
	Port           int            `yaml:"port"`
 | 
			
		||||
	Encryption     MailEncryption `yaml:"encryption"`
 | 
			
		||||
	CertValidation bool           `yaml:"cert_validation"`
 | 
			
		||||
	Username       string         `yaml:"username"`
 | 
			
		||||
	Password       string         `yaml:"password"`
 | 
			
		||||
	AuthType       MailAuthType   `yaml:"auth_type"`
 | 
			
		||||
 | 
			
		||||
	From     string `yaml:"from"`
 | 
			
		||||
	LinkOnly bool   `yaml:"link_only"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								internal/config/web.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								internal/config/web.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
type WebConfig struct {
 | 
			
		||||
	RequestLogging    bool   `yaml:"request_logging"`
 | 
			
		||||
	ExternalUrl       string `yaml:"external_url"`
 | 
			
		||||
	ListeningAddress  string `yaml:"listening_address"`
 | 
			
		||||
	SessionIdentifier string `yaml:"session_identifier"`
 | 
			
		||||
	SessionSecret     string `yaml:"session_secret"`
 | 
			
		||||
	CsrfSecret        string `yaml:"csrf_secret"`
 | 
			
		||||
	SiteTitle         string `yaml:"site_title"`
 | 
			
		||||
	SiteCompanyName   string `yaml:"site_company_name"`
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user