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 }