func TestParseTLSWithEnvironmentVariables(t *testing.T) { config := configure(fmt.Sprintf(`{ "server": { "tls_cert_file": "%s", "client_ca_file": "nosuchfile" } }`, Cert)) vars := map[string]string{ "SERVER_TLS_KEY_FILE": Key, "SERVER_CLIENT_CA_FILE": Root, } setupEnvironmentVariables(t, vars) defer cleanupEnvironmentVariables(t, vars) tlsConfig, err := ParseServerTLS(config, true) require.NoError(t, err) expectedCert, err := tls.LoadX509KeyPair(Cert, Key) require.NoError(t, err) expectedRoot, err := trustmanager.LoadCertFromFile(Root) require.NoError(t, err) require.Len(t, tlsConfig.Certificates, 1) require.True(t, reflect.DeepEqual(expectedCert, tlsConfig.Certificates[0])) subjects := tlsConfig.ClientCAs.Subjects() require.Len(t, subjects, 1) require.True(t, bytes.Equal(expectedRoot.RawSubject, subjects[0])) require.Equal(t, tlsConfig.ClientAuth, tls.RequireAndVerifyClientCert) }
func TestParseTLSWithTLS(t *testing.T) { config := configure(fmt.Sprintf(`{ "server": { "tls_cert_file": "%s", "tls_key_file": "%s", "client_ca_file": "%s" } }`, Cert, Key, Root)) tlsConfig, err := ParseServerTLS(config, false) require.NoError(t, err) expectedCert, err := tls.LoadX509KeyPair(Cert, Key) require.NoError(t, err) expectedRoot, err := trustmanager.LoadCertFromFile(Root) require.NoError(t, err) require.Len(t, tlsConfig.Certificates, 1) require.True(t, reflect.DeepEqual(expectedCert, tlsConfig.Certificates[0])) subjects := tlsConfig.ClientCAs.Subjects() require.Len(t, subjects, 1) require.True(t, bytes.Equal(expectedRoot.RawSubject, subjects[0])) require.Equal(t, tlsConfig.ClientAuth, tls.RequireAndVerifyClientCert) }
//TODO (diogo): Ask the use if she wants to trust the GUN in the cert func keysTrust(cmd *cobra.Command, args []string) { if len(args) < 1 { cmd.Usage() fatalf("please provide a URL or filename to a certificate") } certLocationStr := args[0] var cert *x509.Certificate // Verify if argument is a valid URL url, err := url.Parse(certLocationStr) if err == nil && url.Scheme != "" { cert, err = trustmanager.GetCertFromURL(certLocationStr) if err != nil { fatalf("error retrieving certificate from url (%s): %v", certLocationStr, err) } } else if _, err := os.Stat(certLocationStr); err == nil { // Try to load the certificate from the file cert, err = trustmanager.LoadCertFromFile(certLocationStr) if err != nil { fatalf("error adding certificate from file: %v", err) } } else { fatalf("please provide a file location or URL for CA certificate.") } // Ask for confirmation before adding certificate into repository fmt.Printf("Are you sure you want to add trust for: %s? (yes/no)\n", cert.Subject.CommonName) confirmed := askConfirm() if !confirmed { fatalf("aborting action.") } err = nil if cert.IsCA { err = caStore.AddCert(cert) } else { err = certificateStore.AddCert(cert) } if err != nil { fatalf("error adding certificate from file: %v", err) } fmt.Printf("Adding: ") printCert(cert) }
func getTransport(gun string, readOnly bool) http.RoundTripper { // Attempt to get a root CA from the config file. Nil is the host defaults. rootPool := x509.NewCertPool() rootCAFile := mainViper.GetString("remote_server.root_ca") if rootCAFile != "" { // If we haven't been given an Absolute path, we assume it's relative // from the configuration directory (~/.notary by default) if !filepath.IsAbs(rootCAFile) { rootCAFile = filepath.Join(configPath, rootCAFile) } rootCert, err := trustmanager.LoadCertFromFile(rootCAFile) if err != nil { fatalf("could not load root ca file. %s", err.Error()) } rootPool.AddCert(rootCert) } // skipTLSVerify is false by default so verification will // be performed. tlsConfig := &tls.Config{ InsecureSkipVerify: mainViper.GetBool("remote_server.skipTLSVerify"), MinVersion: tls.VersionTLS10, RootCAs: rootPool, } base := &http.Transport{ Proxy: http.ProxyFromEnvironment, Dial: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, DualStack: true, }).Dial, TLSHandshakeTimeout: 10 * time.Second, TLSClientConfig: tlsConfig, DisableKeepAlives: true, } return tokenAuth(base, gun, readOnly) }
func TestCertsToRemove(t *testing.T) { // Get a few certificates to test with cert1, err := trustmanager.LoadCertFromFile("../fixtures/secure.example.com.crt") assert.NoError(t, err) cert1KeyID, err := trustmanager.FingerprintCert(cert1) assert.NoError(t, err) // Get intermediate certificate cert2, err := trustmanager.LoadCertFromFile("../fixtures/self-signed_secure.example.com.crt") assert.NoError(t, err) cert2KeyID, err := trustmanager.FingerprintCert(cert2) assert.NoError(t, err) // Get leaf certificate cert3, err := trustmanager.LoadCertFromFile("../fixtures/self-signed_docker.com-notary.crt") assert.NoError(t, err) cert3KeyID, err := trustmanager.FingerprintCert(cert3) assert.NoError(t, err) // Call CertsToRemove with only one old and one new oldCerts := []*x509.Certificate{cert1} newCerts := []*x509.Certificate{cert2} certificates := certsToRemove(oldCerts, newCerts) assert.Len(t, certificates, 1) _, ok := certificates[cert1KeyID] assert.True(t, ok) // Call CertsToRemove with two old and one new oldCerts = []*x509.Certificate{cert1, cert2} newCerts = []*x509.Certificate{cert3} certificates = certsToRemove(oldCerts, newCerts) assert.Len(t, certificates, 2) _, ok = certificates[cert1KeyID] assert.True(t, ok) _, ok = certificates[cert2KeyID] assert.True(t, ok) _, ok = certificates[cert3KeyID] assert.False(t, ok) // Call CertsToRemove with two new and one old oldCerts = []*x509.Certificate{cert3} newCerts = []*x509.Certificate{cert2, cert1} certificates = certsToRemove(oldCerts, newCerts) assert.Len(t, certificates, 1) _, ok = certificates[cert3KeyID] assert.True(t, ok) _, ok = certificates[cert1KeyID] assert.False(t, ok) _, ok = certificates[cert2KeyID] assert.False(t, ok) // Call CertsToRemove with three old certificates and no new oldCerts = []*x509.Certificate{cert1, cert2, cert3} newCerts = []*x509.Certificate{} certificates = certsToRemove(oldCerts, newCerts) assert.Len(t, certificates, 0) _, ok = certificates[cert1KeyID] assert.False(t, ok) _, ok = certificates[cert2KeyID] assert.False(t, ok) _, ok = certificates[cert3KeyID] assert.False(t, ok) // Call CertsToRemove with three new certificates and no old oldCerts = []*x509.Certificate{} newCerts = []*x509.Certificate{cert1, cert2, cert3} certificates = certsToRemove(oldCerts, newCerts) assert.Len(t, certificates, 0) _, ok = certificates[cert1KeyID] assert.False(t, ok) _, ok = certificates[cert2KeyID] assert.False(t, ok) _, ok = certificates[cert3KeyID] assert.False(t, ok) }
func TestValidateRootWithPinnedCA(t *testing.T) { var testSignedRoot data.Signed var signedRootBytes bytes.Buffer // Temporary directory where test files will be created tempBaseDir, err := ioutil.TempDir("", "notary-test-") defer os.RemoveAll(tempBaseDir) require.NoError(t, err, "failed to create a temporary directory: %s", err) templ, _ := template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate) templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validPEMEncodedRSARoot}) // Unmarshal our signedRoot json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot) typedSignedRoot, err := data.RootFromSigned(&testSignedRoot) require.NoError(t, err) // This call to trustpinning.ValidateRoot will fail because we have an invalid path for the CA _, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{CA: map[string]string{"docker.com/notary": filepath.Join(tempBaseDir, "nonexistent")}}) require.Error(t, err) // This call to trustpinning.ValidateRoot will fail because we have no valid GUNs to use, and TOFUS is disabled _, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{CA: map[string]string{"othergun": filepath.Join(tempBaseDir, "nonexistent")}, DisableTOFU: true}) require.Error(t, err) // This call to trustpinning.ValidateRoot will succeed because we have no valid GUNs to use and we fall back to enabled TOFUS validatedRoot, err := trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{CA: map[string]string{"othergun": filepath.Join(tempBaseDir, "nonexistent")}, DisableTOFU: false}) require.NoError(t, err) generateRootKeyIDs(typedSignedRoot) require.Equal(t, typedSignedRoot, validatedRoot) // Write an invalid CA cert (not even a PEM) to the tempDir and ensure validation fails when using it invalidCAFilepath := filepath.Join(tempBaseDir, "invalid.ca") require.NoError(t, ioutil.WriteFile(invalidCAFilepath, []byte("ABSOLUTELY NOT A PEM"), 0644)) // Using this invalid CA cert should fail on trustpinning.ValidateRoot _, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{CA: map[string]string{"docker.com/notary": invalidCAFilepath}, DisableTOFU: true}) require.Error(t, err) validCAFilepath := "../fixtures/root-ca.crt" // If we pass an invalid Certs entry in addition to this valid CA entry, since Certs has priority for pinning we will fail _, err = trustpinning.ValidateRoot(nil, &testSignedRoot, "docker.com/notary", trustpinning.TrustPinConfig{Certs: map[string][]string{"docker.com/notary": {"invalidID"}}, CA: map[string]string{"docker.com/notary": validCAFilepath}, DisableTOFU: true}) require.Error(t, err) // Now construct a new root with a valid cert chain, such that signatures are correct over the 'notary-signer' GUN. Pin the root-ca and validate leafCert, err := trustmanager.LoadCertFromFile("../fixtures/notary-signer.crt") require.NoError(t, err) intermediateCert, err := trustmanager.LoadCertFromFile("../fixtures/intermediate-ca.crt") require.NoError(t, err) pemChainBytes, err := trustmanager.CertChainToPEM([]*x509.Certificate{leafCert, intermediateCert}) require.NoError(t, err) newRootKey := data.NewPublicKey(data.RSAx509Key, pemChainBytes) rootRole, err := data.NewRole(data.CanonicalRootRole, 1, []string{newRootKey.ID()}, nil) require.NoError(t, err) testRoot, err := data.NewRoot( map[string]data.PublicKey{newRootKey.ID(): newRootKey}, map[string]*data.RootRole{ data.CanonicalRootRole: &rootRole.RootRole, data.CanonicalTimestampRole: &rootRole.RootRole, data.CanonicalTargetsRole: &rootRole.RootRole, data.CanonicalSnapshotRole: &rootRole.RootRole}, false, ) testRoot.Signed.Version = 1 require.NoError(t, err, "Failed to create new root") keyReader, err := os.Open("../fixtures/notary-signer.key") require.NoError(t, err, "could not open key file") pemBytes, err := ioutil.ReadAll(keyReader) require.NoError(t, err, "could not read key file") privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, "") require.NoError(t, err) store, err := trustmanager.NewKeyFileStore(tempBaseDir, passphraseRetriever) require.NoError(t, err) cs := cryptoservice.NewCryptoService(store) err = store.AddKey(trustmanager.KeyInfo{Role: data.CanonicalRootRole, Gun: "notary-signer"}, privKey) require.NoError(t, err) newTestSignedRoot, err := testRoot.ToSigned() require.NoError(t, err) err = signed.Sign(cs, newTestSignedRoot, []data.PublicKey{newRootKey}, 1, nil) require.NoError(t, err) newTypedSignedRoot, err := data.RootFromSigned(newTestSignedRoot) require.NoError(t, err) // Check that we validate correctly against a pinned CA and provided bundle validatedRoot, err = trustpinning.ValidateRoot(nil, newTestSignedRoot, "notary-signer", trustpinning.TrustPinConfig{CA: map[string]string{"notary-signer": validCAFilepath}, DisableTOFU: true}) require.NoError(t, err) generateRootKeyIDs(newTypedSignedRoot) require.Equal(t, newTypedSignedRoot, validatedRoot) // Add an expired CA for the same gun to our previous pinned bundle, ensure that we still validate correctly goodRootCABundle, err := trustmanager.LoadCertBundleFromFile(validCAFilepath) require.NoError(t, err) memKeyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever) cryptoService := cryptoservice.NewCryptoService(memKeyStore) testPubKey, err := cryptoService.Create("root", "notary-signer", data.ECDSAKey) require.NoError(t, err) testPrivKey, _, err := memKeyStore.GetKey(testPubKey.ID()) require.NoError(t, err) expiredCert, err := generateExpiredTestingCertificate(testPrivKey, "notary-signer") require.NoError(t, err) bundleWithExpiredCert, err := trustmanager.CertChainToPEM(append(goodRootCABundle, expiredCert)) require.NoError(t, err) bundleWithExpiredCertPath := filepath.Join(tempBaseDir, "bundle_with_expired_cert.pem") require.NoError(t, ioutil.WriteFile(bundleWithExpiredCertPath, bundleWithExpiredCert, 0644)) // Check that we validate correctly against a pinned CA and provided bundle validatedRoot, err = trustpinning.ValidateRoot(nil, newTestSignedRoot, "notary-signer", trustpinning.TrustPinConfig{CA: map[string]string{"notary-signer": bundleWithExpiredCertPath}, DisableTOFU: true}) require.NoError(t, err) require.Equal(t, newTypedSignedRoot, validatedRoot) testPubKey2, err := cryptoService.Create("root", "notary-signer", data.ECDSAKey) require.NoError(t, err) testPrivKey2, _, err := memKeyStore.GetKey(testPubKey2.ID()) require.NoError(t, err) expiredCert2, err := generateExpiredTestingCertificate(testPrivKey2, "notary-signer") require.NoError(t, err) allExpiredCertBundle, err := trustmanager.CertChainToPEM([]*x509.Certificate{expiredCert, expiredCert2}) require.NoError(t, err) allExpiredCertPath := filepath.Join(tempBaseDir, "all_expired_cert.pem") require.NoError(t, ioutil.WriteFile(allExpiredCertPath, allExpiredCertBundle, 0644)) // Now only use expired certs in the bundle, we should fail _, err = trustpinning.ValidateRoot(nil, newTestSignedRoot, "notary-signer", trustpinning.TrustPinConfig{CA: map[string]string{"notary-signer": allExpiredCertPath}, DisableTOFU: true}) require.Error(t, err) // Add a CA cert for a that won't validate against the root leaf certificate testPubKey3, err := cryptoService.Create("root", "notary-signer", data.ECDSAKey) require.NoError(t, err) testPrivKey3, _, err := memKeyStore.GetKey(testPubKey3.ID()) require.NoError(t, err) validCert, err := cryptoservice.GenerateCertificate(testPrivKey3, "notary-signer", time.Now(), time.Now().AddDate(1, 0, 0)) require.NoError(t, err) bundleWithWrongCert, err := trustmanager.CertChainToPEM([]*x509.Certificate{validCert}) require.NoError(t, err) bundleWithWrongCertPath := filepath.Join(tempBaseDir, "bundle_with_expired_cert.pem") require.NoError(t, ioutil.WriteFile(bundleWithWrongCertPath, bundleWithWrongCert, 0644)) _, err = trustpinning.ValidateRoot(nil, newTestSignedRoot, "notary-signer", trustpinning.TrustPinConfig{CA: map[string]string{"notary-signer": bundleWithWrongCertPath}, DisableTOFU: true}) require.Error(t, err) }