mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-20 15:31:24 +02:00
docs: add per package README for implementation details (AI generated with human review)
This commit is contained in:
282
internal/acl/README.md
Normal file
282
internal/acl/README.md
Normal file
@@ -0,0 +1,282 @@
|
||||
# ACL (Access Control List)
|
||||
|
||||
Access control at the TCP connection level with IP/CIDR, timezone, and country-based filtering.
|
||||
|
||||
## Overview
|
||||
|
||||
The ACL package provides network-level access control by wrapping TCP listeners and validating incoming connections against configurable allow/deny rules. It integrates with MaxMind GeoIP for geographic-based filtering and supports access logging with notification batching.
|
||||
|
||||
### Primary consumers
|
||||
|
||||
- `internal/entrypoint` - Wraps the main TCP listener for connection filtering
|
||||
- Operators - Configure rules via YAML configuration
|
||||
|
||||
### Non-goals
|
||||
|
||||
- HTTP request-level filtering (handled by middleware)
|
||||
- Authentication or authorization (see `internal/auth`)
|
||||
- VPN or tunnel integration
|
||||
|
||||
### Stability
|
||||
|
||||
Stable internal package. The public API is the `Config` struct and its methods.
|
||||
|
||||
## Public API
|
||||
|
||||
### Exported types
|
||||
|
||||
```go
|
||||
type Config struct {
|
||||
Default string // "allow" or "deny" (default: "allow")
|
||||
AllowLocal *bool // Allow private/loopback IPs (default: true)
|
||||
Allow Matchers // Allow rules
|
||||
Deny Matchers // Deny rules
|
||||
Log *accesslog.ACLLoggerConfig // Access logging configuration
|
||||
|
||||
Notify struct {
|
||||
To []string // Notification providers
|
||||
Interval time.Duration // Notification frequency (default: 1m)
|
||||
IncludeAllowed *bool // Include allowed in notifications (default: false)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
type Matcher struct {
|
||||
match MatcherFunc
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
type Matchers []Matcher
|
||||
```
|
||||
|
||||
### Exported functions and methods
|
||||
|
||||
```go
|
||||
func (c *Config) Validate() gperr.Error
|
||||
```
|
||||
|
||||
Validates configuration and sets defaults. Must be called before `Start`.
|
||||
|
||||
```go
|
||||
func (c *Config) Start(parent task.Parent) gperr.Error
|
||||
```
|
||||
|
||||
Initializes the ACL, starts the logger and notification goroutines.
|
||||
|
||||
```go
|
||||
func (c *Config) IPAllowed(ip net.IP) bool
|
||||
```
|
||||
|
||||
Returns true if the IP is allowed based on configured rules. Performs caching and GeoIP lookup if needed.
|
||||
|
||||
```go
|
||||
func (c *Config) WrapTCP(lis net.Listener) net.Listener
|
||||
```
|
||||
|
||||
Wraps a `net.Listener` to filter connections by IP.
|
||||
|
||||
```go
|
||||
func (matcher *Matcher) Parse(s string) error
|
||||
```
|
||||
|
||||
Parses a matcher string in the format `{type}:{value}`. Supported types: `ip`, `cidr`, `tz`, `country`.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Core components
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[TCP Listener] --> B[TCPListener Wrapper]
|
||||
B --> C{IP Allowed?}
|
||||
C -->|Yes| D[Accept Connection]
|
||||
C -->|No| E[Close Connection]
|
||||
|
||||
F[Config] --> G[Validate]
|
||||
G --> H[Start]
|
||||
H --> I[Matcher Evaluation]
|
||||
I --> C
|
||||
|
||||
J[MaxMind] -.-> K[IP Lookup]
|
||||
K -.-> I
|
||||
|
||||
L[Access Logger] -.-> M[Log & Notify]
|
||||
M -.-> B
|
||||
```
|
||||
|
||||
### Connection filtering flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant TCPListener
|
||||
participant Config
|
||||
participant MaxMind
|
||||
participant Logger
|
||||
|
||||
Client->>TCPListener: Connection Request
|
||||
TCPListener->>Config: IPAllowed(clientIP)
|
||||
|
||||
alt Loopback IP
|
||||
Config-->>TCPListener: true
|
||||
else Private IP (allow_local)
|
||||
Config-->>TCPListener: true
|
||||
else Cached Result
|
||||
Config-->>TCPListener: Cached Result
|
||||
else Evaluate Allow Rules
|
||||
Config->>Config: Check Allow list
|
||||
alt Matches
|
||||
Config->>Config: Cache true
|
||||
Config-->>TCPListener: Allowed
|
||||
else Evaluate Deny Rules
|
||||
Config->>Config: Check Deny list
|
||||
alt Matches
|
||||
Config->>Config: Cache false
|
||||
Config-->>TCPListener: Denied
|
||||
else Default Action
|
||||
Config->>MaxMind: Lookup GeoIP
|
||||
MaxMind-->>Config: IPInfo
|
||||
Config->>Config: Apply default rule
|
||||
Config->>Config: Cache result
|
||||
Config-->>TCPListener: Result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
alt Logging enabled
|
||||
Config->>Logger: Log access attempt
|
||||
end
|
||||
```
|
||||
|
||||
### Matcher types
|
||||
|
||||
| Type | Format | Example |
|
||||
| -------- | ----------------- | --------------------- |
|
||||
| IP | `ip:address` | `ip:192.168.1.1` |
|
||||
| CIDR | `cidr:network` | `cidr:192.168.0.0/16` |
|
||||
| TimeZone | `tz:timezone` | `tz:Asia/Shanghai` |
|
||||
| Country | `country:ISOCode` | `country:GB` |
|
||||
|
||||
## Configuration Surface
|
||||
|
||||
### Config sources
|
||||
|
||||
Configuration is loaded from `config/config.yml` under the `acl` key.
|
||||
|
||||
### Schema
|
||||
|
||||
```yaml
|
||||
acl:
|
||||
default: "allow" # "allow" or "deny"
|
||||
allow_local: true # Allow private/loopback IPs
|
||||
log:
|
||||
log_allowed: false # Log allowed connections
|
||||
notify:
|
||||
to: ["gotify"] # Notification providers
|
||||
interval: "1m" # Notification interval
|
||||
include_allowed: false # Include allowed in notifications
|
||||
```
|
||||
|
||||
### Hot-reloading
|
||||
|
||||
Configuration requires restart. The ACL does not support dynamic rule updates.
|
||||
|
||||
## Dependency and Integration Map
|
||||
|
||||
### Internal dependencies
|
||||
|
||||
- `internal/maxmind` - IP geolocation lookup
|
||||
- `internal/logging/accesslog` - Access logging
|
||||
- `internal/notif` - Notifications
|
||||
- `internal/task/task.go` - Lifetime management
|
||||
|
||||
### Integration points
|
||||
|
||||
```go
|
||||
// Entrypoint uses ACL to wrap the TCP listener
|
||||
aclListener := config.ACL.WrapTCP(listener)
|
||||
http.Server.Serve(aclListener, entrypoint)
|
||||
```
|
||||
|
||||
## Observability
|
||||
|
||||
### Logs
|
||||
|
||||
- `ACL started` - Configuration summary on start
|
||||
- `log_notify_loop` - Access attempts (allowed/denied)
|
||||
|
||||
Log levels: `Info` for startup, `Debug` for client closure.
|
||||
|
||||
### Metrics
|
||||
|
||||
No metrics are currently exposed.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- Loopback and private IPs are always allowed unless explicitly denied
|
||||
- Cache TTL is 1 minute to limit memory usage
|
||||
- Notification channel has a buffer of 100 to prevent blocking
|
||||
- Failed connections are immediately closed without response
|
||||
|
||||
## Failure Modes and Recovery
|
||||
|
||||
| Failure | Behavior | Recovery |
|
||||
| --------------------------------- | ------------------------------------- | --------------------------------------------- |
|
||||
| Invalid matcher syntax | Validation fails on startup | Fix configuration syntax |
|
||||
| MaxMind database unavailable | GeoIP lookups return unknown location | Default action applies; cache hit still works |
|
||||
| Notification provider unavailable | Notification dropped | Error logged, continues operation |
|
||||
| Cache full | No eviction, uses Go map | No action needed |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic configuration
|
||||
|
||||
```go
|
||||
aclConfig := &acl.Config{
|
||||
Default: "allow",
|
||||
AllowLocal: ptr(true),
|
||||
Allow: acl.Matchers{
|
||||
{match: matchIP(net.ParseIP("192.168.1.0/24"))},
|
||||
},
|
||||
Deny: acl.Matchers{
|
||||
{match: matchISOCode("CN")},
|
||||
},
|
||||
}
|
||||
if err := aclConfig.Validate(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := aclConfig.Start(parent); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
### Wrapping a TCP listener
|
||||
|
||||
```go
|
||||
listener, err := net.Listen("tcp", ":443")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Wrap with ACL
|
||||
aclListener := aclConfig.WrapTCP(listener)
|
||||
|
||||
// Use with HTTP server
|
||||
server := &http.Server{}
|
||||
server.Serve(aclListener)
|
||||
```
|
||||
|
||||
### Creating custom matchers
|
||||
|
||||
```go
|
||||
matcher := &acl.Matcher{}
|
||||
err := matcher.Parse("country:US")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Use the matcher
|
||||
allowed := matcher.match(ipInfo)
|
||||
```
|
||||
Reference in New Issue
Block a user