Installing (and upgrading) a Learn Developer Machine from scratch

Blog Post created by mc0078052 on Feb 6, 2019

Let me start by saying how much I appreciate the Developer Virtual Machine and the work that Blackboard has put into optimizing the install and upgrade process of Learn.


Unfortunately, I cannot use the DVM directly on my machine because I already have Hyper-V in use. It would require bcdedit & a reboot to toggle hypervisors.


To get going "quickly," I used VirtualBox's tools to convert the .vmdk to a .vhd. Then I manually attached the hard disk to a custom VM in Hyper-V. It works perfectly with the exception of shutting down/rebooting. The VM has to be reset again to get the bootloader to fire. My DVM eventually needs to be rebooted after repeatedly deploying a building block.


Our production and test environments are inside Blackboard's managed hosting, so going into this I had 0 experience with the server-side operations of Learn.


Once my project was done, I started to get curious. Now that I'll need to test my building block regularly, how do I install a signed certificate so that testers' browsers stop complaining?

  • The official documentation briefly goes over installing a signed certificate on a single-server install, but it leaves out the fact that the keystore password is now encrypted.

The DVM available was out of date. How do I upgrade it?

  • The upgrade process requires an that was not left behind by the creator of the DVM. I found a blog post Upgrade Your DVM that includes everything you need to upgrade to the latest Learn version.


Ultimately the DVM is designed to be a throw-away install, but I need it to last longer and be a tad more secure. So I've documented the install process from scratch.


You will need:


RHEL 7.5 is a supported operating system, and I am familiar with it, so I am using CentOS.


You will also need a way to transfer files to the server. There are several ways including using scp, wget from a [local] server, mounting a share, or configuring samba.


With the hard drive capacity set to 20 GB, it will be 52% full after Learn installation. If you delete the, it will be 43% full. If you delete the installer directory, it will be 27% full.


(Scenario A) Hyper-V Virtual Machine Setup


I use Hyper-V Manager on Windows 10 Pro.


Manually create a new Virtual Machine

  • under Specify Generation: select Generation 2
  • under Assign Memory: set 4096 MB, DISABLE the option "Use Dynamic Memory for this virtual machine"
  • under Configure Networking: select external switch
  • under Connect Virtual Hard Disk: create a virtual hard disk with 20 GB
  • under Installation Options: select Install an operating system from a bootable image file, and select .iso


Edit the newly created VM Settings

  • under Security: DISABLE the option "Enable Secure Boot"
  • under Processor: increase the "Number of virtual processors" (at least 2)
  • under Network Adapter: verify settings are correct (I need to set a VLAN id in my environment)
  • under Checkpoints: DISABLE the option "Use automatic checkpoints" - unless you want them!


(Scenario B) vSphere 6.5 (ESXi) Virtual Machine Setup


Manually create a new Virtual Machine

  • under Select a guest OS: select "Linux" family, select "CentOS 7 (64-bit)" version
  • under Customize hardware:
    • CPU: 2, cores per socket
    • Memory: 4 GB
    • New Hard disk: 20 GB
    • New Network: as needed
    • New CD/DVD Drive:
      • if you uploaded the iso to a datastore, select Datastore ISO file and ENABLE the option "Connect At Power On"
      • otherwise, select Client Device - you must manually connect the iso from the remote console


Starting the server build


Start the VM

  • The iso's bootloader should fire. You might have to play with the boot order.
  • Select Install CentOS 7


CentOS installer

  • under Network & Host Name:
    • click ON to enable Ethernet
    • set the Hostname if desired - make sure you click apply
  • if you are not using the Minimal installer iso:
    • under Software Selection: select Minimal Install


During install

  • under Root Password:
    • set Password as needed
  • under User Creation
    • set "Full name" to bbuser
    • set "User name" to bbuser
    • ENABLE the option "Make this user administrator"
    • set Password as needed




Post OS install


If you need to manually set a static IP, using the console, login as bbuser

$ sudo nmtui

  • Edit a connection
    • set IPv4 - manual (did not test with IPv6 - ignore)
  • Activate a connection
    • toggle state for IP changes to take affect immediately


Login as bbuser using SSH


(optional) configure the wheel group to not require a password for sudo

