diff --git a/lib/integrations/awsoidc/list_ec2ice.go b/lib/integrations/awsoidc/list_ec2ice.go index 49b05c97c10c9..2e4338f7568b1 100644 --- a/lib/integrations/awsoidc/list_ec2ice.go +++ b/lib/integrations/awsoidc/list_ec2ice.go @@ -56,33 +56,33 @@ func (req *ListEC2ICERequest) CheckAndSetDefaults() error { // EC2InstanceConnectEndpoint is the Teleport representation of an EC2 Instance Connect Endpoint type EC2InstanceConnectEndpoint struct { // Name is the endpoint name. - Name string + Name string `json:"name,omitempty"` // State is the endpoint state. // Known values: // create-in-progress | create-complete | create-failed | delete-in-progress | delete-complete | delete-failed - State string + State string `json:"state,omitempty"` // StateMessage contains a message describing the state of the EICE. // Can be empty. - StateMessage string + StateMessage string `json:"stateMessage,omitempty"` // DashboardLink is a URL to AWS Console where the user can see the EC2 Instance Connect Endpoint. - DashboardLink string + DashboardLink string `json:"dashboardLink,omitempty"` // SubnetID is the subnet used by the endpoint. // Please note that the Endpoint should be able to reach any subnet within the VPC. - SubnetID string + SubnetID string `json:"subnetId,omitempty"` } // ListEC2ICEResponse contains a page of AWS EC2 Instances as Teleport Servers. type ListEC2ICEResponse struct { // EC2ICEs contains the page of EC2 Instance Connect Endpoint. - EC2ICEs []EC2InstanceConnectEndpoint + EC2ICEs []EC2InstanceConnectEndpoint `json:"ec2InstanceConnectEndpoints,omitempty"` // NextToken is used for pagination. // If non-empty, it can be used to request the next page. - NextToken string + NextToken string `json:"nextToken,omitempty"` } // ListEC2ICEClient describes the required methods to List EC2 Instances using a 3rd Party API. diff --git a/lib/integrations/awsoidc/list_security_groups.go b/lib/integrations/awsoidc/list_security_groups.go index 27a616906befb..ff56f1c145fe6 100644 --- a/lib/integrations/awsoidc/list_security_groups.go +++ b/lib/integrations/awsoidc/list_security_groups.go @@ -53,23 +53,23 @@ func (req *ListSecurityGroupsRequest) CheckAndSetDefaults() error { type SecurityGroup struct { // Name is the Security Group name. // This is just a friendly name and should not be used for further API calls - Name string + Name string `json:"name"` // ID is the security group ID. // This is the value that should be used when doing further API calls. - ID string + ID string `json:"id"` // Description is a small description of the Security Group. // Might be empty. - Description string + Description string `json:"description"` // InboundRules describe the Security Group Inbound Rules. // The CIDR of each rule represents the source IP that the rule applies to. - InboundRules []SecurityGroupRule + InboundRules []SecurityGroupRule `json:"inboundRules"` // OutboundRules describe the Security Group Outbound Rules. // The CIDR of each rule represents the destination IP that the rule applies to. - OutboundRules []SecurityGroupRule + OutboundRules []SecurityGroupRule `json:"outboundRules"` } // SecurityGroupRule is a SecurityGroup role. @@ -79,34 +79,34 @@ type SecurityGroupRule struct { // If the rule applies to all protocols, the "all" value is used. // The IP protocol name ( tcp , udp , icmp , icmpv6 ) or number (see Protocol // Numbers (http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml)). - IPProtocol string + IPProtocol string `json:"ipProtocol"` // FromPort is the inclusive start of the Port range for the Rule. - FromPort int + FromPort int `json:"fromPort"` // ToPort is the inclusive end of the Port range for the Rule. - ToPort int + ToPort int `json:"toPort"` // CIDRs contains a list of IP ranges that this rule applies to and a description for the value. - CIDRs []CIDR + CIDRs []CIDR `json:"cidrs"` } // CIDR has a CIDR (IP Range) and a description for the value. type CIDR struct { // CIDR is the IP range using CIDR notation. - CIDR string + CIDR string `json:"cidr"` // Description contains a small text describing the CIDR. - Description string + Description string `json:"description"` } // ListSecurityGroupsResponse contains a page of SecurityGroups. type ListSecurityGroupsResponse struct { // SecurityGroups contains the page of VPC Security Groups. - SecurityGroups []SecurityGroup + SecurityGroups []SecurityGroup `json:"securityGroups"` // NextToken is used for pagination. // If non-empty, it can be used to request the next page. - NextToken string + NextToken string `json:"nextToken"` } // ListSecurityGroupsClient describes the required methods to List Security Groups a 3rd Party API. diff --git a/lib/web/integrations_awsoidc.go b/lib/web/integrations_awsoidc.go index bd8520bad2fe5..f0107535c94e5 100644 --- a/lib/web/integrations_awsoidc.go +++ b/lib/web/integrations_awsoidc.go @@ -331,7 +331,7 @@ func (h *Handler) awsOIDCListEC2(w http.ResponseWriter, r *http.Request, p httpr }, nil } -// awsOIDCListSecurityGroups returns a list of VPC Security Groups the ListSecurityGroups action of the AWS OIDC Integration. +// awsOIDCListSecurityGroups returns a list of VPC Security Groups using the ListSecurityGroups action of the AWS OIDC Integration. func (h *Handler) awsOIDCListSecurityGroups(w http.ResponseWriter, r *http.Request, p httprouter.Params, sctx *SessionContext, site reversetunnelclient.RemoteSite) (any, error) { ctx := r.Context() diff --git a/lib/web/servers.go b/lib/web/servers.go index 144e398fd7d85..082f82f3578e7 100644 --- a/lib/web/servers.go +++ b/lib/web/servers.go @@ -415,5 +415,15 @@ func (h *Handler) handleNodeCreate(w http.ResponseWriter, r *http.Request, p htt return nil, trace.Wrap(err) } - return server, nil + accessChecker, err := sctx.GetUserAccessChecker() + if err != nil { + return nil, trace.Wrap(err) + } + + uiServer, err := ui.MakeServer(site.GetName(), server, accessChecker) + if err != nil { + return nil, trace.Wrap(err) + } + + return uiServer, nil } diff --git a/lib/web/ui/integration.go b/lib/web/ui/integration.go index 084ec65e809f0..cb3fe5d1e3aeb 100644 --- a/lib/web/ui/integration.go +++ b/lib/web/ui/integration.go @@ -255,14 +255,14 @@ type AWSOIDCDeployEC2ICERequest struct { // Region is the AWS Region. Region string `json:"region"` // SubnetID is the subnet id for the EC2 Instance Connect Endpoint. - SubnetID string `json:"subnetID"` + SubnetID string `json:"subnetId"` // SecurityGroupIDs is the list of SecurityGroups to apply to the Endpoint. // If not specified, the Endpoint will receive the default SG for the Subnet's VPC. SecurityGroupIDs []string `json:"securityGroupIds"` } -// AWSOIDCDeployEC2ICEResponse contains a list of AWS Instance Connect Endpoints and a next token if more pages are available. +// AWSOIDCDeployEC2ICEResponse is the response after creating an AWS EC2 Instance Connect Endpoint. type AWSOIDCDeployEC2ICEResponse struct { - // Name is the endpoint Name that was created. + // Name is the name of the endpoint that was created. Name string `json:"name"` } diff --git a/lib/web/ui/server.go b/lib/web/ui/server.go index 3aa86370d89a3..5a855c34ecc37 100644 --- a/lib/web/ui/server.go +++ b/lib/web/ui/server.go @@ -57,7 +57,18 @@ type Server struct { // SSHLogins is the list of logins this user can use on this server SSHLogins []string `json:"sshLogins"` // AWS contains metadata for instances hosted in AWS. - AWS *types.AWSInfo `json:"aws,omitempty"` + AWS *AWSMetadata `json:"aws,omitempty"` +} + +// AWSMetadata describes the AWS metadata for instances hosted in AWS. +// This type is the same as types.AWSInfo but has json fields in camelCase form for the WebUI. +type AWSMetadata struct { + AccountID string `json:"accountId"` + InstanceID string `json:"instanceId"` + Region string `json:"region"` + VPCID string `json:"vpcId"` + Integration string `json:"integration"` + SubnetID string `json:"subnetId"` } // sortedLabels is a sort wrapper that sorts labels by name @@ -98,6 +109,17 @@ func MakeServer(clusterName string, server types.Server, accessChecker services. SSHLogins: serverLogins, } + if server.GetSubKind() == types.SubKindOpenSSHEICENode { + awsMetadata := server.GetAWSInfo() + uiServer.AWS = &AWSMetadata{ + AccountID: awsMetadata.AccountID, + InstanceID: awsMetadata.InstanceID, + Region: awsMetadata.Region, + Integration: awsMetadata.Integration, + SubnetID: awsMetadata.SubnetID, + } + } + return uiServer, nil } diff --git a/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.story.tsx b/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.story.tsx index 389f039ea9102..7326fac2f8013 100644 --- a/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.story.tsx +++ b/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.story.tsx @@ -16,7 +16,6 @@ import React from 'react'; -import { AwsRegionSelector } from './AwsRegionSelector'; import { DatabaseList } from './RdsDatabaseList'; import { CheckedAwsRdsDatabase } from './EnrollRdsDatabase'; @@ -24,33 +23,6 @@ export default { title: 'Teleport/Discover/Database/EnrollRds', }; -export const AwsRegionsSelectorDisabled = () => ( - null} - onRefresh={() => null} - disableSelector={true} - clear={() => null} - /> -); - -export const AwsRegionsSelectorEnabled = () => ( - null} - onRefresh={() => null} - disableSelector={false} - clear={() => null} - /> -); - -export const AwsRegionsSelectorRefreshEnabled = () => ( - null} - onRefresh={() => null} - disableSelector={false} - clear={() => null} - /> -); - export const RdsDatabaseList = () => ( {fetchDbAttempt.statusText} )} + + Select the AWS Region you would like to see databases for: + ( + <> + + Select the AWS Region you would like to see resources for: + + null} + onRefresh={() => null} + disableSelector={true} + clear={() => null} + /> + +); + +export const Enabled = () => ( + <> + + Select the AWS Region you would like to see resources for: + + null} + onRefresh={() => null} + disableSelector={false} + clear={() => null} + /> + +); diff --git a/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/AwsRegionSelector.tsx b/web/packages/teleport/src/Discover/Shared/AwsRegionSelector/AwsRegionSelector.tsx similarity index 93% rename from web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/AwsRegionSelector.tsx rename to web/packages/teleport/src/Discover/Shared/AwsRegionSelector/AwsRegionSelector.tsx index ed381a07ee2f4..7b61ed141e3ec 100644 --- a/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/AwsRegionSelector.tsx +++ b/web/packages/teleport/src/Discover/Shared/AwsRegionSelector/AwsRegionSelector.tsx @@ -15,7 +15,7 @@ */ import React, { useState } from 'react'; -import { Box, Text, Flex, ButtonSecondary, LabelInput } from 'design'; +import { Box, Flex, ButtonSecondary, LabelInput } from 'design'; import Select, { Option } from 'shared/components/Select'; import { Refresh as RefreshIcon } from 'design/Icon'; @@ -42,9 +42,6 @@ export function AwsRegionSelector({ return ( - - Select the AWS Region you would like to see databases for: - AWS Region diff --git a/web/packages/teleport/src/Discover/Shared/AwsRegionSelector/index.ts b/web/packages/teleport/src/Discover/Shared/AwsRegionSelector/index.ts new file mode 100644 index 0000000000000..de94e0d3db7d4 --- /dev/null +++ b/web/packages/teleport/src/Discover/Shared/AwsRegionSelector/index.ts @@ -0,0 +1,17 @@ +/** + * Copyright 2023 Gravitational, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { AwsRegionSelector } from './AwsRegionSelector'; diff --git a/web/packages/teleport/src/config.ts b/web/packages/teleport/src/config.ts index 13f865f13eddc..e0418b143cc2e 100644 --- a/web/packages/teleport/src/config.ts +++ b/web/packages/teleport/src/config.ts @@ -161,6 +161,7 @@ const cfg = { '/v1/webapi/sites/:clusterId/resources?searchAsRoles=:searchAsRoles?&limit=:limit?&startKey=:startKey?&kinds=:kinds?&query=:query?&search=:search?&sort=:sort?', nodesPath: '/v1/webapi/sites/:clusterId/nodes?searchAsRoles=:searchAsRoles?&limit=:limit?&startKey=:startKey?&query=:query?&search=:search?&sort=:sort?', + nodesPathNoParams: '/v1/webapi/sites/:clusterId/nodes', databaseServicesPath: `/v1/webapi/sites/:clusterId/databaseservices`, databaseIamPolicyPath: `/v1/webapi/sites/:clusterId/databases/:database/iam/policy`, @@ -240,6 +241,18 @@ const cfg = { '/v1/webapi/sites/:clusterId/integrations/aws-oidc/:name/databases', awsDeployTeleportServicePath: '/v1/webapi/sites/:clusterId/integrations/aws-oidc/:name/deployservice', + awsSecurityGroupsListPath: + '/v1/webapi/sites/:clusterId/integrations/aws-oidc/:name/securitygroups', + + ec2InstancesListPath: + '/v1/webapi/sites/:clusterId/integrations/aws-oidc/:name/ec2', + ec2InstanceConnectEndpointsListPath: + '/v1/webapi/sites/:clusterId/integrations/aws-oidc/:name/ec2ice', + // Returns a script that configures the required IAM permissions to enable the usage of EC2 Instance Connect Endpoint to access EC2 instances. + ec2InstanceConnectIAMConfigureScriptPath: + '/v1/webapi/scripts/integrations/configure/eice-iam.sh?awsRegion=:region&role=:awsOidcRoleArn', + ec2InstanceConnectDeployPath: + '/v1/webapi/sites/:site/integrations/aws-oidc/:name/deployec2ice', userGroupsListPath: '/v1/webapi/sites/:clusterId/user-groups?searchAsRoles=:searchAsRoles?&limit=:limit?&startKey=:startKey?&query=:query?&search=:search?&sort=:sort?', @@ -544,13 +557,17 @@ const cfg = { }); }, - getClusterNodesUrl(clusterId: string, params: UrlResourcesParams) { + getClusterNodesUrl(clusterId: string, params?: UrlResourcesParams) { return generateResourcePath(cfg.api.nodesPath, { clusterId, ...params, }); }, + getClusterNodesUrlNoParams(clusterId: string) { + return generatePath(cfg.api.nodesPathNoParams, { clusterId }); + }, + getDatabaseServicesUrl(clusterId: string) { return generatePath(cfg.api.databaseServicesPath, { clusterId, @@ -809,6 +826,53 @@ const cfg = { return generatePath(cfg.routes.requests, { requestId }); }, + getListEc2InstancesUrl(integrationName: string) { + const clusterId = cfg.proxyCluster; + + return generatePath(cfg.api.ec2InstancesListPath, { + clusterId, + name: integrationName, + }); + }, + + getListEc2InstanceConnectEndpointsUrl(integrationName: string) { + const clusterId = cfg.proxyCluster; + + return generatePath(cfg.api.ec2InstanceConnectEndpointsListPath, { + clusterId, + name: integrationName, + }); + }, + + getDeployEc2InstanceConnectEndpointUrl(integrationName: string) { + const clusterId = cfg.proxyCluster; + + return generatePath(cfg.api.ec2InstanceConnectDeployPath, { + clusterId, + name: integrationName, + }); + }, + + getListSecurityGroupsUrl(integrationName: string) { + const clusterId = cfg.proxyCluster; + + return generatePath(cfg.api.awsSecurityGroupsListPath, { + clusterId, + name: integrationName, + }); + }, + + getEc2InstanceConnectIAMConfigureScriptUrl( + params: UrlEc2InstanceIamConfigureScriptParams + ) { + return ( + cfg.baseUrl + + generatePath(cfg.api.ec2InstanceConnectIAMConfigureScriptPath, { + ...params, + }) + ); + }, + init(backendConfig = {}) { mergeDeep(this, backendConfig); }, @@ -912,4 +976,9 @@ export interface UrlDeployServiceIamConfigureScriptParams { taskRoleArn: string; } +export interface UrlEc2InstanceIamConfigureScriptParams { + region: Regions; + awsOidcRoleArn: string; +} + export default cfg; diff --git a/web/packages/teleport/src/services/integrations/integrations.ts b/web/packages/teleport/src/services/integrations/integrations.ts index a0b48122d0688..0557dec20296f 100644 --- a/web/packages/teleport/src/services/integrations/integrations.ts +++ b/web/packages/teleport/src/services/integrations/integrations.ts @@ -17,6 +17,8 @@ import api from 'teleport/services/api'; import cfg from 'teleport/config'; +import makeNode from '../nodes/makeNode'; + import { Integration, IntegrationCreateRequest, @@ -29,6 +31,16 @@ import { RdsEngineIdentifier, AwsOidcDeployServiceRequest, AwsOidcDeployServiceResponse, + ListEc2InstancesRequest, + ListEc2InstancesResponse, + Ec2InstanceConnectEndpoint, + ListEc2InstanceConnectEndpointsRequest, + ListEc2InstanceConnectEndpointsResponse, + ListAwsSecurityGroupsRequest, + ListAwsSecurityGroupsResponse, + DeployEc2InstanceConnectEndpointRequest, + DeployEc2InstanceConnectEndpointResponse, + SecurityGroup, } from './types'; export const integrationService = { @@ -122,6 +134,66 @@ export const integrationService = { ): Promise { return api.post(cfg.getAwsDeployTeleportServiceUrl(integrationName), req); }, + + // Returns a list of EC2 Instances using the ListEC2ICE action of the AWS OIDC Integration. + fetchAwsEc2Instances( + integrationName, + req: ListEc2InstancesRequest + ): Promise { + return api + .post(cfg.getListEc2InstancesUrl(integrationName), req) + .then(json => { + const instances = json?.servers ?? []; + return { + instances: instances.map(makeNode), + nextToken: json?.nextToken, + }; + }); + }, + + // Returns a list of EC2 Instance Connect Endpoints using the ListEC2ICE action of the AWS OIDC Integration. + fetchAwsEc2InstanceConnectEndpoints( + integrationName, + req: ListEc2InstanceConnectEndpointsRequest + ): Promise { + return api + .post(cfg.getListEc2InstanceConnectEndpointsUrl(integrationName), req) + .then(json => { + const endpoints = json?.ec2InstanceConnectEndpoints ?? []; + + return { + endpoints: endpoints.map(makeEc2InstanceConnectEndpoint), + nextToken: json?.nextToken, + }; + }); + }, + + // Deploys an EC2 Instance Connect Endpoint. + deployAwsEc2InstanceConnectEndpoint( + integrationName, + req: DeployEc2InstanceConnectEndpointRequest + ): Promise { + return api + .post(cfg.getDeployEc2InstanceConnectEndpointUrl(integrationName), req) + .then(json => ({ name: json?.name })); + }, + + // Returns a list of VPC Security Groups using the ListSecurityGroups action of the AWS OIDC Integration. + fetchSecurityGroups( + integrationName, + req: ListAwsSecurityGroupsRequest + ): Promise { + return api + .post(cfg.getListSecurityGroupsUrl(integrationName), req) + .then(json => { + const securityGroups = json?.securityGroups ?? []; + + return { + securityGroups: securityGroups.map(makeSecurityGroup), + nextToken: json?.nextToken, + }; + }); + }, }; export function makeIntegrations(json: any): Integration[] { @@ -165,3 +237,30 @@ export function makeAwsDatabase(json: any): AwsRdsDatabase { region: aws?.region, }; } + +function makeEc2InstanceConnectEndpoint(json: any): Ec2InstanceConnectEndpoint { + json = json ?? {}; + const { name, state, stateMessage, dashboardLink, subnetId } = json; + + return { + name, + state, + stateMessage, + dashboardLink, + subnetId, + }; +} + +function makeSecurityGroup(json: any): SecurityGroup { + json = json ?? {}; + + const { name, id, description = '', inboundRules, outboundRules } = json; + + return { + name, + id, + description, + inboundRules: inboundRules ?? [], + outboundRules: outboundRules ?? [], + }; +} diff --git a/web/packages/teleport/src/services/integrations/types.ts b/web/packages/teleport/src/services/integrations/types.ts index ebb6d61cbf8f6..6c637d4e27673 100644 --- a/web/packages/teleport/src/services/integrations/types.ts +++ b/web/packages/teleport/src/services/integrations/types.ts @@ -16,6 +16,8 @@ import { Label } from 'teleport/types'; +import { Node } from '../nodes'; + /** * type Integration v. type Plugin: * @@ -251,3 +253,103 @@ export type AwsOidcDeployServiceResponse = { // URL in Amazon Console. serviceDashboardUrl: string; }; + +export type ListEc2InstancesRequest = { + region: Regions; + nextToken?: string; +}; + +export type ListEc2InstancesResponse = { + // instances is the list of EC2 Instances. + instances: Node[]; + nextToken?: string; +}; + +export type ListEc2InstanceConnectEndpointsRequest = { + region: Regions; + // vpcId is the VPC to filter EC2 Instance Connect Endpoints. + vpcId: string; + nextToken?: string; +}; + +export type ListEc2InstanceConnectEndpointsResponse = { + // endpoints is the list of EC2 Instance Connect Endpoints. + endpoints: Ec2InstanceConnectEndpoint[]; + nextToken?: string; +}; + +export type Ec2InstanceConnectEndpoint = { + name: string; + // state is the current state of the EC2 Instance Connect Endpoint. + state: + | 'create-in-progress' + | 'create-complete' + | 'create-failed' + | 'delete-in-progress' + | 'delete-complete' + | 'delete-failed'; + // stateMessage is an optional message describing the state of the EICE, such as an error message. + stateMessage?: string; + // dashboardLink is a URL to AWS Console where the user can see the EC2 Instance Connect Endpoint. + dashboardLink: string; + // subnetID is the subnet used by the Endpoint. Please note that the Endpoint should be able to reach any subnet within the VPC. + subnetId: string; +}; + +export type DeployEc2InstanceConnectEndpointRequest = { + region: Regions; + // subnetID is the subnet id for the EC2 Instance Connect Endpoint. + subnetId: string; + // securityGroupIDs is the list of SecurityGroups to apply to the Endpoint. If not specified, the Endpoint will receive the default SG for the subnet's VPC. + securityGroupIds?: string[]; +}; + +export type DeployEc2InstanceConnectEndpointResponse = { + // name is the name of the EC2 Instance Connect Endpoint that was created. + name: string; +}; + +export type ListAwsSecurityGroupsRequest = { + // VPCID is the VPC to filter Security Groups. + vpcId: string; + nextToken?: string; +}; + +export type ListAwsSecurityGroupsResponse = { + securityGroups: SecurityGroup[]; + nextToken?: string; +}; + +export type SecurityGroup = { + // Name is the Security Group name. + // This is just a friendly name and should not be used for further API calls + name: string; + // ID is the security group ID. + // This is the value that should be used when doing further API calls. + id: string; + description: string; + // InboundRules describe the Security Group Inbound Rules. + // The CIDR of each rule represents the source IP that the rule applies to. + inboundRules: SecurityGroupRule[]; + // OutboundRules describe the Security Group Outbound Rules. + // The CIDR of each rule represents the destination IP that the rule applies to. + outboundRules: SecurityGroupRule[]; +}; + +export type SecurityGroupRule = { + // IPProtocol is the protocol used to describe the rule. + ipProtocol: string; + // FromPort is the inclusive start of the Port range for the Rule. + fromPort: string; + // ToPort is the inclusive end of the Port range for the Rule. + toPort: string; + // CIDRs contains a list of IP ranges that this rule applies to and a description for the value. + cidrs: Cidr[]; +}; + +export type Cidr = { + // CIDR is the IP range using CIDR notation. + cidr: string; + // Description contains a small text describing the CIDR. + description: string; +}; diff --git a/web/packages/teleport/src/services/nodes/makeNode.ts b/web/packages/teleport/src/services/nodes/makeNode.ts index 67ec4af9d63c2..0c5bfea629fc2 100644 --- a/web/packages/teleport/src/services/nodes/makeNode.ts +++ b/web/packages/teleport/src/services/nodes/makeNode.ts @@ -14,11 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { Node } from './types'; +import { Node, AwsMetadata } from './types'; export default function makeNode(json: any): Node { json = json ?? {}; - const { id, siteId, subKind, hostname, addr, tunnel, tags, sshLogins } = json; + const { id, siteId, subKind, hostname, addr, tunnel, tags, sshLogins, aws } = + json; return { kind: 'node', @@ -30,6 +31,21 @@ export default function makeNode(json: any): Node { addr, tunnel, sshLogins: sshLogins ?? [], + awsMetadata: aws ? makeAwsMetadata(aws) : undefined, + }; +} + +function makeAwsMetadata(json: any): AwsMetadata { + json = json ?? {}; + const { accountId, instanceId, region, vpcId, integration, subnetId } = json; + + return { + accountId, + instanceId, + region, + vpcId, + integration, + subnetId, }; } diff --git a/web/packages/teleport/src/services/nodes/nodes.ts b/web/packages/teleport/src/services/nodes/nodes.ts index fd3bc6fbf94ba..d7443637f3928 100644 --- a/web/packages/teleport/src/services/nodes/nodes.ts +++ b/web/packages/teleport/src/services/nodes/nodes.ts @@ -18,7 +18,7 @@ import api from 'teleport/services/api'; import cfg, { UrlResourcesParams } from 'teleport/config'; import { ResourcesResponse } from 'teleport/services/agents'; -import { Node } from './types'; +import { Node, CreateNodeRequest } from './types'; import makeNode from './makeNode'; class NodeService { @@ -39,6 +39,13 @@ class NodeService { }; }); } + + // Creates a Node. + createNode(clusterId: string, req: CreateNodeRequest): Promise { + return api + .post(cfg.getClusterNodesUrlNoParams(clusterId), req) + .then(makeNode); + } } export default NodeService; diff --git a/web/packages/teleport/src/services/nodes/types.ts b/web/packages/teleport/src/services/nodes/types.ts index 916ec15319929..254c528b1c9aa 100644 --- a/web/packages/teleport/src/services/nodes/types.ts +++ b/web/packages/teleport/src/services/nodes/types.ts @@ -25,9 +25,28 @@ export interface Node { tunnel: boolean; subKind?: string; sshLogins: string[]; + awsMetadata?: AwsMetadata; } export interface BashCommand { text: string; expires: string; } + +export type AwsMetadata = { + accountId: string; + instanceId: string; + region: string; + vpcId: string; + integration: string; + subnetId: string; +}; + +export type CreateNodeRequest = { + name: string; + subKind: string; + hostname: string; + addr: string; + labels?: ResourceLabel[]; + aws?: AwsMetadata; +};