Add the ability to disable ACME through an OS environment variable (#20369)

* Add the ability to disable ACME through an OS environment variable

 - Disable ACME through the VAULT_DISABLE_PUBLIC_ACME environment
   variable.

* PR feedback

 - Switch to using t.Setenv instead of manually doing it
 - Remove t.Parallel from the test not to influence others

* make fmt
This commit is contained in:
Steven Clark 2023-04-26 13:21:00 -04:00 committed by GitHub
parent 0538fc37dd
commit cbb5b2fa22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 50 additions and 3 deletions

View File

@ -20,6 +20,8 @@ const ErrorContentType = "application/problem+json"
// See RFC 8555 Section 6.7. Errors.
var ErrAccountDoesNotExist = errors.New("The request specified an account that does not exist")
var ErrAcmeDisabled = errors.New("ACME feature is disabled")
var (
ErrAlreadyRevoked = errors.New("The request specified a certificate to be revoked that has already been revoked")
ErrBadCSR = errors.New("The CSR is unacceptable")
@ -147,6 +149,10 @@ func TranslateError(given error) (*logical.Response, error) {
return nil, given
}
if errors.Is(given, ErrAcmeDisabled) {
return logical.RespondWithStatusCode(nil, nil, http.StatusNotFound)
}
// We're multierror aware here: if we're given a list of errors, assume
// they're structured so the first error is the outer error and the inner
// subproblems are subsequent in the multierror.

View File

@ -9,6 +9,8 @@ import (
"fmt"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"github.com/hashicorp/vault/sdk/framework"
@ -53,9 +55,8 @@ func (b *backend) acmeWrapper(op acmeOperation) framework.OperationFunc {
return acmeErrorWrapper(func(ctx context.Context, r *logical.Request, data *framework.FieldData) (*logical.Response, error) {
sc := b.makeStorageContext(ctx, r.Storage)
if false {
// TODO sclark: Check if ACME is enable here
return nil, fmt.Errorf("ACME is disabled in configuration: %w", ErrServerInternal)
if isAcmeDisabled(sc) {
return nil, ErrAcmeDisabled
}
if b.useLegacyBundleCaStorage() {
@ -323,3 +324,22 @@ func getRequestedAcmeIssuerFromPath(data *framework.FieldData) string {
}
return requestedIssuer
}
func isAcmeDisabled(sc *storageContext) bool {
if disableAcmeRaw := os.Getenv("VAULT_DISABLE_PUBLIC_ACME"); disableAcmeRaw != "" {
disableAcme, err := strconv.ParseBool(disableAcmeRaw)
if err != nil {
sc.Backend.Logger().Warn("could not parse env var VAULT_DISABLE_PUBLIC_ACME", "error", err)
disableAcme = false
}
// The OS environment if true will override any configuration option.
if disableAcme {
return true
}
}
// TODO: Implement configuration based check here.
return false
}

View File

@ -396,6 +396,27 @@ func TestAcmeAccountsCrossingDirectoryPath(t *testing.T) {
// swallows the error we are sending back to a no account error
}
// TestAcmeDisabledWithEnvVar verifies if VAULT_DISABLE_PUBLIC_ACME is set that we completely
// disable the ACME service
func TestAcmeDisabledWithEnvVar(t *testing.T) {
t.Setenv("VAULT_DISABLE_PUBLIC_ACME", "true")
cluster, client, _ := setupAcmeBackend(t)
defer cluster.Cleanup()
for _, method := range []string{http.MethodHead, http.MethodGet} {
t.Run(fmt.Sprintf("%s", method), func(t *testing.T) {
req := client.NewRequest(method, "/v1/pki/acme/new-nonce")
_, err := client.RawRequestWithContext(ctx, req)
require.Error(t, err, "should have received an error as ACME should have been disabled")
if apiError, ok := err.(*api.ResponseError); ok {
require.Equal(t, 404, apiError.StatusCode)
}
})
}
}
func setupAcmeBackend(t *testing.T) (*vault.TestCluster, *api.Client, string) {
cluster, client := setupTestPkiCluster(t)