$ echo '%wheel ALL=(ALL) NOPASSWD: ALL' | sudo tee /etc/sudoers.d/wheel


Update to the latest packages

$ sudo yum update -y


Install the needed packages

$ sudo yum install open-vm-tools wget unzip postgresql-server postgresql-contrib policycoreutils-python


Transfer the latest Oracle JDK 8 for Linux x64 rpm to server (can be deleted later to reclaim space)


Install the jdk8 rpm

$ sudo yum localinstall jdk-8uXXXXX-linux-x64.rpm


Add a missing shared library for Java (the rpm does not include the link).

  • You can confirm this by running $ ldd `which java`

$ echo '/usr/java/latest/jre/lib/amd64/jli' | sudo tee /etc/; sudo ldconfig


Add JAVA_HOME (not required, but recommended) and PGDATA (required, location of DB) to default environment

$ echo -e '#!/bin/bash\nexport JAVA_HOME=/usr/java/latest\nexport PGDATA=/usr/local/bbdata' | sudo tee /etc/profile.d/


(optional) prepare for publickey auth

$ mkdir --mode=750 ~/.ssh; touch ~/.ssh/authorized_keys; chmod 640 ~/.ssh/authorized_keys

  • add your keys


Configure OpenSSH to only allow bbuser to login (no root)

$ echo -e 'AllowUsers bbuser' | sudo tee -a /etc/ssh/sshd_config


(option A) Redirect 8080/8443 to 80/443

$ sudo firewall-cmd --permanent --add-masquerade; sudo firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080; sudo firewall-cmd --permanent --add-forward-port=port=443:proto=tcp:toport=8443


(option B) Open 8080/8443

$ sudo firewall-cmd --permanent --add-port=8080/tcp; sudo firewall-cmd --permanent --add-port=8443/tcp


(optional) Allow postgresql remotely

$ sudo firewall-cmd --permanent --add-service=postgresql


(optional) Allow tomcat debugging remotely

$ sudo firewall-cmd --permanent --add-port=2222/tcp


(optional) Configure postfix if you need local mail configuration

$ sudo vi /etc/postfix/; sudo systemctl enable postfix


Set max number of open file descriptors

$ echo -e '@bbuser soft nofile 5000\n@bbuser hard nofile 5000' | sudo tee -a /etc/security/limits.conf


Overwrite default PGDATA environment variable for postgresql service

$ echo -e '.include /lib/systemd/system/postgresql.service\n[Service]\nEnvironment=PGDATA=/usr/local/bbdata' | sudo tee /etc/systemd/system/postgresql.service


Create PGDATA directory and assign ownership to postgres

$ sudo mkdir -p /usr/local/bbdata; sudo chown postgres:postgres /usr/local/bbdata


Relabel PGDATA's SELINUX context

$ sudo semanage fcontext -a -s system_u -t postgresql_db_t '/usr/local/bbdata(/.*)?'; sudo restorecon /usr/local/bbdata


(if ever needed) Disable SELINUX

  • temporarily:
    • $ sudo setenforce permissive
  • permanently, requires reboot
    • $ sudo sed -i -e 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config


Reboot so that environment, firewall, and other settings take affect

$ sudo reboot


Login as bbuser using SSH


Initialize postgresql db; enable and start service

$ sudo postgresql-setup initdb; sudo systemctl enable postgresql; sudo systemctl start postgresql


Launch into psql as postgres

$ sudo -i -u postgres psql


From the postgresql shell paste in:

  • \q


Configure postgresql authentication to require a password instead of using "current user"

$ sudo sed -i -e 's/ ident$/ md5/' -e 's/ peer$/ md5/' $PGDATA/pg_hba.conf; sudo systemctl restart postgresql


Add blackboard service to systemd; enable it

$ echo -e '[Unit]\nDescription=blackboard\nAfter=postgresql.service\n\n[Service]\nLimitNOFILE=5000\nType=oneshot\nRemainAfterExit=yes\nExecStart=/usr/local/blackboard/tools/admin/ services.start\nExecStop=/usr/local/blackboard/tools/admin/ services.stop\n\n[Install]\' | sudo tee /etc/systemd/system/blackboard.service; sudo systemctl enable blackboard


Create blackboard and bbinstaller directories and assign ownership to bbuser

