Saltstack FreeBSD Jails — Part 2
After experimenting with Salt’s jails-aware functions for about 2 weeks, I opted for BastilleBSD for jails management and configuration.
I initially wanted to go for:
- Bastille for manual jail lifecycle management (create, update and upgrade mainly)
- Salt for jail configuration
But…
Salt service.running
is actually NOT jails-aware!
This came as a big disappointment and it took me a while to come to the
realization. I see service.running
as one of the main functions, with
file.managed
and pkg.installed
, for installing and configuring a service
with Salt.
The root cause is that, unlike salt/states/pkg.py
, salt/states/service.py
does not pass **kwargs
to os-specific implementations like
salt/modules/freebsdservice.py
. And thus functions in
salt/modules/freebsdservice.py
do not receive the jail
keyword argument 😡
Draft for a fix
--- a/salt/states/service.py 2021-03-24 22:54:42.000000000 +0100
+++ b/salt/states/service.py 2023-03-22 23:54:52.130686730 +0100
@@ -134,7 +134,7 @@
# is service available?
try:
- if not _available(name, ret):
+ if not _available(name, ret, **kwargs):
return ret
except CommandExecutionError as exc:
ret["result"] = False
@@ -245,7 +245,7 @@
# is service available?
try:
- if not _available(name, ret):
+ if not _available(name, ret, **kwargs):
ret["result"] = True
return ret
except CommandExecutionError as exc:
@@ -344,15 +344,15 @@
return ret
-def _available(name, ret):
+def _available(name, ret, **kwargs):
"""
Check if the service is available
"""
avail = False
if "service.available" in __salt__:
- avail = __salt__["service.available"](name)
+ avail = __salt__["service.available"](name, **kwargs)
elif "service.get_all" in __salt__:
- avail = name in __salt__["service.get_all"]()
+ avail = name in __salt__["service.get_all"](**kwargs)
if not avail:
ret["result"] = False
ret["comment"] = "The named service {} is not available".format(name)
@@ -440,7 +440,7 @@
# Check if the service is available
try:
- if not _available(name, ret):
+ if not _available(name, ret, **kwargs):
if __opts__.get("test"):
ret["result"] = None
ret["comment"] = (
@@ -453,6 +453,7 @@
ret["comment"] = exc.strerror
return ret
+ # XXX uh what has systemd to do with generic service handling?!
status_kwargs, warnings = _get_systemd_only(__salt__["service.status"], kwargs)
if warnings:
_add_warnings(ret, warnings)
@@ -635,7 +636,7 @@
# Check if the service is available
try:
- if not _available(name, ret):
+ if not _available(name, ret, **kwargs):
if __opts__.get("test"):
ret["result"] = None
ret["comment"] = (
--- a/salt/modules/freebsdservice.py 2021-03-24 22:54:42.000000000 +0100
+++ b/salt/modules/freebsdservice.py 2023-03-22 23:55:02.160776847 +0100
@@ -348,7 +348,7 @@
return not enabled(name, **kwargs)
-def available(name, jail=None):
+def available(name, jail=None, **kwargs):
"""
Check that the given service is available.
@@ -362,6 +362,9 @@
salt '*' service.available sshd
"""
+ log.error(f"__kwargs={kwargs}")
+ jail = jail if jail is not None else kwargs.get("jail", "")
+ log.error(f"__jail={jail}")
return name in get_all(jail)
@@ -384,7 +387,7 @@
return name not in get_all(jail)
-def get_all(jail=None):
+def get_all(jail=None, **kwargs):
"""
Return a list of all available services
@@ -398,8 +401,10 @@
salt '*' service.get_all
"""
+ jail = jail if jail is not None else kwargs.get("jail", "")
ret = []
service = _cmd(jail)
+ log.warning(f"__service={service}")
for srv in __salt__["cmd.run"]("{0} -l".format(service)).splitlines():
if not srv.isupper():
ret.append(srv)
@@ -478,7 +483,7 @@
return not __salt__["cmd.retcode"](cmd, python_shell=False)
-def status(name, sig=None, jail=None):
+def status(name, sig=None, jail=None, **kwargs):
"""
Return the status for a service.
If the name contains globbing, a dict mapping service name to True/False
@@ -503,6 +508,8 @@
salt '*' service.status <service name> [service signature]
"""
+ jail = jail if jail is not None else kwargs.get("jail", "")
+
if sig:
return bool(__salt__["status.pid"](sig))
Note I went as far as rewriting the definition of a couple services to make them jail-aware, that is making them applicable to host machines or jails.
Salt PR, a hazardous path?
The Salt project on Github currently displays 2.5k open issues, 600+ open PRs. This feels like a lot.
The Salt community on slack was reactive and welcoming, explaining that a PR will surely be accepted as long as I provide tests. I also learned there is a community call where I can directly talk to the core team ❤️.
That’s encouraging but there was another aspect that unsettled me a bit: the pertinence of my potential PR. Am I the only one interested in that feature? How do others use Salt for jails? How come I am seemingly the first user to spot the issue? It seems there are already very few FreeBSD sysadmins, much less managing Jails…
Why not rather use a trendy tool?
Nice first experience with Bastille
Actually creating Bastille
templates for
service packaging is pretty straight forward: Bastillefile
feels like
Dockerfile
. There are some less documented tricks, like the CONFIG
keyword,
which calls bastille config
under the hood.
Most service definitions will be covered with PKG
, SYSRC
,
SERVICE
. MOUNT
and OVERLAY
will help with actual jail definitions. So I
have a template for each: jail definition and service definition. The former
INCLUDE
ing the latter.
I store and deploy templates with config files in Salt. Secrets are injected into config files with Salt pillars.
Pros:
- Simple install, simple maintenance (less jinja wrangling, easy to iterate).
- Flexibility. Fast, feature-rich, user-friendly tool.
bastille template
is idempotent in my experience.
Cons:
- No periodic state checking (ala Salt/Puppet/Chef). More ansible-like.
- Tooling fragmentation. Specific tool for Jail management and
configuration. The divergence is slightly worsened by the fact that Bastille
uses a distinct
jail.conf
per jail1. - Services can only be deployed into jails.
Conclusion
Overall it feels like Bastille removes some complexity and seems to get some traction[Cit. needed]. So I’ll go and embrace the Bastille way. I’ll continue migrating the few remaining jails and package services still on the host.
-
For ex.
jail -c|-r
does not work. At least for now: FreeBSD just introduced new default configuration files/etc/jail.conf.d/*.conf
and/etc/jail.*.conf
. Bastille might take advantage of that in the future. ↩︎