Skip to content

Commit 0f49389

Browse files
committed
Gradle plugin registry compatibility.
Based on some feedback from the Gradle plugin portal review, the package names have been changed. on-behalf-of: @zynga [email protected]
1 parent 99b4261 commit 0f49389

File tree

6 files changed

+63
-37
lines changed

6 files changed

+63
-37
lines changed

README.md

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ The problem with this feature is that Gradle uses an outdated set of Git librari
1212
* No support for modern private key formats (eg. EdDSA).
1313
* No support for password authentication.
1414
* No support for encrypted private keys.
15-
* SHA1 for all key hashing. On top of being insecure, SHA1 hashes are explicitly [forbidden](https://github.blog/2021-09-01-improving-git-protocol-security-github/#dropping-insecure-algorithms) by GitHub. ***This means that Gradle source dependencies will not work with GitHub under any circumstances!***
15+
* SHA1 for all key hashing. On top of being insecure, SHA1 hashes are explicitly [forbidden](https://github.blog/2021-09-01-improving-git-protocol-security-github/#dropping-insecure-algorithms) by GitHub. ***This means that SSH Gradle source dependencies will not work with GitHub under any circumstances!*** As an alternative, HTTPS access can still work but without any support from Gradle for different authentication mechanisms, this is limited only to repositories that allow fully anonymous access.
1616

17-
This plugin fixes these issues by automatically spinning up local, ephemeral SSH proxy servers when a source dependency is identified in a build to act as a ‘man in the middle’ when Gradle attempts to retrieve the dependency. With this setup, the proxy can connect to the source repository using a [modern SSH implementation](https://mina.apache.org/sshd-project/), and provide a backward compatible SSH server to Gradle at the same time. In addition to modernizing the SSH implementation, the proxy server can inject additional authentication mechanisms, such as support for passwords, modern private key formats, key encryption, and SSH agent support, all of which Gradle source dependencies lack. This is important as it ensures that all possible users are not simply locked out of one of the most popular Git hosting services because of their development infrastructure.
17+
This plugin fixes these issues by automatically spinning up local, ephemeral SSH proxy servers when a source dependency is identified in a build to act as a ‘man in the middle’ when Gradle attempts to retrieve the dependency. With this setup, the proxy can connect to the source repository using a [modern SSH implementation](https://mina.apache.org/sshd-project/), and provide a backward compatible SSH server to Gradle at the same time. In addition to modernizing the SSH implementation, the proxy server can inject additional authentication mechanisms, such as support for passwords, modern private key formats, key encryption, and SSH agent support, all of which Gradle source dependencies lack. This is important as it ensures that users are not simply locked out the most popular Git hosting services because of their development infrastructure.
1818

1919
Unlike other plugins that may address this issue by introducing their own set of configuration structures and dependency resolution mechanisms, this plugin does not attempt to replace a core Gradle feature and therefore has full support from modern IDEs, which recognize source dependencies and treat them appropriately.
2020

@@ -24,7 +24,7 @@ Unlike other plugins that may address this issue by introducing their own set of
2424
The only difference when compared to the base usage of source dependencies, aside from including the plugin, is the call to 'sshProxy' when specifying the dependency's URI. If the input URI does not use the SSH scheme or is otherwise invalid, no proxy redirection takes place and it will behave as if the plugin were not applied to the repository specification.
2525

2626
```
27-
import com.zynga.aquinney.sshcmdproxy.SSHCmdProxy.sshProxy;
27+
import io.github.aquinney0.sshcmdproxy.SSHCmdProxy.sshProxy;
2828
2929
pluginManagement {
3030
repositories {
@@ -36,7 +36,7 @@ pluginManagement {
3636
plugins {
3737
// Apply the foojay-resolver plugin to allow automatic download of JDKs
3838
id("org.gradle.toolchains.foojay-resolver-convention") version "0.4.0"
39-
id("com.zynga.aquinney.ssh-cmd-proxy") version "1.0"
39+
id("io.github.aquinney0.ssh-cmd-proxy") version "1.0"
4040
}
4141
4242
rootProject.name = "project"
@@ -60,25 +60,25 @@ On systems that support UNIX domain sockets (Linux, MacOS, UNIX, Windows 10 buil
6060
### Plugin Settings
6161
Plugin settings can be applied via [Gradle configuration](https://docs.gradle.org/current/userguide/build_environment.html) to customize some functionality. All entries are optional and need only be added if customization or additional authentication methods are required.
6262

63-
* **com.zynga.aquinney.ssh-cmd-proxy.key-password-<ANY_VALUE>** - string value that indicates a password that should be used in attempting to decode an encrypted private key file. The plugin may use these values if other methods of authentication were not successful. There can be any number of these properties and the plugin will attempt each on encrypted private key files until one is successful.
64-
* **com.zynga.aquinney.ssh-cmd-proxy.password-\<USER\>@\<HOST\>[:\<PORT\>]** - string value that indicates the password to use when connecting as a particular user to an SSH Git service. If the default SSH port (22) is targeted, the port section can be omitted. This value may be used if other authentication mechanisms are not successful.
65-
* **com.zynga.aquinney.ssh-cmd-proxy.connect-timeout** - integer value that represents the maximum amount of time in milliseconds that the proxy server will wait in attempting to get a connection to a remote SSH Git service. Defaults to 3000.
66-
* **com.zynga.aquinney.ssh-cmd-proxy.auth-timeout** - integer value that represents the maximum amount ot time in milliseconds that the proxy server will wait in attempting to authenticate after connecting to a remote SSH Git service. Defaults to 3000.
67-
* **com.zynga.aquinney.ssh-cmd-proxy.channel-timeout** - integer value that represents the maximum amount ot time in milliseconds that the proxy server will wait in attempting to open a channel after authenticating with a remote SSH Git service. Defaults to 3000.
63+
* **io.github.aquinney0.ssh-cmd-proxy.key-password-<ANY_VALUE>** - string value that indicates a password that should be used in attempting to decode an encrypted private key file. The plugin may use these values if other methods of authentication were not successful. There can be any number of these properties and the plugin will attempt each on encrypted private key files until one is successful.
64+
* **io.github.aquinney0.ssh-cmd-proxy.password-\<USER\>@\<HOST\>[:\<PORT\>]** - string value that indicates the password to use when connecting as a particular user to an SSH Git service. If the default SSH port (22) is targeted, the port section can be omitted. This value may be used if other authentication mechanisms are not successful.
65+
* **io.github.aquinney0.ssh-cmd-proxy.connect-timeout** - integer value that represents the maximum amount of time in milliseconds that the proxy server will wait in attempting to get a connection to a remote SSH Git service. Defaults to 3000.
66+
* **io.github.aquinney0.ssh-cmd-proxy.auth-timeout** - integer value that represents the maximum amount of time in milliseconds that the proxy server will wait in attempting to authenticate after connecting to a remote SSH Git service. Defaults to 3000.
67+
* **io.github.aquinney0.ssh-cmd-proxy.channel-timeout** - integer value that represents the maximum amount of time in milliseconds that the proxy server will wait in attempting to open a channel after authenticating with a remote SSH Git service. Defaults to 3000.
6868

6969
#### Example ~/.gradle/gradle.properties
7070
```properties
7171
# The suffixes of these property names is not relevant - they are used for local private key decryption and are tried iteratively.
72-
com.zynga.aquinney.ssh-cmd-proxy.key-password-firstFileSecret=password
73-
com.zynga.aquinney.ssh-cmd-proxy.key-password-secondFileSecret=thisIsMyDecryptionKey
72+
io.github.aquinney0.ssh-cmd-proxy.key-password-firstFileSecret=password
73+
io.github.aquinney0.ssh-cmd-proxy.key-password-secondFileSecret=thisIsMyDecryptionKey
7474

7575
# Passwords may only be applied to the user/host/port indicated in the property names.
76-
com.zynga.aquinney.ssh-cmd-proxy.password-aquinney0@github.com=andrewsPassword
77-
com.zynga.aquinney[email protected]\:22=andrewsCorporatePassword
76+
io.github.aquinney0.ssh-cmd-proxy.password-aquinney0@github.com=andrewsPassword
77+
io.github.aquinney0[email protected]\:22=andrewsCorporatePassword
7878

79-
com.zynga.aquinney.ssh-cmd-proxy.connect-timeout=1000
80-
com.zynga.aquinney.ssh-cmd-proxy.auth-timeout=1000
81-
com.zynga.aquinney.ssh-cmd-proxy.channel-timeout=1000
79+
io.github.aquinney0.ssh-cmd-proxy.connect-timeout=1000
80+
io.github.aquinney0.ssh-cmd-proxy.auth-timeout=1000
81+
io.github.aquinney0.ssh-cmd-proxy.channel-timeout=1000
8282
```
8383

8484
## Requirements
@@ -88,6 +88,21 @@ com.zynga.aquinney.ssh-cmd-proxy.channel-timeout=1000
8888

8989
The plugin is built for Java 17. Theoretically, it could be targeted at 16, but no earlier since it makes use of the Java 16 support for UNIX domain sockets. The new Java support is used instead of relying on MINA's method that requires the Tomcat Native libraries preinstalled.
9090

91+
### Compatibility Notes
92+
93+
* There appears to be a bug with [Gradle 8.7.0](https://github.com/gradle/gradle/releases/tag/v8.7.0) that is causing the JGit SSH implementation to be unable to connect to any SSH repository. This is unrelated to this plugin and is reproducible with any source dependency. Therefore, it is recommended that 8.7 be avoided if source dependencies are a required feature.
94+
95+
```
96+
2024-04-12T12:59:02.542-0400 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] Caused by: java.lang.NoSuchMethodError: 'java.lang.Object org.apache.sshd.client.future.ConnectFuture.verify()'
97+
2024-04-12T12:59:02.542-0400 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] at org.eclipse.jgit.transport.sshd.SshdSession.connect(SshdSession.java:189)
98+
2024-04-12T12:59:02.543-0400 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] at org.eclipse.jgit.transport.sshd.SshdSession.connect(SshdSession.java:142)
99+
2024-04-12T12:59:02.543-0400 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] at org.eclipse.jgit.transport.sshd.SshdSession.connect(SshdSession.java:99)
100+
2024-04-12T12:59:02.543-0400 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] at org.eclipse.jgit.transport.sshd.SshdSessionFactory.getSession(SshdSessionFactory.java:235)
101+
2024-04-12T12:59:02.543-0400 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] at org.eclipse.jgit.transport.sshd.SshdSessionFactory.getSession(SshdSessionFactory.java:1)
102+
2024-04-12T12:59:02.543-0400 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] at org.eclipse.jgit.transport.SshTransport.getSession(SshTransport.java:107)
103+
2024-04-12T12:59:02.543-0400 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] at org.eclipse.jgit.transport.TransportGitSsh$SshFetchConnection.<init>(TransportGitSsh.java:281)
104+
```
105+
91106
## Contributing
92107

93108
The code uses the [Google Java Style](https://github.com/google/google-java-format).

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

plugin/build.gradle.kts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ dependencies {
2929
implementation("net.i2p.crypto:eddsa:0.3.0")
3030
}
3131

32-
group = "com.zynga"
32+
group = "io.github.aquinney0"
3333
version = "1.0"
3434

3535
// Apply a specific Java toolchain to ease working on different environments.
@@ -39,6 +39,11 @@ java {
3939
}
4040
}
4141

42+
signing {
43+
useGpgCmd()
44+
sign(configurations.runtimeElements.get())
45+
}
46+
4247
publishing {
4348
publications {
4449
create<MavenPublication>("SSHCmdProxy") {
@@ -54,13 +59,20 @@ publishing {
5459
}
5560
}
5661

62+
// Fixes the Gradle bug: https://discuss.gradle.org/t/implicit-dependency-among-tasks-but-the-tasks-do-not-exist/46127
63+
tasks.configureEach {
64+
if (name == "publishPluginMavenPublicationToMavenLocal") {
65+
mustRunAfter(tasks.findByName("signRuntimeElements"))
66+
}
67+
}
68+
5769
gradlePlugin {
5870
website = "https://github.com/zynga/ssh-cmd-proxy"
5971
vcsUrl = "https://github.com/zynga/ssh-cmd-proxy.git"
6072
plugins {
6173
create("SSHCmdProxy") {
62-
id = "com.zynga.aquinney.ssh-cmd-proxy"
63-
implementationClass = "com.zynga.aquinney.sshcmdproxy.SSHCmdProxy"
74+
id = "io.github.aquinney0.ssh-cmd-proxy"
75+
implementationClass = "io.github.aquinney0.sshcmdproxy.SSHCmdProxy"
6476
displayName = "SSH Command Proxy"
6577
description =
6678
"A Gradle plugin that fixes some technical deficiencies and expands the capabilities of source dependencies."
@@ -69,11 +81,6 @@ gradlePlugin {
6981
}
7082
}
7183

72-
signing {
73-
useGpgCmd()
74-
sign(configurations.runtimeElements.get())
75-
}
76-
7784
spotless {
7885
java {
7986
googleJavaFormat()
@@ -103,4 +110,4 @@ tasks.named<Task>("check") {
103110
tasks.named<Test>("test") {
104111
// Use JUnit Jupiter for unit tests.
105112
useJUnitPlatform()
106-
}
113+
}

plugin/src/main/java/com/zynga/aquinney/sshcmdproxy/ProxyInstance.java renamed to plugin/src/main/java/io/github/aquinney0/sshcmdproxy/ProxyInstance.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.zynga.aquinney.sshcmdproxy;
1+
package io.github.aquinney0.sshcmdproxy;
22

33
import java.io.*;
44
import java.net.*;
@@ -60,6 +60,7 @@ public ProxyInstance(
6060
final SimpleGeneratorHostKeyProvider idProvider =
6161
new SimpleGeneratorHostKeyProvider() {
6262
{
63+
// RSA seems like the best choice for supporting a legacy SSH client.
6364
setAlgorithm(KeyUtils.RSA_ALGORITHM);
6465
}
6566
};
@@ -255,7 +256,7 @@ public void run() {
255256
new FilePasswordProvider() {
256257
private final List<String> keyPasswords =
257258
configurationSource
258-
.getProperties("com.zynga.aquinney.ssh-cmd-proxy.key-password-")
259+
.getProperties("io.github.aquinney0.ssh-cmd-proxy.key-password-")
259260
.stream()
260261
.toList();
261262

@@ -284,7 +285,7 @@ public ResourceDecodeResult handleDecodeAttemptResult(
284285
.connect(gitService.toString())
285286
.verify(
286287
Duration.ofMillis(
287-
numberProperty("com.zynga.aquinney.ssh-cmd-proxy.connect-timeout", 3000)))
288+
numberProperty("io.github.aquinney0.ssh-cmd-proxy.connect-timeout", 3000)))
288289
.getClientSession()) {
289290
logger.info("[{}] Connection successful.", gitService);
290291
if (!GenericUtils.isEmpty(sshClient.getString(SshAgent.SSH_AUTHSOCKET_ENV_NAME))) {
@@ -303,7 +304,7 @@ public SshAgent createClient(Session session, FactoryManager manager)
303304
final InetSocketAddress socketAddress =
304305
(InetSocketAddress) clientSession.getConnectAddress();
305306
final String basePropertyName =
306-
"com.zynga.aquinney.ssh-cmd-proxy.password-"
307+
"io.github.aquinney0.ssh-cmd-proxy.password-"
307308
+ Optional.ofNullable(clientSession.getUsername())
308309
.map(username -> username + "@")
309310
.orElse("")
@@ -325,19 +326,22 @@ public SshAgent createClient(Session session, FactoryManager manager)
325326
.auth()
326327
.verify(
327328
Duration.ofMillis(
328-
numberProperty("com.zynga.aquinney.ssh-cmd-proxy.auth-timeout", 3000)));
329+
numberProperty("io.github.aquinney0.ssh-cmd-proxy.auth-timeout", 3000)));
329330
logger.info("[{}] Authentication successful.", gitService);
331+
logger.info("[{}] Opening channel...", gitService);
330332
try (final ClientChannel clientChannel = clientSession.createExecChannel(getCommand())) {
331333
clientChannel.setIn(getInputStream());
332334
clientChannel.setOut(getOutputStream());
333335
clientChannel.setErr(getErrorStream());
334-
logger.info("[{}] Opening channel...", gitService);
335336
clientChannel
336337
.open()
337338
.verify(
338339
Duration.ofMillis(
339-
numberProperty("com.zynga.aquinney.ssh-cmd-proxy.channel-timeout", 3000)));
340-
logger.info("[{}] Channel opened successfully.", gitService);
340+
numberProperty("io.github.aquinney0.ssh-cmd-proxy.channel-timeout", 3000)));
341+
logger.info(
342+
"[{}] Channel opened successfully, processing command: {}",
343+
gitService,
344+
getCommand());
341345
clientChannel.waitFor(List.of(ClientChannelEvent.CLOSED), 0);
342346
}
343347
}
@@ -377,7 +381,7 @@ private long numberProperty(String propertyName, long defaultValue) {
377381
public static void main(String[] args) throws Throwable {
378382
try (final ProxyInstance proxyInstance =
379383
new ProxyInstance(
380-
URI.create("ssh://github.com"),
384+
URI.create("ssh://github-internal.zynga.com"),
381385
7272,
382386
new ConfigurationSource() {
383387
@Override

plugin/src/main/java/com/zynga/aquinney/sshcmdproxy/SSHCmdProxy.java renamed to plugin/src/main/java/io/github/aquinney0/sshcmdproxy/SSHCmdProxy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.zynga.aquinney.sshcmdproxy;
1+
package io.github.aquinney0.sshcmdproxy;
22

33
import java.io.Closeable;
44
import java.io.IOException;

plugin/src/main/java/com/zynga/aquinney/sshcmdproxy/UnixSocketAgentClient.java renamed to plugin/src/main/java/io/github/aquinney0/sshcmdproxy/UnixSocketAgentClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.zynga.aquinney.sshcmdproxy;
1+
package io.github.aquinney0.sshcmdproxy;
22

33
import java.io.IOException;
44
import java.io.InterruptedIOException;

0 commit comments

Comments
 (0)