mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-01 22:15:51 +00:00
cmd/tailscale: make cert subcommand give hints on access denied
Lot of people have been hitting this.
Now it says:
$ tailscale cert tsdev.corp.ts.net
Access denied: cert access denied
Use 'sudo tailscale cert' or 'tailscale up --operator=$USER' to not require root.
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
(cherry picked from commit f62e6d83a9
)
This commit is contained in:
parent
349015098d
commit
fd7e7ed5d3
@ -76,6 +76,20 @@ type errorJSON struct {
|
|||||||
Error string
|
Error string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AccessDeniedError is an error due to permissions.
|
||||||
|
type AccessDeniedError struct {
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *AccessDeniedError) Error() string { return fmt.Sprintf("Access denied: %v", e.err) }
|
||||||
|
func (e *AccessDeniedError) Unwrap() error { return e.err }
|
||||||
|
|
||||||
|
// IsAccessDeniedError reports whether err is or wraps an AccessDeniedError.
|
||||||
|
func IsAccessDeniedError(err error) bool {
|
||||||
|
var ae *AccessDeniedError
|
||||||
|
return errors.As(err, &ae)
|
||||||
|
}
|
||||||
|
|
||||||
// bestError returns either err, or if body contains a valid JSON
|
// bestError returns either err, or if body contains a valid JSON
|
||||||
// object of type errorJSON, its non-empty error body.
|
// object of type errorJSON, its non-empty error body.
|
||||||
func bestError(err error, body []byte) error {
|
func bestError(err error, body []byte) error {
|
||||||
@ -86,6 +100,14 @@ func bestError(err error, body []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func errorMessageFromBody(body []byte) string {
|
||||||
|
var j errorJSON
|
||||||
|
if err := json.Unmarshal(body, &j); err == nil && j.Error != "" {
|
||||||
|
return j.Error
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(string(body))
|
||||||
|
}
|
||||||
|
|
||||||
func send(ctx context.Context, method, path string, wantStatus int, body io.Reader) ([]byte, error) {
|
func send(ctx context.Context, method, path string, wantStatus int, body io.Reader) ([]byte, error) {
|
||||||
req, err := http.NewRequestWithContext(ctx, method, "http://local-tailscaled.sock"+path, body)
|
req, err := http.NewRequestWithContext(ctx, method, "http://local-tailscaled.sock"+path, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -104,6 +126,9 @@ func send(ctx context.Context, method, path string, wantStatus int, body io.Read
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if res.StatusCode != wantStatus {
|
if res.StatusCode != wantStatus {
|
||||||
|
if res.StatusCode == 403 {
|
||||||
|
return nil, &AccessDeniedError{errors.New(errorMessageFromBody(slurp))}
|
||||||
|
}
|
||||||
err := fmt.Errorf("HTTP %s: %s (expected %v)", res.Status, slurp, wantStatus)
|
err := fmt.Errorf("HTTP %s: %s (expected %v)", res.Status, slurp, wantStatus)
|
||||||
return nil, bestError(err, slurp)
|
return nil, bestError(err, slurp)
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/peterbourgon/ff/v2/ffcli"
|
"github.com/peterbourgon/ff/v2/ffcli"
|
||||||
@ -85,6 +86,9 @@ func runCert(ctx context.Context, args []string) error {
|
|||||||
certArgs.keyFile = domain + ".key"
|
certArgs.keyFile = domain + ".key"
|
||||||
}
|
}
|
||||||
certPEM, keyPEM, err := tailscale.CertPair(ctx, domain)
|
certPEM, keyPEM, err := tailscale.CertPair(ctx, domain)
|
||||||
|
if tailscale.IsAccessDeniedError(err) && os.Getuid() != 0 && runtime.GOOS != "windows" {
|
||||||
|
return fmt.Errorf("%v\n\nUse 'sudo tailscale cert' or 'tailscale up --operator=$USER' to not require root.", err)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user