[How To] Install Turtl Server on Debian 9 with Apache ReverseProxy & SSL

2019.05.10

Project website: https://turtlapp.com/
Project Github:  https://github.com/turtl/server

 

 

 

# Install Node.js
curl -sL https://deb.nodesource.com/setup_12.x | bash -
apt install -y nodejs

 

 

 

 

# Install & Configure PostgreSQL

 

# Install the required packages:

apt install -y postgresql postgresql-contrib pwgen git

 

# confirm your installation:

sudo -u postgres psql -c "SELECT version();"

 

# Generate a secure password for PostgreSQL user :

pwgen -s 32 1

 

# access the PostgreSQL shell:

su - postgres

 

# Create a new database:

createdb turtldb

 

# Create a new PostgreSQL user:

createuser turtlusr --pwprompt

 

# Connect to the database:

psql turtldb

 

# Grant all privileges in the database to the user:

GRANT ALL PRIVILEGES ON DATABASE turtldb TO turtlusr;

 

# Exit PostgreSQL

\q

 

 

 

 

# Install Turtl server
 

# create the working directory:

mkdir /opt/turtl

 

# create a system user to run turtl service:

useradd -r -s /sbin/nologin -d /opt/turtl -U turtl

 

# clone the git repository:

cd /opt/turtl
git clone https://github.com/turtl/server && cd server

 

# Give correct ownership to turtl:

chown turtl:turtl -R /opt/turtl

 

# Log in as turtl user to install:

su turtl -s /bin/bash

 

# Install turtl:

npm install

 

 

# copy the config file and edit the variables:

cp config/config.yaml.default config/config.yaml
vi config/config.yaml

 

# fill as per the example bellow
# use an online text generator such as http://randomtextgenerator.com/ to generate the hash (remove all paragraphs)
# skip the email section if you do not wish to enable the plug-in

---
server:
# Per default, turtl will listen on all IP addresses
# You can choose the IP it will use with this parameter
host: '127.0.0.1'
port: 8181

db:
connstr: 'postgres://turtlusr:<yourpassword>@127.0.0.1:5432/turtldb'
pool: 24

loglevel: 'debug'

app:
# ALWAYS false in production. Always.
# Set to 'I UNDERSTAND THIS VIOLATES THE PRIVACY OF MY USERS' to enable
enable_bookmarker_proxy: false
# no trailing slash
api_url: 'https://turtl.<yourwebsite>.com'
www_url: 'https://<yourwebsite>.com'
emails:
    admin: 'admin@<yourwebsite>.com'
    info: 'Turtl <info@<yourwebsite>.com>'
    invites: 'invites@<yourwebsite>.com'
# TODO: replace this with a long, unique value. seriously. write down a dream
# you had, or the short story you came up with during your creative writing
# class in your freshmen year of college. have fun with it.
secure_hash_salt: "New the her nor case that lady paid read. Invitation friendship travelling eat everything the out two. Shy you who scarcely expenses debating hastened resolved. Always polite moment on is warmth spirit it to hearts. Downs those still witty an balls so chief so. Moment an little remain no up lively no. Way brought may off our regular country towards adapted cheered. She suspicion dejection saw instantly. Well deny may real one told yet saw hard dear. Bed chief house rapid right the. Set noisy one state tears which. No girl oh part must fact high my he. Simplicity in excellence melancholy as remarkably discovered. Own partiality motionless was old excellence she inquietude contrasted. Sister giving so wicket cousin of an he rather marked. Of on game part body rich. Adapted mr savings venture it or comfort affixed friends. Full age sex set feel her told. Tastes giving in passed direct me valley as supply. End great stood boy noisy often way taken short. Rent the size our more door. Years no place abode in no child my. Man pianoforte too solicitude friendship devonshire ten ask. Course sooner its silent but formal she led. Extensive he assurance extremity at breakfast. Dear sure ye sold fine sell on. Projection at up connection literature insensible motionless projecting. Feet evil to hold long he open knew an no. Apartments occasional boisterous as solicitude to introduced. Or fifteen covered we enjoyed demesne is in prepare. In stimulated my everything it literature. Greatly explain attempt perhaps in feeling he. House men taste bed not drawn joy. Through enquire however do equally herself at. Greatly way old may you present improve. Wishing the feeling village him musical. Affronting imprudence do he he everything. Sex lasted dinner wanted indeed wished out law. Far advanced settling say finished raillery. Offered chiefly farther of my no colonel shyness. Such on help ye some door if in. Laughter proposal laughing any son law consider. Needed except up piqued an. Effects present letters inquiry no an removed or friends. Desire behind latter me though in. Supposing shameless am he engrossed up additions. My possible peculiar together to. Desire so better am cannot he up before points. Remember mistaken opinions it pleasure of debating. Court front maids forty if aware their at. Chicken use are pressed removed. Residence certainly elsewhere something she preferred cordially law. Age his surprise formerly mrs perceive few stanhill moderate. Of in power match on truth worse voice would. Large an it sense shall an match learn. By expect it result silent in formal of. Ask eat questions abilities described elsewhere assurance. Appetite in unlocked advanced breeding position concerns as. Cheerful get shutters yet for repeated screened. An no am cause hopes at three. Prevent behaved fertile he is mistake on. Finished her are its honoured drawings nor. Pretty see mutual thrown all not edward ten. Particular an boisterous up he reasonably frequently. Several any had enjoyed shewing studied two. Up intention remainder sportsmen behaviour ye happiness. Few again any alone style added abode ask. Nay projecting unpleasing boisterous eat discovered solicitude. Own six moments produce elderly pasture far arrival. Hold our year they ten upon. Gentleman contained so intention sweetness in on resolving. Consulted perpetual of pronounce me delivered. Too months nay end change relied who beauty wishes matter. Shew of john real park so rest we on. Ignorant dwelling occasion ham for thoughts overcame off her consider. Polite it elinor is depend. His not get talked effect worthy barton. Household shameless incommode at no objection behaviour. Especially do at he possession insensible sympathize boisterous it. Songs he on an widen me event truth. Certain law age brother sending amongst why covered. She who arrival end how fertile enabled. Brother she add yet see minuter natural smiling article painted. Themselves at dispatched interested insensible am be prosperous reasonably it. In either so spring wished. Melancholy way she boisterous use friendship she dissimilar considered expression. Sex quick arose mrs lived. Mr things do plenty others an vanity myself waited to. Always parish tastes at as mr father dining at."
# set to true if you think it's ok to SEND invites if you have not confirmed
# your account. great for testing, not so great for production. but what do
# i know...
allow_unconfirmed_invites: false

