Skip to content

Commit

Permalink
Merge pull request #575 from terraform-routeros/vaerh/ipsec
Browse files Browse the repository at this point in the history
Closes #91
Closes #402
  • Loading branch information
vaerh authored Oct 7, 2024
2 parents cb4635a + 191f4de commit acc215d
Show file tree
Hide file tree
Showing 49 changed files with 2,044 additions and 185 deletions.
5 changes: 5 additions & 0 deletions examples/resources/routeros_ip_ipsec_identity/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#The ID can be found via API or the terminal
#The command for the terminal is -> :put [/ip/ipsec/identity get [print show-ids]]
terraform import routeros_ip_ipsec_identity.test *3
#Or you can import a resource using one of its attributes
terraform import routeros_ip_ipsec_identity.test "peer=NordVPN"
21 changes: 21 additions & 0 deletions examples/resources/routeros_ip_ipsec_identity/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
resource "routeros_ip_ipsec_mode_config" "test" {
name = "NordVPN"
responder = false
}

resource "routeros_ip_ipsec_peer" "test" {
address = "lv20.nordvpn.com"
exchange_mode = "ike2"
name = "NordVPN"
}

resource "routeros_ip_ipsec_identity" "test" {
auth-method = "eap"
certificate = ""
eap-methods = "eap-mschapv2"
generate-policy = "port-strict"
mode-config = routeros_ip_ipsec_mode_config.test.name
peer = routeros_ip_ipsec_peer.test.name
username = "[email protected]"
password = "secret"
}
5 changes: 5 additions & 0 deletions examples/resources/routeros_ip_ipsec_key/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#The ID can be found via API or the terminal
#The command for the terminal is -> :put [/ip/ipsec/key get [print show-ids]]
terraform import routeros_ip_ipsec_key.test *3
#Or you can import a resource using one of its attributes
terraform import routeros_ip_ipsec_key.test "name=test-key"
4 changes: 4 additions & 0 deletions examples/resources/routeros_ip_ipsec_key/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
resource "routeros_ip_ipsec_key" "test" {
name = "test-key"
key_size = 2048
}
5 changes: 5 additions & 0 deletions examples/resources/routeros_ip_ipsec_mode_config/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#The ID can be found via API or the terminal
#The command for the terminal is -> :put [/ip/ipsec/mode/config get [print show-ids]]
terraform import routeros_ip_ipsec_mode_config.test *3
#Or you can import a resource using one of its attributes
terraform import routeros_ip_ipsec_mode_config.test "address=1.2.3.4"
6 changes: 6 additions & 0 deletions examples/resources/routeros_ip_ipsec_mode_config/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
resource "routeros_ip_ipsec_mode_config" "test" {
name = "test-cfg"
address = "1.2.3.4"
split_include = ["0.0.0.0/0"]
split_dns = ["1.1.1.1"]
}
5 changes: 5 additions & 0 deletions examples/resources/routeros_ip_ipsec_peer/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#The ID can be found via API or the terminal
#The command for the terminal is -> :put [/ip/ipsec/peer get [print show-ids]]
terraform import routeros_ip_ipsec_peer.test *3
#Or you can import a resource using one of its attributes
terraform import routeros_ip_ipsec_peer.test "name=NordVPN"
5 changes: 5 additions & 0 deletions examples/resources/routeros_ip_ipsec_peer/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
resource "routeros_ip_ipsec_peer" "test" {
address = "lv20.nordvpn.com"
exchange_mode = "ike2"
name = "NordVPN"
}
5 changes: 5 additions & 0 deletions examples/resources/routeros_ip_ipsec_policy/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#The ID can be found via API or the terminal
#The command for the terminal is -> :put [/ip/ipsec/policy get [print show-ids]]
terraform import routeros_ip_ipsec_policy.test *3
#Or you can import a resource using one of its attributes
terraform import routeros_ip_ipsec_policy.test "group=test-group"
11 changes: 11 additions & 0 deletions examples/resources/routeros_ip_ipsec_policy/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
resource "routeros_ip_ipsec_policy_group" "group-for-policy" {
name = "test-group"
}

