Skip to content

Commit

Permalink
feat: Add OpenVPN Server support
Browse files Browse the repository at this point in the history
  • Loading branch information
vaerh committed May 3, 2023
1 parent a37f926 commit 6477fcd
Show file tree
Hide file tree
Showing 16 changed files with 988 additions and 0 deletions.
3 changes: 3 additions & 0 deletions examples/resources/routeros_interface_ovpn_server/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#The ID can be found via API or the terminal
#The command for the terminal is -> :put [/interface/ovpn-server get [print show-ids]]
terraform import routeros_interface_ovpn_server.user1 *29
5 changes: 5 additions & 0 deletions examples/resources/routeros_interface_ovpn_server/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
resource "routeros_interface_ovpn_server" "user1" {
name = "ovpn-in1"
user = "user1"
depends_on = [routeros_ovpn_server.server]
}
1 change: 1 addition & 0 deletions examples/resources/routeros_openvpn_server/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
terraform import routeros_openvpn_server.server .
52 changes: 52 additions & 0 deletions examples/resources/routeros_openvpn_server/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
resource "routeros_ip_pool" "ovpn-pool" {
name = "ovpn-pool"
ranges = ["192.168.77.2-192.168.77.254"]
}

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_server_crt" {
name = "OpenVPN-Server-Certificate"
common_name = "Mikrotik OpenVPN"
key_size = "prime256v1"
key_usage = ["digital-signature", "key-encipherment", "tls-server"]
sign {
ca = routeros_system_certificate.ovpn_ca.name
}
}

resource "routeros_ppp_profile" "test" {
name = "ovpn"
local_address = "192.168.77.1"
remote_address = "ovpn-pool"
use_upnp = "no"
}

resource "routeros_ppp_secret" "test" {
name = "user-test"
password = "123"
profile = routeros_ppp_profile.test.name
}

resource "routeros_ovpn_server" "server" {
enabled = true
certificate = routeros_system_certificate.ovpn_server_crt.name
auth = "sha256,sha512"
tls_version = "only-1.2"
default_profile = routeros_ppp_profile.test.name
}

