I want to be able to run a single script from my client machine, and push HTML changes and nginx config changes to a remote server. I do not want to be prompted for passwords or any sort of confirmation at any point in this process.

This requires a number of steps, but is certainly doable.

Client SSH key

First, make sure you have an SSH key on your client that is not password protected.

You can use ssh-keygen to generate a new key if needed.

# Run this on the client.
ssh-keygen -t rsa

Here are a couple guides that explain the basics for SSH keys.

Deploy user

Add a deploy user on the server.

# Run this on the server.
sudo useradd -m -s /bin/bash deploy

SSH access for deploy user

Create the appropriate directories and files on the server.

# Run this on the server.
sudo mkdir /home/deploy/.ssh
sudo chown deploy:deploy /home/deploy/.ssh/
sudo chmod 700 /home/deploy/.ssh/
sudo touch /home/deploy.ssh/authorized_keys

Grant the SSH key from the client access on the server. Do this by appending the public SSH key (typically id_rsa.pub) from the client to the /home/deploy/.ssh/authorized_keys file on the remote server.

It is as simple as a copy and paste, but there are many guides online that explain how to do this.

Ensure the permissions are correct for the authorized_keys file.

# Run this on the server.
sudo chown deploy:deploy /home/deploy/.ssh/authorized_keys
sudo chmod 600 /home/deploy/.ssh/authorized_keys

This will enable passwordless SSH authentication. This way, the server will never prompt for a password when connecting.

Passwordless nginx changes

Change the ownership of nginx config directories so that the deploy user can edit them without needing sudo access.

# Run this on the server.
sudo chown root:deploy /etc/nginx/{sites-enabled,sites-available}
sudo chmod 775 /etc/nginx/{sites-enabled,sites-available}

On the server, add a special sudoers config so that the deploy user can run service nginx restart without needing a password.

# /etc/sudoers.d/deploy
deploy ALL=(ALL) NOPASSWD: /usr/sbin/service nginx restart
# Run this on the server.
sudo chmod 0440 /etc/sudoers.d/deploy

Now the deploy user can run sudo service nginx restart without a password, and only that command.

Leveraging our changes

With everything in place, we should now be able to smoothly run a deploy from the client.

Here is an example for my own site that I use when deploying to Digital Ocean.

First, I copy my files to the server with a deploy.sh script.

Run this on your client.

#!/bin/bash

rsync \
	-avze 'ssh -o StrictHostKeyChecking=no' \
	--delete \
	./ deploy@willhaley.com:/srv/willhaley.com/

ssh deploy@willhaley.com '/bin/bash /srv/willhaley.com/bounce.sh'

Note the StrictHostKeyChecking=no. This prevents the client from asking if the server should be trusted or not.

Note also where the bounce.sh command is run on the remote server. That is the command that configures and restarts nginx on the remote server.

bounce.sh is a command that handles the nginx updates. Here is an example.

#!/usr/bin/env bash

cp /srv/nginx/willhaley.com/config/willhaley.com.conf /etc/nginx/sites-available/
ln -fs /etc/nginx/sites-available/willhaley.com.conf /etc/nginx/sites-enabled/

sudo service nginx restart

Based on how we modified permissions earlier, and granted access to deploy to restart nginx without a password, this can all be kicked off by the client without any password prompts.

Citations

  1. Non-privileged, non-root, user to start or restart webserver server such as nginx without root or sudo
  2. How can I avoid SSH’s host verification for known hosts?
  3. Upstart: allowing a normal user to stop and start my custom service
  4. How to modify a invalid /etc/sudoers file? It throws out an error and not allowing me to edit again