resource "routeros_ip_ipsec_policy" "policy" {
dst_address = "0.0.0.0/0"
group = routeros_ip_ipsec_policy_group.group-for-policy.name
proposal = "NordVPN"
src_address = "0.0.0.0/0"
template = true
}
5 changes: 5 additions & 0 deletions examples/resources/routeros_ip_ipsec_policy_group/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#The ID can be found via API or the terminal
#The command for the terminal is -> :put [/ip/ipsec/policy/group get [print show-ids]]
terraform import routeros_ip_ipsec_policy_group.test *3
#Or you can import a resource using one of its attributes
terraform import routeros_ip_ipsec_policy_group.test "name=test-group"
3 changes: 3 additions & 0 deletions examples/resources/routeros_ip_ipsec_policy_group/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
resource "routeros_ip_ipsec_policy_group" "test" {
name = "test-group"
}
5 changes: 5 additions & 0 deletions examples/resources/routeros_ip_ipsec_profile/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#The ID can be found via API or the terminal
#The command for the terminal is -> :put [/ip/ipsec/profile get [print show-ids]]
terraform import routeros_ip_ipsec_profile.test *3
#Or you can import a resource using one of its attributes
terraform import routeros_ip_ipsec_profile.test "name=test-profile"
7 changes: 7 additions & 0 deletions examples/resources/routeros_ip_ipsec_profile/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
resource "routeros_ip_ipsec_profile" "test" {
name = "test-profile"
hash_algorithm = "sha256"
enc_algorithm = ["aes-192", "aes-256"]
dh_group = ["ecp384", "ecp521"]
nat_traversal = false
}
5 changes: 5 additions & 0 deletions examples/resources/routeros_ip_ipsec_proposal/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#The ID can be found via API or the terminal
#The command for the terminal is -> :put [/ip/ipsec/proposal get [print show-ids]]
terraform import routeros_ip_ipsec_proposal.test *3
#Or you can import a resource using one of its attributes
terraform import routeros_ip_ipsec_proposal.test "name=NordVPN"
5 changes: 5 additions & 0 deletions examples/resources/routeros_ip_ipsec_proposal/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
resource "routeros_ip_ipsec_proposal" "test" {
name = "NordVPN"
pfs_group = "none"
lifetime = "45m10s"
}
1 change: 1 addition & 0 deletions examples/resources/routeros_ip_ipsec_settings/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
terraform import routeros_ip_ipsec_settings.test .
4 changes: 4 additions & 0 deletions examples/resources/routeros_ip_ipsec_settings/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
resource "routeros_ip_ipsec_settings" "test" {
xauth_use_radius = true
interim_update = "60s"
}
9 changes: 9 additions & 0 deletions routeros/mikrotik.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ func (m MikrotikItem) GetID(t IdType) string {
return ""
}

func (m *MikrotikItem) replace(swap any) {
switch t := swap.(type) {
case *MikrotikItem:
*m = *t
default:
panic("not the same type")
}
}

