소스 검색

cmd/tsidp,tsnet: update tsidp oidc-key store path (#16735)

The tsidp oidc-key.json ended up in the root directory
or home dir of the user process running it.

Update this to store it in a known location respecting
the TS_STATE_DIR and flagDir options.

Fixes #16734

Signed-off-by: Mike O'Driscoll <[email protected]>
Mike O'Driscoll 7 달 전
부모
커밋
47b5f10165
2개의 변경된 파일29개의 추가작업 그리고 3개의 파일을 삭제
  1. 23 3
      cmd/tsidp/tsidp.go
  2. 6 0
      tsnet/tsnet.go

+ 23 - 3
cmd/tsidp/tsidp.go

@@ -29,6 +29,7 @@ import (
 	"net/url"
 	"os"
 	"os/signal"
+	"path/filepath"
 	"strconv"
 	"strings"
 	"sync"
@@ -60,6 +61,9 @@ type ctxConn struct{}
 // accessing the IDP over Funnel are persisted.
 const funnelClientsFile = "oidc-funnel-clients.json"
 
+// oidcKeyFile is where the OIDC private key is persisted.
+const oidcKeyFile = "oidc-key.json"
+
 var (
 	flagVerbose            = flag.Bool("verbose", false, "be verbose")
 	flagPort               = flag.Int("port", 443, "port to listen on")
@@ -80,12 +84,14 @@ func main() {
 	var (
 		lc          *local.Client
 		st          *ipnstate.Status
+		rootPath    string
 		err         error
 		watcherChan chan error
 		cleanup     func()
 
 		lns []net.Listener
 	)
+
 	if *flagUseLocalTailscaled {
 		lc = &local.Client{}
 		st, err = lc.StatusWithoutPeers(ctx)
@@ -110,6 +116,15 @@ func main() {
 			log.Fatalf("failed to listen on any of %v", st.TailscaleIPs)
 		}
 
+		if flagDir == nil || *flagDir == "" {
+			// use user config directory as storage for tsidp oidc key
+			configDir, err := os.UserConfigDir()
+			if err != nil {
+				log.Fatalf("getting user config directory: %v", err)
+			}
+			rootPath = filepath.Join(configDir, "tsidp")
+		}
+
 		// tailscaled needs to be setting an HTTP header for funneled requests
 		// that older versions don't provide.
 		// TODO(naman): is this the correct check?
@@ -127,6 +142,8 @@ func main() {
 			Hostname: *flagHostname,
 			Dir:      *flagDir,
 		}
+		rootPath = ts.GetRootPath()
+		log.Printf("tsidp root path: %s", rootPath)
 		if *flagVerbose {
 			ts.Logf = log.Printf
 		}
@@ -157,7 +174,9 @@ func main() {
 		lc:          lc,
 		funnel:      *flagFunnel,
 		localTSMode: *flagUseLocalTailscaled,
+		rootPath:    rootPath,
 	}
+
 	if *flagPort != 443 {
 		srv.serverURL = fmt.Sprintf("https://%s:%d", strings.TrimSuffix(st.Self.DNSName, "."), *flagPort)
 	} else {
@@ -285,6 +304,7 @@ type idpServer struct {
 	serverURL   string // "https://foo.bar.ts.net"
 	funnel      bool
 	localTSMode bool
+	rootPath    string // root path, used for storing state files
 
 	lazyMux        lazy.SyncValue[*http.ServeMux]
 	lazySigningKey lazy.SyncValue[*signingKey]
@@ -819,8 +839,9 @@ func (s *idpServer) oidcSigner() (jose.Signer, error) {
 
 func (s *idpServer) oidcPrivateKey() (*signingKey, error) {
 	return s.lazySigningKey.GetErr(func() (*signingKey, error) {
+		keyPath := filepath.Join(s.rootPath, oidcKeyFile)
 		var sk signingKey
-		b, err := os.ReadFile("oidc-key.json")
+		b, err := os.ReadFile(keyPath)
 		if err == nil {
 			if err := sk.UnmarshalJSON(b); err == nil {
 				return &sk, nil
@@ -835,7 +856,7 @@ func (s *idpServer) oidcPrivateKey() (*signingKey, error) {
 		if err != nil {
 			log.Fatalf("Error marshaling key: %v", err)
 		}
-		if err := os.WriteFile("oidc-key.json", b, 0600); err != nil {
+		if err := os.WriteFile(keyPath, b, 0600); err != nil {
 			log.Fatalf("Error writing key: %v", err)
 		}
 		return &sk, nil
@@ -869,7 +890,6 @@ func (s *idpServer) serveJWKS(w http.ResponseWriter, r *http.Request) {
 	}); err != nil {
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 	}
-	return
 }
 
 // openIDProviderMetadata is a partial representation of

+ 6 - 0
tsnet/tsnet.go

@@ -1268,6 +1268,12 @@ func (s *Server) listen(network, addr string, lnOn listenOn) (net.Listener, erro
 	return ln, nil
 }
 
+// GetRootPath returns the root path of the tsnet server.
+// This is where the state file and other data is stored.
+func (s *Server) GetRootPath() string {
+	return s.rootPath
+}
+
 // CapturePcap can be called by the application code compiled with tsnet to save a pcap
 // of packets which the netstack within tsnet sees. This is expected to be useful during
 // debugging, probably not useful for production.