...

Source file src/github.com/letsencrypt/boulder/test/integration/revocation_test.go

Documentation: github.com/letsencrypt/boulder/test/integration

     1  //go:build integration
     2  
     3  package integration
     4  
     5  import (
     6  	"crypto"
     7  	"crypto/ecdsa"
     8  	"crypto/elliptic"
     9  	"crypto/rand"
    10  	"crypto/x509"
    11  	"fmt"
    12  	"io"
    13  	"net/http"
    14  	"strings"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/eggsampler/acme/v3"
    19  	"golang.org/x/crypto/ocsp"
    20  
    21  	"github.com/letsencrypt/boulder/test"
    22  	ocsp_helper "github.com/letsencrypt/boulder/test/ocsp/helper"
    23  )
    24  
    25  // isPrecert returns true if the provided cert has an extension with the OID
    26  // equal to OIDExtensionCTPoison.
    27  func isPrecert(cert *x509.Certificate) bool {
    28  	for _, ext := range cert.Extensions {
    29  		if ext.Id.Equal(OIDExtensionCTPoison) {
    30  			return true
    31  		}
    32  	}
    33  	return false
    34  }
    35  
    36  // TestRevocation tests that a certificate can be revoked using all of the
    37  // RFC 8555 revocation authentication mechanisms. It does so for both certs and
    38  // precerts (with no corresponding final cert), and for both the Unspecified and
    39  // keyCompromise revocation reasons.
    40  func TestRevocation(t *testing.T) {
    41  	t.Parallel()
    42  
    43  	type authMethod string
    44  	var (
    45  		byAccount authMethod = "byAccount"
    46  		byAuth    authMethod = "byAuth"
    47  		byKey     authMethod = "byKey"
    48  	)
    49  
    50  	type certKind string
    51  	var (
    52  		finalcert certKind = "cert"
    53  		precert   certKind = "precert"
    54  	)
    55  
    56  	type testCase struct {
    57  		method authMethod
    58  		reason int
    59  		kind   certKind
    60  	}
    61  
    62  	var testCases []testCase
    63  	for _, kind := range []certKind{precert, finalcert} {
    64  		for _, reason := range []int{ocsp.Unspecified, ocsp.KeyCompromise} {
    65  			for _, method := range []authMethod{byAccount, byAuth, byKey} {
    66  				testCases = append(testCases, testCase{
    67  					method: method,
    68  					reason: reason,
    69  					kind:   kind,
    70  					// We do not expect any of these revocation requests to error.
    71  					// The ones done byAccount will succeed as requested, but will not
    72  					// result in the key being blocked for future issuance.
    73  					// The ones done byAuth will succeed, but will be overwritten to have
    74  					// reason code 5 (cessationOfOperation).
    75  					// The ones done byKey will succeed, but will be overwritten to have
    76  					// reason code 1 (keyCompromise), and will block the key.
    77  				})
    78  			}
    79  		}
    80  	}
    81  
    82  	for _, tc := range testCases {
    83  		name := fmt.Sprintf("%s_%d_%s", tc.kind, tc.reason, tc.method)
    84  		t.Run(name, func(t *testing.T) {
    85  			issueClient, err := makeClient()
    86  			test.AssertNotError(t, err, "creating acme client")
    87  
    88  			certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    89  			test.AssertNotError(t, err, "creating random cert key")
    90  
    91  			domain := random_domain()
    92  
    93  			// Try to issue a certificate for the name.
    94  			var cert *x509.Certificate
    95  			switch tc.kind {
    96  			case finalcert:
    97  				res, err := authAndIssue(issueClient, certKey, []string{domain}, true)
    98  				test.AssertNotError(t, err, "authAndIssue failed")
    99  				cert = res.certs[0]
   100  
   101  			case precert:
   102  				// Make sure the ct-test-srv will reject generating SCTs for the domain,
   103  				// so we only get a precert and no final cert.
   104  				err := ctAddRejectHost(domain)
   105  				test.AssertNotError(t, err, "adding ct-test-srv reject host")
   106  
   107  				_, err = authAndIssue(issueClient, certKey, []string{domain}, true)
   108  				test.AssertError(t, err, "expected error from authAndIssue, was nil")
   109  				if !strings.Contains(err.Error(), "urn:ietf:params:acme:error:serverInternal") ||
   110  					!strings.Contains(err.Error(), "SCT embedding") {
   111  					t.Fatal(err)
   112  				}
   113  
   114  				// Instead recover the precertificate from CT.
   115  				cert, err = ctFindRejection([]string{domain})
   116  				if err != nil || cert == nil {
   117  					t.Fatalf("couldn't find rejected precert for %q", domain)
   118  				}
   119  				// And make sure the cert we found is in fact a precert.
   120  				if !isPrecert(cert) {
   121  					t.Fatal("precert was missing poison extension")
   122  				}
   123  
   124  			default:
   125  				t.Fatalf("unrecognized cert kind %q", tc.kind)
   126  			}
   127  
   128  			// Initially, the cert should have a Good OCSP response.
   129  			ocspConfig := ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Good)
   130  			_, err = ocsp_helper.ReqDER(cert.Raw, ocspConfig)
   131  			test.AssertNotError(t, err, "requesting OCSP for precert")
   132  
   133  			// Set up the account and key that we'll use to revoke the cert.
   134  			var revokeClient *client
   135  			var revokeKey crypto.Signer
   136  			switch tc.method {
   137  			case byAccount:
   138  				// When revoking by account, use the same client and key as were used
   139  				// for the original issuance.
   140  				revokeClient = issueClient
   141  				revokeKey = revokeClient.PrivateKey
   142  
   143  			case byAuth:
   144  				// When revoking by auth, create a brand new client, authorize it for
   145  				// the same domain, and use that account and key for revocation. Ignore
   146  				// errors from authAndIssue because all we need is the auth, not the
   147  				// issuance.
   148  				revokeClient, err = makeClient()
   149  				test.AssertNotError(t, err, "creating second acme client")
   150  				_, _ = authAndIssue(revokeClient, certKey, []string{domain}, true)
   151  				revokeKey = revokeClient.PrivateKey
   152  
   153  			case byKey:
   154  				// When revoking by key, create a brand new client and use it with
   155  				// the cert's key for revocation.
   156  				revokeClient, err = makeClient()
   157  				test.AssertNotError(t, err, "creating second acme client")
   158  				revokeKey = certKey
   159  
   160  			default:
   161  				t.Fatalf("unrecognized revocation method %q", tc.method)
   162  			}
   163  
   164  			// Revoke the cert using the specified key and client.
   165  			err = revokeClient.RevokeCertificate(
   166  				revokeClient.Account,
   167  				cert,
   168  				revokeKey,
   169  				tc.reason,
   170  			)
   171  
   172  			test.AssertNotError(t, err, "revocation should have succeeded")
   173  
   174  			// Check the OCSP response for the certificate again. It should now be
   175  			// revoked. If the request was made by demonstrating control over the
   176  			// names, the reason should be overwritten to CessationOfOperation (5),
   177  			// and if the request was made by key, then the reason should be set to
   178  			// KeyCompromise (1).
   179  			ocspConfig = ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Revoked)
   180  			switch tc.method {
   181  			case byAuth:
   182  				ocspConfig = ocspConfig.WithExpectReason(ocsp.CessationOfOperation)
   183  			case byKey:
   184  				ocspConfig = ocspConfig.WithExpectReason(ocsp.KeyCompromise)
   185  			default:
   186  				ocspConfig = ocspConfig.WithExpectReason(tc.reason)
   187  			}
   188  			_, err = ocsp_helper.ReqDER(cert.Raw, ocspConfig)
   189  			test.AssertNotError(t, err, "requesting OCSP for revoked cert")
   190  		})
   191  	}
   192  }
   193  
   194  // TestReRevocation verifies that a certificate can have its revocation
   195  // information updated only when both of the following are true:
   196  // a) The certificate was not initially revoked for reason keyCompromise; and
   197  // b) The second request is authenticated using the cert's keypair.
   198  // In which case the revocation reason (but not revocation date) will be
   199  // updated to be keyCompromise.
   200  func TestReRevocation(t *testing.T) {
   201  	t.Parallel()
   202  
   203  	type authMethod string
   204  	var (
   205  		byAccount authMethod = "byAccount"
   206  		byKey     authMethod = "byKey"
   207  	)
   208  
   209  	type testCase struct {
   210  		method1     authMethod
   211  		reason1     int
   212  		method2     authMethod
   213  		reason2     int
   214  		expectError bool
   215  	}
   216  
   217  	testCases := []testCase{
   218  		{method1: byAccount, reason1: 0, method2: byAccount, reason2: 0, expectError: true},
   219  		{method1: byAccount, reason1: 1, method2: byAccount, reason2: 1, expectError: true},
   220  		{method1: byAccount, reason1: 0, method2: byKey, reason2: 1, expectError: false},
   221  		{method1: byAccount, reason1: 1, method2: byKey, reason2: 1, expectError: true},
   222  		{method1: byKey, reason1: 1, method2: byKey, reason2: 1, expectError: true},
   223  	}
   224  
   225  	for i, tc := range testCases {
   226  		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
   227  			issueClient, err := makeClient()
   228  			test.AssertNotError(t, err, "creating acme client")
   229  
   230  			certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   231  			test.AssertNotError(t, err, "creating random cert key")
   232  
   233  			// Try to issue a certificate for the name.
   234  			domain := random_domain()
   235  			res, err := authAndIssue(issueClient, certKey, []string{domain}, true)
   236  			test.AssertNotError(t, err, "authAndIssue failed")
   237  			cert := res.certs[0]
   238  
   239  			// Initially, the cert should have a Good OCSP response.
   240  			ocspConfig := ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Good)
   241  			_, err = ocsp_helper.ReqDER(cert.Raw, ocspConfig)
   242  			test.AssertNotError(t, err, "requesting OCSP for precert")
   243  
   244  			// Set up the account and key that we'll use to revoke the cert.
   245  			var revokeClient *client
   246  			var revokeKey crypto.Signer
   247  			switch tc.method1 {
   248  			case byAccount:
   249  				// When revoking by account, use the same client and key as were used
   250  				// for the original issuance.
   251  				revokeClient = issueClient
   252  				revokeKey = revokeClient.PrivateKey
   253  
   254  			case byKey:
   255  				// When revoking by key, create a brand new client and use it with
   256  				// the cert's key for revocation.
   257  				revokeClient, err = makeClient()
   258  				test.AssertNotError(t, err, "creating second acme client")
   259  				revokeKey = certKey
   260  
   261  			default:
   262  				t.Fatalf("unrecognized revocation method %q", tc.method1)
   263  			}
   264  
   265  			// Revoke the cert using the specified key and client.
   266  			err = revokeClient.RevokeCertificate(
   267  				revokeClient.Account,
   268  				cert,
   269  				revokeKey,
   270  				tc.reason1,
   271  			)
   272  			test.AssertNotError(t, err, "initial revocation should have succeeded")
   273  
   274  			// Check the OCSP response for the certificate again. It should now be
   275  			// revoked.
   276  			ocspConfig = ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Revoked).WithExpectReason(tc.reason1)
   277  			_, err = ocsp_helper.ReqDER(cert.Raw, ocspConfig)
   278  			test.AssertNotError(t, err, "requesting OCSP for revoked cert")
   279  
   280  			// Set up the account and key that we'll use to *re*-revoke the cert.
   281  			switch tc.method2 {
   282  			case byAccount:
   283  				// When revoking by account, use the same client and key as were used
   284  				// for the original issuance.
   285  				revokeClient = issueClient
   286  				revokeKey = revokeClient.PrivateKey
   287  
   288  			case byKey:
   289  				// When revoking by key, create a brand new client and use it with
   290  				// the cert's key for revocation.
   291  				revokeClient, err = makeClient()
   292  				test.AssertNotError(t, err, "creating second acme client")
   293  				revokeKey = certKey
   294  
   295  			default:
   296  				t.Fatalf("unrecognized revocation method %q", tc.method2)
   297  			}
   298  
   299  			// Re-revoke the cert using the specified key and client.
   300  			err = revokeClient.RevokeCertificate(
   301  				revokeClient.Account,
   302  				cert,
   303  				revokeKey,
   304  				tc.reason2,
   305  			)
   306  
   307  			switch tc.expectError {
   308  			case true:
   309  				test.AssertError(t, err, "second revocation should have failed")
   310  
   311  				// Check the OCSP response for the certificate again. It should still be
   312  				// revoked, with the same reason.
   313  				ocspConfig = ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Revoked).WithExpectReason(tc.reason1)
   314  				_, err = ocsp_helper.ReqDER(cert.Raw, ocspConfig)
   315  				test.AssertNotError(t, err, "requesting OCSP for revoked cert")
   316  
   317  			case false:
   318  				test.AssertNotError(t, err, "second revocation should have succeeded")
   319  
   320  				// Check the OCSP response for the certificate again. It should now be
   321  				// revoked with reason keyCompromise.
   322  				ocspConfig = ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Revoked).WithExpectStatus(tc.reason2)
   323  				_, err = ocsp_helper.ReqDER(cert.Raw, ocspConfig)
   324  				test.AssertNotError(t, err, "requesting OCSP for revoked cert")
   325  			}
   326  		})
   327  	}
   328  }
   329  
   330  func TestRevokeWithKeyCompromiseBlocksKey(t *testing.T) {
   331  	t.Parallel()
   332  
   333  	type authMethod string
   334  	var (
   335  		byAccount authMethod = "byAccount"
   336  		byKey     authMethod = "byKey"
   337  	)
   338  
   339  	// Test keyCompromise revocation both when revoking by certificate key and
   340  	// revoking by subscriber key. Both should work, although with slightly
   341  	// different behavior.
   342  	for _, method := range []authMethod{byKey, byAccount} {
   343  		c, err := makeClient("mailto:example@letsencrypt.org")
   344  		test.AssertNotError(t, err, "creating acme client")
   345  
   346  		certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   347  		test.AssertNotError(t, err, "failed to generate cert key")
   348  
   349  		res, err := authAndIssue(c, certKey, []string{random_domain()}, true)
   350  		test.AssertNotError(t, err, "authAndIssue failed")
   351  		cert := res.certs[0]
   352  
   353  		// Revoke the cert with reason keyCompromise, either authenticated via the
   354  		// issuing account, or via the certificate key itself.
   355  		switch method {
   356  		case byAccount:
   357  			err = c.RevokeCertificate(c.Account, cert, c.PrivateKey, ocsp.KeyCompromise)
   358  		case byKey:
   359  			err = c.RevokeCertificate(acme.Account{}, cert, certKey, ocsp.KeyCompromise)
   360  		}
   361  		test.AssertNotError(t, err, "failed to revoke certificate")
   362  
   363  		// Check the OCSP response. It should be revoked with reason = 1 (keyCompromise).
   364  		ocspConfig := ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Revoked).WithExpectReason(ocsp.KeyCompromise)
   365  		_, err = ocsp_helper.ReqDER(cert.Raw, ocspConfig)
   366  		test.AssertNotError(t, err, "requesting OCSP for revoked cert")
   367  
   368  		// Attempt to create a new account using the compromised key. This should
   369  		// work when the key was just *reported* as compromised, but fail when
   370  		// the compromise was demonstrated/proven.
   371  		_, err = c.NewAccount(certKey, false, true)
   372  		switch method {
   373  		case byAccount:
   374  			test.AssertNotError(t, err, "NewAccount failed with a non-blocklisted key")
   375  		case byKey:
   376  			test.AssertError(t, err, "NewAccount didn't fail with a blocklisted key")
   377  			test.AssertEquals(t, err.Error(), `acme: error code 400 "urn:ietf:params:acme:error:badPublicKey": public key is forbidden`)
   378  		}
   379  	}
   380  }
   381  
   382  func TestBadKeyRevoker(t *testing.T) {
   383  	// Both accounts have two email addresses, one of which is shared between
   384  	// them. All three addresses should receive mail, because the revocation
   385  	// request is signed by the certificate key, not an account key, so we don't
   386  	// know who requested the revocation. Finally, a third account with no address
   387  	// to ensure the bad-key-revoker handles that gracefully.
   388  	revokerClient, err := makeClient("mailto:revoker@letsencrypt.org", "mailto:shared@letsencrypt.org")
   389  	test.AssertNotError(t, err, "creating acme client")
   390  	revokeeClient, err := makeClient("mailto:shared@letsencrypt.org", "mailto:revokee@letsencrypt.org")
   391  	test.AssertNotError(t, err, "creating acme client")
   392  	noContactClient, err := makeClient()
   393  	test.AssertNotError(t, err, "creating acme client")
   394  
   395  	certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   396  	test.AssertNotError(t, err, "failed to generate cert key")
   397  
   398  	res, err := authAndIssue(revokerClient, certKey, []string{random_domain()}, true)
   399  	test.AssertNotError(t, err, "authAndIssue failed")
   400  	badCert := res.certs[0]
   401  	t.Logf("Generated to-be-revoked cert with serial %x", badCert.SerialNumber)
   402  
   403  	certs := []*x509.Certificate{}
   404  	for _, c := range []*client{revokerClient, revokeeClient, noContactClient} {
   405  		cert, err := authAndIssue(c, certKey, []string{random_domain()}, true)
   406  		t.Logf("TestBadKeyRevoker: Issued cert with serial %x", cert.certs[0].SerialNumber)
   407  		test.AssertNotError(t, err, "authAndIssue failed")
   408  		certs = append(certs, cert.certs[0])
   409  	}
   410  
   411  	err = revokerClient.RevokeCertificate(
   412  		acme.Account{},
   413  		badCert,
   414  		certKey,
   415  		ocsp.KeyCompromise,
   416  	)
   417  	test.AssertNotError(t, err, "failed to revoke certificate")
   418  
   419  	ocspConfig := ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Revoked).WithExpectReason(ocsp.KeyCompromise)
   420  	_, err = ocsp_helper.ReqDER(badCert.Raw, ocspConfig)
   421  	test.AssertNotError(t, err, "ReqDER failed")
   422  
   423  	for _, cert := range certs {
   424  		for i := 0; i < 5; i++ {
   425  			t.Logf("TestBadKeyRevoker: Requesting OCSP for cert with serial %x (attempt %d)", cert.SerialNumber, i)
   426  			_, err := ocsp_helper.ReqDER(cert.Raw, ocspConfig)
   427  			if err != nil {
   428  				t.Logf("TestBadKeyRevoker: Got bad response: %s", err.Error())
   429  				if i >= 4 {
   430  					t.Fatal("timed out waiting for correct OCSP status")
   431  				}
   432  				time.Sleep(time.Second)
   433  				continue
   434  			}
   435  			break
   436  		}
   437  	}
   438  
   439  	revokeeCount, err := http.Get("http://boulder.service.consul:9381/count?to=revokee@letsencrypt.org&from=bad-key-revoker@test.org")
   440  	test.AssertNotError(t, err, "mail-test-srv GET /count failed")
   441  	defer func() { _ = revokeeCount.Body.Close() }()
   442  	body, err := io.ReadAll(revokeeCount.Body)
   443  	test.AssertNotError(t, err, "failed to read body")
   444  	test.AssertEquals(t, string(body), "1\n")
   445  
   446  	revokerCount, err := http.Get("http://boulder.service.consul:9381/count?to=revoker@letsencrypt.org&from=bad-key-revoker@test.org")
   447  	test.AssertNotError(t, err, "mail-test-srv GET /count failed")
   448  	defer func() { _ = revokerCount.Body.Close() }()
   449  	body, err = io.ReadAll(revokerCount.Body)
   450  	test.AssertNotError(t, err, "failed to read body")
   451  	test.AssertEquals(t, string(body), "1\n")
   452  
   453  	sharedCount, err := http.Get("http://boulder.service.consul:9381/count?to=shared@letsencrypt.org&from=bad-key-revoker@test.org")
   454  	test.AssertNotError(t, err, "mail-test-srv GET /count failed")
   455  	defer func() { _ = sharedCount.Body.Close() }()
   456  	body, err = io.ReadAll(sharedCount.Body)
   457  	test.AssertNotError(t, err, "failed to read body")
   458  	test.AssertEquals(t, string(body), "1\n")
   459  }
   460  
   461  func TestBadKeyRevokerByAccount(t *testing.T) {
   462  	// Both accounts have two email addresses, one of which is shared between
   463  	// them. No accounts should receive any mail, because the revocation request
   464  	// is signed by the account key (not the cert key) and so will not be
   465  	// propagated to other certs sharing the same key.
   466  	revokerClient, err := makeClient("mailto:revoker-moz@letsencrypt.org", "mailto:shared-moz@letsencrypt.org")
   467  	test.AssertNotError(t, err, "creating acme client")
   468  	revokeeClient, err := makeClient("mailto:shared-moz@letsencrypt.org", "mailto:revokee-moz@letsencrypt.org")
   469  	test.AssertNotError(t, err, "creating acme client")
   470  	noContactClient, err := makeClient()
   471  	test.AssertNotError(t, err, "creating acme client")
   472  
   473  	certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   474  	test.AssertNotError(t, err, "failed to generate cert key")
   475  
   476  	res, err := authAndIssue(revokerClient, certKey, []string{random_domain()}, true)
   477  	test.AssertNotError(t, err, "authAndIssue failed")
   478  	badCert := res.certs[0]
   479  	t.Logf("Generated to-be-revoked cert with serial %x", badCert.SerialNumber)
   480  
   481  	certs := []*x509.Certificate{}
   482  	for _, c := range []*client{revokerClient, revokeeClient, noContactClient} {
   483  		cert, err := authAndIssue(c, certKey, []string{random_domain()}, true)
   484  		t.Logf("TestBadKeyRevokerByAccount: Issued cert with serial %x", cert.certs[0].SerialNumber)
   485  		test.AssertNotError(t, err, "authAndIssue failed")
   486  		certs = append(certs, cert.certs[0])
   487  	}
   488  
   489  	err = revokerClient.RevokeCertificate(
   490  		revokerClient.Account,
   491  		badCert,
   492  		revokerClient.PrivateKey,
   493  		ocsp.KeyCompromise,
   494  	)
   495  	test.AssertNotError(t, err, "failed to revoke certificate")
   496  
   497  	ocspConfig := ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Revoked).WithExpectReason(ocsp.KeyCompromise)
   498  	_, err = ocsp_helper.ReqDER(badCert.Raw, ocspConfig)
   499  	test.AssertNotError(t, err, "ReqDER failed")
   500  
   501  	ocspConfig = ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Good)
   502  	for _, cert := range certs {
   503  		for i := 0; i < 5; i++ {
   504  			t.Logf("TestBadKeyRevoker: Requesting OCSP for cert with serial %x (attempt %d)", cert.SerialNumber, i)
   505  			_, err := ocsp_helper.ReqDER(cert.Raw, ocspConfig)
   506  			if err != nil {
   507  				t.Logf("TestBadKeyRevoker: Got bad response: %s", err.Error())
   508  				if i >= 4 {
   509  					t.Fatal("timed out waiting for correct OCSP status")
   510  				}
   511  				time.Sleep(time.Second)
   512  				continue
   513  			}
   514  			break
   515  		}
   516  	}
   517  
   518  	revokeeCount, err := http.Get("http://boulder.service.consul:9381/count?to=revokee-moz@letsencrypt.org&from=bad-key-revoker@test.org")
   519  	test.AssertNotError(t, err, "mail-test-srv GET /count failed")
   520  	defer func() { _ = revokeeCount.Body.Close() }()
   521  	body, err := io.ReadAll(revokeeCount.Body)
   522  	test.AssertNotError(t, err, "failed to read body")
   523  	test.AssertEquals(t, string(body), "0\n")
   524  
   525  	revokerCount, err := http.Get("http://boulder.service.consul:9381/count?to=revoker-moz@letsencrypt.org&from=bad-key-revoker@test.org")
   526  	test.AssertNotError(t, err, "mail-test-srv GET /count failed")
   527  	defer func() { _ = revokerCount.Body.Close() }()
   528  	body, err = io.ReadAll(revokerCount.Body)
   529  	test.AssertNotError(t, err, "failed to read body")
   530  	test.AssertEquals(t, string(body), "0\n")
   531  
   532  	sharedCount, err := http.Get("http://boulder.service.consul:9381/count?to=shared-moz@letsencrypt.org&from=bad-key-revoker@test.org")
   533  	test.AssertNotError(t, err, "mail-test-srv GET /count failed")
   534  	defer func() { _ = sharedCount.Body.Close() }()
   535  	body, err = io.ReadAll(sharedCount.Body)
   536  	test.AssertNotError(t, err, "failed to read body")
   537  	test.AssertEquals(t, string(body), "0\n")
   538  }
   539  

View as plain text