Skip to content

Commit 2e890c0

Browse files
authored
feat: configure the SMS with a list of Azure attestation servers (#306)
1 parent 4626a50 commit 2e890c0

File tree

6 files changed

+257
-64
lines changed

6 files changed

+257
-64
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2025 IEXEC BLOCKCHAIN TECH
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.iexec.sms.tee.session.scone;
18+
19+
import feign.RequestLine;
20+
21+
public interface AzureAttestationServer {
22+
@RequestLine("GET .well-known/openid-configuration")
23+
String canFetchOpenIdMetadata();
24+
}

src/main/java/com/iexec/sms/tee/session/scone/SconeSessionMakerService.java

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.iexec.sms.tee.session.scone;
1818

19+
import com.iexec.common.utils.FeignBuilder;
1920
import com.iexec.commons.poco.tee.TeeFramework;
2021
import com.iexec.sms.tee.ConditionalOnTeeFramework;
2122
import com.iexec.sms.tee.session.base.SecretEnclaveBase;
@@ -30,24 +31,34 @@
3031
import com.iexec.sms.tee.session.scone.cas.SconeSession.Image.Volume;
3132
import com.iexec.sms.tee.session.scone.cas.SconeSession.Security;
3233
import com.iexec.sms.tee.session.scone.cas.SconeSession.Volumes;
34+
import feign.Logger;
3335
import lombok.NonNull;
36+
import lombok.extern.slf4j.Slf4j;
3437
import org.springframework.stereotype.Service;
3538

39+
import java.net.URL;
3640
import java.util.*;
41+
import java.util.stream.Collectors;
3742

3843
//TODO Rename and move
44+
@Slf4j
3945
@Service
4046
@ConditionalOnTeeFramework(frameworks = TeeFramework.SCONE)
4147
public class SconeSessionMakerService {
4248

4349
private final SecretSessionBaseService secretSessionBaseService;
4450
private final SconeSessionSecurityConfig attestationSecurityConfig;
51+
private final Map<URL, AzureAttestationServer> azureAttestationServersMap;
4552

46-
public SconeSessionMakerService(
47-
SecretSessionBaseService secretSessionBaseService,
48-
SconeSessionSecurityConfig attestationSecurityConfig) {
53+
public SconeSessionMakerService(final SecretSessionBaseService secretSessionBaseService,
54+
final SconeSessionSecurityConfig attestationSecurityConfig) {
4955
this.secretSessionBaseService = secretSessionBaseService;
5056
this.attestationSecurityConfig = attestationSecurityConfig;
57+
azureAttestationServersMap = attestationSecurityConfig.getUrls().stream()
58+
.collect(Collectors.toMap(
59+
url -> url,
60+
url -> FeignBuilder.createBuilder(Logger.Level.BASIC).target(AzureAttestationServer.class, url.toString())
61+
));
5162
}
5263

5364
/**
@@ -62,21 +73,19 @@ public SconeSessionMakerService(
6273
* @return session config in yaml string format
6374
*/
6475
@NonNull
65-
public SconeSession generateSession(TeeSessionRequest request)
66-
throws TeeSessionGenerationException {
67-
Volume iexecInVolume = new Volume("iexec_in", "/iexec_in");
68-
Volume iexecOutVolume = new Volume("iexec_out", "/iexec_out");
69-
Volume postComputeTmpVolume = new Volume("post-compute-tmp",
70-
"/post-compute-tmp");
71-
List<SconeEnclave> services = new ArrayList<>();
72-
List<Image> images = new ArrayList<>();
76+
public SconeSession generateSession(final TeeSessionRequest request) throws TeeSessionGenerationException {
77+
final Volume iexecInVolume = new Volume("iexec_in", "/iexec_in");
78+
final Volume iexecOutVolume = new Volume("iexec_out", "/iexec_out");
79+
final Volume postComputeTmpVolume = new Volume("post-compute-tmp", "/post-compute-tmp");
80+
final List<SconeEnclave> services = new ArrayList<>();
81+
final List<Image> images = new ArrayList<>();
7382

74-
SecretSessionBase baseSession = secretSessionBaseService
83+
final SecretSessionBase baseSession = secretSessionBaseService
7584
.getSecretsTokens(request);
7685

7786
// pre (optional)
7887
if (baseSession.getPreCompute() != null) {
79-
SconeEnclave sconePreEnclave = toSconeEnclave(
88+
final SconeEnclave sconePreEnclave = toSconeEnclave(
8089
baseSession.getPreCompute(),
8190
request.getTeeServicesProperties().getPreComputeProperties().getEntrypoint(),
8291
true);
@@ -86,7 +95,7 @@ public SconeSession generateSession(TeeSessionRequest request)
8695
List.of(iexecInVolume)));
8796
}
8897
// app
89-
SconeEnclave sconeAppEnclave = toSconeEnclave(
98+
final SconeEnclave sconeAppEnclave = toSconeEnclave(
9099
baseSession.getAppCompute(),
91100
request.getTaskDescription().getAppCommand(),
92101
false);
@@ -95,7 +104,7 @@ public SconeSession generateSession(TeeSessionRequest request)
95104
sconeAppEnclave.getImageName(),
96105
List.of(iexecInVolume, iexecOutVolume)));
97106
// post
98-
SconeEnclave sconePostEnclave = toSconeEnclave(
107+
final SconeEnclave sconePostEnclave = toSconeEnclave(
99108
baseSession.getPostCompute(),
100109
request.getTeeServicesProperties().getPostComputeProperties().getEntrypoint(),
101110
true);
@@ -104,6 +113,8 @@ public SconeSession generateSession(TeeSessionRequest request)
104113
sconePostEnclave.getImageName(),
105114
List.of(iexecOutVolume, postComputeTmpVolume)));
106115

116+
final URL validAttestationServer = resolveValidAttestationServer();
117+
107118
return SconeSession.builder()
108119
.name(request.getSessionId())
109120
.version("0.3.10")
@@ -118,13 +129,31 @@ public SconeSession generateSession(TeeSessionRequest request)
118129
attestationSecurityConfig.getToleratedInsecureOptions(),
119130
attestationSecurityConfig.getIgnoredSgxAdvisories(),
120131
attestationSecurityConfig.getMode(),
121-
attestationSecurityConfig.getUrl()
132+
validAttestationServer
122133
)
123134
)
124135
.build();
125136
}
126137

