Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more granular access control #126

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,7 @@ ENV ALLOW_RESTARTS=0 \
TASKS=0 \
VERSION=1 \
VOLUMES=0

COPY docker-entrypoint.sh /usr/local/bin/
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg.template
COPY verify.lua /usr/local/etc/haproxy/verify.lua
48 changes: 24 additions & 24 deletions haproxy.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ global

# Turn on stats unix socket
server-state-file /var/lib/haproxy/server-state
lua-load /usr/local/etc/haproxy/verify.lua

defaults
mode http
Expand Down Expand Up @@ -45,33 +46,32 @@ backend docker-events

frontend dockerfrontend
bind ${BIND_CONFIG}
http-request deny unless METH_GET || { env(POST) -m bool }
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally, this line was removed as it would conflict with the new access check. Note that verify_access does not allow methods other than GET and HEAD to pass by default, so it should be perfectly fine to do this.

http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers/[a-zA-Z0-9_.-]+/((stop)|(restart)|(kill)) } { env(ALLOW_RESTARTS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers/[a-zA-Z0-9_.-]+/start } { env(ALLOW_START) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers/[a-zA-Z0-9_.-]+/stop } { env(ALLOW_STOP) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/auth } { env(AUTH) -m bool }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why these first 4 lines are removed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @pedrobaeza, sorry for taking so long to reply. I'm not sure if I understand your question. Only line 52 was removed, the rules for container restart/start/stop are left unchanged.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AUTH section is now being audited by the verify_access function, just like all the other sections.

http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/build } { env(BUILD) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/commit } { env(COMMIT) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/configs } { env(CONFIGS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers } { env(CONTAINERS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/distribution } { env(DISTRIBUTION) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/events } { env(EVENTS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/exec } { env(EXEC) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/grpc } { env(GRPC) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/images } { env(IMAGES) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/info } { env(INFO) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/networks } { env(NETWORKS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/nodes } { env(NODES) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/_ping } { env(PING) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/plugins } { env(PLUGINS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/secrets } { env(SECRETS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/services } { env(SERVICES) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/session } { env(SESSION) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/swarm } { env(SWARM) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/system } { env(SYSTEM) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/tasks } { env(TASKS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/version } { env(VERSION) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/volumes } { env(VOLUMES) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers } { lua.verify_access(CONTAINERS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/exec } { lua.verify_access(EXEC) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/images } { lua.verify_access(IMAGES) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/networks } { lua.verify_access(NETWORKS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/secrets } { lua.verify_access(SECRETS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/services } { lua.verify_access(SERVICES) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/volumes } { lua.verify_access(VOLUMES) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/swarm } { lua.verify_access(SWARM) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/nodes } { lua.verify_access(NODES) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/configs } { lua.verify_access(CONFIGS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/plugins } { lua.verify_access(PLUGINS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/system } { lua.verify_access(SYSTEM) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/auth } { lua.verify_access(AUTH) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/build } { lua.verify_access(BUILD) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/commit } { lua.verify_access(COMMIT) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/distribution } { lua.verify_access(DISTRIBUTION) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/events } { lua.verify_access(EVENTS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/grpc } { lua.verify_access(GRPC) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/info } { lua.verify_access(INFO) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/_ping } { lua.verify_access(PING) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/session } { lua.verify_access(SESSION) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/tasks } { lua.verify_access(TASKS) -m bool }
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/version } { lua.verify_access(VERSION) -m bool }
http-request deny
default_backend dockerbackend

Expand Down
12 changes: 12 additions & 0 deletions verify.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
core.register_fetches("verify_access", function(txn, api)
-- env(api) check is kept for backwards compatibility
local read_allowed = txn.f:env(api) == "1" or txn.f:env(api .. "_READ") == "1"
-- env(POST) check is kept for backwards compatibility
local write_allowed = txn.f:env(api .. "_WRITE") == "1" or (read_allowed and txn.f:env("POST") == "1")
local method = txn.f:method()

local result = ((method == "GET" or method == "HEAD") and read_allowed)
or ((method ~= "GET" and method ~= "HEAD") and write_allowed)

return result
end)
Loading