From 658ba4e2264ed313ce7b8800b0d655333c0e30d0 Mon Sep 17 00:00:00 2001 From: Amos Paul Date: Thu, 26 May 2022 13:14:41 -0700 Subject: [PATCH] Updating API usage of IPsec tunnels Signed-off-by: Amos Paul --- magic_transit_ipsec_tunnel.go | 110 ++++++++++++++------ magic_transit_ipsec_tunnel_test.go | 153 ++++++++++++++++++++-------- magic_transit_tunnel_healthcheck.go | 8 ++ 3 files changed, 197 insertions(+), 74 deletions(-) create mode 100644 magic_transit_tunnel_healthcheck.go diff --git a/magic_transit_ipsec_tunnel.go b/magic_transit_ipsec_tunnel.go index 7ec5531ef4..75540f2042 100644 --- a/magic_transit_ipsec_tunnel.go +++ b/magic_transit_ipsec_tunnel.go @@ -16,23 +16,41 @@ const ( errMagicTransitIPsecTunnelNotDeleted = "When trying to delete IPsec tunnel, API returned deleted: false" ) +// MagicTransitIPsecTunnelPskMetadata contains metadata associated with PSK. +type MagicTransitIPsecTunnelPskMetadata struct { + LastGeneratedOn time.Time `json:"last_generated_on,omitempty"` +} + // MagicTransitIPsecTunnel contains information about an IPsec tunnel. type MagicTransitIPsecTunnel struct { - ID string `json:"id,omitempty"` - CreatedOn *time.Time `json:"created_on,omitempty"` - ModifiedOn *time.Time `json:"modified_on,omitempty"` - Name string `json:"name"` - CustomerEndpoint string `json:"customer_endpoint"` - CloudflareEndpoint string `json:"cloudflare_endpoint"` - InterfaceAddress string `json:"interface_address"` - Description string `json:"description,omitempty"` + ID string `json:"id,omitempty"` + CreatedOn *time.Time `json:"created_on,omitempty"` + ModifiedOn *time.Time `json:"modified_on,omitempty"` + Name string `json:"name"` + CustomerEndpoint string `json:"customer_endpoint"` + CloudflareEndpoint string `json:"cloudflare_endpoint"` + InterfaceAddress string `json:"interface_address"` + Description string `json:"description,omitempty"` + HealthCheck *MagicTransitTunnelHealthcheck `json:"health_check,omitempty"` +} + +// MagicTransitIPsecTunnelReq contains information needed to create IPsec tunnel. +type MagicTransitIPsecTunnelReq struct { + MagicTransitIPsecTunnel + Psk string `json:"psk,omitempty"` +} + +// MagicTransitIPsecTunnelResp contains information about a created IPsec tunnel. +type MagicTransitIPsecTunnelResp struct { + MagicTransitIPsecTunnel + PskMetadata *MagicTransitIPsecTunnelPskMetadata `json:"psk_metadata,omitempty"` } // ListMagicTransitIPsecTunnelsResponse contains a response including IPsec tunnels. type ListMagicTransitIPsecTunnelsResponse struct { Response Result struct { - IPsecTunnels []MagicTransitIPsecTunnel `json:"ipsec_tunnels"` + IPsecTunnels []MagicTransitIPsecTunnelResp `json:"ipsec_tunnels"` } `json:"result"` } @@ -40,21 +58,21 @@ type ListMagicTransitIPsecTunnelsResponse struct { type GetMagicTransitIPsecTunnelResponse struct { Response Result struct { - IPsecTunnel MagicTransitIPsecTunnel `json:"ipsec_tunnel"` + IPsecTunnel MagicTransitIPsecTunnelResp `json:"ipsec_tunnel"` } `json:"result"` } // CreateMagicTransitIPsecTunnelsRequest is an array of IPsec tunnels to create. type CreateMagicTransitIPsecTunnelsRequest struct { - IPsecTunnels []MagicTransitIPsecTunnel `json:"ipsec_tunnels"` + IPsecTunnels []MagicTransitIPsecTunnelReq `json:"ipsec_tunnels"` } // UpdateMagicTransitIPsecTunnelResponse contains a response after updating an IPsec Tunnel. type UpdateMagicTransitIPsecTunnelResponse struct { Response Result struct { - Modified bool `json:"modified"` - ModifiedIPsecTunnel MagicTransitIPsecTunnel `json:"modified_ipsec_tunnel"` + Modified bool `json:"modified"` + ModifiedIPsecTunnel MagicTransitIPsecTunnelResp `json:"modified_ipsec_tunnel"` } `json:"result"` } @@ -62,24 +80,33 @@ type UpdateMagicTransitIPsecTunnelResponse struct { type DeleteMagicTransitIPsecTunnelResponse struct { Response Result struct { - Deleted bool `json:"deleted"` - DeletedIPsecTunnel MagicTransitIPsecTunnel `json:"deleted_ipsec_tunnel"` + Deleted bool `json:"deleted"` + DeletedIPsecTunnel MagicTransitIPsecTunnelResp `json:"deleted_ipsec_tunnel"` + } `json:"result"` +} + +// GenerateMagicTransitIPsecTunnelPSKResponse contains a response after deleting an IPsec Tunnel. +type GenerateMagicTransitIPsecTunnelPSKResponse struct { + Response + Result struct { + Psk string `json:"psk"` + PskMetadata *MagicTransitIPsecTunnelPskMetadata `json:"psk_metadata"` } `json:"result"` } // ListMagicTransitIPsecTunnels lists all IPsec tunnels for a given account // // API reference: https://api.cloudflare.com/#magic-ipsec-tunnels-list-ipsec-tunnels -func (api *API) ListMagicTransitIPsecTunnels(ctx context.Context, accountID string) ([]MagicTransitIPsecTunnel, error) { +func (api *API) ListMagicTransitIPsecTunnels(ctx context.Context, accountID string) ([]MagicTransitIPsecTunnelResp, error) { uri := fmt.Sprintf("/accounts/%s/magic/ipsec_tunnels", accountID) res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil) if err != nil { - return []MagicTransitIPsecTunnel{}, err + return []MagicTransitIPsecTunnelResp{}, err } result := ListMagicTransitIPsecTunnelsResponse{} if err := json.Unmarshal(res, &result); err != nil { - return []MagicTransitIPsecTunnel{}, errors.Wrap(err, errUnmarshalError) + return []MagicTransitIPsecTunnelResp{}, errors.Wrap(err, errUnmarshalError) } return result.Result.IPsecTunnels, nil @@ -88,16 +115,16 @@ func (api *API) ListMagicTransitIPsecTunnels(ctx context.Context, accountID stri // GetMagicTransitIPsecTunnel returns zero or one IPsec tunnel // // API reference: https://api.cloudflare.com/#magic-ipsec-tunnels-ipsec-tunnel-details -func (api *API) GetMagicTransitIPsecTunnel(ctx context.Context, accountID string, id string) (MagicTransitIPsecTunnel, error) { +func (api *API) GetMagicTransitIPsecTunnel(ctx context.Context, accountID string, id string) (MagicTransitIPsecTunnelResp, error) { uri := fmt.Sprintf("/accounts/%s/magic/ipsec_tunnels/%s", accountID, id) res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil) if err != nil { - return MagicTransitIPsecTunnel{}, err + return MagicTransitIPsecTunnelResp{}, err } result := GetMagicTransitIPsecTunnelResponse{} if err := json.Unmarshal(res, &result); err != nil { - return MagicTransitIPsecTunnel{}, errors.Wrap(err, errUnmarshalError) + return MagicTransitIPsecTunnelResp{}, errors.Wrap(err, errUnmarshalError) } return result.Result.IPsecTunnel, nil @@ -106,19 +133,19 @@ func (api *API) GetMagicTransitIPsecTunnel(ctx context.Context, accountID string // CreateMagicTransitIPsecTunnels creates one or more IPsec tunnels // // API reference: https://api.cloudflare.com/#magic-ipsec-tunnels-create-ipsec-tunnels -func (api *API) CreateMagicTransitIPsecTunnels(ctx context.Context, accountID string, tunnels []MagicTransitIPsecTunnel) ([]MagicTransitIPsecTunnel, error) { +func (api *API) CreateMagicTransitIPsecTunnels(ctx context.Context, accountID string, tunnels []MagicTransitIPsecTunnelReq) ([]MagicTransitIPsecTunnelResp, error) { uri := fmt.Sprintf("/accounts/%s/magic/ipsec_tunnels", accountID) res, err := api.makeRequestContext(ctx, http.MethodPost, uri, CreateMagicTransitIPsecTunnelsRequest{ IPsecTunnels: tunnels, }) if err != nil { - return []MagicTransitIPsecTunnel{}, err + return []MagicTransitIPsecTunnelResp{}, err } result := ListMagicTransitIPsecTunnelsResponse{} if err := json.Unmarshal(res, &result); err != nil { - return []MagicTransitIPsecTunnel{}, errors.Wrap(err, errUnmarshalError) + return []MagicTransitIPsecTunnelResp{}, errors.Wrap(err, errUnmarshalError) } return result.Result.IPsecTunnels, nil @@ -127,21 +154,21 @@ func (api *API) CreateMagicTransitIPsecTunnels(ctx context.Context, accountID st // UpdateMagicTransitIPsecTunnel updates an IPsec tunnel // // API reference: https://api.cloudflare.com/#magic-ipsec-tunnels-update-ipsec-tunnel -func (api *API) UpdateMagicTransitIPsecTunnel(ctx context.Context, accountID string, id string, tunnel MagicTransitIPsecTunnel) (MagicTransitIPsecTunnel, error) { +func (api *API) UpdateMagicTransitIPsecTunnel(ctx context.Context, accountID string, id string, tunnel MagicTransitIPsecTunnelReq) (MagicTransitIPsecTunnelResp, error) { uri := fmt.Sprintf("/accounts/%s/magic/ipsec_tunnels/%s", accountID, id) res, err := api.makeRequestContext(ctx, http.MethodPut, uri, tunnel) if err != nil { - return MagicTransitIPsecTunnel{}, err + return MagicTransitIPsecTunnelResp{}, err } result := UpdateMagicTransitIPsecTunnelResponse{} if err := json.Unmarshal(res, &result); err != nil { - return MagicTransitIPsecTunnel{}, errors.Wrap(err, errUnmarshalError) + return MagicTransitIPsecTunnelResp{}, errors.Wrap(err, errUnmarshalError) } if !result.Result.Modified { - return MagicTransitIPsecTunnel{}, errors.New(errMagicTransitIPsecTunnelNotModified) + return MagicTransitIPsecTunnelResp{}, errors.New(errMagicTransitIPsecTunnelNotModified) } return result.Result.ModifiedIPsecTunnel, nil @@ -150,22 +177,41 @@ func (api *API) UpdateMagicTransitIPsecTunnel(ctx context.Context, accountID str // DeleteMagicTransitIPsecTunnel deletes an IPsec Tunnel // // API reference: https://api.cloudflare.com/#magic-ipsec-tunnels-delete-ipsec-tunnel -func (api *API) DeleteMagicTransitIPsecTunnel(ctx context.Context, accountID string, id string) (MagicTransitIPsecTunnel, error) { +func (api *API) DeleteMagicTransitIPsecTunnel(ctx context.Context, accountID string, id string) (MagicTransitIPsecTunnelResp, error) { uri := fmt.Sprintf("/accounts/%s/magic/ipsec_tunnels/%s", accountID, id) res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil) if err != nil { - return MagicTransitIPsecTunnel{}, err + return MagicTransitIPsecTunnelResp{}, err } result := DeleteMagicTransitIPsecTunnelResponse{} if err := json.Unmarshal(res, &result); err != nil { - return MagicTransitIPsecTunnel{}, errors.Wrap(err, errUnmarshalError) + return MagicTransitIPsecTunnelResp{}, errors.Wrap(err, errUnmarshalError) } if !result.Result.Deleted { - return MagicTransitIPsecTunnel{}, errors.New(errMagicTransitIPsecTunnelNotDeleted) + return MagicTransitIPsecTunnelResp{}, errors.New(errMagicTransitIPsecTunnelNotDeleted) } return result.Result.DeletedIPsecTunnel, nil } + +// GenerateMagicTransitIPsecTunnelPSK generates a pre shared key (psk) for an IPsec tunnel +// +// API reference: https://api.cloudflare.com/#magic-ipsec-tunnels-generate-pre-shared-key-psk-for-ipsec-tunnels +func (api *API) GenerateMagicTransitIPsecTunnelPSK(ctx context.Context, accountID string, id string) (string, *MagicTransitIPsecTunnelPskMetadata, error) { + uri := fmt.Sprintf("/accounts/%s/magic/ipsec_tunnels/%s/psk_generate", accountID, id) + res, err := api.makeRequestContext(ctx, http.MethodPost, uri, nil) + + if err != nil { + return "", nil, err + } + + result := GenerateMagicTransitIPsecTunnelPSKResponse{} + if err := json.Unmarshal(res, &result); err != nil { + return "", nil, errors.Wrap(err, errUnmarshalError) + } + + return result.Result.Psk, result.Result.PskMetadata, nil +} diff --git a/magic_transit_ipsec_tunnel_test.go b/magic_transit_ipsec_tunnel_test.go index 26b7b2556d..9041ab79b3 100644 --- a/magic_transit_ipsec_tunnel_test.go +++ b/magic_transit_ipsec_tunnel_test.go @@ -43,8 +43,8 @@ func TestListMagicTransitIPsecTunnels(t *testing.T) { createdOn, _ := time.Parse(time.RFC3339, "2017-06-14T00:00:00Z") modifiedOn, _ := time.Parse(time.RFC3339, "2017-06-14T05:20:00Z") - want := []MagicTransitIPsecTunnel{ - { + want := []MagicTransitIPsecTunnelResp{{ + MagicTransitIPsecTunnel: MagicTransitIPsecTunnel{ ID: "c4a7362d577a6c3019a474fd6f485821", CreatedOn: &createdOn, ModifiedOn: &modifiedOn, @@ -54,7 +54,7 @@ func TestListMagicTransitIPsecTunnels(t *testing.T) { InterfaceAddress: "192.0.2.0/31", Description: "Tunnel for ISP X", }, - } + }} actual, err := client.ListMagicTransitIPsecTunnels(context.Background(), testAccountID) if assert.NoError(t, err) { @@ -93,15 +93,17 @@ func TestGetMagicTransitIPsecTunnel(t *testing.T) { createdOn, _ := time.Parse(time.RFC3339, "2017-06-14T00:00:00Z") modifiedOn, _ := time.Parse(time.RFC3339, "2017-06-14T05:20:00Z") - want := MagicTransitIPsecTunnel{ - ID: "c4a7362d577a6c3019a474fd6f485821", - CreatedOn: &createdOn, - ModifiedOn: &modifiedOn, - Name: "IPsec_1", - CustomerEndpoint: "203.0.113.1", - CloudflareEndpoint: "203.0.113.2", - InterfaceAddress: "192.0.2.0/31", - Description: "Tunnel for ISP X", + want := MagicTransitIPsecTunnelResp{ + MagicTransitIPsecTunnel: MagicTransitIPsecTunnel{ + ID: "c4a7362d577a6c3019a474fd6f485821", + CreatedOn: &createdOn, + ModifiedOn: &modifiedOn, + Name: "IPsec_1", + CustomerEndpoint: "203.0.113.1", + CloudflareEndpoint: "203.0.113.2", + InterfaceAddress: "192.0.2.0/31", + Description: "Tunnel for ISP X", + }, } actual, err := client.GetMagicTransitIPsecTunnel(context.Background(), testAccountID, "c4a7362d577a6c3019a474fd6f485821") @@ -143,18 +145,33 @@ func TestCreateMagicTransitIPsecTunnels(t *testing.T) { createdOn, _ := time.Parse(time.RFC3339, "2017-06-14T00:00:00Z") modifiedOn, _ := time.Parse(time.RFC3339, "2017-06-14T05:20:00Z") - want := []MagicTransitIPsecTunnel{{ - ID: "c4a7362d577a6c3019a474fd6f485821", - CreatedOn: &createdOn, - ModifiedOn: &modifiedOn, - Name: "IPsec_1", - CustomerEndpoint: "203.0.113.1", - CloudflareEndpoint: "203.0.113.2", - InterfaceAddress: "192.0.2.0/31", - Description: "Tunnel for ISP X", + req := []MagicTransitIPsecTunnelReq{{ + MagicTransitIPsecTunnel: MagicTransitIPsecTunnel{ + ID: "c4a7362d577a6c3019a474fd6f485821", + CreatedOn: &createdOn, + ModifiedOn: &modifiedOn, + Name: "IPsec_1", + CustomerEndpoint: "203.0.113.1", + CloudflareEndpoint: "203.0.113.2", + InterfaceAddress: "192.0.2.0/31", + Description: "Tunnel for ISP X", + }, }} - actual, err := client.CreateMagicTransitIPsecTunnels(context.Background(), testAccountID, want) + want := []MagicTransitIPsecTunnelResp{{ + MagicTransitIPsecTunnel: MagicTransitIPsecTunnel{ + ID: "c4a7362d577a6c3019a474fd6f485821", + CreatedOn: &createdOn, + ModifiedOn: &modifiedOn, + Name: "IPsec_1", + CustomerEndpoint: "203.0.113.1", + CloudflareEndpoint: "203.0.113.2", + InterfaceAddress: "192.0.2.0/31", + Description: "Tunnel for ISP X", + }, + }} + + actual, err := client.CreateMagicTransitIPsecTunnels(context.Background(), testAccountID, req) if assert.NoError(t, err) { assert.Equal(t, want, actual) } @@ -192,18 +209,31 @@ func TestUpdateMagicTransitIPsecTunnel(t *testing.T) { createdOn, _ := time.Parse(time.RFC3339, "2017-06-14T00:00:00Z") modifiedOn, _ := time.Parse(time.RFC3339, "2017-06-14T05:20:00Z") - want := MagicTransitIPsecTunnel{ - ID: "c4a7362d577a6c3019a474fd6f485821", - CreatedOn: &createdOn, - ModifiedOn: &modifiedOn, - Name: "IPsec_1", - CustomerEndpoint: "203.0.113.1", - CloudflareEndpoint: "203.0.113.2", - InterfaceAddress: "192.0.2.0/31", - Description: "Tunnel for ISP X", - } + req := MagicTransitIPsecTunnelReq{ + MagicTransitIPsecTunnel: MagicTransitIPsecTunnel{ + ID: "c4a7362d577a6c3019a474fd6f485821", + CreatedOn: &createdOn, + ModifiedOn: &modifiedOn, + Name: "IPsec_1", + CustomerEndpoint: "203.0.113.1", + CloudflareEndpoint: "203.0.113.2", + InterfaceAddress: "192.0.2.0/31", + Description: "Tunnel for ISP X", + }} + + want := MagicTransitIPsecTunnelResp{ + MagicTransitIPsecTunnel: MagicTransitIPsecTunnel{ + ID: "c4a7362d577a6c3019a474fd6f485821", + CreatedOn: &createdOn, + ModifiedOn: &modifiedOn, + Name: "IPsec_1", + CustomerEndpoint: "203.0.113.1", + CloudflareEndpoint: "203.0.113.2", + InterfaceAddress: "192.0.2.0/31", + Description: "Tunnel for ISP X", + }} - actual, err := client.UpdateMagicTransitIPsecTunnel(context.Background(), testAccountID, "c4a7362d577a6c3019a474fd6f485821", want) + actual, err := client.UpdateMagicTransitIPsecTunnel(context.Background(), testAccountID, "c4a7362d577a6c3019a474fd6f485821", req) if assert.NoError(t, err) { assert.Equal(t, want, actual) } @@ -241,15 +271,17 @@ func TestDeleteMagicTransitIPsecTunnel(t *testing.T) { createdOn, _ := time.Parse(time.RFC3339, "2017-06-14T00:00:00Z") modifiedOn, _ := time.Parse(time.RFC3339, "2017-06-14T05:20:00Z") - want := MagicTransitIPsecTunnel{ - ID: "c4a7362d577a6c3019a474fd6f485821", - CreatedOn: &createdOn, - ModifiedOn: &modifiedOn, - Name: "IPsec_1", - CustomerEndpoint: "203.0.113.1", - CloudflareEndpoint: "203.0.113.2", - InterfaceAddress: "192.0.2.0/31", - Description: "Tunnel for ISP X", + want := MagicTransitIPsecTunnelResp{ + MagicTransitIPsecTunnel: MagicTransitIPsecTunnel{ + ID: "c4a7362d577a6c3019a474fd6f485821", + CreatedOn: &createdOn, + ModifiedOn: &modifiedOn, + Name: "IPsec_1", + CustomerEndpoint: "203.0.113.1", + CloudflareEndpoint: "203.0.113.2", + InterfaceAddress: "192.0.2.0/31", + Description: "Tunnel for ISP X", + }, } actual, err := client.DeleteMagicTransitIPsecTunnel(context.Background(), testAccountID, "c4a7362d577a6c3019a474fd6f485821") @@ -257,3 +289,40 @@ func TestDeleteMagicTransitIPsecTunnel(t *testing.T) { assert.Equal(t, want, actual) } } + +func TestDeleteMagicTransitIPsecTunnelGeneratePSK(t *testing.T) { + setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method) + w.Header().Set("content-type", "application/json") + fmt.Fprint(w, `{ + "success": true, + "errors": [], + "messages": [], + "result": { + "psk": "itworks", + "psk_metadata": { + "last_generated_on": "2017-06-14T05:20:00Z" + } + } + }`) + } + + mux.HandleFunc("/accounts/"+testAccountID+"/magic/ipsec_tunnels/c4a7362d577a6c3019a474fd6f485821/psk_generate", handler) + + lastGeneratedOn, _ := time.Parse(time.RFC3339, "2017-06-14T05:20:00Z") + + want := MagicTransitIPsecTunnelPskMetadata{ + LastGeneratedOn: lastGeneratedOn, + } + + want_psk := "itworks" + + psk, actual, err := client.GenerateMagicTransitIPsecTunnelPSK(context.Background(), testAccountID, "c4a7362d577a6c3019a474fd6f485821") + if assert.NoError(t, err) { + assert.Equal(t, want, *actual) + assert.Equal(t, want_psk, psk) + } +} diff --git a/magic_transit_tunnel_healthcheck.go b/magic_transit_tunnel_healthcheck.go new file mode 100644 index 0000000000..f79761e192 --- /dev/null +++ b/magic_transit_tunnel_healthcheck.go @@ -0,0 +1,8 @@ +package cloudflare + +// MagicTransitTunnelHealthcheck contains information about a tunnel health check. +type MagicTransitTunnelHealthcheck struct { + Enabled bool `json:"enabled"` + Target string `json:"target,omitempty"` + Type string `json:"type,omitempty"` +}