github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/nsxt_ipsec_vpn_tunnel.go (about)

     1  /*
     2   * Copyright 2021 VMware, Inc.  All rights reserved.  Licensed under the Apache v2 License.
     3   */
     4  
     5  package govcd
     6  
     7  import (
     8  	"fmt"
     9  	"net/url"
    10  
    11  	"github.com/vmware/go-vcloud-director/v2/types/v56"
    12  	"github.com/vmware/go-vcloud-director/v2/util"
    13  )
    14  
    15  // NsxtIpSecVpnTunnel offers site-to-site connectivity between an Edge Gateway and remote sites which also use NSX-T
    16  // Data Center or which have either third-party hardware routers or VPN gateways that support IPsec. Policy-based IPsec
    17  // VPN requires a VPN policy to be applied to packets to determine which traffic is to be protected by IPsec before
    18  // being passed through a VPN tunnel. This type of VPN is considered static because when a local network topology and
    19  // configuration change, the VPN policy settings must also be updated to accommodate the changes. NSX-T Data Center Edge
    20  // Gateways support split tunnel configuration, with IPsec traffic taking routing precedence. VMware Cloud Director
    21  // supports automatic route redistribution when you use IPsec VPN on an NSX-T edge gateway.
    22  type NsxtIpSecVpnTunnel struct {
    23  	NsxtIpSecVpn *types.NsxtIpSecVpnTunnel
    24  	client       *Client
    25  	// edgeGatewayId is stored here so that pointer receiver functions can embed edge gateway ID into path
    26  	edgeGatewayId string
    27  }
    28  
    29  // GetAllIpSecVpnTunnels returns all IPsec VPN Tunnel configurations
    30  func (egw *NsxtEdgeGateway) GetAllIpSecVpnTunnels(queryParameters url.Values) ([]*NsxtIpSecVpnTunnel, error) {
    31  	client := egw.client
    32  	endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointIpSecVpnTunnel
    33  	apiVersion, err := client.checkOpenApiEndpointCompatibility(endpoint)
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  
    38  	urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID))
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	typeResponses := []*types.NsxtIpSecVpnTunnel{{}}
    44  	err = client.OpenApiGetAllItems(apiVersion, urlRef, queryParameters, &typeResponses, nil)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	// Wrap all typeResponses into NsxtIpSecVpnTunnel types with client
    50  	wrappedResponses := make([]*NsxtIpSecVpnTunnel, len(typeResponses))
    51  	for sliceIndex := range typeResponses {
    52  		wrappedResponses[sliceIndex] = &NsxtIpSecVpnTunnel{
    53  			NsxtIpSecVpn:  typeResponses[sliceIndex],
    54  			client:        client,
    55  			edgeGatewayId: egw.EdgeGateway.ID,
    56  		}
    57  	}
    58  
    59  	return wrappedResponses, nil
    60  }
    61  
    62  // GetIpSecVpnTunnelById retrieves single IPsec VPN Tunnel by ID
    63  func (egw *NsxtEdgeGateway) GetIpSecVpnTunnelById(id string) (*NsxtIpSecVpnTunnel, error) {
    64  	if id == "" {
    65  		return nil, fmt.Errorf("canot find NSX-T IPsec VPN Tunnel configuration without ID")
    66  	}
    67  
    68  	client := egw.client
    69  	endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointIpSecVpnTunnel
    70  	minimumApiVersion, err := client.checkOpenApiEndpointCompatibility(endpoint)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID), id)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	returnObject := &NsxtIpSecVpnTunnel{
    81  		NsxtIpSecVpn:  &types.NsxtIpSecVpnTunnel{},
    82  		client:        client,
    83  		edgeGatewayId: egw.EdgeGateway.ID,
    84  	}
    85  
    86  	err = client.OpenApiGetItem(minimumApiVersion, urlRef, nil, returnObject.NsxtIpSecVpn, nil)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	return returnObject, nil
    92  }
    93  
    94  // GetIpSecVpnTunnelByName retrieves single IPsec VPN Tunnel by Name.
    95  //
    96  // Note. Name uniqueness is not enforced therefore it might exist a few IPsec VPN Tunnels with the same name.
    97  // An error will be returned in that case.
    98  func (egw *NsxtEdgeGateway) GetIpSecVpnTunnelByName(name string) (*NsxtIpSecVpnTunnel, error) {
    99  	if name == "" {
   100  		return nil, fmt.Errorf("canot find NSX-T IPsec VPN Tunnel configuration without Name")
   101  	}
   102  
   103  	allVpns, err := egw.GetAllIpSecVpnTunnels(nil)
   104  	if err != nil {
   105  		return nil, fmt.Errorf("error retrieving all NSX-T IPsec VPN Tunnel configurations: %s", err)
   106  	}
   107  
   108  	var allResults []*NsxtIpSecVpnTunnel
   109  
   110  	for _, vpnConfig := range allVpns {
   111  		if vpnConfig.NsxtIpSecVpn.Name == name {
   112  			allResults = append(allResults, vpnConfig)
   113  		}
   114  	}
   115  
   116  	if len(allResults) > 1 {
   117  		return nil, fmt.Errorf("error - found %d NSX-T IPsec VPN Tunnel configuratios with Name '%s'. Expected 1",
   118  			len(allResults), name)
   119  	}
   120  
   121  	if len(allResults) == 0 {
   122  		return nil, ErrorEntityNotFound
   123  	}
   124  
   125  	// Retrieving again the object by ID, because only it includes Pre-shared Key
   126  	return egw.GetIpSecVpnTunnelById(allResults[0].NsxtIpSecVpn.ID)
   127  }
   128  
   129  // CreateIpSecVpnTunnel creates IPsec VPN Tunnel and returns it
   130  func (egw *NsxtEdgeGateway) CreateIpSecVpnTunnel(ipSecVpnConfig *types.NsxtIpSecVpnTunnel) (*NsxtIpSecVpnTunnel, error) {
   131  	client := egw.client
   132  	endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointIpSecVpnTunnel
   133  	minimumApiVersion, err := client.checkOpenApiEndpointCompatibility(endpoint)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  
   138  	urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, egw.EdgeGateway.ID))
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	task, err := client.OpenApiPostItemAsync(minimumApiVersion, urlRef, nil, ipSecVpnConfig)
   144  	if err != nil {
   145  		return nil, fmt.Errorf("error creating NSX-T IPsec VPN Tunnel configuration: %s", err)
   146  	}
   147  
   148  	err = task.WaitTaskCompletion()
   149  	if err != nil {
   150  		return nil, fmt.Errorf("task failed while creating NSX-T IPsec VPN Tunnel configuration: %s", err)
   151  	}
   152  
   153  	// filtering even by Name is not supported on VCD side
   154  	allVpns, err := egw.GetAllIpSecVpnTunnels(nil)
   155  	if err != nil {
   156  		return nil, fmt.Errorf("error retrieving all NSX-T IPsec VPN Tunnel configuration after creation: %s", err)
   157  	}
   158  
   159  	for index, singleConfig := range allVpns {
   160  		if singleConfig.IsEqualTo(ipSecVpnConfig) {
   161  			// retrieve exact value by ID, because only this endpoint includes private key
   162  			ipSecVpn, err := egw.GetIpSecVpnTunnelById(allVpns[index].NsxtIpSecVpn.ID)
   163  			if err != nil {
   164  				return nil, fmt.Errorf("error retrieving NSX-T IPsec VPN Tunnel configuration: %s", err)
   165  			}
   166  
   167  			return ipSecVpn, nil
   168  		}
   169  	}
   170  
   171  	return nil, fmt.Errorf("error finding NSX-T IPsec VPN Tunnel configuration after creation: %s", ErrorEntityNotFound)
   172  }
   173  
   174  // Update updates NSX-T IPsec VPN Tunnel configuration with newly supplied data.
   175  func (ipSecVpn *NsxtIpSecVpnTunnel) Update(ipSecVpnConfig *types.NsxtIpSecVpnTunnel) (*NsxtIpSecVpnTunnel, error) {
   176  	client := ipSecVpn.client
   177  	endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointIpSecVpnTunnel
   178  	apiVersion, err := client.checkOpenApiEndpointCompatibility(endpoint)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  
   183  	if ipSecVpn.NsxtIpSecVpn.ID == "" {
   184  		return nil, fmt.Errorf("cannot update NSX-T IPsec VPN Tunnel configuration without ID")
   185  	}
   186  
   187  	urlRef, err := client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, ipSecVpn.edgeGatewayId), ipSecVpn.NsxtIpSecVpn.ID)
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  
   192  	returnObject := &NsxtIpSecVpnTunnel{
   193  		NsxtIpSecVpn:  &types.NsxtIpSecVpnTunnel{},
   194  		client:        client,
   195  		edgeGatewayId: ipSecVpn.edgeGatewayId,
   196  	}
   197  
   198  	err = client.OpenApiPutItem(apiVersion, urlRef, nil, ipSecVpnConfig, returnObject.NsxtIpSecVpn, nil)
   199  	if err != nil {
   200  		return nil, fmt.Errorf("error updating NSX-T IPsec VPN Tunnel configuration: %s", err)
   201  	}
   202  
   203  	return returnObject, nil
   204  }
   205  
   206  // Delete allows users to delete NSX-T IPsec VPN Tunnel
   207  func (ipSecVpn *NsxtIpSecVpnTunnel) Delete() error {
   208  	client := ipSecVpn.client
   209  	endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointIpSecVpnTunnel
   210  	minimumApiVersion, err := client.checkOpenApiEndpointCompatibility(endpoint)
   211  	if err != nil {
   212  		return err
   213  	}
   214  
   215  	if ipSecVpn.NsxtIpSecVpn.ID == "" {
   216  		return fmt.Errorf("cannot delete NSX-T IPsec VPN Tunnel configuration without ID")
   217  	}
   218  
   219  	urlRef, err := ipSecVpn.client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, ipSecVpn.edgeGatewayId), ipSecVpn.NsxtIpSecVpn.ID)
   220  	if err != nil {
   221  		return err
   222  	}
   223  
   224  	err = ipSecVpn.client.OpenApiDeleteItem(minimumApiVersion, urlRef, nil, nil)
   225  	if err != nil {
   226  		return fmt.Errorf("error deleting NSX-T IPsec VPN Tunnel configuration: %s", err)
   227  	}
   228  
   229  	return nil
   230  }
   231  
   232  // GetStatus returns status of IPsec VPN Tunnel.
   233  //
   234  // Note. This is not being immediately populated and may appear after some time depending on
   235  // NsxtIpSecVpnTunnelSecurityProfile.DpdConfiguration
   236  func (ipSecVpn *NsxtIpSecVpnTunnel) GetStatus() (*types.NsxtIpSecVpnTunnelStatus, error) {
   237  	client := ipSecVpn.client
   238  	endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointIpSecVpnTunnelStatus
   239  	minimumApiVersion, err := client.checkOpenApiEndpointCompatibility(endpoint)
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  
   244  	if ipSecVpn.NsxtIpSecVpn.ID == "" {
   245  		return nil, fmt.Errorf("cannot get NSX-T IPsec VPN Tunnel status without ID")
   246  	}
   247  
   248  	urlRef, err := ipSecVpn.client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, ipSecVpn.edgeGatewayId, ipSecVpn.NsxtIpSecVpn.ID))
   249  	if err != nil {
   250  		return nil, err
   251  	}
   252  
   253  	ipSecVpnTunnelStatus := &types.NsxtIpSecVpnTunnelStatus{}
   254  
   255  	err = ipSecVpn.client.OpenApiGetItem(minimumApiVersion, urlRef, nil, ipSecVpnTunnelStatus, nil)
   256  	if err != nil {
   257  		return nil, fmt.Errorf("error getting NSX-T IPsec VPN Tunnel status: %s", err)
   258  	}
   259  
   260  	return ipSecVpnTunnelStatus, nil
   261  }
   262  
   263  // UpdateTunnelConnectionProperties allows user to customize IPsec VPN Tunnel Security Profile when the default one
   264  // does not fit requirements.
   265  func (ipSecVpn *NsxtIpSecVpnTunnel) UpdateTunnelConnectionProperties(ipSecVpnTunnelConnectionProperties *types.NsxtIpSecVpnTunnelSecurityProfile) (*types.NsxtIpSecVpnTunnelSecurityProfile, error) {
   266  	client := ipSecVpn.client
   267  	endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointIpSecVpnTunnelConnectionProperties
   268  	minimumApiVersion, err := client.checkOpenApiEndpointCompatibility(endpoint)
   269  	if err != nil {
   270  		return nil, err
   271  	}
   272  
   273  	if ipSecVpn.NsxtIpSecVpn.ID == "" {
   274  		return nil, fmt.Errorf("cannot update NSX-T IPsec VPN Connection Properties without ID")
   275  	}
   276  
   277  	urlRef, err := ipSecVpn.client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, ipSecVpn.edgeGatewayId, ipSecVpn.NsxtIpSecVpn.ID))
   278  	if err != nil {
   279  		return nil, err
   280  	}
   281  
   282  	ipSecVpnTunnelProfile := &types.NsxtIpSecVpnTunnelSecurityProfile{}
   283  	err = ipSecVpn.client.OpenApiPutItem(minimumApiVersion, urlRef, nil, ipSecVpnTunnelConnectionProperties, ipSecVpnTunnelProfile, nil)
   284  	if err != nil {
   285  		return nil, fmt.Errorf("error updating NSX-T IPsec VPN Connection Properties: %s", err)
   286  	}
   287  
   288  	return ipSecVpnTunnelProfile, nil
   289  }
   290  
   291  // GetTunnelConnectionProperties retrieves IPsec VPN Tunnel Security Profile
   292  func (ipSecVpn *NsxtIpSecVpnTunnel) GetTunnelConnectionProperties() (*types.NsxtIpSecVpnTunnelSecurityProfile, error) {
   293  	client := ipSecVpn.client
   294  	endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointIpSecVpnTunnelConnectionProperties
   295  	minimumApiVersion, err := client.checkOpenApiEndpointCompatibility(endpoint)
   296  	if err != nil {
   297  		return nil, err
   298  	}
   299  
   300  	if ipSecVpn.NsxtIpSecVpn.ID == "" {
   301  		return nil, fmt.Errorf("cannot get NSX-T IPsec VPN Connection Properties without ID")
   302  	}
   303  
   304  	urlRef, err := ipSecVpn.client.OpenApiBuildEndpoint(fmt.Sprintf(endpoint, ipSecVpn.edgeGatewayId, ipSecVpn.NsxtIpSecVpn.ID))
   305  	if err != nil {
   306  		return nil, err
   307  	}
   308  
   309  	ipSecVpnTunnelProfile := &types.NsxtIpSecVpnTunnelSecurityProfile{}
   310  	err = ipSecVpn.client.OpenApiGetItem(minimumApiVersion, urlRef, nil, ipSecVpnTunnelProfile, nil)
   311  	if err != nil {
   312  		return nil, fmt.Errorf("error retrieving NSX-T IPsec VPN Connection Properties: %s", err)
   313  	}
   314  
   315  	return ipSecVpnTunnelProfile, nil
   316  }
   317  
   318  // IsEqualTo helps to find NSX-T IPsec VPN Tunnel Configuration
   319  // Combination of LocalAddress and RemoteAddress has to be unique (enforced by API). This is a list of fields compared:
   320  // * LocalEndpoint.LocalAddress
   321  // * RemoteEndpoint.RemoteAddress
   322  func (ipSecVpn *NsxtIpSecVpnTunnel) IsEqualTo(vpnConfig *types.NsxtIpSecVpnTunnel) bool {
   323  	return ipSetVpnRulesEqual(ipSecVpn.NsxtIpSecVpn, vpnConfig)
   324  }
   325  
   326  // ipSetVpnRulesEqual performs comparison of two NSX-T IPsec VPN Tunnels to ease lookup. This is a list of fields compared:
   327  // * LocalEndpoint.LocalAddress
   328  // * RemoteEndpoint.RemoteAddress
   329  func ipSetVpnRulesEqual(first, second *types.NsxtIpSecVpnTunnel) bool {
   330  	util.Logger.Println("comparing NSX-T IP Sev VPN configuration:")
   331  	util.Logger.Printf("%+v\n", first)
   332  	util.Logger.Println("against:")
   333  	util.Logger.Printf("%+v\n", second)
   334  
   335  	// These fields should be enough to cover uniqueness
   336  	if first.LocalEndpoint.LocalAddress == second.LocalEndpoint.LocalAddress &&
   337  		first.RemoteEndpoint.RemoteAddress == second.RemoteEndpoint.RemoteAddress {
   338  		return true
   339  	}
   340  
   341  	return false
   342  }