Skip to content

Commit

Permalink
Merge pull request #2066 from zmrow/netdog-generate
Browse files Browse the repository at this point in the history
Enable network config generation via `netdog`
  • Loading branch information
zmrow authored May 17, 2022
2 parents e49fb60 + f68eec8 commit d340619
Show file tree
Hide file tree
Showing 45 changed files with 1,281 additions and 58 deletions.
15 changes: 15 additions & 0 deletions packages/os/generate-network-config.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[Unit]
Description=Generate network configuration
# Block manual interactions with this service, since it could leave the system in an
# unexpected state
RefuseManualStart=true
RefuseManualStop=true

[Service]
Type=oneshot
ExecStart=/usr/bin/netdog generate-net-config
RemainAfterExit=true
StandardError=journal+console

[Install]
RequiredBy=network-pre.target
4 changes: 3 additions & 1 deletion packages/os/os.spec
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Source114: [email protected]
Source115: link-kernel-modules.service
Source116: load-kernel-modules.service
Source117: cfsignal.service
Source118: generate-network-config.service

# 2xx sources: tmpfilesd configs
Source200: migration-tmpfiles.conf
Expand Down Expand Up @@ -445,7 +446,7 @@ install -d %{buildroot}%{_cross_unitdir}
install -p -m 0644 \
%{S:100} %{S:101} %{S:102} %{S:103} %{S:105} \
%{S:106} %{S:107} %{S:110} %{S:111} %{S:112} \
%{S:113} %{S:114} \
%{S:113} %{S:114} %{S:118} \
%if %{_is_vendor_variant}
%{S:115} %{S:116} \
%endif
Expand Down Expand Up @@ -488,6 +489,7 @@ install -p -m 0644 %{S:300} %{buildroot}%{_cross_udevrulesdir}/80-ephemeral-stor
%files -n %{_cross_os}netdog
%{_cross_bindir}/netdog
%{_cross_tmpfilesdir}/netdog.conf
%{_cross_unitdir}/generate-network-config.service

%files -n %{_cross_os}corndog
%{_cross_bindir}/corndog
Expand Down
28 changes: 0 additions & 28 deletions packages/release/eth0.xml

This file was deleted.

1 change: 0 additions & 1 deletion packages/release/release-tmpfiles.conf
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
C /etc/nsswitch.conf - - - -
C /etc/wicked/ifconfig/eth0.xml - - - -
d /var/log/kdump 0700 root root -
d /sys/fs/cgroup/cpuset/runtime.slice 0755 root root -
d /sys/fs/cgroup/hugetlb/runtime.slice 0755 root root -
Expand Down
5 changes: 0 additions & 5 deletions packages/release/release.spec
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ Source201: proxy-env
Source202: hostname-env
Source203: hosts.template

Source1000: eth0.xml
Source1001: multi-user.target
Source1002: configured.target
Source1003: preconfigured.target
Expand Down Expand Up @@ -106,9 +105,6 @@ Requires: %{_cross_os}wicked
install -d %{buildroot}%{_cross_factorydir}%{_cross_sysconfdir}
install -p -m 0644 %{S:11} %{buildroot}%{_cross_factorydir}%{_cross_sysconfdir}

install -d %{buildroot}%{_cross_factorydir}%{_cross_sysconfdir}/wicked/ifconfig
install -p -m 0644 %{S:1000} %{buildroot}%{_cross_factorydir}%{_cross_sysconfdir}/wicked/ifconfig

install -d %{buildroot}%{_cross_libdir}/repart.d
install -p -m 0644 %{S:96} %{buildroot}%{_cross_libdir}/repart.d/80-local.conf

Expand Down Expand Up @@ -172,7 +168,6 @@ ln -s preconfigured.target %{buildroot}%{_cross_unitdir}/default.target

%files
%{_cross_factorydir}%{_cross_sysconfdir}/nsswitch.conf
%{_cross_factorydir}%{_cross_sysconfdir}/wicked/ifconfig/eth0.xml
%{_cross_sysctldir}/80-release.conf
%{_cross_tmpfilesdir}/release.conf
%{_cross_libdir}/os-release
Expand Down
1 change: 1 addition & 0 deletions packages/wicked/wicked-tmpfiles.conf
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ C /etc/wicked/client.xml - - - -
C /etc/wicked/common.xml - - - -
C /etc/wicked/nanny.xml - - - -
C /etc/wicked/server.xml - - - -
d /etc/wicked/ifconfig 0700 root root -

d /var/lib/wicked 0700 root root -
Z /var/lib/wicked 0700 root root -
5 changes: 5 additions & 0 deletions sources/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions sources/api/netdog/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@ exclude = ["README.md"]
argh = "0.1.4"
dns-lookup = "1.0"
ipnet = { version = "2.0", features = ["serde"] }
indexmap = { version = "1.8", features = ["serde"]}
envy = "0.4"
lazy_static = "1.2"
rand = { version = "0.8", default-features = false, features = ["std", "std_rng"] }
regex = "1.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
serde_plain = "1.0"
serde-xml-rs = "0.5"
snafu = "0.7"
toml = { version = "0.5", features = ["preserve_order"] }

