Hardening SSH With Ansible

54 views
Skip to first unread message

Jon Adcock

unread,
Sep 4, 2019, 9:55:03 AM9/4/19
to Ansible Project
I think its common practice to "harden" SSH by running the following in one of your playbooks:
- name: Disallow root SSH access
lineinfile:
dest: /etc/ssh/sshd_config
regexp: "^PermitRootLogin no"
line: "PermitRootLogin no"
state: present
notify:
- restart sshd


The way I understand this, if "PermitRootLogin no" does not appear in the sshd_config file, it'll append that to the bottom of the file.

So in the scenario where Ansible finds "PermitRootLogin yes" in the file, it will append "PermitRootLogin no" at the end of the config file.

The problem is that when SSH reads "PermitRootLogin yes" earlier in the file THAT configuration setting it what it uses.  So the 'no' setting 
at the end of the file is ignored, and Ansible is not doing anything for me.   (http://man.openbsd.org/sshd_config.5)

Stefan Hornburg (Racke)

unread,
Sep 4, 2019, 10:10:24 AM9/4/19
to ansible...@googlegroups.com
Hello Jon,

you need to adjust the regexp so it covers both "yes" and "no" as configuration values. Or just drop "no" from the
end of the regexp.

Regards
Racke

>
> --
> You received this message because you are subscribed to the Google Groups "Ansible Project" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to
> ansible-proje...@googlegroups.com <mailto:ansible-proje...@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/ansible-project/642cd53b-95e2-4c03-b6a0-9fa807b97768%40googlegroups.com
> <https://groups.google.com/d/msgid/ansible-project/642cd53b-95e2-4c03-b6a0-9fa807b97768%40googlegroups.com?utm_medium=email&utm_source=footer>.


--
Ecommerce and Linux consulting + Perl and web application programming.
Debian and Sympa administration. Provisioning with Ansible.

signature.asc

Jonathan Lozada De La Matta

unread,
Sep 4, 2019, 10:11:52 AM9/4/19
to ansible...@googlegroups.com
I used jinja2 template to solve some of this issues.

To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/5ff043fb-1b91-12f2-a90d-9f4e4120f65d%40linuxia.de.


--

Jonathan Cha'gara Lozada De La Matta

He / Him / His

Red Hat

Senior Automation Practice Consultant & Automation CoP Manager

Join the Automation CoP! https://red.ht/autocop

 

Jon Adcock

unread,
Sep 4, 2019, 10:50:42 AM9/4/19
to Ansible Project
Racke,

  I'm not sure how that helps?  Ansible will still add the 'no' setting at the bottom of the sshd_config file and the 'yes' setting earlier in the sshd_config file is still active.

Thanks,
Jon

Kai Stian Olstad

unread,
Sep 4, 2019, 11:51:35 AM9/4/19
to ansible...@googlegroups.com
On 04.09.2019 16:50, Jon Adcock wrote:
> I'm not sure how that helps? Ansible will still add the 'no' setting at
> the bottom of the sshd_config file and the 'yes' setting earlier in the
> sshd_config file is still active.

It will replace the line, you test it and you'll see.


--
Kai Stian Olstad

Jon Adcock

unread,
Sep 4, 2019, 3:55:13 PM9/4/19
to Ansible Project
Kai, I tried removed the no from my regexp and retested.  Ansible did replace that line!  It did not add a new entry at the end of the config file.
Now I have to go figure out WHO changed the config file from no to yes.
Thank you.

Cyril Stoll

unread,
Sep 5, 2019, 4:07:37 AM9/5/19
to Ansible Project
Hi there

On Wednesday, 4 September 2019 15:55:03 UTC+2, Jon Adcock wrote:
I think its common practice to "harden" SSH by running the following in one of your playbooks:
- name: Disallow root SSH access
lineinfile:
dest: /etc/ssh/sshd_config
regexp: "^PermitRootLogin no"
line: "PermitRootLogin no"
state: present
notify:
- restart sshd
 

 To be more flexible with your regex you could improve it a bit. Like Racke wrote to cover yes or no at the end. Plus I like to cover # in front of the line as well plus empty spaces (the \s in the regex) at the beginning of the line and possibly after the #.  The regex then looks like this:

regexp: "^(#|#\s|\s|)PermitRootLogin(\s*)(no|yes)"

So everything in brackets is separated by pipes | and can be read as either # or # and space or just space or nothing (the last one if the line actually starts with the word Permit). Respectively at the end of the line the brackets part says either yes or no. The brackets in the middle with \s* look for one or many empty spaces before the yes or no.

Best,
Cyril

Cyril Stoll

unread,
Sep 5, 2019, 4:10:17 AM9/5/19
to Ansible Project
 Small correction:

regexp: "^(#|#\s|\s|)PermitRootLogin(\s*)(no|yes)"
 
The brackets around \s* are not needed. So this is correct:

regexp: "^(#|#\s|\s|)PermitRootLogin\s*(no|yes)"

Jon Adcock

unread,
Sep 5, 2019, 7:46:14 AM9/5/19
to Ansible Project
Cyril,when I tried your regex and Ansible barked about the \s   Both before and after PermitRootLogin  I tried double bask-slash s (#|#\\s|\\s|) and that works.


On Wednesday, September 4, 2019 at 9:55:03 AM UTC-4, Jon Adcock wrote:

Stefan Beke

unread,
Sep 5, 2019, 9:24:48 AM9/5/19
to Ansible Project
Actually \s* is not one or many, but zero or many. There should be at least one space so `\s+` 

S C Rigler

unread,
Sep 5, 2019, 9:39:50 AM9/5/19
to ansible...@googlegroups.com
All of these regex's seem a little too specific.  Why not just:

regexp: '^PermitRootLogin\b'
line: 'PermitRootLogin no'

If the entry isn't there it will add it (who cares if a commented entry already exists).  If it already exists and has a different value it will replace the line with the new value.

--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/4a803c9e-7cc5-4b0e-873f-c50814b68c09%40googlegroups.com.

cyril...@uzh.ch

unread,
Sep 5, 2019, 9:47:25 AM9/5/19
to ansible...@googlegroups.com

@Jon glad you found a solution that works for you. Strangely I don't have issues with just one backslash. Might be due to the old python version that is used on macOS.

@Stefan thanks for the correction!

@SCRigler basically you are right. But if someone sees the line that is commented out she/he might think the setting fell back to the default setting and close the config file again without noticing that way down in the config file there is actually a working setting that does overwrite the default. Might make troubleshooting a bit more frustrating that way.
 

Inactive hide details for "S C Rigler" ---05.09.2019 15:39:57---All of these regex's seem a little too specific.  Why not just:"S C Rigler" ---05.09.2019 15:39:57---All of these regex's seem a little too specific.  Why not just: regexp: '^PermitRootLogin\b'

--
You received this message because you are subscribed to a topic in the Google Groups "Ansible Project" group.
To unsubscribe from this topic, visit
https://groups.google.com/d/topic/ansible-project/5MsNYWEQrYA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to
ansible-proje...@googlegroups.com.
To view this discussion on the web visit
https://groups.google.com/d/msgid/ansible-project/CAFbiokcM%2BatiH0bR8gvH5NbLTz%2Bpr%3DSWt0YBK0fVpxXn%3DUU%3DDQ%40mail.gmail.com.

Vladimir Botka

unread,
Sep 5, 2019, 9:55:25 AM9/5/19
to ansible...@googlegroups.com
Hi !

> > regexp: "^(#|#\s|\s|)PermitRootLogin\s*(no|yes)"
> Actually \s* is not *one or many*, but *zero or many*. There should be at
> least one space so `\s+`

1) Searching for both commented and non-commented lines is risky. There
might be both present in the file and the result will be unpredictable.
It's better to ignore commented lines.

regexp: "^(\s*)PermitRootLogin\s+(no|yes)"

2) Searching for (no|yes) does not make any sense. We're going to replace it
anyway. This may make things even worse by preventing a potential syntax
error to be corrected. Replace it with general pattern.

