Showing posts with label HOWTO. Show all posts
Showing posts with label HOWTO. Show all posts

11 December 2025

Redmine 6.1 with Passenger and nginx on Ubuntu 24.04

For the old-school crew who want one reference place to getting Redmine up and running, this guide will walk through the maze of twisty little passages, all alike.

These flexible instructions will get an up to date, robust, secure installation going.

  • Ubuntu 24.04 LTS - a mature Ubuntu version with long term support
  • MySQL - the database
  • Ruby - the technology on which Redmine runs, installed using RVM to manage the ruby version and have access to up to date components
  • Phusion Passenger- the application container in which to run Redmine
  • nginx - the web server within which Redmine runs 

Install MySQL

Install MySQL. For this step, relying on the Ubuntu packages is fine.

sudo apt-get install -y mysql-server libmysqlclient-dev

Connect to the mysql service (sudo mysql) and create the database and provide access to the redmine database user:

sudo mysql

CREATE DATABASE redmine CHARACTER SET utf8mb4;

CREATE USER 'redmine'@'localhost' IDENTIFIED BY 'yourpassword';

GRANT ALL PRIVILEGES ON redmine.* TO 'redmine'@'localhost';

If this is just a test system, move onto the Ruby Installation section.


MySQL Data Disk (Optional)

For production systems, a separate data disk should be used rather that storing data on the same partition as the root system. Stop the database service and then migrate the data directory.

sudo systemctl stop mysql

sudo rsync -avzh /var/lib/mysql  /websvc 

(Replace /websvc with the top level of the new data directory; rsync preseves all the correct permissions)

Modify the data directory configuration in MySQL:

sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf

#datadir = /var/lib/mysql #old location

datadir = /websvc/mysql #new location

Update AppArmor, otherwise the mysql process won't start. 

sudo vim /etc/apparmor.d/tunables/alias

    alias /var/lib/mysql/ -> /websvc/mysql/,

Don't forget the extra comma; it is not a typo!

Now restart both services. If everything has been done correctly, they will start without error.

sudo systemctl reload apparmor

sudo systemctl start mysql

Log into the database system to ensure everything looks okay:

sudo mysql

SELECT @@datadir;

The updated data directory should be evident.

 

Ruby Installation

Many methods exist to install Ruby. Unfortunately, the most convenient using apt-get will leave the system many versions behind, which means many plugins for redmine, and parts of redmine itself, will be unsupported and likely break. 

RVM is the recommended method to provide clean management of ruby versions.

sudo apt-get update

sudo apt-get install -y curl gnupg build-essential

sudo gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB

curl -sSL https://get.rvm.io | sudo bash -s stable

sudo usermod -a -G rvm `whoami`

Exit the session and relog back in, otherwise the following steps won't work. As of this writing ruby 3.4.7 is the latest stable release. 

Redmine 6.1 requires Ruby 3.2, 3.3, or 3.4. 

rvm install 3.4.7

rvm --default use ruby-3.4.7

gem install bundler --no-document

Ruby should now be installed.

ruby -v

ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +PRISM [x86_64-linux]

Note: once setup, each user of RVM needs to be added to the rvm group.

sudo usermod -a -G rvm username


Phusion Passenger Installation

Many options exist for application containers. Phusion has a Passenger-nginx combo that is is purpose built for Ruby and Rails applications. Installation instructions are pulled from the Phusion site.

First step is to install nginx, if it is not already present. 

 sudo apt-get install -y nginx

Then set up the Passenger repository:

# Install PGP key and add HTTPS support for APT

sudo apt-get install -y dirmngr gnupg apt-transport-https ca-certificates curl

curl https://oss-binaries.phusionpassenger.com/auto-software-signing-gpg-key.txt | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/phusion.gpg >/dev/null

# Add Passenger APT repository

sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger noble main > /etc/apt/sources.list.d/passenger.list'

Install Passenger and the nginx module:

