Skip to content

Commit aa1387d

Browse files
authored
Merge pull request #209 from IABTechLab/ian-UID2-1394-update-attest-failure-reaction
Ian UI d2 1394 update attest failure reaction
2 parents 91dc0a4 + 4badf21 commit aa1387d

File tree

7 files changed

+186
-144
lines changed

7 files changed

+186
-144
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<enclave-aws.version>1.1.0</enclave-aws.version>
2323
<enclave-azure.version>1.5.0-115595d597</enclave-azure.version>
2424
<enclave-gcp.version>1.4.2-dd1920710d</enclave-gcp.version>
25-
<uid2-shared.version>5.17.0-7c1a08e2f9</uid2-shared.version>
25+
<uid2-shared.version>5.19.0-0a359c5a31</uid2-shared.version>
2626
<image.version>${project.version}</image.version>
2727
</properties>
2828

src/main/java/com/uid2/operator/Main.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
import com.uid2.operator.monitoring.OperatorMetrics;
99
import com.uid2.operator.monitoring.StatsCollectorVerticle;
1010
import com.uid2.operator.service.SecureLinkValidatorService;
11+
import com.uid2.operator.vertx.OperatorShutdownHandler;
1112
import com.uid2.operator.store.CloudSyncOptOutStore;
1213
import com.uid2.operator.store.OptOutCloudStorage;
13-
import com.uid2.operator.vertx.OperatorDisableHandler;
1414
import com.uid2.operator.vertx.UIDOperatorVerticle;
1515
import com.uid2.shared.ApplicationVersion;
1616
import com.uid2.shared.Utils;
@@ -42,6 +42,7 @@
4242
import io.vertx.micrometer.backends.BackendRegistries;
4343
import org.slf4j.Logger;
4444
import org.slf4j.LoggerFactory;
45+
import software.amazon.awssdk.utils.Pair;
4546

