Skip to content

Access Control

GoDoxy implements access control at two distinct network layers, each operating at different points in the request lifecycle:

LayerDescriptionBlocking Behavior
Layer 4 (TCP/UDP)IP filtering when TCP/UDP connection is establishedConnection dropped immediately
Layer 7 (HTTP)IP filtering when HTTP request is fully receivedHTTP 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

TypeExampleMaxMind Credentials Required
IP addressip:1.2.3.4No
CIDRcidr:1.2.3.4/32No
ISO country codecountry:USYes
Timezonetz:Asia/ShanghaiYes

ACL Configuration

KeyTypeDescriptionDefault
defaultallow or denyDefault actionallow
allow_localboolAllow local addressestrue
allowFilter ListAllow list[]
denyFilter ListDeny list[]
logobjectLog configuration with extra log_allowed field{}
notifyobjectPeriodically send access summary to notification provider{}

ACL Notification Configuration

KeyTypeDescriptionRequiredDefault
tostring listList of notification provider namesNo[]
intervaldurationInterval between notificationsNo1m
include_allowedboolInclude allowed connections in notificationNofalse

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-token

Layer 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.yml under entrypoint section
  • per route in docker labels or route files

Request Whitelist Configuration

KeyTypeDescriptionRequiredDefault
allowIP/CIDR listAllow listNo[]
status_codeintStatus codeNo403
messagestringError messageNoIP 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

KeyTypeDescriptionAllowed ValuesDefault
pathstringPath to the access log file/var/log/access.logrequired
stdoutboolEnable stdout logging (can be used with path)true or falsefalse
keep or retentionSee belowRetention policy30 days
rotate_intervaldurationLog rotation intervalDuration1h

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 MB for 100 Megabytes, 100 mb for 100 Megabits)
  • last {N} (e.g. last 10 for last 10 lines)

HTTP Access Log

FieldTypeDescriptionAllowed valuesDefault
formatstringaccess log formatcommon, combined, jsoncombined
filtersobjectaccess log filters (optional)
filters.*.negativeboolnegative filterstrue or falsefalse
filters.status_codes.valuesinteger or integer rangestatus code filters
filters.method.valuesstringmethod filtersGET, POST, ...
filters.host.valuesstringhost filtershostname
filters.headers.valuesstringheaders filterscase-sensitive key or key=value
filters.cidr.valuesstringCIDR filterssee below
fieldsobjectaccess log fieldssee below
fields.*.defaultstring (field mode)default field behaviorkeep, drop, redactSee Explanation
fields.*.configkey:value mapping (key is case-sensitive)headers fieldskey: field_mode
fields.headers.config.*stringheaders fieldsheader: field_mode
fields.query.config.*stringquery fieldsquery: field_mode
fields.cookies.config.*stringcookies fieldscookie: field_mode

Explanation

  • Multiple access loggers can share the same log file
  • When filters.*.negative is set to true, requests that match any of the negative filters will not be logged
  • When fields.*.default is set to keep, that field will be logged
  • When fields.*.default is set to redact, that field will be redacted as REDACTED
    • fields.query.default = redact will replace the query string with a format like ?key=REDACTED
    • This redaction configuration for other fields (besides query) only takes effect when access_log.format is set to json
  • When fields.*.default is set to drop, that field will be dropped
  • Default field config:
    • query.default = keep
    • cookies.default = drop
    • headers.default = drop

ACL Log

FieldTypeDescriptionRequiredAllowed valuesDefault
log_allowedboollog when IP is allowedbooltrue or falsefalse

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

Released under the MIT License.