server: disable redirects in peer communication
Disable following redirects from peer HTTP communication on the client's side. Etcd server may run into SSRF (Server-side request forgery) when adding a new member. If users provide a malicious peer URL, the existing etcd members may be redirected to another unexpected internal URL when getting the new member's version. Signed-off-by: Ivan Valdes <ivan@vald.es>
This commit is contained in:
parent
39b440caed
commit
838cd9aa00
@ -66,6 +66,9 @@ func getClusterFromRemotePeers(lg *zap.Logger, urls []string, timeout time.Durat
|
|||||||
cc := &http.Client{
|
cc := &http.Client{
|
||||||
Transport: rt,
|
Transport: rt,
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, u := range urls {
|
for _, u := range urls {
|
||||||
addr := u + "/members"
|
addr := u + "/members"
|
||||||
@ -301,6 +304,9 @@ func isCompatibleWithVers(lg *zap.Logger, vers map[string]*version.Versions, loc
|
|||||||
func getVersion(lg *zap.Logger, m *membership.Member, rt http.RoundTripper) (*version.Versions, error) {
|
func getVersion(lg *zap.Logger, m *membership.Member, rt http.RoundTripper) (*version.Versions, error) {
|
||||||
cc := &http.Client{
|
cc := &http.Client{
|
||||||
Transport: rt,
|
Transport: rt,
|
||||||
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
@ -359,7 +365,12 @@ func getVersion(lg *zap.Logger, m *membership.Member, rt http.RoundTripper) (*ve
|
|||||||
}
|
}
|
||||||
|
|
||||||
func promoteMemberHTTP(ctx context.Context, url string, id uint64, peerRt http.RoundTripper) ([]*membership.Member, error) {
|
func promoteMemberHTTP(ctx context.Context, url string, id uint64, peerRt http.RoundTripper) ([]*membership.Member, error) {
|
||||||
cc := &http.Client{Transport: peerRt}
|
cc := &http.Client{
|
||||||
|
Transport: peerRt,
|
||||||
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
},
|
||||||
|
}
|
||||||
// TODO: refactor member http handler code
|
// TODO: refactor member http handler code
|
||||||
// cannot import etcdhttp, so manually construct url
|
// cannot import etcdhttp, so manually construct url
|
||||||
requestUrl := url + "/members/promote/" + fmt.Sprintf("%d", id)
|
requestUrl := url + "/members/promote/" + fmt.Sprintf("%d", id)
|
||||||
|
@ -497,7 +497,12 @@ func (h *hashKVHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// getPeerHashKVHTTP fetch hash of kv store at the given rev via http call to the given url
|
// getPeerHashKVHTTP fetch hash of kv store at the given rev via http call to the given url
|
||||||
func (s *EtcdServer) getPeerHashKVHTTP(ctx context.Context, cid types.ID, url string, rev int64) (*pb.HashKVResponse, error) {
|
func (s *EtcdServer) getPeerHashKVHTTP(ctx context.Context, cid types.ID, url string, rev int64) (*pb.HashKVResponse, error) {
|
||||||
cc := &http.Client{Transport: s.peerRt}
|
cc := &http.Client{
|
||||||
|
Transport: s.peerRt,
|
||||||
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
},
|
||||||
|
}
|
||||||
hashReq := &pb.HashKVRequest{Revision: rev}
|
hashReq := &pb.HashKVRequest{Revision: rev}
|
||||||
hashReqBytes, err := json.Marshal(hashReq)
|
hashReqBytes, err := json.Marshal(hashReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -150,7 +150,12 @@ func RenewHTTP(ctx context.Context, id lease.LeaseID, url string, rt http.RoundT
|
|||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cc := &http.Client{Transport: rt}
|
cc := &http.Client{
|
||||||
|
Transport: rt,
|
||||||
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
},
|
||||||
|
}
|
||||||
req, err := http.NewRequest("POST", url, bytes.NewReader(lreq))
|
req, err := http.NewRequest("POST", url, bytes.NewReader(lreq))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
@ -210,7 +215,12 @@ func TimeToLiveHTTP(ctx context.Context, id lease.LeaseID, keys bool, url string
|
|||||||
|
|
||||||
req = req.WithContext(ctx)
|
req = req.WithContext(ctx)
|
||||||
|
|
||||||
cc := &http.Client{Transport: rt}
|
cc := &http.Client{
|
||||||
|
Transport: rt,
|
||||||
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
},
|
||||||
|
}
|
||||||
var b []byte
|
var b []byte
|
||||||
// buffer errc channel so that errc don't block inside the go routinue
|
// buffer errc channel so that errc don't block inside the go routinue
|
||||||
resp, err := cc.Do(req)
|
resp, err := cc.Do(req)
|
||||||
|
Loading…
Reference in New Issue
Block a user