Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

certbot-apache: use httpd by default for CentOS/RHEL #9402

Merged
merged 10 commits into from
Oct 26, 2022
Merged

Conversation

wgreenberg
Copy link
Collaborator

Fixes #9386

CentOS and RHEL 9 now use httpd instead of apachectl, so that's now the default for that configurator. There's now a special case for older CentOS/RHEL versions to use apachectl, as well as for older Fedora (<28).

@wgreenberg wgreenberg changed the title certbot-apache: use httpd by default for CentOS et al certbot-apache: use httpd by default for CentOS/RHEL Sep 9, 2022
@ohemorange
Copy link
Contributor

Code looks solid, have you already tested it on CentOS/RHEL 8/9 in practice?

@notroj
Copy link

notroj commented Sep 14, 2022

This seems like it will now leave self.options.restart_cmd_alt[0] set to "httpd" in some cases, which looks wrong, since httpd restart is not a valid way to invoke httpd. (Though I am not sure how certbot uses restart_cmd_alt so may be missing something)

On all versions of CentOS, RHEL and Fedora it is fine to restart httpd using apachectl restart.

The special case needed is if you want to do something other than control the daemon, e.g. httpd -t or httpd -v in some form, you should run httpd directly.

@wgreenberg
Copy link
Collaborator Author

Ah, thanks for the clarification @notroj, turns out I misunderstood the RHEL changelog! Updating the PR

A change in RHEL 9 is causing apachectl to error out when used
with additional arguments, resulting in certbot errors. The CentOS
configurator now uses httpd instead for RHEL 9 (and later) derived
distros.
]
new = util.parse_loose_version(os_version) >= util.parse_loose_version('9')
if rhel_derived and new:
self.options.ctl = 'httpd'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this sets the value of --apache-ctl (see certbot --help apache) with no way for the user to override it while continuing to list the default value as apachectl in the help output on RHEL 9+. Is that right?

If so, I think we may want to do something like what we do here and define a new override class with static defaults.

What do you think?

Copy link
Collaborator Author

@wgreenberg wgreenberg Oct 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true, though i was hoping to leave all the finicky distro-specific details out of get_configurator() and instead handle it within CentOSConfigurator.

one issue w/ relying on static defaults is that _prepare_options() always sets options.restart_cmd's binary to options.ctl (https://github.com/certbot/certbot/blob/master/certbot-apache/certbot_apache/_internal/configurator.py#L184-L187), so even if our CentOS 9 defaults have httpd for one and apachectl for the other, they'll both end up as httpd unless we intervene. what about something like:

diff --git a/certbot-apache/certbot_apache/_internal/configurator.py b/certbot-apache/certbot_apache/_internal/configurator.py
index 82b3dcaec..8d7d4eb3a 100644
--- a/certbot-apache/certbot_apache/_internal/configurator.py
+++ b/certbot-apache/certbot_apache/_internal/configurator.py
@@ -178,14 +178,14 @@ class ApacheConfigurator(common.Configurator):
             # Config options use dashes instead of underscores
             if self.conf(o.replace("_", "-")) is not None:
                 setattr(self.options, o, self.conf(o.replace("_", "-")))
+                # If the user overrides apachectl, update the various cmds as well
+                if o == "ctl":
+                    self.options.version_cmd[0] = self.options.ctl
+                    self.options.restart_cmd[0] = self.options.ctl
+                    self.options.conftest_cmd[0] = self.options.ctl
             else:
                 setattr(self.options, o, getattr(self.OS_DEFAULTS, o))
 
-        # Special cases
-        self.options.version_cmd[0] = self.options.ctl
-        self.options.restart_cmd[0] = self.options.ctl
-        self.options.conftest_cmd[0] = self.options.ctl

this way, we can just give CentOS 9 a different set of OS_DEFAULTS and leave its _prepare_options() alone

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Digging into this further, I noticed that we also have an --apache-bin option:

$ certbot --help apache
usage: 
  certbot [SUBCOMMAND] [options] [-d DOMAIN] [-d DOMAIN] ...
...
  --apache-ctl APACHE_CTL
                        Full path to Apache control script (default: apachectl)
  --apache-bin APACHE_BIN
                        Full path to apache2/httpd binary (default: None)

I feel like in the long term if not now, we should make use of this here instead of telling our code that the name of apachectl is httpd. There are different ways to structure this, but I personally think we should try to keep these two options true to their intended usage and update other commands based on their values. By that I mean I'm essentially imagining logic that at least effectively does something like:

