diff --git a/README.md b/README.md index 8cca1e61e38cee63784198660804bb11156d29f0..715b2af240a76cdd8cc516bdb2121ed0e8e8155c 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,11 @@ server: # make sure to share this across the different pods sessionKey: thisisasessionkeyreplacethisjetzt sessionEncryptionKey: thisisasessionkeyreplacethisnunu! - # tries to set the receive / send buffer of the connections to the client + # where to store session details. This can be either file or cookie (default: cookie) + # if a file store is chosen, it is required to have clients 'keep state' to the rdpgw + # instance they are connected to. + sessionStore: cookie + # tries to set the receive / send buffer of the connections to the client # in case of high latency high bandwidth the defaults set by the OS might # be to low for a good experience # receiveBuf: 12582912 diff --git a/cmd/rdpgw/config/configuration.go b/cmd/rdpgw/config/configuration.go index cb851e3c2680da9df67f24a06199f0f8365446c0..4974449f1e829ea567546351685cae2524f395e9 100644 --- a/cmd/rdpgw/config/configuration.go +++ b/cmd/rdpgw/config/configuration.go @@ -1,102 +1,147 @@ package config import ( - "github.com/spf13/viper" + "github.com/knadh/koanf" + "github.com/knadh/koanf/parsers/yaml" + "github.com/knadh/koanf/providers/confmap" + "github.com/knadh/koanf/providers/env" + "github.com/knadh/koanf/providers/file" "log" + "strings" ) type Configuration struct { - Server ServerConfig - OpenId OpenIDConfig - Caps RDGCapsConfig - Security SecurityConfig - Client ClientConfig + Server ServerConfig `koanf:"server"` + OpenId OpenIDConfig `koanf:"openid"` + Caps RDGCapsConfig `koanf:"caps"` + Security SecurityConfig `koanf:"security"` + Client ClientConfig `koanf:"client"` } type ServerConfig struct { - GatewayAddress string - Port int - DisableTLS bool - CertFile string - KeyFile string - Hosts []string - RoundRobin bool - SessionKey string - SessionEncryptionKey string - SessionStore string - SendBuf int - ReceiveBuf int + GatewayAddress string `koanf:"gatewayaddress"` + Port int `koanf:"port"` + CertFile string `koanf:"certfile"` + KeyFile string `koanf:"keyfile"` + Hosts []string `koanf:"hosts"` + RoundRobin bool `koanf:"roundrobin"` + SessionKey string `koanf:"sessionkey"` + SessionEncryptionKey string `koanf:"sessionencryptionkey"` + SessionStore string `koanf:"sessionstore"` + SendBuf int `koanf:"sendbuf"` + ReceiveBuf int `koanf:"recievebuf"` + DisableTLS bool `koanf:"disabletls"` } type OpenIDConfig struct { - ProviderUrl string - ClientId string - ClientSecret string + ProviderUrl string `koanf:"providerurl"` + ClientId string `koanf:"clientid"` + ClientSecret string `koanf:"clientsecret"` } type RDGCapsConfig struct { - SmartCardAuth bool - TokenAuth bool - IdleTimeout int - RedirectAll bool - DisableRedirect bool - EnableClipboard bool - EnablePrinter bool - EnablePort bool - EnablePnp bool - EnableDrive bool + SmartCardAuth bool `koanf:"smartcardauth"` + TokenAuth bool `koanf:"tokenauth"` + IdleTimeout int `koanf:"idletimeout"` + RedirectAll bool `koanf:"redirectall"` + DisableRedirect bool `koanf:"disableredirect"` + EnableClipboard bool `koanf:"enableclipboard"` + EnablePrinter bool `koanf:"enableprinter"` + EnablePort bool `koanf:"enableport"` + EnablePnp bool `koanf:"enablepnp"` + EnableDrive bool `koanf:"enabledrive"` } type SecurityConfig struct { - PAATokenEncryptionKey string - PAATokenSigningKey string - UserTokenEncryptionKey string - UserTokenSigningKey string - VerifyClientIp bool - EnableUserToken bool + PAATokenEncryptionKey string `koanf:"paatokenencryptionkey"` + PAATokenSigningKey string `koanf:"paatokensigningkey"` + UserTokenEncryptionKey string `koanf:"usertokenencryptionkey"` + UserTokenSigningKey string `koanf:"usertokensigningkey"` + VerifyClientIp bool `koanf:"verifyclientip"` + EnableUserToken bool `koanf:"enableusertoken"` } type ClientConfig struct { - NetworkAutoDetect int - BandwidthAutoDetect int - ConnectionType int - UsernameTemplate string - SplitUserDomain bool - DefaultDomain string + NetworkAutoDetect int `koanf:"networkautodetect"` + BandwidthAutoDetect int `koanf:"bandwidthautodetect"` + ConnectionType int `koanf:"connectiontype"` + UsernameTemplate string `koanf:"usernametemplate"` + SplitUserDomain bool `koanf:"splituserdomain"` + DefaultDomain string `koanf:"defaultdomain"` } -func init() { - viper.SetDefault("server.certFile", "server.pem") - viper.SetDefault("server.keyFile", "key.pem") - viper.SetDefault("server.port", 443) - viper.SetDefault("client.networkAutoDetect", 1) - viper.SetDefault("client.bandwidthAutoDetect", 1) - viper.SetDefault("security.verifyClientIp", true) - viper.SetDefault("server.tlsDisabled", false) - viper.SetDefault("server.sessionStore", "cookie") - viper.SetDefault("caps.tokenAuth", true) +func ToCamel(s string) string { + s = strings.TrimSpace(s) + n := strings.Builder{} + n.Grow(len(s)) + var capNext bool = true + for i, v := range []byte(s) { + vIsCap := v >= 'A' && v <= 'Z' + vIsLow := v >= 'a' && v <= 'z' + if capNext { + if vIsLow { + v += 'A' + v -= 'a' + } + } else if i == 0 { + if vIsCap { + v += 'a' + v -= 'A' + } + } + if vIsCap || vIsLow { + n.WriteByte(v) + capNext = false + } else if vIsNum := v >= '0' && v <= '9'; vIsNum { + n.WriteByte(v) + capNext = true + } else { + capNext = v == '_' || v == ' ' || v == '-' || v == '.' + if v == '.' { + n.WriteByte(v) + } + } + } + return n.String() } +var Conf Configuration + func Load(configFile string) Configuration { - var conf Configuration - viper.SetConfigName("rdpgw") - viper.SetConfigFile(configFile) - viper.AddConfigPath(".") - viper.SetEnvPrefix("RDPGW") - viper.AutomaticEnv() + var k = koanf.New(".") - if err := viper.ReadInConfig(); err != nil { - log.Fatalf("No config file found (%s)", err) - } + k.Load(confmap.Provider(map[string]interface{}{ + "Server.CertFile": "server.pem", + "Server.KeyFile": "key.pem", + "Server.TlsDisabled": false, + "Server.Port": 443, + "Server.SessionStore": "cookie", + "Client.NetworkAutoDetect": 1, + "Client.BandwidthAutoDetect": 1, + "Security.VerifyClientIp": true, + "Caps.TokenAuth": true, + }, "."), nil) - if err := viper.Unmarshal(&conf); err != nil { - log.Fatalf("Cannot unmarshal the config file; %s", err) + if err := k.Load(file.Provider(configFile), yaml.Parser()); err != nil { + log.Fatalf("Error loading config from file: %v", err) } - if len(conf.Security.PAATokenSigningKey) < 32 { - log.Fatalf("Token signing key not long enough") + if err := k.Load(env.ProviderWithValue("RDPGW_", ".", func(s string, v string) (string, interface{}) { + key := strings.Replace(strings.ToLower(strings.TrimPrefix(s, "RDPGW_")), "__", ".", -1) + key = ToCamel(key) + return key, v + }), nil); err != nil { + log.Fatalf("Error loading config from file: %v", err) } - return conf -} + koanfTag := koanf.UnmarshalConf{Tag: "koanf"} + k.UnmarshalWithConf("Server", &Conf.Server, koanfTag) + k.UnmarshalWithConf("OpenId", &Conf.OpenId, koanfTag) + k.UnmarshalWithConf("Caps", &Conf.Caps, koanfTag) + k.UnmarshalWithConf("Security", &Conf.Security, koanfTag) + k.UnmarshalWithConf("Client", &Conf.Client, koanfTag) + + return Conf + +} \ No newline at end of file diff --git a/go.mod b/go.mod index e6800722a84c3d2cf3351e6ff73910faff681d04..939e2bd5a918d283bf178323e7e15d41ba056655 100644 --- a/go.mod +++ b/go.mod @@ -7,10 +7,10 @@ require ( github.com/go-jose/go-jose/v3 v3.0.0 github.com/gorilla/sessions v1.2.1 github.com/gorilla/websocket v1.5.0 + github.com/knadh/koanf v1.4.2 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/prometheus/client_golang v1.12.1 github.com/spf13/cobra v1.5.0 - github.com/spf13/viper v1.12.0 golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c ) @@ -20,29 +20,21 @@ require ( github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/gorilla/securecookie v1.1.1 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/magiconair/properties v1.8.6 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect - github.com/spf13/afero v1.8.2 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/subosito/gotenv v1.3.0 // indirect + github.com/stretchr/testify v1.7.1 // indirect golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect - golang.org/x/text v0.3.7 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.0 // indirect - gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0 // indirect )