mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-14 05:00:19 +02:00
Add root-level inbound_mtls_profiles combining optional system CAs with PEM CA files, and entrypoint.inbound_mtls_profile to require client certificates on every HTTPS connection. Route-level inbound_mtls_profile is allowed only without a global profile; per-handshake TLS picks ClientCAs from SNI, and requests fail with 421 when Host and SNI would select different mTLS routes. Compile pools at init (SetInboundMTLSProfiles from state.initEntrypoint) and reject unknown profile refs or mixed global-plus-route configuration. Extend config.example.yml and package READMEs; add entrypoint and config tests for TLS mutation, handshakes, and validation.
332 lines
8.5 KiB
Markdown
332 lines
8.5 KiB
Markdown
# internal/config
|
|
|
|
Centralized YAML configuration management with thread-safe state access and provider initialization.
|
|
|
|
## Overview
|
|
|
|
The config package implements the core configuration management system for GoDoxy, handling YAML configuration loading, provider initialization, route loading, and state transitions. It uses atomic pointers for thread-safe state access and integrates all configuration components.
|
|
|
|
### Primary consumers
|
|
|
|
- `cmd/main.go` - Initializes configuration state on startup
|
|
- `internal/route/provider` - Accesses configuration for route creation
|
|
- `internal/api/v1` - Exposes configuration via REST API
|
|
- All packages that need to access active configuration
|
|
|
|
### Non-goals
|
|
|
|
- Dynamic provider registration after initialization (require config reload)
|
|
|
|
### Stability
|
|
|
|
Stable internal package. Public API consists of `State` interface and state management functions.
|
|
|
|
## Public API
|
|
|
|
### Exported types
|
|
|
|
```go
|
|
type Config struct {
|
|
ACL *acl.Config
|
|
AutoCert *autocert.Config
|
|
Entrypoint entrypoint.Config
|
|
InboundMTLSProfiles map[string]types.InboundMTLSProfile
|
|
Providers Providers
|
|
MatchDomains []string
|
|
Homepage homepage.Config
|
|
Defaults Defaults
|
|
TimeoutShutdown int
|
|
}
|
|
|
|
type Providers struct {
|
|
Files []string
|
|
Docker map[string]types.DockerProviderConfig
|
|
Agents []*agent.AgentConfig
|
|
Notification []*notif.NotificationConfig
|
|
Proxmox []proxmox.Config
|
|
MaxMind *maxmind.Config
|
|
}
|
|
```
|
|
|
|
### State interface
|
|
|
|
```go
|
|
type State interface {
|
|
Task() *task.Task
|
|
Context() context.Context
|
|
Value() *Config
|
|
Entrypoint() entrypoint.Entrypoint
|
|
ShortLinkMatcher() config.ShortLinkMatcher
|
|
AutoCertProvider() server.CertProvider
|
|
LoadOrStoreProvider(key string, value types.RouteProvider) (actual types.RouteProvider, loaded bool)
|
|
DeleteProvider(key string)
|
|
IterProviders() iter.Seq2[string, types.RouteProvider]
|
|
StartProviders() error
|
|
NumProviders() int
|
|
|
|
// Lifecycle management
|
|
StartAPIServers()
|
|
StartMetrics()
|
|
|
|
FlushTmpLog()
|
|
}
|
|
```
|
|
|
|
`StartAPIServers` starts the authenticated API from `common.APIHTTPAddr` and, when `LOCAL_API_ADDR` is set, an additional **unauthenticated** local listener from `common.LocalAPIHTTPAddr`. That address is validated for loopback binds (with optional DNS resolution); non-loopback requires `LOCAL_API_ALLOW_NON_LOOPBACK` and logs a warning. See [`internal/api/v1/README.md`](../api/v1/README.md#configuration-surface).
|
|
|
|
### Exported functions
|
|
|
|
```go
|
|
func NewState() config.State
|
|
```
|
|
|
|
Creates a new configuration state with empty providers map.
|
|
|
|
```go
|
|
func GetState() config.State
|
|
```
|
|
|
|
Returns the active configuration state. Thread-safe via atomic load.
|
|
|
|
```go
|
|
func SetState(state config.State)
|
|
```
|
|
|
|
Sets the active configuration state. Also updates active configs for ACL, entrypoint, homepage, and autocert.
|
|
|
|
```go
|
|
func HasState() bool
|
|
```
|
|
|
|
Returns true if a state is currently active.
|
|
|
|
```go
|
|
func Value() *config.Config
|
|
```
|
|
|
|
Returns the current configuration values.
|
|
|
|
```go
|
|
func (state *state) InitFromFile(filename string) error
|
|
```
|
|
|
|
Initializes state from a YAML file. Uses default config if file doesn't exist.
|
|
|
|
```go
|
|
func (state *state) Init(data []byte) error
|
|
```
|
|
|
|
Initializes state from raw YAML data. Validates, then initializes MaxMind, Proxmox, providers, AutoCert, notifications, access logger, and entrypoint.
|
|
|
|
```go
|
|
func (state *state) StartProviders() error
|
|
```
|
|
|
|
Starts all route providers concurrently.
|
|
|
|
```go
|
|
func (state *state) IterProviders() iter.Seq2[string, types.RouteProvider]
|
|
```
|
|
|
|
Returns an iterator over all providers.
|
|
|
|
## Architecture
|
|
|
|
### Core components
|
|
|
|
```mermaid
|
|
graph TD
|
|
A[config.yml] --> B[State]
|
|
B --> C{Initialize}
|
|
C --> D[Validate YAML]
|
|
C --> E[Init MaxMind]
|
|
C --> F[Init Proxmox]
|
|
C --> G[Load Route Providers]
|
|
C --> H[Init AutoCert]
|
|
C --> I[Init Notifications]
|
|
C --> J[Init Entrypoint]
|
|
|
|
K[ActiveConfig] -.-> B
|
|
|
|
subgraph Providers
|
|
G --> L[Docker Provider]
|
|
G --> M[File Provider]
|
|
G --> N[Agent Provider]
|
|
end
|
|
|
|
subgraph State Management
|
|
B --> O[xsync.Map Providers]
|
|
B --> P[Entrypoint]
|
|
B --> Q[AutoCert Provider]
|
|
B --> R[task.Task]
|
|
end
|
|
```
|
|
|
|
### Initialization pipeline
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant YAML
|
|
participant State
|
|
participant MaxMind
|
|
participant Proxmox
|
|
participant Providers
|
|
participant AutoCert
|
|
participant Notif
|
|
participant Entrypoint
|
|
|
|
YAML->>State: Parse & Validate
|
|
par Initialize in parallel
|
|
State->>MaxMind: Initialize
|
|
State->>Proxmox: Initialize
|
|
and
|
|
State->>Providers: Load Route Providers
|
|
Providers->>State: Store Providers
|
|
end
|
|
State->>AutoCert: Initialize
|
|
State->>Notif: Initialize
|
|
State->>Entrypoint: Configure
|
|
State->>State: Start Providers
|
|
```
|
|
|
|
### Thread safety model
|
|
|
|
```go
|
|
var stateMu sync.RWMutex
|
|
|
|
func GetState() config.State {
|
|
return config.ActiveState.Load()
|
|
}
|
|
|
|
func SetState(state config.State) {
|
|
stateMu.Lock()
|
|
defer stateMu.Unlock()
|
|
config.ActiveState.Store(state)
|
|
}
|
|
```
|
|
|
|
Uses `sync.RWMutex` for write synchronization and `sync/atomic` for read operations.
|
|
|
|
## Configuration Surface
|
|
|
|
### Config sources
|
|
|
|
Configuration is loaded from `config/config.yml`.
|
|
|
|
### Hot-reloading
|
|
|
|
Configuration supports hot-reloading via editing `config/config.yml`.
|
|
|
|
## Dependency and Integration Map
|
|
|
|
### Internal dependencies
|
|
|
|
- `internal/acl` - Access control configuration
|
|
- `internal/autocert` - SSL certificate management
|
|
- `internal/entrypoint` - HTTP entrypoint setup (now via interface)
|
|
- `internal/route/provider` - Route providers (Docker, file, agent)
|
|
- `internal/maxmind` - GeoIP configuration
|
|
- `internal/notif` - Notification providers
|
|
- `internal/proxmox` - LXC container management
|
|
- `internal/homepage/types` - Dashboard configuration
|
|
- `internal/api` - REST API servers
|
|
- `internal/metrics/systeminfo` - System metrics polling
|
|
- `internal/metrics/uptime` - Uptime tracking
|
|
- `github.com/yusing/goutils/task` - Object lifecycle management
|
|
|
|
### External dependencies
|
|
|
|
- `github.com/goccy/go-yaml` - YAML parsing
|
|
- `github.com/puzpuzpuz/xsync/v4` - Concurrent map
|
|
|
|
### Integration points
|
|
|
|
```go
|
|
// API uses config/query to access state
|
|
providers := statequery.RouteProviderList()
|
|
|
|
// Route providers access config state
|
|
for _, p := range config.GetState().IterProviders() {
|
|
// Process provider
|
|
}
|
|
```
|
|
|
|
## Observability
|
|
|
|
### Logs
|
|
|
|
- Configuration parsing and validation errors
|
|
- Provider initialization results
|
|
- Route loading summary
|
|
- Full configuration dump (at debug level)
|
|
|
|
### Metrics
|
|
|
|
No metrics are currently exposed.
|
|
|
|
## Security Considerations
|
|
|
|
- Configuration file permissions should be restricted (contains secrets)
|
|
- TLS certificates are loaded from files specified in config
|
|
- Agent credentials are passed via configuration
|
|
- No secrets are logged (except in debug mode with full config dump)
|
|
|
|
## Failure Modes and Recovery
|
|
|
|
| Failure | Behavior | Recovery |
|
|
| ----------------------------- | ----------------------------------- | -------------------------- |
|
|
| Invalid YAML | Init returns error | Fix YAML syntax |
|
|
| Missing required fields | Validation fails | Add required fields |
|
|
| Provider initialization fails | Error aggregated and returned | Fix provider configuration |
|
|
| Duplicate provider key | Error logged, first provider kept | Rename provider |
|
|
| Route loading fails | Error aggregated, other routes load | Fix route configuration |
|
|
|
|
## Performance Characteristics
|
|
|
|
- Providers are loaded concurrently
|
|
- Routes are loaded concurrently per provider
|
|
- State access is lock-free for reads
|
|
- Atomic pointer for state swap
|
|
|
|
## Usage Examples
|
|
|
|
### Loading configuration
|
|
|
|
```go
|
|
state := config.NewState()
|
|
err := state.InitFromFile("config.yml")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
config.SetState(state)
|
|
```
|
|
|
|
### Accessing configuration
|
|
|
|
```go
|
|
if config.HasState() {
|
|
cfg := config.Value()
|
|
log.Printf("Entrypoint middleware count: %d", len(cfg.Entrypoint.Middlewares))
|
|
log.Printf("Docker providers: %d", len(cfg.Providers.Docker))
|
|
}
|
|
```
|
|
|
|
### Iterating providers
|
|
|
|
```go
|
|
for name, provider := range config.GetState().IterProviders() {
|
|
log.Printf("Provider: %s, Routes: %d", name, provider.NumRoutes())
|
|
}
|
|
```
|
|
|
|
### Accessing entrypoint handler
|
|
|
|
```go
|
|
state := config.GetState()
|
|
// Get entrypoint interface for route management
|
|
ep := state.Entrypoint()
|
|
// Add routes directly to entrypoint
|
|
ep.AddRoute(route)
|
|
```
|