$ sudo mkdir -p /usr/local/blackboard /usr/local/bbinstaller; sudo chown bbuser:bbuser /usr/local/blackboard /usr/local/bbinstaller


Create blackboard license file to server (plain-text in /usr/local/blackboard/config/license/blackboard-license.xml in a working DVM/test environment)

$ vi /usr/local/bbinstaller/blackboard-license.xml


Create (included below)

$ vi /usr/local/bbinstaller/



Install (and upgrade) Learn


Create a checkpoint/snapshot. It should be taken when the system is shutdown for maximum data consistency.


Set temporary variable with the new version. For example $ BBVERSION='3500_0_2'

$ BBVERSION='version_tag'


Create install directory (can be deleted later to reclaim space)

$ mkdir /usr/local/bbinstaller/$BBVERSION


Transfer file to server (can be deleted later to reclaim space)


Unzip to install directory

$ unzip -d /usr/local/bbinstaller/$BBVERSION


Temporarily change to installer directory and then run the installer

$ pushd /usr/local/bbinstaller/$BBVERSION; ./ -c /usr/local/bbinstaller/; popd


Run the PushConfigUpdates admin tool (also tries to fix any permission issues)

$ /usr/local/blackboard/tools/admin/ --no-restart


Start the blackboard service

$ sudo systemctl restart blackboard



(optional) Install (and upgrade) Starting Block


Blackboard periodically releases an updated Starting Block in their repository.


Upload closest Starting Block war to server - can be older, but not newer


Test to make sure it works


Troubleshoot using the output in /usr/local/blackboard/logs/bb-services-log.txt



(optional) Implement CA-signed Certificate for HTTPS


Run the ShowCleartextPasswords admin tool to output the current keystore password

$ /usr/local/blackboard/tools/admin/ | grep appserver.keystore


Set temporary variable with the keystore file

$ BBKEYSTORE='/usr/local/blackboard/config/keystores/tomcat.keystore'


Rename current keystore so we have it in case of issues



Generate new private key for certificate

$ keytool -genkeypair -keystore $BBKEYSTORE -storetype jks -alias tomcat -keysize 2048 -keyalg RSA

  • When asked for "first and last name" input the CN of the certificate.


Output CSR

$ keytool -certreq -keystore $BBKEYSTORE -alias tomcat


Submit CSR to CA. The resulting signed certificate file should be a .p7b.


Transfer signedcert.p7b to server (it is plain text)


Import signed certificate into keystore

$ keytool -importcert -trustcacerts -keystore $BBKEYSTORE -alias tomcat -file signedcert.p7b


Run the PushConfigUpdates admin tool

$ /usr/local/blackboard/tools/admin/ --no-restart --fast


Restart blackboard service

$ sudo systemctl restart blackboard



Contents of


## Hostname and port numbers used when building URLS that get sent ##
##        to browsers or included in notification emails.        ##
##  These should reflect the frontend hostname and ports that are  ##
## used to access the application.                                ##


##  NOTE: this property is read at initial installation only. The  ##
##  value thereafter is set on Admin->System Config->Email Config  ##


## The SMTP host name via which Learn sends email out. This is a mandatory setting. ##
## The port on SMTP server port which Learn connects to send email. It will be protocol-default if not specified. ##
##  The boolean flag to indicate whether the SMTP server requires authentication, the value can be either true or false(default)  ##
## Connection type specifies the way how the emails be encrypted, the valid options are ##
## Default:  the emails are not encrypted.                                              ##
## StartTLS: the emails are encrypted via TLS                                          ##
## SSL:      The emails are emails via SSL                                              ##
## Any other value (including null) will be token as Default.                          ##


##                  tomcat developer properties                    ##


# default passwords inside Learn, modify as desired #




# default postgresql passwords, as pre-configured #


## Enable caching of plugins to local file systems for performance ##
# true - Mimics B2 behavior of Learn SaaS


# typical defaults, do not need editing #






bbconfig.database.server.fullhostname=localhost, Inc.


##      java virtual machine config - java bound processes        ##


bbconfig.jvm.options.extra.tomcat=-XX:+UseCompressedOops -XX:+DoEscapeAnalysis -Xverify:none




# end of



Feb 11, 2019 - added Starting Block installation