sudo apt-get update

sudo apt-get install -y libnginx-mod-http-passenger

And finally move the configuration files to the correct places:

if [ ! -f /etc/nginx/modules-enabled/50-mod-http-passenger.conf ]; then sudo ln -s /usr/share/nginx/modules-available/mod-http-passenger.load /etc/nginx/modules-enabled/50-mod-http-passenger.conf ; fi

sudo ls /etc/nginx/conf.d/mod-http-passenger.conf

Nginx can now be restarted and should have Passenger running within. 

sudo service nginx restart

Optionally, the following health checks can be run.

sudo /usr/bin/passenger-config validate-install

sudo /usr/sbin/passenger-memory-stats

Once the Redmine installation is ready, one more nginx configuration update will be needed. 


Create a Redmine Service Account

Before doing too much with Ruby, create a redmine service account. The home directory is located where redmine is going to be installed - for production systems this should be on a separate partition. 

In this example /websvc/redmine is where Redmine will be installed. This directory will be created when the user is created.

sudo adduser --system --shell /bin/bash --gecos 'Redmine Administrator' --group --disabled-password --home /websvc/redmine redmine; sudo usermod -a -G rvm redmine

Give the account sudo privileges (temporarily).

sudo visudo

    redmine    ALL=(ALL)      NOPASSWD:ALL

As of Ubuntu 21, the user's home directory is locked down such that nginx will not be able to access the Redmine installation. This one step will save many minutes and/or hours of troubleshooting.

sudo su - redmine  # should result in being in the redmine installation directory

chmod 755 .

Whew. Like a hero that doesn't show up until the third reel of a movie, Redmine is finally on the scene.


Install Redmine with the Redmine Service Account

As the redmine user, and in the redmine home directory (/websvc/redmine), pull down the latest stable release.

wget https://www.redmine.org/releases/redmine-6.1.0.tar.gz

tar xvfz redmine-6.1.0.tar.gz

ln -s redmine-6.1.0 redmine

rm redmine-6.1.0.tar.gz


Configure the Redmine mysql Connection

As the redmine service account, update the production database entry with the account connection information.

cd redmine

cp -pR config/database.yml.example config/database.yml

vim config/database.yml

database: redmine

host: localhost

username: redmine

password: "yourpassword"

 

Install Redmine Ruby Requirements

As the redmine service account, adding the Redmine gems dependencies.

bundle config set --local without 'development test'

bundle install

If versions and such match these instructions, the bundle install should go clean:

Bundle complete! 52 Gemfile dependencies, 94 gems now installed.

If versions are different or new features are needed, some iteration may be needed to build and install the gems. This seems to be a "normal" task for ruby administrators. Apply google-fu or a chatbot and iterate.

Generate the token:

bundle exec rake generate_secret_token

Then initialize the database:

RAILS_ENV=production bundle exec rake db:migrate

Optionally load default data as well:

RAILS_ENV=production bundle exec rake redmine:load_default_data


Update nginx to Run Redmine

More configuration will be needed to SSL-ize the system and lock it down. But for now, these steps will get the Redmine site up and running.

These steps do not need to be run as the redmine user.

Confirm the Ruby runtime location, the result will be used in a later step.

passenger-config about ruby-command

To use in Nginx : passenger_ruby /usr/local/rvm/gems/ruby-3.4.7/wrappers/ruby

Update the root location and add passenger configuration. The assumption is your redmine root location is also on the /websvc partition.

sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.orig

sudo vim /etc/nginx/sites-available/default

root /websvc/redmine/redmine/public; #installation location

passenger_enabled on;  #turn on application container

passenger_ruby /usr/local/rvm/gems/ruby-3.4.7/wrappers/ruby; #Ruby runtime

client_max_body_size 10m; # Max attachment size allowed

Then to prevent a mess of 404 errors, comment out the location entry. Missing this step results in only getting a 404 and nothing else.

