4

I'm trying to set up Transmission with OpenVPN on a headless server running a minimal install of CentOS 6.10, and ideally these would start when I boot the system.

I'm able to get everything to run by following the steps here and here — but that only works when running the script (vpn.sh, as per the two tutorials) manually. That script looks like this:

#!/bin/sh

sudo openvpn --cd /etc/openvpn --config /etc/openvpn/conf.ovpn --script-security 2 --up /etc/openvpn/up.sh

I might add that in addition to OpenVPN and Transmission, I also have this Telegram bot that should start after all else is up, so my up.sh file also has one more line at the end and looks like this:

#!/bin/sh

/etc/init.d/transmission-daemon stop
/bin/sed s/IP_ADDRESS/$4/ /var/lib/transmission/.config/transmission/settings_template.json > /var/lib/transmission/.config/transmission/settings.json
/etc/init.d/transmission-daemon start
/etc/init.d/transmission-telegram start

Ideally, I'd be able to do something a bit more like what this post outlines, though I'm not particularly picky about whether all the traffic goes through the VPN or if only the torrent traffic does:

  1. Start OpenVPN at boot (after networking is up);
  2. If the VPN connection goes through, configure Transmission to run through it;
  3. If that goes through, run Transmission (and the Telegram bot);
  4. If at any time the VPN goes down, also bring Transmission (and the Telegram bot) down gracefully.

I tried following the steps outlined in that post, and for some reason I just can't get the event transmission-vpn-up to happen — triggered by the route-up.sh script in the tutorial — and always get initctl: Event failed. I'm not able to emit the event either by following the steps in the post, nor by passing the contents of the script manually to the command line. route-up.sh, as per the post on Ask Ubuntu, looks like this:

#! /bin/bash

/sbin/initctl emit transmission-vpn-up VPN_GATEWAY=$route_vpn_gateway LOCAL_IP=$ifconfig_local

If I pass the contents of down.sh to the command line, though, I get no such error:

/sbin/initctl emit transmission-vpn-down

Tried passing both with sudo and without it.

Is there a simple way for me to set this whole thing up? Am I missing something in particular with emitting the transmission-vpn-up specified in the other tutorial on AU, y'know, given that it's a tutorial for Ubuntu and not CentOS?

Alternatively, would it be easier to just make vpn.sh (the first chunk of code in this post) run at boot? It would be as neat or graceful if the VPN goes down, but it'd do the trick.


Quick Update:

The tutorial on Ask Ubuntu I mentioned above, particularly in the /etc/init/transmission-up.conf is trying to use /usr/sbin/ufw, which I don't have in CentOS — hence the original Event failed error message. I temporarily commented out those bits, and now I don't have any error messages.

However, even without any error messages and with the VPN up and running, it seems like something else is failing because Transmission never starts... any hints on where I could look?

2
+75

Solutions

Here are some detailed aspect and solutions about this question (note that centos 6 is not using systemd, centos switched to systemd on v7)

Solution for centos 7

Setup OpenVPN At Boot:

We can use systemd (implement a service) to make openvpn start at boot, we need to create the service, enable it then start it. (after enabling the service it will be automatically start at boot time)

1 - Create the service: (with root)

cd /etc/systemd/system
touch openvpn-custom.service
chmod 644 openvpn-custom.service

2 - Open the service file: (with root)

open /etc/systemd/system/openvpn-custom.service with the text editor that you use example nano openvpn-custom.service

3 - Edit and setup the service file: (with root)

Paste and adapt the following code into /etc/systemd/system/openvpn-custom.service

[Unit]
Description=OpenVPN Custom Setup Script 
After=network.target network-online.target
Wants=network-online.target

[Service]
Type=forking
RemainAfterExit=yes
ExecStart=/full-path-to/your/vpn-script/vpn.sh
ExecStop=/full-path-to/a-scirpt/that-would-stop-openvpn/vpn-stop.sh

[Install]
WantedBy=multi-user.target

4 - Enable and start the service: (with root)

systemctl enable openvpn-custom.service
systemctl start openvpn-custom.service