[build-dependencies]
cargo-readme = "3.1"
10 changes: 10 additions & 0 deletions sources/api/netdog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ It contains two subcommands meant for use as settings generators:

The subcommand `set-hostname` sets the hostname for the system.

The subcommand `generate-net-config` generates the network interface configuration for the host. If
a `net.toml` file exists in `/var/lib/bottlerocket`, it is used to generate the configuration. If
`net.toml` doesn't exist, the kernel command line `/proc/cmdline` is checked for the prefix
`netdog.default-interface`. If an interface is defined with that prefix, it is used to generate an
interface configuration. A single default interface may be defined on the kernel command line with
the format: `netdog.default-interface=interface-name:option1,option2`. "interface-name" is the
name of the interface, and valid options are "dhcp4" and "dhcp6". A "?" may be added to the option
to signify that the lease for the protocol is optional and the system shouldn't wait for it. A
valid example: `netdog.default-interface=eno1:dhcp4,dhcp6?`.

## Colophon

This text was generated using [cargo-readme](https://crates.io/crates/cargo-readme), and includes the rustdoc from `src/main.rs`.
148 changes: 148 additions & 0 deletions sources/api/netdog/src/interface_name.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
//! The interface_name module contains the definition of a valid network interface name and the
//! code to support creation of the structure from string.
//!
//! A valid network interface name is defined by the criteria in the linux kernel:
//! https://elixir.bootlin.com/linux/v5.10.102/source/net/core/dev.c#L1138
use serde::{Deserialize, Serialize, Serializer};
use snafu::ensure;
use std::convert::TryFrom;
use std::ops::Deref;

/// InterfaceName can only be created from a string that contains a valid network interface name.
/// Validation is handled in the `TryFrom` implementation below.
#[derive(Debug, Eq, PartialEq, Hash, Deserialize)]
#[serde(try_from = "&str")]
pub(crate) struct InterfaceName {
inner: String,
}

impl TryFrom<&str> for InterfaceName {
type Error = error::Error;

fn try_from(input: &str) -> Result<Self> {
// Rust does not treat all Unicode line terminators as starting a new line, so we check for
// specific characters here, rather than just counting from lines().
// https://en.wikipedia.org/wiki/Newline#Unicode
let line_terminators = [
'\n', // newline (0A)
'\r', // carriage return (0D)
'\u{000B}', // vertical tab
'\u{000C}', // form feed
'\u{0085}', // next line
'\u{2028}', // line separator
'\u{2029}', // paragraph separator
];

ensure!(
!input.contains(&line_terminators[..]),
error::InvalidNetworkDeviceNameSnafu {
input,
msg: "contains line terminators"
}
);

// The length for an interface name is defined here:
// https://elixir.bootlin.com/linux/v5.10.102/source/include/uapi/linux/if.h#L33
// The constant definition (16) is a little misleading as the check for it ensures that the
// name is NOT equal to 16. A name must be 1-15 characters.
ensure!(
!input.is_empty() && input.len() <= 15,
error::InvalidNetworkDeviceNameSnafu {
input,
msg: "invalid length, must be 1 to 15 characters long"
}
);

ensure!(
!input.contains('.') && !input.contains('/') && !input.contains(char::is_whitespace),
error::InvalidNetworkDeviceNameSnafu {
input,
msg: "contains invalid characters"
}
);

Ok(Self {
inner: input.to_string(),
})
}
}

impl TryFrom<String> for InterfaceName {
type Error = error::Error;

fn try_from(input: String) -> Result<Self> {
Self::try_from(input.as_ref())
}
}

impl Deref for InterfaceName {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.inner
}
}

impl Serialize for InterfaceName {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.inner)
}
}

mod error {
use snafu::Snafu;

#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))]
pub(crate) enum Error {
#[snafu(display("Invalid network device name '{}': {}", input, msg))]
InvalidNetworkDeviceName { input: String, msg: String },
}
}

pub(crate) use error::Error;
type Result<T> = std::result::Result<T, error::Error>;

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn invalid_interface_name() {
let bad_str = [
&std::iter::repeat("a").take(16).collect::<String>(),
"",
".",
"..",
"f/eno1",
"eno 1",
"eno\n1",
"\n",
"\r",
"\u{000B}",
"\u{000C}",
"\u{0085}",
"\u{2028}",
"\u{2029}",
];
for bad in bad_str {
assert!(InterfaceName::try_from(bad).is_err())
}
}

#[test]
fn valid_interface_name() {
let ok_str = [
&std::iter::repeat("a").take(15).collect::<String>(),
"eno1",
"eth0",
"enp5s0",
"enx0eb36944b633",
];
for ok in ok_str {
assert!(InterfaceName::try_from(ok).is_ok())
}
}
}
Loading

0 comments on commit d340619

Please sign in to comment.