This commit implements multi-tenancy. A tenant is defined based on a value from JWT claims. The key of this value must be specified in the configuration file. This allows us to pick different values when using multiple SSOs. Let me explain more in depth how this works: Cloud API gets a new compose request. Firstly, it extracts a tenant name from JWT claims. The considered claims are configured as an array in cloud_api.jwt.tenant_provider_fields in composer's config file. The channel name for all jobs belonging to this compose is created by `"org-" + tenant`. Why is the channel prefixed by "org-"? To give us options in the future. I can imagine the request having a channel override. This basically means that multiple tenants can share a channel. A real use-case for this is multiple Fedora projects sharing one pool of workers. Why this commit adds a whole new cloud_api section to the config? Because the current config is a mess and we should stop adding new stuff into the koji section. As the Koji API is basically deprecated, we will need to remove it soon nevertheless. Signed-off-by: Ondřej Budai <ondrej@budai.cz>
192 lines
5.1 KiB
Go
192 lines
5.1 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"reflect"
|
|
"strconv"
|
|
|
|
"github.com/BurntSushi/toml"
|
|
)
|
|
|
|
type ComposerConfigFile struct {
|
|
Koji KojiAPIConfig `toml:"koji"`
|
|
Worker WorkerAPIConfig `toml:"worker"`
|
|
WeldrAPI WeldrAPIConfig `toml:"weldr_api"`
|
|
LogLevel string `toml:"log_level"`
|
|
LogFormat string `toml:"log_format"`
|
|
}
|
|
|
|
type KojiAPIConfig struct {
|
|
AllowedDomains []string `toml:"allowed_domains"`
|
|
CA string `toml:"ca"`
|
|
EnableTLS bool `toml:"enable_tls"`
|
|
EnableMTLS bool `toml:"enable_mtls"`
|
|
EnableJWT bool `toml:"enable_jwt"`
|
|
JWTKeysURLs []string `toml:"jwt_keys_urls"`
|
|
JWTKeysCA string `toml:"jwt_ca_file"`
|
|
JWTACLFile string `toml:"jwt_acl_file"`
|
|
JWTTenantProviderFields []string `toml:"jwt_tenant_provider_fields"`
|
|
AWS AWSConfig `toml:"aws_config"`
|
|
}
|
|
|
|
type AWSConfig struct {
|
|
Bucket string `toml:"bucket"`
|
|
}
|
|
|
|
type WorkerAPIConfig struct {
|
|
AllowedDomains []string `toml:"allowed_domains"`
|
|
CA string `toml:"ca"`
|
|
RequestJobTimeout string `toml:"request_job_timeout"`
|
|
BasePath string `toml:"base_path"`
|
|
EnableArtifacts bool `toml:"enable_artifacts"`
|
|
PGHost string `toml:"pg_host" env:"PGHOST"`
|
|
PGPort string `toml:"pg_port" env:"PGPORT"`
|
|
PGDatabase string `toml:"pg_database" env:"PGDATABASE"`
|
|
PGUser string `toml:"pg_user" env:"PGUSER"`
|
|
PGPassword string `toml:"pg_password" env:"PGPASSWORD"`
|
|
PGSSLMode string `toml:"pg_ssl_mode" env:"PGSSLMODE"`
|
|
PGMaxConns int `toml:"pg_max_conns" env:"PGMAXCONNS"`
|
|
EnableTLS bool `toml:"enable_tls"`
|
|
EnableMTLS bool `toml:"enable_mtls"`
|
|
EnableJWT bool `toml:"enable_jwt"`
|
|
JWTKeysURLs []string `toml:"jwt_keys_urls"`
|
|
JWTKeysCA string `toml:"jwt_ca_file"`
|
|
JWTACLFile string `toml:"jwt_acl_file"`
|
|
JWTTenantProviderFields []string `toml:"jwt_tenant_provider_fields"`
|
|
}
|
|
|
|
type WeldrAPIConfig struct {
|
|
DistroConfigs map[string]WeldrDistroConfig `toml:"distros"`
|
|
}
|
|
|
|
type WeldrDistroConfig struct {
|
|
ImageTypeDenyList []string `toml:"image_type_denylist"`
|
|
}
|
|
|
|
// weldrDistrosImageTypeDenyList returns a map of distro-specific Image Type
|
|
// deny lists for Weldr API.
|
|
func (c *ComposerConfigFile) weldrDistrosImageTypeDenyList() map[string][]string {
|
|
distrosImageTypeDenyList := map[string][]string{}
|
|
|
|
for distro, distroConfig := range c.WeldrAPI.DistroConfigs {
|
|
if distroConfig.ImageTypeDenyList != nil {
|
|
distrosImageTypeDenyList[distro] = append([]string{}, distroConfig.ImageTypeDenyList...)
|
|
}
|
|
}
|
|
|
|
return distrosImageTypeDenyList
|
|
}
|
|
|
|
// GetDefaultConfig returns the default configuration of osbuild-composer
|
|
// Defaults:
|
|
// - 'azure-rhui', 'ec2', 'ec2-ha', 'ec2-sap' image types on 'rhel-85' are not exposed via Weldr API
|
|
func GetDefaultConfig() *ComposerConfigFile {
|
|
return &ComposerConfigFile{
|
|
Koji: KojiAPIConfig{
|
|
EnableTLS: true,
|
|
EnableMTLS: true,
|
|
EnableJWT: false,
|
|
AWS: AWSConfig{
|
|
Bucket: "image-builder.service",
|
|
},
|
|
},
|
|
Worker: WorkerAPIConfig{
|
|
RequestJobTimeout: "0",
|
|
BasePath: "/api/worker/v1",
|
|
EnableArtifacts: true,
|
|
EnableTLS: true,
|
|
EnableMTLS: true,
|
|
EnableJWT: false,
|
|
},
|
|
WeldrAPI: WeldrAPIConfig{
|
|
map[string]WeldrDistroConfig{
|
|
"rhel-*": {
|
|
ImageTypeDenyList: []string{
|
|
"azure-rhui",
|
|
"ec2",
|
|
"ec2-ha",
|
|
"ec2-sap",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
LogLevel: "info",
|
|
LogFormat: "text",
|
|
}
|
|
}
|
|
|
|
func LoadConfig(name string) (*ComposerConfigFile, error) {
|
|
c := GetDefaultConfig()
|
|
_, err := toml.DecodeFile(name, c)
|
|
if err != nil && !os.IsNotExist(err) {
|
|
return nil, err
|
|
}
|
|
err = loadConfigFromEnv(c)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
func loadConfigFromEnv(intf interface{}) error {
|
|
t := reflect.TypeOf(intf).Elem()
|
|
v := reflect.ValueOf(intf).Elem()
|
|
|
|
for i := 0; i < v.NumField(); i++ {
|
|
fieldT := t.Field(i)
|
|
fieldV := v.Field(i)
|
|
kind := fieldV.Kind()
|
|
|
|
switch kind {
|
|
case reflect.String:
|
|
key, ok := fieldT.Tag.Lookup("env")
|
|
if !ok {
|
|
continue
|
|
}
|
|
confV, ok := os.LookupEnv(key)
|
|
if !ok {
|
|
continue
|
|
}
|
|
fieldV.SetString(confV)
|
|
case reflect.Int:
|
|
key, ok := fieldT.Tag.Lookup("env")
|
|
if !ok {
|
|
continue
|
|
}
|
|
confV, ok := os.LookupEnv(key)
|
|
if !ok {
|
|
continue
|
|
}
|
|
value, err := strconv.ParseInt(confV, 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fieldV.SetInt(value)
|
|
case reflect.Bool:
|
|
// no-op
|
|
continue
|
|
case reflect.Slice:
|
|
// no-op
|
|
continue
|
|
case reflect.Map:
|
|
// no-op
|
|
continue
|
|
case reflect.Struct:
|
|
err := loadConfigFromEnv(fieldV.Addr().Interface())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
return fmt.Errorf("Unsupported type: %s", kind)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DumpConfig(c ComposerConfigFile, w io.Writer) error {
|
|
// sensor sensitive fields
|
|
c.Worker.PGPassword = ""
|
|
return toml.NewEncoder(w).Encode(c)
|
|
}
|