mirror of
https://github.com/rclone/rclone.git
synced 2024-11-22 04:41:17 +08:00
lib/http: disable automatic authentication skipping for unix sockets
Disabling the authentication for unix sockets makes it impossible to use `rclone serve` behind a proxy that that communicates with rclone via a unix socket. Re-enabling the authentication should not have any effect on most users of unix sockets as they do not set authentication up with a unix socket anyway.
This commit is contained in:
parent
175aa07cdd
commit
29fd894189
|
@ -36,12 +36,6 @@ func IsAuthenticated(r *http.Request) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsUnixSocket checks if the request was received on a unix socket, used to skip auth & CORS
|
|
||||||
func IsUnixSocket(r *http.Request) bool {
|
|
||||||
v, _ := r.Context().Value(ctxKeyUnixSock).(bool)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// PublicURL returns the URL defined in NewBaseContext, used for logging & CORS
|
// PublicURL returns the URL defined in NewBaseContext, used for logging & CORS
|
||||||
func PublicURL(r *http.Request) string {
|
func PublicURL(r *http.Request) string {
|
||||||
v, _ := r.Context().Value(ctxKeyPublicURL).(string)
|
v, _ := r.Context().Value(ctxKeyPublicURL).(string)
|
||||||
|
|
|
@ -57,11 +57,6 @@ func NewLoggedBasicAuthenticator(realm string, secrets goauth.SecretProvider) *L
|
||||||
func basicAuth(authenticator *LoggedBasicAuth) func(next http.Handler) http.Handler {
|
func basicAuth(authenticator *LoggedBasicAuth) func(next http.Handler) http.Handler {
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// skip auth for unix socket
|
|
||||||
if IsUnixSocket(r) {
|
|
||||||
next.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// skip auth for CORS preflight
|
// skip auth for CORS preflight
|
||||||
if r.Method == "OPTIONS" {
|
if r.Method == "OPTIONS" {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
|
@ -123,11 +118,6 @@ func MiddlewareAuthBasic(user, pass, realm, salt string) Middleware {
|
||||||
func MiddlewareAuthCustom(fn CustomAuthFn, realm string, userFromContext bool) Middleware {
|
func MiddlewareAuthCustom(fn CustomAuthFn, realm string, userFromContext bool) Middleware {
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// skip auth for unix socket
|
|
||||||
if IsUnixSocket(r) {
|
|
||||||
next.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// skip auth for CORS preflight
|
// skip auth for CORS preflight
|
||||||
if r.Method == "OPTIONS" {
|
if r.Method == "OPTIONS" {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
|
@ -175,11 +165,6 @@ func MiddlewareCORS(allowOrigin string) Middleware {
|
||||||
|
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// skip cors for unix sockets
|
|
||||||
if IsUnixSocket(r) {
|
|
||||||
next.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if allowOrigin != "" {
|
if allowOrigin != "" {
|
||||||
w.Header().Add("Access-Control-Allow-Origin", allowOrigin)
|
w.Header().Add("Access-Control-Allow-Origin", allowOrigin)
|
||||||
|
|
|
@ -39,8 +39,7 @@ If you set ` + "`--{{ .Prefix }}addr`" + ` to listen on a public or LAN accessib
|
||||||
then using Authentication is advised - see the next section for info.
|
then using Authentication is advised - see the next section for info.
|
||||||
|
|
||||||
You can use a unix socket by setting the url to ` + "`unix:///path/to/socket`" + `
|
You can use a unix socket by setting the url to ` + "`unix:///path/to/socket`" + `
|
||||||
or just by using an absolute path name. Note that unix sockets bypass the
|
or just by using an absolute path name.
|
||||||
authentication - this is expected to be done with file system permissions.
|
|
||||||
|
|
||||||
` + "`--{{ .Prefix }}addr`" + ` may be repeated to listen on multiple IPs/ports/sockets.
|
` + "`--{{ .Prefix }}addr`" + ` may be repeated to listen on multiple IPs/ports/sockets.
|
||||||
Socket activation, described further below, can also be used to accomplish the same.
|
Socket activation, described further below, can also be used to accomplish the same.
|
||||||
|
|
|
@ -3,6 +3,7 @@ package http
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -63,46 +64,96 @@ func testReadTestdataFile(t *testing.T, path string) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewServerUnix(t *testing.T) {
|
func TestNewServerUnix(t *testing.T) {
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
tempDir := t.TempDir()
|
tempDir := t.TempDir()
|
||||||
path := filepath.Join(tempDir, "rclone.sock")
|
path := filepath.Join(tempDir, "rclone.sock")
|
||||||
|
|
||||||
cfg := DefaultCfg()
|
servers := []struct {
|
||||||
cfg.ListenAddr = []string{path}
|
name string
|
||||||
|
status int
|
||||||
auth := AuthConfig{
|
result string
|
||||||
BasicUser: "test",
|
cfg Config
|
||||||
BasicPass: "test",
|
auth AuthConfig
|
||||||
|
user string
|
||||||
|
pass string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "ServerWithoutAuth/NoCreds",
|
||||||
|
status: http.StatusOK,
|
||||||
|
result: "hello world",
|
||||||
|
cfg: Config{
|
||||||
|
ListenAddr: []string{path},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
name: "ServerWithAuth/NoCreds",
|
||||||
|
status: http.StatusUnauthorized,
|
||||||
|
cfg: Config{
|
||||||
|
ListenAddr: []string{path},
|
||||||
|
},
|
||||||
|
auth: AuthConfig{
|
||||||
|
BasicUser: "test",
|
||||||
|
BasicPass: "test",
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
name: "ServerWithAuth/GoodCreds",
|
||||||
|
status: http.StatusOK,
|
||||||
|
result: "hello world",
|
||||||
|
cfg: Config{
|
||||||
|
ListenAddr: []string{path},
|
||||||
|
},
|
||||||
|
auth: AuthConfig{
|
||||||
|
BasicUser: "test",
|
||||||
|
BasicPass: "test",
|
||||||
|
},
|
||||||
|
user: "test",
|
||||||
|
pass: "test",
|
||||||
|
}, {
|
||||||
|
name: "ServerWithAuth/BadCreds",
|
||||||
|
status: http.StatusUnauthorized,
|
||||||
|
cfg: Config{
|
||||||
|
ListenAddr: []string{path},
|
||||||
|
},
|
||||||
|
auth: AuthConfig{
|
||||||
|
BasicUser: "test",
|
||||||
|
BasicPass: "test",
|
||||||
|
},
|
||||||
|
user: "testBAD",
|
||||||
|
pass: "testBAD",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := NewServer(ctx, WithConfig(cfg), WithAuth(auth))
|
for _, ss := range servers {
|
||||||
require.NoError(t, err)
|
t.Run(ss.name, func(t *testing.T) {
|
||||||
defer func() {
|
s, err := NewServer(context.Background(), WithConfig(ss.cfg), WithAuth(ss.auth))
|
||||||
require.NoError(t, s.Shutdown())
|
require.NoError(t, err)
|
||||||
_, err := os.Stat(path)
|
defer func() {
|
||||||
require.ErrorIs(t, err, os.ErrNotExist, "shutdown should remove socket")
|
require.NoError(t, s.Shutdown())
|
||||||
}()
|
_, err := os.Stat(path)
|
||||||
|
require.ErrorIs(t, err, os.ErrNotExist, "shutdown should remove socket")
|
||||||
|
}()
|
||||||
|
|
||||||
require.Empty(t, s.URLs(), "unix socket should not appear in URLs")
|
require.Empty(t, s.URLs(), "unix socket should not appear in URLs")
|
||||||
|
|
||||||
expected := []byte("hello world")
|
s.Router().Mount("/", testEchoHandler([]byte(ss.result)))
|
||||||
s.Router().Mount("/", testEchoHandler(expected))
|
s.Serve()
|
||||||
s.Serve()
|
|
||||||
|
|
||||||
client := testNewHTTPClientUnix(path)
|
client := testNewHTTPClientUnix(path)
|
||||||
req, err := http.NewRequest("GET", "http://unix", nil)
|
req, err := http.NewRequest("GET", "http://unix", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
req.SetBasicAuth(ss.user, ss.pass)
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
testExpectRespBody(t, resp, expected)
|
resp, err := client.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer func() {
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
require.Equal(t, http.StatusOK, resp.StatusCode, "unix sockets should ignore auth")
|
require.Equal(t, ss.status, resp.StatusCode, fmt.Sprintf("should return status %d", ss.status))
|
||||||
|
|
||||||
for _, key := range _testCORSHeaderKeys {
|
if ss.result != "" {
|
||||||
require.NotContains(t, resp.Header, key, "unix sockets should not be sent CORS headers")
|
testExpectRespBody(t, resp, []byte(ss.result))
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user