1package autoconf 2 3import ( 4 "context" 5 "fmt" 6 "net" 7 "strings" 8 9 "github.com/hashicorp/consul/agent/structs" 10) 11 12func (ac *AutoConfig) autoEncryptInitialCerts(ctx context.Context) (*structs.SignedResponse, error) { 13 // generate a CSR 14 csr, key, err := ac.generateCSR() 15 if err != nil { 16 return nil, err 17 } 18 19 ac.acConfig.Waiter.Reset() 20 for { 21 resp, err := ac.autoEncryptInitialCertsOnce(ctx, csr, key) 22 switch { 23 case err == nil && resp != nil: 24 return resp, nil 25 case err != nil: 26 ac.logger.Error(err.Error()) 27 default: 28 ac.logger.Error("No error returned when fetching certificates from the servers but no response was either") 29 } 30 31 if err := ac.acConfig.Waiter.Wait(ctx); err != nil { 32 ac.logger.Info("interrupted during retrieval of auto-encrypt certificates", "err", err) 33 return nil, err 34 } 35 } 36} 37 38func (ac *AutoConfig) autoEncryptInitialCertsOnce(ctx context.Context, csr, key string) (*structs.SignedResponse, error) { 39 request := structs.CASignRequest{ 40 WriteRequest: structs.WriteRequest{Token: ac.acConfig.Tokens.AgentToken()}, 41 Datacenter: ac.config.Datacenter, 42 CSR: csr, 43 } 44 var resp structs.SignedResponse 45 46 servers, err := ac.joinHosts() 47 if err != nil { 48 return nil, err 49 } 50 51 for _, s := range servers { 52 // try each IP to see if we can successfully make the request 53 for _, addr := range ac.resolveHost(s) { 54 if ctx.Err() != nil { 55 return nil, ctx.Err() 56 } 57 58 ac.logger.Debug("making AutoEncrypt.Sign RPC", "addr", addr.String()) 59 err = ac.acConfig.DirectRPC.RPC(ac.config.Datacenter, ac.config.NodeName, &addr, "AutoEncrypt.Sign", &request, &resp) 60 if err != nil { 61 ac.logger.Error("AutoEncrypt.Sign RPC failed", "addr", addr.String(), "error", err) 62 continue 63 } 64 65 resp.IssuedCert.PrivateKeyPEM = key 66 return &resp, nil 67 } 68 } 69 return nil, fmt.Errorf("No servers successfully responded to the auto-encrypt request") 70} 71 72func (ac *AutoConfig) joinHosts() ([]string, error) { 73 // use servers known to gossip if there are any 74 if ac.acConfig.ServerProvider != nil { 75 if srv := ac.acConfig.ServerProvider.FindLANServer(); srv != nil { 76 return []string{srv.Addr.String()}, nil 77 } 78 } 79 80 hosts, err := ac.discoverServers(ac.config.RetryJoinLAN) 81 if err != nil { 82 return nil, err 83 } 84 85 var addrs []string 86 87 // The addresses we use for auto-encrypt are the retry join and start join 88 // addresses. These are for joining serf and therefore we cannot rely on the 89 // ports for these. This loop strips any port that may have been specified and 90 // will let subsequent resolveAddr calls add on the default RPC port. 91 for _, addr := range append(ac.config.StartJoinAddrsLAN, hosts...) { 92 host, _, err := net.SplitHostPort(addr) 93 if err != nil { 94 if strings.Contains(err.Error(), "missing port in address") { 95 host = addr 96 } else { 97 ac.logger.Warn("error splitting host address into IP and port", "address", addr, "error", err) 98 continue 99 } 100 } 101 addrs = append(addrs, host) 102 } 103 104 if len(addrs) == 0 { 105 return nil, fmt.Errorf("no auto-encrypt server addresses available for use") 106 } 107 108 return addrs, nil 109} 110