1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.named.redisson;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.net.URI;
24 import java.nio.file.Files;
25 import java.nio.file.Path;
26 import java.nio.file.Paths;
27 import java.util.Locale;
28
29 import org.eclipse.aether.named.support.NamedLockFactorySupport;
30 import org.redisson.Redisson;
31 import org.redisson.api.RedissonClient;
32 import org.redisson.config.Config;
33
34
35
36
37 public abstract class RedissonNamedLockFactorySupport extends NamedLockFactorySupport {
38 protected static final String NAME_PREFIX = "maven:resolver:";
39
40 private static final String DEFAULT_CONFIG_FILE_NAME = "maven-resolver-redisson.yaml";
41
42 private static final String DEFAULT_CLIENT_NAME = "maven-resolver";
43
44
45
46
47
48
49
50
51 public static final String SYSTEM_PROP_CONFIG_FILE = "aether.syncContext.named.redisson.configFile";
52
53
54
55
56
57
58
59
60
61 public static final String SYSTEM_PROP_REDIS_ADDRESS = "aether.syncContext.named.redisson.address";
62
63 public static final String DEFAULT_REDIS_ADDRESS = "redis://localhost:6379";
64
65
66
67
68
69
70
71
72
73
74
75
76
77 public static final String SYSTEM_PROP_ALLOW_INSECURE_ADDRESS =
78 "aether.syncContext.named.redisson.allowInsecureAddress";
79
80 protected final RedissonClient redissonClient;
81
82 public RedissonNamedLockFactorySupport() {
83 this.redissonClient = createRedissonClient();
84 }
85
86 @Override
87 protected void doShutdown() {
88 logger.trace("Shutting down Redisson client with id '{}'", redissonClient.getId());
89 redissonClient.shutdown();
90 }
91
92 private RedissonClient createRedissonClient() {
93 Path configFilePath = null;
94
95 String configFile = System.getProperty(SYSTEM_PROP_CONFIG_FILE);
96 if (configFile != null && !configFile.isEmpty()) {
97 configFilePath = Paths.get(configFile);
98 if (Files.notExists(configFilePath)) {
99 throw new IllegalArgumentException(
100 "The specified Redisson config file does not exist: " + configFilePath);
101 }
102 }
103
104 if (configFilePath == null) {
105 String mavenConf = System.getProperty("maven.conf");
106 if (mavenConf != null && !mavenConf.isEmpty()) {
107 configFilePath = Paths.get(mavenConf, DEFAULT_CONFIG_FILE_NAME);
108 if (Files.notExists(configFilePath)) {
109 configFilePath = null;
110 }
111 }
112 }
113
114 Config config;
115
116 if (configFilePath != null) {
117 logger.trace("Reading Redisson config file from '{}'", configFilePath);
118 try (InputStream is = Files.newInputStream(configFilePath)) {
119 config = Config.fromYAML(is);
120 } catch (IOException e) {
121 throw new IllegalStateException("Failed to read Redisson config file: " + configFilePath, e);
122 }
123 } else {
124 config = new Config();
125 String defaultRedisAddress = System.getProperty(SYSTEM_PROP_REDIS_ADDRESS, DEFAULT_REDIS_ADDRESS);
126 if (isInsecureRemoteAddress(defaultRedisAddress)) {
127 if (Boolean.getBoolean(SYSTEM_PROP_ALLOW_INSECURE_ADDRESS)) {
128 logger.warn(
129 "Using plaintext Redis address '{}' for lock state guarding local repository writes;"
130 + " the connection is unencrypted and unauthenticated at the transport, so the"
131 + " endpoint and the network path to it must be trusted and isolated",
132 defaultRedisAddress);
133 } else {
134 throw new IllegalStateException("Refusing plaintext non-loopback Redis address '"
135 + defaultRedisAddress + "': lock answers from a tampered or spoofed Redis can void"
136 + " mutual exclusion and corrupt the shared local repository. Use a 'rediss://' (TLS)"
137 + " address, or a Redisson configuration file ('" + SYSTEM_PROP_CONFIG_FILE
138 + "') with authentication, or explicitly opt in with -D"
139 + SYSTEM_PROP_ALLOW_INSECURE_ADDRESS + "=true");
140 }
141 }
142 config.useSingleServer().setAddress(defaultRedisAddress).setClientName(DEFAULT_CLIENT_NAME);
143 }
144
145 RedissonClient redissonClient = Redisson.create(config);
146 logger.trace("Created Redisson client with id '{}'", redissonClient.getId());
147
148 return redissonClient;
149 }
150
151
152
153
154
155
156 static boolean isInsecureRemoteAddress(String address) {
157 if (address.toLowerCase(Locale.ROOT).startsWith("rediss://")) {
158 return false;
159 }
160 String host;
161 try {
162 host = URI.create(address).getHost();
163 } catch (IllegalArgumentException e) {
164 return true;
165 }
166 if (host == null) {
167 return true;
168 }
169 return !("localhost".equalsIgnoreCase(host)
170 || host.startsWith("127.")
171 || "::1".equals(host)
172 || "[::1]".equals(host));
173 }
174 }