Application Server

The hostnames referenced throughout this documentation will be elentra.med.university.edu and staging.med.university.edu, which must be replaced by your actual DNS hostnames.

This documentation can be used as a reference to create both your Production Application Server and/or Staging Application Server on a Enterprise Linux 8 virtual machine.

  1. SSH into server and sudo to root:

    ssh service@elentra.med.university.edu
    sudo -s
  2. Change the SELINUX variable in /etc/selinux/config to permissive to prevent unforeseen and difficult to diagnose issues:

    SELINUX=permissive
  3. Add the following lines to /etc/hosts file:

    127.0.0.1   elentra.med.university.edu
    127.0.0.1   staging.med.university.edu
  4. Edit the hostname of the virtual machine in the /etc/hostname file:

    elentra.med.university.edu
  5. Install screen, update RHEL, and reboot:

    dnf install screen
    screen
    dnf update
    reboot
  6. SSH back into server, and install the Inline with Upstream Stable (IUS Community) package.

    ssh service@elentra.med.university.edu
    sudo -s
    screen
    
    dnf -y install http://rpms.remirepo.net/enterprise/remi-release-8.rpm \
    https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
  7. Install Snapd and htmldoc:

    dnf -y install snapd
    systemctl enable --now snapd.socket
    ln -s /var/lib/snapd/snap /snap
    snap install htmldoc

    Note: You may have to wait a couple of minutes before running the snap install command, so that the snap database can initialize.

  8. Install Apache, OpenSSL, PHP, Git, HTMLDoc, mariadb (client), ClamAV, and NTP packages:

    dnf module install php:remi-8.1 -y
    dnf -y install git \
                   curl \
                   wget \
                   unzip \
                   openssl \
                   httpd \
                   mod_ssl \
                   php-cli \
                   php-gd \
                   php-devel \
                   php-pdo \
                   php-mysqlnd \
                   php-intl \
                   php-mbstring \
                   php-bcmath \
                   php-ldap \
                   php-imap \
                   php-soap \
                   php-xml \
                   php-xmlrpc \
                   php-tidy \
                   php-opcache \
                   php-json \
                   php-sodium \
                   php-pecl-redis \
                   php-pecl-zip \
                   mariadb \
                   clamav \
                   chrony \
                   supervisor
  9. Install wkhtmltopdf from the binary package because the yum package provided by EPEL is broken:

    curl -SL https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.4/wkhtmltox-0.12.4_linux-generic-amd64.tar.xz | tar -xJC /root && cp /root/wkhtmltox/bin/* /usr/bin
    dnf install xorg-x11-fonts-Type1
    dnf install xorg-x11-fonts-75dpi
    wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox-0.12.6.1-2.almalinux8.x86_64.rpm
    rpm -Uvh wkhtmltox-0.12.6.1-2.almalinux8.x86_64.rpm
  10. Start Apache and Supervisor, and set to start on system startup:

    systemctl enable httpd
    systemctl start httpd
    systemctl enable supervisord
    systemctl start supervisord
  11. Create a new file called /etc/php.d/elentra.ini and add the following:

    date.timezone = America/Toronto
    display_errors = Off
    error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT
    expose_php = Off
    memory_limit = 512M
    post_max_size = 512M
    session.cookie_secure = 1
    session.cookie_httponly = 1
    session.cookie_samesite = Strict
    upload_max_filesize = 512M
  12. Create an Elentra system user called production, which is used for production deployments:

    useradd -m production
    passwd production
    usermod -G apache -a production
    usermod -G production -a apache
  13. Create and permission the SSH authorized_keys file for the production user.

    cd /home/production
    mkdir /home/production/.ssh
    touch /home/production/.ssh/authorized_keys
    chown -R production:production /home/production/.ssh
    chmod 700 /home/production/.ssh
    chmod 600 /home/production/.ssh/authorized_keys
  14. Add all developers' SSH public keys (i.e. cat ~/.ssh/id_rsa.pub) that are allowed to deploy Elentra to your production environment to the new authorized_keys file.

    vim /home/production/.ssh/authorized_keys
  15. Create an Elentra system user called staging, which is used for staging deployments:

    useradd -m staging
    passwd staging
    usermod -G apache -a staging
    usermod -G staging -a apache
  16. Create and permission the SSH authorized_keys file for the staging user.

    cd /home/staging
    mkdir /home/staging/.ssh
    touch /home/staging/.ssh/authorized_keys
    chown -R staging:staging /home/staging/.ssh
    chmod 700 /home/staging/.ssh
    chmod 600 /home/staging/.ssh/authorized_keys
  17. Add all developers' SSH public keys (i.e. cat ~/.ssh/id_rsa.pub) that are allowed to deploy Elentra to your staging environment to the new authorized_keys file.

    vim /home/staging/.ssh/authorized_keys
  18. Create and appropriately permission the Apache document root and Elentra storage directories for production.

    mkdir -p /var/www/vhosts/elentra.med.university.edu/storage/
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/annualreports
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/app
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/app/cbe-uploads
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/app/cbe-uploads/learner-self-assessment-files
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/app/public
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/cache
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/cbme-uploads
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/cbme-uploads/advisor-files
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/community-discussions
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/community-galleries
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/community-shares
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/eportfolio
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/event-files
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/exam-files
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/framework
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/framework/cache
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/framework/cache/data
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/framework/sessions
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/framework/views
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/logs
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/lor
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/msprs
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/resource-images
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/secure-access
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/syllabi
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/tmp
    mkdir /var/www/vhosts/elentra.med.university.edu/storage/user-photos
    chown -R production:production /var/www/vhosts/elentra.med.university.edu
    chmod -R 777 /var/www/vhosts/elentra.med.university.edu/storage/*
  19. Create and appropriately permission the Apache document root and Elentra storage directories for staging.

    mkdir -p /var/www/vhosts/staging.med.university.edu/storage/
    mkdir /var/www/vhosts/staging.med.university.edu/storage/annualreports
    mkdir /var/www/vhosts/staging.med.university.edu/storage/app
    mkdir /var/www/vhosts/staging.med.university.edu/storage/app/cbe-uploads
    mkdir /var/www/vhosts/staging.med.university.edu/storage/app/cbe-uploads/learner-self-assessment-files
    mkdir /var/www/vhosts/staging.med.university.edu/storage/app/public
    mkdir /var/www/vhosts/staging.med.university.edu/storage/cache
    mkdir /var/www/vhosts/staging.med.university.edu/storage/cbme-uploads
    mkdir /var/www/vhosts/staging.med.university.edu/storage/cbme-uploads/advisor-files
    mkdir /var/www/vhosts/staging.med.university.edu/storage/community-discussions
    mkdir /var/www/vhosts/staging.med.university.edu/storage/community-galleries
    mkdir /var/www/vhosts/staging.med.university.edu/storage/community-shares
    mkdir /var/www/vhosts/staging.med.university.edu/storage/eportfolio
    mkdir /var/www/vhosts/staging.med.university.edu/storage/event-files
    mkdir /var/www/vhosts/staging.med.university.edu/storage/exam-files
    mkdir /var/www/vhosts/staging.med.university.edu/storage/framework
    mkdir /var/www/vhosts/staging.med.university.edu/storage/framework/cache
    mkdir /var/www/vhosts/staging.med.university.edu/storage/framework/cache/data
    mkdir /var/www/vhosts/staging.med.university.edu/storage/framework/sessions
    mkdir /var/www/vhosts/staging.med.university.edu/storage/framework/views
    mkdir /var/www/vhosts/staging.med.university.edu/storage/logs
    mkdir /var/www/vhosts/staging.med.university.edu/storage/lor
    mkdir /var/www/vhosts/staging.med.university.edu/storage/msprs
    mkdir /var/www/vhosts/staging.med.university.edu/storage/resource-images
    mkdir /var/www/vhosts/staging.med.university.edu/storage/secure-access
    mkdir /var/www/vhosts/staging.med.university.edu/storage/syllabi
    mkdir /var/www/vhosts/staging.med.university.edu/storage/tmp
    mkdir /var/www/vhosts/staging.med.university.edu/storage/user-photos
    chown -R staging:staging /var/www/vhosts/staging.med.university.edu
    chmod -R 777 /var/www/vhosts/staging.med.university.edu/storage/*
  20. Generate the SSL private keys required for each of your hostnames:

    mkdir -p /root/certificates/2023
    cd /root/certificates/2023
    openssl genrsa -out elentra.med.university.edu.key 2048
    openssl genrsa -out staging.med.university.edu.key 2048
  21. Generate the SSL certificate signing requests (CSRs) for your certificate authority for each of your hostnames.

    openssl req -new -key elentra.med.university.edu.key -out elentra.med.university.edu.csr
    openssl req -new -key staging.med.university.edu.key -out staging.med.university.edu.csr
    • You will be asked a number of questions, answer accordingly, but do not answer enter anything for "Email Address", "A challenge password", or "An optional company name:

      You are about to be asked to enter information that will be incorporated
      into your certificate request.
      What you are about to enter is what is called a Distinguished Name or a DN.
      There are quite a few fields but you can leave some blank
      For some fields there will be a default value,
      If you enter '.', the field will be left blank.
      -----
      Country Name (2 letter code) [XX]:CA
      State or Province Name (full name) []:Ontario
      Locality Name (eg, city) [Default City]:Kingston
      Organization Name (eg, company) [Default Company Ltd]:Queen's University
      Organizational Unit Name (eg, section) []:Health Sciences Education Technology Unit
      Common Name (eg, your name or your server's hostname) []:elentra.med.university.edu
      Email Address []:
      Please enter the following 'extra' attributes
      to be sent with your certificate request
      A challenge password []:
      An optional company name []:
  22. If you have a valid Certificate Authority certificate, you should create a .crt file foreach hostname and paste in the certificate text:

    vim /root/certificates/2023/elentra.med.university.edu.crt
    vim /root/certificates/2023/staging.med.university.edu.crt
    • You will also likely have a certificate authority root chain certificate. Also paste this into a file called ca-certificate.crt

  23. If you are only creating self-signed certificates, you should do this for each hostname:

    openssl x509 -req -days 365 -in elentra.med.university.edu.csr -signkey elentra.med.university.edu.key -out elentra.med.university.edu.crt
    openssl x509 -req -days 365 -in staging.med.university.edu.csr -signkey staging.med.university.edu.key -out staging.med.university.edu.crt
  24. Install the certificates in the Apache virtual host directory:

    mkdir /var/www/vhosts/elentra.med.university.edu/cert/
    cp /root/certificates/2023/elentra.med.university.edu.crt /var/www/vhosts/elentra.med.university.edu/cert/
    cp /root/certificates/2023/elentra.med.university.edu.key /var/www/vhosts/elentra.med.university.edu/cert/
    mkdir /var/www/vhosts/staging.med.university.edu/cert/
    cp /root/certificates/2023/staging.med.university.edu.crt /var/www/vhosts/staging.med.university.edu/cert/
    cp /root/certificates/2023/staging.med.university.edu.key /var/www/vhosts/staging.med.university.edu/cert/
  25. Create the Apache VirtualHosts by creating a file named 000-elentra.conf and placing it /etc/httpd/conf.d/.

    • For configurations where the web server is directly accessible from the internet, the file should contain the following:

      # This will limit what information Apache reveals about itself.
      ServerTokens Prod
      ServerSignature Off
      TraceEnable Off
      
      SSLStaplingCache "shmcb:logs/stapling-cache(150000)"
      
      # Apache performance tuning options for more connections.
      #<IfModule mpm_prefork_module>
      #    MaxRequestWorkers 512
      #    ServerLimit 512
      #</IfModule>
      
      # Production
      <VirtualHost *:80>
          ServerName elentra.med.university.edu
          ServerAdmin sysadmin@med.university.edu
      
          RewriteEngine On
          RewriteCond %{HTTPS} off
          RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
      </VirtualHost>
      <VirtualHost *:443>
          ServerName elentra.med.university.edu:443
          ServerAdmin sysadmin@med.university.edu
      
          SSLEngine on
          SSLProtocol -all +TLSv1.2
          SSLHonorCipherOrder on
          SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH
      
          SSLCertificateFile /var/www/vhosts/elentra.med.university.edu/cert/elentra.med.university.edu.crt
          SSLCertificateKeyFile /var/www/vhosts/elentra.med.university.edu/cert/elentra.med.university.edu.key
          #SSLCACertificateFile /var/www/vhosts/elentra.med.university.edu/cert/ca-certificate.crt
      
          SSLUseStapling on
      
          Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains;"
          Header always set X-Frame-Options SAMEORIGIN
      
          DocumentRoot /var/www/vhosts/elentra.med.university.edu/current/www-root
          <Directory "/var/www/vhosts/elentra.med.university.edu/current/www-root">
              Options FollowSymLinks
              Require all granted
              AllowOverride all
          </Directory>
      	ProxyPass "/app/" "ws://localhost:6000/app/"
          ProxyPassReverse "/app/" "ws://localhost:6000/app/"
          ProxyPass "/apps/" "http://localhost:6000/apps/"
          ProxyPassReverse "/apps/" "http://localhost:6000/apps/"
      </VirtualHost>
      
      # Staging
      <VirtualHost *:80>
          ServerName staging.med.university.edu
          ServerAdmin sysadmin@med.university.edu
      
          RewriteEngine On
          RewriteCond %{HTTPS} off
          RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
      </VirtualHost>
      <VirtualHost *:443>
          ServerName staging.med.university.edu:443
          ServerAdmin sysadmin@med.university.edu
      
          SSLEngine on
          SSLProtocol -all +TLSv1.2
          SSLHonorCipherOrder on
          SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH
      
          SSLCertificateFile /var/www/vhosts/staging.med.university.edu/cert/staging.med.university.edu.crt
          SSLCertificateKeyFile /var/www/vhosts/staging.med.university.edu/cert/staging.med.university.edu.key
          #SSLCACertificateFile /var/www/vhosts/staging.med.university.edu/cert/ca-certificate.crt
      
          SSLUseStapling on
      
          Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains;"
          Header always set X-Frame-Options SAMEORIGIN
      
          DocumentRoot /var/www/vhosts/staging.med.university.edu/current/www-root
          <Directory "/var/www/vhosts/staging.med.university.edu/current/www-root">
              Options FollowSymLinks
              Require all granted
              AllowOverride all
          </Directory>
      	ProxyPass "/app/" "ws://localhost:6000/app/"
          ProxyPassReverse "/app/" "ws://localhost:6000/app/"
          ProxyPass "/apps/" "http://localhost:6000/apps/"
          ProxyPassReverse "/apps/" "http://localhost:6000/apps/"
      </VirtualHost>
    • For configurations where a load balancer sits in front of the web server and the load balancer communicates with the web server over http, the file should look like this:

      # This will limit what information Apache reveals about itself.
      ServerTokens Prod
      ServerSignature Off
      TraceEnable Off
      
      SSLStaplingCache "shmcb:logs/stapling-cache(150000)"
      
      # Apache performance tuning options for more connections.
      #<IfModule mpm_prefork_module>
      #    MaxRequestWorkers 512
      #    ServerLimit 512
      #</IfModule>
      
      # Production
      <VirtualHost *:80>
          ServerName elentra.med.university.edu
          ServerAdmin sysadmin@med.university.edu
      
          DocumentRoot /var/www/vhosts/elentra.med.university.edu/current/www-root
          <Directory "/var/www/vhosts/elentra.med.university.edu/current/www-root">
              Options FollowSymLinks
              Require all granted
              AllowOverride all
          </Directory>
      
          ProxyPass "/app/" "ws://localhost:6000/app/"
          ProxyPassReverse "/app/" "ws://localhost:6000/app/"
          ProxyPass "/apps/" "http://localhost:6000/apps/"
          ProxyPassReverse "/apps/" "http://localhost:6000/apps/"
      </VirtualHost>
      
      # Staging
      <VirtualHost *:80>
          ServerName staging.med.university.edu
          ServerAdmin sysadmin@med.university.edu
      
          DocumentRoot /var/www/vhosts/staging.med.university.edu/current/www-root
          <Directory "/var/www/vhosts/staging.med.university.edu/current/www-root">
              Options FollowSymLinks
              Require all granted
              AllowOverride all
          </Directory>
      
          ProxyPass "/app/" "ws://localhost:6000/app/"
          ProxyPassReverse "/app/" "ws://localhost:6000/app/"
          ProxyPass "/apps/" "http://localhost:6000/apps/"
          ProxyPassReverse "/apps/" "http://localhost:6000/apps/"
      </VirtualHost>
  26. Do a deployment to the new server.

  27. Point the app.json in the storage folder to the corresponding file in the code

    ln -s /var/www/vhosts/host.name.edu/current/www-root/core/storage/app/modules/app.json /var/www/vhosts/host.name.edu/storage/app/modules/app.json
  28. Create a new file in the /etc/supervisord.d directory called elentra.ini, and use the following template snippet as a reference to create your own file. Please make sure that you have the correct path in command and stdout_logfile, and that user is the correct system account that your existing cron jobs are run as.\

    [program:elentra-queue-staging]
    process_name=%(program_name)s_%(process_num)02d
    command=php /var/www/vhosts/staging.elentra.med.university.edu/current/www-root/core/library/vendor/elentrapackages/elentra-1x-api/artisan queue:work --queue=high,emails,default,low --env=staging
    autostart=true
    autorestart=true
    user=staging
    numprocs=1
    redirect_stderr=true
    stdout_logfile=/var/www/vhosts/staging.elentra.med.university.edu/storage/logs/worker.log
    
    [program:elentra-queue-production]
    process_name=%(program_name)s_%(process_num)02d
    command=php /var/www/vhosts/elentra.med.university.edu/current/www-root/core/library/vendor/elentrapackages/elentra-1x-api/artisan queue:work --queue=high,emails,default,low --env=production
    autostart=true
    autorestart=true
    user=production
    numprocs=1
    redirect_stderr=true
    stdout_logfile=/var/www/vhosts/elentra.med.university.edu/storage/logs/worker.log
    
    [program:elentra-websocket-staging]
    process_name=%(program_name)s_%(process_num)02d
    command=php /var/www/vhosts/staging.elentra.med.university.edu/current/www-root/core/library/vendor/elentrapackages/elentra-1x-api/artisan websockets:serve --port=6000 --env=staging
    autostart=true
    autorestart=true
    user=staging
    numprocs=1
    redirect_stderr=true
    stdout_logfile=/var/www/vhosts/staging.elentra.med.university.edu/storage/logs/websocket.log
    
    [program:elentra-websocket-production]
    process_name=%(program_name)s_%(process_num)02d
    command=php /var/www/vhosts/elentra.med.university.edu/current/www-root/core/library/vendor/elentrapackages/elentra-1x-api/artisan websockets:serve --port=6000 --env=production
    autostart=true
    autorestart=true
    user=production
    numprocs=1
    redirect_stderr=true
    stdout_logfile=/var/www/vhosts/elentra.med.university.edu/storage/logs/websocket.log
    
    [group:elentra]
    programs=elentra-queue-staging,elentra-queue-production,elentra-websocket-staging,elentra-websocket-production
  29. Test your new Apache configuration, then restart Apache and Supervisor.

    apachectl configtest
    systemctl restart httpd
    systemctl restart supervisord

Last updated