mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #39076 from thaJeztah/remove_hack_malformed_host_header
Remove hack MalformedHostHeaderOverride
This commit is contained in:
commit
48da116486
5 changed files with 0 additions and 264 deletions
|
@ -641,7 +641,6 @@ func loadListeners(cli *DaemonCli, serverConfig *apiserver.Config) ([]string, er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ls = wrapListeners(proto, ls)
|
|
||||||
// If we're binding to a TCP port, make sure that a container doesn't try to use it.
|
// If we're binding to a TCP port, make sure that a container doesn't try to use it.
|
||||||
if proto == "tcp" {
|
if proto == "tcp" {
|
||||||
if err := allocateDaemonPort(addr); err != nil {
|
if err := allocateDaemonPort(addr); err != nil {
|
||||||
|
|
|
@ -13,7 +13,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containerd/containerd/runtime/v1/linux"
|
"github.com/containerd/containerd/runtime/v1/linux"
|
||||||
"github.com/docker/docker/cmd/dockerd/hack"
|
|
||||||
"github.com/docker/docker/daemon"
|
"github.com/docker/docker/daemon"
|
||||||
"github.com/docker/docker/daemon/config"
|
"github.com/docker/docker/daemon/config"
|
||||||
"github.com/docker/docker/libcontainerd/supervisor"
|
"github.com/docker/docker/libcontainerd/supervisor"
|
||||||
|
@ -122,18 +121,6 @@ func allocateDaemonPort(addr string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func wrapListeners(proto string, ls []net.Listener) []net.Listener {
|
|
||||||
switch proto {
|
|
||||||
case "unix":
|
|
||||||
ls[0] = &hack.MalformedHostHeaderOverride{Listener: ls[0]}
|
|
||||||
case "fd":
|
|
||||||
for i := range ls {
|
|
||||||
ls[i] = &hack.MalformedHostHeaderOverride{Listener: ls[i]}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ls
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCgroupParent(config *config.Config) string {
|
func newCgroupParent(config *config.Config) string {
|
||||||
cgroupParent := "docker"
|
cgroupParent := "docker"
|
||||||
useSystemd := daemon.UsingSystemd(config)
|
useSystemd := daemon.UsingSystemd(config)
|
||||||
|
|
|
@ -3,7 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
@ -86,10 +85,6 @@ func allocateDaemonPort(addr string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func wrapListeners(proto string, ls []net.Listener) []net.Listener {
|
|
||||||
return ls
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCgroupParent(config *config.Config) string {
|
func newCgroupParent(config *config.Config) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,121 +0,0 @@
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package hack // import "github.com/docker/docker/cmd/dockerd/hack"
|
|
||||||
|
|
||||||
import "net"
|
|
||||||
|
|
||||||
// MalformedHostHeaderOverride is a wrapper to be able
|
|
||||||
// to overcome the 400 Bad request coming from old docker
|
|
||||||
// clients that send an invalid Host header.
|
|
||||||
type MalformedHostHeaderOverride struct {
|
|
||||||
net.Listener
|
|
||||||
}
|
|
||||||
|
|
||||||
// MalformedHostHeaderOverrideConn wraps the underlying unix
|
|
||||||
// connection and keeps track of the first read from http.Server
|
|
||||||
// which just reads the headers.
|
|
||||||
type MalformedHostHeaderOverrideConn struct {
|
|
||||||
net.Conn
|
|
||||||
first bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var closeConnHeader = []byte("\r\nConnection: close\r")
|
|
||||||
|
|
||||||
// Read reads the first *read* request from http.Server to inspect
|
|
||||||
// the Host header. If the Host starts with / then we're talking to
|
|
||||||
// an old docker client which send an invalid Host header. To not
|
|
||||||
// error out in http.Server we rewrite the first bytes of the request
|
|
||||||
// to sanitize the Host header itself.
|
|
||||||
// In case we're not dealing with old docker clients the data is just passed
|
|
||||||
// to the server w/o modification.
|
|
||||||
func (l *MalformedHostHeaderOverrideConn) Read(b []byte) (n int, err error) {
|
|
||||||
// http.Server uses a 4k buffer
|
|
||||||
if l.first && len(b) == 4096 {
|
|
||||||
// This keeps track of the first read from http.Server which just reads
|
|
||||||
// the headers
|
|
||||||
l.first = false
|
|
||||||
// The first read of the connection by http.Server is done limited to
|
|
||||||
// DefaultMaxHeaderBytes (usually 1 << 20) + 4096.
|
|
||||||
// Here we do the first read which gets us all the http headers to
|
|
||||||
// be inspected and modified below.
|
|
||||||
c, err := l.Conn.Read(b)
|
|
||||||
if err != nil {
|
|
||||||
return c, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
start, end int
|
|
||||||
firstLineFeed = -1
|
|
||||||
buf []byte
|
|
||||||
)
|
|
||||||
for i := 0; i <= c-1-7; i++ {
|
|
||||||
if b[i] == '\n' && firstLineFeed == -1 {
|
|
||||||
firstLineFeed = i
|
|
||||||
}
|
|
||||||
if b[i] != '\n' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if b[i+1] == '\r' && b[i+2] == '\n' {
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if b[i+1] != 'H' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if b[i+2] != 'o' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if b[i+3] != 's' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if b[i+4] != 't' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if b[i+5] != ':' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if b[i+6] != ' ' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if b[i+7] != '/' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// ensure clients other than the docker clients do not get this hack
|
|
||||||
if i != firstLineFeed {
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
start = i + 7
|
|
||||||
// now find where the value ends
|
|
||||||
for ii, bbb := range b[start:c] {
|
|
||||||
if bbb == '\n' {
|
|
||||||
end = start + ii
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf = make([]byte, 0, c+len(closeConnHeader)-(end-start))
|
|
||||||
// strip the value of the host header and
|
|
||||||
// inject `Connection: close` to ensure we don't reuse this connection
|
|
||||||
buf = append(buf, b[:start]...)
|
|
||||||
buf = append(buf, closeConnHeader...)
|
|
||||||
buf = append(buf, b[end:c]...)
|
|
||||||
copy(b, buf)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if len(buf) == 0 {
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
return len(buf), nil
|
|
||||||
}
|
|
||||||
return l.Conn.Read(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept makes the listener accepts connections and wraps the connection
|
|
||||||
// in a MalformedHostHeaderOverrideConn initializing first to true.
|
|
||||||
func (l *MalformedHostHeaderOverride) Accept() (net.Conn, error) {
|
|
||||||
c, err := l.Listener.Accept()
|
|
||||||
if err != nil {
|
|
||||||
return c, err
|
|
||||||
}
|
|
||||||
return &MalformedHostHeaderOverrideConn{c, true}, nil
|
|
||||||
}
|
|
|
@ -1,124 +0,0 @@
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package hack // import "github.com/docker/docker/cmd/dockerd/hack"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type bufConn struct {
|
|
||||||
net.Conn
|
|
||||||
buf *bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bc *bufConn) Read(b []byte) (int, error) {
|
|
||||||
return bc.buf.Read(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHeaderOverrideHack(t *testing.T) {
|
|
||||||
tests := [][2][]byte{
|
|
||||||
{
|
|
||||||
[]byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\n"),
|
|
||||||
[]byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\r\n\r\n"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\nFoo: Bar\r\n"),
|
|
||||||
[]byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\nFoo: Bar\r\n"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\ntest something!"),
|
|
||||||
[]byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\r\n\r\ntest something!"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\ntest something! " + strings.Repeat("test", 15000)),
|
|
||||||
[]byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\r\n\r\ntest something! " + strings.Repeat("test", 15000)),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[]byte("GET /foo\nFoo: Bar\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\n"),
|
|
||||||
[]byte("GET /foo\nFoo: Bar\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\n"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test for https://github.com/docker/docker/issues/23045
|
|
||||||
h0 := "GET /foo\nUser-Agent: Docker\r\n\r\n"
|
|
||||||
h0 = h0 + strings.Repeat("a", 4096-len(h0)-1) + "\n"
|
|
||||||
tests = append(tests, [2][]byte{[]byte(h0), []byte(h0)})
|
|
||||||
|
|
||||||
for _, pair := range tests {
|
|
||||||
read := make([]byte, 4096)
|
|
||||||
client := &bufConn{
|
|
||||||
buf: bytes.NewBuffer(pair[0]),
|
|
||||||
}
|
|
||||||
l := MalformedHostHeaderOverrideConn{client, true}
|
|
||||||
|
|
||||||
n, err := l.Read(read)
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
t.Fatalf("read: %d - %d, err: %v\n%s", n, len(pair[0]), err, string(read[:n]))
|
|
||||||
}
|
|
||||||
if !bytes.Equal(read[:n], pair[1][:n]) {
|
|
||||||
t.Fatalf("\n%s\n%s\n", read[:n], pair[1][:n])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkWithHack(b *testing.B) {
|
|
||||||
client, srv := net.Pipe()
|
|
||||||
done := make(chan struct{})
|
|
||||||
req := []byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\n")
|
|
||||||
read := make([]byte, 4096)
|
|
||||||
b.SetBytes(int64(len(req) * 30))
|
|
||||||
|
|
||||||
l := MalformedHostHeaderOverrideConn{client, true}
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
if _, err := srv.Write(req); err != nil {
|
|
||||||
srv.Close()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
l.first = true // make sure each subsequent run uses the hack parsing
|
|
||||||
}
|
|
||||||
close(done)
|
|
||||||
}()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
for i := 0; i < 30; i++ {
|
|
||||||
if n, err := l.Read(read); err != nil && err != io.EOF {
|
|
||||||
b.Fatalf("read: %d - %d, err: %v\n%s", n, len(req), err, string(read[:n]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
l.Close()
|
|
||||||
<-done
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkNoHack(b *testing.B) {
|
|
||||||
client, srv := net.Pipe()
|
|
||||||
done := make(chan struct{})
|
|
||||||
req := []byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\n")
|
|
||||||
read := make([]byte, 4096)
|
|
||||||
b.SetBytes(int64(len(req) * 30))
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
if _, err := srv.Write(req); err != nil {
|
|
||||||
srv.Close()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close(done)
|
|
||||||
}()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
for i := 0; i < 30; i++ {
|
|
||||||
if _, err := client.Read(read); err != nil && err != io.EOF {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
client.Close()
|
|
||||||
<-done
|
|
||||||
}
|
|
Loading…
Reference in a new issue