if self.options.version_cmd[0] == self.OS_DEFAULTS.ctl:
    self.options.version_cmd[0] = self.options.ctl
else:
    self.options.version_cmd[0] = self.options.bin

What do you think?

Also, on RHEL 9+ and friends, are these values from higher up in this file correct? Do we want to be using apachectl at all?

        restart_cmd=['apachectl', 'graceful'],
        restart_cmd_alt=['apachectl', 'restart'],
        conftest_cmd=['apachectl', 'configtest'],

I notice we leave them unmodified.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, on RHEL 9+ and friends, are these values from higher up in this file correct? Do we want to be using apachectl at all?

Authoritatively: yes, you should use apachectl restart or graceful to restart the daemon on all versions of RHEL, and will be supported in all future versions. This is supported as a thin wrapper around systemctl $action httpd to manipulate the systemd service. apachectl configtest should also work for all versions since it's so widely used. The thing we don't support is passing arguments in.

Copy link
Collaborator Author

@wgreenberg wgreenberg Oct 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went w/ @bmw's suggestion and reworked this to use two different configurator classes for the different scenarios (i.e. older CentOSy OSes vs. new RHEL-derived OSes). this ended up adding some more complexity to get_configurator(), but in the end i think it makes more sense this way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wgreenberg, sorry for the conflicting suggestions between my first and second comments in the thread here. What do you think about what I said in that second post though at #9402 (comment) about using the apache_bin option instead of configuring our code to believe that the name to the Apache control script is httpd?

Copy link
Member

@bmw bmw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is getting close! Based on your answers to some of my inline questions, I think we may want to consider keeping a single CentOS override class and doing the command overriding conditionally by inspecting the OS again like you did in the initial version of this PR. I'm not tied to this specific design, although I do think it may allow us to simplify/undo some of the other changes here.

I think we should also add a changelog entry about this but other than that and my inline comments, this LGTM!

As of RHEL 9, apachectl can't be passed flags like "-t -D", so instead
use options.bin (i.e. httpd)
"""
if not self.options.bin:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we call super()._override_cmds() here so values like self.options.restart_cmd[0] get set based on the ctl value and then in addition set self.options.version_cmd[0] = self.options.bin below?

super()._prepare_options()
if not self.options.restart_cmd_alt: # pragma: no cover
raise ValueError("OS option restart_cmd_alt must be set for CentOS.")
self.options.restart_cmd_alt[0] = self.options.ctl
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to do this for RHEL 9+? If not, do you know what changed between new and old versions?

"amazon": override_centos.CentOSConfigurator,
"gentoo": override_gentoo.GentooConfigurator,
"gentoo base system": override_gentoo.GentooConfigurator,
"opensuse": override_suse.OpenSUSEConfigurator,
"suse": override_suse.OpenSUSEConfigurator,
"sles": override_suse.OpenSUSEConfigurator,
"scientific": override_centos.CentOSConfigurator,
"scientific linux": override_centos.CentOSConfigurator,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I worry about removing entries from this dict due to the code at

os_like = util.get_systemd_os_like()
if os_like:
for os_name in os_like:
override_class = OVERRIDE_CLASSES.get(os_name)

What do you think?

@wgreenberg
Copy link
Collaborator Author

@bmw i reworked this into a single class, ready for review again!

self.options.get_includes_cmd[0] = self.options.bin
self.options.get_defines_cmd[0] = self.options.bin
else:
super()._override_cmds()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we may want to unconditionally call this at the top of this function so values like self.options.restart_cmd[0] get set to a possibly custom self.options.ctl value. Otherwise, this PR LGTM!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack, yup!

@wgreenberg wgreenberg merged commit eed1afb into master Oct 26, 2022
@wgreenberg wgreenberg deleted the fix-9386 branch October 26, 2022 22:07
@alexzorin
Copy link
Collaborator

CHANGELOG entry needs to be moved from 1.25.0 to 1.32.0.

@wgreenberg
Copy link
Collaborator Author

d'oh, good catch @alexzorin. fixed here: #9444

@bmw
Copy link
Member

bmw commented Oct 27, 2022

Yeah thanks for catching this Alex. This recurring issue motivates me a bit to take another look at #8272.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

--apache doesn't work on RHEL9, need to use httpd rather than apachectl
5 participants