# Inspiration: https://github.com/Tecnativa/docker-socket-proxy/blob/master/haproxy.cfg

global
    maxconn 10

defaults
    timeout connect 10s
    timeout client 10s
    timeout server 10s

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 }
    # docker system _ping
    http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/_ping } 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 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*\[[^\]]*(\"Type\":\s*\"(?!volume\b)\w+\"[^\]]*)+\]"
    http-request deny if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers/create } nc_app_container_name !one_mount_volume binds_present type_not_volume METH_POST

    # ACL to restrict container creation, that it has HostConfig.Privileged not set
    acl no_privileged_flag req.body -m reg -i "\"HostConfig\":\s?{[^}]*\"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?{[^}]*\"Source\":\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?\"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