#location / {

    # First attempt to serve request as file, then

    # as directory, then fall back to displaying a 404.

    #try_files $uri $uri/ =404;

#}

Now everything should be ready to start. This is done by restarting nginx. 

sudo service nginx restart

Monitor the following logs to ensure things start clean:

tail -f /var/log/nginx/error.log

tail -f  /data/redmine/redmine/log/production.log

Congratulations, the redmine site should now be up and available.

Initial login is admin : admin. 


Clean up

Remove sudo privileges from the redmine account. Move the site to HTTPS, ideally using the excellent Let's Encrypt service.

 

Links of Interest

General Redmine installation

Always a good place to review the latest information on generic Redmine installations.

https://www.redmine.org/projects/redmine/wiki/RedmineInstall


Phusion Passenger installation

https://www.phusionpassenger.com/docs/tutorials/deploy_to_production/installations/oss/ownserver/ruby/nginx/




14 January 2018

Redmine, Passenger, and Nginx on Ubuntu 16.04

Redmine with Passenger and Nginx on Ubuntu 16.04

Just in time for Ubuntu 18, here are Ubuntu 16 instructions for getting Redmine up and running. Though better than on Ubuntu 14, this is still a maze of twisty little passages, all alike. 

Here are flexible instructions to get an up to date, robust, secure installation going.
  • Ubuntu 16.04 LTS - a mature Ubuntu version with long term support 
  • MySQL - the database 
  • Ruby - the technology on which Redmine runs, installed using RVM to manage the ruby version and have access to up to date components 
  • Phusion Passenger- the application server in which to run Redmine 
  • Nginx - the web server within which Redmine runs   

 

MySQL Configuration

Install MySQL. For this step, relying on the Ubuntu packages is fine.
sudo apt-get install -y mysql-server libmysqlclient-dev
 Connect to the mysql service (mysql -p) and create the database and provide access to the redmine user:
mysql -p -u root
CREATE DATABASE redmine CHARACTER SET utf8;
CREATE USER 'redmine'@'localhost' IDENTIFIED BY 'yourpassword';
GRANT ALL PRIVILEGES ON redmine.* TO 'redmine'@'localhost';
If this is just a test system, move onto the Ruby Installation section. 

For production systems, a separate data disk should be used rather that storing data on the same partition as the root system. Stop the service and then migrate the data directory.
 service mysql stop
Modify the data directory configuration in MySQL:
sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
#datadir = /var/lib/mysql #old location
datadir = /data/mysql # new location
On install, MySQL 5.7 automatically initiates the data directory. So this must be moved to the new location.
sudo mkdir /data
sudo mv /var/lib/mysql/ /data/mysql/
Update AppArmor, otherwise the mysql process won't start. The MySQL 5.7 and Ubuntu combination have an AppArmor bug that shows when moving the data directory. The error will show in syslog like this:
Jan 12 10:03:12 ubuntu16 kernel: [ 1289.012262] audit: type=1400 audit(1515780192.392:116): apparmor="DENIED" operation="open" profile="/usr/sbin/mysqld" name="/proc/8834/status" pid=8834 comm="mysqld" requested_mask="r" denied_mask="r" fsuid=111 ouid=111
Jan 12 10:03:12 ubuntu16 kernel: [ 1289.012897] audit: type=1400 audit(1515780192.396:117): apparmor="DENIED" operation="open" profile="/usr/sbin/mysqld" name="/sys/devices/system/node/" pid=8834 comm="mysqld" requested_mask="r" denied_mask="r" fsuid=111 ouid=0
To really geek out, this bug has one of the more sad but funny threads I've read in a long time. They eventually get to the right conclusion and open two bugs, but getting there is a journey. We will patch it ourselves, because the bugs haven't been actually fixed in the Ubuntu release.

