diff --git a/.kitchen.yml b/.kitchen.yml index 85342c4..4ea17ba 100644 --- a/.kitchen.yml +++ b/.kitchen.yml @@ -15,7 +15,16 @@ suites: - recipe[phabricator::default] attributes: { "phabricator": { - "domain": "phabricator.example.com" + "domain": "phabricator.example.com", + "vcs_ssh": { + "hosting_enabled": true + } + }, + "authorization": { + "sudo": { + "users": ["vagrant"], + "passwordless": true + } } } - name: arcanist diff --git a/README.md b/README.md index 512957a..9f71b54 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,11 @@ This cookbook has been tested on Ubuntu 12.04 and 14.04. - `nginx ~> 2.7` - `mysql ~> 6.0` - `database ~> 3.1` +- `openssl ~> 4.4` Attributes ---------- -See `attributes/default.rb`. +See `attributes/default.rb` and `attributes/repo_hosting.rb`. Usage ----- @@ -38,11 +39,42 @@ Just include `phabricator` in your node's `run_list`: MySQL Installation ------------------ -If `node['phabricator]['mysql_host']` is set to `localhost`, the cookbook will +If `node['phabricator']['mysql_host']` is set to `localhost`, the cookbook will install and configure the MySQL server appropriately. Otherwise, it will configure Phabricator to connect to an external database. In the latter case, a MySQL database user will //not// be managed by this cookbook. +Repository Hosting +------------------ + +If running Ubuntu 14.04, this recipe can also setup Phabricator for hosting repositories +over SSH. (It requires functionality in OpenSSH 6.2 or newer, which is not available for +Ubuntu 12.04). + +If `node['phabricator']['vcs_ssh']['hosting_enabled']` is set to `true`, the cookbook will +set up the server for serving up VCS over SSH. +Defaults to `false` to preserve behaviour with previous versions. + +It will setup a seperate daemon called `ssh-vcs`, which is configured to listen on a +different port to the standard system sshd, this is controlled via +`default['phabricator']['vcs_ssh']['port']` It defaults to `617`. + +This is a highly locked down SSH process following recommendations from upstream: +[https://secure.phabricator.com/book/phabricator/article/diffusion_hosting/] + +The user used for VCS is controlled via `default['phabricator']['vcs_ssh']['user']` and defaults +to `git`. + +When interacting with Diffusion over ssh the following is recommended in your `~/.ssh/config`: + +``` +Host phabricator.example.com + User git + Port 617 + Compression yes +``` + + Contributing ------------ 1. Fork the repository on Github diff --git a/attributes/vcs_ssh_hosting.rb b/attributes/vcs_ssh_hosting.rb new file mode 100644 index 0000000..ae6f0eb --- /dev/null +++ b/attributes/vcs_ssh_hosting.rb @@ -0,0 +1,28 @@ +# Default to previous behaviour of not setting up SSH for VCS Hosting. +# NB this setting has no effect on Ubuntu 12.04 as its version of OpenSSH +# is too old to work. +# See: +# [https://secure.phabricator.com/book/phabricator/article/diffusion_hosting/#configuring-ssh] +default['phabricator']['vcs_ssh']['hosting_enabled'] = false + +# Port that VCS SSH will listen on (seperate daemon) +default['phabricator']['vcs_ssh']['port'] = '617' + +# Username for SSH for VCS +default['phabricator']['vcs_ssh']['user'] = 'git' + +# Homedir for VCS user. NB this is not the repo path. See +# `node['phabricator']['repository_path']` in the default attributes +# file for this.` +home_dir_prefix = '/home' +home_dir = File.join(home_dir_prefix, node['phabricator']['vcs_ssh']['user']) +default['phabricator']['vcs_ssh']['home_dir'] = home_dir + +append_paths = [] +append_paths << File.join(node['phabricator']['path'], '/phabricator/support/bin') +append_paths << '/bin' +append_paths << '/usr/bin' +append_paths << '/usr/local/bin' +append_paths << '/usr/lib/git-core' + +default['phabricator']['config']['environment.append-paths'] = %('#{append_paths}') diff --git a/metadata.rb b/metadata.rb index cbaa3a2..cc7c0cc 100644 --- a/metadata.rb +++ b/metadata.rb @@ -15,3 +15,5 @@ depends 'nginx', '~> 2.7' depends 'mysql', '~> 6.0' depends 'database', '~> 3.1' +depends 'sudo', '~> 2.7.2' +depends 'openssl', '~> 4.4.0' diff --git a/recipes/default.rb b/recipes/default.rb index deaec72..a6c27ff 100644 --- a/recipes/default.rb +++ b/recipes/default.rb @@ -65,7 +65,7 @@ action :create user node['phabricator']['user'] group node['phabricator']['group'] - mode "0750" + mode "0755" end %w{ phabricator libphutil arcanist }.each do |repo| @@ -83,7 +83,7 @@ action :create user node['phabricator']['user'] group node['phabricator']['group'] - mode "0750" + mode "0755" end # Set up file storage @@ -187,3 +187,12 @@ node.set['phabricator']['storage_upgrade_done'] = true end end + +# Setup ssh repo hosting if we want it! +# Only applicable on 14.04 or newer, due to need for AuthorizedKeysCommand which came in +# OpenSSH 6.2. +if node['platform_version'] >= '14.04' + if node['phabricator']['vcs_ssh']['hosting_enabled'] + include_recipe 'phabricator::vcs_ssh_hosting' + end +end diff --git a/recipes/vcs_ssh_hosting.rb b/recipes/vcs_ssh_hosting.rb new file mode 100644 index 0000000..19a4795 --- /dev/null +++ b/recipes/vcs_ssh_hosting.rb @@ -0,0 +1,110 @@ +# +# Cookbook Name:: phabricator +# Recipe:: vcs_ssh_hosting +# +# Copyright 2014, MET Norway +# +# Authors: Kim Tore Jensen , +# Andrew Mulholland +# +# Sets up phabricator for hosting repositories over SSH + +# To prevent the `node['phabricator']['vcs_ssh']['user']` (i.e. git ) user from being locked, a +# password needs to be provided with account creation. Using OpenSSLCookbook's RandomPassword +# function to generate a secure password. +unless node['phabricator']['vcs_ssh']['password'] + Chef::Recipe.send(:include, OpenSSLCookbook::RandomPassword) + require 'digest/sha2' + password = Digest::SHA512.hexdigest(random_password(length: 50)) + node.set['phabricator']['vcs_ssh']['password'] = password +end + +user node['phabricator']['vcs_ssh']['user'] do + comment 'VCS User' + home node['phabricator']['vcs_ssh']['home_dir'] + shell '/bin/sh' + system true + # This password is never used! + password node['phabricator']['vcs_ssh']['password'] + action [:create, :unlock] +end + +directory node['phabricator']['vcs_ssh']['home_dir'] do + action :create + owner node['phabricator']['vcs_ssh']['user'] + mode '0755' +end + +directory '/etc/ssh-phabricator' do + action :create +end + +# Install Upstart init script for the SSH Daemon +template '/etc/init/ssh-vcs.conf' do + source 'upstart/ssh-vcs.conf.erb' + owner 'root' + group 'root' + mode '0755' + notifies :restart, 'service[ssh-vcs]' +end + +service 'ssh-vcs' do + provider Chef::Provider::Service::Upstart + supports status: true, restart: true, reload: true + action [:enable, :start] +end + +template '/etc/ssh-phabricator/sshd_config' do + source 'vcs_ssh/sshd_config.erb' + owner 'root' + group 'root' + mode '0755' + notifies :restart, 'service[ssh-vcs]' +end + +directory '/usr/libexec' do + owner 'root' + group 'root' + mode '0755' +end + +template '/usr/libexec/ssh-phabricator-hook' do + source 'vcs_ssh/ssh-phabricator-hook.erb' + owner 'root' + group 'root' + mode '0755' +end + +# enable /etc/sudoers.d directory to enable the sudoer provider to work +node.override['authorization']['sudo']['include_sudoers_d'] = true + +# We use template here, because right now upstream `sudo` cookbook doesn't support +# setting setenv, which is needed because phab runs `sudo -E`. +# Have filed https://github.com/chef-cookbooks/sudo/pull/72 for this. +sudo 'vcs_user' do + template 'vcs_ssh/phab_sudo.erb' + variables commands: ['/usr/bin/git-upload-pack', + '/usr/bin/git-receive-pack', + '/usr/bin/hg', + '/usr/bin/svnserve'], + nopasswd: true, + setenv: true, + sudoer: node['phabricator']['vcs_ssh']['user'], + runas: node['phabricator']['user'] +end + +sudo 'www-data' do + template 'vcs_ssh/phab_sudo.erb' + variables commands: ['/usr/bin/git-http-backend'], + nopasswd: true, + setenv: true, + sudoer: 'www-data', + runas: node['phabricator']['user'] +end + +# Install Git, Mercurial, SVN +%w(git subversion mercurial).each do |pkg| + package pkg do + action :install + end +end diff --git a/templates/default/upstart/ssh-vcs.conf.erb b/templates/default/upstart/ssh-vcs.conf.erb new file mode 100644 index 0000000..b293d99 --- /dev/null +++ b/templates/default/upstart/ssh-vcs.conf.erb @@ -0,0 +1,27 @@ +# ssh for VCS +# +# This OpenSSH server provides ssh access to VCS for Phabricator + +description "SSH-VCS server" + +start on runlevel [2345] +stop on runlevel [!2345] + +respawn +respawn limit 10 5 +umask 022 + +env SSH_SIGSTOP=1 +expect stop + +# 'sshd -D' leaks stderr and confuses things in conjunction with 'console log' +console none + +pre-start script + test -x /usr/sbin/sshd || { stop; exit 0; } + +end script + +# if you used to set SSHD_OPTS in /etc/default/ssh, you can change the +# 'exec' line here instead +exec /usr/sbin/sshd -D -f /etc/ssh-phabricator/sshd_config diff --git a/templates/default/vcs_ssh/phab_sudo.erb b/templates/default/vcs_ssh/phab_sudo.erb new file mode 100644 index 0000000..73bc8b7 --- /dev/null +++ b/templates/default/vcs_ssh/phab_sudo.erb @@ -0,0 +1,6 @@ +# This file is managed by Chef. +# Do NOT modify this file directly. + +<% @commands.each do |command| -%> +<%= @sudoer %> ALL=(<%= @runas %>) <%= 'NOPASSWD:' if @nopasswd %><%= 'SETENV:' if @setenv %><%= command %> +<% end -%> diff --git a/templates/default/vcs_ssh/ssh-phabricator-hook.erb b/templates/default/vcs_ssh/ssh-phabricator-hook.erb new file mode 100644 index 0000000..16e9b00 --- /dev/null +++ b/templates/default/vcs_ssh/ssh-phabricator-hook.erb @@ -0,0 +1,13 @@ +#!/bin/sh +# +VCSUSER="<%= node['phabricator']['vcs_ssh']['user'] %>" +# +# Path to Phabricator directory. +ROOT="<%= File.join(node['phabricator']['path'],'phabricator') %>" + +if [ "$1" != "$VCSUSER" ]; +then + exit 1 +fi + +exec "$ROOT/bin/ssh-auth" $@ diff --git a/templates/default/vcs_ssh/sshd_config.erb b/templates/default/vcs_ssh/sshd_config.erb new file mode 100644 index 0000000..aadd256 --- /dev/null +++ b/templates/default/vcs_ssh/sshd_config.erb @@ -0,0 +1,12 @@ +AuthorizedKeysCommand /usr/libexec/ssh-phabricator-hook +AuthorizedKeysCommandUser <%= node['phabricator']['vcs_ssh']['user'] %> + +Port <%= node['phabricator']['vcs_ssh']['port'] %> +Protocol 2 +PermitRootLogin no +AllowAgentForwarding no +AllowTcpForwarding no +PrintMotd no +PrintLastLog no +PasswordAuthentication no +AuthorizedKeysFile none diff --git a/test/integration/default/bats/default.bats b/test/integration/default/bats/default.bats index dfe79a1..909aaa3 100644 --- a/test/integration/default/bats/default.bats +++ b/test/integration/default/bats/default.bats @@ -22,3 +22,21 @@ [ $status -eq 0 ] } } + +@test "SSHD VCS is running" { + if [ $(lsb_release -r | awk '{print $2}') == "12.04" ]; then + skip "SSH hosting not supported on 12.04" + fi + + run service ssh-vcs status + [ $status -eq 0 ] +} + +@test "SSHd for VCS is listening" { + if [ $(lsb_release -r | awk '{print $2}') == "12.04" ]; then + skip "SSH hosting not supported on 12.04" + fi + + run sh -c "netstat -nlp |grep :617" + [ $status -eq 0 ] +}