@@ -17,10 +17,10 @@ package certmagic
17
17
import (
18
18
"context"
19
19
"crypto/x509"
20
+ "encoding/json"
20
21
"fmt"
21
22
"net"
22
23
"net/http"
23
- "net/url"
24
24
"strconv"
25
25
"strings"
26
26
"time"
@@ -68,6 +68,9 @@ func (iss *ZeroSSLIssuer) Issue(ctx context.Context, csr *x509.CertificateReques
68
68
client := iss .getClient ()
69
69
70
70
identifiers := namesFromCSR (csr )
71
+ if len (identifiers ) == 0 {
72
+ return nil , fmt .Errorf ("no identifiers on CSR" )
73
+ }
71
74
72
75
logger := iss .Logger
73
76
if logger == nil {
@@ -104,35 +107,7 @@ func (iss *ZeroSSLIssuer) Issue(ctx context.Context, csr *x509.CertificateReques
104
107
105
108
httpVerifier := & httpSolver {
106
109
address : net .JoinHostPort (iss .ListenHost , strconv .Itoa (iss .getHTTPPort ())),
107
- handler : http .HandlerFunc (func (rw http.ResponseWriter , req * http.Request ) {
108
- if ! strings .HasPrefix (req .URL .Path , zerosslValidationPathPrefix ) {
109
- return
110
- }
111
-
112
- validation , ok := cert .Validation .OtherMethods [req .Host ]
113
- if ! ok {
114
- rw .WriteHeader (http .StatusNotFound )
115
- return
116
- }
117
-
118
- // ensure URL matches
119
- validationURL , err := url .Parse (validation .FileValidationURLHTTP )
120
- if err != nil {
121
- logger .Error ("got invalid URL from CA" ,
122
- zap .String ("file_validation_url" , validation .FileValidationURLHTTP ),
123
- zap .Error (err ))
124
- rw .WriteHeader (http .StatusInternalServerError )
125
- return
126
- }
127
- if req .URL .Path != validationURL .Path {
128
- rw .WriteHeader (http .StatusNotFound )
129
- return
130
- }
131
-
132
- logger .Info ("served HTTP validation file" )
133
-
134
- fmt .Fprint (rw , strings .Join (validation .FileValidationContent , "\n " ))
135
- }),
110
+ handler : iss .HTTPValidationHandler (http .NewServeMux ()),
136
111
}
137
112
138
113
var solver acmez.Solver = httpVerifier
@@ -144,10 +119,23 @@ func (iss *ZeroSSLIssuer) Issue(ctx context.Context, csr *x509.CertificateReques
144
119
}
145
120
}
146
121
147
- if err = solver .Present (ctx , acme.Challenge {}); err != nil {
148
- return nil , fmt .Errorf ("presenting token for verification: %v" , err )
122
+ // since the distributed solver was originally designed for ACME,
123
+ // the API is geared around ACME challenges. ZeroSSL's HTTP validation
124
+ // is very similar to the HTTP challenge, but not quite compatible,
125
+ // so we kind of shim the ZeroSSL validation data into a Challenge
126
+ // object... it is not a perfect use of this type but it's pretty close
127
+ valInfo := cert .Validation .OtherMethods [identifiers [0 ]]
128
+ fakeChallenge := acme.Challenge {
129
+ Identifier : acme.Identifier {
130
+ Value : identifiers [0 ], // used for storage key
131
+ },
132
+ URL : valInfo .FileValidationURLHTTP ,
133
+ Token : strings .Join (cert .Validation .OtherMethods [identifiers [0 ]].FileValidationContent , "\n " ),
134
+ }
135
+ if err = solver .Present (ctx , fakeChallenge ); err != nil {
136
+ return nil , fmt .Errorf ("presenting validation file for verification: %v" , err )
149
137
}
150
- defer solver .CleanUp (ctx , acme. Challenge {} )
138
+ defer solver .CleanUp (ctx , fakeChallenge )
151
139
} else {
152
140
verificationMethod = zerossl .CNAMEVerification
153
141
logger = logger .With (zap .String ("verification_method" , string (verificationMethod )))
@@ -273,6 +261,32 @@ func (iss *ZeroSSLIssuer) Revoke(ctx context.Context, cert CertificateResource,
273
261
return iss .getClient ().RevokeCertificate (ctx , cert .IssuerData .(zerossl.CertificateObject ).ID , r )
274
262
}
275
263
264
+ func (iss * ZeroSSLIssuer ) getDistributedValidationInfo (ctx context.Context , identifier string ) (acme.Challenge , bool , error ) {
265
+ ds := distributedSolver {
266
+ storage : iss .Storage ,
267
+ storageKeyIssuerPrefix : StorageKeys .Safe (iss .IssuerKey ()),
268
+ }
269
+ tokenKey := ds .challengeTokensKey (identifier )
270
+
271
+ valObjectBytes , err := iss .Storage .Load (ctx , tokenKey )
272
+ if err != nil {
273
+ return acme.Challenge {}, false , fmt .Errorf ("opening distributed challenge token file %s: %v" , tokenKey , err )
274
+ }
275
+
276
+ if len (valObjectBytes ) == 0 {
277
+ return acme.Challenge {}, false , fmt .Errorf ("no information found to solve challenge for identifier: %s" , identifier )
278
+ }
279
+
280
+ // since the distributed solver's API is geared around ACME challenges,
281
+ // we crammed the validation info into a Challenge object
282
+ var chal acme.Challenge
283
+ if err = json .Unmarshal (valObjectBytes , & chal ); err != nil {
284
+ return acme.Challenge {}, false , fmt .Errorf ("decoding HTTP validation token file %s (corrupted?): %v" , tokenKey , err )
285
+ }
286
+
287
+ return chal , true , nil
288
+ }
289
+
276
290
const (
277
291
zerosslAPIBase = "https://" + zerossl .BaseURL + "/acme"
278
292
zerosslValidationPathPrefix = "/.well-known/pki-validation/"
0 commit comments