4647
import javax.management.*;
4748
import java.lang.management.ManagementFactory;
@@ -69,11 +70,11 @@ public class Main {
6970
private final RotatingClientSideKeypairStore clientSideKeypairProvider;
7071
private final RotatingSaltProvider saltProvider;
7172
private final CloudSyncOptOutStore optOutStore;
73+
private OperatorShutdownHandler shutdownHandler = null;
7274
private final OperatorMetrics metrics;
7375
private final boolean clientSideTokenGenerate;
7476
private final boolean validateServiceLinks;
7577
private IStatsCollectorQueue _statsCollectorQueue;
76-
private OperatorDisableHandler disableHandler = null;
7778
private RotatingServiceStore serviceProvider;
7879
private RotatingServiceLinkStore serviceLinkProvider;
7980

@@ -101,10 +102,9 @@ public Main(Vertx vertx, JsonObject config) throws Exception {
101102

102103
DownloadCloudStorage fsStores;
103104
if (coreAttestUrl != null) {
104-
Duration disableWaitTime = Duration.ofHours(this.config.getInteger(Const.Config.FailureShutdownWaitHoursProp, 120));
105-
this.disableHandler = new OperatorDisableHandler(disableWaitTime, Clock.systemUTC());
105+
this.shutdownHandler = new OperatorShutdownHandler(Duration.ofHours(12), Clock.systemUTC());
106106

107-
var clients = createUidClients(this.vertx, coreAttestUrl, operatorKey, this.disableHandler::handleResponseStatus);
107+
var clients = createUidClients(this.vertx, coreAttestUrl, operatorKey, this.shutdownHandler::handleResponse);
108108
UidCoreClient coreClient = clients.getKey();
109109
UidOptOutClient optOutClient = clients.getValue();
110110
fsStores = coreClient;
@@ -252,8 +252,6 @@ private ICloudStorage wrapCloudStorageForOptOut(ICloudStorage cloudStorage) {
252252
private void run() throws Exception {
253253
Supplier<Verticle> operatorVerticleSupplier = () -> {
254254
UIDOperatorVerticle verticle = new UIDOperatorVerticle(config, this.clientSideTokenGenerate, siteProvider, clientKeyProvider, clientSideKeypairProvider, getKeyManager(), saltProvider, optOutStore, Clock.systemUTC(), _statsCollectorQueue, new SecureLinkValidatorService(this.serviceLinkProvider, this.serviceProvider));
255-
if (this.disableHandler != null)
256-
verticle.setDisableHandler(this.disableHandler);
257255
return verticle;
258256
};
259257

@@ -453,15 +451,15 @@ public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticC
453451
.register(globalRegistry);
454452
}
455453

456-
private Map.Entry<UidCoreClient, UidOptOutClient> createUidClients(Vertx vertx, String attestationUrl, String clientApiToken, Handler<Integer> responseWatcher) throws Exception {
454+
private Map.Entry<UidCoreClient, UidOptOutClient> createUidClients(Vertx vertx, String attestationUrl, String clientApiToken, Handler<Pair<Integer, String>> responseWatcher) throws Exception {
457455
AttestationTokenRetriever attestationTokenRetriever = getAttestationTokenRetriever(vertx, attestationUrl, clientApiToken, responseWatcher);
458456
Boolean enforceHttps = this.config.getBoolean("enforce_https", true);
459457
UidCoreClient coreClient = new UidCoreClient(clientApiToken, CloudUtils.defaultProxy, enforceHttps, attestationTokenRetriever);
460458
UidOptOutClient optOutClient = new UidOptOutClient(clientApiToken, CloudUtils.defaultProxy, enforceHttps, attestationTokenRetriever);
461459
return new AbstractMap.SimpleEntry<>(coreClient, optOutClient);
462460
}
463461

464-
private AttestationTokenRetriever getAttestationTokenRetriever(Vertx vertx, String attestationUrl, String clientApiToken, Handler<Integer> responseWatcher) throws Exception {
462+
private AttestationTokenRetriever getAttestationTokenRetriever(Vertx vertx, String attestationUrl, String clientApiToken, Handler<Pair<Integer, String>> responseWatcher) throws Exception {
465463
String enclavePlatform = this.config.getString("enclave_platform");
466464
if (Strings.isNullOrEmpty(enclavePlatform)) {
467465
return new AttestationTokenRetriever(vertx, attestationUrl, clientApiToken, this.appVersion, new NoAttestationProvider(), responseWatcher, CloudUtils.defaultProxy);

src/main/java/com/uid2/operator/vertx/OperatorDisableHandler.java

Lines changed: 0 additions & 54 deletions
This file was deleted.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.uid2.operator.vertx;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
import software.amazon.awssdk.utils.Pair;
6+
7+
import java.time.Clock;
8+
import java.time.Duration;
9+
import java.time.Instant;
10+
import java.util.concurrent.atomic.AtomicReference;
11+
12+
public class OperatorShutdownHandler {
13+
private static final Logger LOGGER = LoggerFactory.getLogger(OperatorShutdownHandler.class);
14+
private final Duration shutdownWaitTime;
15+
private final AtomicReference<Instant> failureStartTime = new AtomicReference<>(null);
16+
private final Clock clock;
17+
18+
public OperatorShutdownHandler(Duration shutdownWaitTime, Clock clock) {
19+
this.shutdownWaitTime = shutdownWaitTime;
20+
this.clock = clock;
21+
}
22+
23+
24+
public void handleResponse(Pair<Integer, String> response) {
25+
if (response.left() == 401) {
26+
LOGGER.error("core attestation failed with 401, shutting down operator, core response: " + response.right());
27+
System.exit(1);
28+
}
29+
if (response.left() == 200) {
30+
failureStartTime.set(null);
31+
} else {
32+
Instant t = failureStartTime.get();
33+
if (t == null) {
34+
failureStartTime.set(clock.instant());
35+
} else if (Duration.between(t, clock.instant()).compareTo(this.shutdownWaitTime) > 0) {
36+
LOGGER.error("core attestation has been in failed state for too long. shutting down operator");
37+
System.exit(1);
38+
}
39+
}
40+
}
41+
}

src/main/java/com/uid2/operator/vertx/UIDOperatorVerticle.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ public class UIDOperatorVerticle extends AbstractVerticle {
9898
private final Map<String, Counter> _identityMapRequestWithUnmapped = new HashMap<>();
9999
private final IdentityScope identityScope;
100100
private final V2PayloadHandler v2PayloadHandler;
101-
private Handler<RoutingContext> disableHandler = null;
102101
private final boolean phoneSupport;
103102
private final int tcfVendorId;
104103
private final IStatsCollectorQueue _statsCollectorQueue;
@@ -178,17 +177,9 @@ public void start(Promise<Void> startPromise) throws Exception {
178177

179178
}
180179

181-
public void setDisableHandler(Handler<RoutingContext> h) {
182-
this.disableHandler = h;
183-
}
184-
185180
private Router createRoutesSetup() throws IOException {
186181
final Router router = Router.router(vertx);
187182

188-
if (this.disableHandler != null) {
189-
router.route().handler(this.disableHandler);
190-
}
191-
192183
router.allowForward(AllowForwardHeaders.X_FORWARD);
193184
router.route().handler(new RequestCapturingHandler());
194185
router.route().handler(new ClientVersionCapturingHandler("static/js", "*.js"));
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package com.uid2.operator;
2+
3+
import ch.qos.logback.classic.Logger;
4+
import ch.qos.logback.classic.spi.ILoggingEvent;
5+
import ch.qos.logback.core.read.ListAppender;
6+
import com.uid2.operator.vertx.OperatorShutdownHandler;
7+
import io.vertx.core.Vertx;
8+
import io.vertx.junit5.VertxExtension;
9+
import io.vertx.junit5.VertxTestContext;
10+
import org.junit.jupiter.api.AfterEach;
11+
import org.junit.jupiter.api.Assertions;
12+
import org.junit.jupiter.api.BeforeEach;
13+
import org.junit.jupiter.api.Test;
14+
import org.junit.jupiter.api.extension.ExtendWith;
15+
import org.mockito.Mock;
16+
import org.mockito.MockitoAnnotations;
17+
import org.slf4j.LoggerFactory;
18+
import software.amazon.awssdk.utils.Pair;
19+
20+
import java.security.Permission;
21+
import java.time.Clock;
22+
import java.time.Duration;
23+
import java.time.Instant;
24+
import java.time.temporal.ChronoUnit;
25+
26+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
27+
import static org.mockito.Mockito.when;
28+
29+
@ExtendWith(VertxExtension.class)
30+
public class OperatorShutdownHandlerTest {
31+
32+
private AutoCloseable mocks;
33+
@Mock private Clock clock;
34+
private OperatorShutdownHandler operatorShutdownHandler;
35+
36+
class NoExitSecurityManager extends SecurityManager {
37+
@Override
38+
public void checkPermission(Permission perm) { }
39+
40+
@Override
41+
public void checkExit(int status) {
42+
super.checkExit(status);
43+
throw new RuntimeException(String.valueOf(status));
44+
}
45+
}
46+
47+
@BeforeEach
48+
void beforeEach() {
49+
mocks = MockitoAnnotations.openMocks(this);
50+
when(clock.instant()).thenAnswer(i -> Instant.now());
51+
this.operatorShutdownHandler = new OperatorShutdownHandler(Duration.ofHours(12), clock);
52+
}
53+
54+
@AfterEach
55+
void afterEach() throws Exception {
56+
mocks.close();
57+
}
58+
59+
@Test
60+
void shutdownOn401(Vertx vertx, VertxTestContext testContext) {
61+
SecurityManager origSecurityManager = System.getSecurityManager();
62+
try {
63+
System.setSecurityManager(new NoExitSecurityManager());
64+
65+
ListAppender<ILoggingEvent> logWatcher = new ListAppender<>();
66+
logWatcher.start();
67+
((Logger) LoggerFactory.getLogger(OperatorShutdownHandler.class)).addAppender(logWatcher);
68+
69+
// Revoke auth
70+
try {
71+
this.operatorShutdownHandler.handleResponse(Pair.of(401, "Unauthorized"));
72+
} catch (RuntimeException e) {
73+
Assertions.assertTrue(logWatcher.list.get(0).getFormattedMessage().contains("core attestation failed with 401, shutting down operator, core response: "));
74+
testContext.completeNow();
75+
}
76+
} finally {
77+
System.setSecurityManager(origSecurityManager);
78+
}
79+
}
80+
81+
@Test
82+
void shutdownOnFailedTooLong(Vertx vertx, VertxTestContext testContext) {
83+
SecurityManager origSecurityManager = System.getSecurityManager();
84+
try {
85+
System.setSecurityManager(new NoExitSecurityManager());
86+
87+
ListAppender<ILoggingEvent> logWatcher = new ListAppender<>();
88+
logWatcher.start();
89+
((Logger) LoggerFactory.getLogger(OperatorShutdownHandler.class)).addAppender(logWatcher);
90+
91+
this.operatorShutdownHandler.handleResponse(Pair.of(500, ""));
92+
93+
when(clock.instant()).thenAnswer(i -> Instant.now().plus(12, ChronoUnit.HOURS).plusSeconds(60));
94+
try {
95+
this.operatorShutdownHandler.handleResponse(Pair.of(500, ""));
96+
} catch (RuntimeException e) {
97+
Assertions.assertTrue(logWatcher.list.get(0).getFormattedMessage().contains("core attestation has been in failed state for too long. shutting down operator"));
98+
testContext.completeNow();
99+
}
100+
} finally {
101+
System.setSecurityManager(origSecurityManager);
102+
}
103+
}
104+
105+
@Test
106+
void attestRecoverOnSuccess(Vertx vertx, VertxTestContext testContext) {
107+
SecurityManager origSecurityManager = System.getSecurityManager();
108+
try {
109+
System.setSecurityManager(new NoExitSecurityManager());
110+
111+
ListAppender<ILoggingEvent> logWatcher = new ListAppender<>();
112+
logWatcher.start();
113+
((Logger) LoggerFactory.getLogger(OperatorShutdownHandler.class)).addAppender(logWatcher);
114+
115+
this.operatorShutdownHandler.handleResponse(Pair.of(500, ""));
116+
when(clock.instant()).thenAnswer(i -> Instant.now().plus(6, ChronoUnit.HOURS));
117+
this.operatorShutdownHandler.handleResponse(Pair.of(200, ""));
118+
119+
when(clock.instant()).thenAnswer(i -> Instant.now().plus(12, ChronoUnit.HOURS));
120+
assertDoesNotThrow(() -> {
121+
this.operatorShutdownHandler.handleResponse(Pair.of(500, ""));
122+
});
123+
testContext.completeNow();
124+
} finally {
125+
System.setSecurityManager(origSecurityManager);
126+
}
127+
}
128+
}

0 commit comments

Comments
 (0)