Access Control
GoDoxy implements access control at two distinct network layers, each operating at different points in the request lifecycle:
| Layer | Description | Blocking Behavior |
|---|---|---|
| Layer 4 (TCP/UDP) | IP filtering when TCP/UDP connection is established | Connection dropped immediately |
| Layer 7 (HTTP) | IP filtering when HTTP request is fully received | HTTP error response returned |
Layer 4
Transport-layer filtering before HTTP data exchange. Blocked IPs see immediate connection drop with no response. Best for blocking scanners early.
Supported Filters
| Type | Example | MaxMind Credentials Required |
|---|---|---|
| IP address | ip:1.2.3.4 | No |
| CIDR | cidr:1.2.3.4/32 | No |
| ISO country code | country:US | Yes |
| Timezone | tz:Asia/Shanghai | Yes |
ACL Configuration
| Key | Type | Description | Default |
|---|---|---|---|
default | allow or deny | Default action | allow |
allow_local | bool | Allow local addresses | true |
allow | Filter List | Allow list | [] |
deny | Filter List | Deny list | [] |
log | object | Log configuration with extra log_allowed field | {} |
notify | object | Periodically send access summary to notification provider | {} |
ACL Notification Configuration
| Key | Type | Description | Required | Default |
|---|---|---|---|---|
to | string list | List of notification provider names | No | [] |
interval | duration | Interval between notifications | No | 1m |
include_allowed | bool | Include allowed connections in notification | No | false |
ACL Example
yaml
# config.yml
acl:
default: allow # or deny (default: allow)
allow_local: true # or false (default: true)
allow:
- ip:1.2.3.4
- cidr:1.2.3.4/32
- country:US
- tz:Asia/Shanghai
deny:
- ip:1.2.3.4
- cidr:1.2.3.4/32
- country:US
- tz:Asia/Shanghai
log:
buffer_size: 65536 # (default: 64KB)
path: /app/logs/acl.log # (default: none)
stdout: false # (default: false)
keep: last 10 # (default: none)
log_allowed: true # (default: false)
notify:
to: [discord]
interval: 1h
include_allowed: true
providers:
maxmind:
account_id: 123456
license_key: your-license-key
database: geolite # or geoip2 if you have subscription
notification:
- name: discord
provider: discord
url: https://discord.com/api/webhooks/1234567890/abcdefghijklmnopqrstuvwxyz
token: your-tokenLayer 7
HTTP-level filtering after request headers/body received. Blocked IPs see HTTP 403 with error message. Good for traffic that should get feedback.
NOTE
HTTP Access loggers can be configured
- in
config.ymlunder entrypoint section - per route in docker labels or route files
Request Whitelist Configuration
| Key | Type | Description | Required | Default |
|---|---|---|---|---|
allow | IP/CIDR list | Allow list | No | [] |
status_code | int | Status code | No | 403 |
message | string | Error message | No | IP not allowed |
Request Whitelist Example
yaml
# config.yml
entrypoint:
middlewares: # allow only local (private) ips
- use: CIDRWhiteList
allow:
- 127.0.0.1/32
- 172.16.0.0/12
- 192.168.0.0/16
- 10.0.0.0/8
status_code: 403
message: Forbidden
access_log:
format: json
path: /app/logs/access.json.log
filters: # skip logging requests from local (private) ips
cidr:
negative: true
values:
- 127.0.0.1/32
- 172.16.0.0/12
- 192.168.0.0/16
- 10.0.0.0/8
fields:
headers:
default: drop # drop app headers in log
config: # keep only these
X-Real-Ip: keep
CF-Connecting-Ip: keep
X-Forwarded-For: keep
# docker labels
proxy.#1.middlewares.cidr_whitelist: |
allow:
- 10.0.0.0/8
- 192.168.0.0/16
status_code: 403
message: "IP not allowed"Access Logging
Common Fields
| Key | Type | Description | Allowed Values | Default |
|---|---|---|---|---|
path | string | Path to the access log file | /var/log/access.log | required |
stdout | bool | Enable stdout logging (can be used with path) | true or false | false |
keep or retention | See below | Retention policy | 30 days | |
rotate_interval | duration | Log rotation interval | Duration | 1h |
Log rotation
NOTE
To enable log rotation, you may set keep or retention to a retention policy.
Default: 30 days
Format:
{N} days|weeks|months(e.g.30 days){N} KB|MB|GB|kb|mb|gb(e.g.100 MBfor 100 Megabytes,100 mbfor 100 Megabits)last {N}(e.g.last 10for last 10 lines)
HTTP Access Log
| Field | Type | Description | Allowed values | Default |
|---|---|---|---|---|
format | string | access log format | common, combined, json | combined |
filters | object | access log filters (optional) | ||
filters.*.negative | bool | negative filters | true or false | false |
filters.status_codes.values | integer or integer range | status code filters | ||
filters.method.values | string | method filters | GET, POST, ... | |
filters.host.values | string | host filters | hostname | |
filters.headers.values | string | headers filters | case-sensitive key or key=value | |
filters.cidr.values | string | CIDR filters | see below | |
fields | object | access log fields | see below | |
fields.*.default | string (field mode) | default field behavior | keep, drop, redact | See Explanation |
fields.*.config | key:value mapping (key is case-sensitive) | headers fields | key: field_mode | |
fields.headers.config.* | string | headers fields | header: field_mode | |
fields.query.config.* | string | query fields | query: field_mode | |
fields.cookies.config.* | string | cookies fields | cookie: field_mode |
Explanation
- Multiple access loggers can share the same log file
- When
filters.*.negativeis set totrue, requests that match any of the negative filters will not be logged - When
fields.*.defaultis set tokeep, that field will be logged - When
fields.*.defaultis set toredact, that field will be redacted asREDACTEDfields.query.default=redactwill replace the query string with a format like?key=REDACTED- This redaction configuration for other fields (besides query) only takes effect when
access_log.formatis set tojson
- When
fields.*.defaultis set todrop, that field will be dropped - Default field config:
query.default=keepcookies.default=dropheaders.default=drop
ACL Log
| Field | Type | Description | Required | Allowed values | Default |
|---|---|---|---|---|---|
log_allowed | bool | log when IP is allowed | bool | true or false | false |
Full Example
yaml
# config.yml
entrypoint:
access_log:
format: json
path: /var/log/example.log
filters:
status_codes:
values:
- 200-299
- 101
method:
values:
- GET
host:
values:
- example.y.z
headers:
negative: true
values:
- foo=bar # when key "foo" is present and value is `bar`
- baz # when key "baz" is present
cidr:
values:
- 192.168.10.0/24
fields:
headers:
default: keep
config:
foo: redact
query:
default: drop
config:
foo: keep
cookies:
default: redact
config:
foo: keep
# route file
# same as above, but under your app config, e.g.
app1:
access_log:
format: json
...
# docker labels - string as inline mapping
proxy.app1.access_log: |
format: json
...
# docker labels - full label
proxy.app1.access_log.format: json
proxy.app1.access_log.filters.status_codes.values: 200-299,300
proxy.app1.access_log.fields.headers.config.foo: redact