package cloudflare import ( "context" "errors" "fmt" "net/http" "time" "github.com/goccy/go-json" ) // Magic Transit IPsec Tunnel Error messages. const ( errMagicTransitIPsecTunnelNotModified = "When trying to modify IPsec tunnel, API returned modified: false" errMagicTransitIPsecTunnelNotDeleted = "When trying to delete IPsec tunnel, API returned deleted: false" ) type RemoteIdentities struct { HexID string `json:"hex_id"` FQDNID string `json:"fqdn_id"` UserID string `json:"user_id"` } // 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,omitempty"` CloudflareEndpoint string `json:"cloudflare_endpoint"` InterfaceAddress string `json:"interface_address"` Description string `json:"description,omitempty"` HealthCheck *MagicTransitTunnelHealthcheck `json:"health_check,omitempty"` Psk string `json:"psk,omitempty"` PskMetadata *MagicTransitIPsecTunnelPskMetadata `json:"psk_metadata,omitempty"` RemoteIdentities *RemoteIdentities `json:"remote_identities,omitempty"` AllowNullCipher bool `json:"allow_null_cipher"` ReplayProtection *bool `json:"replay_protection,omitempty"` } // ListMagicTransitIPsecTunnelsResponse contains a response including IPsec tunnels. type ListMagicTransitIPsecTunnelsResponse struct { Response Result struct { IPsecTunnels []MagicTransitIPsecTunnel `json:"ipsec_tunnels"` } `json:"result"` } // GetMagicTransitIPsecTunnelResponse contains a response including zero or one IPsec tunnels. type GetMagicTransitIPsecTunnelResponse struct { Response Result struct { IPsecTunnel MagicTransitIPsecTunnel `json:"ipsec_tunnel"` } `json:"result"` } // CreateMagicTransitIPsecTunnelsRequest is an array of IPsec tunnels to create. type CreateMagicTransitIPsecTunnelsRequest struct { IPsecTunnels []MagicTransitIPsecTunnel `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"` } `json:"result"` } // DeleteMagicTransitIPsecTunnelResponse contains a response after deleting an IPsec Tunnel. type DeleteMagicTransitIPsecTunnelResponse struct { Response Result struct { Deleted bool `json:"deleted"` DeletedIPsecTunnel MagicTransitIPsecTunnel `json:"deleted_ipsec_tunnel"` } `json:"result"` } // GenerateMagicTransitIPsecTunnelPSKResponse contains a response after generating 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) { uri := fmt.Sprintf("/accounts/%s/magic/ipsec_tunnels", accountID) res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil) if err != nil { return []MagicTransitIPsecTunnel{}, err } result := ListMagicTransitIPsecTunnelsResponse{} if err := json.Unmarshal(res, &result); err != nil { return []MagicTransitIPsecTunnel{}, fmt.Errorf("%s: %w", errUnmarshalError, err) } return result.Result.IPsecTunnels, nil } // 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) { 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 } result := GetMagicTransitIPsecTunnelResponse{} if err := json.Unmarshal(res, &result); err != nil { return MagicTransitIPsecTunnel{}, fmt.Errorf("%s: %w", errUnmarshalError, err) } return result.Result.IPsecTunnel, nil } // 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) { 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 } result := ListMagicTransitIPsecTunnelsResponse{} if err := json.Unmarshal(res, &result); err != nil { return []MagicTransitIPsecTunnel{}, fmt.Errorf("%s: %w", errUnmarshalError, err) } return result.Result.IPsecTunnels, nil } // 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) { 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 } result := UpdateMagicTransitIPsecTunnelResponse{} if err := json.Unmarshal(res, &result); err != nil { return MagicTransitIPsecTunnel{}, fmt.Errorf("%s: %w", errUnmarshalError, err) } if !result.Result.Modified { return MagicTransitIPsecTunnel{}, errors.New(errMagicTransitIPsecTunnelNotModified) } return result.Result.ModifiedIPsecTunnel, nil } // 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) { 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 } result := DeleteMagicTransitIPsecTunnelResponse{} if err := json.Unmarshal(res, &result); err != nil { return MagicTransitIPsecTunnel{}, fmt.Errorf("%s: %w", errUnmarshalError, err) } if !result.Result.Deleted { return MagicTransitIPsecTunnel{}, 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, fmt.Errorf("%s: %w", errUnmarshalError, err) } return result.Result.Psk, result.Result.PskMetadata, nil }