Skip to content

Commit

Permalink
add windows support
Browse files Browse the repository at this point in the history
  • Loading branch information
jaymzh committed Sep 19, 2020
1 parent eea046d commit 110cdd4
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 33 deletions.
36 changes: 24 additions & 12 deletions cookbooks/fb_ssh/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,31 +25,36 @@ packages for ssh.
You can skip package management if you have local packages or otherwise need to
do your own management by setting `manage_packages` to false.

Given the many ways to manage packages on Windows, especially for SSH,
we default `manage_packages` to false on Windows.

### Server configuration (sshd_config)
The `sshd_config` hash holds configs that go into `/etc/ssh/sshd_config`. In
general each key can have one of three types, bool, string/ints, or array.

Bools are translated into `yes`/`no` when emitted into the config file. These
are straight-forward:

```
```ruby
node.default['fb_ssh']['sshd_config']['PubkeyAuthentication'] = true
```

Becomes:
```

```text
PubkeyAuthentication yes
```

Strings and ints are always treated like normal strings:

```
```ruby
node.default['fb_ssh']['sshd_config']['ClientAliveInterval'] = 0
node.default['fb_ssh']['sshd_config']['ForceCommand'] = '/bin/false'
```

Becomes:
```

```text
ClientAliveInterval 0
ForceCommand /bin/false
```
Expand All @@ -59,7 +64,7 @@ here to make management easy, one could clearly take a multi-value value key
and make it a string and it would work, but we support arrays to make modifying
the value later in the runlist easier. For example:

