Skip to content

Commit 82f7f9d

Browse files
[Remote Clusters] Add IPv6 support (#233415)
fixes #228298 - Add IPv6 address support for remote cluster seed nodes and proxy addresses using bracket notation ( [::1]:9300 ) - Move address validation from client-side to shared common library using ipaddr.js for robust IPv4/IPv6 validation - Update cluster serialization and cloud URL parsing to handle IPv6 addresses correctly ## How to test 1. Spin up local kibana and es on default ports (`yarn es snapshot` and `yarn start --no-base-path`) 2. Go to `/app/management/data/remote_clusters` 3. Click "add new cluster" 4. Click "certificates celect" 5. Add loopback ipv6 address to seed pointing to cluster communication port `[::1]:9300` 6. Observe no client side error for `seed` field, proving that ipv6 syntax is now valid 7. Add `test` name 8. `Uncheck skip_unavailable` 10. Click `next` 11. Click `create remote cluster` 12. Observe live cluster connection in the list of remote clusters ## Release Note Added IPv6 support to address fields in Remote Clusters UI --------- Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
1 parent 61f6690 commit 82f7f9d

File tree

16 files changed

+682
-189
lines changed

16 files changed

+682
-189
lines changed

x-pack/platform/plugins/private/remote_clusters/__jest__/client_integration/add/remote_clusters_add.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,13 @@ describe('Create Remote cluster', () => {
151151
});
152152

153153
describe('seeds', () => {
154-
test('should only allow alpha-numeric characters and "-" (dash) in the node "host" part', async () => {
154+
test('should only allow hostname, ipv4 and ipv6 format in the node "host" part', async () => {
155155
await actions.formStep.button.click(); // display form errors
156156

157157
const expectInvalidChar = (char: string) => {
158158
actions.formStep.seedsInput.setValue(`192.16${char}:3000`);
159159
expect(actions.getErrorMessages()).toContain(
160-
`Seed node must use host:port format. Example: 127.0.0.1:9400, localhost:9400. Hosts can only consist of letters, numbers, and dashes.`
160+
`Seed node must use host:port format. Example: 127.0.0.1:9400, [::1]:9400 or localhost:9400. Hosts can only consist of letters, numbers, and dashes.`
161161
);
162162
};
163163

@@ -289,7 +289,7 @@ describe('Create Remote cluster', () => {
289289
const expectInvalidChar = (char: string) => {
290290
actions.formStep.proxyAddressInput.setValue(`192.16${char}:3000`);
291291
expect(actions.getErrorMessages()).toContain(
292-
'Address must use host:port format. Example: 127.0.0.1:9400, localhost:9400. Hosts can only consist of letters, numbers, and dashes.'
292+
'Address must use host:port format. Example: 127.0.0.1:9400, [::1]:9400 or localhost:9400. Hosts can only consist of letters, numbers, and dashes.'
293293
);
294294
};
295295

x-pack/platform/plugins/private/remote_clusters/common/lib/cluster_serialization.test.ts

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -135,41 +135,52 @@ describe('cluster_serialization', () => {
135135
});
136136
});
137137

138-
it('should deserialize a cluster that contains a deprecated proxy address and is in cloud', () => {
139-
expect(
140-
deserializeCluster(
141-
'test_cluster',
142-
{
143-
seeds: ['localhost:9300'],
144-
connected: true,
145-
num_nodes_connected: 1,
146-
max_connections_per_cluster: 3,
147-
initial_connect_timeout: '30s',
148-
skip_unavailable: false,
149-
transport: {
150-
ping_schedule: '-1',
151-
compress: false,
138+
it.each([
139+
{ title: '(hostname)', address: 'localhost:3000', host: 'localhost' },
140+
{ title: '(IPv4)', address: '123.1.2.142:3000', host: '123.1.2.142' },
141+
{
142+
title: '(IPv6)',
143+
address: '[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:3000',
144+
host: '[2001:0db8:85a3:0000:0000:8a2e:0370:7334]',
145+
},
146+
])(
147+
'should deserialize a cluster that contains a deprecated proxy address and is in cloud $title',
148+
({ address, host }) => {
149+
expect(
150+
deserializeCluster(
151+
'test_cluster',
152+
{
153+
seeds: [address],
154+
connected: true,
155+
num_nodes_connected: 1,
156+
max_connections_per_cluster: 3,
157+
initial_connect_timeout: '30s',
158+
skip_unavailable: false,
159+
transport: {
160+
ping_schedule: '-1',
161+
compress: false,
162+
},
152163
},
153-
},
154-
'localhost:9300',
155-
true
156-
)
157-
).toEqual({
158-
name: 'test_cluster',
159-
proxyAddress: 'localhost:9300',
160-
mode: 'proxy',
161-
hasDeprecatedProxySetting: true,
162-
isConnected: true,
163-
connectedNodesCount: 1,
164-
maxConnectionsPerCluster: 3,
165-
initialConnectTimeout: '30s',
166-
skipUnavailable: false,
167-
transportPingSchedule: '-1',
168-
transportCompress: false,
169-
serverName: 'localhost',
170-
securityModel: SECURITY_MODEL.CERTIFICATE,
171-
});
172-
});
164+
address,
165+
true
166+
)
167+
).toEqual({
168+
name: 'test_cluster',
169+
proxyAddress: address,
170+
mode: 'proxy',
171+
hasDeprecatedProxySetting: true,
172+
isConnected: true,
173+
connectedNodesCount: 1,
174+
maxConnectionsPerCluster: 3,
175+
initialConnectTimeout: '30s',
176+
skipUnavailable: false,
177+
transportPingSchedule: '-1',
178+
transportCompress: false,
179+
serverName: host,
180+
securityModel: SECURITY_MODEL.CERTIFICATE,
181+
});
182+
}
183+
);
173184

174185
it('should deserialize a cluster object with arbitrary missing properties', () => {
175186
expect(

x-pack/platform/plugins/private/remote_clusters/common/lib/cluster_serialization.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* 2.0.
66
*/
77

8+
import { extractHostAndPort } from './validate_address';
89
import { PROXY_MODE, SNIFF_MODE, SECURITY_MODEL } from '../constants';
910

1011
// Values returned from ES GET /_remote/info
@@ -143,19 +144,21 @@ export function deserializeCluster(
143144
};
144145
}
145146

147+
const deprecatedProxyHost = deprecatedProxyAddress
148+
? extractHostAndPort(deprecatedProxyAddress)?.host
149+
: undefined;
150+
146151
// If a user has a remote cluster with the deprecated proxy setting,
147152
// we transform the data to support the new implementation and also flag the deprecation
148-
if (deprecatedProxyAddress) {
149-
// Cloud-specific logic: Create default server name, since field doesn't exist in deprecated implementation
150-
const defaultServerName = deprecatedProxyAddress.split(':')[0];
151-
153+
if (deprecatedProxyAddress && deprecatedProxyHost) {
152154
deserializedClusterObject = {
153155
...deserializedClusterObject,
154156
proxyAddress: deprecatedProxyAddress,
155157
seeds: undefined,
156158
hasDeprecatedProxySetting: true,
157159
mode: PROXY_MODE,
158-
serverName: isCloudEnabled ? defaultServerName : undefined,
160+
// Cloud-specific logic: Create default server name, since this field doesn't exist in deprecated implementation
161+
serverName: isCloudEnabled ? deprecatedProxyHost : undefined,
159162
};
160163
}
161164

x-pack/platform/plugins/private/remote_clusters/common/lib/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ export type {
1212
ClusterPayloadEs,
1313
} from './cluster_serialization';
1414
export { deserializeCluster, serializeCluster } from './cluster_serialization';
15+
export { extractHostAndPort, isAddressValid, isPortValid } from './validate_address';

0 commit comments

Comments
 (0)