-
Notifications
You must be signed in to change notification settings - Fork 140
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
fb_networkmanager: new cookbook to manage NetworkManager #127
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with having this in the repo, but I'd like @michel-slm to review it as well.
Also adding @jgoguen who writes the original CPE network manager cookbook. |
re: testing on Fedora - that can probably wait until after this review is done, and we then try switching to it and send further PRs to fix any issue we find. |
content.each_pair do |sect, opts| | ||
doc.section(sect, :option_sep => '=') do |section| | ||
opts.each_pair do |opt, val| | ||
v = [val].flatten.join(',') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NetworkManager connection files (but not NetworkManager.conf
, because consistency!) use semicolons for multi-value separators, and requires a trailing semicolon for multi-value fields. Using a semicolon in a value makes IniParse
wrap the value in quotes, which breaks NM parsing the file.
When I wrote a NetworkManager cookbook, I ended up having to manually generate the not-quite-INI text myself. You can imagine how impressed I was (still am) about having to do that when NM could have just used the proper INI format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you give me an example to test? I had eap=peap;
and dropped it to eap=peap
and it worked fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jgoguen I don't think the trailing semicolon is necessary, and here's some data:
[phil@ldx-vulturus ~]$ sudo grep ^eap /etc/NetworkManager/system-connections/fb_networkmanager_vicarious_wifi_hq
eap=peap
[phil@ldx-vulturus ~]$ nmcli c show 6f1f4851-15a3-321f-beec-fbfc873c9f85 | grep 802-1x.eap
802-1x.eap: peap
vs
root@ldx-vulturus:~# sudo grep ^eap /etc/NetworkManager/system-connections/fb_networkmanager_vicarious_wifi_hq
eap=peap;
root@ldx-vulturus:~# nmcli c reload
root@ldx-vulturus:~# nmcli c show 6f1f4851-15a3-321f-beec-fbfc873c9f85 | grep 802-1x.eap
802-1x.eap: peap
They're identical.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, what version of NetworkManager? I have 1.20 and even for connections manually created fresh it's still forcing semicolon-separate lists with a trailing semicolon.
I have 1.22 on another laptop, I can check what it does later tonight.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1.10.6
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder then if this INI-incompatible change is the new format. I created new connections on both 1.20 and 1.22, both forced trailing semicolons for multi-value fiends. It would work if I remove the trailing semicolon, if I restart NetworkManager itself not just reload connections, but as soon as anything modified the connection file it put the trailing semicolons back.
Where it uses semicolons as list item separators, I wonder if IniParse
would see the first value and then discard the rest treating it as a comment…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
certainly it does like to add them back. I just didn't find any problem to not having them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I'm going to assume there was something borked on my system. So as long as this can output semicolon-delimited lists for the connection files and comma-delimited lists for NetworkManager.conf
it's good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't today. I'll add that and tests. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh huh, I didn't get to this... I'll come back to this
|
||
# we don't test to make sure IniParse does what IniParse does, | ||
# instead we teste the things we're doing custom... | ||
context '#to_ini' do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a test for array fields. If you call FB::NetworkManager.to_ini
on this hash:
{
'connection' => {
'permissions' => %w{user:jgoguen:},
},
'802-1x' => {
'identity' => 'jgoguen',
'eap' => %w{tls},
},
}
I expect this to be the generated config file:
[connection]
permissions=user:jgoguen:;
[802-1x]
identity=jgoguen
eap=tls;
permissions
and eap
are two fields that NetworkManager requires be multi-value, including the trailing semicolon after the last value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
define doesn't work? it doesn't complain for sure, and it connects...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In NM 1.20 and 1.22, what I found in my testing is that if I remove trailing semicolons I consistently have to restart NM for it to work with the changed connection file, but adding a trailing semicolon (which NM will do itself when it tries to update a multi-value field) works just by reloading the connections. So if we write out a connection file that removes a trailing semicolon, based on my experience we'll need to restart the entire NetworkManager daemon instead of just reloading the configs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will experiment with this.
let(:from_contents) do | ||
<<~EOS | ||
[oldsection] | ||
key=value | ||
|
||
[section1] | ||
existing=stuff | ||
optionA=previous_value | ||
|
||
[section2] | ||
blue=red | ||
EOS | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add another test case where there's multi-value fields in the contents. This content:
[oldsection]
key=value
foo=bar;baz;
[section1]
existing=stuff
optionA=previous_value
optionB=red;orange;yellow;
must generate this hash:
{
'oldsection' => {
'key' => 'value',
'foo' => [
'bar',
'baz',
],
},
'section1' => {
'existing' => 'stuff',
'optionA' => 'previous_value',
'optionB' => [
'red',
'orange',
'yellow',
],
},
}
vpnhome = '/var/lib/openvpn/chroot' | ||
FB::Users.initialize_group(node, 'nm-openvpn') | ||
node.default['fb_users']['users']['nm-openvpn'] = { | ||
'gid' => 'nm-openvpn', | ||
# If /var/lib/openvpn doesn't exist yet, we can't create it in time | ||
# but we want to create the user, so for the first run, create it | ||
# with /tmp | ||
'home' => ::File.directory?(vpnhome) ? vpnhome : '/tmp', | ||
'shell' => '/usr/sbin/nologin', | ||
'action' => :add, | ||
} | ||
|
||
packages = %w{ | ||
network-manager | ||
openvpn | ||
network-manager-openvpn-gnome | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it assured we definitely want OpenVPN always configured? I'd prefer making VPN optional and allow choosing either/both of at least OpenVPN and openconnect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't force anyone to use openvpn. It just happens to setup openvpn only because I didn't have a test-case for openconnect. It's not mutually-exclusive afaik?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, good catch @jgoguen. Yes, we should gate this behind an attribute and make it optional. VPN setup is totally fine here, but we shouldn't do it by default, and we should write in a way that can support multiple backends (even if you only implement openvpn for now).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm just installing the right packages, you can make NM use whatever backened you want in the connection. We can install all the backends. This doesn't configure any VPNs... it just makes it available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But if I'm never going to use OpenVPN, I need openconnect
, there's no point to having it installed. If we gate it behind an attribute we can allow administrators to better control what gets installed.
As soon as we're going to have multiple VPN backends installed by Chef (plus any sanity work, like making sure the user is created) it may be better if we instead have a series of includes:
include_recipe 'fb_networkmanager::openvpn
include_recipe 'fb_networkmanager::openconnect
Each one can have an early return based on something like node['fb_networkmanager']['enable'] && node['fb_networkmanager'][vpntype]['enable']
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, yes you're right. It would need to be a set of resources (or a resource called multiple times with different actions, which just no).
@davide125 any strong opinion? I'd still like to have some gate for these but not strongly enough to demand that much extra work and testing for little gain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have a gate, manage_packages
. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not convinced we should be installing openvpn
(and openconnect
or vpnc
or whatever for that matter) itself here; a bunch of these will happily drop off system services, and can have interesting side effects if not setup properly. I think the ideal state would be that we have fb_openvpn
/ fb_openconnect
/ etc to manage them properly, and that fb_networkmanager
just uses their API and takes care of the n-m specific bits (in this case, the user and the plugin package). I do realize this is a fuckton of work though, and it doesn't make sense to block this PR on it.
How about this: we move the openvpn stuff to a fb_networkmanager::openvpn
, and we just don't include it in the default recipe. In the README, we document that if you want openvpn you should include. This is akin to how we handle some of the more corner cases in fb_systemd
iirc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
except that doesn't make sense for fb_networkmanater to use an fb_openvpn API, because it's not configuring it statically. NetworkManager internally uses those things, but it needs the packages installed.
We can certainly make extra recipes for them and that's fine, but it's literally Just The Packages, to be clear.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still contend we should split it out. If we had a fb_openvpn
, the way I see it fb_networkmanager::openvpn
would just include fb_openvpn::packages
and then install its thing. For now, I think it'd be fine to install both openvpn
and the other package directly in a fb_networkmanager::openvpn
. N-M can support like 5 or 6 different type of VPNs, some with fairly complex and invasive dependencies. We shouldn't force the user to have them all installed by default.
This cookbook aims to manage NetworkManager in the most controlled way possible. It might-could-should be sent over to CPE cookbooks, I can redirect over there if preferred. While, much to my dismay, some people do run NM on servers, and this will work for that, it was built to handle this client machine case. NetworkManager treats its config files as databases and can change them, so they can't be handled simply as templates - more finesse is needed. However, write-once doesn't provide the level of management that FB-API-style cookbooks expect. This marries the two. All desired settings are managed 100%, and NetworkManager is allowed to make additive settings (such as "seen-bssids" or "password" [sic]) so that it can continue to function for users. It is immune to the format changes that NM can make - spacing and ordering changes will not cause the file to be updated any more than ignorable settings. When updating the file, care is taken to stick close to NM's style. I considered making blacklist/whitelists of fields that NM could modify, but in practice since we merge in the user's config, the user's config is effectively a blacklist, and whitelist would likely be far too fragile. It has full docs, including on it's Migration Feature which will be useful for anyone who had their own Chef to do this but wants to migrate. It also comes with pretty good test coverage. Signed-off-by: Phil Dibowitz <[email protected]>
d5c158e
to
c332057
Compare
Overall logic lgtm, but I'd like @jgoguen to do another pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall LGTM. I'd like to see the VPN parts split out, but setting manage_packages
to false
is reasonable enough in the meantime.
|
||
The rendered config here will set `connection.autoconnect-priority` to 100 | ||
if there is no value for it found in the existing file, but will use the value | ||
in the file if it exists. The logic here is quote simple: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the file if it exists. The logic here is quote simple: | |
in the file if it exists. The logic here is quite simple: |
if node['fb_users'] | ||
vpnhome = '/var/lib/openvpn/chroot' | ||
FB::Users.initialize_group(node, 'nm-openvpn') | ||
node.default['fb_users']['users']['nm-openvpn'] = { | ||
'gid' => 'nm-openvpn', | ||
# If /var/lib/openvpn doesn't exist yet, we can't create it in time | ||
# but we want to create the user, so for the first run, create it | ||
# with /tmp | ||
'home' => ::File.directory?(vpnhome) ? vpnhome : '/tmp', | ||
'shell' => '/usr/sbin/nologin', | ||
'action' => :add, | ||
} | ||
end | ||
|
||
packages = %w{ | ||
network-manager | ||
openvpn | ||
network-manager-openvpn-gnome | ||
} | ||
|
||
package packages do | ||
only_if do | ||
node['fb_networkmanager']['enable'] && | ||
node['fb_networkmanager']['manage_packages'] | ||
end | ||
action :upgrade | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd still like to see the VPN parts split out into a separate cookbook, but for now setting node.default['fb_networkmanager']['manage_packages']
to false
is a reasonable enough workaround.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It definitely doesn't make sense in another cookbook. It's all network-manager configs. you don't configure vpn, you configure network-manager and it may or may not use backends.
I can make enable_openvpn_packages
if we want, or make it a separate recipe... but I think a config value seems sane. Then I can something like
package "network manager packages" do
only_if do
node['fb_networkmanager']['enable'] &&
node['fb_networkmanager']['manage_packages']
end
package_names lazy {
p = %w{network-manager}
if node['fb_networkmanager']['enable_openvpn_packages']
p += %w{openvpn network-manager-openvpn-gnome}
end
p
}
action :upgrade
end
I don't love it, but if people feel strongly.
I missed the "lists are different in the two files" and the trailing semi-colon thing, will do those soon. |
This cookbook aims to manage NetworkManager in the most controlled
way possible. It might-could-should be sent over to CPE cookbooks,
I can redirect over there if preferred.
While, much to my dismay, some people do run NM on servers, and this
will work for that, it was built to handle this client machine case.
NetworkManager treats its config files as databases and can change them,
so they can't be handled simply as templates - more finesse is needed.
However, write-once doesn't provide the level of management that
FB-API-style cookbooks expect. This marries the two. All desired
settings are managed 100%, and NetworkManager is allowed to make
additive settings (such as "seen-bssids" or "password" [sic]) so that it
can continue to function for users.
It is immune to the format changes that NM can make - spacing and
ordering changes will not cause the file to be updated any more than
ignorable settings. When updating the file, care is taken to stick
close to NM's style.
I considered making blacklist/whitelists of fields that NM could
modify, but in practice since we merge in the user's config, the user's
config is effectively a blacklist, and whitelist would likely be far
too fragile.
It has full docs, including on it's Migration Feature which will be
useful for anyone who had their own Chef to do this but wants to
migrate.
It also comes with pretty good test coverage.