```
```ruby
node.default['fb_ssh']['sshd_config']['AuthorizedKeysFile'] = [
'.ssh/authorized_keys',
'.ssh/authorized_keys2',
Expand All @@ -68,13 +73,14 @@ node.default['fb_ssh']['sshd_config']['AuthorizedKeysFile'] = [

Means later it's easy for someone to do:

```
```ruby
node.default['fb_ssh']['sshd_config']['AuthorizedKeysFile'].
delete('.ssh/authorized_keys2')
```

or:
```

```ruby
node.default['fb_ssh']['sshd_config']['AuthorizedKeysFile'] <<
'/etc/ssh/authorized_keys/%u'
```
Expand All @@ -96,7 +102,7 @@ change the order of your match statements, so be careful.
Match statements are the exception to the datatype rule above - their value is
a hash, and that hash is treated the same as the top-level sshd_config hash:

```
```ruby
node.default['fb_ssh']['sshd_config']['Match Address 1.2.3.4'] => {
'PasswordAuthentication' => true,
}
Expand All @@ -114,7 +120,7 @@ happen:
`node['fb_ssh']['authorized_principals_users']`. The format of the
`authorized_principals` attribute is:

```
```ruby
node.default['fb_ssh']['authorized_principals'][$USER] = ['one', 'two']
```

Expand All @@ -130,15 +136,15 @@ These work similarly to Authorized Principals. If you set
`node['fb_ssh']['authorized_keys_users']`. The format of the items
in databag is:

```
```ruby
{
'id': $USER,
'keyname1': $KEY1,
'keyname2': $KEY2,
...
}
```

There should be one item for each user, as many keys as you'd like may
be in that item.

Expand All @@ -151,11 +157,17 @@ node.default['fb_ssh']['authorized_keys']['john']['key1'] = '...'

Anything in the node overrides databags.

*NOTE FOR WINDOWS USERS*: On Windows the keys are managed in the homedirectory,
not in a central location. This is because usernames are often in the format of
`domain\user`, which means that `%u` causes sshd to expand the path to
`C:\ProgramData\ssh\authorized_keys\domain\\user`, which is an illegal filename
you can never make.

### Client config (ssh_config)
The client config works the same as the server config, except the special-case
is `Host` keys instead of `Match` keys. As an example:

```
```ruby
node.default['fb_ssh']['ssh_config']['ForwardAgent'] = true
node.default['fb_ssh']['ssh_config']['Host *.cool.com'] = {
'ForwardX11' => true,
Expand Down
2 changes: 1 addition & 1 deletion cookbooks/fb_ssh/attributes/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

default['fb_ssh'] = {
'enable_central_authorized_keys' => false,
'manage_packages' => true,
'manage_packages' => !node.windows?,
'sshd_config' => {
'PermitRootLogin' => false,
'UsePAM' => true,
Expand Down
8 changes: 6 additions & 2 deletions cookbooks/fb_ssh/libraries/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@

module FB
class SSH
def self.confdir(node)
node.windows? ? 'C:/ProgramData/ssh' : '/etc/ssh'
end

DESTDIR = {
'keys' => '/etc/ssh/authorized_keys',
'principals' => '/etc/ssh/authorized_princs',
'keys' => 'authorized_keys',
'principals' => 'authorized_princs',
}.freeze
end
end
1 change: 1 addition & 0 deletions cookbooks/fb_ssh/metadata.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@
supports 'centos'
supports 'debian'
supports 'ubuntu'
supports 'windows'
39 changes: 29 additions & 10 deletions cookbooks/fb_ssh/recipes/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@
client_pkg = value_for_platform_family(
['rhel', 'fedora'] => 'openssh-clients',
['debian'] => 'openssh-client',
# not used, but keeps the resource compiling
['windows'] => 'openssh-client',
)

svc = value_for_platform_family(
['rhel', 'fedora'] => 'sshd',
['debian'] => 'ssh',
['windows'] => 'sshd',
)

package client_pkg do
Expand All @@ -34,11 +37,13 @@
end

package 'openssh-server' do
only_if { node['fb_ssh']['manage_packages'] }
action :upgrade
notifies :restart, 'service[ssh]'
end

whyrun_safe_ruby_block 'handle late binding ssh configs' do
not_if { node.windows? }
block do
%w{keys principals}.each do |type|
enable_name = "enable_central_authorized_#{type}"
Expand All @@ -50,30 +55,38 @@
)
end
node.default['fb_ssh']['sshd_config'][cfgname] =
"#{FB::SSH::DESTDIR[type]}/%u"
File.join(FB::SSH.confdir(node), FB::SSH::DESTDIR[type], '%u')
end
end
end
end

template '/etc/ssh/sshd_config' do
template ::File.join(FB::SSH.confdir(node), 'sshd_config') do
source 'ssh_config.erb'
owner 'root'
group 'root'
mode '0644'
unless node.windows?
owner 'root'
group 'root'
mode '0644'
if node.windows?
verify '"C:/Program Files/OpenSSH-Win64/sshd.exe" -t -f %{path}'
else
verify '/usr/sbin/sshd -t -f %{path}'
end
end
variables({ :type => 'sshd_config' })
verify '/usr/sbin/sshd -t -f %{path}'
# in firstboot we may not be able to get in until ssh is restarted
# on the desired config, so restart immediately. Otherwise, delay
ntype = node.firstboot_any_phase? ? :immediately : :delayed
notifies :restart, 'service[ssh]', ntype
end

template '/etc/ssh/ssh_config' do
template ::File.join(FB::SSH.confdir(node), 'ssh_config') do
source 'ssh_config.erb'
owner 'root'
group 'root'
mode '0644'
unless node.windows?
owner 'root'
group 'root'
mode '0644'
end
variables({ :type => 'ssh_config' })
end

Expand All @@ -94,3 +107,9 @@
service_name svc
action [:enable, :start]
end

if node.windows?
service 'ssh-agent' do
action [:enable, :start]
end
end
42 changes: 34 additions & 8 deletions cookbooks/fb_ssh/resources/authorization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@

action_class do
def manage(type)
keydir = FB::SSH::DESTDIR[type]
keydir = ::File.join(FB::SSH.confdir(node), FB::SSH::DESTDIR[type])

directory keydir do
owner 'root'
group 'root'
mode '0755'
unless node.windows?
owner 'root'
group 'root'
mode '0755'
end
end

unless node['fb_ssh']["authorized_#{type}_users"].empty?
Expand All @@ -40,11 +42,35 @@ def manage(type)
auth_map.each_key do |user|
next if allowed_users && !allowed_users.include?(user)

template "#{keydir}/#{user}" do
# windows sucks and on ssh the "username" is "corp\\whatever" which is
# not a valid file name. Ugh. So we leave it in the user's homedir
if node.windows?
user = user.split('\\').last
homedir = "C:/Users/#{user}"
keyfile = "#{homedir}/.ssh/authorized_keys"
# users who don't have homedirectories, we skip
next unless ::File.exist?(homedir)

directory "#{homedir}/.ssh" do
rights :read, user
rights :full_control, 'Administrators'
inherits false
end
else
keyfile = "#{keydir}/#{user}"
end

template keyfile do
source "authorized_#{type}.erb"
owner 'root'
group 'root'
mode '0644'
if node.windows?
rights :read, user
rights :full_control, 'Administrators'
inherits false
else
owner 'root'
group 'root'
mode '0644'
end
if type == 'keys' && !auth_map[user]
d = data_bag_item('fb_ssh_authorized_keys', user)
d.delete('id')
Expand Down

0 comments on commit 110cdd4

Please sign in to comment.