you can check the status of the service with systemctl status openvpn-custom.service

Setup Transmission And Telegram At Boot (based on openvpn status):

Same as openvpn solution create a new service (then enable and start it) containing the following code

[Unit]
Description=Application Depending on OpenVPN 
After=network.target network-online.target openvpn-custom.service
Wants=network-online.target openvpn-custom.service

[Service]
Type=forking
RemainAfterExit=yes
ExecStart=/full-path-to/your/ovpn-applications.sh
ExecStop=/full-path-to/a-scirpt/that-would-stop-apps/ovpn-applications-stop.sh

[Install]
WantedBy=multi-user.target

Your ovpn-applications.sh would look like something like this

#!/bin/sh
/bin/sed s/IP_ADDRESS/$4/ /var/lib/transmission/.config/transmission/settings_template.json > /var/lib/transmission/.config/transmission/settings.json
/etc/init.d/transmission-daemon start
/etc/init.d/transmission-telegram start

After=openvpn-custom.service and Wants=openvpn-custom.service would make that service depending on openvpn and thus if openvpn service is not started or fail the other service would not start

Solution for centos 6

Setup OpenVPN At Boot:

1 - Create the service: (with root)

cd /etc/rc.d/init.d
touch openvpn-custom
chmod 755 openvpn-custom

2 - Open the service file: (with root)

open /etc/rc.d/init.d/openvpn-custom with the text editor that you use example nano openvpn-custom

3 - Edit and setup the service file: (with root)

#!/bin/bash
#
# chkconfig: 2345 55 45
# description: Custom openvpn script
# processname: openvpn
#
### BEGIN INIT INFO
# Provides: openvpn
# Required-Start: $network
# Required-Stop: $network
# Default-Start: 2 3 4 5
# Short-Description: The openvpn daemon
# Description: The openvpn daemon custom script
### END INIT INFO

#/etc/rc.d/init.d/openvpn-custom
# Source function library.
. /etc/init.d/functions


start() {
        echo -n "Starting custom openvpn... "
        /full-path-to/your/vpn-script/vpn.sh
        return 0
}

stop() {
        echo -n "Shutting down custom openvpn... "
        /full-path-to/a-scirpt/that-would-stop-openvpn/vpn-stop.sh
        return 0
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    status)
        ;;
    restart)
        stop
        start
        ;;
    reload)
        ;;
    *)
        echo "Usage: openvpn-custom {start|stop|status|reload|restart}"
 exit 1
        ;;
esac
exit $?

4 - Enable and start the service: (with root)

chkconfig openvpn-custom on
service openvpn-custom start

you can check the status of the service with service openvpn-custom status

How to make sure openvpn only start when the network is ready

The chkconfig definition at the start of an initscript determines what S/K number it gets.

Each "runlevel" is actually just a directory (/etc/rc*.d/) full of symlinks to initscripts (/etc/init.d/) and those symlinks are named with numbered S and K entries.

S means start and K means kill. When init enters a runlevel, it starts with S01 and works its way through to S99, running each initscript to start the service which that script controls. When init leaves a runlevel, it starts with K01 and works its way through to K99, running each script to stop the service which that script controls.

man chkconfig gives an example of both the chkconfig-style service definition, and the Linux Standards Base (LSB) style service definition.

If an initscript has both types defined, the LSB definition should take precedence over the chkconfig definition.

If you have a service which depends on network connectivity, you can either make sure your service starts after /etc/rc*.d/S10network by giving it a start number after 10, or you can see that /etc/init.d/network has the LSB definition Provides: $network, so you can use the LSB definition Required-Start: $network in your initscript.

Setup Transmission And Telegram At Boot (based on openvpn status):

Same as openvpn solution create a new service (then enable and start it) containing the following code (/etc/rc.d/init.d/openvpn-apps)

