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