{"id":489,"date":"2020-03-03T16:47:57","date_gmt":"2020-03-03T15:47:57","guid":{"rendered":"http:\/\/crudelis.fr\/site\/sblog\/?p=489"},"modified":"2022-07-30T18:16:12","modified_gmt":"2022-07-30T16:16:12","slug":"yunohost-inside-yunohost-through-a-vpn","status":"publish","type":"post","link":"https:\/\/crudelis.fr\/site\/sblog\/2020\/03\/yunohost-inside-yunohost-through-a-vpn\/","title":{"rendered":"YunoHost inside YunoHost through a VPN"},"content":{"rendered":"<p>Into this post we're going to see how to deploy a <a href=\"https:\/\/yunohost.org\/\">YunoHost<\/a> server into an existing YunoHost server by using <a href=\"https:\/\/linuxcontainers.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">LXC<\/a> in order to configure the guest YunoHost to connect throught <a href=\"https:\/\/protonvpn.com\" target=\"_blank\" rel=\"noopener noreferrer\">ProtonVPN<\/a>.<\/p>\r\n<p>The idea is to isolate a service behind a VPN, but still using the convenience of YunoHost. Without affecting the whole server and all the services it provides.<\/p>\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Install LXC<\/h2>\r\n\r\n\r\n\r\n<p>First things first, let's install LXC:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>sudo apt update\r\nsudo apt install lxc lxctl<\/code><\/pre>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Host network configuration<\/h2>\r\n\r\n\r\n\r\n<p>Allow the kernel to forward traffic:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>echo \"net.ipv4.ip_forward=1\" | sudo tee \/etc\/sysctl.d\/lxc_ynh.conf\r\nsudo sysctl -p \/etc\/sysctl.d\/lxc_ynh.conf<\/code><\/pre>\r\n\r\n\r\n\r\n<p>Add a bridge to route the traffic between the host and the LXC guest:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>sudo nano \/etc\/network\/interfaces.d\/lxc_ynh<\/code><\/pre>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>auto lxc_ynh\r\n    iface lxc_ynh inet static\r\n    address 10.1.5.1\/24\r\n    bridge_ports none\r\n    bridge_fd 0\r\n    bridge_maxwait 0\r\n    up iptables -A FORWARD -i lxc_ynh -o eth0 -j ACCEPT\r\n    up iptables -A FORWARD -i eth0 -o lxc_ynh -j ACCEPT\r\n    up iptables -t nat -A POSTROUTING -s 10.1.5.0\/24 -j MASQUERADE<\/code><\/pre>\r\n\r\n\r\n\r\n<p>Then start the bridge:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>sudo ifup lxc_ynh --interfaces=\/etc\/network\/interfaces.d\/lxc_ynh<\/code><\/pre>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Create the container<\/h2>\r\n\r\n\r\n\r\n<p>Create a minimalist Debian container:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>sudo lxc-create -n lxc_ynh -t debian -- -r buster<\/code><\/pre>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Container configuration<\/h2>\r\n\r\n\r\n\r\n<p>The container configuration will be done into the file <code>\/var\/lib\/lxc\/lxc_ynh\/config<\/code><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>sudo nano \/var\/lib\/lxc\/lxc_ynh\/config<\/code><\/pre>\r\n\r\n\r\n\r\n<p>Remove the line <code>lxc.net.0.type = empty<\/code><br>And add the following ones:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code># Network\r\nlxc.net.0.type = veth\r\nlxc.net.0.flags = up\r\nlxc.net.0.link = lxc_ynh\r\nlxc.net.0.name = eth0\r\nlxc.net.0.hwaddr = 00:FF:AA:00:00:01\r\nlxc.net.0.ipv4.address = 10.1.5.10\r\nlxc.net.0.ipv4.gateway = 10.1.5.1\r\n\r\n# Autostart\r\nlxc.start.auto = 1<\/code><\/pre>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">First steps with the container<\/h2>\r\n\r\n\r\n\r\n<p>Start the LXC container:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>sudo lxc-start -n lxc_ynh -d<\/code><\/pre>\r\n\r\n\r\n\r\n<p>And update the apt cache (That's also a way to check if the network is working):<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>sudo lxc-attach -n lxc_ynh -- apt-get update<\/code><\/pre>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\">If the container fails to resolve domain names, you likely have to change the dns for the next steps.\r\n\r\nGo to the file <code>\/var\/lib\/lxc\/lxc_ynh\/rootfs\/etc\/resolv.conf<\/code> and replace 127.0.0.1 by a dns address you want, your gateway is usually a good choice.<\/pre>\r\n\r\n\r\n\r\n<p>Now that the network is working into your container, install the packages needed for a fully working Debian:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>sudo lxc-attach -n lxc_ynh -- apt-get install -y aptitude sudo git ssh openssh-server\r\nsudo lxc-attach -n lxc_ynh -- aptitude install -y ~pstandard ~prequired ~pimportant<\/code><\/pre>\r\n\r\n\r\n\r\n<p>You now have a perfectly working Debian into a LXC container.<br>So, now let's go directly into the container:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>sudo lxc-attach -n lxc_ynh -- \/bin\/bash<\/code><\/pre>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\">This is one of the best way to go into your container to have a proper terminal to work. Could be useful to keep that for further operations.<\/pre>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Install YunoHost into the container<\/h2>\r\n\r\n\r\n\r\n<p> Now that we're into the container, you'll notice that the prompt has changed to <code>root@lxc_ynh<\/code>, we're going to use that syntax for every command to use into the container.<br><br>And first, we're going to install YunoHost:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>root@lxc_ynh:\/# git clone https:\/\/github.com\/YunoHost\/install_script \/tmp\/install_script\r\nroot@lxc_ynh:\/# cd \/tmp\/install_script; .\/install_yunohost -a\r\nroot@lxc_ynh:\/# yunohost tools postinstall\r\nroot@lxc_ynh:\/# yunohost user create MYUSER<\/code><\/pre>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\">Change <code>MYUSER<\/code> by the name you want for your user.<\/pre>\r\n\r\n\r\n\r\n<p>To ease the usage of a Let's Encrypt certificate, we're going to let the host handle it.<br>First we're going to share the certificates between the host and the guest by mounting the directory into the container.<br>In order to do so, we're going back to the file <code>\/var\/lib\/lxc\/lxc_ynh\/config<\/code><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>sudo nano \/var\/lib\/lxc\/lxc_ynh\/config<\/code><\/pre>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\">Please notice that there isn't the prompt <code>root@lxc_ynh<\/code> as said before. This command has to be executed into the host, not the container.<\/pre>\r\n\r\n\r\n\r\n<p>And add these few lines<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code># Mount between host and guest\r\n# SSL Certificates\r\nlxc.mount.entry=\/etc\/yunohost\/certs etc\/yunohost\/certs none ro,bind 0 0<\/code><\/pre>\r\n\r\n\r\n\r\n<p>Yet, after a restart of the container, <code>ssl-cert<\/code> will probably have a problem to read the certificate because of the mapping of the groups between the host and the LXC guest.<br>And, as the certificates are not readable except by <code>root<\/code> and <code>ssl-cert<\/code>, ldap will fail to read it...<\/p>\r\n\r\n\r\n\r\n<p>Start by checking the permissions<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>root@lxc_ynh:\/# ls -l \/etc\/yunohost\/certs\/<\/code><\/pre>\r\n\r\n\r\n\r\n<p>All files should belong to root:ssl-cert. If not, you have an issue with the gid of ssl-cert. To fix that, we will modify the gid to have the same than the host.<\/p>\r\n\r\n\r\n\r\n<p>First, find the gid of ssl-cert into the host<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>cat \/etc\/group | grep ssl-cert<\/code><\/pre>\r\n\r\n\r\n\r\n<p>Then find the current group using that gid into the guest<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>root@lxc_ynh:\/# cat \/etc\/group | grep 'gid ssl-cert'<\/code><\/pre>\r\n\r\n\r\n\r\n<p>Change the gid of the group impersonating ssl-cert gid and change the ownership of its files.<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>root@lxc_ynh:\/# groupmod -g 'free gid' 'impersonating group'\r\nroot@lxc_ynh:\/# find \/ -group 'free gid' -exec chgrp -h 'impersonating group' {} \\;<\/code><\/pre>\r\n\r\n\r\n\r\n<p>Then give ssl-cert its correct gid so it would be the same as the host.<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>root@lxc_ynh:\/# groupmod -g 'gid ssl-cert' ssl-cert<\/code><\/pre>\r\n\r\n\r\n\r\n<p>Now <code>ssl-cert<\/code> will be able to read correctly the certificates, as its gid is the same as the host.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Configure the host to access the container<\/h2>\r\n\r\n\r\n\r\n<p>Into the admin panel of your host YunoHost, add the domain you have chosen for your guest YunoHost during the post install just before.<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\">If has to be the same, otherwise, the redirection and the certificate won't match<\/pre>\r\n\r\n\r\n\r\n<p>And install a Let's Encrypt certificate for this new domain.<\/p>\r\n\r\n\r\n\r\n<p>Now, we need to redirect the request to this domain toward the container.<br>To do that, we're going to use the easy way by installing the app <a rel=\"noreferrer noopener\" aria-label=\"redirect (opens in a new tab)\" href=\"https:\/\/github.com\/YunoHost-Apps\/redirect_ynh\" target=\"_blank\">redirect<\/a> with the following configuration:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>Choose a domain for your redirect: The domain you used for your YunoHost into the container.\r\nChoose a path for your redirect:   \/\r\nRedirect destination path:         https:\/\/10.1.5.10\r\nRedirect type:                     Proxy, insivible [...]. Everybody will be able to access it.<\/code><\/pre>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\">Don't worry about the warning, we're not going to install anything else for that domain into this YunoHost. Everything else will be installed into the guest YunoHost.<\/pre>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\">If you're not using YunoHost, you should configure the domain yourself and handle the certificate.\r\nFor the redirection to the container, you'll need a nginx proxy pass instruction.\r\nHere the nginx config set while using the app redirect_ynh:\r\n\r\n<code>location \/ {\r\n   proxy_pass        https:\/\/10.1.5.10;\r\n   proxy_redirect    off;\r\n   proxy_set_header  Host $host;\r\n   proxy_set_header  X-Real-IP $remote_addr;\r\n   proxy_set_header  X-Forwarded-Proto $scheme;\r\n   proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;\r\n   proxy_set_header  X-Forwarded-Host $server_name;\r\n   proxy_set_header  X-Forwarded-Port $server_port;\r\n\r\n   proxy_http_version 1.1;\r\n   proxy_set_header Upgrade $http_upgrade;\r\n   proxy_set_header Connection \"upgrade\";\r\n }<\/code><\/pre>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Enjoy it<\/h2>\r\n\r\n\r\n\r\n<p>In order to mount the certificate, and shake up a little bit all the stuff, let's restart the container:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>sudo lxc-stop -n lxc_ynh\r\nsudo lxc-start -n lxc_ynh -d<\/code><\/pre>\r\n\r\n\r\n\r\n<p>Now, from a not to bothering browser (be careful with any cache you may have) you should be able to reach your guest YunoHost from the domain name you've chosen.<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\">It can be useful to go into private browsing to avoid any cache that would got you some trouble.<\/pre>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\"><em>While you should reach the portal of your guest YunoHost, you won't be able to reach the admin panel.\r\nFortunately, you still have the CLI command available.\r\nI hope to find a way to fix that issue...<\/em><\/pre>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Configure the container to support a VPN connection<\/h2>\r\n\r\n\r\n\r\n<p>The container is unable to connect to a VPN because it doesn't have a TUN interface.<br>We're going to add that interface, by adding those lines to the config file <code>\/var\/lib\/lxc\/lxc_ynh\/config<\/code>:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code># VPN specific configuration\r\nlxc.autodev = 1\r\nlxc.cgroup.devices.allow = c 10:200 rwm\r\nlxc.hook.autodev = sh -c \"cd ${LXC_ROOTFS_MOUNT}\/dev; mkdir -p net; test -e net\/tun || mknod net\/tun c 10 200\"<\/code><\/pre>\r\n\r\n\r\n\r\n<p>Then restart the container:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>sudo lxc-stop -n lxc_ynh\r\nsudo lxc-start -n lxc_ynh -d<\/code><\/pre>\r\n\r\n\r\n\r\n<p>Now that the container can have a VPN client, let's install the <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/ProtonVPN\/linux-cli-community\" data-type=\"URL\" data-id=\"https:\/\/github.com\/ProtonVPN\/linux-cli-community\" target=\"_blank\">open source CLI client for ProtonVPN<\/a> into the container:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>root@lxc_ynh:\/# apt install openvpn dialog python3-pip python3-setuptools iptables-persistent\r\nroot@lxc_ynh:\/# sudo pip3 install protonvpn-cli<\/code><\/pre>\r\n\r\n\r\n\r\n<p>Configure the VPN:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>root@lxc_ynh:\/# sudo protonvpn init<\/code><\/pre>\r\n\r\n\r\n\r\n<pre id=\"block-9038aa4e-92dd-4e33-9ebd-08ba7a532f21\" class=\"wp-block-preformatted\">Note that you need sudo here to run the cli ProtonVPN app.<\/pre>\r\n\r\n\r\n\r\n<p>The VPN client is configured, but not yet connected. You can run the command <code>protonvpn status<\/code> to see that you're not connected to the VPN.<br>So first, connect to the VPN:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>root@lxc_ynh:\/# sudo protonvpn connect -f\r\nroot@lxc_ynh:\/# sudo protonvpn status<\/code><\/pre>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\">Find all the available commands of the app <a rel=\"noreferrer noopener\" aria-label=\"here (opens in a new tab)\" href=\"https:\/\/github.com\/ProtonVPN\/protonvpn-cli-ng\/blob\/master\/USAGE.md#list-of-all-commands\" target=\"_blank\">here<\/a>.<\/pre>\r\n\r\n\r\n\r\n<p>Your guest YunoHost, into its LXC container is now protected behind the VPN.<br>But as soon as you will restart the container, the VPN will be disconnected.<br>To prevent that situation, we're going to create a systemd config for the ProtonVPN app.<br>Since a documentation already exist, let's follow it: <a href=\"https:\/\/github.com\/ProtonVPN\/protonvpn-cli-ng\/blob\/master\/USAGE.md#via-systemd-service\">https:\/\/github.com\/ProtonVPN\/protonvpn-cli-ng\/blob\/master\/USAGE.md#via-systemd-service<\/a><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\">This systemd config has to be added into the LXC container.<\/pre>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">You're all set<\/h2>\r\n\r\n\r\n\r\n<p>Everything should be ok now.<br>You can restart your container to make sure everything is working and you're still connected to the VPN.<\/p>\r\n\r\n\r\n\r\n<p>And now it's time to install the services you want into your YunoHost <a href=\"https:\/\/yunohost.org\/#\/apps\">https:\/\/yunohost.org\/#\/apps<\/a><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-preformatted\">If you want to use a multimedia app, it can be useful to mount your \/home\/yunohost.multimedia into the LXC container to share the media between both the host and the guest.\r\n\r\nCreate first the directory:\r\n<code>sudo mkdir \/var\/lib\/lxc\/lxc_ynh\/rootfs\/home\/yunohost.multimedia<\/code>\r\nAnd add a mount line into <code>\/var\/lib\/lxc\/lxc_ynh\/config<\/code>:\r\n<code>lxc.mount.entry=\/home\/yunohost.multimedia home\/yunohost.multimedia none bind 0 0<\/code>\r\nRestart the container and that's ok.<\/pre>\r\n\r\n\r\n\r\n<p><\/p>\r\n","protected":false},"excerpt":{"rendered":"<p>Into this post we're going to see how to deploy a YunoHost server into an existing YunoHost server by using LXC in order to configure the guest YunoHost to connect throught ProtonVPN. The idea is to isolate a service behind a VPN, but still using the convenience of YunoHost. Without affecting the whole server and [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_links_to":"","_links_to_target":""},"categories":[3],"tags":[],"class_list":["post-489","post","type-post","status-publish","format-standard","hentry","category-home-server"],"_links":{"self":[{"href":"https:\/\/crudelis.fr\/site\/sblog\/wp-json\/wp\/v2\/posts\/489","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/crudelis.fr\/site\/sblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/crudelis.fr\/site\/sblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/crudelis.fr\/site\/sblog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/crudelis.fr\/site\/sblog\/wp-json\/wp\/v2\/comments?post=489"}],"version-history":[{"count":33,"href":"https:\/\/crudelis.fr\/site\/sblog\/wp-json\/wp\/v2\/posts\/489\/revisions"}],"predecessor-version":[{"id":914,"href":"https:\/\/crudelis.fr\/site\/sblog\/wp-json\/wp\/v2\/posts\/489\/revisions\/914"}],"wp:attachment":[{"href":"https:\/\/crudelis.fr\/site\/sblog\/wp-json\/wp\/v2\/media?parent=489"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/crudelis.fr\/site\/sblog\/wp-json\/wp\/v2\/categories?post=489"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/crudelis.fr\/site\/sblog\/wp-json\/wp\/v2\/tags?post=489"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}