@ -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