Hi there,
I’d like to share my complete solution in case someone will find it helpful.
I’m not sure it’s the most elegant way but it’s what I could achieve with my some what limited knowledge.
My setup:
- Raspberry Pi 4 running OH 3.0.1 on openhabian.
- An openVPN server running as a service on the same Pi (installed with PiVPN)
First I created two shell scripts inside /usr/bin/
:
-
startService.sh
for starting the service:
#!/bin/bash
sudo systemctl start openvpn.service
-
stopService.sh
for stopping the service:
#!/bin/bash
sudo systemctl stop openvpn.service
And added an execution permission for both of them:
sudo chmod +x /usr/bin/startService.sh
sudo chmod +x /usr/bin/stopService.sh
The next steps I’m not sure are necessary but I did them nevertheless. I remember that I had to do them in OH2 (on an openhabian box) in order to execute sudo
commands from rules, because the user running those commands would be openhab
and not openhabian
.
- Allow the user
openhab
to run sudo
commands:
sudo adduser openhab sudo
- Create a file with sudo commands that
openhab
can run without a password:
sudo visudo -f /etc/sudoers.d/OHAdminPermissions
- Add the following lines to the file:
# Allow openhab user to execute commands
openhab ALL=(ALL) NOPASSWD: /usr/bin/startService.sh, /usr/bin/stopService.sh
Next is the OH stuff.
Create the relevant items:
Switch Sys_VPNControlSwitch "System VPN Control" <vpn> ["Point"]
String Sys_VPNStatus "System VPN Status" <vpnstatus> ["Point"]
Relevant sitemap lines:
Switch item=Sys_VPNControlSwitch label="VPN Server Control"
Text item=Sys_VPNStatus label="VPN Server Status [%s]"
And the rule to control the server:
val String filename = "system.rules"
var Timer timer = null
rule "Control VPN Server"
when
Item Sys_VPNControlSwitch received command
then
logInfo(filename, "Controling the VPN server. Seting to " + receivedCommand)
if (receivedCommand == ON) {
executeCommandLine("sudo","/usr/bin/vpn/startService.sh")
if (timer===null) {
timer = createTimer(now.plusSeconds(1), [|
val String serviceStatusRaw = executeCommandLine(Duration.ofSeconds(15),"systemctl","status","openvpn.service")
val String serviceStatus = (serviceStatusRaw.split("\n").get(2)).stripLeading().split(" ").get(1)
if (serviceStatus == "active") {
Sys_VPNStatus.postUpdate("Active")
} else {
Sys_VPNStatus.postUpdate(serviceStatus)
Sys_VPNControlSwitch.postUpdate(OFF)
}
logInfo(filename, "VPN Status is: " + serviceStatus)
timer=null
])}
} else if (receivedCommand == OFF) {
executeCommandLine("sudo","/usr/bin/vpn/stopService.sh")
if (timer===null) {
timer = createTimer(now.plusSeconds(1), [|
val String serviceStatusRaw = executeCommandLine(Duration.ofSeconds(15),"systemctl","status","openvpn.service")
val String serviceStatus = (serviceStatusRaw.split("\n").get(2)).stripLeading().split(" ").get(1)
if (serviceStatus == "inactive") {
Sys_VPNStatus.postUpdate("Inactive")
} else if (serviceStatus == "active") {
Sys_VPNStatus.postUpdate("Active")
Sys_VPNControlSwitch.postUpdate(ON)
} else {
Sys_VPNStatus.postUpdate(serviceStatus)
}
logInfo(filename, "VPN Status is: " + serviceStatus)
timer=null
])}
}
end
The logic of the rule:
- Run the appropriate script according to the switch.
- Wait a second and check the service status.
- If the actual status doesn’t match what should be, revert the switch to the right position so the user can switch it back again.
The method I found to check the service status is as follows:
- Run a
systemctl status
command. The result saved into serviceStatusRaw
and looks something like this:
● openvpn.service - OpenVPN service
Loaded: loaded (/lib/systemd/system/openvpn.service; enabled; vendor preset: enabled)
Active: inactive (dead) since Thu 2021-04-01 09:32:07 IDT; 40min ago
Process: 20069 ExecStart=/bin/true (code=exited, status=0/SUCCESS)
Main PID: 20069 (code=exited, status=0/SUCCESS)
- The following manipulation will get the second line of that answer, strip the leading whitesapces and get that second word after
Active:
which should indicate the status of the service. Should be active
or inactive
but there could other values in case of an error of something else.
In conclusion:
- I now have an openVPN server running on my Pi.
- I can control it’s state from the one of mobile apps connected through cloud connector so it’s available also without the VPN connection.
- So incase I want to connect to my system from the internet, I start the server from the app and then connect to the server with an openVPN client from my phone.
Again, I’m in no way an expert in these things so I’m sure there are more “correct” methods to do all the things I did. I’d be happy to get any response or ideas to make it better.
Cheers!