spring-projects / spring-vault

Provides familiar Spring abstractions for HashiCorp Vault
https://spring.io/projects/spring-vault
Apache License 2.0
283 stars 186 forks source link

Sign SSH Key gives 400 error: role not found #637

Closed shazinahmed closed 3 years ago

shazinahmed commented 3 years ago

I am currently using Spring Vault in my project for storing secrets in KV backend, authenticated using AppRole. That works well.

Now, I am trying to add SSH key signing capability in the app. Even though I am able to create certificates using the CLI, doing it through spring-vault gives me 400 errors. Below are the details.

Over CLI (using same role-id and secret-id):

 ❯ vault write github-cert-dev/sign/backend-role -<<EOH
{
  "public_key": "$PUBLIC_SSH_KEY",
  "extensions": {
    "login@github.com":"a-shazahamedabc"
  },
  "valid_principals": "shazin.ahamed@abcd",
  "key_id": "shazin.ahamed@abcd"
}
EOH
Key              Value
---              -----
serial_number    ff815f52f4bcaf3e
signed_key       ssh-ed25519-cert-v01@openssh.com 
<signed key here>

Now if I try to do the same programatically. I get the below error:

org.springframework.vault.VaultException: Status 400 : Unknown role: backend-role; nested exception is org.springframework.web.client.HttpClientErrorException$BadRequest: 400 : [{"errors":["Unknown role: backend-role"]}
]
    at org.springframework.vault.client.VaultResponses.buildException(VaultResponses.java:64) ~[spring-vault-core-2.3.0.jar:2.3.0]
    at org.springframework.vault.core.VaultTemplate.doWithSession(VaultTemplate.java:451) ~[spring-vault-core-2.3.0.jar:2.3.0]
    at org.springframework.vault.core.VaultTemplate.write(VaultTemplate.java:403) ~[spring-vault-core-2.3.0.jar:2.3.0]
    at ai.arteria.infra.service.vault.VaultService.getSignedGHCertificate(VaultService.java:25) ~[classes/:na]
    at ai.arteria.infra.scheduler.CertificateCreationScheduler.createAndUploadCertificates(CertificateCreationScheduler.java:29) ~[classes/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84) ~[spring-context-5.3.3.jar:5.3.3]
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-5.3.3.jar:5.3.3]
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[na:na]
    at java.base/java.util.concurrent.FutureTask.runAndReset$$$capture(FutureTask.java:305) ~[na:na]
    at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java) ~[na:na]
    at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) ~[na:na]
    at java.base/java.lang.Thread.run(Thread.java:832) ~[na:na]
Caused by: org.springframework.web.client.HttpClientErrorException$BadRequest: 400 : [{"errors":["Unknown role: backend-role"]}
]
    at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:101) ~[spring-web-5.3.3.jar:5.3.3]
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:186) ~[spring-web-5.3.3.jar:5.3.3]
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:125) ~[spring-web-5.3.3.jar:5.3.3]
    at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63) ~[spring-web-5.3.3.jar:5.3.3]
    at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:818) ~[spring-web-5.3.3.jar:5.3.3]
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:776) ~[spring-web-5.3.3.jar:5.3.3]
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:710) ~[spring-web-5.3.3.jar:5.3.3]
    at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:436) ~[spring-web-5.3.3.jar:5.3.3]
    at org.springframework.vault.core.VaultTemplate.lambda$write$3(VaultTemplate.java:403) ~[spring-vault-core-2.3.0.jar:2.3.0]
    at org.springframework.vault.core.VaultTemplate.doWithSession(VaultTemplate.java:448) ~[spring-vault-core-2.3.0.jar:2.3.0]
    ... 16 common frames omitted

Below is the relevant code:

        Map<String, String> extensions = Map.of("login@github.com", ghUsername);
        VaultSignedCertificateRequest request = VaultSignedCertificateRequest.builder().publicKey(publicKey).extensions(extensions).validPrincipals(email).keyId(email).build();
        VaultResponse response = vaultTemplate.write("github-cert-dev/sign/backend-role", new Gson().toJson(request));
        logger.info(response.getData().toString());

The POJO used:

@Data
@Builder
public class VaultSignedCertificateRequest {
    @SerializedName("public_key")
    private String publicKey;
    @SerializedName("extensions")
    private Map<String, String> extensions;
    @SerializedName("valid_principals")
    private String validPrincipals;
    @SerializedName("key_id")
    private String keyId;
}

The version:

        <dependency>
            <groupId>org.springframework.vault</groupId>
            <artifactId>spring-vault-core</artifactId>
            <version>2.3.0</version>
        </dependency>

Am I doing something wrong here? Please let me know if you need more details.

Thank you!

shazinahmed commented 3 years ago

Additional observation.

This gives me a result

❯ vault read github-cert-dev/roles/backend-role
Key                         Value
---                         -----
algorithm_signer            n/a
allow_bare_domains          false
allow_host_certificates     false
allow_subdomains            false
allow_user_certificates     true
allow_user_key_ids          true
allowed_critical_options    n/a
allowed_domains             n/a
allowed_extensions          permit-pty,permit-port-forwarding,login@github.com
allowed_user_key_lengths    map[]
allowed_users               *
allowed_users_template      false
default_critical_options    map[]
default_extensions          map[permit-pty:]
default_user                n/a
key_bits                    0
key_id_format               n/a
key_type                    ca
max_ttl                     96h
ttl                         48h

But this give me null

vaultTemplate.read("github-cert-dev/roles/backend-role")
shazinahmed commented 3 years ago

I created another signing role and the application works with it. Looks like it is an issue with the backend than the spring-vault. Hence closing the issue.