# apparmor.d - Full set of apparmor profiles
# Copyright (C) 2002-2005 Novell/SUSE
# Copyright (C) 2012 Canonical Ltd.
# Copyright (C) 2015-2016 Simon Deziel
# Copyright (C) 2022-2024 Alexandre Pujol <alexandre@pujol.io>
# SPDX-License-Identifier: GPL-2.0-only

# Adapted from https://gitlab.com/apparmor/apparmor/-/blob/master/profiles/apparmor/profiles/extras/usr.sbin.sshd

# As SSH is used to administrate a server this is limited.
# If you want real protection disallow SSH access.


abi <abi/4.0>,

include <tunables/global>

@{exec_path} = @{sbin}/sshd
@{att} = /att/sshd/
profile sshd /{,usr/}{,s}bin/sshd  flags=(attach_disconnected,attach_disconnected.path=@{att},complain) {
  include <abstractions/attached/base>
  include <abstractions/authentication>
  include <abstractions/bus-system>
  include <abstractions/attached/consoles>
  include <abstractions/hosts_access>
  include <abstractions/nameservice-strict>
  include <abstractions/ssl_certs>
  include <abstractions/wutmp>


  capability audit_write,
  capability chown,
  capability dac_override,
  capability dac_read_search,
  capability fowner,
  capability fsetid,
  capability kill,
  capability net_bind_service,
  capability setgid,
  capability setuid,
  capability sys_chroot,
  capability sys_resource,

  # sshd doesn't require net_admin. libpam-systemd tries to
  # use it if available to set the send/receive buffers size,
  # but will fall back to a non-privileged version if it fails.
  deny capability net_admin,

  network inet  stream,
  network inet6 stream,
  network inet  dgram,
  network inet6 dgram,
  network netlink raw,

  unix type=stream peer=(label=sshd-session),

  signal receive set=hup peer=@{p_systemd},

  ptrace (read trace) peer=@{p_systemd},

  dbus send bus=system path=/org/freedesktop/login1
       interface=org.freedesktop.login1.Manager
       member={CreateSession,ReleaseSession,CreateSessionWithPIDFD}
       peer=(name=org.freedesktop.login1, label="@{p_systemd_logind}"),

  dbus send bus=system path=/org/freedesktop/home1
       interface=org.freedesktop.home1.Manager
       member=GetUserRecordByName
       peer=(name=org.freedesktop.home1, label="@{p_systemd_homed}"),

  @{exec_path} mrix,

  @{sbin}/sshd.hmac r,

  @{bin}/@{shells}                   ux,
  @{bin}/false                       ix,
  @{sbin}/nologin                    px,
  @{bin}/passwd                      px,
  @{lib}/{openssh,ssh}/sftp-server   px,
  @{lib}/{openssh,ssh}/sshd-auth     px,
  @{lib}/{openssh,ssh}/sshd-session  px,

  @{etc_ro}/environment r,
  @{etc_ro}/security/limits.d/{,*.conf} r,
  @{etc_rw}/motd r,
  @{etc_rw}/motd.d/{,**} r,
  /etc/default/locale r,
  /etc/gss/mech.d/{,*} r,
  /etc/issue.net r,
  /etc/legal r,
  /etc/machine-id r,
  /etc/motd r,
  /etc/shells r,

  @{etc_ro}/ssh/sshd_config r,
  @{etc_ro}/ssh/sshd_config.d/{,*} r,
  /etc/ssh/ssh_host_* r,

  /var/lib/lastlog/ r,
  /var/lib/lastlog/* rwk,
  /var/lib/wtmpdb/ r,
  /var/lib/wtmpdb/* rwk,

  # For scp
  owner @{user_download_dirs}/{,**} rwl,
  owner @{user_sync_dirs}/{,**} rwl,

  @{HOME}/@{XDG_SSH_DIR}/authorized_keys* r,
  owner @{user_cache_dirs}/{,motd*} rw,

  @{att}@{run}/systemd/sessions/@{int}.ref rw,

        @{run}/faillock/@{user} rwk,
        @{run}/motd.d/{,*} r,
        @{run}/motd.dynamic rw,
        @{run}/motd.dynamic.new rw,
        @{run}/systemd/notify w,
  owner @{run}/sshd{,.init}.pid wl,

  @{sys}/fs/cgroup/*/user/*/@{int}/ rw,
  @{sys}/fs/cgroup/systemd/user.slice/user-@{uid}.slice/session-@{word}.scope/ rw,

        @{PROC}/@{pids}/fd/ r,
        @{PROC}/1/environ r,
        @{PROC}/cmdline r,
  owner @{PROC}/@{pid}/limits r,
  owner @{PROC}/@{pid}/loginuid rw,
  owner @{PROC}/@{pid}/mounts r,
  owner @{PROC}/@{pid}/oom_adj rw,
  owner @{PROC}/@{pid}/oom_score_adj rw,
  owner @{PROC}/@{pid}/uid_map r,

  /dev/ptmx rw,
  /dev/tty@{int} rw,
  /dev/ttyS@{int} rw,

  include if exists <local/sshd>
}

# vim:syntax=apparmor
