mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-27 06:03:48 +03:00
cmd: Split unix sockets for admin endpoint addresses (#5696)
* cmd: fix cli when admin endpoint uses new unix socket permission format Fixes a bug where the following Caddyfile ```Caddyfile { admin unix/admin.sock|0660 } ``` and `caddy reload --config Caddyfile` would throw the following error instead of reloading it: ``` INFO using provided configuration {"config_file": "Caddyfile", "config_adapter": ""} Error: sending configuration to instance: performing request: Post "http://127.0.0.1/load": dial unix admin.sock|0660: connect: no such file or directory [ERROR] exit status 1 ``` --- This bug also affected `caddy start` and `caddy stop`. * Move splitter function to internal --------- Co-authored-by: Matthew Holt <mholt@users.noreply.github.com>
This commit is contained in:
parent
65e33fc1ee
commit
8d304a4566
4 changed files with 73 additions and 37 deletions
|
@ -35,6 +35,7 @@ import (
|
||||||
"github.com/caddyserver/caddy/v2"
|
"github.com/caddyserver/caddy/v2"
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
"github.com/caddyserver/caddy/v2/caddyconfig"
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||||
|
"github.com/caddyserver/caddy/v2/internal"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -611,6 +612,16 @@ func AdminAPIRequest(adminAddr, method, uri string, headers http.Header, body io
|
||||||
origin := "http://" + parsedAddr.JoinHostPort(0)
|
origin := "http://" + parsedAddr.JoinHostPort(0)
|
||||||
if parsedAddr.IsUnixNetwork() {
|
if parsedAddr.IsUnixNetwork() {
|
||||||
origin = "http://127.0.0.1" // bogus host is a hack so that http.NewRequest() is happy
|
origin = "http://127.0.0.1" // bogus host is a hack so that http.NewRequest() is happy
|
||||||
|
|
||||||
|
// the unix address at this point might still contain the optional
|
||||||
|
// unix socket permissions, which are part of the address/host.
|
||||||
|
// those need to be removed first, as they aren't part of the
|
||||||
|
// resulting unix file path
|
||||||
|
addr, _, err := internal.SplitUnixSocketPermissionsBits(parsedAddr.Host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
parsedAddr.Host = addr
|
||||||
}
|
}
|
||||||
|
|
||||||
// form the request
|
// form the request
|
||||||
|
|
56
internal/sockets.go
Normal file
56
internal/sockets.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright 2015 Matthew Holt and The Caddy 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 internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SplitUnixSocketPermissionsBits takes a unix socket address in the
|
||||||
|
// unusual "path|bits" format (e.g. /run/caddy.sock|0222) and tries
|
||||||
|
// to split it into socket path (host) and permissions bits (port).
|
||||||
|
// Colons (":") can't be used as separator, as socket paths on Windows
|
||||||
|
// may include a drive letter (e.g. `unix/c:\absolute\path.sock`).
|
||||||
|
// Permission bits will default to 0200 if none are specified.
|
||||||
|
// Throws an error, if the first carrying bit does not
|
||||||
|
// include write perms (e.g. `0422` or `022`).
|
||||||
|
// Symbolic permission representation (e.g. `u=w,g=w,o=w`)
|
||||||
|
// is not supported and will throw an error for now!
|
||||||
|
func SplitUnixSocketPermissionsBits(addr string) (path string, fileMode fs.FileMode, err error) {
|
||||||
|
addrSplit := strings.SplitN(addr, "|", 2)
|
||||||
|
|
||||||
|
if len(addrSplit) == 2 {
|
||||||
|
// parse octal permission bit string as uint32
|
||||||
|
fileModeUInt64, err := strconv.ParseUint(addrSplit[1], 8, 32)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, fmt.Errorf("could not parse octal permission bits in %s: %v", addr, err)
|
||||||
|
}
|
||||||
|
fileMode = fs.FileMode(fileModeUInt64)
|
||||||
|
|
||||||
|
// FileMode.String() returns a string like `-rwxr-xr--` for `u=rwx,g=rx,o=r` (`0754`)
|
||||||
|
if string(fileMode.String()[2]) != "w" {
|
||||||
|
return "", 0, fmt.Errorf("owner of the socket requires '-w-' (write, octal: '2') permissions at least; got '%s' in %s", fileMode.String()[1:4], addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return addrSplit[0], fileMode, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// default to 0200 (symbolic: `u=w,g=,o=`)
|
||||||
|
// if no permission bits are specified
|
||||||
|
return addr, 0200, nil
|
||||||
|
}
|
39
listeners.go
39
listeners.go
|
@ -31,6 +31,7 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/caddyserver/caddy/v2/internal"
|
||||||
"github.com/quic-go/quic-go"
|
"github.com/quic-go/quic-go"
|
||||||
"github.com/quic-go/quic-go/http3"
|
"github.com/quic-go/quic-go/http3"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
@ -157,7 +158,7 @@ func (na NetworkAddress) listen(ctx context.Context, portOffset uint, config net
|
||||||
// is independent of permissions bits
|
// is independent of permissions bits
|
||||||
if na.IsUnixNetwork() {
|
if na.IsUnixNetwork() {
|
||||||
var err error
|
var err error
|
||||||
address, unixFileMode, err = splitUnixSocketPermissionsBits(na.Host)
|
address, unixFileMode, err = internal.SplitUnixSocketPermissionsBits(na.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -319,40 +320,6 @@ func IsUnixNetwork(netw string) bool {
|
||||||
return strings.HasPrefix(netw, "unix")
|
return strings.HasPrefix(netw, "unix")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes a unix socket address in the unusual "path|bits" format
|
|
||||||
// (e.g. /run/caddy.sock|0222) and tries to split it into
|
|
||||||
// socket path (host) and permissions bits (port). Colons (":")
|
|
||||||
// can't be used as separator, as socket paths on Windows may
|
|
||||||
// include a drive letter (e.g. `unix/c:\absolute\path.sock`).
|
|
||||||
// Permission bits will default to 0200 if none are specified.
|
|
||||||
// Throws an error, if the first carrying bit does not
|
|
||||||
// include write perms (e.g. `0422` or `022`).
|
|
||||||
// Symbolic permission representation (e.g. `u=w,g=w,o=w`)
|
|
||||||
// is not supported and will throw an error for now!
|
|
||||||
func splitUnixSocketPermissionsBits(addr string) (path string, fileMode fs.FileMode, err error) {
|
|
||||||
addrSplit := strings.SplitN(addr, "|", 2)
|
|
||||||
|
|
||||||
if len(addrSplit) == 2 {
|
|
||||||
// parse octal permission bit string as uint32
|
|
||||||
fileModeUInt64, err := strconv.ParseUint(addrSplit[1], 8, 32)
|
|
||||||
if err != nil {
|
|
||||||
return "", 0, fmt.Errorf("could not parse octal permission bits in %s: %v", addr, err)
|
|
||||||
}
|
|
||||||
fileMode = fs.FileMode(fileModeUInt64)
|
|
||||||
|
|
||||||
// FileMode.String() returns a string like `-rwxr-xr--` for `u=rwx,g=rx,o=r` (`0754`)
|
|
||||||
if string(fileMode.String()[2]) != "w" {
|
|
||||||
return "", 0, fmt.Errorf("owner of the socket requires '-w-' (write, octal: '2') permissions at least; got '%s' in %s", fileMode.String()[1:4], addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return addrSplit[0], fileMode, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// default to 0200 (symbolic: `u=w,g=,o=`)
|
|
||||||
// if no permission bits are specified
|
|
||||||
return addr, 0200, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseNetworkAddress parses addr into its individual
|
// ParseNetworkAddress parses addr into its individual
|
||||||
// components. The input string is expected to be of
|
// components. The input string is expected to be of
|
||||||
// the form "network/host:port-range" where any part is
|
// the form "network/host:port-range" where any part is
|
||||||
|
@ -377,7 +344,7 @@ func ParseNetworkAddressWithDefaults(addr, defaultNetwork string, defaultPort ui
|
||||||
network = defaultNetwork
|
network = defaultNetwork
|
||||||
}
|
}
|
||||||
if IsUnixNetwork(network) {
|
if IsUnixNetwork(network) {
|
||||||
_, _, err := splitUnixSocketPermissionsBits(host)
|
_, _, err := internal.SplitUnixSocketPermissionsBits(host)
|
||||||
return NetworkAddress{
|
return NetworkAddress{
|
||||||
Network: network,
|
Network: network,
|
||||||
Host: host,
|
Host: host,
|
||||||
|
|
|
@ -17,6 +17,8 @@ package caddy
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/caddyserver/caddy/v2/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSplitNetworkAddress(t *testing.T) {
|
func TestSplitNetworkAddress(t *testing.T) {
|
||||||
|
@ -634,7 +636,7 @@ func TestSplitUnixSocketPermissionsBits(t *testing.T) {
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
actualPath, actualFileMode, err := splitUnixSocketPermissionsBits(tc.input)
|
actualPath, actualFileMode, err := internal.SplitUnixSocketPermissionsBits(tc.input)
|
||||||
if tc.expectErr && err == nil {
|
if tc.expectErr && err == nil {
|
||||||
t.Errorf("Test %d: Expected error but got: %v", i, err)
|
t.Errorf("Test %d: Expected error but got: %v", i, err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue