# Inspiration: https://github.com/Tecnativa/docker-socket-proxy/blob/master/haproxy.cfg global maxconn 10 defaults timeout connect 30s timeout client 30s timeout server 1800s frontend http mode http bind :::2375 v4v6 http-request deny unless { src 127.0.0.1 } || { src ::1 } || { src NC_IPV4_PLACEHOLDER } || { src NC_IPV6_PLACEHOLDER } # ACL to detect JSON \uXXXX unicode escape sequences in the request body. # All values we accept (names, types, etc.) are restricted to plain ASCII # (e.g. ^nc_app_[a-zA-Z0-9_.-]+), so a legitimate request never needs one. # Without this, an attacker can hide literal strings such as "device" or # "Binds" from the other regex-based ACLs below by encoding a character as # e.g. "\u0064evice", bypassing those keyword checks entirely. acl body_has_unicode_escape req.body -m reg -i "\\\\u[0-9a-f]{4}" http-request deny if body_has_unicode_escape # docker system _ping http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/_ping$ } METH_GET # docker inspect image: GET images/%s/json http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/images/.*/json } METH_GET # container inspect: GET containers/%s/json http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers/nc_app_[a-zA-Z0-9_.-]+/json } METH_GET # container inspect: GET containers/%s/logs http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers/nc_app_[a-zA-Z0-9_.-]+/logs } METH_GET # container start/stop: POST containers/%s/start containers/%s/stop http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers/nc_app_[a-zA-Z0-9_.-]+/((start)|(stop)) } METH_POST # container rm: DELETE containers/%s http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers/nc_app_[a-zA-Z0-9_.-]+ } METH_DELETE # container update/exec: POST containers/%s/update containers/%s/exec http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers/nc_app_[a-zA-Z0-9_.-]+/((update)|(exec)) } METH_POST # container put: PUT containers/%s/archive http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers/nc_app_[a-zA-Z0-9_.-]+/archive } METH_PUT # run exec instance: POST exec/%s http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/exec/[a-zA-Z0-9_.-]+/start } METH_POST # container create: POST containers/create?name=%s # ACL to restrict container name to nc_app_[a-zA-Z0-9_.-]+ acl nc_app_container_name url_param(name) -m reg -i "^nc_app_[a-zA-Z0-9_.-]+" # ACL to restrict the number of Mounts to 1 acl one_mount_volume req.body -m reg -i "\"Mounts\"\s*:\s*\[\s*(?:(?!\"Mounts\"\s*:\s*\[)[^}]*)}[^}]*\]" # ACL to deny if there are any binds acl binds_present req.body -m reg -i "\"HostConfig\"\s*:.*\"Binds\"\s*:" # ACL to restrict the type of Mounts to volume acl type_not_volume req.body -m reg -i "\"Mounts\"\s*:\s*\[[^\]]*(\"Type\"\s*:\s*\"(?!volume\b)\w+\"[^\]]*)+\]" # Deny if HostConfig.Binds is present, independently of other mounts or volumes http-request deny if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers/create } nc_app_container_name binds_present # Deny if required conditions are not met. http-request deny if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers/create } nc_app_container_name !one_mount_volume type_not_volume METH_POST # ACL to restrict container creation, that it has HostConfig.Privileged(by searching for "Privileged" word in all payload) acl no_privileged_flag req.body -m reg -i "\"Privileged\"" # ACL to allow mount volume with strict pattern for name: nc_app_[a-zA-Z0-9_.-]+_data acl nc_app_volume_data_only req.body -m reg -i "\"Mounts\"\s*:\s*\[\s*{[^}]*\"Source\"\s*:\s*\"nc_app_[a-zA-Z0-9_.-]+_data\"" http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers/create } nc_app_container_name !no_privileged_flag nc_app_volume_data_only METH_POST # end of container create # volume create: POST volumes/create # restrict name acl nc_app_volume_data req.body -m reg -i "\"Name\"\s*:\s*\"nc_app_[a-zA-Z0-9_.-]+_data\"" # do not allow to use "device" word e.g., "--opt device=:/path/to/dir" acl volume_no_device req.body -m reg -i "\"device\"" http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/volumes/create } nc_app_volume_data !volume_no_device METH_POST # volume rm: DELETE volumes/%s http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/volumes/nc_app_[a-zA-Z0-9_.-]+_data } METH_DELETE # image pull: POST images/create?fromImage=%s http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/images/create } METH_POST http-request deny default_backend dockerbackend backend dockerbackend mode http server dockersocket /var/run/docker.sock