From 6db9112d7f05b14429445f186c0cbff9a2f6e516 Mon Sep 17 00:00:00 2001 From: Emmanuel Bourg <ebourg@apache.org> Date: Fri, 14 Feb 2025 19:02:00 +0100 Subject: [PATCH 1/3] Set the next version to 7.2 --- jsign-ant/pom.xml | 4 ++-- jsign-cli/pom.xml | 4 ++-- jsign-core/pom.xml | 4 ++-- jsign-crypto/pom.xml | 4 ++-- jsign-gradle-plugin/pom.xml | 4 ++-- jsign-maven-plugin/pom.xml | 4 ++-- jsign/pom.xml | 4 ++-- pom.xml | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/jsign-ant/pom.xml b/jsign-ant/pom.xml index e8630253..5a880ace 100644 --- a/jsign-ant/pom.xml +++ b/jsign-ant/pom.xml @@ -6,11 +6,11 @@ <parent> <groupId>net.jsign</groupId> <artifactId>jsign-parent</artifactId> - <version>7.1</version> + <version>7.2-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <name>Jsign - Authenticode signing in Java (Ant Task)</name> - <version>7.1</version> + <version>7.2-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> diff --git a/jsign-cli/pom.xml b/jsign-cli/pom.xml index 20e0fd1d..63dba87b 100644 --- a/jsign-cli/pom.xml +++ b/jsign-cli/pom.xml @@ -6,11 +6,11 @@ <parent> <groupId>net.jsign</groupId> <artifactId>jsign-parent</artifactId> - <version>7.1</version> + <version>7.2-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <name>Jsign - Authenticode signing in Java (Command Line Tool)</name> - <version>7.1</version> + <version>7.2-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> diff --git a/jsign-core/pom.xml b/jsign-core/pom.xml index 745f99c8..e85cf1cd 100644 --- a/jsign-core/pom.xml +++ b/jsign-core/pom.xml @@ -6,11 +6,11 @@ <parent> <groupId>net.jsign</groupId> <artifactId>jsign-parent</artifactId> - <version>7.1</version> + <version>7.2-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <name>Jsign - Authenticode signing in Java (Core)</name> - <version>7.1</version> + <version>7.2-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> diff --git a/jsign-crypto/pom.xml b/jsign-crypto/pom.xml index 46b1eba4..b42930ce 100644 --- a/jsign-crypto/pom.xml +++ b/jsign-crypto/pom.xml @@ -6,11 +6,11 @@ <parent> <groupId>net.jsign</groupId> <artifactId>jsign-parent</artifactId> - <version>7.1</version> + <version>7.2-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <name>Jsign - Authenticode signing in Java (Crypto)</name> - <version>7.1</version> + <version>7.2-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> diff --git a/jsign-gradle-plugin/pom.xml b/jsign-gradle-plugin/pom.xml index d8a8d08d..520439ea 100644 --- a/jsign-gradle-plugin/pom.xml +++ b/jsign-gradle-plugin/pom.xml @@ -6,11 +6,11 @@ <parent> <groupId>net.jsign</groupId> <artifactId>jsign-parent</artifactId> - <version>7.1</version> + <version>7.2-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <name>Jsign - Authenticode signing in Java (Gradle Plugin)</name> - <version>7.1</version> + <version>7.2-SNAPSHOT</version> <packaging>jar</packaging> <repositories> diff --git a/jsign-maven-plugin/pom.xml b/jsign-maven-plugin/pom.xml index 1d32ac55..8eb38166 100644 --- a/jsign-maven-plugin/pom.xml +++ b/jsign-maven-plugin/pom.xml @@ -6,11 +6,11 @@ <parent> <groupId>net.jsign</groupId> <artifactId>jsign-parent</artifactId> - <version>7.1</version> + <version>7.2-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <name>Jsign - Authenticode signing in Java (Maven Plugin)</name> - <version>7.1</version> + <version>7.2-SNAPSHOT</version> <packaging>maven-plugin</packaging> <properties> diff --git a/jsign/pom.xml b/jsign/pom.xml index 9fa91091..3c650fa4 100644 --- a/jsign/pom.xml +++ b/jsign/pom.xml @@ -6,11 +6,11 @@ <parent> <groupId>net.jsign</groupId> <artifactId>jsign-parent</artifactId> - <version>7.1</version> + <version>7.2-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <name>Jsign - Authenticode signing in Java (Distribution)</name> - <version>7.1</version> + <version>7.2-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> diff --git a/pom.xml b/pom.xml index 44356c3a..527868ec 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ <groupId>net.jsign</groupId> <artifactId>jsign-parent</artifactId> <name>Jsign - Authenticode signing in Java (Parent)</name> - <version>7.1</version> + <version>7.2-SNAPSHOT</version> <packaging>pom</packaging> <inceptionYear>2012</inceptionYear> From c0cb9fadd5869fd8320627771ed710f10bce0b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= <me@alegon.dev> Date: Sat, 22 Mar 2025 17:46:11 +0100 Subject: [PATCH 2/3] Also query ECS container creds endpoint for KMS when no storepass is set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue #147 and its linked PR made it much more ergonomic to use `jsign` within an AWS EC2 instance, but as highlighted in the latter comments on that issue, it's not helpful when running ´jsign´ in the context of AWS Elastic Container Service (ECS) based products, such as AWS Fargate, AWS Codebuild, and AWS Greengrass. The precise technical reason for this is that ECS environments don't have that EC2 IMDSv2 endpoint exposed to them, so they can't use it. [As the official AWS SDK documentation states](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/credentials-chain.html), the official SDK handles this situation by fetching credentials from a specific ECS container credentials endpoint, which doesn't share any host or path with IMDSv2, on the `ContainerCredentialsProvider` class. Roughly, the official container credentials provider works as follows: - If the `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` environment variable is set, it concatenates it with a well-known base URI, `http://169.254.170.2`, configurable through the [`AWS_CONTAINER_SERVICE_ENDPOINT`](https://github.com/aws/aws-sdk-java-v2/blob/186b433dd32366b095c8bc415342accb8b734cf7/core/sdk-core/src/main/java/software/amazon/awssdk/core/SdkSystemSetting.java#L112) parameter. (In production deployments, such URI can be considered a constant; changing it is mainly useful for testing purposes.) Then, it sends a HTTP GET request to the resulting endpoint, which is expected to return a JSON object in its body with the AWS credentials for the container. Optionally, an `Authorization` header in the request may be set to the value of either the `AWS_CONTAINER_AUTHORIZATION_TOKEN` or `AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE` environment variables, if any of those are set. - Else, if the `AWS_CONTAINER_CREDENTIALS_FULL_URI` environment variable is set, the same request as above is sent to the endpoint determined by this variable, without any modification. The contents and handling of the response to this request are the same as above. Experimentally, I've found AWS Fargate to only set `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI`, without any authorization token. But the AWS documentation suggests that AWS EKS may make use of the full URI and token environment variables, so it's fair to say that supporting them is also useful in practice. To improve on the situation, these changes implement a lightweight but feature-complete ECS container credentials client in ´jsign´. This credentials client attempts to fetch credentials before its counterpart for IMDSv2, which guarantees that both EC2 and ECS environments are handled appropriately without any manual configuration, following the same logic as the AWS SDK. If neither credentials provider worked, the user is instructed to manually pass AWS credentials as before, and the resulting exception stack trace contains details on why both providers failed. I've tested this custom client logic to work successfully on a real AWS Fargate deployment. The documentation was also updated to reflect this new provider, and unit tests for it added. --- docs/index.html | 3 +- .../src/main/java/net/jsign/KeyStoreType.java | 8 +- .../java/net/jsign/jca/AmazonCredentials.java | 21 ++- .../jsign/jca/AmazonECSCredentialsClient.java | 155 ++++++++++++++++++ .../java/net/jsign/KeyStoreBuilderTest.java | 2 +- .../jca/AmazonECSCredentialsClientTest.java | 96 +++++++++++ jsign/src/deb/data/usr/share/man/man1/jsign.1 | 5 +- 7 files changed, 277 insertions(+), 13 deletions(-) create mode 100644 jsign-crypto/src/main/java/net/jsign/jca/AmazonECSCredentialsClient.java create mode 100644 jsign-crypto/src/test/java/net/jsign/jca/AmazonECSCredentialsClientTest.java diff --git a/docs/index.html b/docs/index.html index 9d7244cd..f712b528 100644 --- a/docs/index.html +++ b/docs/index.html @@ -710,7 +710,8 @@ <h4 id="example-awskms">Signing with AWS Key Management Service</h4> <p>The AWS access key, secret key, and optionally the session token, are concatenated and used as the <code>storepass</code> parameter; if the latter is not provided, Jsign attempts to fetch the credentials -from the environment variables (<code>AWS_ACCESS_KEY_ID</code>, <code>AWS_SECRET_ACCESS_KEY</code> and <code>AWS_SESSION_TOKEN</code>) +from the environment variables (<code>AWS_ACCESS_KEY_ID</code>, <code>AWS_SECRET_ACCESS_KEY</code> and <code>AWS_SESSION_TOKEN</code>), +from the <a href="https://docs.aws.amazon.com/sdkref/latest/guide/feature-container-credentials.html">ECS container credentials endpoint</a>, or from the <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html">IMDSv2</a> service when running on an AWS EC2 instance.</p> diff --git a/jsign-crypto/src/main/java/net/jsign/KeyStoreType.java b/jsign-crypto/src/main/java/net/jsign/KeyStoreType.java index 5a0cf7ac..9ab8e1c2 100644 --- a/jsign-crypto/src/main/java/net/jsign/KeyStoreType.java +++ b/jsign-crypto/src/main/java/net/jsign/KeyStoreType.java @@ -32,6 +32,7 @@ import java.util.LinkedHashSet; import java.util.Set; import java.util.function.Function; + import javax.smartcardio.CardException; import net.jsign.jca.AmazonCredentials; @@ -294,7 +295,8 @@ Set<String> getAliases(KeyStore keystore) throws KeyStoreException { * <p>The AWS access key, secret key, and optionally the session token, are concatenated and used as * the storepass parameter; if the latter is not provided, Jsign attempts to fetch the credentials from * the environment variables (<code>AWS_ACCESS_KEY_ID</code>, <code>AWS_SECRET_ACCESS_KEY</code> and - * <code>AWS_SESSION_TOKEN</code>) or from the IMDSv2 service when running on an AWS EC2 instance.</p> + * <code>AWS_SESSION_TOKEN</code>), from the ECS container credentials endpoint, or from the IMDSv2 + * service when running on an AWS EC2 instance.</p> * * <p>In any case, the credentials must allow the following actions: <code>kms:ListKeys</code>, * <code>kms:DescribeKey</code> and <code>kms:Sign</code>.</p> @@ -321,9 +323,9 @@ Provider getProvider(KeyStoreBuilder params) { } catch (UnknownServiceException e) { throw new IllegalArgumentException("storepass " + params.parameterName() + " must specify the AWS credentials: <accessKey>|<secretKey>[|<sessionToken>]" - + ", when not running from an EC2 instance (" + e.getMessage() + ")", e); + + ", when not running from ECS or an EC2 instance", e); } catch (IOException e) { - throw new RuntimeException("An error occurred while fetching temporary credentials from IMDSv2 service", e); + throw new RuntimeException("Failed fetching temporary credentials from ECS and IMDSv2 services", e); } } diff --git a/jsign-crypto/src/main/java/net/jsign/jca/AmazonCredentials.java b/jsign-crypto/src/main/java/net/jsign/jca/AmazonCredentials.java index b5517241..eb459eeb 100644 --- a/jsign-crypto/src/main/java/net/jsign/jca/AmazonCredentials.java +++ b/jsign-crypto/src/main/java/net/jsign/jca/AmazonCredentials.java @@ -70,14 +70,15 @@ public static AmazonCredentials parse(String credentials) throws IllegalArgument * <ul> * <li>The environment variables <tt>AWS_ACCESS_KEY_ID</tt> (or <tt>AWS_ACCESS_KEY</tt>), * <tt>AWS_SECRET_KEY</tt> (or <tt>AWS_SECRET_ACCESS_KEY</tt>) and <tt>AWS_SESSION_TOKEN</tt></li> + * <li>The ECS container credentials service (ECS, EKS, Greengrass, Fargate)</li> * <li>The EC2 instance metadata service (IMDSv2)</li> * </ul> */ public static AmazonCredentials getDefault() throws IOException { if (getenv("AWS_ACCESS_KEY_ID") != null || getenv("AWS_ACCESS_KEY") != null) { - String accesKey = getenv("AWS_ACCESS_KEY_ID"); - if (accesKey == null) { - accesKey = getenv("AWS_ACCESS_KEY"); + String accessKey = getenv("AWS_ACCESS_KEY_ID"); + if (accessKey == null) { + accessKey = getenv("AWS_ACCESS_KEY"); } String secretKey = getenv("AWS_SECRET_KEY"); if (secretKey == null) { @@ -85,10 +86,18 @@ public static AmazonCredentials getDefault() throws IOException { } String sessionToken = getenv("AWS_SESSION_TOKEN"); - return new AmazonCredentials(accesKey, secretKey, sessionToken); - + return new AmazonCredentials(accessKey, secretKey, sessionToken); } else { - return new AmazonIMDS2Client().getCredentials(); + try { + return new AmazonECSCredentialsClient().getCredentials(); + } catch (IOException ecsException) { + try { + return new AmazonIMDS2Client().getCredentials(); + } catch (IOException imds2Exception) { + ecsException.addSuppressed(imds2Exception); + throw ecsException; + } + } } } diff --git a/jsign-crypto/src/main/java/net/jsign/jca/AmazonECSCredentialsClient.java b/jsign-crypto/src/main/java/net/jsign/jca/AmazonECSCredentialsClient.java new file mode 100644 index 00000000..fd0f5317 --- /dev/null +++ b/jsign-crypto/src/main/java/net/jsign/jca/AmazonECSCredentialsClient.java @@ -0,0 +1,155 @@ +/* + * Copyright 2025 Alejandro González + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.jsign.jca; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.UnknownServiceException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; + +import com.cedarsoftware.util.io.JsonIoException; +import com.cedarsoftware.util.io.JsonReader; + +/** + * Client to query the Elastic Container Service (ECS) credential metadata + * endpoint for containers running in AWS. + * + * @since 7.2 + * @see + * <a href="https://docs.aws.amazon.com/sdkref/latest/guide/feature-container-credentials.html">Using + * the Amazon ECS container credentials provider</a> + * @see + * <a href="https://github.com/aws/aws-sdk-java-v2/blob/master/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProvider.java">ContainerCredentialsProvider</a> + */ +class AmazonECSCredentialsClient { + + private static final URL DEFAULT_AWS_CONTAINER_SERVICE_ENDPOINT; + + private final URL endpoint; + + static { + try { + DEFAULT_AWS_CONTAINER_SERVICE_ENDPOINT = new URL("http://169.254.170.2"); + } catch (MalformedURLException e) { + throw new AssertionError("Invalid default URI for AWS container credential metadata endpoint", e); + } + } + + /** + * Creates a new client to query the ECS credential metadata endpoint, using + * the endpoint URL provided by the environment variables + * {@code AWS_CONTAINER_CREDENTIALS_RELATIVE_URI} or + * {@code AWS_CONTAINER_CREDENTIALS_FULL_URI}. + * + * @throws UnknownServiceException If no valid ECS endpoint URL is + * available. + */ + AmazonECSCredentialsClient() throws UnknownServiceException { + this(defaultCredentialsUrl()); + } + + /** + * Creates a new client to query the ECS credential metadata endpoint, using + * the specified endpoint URL. + * + * @param endpoint The URL of the ECS credential metadata endpoint. + * @throws IllegalArgumentException If the endpoint URL is null or has an + * unexpected protocol. + */ + AmazonECSCredentialsClient(URL endpoint) { + if (endpoint == null || (!"http".equals(endpoint.getProtocol()) && !"https".equals(endpoint.getProtocol()))) { + throw new IllegalArgumentException( + "Null endpoint or unexpected protocol for AWS container credential metadata endpoint: " + endpoint + ); + } + + this.endpoint = endpoint; + } + + /** + * Queries the ECS credential metadata endpoint to obtain the credentials + * for the container. + */ + public AmazonCredentials getCredentials() throws IOException { + HttpURLConnection connection = (HttpURLConnection) this.endpoint.openConnection(); + connection.setConnectTimeout(3000); + connection.setReadTimeout(3000); + + String authToken = System.getenv("AWS_CONTAINER_AUTHORIZATION_TOKEN"); + if (authToken == null) { + String authTokenFile = System.getenv("AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE"); + if (authTokenFile != null) { + authToken = new String(Files.readAllBytes(Paths.get(authTokenFile)), StandardCharsets.UTF_8); + } + } + if (authToken != null) { + connection.addRequestProperty("Authorization", authToken); + } + + int responseCode = connection.getResponseCode(); + if (responseCode != 200) { + String responseMessage = connection.getResponseMessage(); + throw new IOException(String.format( + "Unexpected HTTP response code fetching AWS container credentials: %d (%s)", + responseCode, responseMessage == null ? "No message" : responseMessage + )); + } + + try { + Map<String, String> json = JsonReader.jsonToMaps(connection.getInputStream(), new HashMap<>()); + return new AmazonCredentials(json.get("AccessKeyId"), json.get("SecretAccessKey"), json.get("Token")); + } catch (JsonIoException e) { + throw new IOException("Error parsing JSON response from AWS container credentials endpoint", e); + } + } + + /** + * Returns the URL of the ECS credential metadata endpoint, using the + * environment variables {@code AWS_CONTAINER_CREDENTIALS_RELATIVE_URI} or + * {@code AWS_CONTAINER_CREDENTIALS_FULL_URI}. + * + * @throws UnknownServiceException If no valid ECS endpoint URL is + * available. + */ + private static URL defaultCredentialsUrl() throws UnknownServiceException { + String relativeUri, fullUri; + URL endpoint; + + if ((relativeUri = System.getenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI")) != null) { + try { + endpoint = new URL(DEFAULT_AWS_CONTAINER_SERVICE_ENDPOINT, relativeUri); + } catch (MalformedURLException e) { + throw new UnknownServiceException("Invalid relative URI for AWS container credential metadata endpoint: " + relativeUri); + } + } else if ((fullUri = System.getenv("AWS_CONTAINER_CREDENTIALS_FULL_URI")) != null) { + try { + endpoint = new URL(fullUri); + } catch (MalformedURLException e) { + throw new UnknownServiceException("Invalid full URI for AWS container credential metadata endpoint: " + fullUri); + } + } else { + throw new UnknownServiceException("No AWS container credential metadata endpoint URIs available"); + } + + return endpoint; + } +} diff --git a/jsign-crypto/src/test/java/net/jsign/KeyStoreBuilderTest.java b/jsign-crypto/src/test/java/net/jsign/KeyStoreBuilderTest.java index 65be95ce..42b1ef1e 100644 --- a/jsign-crypto/src/test/java/net/jsign/KeyStoreBuilderTest.java +++ b/jsign-crypto/src/test/java/net/jsign/KeyStoreBuilderTest.java @@ -110,7 +110,7 @@ public void testBuildAWS() throws Exception { e = assertThrows(IllegalArgumentException.class, builder::build); assertTrue("message", e.getMessage().matches( - "storepass parameter must specify the AWS credentials\\: \\<accessKey\\>\\|\\<secretKey\\>\\[\\|\\<sessionToken\\>\\], when not running from an EC2 instance \\(.*\\)")); + "storepass parameter must specify the AWS credentials\\: \\<accessKey\\>\\|\\<secretKey\\>\\[\\|\\<sessionToken\\>\\], when not running from ECS or an EC2 instance")); builder.storepass("<accessKey>|<secretKey>|<sessionToken>"); diff --git a/jsign-crypto/src/test/java/net/jsign/jca/AmazonECSCredentialsClientTest.java b/jsign-crypto/src/test/java/net/jsign/jca/AmazonECSCredentialsClientTest.java new file mode 100644 index 00000000..7da5b9f1 --- /dev/null +++ b/jsign-crypto/src/test/java/net/jsign/jca/AmazonECSCredentialsClientTest.java @@ -0,0 +1,96 @@ +/* + * Copyright 2025 Alejandro González + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.jsign.jca; + +import java.io.IOException; +import java.net.URL; + +import org.junit.After; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; + +import static net.jadler.Jadler.closeJadler; +import static net.jadler.Jadler.initJadler; +import static net.jadler.Jadler.onRequest; +import static net.jadler.Jadler.port; + +public class AmazonECSCredentialsClientTest { + + @Before + public void setUp() { + initJadler().withDefaultResponseStatus(404); + } + + @After + public void tearDown() { + closeJadler(); + } + + @Test + public void testUnreachable() throws Exception { + AmazonECSCredentialsClient client = new AmazonECSCredentialsClient( + new URL("http://localhost:31457") + ); + + assertThrows(IOException.class, client::getCredentials); + } + + @Test + public void testServerError() throws Exception { + onRequest() + .havingMethodEqualTo("GET") + .havingPathEqualTo("/get-credentials") + .havingQueryStringEqualTo("a=1") + .respond() + .withStatus(503); + + AmazonECSCredentialsClient client = new AmazonECSCredentialsClient( + new URL("http://localhost:" + port() + "/get-credentials?a=1") + ); + + Exception e = assertThrows(IOException.class, client::getCredentials); + assertTrue("message", e.getMessage().startsWith("Unexpected HTTP response code fetching AWS container credentials: 503")); + } + + @Test + public void testGetCredentials() throws Exception { + onRequest() + .havingMethodEqualTo("GET") + .havingPathEqualTo("/get-credentials") + .havingQueryStringEqualTo("a=1") + .respond() + .withStatus(200) + .withBody("{" + + "\"AccessKeyId\" : \"accessKey\", " + + "\"SecretAccessKey\" : \"secretKey\", " + + "\"Token\" : \"sessionToken\"" + + "}"); + + AmazonECSCredentialsClient client = new AmazonECSCredentialsClient( + new URL("http://localhost:" + port() + "/get-credentials?a=1") + ); + + AmazonCredentials credentials = client.getCredentials(); + assertNotNull("credentials", credentials); + assertEquals("access key", "accessKey", credentials.getAccessKey()); + assertEquals("secret key", "secretKey", credentials.getSecretKey()); + assertEquals("session token", "sessionToken", credentials.getSessionToken()); + } +} diff --git a/jsign/src/deb/data/usr/share/man/man1/jsign.1 b/jsign/src/deb/data/usr/share/man/man1/jsign.1 index 501216de..f8512d9d 100644 --- a/jsign/src/deb/data/usr/share/man/man1/jsign.1 +++ b/jsign/src/deb/data/usr/share/man/man1/jsign.1 @@ -346,8 +346,9 @@ The keystore parameter references the AWS region. The AWS access key, secret key, and optionally the session token, are concatenated and used as the storepass parameter; if the latter is not provided, Jsign attempts to fetch the credentials -from the environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN) -or from the IMDSv2 service when running on an AWS EC2 instance. +from the environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN), +from the ECS container credentials endpoint, or from the IMDSv2 service when running on an AWS +EC2 instance. In any case, the credentials must allow the following actions: kms:ListKeys, kms:DescribeKey and kms:Sign. From 535387c9e7b1729fde37b3820e588a0c79611877 Mon Sep 17 00:00:00 2001 From: Emmanuel Bourg <ebourg@apache.org> Date: Sat, 22 Mar 2025 23:03:51 +0100 Subject: [PATCH 3/3] Changelog & credits update --- README.md | 4 ++++ docs/index.html | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b6a8d90d..2910aa61 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,10 @@ See https://ebourg.github.io/jsign for more information. ## Changes +#### Version 7.2 (in development) + +* ECS container credentials are now supported when signing with AWS KMS (contributed by Alejandro González) + #### Version 7.1 (2025-02-14) * New signing service: SignPath diff --git a/docs/index.html b/docs/index.html index f712b528..847aaada 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1122,8 +1122,8 @@ <h3 id="credits">Credits</h3> MSI signing was possible thanks to the work done by the <a href="https://github.com/mtrojnar/osslsigncode">osslsigncode</a> and <a href="https://poi.apache.org/">Apache POI</a> projects.</p> <p>Jsign includes contributions from Emmanuel Bourg, Florent Daigniere, Michael Szediwy, Michael Peterson, Markus Kilås, -Erwin Tratar, Björn Kautler, Joseph Lee, Maria Merkel, Vincent Malmedy, Sebastian Müller, Sebastian Stamm -and Eatay Mizrachi.</p> +Erwin Tratar, Björn Kautler, Joseph Lee, Maria Merkel, Vincent Malmedy, Sebastian Müller, Sebastian Stamm, +Eatay Mizrachi and Alejandro González.</p> <h3 id="contacts">Contact</h3>