sync:
# how many sync records can a client send at a time? it's a good idea to have
# a limit here, lest a rogue client flood the server with sync items
max_bulk_sync_records: 32

plugins:
plugin_location: '/opt/turtl/server/plugins'
# each key here corresponds to a folder name in the plugins folder, so `email`
# below would be a plugin at /var/www/turtl/server/plugins/email (see the
# example-plugins/ folder for an email plugin you can use)
email:
enabled: true
endpoint: "smtps://''<youremail>':'<yourpassword>'@<youremailserver>/?pool=true"
defaults: {}

uploads:
# if set to a path, files will be uploaded to the local filesystem instead of
# S3. otherwise, set to false
local: '/opt/turtl/server/public/uploads'
# if true, downloading local files will be proxied through the turtl server.
# this avoids needing to set up any CORS config in your favorite webserver,
# but may slightly affect performance on high-demand servers.
local_proxy: true
# if local_proxy is false, this is should be the url path the uploaded files
# are publicly available on
url: 'http://api.turtl.dev/uploads'

s3:
token: 'IHADAPETSNAKEBUTHEDIEDNOOOOO'
secret: ''
bucket: ''
endpoint: 'https://s3.amazonaws.com'

 

# Create the plugins directory

mkdir /opt/turtl/server/plugins

 

# To enable the email plugin, copy it to the correct folder

cp -R example-plugins/email plugins

 

# Install the nodemailer module

npm install nodemailer

 

# run the initial script:

./scripts/init-db.sh

 

 

# to test-run the server issue:

node server.js

 

# to exit, CTRL + c

 

# exit the turtl user shell:

exit

 

 

 

 
# Start Turtl service at boot:

 

# Create an systemd service:

vi /lib/systemd/system/turtl.service

 

# Copy/Paste the bellow:

[Unit]
Description=turtl_service
After=network.target mysql.service postgresql.service

[Service]
User=turtl
ExecStart=/usr/bin/node /opt/turtl/server/server.js
Restart=always

[Install]
WantedBy=multi-user.target

 

# Give the correct permissions:

chmod 0644 /lib/systemd/system/turtl.service

 

# start the service:

systemctl start turtl

 

# Enable the service at boot:

systemctl enable turtl

 

 

 

 

# Get a certbot ssl certificate

 

# install certbot:

apt install -y certbot

 

# issue a ssl certificate for your domain:

certbot certonly --standalone --email admin@<yourdomain>.com --agree-tos -d turtl.<yourdomain>.com

 

 

 

 

# Configure the Apache virtual host with reverse proxy:

 

# create the virtual host

vi /etc/apache2/sites-available/turtl.conf


# Paste as per the bellow:

<VirtualHost *:80>
ServerName turtl.<yourdomain>.com
ServerAdmin webmaster@<yourdomain>.com

CustomLog ${APACHE_LOG_DIR}/turtl.log combined

RewriteEngine On
RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/.*
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [QSA,L,R=301]
</VirtualHost>


<VirtualHost *:443>
ServerName turtl.<yourdomain>.com
ServerAdmin webmaster@<yourdomain>.com

SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/turtl.<yourdomain>.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/turtl.<yourdomain>.com/privkey.pem
SSLProtocol all -SSLv2 -SSLv3
SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
SSLHonorCipherOrder on
SSLCompression off
SSLOptions +StrictRequire

ProxyRequests Off
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
ProxyPass / http://localhost:8181/
ProxyPassReverse / http://localhost:8181/

LogLevel info
CustomLog ${APACHE_LOG_DIR}/turtl.log combined
</VirtualHost>

 


# Save and enable the site:

a2ensite turtl

 

# Enable required modules

a2enmod proxy
a2enmod proxy_html
a2enmod proxy_http

 

# Restart apache

systemctl restart apache2

 

 

 

 

# Creating account on first-time use

 

# First time connecting, choose to create an account, input email and password and change the server address under "advanced settings" to:

https://turtl.<yourdomain>.com



# to disable further registrations edit the file:

vi /opt/turtl/server/helpers/auth.js


# comment out the "post /users" line as per the bellow:

var public_routes = [];
[
'get /',
//'post /users',
'get /users/confirm/[^/]+/[a-f0-9]+',
'post /cla/sign',
'get /health/[a-z0-9]+',
'get /users/delete/[^/]+/[a-f0-9]+',
'post /users/delete/[^/]+',
].map(add_public_route);

 

# restart the service:

systemctl restart turtl

 

 

# reboot and make sure the server is running and you can connect

 

 

 

That's it! You should now have a fully functional private turtl server. have fun with it.