mirror of
https://github.com/fosrl/pangolin.git
synced 2025-06-30 09:04:48 +02:00
217 lines
6.1 KiB
Go
217 lines
6.1 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
"os"
|
||
|
|
||
|
"gopkg.in/yaml.v3"
|
||
|
)
|
||
|
|
||
|
// TraefikConfig represents the structure of the main Traefik configuration
|
||
|
type TraefikConfig struct {
|
||
|
Experimental struct {
|
||
|
Plugins struct {
|
||
|
Badger struct {
|
||
|
Version string `yaml:"version"`
|
||
|
} `yaml:"badger"`
|
||
|
} `yaml:"plugins"`
|
||
|
} `yaml:"experimental"`
|
||
|
CertificatesResolvers struct {
|
||
|
LetsEncrypt struct {
|
||
|
Acme struct {
|
||
|
Email string `yaml:"email"`
|
||
|
} `yaml:"acme"`
|
||
|
} `yaml:"letsencrypt"`
|
||
|
} `yaml:"certificatesResolvers"`
|
||
|
}
|
||
|
|
||
|
// DynamicConfig represents the structure of the dynamic configuration
|
||
|
type DynamicConfig struct {
|
||
|
HTTP struct {
|
||
|
Routers map[string]struct {
|
||
|
Rule string `yaml:"rule"`
|
||
|
} `yaml:"routers"`
|
||
|
} `yaml:"http"`
|
||
|
}
|
||
|
|
||
|
// ConfigValues holds the extracted configuration values
|
||
|
type ConfigValues struct {
|
||
|
DashboardDomain string
|
||
|
LetsEncryptEmail string
|
||
|
BadgerVersion string
|
||
|
}
|
||
|
|
||
|
// ReadTraefikConfig reads and extracts values from Traefik configuration files
|
||
|
func ReadTraefikConfig(mainConfigPath, dynamicConfigPath string) (*ConfigValues, error) {
|
||
|
// Read main config file
|
||
|
mainConfigData, err := os.ReadFile(mainConfigPath)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("error reading main config file: %w", err)
|
||
|
}
|
||
|
|
||
|
var mainConfig TraefikConfig
|
||
|
if err := yaml.Unmarshal(mainConfigData, &mainConfig); err != nil {
|
||
|
return nil, fmt.Errorf("error parsing main config file: %w", err)
|
||
|
}
|
||
|
|
||
|
// Read dynamic config file
|
||
|
dynamicConfigData, err := os.ReadFile(dynamicConfigPath)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("error reading dynamic config file: %w", err)
|
||
|
}
|
||
|
|
||
|
var dynamicConfig DynamicConfig
|
||
|
if err := yaml.Unmarshal(dynamicConfigData, &dynamicConfig); err != nil {
|
||
|
return nil, fmt.Errorf("error parsing dynamic config file: %w", err)
|
||
|
}
|
||
|
|
||
|
// Extract values
|
||
|
values := &ConfigValues{
|
||
|
BadgerVersion: mainConfig.Experimental.Plugins.Badger.Version,
|
||
|
LetsEncryptEmail: mainConfig.CertificatesResolvers.LetsEncrypt.Acme.Email,
|
||
|
}
|
||
|
|
||
|
// Extract DashboardDomain from router rules
|
||
|
// Look for it in the main router rules
|
||
|
for _, router := range dynamicConfig.HTTP.Routers {
|
||
|
if router.Rule != "" {
|
||
|
// Extract domain from Host(`mydomain.com`)
|
||
|
if domain := extractDomainFromRule(router.Rule); domain != "" {
|
||
|
values.DashboardDomain = domain
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return values, nil
|
||
|
}
|
||
|
|
||
|
// extractDomainFromRule extracts the domain from a router rule
|
||
|
func extractDomainFromRule(rule string) string {
|
||
|
// Look for the Host(`mydomain.com`) pattern
|
||
|
if start := findPattern(rule, "Host(`"); start != -1 {
|
||
|
end := findPattern(rule[start:], "`)")
|
||
|
if end != -1 {
|
||
|
return rule[start+6 : start+end]
|
||
|
}
|
||
|
}
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
// findPattern finds the start of a pattern in a string
|
||
|
func findPattern(s, pattern string) int {
|
||
|
return bytes.Index([]byte(s), []byte(pattern))
|
||
|
}
|
||
|
|
||
|
type Volume string
|
||
|
type Port string
|
||
|
type Expose string
|
||
|
|
||
|
type HealthCheck struct {
|
||
|
Test []string `yaml:"test,omitempty"`
|
||
|
Interval string `yaml:"interval,omitempty"`
|
||
|
Timeout string `yaml:"timeout,omitempty"`
|
||
|
Retries int `yaml:"retries,omitempty"`
|
||
|
}
|
||
|
|
||
|
type DependsOnCondition struct {
|
||
|
Condition string `yaml:"condition,omitempty"`
|
||
|
}
|
||
|
|
||
|
type Service struct {
|
||
|
Image string `yaml:"image,omitempty"`
|
||
|
ContainerName string `yaml:"container_name,omitempty"`
|
||
|
Environment map[string]string `yaml:"environment,omitempty"`
|
||
|
HealthCheck *HealthCheck `yaml:"healthcheck,omitempty"`
|
||
|
DependsOn map[string]DependsOnCondition `yaml:"depends_on,omitempty"`
|
||
|
Labels []string `yaml:"labels,omitempty"`
|
||
|
Volumes []Volume `yaml:"volumes,omitempty"`
|
||
|
Ports []Port `yaml:"ports,omitempty"`
|
||
|
Expose []Expose `yaml:"expose,omitempty"`
|
||
|
Restart string `yaml:"restart,omitempty"`
|
||
|
Command interface{} `yaml:"command,omitempty"`
|
||
|
NetworkMode string `yaml:"network_mode,omitempty"`
|
||
|
CapAdd []string `yaml:"cap_add,omitempty"`
|
||
|
}
|
||
|
|
||
|
type Network struct {
|
||
|
Driver string `yaml:"driver,omitempty"`
|
||
|
Name string `yaml:"name,omitempty"`
|
||
|
}
|
||
|
|
||
|
type DockerConfig struct {
|
||
|
Version string `yaml:"version,omitempty"`
|
||
|
Services map[string]Service `yaml:"services"`
|
||
|
Networks map[string]Network `yaml:"networks,omitempty"`
|
||
|
}
|
||
|
|
||
|
func AddCrowdSecService(configPath string) error {
|
||
|
// Read existing config
|
||
|
data, err := os.ReadFile(configPath)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Parse existing config
|
||
|
var config DockerConfig
|
||
|
if err := yaml.Unmarshal(data, &config); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Create CrowdSec service
|
||
|
crowdsecService := Service{
|
||
|
Image: "crowdsecurity/crowdsec:latest",
|
||
|
ContainerName: "crowdsec",
|
||
|
Environment: map[string]string{
|
||
|
"GID": "${GID-1000}",
|
||
|
"COLLECTIONS": "crowdsecurity/traefik crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-generic-rules",
|
||
|
"ENROLL_INSTANCE_NAME": "pangolin-crowdsec",
|
||
|
"PARSERS": "crowdsecurity/whitelists",
|
||
|
"ACQUIRE_FILES": "/var/log/traefik/*.log",
|
||
|
"ENROLL_TAGS": "docker",
|
||
|
},
|
||
|
HealthCheck: &HealthCheck{
|
||
|
Test: []string{"CMD", "cscli", "capi", "status"},
|
||
|
},
|
||
|
DependsOn: map[string]DependsOnCondition{
|
||
|
"gerbil": {},
|
||
|
},
|
||
|
Labels: []string{"traefik.enable=false"},
|
||
|
Volumes: []Volume{
|
||
|
"./config/crowdsec:/etc/crowdsec",
|
||
|
"./config/crowdsec/db:/var/lib/crowdsec/data",
|
||
|
"./config/crowdsec_logs/auth.log:/var/log/auth.log:ro",
|
||
|
"./config/crowdsec_logs/syslog:/var/log/syslog:ro",
|
||
|
"./config/crowdsec_logs:/var/log",
|
||
|
"./config/traefik/logs:/var/log/traefik",
|
||
|
},
|
||
|
Ports: []Port{
|
||
|
"9090:9090",
|
||
|
"6060:6060",
|
||
|
},
|
||
|
Expose: []Expose{
|
||
|
"9090",
|
||
|
"6060",
|
||
|
"7422",
|
||
|
},
|
||
|
Restart: "unless-stopped",
|
||
|
Command: "-t",
|
||
|
}
|
||
|
|
||
|
// Add CrowdSec service to config
|
||
|
if config.Services == nil {
|
||
|
config.Services = make(map[string]Service)
|
||
|
}
|
||
|
config.Services["crowdsec"] = crowdsecService
|
||
|
|
||
|
// Marshal config with better formatting
|
||
|
yamlData, err := yaml.Marshal(&config)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Write config back to file
|
||
|
return os.WriteFile(configPath, yamlData, 0644)
|
||
|
}
|