aboutsummaryrefslogtreecommitdiffstats
path: root/modules/openssh
diff options
context:
space:
mode:
Diffstat (limited to 'modules/openssh')
-rw-r--r--modules/openssh/manifests/pubkeys_directory.pp17
-rw-r--r--modules/openssh/manifests/server.pp3
-rw-r--r--modules/openssh/manifests/ssh_keys_from_ldap.pp26
-rw-r--r--modules/openssh/manifests/symlink_user.pp19
-rwxr-xr-xmodules/openssh/templates/ldap-sshkey2file.py204
-rw-r--r--modules/openssh/templates/sshd_config19
-rw-r--r--modules/openssh/templates/sshd_config_ldap3
7 files changed, 171 insertions, 120 deletions
diff --git a/modules/openssh/manifests/pubkeys_directory.pp b/modules/openssh/manifests/pubkeys_directory.pp
deleted file mode 100644
index cbcaeb88..00000000
--- a/modules/openssh/manifests/pubkeys_directory.pp
+++ /dev/null
@@ -1,17 +0,0 @@
-class openssh::pubkeys_directory {
- $pubkeys_directory = '/var/lib/pubkeys'
- file { $pubkeys_directory:
- ensure => directory,
- }
-
- file { "$pubkeys_directory/root":
- ensure => directory,
- mode => '0700',
- }
-
- file { "$pubkeys_directory/root/authorized_keys":
- ensure => link,
- target => '/root/.ssh/authorized_keys',
- mode => '0700',
- }
-}
diff --git a/modules/openssh/manifests/server.pp b/modules/openssh/manifests/server.pp
index ce60646b..c45268d2 100644
--- a/modules/openssh/manifests/server.pp
+++ b/modules/openssh/manifests/server.pp
@@ -1,6 +1,7 @@
class openssh::server {
# some trick to manage sftp server, who is arch dependent on mdv
- $path_to_sftp = "$::lib_dir/ssh/"
+ # TODO: the path changed on Mageia 6 to /usr/libexec/openssh/sftp-server
+ $path_to_sftp = "${::lib_dir}/ssh/"
package { 'openssh-server': }
diff --git a/modules/openssh/manifests/ssh_keys_from_ldap.pp b/modules/openssh/manifests/ssh_keys_from_ldap.pp
index d35f4d2a..9ea6c139 100644
--- a/modules/openssh/manifests/ssh_keys_from_ldap.pp
+++ b/modules/openssh/manifests/ssh_keys_from_ldap.pp
@@ -1,32 +1,20 @@
-class openssh::ssh_keys_from_ldap($symlink_users = [],
- $config = '') inherits server {
- # root account authorized_keys will be symlinked
- # if you want to add symlink on other accounts, use $symlink_users parameter
-
- File ['/etc/ssh/sshd_config'] {
- content => template('openssh/sshd_config','openssh/sshd_config_ldap')
- }
-
- package { 'python-ldap': }
-
- include openssh::pubkeys_directory
- $pubkeys_directory = $openssh::pubkeys_directory::pubkeys_directory
-
- symlink_user { $symlink_users: }
+class openssh::ssh_keys_from_ldap inherits server {
+ package { 'python3-ldap': }
$ldap_pwfile = '/etc/ldap.secret'
+ $nslcd_conf_file = '/etc/nslcd.conf'
$ldap_servers = get_ldap_servers()
- mga-common::local_script { 'ldap-sshkey2file.py':
+ mga_common::local_script { 'ldap-sshkey2file.py':
content => template('openssh/ldap-sshkey2file.py'),
- require => Package['python-ldap']
+ require => Package['python3-ldap']
}
cron { 'sshkey2file':
- command => '/usr/local/bin/ldap-sshkey2file.py',
+ command => '/bin/bash -c "/usr/local/bin/ldap-sshkey2file.py && ( [[ -f /usr/bin/mgagit && -d /var/lib/git/.gitolite ]] && /bin/su -c \'/usr/bin/mgagit glrun\' - git ) ||:"',
hour => '*',
minute => '*/10',
user => 'root',
environment => 'MAILTO=root',
- require => Local_script['ldap-sshkey2file.py'],
+ require => Mga_common::Local_script['ldap-sshkey2file.py'],
}
}
diff --git a/modules/openssh/manifests/symlink_user.pp b/modules/openssh/manifests/symlink_user.pp
deleted file mode 100644
index f2e107b1..00000000
--- a/modules/openssh/manifests/symlink_user.pp
+++ /dev/null
@@ -1,19 +0,0 @@
-define openssh::symlink_user() {
- include openssh::pubkeys_directory
- $pubkeys_directory = $openssh::pubkeys_directory::pubkeys_directory
- file { "$pubkeys_directory/$name":
- ensure => directory,
- owner => $name,
- group => $name,
- mode => '0700',
- }
-
- file { "$pubkeys_directory/$name/authorized_keys":
- # FIXME : fragile approximation for $HOME
- ensure => link,
- target => "/home/$name/.ssh/authorized_keys",
- mode => '0700',
- }
-}
-
-
diff --git a/modules/openssh/templates/ldap-sshkey2file.py b/modules/openssh/templates/ldap-sshkey2file.py
index eb8456ab..934e2865 100755
--- a/modules/openssh/templates/ldap-sshkey2file.py
+++ b/modules/openssh/templates/ldap-sshkey2file.py
@@ -1,96 +1,194 @@
-#!/usr/bin/python
+#!/usr/bin/python3
-import sys
+import argparse
import os
import random
+import shutil
+import sys
+import tempfile
+import textwrap
+from typing import Iterable
try:
import ldap
-except ImportError, e:
- print "Please install python-ldap before running this program"
+except ImportError:
+ print("Please install python-ldap before running this program")
sys.exit(1)
-basedn="<%= dc_suffix %>"
-peopledn="ou=people,%s" % basedn
+basedn = "<%= @dc_suffix %>"
+peopledn = f"ou=people,{basedn}"
<%-
ldap_servers.map! { |l| "'ldaps://#{l}'" }
-%>
-uris=[<%= ldap_servers.join(", ") %>]
+uris = [<%= ldap_servers.join(", ") %>]
random.shuffle(uris)
uri = " ".join(uris)
-timeout=5
-binddn="cn=<%= fqdn %>,ou=Hosts,%s" % basedn
-pwfile="<%= ldap_pwfile %>"
+timeout = 5
+binddn = f"cn=<%= @fqdn %>,ou=Hosts,{basedn}"
+ldap_secret_file = "<%= @ldap_pwfile %>"
+nslcd_conf_file = "<%= @nslcd_conf_file %>"
# filter out disabled accounts also
# too bad uidNumber doesn't support >= filters
-filter="(&(objectClass=inetOrgPerson)(objectClass=ldapPublicKey)(objectClass=posixAccount)(sshPublicKey=*))"
-keypathprefix="<%= pubkeys_directory %>"
-
-def usage():
- print "%s" % sys.argv[0]
- print
- print "Will fetch all enabled user accounts under %s" % peopledn
- print "with ssh keys in them and write each one to"
- print "%s/<login>/authorized_keys" % keypathprefix
- print
- print "This script is intented to be run from cron as root"
- print
-
-def get_pw(pwfile):
+objfilter = "(&(objectClass=inetOrgPerson)(objectClass=ldapPublicKey)(objectClass=posixAccount)(sshPublicKey=*))"
+keypathprefix = "/home"
+
+parser = argparse.ArgumentParser(
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ description=textwrap.dedent(f'''\
+ Will fetch all enabled user accounts under {peopledn}
+ with ssh keys in them and write each one to
+ {keypathprefix}/<login>/.ssh/authorized_keys
+
+ It will return failure when no keys are updated and success
+ when one or more keys have changed.
+
+ This script is intended to be run from cron as root;
+ '''))
+parser.add_argument('-n', '--dry-run', action='store_true')
+parser.add_argument('-v', '--verbose', action='store_true')
+args = parser.parse_args()
+
+
+def get_bindpw() -> str:
try:
- f = open(pwfile, 'r')
- except IOError, e:
- print "Error while reading password file, aborting"
- print e
- sys.exit(1)
- pw = f.readline().strip()
- f.close()
- return pw
+ return get_nslcd_bindpw(nslcd_conf_file)
+ except:
+ pass
-def write_keys(keys, user, uid, gid):
try:
- os.makedirs("%s/%s" % (keypathprefix,user), 0700)
+ return get_ldap_secret(ldap_secret_file)
except:
pass
- keyfile = "%s/%s/authorized_keys" % (keypathprefix,user)
- f = open(keyfile, 'w')
+
+ print("Error while reading password file, aborting")
+ sys.exit(1)
+
+
+def get_nslcd_bindpw(pwfile: str) -> str:
+ try:
+ with open(pwfile, 'r') as f:
+ pwfield = "bindpw"
+ for line in f:
+ ls = line.strip().split()
+ if len(ls) == 2 and ls[0] == pwfield:
+ return ls[1]
+ except IOError as e:
+ print("Error while reading nslcd file " + pwfile)
+ print(e)
+ raise
+
+ print("No " + pwfield + " field found in nslcd file " + pwfile)
+ raise Exception()
+
+
+def get_ldap_secret(pwfile: str) -> str:
+ try:
+ with open(pwfile, 'r') as f:
+ pw = f.readline().strip()
+ except IOError as e:
+ print("Error while reading password file " + pwfile)
+ print(e)
+ raise
+ return pw
+
+
+def write_keys(keys: Iterable[bytes], user: bytes, uid: int, gid: int) -> bool:
+ userdir = f"{keypathprefix}/{user.decode('utf-8')}"
+ keyfile = f"{userdir}/.ssh/authorized_keys"
+
+ fromldap = ""
for key in keys:
- f.write(key.strip() + "\n")
- f.close()
- os.chmod(keyfile, 0600)
+ fromldap += key.decode("utf-8").strip() + "\n"
+
+ fromfile = ""
+ try:
+ with open(keyfile, 'r') as f:
+ fromfile = f.read()
+ except FileNotFoundError:
+ pass
+
+ if fromldap == fromfile:
+ return False
+
+ if args.dry_run:
+ print(f"Would write {keyfile}")
+ return True
+
+ if args.verbose:
+ print(f"Writing {keyfile}")
+
+ if not os.path.isdir(userdir):
+ shutil.copytree('/etc/skel', userdir)
+ os.chown(userdir, uid, gid)
+ for root, dirs, files in os.walk(userdir):
+ for d in dirs:
+ os.chown(os.path.join(root, d), uid, gid)
+ for f in files:
+ os.chown(os.path.join(root, f), uid, gid)
+
+ try:
+ os.makedirs(f"{userdir}/.ssh", 0o700)
+ except FileExistsError:
+ pass
+ os.chmod(f"{userdir}/.ssh", 0o700)
+ os.chown(f"{userdir}/.ssh", uid, gid)
+
+ with tempfile.NamedTemporaryFile(
+ prefix='ldap-sshkey2file-', mode='w', delete=False) as tmpfile:
+ tmpfile.write(fromldap)
+ os.chmod(tmpfile.name, 0o600)
+ os.chown(tmpfile.name, uid, gid)
+ shutil.move(tmpfile.name, keyfile)
+ # Hmm, apparently shutil.move does not preserve user/group so let's reapply
+ # them. I still like doing it before as this should be more "atomic"
+ # if it actually worked, so it's "good practice", even if shutil.move sucks
os.chown(keyfile, uid, gid)
- os.chmod("%s/%s" % (keypathprefix,user), 0700)
- os.chown("%s/%s" % (keypathprefix,user), uid, gid)
+ os.chmod(keyfile, 0o600)
+ return True
-if len(sys.argv) != 1:
- usage()
- sys.exit(1)
-bindpw = get_pw(pwfile)
+bindpw = get_bindpw()
+changed = False
try:
ld = ldap.initialize(uri)
ld.set_option(ldap.OPT_NETWORK_TIMEOUT, timeout)
if uri.startswith("ldap:/"):
ld.start_tls_s()
ld.bind_s(binddn, bindpw)
- res = ld.search_s(peopledn, ldap.SCOPE_ONELEVEL, filter, ['uid','sshPublicKey','uidNumber','gidNumber'])
+ res = ld.search_s(peopledn, ldap.SCOPE_ONELEVEL, objfilter,
+ ['uid', 'sshPublicKey', 'uidNumber', 'gidNumber'])
try:
- os.makedirs(keypathprefix, 0701)
- except:
+ os.makedirs(keypathprefix, 0o701)
+ except FileExistsError:
pass
+
+ if args.verbose:
+ print("Found users:",
+ ", ".join(sorted([x[1]['uid'][0].decode('utf-8') for x in res])))
+
for result in res:
dn, entry = result
# skip possible system users
- if int(entry['uidNumber'][0]) < 500:
+ if 'uidNumber' not in entry or int(entry['uidNumber'][0]) < 500:
continue
- write_keys(entry['sshPublicKey'], entry['uid'][0], int(entry['uidNumber'][0]), int(entry['gidNumber'][0]))
+ if write_keys(entry['sshPublicKey'], entry['uid'][0],
+ int(entry['uidNumber'][0]), int(entry['gidNumber'][0])):
+ changed = True
+
ld.unbind_s()
-except Exception, e:
- print "Error"
+except Exception:
+ print("Error")
raise
-sys.exit(0)
+if changed:
+ if args.verbose:
+ print("SSH keys changed")
+ sys.exit(0)
+
+if args.verbose:
+ print("No changes in SSH keys")
+sys.exit(1)
# vim:ts=4:sw=4:et:ai:si
diff --git a/modules/openssh/templates/sshd_config b/modules/openssh/templates/sshd_config
index 27eee0f1..56ddd725 100644
--- a/modules/openssh/templates/sshd_config
+++ b/modules/openssh/templates/sshd_config
@@ -18,11 +18,10 @@
# The default requires explicit activation of protocol 1
#Protocol 2
-# HostKey for protocol version 1
-HostKey /etc/ssh/ssh_host_key
# HostKeys for protocol version 2
HostKey /etc/ssh/ssh_host_rsa_key
-HostKey /etc/ssh/ssh_host_dsa_key
+HostKey /etc/ssh/ssh_host_ecdsa_key
+HostKey /etc/ssh/ssh_host_ed25519_key
# Lifetime and size of ephemeral version 1 server key
#KeyRegenerationInterval 1h
@@ -57,11 +56,11 @@ PermitRootLogin without-password
#IgnoreRhosts yes
# To disable tunneled clear text passwords, change to no here!
-#PasswordAuthentication yes
+PasswordAuthentication no
#PermitEmptyPasswords no
# Change to no to disable s/key passwords
-#ChallengeResponseAuthentication yes
+ChallengeResponseAuthentication no
# Kerberos options
#KerberosAuthentication no
@@ -82,7 +81,7 @@ PermitRootLogin without-password
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and ChallengeResponseAuthentication to 'no'.
-UsePAM yes
+UsePAM no
# Accept locale-related environment variables
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
@@ -99,7 +98,6 @@ X11Forwarding yes
#PrintLastLog yes
#TCPKeepAlive yes
#UseLogin no
-UsePrivilegeSeparation yes
#PermitUserEnvironment no
#Compression delayed
#ClientAliveInterval 0
@@ -114,10 +112,15 @@ UsePrivilegeSeparation yes
#Banner none
# override default of no subsystems
-Subsystem sftp <%= path_to_sftp %>/sftp-server
+Subsystem sftp /usr/libexec/openssh/sftp-server
# Example of overriding settings on a per-user basis
#Match User anoncvs
# X11Forwarding no
# AllowTcpForwarding no
# ForceCommand cvs server
+<% if @hostname == 'duvel' then %>
+# git command is already forced to "gitolite <username>" in /var/lib/git/.ssh/authorized_keys
+Match User *,!schedbot,!root,!git Group *,!mga-sysadmin,!mga-unrestricted_shell_access
+ ForceCommand /usr/local/bin/sv_membersh.pl -c "$SSH_ORIGINAL_COMMAND"
+<% end %>
diff --git a/modules/openssh/templates/sshd_config_ldap b/modules/openssh/templates/sshd_config_ldap
deleted file mode 100644
index 31b29e21..00000000
--- a/modules/openssh/templates/sshd_config_ldap
+++ /dev/null
@@ -1,3 +0,0 @@
-
-AuthorizedKeysFile /var/lib/pubkeys/%u/authorized_keys
-