Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ s3 {

## Authorization (OPA)

Cachew uses [Open Policy Agent](https://www.openpolicyagent.org/) for request authorization. The default policy allows GET/HEAD from any source and all methods from `127.0.0.1`.
Cachew uses [Open Policy Agent](https://www.openpolicyagent.org/) for request authorization. The default policy allows all requests from localhost and restricts remote access to non-admin paths (`/api/*`, `/admin/*`).

Policies must be in `package cachew.authz` and define an `allow` rule. If `allow` is true the request proceeds; otherwise it is rejected with 403.

Expand Down
4 changes: 2 additions & 2 deletions internal/opa/opa.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ import (
"github.com/block/cachew/internal/logging"
)

// DefaultPolicy allows GET and HEAD from any source, and all methods from localhost.
// DefaultPolicy allows all requests from localhost and restricts remote access to non-admin paths.
const DefaultPolicy = `package cachew.authz

default allow := false
allow if input.method in {"GET", "HEAD"}
allow if startswith(input.remote_addr, "127.0.0.1:")
allow if not input.path[0] in {"api", "admin"}
`

// Config for OPA policy evaluation. If neither Policy nor PolicyFile is set,
Expand Down
18 changes: 11 additions & 7 deletions internal/opa/opa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,19 @@ func TestMiddlewareDefaultPolicy(t *testing.T) {
tests := []struct {
Name string
Method string
Path string
RemoteAddr string
ExpectedStatus int
}{
{"GETFromAnywhere", http.MethodGet, "10.0.0.1:9999", http.StatusOK},
{"HEADFromAnywhere", http.MethodHead, "10.0.0.1:9999", http.StatusOK},
{"POSTFromLocalhost", http.MethodPost, "127.0.0.1:12345", http.StatusOK},
{"PUTFromLocalhost", http.MethodPut, "127.0.0.1:12345", http.StatusOK},
{"POSTFromRemote", http.MethodPost, "10.0.0.1:9999", http.StatusForbidden},
{"DELETEFromRemote", http.MethodDelete, "10.0.0.1:9999", http.StatusForbidden},
{"LocalhostGetAdmin", http.MethodGet, "/admin/log/level", "127.0.0.1:12345", http.StatusOK},
{"LocalhostPostAPI", http.MethodPost, "/api/v1/object/ns/key", "127.0.0.1:12345", http.StatusOK},
{"RemoteGetStrategy", http.MethodGet, "/git/github.com/org/repo", "10.0.0.1:9999", http.StatusOK},
{"RemotePostStrategy", http.MethodPost, "/git/github.com/org/repo", "10.0.0.1:9999", http.StatusOK},
{"RemoteLiveness", http.MethodGet, "/_liveness", "10.0.0.1:9999", http.StatusOK},
{"RemoteReadiness", http.MethodGet, "/_readiness", "10.0.0.1:9999", http.StatusOK},
{"RemoteGetAdmin", http.MethodGet, "/admin/pprof/", "10.0.0.1:9999", http.StatusForbidden},
{"RemotePostAPI", http.MethodPost, "/api/v1/object/ns/key", "10.0.0.1:9999", http.StatusForbidden},
{"RemoteDeleteAPI", http.MethodDelete, "/api/v1/object/ns/key", "10.0.0.1:9999", http.StatusForbidden},
}

next := http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {})
Expand All @@ -40,7 +44,7 @@ func TestMiddlewareDefaultPolicy(t *testing.T) {

for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
r := newRequest(test.Method, "/some/path")
r := newRequest(test.Method, test.Path)
r.RemoteAddr = test.RemoteAddr
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
Expand Down