aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/WireguardKeyWithTimestamp.java39
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/NodeSpec.java40
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepository.java65
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/GetWireguardResponse.java28
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/NodeRepositoryNode.java20
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/wireguard/WireguardPeer.java6
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepositoryTest.java16
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/wireguard/WireguardPeerTest.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java106
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDb.java2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java17
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java14
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java15
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/WireguardResponse.java19
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java9
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/cfg1.json6
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node2.json4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node4-wg.json4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/wireguard.json6
19 files changed, 246 insertions, 174 deletions
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/WireguardKeyWithTimestamp.java b/config-provisioning/src/main/java/com/yahoo/config/provision/WireguardKeyWithTimestamp.java
new file mode 100644
index 00000000000..ecc1cf71113
--- /dev/null
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/WireguardKeyWithTimestamp.java
@@ -0,0 +1,39 @@
+package com.yahoo.config.provision;
+
+import com.yahoo.jdisc.Timer;
+
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Random;
+
+/**
+ * @author gjoranv
+ */
+public record WireguardKeyWithTimestamp(WireguardKey key, Instant timestamp) {
+
+ public static final int KEY_ROTATION_BASE = 60;
+ public static final int KEY_ROTATION_VARIANCE = 10;
+ public static final int KEY_EXPIRY = KEY_ROTATION_BASE + KEY_ROTATION_VARIANCE + 5;
+
+ public WireguardKeyWithTimestamp {
+ if (key == null) throw new IllegalArgumentException("Wireguard key cannot be null");
+ if (timestamp == null) timestamp = Instant.EPOCH;
+ }
+
+ public static WireguardKeyWithTimestamp from(String key, long msTimestamp) {
+ return new WireguardKeyWithTimestamp(WireguardKey.from(key), Instant.ofEpochMilli(msTimestamp));
+ }
+
+ public boolean isDueForRotation(Timer timer, ChronoUnit unit, Random random) {
+ return timer.currentTime().isAfter(keyRotationDueAt(unit, random));
+ }
+
+ public boolean hasExpired(Timer timer, ChronoUnit unit) {
+ return timer.currentTime().isAfter(timestamp.plus(KEY_EXPIRY, unit));
+ }
+
+ private Instant keyRotationDueAt(ChronoUnit unit, Random random) {
+ return timestamp.plus(KEY_ROTATION_BASE + random.nextInt(KEY_ROTATION_VARIANCE), unit);
+ }
+
+}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/NodeSpec.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/NodeSpec.java
index 0300d7e92ff..d902fb7b3c4 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/NodeSpec.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/NodeSpec.java
@@ -9,6 +9,7 @@ import com.yahoo.config.provision.DockerImage;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.WireguardKey;
+import com.yahoo.config.provision.WireguardKeyWithTimestamp;
import com.yahoo.vespa.hosted.node.admin.task.util.file.DiskSize;
import java.net.URI;
@@ -73,9 +74,7 @@ public class NodeSpec {
private final List<TrustStoreItem> trustStore;
- private final Optional<WireguardKey> wireguardPubkey;
-
- private final Optional<Instant> wireguardKeyTimestamp;
+ private final Optional<WireguardKeyWithTimestamp> wireguardKeyWithTimestamp;
private final boolean wantToRebuild;
@@ -112,8 +111,7 @@ public class NodeSpec {
Optional<URI> archiveUri,
Optional<ApplicationId> exclusiveTo,
List<TrustStoreItem> trustStore,
- Optional<WireguardKey> wireguardPubkey,
- Optional<Instant> wireguardKeyTimestamp,
+ Optional<WireguardKeyWithTimestamp> wireguardPubkey,
boolean wantToRebuild) {
if (state == NodeState.active) {
@@ -157,8 +155,7 @@ public class NodeSpec {
this.archiveUri = Objects.requireNonNull(archiveUri);
this.exclusiveTo = Objects.requireNonNull(exclusiveTo);
this.trustStore = Objects.requireNonNull(trustStore);
- this.wireguardPubkey = Objects.requireNonNull(wireguardPubkey);
- this.wireguardKeyTimestamp = Objects.requireNonNull(wireguardKeyTimestamp);
+ this.wireguardKeyWithTimestamp = Objects.requireNonNull(wireguardPubkey);
this.wantToRebuild = wantToRebuild;
}
@@ -313,9 +310,7 @@ public class NodeSpec {
return trustStore;
}
- public Optional<WireguardKey> wireguardPubkey() { return wireguardPubkey; }
-
- public Optional<Instant> wireguardKeyTimestamp() { return wireguardKeyTimestamp; }
+ public Optional<WireguardKeyWithTimestamp> wireguardKeyWithTimestamp() { return wireguardKeyWithTimestamp; }
public boolean wantToRebuild() {
return wantToRebuild;
@@ -358,8 +353,7 @@ public class NodeSpec {
Objects.equals(archiveUri, that.archiveUri) &&
Objects.equals(exclusiveTo, that.exclusiveTo) &&
Objects.equals(trustStore, that.trustStore) &&
- Objects.equals(wireguardPubkey, that.wireguardPubkey) &&
- Objects.equals(wireguardKeyTimestamp, that.wireguardKeyTimestamp) &&
+ Objects.equals(wireguardKeyWithTimestamp, that.wireguardKeyWithTimestamp) &&
Objects.equals(wantToRebuild, that.wantToRebuild);
}
@@ -398,8 +392,7 @@ public class NodeSpec {
archiveUri,
exclusiveTo,
trustStore,
- wireguardPubkey,
- wireguardKeyTimestamp,
+ wireguardKeyWithTimestamp,
wantToRebuild);
}
@@ -438,8 +431,7 @@ public class NodeSpec {
+ " archiveUri=" + archiveUri
+ " exclusiveTo=" + exclusiveTo
+ " trustStore=" + trustStore
- + " wireguardPubkey=" + wireguardPubkey
- + " wireguardKeyTimestamp=" + wireguardKeyTimestamp
+ + " wireguardPubkey=" + wireguardKeyWithTimestamp
+ " wantToRebuild=" + wantToRebuild
+ " }";
}
@@ -477,8 +469,7 @@ public class NodeSpec {
private Optional<URI> archiveUri = Optional.empty();
private Optional<ApplicationId> exclusiveTo = Optional.empty();
private List<TrustStoreItem> trustStore = List.of();
- private Optional<WireguardKey> wireguardPubkey = Optional.empty();
- private Optional<Instant> wireguardKeyTimestamp = Optional.empty();
+ private Optional<WireguardKeyWithTimestamp> wireguardPubkey = Optional.empty();
private boolean wantToRebuild = false;
public Builder() {}
@@ -514,8 +505,7 @@ public class NodeSpec {
node.archiveUri.ifPresent(this::archiveUri);
node.exclusiveTo.ifPresent(this::exclusiveTo);
trustStore(node.trustStore);
- node.wireguardPubkey.ifPresent(this::wireguardPubkey);
- node.wireguardKeyTimestamp.ifPresent(this::wireguardKeyTimestamp);
+ node.wireguardKeyWithTimestamp.ifPresent(this::wireguardKeyWithTimestamp);
wantToRebuild(node.wantToRebuild);
}
@@ -704,13 +694,13 @@ public class NodeSpec {
return this;
}
- public Builder wireguardPubkey(WireguardKey wireguardPubKey) {
- this.wireguardPubkey = Optional.of(wireguardPubKey);
+ public Builder wireguardPubkey(WireguardKey wireguardPubkey) {
+ this.wireguardPubkey = Optional.of(new WireguardKeyWithTimestamp(wireguardPubkey, Instant.EPOCH));
return this;
}
- public Builder wireguardKeyTimestamp(Instant wireguardKeyTimestamp) {
- this.wireguardKeyTimestamp = Optional.of(wireguardKeyTimestamp);
+ public Builder wireguardKeyWithTimestamp(WireguardKeyWithTimestamp wireguardPubKey) {
+ this.wireguardPubkey = Optional.of(wireguardPubKey);
return this;
}
@@ -846,7 +836,7 @@ public class NodeSpec {
wantedFirmwareCheck, currentFirmwareCheck, modelName,
resources, realResources, ipAddresses, additionalIpAddresses,
reports, events, parentHostname, archiveUri, exclusiveTo, trustStore,
- wireguardPubkey, wireguardKeyTimestamp, wantToRebuild);
+ wireguardPubkey, wantToRebuild);
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepository.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepository.java
index a9cc2d698e9..17d3b51398f 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepository.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepository.java
@@ -11,6 +11,7 @@ import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.WireguardKey;
+import com.yahoo.config.provision.WireguardKeyWithTimestamp;
import com.yahoo.config.provision.host.FlavorOverrides;
import com.yahoo.vespa.hosted.node.admin.configserver.ConfigServerApi;
import com.yahoo.vespa.hosted.node.admin.configserver.HttpException;
@@ -139,26 +140,28 @@ public class RealNodeRepository implements NodeRepository {
return response.nodes.stream()
.mapMulti((NodeRepositoryNode node, Consumer<WireguardPeer> consumer) -> {
- if (node.wireguardPubkey == null || node.wireguardPubkey.isEmpty()) return;
- List<VersionedIpAddress> ipAddresses = node.ipAddresses.stream()
- .map(InetAddresses::forString)
- .filter(address -> !address.isLoopbackAddress() && !address.isLinkLocalAddress() && !address.isSiteLocalAddress())
- .map(VersionedIpAddress::from)
- .toList();
- if (ipAddresses.isEmpty()) return;
+ var keyWithTimestamp = createWireguardKeyWithTimestamp(node.wireguardKeyWithTimestamp,
+ node.wireguardPubkey,
+ node.wireguardKeyTimestamp);
+ if (keyWithTimestamp == null) return;
- // Unbox to prevent NPE
- long keyTimestamp = node.wireguardKeyTimestamp == null ? 0L : node.wireguardKeyTimestamp;
+ List<VersionedIpAddress> ipAddresses = getIpAddresses(node);
+ if (ipAddresses.isEmpty()) return;
- consumer.accept(new WireguardPeer(HostName.of(node.hostname),
- ipAddresses,
- WireguardKey.from(node.wireguardPubkey),
- Instant.ofEpochMilli(keyTimestamp)));
+ consumer.accept(new WireguardPeer(HostName.of(node.hostname), ipAddresses, keyWithTimestamp));
})
.sorted()
.toList();
}
+ private static List<VersionedIpAddress> getIpAddresses(NodeRepositoryNode node) {
+ return node.ipAddresses.stream()
+ .map(InetAddresses::forString)
+ .filter(address -> !address.isLoopbackAddress() && !address.isLinkLocalAddress() && !address.isSiteLocalAddress())
+ .map(VersionedIpAddress::from)
+ .toList();
+ }
+
@Override
public List<WireguardPeer> getConfigserverPeers() {
GetWireguardResponse response = configServerApi.get("/nodes/v2/wireguard", GetWireguardResponse.class);
@@ -246,8 +249,9 @@ public class RealNodeRepository implements NodeRepository {
Optional.ofNullable(node.archiveUri).map(URI::create),
Optional.ofNullable(node.exclusiveTo).map(ApplicationId::fromSerializedForm),
trustStore,
- Optional.ofNullable(node.wireguardPubkey).map(WireguardKey::from),
- Optional.ofNullable(node.wireguardKeyTimestamp).map(Instant::ofEpochMilli),
+ Optional.ofNullable(createWireguardKeyWithTimestamp(node.wireguardKeyWithTimestamp,
+ node.wireguardPubkey,
+ node.wireguardKeyTimestamp)),
node.wantToRebuild);
}
@@ -364,20 +368,39 @@ public class RealNodeRepository implements NodeRepository {
node.trustStore = nodeAttributes.getTrustStore().stream()
.map(item -> new NodeRepositoryNode.TrustStoreItem(item.fingerprint(), item.expiry().toEpochMilli()))
.toList();
- node.wireguardPubkey = nodeAttributes.getWireguardPubkey().map(WireguardKey::value).orElse(null);
+ // This is used for patching, and timestamp must only be set on the server side, hence sending EPOCH.
+ node.wireguardKeyWithTimestamp = nodeAttributes.getWireguardPubkey()
+ .map(key -> new NodeRepositoryNode.WireguardKeyWithTimestamp(key.value(), 0L))
+ .orElse(null);
Map<String, JsonNode> reports = nodeAttributes.getReports();
node.reports = reports == null || reports.isEmpty() ? null : new TreeMap<>(reports);
+ // TODO wg: remove when all nodes are using new key+timestamp format
+ node.wireguardPubkey = nodeAttributes.getWireguardPubkey().map(WireguardKey::value).orElse(null);
return node;
}
private static WireguardPeer createConfigserverPeer(GetWireguardResponse.Configserver configServer) {
- // Unbox to prevent NPE
- long keyTimestamp = configServer.wireguardKeyTimestamp == null ? 0L : configServer.wireguardKeyTimestamp;
-
return new WireguardPeer(HostName.of(configServer.hostname),
configServer.ipAddresses.stream().map(VersionedIpAddress::from).toList(),
- WireguardKey.from(configServer.wireguardPubkey),
- Instant.ofEpochMilli(keyTimestamp));
+ createWireguardKeyWithTimestamp(configServer.wireguardKeyWithTimestamp,
+ configServer.wireguardPubkey,
+ configServer.wireguardKeyTimestamp));
+ }
+
+ private static WireguardKeyWithTimestamp createWireguardKeyWithTimestamp(NodeRepositoryNode.WireguardKeyWithTimestamp wirguardJson,
+ String oldKeyJson, Long oldTimestampJson) {
+ if (wirguardJson != null && wirguardJson.key != null && ! wirguardJson.key.isEmpty()) {
+ return new WireguardKeyWithTimestamp(WireguardKey.from(wirguardJson.key),
+ Instant.ofEpochMilli(wirguardJson.timestamp));
+ // TODO wg: remove when all nodes are using new key+timestamp format
+ } else if (oldKeyJson != null) {
+ var timestamp = oldTimestampJson != null ? oldTimestampJson : 0L;
+ return new WireguardKeyWithTimestamp(WireguardKey.from(oldKeyJson),
+ Instant.ofEpochMilli(timestamp));
+ // TODO END
+ } else return null;
+
}
+
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/GetWireguardResponse.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/GetWireguardResponse.java
index dcbf4cc163f..47903795ef7 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/GetWireguardResponse.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/GetWireguardResponse.java
@@ -27,27 +27,23 @@ public class GetWireguardResponse {
public static class Configserver {
@JsonProperty("hostname")
- public final String hostname;
+ public String hostname;
@JsonProperty("ipAddresses")
- public final List<String> ipAddresses;
+ public List<String> ipAddresses;
+
+ @JsonProperty("wireguard")
+ public NodeRepositoryNode.WireguardKeyWithTimestamp wireguardKeyWithTimestamp;
- @JsonProperty("wireguardPubkey")
- public final String wireguardPubkey;
+ // TODO wg: remove when all nodes use new key+timestamp format
+ @JsonProperty("wireguardPubkey")
+ @JsonInclude(JsonInclude.Include.NON_EMPTY)
+ public String wireguardPubkey;
@JsonProperty("wireguardKeyTimestamp")
- public final Long wireguardKeyTimestamp;
-
- @JsonCreator
- public Configserver(@JsonProperty("hostname") String hostname,
- @JsonProperty("ipAddresses") List<String> ipAddresses,
- @JsonProperty("wireguardPubkey") String wireguardPubkey,
- @JsonProperty("wireguardKeyTimestamp") Long wireguardKeyTimestamp) {
- this.hostname = hostname;
- this.ipAddresses = ipAddresses;
- this.wireguardPubkey = wireguardPubkey;
- this.wireguardKeyTimestamp = wireguardKeyTimestamp;
- }
+ @JsonInclude(JsonInclude.Include.NON_EMPTY)
+ public Long wireguardKeyTimestamp;
+
}
}
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/NodeRepositoryNode.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/NodeRepositoryNode.java
index 3d0d052a877..35ca757ebbe 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/NodeRepositoryNode.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/bindings/NodeRepositoryNode.java
@@ -92,6 +92,10 @@ public class NodeRepositoryNode {
@JsonProperty("trustStore")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public List<TrustStoreItem> trustStore;
+ @JsonProperty("wireguard")
+ public WireguardKeyWithTimestamp wireguardKeyWithTimestamp;
+
+ // TODO wg: remove separate key and timestamp when all nodes use new keyWithTimestamp
@JsonProperty("wireguardPubkey")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public String wireguardPubkey;
@@ -141,13 +145,25 @@ public class NodeRepositoryNode {
", exclusiveTo='" + exclusiveTo + '\'' +
", history=" + history +
", trustStore=" + trustStore +
- ", wireguardPubkey=" + wireguardPubkey +
- ", wireguardKeyTimestamp=" + wireguardKeyTimestamp +
+ ", wireguard=" + wireguardKeyTimestamp +
", reports=" + reports +
'}';
}
@JsonIgnoreProperties(ignoreUnknown = true)
+ public static class WireguardKeyWithTimestamp {
+ @JsonProperty("key")
+ public String key;
+ @JsonProperty("timestamp")
+ public long timestamp;
+
+ public WireguardKeyWithTimestamp(@JsonProperty("key") String key, @JsonProperty("timestamp") long timestamp) {
+ this.key = key;
+ this.timestamp = timestamp;
+ }
+ }
+
+ @JsonIgnoreProperties(ignoreUnknown = true)
public static class Owner {
@JsonProperty("tenant")
public String tenant;
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/wireguard/WireguardPeer.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/wireguard/WireguardPeer.java
index b5428f57f08..e5ab9a1ce31 100644
--- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/wireguard/WireguardPeer.java
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/wireguard/WireguardPeer.java
@@ -1,10 +1,9 @@
package com.yahoo.vespa.hosted.node.admin.wireguard;
import com.yahoo.config.provision.HostName;
-import com.yahoo.config.provision.WireguardKey;
+import com.yahoo.config.provision.WireguardKeyWithTimestamp;
import com.yahoo.vespa.hosted.node.admin.task.util.network.VersionedIpAddress;
-import java.time.Instant;
import java.util.List;
/**
@@ -15,8 +14,7 @@ import java.util.List;
*/
public record WireguardPeer(HostName hostname,
List<VersionedIpAddress> ipAddresses,
- WireguardKey publicKey,
- Instant wireguardKeyTimestamp) implements Comparable<WireguardPeer> {
+ WireguardKeyWithTimestamp keyWithTimestamp) implements Comparable<WireguardPeer> {
public WireguardPeer {
if (ipAddresses.isEmpty()) throw new IllegalArgumentException("No IP addresses for peer node " + hostname.value());
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepositoryTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepositoryTest.java
index 98e65d03f2f..ee3eac22d02 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepositoryTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/RealNodeRepositoryTest.java
@@ -9,6 +9,7 @@ import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.WireguardKey;
+import com.yahoo.config.provision.WireguardKeyWithTimestamp;
import com.yahoo.config.provision.host.FlavorOverrides;
import com.yahoo.vespa.hosted.node.admin.configserver.ConfigServerApi;
import com.yahoo.vespa.hosted.node.admin.configserver.ConfigServerApiImpl;
@@ -140,6 +141,7 @@ public class RealNodeRepositoryTest {
var dockerImage = "registry.example.com/repo/image-1:6.2.3";
var wireguardKey = WireguardKey.from("111122223333444455556666777788889999000042c=");
var wireguardKeyTimestamp = Instant.ofEpochMilli(123L); // Instant from clock in MockNodeRepository
+ var keyWithTimestamp = new WireguardKeyWithTimestamp(wireguardKey, wireguardKeyTimestamp);
nodeRepositoryApi.updateNodeAttributes(
hostname,
@@ -151,8 +153,7 @@ public class RealNodeRepositoryTest {
NodeSpec hostSpec = nodeRepositoryApi.getOptionalNode(hostname).orElseThrow();
assertEquals(1, hostSpec.currentRestartGeneration().orElseThrow());
assertEquals(dockerImage, hostSpec.currentDockerImage().orElseThrow().asString());
- assertEquals(wireguardKey.value(), hostSpec.wireguardPubkey().orElseThrow().value());
- assertEquals(wireguardKeyTimestamp, hostSpec.wireguardKeyTimestamp().orElseThrow());
+ assertEquals(keyWithTimestamp, hostSpec.wireguardKeyWithTimestamp().orElseThrow());
}
@Test
@@ -215,7 +216,7 @@ public class RealNodeRepositoryTest {
assertWireguardPeer(cfgPeers.get(0), "cfg1.yahoo.com",
"::201:1",
"lololololololololololololololololololololoo=",
- Instant.ofEpochMilli(456L));
+ 456L);
//// Exclave nodes ////
@@ -227,16 +228,17 @@ public class RealNodeRepositoryTest {
assertWireguardPeer(exclavePeers.get(0), "dockerhost2.yahoo.com",
"::101:1",
"000011112222333344445555666677778888999900c=",
- Instant.ofEpochMilli(123L));
+ 123L);
}
private void assertWireguardPeer(WireguardPeer peer, String hostname, String ipv6,
- String publicKey, Instant keyTimestamp) {
+ String publicKey, long keyTimestamp) {
assertEquals(hostname, peer.hostname().value());
assertEquals(1, peer.ipAddresses().size());
assertIp(peer.ipAddresses().get(0), ipv6, 6);
- assertEquals(publicKey, peer.publicKey().value());
- assertEquals(keyTimestamp, peer.wireguardKeyTimestamp());
+ var expectedKeyWithTimestamp = new WireguardKeyWithTimestamp(WireguardKey.from(publicKey),
+ Instant.ofEpochMilli(keyTimestamp));
+ assertEquals(expectedKeyWithTimestamp, peer.keyWithTimestamp());
}
private void assertIp(VersionedIpAddress ip, String expectedIp, int expectedVersion) {
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/wireguard/WireguardPeerTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/wireguard/WireguardPeerTest.java
index cd76b221c9e..6ee896e3db6 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/wireguard/WireguardPeerTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/wireguard/WireguardPeerTest.java
@@ -2,6 +2,7 @@ package com.yahoo.vespa.hosted.node.admin.wireguard;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.WireguardKey;
+import com.yahoo.config.provision.WireguardKeyWithTimestamp;
import com.yahoo.vespa.hosted.node.admin.task.util.network.VersionedIpAddress;
import org.junit.jupiter.api.Test;
@@ -31,6 +32,7 @@ public class WireguardPeerTest {
private static WireguardPeer peer(String hostname) {
return new WireguardPeer(HostName.of(hostname), List.of(VersionedIpAddress.from("::1:1")),
- WireguardKey.generateRandomForTesting(), Instant.EPOCH);
+ new WireguardKeyWithTimestamp(WireguardKey.generateRandomForTesting(), Instant.EPOCH));
}
+
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java
index 24159b88a9b..d5e891a33c7 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java
@@ -10,7 +10,7 @@ import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.TenantName;
-import com.yahoo.config.provision.WireguardKey;
+import com.yahoo.config.provision.WireguardKeyWithTimestamp;
import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.hosted.provision.lb.LoadBalancers;
import com.yahoo.vespa.hosted.provision.node.Agent;
@@ -64,8 +64,7 @@ public final class Node implements Nodelike {
private final CloudAccount cloudAccount;
/** Only set for configservers and exclave nodes */
- private final Optional<WireguardKey> wireguardPubKey;
- private final Optional<Instant> wireguardKeyTimestamp;
+ private final Optional<WireguardKeyWithTimestamp> wireguardPubKey;
/** Record of the last event of each type happening to this node */
private final History history;
@@ -96,8 +95,8 @@ public final class Node implements Nodelike {
NodeType type, Reports reports, Optional<String> modelName, Optional<TenantName> reservedTo,
Optional<ApplicationId> exclusiveToApplicationId, Optional<Duration> hostTTL, Optional<Instant> hostEmptyAt,
Optional<ClusterSpec.Type> exclusiveToClusterType, Optional<String> switchHostname,
- List<TrustStoreItem> trustStoreItems, CloudAccount cloudAccount, Optional<WireguardKey> wireguardPubKey,
- Optional<Instant> wireguardKeyTimestamp) {
+ List<TrustStoreItem> trustStoreItems, CloudAccount cloudAccount,
+ Optional<WireguardKeyWithTimestamp> wireguardPubKey) {
this.id = Objects.requireNonNull(id, "A node must have an ID");
this.extraId = Objects.requireNonNull(extraId, "Extra ID cannot be null");
this.hostname = requireNonEmptyString(hostname, "A node must have a hostname");
@@ -120,7 +119,6 @@ public final class Node implements Nodelike {
this.trustStoreItems = Objects.requireNonNull(trustStoreItems).stream().distinct().toList();
this.cloudAccount = Objects.requireNonNull(cloudAccount);
this.wireguardPubKey = Objects.requireNonNull(wireguardPubKey);
- this.wireguardKeyTimestamp = Objects.requireNonNull(wireguardKeyTimestamp);
if (state == State.active)
requireNonEmpty(ipConfig.primary(), "Active node " + hostname + " must have at least one valid IP address");
@@ -264,15 +262,10 @@ public final class Node implements Nodelike {
}
/** Returns the wireguard public key of this node. Only relevant for enclave nodes. */
- public Optional<WireguardKey> wireguardPubKey() {
+ public Optional<WireguardKeyWithTimestamp> wireguardPubKey() {
return wireguardPubKey;
}
- /** Returns the timestamp of the wireguard key of this node. Only relevant for enclave nodes. */
- public Optional<Instant> wireguardKeyTimestamp() {
- return wireguardKeyTimestamp;
- }
-
/**
* Returns a copy of this where wantToFail is set to true and history is updated to reflect this.
*/
@@ -367,16 +360,14 @@ public final class Node implements Nodelike {
public Node with(Status status) {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history, type,
reports, modelName, reservedTo, exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
/** Returns a node with the type assigned to the given value */
public Node with(NodeType type) {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history, type,
reports, modelName, reservedTo, exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
/** Returns a node with the flavor assigned to the given value */
@@ -385,40 +376,35 @@ public final class Node implements Nodelike {
History updateHistory = history.with(new History.Event(History.Event.Type.resized, agent, instant));
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, updateHistory, type,
reports, modelName, reservedTo, exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
/** Returns a copy of this with the reboot generation set to generation */
public Node withReboot(Generation generation) {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status.withReboot(generation), state, allocation,
history, type, reports, modelName, reservedTo, exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
/** Returns a copy of this with given id set */
public Node withId(String id) {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation,
history, type, reports, modelName, reservedTo, exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
/** Returns a copy of this with model name set to given value */
public Node withModelName(String modelName) {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history,
type, reports, Optional.of(modelName), reservedTo, exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
/** Returns a copy of this with model name cleared */
public Node withoutModelName() {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history,
type, reports, Optional.empty(), reservedTo, exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
/** Returns a copy of this with a history record saying it was detected to be down at this instant */
@@ -460,24 +446,21 @@ public final class Node implements Nodelike {
public Node with(Allocation allocation) {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, Optional.of(allocation), history,
type, reports, modelName, reservedTo, exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
/** Returns a copy of this node with IP config set to the given value. */
public Node with(IP.Config ipConfig) {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history,
type, reports, modelName, reservedTo, exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
/** Returns a copy of this node with the parent hostname assigned to the given value. */
public Node withParentHostname(String parentHostname) {
return new Node(id, extraId, ipConfig, hostname, Optional.of(parentHostname), flavor, status, state, allocation,
history, type, reports, modelName, reservedTo, exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
public Node withReservedTo(TenantName tenant) {
@@ -485,73 +468,59 @@ public final class Node implements Nodelike {
throw new IllegalArgumentException("Only host nodes can be reserved, " + hostname + " has type " + type);
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history,
type, reports, modelName, Optional.of(tenant), exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
/** Returns a copy of this node which is not reserved to a tenant */
public Node withoutReservedTo() {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history,
type, reports, modelName, Optional.empty(), exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
public Node withExclusiveToApplicationId(ApplicationId exclusiveTo) {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history,
type, reports, modelName, reservedTo, Optional.ofNullable(exclusiveTo), hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
public Node withExtraId(Optional<String> extraId) {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history,
type, reports, modelName, reservedTo, exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
public Node withHostTTL(Duration hostTTL) {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history,
type, reports, modelName, reservedTo, exclusiveToApplicationId, Optional.ofNullable(hostTTL), hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
public Node withHostEmptyAt(Instant hostEmptyAt) {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history,
type, reports, modelName, reservedTo, exclusiveToApplicationId, hostTTL, Optional.ofNullable(hostEmptyAt),
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
public Node withExclusiveToClusterType(ClusterSpec.Type exclusiveTo) {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history,
type, reports, modelName, reservedTo, exclusiveToApplicationId, hostTTL, hostEmptyAt,
- Optional.ofNullable(exclusiveTo), switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ Optional.ofNullable(exclusiveTo), switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
- public Node withWireguardPubkey(WireguardKey wireguardPubkey) {
+ public Node withWireguardPubkey(WireguardKeyWithTimestamp wireguardPubkey) {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history,
type, reports, modelName, reservedTo, exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, Optional.ofNullable(wireguardPubkey),
- wireguardKeyTimestamp);
- }
-
- public Node withWireguardKeyTimestamp(Instant wireguardKeyTimestamp) {
- return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history,
- type, reports, modelName, reservedTo, exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- Optional.ofNullable(wireguardKeyTimestamp));
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount,
+ Optional.ofNullable(wireguardPubkey));
}
/** Returns a copy of this node with switch hostname set to given value */
public Node withSwitchHostname(String switchHostname) {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history,
type, reports, modelName, reservedTo, exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, Optional.ofNullable(switchHostname), trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, Optional.ofNullable(switchHostname), trustStoreItems, cloudAccount,
+ wireguardPubKey);
}
/** Returns a copy of this node with switch hostname unset */
@@ -604,22 +573,19 @@ public final class Node implements Nodelike {
public Node with(History history) {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history,
type, reports, modelName, reservedTo, exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
public Node with(Reports reports) {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history,
type, reports, modelName, reservedTo, exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
public Node with(List<TrustStoreItem> trustStoreItems) {
return new Node(id, extraId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history,
type, reports, modelName, reservedTo, exclusiveToApplicationId, hostTTL, hostEmptyAt,
- exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey,
- wireguardKeyTimestamp);
+ exclusiveToClusterType, switchHostname, trustStoreItems, cloudAccount, wireguardPubKey);
}
private static Optional<String> requireNonEmptyString(Optional<String> value, String message) {
@@ -767,8 +733,7 @@ public final class Node implements Nodelike {
private History history;
private List<TrustStoreItem> trustStoreItems;
private CloudAccount cloudAccount = CloudAccount.empty;
- private WireguardKey wireguardPubKey;
- private Instant wireguardKeyTimestamp;
+ private WireguardKeyWithTimestamp wireguardPubKey;
private Builder(String id, String hostname, Flavor flavor, State state, NodeType type) {
this.id = id;
@@ -858,16 +823,11 @@ public final class Node implements Nodelike {
return this;
}
- public Builder wireguardPubKey(WireguardKey wireguardPubKey) {
+ public Builder wireguardKey(WireguardKeyWithTimestamp wireguardPubKey) {
this.wireguardPubKey = wireguardPubKey;
return this;
}
- public Builder wireguardKeyTimestamp(Instant wireguardKeyTimestamp) {
- this.wireguardKeyTimestamp = wireguardKeyTimestamp;
- return this;
- }
-
public Node build() {
return new Node(id, Optional.empty(), Optional.ofNullable(ipConfig).orElse(IP.Config.EMPTY), hostname, Optional.ofNullable(parentHostname),
flavor, Optional.ofNullable(status).orElseGet(Status::initial), state, Optional.ofNullable(allocation),
@@ -875,7 +835,7 @@ public final class Node implements Nodelike {
Optional.ofNullable(modelName), Optional.ofNullable(reservedTo), Optional.ofNullable(exclusiveToApplicationId),
Optional.ofNullable(hostTTL), Optional.ofNullable(hostEmptyAt), Optional.ofNullable(exclusiveToClusterType),
Optional.ofNullable(switchHostname), Optional.ofNullable(trustStoreItems).orElseGet(List::of), cloudAccount,
- Optional.ofNullable(wireguardPubKey), Optional.ofNullable(wireguardKeyTimestamp));
+ Optional.ofNullable(wireguardPubKey));
}
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDb.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDb.java
index 3c3868bfeb8..c7876032374 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDb.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDb.java
@@ -222,7 +222,7 @@ public class CuratorDb {
node.type(), node.reports(), node.modelName(), node.reservedTo(),
node.exclusiveToApplicationId(), node.hostTTL(), node.hostEmptyAt(),
node.exclusiveToClusterType(), node.switchHostname(), node.trustedCertificates(),
- node.cloudAccount(), node.wireguardPubKey(), node.wireguardKeyTimestamp());
+ node.cloudAccount(), node.wireguardPubKey());
curatorTransaction.add(createOrSet(nodePath(newNode), nodeSerializer.toJson(newNode)));
writtenNodes.add(newNode);
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java
index 870e678a250..73531d650d5 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java
@@ -16,6 +16,7 @@ import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.WireguardKey;
+import com.yahoo.config.provision.WireguardKeyWithTimestamp;
import com.yahoo.config.provision.host.FlavorOverrides;
import com.yahoo.config.provision.serialization.NetworkPortsSerializer;
import com.yahoo.slime.ArrayTraverser;
@@ -188,8 +189,10 @@ public class NodeSerializer {
if (!node.cloudAccount().isUnspecified()) {
object.setString(cloudAccountKey, node.cloudAccount().value());
}
- node.wireguardPubKey().ifPresent(pubKey -> object.setString(wireguardPubKeyKey, pubKey.value()));
- node.wireguardKeyTimestamp().ifPresent(timestamp -> object.setLong(wireguardKeyTimestampKey, timestamp.toEpochMilli()));
+ node.wireguardPubKey().ifPresent(pubKey -> {
+ object.setString(wireguardPubKeyKey, pubKey.key().value());
+ object.setLong(wireguardKeyTimestampKey, pubKey.timestamp().toEpochMilli());
+ });
}
private void toSlime(Flavor flavor, Cursor object) {
@@ -284,8 +287,7 @@ public class NodeSerializer {
SlimeUtils.optionalString(object.field(switchHostnameKey)),
trustedCertificatesFromSlime(object),
SlimeUtils.optionalString(object.field(cloudAccountKey)).map(CloudAccount::from).orElse(CloudAccount.empty),
- SlimeUtils.optionalString(object.field(wireguardPubKeyKey)).map(WireguardKey::from),
- SlimeUtils.optionalInstant(object.field(wireguardKeyTimestampKey)));
+ wireguardKeyWithTimestampFromSlime(object.field(wireguardPubKeyKey), object.field(wireguardKeyTimestampKey)));
}
private Status statusFromSlime(Inspector object) {
@@ -397,6 +399,13 @@ public class NodeSerializer {
.toList();
}
+ private Optional<WireguardKeyWithTimestamp> wireguardKeyWithTimestampFromSlime(Inspector keyObject, Inspector timestampObject) {
+ if ( ! keyObject.valid()) return Optional.empty();
+ return SlimeUtils.optionalString(keyObject).map(
+ key -> new WireguardKeyWithTimestamp(WireguardKey.from(key),
+ SlimeUtils.optionalInstant(timestampObject).orElse(null)));
+ }
+
// ----------------- Enum <-> string mappings ----------------------------------------
/** Returns the event type, or null if this event type should be ignored */
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java
index 9f1ab3dc3d5..cad034e01aa 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java
@@ -11,6 +11,7 @@ import com.yahoo.config.provision.NodeFlavors;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.WireguardKey;
+import com.yahoo.config.provision.WireguardKeyWithTimestamp;
import com.yahoo.slime.Cursor;
import com.yahoo.slime.Inspector;
import com.yahoo.slime.ObjectTraverser;
@@ -108,7 +109,8 @@ public class NodePatcher {
"reports",
"trustStore",
"vespaVersion",
- "wireguardPubkey"));
+ "wireguardPubkey", // TODO wg: remove when all nodes use new key+timestamp format
+ "wireguard"));
if (!disallowedFields.isEmpty()) {
throw new IllegalArgumentException("Patching fields not supported: " + disallowedFields);
}
@@ -271,9 +273,13 @@ public class NodePatcher {
return value.type() == Type.NIX ? node.withoutSwitchHostname() : node.withSwitchHostname(value.asString());
case "trustStore":
return nodeWithTrustStore(node, value);
- case "wireguardPubkey":
- return node.withWireguardPubkey(SlimeUtils.optionalString(value).map(WireguardKey::new).orElse(null))
- .withWireguardKeyTimestamp(clock.instant());
+ case "wireguard":
+ // This is where we set the key timestamp.
+ var key = SlimeUtils.optionalString(value.field("key")).map(WireguardKey::new).orElse(null);
+ return node.withWireguardPubkey(new WireguardKeyWithTimestamp(key, clock.instant()));
+ case "wireguardPubkey": // TODO wg: remove when all nodes use new key+timestamp format
+ var oldKey = SlimeUtils.optionalString(value).map(WireguardKey::new).orElse(null);
+ return node.withWireguardPubkey(new WireguardKeyWithTimestamp(oldKey, clock.instant()));
default:
throw new IllegalArgumentException("Could not apply field '" + name + "' on a node: No such modifiable field");
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java
index a8f526544d7..05bb0a27d69 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java
@@ -8,6 +8,7 @@ import com.yahoo.config.provision.ClusterMembership;
import com.yahoo.config.provision.DockerImage;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeResources;
+import com.yahoo.config.provision.WireguardKeyWithTimestamp;
import com.yahoo.config.provision.serialization.NetworkPortsSerializer;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.restapi.SlimeJsonResponse;
@@ -192,8 +193,13 @@ class NodesResponse extends SlimeJsonResponse {
if (!node.cloudAccount().isUnspecified()) {
object.setString("cloudAccount", node.cloudAccount().value());
}
- node.wireguardPubKey().ifPresent(key -> object.setString("wireguardPubkey", key.value()));
- node.wireguardKeyTimestamp().ifPresent(timestamp -> object.setLong("wireguardKeyTimestamp", timestamp.toEpochMilli()));
+ node.wireguardPubKey().ifPresent(key -> toSlime(key, object.setObject("wireguard")));
+
+ // TODO wg: remove when all nodes have upgraded to new key+timestamp format
+ node.wireguardPubKey().ifPresent(key -> {
+ object.setString("wireguardPubkey", key.key().value());
+ object.setLong("wireguardKeyTimestamp", key.timestamp().toEpochMilli());
+ });
}
private Version resolveVersionFlag(StringFlag flag, Node node, Allocation allocation) {
@@ -237,6 +243,11 @@ class NodesResponse extends SlimeJsonResponse {
}
}
+ static void toSlime(WireguardKeyWithTimestamp keyWithTimestamp, Cursor object) {
+ object.setString("key", keyWithTimestamp.key().value());
+ object.setLong("timestamp", keyWithTimestamp.timestamp().toEpochMilli());
+ }
+
private Optional<DockerImage> currentContainerImage(Node node) {
if (node.status().containerImage().isPresent()) {
return node.status().containerImage();
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/WireguardResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/WireguardResponse.java
index 16e85dfa48a..e29c4f1b87a 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/WireguardResponse.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/WireguardResponse.java
@@ -1,7 +1,7 @@
package com.yahoo.vespa.hosted.provision.restapi;
import com.yahoo.config.provision.NodeType;
-import com.yahoo.config.provision.WireguardKey;
+import com.yahoo.config.provision.WireguardKeyWithTimestamp;
import com.yahoo.restapi.SlimeJsonResponse;
import com.yahoo.slime.Cursor;
import com.yahoo.vespa.hosted.provision.Node;
@@ -10,9 +10,9 @@ import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.node.IP;
import java.net.InetAddress;
-import java.time.Instant;
import java.util.List;
-import java.util.Optional;
+
+import static com.yahoo.vespa.hosted.provision.restapi.NodesResponse.toSlime;
/**
* A response containing the wireguard peer config for each configserver that has a public key.
@@ -36,17 +36,20 @@ public class WireguardResponse extends SlimeJsonResponse {
.toList();
if (ipAddresses.isEmpty()) continue;
- addConfigserver(cfgArray.addObject(), cfg.hostname(), cfg.wireguardPubKey().get(),
- cfg.wireguardKeyTimestamp(), ipAddresses);
+ addConfigserver(cfgArray.addObject(), cfg.hostname(), cfg.wireguardPubKey().get(), ipAddresses);
}
}
- private void addConfigserver(Cursor cfgEntry, String hostname, WireguardKey key, Optional<Instant> keyTimestamp,
+ private void addConfigserver(Cursor cfgEntry, String hostname, WireguardKeyWithTimestamp keyWithTimestamp,
List<String> ipAddresses) {
cfgEntry.setString("hostname", hostname);
- cfgEntry.setString("wireguardPubkey", key.value());
- cfgEntry.setLong("wireguardKeyTimestamp", keyTimestamp.orElse(Instant.EPOCH).toEpochMilli());
+
+ // TODO wg: remove when all nodes are using new key+timestamp format
+ cfgEntry.setString("wireguardPubkey", keyWithTimestamp.key().value());
+ cfgEntry.setLong("wireguardKeyTimestamp", keyWithTimestamp.timestamp().toEpochMilli());
+
NodesResponse.ipAddressesToSlime(ipAddresses, cfgEntry.setArray("ipAddresses"));
+ toSlime(keyWithTimestamp, cfgEntry.setObject("wireguard"));
}
private static boolean isPublicIp(String ipAddress) {
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java
index 72225763381..2fb549acc11 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java
@@ -21,6 +21,7 @@ import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.WireguardKey;
+import com.yahoo.config.provision.WireguardKeyWithTimestamp;
import com.yahoo.config.provision.Zone;
import com.yahoo.config.provision.ZoneEndpoint;
import com.yahoo.config.provision.ZoneEndpoint.AccessType;
@@ -161,8 +162,8 @@ public class MockNodeRepository extends NodeRepository {
// Emulate host in tenant account
nodes.add(Node.create("dockerhost2", ipConfig(101, 1, 3), "dockerhost2.yahoo.com",
flavors.getFlavorOrThrow("large"), NodeType.host)
- .wireguardPubKey(WireguardKey.from("000011112222333344445555666677778888999900c="))
- .wireguardKeyTimestamp(Instant.ofEpochMilli(123L))
+ .wireguardKey(new WireguardKeyWithTimestamp(WireguardKey.from("000011112222333344445555666677778888999900c="),
+ Instant.ofEpochMilli(123L)))
.cloudAccount(tenantAccount).build());
nodes.add(Node.create("dockerhost3", ipConfig(102, 1, 3), "dockerhost3.yahoo.com",
flavors.getFlavorOrThrow("large"), NodeType.host).cloudAccount(defaultCloudAccount).build());
@@ -176,8 +177,8 @@ public class MockNodeRepository extends NodeRepository {
// Config servers
nodes.add(Node.create("cfg1", ipConfig(201), "cfg1.yahoo.com", flavors.getFlavorOrThrow("default"), NodeType.config)
.cloudAccount(defaultCloudAccount)
- .wireguardPubKey(WireguardKey.from("lololololololololololololololololololololoo="))
- .wireguardKeyTimestamp(Instant.ofEpochMilli(456L))
+ .wireguardKey(new WireguardKeyWithTimestamp(WireguardKey.from("lololololololololololololololololololololoo="),
+ Instant.ofEpochMilli(456L)))
.build());
nodes.add(Node.create("cfg2", ipConfig(202), "cfg2.yahoo.com", flavors.getFlavorOrThrow("default"), NodeType.config)
.cloudAccount(defaultCloudAccount)
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/cfg1.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/cfg1.json
index 928e91861a2..54a0e7e9757 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/cfg1.json
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/cfg1.json
@@ -119,6 +119,10 @@
],
"additionalIpAddresses": [],
"cloudAccount": "aws:111222333444",
- "wireguardPubkey":"lololololololololololololololololololololoo=",
+ "wireguard": {
+ "key": "lololololololololololololololololololololoo=",
+ "timestamp": 456
+ },
+ "wireguardPubkey": "lololololololololololololololololololololoo=",
"wireguardKeyTimestamp": 456
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node2.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node2.json
index 72b5483d849..d3f1a8082ae 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node2.json
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/docker-node2.json
@@ -117,6 +117,10 @@
"ipAddresses": ["127.0.101.1", "::101:1"],
"additionalIpAddresses": ["::101:2", "::101:3", "::101:4"],
"cloudAccount": "aws:777888999000",
+ "wireguard": {
+ "key": "000011112222333344445555666677778888999900c=",
+ "timestamp": 123
+ },
"wireguardPubkey": "000011112222333344445555666677778888999900c=",
"wireguardKeyTimestamp": 123
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node4-wg.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node4-wg.json
index d0d6df71fc1..404cf9a9a80 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node4-wg.json
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/node4-wg.json
@@ -118,6 +118,10 @@
"ipAddresses": ["127.0.4.1", "::4:1"],
"additionalIpAddresses": [],
"cloudAccount": "aws:111222333444",
+ "wireguard": {
+ "key": "lololololololololololololololololololololoo=",
+ "timestamp": 123
+ },
"wireguardPubkey": "lololololololololololololololololololololoo=",
"wireguardKeyTimestamp": 123
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/wireguard.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/wireguard.json
index 7bee06adc87..8e9af7f680f 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/wireguard.json
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/wireguard.json
@@ -4,7 +4,11 @@
"hostname": "cfg1.yahoo.com",
"wireguardPubkey": "lololololololololololololololololololololoo=",
"wireguardKeyTimestamp":456,
- "ipAddresses": ["::201:1"]
+ "ipAddresses": ["::201:1"],
+ "wireguard": {
+ "key": "lololololololololololololololololololololoo=",
+ "timestamp": 456
+ }
}
]
}