# The resource should be created only after the OpenVPN server is enabled!
resource "routeros_interface_ovpn_server" "user1" {
name = "ovpn-in1"
user = "user1"
depends_on = [routeros_ovpn_server.server]
}
3 changes: 3 additions & 0 deletions examples/resources/routeros_ppp_profile/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#The ID can be found via API or the terminal
#The command for the terminal is -> :put [/ppp/profile get [print show-ids]]
terraform import routeros_ppp_profile.test *6
6 changes: 6 additions & 0 deletions examples/resources/routeros_ppp_profile/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
resource "routeros_ppp_profile" "test" {
name = "ovpn"
local_address = "192.168.77.1"
remote_address = "ovpn-pool"
use_upnp = "no"
}
3 changes: 3 additions & 0 deletions examples/resources/routeros_ppp_secret/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#The ID can be found via API or the terminal
#The command for the terminal is -> :put [/ppp/secret get [print show-ids]]
terraform import routeros_ppp_secret.test *6
5 changes: 5 additions & 0 deletions examples/resources/routeros_ppp_secret/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
resource "routeros_ppp_secret" "test" {
name = "user-test"
password = "123"
profile = "default"
}
8 changes: 8 additions & 0 deletions routeros/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ func Provider() *schema.Provider {
"routeros_interface_wireguard_peer": ResourceInterfaceWireguardPeer(),
"routeros_interface_list": ResourceInterfaceList(),
"routeros_interface_list_member": ResourceInterfaceListMember(),
"routeros_interface_ovpn_server": ResourceInterfaceOpenVPNServer(),

// Aliases for interface objects to retain compatibility between original and fork
"routeros_bridge": ResourceInterfaceBridge(),
Expand Down Expand Up @@ -143,6 +144,13 @@ func Provider() *schema.Provider {

// Routing tables
"routeros_routing_table": ResourceRoutingTable(),

// VPN
"routeros_ovpn_server": ResourceOpenVPNServer(),

// PPP
"routeros_ppp_profile": ResourcePPPProfile(),
"routeros_ppp_secret": ResourcePPPSecret(),
},
DataSourcesMap: map[string]*schema.Resource{
"routeros_interfaces": DatasourceInterfaces(),
Expand Down
67 changes: 67 additions & 0 deletions routeros/resource_interface_ovpn_server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package routeros

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

/*
{
".id": "*1D",
"comment": "comment",
"client-address": "172.18.0.2",
"disabled": "false",
"encoding": "BF-128-CBC/SHA256",
"mtu": "1500",
"name": "ovpn-in1",
"running": "true",
"uptime": "1m25s",
"user": "user1"
}
*/

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

"client_address": {
Type: schema.TypeString,
Computed: true,
Description: "The address of the remote side.",
},
KeyComment: PropCommentRw,
KeyDisabled: PropDisabledRw,
"encoding": {
Type: schema.TypeString,
Computed: true,
Description: "Encryption characteristics.",
},
KeyMtu: PropL2MtuRo,
KeyName: PropName("Interface name (Example: ovpn-in1)."),
KeyRunning: PropRunningRo,
"uptime": {
Type: schema.TypeString,
Computed: true,
Description: "Connection uptime.",
},
"user": {
Type: schema.TypeString,
Optional: true,
Description: "User name used for authentication.",
},
}

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

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

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

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

/*
{
"auth": "sha1,md5,sha256,sha512",
"certificate": "root-cert",
"cipher": "blowfish128,aes128-cbc",
"default-profile": "default",
"enable-tun-ipv6": "false",
"enabled": "true",
"ipv6-prefix-len": "64",
"keepalive-timeout": "60",
"mac-address": "FE:01:63:24:35:19",
"max-mtu": "1500",
"mode": "ip",
"netmask": "24",
"port": "1194",
"protocol": "tcp",
"redirect-gateway": "disabled",
"reneg-sec": "3600",
"require-client-certificate": "true",
"tls-version": "only-1.2",
"tun-server-ipv6": "::"
}
*/

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

"auth": {
Type: schema.TypeString,
Optional: true,
Default: "sha1,md5,sha256,sha512",
Description: "Authentication methods that the server will accept.",
ValidateDiagFunc: ValidationMultiValInSlice([]string{"md5", "sha1", "null", "sha256", "sha512"}, false, false),
},
"certificate": {
Type: schema.TypeString,
Required: true,
Description: "Name of the certificate that the OVPN server will use.",
},
"cipher": {
Type: schema.TypeString,
Optional: true,
Default: "blowfish128,aes128-cbc",
Description: "Allowed ciphers.",
ValidateDiagFunc: ValidationMultiValInSlice([]string{
"null", "aes128-cbc", "aes128-gcm", "aes192-cbc", "aes192-gcm", "aes256-cbc", "aes256-gcm", "blowfish128",
}, false, false),
},
"default_profile": {
Type: schema.TypeString,
Optional: true,
Default: "default",
Description: "Default profile to use.",
},
"enable_tun_ipv6": {
Type: schema.TypeBool,
Optional: true,
Description: "Specifies if IPv6 IP tunneling mode should be possible with this OVPN server.",
},
"enabled": {
Type: schema.TypeBool,
Optional: true,
Description: "Defines whether the OVPN server is enabled or not.",
},
"ipv6_prefix_len": {
Type: schema.TypeInt,
Optional: true,
Default: 64,
Description: "Length of IPv6 prefix for IPv6 address which will be used when generating OVPN interface " +
"on the server side.",
ValidateFunc: validation.IntBetween(1, 128),
},
"keepalive_timeout": {
Type: schema.TypeString,
Optional: true,
Default: "60",
Description: "Defines the time period (in seconds) after which the router is starting to send " +
"keepalive packets every second. If no traffic and no keepalive responses have come for " +
"that period of time (i.e. 2 * keepalive-timeout), not responding client is proclaimed " +
"disconnected",
DiffSuppressFunc: TimeEquall,
},
"mac_address": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Description: "Automatically generated MAC address of the server.",
},
"max_mtu": {
Type: schema.TypeInt,
Optional: true,
Default: 1500,
Description: "Maximum Transmission Unit. Max packet size that the OVPN interface will be able to send " +
"without packet fragmentation.",
ValidateFunc: validation.IntBetween(64, 65535),
},
"mode": {
Type: schema.TypeString,
Optional: true,
Default: "ip",
Description: "Layer3 or layer2 tunnel mode (alternatively tun, tap)",
ValidateFunc: validation.StringInSlice([]string{"ip", "ethernet"}, false),
},
"netmask": {
Type: schema.TypeInt,
Optional: true,
Default: 24,
Description: "Subnet mask to be applied to the client.",
ValidateFunc: validation.IntBetween(0, 32),
},
"port": {
Type: schema.TypeInt,
Optional: true,
Default: 1194,
Description: "Port to run the server on.",
ValidateFunc: validation.IntBetween(1, 65535),
},
"protocol": {
Type: schema.TypeString,
Optional: true,
Default: "tcp",
Description: "indicates the protocol to use when connecting with the remote endpoint.",
ValidateFunc: validation.StringInSlice([]string{"tcp", "udp"}, false),
},
"redirect_gateway": {
Type: schema.TypeString,
Optional: true,
Default: "",
Description: "Specifies what kind of routes the OVPN client must add to the routing table. def1 – Use " +
"this flag to override the default gateway by using 0.0.0.0/1 and 128.0.0.0/1 rather " +
"than 0.0.0.0/0. This has the benefit of overriding but not wiping out the original " +
"default gateway. disabled - Do not send redirect-gateway flags to the OVPN client. ipv6 " +
"- Redirect IPv6 routing into the tunnel on the client side. This works similarly to the " +
"def1 flag, that is, more specific IPv6 routes are added (2000::/4 and 3000::/4), " +
"covering the whole IPv6 unicast space.",
ValidateDiagFunc: ValidationMultiValInSlice([]string{"def1", "disabled", "ipv6"}, false, false),
},
"reneg_sec": {
Type: schema.TypeInt,
Optional: true,
Default: 3600,
Description: "Renegotiate data channel key after n seconds (default=3600).",
},
"require_client_certificate": {
Type: schema.TypeBool,
Optional: true,
Description: "If set to yes, then the server checks whether the client's certificate belongs to the " +
"same certificate chain.",
},
"tls_version": {
Type: schema.TypeString,
Optional: true,
Description: "Specifies which TLS versions to allow.",
ValidateFunc: validation.StringInSlice([]string{"any", "only-1.2"}, false),
},
"tun_server_ipv6": {
Type: schema.TypeString,
Optional: true,
Default: "::",
Description: "IPv6 prefix address which will be used when generating the OVPN interface on the server " +
"side.",
},
}

return &schema.Resource{
CreateContext: DefaultSystemCreate(resSchema),
ReadContext: DefaultSystemRead(resSchema),
UpdateContext: DefaultSystemUpdate(resSchema),
DeleteContext: DefaultSystemDelete(resSchema),

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

Schema: resSchema,
}
}
Loading

0 comments on commit 6477fcd

Please sign in to comment.