Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Need the ssh plugin

  • Loading branch information...
commit 0a6b57620821504de8fe6d2687d81e29b091d982 1 parent 30c81c7
Eric Davis authored
2  app/manifests/application_manifest.rb
View
@@ -203,6 +203,8 @@ def sqlite_stack
plugin :iptables
recipe :iptables
+ recipe :ssh
+
# Add your application's custom requirements here
def application_packages
88 vendor/plugins/moonshine_ssh/README.rdoc
View
@@ -0,0 +1,88 @@
+= Moonshine_SSH
+
+== A plugin for Moonshine[http://github.com/railsmachine/moonshine]
+
+This plugin provides a few security improvements for your default SSH
+configuration. It also gives you the ability to easily customize your
+settings. Browse through the sshd_config in the <tt>templates/</tt> directory
+to see the available settings.
+
+The new configuration file is tested before it's used, so there's less
+chance that you'll accidentally lock yourself out.
+
+=== Instructions
+
+* <tt>script/plugin install git://github.com/railsmachine/moonshine_ssh.git</tt>
+* Edit moonshine.yml to customize plugin settings if desired:
+ :ssh
+ :port: 9022
+ :allow_users:
+ - rob
+ - rails
+* Include the plugin and recipe you in your manifest:
+ plugin :ssh
+ recipe :ssh
+
+=== SFTP-only users
+
+OpenSSH supports chrooting users natively. You can create users who will be chrooted
+and only allowed to use SFTP (no console access) by adding the following to your
+moonshine.yml:
+
+ :ssh:
+ :sftponly: true
+
+Then add this to your manifest:
+
+ plugin :ssh
+ recipe :ssh
+
+This creates a user called sftponly with a randomized password. To allow access, you can manag authorized_keys through your manifest:
+
+ file '/home/sftponly/home/sftponly/.ssh/authorized_keys',
+ :ensure => :present,
+ :content => YOUR_SSH_PUBKEYS
+
+Once connected via sftp, the user will be chrooted to /home/sftponly where they will only see a
+'home' directory. The user can upload files to /home/sftponly. For a normal user, the uploaded
+files will be located at /home/sftponly/home/sftponly.
+
+==== Advanced
+
+For a more complicated example, we'll consider a user called 'rob' who needs to upload
+files into a directory under the Rails application's shared/ directory. Since he will
+be chrooted, he can't have direct access to the directory. Also, the directory is owned
+by the rails group, so he'll need to be a member of that. Finally, we don't want to worry
+about public keys, so he'll need a password.
+
+In your moonshine.yml:
+
+ :ssh:
+ :sftponly:
+ :users:
+ :rob:
+ :groups: rails
+ :password: sooper_sekrit
+
+In your manifest:
+
+ plugin :ssh
+ recipe :ssh
+ def mount_assets
+ file '/home/rob/home/rob/assets',
+ :ensure => :directory,
+ :owner => 'rob',
+ :require => file('/home/rob/home/rob')
+
+ mount '/home/rob/home/rob/assets',
+ :ensure => :mounted,
+ :device => "#{configuration[:deploy_to]}/shared/assets/",
+ :options => 'bind',
+ :fstype => :none,
+ :atboot => true,
+ :remounts => true,
+ :require => file('/home/rob/home/rob/assets')
+ end
+ recipe :mount_assets
+
+Then deploy, and you're done! The user's /home/rob/assets directory is now actually the shared assets directory. Anything uploaded there will be available to the application automatically.
89 vendor/plugins/moonshine_ssh/lib/ssh.rb
View
@@ -0,0 +1,89 @@
+module SSH
+
+ # Ensures the SSH server is installed, running, and configured
+ # per specifications. Use <tt>configure</tt> to change defaults.
+ # The available options can be gathered by perusing the sshd_config
+ # template.
+ #
+ # configure(:ssh => {:permit_root_login => 'yes', :port => 9022})
+ #
+ def ssh(options = {})
+
+ package 'ssh', :ensure => :installed
+ service 'ssh', :enable => true, :ensure => :running
+
+ if options[:sftponly]
+ options[:subsystem] = {:sftp => 'internal-sftp'}
+ sftponly(options[:sftponly])
+ end
+
+ file '/etc/ssh/sshd_config.new',
+ :mode => '644',
+ :content => template(File.join(File.dirname(__FILE__), '..', 'templates', 'sshd_config'), binding),
+ :require => package('ssh'),
+ :notify => exec('update_sshd_config')
+
+ exec 'cp /etc/ssh/sshd_config.new /etc/ssh/sshd_config',
+ :alias => 'update_sshd_config',
+ :onlyif => '/usr/sbin/sshd -t -f /etc/ssh/sshd_config.new',
+ :refreshonly => true,
+ :require => file('/etc/ssh/sshd_config.new'),
+ :notify => service('ssh')
+
+ end
+
+ private
+
+ # Sets up users and directories for chrooted, sftp-only access
+ # By default, the chroot is /home/USERNAME and the user's home
+ # will be inside that, at /home/USERNAME/home/USERNAME
+ def sftponly(options)
+
+ group 'sftponly', :ensure => :present
+
+ exec "fake shell",
+ :command => "echo /bin/false >> /etc/shells",
+ :onlyif => "test -z `grep /bin/false /etc/shells`"
+
+ (options[:users]||'sftponly').to_a.each do |user,hash|
+ user = user.to_s
+
+ chroot = options[:chroot_directory] || "/home/#{user}"
+ homedir = chroot + ( hash[:home] || "/home/#{user}" )
+
+ parent_directories homedir, :owner => 'root', :mode => '755'
+ file homedir,
+ :ensure => :directory,
+ :owner => user,
+ :group => user,
+ :require => user(user)
+
+ user user,
+ :ensure => :present,
+ :home => "/home/#{user}/home/#{user}",
+ :shell => "/bin/false",
+ :groups => (['sftponly'] + hash[:groups].to_a).uniq,
+ :require => [group('sftponly'),exec('fake shell')],
+ :notify => exec("#{user} password")
+
+ password = hash[:password] || rand_pass(6)
+ exec "#{user} password",
+ :command => "echo #{user}:#{password} | chpasswd",
+ :refreshonly => true
+
+ end
+ end
+
+ def parent_directories(path,options)
+ options.merge!({:ensure => :directory})
+ while path != "/"
+ path = File.split(path)[0]
+ file path, options
+ end
+ end
+
+ def rand_pass(len)
+ Array.new(len/2) { rand(256) }.pack('C*').unpack('H*').first
+ end
+
+end
3  vendor/plugins/moonshine_ssh/moonshine/init.rb
View
@@ -0,0 +1,3 @@
+require "#{File.dirname(__FILE__)}/../lib/ssh.rb"
+
+include SSH
8 vendor/plugins/moonshine_ssh/spec/spec_helper.rb
View
@@ -0,0 +1,8 @@
+require 'rubygems'
+ENV['RAILS_ENV'] = 'test'
+ENV['RAILS_ROOT'] ||= File.dirname(__FILE__) + '/../../../..'
+
+require File.join(File.dirname(__FILE__), '..', '..', 'moonshine', 'lib', 'moonshine.rb')
+require File.join(File.dirname(__FILE__), '..', 'lib', 'ssh.rb')
+
+require 'shadow_puppet/test'
86 vendor/plugins/moonshine_ssh/spec/ssh_spec.rb
View
@@ -0,0 +1,86 @@
+require File.join(File.dirname(__FILE__), 'spec_helper.rb')
+
+class SSHManifest < Moonshine::Manifest
+ plugin :ssh
+end
+
+describe SSH do
+
+ before do
+ @manifest = SSHManifest.new
+ end
+
+
+ it "should be executable" do
+ @manifest.should be_executable
+ end
+
+ it "should load the template file" do
+ File.should_receive(:read).with(File.expand_path(File.join(File.dirname(__FILE__), '..', 'templates', 'sshd_config'))).and_return "SSH CONFIG"
+ @manifest.ssh
+ end
+
+ it "should set default values" do
+ @manifest.ssh
+ @manifest.files.should include("/etc/ssh/sshd_config.new")
+ sshd_config = @manifest.files["/etc/ssh/sshd_config.new"].content
+ sshd_config.should match /Port 22/
+ sshd_config.should match /PermitRootLogin no/
+ end
+
+ it "should check the configuration file before updating" do
+ @manifest.ssh
+ @manifest.execs.should include("cp /etc/ssh/sshd_config.new /etc/ssh/sshd_config")
+ @manifest.execs["cp /etc/ssh/sshd_config.new /etc/ssh/sshd_config"].onlyif.should match /sshd -t/
+ end
+
+ it "should allow customization" do
+ @manifest.ssh( :port => 9022 )
+ @manifest.files["/etc/ssh/sshd_config.new"].content.should match /Port 9022/
+ end
+
+ describe "configured for sftponly" do
+
+ before do
+ @manifest.ssh(:sftponly => {
+ :users => {
+ :rob => {
+ :password => 'sekrit',
+ :groups => 'rails'
+ }
+ }
+ })
+ end
+
+ it "should create the sftponly group" do
+ @manifest.groups.should include('sftponly')
+ end
+
+ it "should add the user" do
+ @manifest.users.should include('rob')
+ @manifest.execs['rob password'].command.should == "echo rob:sekrit | chpasswd"
+ end
+
+ it "should add user to sftponly and extra groups if requested" do
+ @manifest.users['rob'].groups.should == ['sftponly','rails']
+ end
+
+ it "should create the home directory" do
+ @manifest.files.should include('/home/rob/home/rob')
+ @manifest.files['/home/rob/home/rob'].owner.should == 'rob'
+ @manifest.files['/home/rob/home'].owner.should == 'root'
+ @manifest.files['/home/rob'].owner.should == 'root'
+ end
+
+ it "should set the sftp subsystem" do
+ @manifest.files['/etc/ssh/sshd_config.new'].content.should match /Subsystem sftp internal-sftp/
+ end
+
+ it "should add a group matcher to the sshd config" do
+ @manifest.files['/etc/ssh/sshd_config.new'].content.should match /Match Group sftponly/
+ @manifest.files['/etc/ssh/sshd_config.new'].content.should match /ChrootDirectory \/home\/%u/
+ end
+
+ end
+
+end
110 vendor/plugins/moonshine_ssh/templates/sshd_config
View
@@ -0,0 +1,110 @@
+# Moonshine_SSH generated configuration file
+# See the sshd(8) manpage for details
+#
+# The default configuration is based on the Ubuntu
+# defaults with a few tweaks, mostly for security.
+
+# What ports, IPs and protocols we listen for
+Port <%= options[:port] || 22 %>
+# Use these options to restrict which interfaces/protocols sshd will bind to
+#ListenAddress ::
+#ListenAddress 0.0.0.0
+<% if options[:listen_address] %>
+ListenAddress <%= options[:listen_address] %>
+<% end %>
+Protocol <%= options[:protocol] || 2 %>
+# HostKeys for protocol version 2
+HostKey /etc/ssh/ssh_host_rsa_key
+HostKey /etc/ssh/ssh_host_dsa_key
+#Privilege Separation is turned on for security
+UsePrivilegeSeparation <%= options[:use_privilege_separation] || 'yes' %>
+
+# Lifetime and size of ephemeral version 1 server key
+KeyRegenerationInterval <%= options[:key_regeneration_interval] || 3600 %>
+ServerKeyBits <%= options[:server_key_bits] || 768 %>
+
+# Logging
+SyslogFacility <%= options[:syslog_facility] || 'AUTH' %>
+LogLevel <%= options[:log_level] || 'INFO' %>
+
+# Authentication:
+LoginGraceTime <%= options[:login_grace_time] || 30 %>
+PermitRootLogin <%= options[:permit_root_login] || 'no' %>
+StrictModes <%= options[:strict_modes] || 'yes' %>
+
+RSAAuthentication yes
+PubkeyAuthentication yes
+#AuthorizedKeysFile %h/.ssh/authorized_keys
+
+# Don't read the user's ~/.rhosts and ~/.shosts files
+IgnoreRhosts yes
+# For this to work you will also need host keys in /etc/ssh_known_hosts
+RhostsRSAAuthentication no
+# similar for protocol version 2
+HostbasedAuthentication no
+# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication
+#IgnoreUserKnownHosts yes
+
+# To enable empty passwords, change to yes (NOT RECOMMENDED)
+PermitEmptyPasswords no
+
+# Change to yes to enable challenge-response passwords (beware issues with
+# some PAM modules and threads)
+ChallengeResponseAuthentication no
+
+# Change to no to disable tunnelled clear text passwords
+PasswordAuthentication <%= options[:password_authentication] || 'yes' %>
+
+# Kerberos options
+#KerberosAuthentication no
+#KerberosGetAFSToken no
+#KerberosOrLocalPasswd yes
+#KerberosTicketCleanup yes
+
+# GSSAPI options
+#GSSAPIAuthentication no
+#GSSAPICleanupCredentials yes
+
+X11Forwarding <%= options[:x11_forwarding] || 'yes' %>
+X11DisplayOffset <%= options[:x11_display_offsets] || 10 %>
+PrintMotd <%= options[:print_motd] || 'no' %>
+PrintLastLog <%= options[:print_last_log] || 'yes' %>
+TCPKeepAlive <%= options[:tcp_keep_alive] || 'yes' %>
+#UseLogin no
+
+# http://techgurulive.com/2008/09/15/how-to-protect-ssh-from-multiple-and-parallel-coordinated-attacks/
+MaxStartups <%= options[:max_startups] || '2:50:5' %>
+
+<% if options[:use_banner] %>
+Banner /etc/issue.net
+<% end %>
+
+# Allow client to pass locale environment variables
+AcceptEnv LANG LC_*
+
+<% if options[:subsystem] %>
+ <% options[:subsystem].each do |subsystem,command| %>
+Subsystem <%= subsystem %> <%= command %>
+ <% end %>
+<% else %>
+Subsystem sftp /usr/lib/openssh/sftp-server
+<% end %>
+
+UsePAM <%= options[:use_pam] || 'yes' %>
+
+<% if options[:allow_users] %>
+AllowUsers <%= options[:allow_users].to_a.join(' ') %>
+<% end %>
+<% if options[:allow_groups] %>
+AllowGroups <%= options[:allow_groups].to_a.join(' ') %>
+<% end %>
+
+<%= options[:extra] %>
+
+<% if options[:sftponly] %>
+Match Group sftponly
+ ChrootDirectory <%= options[:sftponly][:chroot_directory] || '/home/%u' %>
+ ForceCommand internal-sftp
+ X11Forwarding no
+ AllowTcpForwarding no
+<% end %>
Please sign in to comment.
Something went wrong with that request. Please try again.