Skip to content

Commit

Permalink
feature(gateway): add VPNaaS support (#306)
Browse files Browse the repository at this point in the history
  • Loading branch information
ka-myl authored Mar 14, 2024
1 parent 014c955 commit ec1fe3b
Show file tree
Hide file tree
Showing 13 changed files with 1,897 additions and 8 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.idea
.vscode
/go.work
/go.work.sum
temp/
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ See updating [Changelog example here](https://keepachangelog.com/en/1.0.0/)

## [Unreleased]

### Added
- **Experimental**, Gateway: support for VPN feature. Note that VPN feature is currently in beta, you can learn more about it on the [product page](https://upcloud.com/resources/docs/networking#nat-and-vpn-gateways)

## [8.2.0]

### Added
Expand Down
136 changes: 134 additions & 2 deletions upcloud/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,23 @@ import "time"
// GatewayConfiguredStatus represents a desired status of the service
type GatewayConfiguredStatus string

// GatewayConfiguredStatus represents a current actual status of the service
// GatewayConfiguredStatus represents current, actual status of the service
type GatewayOperationalState string

// GatewayFeature represents a feature of the service
type GatewayFeature string

// GatewayTunnelOperationalState represents current, actual status of the tunnel
type GatewayTunnelOperationalState string

type (
GatewayConnectionType string
GatewayRouteType string
GatewayIPSecAuthType string
GatewayIPSecAlgorithm string
GatewayIPSecIntegrityAlgorithm string
)

const (
GatewayConfiguredStatusStarted GatewayConfiguredStatus = "started"
GatewayConfiguredStatusStopped GatewayConfiguredStatus = "stopped"
Expand All @@ -30,14 +41,55 @@ const (
GatewayOperationalStateDeleteLinkNetwork GatewayOperationalState = "delete-link-network"
GatewayOperationalStateDeleteService GatewayOperationalState = "delete-service"

// GatewayFeatureNAT is the network address translation (NAT) feature of the network gateway
GatewayTunnelOperationalStateUninitialized GatewayTunnelOperationalState = "uninitialized"
GatewayTunnelOperationalStateCreated GatewayTunnelOperationalState = "created"
GatewayTunnelOperationalStateConnecting GatewayTunnelOperationalState = "connecting"
GatewayTunnelOperationalStateEstabilished GatewayTunnelOperationalState = "established"
GatewayTunnelOperationalStateRekeying GatewayTunnelOperationalState = "rekeying"
GatewayTunnelOperationalStateRekeyed GatewayTunnelOperationalState = "rekeyed"
GatewayTunnelOperationalStateDeleting GatewayTunnelOperationalState = "deleting"
GatewayTunnelOperationalStateDestroying GatewayTunnelOperationalState = "destroying"
GatewayTunnelOperationalStateUnknown GatewayTunnelOperationalState = "unknown"

// GatewayFeatureNAT is a Network Address Translation (NAT) service that offers a way for cloud servers in SDN private networks to connect to the Internet through the public IP assigned to the network gateway service
GatewayFeatureNAT GatewayFeature = "nat"

// GatewayFeatureVPN is a Virtual Private Network (VPN) service used to establish an encrypted network connection when using public networks
// Please note that VPN feature is currently in beta. You can learn more about it on its [product page]
// Also note that VPN is available only in some of the gateway plans. To check which plans support VPN, you can use the GetGatewayPlans method.
//
// [product page]: https://upcloud.com/resources/docs/networking#nat-and-vpn-gateways
GatewayFeatureVPN GatewayFeature = "vpn"

GatewayConnectionTypeIPSec GatewayConnectionType = "ipsec"

GatewayRouteTypeStatic GatewayRouteType = "static"

GatewayTunnelIPSecAuthTypePSK GatewayIPSecAuthType = "psk"

GatewayIPSecAlgorithm_aes128gcm16 GatewayIPSecAlgorithm = "aes128gcm16"
GatewayIPSecAlgorithm_aes128gcm128 GatewayIPSecAlgorithm = "aes128gcm128"
GatewayIPSecAlgorithm_aes192gcm16 GatewayIPSecAlgorithm = "aes192gcm16"
GatewayIPSecAlgorithm_aes192gcm128 GatewayIPSecAlgorithm = "aes192gcm128"
GatewayIPSecAlgorithm_aes256gcm16 GatewayIPSecAlgorithm = "aes256gcm16"
GatewayIPSecAlgorithm_aes256gcm128 GatewayIPSecAlgorithm = "aes256gcm128"
GatewayIPSecAlgorithm_aes128 GatewayIPSecAlgorithm = "aes128"
GatewayIPSecAlgorithm_aes192 GatewayIPSecAlgorithm = "aes192"
GatewayIPSecAlgorithm_aes256 GatewayIPSecAlgorithm = "aes256"

GatewayIPSecIntegrityAlgorithm_aes128gmac GatewayIPSecIntegrityAlgorithm = "aes128gmac"
GatewayIPSecIntegrityAlgorithm_aes256gmac GatewayIPSecIntegrityAlgorithm = "aes256gmac"
GatewayIPSecIntegrityAlgorithm_sha1 GatewayIPSecIntegrityAlgorithm = "sha1"
GatewayIPSecIntegrityAlgorithm_sha256 GatewayIPSecIntegrityAlgorithm = "sha256"
GatewayIPSecIntegrityAlgorithm_sha384 GatewayIPSecIntegrityAlgorithm = "sha384"
GatewayIPSecIntegrityAlgorithm_sha512 GatewayIPSecIntegrityAlgorithm = "sha512"
)

type Gateway struct {
UUID string `json:"uuid,omitempty"`
Name string `json:"name,omitempty"`
Zone string `json:"zone,omitempty"`
Plan string `json:"plan,omitempty"`
Labels []Label `json:"labels,omitempty"`
ConfiguredStatus GatewayConfiguredStatus `json:"configured_status,omitempty"`
OperationalState GatewayOperationalState `json:"operational_state,omitempty"`
Expand All @@ -46,6 +98,7 @@ type Gateway struct {
CreatedAt time.Time `json:"created_at,omitempty"`
UpdatedAt time.Time `json:"updated_at,omitempty"`
Addresses []GatewayAddress `json:"addresses,omitempty"`
Connections []GatewayConnection `json:"connections,omitempty"`
}

type GatewayAddress struct {
Expand All @@ -57,3 +110,82 @@ type GatewayRouter struct {
CreatedAt time.Time `json:"created_at,omitempty"`
UUID string `json:"uuid,omitempty"`
}

type GatewayConnection struct {
Name string `json:"name,omitempty"`
Type GatewayConnectionType `json:"type,omitempty"`
LocalRoutes []GatewayRoute `json:"local_routes,omitempty"`
RemoteRoutes []GatewayRoute `json:"remote_routes,omitempty"`
Tunnels []GatewayTunnel `json:"tunnels,omitempty"`
CreatedAt time.Time `json:"created_at,omitempty"`
UpdatedAt time.Time `json:"updated_at,omitempty"`
}

type GatewayRoute struct {
Name string `json:"name,omitempty"`
StaticNetwork string `json:"static_network,omitempty"`
Type GatewayRouteType `json:"type,omitempty"`
}

type GatewayTunnel struct {
Name string `json:"name,omitempty"`
LocalAddress GatewayTunnelLocalAddress `json:"local_address,omitempty"`
RemoteAddress GatewayTunnelRemoteAddress `json:"remote_address,omitempty"`
IPSec GatewayTunnelIPSec `json:"ipsec,omitempty"`
OperationalState GatewayTunnelOperationalState `json:"operational_state,omitempty"`
CreatedAt time.Time `json:"created_at,omitempty"`
UpdatedAt time.Time `json:"updated_at,omitempty"`
}

type GatewayTunnelLocalAddress struct {
// Name of the UpCloud gateway address; should correspond to the name of one of the gateway address structs
Name string `json:"name,omitempty"`
}

type GatewayTunnelRemoteAddress struct {
// Address is a remote peer address VPN will connect to; must be global non-private unicast IP address.
Address string `json:"address,omitempty"`
}

type GatewayTunnelIPSec struct {
// Tunnel IPSec authentication object
Authentication GatewayTunnelIPSecAuth `json:"authentication,omitempty"`
// IKE SA rekey time in seconds
RekeyTime int `json:"rekey_time,omitempty"`
// IKE child SA rekey time in seconds
ChildRekeyTime int `json:"child_rekey_time,omitempty"`
// Delay before sending Dead Peer Detection packets if no traffic is detected, in seconds
DPDDelay int `json:"dpd_delay,omitempty"`
// Timeout period for DPD reply before considering the peer to be dead, in seconds
DPDTimeout int `json:"dpd_timeout,omitempty"`
// Maximum IKE SA lifetime in seconds
IKELifetime int `json:"ike_lifetime,omitempty"`
// List of Phase 1: Proposal algorithms
Phase1Algorithms []GatewayIPSecAlgorithm `json:"phase1_algorithms,omitempty"`
// List of Phase 1 integrity algorithms
Phase1IntegrityAlgorithms []GatewayIPSecIntegrityAlgorithm `json:"phase1_integrity_algorithms,omitempty"`
// List of Phase 1 Diffie-Hellman group numbers
Phase1DHGroupNumbers []int `json:"phase1_dh_group_numbers,omitempty"`
// List of Phase 2: Security Association algorithms
Phase2Algorithms []GatewayIPSecAlgorithm `json:"phase2_algorithms,omitempty"`
// List of Phase 2 integrity algorithms
Phase2IntegrityAlgorithms []GatewayIPSecIntegrityAlgorithm `json:"phase2_integrity_algorithms,omitempty"`
// List of Phase 2 Diffie-Hellman group numbers
Phase2DHGroupNumbers []int `json:"phase2_dh_group_numbers,omitempty"`
}

type GatewayTunnelIPSecAuth struct {
Authentication GatewayIPSecAuthType `json:"authentication,omitempty"`
// PSK is a user-provided pre-shared key.
// Note that this field is only meant to be used when providing API with your pre-shared key; it will always be empty in API responses
PSK string `json:"psk,omitempty"`
}

type GatewayPlan struct {
Name string `json:"name,omitempty"`
PerGatewayBandwidthMbps int `json:"per_gateway_bandwidth_mbps,omitempty"`
PerGatewayMaxConnections int `json:"per_gateway_max_connections,omitempty"`
ServerNumber int `json:"server_number,omitempty"`
SupportedFeatures []GatewayFeature `json:"supported_features,omitempty"`
VPNTunnelAmount int `json:"vpn_tunnel_amount,omitempty"`
}
165 changes: 163 additions & 2 deletions upcloud/gateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package upcloud

import (
"testing"
"time"

"github.com/stretchr/testify/require"
)

func TestGateway(t *testing.T) {
Expand All @@ -18,7 +21,8 @@ func TestGateway(t *testing.T) {
"configured_status": "started",
"created_at": "2022-12-01T09:04:08.529138Z",
"features": [
"nat"
"nat",
"vpn"
],
"name": "example-gateway",
"operational_state": "running",
Expand All @@ -36,10 +40,66 @@ func TestGateway(t *testing.T) {
],
"updated_at": "2022-12-01T19:04:08.529138Z",
"uuid": "10c153e0-12e4-4dea-8748-4f34850ff76d",
"zone": "fi-hel1"
"zone": "fi-hel1",
"plan": "advanced",
"connections": [
{
"name": "example-connection",
"type": "ipsec",
"local_routes": [
{
"name": "upcloud-example-route",
"type": "static",
"static_network": "10.0.0.0/24"
}
],
"remote_routes": [
{
"name": "remote-example-route",
"type": "static",
"static_network": "10.0.1.0/24"
}
],
"tunnels": [
{
"name": "example-tunnel-1",
"local_address": {
"name": "public-ip-1"
},
"remote_address": {
"address": "100.10.0.111"
},
"ipsec": {
"authentication": {
"authentication": "psk"
},
"child_rekey_time": 1440,
"dpd_delay": 30,
"dpd_timeout": 120,
"ike_lifetime": 86400,
"phase1_algorithms": ["aes128gcm128", "aes256gcm128"],
"phase1_dh_group_numbers": [14, 16, 18, 19, 20, 21],
"phase1_integrity_algorithms": ["aes128gmac", "aes256gmac", "sha256", "sha384", "sha512"],
"phase2_algorithms": ["aes128gcm128", "aes256gcm128"],
"phase2_dh_group_numbers": [14, 16, 18, 19, 20, 21],
"phase2_integrity_algorithms": ["aes128gmac", "aes256gmac", "sha256", "sha384", "sha512"],
"rekey_time": 14400
},
"operational_state": "established",
"created_at": "2022-12-01T09:04:08.529138Z",
"updated_at": "2022-12-01T09:04:08.529138Z"
}
],
"created_at": "2022-12-01T09:04:08.529138Z",
"updated_at": "2022-12-01T09:04:08.529138Z"
}
]
}
`

timestamp, err := time.Parse(time.RFC3339, "2022-12-01T09:04:08.529138Z")
require.NoError(t, err)

gateway := &Gateway{
Addresses: []GatewayAddress{{
Address: "192.0.2.96",
Expand All @@ -49,6 +109,7 @@ func TestGateway(t *testing.T) {
CreatedAt: timeParse("2022-12-01T09:04:08.529138Z"),
Features: []GatewayFeature{
GatewayFeatureNAT,
GatewayFeatureVPN,
},
Name: "example-gateway",
OperationalState: "running",
Expand All @@ -64,7 +125,107 @@ func TestGateway(t *testing.T) {
UpdatedAt: timeParse("2022-12-01T19:04:08.529138Z"),
UUID: "10c153e0-12e4-4dea-8748-4f34850ff76d",
Zone: "fi-hel1",
Plan: "advanced",
Connections: []GatewayConnection{
{
Name: "example-connection",
Type: GatewayConnectionTypeIPSec,
LocalRoutes: []GatewayRoute{
{
Name: "upcloud-example-route",
Type: GatewayRouteTypeStatic,
StaticNetwork: "10.0.0.0/24",
},
},
RemoteRoutes: []GatewayRoute{
{
Name: "remote-example-route",
Type: GatewayRouteTypeStatic,
StaticNetwork: "10.0.1.0/24",
},
},
Tunnels: []GatewayTunnel{
{
Name: "example-tunnel-1",
LocalAddress: GatewayTunnelLocalAddress{
Name: "public-ip-1",
},
RemoteAddress: GatewayTunnelRemoteAddress{
Address: "100.10.0.111",
},
IPSec: GatewayTunnelIPSec{
Authentication: GatewayTunnelIPSecAuth{
Authentication: GatewayTunnelIPSecAuthTypePSK,
},
ChildRekeyTime: 1440,
DPDDelay: 30,
DPDTimeout: 120,
IKELifetime: 86400,
Phase1Algorithms: []GatewayIPSecAlgorithm{
GatewayIPSecAlgorithm_aes128gcm128,
GatewayIPSecAlgorithm_aes256gcm128,
},
Phase1DHGroupNumbers: []int{14, 16, 18, 19, 20, 21},
Phase1IntegrityAlgorithms: []GatewayIPSecIntegrityAlgorithm{
GatewayIPSecIntegrityAlgorithm_aes128gmac,
GatewayIPSecIntegrityAlgorithm_aes256gmac,
GatewayIPSecIntegrityAlgorithm_sha256,
GatewayIPSecIntegrityAlgorithm_sha384,
GatewayIPSecIntegrityAlgorithm_sha512,
},
Phase2Algorithms: []GatewayIPSecAlgorithm{
GatewayIPSecAlgorithm_aes128gcm128,
GatewayIPSecAlgorithm_aes256gcm128,
},
Phase2DHGroupNumbers: []int{14, 16, 18, 19, 20, 21},
Phase2IntegrityAlgorithms: []GatewayIPSecIntegrityAlgorithm{
GatewayIPSecIntegrityAlgorithm_aes128gmac,
GatewayIPSecIntegrityAlgorithm_aes256gmac,
GatewayIPSecIntegrityAlgorithm_sha256,
GatewayIPSecIntegrityAlgorithm_sha384,
GatewayIPSecIntegrityAlgorithm_sha512,
},
RekeyTime: 14400,
},
OperationalState: GatewayTunnelOperationalStateEstabilished,
CreatedAt: timestamp,
UpdatedAt: timestamp,
},
},
CreatedAt: timestamp,
UpdatedAt: timestamp,
},
},
}

testJSON(t, &Gateway{}, gateway, jsonStr)
}

func TestGatewayPlan(t *testing.T) {
t.Parallel()

jsonStr := `
{
"name": "advanced",
"per_gateway_bandwidth_mbps": 10000,
"per_gateway_max_connections": 100000,
"server_number": 2,
"supported_features": [
"nat",
"vpn"
],
"vpn_tunnel_amount": 10
}
`

plan := &GatewayPlan{
Name: "advanced",
PerGatewayBandwidthMbps: 10000,
PerGatewayMaxConnections: 100000,
ServerNumber: 2,
SupportedFeatures: []GatewayFeature{GatewayFeatureNAT, GatewayFeatureVPN},
VPNTunnelAmount: 10,
}

testJSON(t, &GatewayPlan{}, plan, jsonStr)
}
Loading

0 comments on commit ec1fe3b

Please sign in to comment.