Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: use proxy admin port via rpc #2661

Merged
merged 4 commits into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ public final class PortNumbers {
/** Port for connection between client and server proxy. */
public static final int PROXY_PORT = 5500;

/** Admin port for proxy. */
public static final int ADMIN_PORT = 5566;

/**
* Signer grpc service port.
*/
Expand All @@ -52,7 +49,4 @@ public final class PortNumbers {
/** Port for Distributed Files Client. */
public static final int CONFIGURATION_CLIENT_PORT = 5665;

/** Port of the operational monitoring daemon. */
public static final int OP_MONITOR_DAEMON_PORT = 2080;

}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public AdminPort(int portNumber) {
}

@PostConstruct
public void afterPropertiesSet() throws Exception {
public void init() throws Exception {
LOG.info("Started AdminPort on port {}", portNumber);

server.start();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@
import io.grpc.util.AdvancedTlsX509KeyManager;
import io.grpc.util.AdvancedTlsX509TrustManager;
import io.grpc.util.CertificateUtils;
import io.quarkus.arc.profile.UnlessBuildProfile;
import io.quarkus.scheduler.Scheduled;
import io.quarkus.scheduler.ScheduledExecution;
import io.quarkus.scheduler.Scheduler;
import io.quarkus.vault.VaultPKISecretEngine;
import io.quarkus.vault.VaultPKISecretEngineFactory;
import io.quarkus.vault.pki.DataFormat;
import io.quarkus.vault.pki.GenerateCertificateOptions;
import io.quarkus.vault.pki.PrivateKeyEncoding;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import lombok.extern.slf4j.Slf4j;
import org.niis.xroad.common.properties.CommonRpcProperties;
import org.niis.xroad.common.rpc.VaultKeyProvider;
Expand All @@ -50,18 +50,19 @@
import java.security.cert.X509Certificate;

@Slf4j
@ApplicationScoped
@UnlessBuildProfile("test")
public class QuarkusReloadableVaultKeyManager implements VaultKeyProvider {
private final CommonRpcProperties rpcProperties;
private final AdvancedTlsX509KeyManager keyManager = new AdvancedTlsX509KeyManager();

private final AdvancedTlsX509TrustManager trustManager;
private final VaultPKISecretEngine pkiSecretEngine;
private final Scheduler scheduler;

public QuarkusReloadableVaultKeyManager(CommonRpcProperties rpcProperties,
VaultPKISecretEngineFactory pkiSecretEngineFactory) throws CertificateException {
VaultPKISecretEngineFactory pkiSecretEngineFactory,
Scheduler scheduler) throws CertificateException {
this.rpcProperties = rpcProperties;
this.scheduler = scheduler;
this.pkiSecretEngine = pkiSecretEngineFactory.engine(rpcProperties.certificateProvisioning().secretStorePkiPath());

this.trustManager = AdvancedTlsX509TrustManager.newBuilder()
Expand All @@ -70,8 +71,14 @@ public QuarkusReloadableVaultKeyManager(CommonRpcProperties rpcProperties,
}

@PostConstruct
public void afterPropertiesSet() throws Exception {
reload();
public void init() throws Exception {
log.info("Scheduling certificate reload job");
scheduler.newJob(getClass().getSimpleName())
.setInterval("%sm".formatted(rpcProperties.certificateProvisioning().refreshIntervalMinutes()))
.setDelayed("0s")
.setTask(this::reload)
.setConcurrentExecution(Scheduled.ConcurrentExecution.SKIP)
.schedule();
}

@Override
Expand All @@ -84,30 +91,33 @@ public TrustManager getTrustManager() {
return trustManager;
}

@Scheduled(every = "${xroad.common.rpc.certificate-provisioning.refresh-interval-minutes}m")
public void reload() throws Exception {
var request = buildVaultCertificateRequest();
if (log.isDebugEnabled()) {
log.debug("Requesting new certificate from Vault secret-store [{}] with request cn: {}, altNames: {}, ipSubjectAltNames: {}",
rpcProperties.certificateProvisioning().secretStorePkiPath(), "CN", "altNames", "ipSubjectAltNames");
}

var vaultResponse = pkiSecretEngine.generateCertificate(rpcProperties.certificateProvisioning().issuanceRoleName(), request);


if (vaultResponse != null) {
log.info("Received new certificate from Vault. [{}]", vaultResponse);
var cert = vaultResponse.certificate.getCertificate();
if (vaultResponse.privateKey.getData() instanceof String data) {
var privateKey = CertificateUtils.getPrivateKey(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)));
var certTrustChain = vaultResponse.issuingCA.getCertificate();
public void reload(ScheduledExecution execution) {
try {
var request = buildVaultCertificateRequest();
if (log.isDebugEnabled()) {
log.debug("Requesting new certificate from Vault secret-store [{}] with request cn: {}, "
+ "altNames: {}, ipSubjectAltNames: {}",
rpcProperties.certificateProvisioning().secretStorePkiPath(), "CN", "altNames", "ipSubjectAltNames");
}

log.info("Received new certificate from Vault.");
keyManager.updateIdentityCredentials(new X509Certificate[]{cert}, privateKey);
trustManager.updateTrustCredentials(new X509Certificate[]{certTrustChain});
var vaultResponse = pkiSecretEngine.generateCertificate(rpcProperties.certificateProvisioning().issuanceRoleName(), request);

if (vaultResponse != null) {
log.info("Received new certificate from Vault. [{}]", vaultResponse);
var cert = vaultResponse.certificate.getCertificate();
if (vaultResponse.privateKey.getData() instanceof String data) {
var privateKey = CertificateUtils.getPrivateKey(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)));
var certTrustChain = vaultResponse.issuingCA.getCertificate();

log.info("Received new certificate from Vault.");
keyManager.updateIdentityCredentials(new X509Certificate[]{cert}, privateKey);
trustManager.updateTrustCredentials(new X509Certificate[]{certTrustChain});
}
} else {
log.error("Failed to get certificate from Vault. Data is null.");
}
} else {
log.error("Failed to get certificate from Vault. Data is null.");
} catch (Exception e) {
log.error("Failed to reload certificate from Vault", e);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,29 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.niis.xroad.proxy.proto;

import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithDefault;
import io.smallrye.config.WithName;
package org.niis.xroad.common.rpc.quarkus;

@ConfigMapping(prefix = "xroad.common.rpc.channel.proxy")
public interface QuarkusProxyRpcChannelProperties extends ProxyRpcChannelProperties {
import io.quarkus.scheduler.Scheduler;
import io.quarkus.vault.VaultPKISecretEngineFactory;
import jakarta.enterprise.context.ApplicationScoped;
import org.niis.xroad.common.properties.CommonRpcProperties;
import org.niis.xroad.common.rpc.NoopVaultKeyProvider;
import org.niis.xroad.common.rpc.VaultKeyProvider;

@Override
@WithDefault("127.0.0.1")
String host();

@Override
@WithDefault("5567")
int port();

@Override
@WithName("deadline-after")
@WithDefault("60000")
int deadlineAfter();
public class QuarkusRpcConfig {

@ApplicationScoped
VaultKeyProvider vaultKeyProvider(CommonRpcProperties rpcProperties, VaultPKISecretEngineFactory pkiSecretEngineFactory,
Scheduler scheduler)
throws Exception {
if (rpcProperties.useTls()) {
QuarkusReloadableVaultKeyManager manager = new QuarkusReloadableVaultKeyManager(rpcProperties,
pkiSecretEngineFactory, scheduler);
manager.init();
return manager;
} else {
return new NoopVaultKeyProvider();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ dependencies {
implementation(project(":service:monitor:monitor-api"))
implementation(project(":service:configuration-client:configuration-client-rpc-client"))
implementation(project(":service:monitor:monitor-rpc-client"))
implementation(project(":service:proxy:proxy-rpc-client"))
implementation(project(":common:common-rpc-spring"))
implementation(project(":common:common-properties-spring"))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
import org.niis.xroad.confclient.rpc.ConfClientRpcClient;
import org.niis.xroad.monitor.rpc.EnvMonitorRpcChannelProperties;
import org.niis.xroad.monitor.rpc.MonitorRpcClient;
import org.niis.xroad.proxy.proto.ProxyRpcChannelProperties;
import org.niis.xroad.proxy.proto.ProxyRpcClient;
import org.niis.xroad.signer.client.spring.SpringSignerClientConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
Expand All @@ -48,7 +50,8 @@
SpringSignerClientConfiguration.class})
@EnableConfigurationProperties({
RpcClientsConfig.SpringEnvMonitorRpcChannelProperties.class,
RpcClientsConfig.SpringConfClientRpcChannelProperties.class})
RpcClientsConfig.SpringConfClientRpcChannelProperties.class,
RpcClientsConfig.SpringProxyRpcChannelProperties.class})
class RpcClientsConfig {

@Bean
Expand Down Expand Up @@ -86,7 +89,7 @@ ConfClientRpcClient confClientRpcClient(RpcChannelFactory rpcChannelFactory, Spr
}

@Setter
@ConfigurationProperties(prefix = "xroad.common.rpc.channel.configuration-client")
@ConfigurationProperties(prefix = ConfClientRpcChannelProperties.PREFIX)
public static class SpringConfClientRpcChannelProperties implements ConfClientRpcChannelProperties {
private String host = DEFAULT_HOST;
private int port = Integer.parseInt(DEFAULT_PORT);
Expand All @@ -107,4 +110,32 @@ public int deadlineAfter() {
return deadlineAfter;
}
}

@Bean
ProxyRpcClient proxyRpcClient(RpcChannelFactory rpcChannelFactory, SpringProxyRpcChannelProperties proxyRpcChannelProperties) {
return new ProxyRpcClient(rpcChannelFactory, proxyRpcChannelProperties);
}

@Setter
@ConfigurationProperties(prefix = ProxyRpcChannelProperties.PREFIX)
static class SpringProxyRpcChannelProperties implements ProxyRpcChannelProperties {
private String host = DEFAULT_HOST;
private int port = Integer.parseInt(DEFAULT_PORT);
private int deadlineAfter = Integer.parseInt(DEFAULT_DEADLINE_AFTER);

@Override
public String host() {
return host;
}

@Override
public int port() {
return port;
}

@Override
public int deadlineAfter() {
return deadlineAfter;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,62 +25,34 @@
*/
package org.niis.xroad.securityserver.restapi.service;

import ee.ria.xroad.common.PortNumbers;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.niis.xroad.proxy.proto.ProxyRpcClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.time.Duration;

/**
* Service for clearing proxy cache.
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class ClearCacheService {
private static final int REST_TEMPLATE_TIMEOUT_MS = 5000;

private final RestTemplate restTemplate;
private final String clearConfCacheUrl;

@Autowired
public ClearCacheService(@Value("${url.clear-configuration-cache}") String clearConfCacheUrl,
RestTemplateBuilder restTemplateBuilder) {
this.clearConfCacheUrl = String.format(clearConfCacheUrl, PortNumbers.ADMIN_PORT);
this.restTemplate = restTemplateBuilder
.setReadTimeout(Duration.ofMillis(REST_TEMPLATE_TIMEOUT_MS))
.build();
}

// used for testing
ClearCacheService() {
clearConfCacheUrl = null;
restTemplate = null;
}
private final ProxyRpcClient proxyRpcClient;

/**
* Sends an http request to proxy in order to trigger the clearing (invalidating) of cache.
* Sends rpc request to proxy in order to trigger the clearing (invalidating) of cache.
* @return true if clearing succeeded, false otherwise (error is logged)
*/
public boolean executeClearConfigurationCache() {
log.info("Starting to clear configuration cache {}", clearConfCacheUrl);
ResponseEntity<String> response = null;
log.info("Starting to clear configuration cache");
try {
response = restTemplate.getForEntity(clearConfCacheUrl, String.class);
proxyRpcClient.clearConfCache();
log.info("Configuration cache cleared");
return true;
} catch (Exception e) {
log.error("Clearing cache failed", e);
return false;
}
if (response != null && response.getStatusCode() != HttpStatus.OK) {
log.error("Clearing cache failed, HttpStatus={}", response.getStatusCode().value());
return false;
}
return true;
}
}
Loading
Loading