Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ipn/ipnserver: add TS_PERMIT_CERT_UID envknob to give webservers cert access #3809

Merged
merged 1 commit into from Jan 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 22 additions & 0 deletions ipn/ipnserver/server.go
Expand Up @@ -32,6 +32,7 @@ import (
"inet.af/netaddr"
"inet.af/peercred"
"tailscale.com/control/controlclient"
"tailscale.com/envknob"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnlocal"
"tailscale.com/ipn/localapi"
Expand Down Expand Up @@ -445,6 +446,26 @@ func (s *Server) localAPIPermissions(ci connIdentity) (read, write bool) {
return false, false
}

// connCanFetchCerts reports whether ci is allowed to fetch HTTPS
// certs from this server when it wouldn't otherwise be able to.
//
// That is, this reports whether ci should grant additional
// capabilities over what the conn would otherwise be able to do.
//
// For now this only returns true on Unix machines when
// TS_PERMIT_CERT_UID is set the to the userid of the peer
// connection. It's intended to give your non-root webserver access
// (www-data, caddy, nginx, etc) to certs.
func (s *Server) connCanFetchCerts(ci connIdentity) bool {
if ci.IsUnixSock && ci.Creds != nil {
connUID, ok := ci.Creds.UserID()
if ok && connUID == envknob.String("TS_PERMIT_CERT_UID") {
return true
}
}
return false
}

// registerDisconnectSub adds ch as a subscribe to connection disconnect
// events. If add is false, the subscriber is removed.
func (s *Server) registerDisconnectSub(ch chan<- struct{}, add bool) {
Expand Down Expand Up @@ -1075,6 +1096,7 @@ func (psc *protoSwitchConn) Close() error {
func (s *Server) localhostHandler(ci connIdentity) http.Handler {
lah := localapi.NewHandler(s.b, s.logf, s.backendLogID)
lah.PermitRead, lah.PermitWrite = s.localAPIPermissions(ci)
lah.PermitCert = s.connCanFetchCerts(ci)

return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/localapi/") {
Expand Down
2 changes: 1 addition & 1 deletion ipn/localapi/cert.go
Expand Up @@ -66,7 +66,7 @@ func (h *Handler) certDir() (string, error) {
var acmeDebug = envknob.Bool("TS_DEBUG_ACME")

func (h *Handler) serveCert(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite {
if !h.PermitWrite && !h.PermitCert {
http.Error(w, "cert access denied", http.StatusForbidden)
return
}
Expand Down
7 changes: 7 additions & 0 deletions ipn/localapi/localapi.go
Expand Up @@ -52,8 +52,15 @@ type Handler struct {
PermitRead bool

// PermitWrite is whether mutating HTTP handlers are allowed.
// If PermitWrite is true, everything is allowed.
// It effectively means that the user is root or the admin
// (operator user).
PermitWrite bool

// PermitCert is whether the client is additionally granted
// cert fetching access.
PermitCert bool

b *ipnlocal.LocalBackend
logf logger.Logf
backendLogID string
Expand Down