VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/CloudGateway.cpp@ 94321

Last change on this file since 94321 was 93312, checked in by vboxsync, 2 years ago

CloudNet: ​bugref:9469 Replace local gateway with DrvCloudTunnel. Build 25519 encryption support in libssh. Add missing version bump in machine settings to 1.18 if cloud attachment is used.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.7 KB
Line 
1/* $Id: CloudGateway.cpp 93312 2022-01-18 13:15:12Z vboxsync $ */
2/** @file
3 * Implementation of local and cloud gateway management.
4 */
5
6/*
7 * Copyright (C) 2019-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_CONSOLE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "ApplianceImpl.h"
30#include "CloudNetworkImpl.h"
31#include "CloudGateway.h"
32
33#include <iprt/http.h>
34#include <iprt/inifile.h>
35#include <iprt/net.h>
36#include <iprt/path.h>
37#include <iprt/vfs.h>
38#include <iprt/uri.h>
39#ifdef DEBUG
40#include <iprt/file.h>
41#include <VBox/com/utils.h>
42#endif
43
44#ifdef VBOX_WITH_LIBSSH
45/* Prevent inclusion of Winsock2.h */
46#define _WINSOCK2API_
47#include <libssh/libssh.h>
48#endif /* VBOX_WITH_LIBSSH */
49
50
51static HRESULT setMacAddress(const Utf8Str& str, RTMAC& mac)
52{
53 int rc = RTNetStrToMacAddr(str.c_str(), &mac);
54 if (RT_FAILURE(rc))
55 {
56 LogRel(("CLOUD-NET: Invalid MAC address '%s'\n", str.c_str()));
57 return E_INVALIDARG;
58 }
59 return S_OK;
60}
61
62
63HRESULT GatewayInfo::setCloudMacAddress(const Utf8Str& mac)
64{
65 return setMacAddress(mac, mCloudMacAddress);
66}
67
68
69HRESULT GatewayInfo::setLocalMacAddress(const Utf8Str& mac)
70{
71 return setMacAddress(mac, mLocalMacAddress);
72}
73
74
75class CloudError
76{
77public:
78 CloudError(HRESULT hrc, const Utf8Str& strText) : mHrc(hrc), mText(strText) {};
79 HRESULT getRc() { return mHrc; };
80 Utf8Str getText() { return mText; };
81
82private:
83 HRESULT mHrc;
84 Utf8Str mText;
85};
86
87
88static void handleErrors(HRESULT hrc, const char *pszFormat, ...)
89{
90 if (FAILED(hrc))
91 {
92 va_list va;
93 va_start(va, pszFormat);
94 Utf8Str strError(pszFormat, va);
95 va_end(va);
96 LogRel(("CLOUD-NET: %s (rc=%x)\n", strError.c_str(), hrc));
97 throw CloudError(hrc, strError);
98 }
99
100}
101
102
103class CloudClient
104{
105public:
106 CloudClient(ComPtr<IVirtualBox> virtualBox, const Bstr& strProvider, const Bstr& strProfile);
107 ~CloudClient() {};
108
109 void startCloudGateway(const ComPtr<ICloudNetwork> &network, GatewayInfo& gateway);
110 void stopCloudGateway(const GatewayInfo& gateway);
111
112private:
113 ComPtr<ICloudProviderManager> mManager;
114 ComPtr<ICloudProvider> mProvider;
115 ComPtr<ICloudProfile> mProfile;
116 ComPtr<ICloudClient> mClient;
117};
118
119
120CloudClient::CloudClient(ComPtr<IVirtualBox> virtualBox, const Bstr& strProvider, const Bstr& strProfile)
121{
122 HRESULT hrc = virtualBox->COMGETTER(CloudProviderManager)(mManager.asOutParam());
123 handleErrors(hrc, "Failed to obtain cloud provider manager object");
124 hrc = mManager->GetProviderByShortName(strProvider.raw(), mProvider.asOutParam());
125 handleErrors(hrc, "Failed to obtain cloud provider '%ls'", strProvider.raw());
126 hrc = mProvider->GetProfileByName(strProfile.raw(), mProfile.asOutParam());
127 handleErrors(hrc, "Failed to obtain cloud profile '%ls'", strProfile.raw());
128 hrc = mProfile->CreateCloudClient(mClient.asOutParam());
129 handleErrors(hrc, "Failed to create cloud client");
130}
131
132
133void CloudClient::startCloudGateway(const ComPtr<ICloudNetwork> &network, GatewayInfo& gateway)
134{
135 ComPtr<IProgress> progress;
136 ComPtr<ICloudNetworkGatewayInfo> gatewayInfo;
137 HRESULT hrc = mClient->StartCloudNetworkGateway(network, Bstr(gateway.mPublicSshKey).raw(),
138 gatewayInfo.asOutParam(), progress.asOutParam());
139 handleErrors(hrc, "Failed to launch compute instance");
140 hrc = progress->WaitForCompletion(-1);
141 handleErrors(hrc, "Failed to launch compute instance (wait)");
142
143 Bstr instanceId;
144 hrc = gatewayInfo->COMGETTER(InstanceId)(instanceId.asOutParam());
145 handleErrors(hrc, "Failed to get launched compute instance id");
146 gateway.mGatewayInstanceId = instanceId;
147
148 Bstr publicIP;
149 hrc = gatewayInfo->COMGETTER(PublicIP)(publicIP.asOutParam());
150 handleErrors(hrc, "Failed to get cloud gateway public IP address");
151 gateway.mCloudPublicIp = publicIP;
152
153 Bstr secondaryPublicIP;
154 hrc = gatewayInfo->COMGETTER(SecondaryPublicIP)(secondaryPublicIP.asOutParam());
155 handleErrors(hrc, "Failed to get cloud gateway secondary public IP address");
156 gateway.mCloudSecondaryPublicIp = secondaryPublicIP;
157
158 Bstr macAddress;
159 hrc = gatewayInfo->COMGETTER(MacAddress)(macAddress.asOutParam());
160 handleErrors(hrc, "Failed to get cloud gateway public IP address");
161 gateway.setCloudMacAddress(macAddress);
162}
163
164
165void CloudClient::stopCloudGateway(const GatewayInfo& gateway)
166{
167 ComPtr<IProgress> progress;
168 HRESULT hrc = mClient->TerminateInstance(Bstr(gateway.mGatewayInstanceId).raw(), progress.asOutParam());
169 handleErrors(hrc, "Failed to terminate compute instance");
170#if 0
171 /* Someday we may want to wait until the cloud gateway has terminated. */
172 hrc = progress->WaitForCompletion(-1);
173 handleErrors(hrc, "Failed to terminate compute instance (wait)");
174#endif
175}
176
177
178HRESULT startCloudGateway(ComPtr<IVirtualBox> virtualBox, ComPtr<ICloudNetwork> network, GatewayInfo& gateway)
179{
180 HRESULT hrc = S_OK;
181
182 try {
183 hrc = network->COMGETTER(Provider)(gateway.mCloudProvider.asOutParam());
184 hrc = network->COMGETTER(Profile)(gateway.mCloudProfile.asOutParam());
185 CloudClient client(virtualBox, gateway.mCloudProvider, gateway.mCloudProfile);
186 client.startCloudGateway(network, gateway);
187 }
188 catch (CloudError e)
189 {
190 hrc = e.getRc();
191 }
192
193 return hrc;
194}
195
196
197#if 0 /* Disabled until proxy support is implemented */
198static bool getProxyForIpAddr(ComPtr<IVirtualBox> virtualBox, const com::Utf8Str &strIpAddr, Bstr &strProxyType, Bstr &strProxyHost, Bstr &strProxyPort)
199{
200#ifndef VBOX_WITH_PROXY_INFO
201 RT_NOREF(virtualBox, strIpAddr, strProxyType, strProxyHost, strProxyPort);
202 LogRel(("CLOUD-NET: Proxy support is disabled. Using direct connection.\n"));
203 return false;
204#else /* VBOX_WITH_PROXY_INFO */
205 ComPtr<ISystemProperties> systemProperties;
206 ProxyMode_T enmProxyMode;
207 HRESULT hrc = virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
208 if (FAILED(hrc))
209 {
210 LogRel(("CLOUD-NET: Failed to obtain system properties. hrc=%x\n", hrc));
211 return false;
212 }
213 hrc = systemProperties->COMGETTER(ProxyMode)(&enmProxyMode);
214 if (FAILED(hrc))
215 {
216 LogRel(("CLOUD-NET: Failed to obtain default machine folder. hrc=%x\n", hrc));
217 return false;
218 }
219 if (enmProxyMode == ProxyMode_NoProxy)
220 return false;
221
222 Bstr proxyUrl;
223 if (enmProxyMode == ProxyMode_Manual)
224 {
225 hrc = systemProperties->COMGETTER(ProxyURL)(proxyUrl.asOutParam());
226 if (FAILED(hrc))
227 {
228 LogRel(("CLOUD-NET: Failed to obtain proxy URL. hrc=%x\n", hrc));
229 return false;
230 }
231 Utf8Str strProxyUrl = proxyUrl;
232 if (!strProxyUrl.contains("://"))
233 strProxyUrl = "http://" + strProxyUrl;
234 const char *pcszProxyUrl = strProxyUrl.c_str();
235 RTURIPARSED Parsed;
236 int rc = RTUriParse(pcszProxyUrl, &Parsed);
237 if (RT_FAILURE(rc))
238 {
239 LogRel(("CLOUD-NET: Failed to parse proxy URL: %ls (rc=%d)\n", proxyUrl.raw(), rc));
240 return false;
241 }
242 char *pszHost = RTUriParsedAuthorityHost(pcszProxyUrl, &Parsed);
243 if (!pszHost)
244 {
245 LogRel(("CLOUD-NET: Failed to get proxy host name from proxy URL: %s\n", pcszProxyUrl));
246 return false;
247 }
248 strProxyHost = pszHost;
249 RTStrFree(pszHost);
250 char *pszScheme = RTUriParsedScheme(pcszProxyUrl, &Parsed);
251 if (!pszScheme)
252 {
253 LogRel(("CLOUD-NET: Failed to get proxy scheme from proxy URL: %s\n", pcszProxyUrl));
254 return false;
255 }
256 strProxyType = Utf8Str(pszScheme).toUpper();
257 RTStrFree(pszScheme);
258 uint32_t uProxyPort = RTUriParsedAuthorityPort(pcszProxyUrl, &Parsed);
259 if (uProxyPort == UINT32_MAX)
260 if (!pszScheme)
261 {
262 LogRel(("CLOUD-NET: Failed to get proxy port from proxy URL: %s\n", pcszProxyUrl));
263 return false;
264 }
265 strProxyPort = BstrFmt("%d", uProxyPort);
266 }
267 else
268 {
269 /* Attempt to use system proxy settings (ProxyMode_System) */
270 RTHTTP hHttp;
271 int rc = RTHttpCreate(&hHttp);
272 if (RT_FAILURE(rc))
273 {
274 LogRel(("CLOUD-NET: Failed to create HTTP context (rc=%Rrc)\n", rc));
275 return false;
276 }
277 rc = RTHttpUseSystemProxySettings(hHttp);
278 if (RT_FAILURE(rc))
279 {
280 LogRel(("CLOUD-NET: Failed to use system proxy (rc=%Rrc)\n", rc));
281 RTHttpDestroy(hHttp);
282 return false;
283 }
284
285 RTHTTPPROXYINFO proxy;
286 rc = RTHttpQueryProxyInfoForUrl(hHttp, ("http://" + strIpAddr).c_str(), &proxy);
287 if (RT_FAILURE(rc))
288 {
289 LogRel(("CLOUD-NET: Failed to get proxy for %s (rc=%Rrc)\n", strIpAddr.c_str(), rc));
290 RTHttpDestroy(hHttp);
291 return false;
292 }
293 switch (proxy.enmProxyType)
294 {
295 case RTHTTPPROXYTYPE_NOPROXY:
296 RTHttpFreeProxyInfo(&proxy);
297 RTHttpDestroy(hHttp);
298 return false;
299 case RTHTTPPROXYTYPE_HTTP:
300 strProxyType = "HTTP";
301 break;
302 case RTHTTPPROXYTYPE_HTTPS:
303 strProxyType = "HTTPS";
304 break;
305 case RTHTTPPROXYTYPE_SOCKS4:
306 strProxyType = "SOCKS4";
307 break;
308 case RTHTTPPROXYTYPE_SOCKS5:
309 strProxyType = "SOCKS5";
310 break;
311 case RTHTTPPROXYTYPE_UNKNOWN:
312 case RTHTTPPROXYTYPE_INVALID:
313 case RTHTTPPROXYTYPE_END:
314 case RTHTTPPROXYTYPE_32BIT_HACK:
315 break;
316 }
317 AssertStmt(strProxyType.isNotEmpty(), LogRel(("CLOUD-NET: Unknown proxy type: %d\n", proxy.enmProxyType)));
318 strProxyHost = proxy.pszProxyHost;
319 if (proxy.uProxyPort != UINT32_MAX)
320 strProxyPort.printf("%d", proxy.uProxyPort);
321 RTHttpFreeProxyInfo(&proxy);
322 RTHttpDestroy(hHttp);
323 }
324 return true;
325#endif /* VBOX_WITH_PROXY_INFO */
326}
327#endif
328
329HRESULT stopCloudGateway(ComPtr<IVirtualBox> virtualBox, GatewayInfo& gateway)
330{
331 if (gateway.mGatewayInstanceId.isEmpty())
332 return S_OK;
333
334 LogRel(("CLOUD-NET: Terminating cloud gateway instance '%s'...\n", gateway.mGatewayInstanceId.c_str()));
335
336 HRESULT hrc = S_OK;
337 try {
338 CloudClient client(virtualBox, gateway.mCloudProvider, gateway.mCloudProfile);
339 client.stopCloudGateway(gateway);
340#if 0
341# ifdef DEBUG
342 char szKeyPath[RTPATH_MAX];
343
344 int rc = GetVBoxUserHomeDirectory(szKeyPath, sizeof(szKeyPath), false /* fCreateDir */);
345 if (RT_SUCCESS(rc))
346 {
347 rc = RTPathAppend(szKeyPath, sizeof(szKeyPath), "gateway-key.pem");
348 AssertRCReturn(rc, rc);
349 rc = RTFileDelete(szKeyPath);
350 if (RT_FAILURE(rc))
351 LogRel(("WARNING! Failed to delete private key %s with rc=%d\n", szKeyPath, rc));
352 }
353 else
354 LogRel(("WARNING! Failed to get VirtualBox user home directory with '%Rrc'\n", rc));
355# endif /* DEBUG */
356#endif
357 }
358 catch (CloudError e)
359 {
360 hrc = e.getRc();
361 LogRel(("CLOUD-NET: Failed to terminate cloud gateway instance (rc=%x).\n", hrc));
362 }
363 gateway.mGatewayInstanceId.setNull();
364 return hrc;
365}
366
367
368HRESULT generateKeys(GatewayInfo& gateway)
369{
370#ifndef VBOX_WITH_LIBSSH
371 RT_NOREF(gateway);
372 return E_NOTIMPL;
373#else /* VBOX_WITH_LIBSSH */
374 ssh_key single_use_key;
375 int rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 2048, &single_use_key);
376 if (rc != SSH_OK)
377 {
378 LogRel(("Failed to generate a key pair. rc = %d\n", rc));
379 return E_FAIL;
380 }
381
382 char *pstrKey = NULL;
383 rc = ssh_pki_export_privkey_base64(single_use_key, NULL, NULL, NULL, &pstrKey);
384 if (rc != SSH_OK)
385 {
386 LogRel(("Failed to export private key. rc = %d\n", rc));
387 return E_FAIL;
388 }
389 gateway.mPrivateSshKey = pstrKey;
390#if 0
391# ifdef DEBUG
392 char szConfigPath[RTPATH_MAX];
393
394 rc = GetVBoxUserHomeDirectory(szConfigPath, sizeof(szConfigPath), false /* fCreateDir */);
395 if (RT_SUCCESS(rc))
396 {
397 rc = RTPathAppend(szConfigPath, sizeof(szConfigPath), "gateway-key.pem");
398 AssertRCReturn(rc, rc);
399 rc = ssh_pki_export_privkey_file(single_use_key, NULL, NULL, NULL, szConfigPath);
400 if (rc != SSH_OK)
401 {
402 LogRel(("Failed to export private key to %s with rc=%d\n", szConfigPath, rc));
403 return E_FAIL;
404 }
405# ifndef RT_OS_WINDOWS
406 rc = RTPathSetMode(szConfigPath, RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR); /* Satisfy ssh client */
407 AssertRCReturn(rc, rc);
408# endif
409 }
410 else
411 {
412 LogRel(("Failed to get VirtualBox user home directory with '%Rrc'\n", rc));
413 return E_FAIL;
414 }
415# endif /* DEBUG */
416#endif
417 ssh_string_free_char(pstrKey);
418 pstrKey = NULL;
419 rc = ssh_pki_export_pubkey_base64(single_use_key, &pstrKey);
420 if (rc != SSH_OK)
421 {
422 LogRel(("Failed to export public key. rc = %d\n", rc));
423 return E_FAIL;
424 }
425 gateway.mPublicSshKey = Utf8StrFmt("ssh-rsa %s single-use-key", pstrKey);
426 ssh_string_free_char(pstrKey);
427 ssh_key_free(single_use_key);
428
429 return S_OK;
430#endif /* VBOX_WITH_LIBSSH */
431}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use