127-
private SconeEnclave toSconeEnclave(SecretEnclaveBase enclaveBase, String command, boolean addJavaEnvVars) {
138+
private URL resolveValidAttestationServer() {
139+
// The keys of the Map are shuffled to avoid always querying servers in the same order
140+
final List<URL> urls = new ArrayList<>(azureAttestationServersMap.keySet());
141+
Collections.shuffle(urls);
142+
for (final URL attestationServerUrl : urls) {
143+
try {
144+
azureAttestationServersMap.get(attestationServerUrl).canFetchOpenIdMetadata();
145+
log.debug("Resolved attestation server [url:{}]", attestationServerUrl);
146+
return attestationServerUrl;
147+
} catch (Exception e) {
148+
log.error("Failed to check Azure attestation server liveness [url:{}]", attestationServerUrl, e);
149+
}
150+
}
151+
return null;
152+
}
153+
154+
private SconeEnclave toSconeEnclave(final SecretEnclaveBase enclaveBase,
155+
final String command,
156+
final boolean addJavaEnvVars) {
128157
final HashMap<String, Object> enclaveEnvironment = new HashMap<>(enclaveBase.getEnvironment());
129158
if (addJavaEnvVars) {
130159
enclaveEnvironment.putAll(

src/main/java/com/iexec/sms/tee/session/scone/SconeSessionSecurityConfig.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@
1919
import com.iexec.commons.poco.tee.TeeFramework;
2020
import com.iexec.sms.tee.ConditionalOnTeeFramework;
2121
import jakarta.validation.constraints.NotBlank;
22+
import jakarta.validation.constraints.NotNull;
2223
import lombok.Value;
2324
import org.springframework.boot.context.properties.ConfigurationProperties;
25+
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
26+
import org.springframework.validation.annotation.Validated;
2427

2528
import java.net.URL;
2629
import java.util.List;
2730

2831
@Value
32+
@Validated
2933
@ConfigurationProperties(prefix = "tee.scone.attestation")
3034
@ConditionalOnTeeFramework(frameworks = TeeFramework.SCONE)
3135
public class SconeSessionSecurityConfig {
@@ -34,14 +38,35 @@ public class SconeSessionSecurityConfig {
3438
@NotBlank
3539
String mode;
3640
URL url;
41+
List<@NotNull URL> urls;
3742

38-
public SconeSessionSecurityConfig(List<String> toleratedInsecureOptions, List<String> ignoredSgxAdvisories, String mode, URL url) {
43+
public SconeSessionSecurityConfig(final List<String> toleratedInsecureOptions,
44+
final List<String> ignoredSgxAdvisories,
45+
final String mode,
46+
final URL url,
47+
final List<URL> urls) {
3948
this.toleratedInsecureOptions = toleratedInsecureOptions;
4049
this.ignoredSgxAdvisories = ignoredSgxAdvisories;
4150
this.mode = mode;
4251
this.url = url;
43-
if ("maa".equals(this.mode) && this.url == null) {
52+
if (urls != null && !urls.isEmpty()) {
53+
this.urls = urls;
54+
} else if (url != null) {
55+
this.urls = List.of(url);
56+
} else {
57+
this.urls = List.of();
58+
}
59+
if ("maa".equals(this.mode) && this.urls.isEmpty()) {
4460
throw new IllegalArgumentException("Attestation URL can not be null when scone session mode is 'maa'");
4561
}
4662
}
63+
64+
/**
65+
* @deprecated use {@code tee.scone.attestation.urls} configuration property instead
66+
*/
67+
@Deprecated(forRemoval = true)
68+
@DeprecatedConfigurationProperty(replacement = "tee.scone.attestation.urls", reason = "replaced with a list")
69+
public URL getUrl() {
70+
return this.url;
71+
}
4772
}

src/main/resources/application-scone.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ tee:
3535
ignored-sgx-advisories: ${IEXEC_IGNORED_SGX_ADVISORIES:} # e.g.: INTEL-SA-00220,INTEL-SA-00270
3636
mode: maa
3737
url: https://sharedweu.weu.attest.azure.net
38+
urls: [ https://sharedweu.weu.attest.azure.net ]

0 commit comments

Comments
 (0)