github.com/dddengyunjie/fabric-ca@v0.0.0-20190606043049-92df60ae2f0f/lib/ca.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package lib 8 9 import ( 10 "bytes" 11 "crypto/dsa" 12 "crypto/ecdsa" 13 "crypto/rsa" 14 "crypto/x509" 15 "encoding/pem" 16 "fmt" 17 "io/ioutil" 18 "os" 19 "path" 20 "path/filepath" 21 "strconv" 22 "sync" 23 "time" 24 25 "github.com/cloudflare/cfssl/certdb" 26 "github.com/cloudflare/cfssl/config" 27 cfcsr "github.com/cloudflare/cfssl/csr" 28 "github.com/cloudflare/cfssl/initca" 29 "github.com/cloudflare/cfssl/log" 30 "github.com/cloudflare/cfssl/signer" 31 cflocalsigner "github.com/cloudflare/cfssl/signer/local" 32 "github.com/hyperledger/fabric-ca/api" 33 "github.com/hyperledger/fabric-ca/lib/attr" 34 "github.com/hyperledger/fabric-ca/lib/caerrors" 35 "github.com/hyperledger/fabric-ca/lib/common" 36 "github.com/hyperledger/fabric-ca/lib/metadata" 37 "github.com/hyperledger/fabric-ca/lib/server/db" 38 cadb "github.com/hyperledger/fabric-ca/lib/server/db" 39 cadbfactory "github.com/hyperledger/fabric-ca/lib/server/db/factory" 40 "github.com/hyperledger/fabric-ca/lib/server/db/mysql" 41 "github.com/hyperledger/fabric-ca/lib/server/db/postgres" 42 "github.com/hyperledger/fabric-ca/lib/server/db/sqlite" 43 dbutil "github.com/hyperledger/fabric-ca/lib/server/db/util" 44 idemix "github.com/hyperledger/fabric-ca/lib/server/idemix" 45 "github.com/hyperledger/fabric-ca/lib/server/ldap" 46 "github.com/hyperledger/fabric-ca/lib/server/user" 47 cadbuser "github.com/hyperledger/fabric-ca/lib/server/user" 48 "github.com/hyperledger/fabric-ca/lib/tcert" 49 "github.com/hyperledger/fabric-ca/lib/tls" 50 "github.com/hyperledger/fabric-ca/util" 51 "github.com/hyperledger/fabric/bccsp" 52 "github.com/hyperledger/fabric/bccsp/gm" 53 "github.com/hyperledger/fabric/common/attrmgr" 54 "github.com/pkg/errors" 55 "github.com/tjfoc/gmsm/sm2" 56 ) 57 58 const ( 59 defaultDatabaseType = "sqlite3" 60 // CAChainParentFirstEnvVar is the name of the environment variable that needs to be set 61 // for server to return CA chain in parent-first order 62 CAChainParentFirstEnvVar = "CA_CHAIN_PARENT_FIRST" 63 ) 64 65 var ( 66 // Default root CA certificate expiration is 15 years (in hours). 67 defaultRootCACertificateExpiration = "131400h" 68 // Default intermediate CA certificate expiration is 5 years (in hours). 69 defaultIntermediateCACertificateExpiration = parseDuration("43800h") 70 // Default issued certificate expiration is 1 year (in hours). 71 defaultIssuedCertificateExpiration = parseDuration("8760h") 72 ) 73 74 // CA represents a certificate authority which signs, issues and revokes certificates 75 type CA struct { 76 // The home directory for the CA 77 HomeDir string 78 // The CA's configuration 79 Config *CAConfig 80 // The file path of the config file 81 ConfigFilePath string 82 // The database handle used to store certificates and optionally 83 // the user registry information, unless LDAP it enabled for the 84 // user registry function. 85 db db.FabricCADB 86 // The crypto service provider (BCCSP) 87 csp bccsp.BCCSP 88 // The certificate DB accessor 89 certDBAccessor *CertDBAccessor 90 // The user registry 91 registry user.Registry 92 // The signer used for enrollment 93 enrollSigner signer.Signer 94 // Idemix issuer 95 issuer idemix.Issuer 96 // The options to use in verifying a signature in token-based authentication 97 verifyOptions *x509.VerifyOptions 98 // The attribute manager 99 attrMgr *attrmgr.Mgr 100 // The tcert manager for this CA 101 tcertMgr *tcert.Mgr 102 // The key tree 103 keyTree *tcert.KeyTree 104 // The server hosting this CA 105 server *Server 106 // DB levels 107 levels *dbutil.Levels 108 // CA mutex 109 mutex sync.Mutex 110 } 111 112 const ( 113 certificateError = "Invalid certificate in file" 114 ) 115 116 // newCA creates a new CA with the specified 117 // home directory, parent server URL, and config 118 func newCA(caFile string, config *CAConfig, server *Server, renew bool) (*CA, error) { 119 ca := new(CA) 120 ca.ConfigFilePath = caFile 121 err := initCA(ca, filepath.Dir(caFile), config, server, renew) 122 if err != nil { 123 err2 := ca.closeDB() 124 if err2 != nil { 125 log.Errorf("Close DB failed: %s", err2) 126 } 127 return nil, err 128 } 129 return ca, nil 130 } 131 132 // initCA will initialize the passed in pointer to a CA struct 133 func initCA(ca *CA, homeDir string, config *CAConfig, server *Server, renew bool) error { 134 ca.HomeDir = homeDir 135 ca.Config = config 136 ca.server = server 137 138 err := ca.init(renew) 139 if err != nil { 140 return err 141 } 142 log.Debug("Initializing Idemix issuer...") 143 ca.issuer = idemix.NewIssuer(ca.Config.CA.Name, ca.HomeDir, 144 &ca.Config.Idemix, ca.csp, idemix.NewLib()) 145 err = ca.issuer.Init(renew, ca.db, ca.levels) 146 if err != nil { 147 return errors.WithMessage(err, fmt.Sprintf("Failed to initialize Idemix issuer for CA '%s'", err.Error())) 148 } 149 return nil 150 } 151 152 // Init initializes an instance of a CA 153 func (ca *CA) init(renew bool) (err error) { 154 log.Debugf("Init CA with home %s and config %+v", ca.HomeDir, *ca.Config) 155 156 // Initialize the config, setting defaults, etc 157 log.Info("#################name = ", ca.Config.CSP.ProviderName) 158 SetProviderName(ca.Config.CSP.ProviderName) 159 err = ca.initConfig() 160 if err != nil { 161 return err 162 } 163 164 // Initialize the crypto layer (BCCSP) for this CA 165 ca.csp, err = util.InitBCCSP(&ca.Config.CSP, "", ca.HomeDir) 166 if err != nil { 167 return err 168 } 169 170 // Initialize key materials 171 err = ca.initKeyMaterial(renew) 172 if err != nil { 173 return err 174 } 175 176 // Initialize the database 177 err = ca.initDB() 178 if err != nil { 179 log.Error("Error occurred initializing database: ", err) 180 // Return if a server configuration error encountered (e.g. Invalid max enrollment for a bootstrap user) 181 if caerrors.IsFatalError(err) { 182 return err 183 } 184 } 185 // Initialize the enrollment signer 186 err = ca.initEnrollmentSigner() 187 if err != nil { 188 return err 189 } 190 // Create the attribute manager 191 ca.attrMgr = attrmgr.New() 192 // Initialize TCert handling 193 keyfile := ca.Config.CA.Keyfile 194 certfile := ca.Config.CA.Certfile 195 ca.tcertMgr, err = tcert.LoadMgr(keyfile, certfile, ca.csp) 196 if err != nil { 197 return err 198 } 199 // FIXME: The root prekey must be stored persistently in DB and retrieved here if not found 200 rootKey, err := genRootKey(ca.csp) 201 if err != nil { 202 return err 203 } 204 ca.keyTree = tcert.NewKeyTree(ca.csp, rootKey) 205 log.Debug("CA initialization successful") 206 // Successful initialization 207 return nil 208 } 209 210 // Initialize the CA's key material 211 func (ca *CA) initKeyMaterial(renew bool) error { 212 log.Debug("Initialize key material") 213 214 // Make the path names absolute in the config 215 err := ca.makeFileNamesAbsolute() 216 if err != nil { 217 return err 218 } 219 220 keyFile := ca.Config.CA.Keyfile 221 certFile := ca.Config.CA.Certfile 222 223 // If we aren't renewing and the key and cert files exist, do nothing 224 if !renew { 225 // If they both exist, the CA was already initialized 226 keyFileExists := util.FileExists(keyFile) 227 certFileExists := util.FileExists(certFile) 228 if keyFileExists && certFileExists { 229 log.Info("The CA key and certificate files already exist") 230 log.Infof("Key file location: %s", keyFile) 231 log.Infof("Certificate file location: %s", certFile) 232 err = ca.validateCertAndKey(certFile, keyFile) 233 if err != nil { 234 return errors.WithMessage(err, "Validation of certificate and key failed") 235 } 236 // Load CN from existing enrollment information and set CSR accordingly 237 // CN needs to be set, having a multi CA setup requires a unique CN and can't 238 // be left blank 239 ca.Config.CSR.CN, err = ca.loadCNFromEnrollmentInfo(certFile) 240 if err != nil { 241 return err 242 } 243 return nil 244 } 245 246 // If key file does not exist but certFile does, key file is probably 247 // stored by BCCSP, so check for that now. 248 if certFileExists { 249 _, _, _, err = util.GetSignerFromCertFile(certFile, ca.csp) 250 if err != nil { 251 return errors.WithMessage(err, fmt.Sprintf("Failed to find private key for certificate in '%s'", certFile)) 252 } 253 // Yes, it is stored by BCCSP 254 log.Info("The CA key and certificate already exist") 255 log.Infof("The key is stored by BCCSP provider '%s'", ca.Config.CSP.ProviderName) 256 log.Infof("The certificate is at: %s", certFile) 257 // Load CN from existing enrollment information and set CSR accordingly 258 // CN needs to be set, having a multi CA setup requires a unique CN and can't 259 // be left blank 260 ca.Config.CSR.CN, err = ca.loadCNFromEnrollmentInfo(certFile) 261 if err != nil { 262 return errors.WithMessage(err, fmt.Sprintf("Failed to get CN for certificate in '%s'", certFile)) 263 } 264 return nil 265 } 266 log.Warning(caerrors.NewServerError(caerrors.ErrCACertFileNotFound, "The specified CA certificate file %s does not exist", certFile)) 267 } 268 269 // Get the CA cert 270 cert, err := ca.getCACert() 271 if err != nil { 272 return err 273 } 274 // Store the certificate to file 275 err = writeFile(certFile, cert, 0644) 276 if err != nil { 277 return errors.Wrap(err, "Failed to store certificate") 278 } 279 log.Infof("The CA key and certificate were generated for CA %s", ca.Config.CA.Name) 280 log.Infof("The key was stored by BCCSP provider '%s'", ca.Config.CSP.ProviderName) 281 log.Infof("The certificate is at: %s", certFile) 282 283 return nil 284 } 285 286 // Get the CA certificate for this CA 287 func (ca *CA) getCACert() (cert []byte, err error) { 288 if ca.Config.Intermediate.ParentServer.URL != "" { 289 // This is an intermediate CA, so call the parent fabric-ca-server 290 // to get the cert 291 log.Debugf("Getting CA cert; parent server URL is %s", util.GetMaskedURL(ca.Config.Intermediate.ParentServer.URL)) 292 clientCfg := ca.Config.Client 293 if clientCfg == nil { 294 clientCfg = &ClientConfig{} 295 } 296 // Copy over the intermediate configuration into client configuration 297 clientCfg.TLS = ca.Config.Intermediate.TLS 298 clientCfg.Enrollment = ca.Config.Intermediate.Enrollment 299 clientCfg.CAName = ca.Config.Intermediate.ParentServer.CAName 300 clientCfg.CSP = ca.Config.CSP 301 clientCfg.CSR = ca.Config.CSR 302 clientCfg.CSP = ca.Config.CSP 303 if ca.Config.CSR.CN != "" { 304 return nil, errors.Errorf("CN '%s' cannot be specified for an intermediate CA. Remove CN from CSR section for enrollment of intermediate CA to be successful", ca.Config.CSR.CN) 305 } 306 if clientCfg.Enrollment.Profile == "" { 307 clientCfg.Enrollment.Profile = "ca" 308 } 309 if clientCfg.Enrollment.CSR == nil { 310 clientCfg.Enrollment.CSR = &api.CSRInfo{} 311 } 312 if clientCfg.Enrollment.CSR.CA == nil { 313 clientCfg.Enrollment.CSR.CA = &cfcsr.CAConfig{PathLength: 0, PathLenZero: true} 314 } 315 log.Debugf("Intermediate enrollment request: %+v, CSR: %+v, CA: %+v", 316 clientCfg.Enrollment, clientCfg.Enrollment.CSR, clientCfg.Enrollment.CSR.CA) 317 var resp *EnrollmentResponse 318 resp, err = clientCfg.Enroll(ca.Config.Intermediate.ParentServer.URL, ca.HomeDir) 319 if err != nil { 320 return nil, err 321 } 322 // Set the CN for an intermediate server to be the ID used to enroll with root CA 323 ca.Config.CSR.CN = resp.Identity.GetName() 324 ecert := resp.Identity.GetECert() 325 if ecert == nil { 326 return nil, errors.New("No enrollment certificate returned by parent server") 327 } 328 cert = ecert.Cert() 329 // Store the chain file as the concatenation of the parent's chain plus the cert. 330 chainPath := ca.Config.CA.Chainfile 331 chain, err := ca.concatChain(resp.CAInfo.CAChain, cert) 332 if err != nil { 333 return nil, err 334 } 335 err = os.MkdirAll(path.Dir(chainPath), 0755) 336 if err != nil { 337 return nil, errors.Wrap(err, "Failed to create intermediate chain file directory") 338 } 339 err = util.WriteFile(chainPath, chain, 0644) 340 if err != nil { 341 return nil, errors.WithMessage(err, "Failed to create intermediate chain file") 342 } 343 log.Debugf("Stored intermediate certificate chain at %s", chainPath) 344 } else { 345 // This is a root CA, so create a CSR (Certificate Signing Request) 346 if ca.Config.CSR.CN == "" { 347 ca.Config.CSR.CN = "fabric-ca-server" 348 } 349 csr := &ca.Config.CSR 350 if csr.CA == nil { 351 csr.CA = &cfcsr.CAConfig{} 352 } 353 if csr.CA.Expiry == "" { 354 csr.CA.Expiry = defaultRootCACertificateExpiration 355 } 356 357 if (csr.KeyRequest == nil) || (csr.KeyRequest.Algo == "" && csr.KeyRequest.Size == 0) { 358 csr.KeyRequest = GetKeyRequest(ca.Config) 359 } 360 req := cfcsr.CertificateRequest{ 361 CN: csr.CN, 362 Names: csr.Names, 363 Hosts: csr.Hosts, 364 KeyRequest: &cfcsr.BasicKeyRequest{A: csr.KeyRequest.Algo, S: csr.KeyRequest.Size}, 365 CA: csr.CA, 366 SerialNumber: csr.SerialNumber, 367 } 368 log.Debugf("Root CA certificate request: %+v", req) 369 // Generate the key/signer 370 key, cspSigner, err := util.BCCSPKeyRequestGenerate(&req, ca.csp) 371 if err != nil { 372 return nil, err 373 } 374 // Call CFSSL to initialize the CA 375 if IsGMConfig() { 376 cert, err = createGmSm2Cert(key, &req, cspSigner) 377 } else { 378 cert, _, err = initca.NewFromSigner(&req, cspSigner) 379 } 380 if err != nil { 381 return nil, errors.WithMessage(err, "Failed to create new CA certificate") 382 } 383 } 384 return cert, nil 385 } 386 387 // Return a certificate chain which is the concatenation of chain and cert 388 func (ca *CA) concatChain(chain []byte, cert []byte) ([]byte, error) { 389 result := make([]byte, len(chain)+len(cert)) 390 parentFirst, ok := os.LookupEnv(CAChainParentFirstEnvVar) 391 parentFirstBool := false 392 // If CA_CHAIN_PARENT_FIRST env variable is set then get the boolean 393 // value 394 if ok { 395 var err error 396 parentFirstBool, err = strconv.ParseBool(parentFirst) 397 if err != nil { 398 return nil, errors.Wrapf(err, "failed to parse the environment variable '%s'", CAChainParentFirstEnvVar) 399 } 400 } 401 if parentFirstBool { 402 copy(result[:len(chain)], chain) 403 copy(result[len(chain):], cert) 404 } else { 405 copy(result[:len(cert)], cert) 406 copy(result[len(cert):], chain) 407 } 408 return result, nil 409 } 410 411 // Get the certificate chain for the CA 412 func (ca *CA) getCAChain() (chain []byte, err error) { 413 if ca.Config == nil { 414 return nil, errors.New("The server has no configuration") 415 } 416 certAuth := &ca.Config.CA 417 // If the chain file exists, we always return the chain from here 418 if util.FileExists(certAuth.Chainfile) { 419 return util.ReadFile(certAuth.Chainfile) 420 } 421 // Otherwise, if this is a root CA, we always return the contents of the CACertfile 422 if ca.Config.Intermediate.ParentServer.URL == "" { 423 return util.ReadFile(certAuth.Certfile) 424 } 425 // If this is an intermediate CA but the ca.Chainfile doesn't exist, 426 // it is an error. It should have been created during intermediate CA enrollment. 427 return nil, errors.Errorf("Chain file does not exist at %s", certAuth.Chainfile) 428 } 429 430 // Initialize the configuration for the CA setting any defaults and making filenames absolute 431 func (ca *CA) initConfig() (err error) { 432 // Init home directory if not set 433 if ca.HomeDir == "" { 434 ca.HomeDir, err = os.Getwd() 435 if err != nil { 436 return errors.Wrap(err, "Failed to initialize CA's home directory") 437 } 438 } 439 log.Debugf("CA Home Directory: %s", ca.HomeDir) 440 // Init config if not set 441 if ca.Config == nil { 442 ca.Config = new(CAConfig) 443 ca.Config.Registry.MaxEnrollments = -1 444 } 445 // Set config defaults 446 cfg := ca.Config 447 if cfg.Version == "" { 448 cfg.Version = "0" 449 } 450 if cfg.CA.Certfile == "" { 451 cfg.CA.Certfile = "ca-cert.pem" 452 } 453 if cfg.CA.Keyfile == "" { 454 cfg.CA.Keyfile = "ca-key.pem" 455 } 456 if cfg.CA.Chainfile == "" { 457 cfg.CA.Chainfile = "ca-chain.pem" 458 } 459 if cfg.CSR.CA == nil { 460 cfg.CSR.CA = &cfcsr.CAConfig{} 461 } 462 if cfg.CSR.CA.Expiry == "" { 463 cfg.CSR.CA.Expiry = defaultRootCACertificateExpiration 464 } 465 if cfg.Signing == nil { 466 cfg.Signing = &config.Signing{} 467 } 468 cs := cfg.Signing 469 if cs.Profiles == nil { 470 cs.Profiles = make(map[string]*config.SigningProfile) 471 } 472 caProfile := cs.Profiles["ca"] 473 initSigningProfile(&caProfile, 474 defaultIntermediateCACertificateExpiration, 475 true) 476 cs.Profiles["ca"] = caProfile 477 initSigningProfile( 478 &cs.Default, 479 defaultIssuedCertificateExpiration, 480 false) 481 tlsProfile := cs.Profiles["tls"] 482 initSigningProfile(&tlsProfile, 483 defaultIssuedCertificateExpiration, 484 false) 485 cs.Profiles["tls"] = tlsProfile 486 err = ca.checkConfigLevels() 487 if err != nil { 488 return err 489 } 490 // Set log level if debug is true 491 if ca.server != nil && ca.server.Config != nil && ca.server.Config.Debug { 492 log.Level = log.LevelDebug 493 } 494 ca.normalizeStringSlices() 495 496 return nil 497 } 498 499 func getVerifyOptions(ca *CA) (*sm2.VerifyOptions, error) { 500 chain, err := ca.getCAChain() 501 if err != nil { 502 return nil, err 503 } 504 block, rest := pem.Decode(chain) 505 if block == nil { 506 return nil, errors.New("No root certificate was found") 507 } 508 rootCert, err := sm2.ParseCertificate(block.Bytes) 509 if err != nil { 510 return nil, fmt.Errorf("Failed to parse root certificate: %s", err) 511 } 512 rootPool := sm2.NewCertPool() 513 rootPool.AddCert(rootCert) 514 var intPool *sm2.CertPool 515 if len(rest) > 0 { 516 intPool = sm2.NewCertPool() 517 if !intPool.AppendCertsFromPEM(rest) { 518 return nil, errors.New("Failed to add intermediate PEM certificates") 519 } 520 } 521 return &sm2.VerifyOptions{ 522 Roots: rootPool, 523 Intermediates: intPool, 524 KeyUsages: []sm2.ExtKeyUsage{sm2.ExtKeyUsageAny}, 525 }, nil 526 } 527 528 // VerifyCertificate verifies that 'cert' was issued by this CA 529 // Return nil if successful; otherwise, return an error. 530 func (ca *CA) VerifyCertificate(cert *x509.Certificate) error { 531 sm2Cert := gm.ParseX509Certificate2Sm2(cert) 532 opts, err := getVerifyOptions(ca) 533 if err != nil { 534 return errors.WithMessage(err, "Failed to get verify options") 535 } 536 _, err = sm2Cert.Verify(*opts) 537 if err != nil { 538 return errors.WithMessage(err, "Failed to verify certificate") 539 } 540 return nil 541 } 542 543 // Get the options to verify 544 func (ca *CA) getVerifyOptions() (*x509.VerifyOptions, error) { 545 if ca.verifyOptions != nil { 546 return ca.verifyOptions, nil 547 } 548 chain, err := ca.getCAChain() 549 if err != nil { 550 return nil, err 551 } 552 var intPool *x509.CertPool 553 var rootPool *x509.CertPool 554 555 for len(chain) > 0 { 556 var block *pem.Block 557 block, chain = pem.Decode(chain) 558 if block == nil { 559 break 560 } 561 if block.Type != "CERTIFICATE" { 562 continue 563 } 564 565 cert, err := x509.ParseCertificate(block.Bytes) 566 if err != nil { 567 return nil, errors.Wrap(err, "Failed to parse CA chain certificate") 568 } 569 570 if !cert.IsCA { 571 return nil, errors.New("A certificate in the CA chain is not a CA certificate") 572 } 573 574 // If authority key id is not present or if it is present and equal to subject key id, 575 // then it is a root certificate 576 if len(cert.AuthorityKeyId) == 0 || bytes.Equal(cert.AuthorityKeyId, cert.SubjectKeyId) { 577 if rootPool == nil { 578 rootPool = x509.NewCertPool() 579 } 580 rootPool.AddCert(cert) 581 } else { 582 if intPool == nil { 583 intPool = x509.NewCertPool() 584 } 585 intPool.AddCert(cert) 586 } 587 } 588 589 ca.verifyOptions = &x509.VerifyOptions{ 590 Roots: rootPool, 591 Intermediates: intPool, 592 KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, 593 } 594 return ca.verifyOptions, nil 595 } 596 597 // Initialize the database for the CA 598 func (ca *CA) initDB() error { 599 log.Debug("Initializing DB") 600 601 // If DB is initialized, don't need to proceed further 602 if ca.db != nil && ca.db.IsInitialized() { 603 return nil 604 } 605 606 ca.mutex.Lock() 607 defer ca.mutex.Unlock() 608 609 // After obtaining a lock, check again to see if DB got initialized by another process 610 if ca.db != nil && ca.db.IsInitialized() { 611 return nil 612 } 613 614 dbCfg := &ca.Config.DB 615 dbError := false 616 var err error 617 618 if dbCfg.Type == "" || dbCfg.Type == defaultDatabaseType { 619 620 dbCfg.Type = defaultDatabaseType 621 622 if dbCfg.Datasource == "" { 623 dbCfg.Datasource = "fabric-ca-server.db" 624 } 625 626 dbCfg.Datasource, err = util.MakeFileAbs(dbCfg.Datasource, ca.HomeDir) 627 if err != nil { 628 return err 629 } 630 } 631 632 // Strip out user:pass from datasource for logging 633 ds := dbCfg.Datasource 634 ds = dbutil.MaskDBCred(ds) 635 636 log.Debugf("Initializing '%s' database at '%s'", dbCfg.Type, ds) 637 caDB, err := cadbfactory.New(dbCfg.Type, dbCfg.Datasource, ca.Config.CA.Name, &dbCfg.TLS, ca.csp, ca.server.Operations) 638 if err != nil { 639 return err 640 } 641 err = caDB.Connect() 642 if err != nil { 643 return err 644 } 645 sqlxdb, err := caDB.Create() 646 if err != nil { 647 return err 648 } 649 650 ca.db = sqlxdb 651 // Set the certificate DB accessor 652 ca.certDBAccessor = NewCertDBAccessor(ca.db, ca.levels.Certificate) 653 654 // If DB initialization fails and we need to reinitialize DB, need to make sure to set the DB accessor for the signer 655 if ca.enrollSigner != nil { 656 ca.enrollSigner.SetDBAccessor(ca.certDBAccessor) 657 } 658 659 // Initialize user registry to either use DB or LDAP 660 err = ca.initUserRegistry() 661 if err != nil { 662 return err 663 } 664 665 err = ca.checkDBLevels() 666 if err != nil { 667 return err 668 } 669 670 // Migrate the database 671 curLevels, err := cadb.CurrentDBLevels(ca.db) 672 if err != nil { 673 return errors.Wrap(err, "Failed to current ca levels") 674 } 675 migrator, err := getMigrator(ca.db.DriverName(), ca.db.BeginTx(), curLevels, ca.server.levels) 676 if err != nil { 677 return errors.Wrap(err, "Failed to get migrator") 678 } 679 err = db.Migrate(migrator, curLevels, ca.server.levels) 680 if err != nil { 681 return errors.Wrap(err, "Failed to migrate database") 682 } 683 684 // If not using LDAP, migrate database if needed to latest version and load the users and affiliations table 685 if !ca.Config.LDAP.Enabled { 686 err = ca.loadUsersTable() 687 if err != nil { 688 log.Error(err) 689 dbError = true 690 if caerrors.IsFatalError(err) { 691 return err 692 } 693 } 694 695 err = ca.loadAffiliationsTable() 696 if err != nil { 697 log.Error(err) 698 dbError = true 699 } 700 } 701 702 if dbError { 703 return errors.Errorf("Failed to initialize %s database at %s ", dbCfg.Type, ds) 704 } 705 706 ca.db.SetDBInitialized(true) 707 log.Infof("Initialized %s database at %s", dbCfg.Type, ds) 708 709 return nil 710 } 711 712 // Close CA's DB 713 func (ca *CA) closeDB() error { 714 if ca.db != nil { 715 err := ca.db.Close() 716 ca.db = nil 717 if err != nil { 718 return errors.Wrapf(err, "Failed to close CA database, where CA home directory is '%s'", ca.HomeDir) 719 } 720 } 721 return nil 722 } 723 724 // Initialize the user registry interface 725 func (ca *CA) initUserRegistry() error { 726 log.Debug("Initializing identity registry") 727 var err error 728 ldapCfg := &ca.Config.LDAP 729 730 if ldapCfg.Enabled { 731 // Use LDAP for the user registry 732 ca.registry, err = ldap.NewClient(ldapCfg, ca.server.csp) 733 log.Debugf("Initialized LDAP identity registry; err=%s", err) 734 if err == nil { 735 log.Info("Successfully initialized LDAP client") 736 } else { 737 log.Warningf("Failed to initialize LDAP client; err=%s", err) 738 } 739 return err 740 } 741 742 // Use the DB for the user registry 743 ca.registry = NewDBAccessor(ca.db) 744 log.Debug("Initialized DB identity registry") 745 return nil 746 } 747 748 // Initialize the enrollment signer 749 func (ca *CA) initEnrollmentSigner() (err error) { 750 log.Debug("Initializing enrollment signer") 751 c := ca.Config 752 753 // If there is a config, use its signing policy. Otherwise create a default policy. 754 var policy *config.Signing 755 if c.Signing != nil { 756 policy = c.Signing 757 } else { 758 policy = &config.Signing{ 759 Profiles: map[string]*config.SigningProfile{}, 760 Default: config.DefaultConfig(), 761 } 762 policy.Default.CAConstraint.IsCA = true 763 } 764 765 // Make sure the policy reflects the new remote 766 parentServerURL := ca.Config.Intermediate.ParentServer.URL 767 if parentServerURL != "" { 768 err = policy.OverrideRemotes(parentServerURL) 769 if err != nil { 770 return errors.Wrap(err, "Failed initializing enrollment signer") 771 } 772 } 773 774 ca.enrollSigner, err = util.BccspBackedSigner(c.CA.Certfile, c.CA.Keyfile, policy, ca.csp) 775 if err != nil { 776 return err 777 } 778 ca.enrollSigner.SetDBAccessor(ca.certDBAccessor) 779 780 // Successful enrollment 781 return nil 782 } 783 784 // loadUsersTable adds the configured users to the table if not already found 785 func (ca *CA) loadUsersTable() error { 786 log.Debug("Loading identity table") 787 registry := &ca.Config.Registry 788 for _, id := range registry.Identities { 789 log.Debugf("Loading identity '%s'", id.Name) 790 err := ca.addIdentity(&id, false) 791 if err != nil { 792 return errors.WithMessage(err, "Failed to load identity table") 793 } 794 } 795 log.Debug("Successfully loaded identity table") 796 return nil 797 } 798 799 // loadAffiliationsTable adds the configured affiliations to the table 800 func (ca *CA) loadAffiliationsTable() error { 801 log.Debug("Loading affiliations table") 802 err := ca.loadAffiliationsTableR(ca.Config.Affiliations, "") 803 if err != nil { 804 return errors.WithMessage(err, "Failed to load affiliations table") 805 } 806 log.Debug("Successfully loaded affiliations table") 807 return nil 808 } 809 810 // Recursive function to load the affiliations table hierarchy 811 func (ca *CA) loadAffiliationsTableR(val interface{}, parentPath string) (err error) { 812 var path string 813 if val == nil { 814 return nil 815 } 816 switch val.(type) { 817 case string: 818 path = affiliationPath(val.(string), parentPath) 819 err = ca.addAffiliation(path, parentPath) 820 if err != nil { 821 return err 822 } 823 case []string: 824 for _, ele := range val.([]string) { 825 err = ca.loadAffiliationsTableR(ele, parentPath) 826 if err != nil { 827 return err 828 } 829 } 830 case []interface{}: 831 for _, ele := range val.([]interface{}) { 832 err = ca.loadAffiliationsTableR(ele, parentPath) 833 if err != nil { 834 return err 835 } 836 } 837 default: 838 for name, ele := range val.(map[string]interface{}) { 839 path = affiliationPath(name, parentPath) 840 err = ca.addAffiliation(path, parentPath) 841 if err != nil { 842 return err 843 } 844 err = ca.loadAffiliationsTableR(ele, path) 845 if err != nil { 846 return err 847 } 848 } 849 } 850 return nil 851 } 852 853 // Add an identity to the registry 854 func (ca *CA) addIdentity(id *CAConfigIdentity, errIfFound bool) error { 855 var err error 856 user, _ := ca.registry.GetUser(id.Name, nil) 857 if user != nil { 858 if errIfFound { 859 return errors.Errorf("Identity '%s' is already registered", id.Name) 860 } 861 log.Debugf("Identity '%s' already registered, loaded identity", user.GetName()) 862 return nil 863 } 864 865 id.MaxEnrollments, err = getMaxEnrollments(id.MaxEnrollments, ca.Config.Registry.MaxEnrollments) 866 if err != nil { 867 return caerrors.NewFatalError(caerrors.ErrConfig, "Configuration Error: %s", err) 868 } 869 870 attrs, err := attr.ConvertAttrs(id.Attrs) 871 872 if err != nil { 873 return err 874 } 875 876 rec := cadbuser.Info{ 877 Name: id.Name, 878 Pass: id.Pass, 879 Type: id.Type, 880 Affiliation: id.Affiliation, 881 Attributes: attrs, 882 MaxEnrollments: id.MaxEnrollments, 883 Level: ca.levels.Identity, 884 } 885 err = ca.registry.InsertUser(&rec) 886 if err != nil { 887 return errors.WithMessage(err, fmt.Sprintf("Failed to insert identity '%s'", id.Name)) 888 } 889 log.Debugf("Registered identity: %+v", id) 890 return nil 891 } 892 893 func (ca *CA) addAffiliation(path, parentPath string) error { 894 return ca.registry.InsertAffiliation(path, parentPath, ca.levels.Affiliation) 895 } 896 897 // CertDBAccessor returns the certificate DB accessor for CA 898 func (ca *CA) CertDBAccessor() *CertDBAccessor { 899 return ca.certDBAccessor 900 } 901 902 // DBAccessor returns the registry DB accessor for server 903 func (ca *CA) DBAccessor() user.Registry { 904 return ca.registry 905 } 906 907 // GetDB returns pointer to database 908 func (ca *CA) GetDB() db.FabricCADB { 909 return ca.db 910 } 911 912 // GetCertificate returns a single certificate matching serial and aki, if multiple certificates 913 // found for serial and aki an error is returned 914 func (ca *CA) GetCertificate(serial, aki string) (*certdb.CertificateRecord, error) { 915 certs, err := ca.CertDBAccessor().GetCertificate(serial, aki) 916 if err != nil { 917 return nil, caerrors.NewHTTPErr(500, caerrors.ErrCertNotFound, "Failed searching certificates: %s", err) 918 } 919 if len(certs) == 0 { 920 return nil, caerrors.NewAuthenticationErr(caerrors.ErrCertNotFound, "Certificate not found with AKI '%s' and serial '%s'", aki, serial) 921 } 922 if len(certs) > 1 { 923 return nil, caerrors.NewAuthenticationErr(caerrors.ErrCertNotFound, "Multiple certificates found, when only should exist with AKI '%s' and serial '%s' combination", aki, serial) 924 } 925 return &certs[0], nil 926 } 927 928 // Make all file names in the CA config absolute 929 func (ca *CA) makeFileNamesAbsolute() error { 930 log.Debug("Making CA filenames absolute") 931 932 fields := []*string{ 933 &ca.Config.CA.Certfile, 934 &ca.Config.CA.Keyfile, 935 &ca.Config.CA.Chainfile, 936 } 937 err := util.MakeFileNamesAbsolute(fields, ca.HomeDir) 938 if err != nil { 939 return err 940 } 941 err = tls.AbsTLSClient(&ca.Config.DB.TLS, ca.HomeDir) 942 if err != nil { 943 return err 944 } 945 err = tls.AbsTLSClient(&ca.Config.LDAP.TLS, ca.HomeDir) 946 if err != nil { 947 return err 948 } 949 return nil 950 } 951 952 // Convert all comma separated strings to string arrays 953 func (ca *CA) normalizeStringSlices() { 954 fields := []*[]string{ 955 &ca.Config.CSR.Hosts, 956 &ca.Config.DB.TLS.CertFiles, 957 &ca.Config.LDAP.TLS.CertFiles, 958 } 959 for _, namePtr := range fields { 960 norm := util.NormalizeStringSlice(*namePtr) 961 *namePtr = norm 962 } 963 } 964 965 // userHasAttribute returns nil error and the value of the attribute 966 // if the user has the attribute, or an appropriate error if the user 967 // does not have this attribute. 968 func (ca *CA) userHasAttribute(username, attrname string) (string, error) { 969 val, err := ca.getUserAttrValue(username, attrname) 970 if err != nil { 971 return "", err 972 } 973 if val == "" { 974 return "", errors.Errorf("Identity '%s' does not have attribute '%s'", username, attrname) 975 } 976 return val, nil 977 } 978 979 // attributeIsTrue returns nil if the attribute has 980 // one of the following values: "1", "t", "T", "true", "TRUE", "True"; 981 // otherwise it will return an error 982 func (ca *CA) attributeIsTrue(username, attrname string) error { 983 val, err := ca.userHasAttribute(username, attrname) 984 if err != nil { 985 return err 986 } 987 val2, err := strconv.ParseBool(val) 988 if err != nil { 989 return errors.Wrapf(err, "Invalid value for attribute '%s' of identity '%s'", attrname, username) 990 } 991 if val2 { 992 return nil 993 } 994 return errors.Errorf("Attribute '%s' is not set to true for identity '%s'", attrname, username) 995 } 996 997 // getUserAttrValue returns a user's value for an attribute 998 func (ca *CA) getUserAttrValue(username, attrname string) (string, error) { 999 log.Debugf("getUserAttrValue identity=%s, attr=%s", username, attrname) 1000 user, err := ca.registry.GetUser(username, []string{attrname}) 1001 if err != nil { 1002 return "", err 1003 } 1004 attrval, err := user.GetAttribute(attrname) 1005 if err != nil { 1006 return "", errors.WithMessage(err, fmt.Sprintf("Failed to get attribute '%s' for user '%s'", attrname, user.GetName())) 1007 } 1008 log.Debugf("getUserAttrValue identity=%s, name=%s, value=%s", username, attrname, attrval) 1009 return attrval.Value, nil 1010 } 1011 1012 // getUserAffiliation returns a user's affiliation 1013 func (ca *CA) getUserAffiliation(username string) (string, error) { 1014 log.Debugf("getUserAffilliation identity=%s", username) 1015 user, err := ca.registry.GetUser(username, nil) 1016 if err != nil { 1017 return "", err 1018 } 1019 aff := cadbuser.GetAffiliation(user) 1020 log.Debugf("getUserAffiliation identity=%s, aff=%s", username, aff) 1021 return aff, nil 1022 } 1023 1024 // fillCAInfo fills the CA info structure appropriately 1025 func (ca *CA) fillCAInfo(info *common.CAInfoResponseNet) error { 1026 caChain, err := ca.getCAChain() 1027 if err != nil { 1028 return err 1029 } 1030 info.CAName = ca.Config.CA.Name 1031 info.CAChain = util.B64Encode(caChain) 1032 1033 ipkBytes, err := ca.issuer.IssuerPublicKey() 1034 if err != nil { 1035 return err 1036 } 1037 rpkBytes, err := ca.issuer.RevocationPublicKey() 1038 if err != nil { 1039 return err 1040 } 1041 info.IssuerPublicKey = util.B64Encode(ipkBytes) 1042 info.IssuerRevocationPublicKey = util.B64Encode(rpkBytes) 1043 return nil 1044 } 1045 1046 // Perfroms checks on the provided CA cert to make sure it's valid 1047 func (ca *CA) validateCertAndKey(certFile string, keyFile string) error { 1048 log.Debug("Validating the CA certificate and key") 1049 var err error 1050 var certPEM []byte 1051 1052 certPEM, err = ioutil.ReadFile(certFile) 1053 if err != nil { 1054 return errors.Wrapf(err, certificateError+" '%s'", certFile) 1055 } 1056 1057 cert, err := util.GetX509CertificateFromPEM(certPEM) 1058 if err != nil { 1059 return errors.WithMessage(err, fmt.Sprintf(certificateError+" '%s'", certFile)) 1060 } 1061 1062 if err = validateDates(cert); err != nil { 1063 return errors.WithMessage(err, fmt.Sprintf(certificateError+" '%s'", certFile)) 1064 } 1065 if err = validateUsage(cert, ca.Config.CA.Name); err != nil { 1066 return errors.WithMessage(err, fmt.Sprintf(certificateError+" '%s'", certFile)) 1067 } 1068 if err = validateIsCA(cert); err != nil { 1069 return errors.WithMessage(err, fmt.Sprintf(certificateError+" '%s'", certFile)) 1070 } 1071 if err = validateKeyType(cert); err != nil { 1072 return errors.WithMessage(err, fmt.Sprintf(certificateError+" '%s'", certFile)) 1073 } 1074 if err = validateKeySize(cert); err != nil { 1075 return errors.WithMessage(err, fmt.Sprintf(certificateError+" '%s'", certFile)) 1076 } 1077 if err = validateMatchingKeys(cert, keyFile); err != nil { 1078 return errors.WithMessage(err, fmt.Sprintf("Invalid certificate and/or key in files '%s' and '%s'", certFile, keyFile)) 1079 } 1080 log.Debug("Validation of CA certificate and key successful") 1081 1082 return nil 1083 } 1084 1085 // Returns expiration of the CA certificate 1086 func (ca *CA) getCACertExpiry() (time.Time, error) { 1087 var caexpiry time.Time 1088 signer, ok := ca.enrollSigner.(*cflocalsigner.Signer) 1089 if ok { 1090 cacert, err := signer.Certificate("", "ca") 1091 if err != nil { 1092 log.Errorf("Failed to get CA certificate for CA %s: %s", ca.Config.CA.Name, err) 1093 return caexpiry, err 1094 } else if cacert != nil { 1095 caexpiry = cacert.NotAfter 1096 } 1097 } else { 1098 log.Errorf("Not expected condition as the enrollSigner can only be cfssl/signer/local/Signer") 1099 return caexpiry, errors.New("Unexpected error while getting CA certificate expiration") 1100 } 1101 return caexpiry, nil 1102 } 1103 1104 func canSignCRL(cert *x509.Certificate) bool { 1105 return cert.KeyUsage&x509.KeyUsageCRLSign != 0 1106 } 1107 1108 func validateDates(cert *x509.Certificate) error { 1109 log.Debug("Check CA certificate for valid dates") 1110 1111 notAfter := cert.NotAfter 1112 currentTime := time.Now().UTC() 1113 1114 if currentTime.After(notAfter) { 1115 return errors.New("Certificate provided has expired") 1116 } 1117 1118 notBefore := cert.NotBefore 1119 if currentTime.Before(notBefore) { 1120 return errors.New("Certificate provided not valid until later date") 1121 } 1122 1123 return nil 1124 } 1125 1126 func validateUsage(cert *x509.Certificate, caName string) error { 1127 log.Debug("Check CA certificate for valid usages") 1128 1129 if cert.KeyUsage == 0 { 1130 return errors.New("No usage specified for certificate") 1131 } 1132 1133 if cert.KeyUsage&x509.KeyUsageCertSign == 0 { 1134 return errors.New("The 'cert sign' key usage is required") 1135 } 1136 1137 if !canSignCRL(cert) { 1138 log.Warningf("The CA certificate for the CA '%s' does not have 'crl sign' key usage, so the CA will not be able generate a CRL", caName) 1139 } 1140 return nil 1141 } 1142 1143 func validateIsCA(cert *x509.Certificate) error { 1144 log.Debug("Check CA certificate for valid IsCA value") 1145 1146 if !cert.IsCA { 1147 return errors.New("Certificate not configured to be used for CA") 1148 } 1149 1150 return nil 1151 } 1152 1153 func validateKeyType(cert *x509.Certificate) error { 1154 log.Debug("Check that key type is supported") 1155 1156 switch cert.PublicKey.(type) { 1157 case *dsa.PublicKey: 1158 return errors.New("Unsupported key type: DSA") 1159 } 1160 1161 return nil 1162 } 1163 1164 func validateKeySize(cert *x509.Certificate) error { 1165 log.Debug("Check that key size is of appropriate length") 1166 1167 switch cert.PublicKey.(type) { 1168 case *rsa.PublicKey: 1169 size := cert.PublicKey.(*rsa.PublicKey).N.BitLen() 1170 if size < 2048 { 1171 return errors.New("Key size is less than 2048 bits") 1172 } 1173 } 1174 1175 return nil 1176 } 1177 1178 func validateMatchingKeys(cert *x509.Certificate, keyFile string) error { 1179 log.Debug("Check that public key and private key match") 1180 1181 keyPEM, err := ioutil.ReadFile(keyFile) 1182 if err != nil { 1183 return err 1184 } 1185 1186 pubKey := cert.PublicKey 1187 switch pubKey.(type) { 1188 case *rsa.PublicKey: 1189 privKey, err := util.GetRSAPrivateKey(keyPEM) 1190 if err != nil { 1191 return err 1192 } 1193 1194 if privKey.PublicKey.N.Cmp(pubKey.(*rsa.PublicKey).N) != 0 { 1195 return errors.New("Public key and private key do not match") 1196 } 1197 case *ecdsa.PublicKey: 1198 log.Debug("Check that public key and private key match11") 1199 pub, _ := cert.PublicKey.(*ecdsa.PublicKey) 1200 log.Debug("check public key") 1201 switch pub.Curve { 1202 case sm2.P256Sm2(): 1203 privKey, err := util.GetSM2PrivateKey(keyPEM) 1204 if err != nil { 1205 return err 1206 } 1207 if pub.X.Cmp(privKey.X) != 0 || pub.Y.Cmp(privKey.Y) != 0 { 1208 return errors.New("sm2 private key does not match public key") 1209 } 1210 default: 1211 privKey, err := util.GetECPrivateKey(keyPEM) 1212 if err != nil { 1213 return err 1214 } 1215 1216 if privKey.PublicKey.X.Cmp(pubKey.(*ecdsa.PublicKey).X) != 0 { 1217 return errors.New("Public key and private key do not match") 1218 } 1219 } 1220 } 1221 1222 return nil 1223 } 1224 1225 // Load CN from existing enrollment information 1226 func (ca *CA) loadCNFromEnrollmentInfo(certFile string) (string, error) { 1227 log.Debug("Loading CN from existing enrollment information") 1228 cert, err := util.ReadFile(certFile) 1229 if err != nil { 1230 log.Debugf("No cert found at %s", certFile) 1231 return "", err 1232 } 1233 name, err := util.GetEnrollmentIDFromPEM(cert) 1234 if err != nil { 1235 return "", err 1236 } 1237 return name, nil 1238 } 1239 1240 // This function returns an error if the version specified in the configuration file is greater than the server version 1241 func (ca *CA) checkConfigLevels() error { 1242 var err error 1243 serverVersion := metadata.GetVersion() 1244 configVersion := ca.Config.Version 1245 log.Debugf("Checking configuration file version '%+v' against server version: '%+v'", configVersion, serverVersion) 1246 // Check configuration file version against server version to make sure that newer configuration file is not being used with server 1247 cmp, err := metadata.CmpVersion(configVersion, serverVersion) 1248 if err != nil { 1249 return errors.WithMessage(err, "Failed to compare version") 1250 } 1251 if cmp == -1 { 1252 return fmt.Errorf("Configuration file version '%s' is higher than server version '%s'", configVersion, serverVersion) 1253 } 1254 cfg, err := metadata.GetLevels(ca.Config.Version) 1255 if err != nil { 1256 return err 1257 } 1258 ca.levels = cfg 1259 return nil 1260 } 1261 1262 func (ca *CA) checkDBLevels() error { 1263 // Check database table levels against server levels to make sure that a database levels are compatible with server 1264 levels, err := db.CurrentDBLevels(ca.db) 1265 if err != nil { 1266 return err 1267 } 1268 sl, err := metadata.GetLevels(metadata.GetVersion()) 1269 if err != nil { 1270 return err 1271 } 1272 log.Debugf("Checking database levels '%+v' against server levels '%+v'", levels, sl) 1273 if (levels.Identity > sl.Identity) || (levels.Affiliation > sl.Affiliation) || (levels.Certificate > sl.Certificate) || 1274 (levels.Credential > sl.Credential) || (levels.Nonce > sl.Nonce) || (levels.RAInfo > sl.RAInfo) { 1275 return caerrors.NewFatalError(caerrors.ErrDBLevel, "The version of the database is newer than the server version. Upgrade your server.") 1276 } 1277 return nil 1278 } 1279 1280 func writeFile(file string, buf []byte, perm os.FileMode) error { 1281 err := os.MkdirAll(filepath.Dir(file), 0755) 1282 if err != nil { 1283 return err 1284 } 1285 return ioutil.WriteFile(file, buf, perm) 1286 } 1287 1288 func affiliationPath(name, parent string) string { 1289 if parent == "" { 1290 return name 1291 } 1292 return fmt.Sprintf("%s.%s", parent, name) 1293 } 1294 1295 func parseDuration(str string) time.Duration { 1296 d, err := time.ParseDuration(str) 1297 if err != nil { 1298 panic(err) 1299 } 1300 return d 1301 } 1302 1303 func initSigningProfile(spp **config.SigningProfile, expiry time.Duration, isCA bool) { 1304 sp := *spp 1305 if sp == nil { 1306 sp = &config.SigningProfile{CAConstraint: config.CAConstraint{IsCA: isCA}} 1307 *spp = sp 1308 } 1309 if sp.Usage == nil { 1310 sp.Usage = []string{"cert sign", "crl sign"} 1311 } 1312 if sp.Expiry == 0 { 1313 sp.Expiry = expiry 1314 } 1315 if sp.ExtensionWhitelist == nil { 1316 sp.ExtensionWhitelist = map[string]bool{} 1317 } 1318 // This is set so that all profiles permit an attribute extension in CFSSL 1319 sp.ExtensionWhitelist[attrmgr.AttrOIDString] = true 1320 } 1321 1322 type wallClock struct{} 1323 1324 func (wc wallClock) Now() time.Time { 1325 return time.Now() 1326 } 1327 1328 func getMigrator(driverName string, tx cadb.FabricCATx, curLevels, srvLevels *dbutil.Levels) (cadb.Migrator, error) { 1329 var migrator cadb.Migrator 1330 switch driverName { 1331 case "sqlite3": 1332 migrator = sqlite.NewMigrator(tx, curLevels, srvLevels) 1333 case "mysql": 1334 migrator = mysql.NewMigrator(tx, curLevels, srvLevels) 1335 case "postgres": 1336 migrator = postgres.NewMigrator(tx, curLevels, srvLevels) 1337 default: 1338 return nil, errors.Errorf("Unsupported database type: %s", driverName) 1339 } 1340 return migrator, nil 1341 }