For the complete documentation index, see llms.txt. This page is also available as Markdown.

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