regexp: "^(\s*)PermitRootLogin\s+(.*)$"

3) Always validate the configuration

validate: "{{ sshd_path }} -t -f %s"

my two cents, Cheers,

-vlado

Jon Adcock

unread,
Sep 5, 2019, 11:28:36 AM9/5/19
to Ansible Project
Vlado, It's my understanding that the validate step should be done once in the playbook.  Not for each sshd action in the playbook.  Is that what you are saying?


On Wednesday, September 4, 2019 at 9:55:03 AM UTC-4, Jon Adcock wrote:

Kai Stian Olstad

unread,
Sep 5, 2019, 12:27:55 PM9/5/19
to ansible...@googlegroups.com
On 05.09.2019 13:46, Jon Adcock wrote:
> Cyril,when I tried your regex and Ansible barked about the \s Both before
> and after PermitRootLogin I tried double bask-slash s (#|#\\s|\\s|) and
> that works.

That is because you are using double quotes and not single quotes around the regexp.
You could also remove the quotes since you don't actually need them.


--
Kai Stian Olstad

Kai Stian Olstad

unread,
Sep 5, 2019, 12:35:54 PM9/5/19
to ansible...@googlegroups.com
On 05.09.2019 17:28, Jon Adcock wrote:
> Vlado, It's my understanding that the validate step should be done once in
> the playbook. Not for each sshd action in the playbook. Is that what you
> are saying?

If you are going to do hardening and have some kind reliability that it work you should not be using the lineinfile module.
Instead go for the copy or template module that Jonathan mention previously in this thread.

The reason for that is that lineinfile _*only*_ changes the last line it find in the file and openssh only uses the first line it find.

So you Ansible code can easily be tricked by these lines.

PermitRootLogin yes
PermitRootLogin no

or

PermitRootLogin yes
#PermitRootLogin yes


And many other combination.


--
Kai Stian Olstad

Vladimir Botka

unread,
Sep 5, 2019, 1:56:11 PM9/5/19
to Jon Adcock, ansible...@googlegroups.com
On Thu, 5 Sep 2019 08:28:36 -0700 (PDT)
Jon Adcock <jon.a...@gmail.com> wrote:

> Vlado, It's my understanding that the validate step should be done once in
> the playbook. Not for each sshd action in the playbook. Is that what you
> are saying?

Jon, how this might be accomplished? "validate" by design says:
https://docs.ansible.com/ansible/latest/modules/lineinfile_module.html

"The validation command to run before copying into place..."

It works for me fine that way. The burden of the repeated validation is
present in the first run only (when idempotent). It's worth to be sure sshd
will keep running.

Cheers,

-vlado

Mike Eggleston

unread,
Sep 5, 2019, 2:28:02 PM9/5/19
to ansible...@googlegroups.com
And the presented regents is case specific. Try “(?i)...”.

Mike
> --
> You received this message because you are subscribed to the Google Groups "Ansible Project" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/20190905155509.186e2ce1%40gmail.com.
Reply all
Reply to author
Forward
0 new messages