mirror of
https://git.lapiole.org/dani/ansible-roles.git
synced 2025-07-27 00:05:44 +02:00
Update to 2021-12-01 19:13
This commit is contained in:
67
roles/ssh/defaults/main.yml
Normal file
67
roles/ssh/defaults/main.yml
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
|
||||
# List of port sshd will bind to
|
||||
sshd_ports: [ '22' ]
|
||||
|
||||
# Will restrict ssh access to the following IP
|
||||
#
|
||||
sshd_src_ip: []
|
||||
# sshd_src_ip:
|
||||
# - 12.13.14.15
|
||||
# - 192.168.17.0/24
|
||||
|
||||
sshd_permit_root_login: no
|
||||
sshd_password_auth: yes
|
||||
|
||||
# Control the AllowUsers, DenyUsers, AllowGroups and DenyGroups
|
||||
# sshd_allow_users:
|
||||
# - fws
|
||||
# - dani
|
||||
# sshd_deny_users:
|
||||
# - dimitri
|
||||
# - flo
|
||||
# sshd_allow_groups:
|
||||
# - tech
|
||||
# - support
|
||||
# sshd_deny_groups:
|
||||
# - sales
|
||||
# - interim
|
||||
#
|
||||
#
|
||||
|
||||
# User configuration
|
||||
#ssh_users:
|
||||
# - name: dani
|
||||
# create_user: False
|
||||
# ssh_keys:
|
||||
# - 'ssh-rsa AAAAB3NzaC1yc2...'
|
||||
# - 'ssh-rsa AAAAB3NzaC1yc2...'
|
||||
# key_options:
|
||||
# - from="192.168.3.7"
|
||||
# - no-pty
|
||||
# sftp_only: True
|
||||
# chroot: /var/www/html
|
||||
# keys_file: %h/.ssh/authorized_keys
|
||||
# allow_forwarding: False
|
||||
# sudo_defaults:
|
||||
# - '!env_reset'
|
||||
# - '!requiretty'
|
||||
# sudo:
|
||||
# - cmd:
|
||||
# - /usr/local/bin/
|
||||
# run_as: root
|
||||
# nopasswd: False
|
||||
#
|
||||
#ssh_extra_users (can be used as ssh_users)
|
||||
#
|
||||
#
|
||||
# Max number of conn / minute. 0 to disable rate limit
|
||||
sshd_max_conn_per_minute: 0
|
||||
|
||||
# Authorized Keys custom command
|
||||
# sshd_authorized_keys_command: /usr/local/bin/ssh-getkeys
|
||||
# sshd_authorized_keys_command_user: ldapsshkey
|
||||
|
||||
# Use DNS. If disabled, kerb auth won't be used (as it uses DNS)
|
||||
# You might need to disable it when you need no SSH login delay even if DNS is unavailable
|
||||
sshd_use_dns: True
|
4
roles/ssh/handlers/main.yml
Normal file
4
roles/ssh/handlers/main.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
|
||||
- name: restart sshd
|
||||
service: name=sshd state=restarted enabled=yes
|
1
roles/ssh/meta/main.yml
Normal file
1
roles/ssh/meta/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
139
roles/ssh/tasks/main.yml
Normal file
139
roles/ssh/tasks/main.yml
Normal file
@@ -0,0 +1,139 @@
|
||||
---
|
||||
|
||||
- name: Install ssh components
|
||||
yum:
|
||||
name:
|
||||
- openssh-server
|
||||
- openssh-clients
|
||||
when: ansible_os_family == 'RedHat'
|
||||
tags: ssh
|
||||
|
||||
- name: Install ssh components
|
||||
apt:
|
||||
name:
|
||||
- openssh-server
|
||||
- openssh-client
|
||||
when: ansible_os_family == 'Debian'
|
||||
tags: ssh
|
||||
|
||||
- name: Allow ssh port in SELinux
|
||||
seport: ports={{ sshd_ports|join(',') }} proto=tcp setype=ssh_port_t state=present
|
||||
when: ansible_selinux.status == 'enabled'
|
||||
tags: ssh
|
||||
|
||||
- name: Combine SSH users
|
||||
set_fact:
|
||||
ssh_users: "{{ ssh_users + ssh_extra_users | default([]) }}"
|
||||
tags: ssh
|
||||
|
||||
- name: Deploy sshd configuration
|
||||
template: src=sshd_config.j2 dest=/etc/ssh/sshd_config backup=yes
|
||||
notify: restart sshd
|
||||
tags: ssh
|
||||
|
||||
- name: Set SSH rate limit
|
||||
iptables_raw:
|
||||
name: sshd_limit
|
||||
rules: |
|
||||
-A INPUT -p tcp -m state --state NEW -m multiport --dports {{ sshd_ports | join(',') }} -m recent --name ssh_limit --set
|
||||
-A INPUT -p tcp -m state --state NEW -m multiport --dports {{ sshd_ports | join(',') }} -m recent --name ssh_limit --rcheck --seconds 60 --hitcount {{ sshd_max_conn_per_minute }} -j LOG --log-prefix "Firewall (ssh limit): "
|
||||
-A INPUT -p tcp -m state --state NEW -m multiport --dports {{ sshd_ports | join(',') }} -m recent --name ssh_limit --rcheck --seconds 60 --hitcount {{ sshd_max_conn_per_minute }} -j REJECT
|
||||
state: "{{ (sshd_max_conn_per_minute > 0) | ternary('present','absent') }}"
|
||||
weight: 10
|
||||
when: iptables_manage | default(True)
|
||||
tags: ssh,firewall
|
||||
|
||||
- name: Handle ssh ports
|
||||
iptables_raw:
|
||||
name: sshd_ports
|
||||
state: "{{ (sshd_src_ip is defined and sshd_src_ip | length > 0) | ternary('present','absent') }}"
|
||||
rules: "-A INPUT -m state --state new -p tcp -m multiport --dports {{ sshd_ports | join(',') }} -s {{ sshd_src_ip | flatten | join(',') }} -j ACCEPT"
|
||||
when: iptables_manage | default(True)
|
||||
tags: ssh,firewall
|
||||
|
||||
- name: Create top level authorized keys directory
|
||||
file: path=/etc/ssh/authorized_keys/ state=directory mode=755 owner=root group=root
|
||||
tags: ssh
|
||||
|
||||
- name: Create an SSH key pair for root
|
||||
user:
|
||||
name: root
|
||||
generate_ssh_key: yes
|
||||
ssh_key_file: .ssh/id_rsa
|
||||
tags: ssh
|
||||
|
||||
# Do this in two times, to prevent hitting a bug in ansible
|
||||
# where usermod could be called before useradd
|
||||
# See https://github.com/ansible/ansible/issues/22576
|
||||
- name: Create ssh users
|
||||
user:
|
||||
name: "{{ item.name }}"
|
||||
with_items: "{{ ssh_users }}"
|
||||
register: ssh_create_user
|
||||
when: item.create_user | default(False)
|
||||
tags: ssh
|
||||
|
||||
- name: Check if sssd is installed
|
||||
stat: path=/usr/sbin/sss_cache
|
||||
register: ssh_sss_cache
|
||||
tags: ssh
|
||||
|
||||
# Flush sss cache so we can modify freshly created users
|
||||
- name: Reset sss cache
|
||||
command: sss_cache -E
|
||||
when: ssh_sss_cache.stat.exists and ssh_create_user.results | selectattr('changed','equalto',True) | list | length > 0
|
||||
tags: ssh
|
||||
|
||||
- name: Set ssh user attributes
|
||||
user:
|
||||
name: "{{ item.name }}"
|
||||
comment: "{{ item.full_name | default(omit) }}"
|
||||
shell: "{{ item.shell | default(omit) }}"
|
||||
with_items: "{{ ssh_users }}"
|
||||
when: item.create_user | default(False)
|
||||
tags: ssh
|
||||
|
||||
- name: Create private dir for Authorized keys
|
||||
file: path=/etc/ssh/authorized_keys/{{ item.name }} state=directory mode=700 owner={{ item.name }}
|
||||
ignore_errors: True # Needed eg, if LDAP isn't available on first run
|
||||
with_items: "{{ ssh_users }}"
|
||||
tags: ssh
|
||||
|
||||
- name: Deploy ssh user keys
|
||||
authorized_key:
|
||||
user: "{{ item.name }}"
|
||||
key: "{{ item.ssh_keys| default([]) | join(\"\n\") }}"
|
||||
key_options: "{{ item.key_options | default([]) | join(',') }}"
|
||||
path: "/etc/ssh/authorized_keys/{{ item.name }}/authorized_keys"
|
||||
manage_dir: False
|
||||
exclusive: True
|
||||
ignore_errors: True # Needed eg, if LDAP isn't available on first run
|
||||
#when: item.ssh_keys is defined
|
||||
with_items: "{{ ssh_users }}"
|
||||
tags: ssh
|
||||
|
||||
- name: Ensure permissions and ownership on authorized_keys files
|
||||
file:
|
||||
path: /etc/ssh/authorized_keys/{{ item.name }}/authorized_keys
|
||||
mode: 0600
|
||||
owner: "{{ item.name }}"
|
||||
when: item.ssh_keys is defined
|
||||
ignore_errors: True
|
||||
with_items: "{{ ssh_users }}"
|
||||
tags: ssh
|
||||
|
||||
- name: List all authorized keys directories
|
||||
shell: ls -1 /etc/ssh/authorized_keys | xargs -n1 basename
|
||||
register: existing_ssh_keys
|
||||
changed_when: False
|
||||
tags: ssh
|
||||
|
||||
- name: Remove unmanaged ssh keys
|
||||
file: path=/etc/ssh/authorized_keys/{{ item }} state=absent
|
||||
with_items: "{{ existing_ssh_keys.stdout_lines | default([]) }}"
|
||||
when: item not in ssh_users | map(attribute='name')
|
||||
tags: ssh
|
||||
|
||||
- name: Deploy sudo fragment
|
||||
template: src=sudo.j2 dest=/etc/sudoers.d/ssh_users mode=600
|
||||
tags: ssh
|
91
roles/ssh/templates/sshd_config.j2
Normal file
91
roles/ssh/templates/sshd_config.j2
Normal file
@@ -0,0 +1,91 @@
|
||||
AddressFamily inet
|
||||
Protocol 2
|
||||
SyslogFacility AUTHPRIV
|
||||
PermitRootLogin {{ (sshd_permit_root_login == True) | ternary('yes','no') }}
|
||||
PasswordAuthentication {{ (sshd_password_auth == True) | ternary('yes','no') }}
|
||||
|
||||
{% if ad_auth is defined and ad_auth and sshd_use_dns %}
|
||||
GSSAPIAuthentication yes
|
||||
GSSAPIKeyExchange yes
|
||||
GSSAPIStoreCredentialsOnRekey yes
|
||||
{% endif %}
|
||||
|
||||
UseDNS {{ sshd_use_dns | ternary('yes', 'no') }}
|
||||
|
||||
{% if sshd_authorized_keys_command is defined %}
|
||||
AuthorizedKeysCommand {{ sshd_authorized_keys_command }}
|
||||
{% if sshd_authorized_keys_command_user is defined %}
|
||||
AuthorizedKeysCommandUser {{ sshd_authorized_keys_command_user }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
HostKey /etc/ssh/ssh_host_rsa_key
|
||||
HostKey /etc/ssh/ssh_host_ed25519_key
|
||||
HostKey /etc/ssh/ssh_host_ecdsa_key
|
||||
|
||||
AuthorizedKeysFile /etc/ssh/authorized_keys/%u/authorized_keys
|
||||
|
||||
{% if sshd_deny_users is defined and sshd_deny_users | length > 0 %}
|
||||
DenyUsers {{ sshd_deny_users | join(' ') }}
|
||||
{% endif %}
|
||||
|
||||
{% if sshd_allow_users is defined and sshd_allow_users | length > 0 %}
|
||||
AllowUsers {{ sshd_allow_users | join(' ') }}
|
||||
{% endif %}
|
||||
|
||||
{% if sshd_deny_groups is defined and sshd_deny_groups | length > 0 %}
|
||||
DenyGroups {{ sshd_deny_groups | join(' ') }}
|
||||
{% endif %}
|
||||
|
||||
{% if sshd_allow_groups is defined and sshd_allow_groups | length > 0 %}
|
||||
AllowGroups {{ sshd_allow_groups | join(' ') }}
|
||||
{% endif %}
|
||||
|
||||
{% for port in sshd_ports %}
|
||||
Port {{ port }}
|
||||
{% endfor %}
|
||||
|
||||
ChallengeResponseAuthentication no
|
||||
UsePAM yes
|
||||
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
|
||||
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
|
||||
AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
|
||||
AcceptEnv XMODIFIERS
|
||||
AcceptEnv LC_PVE_*
|
||||
X11Forwarding no
|
||||
Subsystem sftp internal-sftp
|
||||
|
||||
# Local user are managed separately
|
||||
Match User root,ansible,lbkp,zimbra,zfs-recv
|
||||
AuthorizedKeysFile /etc/ssh/authorized_keys/%u/authorized_keys %h/.ssh/authorized_keys
|
||||
|
||||
{% for user in ssh_users | default([]) %}
|
||||
Match user {{ user.name }}
|
||||
{% if user.chroot is defined %}
|
||||
ChrootDirectory {{ user.chroot }}
|
||||
{% endif %}
|
||||
{% if user.sftp_only | default(False) %}
|
||||
ForceCommand internal-sftp{% if user.sftp_cd is defined %} -d {{ user.sftp_cd }}{% endif %}
|
||||
{% endif %}
|
||||
{% if user.allow_forwarding is defined %}
|
||||
AllowTCPForwarding {{ user.allow_forwarding | ternary('yes', 'no') }}
|
||||
X11Forwarding {{ user.allow_forwarding | ternary('yes', 'no') }}
|
||||
{% endif %}
|
||||
{% if user.keys_file is defined %}
|
||||
AuthorizedKeysFile {{ user.keys_file }}
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% for client in wh_clients | default([]) %}
|
||||
# Web hosting client {{ client.name }}
|
||||
# hosted app {{ client.apps | map(attribute='name') | list | join(', ') }}
|
||||
Match Group client_{{ client.name }}{{ (samba_realm is defined) | ternary('@' + samba_realm | upper,'') }}
|
||||
ChrootDirectory /opt/wh/{{ client.name }}
|
||||
ForceCommand internal-sftp
|
||||
AllowTCPForwarding no
|
||||
X11Forwarding no
|
||||
AuthorizedKeysFile /etc/ssh/wh/{{ client.name }}/authorized_keys
|
||||
|
||||
{% endfor %}
|
11
roles/ssh/templates/sudo.j2
Normal file
11
roles/ssh/templates/sudo.j2
Normal file
@@ -0,0 +1,11 @@
|
||||
{% for user in ssh_users | default([]) %}
|
||||
{% if user.sudo_defaults is defined and user.sudo_defaults | length > 0 %}
|
||||
Defaults:{{ user.name }} {{ user.sudo_defaults | join(',') }}
|
||||
{% endif %}
|
||||
{% if user.sudo is defined %}
|
||||
{% for command in user.sudo %}
|
||||
{{ user.name }} ALL=({{ command.run_as | default('root') }}) {% if command.nopasswd is defined and command.nopasswd %} NOPASSWD: {% endif %} {{ command.cmd | join(',') }}
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
Reference in New Issue
Block a user