aboutsummaryrefslogtreecommitdiffstats
path: root/modules/openssh
diff options
context:
space:
mode:
Diffstat (limited to 'modules/openssh')
-rw-r--r--modules/openssh/manifests/init.pp26
-rw-r--r--modules/openssh/manifests/server.pp17
-rw-r--r--modules/openssh/manifests/ssh_keys_from_ldap.pp20
-rwxr-xr-xmodules/openssh/templates/ldap-sshkey2file.py194
-rw-r--r--modules/openssh/templates/sshd_config22
5 files changed, 245 insertions, 34 deletions
diff --git a/modules/openssh/manifests/init.pp b/modules/openssh/manifests/init.pp
index e55660fd..bae0fa5c 100644
--- a/modules/openssh/manifests/init.pp
+++ b/modules/openssh/manifests/init.pp
@@ -1,25 +1 @@
-class openssh {
-
- # some trick to manage sftp server, who is arch dependent on mdv
- $path_to_sftp = "$lib_dir/ssh/"
-
- package { "openssh-server":
- ensure => installed
- }
-
- service { sshd:
- ensure => running,
- path => "/etc/init.d/sshd",
- subscribe => [ Package["openssh-server"], File["sshd_config"] ]
- }
-
- file { "sshd_config":
- path => "/etc/ssh/sshd_config",
- ensure => present,
- owner => root,
- group => root,
- mode => 644,
- require => Package["openssh-server"],
- content => template("openssh/sshd_config")
- }
-}
+class openssh { }
diff --git a/modules/openssh/manifests/server.pp b/modules/openssh/manifests/server.pp
new file mode 100644
index 00000000..c45268d2
--- /dev/null
+++ b/modules/openssh/manifests/server.pp
@@ -0,0 +1,17 @@
+class openssh::server {
+ # some trick to manage sftp server, who is arch dependent on mdv
+ # TODO: the path changed on Mageia 6 to /usr/libexec/openssh/sftp-server
+ $path_to_sftp = "${::lib_dir}/ssh/"
+
+ package { 'openssh-server': }
+
+ service { 'sshd':
+ subscribe => Package['openssh-server'],
+ }
+
+ file { '/etc/ssh/sshd_config':
+ require => Package['openssh-server'],
+ content => template('openssh/sshd_config'),
+ notify => Service['sshd']
+ }
+}
diff --git a/modules/openssh/manifests/ssh_keys_from_ldap.pp b/modules/openssh/manifests/ssh_keys_from_ldap.pp
new file mode 100644
index 00000000..9ea6c139
--- /dev/null
+++ b/modules/openssh/manifests/ssh_keys_from_ldap.pp
@@ -0,0 +1,20 @@
+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':
+ content => template('openssh/ldap-sshkey2file.py'),
+ require => Package['python3-ldap']
+ }
+
+ cron { 'sshkey2file':
+ 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 => Mga_common::Local_script['ldap-sshkey2file.py'],
+ }
+}
diff --git a/modules/openssh/templates/ldap-sshkey2file.py b/modules/openssh/templates/ldap-sshkey2file.py
new file mode 100755
index 00000000..934e2865
--- /dev/null
+++ b/modules/openssh/templates/ldap-sshkey2file.py
@@ -0,0 +1,194 @@
+#!/usr/bin/python3
+
+import argparse
+import os
+import random
+import shutil
+import sys
+import tempfile
+import textwrap
+from typing import Iterable
+
+try:
+ import ldap
+except ImportError:
+ print("Please install python-ldap before running this program")
+ sys.exit(1)
+
+basedn = "<%= @dc_suffix %>"
+peopledn = f"ou=people,{basedn}"
+<%-
+ ldap_servers.map! { |l| "'ldaps://#{l}'" }
+-%>
+uris = [<%= ldap_servers.join(", ") %>]
+random.shuffle(uris)
+uri = " ".join(uris)
+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
+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:
+ return get_nslcd_bindpw(nslcd_conf_file)
+ except:
+ pass
+
+ try:
+ return get_ldap_secret(ldap_secret_file)
+ except:
+ pass
+
+ 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:
+ 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(keyfile, 0o600)
+ return True
+
+
+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, objfilter,
+ ['uid', 'sshPublicKey', 'uidNumber', 'gidNumber'])
+ try:
+ 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 'uidNumber' not in entry or int(entry['uidNumber'][0]) < 500:
+ continue
+ if write_keys(entry['sshPublicKey'], entry['uid'][0],
+ int(entry['uidNumber'][0]), int(entry['gidNumber'][0])):
+ changed = True
+
+ ld.unbind_s()
+except Exception:
+ print("Error")
+ raise
+
+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 cb40a961..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
@@ -45,6 +44,7 @@ PermitRootLogin without-password
#PubkeyAuthentication yes
#AuthorizedKeysFile .ssh/authorized_keys
+
# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
#RhostsRSAAuthentication no
# similar for protocol version 2
@@ -56,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
@@ -81,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 no
+UsePAM no
# Accept locale-related environment variables
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
@@ -89,7 +89,7 @@ AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL
#AllowAgentForwarding yes
-#AllowTcpForwarding yes
+AllowTcpForwarding no
#GatewayPorts no
X11Forwarding yes
#X11DisplayOffset 10
@@ -98,7 +98,6 @@ X11Forwarding yes
#PrintLastLog yes
#TCPKeepAlive yes
#UseLogin no
-UsePrivilegeSeparation yes
#PermitUserEnvironment no
#Compression delayed
#ClientAliveInterval 0
@@ -113,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 %>