diff --git a/config/auth.go b/config/auth.go new file mode 100644 index 0000000000000000000000000000000000000000..c03fe9b030452bddef467edfb1eb7be47614c53e --- /dev/null +++ b/config/auth.go @@ -0,0 +1,63 @@ +/* + * Copyright (c) KylinSoft Co., Ltd. 2025.All rights reserved. + * PilotGo-plugin-topology licensed under the Mulan Permissive Software License, Version 2. + * See LICENSE file for more details. + * Author: waneng + */ +package config + +import ( + "errors" + "fmt" + "os" + + auth "github.com/abbot/go-http-auth" + shttp "github.com/skydive-project/skydive/graffiti/http" +) + +// NewAuthenticationBackendByName creates a new auth backend based on the name +func NewAuthenticationBackendByName(name string) (backend shttp.AuthenticationBackend, err error) { + typ := GetString("auth." + name + ".type") + switch typ { + case "basic": + role := GetString("auth." + name + ".role") + if role == "" { + role = shttp.DefaultUserRole + } + + var provider auth.SecretProvider + if file := GetString("auth." + name + ".file"); file != "" { + if _, err := os.Stat(file); err != nil { + return nil, err + } + + provider = auth.HtpasswdFileProvider(file) + } else if users := GetStringMapString("auth." + name + ".users"); users != nil && len(users) > 0 { + provider = shttp.NewHtpasswdMapProvider(users).SecretProvider() + } else { + return nil, errors.New("No htpassword provider set, you set either file or inline sections") + } + + backend, err = shttp.NewBasicAuthenticationBackend(name, provider, role) + case "keystone": + authURL := GetString("auth." + name + ".auth_url") + domain := GetString("auth." + name + ".domain_name") + tenant := GetString("auth." + name + ".tenant_name") + + role := GetString("auth." + name + ".role") + if role == "" { + role = shttp.DefaultUserRole + } + + backend, err = shttp.NewKeystoneBackend(name, authURL, tenant, domain, role) + case "noauth": + backend = shttp.NewNoAuthenticationBackend() + default: + err = fmt.Errorf("Authentication type unknown or backend not defined for: %s", name) + } + + if err != nil { + return nil, err + } + return backend, nil +} diff --git a/config/config.go b/config/config.go index 175c3f858d998a77923455283ddbad2cb5b5d133..571fd99222e81d09bf417541b1ee6f4f84113bdc 100644 --- a/config/config.go +++ b/config/config.go @@ -1,18 +1,8 @@ /* - * Copyright (C) 2015 Red Hat, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy ofthe License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specificlanguage governing permissions and - * limitations under the License. - * + * Copyright (c) KylinSoft Co., Ltd. 2025.All rights reserved. + * PilotGo-plugin-topology licensed under the Mulan Permissive Software License, Version 2. + * See LICENSE file for more details. + * Author: waneng */ package config diff --git a/config/config_test.go b/config/config_test.go index 8de58d600950bd12dd9577beef7d29b1e3054baa..05205678fcfd2a6c6dac4568023e9b7d6b1f732a 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -1,20 +1,3 @@ -/* - * Copyright (C) 2018 Red Hat, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy ofthe License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specificlanguage governing permissions and - * limitations under the License. - * - */ - package config import ( diff --git a/config/logging.go b/config/logging.go new file mode 100644 index 0000000000000000000000000000000000000000..6e2aca8fe674228e84bf1a11b96a8c7315f2e5d7 --- /dev/null +++ b/config/logging.go @@ -0,0 +1,65 @@ +/* + * Copyright (c) KylinSoft Co., Ltd. 2025.All rights reserved. + * PilotGo-plugin-topology licensed under the Mulan Permissive Software License, Version 2. + * See LICENSE file for more details. + * Author: waneng + */ +package config + +import ( + "fmt" + "os" + + "github.com/skydive-project/skydive/graffiti/logging" +) + +// InitLogging set up logging based on the section "logging" of +// the configuration file +func InitLogging() error { + color := GetBool("logging.color") + id := GetString("host_id") + ":" + GetString("logging.id") + defaultEncoder := cfg.GetString("logging.encoder") + defaultLogLevel := cfg.GetString("logging.level") + + var err error + var backend logging.Backend + var loggers []*logging.LoggerConfig + for _, name := range cfg.GetStringSlice("logging.backends") { + switch name { + case "file": + filename := cfg.GetString("logging.file.path") + backend, err = logging.NewFileBackend(filename) + if err != nil { + return err + } + case "syslog": + syslogTag := cfg.GetString("logging.syslog.tag") + protocol := cfg.GetString("logging.syslog.protocol") + addr := cfg.GetString("logging.syslog.address") + backend, err = logging.NewSyslogBackend(protocol, addr, syslogTag) + if err != nil { + return err + } + case "stderr": + backend = logging.NewStdioBackend(os.Stderr) + case "stdout": + backend = logging.NewStdioBackend(os.Stdout) + default: + return fmt.Errorf("Invalid logging backend: %s", name) + } + + prefix := "logging." + name + encoder := defaultEncoder + logLevel := defaultLogLevel + if e := cfg.GetString(prefix + ".encoder"); e != "" { + encoder = e + } + if l := cfg.GetString(prefix + ".level"); l != "" { + logLevel = l + } + + loggers = append(loggers, logging.NewLoggerConfig(backend, logLevel, encoder)) + } + + return logging.InitLogging(id, color, loggers) +} diff --git a/config/rbac.go b/config/rbac.go new file mode 100644 index 0000000000000000000000000000000000000000..14244f46e259c4614428cbfd305ca8756b553202 --- /dev/null +++ b/config/rbac.go @@ -0,0 +1,113 @@ +/* + * Copyright (c) KylinSoft Co., Ltd. 2025.All rights reserved. + * PilotGo-plugin-topology licensed under the Mulan Permissive Software License, Version 2. + * See LICENSE file for more details. + * Author: waneng + */ +package config + +import ( + "bufio" + "bytes" + "io" + "strconv" + "strings" + + "github.com/casbin/casbin/log" + "github.com/casbin/casbin/model" + "github.com/casbin/casbin/persist" + etcd "go.etcd.io/etcd/client/v2" + + "github.com/skydive-project/skydive/graffiti/logging" + "github.com/skydive-project/skydive/graffiti/rbac" + "github.com/skydive-project/skydive/statics" +) + +type logger struct { + enabled bool +} + +func (l *logger) EnableLog(state bool) { + l.enabled = state +} + +func (l *logger) IsEnabled() bool { + return l.enabled +} + +func (l *logger) Print(args ...interface{}) { + logging.GetLogger().Debug(args...) +} + +func (l *logger) Printf(fmt string, args ...interface{}) { + logging.GetLogger().Debugf(fmt, args...) +} + +func loadSection(model model.Model, key string, sec string) { + getKey := func(i int) string { + if i == 0 { + return sec + } + return sec + strconv.Itoa(i) + } + + entries := GetStringSlice("rbac.model." + key) + for i, entry := range entries { + model.AddDef(sec, getKey(i), entry) + } +} + +func loadConfigPolicy(model model.Model) { + policies := GetStringSlice("rbac.policy") + for _, line := range policies { + persist.LoadPolicyLine(line, model) + } +} + +func loadPolicy(content []byte, model model.Model) error { + buf := bufio.NewReader(bytes.NewReader([]byte(content))) + for { + line, err := buf.ReadString('\n') + line = strings.TrimSpace(line) + persist.LoadPolicyLine(line, model) + if err != nil { + if err == io.EOF { + return nil + } + return err + } + } +} + +func loadStaticPolicy(model model.Model) error { + content, err := statics.Asset("rbac/policy.csv") + if err != nil { + return err + } + + return loadPolicy(content, model) +} + +// InitRBAC inits the RBAC mechanism. It load +// - the model from the configuration +// - a policy on etcd +// - a policy bundled in the executable +// - additional policy rules from the configuration +func InitRBAC(kapi etcd.KeysAPI) error { + log.SetLogger(&logger{enabled: true}) + + m := model.Model{} + loadSection(m, "request_definition", "r") + loadSection(m, "policy_definition", "p") + loadSection(m, "policy_effect", "e") + loadSection(m, "matchers", "m") + loadSection(m, "role_definition", "g") + + return rbac.Init(m, kapi, func(m model.Model) error { + if err := loadStaticPolicy(m); err != nil { + return err + } + loadConfigPolicy(m) + return nil + }) +} diff --git a/config/tls.go b/config/tls.go new file mode 100644 index 0000000000000000000000000000000000000000..0d80414e98e456fb0076098cb4a4d786ca2d327d --- /dev/null +++ b/config/tls.go @@ -0,0 +1,59 @@ +/* + * Copyright (c) KylinSoft Co., Ltd. 2025.All rights reserved. + * PilotGo-plugin-topology licensed under the Mulan Permissive Software License, Version 2. + * See LICENSE file for more details. + * Author: waneng + */ +package config + +import ( + "crypto/tls" + + gtls "github.com/skydive-project/skydive/graffiti/tls" +) + +// GetTLSClientConfig returns TLS config to be used by client +func GetTLSClientConfig(setupRootCA bool) (*tls.Config, error) { + certPEM := GetString("tls.client_cert") + keyPEM := GetString("tls.client_key") + var tlsConfig *tls.Config + if certPEM != "" && keyPEM != "" { + var err error + tlsConfig, err = gtls.SetupTLSClientConfig(certPEM, keyPEM) + if err != nil { + return nil, err + } + if setupRootCA { + rootCaPEM := GetString("tls.ca_cert") + tlsConfig.RootCAs, err = gtls.SetupTLSLoadCA(rootCaPEM) + if err != nil { + return nil, err + } + } + } + return tlsConfig, nil +} + +// GetTLSServerConfig returns TLS config to be used by server +func GetTLSServerConfig(setupRootCA bool) (*tls.Config, error) { + certPEM := GetString("tls.server_cert") + keyPEM := GetString("tls.server_key") + + tlsConfig, err := gtls.SetupTLSServerConfig(certPEM, keyPEM) + if err != nil { + return nil, err + } + + if GetBool("tls.require_client_cert") { + tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert + } + + if setupRootCA { + rootCaPEM := GetString("tls.ca_cert") + tlsConfig.ClientCAs, err = gtls.SetupTLSLoadCA(rootCaPEM) + if err != nil { + return nil, err + } + } + return tlsConfig, nil +} diff --git a/config/websocket.go b/config/websocket.go new file mode 100644 index 0000000000000000000000000000000000000000..bbbc2255eb0262a76ccff6c64392dda5c3a56721 --- /dev/null +++ b/config/websocket.go @@ -0,0 +1,60 @@ +/* + * Copyright (c) KylinSoft Co., Ltd. 2025.All rights reserved. + * PilotGo-plugin-topology licensed under the Mulan Permissive Software License, Version 2. + * See LICENSE file for more details. + * Author: waneng + */ +package config + +import ( + "net/http" + "net/url" + "time" + + shttp "github.com/skydive-project/skydive/graffiti/http" + "github.com/skydive-project/skydive/graffiti/service" + "github.com/skydive-project/skydive/graffiti/websocket" +) + +// NewWSClientOpts creates WebSocket options object from the configuration +func NewWSClientOpts(authOpts *shttp.AuthenticationOpts) (*websocket.ClientOpts, error) { + // override some of the options with config value + tlsConfig, err := GetTLSClientConfig(true) + if err != nil { + return nil, err + } + + return &websocket.ClientOpts{ + QueueSize: GetInt("http.ws.queue_size"), + WriteCompression: GetBool("http.ws.enable_write_compression"), + TLSConfig: tlsConfig, + AuthOpts: authOpts, + Headers: http.Header{}, + }, nil +} + +// NewWSClient creates a Client based on the configuration +func NewWSClient(clientType service.Type, url *url.URL, opts websocket.ClientOpts) (*websocket.Client, error) { + host := GetString("host_id") + + return websocket.NewClient(host, clientType, url, opts), nil +} + +// NewWSServerOpts returns WebSocket server options +func NewWSServerOpts() websocket.ServerOpts { + pingDelay := time.Duration(GetInt("http.ws.ping_delay")) * time.Second + + return websocket.ServerOpts{ + WriteCompression: GetBool("http.ws.enable_write_compression"), + QueueSize: GetInt("http.ws.queue_size"), + PingDelay: pingDelay, + PongTimeout: time.Duration(GetInt("http.ws.pong_timeout"))*time.Second + pingDelay, + } +} + +// NewWSServer creates a Server based on the configuration +func NewWSServer(server *shttp.Server, endpoint string, authBackend shttp.AuthenticationBackend) *websocket.Server { + opts := NewWSServerOpts() + opts.AuthBackend = authBackend + return websocket.NewServer(server, endpoint, opts) +}