#!/bin/bash
#
# chkconfig: 2345 55 45
# description: Custom openvpn-apps script
# processname: openvpn-apps
#
### BEGIN INIT INFO
# Provides: openvpn-apps
# Required-Start: $network
# Required-Stop: $network
# Default-Start: 2 3 4 5
# Short-Description: The openvpn-apps daemon
# Description: The openvpn daemon custom script
### END INIT INFO
#/etc/rc.d/init.d/openvpn-apps

# Source function library.
. /etc/init.d/functions


start() {
        echo -n "Starting custom openvpn... "
        /full-path-to/your/apps.sh
        return 0
}

stop() {
        echo -n "Shutting down custom openvpn... "
        /full-path-to/your/stop-apps.sh
        return 0
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    status)
        ;;
    restart)
        stop
        start
        ;;
    reload)
        ;;
    *)
        echo "Usage: openvpn-apps {start|stop|status|reload|restart}"
 exit 1
        ;;
esac
exit $?

Your apps.sh would look like something like this

#!/bin/sh
/bin/sed s/IP_ADDRESS/$4/ /var/lib/transmission/.config/transmission/settings_template.json > /var/lib/transmission/.config/transmission/settings.json
/etc/init.d/transmission-daemon start
/etc/init.d/transmission-telegram start

To make your apps.sh dependent on openvpn status you can fist delay the start of apps.sh with sleep or write a bash loop that check openvpn with ps or check the result of a ping to your openvn gateway (in apps.sh) or you could just check the result of service openvpn-custom status (in apps.sh)

Solutions (6 & 7) notes:

Fail safe openvpn:

You can setup a "fail safe" to make the network go down if openvpn is not working and thus prevent connections leaks when the vpn is not working for this you can use this or that solution

Force an application to use specific interface:

If your openvpn setup is tunneling/routing the whole system; there is no need for this but if you are not routing all the traffic through the openvpn interface you can proxify/bind/force your applications (transmission/telegram) to use the vpn; Under Linux there are several solution to bind an application to a specific interface each one have its pro and con this unix stackexchange answer explain with detailed most of the available possibilities

  • Couple of questions: 1) in step 3, is there a preference over running a start and a stop script instead of just calling the services as needed from there? 2) you mention I can have the openvpn-apps service check the status of openvpn-custom, but your current implementation returns nothing when I check its status; any hints on that? 3) is there a preference over having two separate custom services, rather than instead using the --up parameter when initializing the vpn in openvpn-custom? – Marcy May 3 at 9:41
  • Oh, and also openvpn-custom is missing something, since using chkconfig on it returns service openvpn-custom does not support chkconfig. – Marcy May 3 at 9:54
  • 1: you can disable the service from boot and call it whenever you want with service ... start and stop (can you be more precise on the purpose here ?) 2: normally you can check the status with service openvpn-custom status but otherwise you could do better and check connectivity on your vpn network for instance in your bash ping an ip on the vpn end see the script bellow 3: if every time you use the vpn you will need your applications, you can put everything in a signe script openvpn-custom --up is useful tho because it's launched only when the connection succeed – intika May 3 at 11:28
  • ping -c 1 -n -w 1 10.1.0.1 &> /dev/null;if [ $? -ne 0 ]; then echo "ping nok"; else echo ''ping ok"; fi; – intika May 3 at 11:29
  • i am editing my answer to fix openvpn-custom script to add whats missing you are right, here is what i forget (comment are required) stackoverflow.com/questions/5133552/… – intika May 3 at 11:32
1

In your case I think you might also stick to standard procedures as most as possible.

Starting from a pristine CentOS 6.10 minimal ISO installation, I would proceed as follows:

First, enable unconfined OpenVPN: CentoOS 6 comes with a stock SELinux policy that by default runs /usr/sbin/openvpn binary in a restricted mode that basically allows it only networking setups, thus preventing any helper script to be able to do anything useful. However CentOS 6 also provides one simple setting to enable unconfined OpenVPNs. As superuser run:

setsebool -P openvpn_run_unconfined on

hold on a few seconds, as it may take a little while.

You might want to put it back in confined mode once everything works. There a few viable approaches I will come back later to.

Then, to proceed with basic setup, install EPEL repository into your system’s yum:

yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm

That gives your system’s yum a repository that provides RPMs for openvpn and transmission packaged for CentOS 6.

After that you can do:

yum install openvpn transmission-daemon

Then install the transmission-telegram bot like you’ve already done. Just make sure to place it in a directory indicated in $PATH. Taking into consideration CentOS 6's SELinux policy, /usr/local/bin may be a good choice.

(Please note that I can't really expand more on specifics for the Telegram bot because I don't know anything about it and can't even test it because I don't use Telegram at all.)

Then make an openvpn-transmission-up.sh script like:

#!/bin/bash

PATH+=":/sbin:/usr/sbin"
service transmission-daemon start
service transmission-daemon status && telegram-bot-start.sh

The telegram-bot-start.sh is just a hypothetical wrapper to run the actual command that starts the Telegram bot, and is executed only if transmission-daemon started successfully. You might also use the direct command instead of a wrapper script, if the direct command can be made a one-liner to be put after the &&.

Then also make an openvpn-transmission-down.sh script like:

#!/bin/bash

PATH+=":/sbin:/usr/sbin"
telegram-bot-stop.sh
service transmission-daemon stop || true

Here too you might use the direct commands for stopping the Telegram bot, instead of the telegram-bot-stop.sh wrapper, in this case even if they are actually a sequence of commands.

Then make the above two scripts runnable:

chmod +x openvpn-transmission-up.sh openvpn-transmission-down.sh

and place them in /etc/openvpn/scripts directory (again to comply with CentoOS SELinux policy). Create that directory if you don't have it yet.

Then put your own config for OpenVPN into /etc/openvpn. Such file can be named however you like but it must have a .conf suffix.

Make sure you have the following lines in that .conf file:

script-security 2
route-up scripts/openvpn-transmission-up.sh
route-pre-down scripts/openvpn-transmission-down.sh

If you already have route-up or route-pre-down scripts, then just merge your pre-existing scripts with the above ones respectively, but make sure to put them in /etc/openvpn/scripts.

The above setup makes sure your Transmission and Telegram bot always follow the VPN tunnel's fate.

Finally set openvpn to run at boot:

chkconfig openvpn on

This last setting relies on the setup that comes with the RPM package for OpenVPN, which makes it run only after general networking has successfully started.

Note that I think you don't actually need to configure Transmission, because the default configuration that comes with its RPM package should suffice. However, if you wish to reuse your sed command to set the IP_ADDRESS, then place that sed command in the openvpn-transmission-up.sh script before starting transmission-daemon. Anyways, it is usually better to use $ifconfig_local instead of $4.

At this point you should be all set. You may test it through a simple service openvpn followed by start or stop or restart etc. Or by just rebooting your headless server.

NOTE that if your VPN provider requires authentication through username and password, you will need to put them into a file on two distinct lines. This file should reside in /etc/openvpn to comply with SELinux policy. Then include auth-user-pass /etc/openvpn/credentials-file into your openvpn .conf file. If you don't do so, CentOS will stop during boot waiting for interactive input of credentials, or simply OpenVPN might just refuse to start.


With regard to (re-)hardening the above setup, depending on your preferences you might be happy as it is: the setup thus far takes advantage of allowable settings pre-installed by CentOS, including the one to allow OpenVPN to run with less restrictions, which basically allows the helper scripts to freely execute the binaries available on the system. As loosen as that may sound, it is still more constrained than no SELinux at all, and by leaving things this way, both Transmission and the Telegram bot run with a bit more constraints than their stock (or manual) installation would allow.

If you rather wish to put OpenVPN back in confined operation, it can still be fairly easy by taking advantage of the fact that CentOS 6 actually uses Upstart as its /sbin/init even though it relies much of the boot operations to the legacy SysVinit-style scripts. However you need some additional steps:

  1. a (luckily simple) custom SELinux policy to at least allow OpenVPN talk to Upstart so that it asks this latter to start Transmission and the Telegram bot
  2. a couple of (simple) Upstart jobs for starting Transmission through its legacy scripts and for the Telegram bot