To update AppArmor:
sudo vim /etc/apparmor.d/usr.sbin.mysqld
In the "Allow system resource access" section add the following to fix the bug(s): 
/sys/devices/system/node/ r,
/sys/devices/system/node/** r,
/proc/** r,
And under the "Allow data dir access" section change the entries from /var/lib/mysql/ to the new directory, /data/mysql/.
# Allow data dir access
#  /var/lib/mysql/ r,
#  /var/lib/mysql/** rwk,
/data/mysql/ r,
/data/mysql/** rwk,
Reload AppArmor:
sudo systemctl reload apparmor
Restart mysql:
 service mysql start
The system should start without error if everything has been done correctly. Run the following command to ensure everything looks okay:
mysql -p -u redmine
 

Ruby Installation

Many methods exist to install Ruby. Unfortunately, the most convenient using apt-get will leave the system many versions behind, which means many plugins for redmine, and parts of redmine itself, will be unsupported. RVM is a tried and true method to provide clean management of ruby versions. 

For whatever reason, these steps must be run as root:
sudo su - #get into root
apt-add-repository -y ppa:rael-gc/rvm
apt-get update
apt-get install rvm
Once done, logout and log back in as root.
exit
sudo su - #log out and log back in
command curl -sSL https://rvm.io/mpapis.asc | gpg --import -
rvmsudo rvm get stable
rvm requirements
rvm install 2.5.0
rvm use 2.5.0 --default
Test that the install looks okay. 
ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux] 
  Note: once setup, each user of rvm needs to be added to the rvm group. 
sudo usermod -a -G rvm username

Phusion Passenger Installation

Again, many options exist for application containers. Phusion has a Passenger-Nginx combo that is straight-forward to install and configure. It does not need to be done as root.

Details of this installation can be found on the Phusion site.
sudo apt-get install -y dirmngr gnupg
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7
sudo apt-get install -y apt-transport-https ca-certificates
sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger xenial main > /etc/apt/sources.list.d/passenger.list'
sudo apt-get update
sudo apt-get install -y nginx-extras passenger

Nginx Configuration 

Passenger configuration with nginx has simplified since previous versions. 

Enable passenger in the nginx:
sudo vim /etc/nginx/nginx.conf
Uncomment the following line:
include /etc/nginx/passenger.conf;
 Restart nginx:
sudo service nginx restart
 Confirm everything looks correct
sudo /usr/bin/passenger-config validate-install
sudo /usr/sbin/passenger-memory-stats
The result will be something like this:
/usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini
Also, confirm the correct location of the correct ruby.
which passenger-config 
/usr/bin/passenger-config #use this result to perform the next command
/usr/bin/passenger-config --ruby-command
The result will be something like this:
/usr/local/rvm/gems/ruby-2.5.0/wrappers/ruby
Build out the site - assuming http for now. More configuration will be needed to SSL-ize the system and lock it down. But for now, this will get things correct and running. 
sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.orig
sudo vim /etc/nginx/sites-available/default
Update the root location and add passenger configuration. The assumption is your redmine root location is also on the /data partition. This directory will be created when we add the redmine account.
root /data/redmine/redmine/public/; #installation location
passenger_enabled on; #turn on application container
client_max_body_size 10m; # Max attachement size allowed
  
Then to prevent a mess of 404 errors, comment out the location entry. Missing this step results in a special level of redmine 404 hell.
#location / {
    # First attempt to serve request as file, then
    # as directory, then fall back to displaying a 404.
    #try_files $uri $uri/ =404;
#}
No need to restart nginx just yet. The system is almost ready for redmine installation.

 

Create the redmine account

Before doing too much with ruby, create a redmine service account. Note: Ideally the home directory is located where redmine is going to be installed - for production systems this should be on a separate partition. 
sudo adduser --system --shell /bin/bash --gecos 'Redmine Administrator' --group --disabled-password --home /data/redmine redmine; sudo usermod -a -G rvm redmine 
 Give the account sudo privileges (temporarily).
sudo visudo 
redmine    ALL=(ALL)      NOPASSWD:ALL

Welcome to dependency-o-rama 

The ruby add-on dependencies next depends (ha ha get it?) on various ruby pieces needed to install ruby components. A minimal list will look something like this: 
sudo apt-get install -y build-essential imagemagick libmagickwand-dev

Redmine, remember this was the main point of the article?

Whew. Like a hero that doesn't show up until the third reel of a movie, redmine is finally on the scene. Yes, this is just like Batman vs Superman. There is a lot of build up to the main event, and when you get there it is anticlimactic.

Install the latest redmine

Switch to the redmine user and pull down the latest stable release.
sudo su - redmine # should result in being in the redmine installation directory
wget http://www.redmine.org/releases/redmine-3.4.4.tar.gz
tar xvfz redmine-3.4.4.tar.gz
ln -s redmine-3.4.4 redmine
rm redmine-3.4.4.tar.gz

Configure the mysql connection 

Update the production entry with the account connection information.
cd redmine 
cp -pR config/database.yml.example config/database.yml
vim config/database.yml

Adding gems - truly outrageous!

Configure the gems - and avoid doing this as root. As with everything in redmine administration, this should be as that fancy redmine service account:
gem install bundler
bundle install --without development test
If versions and such match these instructions, the bundle install should go clean:
Bundle complete! 31 Gemfile dependencies, 55 gems now installed.
Gems in the groups development and test were not installed.
Use `bundle show [gemname]` to see where a bundled gem is installed.

If versions are different or new features are needed, some iteration may be needed to build and install the gems. This seems to be a "normal" task for ruby administrators. Apply google-fu and iterate. 

Rake magic

Next run the magic rake commands. All magic comes in three. 
bundle exec rake generate_secret_token
RAILS_ENV=production bundle exec rake db:migrate
RAILS_ENV=production bundle exec rake redmine:load_default_data
Now everything should be ready to start. This is done by restarting nginx. Monitor the following logs to ensure things start clean: 
tail -f /var/log/nginx/error.log
tail -f  /data/redmine/redmine/log/production.log

Clean up

Congratulations, the redmine site should now be up and available. 

Remove sudo privileges from the redmine account. Move the site to HTTPS, ideally using the excellent Let's Encrypt service. These instructions can be found here.. 


Links of interest 

General Redmine installation 
Always a good place to review the latest information on generic Redmine installations.
https://www.redmine.org/projects/redmine/wiki/RedmineInstall
 
Using Google Authentication
If using Google Apps or Google Auth is of interest with Redmine, a longstanding plugin has been brought back to life with a patch. 

The plugin can be found here:
https://github.com/twinslash/redmine_omniauth_google

But in modern ruby and google land, a patch will be needed. The plugin will install just fine, but it will not save any configuration information, nor will it put the google button on the login page without the patch below.
https://github.com/twinslash/redmine_omniauth_google/pull/42

Though out of date, this article has some pointers on setting up the google side:
https://adminsdiary.wordpress.com/common-installations/redmine-installation-with-google-authentication/

Installing RVM 
https://github.com/rvm/ubuntu_rvm

Instead of MySQL, consider Aurora 
For some of my more recent Redmine installations, I've been using Amazon Aurora. It is cheaper than MySQL on AWS, compatible with MySQL 5.6, backups are taken care of, and even has regional failover built in. Much better than researching AppArmor patches because you want to simply move a data directory. 

Setting up redmine is the same as above, but instead of doing MySQL, connect to an AWS Aurora instance. (You will still need the MySQL client - libmysqlclient-dev):
https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Aurora.Connecting.html
 

20 October 2017

Secure FTP on S3 with Chroot and Google Authenticator

Secure FTP is really SSH under the covers behaving like an FTP (file transport) service. And instructions exist leveraging S3 as an inexpensive file system backend. But getting SFTP and S3 deployed securely with users isolated took some detective work. Isn't it a odd that secure instructions always take work? Cobbled from multiple sources, here are flexible instructions to get an up to date, robust, secure installation going.

As I've been working a lot in AWS Gov Cloud, these instructions account for this wrinkle.

Multi-factor (MFA) client access is included, since this is quickly becoming a standard requirement.

These instructions assume the following;
  • Ubuntu 16.04 LTS with SSH- a mature Ubuntu version with long term support
  • AWS account with S3 bucket privileges
  • End users on Windows who need an "easy" secure method to share files
  • (optional) MFA soft tokens, such as Google Authenticator

One Time Setup

Patch up the system and install required binaries. 
sudo apt-get update
sudo apt-get upgrade
init 6
sudo apt-get install s3fs #used for mounting S3 filesystem
sudo apt-get install libpam-google-authenticator #MFA
Optionally enable MFA capability for SSH
Note: It is always a good idea when making SSH changes to leave a separate console open and then test the change. Otherwise it is easy to lose access to the box if a mistake is made.
sudo vim /etc/ssh/sshd_config
ChallengeResponseAuthentication yes
For now, protect the ubuntu user access by adding these lines to the sshd_config. It can be updated later once everything is complete. 
Match User ubuntu
        AuthenticationMethods publickey 
Restart SSH for the change to take effect.
sudo systemctl restart sshd.service
Update the PAM authentication module to use Google Authenticator. 
sudo vim /etc/pam.d/sshd
auth required pam_google_authenticator.so nullok
The nullock flags allows for a user who does not have Google Authenticator set up to be able to log in.

Create and Prepare the Mount Point for the S3 bucket

On AWS S3, create a non-public bucket with an account that has API keys - API keys are created in the IAM section of AWS. Using the AWS GUI to create a bucket with owner ACLs (the default) will work just fine.

Create a password file for the S3 file system.
sudo vim /etc/passwd-s3fs
The file format is as follows: bucketname:accessKeyID:secretKeyID

And then lock it down.
sudo chmod 640 /etc/passwd-s3fs
Create a mount point that is owned by root. 
sudo mkdir -p /s3/home
A side bar about mounting a Chroot Directory
In the world of SFTP, using a ChrootDirectory is necessary to limit file system access to users. Without it, SFTP users will be able to traverse all over the server file system and download anything a regular user has read access to, including the passwd file.
 
Mounting the bucket takes a bit of explanation and is very the much the secret sauce to getting this to work correctly. Most online instructions suggest a simple mount command as the root user or mounting with the allow_other flag to allow others to use the mount point.

Both of these options will fail when using a ChrootDirectory. A look at the man pages explains why:
ChrootDirectory 
Specifies the pathname of a directory to chroot(2) to after authentication. At session startup sshd(8) checks that all com- ponents of the pathname are root-owned directories which are not writable by any other user or group. After the chroot, sshd(8) changes the working directory to the user's home directory.
When mounting the bucket as the root user, the mount point permissions will be 700. This means only the root user will have access to what is below. Chroot will fail for SFTP users with a permission denied error: 
safely_chroot: stat("/s3/home/"): Permission denied
When mounting the bucket with the allow_other flag, the mount point permissions will be 777. This means the mount point is too permissive. Chroot will fail for SFTP users with a bad ownership error: 
fatal: bad ownership or modes for chroot directory component "/s3/home/"
The Goldilocks option is a little-known flag known as mount point umask (mp_umask). This should be set to 022 for ChrootDirectory to work correctly.

You're welcome.

Mounting the S3 Bucket

The mounting of the bucket should be tested prior to making it permanent.
sudo s3fs -o allow_other -o mp_umask=022 bucketname mountPoint
A first check is to confirm the bucket is mounted by running the mount command. 
s3fs on /s3/home type fuse.s3fs (rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other)
A second check should be done though because mounting does not mean anything is actually connected.
ls -l /s3/home
ls: cannot access '/s3/home': Transport endpoint is not connected
The "transport endpoint not connected" usually comes down to two possible issues: 
  1. the bucket name is not unique 
  2. the bucket is not in the default location (us-east-1)
Troubleshooting the s3fs command with the -d  and -f flags will help. Specifying the bucket url is always good practice anyway. In GovCloud implementations, it is required. 
sudo s3fs -o allow_other -o mp_umask=022 -o url=http://s3-us-gov-west-1.amazonaws.com (bucketname) (mountPoint) 
Once the bucket really is mounted, un-mount it and then add an enrry to /etc/fstab so it will be mounted at boot.
sudo fusermount -u /s3/home
echo s3fs#(bucketname) (mountPoint) fuse url=http://s3-us-gov-west-1.amazonaws.com,_netdev,rw,nosuid,nodev,use_sse,mp_umask=022,allow_other 0 0 >> /etc/fstab
sudo mount -a
With bucket mounted, the system is now ready for SFTP users

Creating New Groups for SFTP Users


Users who need to access the same files should be put into groups. The groups can then be locked down to specific directories on the bucket. Generally, this should be scripted out. But here are the basic steps to get started. 

Create a new group and set up the environment:
sudo addgroup (groupName)
# Create the chroot directory
sudo mkdir /s3/home/(groupName)/
sudo chmod g+rx /s3/home/(groupName)/

# Create the group-writable directory
sudo mkdir -p /s3/home/(groupName)/controlled/
sudo chmod g+rwx /s3/home/(groupName)/controlled/

# Add the full path to the group
sudo chgrp -R (groupName) /s3/home/(groupName)/
Then add that detail to sshd_config so any new SFTP user in that group is locked down to only using SFTP.
BUCKET_HOME="(mountPoint)"
GROUPNAME="(groupName)"

cat <<EOT>> /etc/ssh/sshd_config
    # Lock down the new group
    Match Group ${GROUPNAME}
    # Only allow SFTP and chroot to the required directory.
    ForceCommand internal-sftp
    ChrootDirectory ${BUCKET_HOME}/${GROUPNAME}/
    # Lock down SSH options
    PermitTunnel no
    AllowAgentForwarding no
    AllowTcpForwarding no
    X11Forwarding no
    EOT
And don't forget to restart SSH to catch the change. 
systemctl restart sshd.service

Add New Users 

Adding new users is straightforward now that everything has been set up.
sudo adduser --ingroup (groupName) (newUser)
passwd (newUser)
 Optionally set the MFA
sudo su - (newUser)
google-authenticator
And note the key for the user.

A successful test should do the following:
  • Allow the user to log into the system via SFTP 
  • The user should be directed on the chroot environment and only see the "controlled" folder
  • The user should be able to write and read to the "controlled" folder
  • The user should *not* be able to SSH into the system




17 May 2016

Nginx HTTPS with Let's Encrypt and Redmine

Nginx HTTPS with Let's Encrypt and Redmine

These instructions are based on Ubuntu and use the Let's Encrypt Certificate Authority. With Let's Encrypt, self-signed certificates on a public Linux server should be ancient history. 

These instructions assume the following;
  • Ubuntu 14.04 LTS - a mature Ubuntu version with long term support 
  • Nginx - the web server within which Redmine runs 
  • A running Redmine system. Details of how to setup Redmine on Nginx can be found here

Getting the certificates from Let's Encrypt

As the Let's Encrypt system has just left beta and is still new, using their instructions are best. For example, the certbot command was introduced this week, replacing the "old" letsencrypt-auto command. However, here is how things work as of today.

Install git and download the Let's Encrypt client. On first run, the client will install dependencies and update itself. 
apt-get install git
git clone https://github.com/certbot/certbot
cd certbot
# Running for the first time will install needed dependencies.
./certbot-auto --help
After everything is installed, the certificate can be requested and downloaded all in one step. Because the Let's Encrypt client uses port 80 for verification of ownership, anything running on that port needs to be temporarily turned off -- in this case nginx.
service nginx stop
./certbot-auto certonly --standalone -d redmine.yourdomain.com
service nginx start
Note where the certificates are installed. For example:

/etc/letsencrypt/live/redmine.yourdomain.com/

As said, these instructions may change at any time. What follows is the standard method for setting up SSL in general for nginx and Redmine. 


Set up the certificates

Generate a set of DH parameters for the Diffie-Hellman handshake. Please don't ask what this is for... just know it is a good thing. 
mkdir /etc/nginx/ssl
chmod 700 /etc/nginx/ssl
openssl dhparam 2048 -out /etc/nginx/ssl/dh2048.pem
In /etc/nginx/sites-available create a new file or update the "default" file.
 

Modify the listen 443 ssl entry.   
server_name redmine.yourdomain.com;
ssl_dhparam    /etc/nginx/ssl/dh2048.pem;
ssl_certificate    /etc/letsencrypt/live/redmine.yourdomain.com/fullchain.pem;
ssl_certificate_key    /etc/letsencrypt/live/redmine.yourdomain.com/privkey.pem; 
NOTE: The ssl_certificate should be set to the fullchain.pem, which includes both the server certificate and the intermediate CA certificates. Setting this just to the cert.pem will work in Chrome, but give a "SEC ERROR UNKNOWN ISSUER" error in Firefox.

Add the redmine details (same as used for port 80). 

root /var/data/redmine/public/;
passenger_enabled on;
client_max_body_size    10m;
Comment out or delete any references to 404, if they exist.
#passenger_spawn_method direct;
#location / {
#       try_files $uri $uri/ =404;
#}
 
If this file was newly created, instead of the "default" file, add a soft link to the new file in /etc/nginx/sites-enabled.
 
Restart nginx and everything should come up on port 443 with a valid certificate. 
   

Lock down the SSL installation 

Now that the site is functional, the configuration should be locked down.
# limit HTTPS to the most recent protocol
ssl_protocols     TLSv1.2;
# define the list of ciphers used
ssl_prefer_server_ciphers on;
# this list is always in flux, but the list below works at time of writing
ssl_ciphers     EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH;   
#set the cache session, 10 minutes is the minimum
ssl_session_cache shared:SSL:10m;
In the end, the the file should look something like this: 

server {
        listen 443 ssl;
        server_name redmine.yourdomain.com;
        ssl_dhparam    /etc/nginx/easy-rsa/keys/dh2048.pem;
        ssl_certificate    /etc/letsencrypt/live/redmine.yourdomain.com/fullchain.pem;
        ssl_certificate_key   /etc/letsencrypt/live/redmine.yourdomain.com/privkey.pem;
        ssl_protocols    TLSv1.2;
        ssl_prefer_server_ciphers on;
        ssl_ciphers    EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH;
        ssl_session_cache shared:SSL:10m;
        root /var/data/redmine/public/;
        passenger_enabled on;
        client_max_body_size    10m;
        #passenger_spawn_method direct;
        #location / {
        #       try_files $uri $uri/ =404;
        #}
}

Restart nginx and if desired, run a certificate security test against the system. 

 
Figure: Qualsys scan
 

Automated renewal

Because the Let's Encrypt philosophy is full automation, the certificates need to be renewed every 90 days. In cron, setup a renewal job to be run every week. For example, at 3:05 AM every Saturday: 
05 03 * * 6 /yourpath/cert-renew.sh
This job needs root privileges. Once the certificate gets into the time window where it can be renewed, cron will do the update "magically".
install-path/certbot-auto renew --standalone --pre-hook "service nginx stop" --post-hook "service nginx start"
This can be tested by doing a dry-run: 
install-path/certbot/certbot-auto renew --dry-run --standalone --pre-hook "service nginx stop" --post-hook "service nginx start"
 

Links of interest 

Renewal script for Let's Encrypt

https://certbot.eff.org/docs/using.html#renewal