1package api 2 3import ( 4 "fmt" 5 "time" 6 7 "github.com/mitchellh/mapstructure" 8) 9 10// CAConfig is the structure for the Connect CA configuration. 11type CAConfig struct { 12 // Provider is the CA provider implementation to use. 13 Provider string 14 15 // Configuration is arbitrary configuration for the provider. This 16 // should only contain primitive values and containers (such as lists 17 // and maps). 18 Config map[string]interface{} 19 20 // State is read-only data that the provider might have persisted for use 21 // after restart or leadership transition. For example this might include 22 // UUIDs of resources it has created. Setting this when writing a 23 // configuration is an error. 24 State map[string]string 25 26 // ForceWithoutCrossSigning indicates that the CA reconfiguration should go 27 // ahead even if the current CA is unable to cross sign certificates. This 28 // risks temporary connection failures during the rollout as new leafs will be 29 // rejected by proxies that have not yet observed the new root cert but is the 30 // only option if a CA that doesn't support cross signing needs to be 31 // reconfigured or mirated away from. 32 ForceWithoutCrossSigning bool 33 34 CreateIndex uint64 35 ModifyIndex uint64 36} 37 38// CommonCAProviderConfig is the common options available to all CA providers. 39type CommonCAProviderConfig struct { 40 LeafCertTTL time.Duration 41 SkipValidate bool 42 CSRMaxPerSecond float32 43 CSRMaxConcurrent int 44} 45 46// ConsulCAProviderConfig is the config for the built-in Consul CA provider. 47type ConsulCAProviderConfig struct { 48 CommonCAProviderConfig `mapstructure:",squash"` 49 50 PrivateKey string 51 RootCert string 52 IntermediateCertTTL time.Duration 53} 54 55// ParseConsulCAConfig takes a raw config map and returns a parsed 56// ConsulCAProviderConfig. 57func ParseConsulCAConfig(raw map[string]interface{}) (*ConsulCAProviderConfig, error) { 58 var config ConsulCAProviderConfig 59 decodeConf := &mapstructure.DecoderConfig{ 60 DecodeHook: mapstructure.StringToTimeDurationHookFunc(), 61 Result: &config, 62 WeaklyTypedInput: true, 63 } 64 65 decoder, err := mapstructure.NewDecoder(decodeConf) 66 if err != nil { 67 return nil, err 68 } 69 70 if err := decoder.Decode(raw); err != nil { 71 return nil, fmt.Errorf("error decoding config: %s", err) 72 } 73 74 return &config, nil 75} 76 77// CARootList is the structure for the results of listing roots. 78type CARootList struct { 79 ActiveRootID string 80 TrustDomain string 81 Roots []*CARoot 82} 83 84// CARoot represents a root CA certificate that is trusted. 85type CARoot struct { 86 // ID is a globally unique ID (UUID) representing this CA root. 87 ID string 88 89 // Name is a human-friendly name for this CA root. This value is 90 // opaque to Consul and is not used for anything internally. 91 Name string 92 93 // RootCertPEM is the PEM-encoded public certificate. 94 RootCertPEM string `json:"RootCert"` 95 96 // Active is true if this is the current active CA. This must only 97 // be true for exactly one CA. For any method that modifies roots in the 98 // state store, tests should be written to verify that multiple roots 99 // cannot be active. 100 Active bool 101 102 CreateIndex uint64 103 ModifyIndex uint64 104} 105 106// LeafCert is a certificate that has been issued by a Connect CA. 107type LeafCert struct { 108 // SerialNumber is the unique serial number for this certificate. 109 // This is encoded in standard hex separated by :. 110 SerialNumber string 111 112 // CertPEM and PrivateKeyPEM are the PEM-encoded certificate and private 113 // key for that cert, respectively. This should not be stored in the 114 // state store, but is present in the sign API response. 115 CertPEM string `json:",omitempty"` 116 PrivateKeyPEM string `json:",omitempty"` 117 118 // Service is the name of the service for which the cert was issued. 119 // ServiceURI is the cert URI value. 120 Service string 121 ServiceURI string 122 123 // ValidAfter and ValidBefore are the validity periods for the 124 // certificate. 125 ValidAfter time.Time 126 ValidBefore time.Time 127 128 CreateIndex uint64 129 ModifyIndex uint64 130} 131 132// CARoots queries the list of available roots. 133func (h *Connect) CARoots(q *QueryOptions) (*CARootList, *QueryMeta, error) { 134 r := h.c.newRequest("GET", "/v1/connect/ca/roots") 135 r.setQueryOptions(q) 136 rtt, resp, err := requireOK(h.c.doRequest(r)) 137 if err != nil { 138 return nil, nil, err 139 } 140 defer closeResponseBody(resp) 141 142 qm := &QueryMeta{} 143 parseQueryMeta(resp, qm) 144 qm.RequestTime = rtt 145 146 var out CARootList 147 if err := decodeBody(resp, &out); err != nil { 148 return nil, nil, err 149 } 150 return &out, qm, nil 151} 152 153// CAGetConfig returns the current CA configuration. 154func (h *Connect) CAGetConfig(q *QueryOptions) (*CAConfig, *QueryMeta, error) { 155 r := h.c.newRequest("GET", "/v1/connect/ca/configuration") 156 r.setQueryOptions(q) 157 rtt, resp, err := requireOK(h.c.doRequest(r)) 158 if err != nil { 159 return nil, nil, err 160 } 161 defer closeResponseBody(resp) 162 163 qm := &QueryMeta{} 164 parseQueryMeta(resp, qm) 165 qm.RequestTime = rtt 166 167 var out CAConfig 168 if err := decodeBody(resp, &out); err != nil { 169 return nil, nil, err 170 } 171 return &out, qm, nil 172} 173 174// CASetConfig sets the current CA configuration. 175func (h *Connect) CASetConfig(conf *CAConfig, q *WriteOptions) (*WriteMeta, error) { 176 r := h.c.newRequest("PUT", "/v1/connect/ca/configuration") 177 r.setWriteOptions(q) 178 r.obj = conf 179 rtt, resp, err := requireOK(h.c.doRequest(r)) 180 if err != nil { 181 return nil, err 182 } 183 defer closeResponseBody(resp) 184 185 wm := &WriteMeta{} 186 wm.RequestTime = rtt 187 return wm, nil 188} 189