mirror of
https://github.com/capistrano/capistrano
synced 2023-03-27 23:21:18 -04:00
More Docs!
This commit is contained in:
parent
0e1bf5d13a
commit
94b8564296
18 changed files with 1835 additions and 120 deletions
|
|
@ -0,0 +1,314 @@
|
|||
---
|
||||
title: Authentication & Authorisation
|
||||
layout: default
|
||||
---
|
||||
|
||||
**Note:** In the documentation we simply recommend creating a single deployment user,
|
||||
and sharing it between team members. If you know why this is a bad idea (or
|
||||
why this may be against regulations in your jurisdiction in some cases, we
|
||||
assume that you know well enough how to use groups, umasking and setgid bits
|
||||
to make this work reliably for unique logins across team members)
|
||||
|
||||
To create this deploy user we'll assume something like the following has been
|
||||
done:
|
||||
|
||||
{% prism bash %}
|
||||
root@remote $ adduser deploy
|
||||
root@remote $ passwd -l deploy
|
||||
{% endprism %}
|
||||
|
||||
The first line creates a completely standard user, it has a home directory,
|
||||
which we'll need in a moment, and has a shell, so it may log in. This needs to
|
||||
be done **on every server in your environment**.
|
||||
|
||||
The second line *locks* the user, it changes the user's password to an
|
||||
untypable string, guaranteeing that the user has no password which can be used
|
||||
to log in.
|
||||
|
||||
### Authentication
|
||||
|
||||
There are two places that we need automated, promptless authentication:
|
||||
|
||||
1. **From our workstation/notebook/etc to our servers.** We do this with **SSH
|
||||
keys**, passphrase protected, ideally, using a **key agent**.
|
||||
2. **From our servers to the repository host**. We do this so that our servers
|
||||
can check out our application code from Github, or similar and install it
|
||||
to the servers. This is usually done using **SSH agent forwarding**, HTTP
|
||||
authentication, or with deploy keys.
|
||||
|
||||
#### 1.1 SSH keys from workstation to servers
|
||||
|
||||
**Note:** If you are on Windows, all bets are off, I'd love it if someone
|
||||
could contribute a Windows guide to this, so we can include it here.
|
||||
|
||||
An SSH key is a mechanism that allows a *public* half one key to be placed on
|
||||
a server, when we want to authenticate with that server, our SSH client uses
|
||||
the **private** part of that key to negotiate with the server, if the keys are
|
||||
correct, then we need to create the key.
|
||||
|
||||
**Hint:** If you have more than one developer in your team, they should all add their
|
||||
public key to the `deploy` user's `authorized_keys` file, that way if someone
|
||||
quits or gets fired, you can remove their key from that file, and the rest of
|
||||
you can keep on shipping!
|
||||
|
||||
{% prism bash %}
|
||||
me@localhost $ ssh-keygen -t rsa -C 'me@my_email_address.com'
|
||||
{% endprism %}
|
||||
|
||||
You'll be prompted for a passphrase, that's fine. Type one and keep it safe.
|
||||
This passphrase ensures that if your computer is stolen, people still need a
|
||||
passphrase to access your keys, in order to access your servers.
|
||||
|
||||
To avoid having to type this passphrase every time you need to use a key, most
|
||||
operating systems have a concept of a *key agent*. This *key agent* stores SSH
|
||||
keys securely between uses, typically the first time a key is needed in a
|
||||
given time period, the SSH agent will load the key, prompt you for your
|
||||
passphrase and then the key agent will remember the key for a certain amount
|
||||
of time (on OSX it tends to be indefinite, on linux this can vary from 15
|
||||
minutes updwards.)
|
||||
|
||||
We can see which keys are loaded in the SSH agent by running `ssh-add -l`
|
||||
|
||||
{% prism bash %}
|
||||
me@localhost $ ssh-add -l
|
||||
2048 af:ce:7e:c5:93:18:39:ff:54:20:7a:2d:ec:05:7c:a5 /Users/me/.ssh/id_rsa (RSA)
|
||||
{% endprism %}
|
||||
|
||||
If you don't see any keys listed, you can simply run `ssh-add`:
|
||||
|
||||
{% prism bash %}
|
||||
me@localhsot $ ssh-add
|
||||
Identity added: /Users/me/.ssh/id_rsa (/Users/me/.ssh/id_rsa)
|
||||
{% endprism %}
|
||||
|
||||
Typically, ssh-add will ask you for the passphrase when you add a key.
|
||||
|
||||
**Note:** Although it's not mandatory to use an SSH agent (one could simply
|
||||
use an unpassphrased key, and rely on SSH to find the key and exchange it).
|
||||
Using an SSH agent makes things more secure, because we can use a passphrased
|
||||
key without being prompts every time it is used. It **also** allows us to use
|
||||
this same key to access the repository *via* the server without creating an
|
||||
additional identity.
|
||||
|
||||
At this point with the key loaded into the agent, we need to put the
|
||||
**public** part of the key into a file on each remote server called
|
||||
`/home/users/deploy/.ssh/authorized_keys`, to get the contents of that file,
|
||||
we can ask our local key agent for the public parts of the keys it has loaded:
|
||||
|
||||
{% prism bash %}
|
||||
me@localhost $ ssh-add -L
|
||||
ssh-rsa jccXJ/JRfGxnkh/8iL........dbfCH/9cDiKa0Dw8XGAo01mU/w== /Users/me/.ssh/id_rsa
|
||||
{% endprism %}
|
||||
|
||||
This will be a lot longer when you run it, I snipped the output because it
|
||||
looked bad.
|
||||
|
||||
This line, as one line, needs to make it to the remote server and be added *to
|
||||
it's own line* of the `deploy` user's `~/.ssh/authorized_keys` file. This file
|
||||
then needs to be changed to permission mode `0600` (owner read/write, group
|
||||
none, other none), in the `~/.ssh` directory which needs the permissions
|
||||
`0700` (owner read/write/execute, group none, other none).
|
||||
|
||||
If you are on linux there often exists a command
|
||||
[`ssh-copy-id`](http://linux.die.net/man/1/ssh-copy-id) which streamlines this
|
||||
process, otherwise the worlflow is something like:
|
||||
|
||||
{% prism bash %}
|
||||
me@localhost $ ssh root@remote
|
||||
root@remote $ su - deploy
|
||||
deploy@remote $ cd ~
|
||||
deploy@remote $ mkdir .ssh
|
||||
deploy@remote $ echo "ssh-rsa jccXJ/JRfGxnkh/8iL........dbfCH/9cDiKa0Dw8XGAo01mU/w== /Users/me/.ssh/id_rsa" >> .ssh/authorized_keys
|
||||
deploy@remote $ chmod 700 .ssh
|
||||
deploy@remote $ chmod 600 .ssh/authorized_keys
|
||||
{% endprism %}
|
||||
|
||||
**Remember:** This needs to be done on every server you want to use, you can
|
||||
use the same key for each one, but only one key per developer is recommended.
|
||||
*Private* keys are named as such for a reason!
|
||||
|
||||
If we did all that correctly, we should now be able to do something like this:
|
||||
|
||||
{% prism bash %}
|
||||
me@localhost $ ssh deploy@one-of-my-servers.com 'hostname; uptime'
|
||||
one-of-my-servers.com
|
||||
19:23:32 up 62 days, 44 min, 1 user, load average: 0.00, 0.01, 0.05
|
||||
{% endprism %}
|
||||
|
||||
That should happen without having to enter a passphrase for your SSH key, or
|
||||
promoting you for an SSH password (which the deploy user doesn't have anyway).
|
||||
|
||||
Verify that this works for all of your servers, and put your private key
|
||||
somewhere safe. If you're working with multiple team members, it often pays to
|
||||
collect everyone's public keys, indeed if your team is already using SSH keys
|
||||
to access Github, you can reach any user's SSH keys at the following URL:
|
||||
|
||||
* `https://github.com/theirusername.keys`
|
||||
|
||||
This can make getting user's keys onto servers much easier, as you can simply
|
||||
`curl`/`wget` each user's key into the authorized keys file on the server
|
||||
directly from Github.
|
||||
|
||||
<blockquote class="twitter-tweet"><p>TIL <a
|
||||
href="https://twitter.com/github">@github</a> exposes the ssh public keys for
|
||||
users. <a href="https://t.co/Wo9g8nxI">https://t.co/Wo9g8nxI</a> Handy for
|
||||
adding devs to servers/repos.</p>— Postmodern (@postmodern_mod3) <a
|
||||
href="https://twitter.com/postmodern_mod3/statuses/300438256200339456">February
|
||||
10, 2013</a></blockquote>
|
||||
<script async src="//platform.twitter.com/widgets.js"
|
||||
charset="utf-8"></script>
|
||||
|
||||
#### 1.2 From our servers to the repository host
|
||||
|
||||
With access from workstations to the servers settled, there is another hop to
|
||||
contend with, which is letting the deploy user get access to the code
|
||||
repository automatically. The options in order of preference:
|
||||
|
||||
##### 1.2.1 SSH Agent Forwarding
|
||||
|
||||
As we've already set up an SSH agent, we can use the *agent forwarding*
|
||||
feature of SSH to make this key agent available to further *hops*. In short,
|
||||
we can use **our own ssh key** to authenticate ourselves from the server, to
|
||||
Github.
|
||||
|
||||
Here's how we can check if that works, first get the URL of the repository:
|
||||
|
||||
{% prism bash %}
|
||||
me@localhost $ git config remote.origin.url
|
||||
git@github.com:capistrano/rails3-bootstrap-devise-cancan.git
|
||||
{% endprism %}
|
||||
|
||||
Here we're listing our private (for testing purposes) fork of the
|
||||
rails3-bootstrap-devise-cancan repository forked from the Rails Examples and
|
||||
Tutorials project.
|
||||
|
||||
We can try to access the repository via our server by doing the following:
|
||||
|
||||
{% prism bash %}
|
||||
# List SSH keys that are loaded into the agent
|
||||
me@localhost $ ssh-add -l
|
||||
# Make sure they key is loaded if 'ssh-add -l' didn't show anything
|
||||
me@localhost $ ssh-add
|
||||
me@localhost $ ssh -A deploy@one-of-my-servers.com 'git ls-remote git@github.com:capistrano/rails3-bootstrap-devise-cancan.git
|
||||
{% endprism %}
|
||||
|
||||
We first check that the agent has the keys loaded, if not we simply load it,
|
||||
and enter the passphrase when prompted.
|
||||
|
||||
Finally we use `ls-remote` from Git to list the remote objects, this is the
|
||||
exact same check that Capistrano does internally before attempting to deploy.
|
||||
The `-A` option may, or may not be required on your system, it's worth trying
|
||||
it both ways just to know how your system treats agent forwarding by default.
|
||||
|
||||
From the SSH documentation:
|
||||
|
||||
{% prism bash %}
|
||||
-A Enables forwarding of the authentication agent connection. This can also be
|
||||
specified on a per-host basis in a configuration file.
|
||||
|
||||
Agent forwarding should be enabled with caution. Users with the ability to
|
||||
bypass file permissions on the remote host (for the agent's UNIX-domain
|
||||
socket) can access the local agent through the forwarded connection. An
|
||||
attacker cannot obtain key material from the agent, however they can perform
|
||||
operations on the keys that enable them to authenticate using the identities
|
||||
loaded into the agent.
|
||||
{% endprism %}
|
||||
|
||||
In laymans terms, you should't use SSH agent forwarding to machines where you
|
||||
don't trust the administrators, as they can can override the permissions on
|
||||
the system and use your keys as if they were you. That said, if you can't
|
||||
trust your server administrators, perhaps they shouldn't have access to your
|
||||
servers!
|
||||
|
||||
##### 1.2.2 HTTP Authentication
|
||||
|
||||
In the case of HTTP authentication **be sure to use HTTPS**, otherwise your
|
||||
password will be sent in cleartext over the network, depending what your hosts
|
||||
network infratructure looks like that might be *very* bad news.
|
||||
|
||||
Typically when we try and list our remote objects, using the https method from
|
||||
Github, we'll be prompted for a username and password:
|
||||
|
||||
##### 1.2.2.1 With a regular username/password
|
||||
|
||||
{% prism bash %}
|
||||
me@localhost $ git ls-remote https://github.com/capistrano/rails3-bootstrap-devise-cancan.git
|
||||
Username for 'https://github.com': myownusername
|
||||
Password for 'https://capistrano@github.com':
|
||||
{% endprism %}
|
||||
|
||||
This challenge response prompt doesn't work well for automating things, so
|
||||
there are two ways to get around this depending on your server's host
|
||||
operating system, the first is to use a `netrc` file, we won't talk about that
|
||||
because the netrc is a global file that doesn't lend itself well to security.
|
||||
|
||||
The other mechanism, and the reason that its **very** important to always use
|
||||
HTTPS not plain ol' HTTP is to embed the username and password in the URL,
|
||||
note this won't work well if your password has special characters:
|
||||
|
||||
{% prism bash %}
|
||||
me@localhost $ git ls-remote https://capistrano:ourverysecretpassword@github.com/capistrano/rails3-bootstrap-devise-cancan.git
|
||||
3419812c9f146d9a84b44bcc2c3caef94da54758HEAD
|
||||
3419812c9f146d9a84b44bcc2c3caef94da54758HEADrefs/heads/master
|
||||
{% endprism %}
|
||||
|
||||
The bigger problem with passwords, whether inlined into the URL, or entered
|
||||
into a `netrc` file, is that the password gives access to **your entire Github
|
||||
Account** not just to one single repository.
|
||||
|
||||
##### 1.2.2.2 With an OAuth Personal API Token
|
||||
|
||||
This mechanism still gives access to **every repository** you can access, but
|
||||
at Github, they recently rolled out a feature called [Personal API
|
||||
Tokens](https://github.com/blog/1509-personal-api-tokens) which allow you to
|
||||
do something like this:
|
||||
|
||||
{% prism bash %}
|
||||
me@localhost $ git ls-remote https://.....................@github.com/capistrano/rails3-bootstrap-devise-cancan.git
|
||||
3419812c9f146d9a84b44bcc2c3caef94da54758HEAD
|
||||
3419812c9f146d9a84b44bcc2c3caef94da54758HEADrefs/heads/master
|
||||
{% endprism %}
|
||||
|
||||
Where `....` is a personal API token, as such:
|
||||
|
||||

|
||||
|
||||
##### 1.2.3 Deploy Keys
|
||||
|
||||
Deploy keys, a feature of Github, and some other platforms allow you to
|
||||
generate a **second** set of SSH keys for the connection between Github and
|
||||
the servers themselves.
|
||||
|
||||
Slightly perversely in this case the public key is uploaded to the repository
|
||||
host, and the private key must be copied to each server that you want to
|
||||
deploy to.
|
||||
|
||||
Github has a quite excellent guide on this, much of which (unsurprisingly)
|
||||
overlaps with the SSH key instructions above.
|
||||
|
||||
* [Github Help: Managing Deploy Keys](https://help.github.com/articles/managing-deploy-keys)
|
||||
|
||||
### Authorisation
|
||||
|
||||
The second part of this topic is that our deploy user needs to be authorised
|
||||
to work in the deployment directory, on the server. That means we need to be
|
||||
able to work, ideally without `sudo` (none of the default Capistrano recipes
|
||||
expect `sudo` to be available), or for your custom recipes, you will need to
|
||||
have configured *passwordless* `sudo`. Configuring `sudo` to give some users
|
||||
access to come commands under some circumstances is beyond the scope of this
|
||||
documentation, but sufficed to say something like:
|
||||
|
||||
{% prism bash %}
|
||||
deploy ALL=NOPASSWD:/etc/init.d/mysqld, /etc/init.d/apache2
|
||||
{% endprism %}
|
||||
|
||||
This example would give the user named `deploy` access to call `sudo
|
||||
/etc/init.d/mysql _________` and the same for the `apache2` control script.
|
||||
|
||||
**Granting passwordless sudo should not be done lightly.** It can be dangerous.
|
||||
For example if an unprivilidged user can *edit* the script that they can run
|
||||
as root, they can easily edit it to do anything they want that is evil. Use
|
||||
this carefully, and ideally architect your systems so that non-privlidged
|
||||
users can restart services, or that services restart *themselves* when they
|
||||
notice change.
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue