Skip to content

Commit

Permalink
feat(ovpn): Add routeros_interface_ovpn_client
Browse files Browse the repository at this point in the history
Closes #452
  • Loading branch information
vaerh committed May 17, 2024
1 parent c05d202 commit 85fd6be
Show file tree
Hide file tree
Showing 3 changed files with 241 additions and 0 deletions.
1 change: 1 addition & 0 deletions routeros/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ func Provider() *schema.Provider {
"routeros_interface_list": ResourceInterfaceList(),
"routeros_interface_list_member": ResourceInterfaceListMember(),
"routeros_interface_ovpn_server": ResourceInterfaceOpenVPNServer(),
"routeros_interface_ovpn_client": ResourceOpenVPNClient(),
"routeros_interface_veth": ResourceInterfaceVeth(),
"routeros_interface_bonding": ResourceInterfaceBonding(),
"routeros_interface_pppoe_client": ResourceInterfacePPPoEClient(),
Expand Down
173 changes: 173 additions & 0 deletions routeros/resource_ovpn_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package routeros

import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

/*
{
".id": "*5C",
"add-default-route": "false",
"auth": "sha1",
"certificate": "none",
"cipher": "blowfish128",
* "connect-to": "192.168.1.1",
"disabled": "false",
"hw-crypto": "false",
"mac-address": "02:E7:60:C6:40:EE",
"max-mtu": "1500",
"mode": "ip",
"name": "ovpn-out1",
"password": "",
"port": "1194",
"profile": "default",
"protocol": "tcp",
"route-nopull": "false",
"running": "false",
"tls-version": "any",
"use-peer-dns": "yes",
* "user": "aaa",
"verify-server-certificate": "false"
}
*/

// https://help.mikrotik.com/docs/display/ROS/OpenVPN
func ResourceOpenVPNClient() *schema.Resource {
resSchema := map[string]*schema.Schema{
MetaResourcePath: PropResourcePath("/interface/ovpn-client"),
MetaId: PropId(Id),

"add_default_route": {
Type: schema.TypeBool,
Optional: true,
Description: "Whether to add OVPN remote address as a default route.",
DiffSuppressFunc: AlwaysPresentNotUserProvided,
},
"auth": {
Type: schema.TypeString,
Optional: true,
Description: "Authentication methods that the server will accept.",
DiffSuppressFunc: AlwaysPresentNotUserProvided,
ValidateDiagFunc: ValidationMultiValInSlice([]string{"md5", "sha1", "null", "sha256", "sha512"}, false, false),
},
"certificate": {
Type: schema.TypeString,
Optional: true,
Description: "Name of the client certificate.",
DiffSuppressFunc: AlwaysPresentNotUserProvided,
},
"cipher": {
Type: schema.TypeString,
Optional: true,
Description: `Allowed ciphers.`,
DiffSuppressFunc: AlwaysPresentNotUserProvided,
ValidateDiagFunc: ValidationMultiValInSlice([]string{
"null", "aes128-cbc", "aes128-gcm", "aes192-cbc", "aes192-gcm", "aes256-cbc", "aes256-gcm", "blowfish128",
// Backward compatibility with ROS v7.7
"aes128", "aes192", "aes256",
}, false, false),
},
KeyComment: PropCommentRw,
"connect_to": {
Type: schema.TypeString,
Required: true,
Description: "Remote address of the OVPN server.",
},
KeyDisabled: PropDisabledRw,
"hw_crypto": {
Type: schema.TypeBool,
Computed: true,
Description: "",
},
KeyMacAddress: PropMacAddressRw(`Mac address of OVPN interface. Will be automatically generated if not specified.`, false),
"max_mtu": {
Type: schema.TypeInt,
Optional: true,
Description: "Maximum Transmission Unit. Max packet size that the OVPN interface will be able to send without packet fragmentation.",
DiffSuppressFunc: AlwaysPresentNotUserProvided,
ValidateFunc: validation.IntBetween(64, 65535),
},
"mode": {
Type: schema.TypeString,
Optional: true,
Description: "Layer3 or layer2 tunnel mode (alternatively tun, tap)",
DiffSuppressFunc: AlwaysPresentNotUserProvided,
ValidateFunc: validation.StringInSlice([]string{"ip", "ethernet"}, false),
},
KeyName: PropName("Descriptive name of the interface."),
"password": {
Type: schema.TypeString,
Optional: true,
Description: "Password used for authentication.",
Sensitive: true,
},
"port": {
Type: schema.TypeInt,
Optional: true,
Description: "Port to connect to.",
DiffSuppressFunc: AlwaysPresentNotUserProvided,
ValidateFunc: validation.IntBetween(1, 65535),
},
"profile": {
Type: schema.TypeString,
Optional: true,
Description: "Specifies which PPP profile configuration will be used when establishing the tunnel.",
DiffSuppressFunc: AlwaysPresentNotUserProvided,
},
"protocol": {
Type: schema.TypeString,
Optional: true,
Description: "Indicates the protocol to use when connecting with the remote endpoint.",
DiffSuppressFunc: AlwaysPresentNotUserProvided,
ValidateFunc: validation.StringInSlice([]string{"tcp", "udp"}, false),
},
"route_nopull": {
Type: schema.TypeBool,
Optional: true,
Description: "Specifies whether to allow the OVPN server to add routes to the OVPN client instance " +
"routing table.",
DiffSuppressFunc: AlwaysPresentNotUserProvided,
},
KeyRunning: PropRunningRo,
"tls_version": {
Type: schema.TypeString,
Optional: true,
Description: "Specifies which TLS versions to allow.",
DiffSuppressFunc: AlwaysPresentNotUserProvided,
ValidateFunc: validation.StringInSlice([]string{"any", "only-1.2"}, false),
},
"use_peer_dns": {
Type: schema.TypeBool,
Optional: true,
Description: "Whether to add DNS servers provided by the OVPN server to IP/DNS configuration.",
DiffSuppressFunc: AlwaysPresentNotUserProvided,
},
"user": {
Type: schema.TypeString,
Required: true,
Description: "User name used for authentication.",
DiffSuppressFunc: AlwaysPresentNotUserProvided,
},
"verify_server_certificate": {
Type: schema.TypeBool,
Optional: true,
Description: `Checks the certificates CN or SAN against the "connect-to" parameter. The IP or ` +
`hostname must be present in the server's certificate.`,
DiffSuppressFunc: AlwaysPresentNotUserProvided,
},
}

return &schema.Resource{
CreateContext: DefaultCreate(resSchema),
ReadContext: DefaultRead(resSchema),
UpdateContext: DefaultUpdate(resSchema),
DeleteContext: DefaultDelete(resSchema),

Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Schema: resSchema,
}
}
67 changes: 67 additions & 0 deletions routeros/resource_ovpn_client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package routeros

import (
"testing"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)

const testInterfaceOpenVPNClient = "routeros_interface_ovpn_client.ovpn-in1"

func TestAccOpenVPNClientTest_basic(t *testing.T) {
for _, name := range testNames {
t.Run(name, func(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
testSetTransportEnv(t, name)
},
ProviderFactories: testAccProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccOpenVPNClientConfig(),
Check: resource.ComposeTestCheckFunc(
testResourcePrimaryInstanceId(testInterfaceOpenVPNClient),
resource.TestCheckResourceAttr(testInterfaceOpenVPNClient, "name", "ovpn-in1"),
resource.TestCheckResourceAttr(testInterfaceOpenVPNClient, "user", "user1"),
resource.TestCheckResourceAttr(testInterfaceOpenVPNClient, "connect_to", "192.168.1.1"),
),
},
},
})

})
}
}

// Complex test for OpenVPN client resources.
func testAccOpenVPNClientConfig() string {
return providerConfig + `
resource "routeros_system_certificate" "ovpn_ca" {
name = "OpenVPN-Root-CA"
common_name = "OpenVPN Root CA"
key_size = "prime256v1"
key_usage = ["key-cert-sign", "crl-sign"]
trusted = true
sign {
}
}
resource "routeros_system_certificate" "ovpn_client_crt" {
name = "OpenVPN-Client-Certificate"
common_name = "Mikrotik OpenVPN Client"
key_size = "prime256v1"
key_usage = ["digital-signature", "key-encipherment", "tls-client"]
sign {
ca = routeros_system_certificate.ovpn_ca.name
}
}
resource "routeros_interface_ovpn_client" "ovpn-in1" {
name = "ovpn-in1"
user = "user1"
connect_to = "192.168.1.1"
certificate = routeros_system_certificate.ovpn_client_crt.name
}
`
}

0 comments on commit 85fd6be

Please sign in to comment.