github.com/canhui/fabric_ca2_2@v2.0.0-alpha+incompatible/cmd/fabric-ca-client/command/config.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package command 8 9 import ( 10 "fmt" 11 "io/ioutil" 12 "net/url" 13 "os" 14 "path/filepath" 15 "reflect" 16 "strings" 17 18 "github.com/cloudflare/cfssl/csr" 19 "github.com/cloudflare/cfssl/log" 20 "github.com/hyperledger/fabric-ca/api" 21 "github.com/hyperledger/fabric-ca/lib" 22 "github.com/hyperledger/fabric-ca/lib/attr" 23 calog "github.com/hyperledger/fabric-ca/lib/common/log" 24 "github.com/hyperledger/fabric-ca/util" 25 "github.com/pkg/errors" 26 ) 27 28 const ( 29 longName = "Hyperledger Fabric Certificate Authority Client" 30 shortName = "fabric-ca client" 31 cmdName = "fabric-ca-client" 32 envVarPrefix = "FABRIC_CA_CLIENT" 33 homeEnvVar = "FABRIC_CA_CLIENT_HOME" 34 ) 35 36 const ( 37 defaultCfgTemplate = ` 38 ############################################################################# 39 # This is a configuration file for the fabric-ca-client command. 40 # 41 # COMMAND LINE ARGUMENTS AND ENVIRONMENT VARIABLES 42 # ------------------------------------------------ 43 # Each configuration element can be overridden via command line 44 # arguments or environment variables. The precedence for determining 45 # the value of each element is as follows: 46 # 1) command line argument 47 # Examples: 48 # a) --url https://localhost:7054 49 # To set the fabric-ca server url 50 # b) --tls.client.certfile certfile.pem 51 # To set the client certificate for TLS 52 # 2) environment variable 53 # Examples: 54 # a) FABRIC_CA_CLIENT_URL=https://localhost:7054 55 # To set the fabric-ca server url 56 # b) FABRIC_CA_CLIENT_TLS_CLIENT_CERTFILE=certfile.pem 57 # To set the client certificate for TLS 58 # 3) configuration file 59 # 4) default value (if there is one) 60 # All default values are shown beside each element below. 61 # 62 # FILE NAME ELEMENTS 63 # ------------------ 64 # The value of all fields whose name ends with "file" or "files" are 65 # name or names of other files. 66 # For example, see "tls.certfiles" and "tls.client.certfile". 67 # The value of each of these fields can be a simple filename, a 68 # relative path, or an absolute path. If the value is not an 69 # absolute path, it is interpretted as being relative to the location 70 # of this configuration file. 71 # 72 ############################################################################# 73 74 ############################################################################# 75 # Client Configuration 76 ############################################################################# 77 78 # URL of the Fabric-ca-server (default: http://localhost:7054) 79 url: <<<URL>>> 80 81 # Membership Service Provider (MSP) directory 82 # This is useful when the client is used to enroll a peer or orderer, so 83 # that the enrollment artifacts are stored in the format expected by MSP. 84 mspdir: <<<MSPDIR>>> 85 86 ############################################################################# 87 # TLS section for secure socket connection 88 # 89 # certfiles - PEM-encoded list of trusted root certificate files 90 # client: 91 # certfile - PEM-encoded certificate file for when client authentication 92 # is enabled on server 93 # keyfile - PEM-encoded key file for when client authentication 94 # is enabled on server 95 ############################################################################# 96 tls: 97 # TLS section for secure socket connection 98 certfiles: 99 client: 100 certfile: 101 keyfile: 102 103 ############################################################################# 104 # Certificate Signing Request section for generating the CSR for an 105 # enrollment certificate (ECert) 106 # 107 # cn - Used by CAs to determine which domain the certificate is to be generated for 108 # 109 # serialnumber - The serialnumber field, if specified, becomes part of the issued 110 # certificate's DN (Distinguished Name). For example, one use case for this is 111 # a company with its own CA (Certificate Authority) which issues certificates 112 # to its employees and wants to include the employee's serial number in the DN 113 # of its issued certificates. 114 # WARNING: The serialnumber field should not be confused with the certificate's 115 # serial number which is set by the CA but is not a component of the 116 # certificate's DN. 117 # 118 # names - A list of name objects. Each name object should contain at least one 119 # "C", "L", "O", or "ST" value (or any combination of these) where these 120 # are abbreviations for the following: 121 # "C": country 122 # "L": locality or municipality (such as city or town name) 123 # "O": organization 124 # "OU": organizational unit, such as the department responsible for owning the key; 125 # it can also be used for a "Doing Business As" (DBS) name 126 # "ST": the state or province 127 # 128 # Note that the "OU" or organizational units of an ECert are always set according 129 # to the values of the identities type and affiliation. OUs are calculated for an enroll 130 # as OU=<type>, OU=<affiliationRoot>, ..., OU=<affiliationLeaf>. For example, an identity 131 # of type "client" with an affiliation of "org1.dept2.team3" would have the following 132 # organizational units: OU=client, OU=org1, OU=dept2, OU=team3 133 # 134 # hosts - A list of host names for which the certificate should be valid 135 # 136 ############################################################################# 137 csr: 138 cn: <<<ENROLLMENT_ID>>> 139 keyrequest: 140 algo: ecdsa 141 size: 256 142 serialnumber: 143 names: 144 - C: US 145 ST: North Carolina 146 L: 147 O: Hyperledger 148 OU: Fabric 149 hosts: 150 - <<<MYHOST>>> 151 152 ############################################################################# 153 # Registration section used to register a new identity with fabric-ca server 154 # 155 # name - Unique name of the identity 156 # type - Type of identity being registered (e.g. 'peer, app, user') 157 # affiliation - The identity's affiliation 158 # maxenrollments - The maximum number of times the secret can be reused to enroll. 159 # Specially, -1 means unlimited; 0 means to use CA's max enrollment 160 # value. 161 # attributes - List of name/value pairs of attribute for identity 162 ############################################################################# 163 id: 164 name: 165 type: 166 affiliation: 167 maxenrollments: 0 168 attributes: 169 # - name: 170 # value: 171 172 ############################################################################# 173 # Enrollment section used to enroll an identity with fabric-ca server 174 # 175 # profile - Name of the signing profile to use in issuing the certificate 176 # label - Label to use in HSM operations 177 ############################################################################# 178 enrollment: 179 profile: 180 label: 181 182 ############################################################################# 183 # Name of the CA to connect to within the fabric-ca server 184 ############################################################################# 185 caname: 186 187 ############################################################################# 188 # BCCSP (BlockChain Crypto Service Provider) section allows to select which 189 # crypto implementation library to use 190 ############################################################################# 191 bccsp: 192 default: SW 193 sw: 194 hash: SHA2 195 security: 256 196 filekeystore: 197 # The directory used for the software file-based keystore 198 keystore: msp/keystore 199 ` 200 ) 201 202 // ConfigInit initializes the configuration for the fabric-ca-client command 203 func (c *ClientCmd) ConfigInit() error { 204 var err error 205 206 c.myViper.AutomaticEnv() // read in environment variables that match 207 logLevel := c.myViper.GetString("loglevel") 208 debug := c.myViper.GetBool("debug") 209 210 // If log level has been set via the new loglevel property use that as the loglevel 211 // and override any default log levels defined for the commands 212 if logLevel != "" { 213 c.logLevel = logLevel 214 } 215 calog.SetLogLevel(c.logLevel, debug) 216 if err != nil { 217 return err 218 } 219 220 c.cfgFileName, c.homeDirectory, err = util.ValidateAndReturnAbsConf(c.cfgFileName, c.homeDirectory, cmdName) 221 if err != nil { 222 return err 223 } 224 225 log.Debugf("Home directory: %s", c.homeDirectory) 226 227 // Set configuration file name for viper and configure it to read env variables 228 c.myViper.SetConfigFile(c.cfgFileName) 229 230 // If the config file doesn't exist, create a default one if enroll 231 // command being executed. Enroll should be the first command to be 232 // executed, and furthermore the default configuration file requires 233 // enrollment ID to populate CN field which is something the enroll 234 // command requires 235 if c.shouldCreateDefaultConfig() { 236 if !util.FileExists(c.cfgFileName) { 237 err = c.createDefaultConfigFile() 238 if err != nil { 239 return errors.WithMessage(err, "Failed to create default configuration file") 240 } 241 log.Infof("Created a default configuration file at %s", c.cfgFileName) 242 } 243 } else { 244 log.Infof("Configuration file location: %s", c.cfgFileName) 245 } 246 247 // Call viper to read the config 248 if util.FileExists(c.cfgFileName) { 249 err = c.myViper.ReadInConfig() 250 if err != nil { 251 return errors.Wrapf(err, "Failed to read config file at '%s'", c.cfgFileName) 252 } 253 } 254 255 err = c.myViper.Unmarshal(c.clientCfg) 256 if err != nil { 257 return errors.Wrapf(err, "Incorrect format in file '%s'", c.cfgFileName) 258 } 259 260 // If the CSR is not for a CA, set the CA pointer to nil 261 if c.clientCfg.CSR.CA != nil && c.clientCfg.CSR.CA.PathLength == 0 && !c.clientCfg.CSR.CA.PathLenZero { 262 c.clientCfg.CSR.CA = nil 263 } 264 265 purl, err := url.Parse(c.clientCfg.URL) 266 if err != nil { 267 return err 268 } 269 270 c.clientCfg.TLS.Enabled = purl.Scheme == "https" 271 272 err = processAttributes(c.cfgAttrs, c.clientCfg) 273 if err != nil { 274 return err 275 } 276 277 err = processAttributeRequests(c.cfgAttrReqs, c.clientCfg) 278 if err != nil { 279 return err 280 } 281 282 err = c.processCsrNames() 283 if err != nil { 284 return err 285 } 286 287 // Check for separaters and insert values back into slice 288 normalizeStringSlices(c.clientCfg) 289 290 // Commands other than 'enroll' and 'getcacert' require that client already 291 // be enrolled 292 if c.requiresEnrollment() { 293 err = checkForEnrollment(c.cfgFileName, c.clientCfg) 294 if err != nil { 295 return err 296 } 297 } 298 299 return nil 300 } 301 302 func (c *ClientCmd) createDefaultConfigFile() error { 303 // Create a default config, if URL provided via CLI or envar update config files 304 var cfg string 305 fabricCAServerURL := c.myViper.GetString("url") 306 if fabricCAServerURL == "" { 307 fabricCAServerURL = util.GetServerURL() 308 } else { 309 URL, err := url.Parse(fabricCAServerURL) 310 if err != nil { 311 return errors.Wrapf(err, "Failed to parse URL '%s'", fabricCAServerURL) 312 } 313 fabricCAServerURL = fmt.Sprintf("%s://%s", URL.Scheme, URL.Host) 314 } 315 316 myhost := c.myViper.GetString("myhost") 317 318 // Do string subtitution to get the default config 319 cfg = strings.Replace(defaultCfgTemplate, "<<<URL>>>", fabricCAServerURL, 1) 320 cfg = strings.Replace(cfg, "<<<MYHOST>>>", myhost, 1) 321 cfg = strings.Replace(cfg, "<<<MSPDIR>>>", c.clientCfg.MSPDir, 1) 322 323 user := "" 324 var err error 325 if c.requiresUser() { 326 user, _, err = util.GetUser(c.myViper) 327 if err != nil { 328 return err 329 } 330 } 331 cfg = strings.Replace(cfg, "<<<ENROLLMENT_ID>>>", user, 1) 332 333 // Create the directory if necessary 334 err = os.MkdirAll(c.homeDirectory, 0755) 335 if err != nil { 336 return errors.Wrapf(err, "Failed to create directory at '%s'", c.homeDirectory) 337 } 338 339 // Now write the file 340 return ioutil.WriteFile(c.cfgFileName, []byte(cfg), 0755) 341 } 342 343 // processAttributes parses attributes from command line or env variable 344 func processAttributes(cfgAttrs []string, cfg *lib.ClientConfig) error { 345 if cfgAttrs != nil { 346 attrMap := make(map[string]string) 347 for _, attr := range cfgAttrs { 348 // skipping empty attributes 349 if len(attr) == 0 { 350 continue 351 } 352 sattr := strings.SplitN(attr, "=", 2) 353 if len(sattr) != 2 { 354 return errors.Errorf("Attribute '%s' is missing '=' ; it "+ 355 "must be of the form <name>=<value>", attr) 356 } 357 attrMap[sattr[0]] = sattr[1] 358 } 359 var err error 360 cfg.ID.Attributes, err = attr.ConvertAttrs(attrMap) 361 if err != nil { 362 return err 363 } 364 } 365 return nil 366 } 367 368 // processAttributeRequests parses attribute requests from command line or env variable 369 // Each string is of the form: <attrName>[:opt] where "opt" means the attribute is 370 // optional and will not return an error if the identity does not possess the attribute. 371 // The default is that each attribute name listed is required and so the identity must 372 // possess the attribute. 373 func processAttributeRequests(cfgAttrReqs []string, cfg *lib.ClientConfig) error { 374 if len(cfgAttrReqs) == 0 { 375 return nil 376 } 377 reqs := make([]*api.AttributeRequest, len(cfgAttrReqs)) 378 for idx, req := range cfgAttrReqs { 379 sreq := strings.Split(req, ":") 380 name := sreq[0] 381 switch len(sreq) { 382 case 1: 383 reqs[idx] = &api.AttributeRequest{Name: name} 384 case 2: 385 if sreq[1] != "opt" { 386 return errors.Errorf("Invalid option in attribute request specification at '%s'; the value after the colon must be 'opt'", req) 387 } 388 reqs[idx] = &api.AttributeRequest{Name: name, Optional: true} 389 default: 390 return errors.Errorf("Multiple ':' characters not allowed in attribute request specification; error at '%s'", req) 391 } 392 } 393 cfg.Enrollment.AttrReqs = reqs 394 return nil 395 } 396 397 // processAttributes parses attributes from command line or env variable 398 func (c *ClientCmd) processCsrNames() error { 399 if c.cfgCsrNames != nil { 400 c.clientCfg.CSR.Names = make([]csr.Name, len(c.cfgCsrNames)) 401 for idx, name := range c.cfgCsrNames { 402 sname := strings.SplitN(name, "=", 2) 403 if len(sname) != 2 { 404 return errors.Errorf("CSR name/value '%s' is missing '=' ; it must be of the form <name>=<value>", name) 405 } 406 v := reflect.ValueOf(&c.clientCfg.CSR.Names[idx]).Elem().FieldByName(sname[0]) 407 if v.IsValid() { 408 v.SetString(sname[1]) 409 } else { 410 return errors.Errorf("Invalid CSR name: '%s'", sname[0]) 411 } 412 } 413 } 414 return nil 415 } 416 417 // GetHomeDirectory returns the client's home directory 418 func (c *ClientCmd) GetHomeDirectory() string { 419 return c.homeDirectory 420 } 421 422 func checkForEnrollment(cfgFileName string, cfg *lib.ClientConfig) error { 423 log.Debug("Checking for enrollment") 424 client := lib.Client{ 425 HomeDir: filepath.Dir(cfgFileName), 426 Config: cfg, 427 } 428 return client.CheckEnrollment() 429 } 430 431 func normalizeStringSlices(cfg *lib.ClientConfig) { 432 fields := []*[]string{ 433 &cfg.CSR.Hosts, 434 &cfg.TLS.CertFiles, 435 } 436 for _, namePtr := range fields { 437 norm := util.NormalizeStringSlice(*namePtr) 438 *namePtr = norm 439 } 440 }