I will start by saying I reccomend installing on Ubuntu. On rpm-based distros you cannot automatically update the package, and must do it manually, increasing the risk of user error and data loss, and increasing demand on manual labour. You must backup your .env file, and the contents of the public/storage directory, to preserve your data before updating. Updating must be done manually by downloading the source code, and installing with composer and node, which are unnecessary for you on Ubuntu also. This guide will be preserved mostly for reference, educational purposes, freedom of choice, etc. I initially tried hosting on CentOS 8 before any other distro, which led me into all of this.
This guide is also going to be updated and supported more slowly now than the Ubuntu guide, so expect the Ubuntu guide to be more up to date, and effectively written.
Update OS, and enable repo for latest version of PHP 7.4
$ sudo yum update
$ sudo yum install yum-utils http://rpms.remirepo.net/enterprise/remi-release-8.rpm
$ sudo yum module reset php
$ sudo yum module enable php:remi-7.4
$ sudo yum install gcc-c++ make php php-{fpm,bcmath,ctype,fileinfo,json,mbstring,pdo,tokenizer,xml,curl,zip,gmp,gd,mysqli} mariadb-server -y
$ sudo yum install libXcomposite libXcursor libXdamage libXext libXi libXtst libmng libXScrnSaver libXrandr libXv alsa-lib cairo pango atk at-spi2-atk gtk3
$ sudo systemctl start mariadb
$ sudo systemctl enable mariadb
$ mysql_secure_installation
Set the password for the root user of the SQL database. Make sure to keep record of this and do not lose it. You will need it in the next step, and for future maintenance of DB
Remove anonymous users? [Y/n] y
Disallow root login remotely? [Y/n] y
Remove test database and access to it? [Y/n] y
Reload privilege tables now? [Y/n] y
$ mysql -u root -p
Enter Password: ******
MariaDB .. > create database invoices;
MariaDB .. > create user 'ninja'@'localhost' identified by 'ninjapass';
MariaDB .. > grant all privileges on invoices.* to 'ninja'@'localhost';
MariaDB .. > flush privileges;
$ curl -sL https://rpm.nodesource.com/setup_14.x | sudo -E bash
$ sudo yum install nodejs -y
$ node -v
v14.7.0
$ npm -v
6.14.7
I will not be giving instructions on other SSL certification methods. You can find and change the appopriate lines in the NGINX config step after this, if you plan to use another cert of your own.
$ sudo mkdir -p /etc/nginx/cert/
$ sudo openssl req -new -x509 -days 365 -nodes -out /etc/nginx/cert/ninja.crt -keyout /etc/nginx/cert/ninja.key
$ sudo chmod 600 /etc/nginx/cert/*
$ sudo yum install nginx
$ sudo vim /etc/nginx/conf.d/invoiceninja.conf
You may specify your own SSL certificate in this file if you are not using openssl above. You will also specify your own domain name below, as per your DNS records or etc.
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
server_name invoices.example.ca;
# Here, enter the path to your invoiceninja directory, in the public dir.
root /usr/share/nginx/invoiceninja/public;
client_max_body_size 20M;
gzip on;
gzip_types application/javascript application/x-javascript text/javascript text/plain application/xml application/json;
gzip_proxied no-cache no-store private expired auth;
gzip_min_length 1000;
index index.php index.html index.htm;
# Enter the path to your existing ssl certificate file, and certificate private key file
# If you don’t have one yet, you can configure one with openssl in the next step.
ssl_certificate "/etc/nginx/cert/ninja.crt";
ssl_certificate_key "/etc/nginx/cert/ninja.key";
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_ciphers 'AES128+EECDH:AES128+EDH:!aNULL';
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
charset utf-8;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
if (!-e $request_filename) {
rewrite ^(.+)$ /index.php?q= last;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# Here we pass to php-fpm listen socket. For configuration see /etc/php-fpm.d/*.conf.
fastcgi_pass unix:/var/run/php-fpm/www.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_intercept_errors off;
fastcgi_buffer_size 16k;
fastcgi_buffers 4 16k;
}
location ~ /\.ht {
deny all;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
access_log /var/log/nginx/ininja.access.log;
error_log /var/log/nginx/ininja.error.log;
sendfile off;
}
server {
listen 80;
server_name invoices.example.ca;
add_header Strict-Transport-Security max-age=2592000;
rewrite ^ https://$server_name$request_uri? permanent;
}
$ sudo mkdir -p /usr/share/nginx/invoiceninja
$ sudo chown -R nginx:nginx /usr/share/nginx/invoiceninja
$ sudo systemctl start nginx
$ sudo systemctl enable nginx
This is only for testing before deployment situations, or for setting up pointers to local instances of InvoiceNinjav4 that you want to migrate your data from (Both instances v4 and v5 must be running on separate environments with resolvable domain names to successfully migrate data from within the InvoiceNinja v4 web UI, on the latest patched versions of v4 only).
modify hosts file to point at server IP with given domain name in nginx, if you do not yet have DNS pointed at the server, or do not yet want to point your DNS at it.
$ sudo vi /etc/hosts
and simply add your domain to the end of this list for localhost, as seen here, or add the remote IP, followed by a space, and the domain name to point at it, all on a new line, like seen below again. NGINX should reroute any http request on the domain (not the direct IP or localhost) name to https.
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 invoices.example.ca
192.168.0.88 invoices2.example.ca
$ sudo firewall-cmd --zone=public --add-service=http --permanent
$ sudo firewall-cmd --zone=public --add-service=https --permanent
$ sudo firewall-cmd --reload
$ sudo vim /etc/php-fpm.d/www.conf
user = nginx
group = nginx
listen = /var/run/php/php-fpm.sock
listen.owner = nginx
listen.group = nginx
listen.mode = 0660
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp
$ sudo mkdir -p /var/lib/php/session
$ sudo mkdir -p /var/run/php/
$ sudo chown -R nginx:nginx /var/lib/php/session/
$ sudo chown -R nginx:nginx /var/run/php/
$ sudo setfacl -dm u:nginx:rwx /var/lib/php/session
$ sudo setfacl -dm u:nginx:rwx /var/lib/php
$ sudo systemctl start php-fpm
$ sudo systemctl enable php-fpm
$ cd /usr/share/nginx/invoiceninja
Fedora, and RedHat based users will need to install source code to run the program, because npm
pulls different dependencies for some systems. So use git
to download the latest code, and pull the v5-stable
branch for the best current release available.
$ sudo git clone https://github.com/invoiceninja/invoiceninja
$ sudo rsync -av invoiceninja/ ./
$ sudo rm -rf invoiceninja
$ git checkout v5-stable
Generate your .env file.
$ sudo cp .env.example .env
Back this up!!
Remember, this key you generated just now is an encryption key for the contents of the database entries. You need this to access any of the data on the SQL database. The artisan key:generate command populates the .env file with your encryption key. after you run this and finish setting up your db and logging into the web client, you really should backup the working .env file somewhere secure, in case you ever clobber it.
If you don't have Composer installed on your server yet, you will install it now, so you can download dependencies for PHP stuff with it.
$ curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/bin --filename=composer
As pointed out by another user, we can install the same versions of software that the developers intended by running composer option 'install' instead of 'update'. 'Update' option would update the software dependencies to new versions (not standardized versions) and update the .lock file included in the package as well. The '--no-dev' option stops composer from installing dependencies for the developers only.
$ sudo -u nginx php -d memory_limit=-1 `which composer` i --no-dev
increase PHP memory limit yourself, and no longer forced to workaround memory limmit issue, get better PHP performance too, as long as you have good amount of RAM to use. Use your text editor of choice.
$ sudo vim /etc/php.ini
You will find and edit the line beginning with memory_limit =
and change the following value to 1024M
. In vim, remember you can find fast with the '/' key in command mode.
memory_limit = 1024M
From then on you can run the following instead of this especially long elaborate php command above:
$ sudo -u nginx php composer i --no -dev
Running npm install as opposed to npm update again has the same intended purpose of standardization of dependencies. The '--no-optional' argument will instruct npm to ignore dependencies for other OS and you won't see the errors when those cannot install.
$ sudo chown -R nginx:nginx ./
$ sudo -u nginx npm install --no-optional
Now we a assign a randomly generated application encryption key to the software.
$ sudo -u nginx php artisan key:generate
And then auto-configure the server. Run php artisan optimize
again anytime you edit the files or make changes in the invoiceninja installation directory.
$ sudo -u nginx php artisan optimize
I've summarized the best I know how, to configure SELINUX appropriately for all the features in the current release of invoiceninja v5, but I might not be able to update this or support you in the future if the codebase changes a bit, or you uncover new behaviours that demand new permissions from SELinux. If you do not want to support SELinux, you can permanently set it to permissive mode or disabled. This is a personal choice, and I cannot reccomend either or for you.
Change the following line from 'enforcing' to or 'disabled' to permanently change SELINUX state. The Gentoo wiki has a nice page for more reading about the difference between these states: https://wiki.gentoo.org/wiki/SELinux/Tutorials/Permissive_versus_enforcing
$ sudo vim /etc/selinux/config
...
SELINUX=disabled
The above command typically only takes affect after reboot.
Under permissive mode, some context rules will still be applied, but those rules can be pre-emptively allowed with the commands below. First though, we must enable SELinux permissive mode, in order to effectively complete the InvoiceNinja setup, especially for PDF rendering and Chrome/Puppeteer, which requires special permissions that I am not smart enough to allow in advance yet. To temporarily set SELINUX to permissive mode, until the next reboot, run the following:
$ sudo setenforce 0
$ sudo yum install policycoreutils-python-utils
$ sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja(/.*)?'
$ sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja/public(/.*)?'
$ sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja/storage(/.*)?'
$ sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja/app(/.*)?'
$ sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja/bootstrap(/.*)?'
$ sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja/config(/.*)?'
$ sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja/database(/.*)?'
$ sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja/resources(/.*)?'
$ sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja/vendor(/.*)?'
$ sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja/tests(/.*)?'
$ sudo restorecon -Rv '/usr/share/nginx/invoiceninja/'
$ sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja(/.*)?'; sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja/public(/.*)?'; sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja/storage(/.*)?'; sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja/app(/.*)?'; sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja/bootstrap(/.*)?'; sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja/config(/.*)?'; sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja/database(/.*)?'; sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja/resources(/.*)?'; sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja/vendor(/.*)?'; sudo semanage fcontext -a -t httpd_sys_rw_content_t '/usr/share/nginx/invoiceninja/tests(/.*)?'; sudo restorecon -Rv '/usr/share/nginx/invoiceninja/'
$ sudo setsebool -P httpd_unified 1
$ sudo setsebool -P httpd_execmem 1
$ sudo setsebool -P httpd_can_network_connect 1
And for users in a VM, you will have an additional policy to set I believe, for example, while testing myself, I use VMware Workstation Player on Windows, and must set the policy:
$ setsebool -P use_virtualbox 1
I cannot provide a full instruction set on explicitly and cleanly setting SELINUX permissions with InvoiceNinja. Depending on your environment, (VM, bare metal, etc), you may have different daemons requesting different permissions also. What I can suggest, is enabling Cockpit web console, and using the SELINUX tab to manage SELINUX permission requests with. The most effective method, is to set SELINUX to permissive mode in cockpit before attempting invoiceninja setup on web page and after testing PDF, email, and logging in after completing setup, address any SELINUX conflicts reported in cockpit, and then re-enable SELINUX enforcing mode. Also note, this will probably be an ongoing thing to monitor, so as you test features in the new InvoiceNinja, you should enable permissive mode on SELINUX again when troubleshooting them, and monitor for SELINUX conflicts. Log into cockpit at https://127.0.0.1:9090 by default, with your username and password from the system.
$ sudo systemctl enable --now cockpit.socket
When running setup the first time, you will fail PDF test with SELINUX in enforcing, \until you attempt PDF test, and run the following, then run PDF test and fail again, and then run following commands a second time in a row - a lot easier to manage if you set SELINUX to 'permissive' and run any conflict resolutions from cockpit after.
$ sudo ausearch -c 'chrome' --raw | audit2allow -M my-chrome
$ semodule -X 300 -i my-chrome.pp
Unfortunately, beyond these steps with SELINUX I cannot offer a more proficient command list. I am still running mostly in enforcing mode, switching back to permissive as I note a broken feature and actively monitor my SELINUX conflicts while testing features and performing work. If someone finds some modifications to the instructions that might accomodate SELINUX better, and pre-emptively configure permissions and policies for chromium and etc, then let me know somehow, I would appreciate that.
Two things need to be backed up, if you don't just backup your entire nginx directory often. You need a backup of your .env file, from the installation directory. This file has a complicated encryption key hash saved, and if this key is lost, you will lose access to your database basically, with no workaround. The entries are encrypted with this key. The other thing we can learn to backup breifly is the database as well. Both can be done with one line commands, that you must modify for your environment. Preferably to a mountpoint on another physical storage device, so that you have something to rebuild with if you lose this one suddenly.
I will not cover it here since there's many resources for it already, but I suggest to setup cron jobs to perform this DB and env backup on a regular basis.
For "ROOTPASS" you can skip this variable and it will prompt for password, but to automate this, you need to pass the password in plain text. You could probably use any other user for this with the permissions set for the whole db though, to avoid passing rootsql pass in plaintext to a cron file.
$ cp .env /mnt/yourbackupdisk/backups-invoiceninja/invoice-backup-envfile
$ sudo mysqldump -u root -p ROOTPASS invoice-db > /mnt/yourbackupdisk/backups-invoiceninja/backup-db.sql
sqldump will dump the contents of the database out for you, but the contents themselves are still encrypted and you need the .env file backed up with your key as well.
to backup your .env file, I can't stress enough, as you host on Enterprise Linux 8 at this time the only way to update is by copying source code package of the next update on top of the existing installation. You don't wan't to do something silly on accident and wipe that .env file and destroy your access to your company data.