我正在尝试编写一个干净编辑/etc/ssh/sshd_config 的剧本,以便它PasswordAuthentication no
和PermitRootLogin no
。
我能想到一些方法都是有问题的。
首先,我可以使用 lineinfile 删除所有与PasswordAuthentication|PermitRootLogin
匹配的行,然后附加我想要的两个新行,但 i) 这可能会以非原子方式失败,并且 ii) 在末尾附加行可以将它们与"匹配"块混合,通常可以出现在末尾。
我也可以使用 lineinfile,将每个行匹配^(# *)?PasswordAuthentication
替换为PasswordAuthentication no
,但如果匹配行尚不存在,则不起作用。另外,如果有多个匹配的行,我将有重复的PasswordAuthentication no
行。
我可以对整个文件使用模板,但这意味着我需要指定所有内容,包括 HostKey,但我不想指定所有内容,并希望将其他选项保留为最初设置的方式。
由于列出的问题,上述方法都不令人满意。是否有一种干净的方法可以可靠地进行所需的更改,是幂等的,并且在中途失败时不会使系统处于错误状态?
此解决方案只需要一个任务,不需要其他文件:
- name: Configure sshd
lineinfile:
path: "/etc/ssh/sshd_config"
regex: "^(#)?{{item.key}}"
line: "{{item.key}} {{item.value}}"
state: present
loop:
- { key: "PermitRootLogin", value: "no" }
- { key: "PasswordAuthentication", value: "no" }
notify:
- restart sshd
正如评论中指出的那样,此解决方案是有风险的,因为它需要正则表达式匹配。如果没有匹配项,则会在文件末尾生成一个新行,该新行可能与sshd_config
文件中的匹配部分冲突。
我可以替换匹配的每一行^(# *)?密码身份验证与密码身份验证 否,也使用 lineinfile,但如果匹配的行尚不存在,则不起作用。另外,如果有多个匹配的行,我将有重复的密码身份验证没有行。
您只是没有获得/测试lineinfile
如何有效工作,因为这正是您正在寻找的解决方案。在您的特定情况下,如果没有反向引用,模块将:
- 查看您要添加的行是否有效,在这种情况下,它将无所事事
- 如果该行不存在,请查找正则表达式以匹配它
- 如果找到一个或多个匹配项,则最后一个匹配项将被给定的行替换
- 如果未找到正则表达式,则将在文件末尾添加该行 如果
- 仍需要在文件中的特定位置添加该行(如果该行不存在),则可以使用
insertbefore
或insertafter
请参阅以下示例:
具有多个匹配行的初始test.config
:
# PasswordAuthentication no
# PermitRootLogin no
somevalue no
# PasswordAuthentication no
# PermitRootLogin no
othervalue yes
# PasswordAuthentication no
# PermitRootLogin no
和test2.config
没有匹配
value none
othervalue no
yetanother yes
测试手册:
---
- name: Line in file test
hosts: localhost
gather_facts: false
tasks:
- name: test replace
lineinfile:
path: "{{ item }}"
regex: ^(# *)?PasswordAuthentication
line: PasswordAuthentication no
loop:
- /path/to/test.config
- /path/to/test2.config
运行 playbook 会在首次运行时更改文件,并在后续运行时报告正常(不再进行任何更改)。以下是模块修改后的文件。
test.config
:
# PasswordAuthentication no
# PermitRootLogin no
somevalue no
# PasswordAuthentication no
# PermitRootLogin no
othervalue yes
PasswordAuthentication no
# PermitRootLogin no
和test2.config
value none
othervalue no
yetanother yes
PasswordAuthentication no
我认为您可以使用lineinfile
并使用state=absent
和state=present
:
- name: deactivate PermitRootLogin
lineinfile:
path: "/etc/ssh/sshd_config"
line: "PermitRootLogin no"
state: present
notify:
- restart sshd
- name: ensure PermitRootLogin is not activated
lineinfile:
path: "/etc/ssh/sshd_config"
line: "PermitRootLogin yes"
state: absent
notify:
- restart sshd
同样适用于PasswordAuthentication yes/no
.
如果模板不是一个选项,则应使用lineinfile。要解决详细信息,请执行以下操作:
.. 附加我想要的两个新行,但我)这可能会非原子失败......
如果无法完成,请修复它并重复播放。
。 如果有多个匹配的行,我将有重复的密码身份验证没有行。
验证配置,如果无法完成,请修复它并重复播放。
validate: "{{ sshd_path }} -t -f %s"
以上方法都不令人满意...
这种期望是不现实的。这两个问题(任意失败、匹配行)都描述了模块不必解决的错误状态。行文件完全符合目的。例如,请参阅 sshd.yml。
您的发行版的默认 sshd 配置/etc/ssh/sshd_config
可能有一个包含指令:
Include /etc/ssh/sshd_config.d
理想情况下,这是在配置的开头,因为sshd_config手册页建议第一个值(每个选项)获胜:for each keyword, the first obtained value will be used
。在撰写本文时,Ubuntu和Fedora似乎就是这种情况。
这使我们能够将所需的配置放入我们控制的文件中,例如:
- name: explicitly configure password authentication=no
become: yes
lineinfile:
path: "/etc/ssh/sshd_config.d/00-user.conf"
line: "PasswordAuthentication no"
state: present
create: true
notify: restart sshd
仅此一项就足以确保我们的配置始终获胜,因为文件按词法顺序处理(参见sshd_config(5))。
但是,为了更加确定我们可以从其他配置文件中删除任何矛盾的配置:
- name: find all sshd configs
become: yes
ansible.builtin.find:
paths: "/etc/ssh/sshd_config.d"
file_type: file
use_regex: true
recurse: yes
register: sshd_configs
- name: remove password authentication=yes if present
become: yes
lineinfile:
path: "{{ item.path }}"
# Ignore any commented lines, don't change more than we need to
# (?i) case insensitive match, ^(?!#) negative lookahead - line must not start with #
regex: "(?i)^(?!#).*PasswordAuthentication.*yes"
state: absent
loop: "{{ sshd_configs.files + [{'path': '/etc/ssh/sshd_config'}] }}"
notify: restart sshd
逐行编辑文件的一种替代且更优雅的选择是将/etc/ssh/sshd_config文件完全替换为新副本。
这是 RedHat Ansible 安全强化指南中建议的方法。它有一个显着的好处,即它保证了定义的行为,因为意外边缘情况的机会显着降低(就像在现有文件上替换正则表达式模式一样)。这也意味着您可以确保整个服务器群的一致性,并在需要时更轻松地整理/改进配置。
建议sshd_config文件:
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
SyslogFacility AUTHPRIV
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication no
ChallengeResponseAuthentication no
GSSAPIAuthentication yes
GSSAPICleanupCredentials no
UsePAM yes
X11Forwarding no
Banner /etc/issue.net
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
Subsystem sftp /usr/libexec/openssh/sftp-server
PermitRootLogin no
要复制sshd_config的 Ansible YAML:
- name: Add hardened SSH config
copy:
dest: /etc/ssh/sshd_config
src: etc/ssh/sshd_config
owner: root
group: root
mode: 0600
notify: Reload SSH
Ansible YAML 重新启动 SSH:
handlers:
- name: Reload SSH
service:
name: sshd
state: reloaded