ipn/ipnlocal: fix the path for writing cert files (#7203)

Fixes #7202.

Change-Id: I1f8e9c59d5e42e7df7a3fbbd82ae2b4293845916
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
This commit is contained in:
M. J. Fromberger
2023-02-07 14:34:04 -08:00
committed by GitHub
parent cab2b2b59e
commit 9be47f789c
5 changed files with 185 additions and 11 deletions

View File

@@ -146,46 +146,56 @@ var errCertExpired = errors.New("cert expired")
func (b *LocalBackend) getCertStore(dir string) certStore {
if hostinfo.GetEnvType() == hostinfo.Kubernetes && dir == "/tmp" {
return certStateStore{b.store}
return certStateStore{StateStore: b.store}
}
return certFileStore(dir)
return certFileStore{dir: dir}
}
// certFileStore implements certStore by storing the cert & key files in the named directory.
type certFileStore string // dir
type certFileStore struct {
dir string
// This field allows a test to override the CA root(s) for certificate
// verification. If nil the default system pool is used.
testRoots *x509.CertPool
}
func (f certFileStore) Read(domain string, now time.Time) (*TLSCertKeyPair, error) {
certPEM, err := os.ReadFile(keyFile(string(f), domain))
certPEM, err := os.ReadFile(certFile(f.dir, domain))
if err != nil {
if os.IsNotExist(err) {
return nil, ipn.ErrStateNotExist
}
return nil, err
}
keyPEM, err := os.ReadFile(certFile(string(f), domain))
keyPEM, err := os.ReadFile(keyFile(f.dir, domain))
if err != nil {
if os.IsNotExist(err) {
return nil, ipn.ErrStateNotExist
}
return nil, err
}
if !validCertPEM(domain, keyPEM, certPEM, now) {
if !validCertPEM(domain, keyPEM, certPEM, f.testRoots, now) {
return nil, errCertExpired
}
return &TLSCertKeyPair{CertPEM: certPEM, KeyPEM: keyPEM, Cached: true}, nil
}
func (f certFileStore) WriteCert(domain string, cert []byte) error {
return os.WriteFile(keyFile(string(f), domain), cert, 0644)
return os.WriteFile(certFile(f.dir, domain), cert, 0644)
}
func (f certFileStore) WriteKey(domain string, key []byte) error {
return os.WriteFile(keyFile(string(f), domain), key, 0600)
return os.WriteFile(keyFile(f.dir, domain), key, 0600)
}
// certStateStore implements certStore by storing the cert & key files in an ipn.StateStore.
type certStateStore struct {
ipn.StateStore
// This field allows a test to override the CA root(s) for certificate
// verification. If nil the default system pool is used.
testRoots *x509.CertPool
}
func (s certStateStore) Read(domain string, now time.Time) (*TLSCertKeyPair, error) {
@@ -197,7 +207,7 @@ func (s certStateStore) Read(domain string, now time.Time) (*TLSCertKeyPair, err
if err != nil {
return nil, err
}
if !validCertPEM(domain, keyPEM, certPEM, now) {
if !validCertPEM(domain, keyPEM, certPEM, s.testRoots, now) {
return nil, errCertExpired
}
return &TLSCertKeyPair{CertPEM: certPEM, KeyPEM: keyPEM, Cached: true}, nil
@@ -458,7 +468,11 @@ func acmeKey(dir string) (crypto.Signer, error) {
return privKey, nil
}
func validCertPEM(domain string, keyPEM, certPEM []byte, now time.Time) bool {
// validCertPEM reports whether the given certificate is valid for domain at now.
//
// If roots != nil, it is used instead of the system root pool. This is meant
// to support testing, and production code should pass roots == nil.
func validCertPEM(domain string, keyPEM, certPEM []byte, roots *x509.CertPool, now time.Time) bool {
if len(keyPEM) == 0 || len(certPEM) == 0 {
return false
}
@@ -466,6 +480,7 @@ func validCertPEM(domain string, keyPEM, certPEM []byte, now time.Time) bool {
if err != nil {
return false
}
var leaf *x509.Certificate
intermediates := x509.NewCertPool()
for i, certDER := range tlsCert.Certificate {
@@ -485,6 +500,7 @@ func validCertPEM(domain string, keyPEM, certPEM []byte, now time.Time) bool {
_, err = leaf.Verify(x509.VerifyOptions{
DNSName: domain,
CurrentTime: now,
Roots: roots,
Intermediates: intermediates,
})
return err == nil