626 lines
19 KiB
Go
626 lines
19 KiB
Go
package main
|
|
|
|
import "crypto/rsa"
|
|
import "crypto/tls"
|
|
import "crypto/x509"
|
|
import "encoding/base64"
|
|
import "encoding/pem"
|
|
import "fmt"
|
|
import "hodu"
|
|
import "net/netip"
|
|
import "os"
|
|
import "regexp"
|
|
import "strings"
|
|
import "time"
|
|
|
|
//import "gopkg.in/yaml.v3"
|
|
import yaml "github.com/goccy/go-yaml"
|
|
|
|
|
|
type ServerRptyConfig struct {
|
|
ClientToken struct {
|
|
Protection string `yaml:"protection"`
|
|
TokenRsaKeyText string `yaml:"token-rsa-key-text"`
|
|
TokenRsaKeyFile string `yaml:"token-rsa-key-file"`
|
|
TokenTtl string `yaml:"token-ttl"`
|
|
} `yaml:"client-token"`
|
|
}
|
|
|
|
type ServerTLSSNIConfig struct {
|
|
Name string `yaml:"name-regex"`
|
|
CertFile string `yaml:"cert-file"`
|
|
KeyFile string `yaml:"key-file"`
|
|
CertText string `yaml:"cert-text"`
|
|
KeyText string `yaml:"key-text"`
|
|
}
|
|
|
|
type ServerTLSConfig struct {
|
|
Enabled bool `yaml:"enabled"`
|
|
CertFile string `yaml:"cert-file"`
|
|
KeyFile string `yaml:"key-file"`
|
|
CertText string `yaml:"cert-text"`
|
|
KeyText string `yaml:"key-text"`
|
|
|
|
ClientAuthType string `yaml:"client-auth-type"`
|
|
ClientCACertFile string `yaml:"client-ca-cert-file"`
|
|
ClientCACertText string `yaml:"client-ca-cert-text"`
|
|
//CipherSuites []Cipher `yaml:"cipher_suites"`
|
|
//CurvePreferences []Curve `yaml:"curve_preferences"`
|
|
//MinVersion TLSVersion `yaml:"min-version"`
|
|
//MaxVersion TLSVersion `yaml:"max-version"`
|
|
//PreferServerCipherSuites bool `yaml:"prefer-server-cipher-suites"`
|
|
//ClientAllowedSans []string `yaml:"client-allowed-sans"`
|
|
|
|
SNIs []ServerTLSSNIConfig `yaml:"snis"`
|
|
}
|
|
|
|
type ClientTLSConfig struct {
|
|
// The Enabled field doesnt indicate using or not using tls.
|
|
// It expresses the rest of items are applied or not in tls configuration.
|
|
Enabled bool `yaml:"enabled"`
|
|
CertFile string `yaml:"cert-file"`
|
|
KeyFile string `yaml:"key-file"`
|
|
CertText string `yaml:"cert-text"`
|
|
KeyText string `yaml:"key-text"`
|
|
ServerCACertFile string `yaml:"server-ca-cert-file"`
|
|
ServerCACertText string `yaml:"server-ca-cert-text"`
|
|
InsecureSkipVerify bool `yaml:"skip-verify"`
|
|
ServerName string `yaml:"server-name"`
|
|
}
|
|
|
|
type HttpAccessRule struct {
|
|
Prefix string `yaml:"prefix"`
|
|
OrgNets []string `yaml:"origin-networks"`
|
|
Action string `yaml:"action"`
|
|
}
|
|
|
|
type HttpAuthConfig struct {
|
|
Enabled bool `yaml:"enabled"`
|
|
Realm string `yaml:"realm"`
|
|
Creds []string `yaml:"credentials"`
|
|
TokenTtl string `yaml:"token-ttl"`
|
|
TokenRsaKeyText string `yaml:"token-rsa-key-text"`
|
|
TokenRsaKeyFile string `yaml:"token-rsa-key-file"`
|
|
AccessRules []HttpAccessRule `yaml:"access-rules"`
|
|
}
|
|
|
|
type CTLServiceConfig struct {
|
|
Prefix string `yaml:"prefix"` // url prefix for control channel endpoints
|
|
Addrs []string `yaml:"addresses"`
|
|
Cors bool `yaml:"cors"`
|
|
Auth HttpAuthConfig `yaml:"auth"`
|
|
}
|
|
|
|
type ECTServiceConfig struct {
|
|
Addrs []string `yaml:"addresses"`
|
|
Auth HttpAuthConfig `yaml:"auth"`
|
|
}
|
|
|
|
type RPXServiceConfig struct {
|
|
Addrs []string `yaml:"addresses"`
|
|
}
|
|
|
|
type RPXClientTokenConfig struct {
|
|
AttrName string `yaml:"attr-name"`
|
|
Regex string `yaml:"regex"`
|
|
SubmatchIndex int `yaml:"submatch-index"`
|
|
|
|
Protection string `yaml:"protection"`
|
|
TokenRsaKeyText string `yaml:"token-rsa-key-text"`
|
|
TokenRsaKeyFile string `yaml:"token-rsa-key-file"`
|
|
TokenTtl string `yaml:"token-ttl"`
|
|
}
|
|
|
|
type PXYServiceConfig struct {
|
|
Addrs []string `yaml:"addresses"`
|
|
Auth HttpAuthConfig `yaml:"auth"`
|
|
}
|
|
|
|
type WPXServiceConfig struct {
|
|
Addrs []string `yaml:"addresses"`
|
|
Auth HttpAuthConfig `yaml:"auth"`
|
|
}
|
|
|
|
type RPCServiceConfig struct { // rpc server-side configuration
|
|
Addrs []string `yaml:"addresses"`
|
|
}
|
|
|
|
type RPCEndpointConfig struct { // rpc client-side configuration
|
|
Authority string `yaml:"authority"`
|
|
Addrs []string `yaml:"addresses"`
|
|
PingIntvl time.Duration `yaml:"ping-interval"`
|
|
PingTmout time.Duration `yaml:"ping-timeout"`
|
|
SeedTmout time.Duration `yaml:"seed-timeout"`
|
|
}
|
|
|
|
type ServerAppConfig struct {
|
|
LogMask []string `yaml:"log-mask"`
|
|
LogFile string `yaml:"log-file"`
|
|
LogMaxSize int64 `yaml:"log-max-size"`
|
|
LogRotate int `yaml:"log-rotate"`
|
|
MaxPeers int `yaml:"max-peer-conns"` // maximum number of connections from peers
|
|
MaxRpcConns int `yaml:"max-rpc-conns"` // maximum number of rpc connections
|
|
MinRpcPingIntvl time.Duration `yaml:"min-rpc-ping-interval"`
|
|
RxcDoneJobRetention *time.Duration `yaml:"rxc-done-job-retention"`
|
|
RxcRunOutputMax *int `yaml:"rxc-run-output-max"`
|
|
HttpReadHeaderTimeout time.Duration `yaml:"http-read-header-timeout"`
|
|
HttpIdleTimeout time.Duration `yaml:"http-idle-timeout"`
|
|
HttpMaxHeaderBytes int `yaml:"http-max-header-bytes"`
|
|
PtyUser string `yaml:"pty-user"`
|
|
PtyShell string `yaml:"pty-shell"`
|
|
XtermHtmlFile string `yaml:"xterm-html-file"`
|
|
}
|
|
|
|
type ClientAppConfig struct {
|
|
LogMask []string `yaml:"log-mask"`
|
|
LogFile string `yaml:"log-file"`
|
|
LogMaxSize int64 `yaml:"log-max-size"`
|
|
LogRotate int `yaml:"log-rotate"`
|
|
MaxPeers int `yaml:"max-peer-conns"` // maximum number of connections from peers
|
|
MaxRpcConns int `yaml:"max-rpc-conns"` // maximum number of rpc connections
|
|
PeerConnTmout time.Duration `yaml:"peer-conn-timeout"`
|
|
HttpReadHeaderTimeout time.Duration `yaml:"http-read-header-timeout"`
|
|
HttpIdleTimeout time.Duration `yaml:"http-idle-timeout"`
|
|
HttpMaxHeaderBytes int `yaml:"http-max-header-bytes"`
|
|
TokenText string `yaml:"token-text"`
|
|
TokenFile string `yaml:"token-file"`
|
|
PtyUser string `yaml:"pty-user"`
|
|
PtyShell string `yaml:"pty-shell"`
|
|
RxcUser string `yaml:"rxc-user"`
|
|
RxcProfileFiles []string `yaml:"rxc-profile-files"`
|
|
RxcProfileReloadMinInterval *time.Duration `yaml:"rxc-profile-reload-min-interval"`
|
|
RpcPingIntvl time.Duration `yaml:"rpc-ping-interval"`
|
|
RpcPingTmout time.Duration `yaml:"rpc-ping-timeout"`
|
|
RpcSeedTmout time.Duration `yaml:"rpc-seed-timeout"`
|
|
XtermHtmlFile string `yaml:"xterm-html-file"`
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
APP ServerAppConfig `yaml:"app"`
|
|
|
|
CTL struct {
|
|
Service CTLServiceConfig `yaml:"service"`
|
|
TLS ServerTLSConfig `yaml:"tls"`
|
|
Rpty ServerRptyConfig `yaml:"rpty"`
|
|
} `yaml:"ctl"`
|
|
|
|
ECT struct {
|
|
Service ECTServiceConfig `yaml:"service"`
|
|
TLS ServerTLSConfig `yaml:"tls"`
|
|
} `yaml:"ect"`
|
|
|
|
RPX struct {
|
|
Service RPXServiceConfig `yaml:"service"`
|
|
TLS ServerTLSConfig `yaml:"tls"`
|
|
ClientToken RPXClientTokenConfig `yaml:"client-token"`
|
|
} `yaml:"rpx"`
|
|
|
|
PXY struct {
|
|
Service PXYServiceConfig `yaml:"service"`
|
|
TLS ServerTLSConfig `yaml:"tls"`
|
|
Target struct {
|
|
// TODO: This will have to be extended to be an array
|
|
// of configurations to cater for different targets
|
|
// It needs a name or a name pattern field to match the target.
|
|
TLS ClientTLSConfig `yaml:"tls"`
|
|
} `yaml:"target"`
|
|
} `yaml:"pxy"`
|
|
|
|
WPX struct {
|
|
Service WPXServiceConfig `yaml:"service"`
|
|
TLS ServerTLSConfig `yaml:"tls"`
|
|
} `yaml:"wpx"`
|
|
|
|
RPC struct {
|
|
Service RPCServiceConfig `yaml:"service"`
|
|
TLS ServerTLSConfig `yaml:"tls"`
|
|
} `yaml:"rpc"`
|
|
}
|
|
|
|
type ClientConfig struct {
|
|
APP ClientAppConfig `yaml:"app"`
|
|
|
|
CTL struct {
|
|
Service CTLServiceConfig `yaml:"service"`
|
|
TLS ServerTLSConfig `yaml:"tls"`
|
|
} `yaml:"ctl"`
|
|
RPC struct {
|
|
Endpoint RPCEndpointConfig `yaml:"endpoint"`
|
|
TLS ClientTLSConfig `yaml:"tls"`
|
|
} `yaml:"rpc"`
|
|
RPX struct {
|
|
Target struct {
|
|
Addr string `yaml:"address"`
|
|
TLS ClientTLSConfig `yaml:"tls"`
|
|
} `yaml:"target"`
|
|
}
|
|
}
|
|
|
|
func load_server_config_to(cfgfile string, cfg *ServerConfig) error {
|
|
var f *os.File
|
|
var yd *yaml.Decoder
|
|
var err error
|
|
|
|
f, err = os.Open(cfgfile)
|
|
if err != nil { return err }
|
|
|
|
yd = yaml.NewDecoder(f, yaml.AllowDuplicateMapKey(), yaml.DisallowUnknownField())
|
|
err = yd.Decode(cfg)
|
|
f.Close()
|
|
return err
|
|
}
|
|
|
|
func load_client_config_to(cfgfile string, cfg *ClientConfig) error {
|
|
var f *os.File
|
|
var yd *yaml.Decoder
|
|
var err error
|
|
|
|
f, err = os.Open(cfgfile)
|
|
if err != nil { return err }
|
|
|
|
yd = yaml.NewDecoder(f, yaml.AllowDuplicateMapKey(), yaml.DisallowUnknownField())
|
|
err = yd.Decode(cfg)
|
|
f.Close()
|
|
return err
|
|
}
|
|
|
|
|
|
func tls_string_to_client_auth_type(str string) tls.ClientAuthType {
|
|
switch str {
|
|
case tls.NoClientCert.String():
|
|
return tls.NoClientCert
|
|
case tls.RequestClientCert.String():
|
|
return tls.RequestClientCert
|
|
case tls.RequireAnyClientCert.String():
|
|
return tls.RequireAnyClientCert
|
|
case tls.VerifyClientCertIfGiven.String():
|
|
return tls.VerifyClientCertIfGiven
|
|
case tls.RequireAndVerifyClientCert.String():
|
|
return tls.RequireAndVerifyClientCert
|
|
default:
|
|
return tls.NoClientCert
|
|
}
|
|
}
|
|
|
|
func log_strings_to_mask(str []string) hodu.LogMask {
|
|
|
|
var mask hodu.LogMask
|
|
|
|
if len(str) > 0 {
|
|
var name string
|
|
|
|
mask = hodu.LogMask(0)
|
|
for _, name = range str {
|
|
|
|
switch name {
|
|
case "all":
|
|
mask = hodu.LOG_ALL
|
|
|
|
case "none":
|
|
mask = hodu.LOG_NONE
|
|
|
|
case "debug":
|
|
mask |= hodu.LogMask(hodu.LOG_DEBUG)
|
|
case "info":
|
|
mask |= hodu.LogMask(hodu.LOG_INFO)
|
|
case "warn":
|
|
mask |= hodu.LogMask(hodu.LOG_WARN)
|
|
case "error":
|
|
mask |= hodu.LogMask(hodu.LOG_ERROR)
|
|
}
|
|
}
|
|
} else {
|
|
// if not specified, log messages of all levels
|
|
mask = hodu.LOG_ALL
|
|
}
|
|
|
|
return mask
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
type ServerSNICert struct {
|
|
name *regexp.Regexp
|
|
cert tls.Certificate
|
|
}
|
|
|
|
func hostname_wildcard_match(wildcard string, name string) bool {
|
|
var suffix_start int
|
|
var suffix_len int
|
|
var prefix_end int
|
|
var i int
|
|
|
|
// must start with "*."
|
|
if len(wildcard) < 3 || wildcard[0] != '*' || wildcard[1] != '.' { return false }
|
|
|
|
// suffix is everything after "*."
|
|
suffix_start = 1 // points to "."
|
|
suffix_len = len(wildcard) - suffix_start
|
|
|
|
// name must be longer than suffix (so there's at least one label)
|
|
if len(name) <= suffix_len { return false }
|
|
|
|
// check suffix match
|
|
if name[len(name)-suffix_len:] != wildcard[suffix_start:] { return false }
|
|
|
|
// check there is exactly one label before suffix
|
|
prefix_end = len(name) - suffix_len
|
|
if prefix_end == 0 { return false }
|
|
|
|
// ensure no '.' in the prefix (only one label)
|
|
for i = 0; i < prefix_end; i++ {
|
|
if name[i] == '.' { return false }
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func make_tls_server_config(cfg *ServerTLSConfig) (*tls.Config, error) {
|
|
var tlscfg *tls.Config
|
|
|
|
if cfg.Enabled {
|
|
var cert tls.Certificate
|
|
var cert_pool *x509.CertPool
|
|
var sni_certs []ServerSNICert
|
|
var i int
|
|
var ok bool
|
|
var err error
|
|
|
|
for i = range cfg.SNIs {
|
|
var regex *regexp.Regexp
|
|
|
|
if cfg.SNIs[i].CertText != "" && cfg.SNIs[i].KeyText != "" {
|
|
cert, err = tls.X509KeyPair([]byte(cfg.SNIs[i].CertText), []byte(cfg.SNIs[i].KeyText))
|
|
} else if cfg.SNIs[i].CertFile != "" && cfg.SNIs[i].KeyFile != "" {
|
|
cert, err = tls.LoadX509KeyPair(cfg.SNIs[i].CertFile, cfg.SNIs[i].KeyFile)
|
|
} else {
|
|
continue;
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load sni key pair - %s", err.Error())
|
|
}
|
|
|
|
regex, err = regexp.Compile(cfg.SNIs[i].Name)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to compile sni name-regex - %s", err.Error())
|
|
}
|
|
|
|
sni_certs = append(sni_certs, ServerSNICert{name: regex, cert: cert});
|
|
}
|
|
|
|
if cfg.CertText != "" && cfg.KeyText != "" {
|
|
cert, err = tls.X509KeyPair([]byte(cfg.CertText), []byte(cfg.KeyText))
|
|
} else if cfg.CertFile != "" && cfg.KeyFile != "" {
|
|
cert, err = tls.LoadX509KeyPair(cfg.CertFile, cfg.KeyFile)
|
|
} else {
|
|
// use the embedded certificate
|
|
cert, err = tls.X509KeyPair(hodu_tls_cert_text, hodu_tls_key_text)
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load key pair - %s", err.Error())
|
|
}
|
|
|
|
cert_pool = x509.NewCertPool()
|
|
if cfg.ClientCACertText != "" {
|
|
ok = cert_pool.AppendCertsFromPEM([]byte(cfg.ClientCACertText))
|
|
if !ok {
|
|
return nil, fmt.Errorf("failed to append configured certificate text to pool")
|
|
}
|
|
} else if cfg.ClientCACertFile != "" {
|
|
var text []byte
|
|
text, err = os.ReadFile(cfg.ClientCACertFile)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load ca certficate file %s - %s", cfg.ClientCACertFile, err.Error())
|
|
}
|
|
ok = cert_pool.AppendCertsFromPEM(text)
|
|
if !ok {
|
|
return nil, fmt.Errorf("failed to append configured certificate file to pool")
|
|
}
|
|
} else {
|
|
// if the client ca cert is not specified, use the bundled tls cert
|
|
// as a trusted ca cert.
|
|
ok = cert_pool.AppendCertsFromPEM(hodu_tls_cert_text)
|
|
if !ok {
|
|
return nil, fmt.Errorf("failed to append builtin certificate to pool")
|
|
}
|
|
}
|
|
|
|
tlscfg = &tls.Config{
|
|
Certificates: []tls.Certificate{cert},
|
|
ClientAuth: tls_string_to_client_auth_type(cfg.ClientAuthType),
|
|
ClientCAs: cert_pool, // trusted CA certs for client certificate verification
|
|
}
|
|
|
|
if (len(sni_certs) > 0) {
|
|
tlscfg.GetCertificate = func (chi *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
|
var server_name string
|
|
var x int
|
|
|
|
server_name = strings.TrimSuffix(strings.ToLower(chi.ServerName), ".")
|
|
if server_name == "" { return nil, nil }
|
|
|
|
for x = range sni_certs {
|
|
if sni_certs[x].name.MatchString(server_name) {
|
|
return &sni_certs[x].cert, nil
|
|
}
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return tlscfg, nil
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
func make_tls_client_config(cfg *ClientTLSConfig) (*tls.Config, error) {
|
|
var tlscfg *tls.Config
|
|
|
|
if cfg.Enabled {
|
|
var cert tls.Certificate
|
|
var cert_pool *x509.CertPool
|
|
var ok bool
|
|
var err error
|
|
|
|
if cfg.CertText != "" && cfg.KeyText != "" {
|
|
cert, err = tls.X509KeyPair([]byte(cfg.CertText), []byte(cfg.KeyText))
|
|
} else if cfg.CertFile != "" && cfg.KeyFile != "" {
|
|
cert, err = tls.LoadX509KeyPair(cfg.CertFile, cfg.KeyFile)
|
|
} else {
|
|
// use the embedded certificate
|
|
cert, err = tls.X509KeyPair(hodu_tls_cert_text, hodu_tls_key_text)
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load key pair - %s", err.Error())
|
|
}
|
|
|
|
cert_pool, err = x509.SystemCertPool()
|
|
if err != nil { cert_pool = x509.NewCertPool() }
|
|
if cfg.ServerCACertText != "" {
|
|
ok = cert_pool.AppendCertsFromPEM([]byte(cfg.ServerCACertText))
|
|
if !ok {
|
|
return nil, fmt.Errorf("failed to append certificate to pool")
|
|
}
|
|
} else if cfg.ServerCACertFile != "" {
|
|
var text []byte
|
|
text, err = os.ReadFile(cfg.ServerCACertFile)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load ca certficate file %s - %s", cfg.ServerCACertFile, err.Error())
|
|
}
|
|
ok = cert_pool.AppendCertsFromPEM(text)
|
|
if !ok {
|
|
return nil, fmt.Errorf("failed to append certificate to pool")
|
|
}
|
|
} else {
|
|
// trust the embedded certificate if not explicitly specified
|
|
ok = cert_pool.AppendCertsFromPEM(hodu_tls_cert_text)
|
|
if !ok {
|
|
return nil, fmt.Errorf("failed to append certificate to pool")
|
|
}
|
|
}
|
|
|
|
if cfg.ServerName == "" { cfg.ServerName = HODU_NAME }
|
|
tlscfg = &tls.Config{
|
|
//Certificates: []tls.Certificate{cert},
|
|
GetClientCertificate: func(cri *tls.CertificateRequestInfo) (*tls.Certificate, error) { return &cert, nil },
|
|
RootCAs: cert_pool,
|
|
InsecureSkipVerify: cfg.InsecureSkipVerify,
|
|
ServerName: cfg.ServerName,
|
|
}
|
|
}
|
|
|
|
return tlscfg, nil
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
func make_rsa_private_key_config(key_text string, key_file string, default_key_text []byte, key_name string) (*rsa.PrivateKey, error) {
|
|
var rsa_key_text []byte
|
|
var rk *rsa.PrivateKey
|
|
var pb *pem.Block
|
|
var b []byte
|
|
var err error
|
|
|
|
if key_text == "" && key_file != "" {
|
|
rsa_key_text, err = os.ReadFile(key_file)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to read %s - %s", key_file, err.Error())
|
|
}
|
|
}
|
|
if len(rsa_key_text) == 0 { rsa_key_text = []byte(key_text) }
|
|
if len(rsa_key_text) == 0 { rsa_key_text = default_key_text }
|
|
if len(rsa_key_text) == 0 { return nil, nil }
|
|
|
|
pb, b = pem.Decode(rsa_key_text)
|
|
if pb == nil || len(b) > 0 {
|
|
return nil, fmt.Errorf("invalid %s text %.32s... - no block or too many blocks", key_name, string(rsa_key_text))
|
|
}
|
|
|
|
rk, err = x509.ParsePKCS1PrivateKey(pb.Bytes)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid %s text %.32s... - %s", key_name, string(rsa_key_text), err.Error())
|
|
}
|
|
|
|
return rk, nil
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
func make_http_auth_config(cfg *HttpAuthConfig) (*hodu.HttpAuthConfig, error) {
|
|
var config hodu.HttpAuthConfig
|
|
var cred string
|
|
var b []byte
|
|
var x []string
|
|
var rk *rsa.PrivateKey
|
|
var rule HttpAccessRule
|
|
var idx int
|
|
var err error
|
|
|
|
config.Enabled = cfg.Enabled
|
|
config.Realm = cfg.Realm
|
|
config.Creds = make(hodu.HttpAuthCredMap)
|
|
config.TokenTtl, err = hodu.ParseDurationString(cfg.TokenTtl)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid token ttl %s - %s", cfg.TokenTtl, err.Error())
|
|
}
|
|
|
|
// convert user credentials
|
|
for _, cred = range cfg.Creds {
|
|
b, err = base64.StdEncoding.DecodeString(cred)
|
|
if err == nil { cred = string(b) }
|
|
|
|
// each entry must be of the form username:password
|
|
x = strings.Split(cred, ":")
|
|
if len(x) != 2 {
|
|
return nil, fmt.Errorf("invalid auth credential - %s", cred)
|
|
}
|
|
|
|
config.Creds[x[0]] = x[1]
|
|
}
|
|
|
|
// load rsa key
|
|
rk, err = make_rsa_private_key_config(cfg.TokenRsaKeyText, cfg.TokenRsaKeyFile, hodu_rsa_key_text, "token rsa key")
|
|
if err != nil { return nil, err }
|
|
config.TokenRsaKey = rk
|
|
|
|
// load access rules
|
|
config.AccessRules = make([]hodu.HttpAccessRule, len(cfg.AccessRules))
|
|
for idx, rule = range cfg.AccessRules {
|
|
var action hodu.HttpAccessAction
|
|
var orgnet string
|
|
var orgnet_idx int
|
|
|
|
if rule.Prefix == "" {
|
|
return nil, fmt.Errorf("blank access rule prefix not allowed")
|
|
}
|
|
|
|
switch strings.ToLower(rule.Action) {
|
|
case "accept":
|
|
action = hodu.HTTP_ACCESS_ACCEPT
|
|
case "reject":
|
|
action = hodu.HTTP_ACCESS_REJECT
|
|
case "auth-required":
|
|
action = hodu.HTTP_ACCESS_AUTH_REQUIRED
|
|
case "cert-required":
|
|
action = hodu.HTTP_ACCESS_CERT_REQUIRED
|
|
default:
|
|
return nil, fmt.Errorf("invalid access rule action %s", rule.Action)
|
|
}
|
|
|
|
config.AccessRules[idx] = hodu.HttpAccessRule{
|
|
Prefix: rule.Prefix,
|
|
Action: action,
|
|
OrgNets: make([]netip.Prefix, len(rule.OrgNets)),
|
|
}
|
|
|
|
for orgnet_idx, orgnet = range rule.OrgNets {
|
|
var netpfx netip.Prefix
|
|
netpfx, err = netip.ParsePrefix(orgnet)
|
|
if err != nil { return nil, fmt.Errorf("invalid network %s - %s", orgnet, err.Error()) }
|
|
config.AccessRules[idx].OrgNets[orgnet_idx] = netpfx
|
|
}
|
|
}
|
|
|
|
return &config, nil
|
|
}
|