// KebabToSnake Convert Mikrotik JSON names to TF schema names: some-filed to some_field.
func KebabToSnake(name string) string {
res := []byte(name)
Expand Down
20 changes: 18 additions & 2 deletions routeros/mikrotik_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@ import (
)

type Client interface {
GetExtraParams() *ExtraParams
GetTransport() TransportType
SendRequest(method crudMethod, url *URL, item MikrotikItem, result interface{}) error
}

type crudMethod int

const (
crudCreate crudMethod = iota
crudUnknown crudMethod = iota
crudCreate
crudRead
crudUpdate
crudDelete
Expand All @@ -38,8 +40,13 @@ const (
crudMove
crudStart
crudStop
crudGenerateKey
)

type ExtraParams struct {
SuppressSysODelWarn bool
}

func NewClient(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {

tlsConf := tls.Config{
Expand Down Expand Up @@ -114,6 +121,9 @@ func NewClient(ctx context.Context, d *schema.ResourceData) (interface{}, diag.D
Username: d.Get("username").(string),
Password: d.Get("password").(string),
Transport: TransportAPI,
extra: &ExtraParams{
SuppressSysODelWarn: d.Get("suppress_syso_del_warn").(bool),
},
}

if useTLS {
Expand All @@ -138,10 +148,16 @@ func NewClient(ctx context.Context, d *schema.ResourceData) (interface{}, diag.D
Username: d.Get("username").(string),
Password: d.Get("password").(string),
Transport: TransportREST,
extra: &ExtraParams{
SuppressSysODelWarn: d.Get("suppress_syso_del_warn").(bool),
},
}

rest.Client = &http.Client{
Timeout: time.Minute,
// ... By default, CreateContext has a 20 minute timeout ...
// but MT REST API timeout is in 60 seconds for any operation.
// Make the timeout smaller so that the lifetime of the context is less than the lifetime of the session.
Timeout: 59 * time.Second,
Transport: &http.Transport{
TLSClientConfig: &tlsConf,
},
Expand Down
6 changes: 6 additions & 0 deletions routeros/mikrotik_client_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type ApiClient struct {
Username string
Password string
Transport TransportType
extra *ExtraParams
*routeros.Client
}

Expand All @@ -33,9 +34,14 @@ var (
crudMove: "/move",
crudStart: "/start",
crudStop: "/stop",
crudGenerateKey: "/generate-key",
}
)

func (c *ApiClient) GetExtraParams() *ExtraParams {
return c.extra
}

func (c *ApiClient) GetTransport() TransportType {
return c.Transport
}
Expand Down
49 changes: 47 additions & 2 deletions routeros/mikrotik_client_rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"io"
"net/http"
"reflect"
"strings"
)

Expand All @@ -16,6 +17,7 @@ type RestClient struct {
Username string
Password string
Transport TransportType
extra *ExtraParams
*http.Client
}

Expand All @@ -40,9 +42,14 @@ var (
crudMove: "POST",
crudStart: "POST",
crudStop: "POST",
crudGenerateKey: "POST",
}
)

func (c *RestClient) GetExtraParams() *ExtraParams {
return c.extra
}

func (c *RestClient) GetTransport() TransportType {
return c.Transport
}
Expand Down Expand Up @@ -97,19 +104,57 @@ func (c *RestClient) SendRequest(method crudMethod, url *URL, item MikrotikItem,

ColorizedDebug(c.ctx, "response body: "+string(body))

// Cast single return values to an array.
if !isJsonArray(body) {
body = append([]byte{'['}, append(body, []byte{']'}...)...)
}

// If the requested value is a slice, then parse the JSON directly into it.
var slice = new([]MikrotikItem)
if result != nil && isSlice(result) {
slice = result.(*[]MikrotikItem)
}

if len(body) != 0 && result != nil {
if err = json.Unmarshal(body, &result); err != nil {
if err = json.Unmarshal(body, &slice); err != nil {

if e, ok := err.(*json.SyntaxError); ok {
ColorizedDebug(c.ctx, fmt.Sprintf("json.Unmarshal(response body): syntax error at byte offset %d", e.Offset))

if err = json.Unmarshal(EscapeChars(body), &result); err != nil {
if err = json.Unmarshal(EscapeChars(body), &slice); err != nil {
return fmt.Errorf("json.Unmarshal(response body): %v", err)
}
} else {
return err
}
}
}

if result != nil && !isSlice(result) && len(*slice) > 0 {
// result.(*MikrotikItem).replace(&(*slice)[0])
for k, v := range (*slice)[0] {
(*result.(*MikrotikItem))[k] = v
}
}

return nil
}

// isSlice The function returns information whether the passed parameter is a slice.
// The incoming type is a variable or pointer.
func isSlice(i any) bool {
t := reflect.TypeOf(i)
if t.Kind() == reflect.Pointer {
t = t.Elem()
}
return t.Kind() == reflect.Slice
}

// isJsonArray The function returns information about the type of JSON response.
// Based on the response, we can cast MT's response to an array of values.
// After some time, we can say that it is easier to operate with an array of values,
// since MT can return '[]' which is not obvious in the process of creating a single resource.
func isJsonArray(b []byte) bool {
b = bytes.TrimLeft(b, " \t\r\n")
return len(b) > 0 && b[0] == '['
}
15 changes: 13 additions & 2 deletions routeros/mikrotik_crud.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package routeros

import (
"context"
"fmt"
)

Expand All @@ -14,16 +15,26 @@ var (

// https://help.mikrotik.com/docs/display/ROS/REST+API

func CreateItem(item MikrotikItem, resourcePath string, c Client) (MikrotikItem, error) {
func CreateItem(ctx context.Context, item MikrotikItem, resourcePath string, c Client) (MikrotikItem, error) {
if item == nil {
return nil, errEmptyItem
}
if resourcePath == "" {
return nil, errEmptyPath
}

var crud = crudCreate

if cm := ctxGetCrudMethod(ctx); cm != crudUnknown {
crud = cm
if c.GetTransport() == TransportREST {
// apiMethodName[crud] is CLI path
resourcePath += apiMethodName[crud]
}
}

res := MikrotikItem{}
err := c.SendRequest(crudCreate, &URL{Path: resourcePath}, item, &res)
err := c.SendRequest(crud, &URL{Path: resourcePath}, item, &res)

return res, err
}
Expand Down
20 changes: 19 additions & 1 deletion routeros/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ func Provider() *schema.Provider {
),
Description: "Whether to verify the SSL certificate or not.",
},
"suppress_syso_del_warn": {
Type: schema.TypeBool,
Optional: true,
DefaultFunc: schema.MultiEnvDefaultFunc(
[]string{"ROS_SUPPRESS_SYSO_DEL_WARN"},
false,
),
Description: "Suppress the system object deletion warning.",
},
},
ResourcesMap: map[string]*schema.Resource{

Expand Down Expand Up @@ -249,7 +258,16 @@ func Provider() *schema.Provider {
"routeros_routing_ospf_interface_template": ResourceRoutingOspfInterfaceTemplate(),

// VPN
"routeros_ovpn_server": ResourceOpenVPNServer(),
"routeros_ip_ipsec_identity": ResourceIpIpsecIdentity(),
"routeros_ip_ipsec_key": ResourceIpIpsecKey(),
"routeros_ip_ipsec_mode_config": ResourceIpIpsecModeConfig(),
"routeros_ip_ipsec_peer": ResourceIpIpsecPeer(),
"routeros_ip_ipsec_policy": ResourceIpIpsecPolicy(),
"routeros_ip_ipsec_policy_group": ResourceIpIpsecPolicyGroup(),
"routeros_ip_ipsec_profile": ResourceIpIpsecProfile(),
"routeros_ip_ipsec_proposal": ResourceIpIpsecProposal(),
"routeros_ip_ipsec_settings": ResourceIpIpsecSettings(),
"routeros_ovpn_server": ResourceOpenVPNServer(),

// PPP
"routeros_ppp_aaa": ResourcePppAaa(),
Expand Down
Loading

0 comments on commit acc215d

Please sign in to comment.