diff options
Diffstat (limited to 'src/VBox/Main/src-client/CloudGateway.cpp')
-rw-r--r-- | src/VBox/Main/src-client/CloudGateway.cpp | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/src/VBox/Main/src-client/CloudGateway.cpp b/src/VBox/Main/src-client/CloudGateway.cpp new file mode 100644 index 00000000..60a468b1 --- /dev/null +++ b/src/VBox/Main/src-client/CloudGateway.cpp @@ -0,0 +1,309 @@ +/* $Id: CloudGateway.cpp $ */ +/** @file + * Implementation of local and cloud gateway management. + */ + +/* + * Copyright (C) 2019-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#define LOG_GROUP LOG_GROUP_MAIN_CONSOLE + +/* Make sure all the stdint.h macros are included - must come first! */ +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS +#endif +#ifndef __STDC_CONSTANT_MACROS +# define __STDC_CONSTANT_MACROS +#endif + +#include "LoggingNew.h" +#include "ApplianceImpl.h" +#include "CloudNetworkImpl.h" +#include "CloudGateway.h" + +#include <iprt/http.h> +#include <iprt/inifile.h> +#include <iprt/net.h> +#include <iprt/path.h> +#include <iprt/vfs.h> +#include <iprt/uri.h> +#ifdef DEBUG +#include <iprt/file.h> +#include <VBox/com/utils.h> +#endif + +#ifdef VBOX_WITH_LIBSSH +/* Prevent inclusion of Winsock2.h */ +#define _WINSOCK2API_ +#include <libssh/libssh.h> +#endif /* VBOX_WITH_LIBSSH */ + + +static HRESULT setMacAddress(const Utf8Str& str, RTMAC& mac) +{ + int rc = RTNetStrToMacAddr(str.c_str(), &mac); + if (RT_FAILURE(rc)) + { + LogRel(("CLOUD-NET: Invalid MAC address '%s'\n", str.c_str())); + return E_INVALIDARG; + } + return S_OK; +} + + +HRESULT GatewayInfo::setCloudMacAddress(const Utf8Str& mac) +{ + return setMacAddress(mac, mCloudMacAddress); +} + + +HRESULT GatewayInfo::setLocalMacAddress(const Utf8Str& mac) +{ + return setMacAddress(mac, mLocalMacAddress); +} + + +class CloudError +{ +public: + CloudError(HRESULT hrc, const Utf8Str& strText) : mHrc(hrc), mText(strText) {}; + HRESULT getRc() { return mHrc; }; + Utf8Str getText() { return mText; }; + +private: + HRESULT mHrc; + Utf8Str mText; +}; + + +static void handleErrors(HRESULT hrc, const char *pszFormat, ...) +{ + if (FAILED(hrc)) + { + va_list va; + va_start(va, pszFormat); + Utf8Str strError(pszFormat, va); + va_end(va); + LogRel(("CLOUD-NET: %s (rc=%x)\n", strError.c_str(), hrc)); + throw CloudError(hrc, strError); + } + +} + + +class CloudClient +{ +public: + CloudClient(ComPtr<IVirtualBox> virtualBox, const Bstr& strProvider, const Bstr& strProfile); + ~CloudClient() {}; + + void startCloudGateway(const ComPtr<ICloudNetwork> &network, GatewayInfo& gateway); + void stopCloudGateway(const GatewayInfo& gateway); + +private: + ComPtr<ICloudProviderManager> mManager; + ComPtr<ICloudProvider> mProvider; + ComPtr<ICloudProfile> mProfile; + ComPtr<ICloudClient> mClient; +}; + + +CloudClient::CloudClient(ComPtr<IVirtualBox> virtualBox, const Bstr& strProvider, const Bstr& strProfile) +{ + HRESULT hrc = virtualBox->COMGETTER(CloudProviderManager)(mManager.asOutParam()); + handleErrors(hrc, "Failed to obtain cloud provider manager object"); + hrc = mManager->GetProviderByShortName(strProvider.raw(), mProvider.asOutParam()); + handleErrors(hrc, "Failed to obtain cloud provider '%ls'", strProvider.raw()); + hrc = mProvider->GetProfileByName(strProfile.raw(), mProfile.asOutParam()); + handleErrors(hrc, "Failed to obtain cloud profile '%ls'", strProfile.raw()); + hrc = mProfile->CreateCloudClient(mClient.asOutParam()); + handleErrors(hrc, "Failed to create cloud client"); +} + + +void CloudClient::startCloudGateway(const ComPtr<ICloudNetwork> &network, GatewayInfo& gateway) +{ + ComPtr<IProgress> progress; + ComPtr<ICloudNetworkGatewayInfo> gatewayInfo; + HRESULT hrc = mClient->StartCloudNetworkGateway(network, Bstr(gateway.mPublicSshKey).raw(), + gatewayInfo.asOutParam(), progress.asOutParam()); + handleErrors(hrc, "Failed to launch compute instance"); + hrc = progress->WaitForCompletion(-1); + handleErrors(hrc, "Failed to launch compute instance (wait)"); + + Bstr instanceId; + hrc = gatewayInfo->COMGETTER(InstanceId)(instanceId.asOutParam()); + handleErrors(hrc, "Failed to get launched compute instance id"); + gateway.mGatewayInstanceId = instanceId; + + Bstr publicIP; + hrc = gatewayInfo->COMGETTER(PublicIP)(publicIP.asOutParam()); + handleErrors(hrc, "Failed to get cloud gateway public IP address"); + gateway.mCloudPublicIp = publicIP; + + Bstr secondaryPublicIP; + hrc = gatewayInfo->COMGETTER(SecondaryPublicIP)(secondaryPublicIP.asOutParam()); + handleErrors(hrc, "Failed to get cloud gateway secondary public IP address"); + gateway.mCloudSecondaryPublicIp = secondaryPublicIP; + + Bstr macAddress; + hrc = gatewayInfo->COMGETTER(MacAddress)(macAddress.asOutParam()); + handleErrors(hrc, "Failed to get cloud gateway public IP address"); + gateway.setCloudMacAddress(macAddress); +} + + +void CloudClient::stopCloudGateway(const GatewayInfo& gateway) +{ + ComPtr<IProgress> progress; + HRESULT hrc = mClient->TerminateInstance(Bstr(gateway.mGatewayInstanceId).raw(), progress.asOutParam()); + handleErrors(hrc, "Failed to terminate compute instance"); +#if 0 + /* Someday we may want to wait until the cloud gateway has terminated. */ + hrc = progress->WaitForCompletion(-1); + handleErrors(hrc, "Failed to terminate compute instance (wait)"); +#endif +} + + +HRESULT startCloudGateway(ComPtr<IVirtualBox> virtualBox, ComPtr<ICloudNetwork> network, GatewayInfo& gateway) +{ + HRESULT hrc = S_OK; + + try { + hrc = network->COMGETTER(Provider)(gateway.mCloudProvider.asOutParam()); + hrc = network->COMGETTER(Profile)(gateway.mCloudProfile.asOutParam()); + CloudClient client(virtualBox, gateway.mCloudProvider, gateway.mCloudProfile); + client.startCloudGateway(network, gateway); + } + catch (CloudError e) + { + hrc = e.getRc(); + } + + return hrc; +} + + +HRESULT stopCloudGateway(ComPtr<IVirtualBox> virtualBox, GatewayInfo& gateway) +{ + if (gateway.mGatewayInstanceId.isEmpty()) + return S_OK; + + LogRel(("CLOUD-NET: Terminating cloud gateway instance '%s'...\n", gateway.mGatewayInstanceId.c_str())); + + HRESULT hrc = S_OK; + try { + CloudClient client(virtualBox, gateway.mCloudProvider, gateway.mCloudProfile); + client.stopCloudGateway(gateway); +#if 0 +# ifdef DEBUG + char szKeyPath[RTPATH_MAX]; + + int rc = GetVBoxUserHomeDirectory(szKeyPath, sizeof(szKeyPath), false /* fCreateDir */); + if (RT_SUCCESS(rc)) + { + rc = RTPathAppend(szKeyPath, sizeof(szKeyPath), "gateway-key.pem"); + AssertRCReturn(rc, rc); + rc = RTFileDelete(szKeyPath); + if (RT_FAILURE(rc)) + LogRel(("WARNING! Failed to delete private key %s with rc=%d\n", szKeyPath, rc)); + } + else + LogRel(("WARNING! Failed to get VirtualBox user home directory with '%Rrc'\n", rc)); +# endif /* DEBUG */ +#endif + } + catch (CloudError e) + { + hrc = e.getRc(); + LogRel(("CLOUD-NET: Failed to terminate cloud gateway instance (rc=%x).\n", hrc)); + } + gateway.mGatewayInstanceId.setNull(); + return hrc; +} + + +HRESULT generateKeys(GatewayInfo& gateway) +{ +#ifndef VBOX_WITH_LIBSSH + RT_NOREF(gateway); + return E_NOTIMPL; +#else /* VBOX_WITH_LIBSSH */ + ssh_key single_use_key; + int rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 2048, &single_use_key); + if (rc != SSH_OK) + { + LogRel(("Failed to generate a key pair. rc = %d\n", rc)); + return E_FAIL; + } + + char *pstrKey = NULL; + rc = ssh_pki_export_privkey_base64(single_use_key, NULL, NULL, NULL, &pstrKey); + if (rc != SSH_OK) + { + LogRel(("Failed to export private key. rc = %d\n", rc)); + return E_FAIL; + } + gateway.mPrivateSshKey = pstrKey; +#if 0 +# ifdef DEBUG + char szConfigPath[RTPATH_MAX]; + + rc = GetVBoxUserHomeDirectory(szConfigPath, sizeof(szConfigPath), false /* fCreateDir */); + if (RT_SUCCESS(rc)) + { + rc = RTPathAppend(szConfigPath, sizeof(szConfigPath), "gateway-key.pem"); + AssertRCReturn(rc, rc); + rc = ssh_pki_export_privkey_file(single_use_key, NULL, NULL, NULL, szConfigPath); + if (rc != SSH_OK) + { + LogRel(("Failed to export private key to %s with rc=%d\n", szConfigPath, rc)); + return E_FAIL; + } +# ifndef RT_OS_WINDOWS + rc = RTPathSetMode(szConfigPath, RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR); /* Satisfy ssh client */ + AssertRCReturn(rc, rc); +# endif + } + else + { + LogRel(("Failed to get VirtualBox user home directory with '%Rrc'\n", rc)); + return E_FAIL; + } +# endif /* DEBUG */ +#endif + ssh_string_free_char(pstrKey); + pstrKey = NULL; + rc = ssh_pki_export_pubkey_base64(single_use_key, &pstrKey); + if (rc != SSH_OK) + { + LogRel(("Failed to export public key. rc = %d\n", rc)); + return E_FAIL; + } + gateway.mPublicSshKey = Utf8StrFmt("ssh-rsa %s single-use-key", pstrKey); + ssh_string_free_char(pstrKey); + ssh_key_free(single_use_key); + + return S_OK; +#endif /* VBOX_WITH_LIBSSH */ +} |