-
Notifications
You must be signed in to change notification settings - Fork 726
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
api: add HTTP audit middleware #4537
Changes from all commits
3a45237
725345f
f30bb7b
198fb0b
a80af26
d9ed82f
5af1d1c
3041f1f
232f798
3af3e83
bd9553b
01f88c7
a4bc982
e25cc68
a313450
5ba83fe
40ef486
e3ddcd9
edd63ae
271e58f
2540d56
ea08f30
4800b61
9a2eaec
894a0fe
befd6d3
4a2eb02
0981c7b
4fdb3d4
8f9d6a7
b68a2ae
932ba4d
2652c93
2eff957
be4a3f3
1b6ab1c
dbaf902
e778858
2e8966f
a112ed9
f83681b
3366bc7
aa11cee
42d8b60
6852387
e186524
71ce331
743ed13
69af07e
97025d4
f455d58
d394ab9
3c40ef8
f9385da
6d1b147
c308901
4216358
27f9a55
b809792
abb2505
cffadda
cb3d40f
8659fc6
9f24848
fc7c44a
43beb8b
64998cd
cc577b3
d5460bd
6a48e2e
193b126
dc52e3d
f8c0098
b1f8d5b
23aafd9
7b4f6b9
0ca8c81
83c4ca9
b7ec272
3975382
ea80ae4
aaaa93e
924f302
c726641
62d31ff
f387831
985f448
cd8f7b7
8b25afe
186fa9e
577480a
6217a8e
e958793
36be023
1b170fe
436892f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// Copyright 2022 TiKV Project Authors. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package audit | ||
|
||
import ( | ||
"net/http" | ||
) | ||
|
||
// BackendLabels is used to store some audit backend labels. | ||
type BackendLabels struct { | ||
Labels []string | ||
} | ||
|
||
// LabelMatcher is used to help backend implement audit.Backend | ||
type LabelMatcher struct { | ||
backendLabel string | ||
} | ||
|
||
// Match is used to check whether backendLabel is in the labels | ||
func (m *LabelMatcher) Match(labels *BackendLabels) bool { | ||
for _, item := range labels.Labels { | ||
if m.backendLabel == item { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
// Sequence is used to help backend implement audit.Backend | ||
type Sequence struct { | ||
before bool | ||
} | ||
|
||
// ProcessBeforeHandler is used to help backend implement audit.Backend | ||
func (s *Sequence) ProcessBeforeHandler() bool { | ||
return s.before | ||
} | ||
|
||
// Backend defines what function audit backend should hold | ||
type Backend interface { | ||
// ProcessHTTPRequest is used to perform HTTP audit process | ||
ProcessHTTPRequest(req *http.Request) bool | ||
// Match is used to determine if the backend matches | ||
Match(*BackendLabels) bool | ||
// ProcessBeforeHandler is used to identify whether this backend should execute before handler | ||
ProcessBeforeHandler() bool | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Copyright 2022 TiKV Project Authors. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package audit | ||
|
||
import ( | ||
"testing" | ||
|
||
. "github.com/pingcap/check" | ||
) | ||
|
||
func Test(t *testing.T) { | ||
TestingT(t) | ||
} | ||
|
||
var _ = Suite(&testAuditSuite{}) | ||
|
||
type testAuditSuite struct { | ||
} | ||
|
||
func (s *testAuditSuite) TestLabelMatcher(c *C) { | ||
matcher := &LabelMatcher{"testSuccess"} | ||
labels1 := &BackendLabels{Labels: []string{"testFail", "testSuccess"}} | ||
c.Assert(matcher.Match(labels1), Equals, true) | ||
|
||
labels2 := &BackendLabels{Labels: []string{"testFail"}} | ||
c.Assert(matcher.Match(labels2), Equals, false) | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -17,8 +17,12 @@ package api | |||||||||||||||||||||||||||||||||||||||
import ( | ||||||||||||||||||||||||||||||||||||||||
"context" | ||||||||||||||||||||||||||||||||||||||||
"net/http" | ||||||||||||||||||||||||||||||||||||||||
"strings" | ||||||||||||||||||||||||||||||||||||||||
"time" | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
"github.com/pingcap/failpoint" | ||||||||||||||||||||||||||||||||||||||||
"github.com/pingcap/log" | ||||||||||||||||||||||||||||||||||||||||
"github.com/tikv/pd/pkg/audit" | ||||||||||||||||||||||||||||||||||||||||
"github.com/tikv/pd/pkg/errs" | ||||||||||||||||||||||||||||||||||||||||
"github.com/tikv/pd/pkg/requestutil" | ||||||||||||||||||||||||||||||||||||||||
"github.com/tikv/pd/server" | ||||||||||||||||||||||||||||||||||||||||
|
@@ -37,7 +41,7 @@ func newRequestInfoMiddleware(s *server.Server) negroni.Handler { | |||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
func (rm *requestInfoMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { | ||||||||||||||||||||||||||||||||||||||||
if !rm.svr.IsServiceMiddlewareEnabled() { | ||||||||||||||||||||||||||||||||||||||||
if !rm.svr.IsAuditMiddlewareEnabled() { | ||||||||||||||||||||||||||||||||||||||||
next(w, r) | ||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
|
@@ -86,3 +90,58 @@ type clusterCtxKey struct{} | |||||||||||||||||||||||||||||||||||||||
func getCluster(r *http.Request) *cluster.RaftCluster { | ||||||||||||||||||||||||||||||||||||||||
return r.Context().Value(clusterCtxKey{}).(*cluster.RaftCluster) | ||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
type auditMiddleware struct { | ||||||||||||||||||||||||||||||||||||||||
svr *server.Server | ||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
func newAuditMiddleware(s *server.Server) negroni.Handler { | ||||||||||||||||||||||||||||||||||||||||
return &auditMiddleware{svr: s} | ||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
// ServeHTTP is used to implememt negroni.Handler for auditMiddleware | ||||||||||||||||||||||||||||||||||||||||
func (s *auditMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { | ||||||||||||||||||||||||||||||||||||||||
if !s.svr.IsAuditMiddlewareEnabled() { | ||||||||||||||||||||||||||||||||||||||||
next(w, r) | ||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
requestInfo, ok := requestutil.RequestInfoFrom(r.Context()) | ||||||||||||||||||||||||||||||||||||||||
if !ok { | ||||||||||||||||||||||||||||||||||||||||
log.Error("failed to get request info when auditing") | ||||||||||||||||||||||||||||||||||||||||
next(w, r) | ||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to return? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure. If return here, what response do we write? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add error log if can't get |
||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
labels := s.svr.GetServiceAuditBackendLabels(requestInfo.ServiceLabel) | ||||||||||||||||||||||||||||||||||||||||
if labels == nil { | ||||||||||||||||||||||||||||||||||||||||
next(w, r) | ||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
failpoint.Inject("addAuditMiddleware", func() { | ||||||||||||||||||||||||||||||||||||||||
w.Header().Add("audit-label", strings.Join(labels.Labels, ",")) | ||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
beforeNextBackends := make([]audit.Backend, 0) | ||||||||||||||||||||||||||||||||||||||||
afterNextBackends := make([]audit.Backend, 0) | ||||||||||||||||||||||||||||||||||||||||
for _, backend := range s.svr.GetAuditBackend() { | ||||||||||||||||||||||||||||||||||||||||
if backend.Match(labels) { | ||||||||||||||||||||||||||||||||||||||||
if backend.ProcessBeforeHandler() { | ||||||||||||||||||||||||||||||||||||||||
beforeNextBackends = append(beforeNextBackends, backend) | ||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||
afterNextBackends = append(afterNextBackends, backend) | ||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
for _, backend := range beforeNextBackends { | ||||||||||||||||||||||||||||||||||||||||
backend.ProcessHTTPRequest(r) | ||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
Comment on lines
+125
to
+138
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The backend can be divided into two types: before the service Handle and after the service Handle |
||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
next(w, r) | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
endTime := time.Now().Unix() | ||||||||||||||||||||||||||||||||||||||||
r = r.WithContext(requestutil.WithEndTime(r.Context(), endTime)) | ||||||||||||||||||||||||||||||||||||||||
for _, backend := range afterNextBackends { | ||||||||||||||||||||||||||||||||||||||||
backend.ProcessHTTPRequest(r) | ||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where does the
Sequence
init?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
creates and inits
Sequence
when creatingBackend