To start, put OpenVPN back in confined mode:

setsebool -P openvpn_run_unconfined off

Now, if you start OpenVPN, the helper scripts won't be able to start Transmission and Telegram bot.

So, the custom SELinux policy:

Make a file named openvpn-talk-upstart.te exactly as follows:

module openvpn-talk-upstart.mod 1.0;

require {
        type openvpn_t;
        type init_t;
        class unix_stream_socket connectto;
}

allow openvpn_t init_t:unix_stream_socket connectto;

Then run the following commands:

checkmodule -m -M -o openvpn-talk-upstart.mod openvpn-talk-upstart.te
semodule_package -o openvpn-talk-upstart.pp -m openvpn-talk-upstart.mod
semodule -i openvpn-talk-upstart.pp

the last command may take a little while.

Then the Upstart jobs:

Make a /etc/init/transmission-up.conf file as follows:

task

exec service transmission-daemon start

and a /etc/init/transmission-down.conf file as follows:

task

exec service transmission-daemon stop

Then you need Upstart jobs for the Telegram bot too.

For consistency with my example I stick to my hypothetical scripts approach, and so the job files, named for instance /etc/init/telegram-bot-up.conf and /etc/init/telegram-bot-down.conf, would be like the Transmission ones, only with execed commands turned into (respectively) /usr/local/bin/telegram-bot-start.sh and /usr/local/bin/telegram-bot-stop.sh.

Note that here, depending on your confidence with Upstart, you might also put the relevant commands directly in the job files, within script ... end-script keywords in place of the exec keyword, or you might even make a fully Upstart-controlled daemon via one single Upstart job file (but not of task type) if you feel so inclined.

After this, make the OpenVPN helper openvpn-transmission-up.sh script in /etc/openvpn/scripts become as follows:

#!/bin/sh

/sbin/start transmission-up && /sbin/start telegram-bot-up

and the openvpn-transmission-down.sh script:

#!/bin/sh

/sbin/start telegram-bot-down
/sbin/start transmission-down || true

Note the /sbin/start command used even though it is actually a "down" operation.

Now everything should work, with OpenVPN in its original confined (ie more secure) mode, but both Transmission and the Telegram bot in their standard "free" (ie less secure) mode.


Should you wish to harden these latter two even more, then you'd need to run them in a confined SELinux context, such as the one for OpenVPN, and then do a careful analysis of the /var/log/audit/audit.log file, in order to adjust the SELinux policy precisely. There are tools that can help you on that, namely the audit2allow command available from the policycoreutils-python package.

You can run that command over the audit.log that comes from a SELinux in permissive mode (see /etc/sysconfig/selinux file), but that might end up allowing too much. Else you can run it over the audit.log that comes from a regular enforcing SELinux, doing it step-by-step until you finally get to a working setup. In this latter way you can be sure that you only allow no more than what is exactly needed.

The output from audit2allow -a -m ... has to be fed to checkmodule -m -M -o ..., then this latter's output to semodule_package -o ... -m ..., and finally a semodule -i of the file generated by semodule_package. This iteration at each step of the analysis.

It can be a very long task, and even when you get to a working setup, it could be so only initially: at some point you might get into unexpected "permission denied" problems later on, e.g. the first time your Transmission, or Telegram Bot, tries to access something it hadn't tried before.

  • 2
    I'd feel much better about this answer if it demonstrated the investigation and steps required to turn SE Linux back on at the end. (Or avoided turning it off) – Jeff Schaller May 4 at 17:19
  • @JeffSchaller Very understandable. I had thought to include a few hints. The thing is, even assuming a pristine CentOS, it still depends on how one chooses to proceed with the scripts: how many of them, where they are placed, what they actually do beside what I described, ... , and then there is that telegram bot which I honestly don't know anything about and can't even test (I don't use telegram..). Also, one might reasonably prefer to leave SELinux off. The best I can do is to stick to the presented setup (leaving out the bot), as a starting point to proceed from there if so desired.. – LL3 May 5 at 0:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.