forked from Fachschaft/nixConfig
Compare commits
3 commits
eb86511fe7
...
aa210d868b
Author | SHA1 | Date | |
---|---|---|---|
aa210d868b | |||
79c731ccd3 | |||
7f5496c9c9 |
13 changed files with 171 additions and 87 deletions
|
@ -4,7 +4,6 @@
|
||||||
../../modules/jitsi.nix
|
../../modules/jitsi.nix
|
||||||
../../roles
|
../../roles
|
||||||
../../roles/vm.nix
|
../../roles/vm.nix
|
||||||
../../modules/vmNetwork.nix
|
|
||||||
];
|
];
|
||||||
|
|
||||||
services.mathebau-jitsi = {
|
services.mathebau-jitsi = {
|
||||||
|
@ -14,6 +13,5 @@
|
||||||
|
|
||||||
# System configuration here
|
# System configuration here
|
||||||
networking.hostName = "ghatanothoa";
|
networking.hostName = "ghatanothoa";
|
||||||
vmNetwork.ipv4 = "192.168.0.25";
|
|
||||||
system.stateVersion = "23.11";
|
system.stateVersion = "23.11";
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
../../modules/mailman.nix
|
../../modules/mailman.nix
|
||||||
../../roles
|
../../roles
|
||||||
../../roles/vm.nix
|
../../roles/vm.nix
|
||||||
../../modules/vmNetwork.nix
|
|
||||||
];
|
];
|
||||||
|
|
||||||
# System configuration here
|
# System configuration here
|
||||||
|
@ -16,7 +15,6 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
networking.hostName = "lobon";
|
networking.hostName = "lobon";
|
||||||
vmNetwork.ipv4 = "192.168.0.22";
|
|
||||||
system.stateVersion = "23.11";
|
system.stateVersion = "23.11";
|
||||||
|
|
||||||
sops.secrets = {
|
sops.secrets = {
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
./hardware-configuration.nix
|
./hardware-configuration.nix
|
||||||
../../roles
|
../../roles
|
||||||
../../roles/vm.nix
|
../../roles/vm.nix
|
||||||
../../modules/vmNetwork.nix
|
|
||||||
];
|
];
|
||||||
|
|
||||||
# System configuration here
|
# System configuration here
|
||||||
|
@ -11,6 +10,5 @@
|
||||||
environment.systemPackages = [pkgs.git];
|
environment.systemPackages = [pkgs.git];
|
||||||
|
|
||||||
networking.hostName = "nodens";
|
networking.hostName = "nodens";
|
||||||
vmNetwork.ipv4 = "192.168.0.18";
|
|
||||||
system.stateVersion = "24.11";
|
system.stateVersion = "24.11";
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
../../modules/mail.nix
|
../../modules/mail.nix
|
||||||
../../roles
|
../../roles
|
||||||
../../roles/vm.nix
|
../../roles/vm.nix
|
||||||
../../modules/vmNetwork.nix
|
|
||||||
];
|
];
|
||||||
|
|
||||||
# System configuration here
|
# System configuration here
|
||||||
|
@ -40,7 +39,6 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
networking.hostName = "nyarlathotep";
|
networking.hostName = "nyarlathotep";
|
||||||
vmNetwork.ipv4 = "192.168.0.17";
|
|
||||||
system.stateVersion = "24.05";
|
system.stateVersion = "24.05";
|
||||||
|
|
||||||
sops.secrets = let
|
sops.secrets = let
|
||||||
|
|
|
@ -360,7 +360,7 @@ in {
|
||||||
# We don't want this in order to not need to persist borg cache and simplify new deployments.
|
# We don't want this in order to not need to persist borg cache and simplify new deployments.
|
||||||
BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK = "yes";
|
BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK = "yes";
|
||||||
};
|
};
|
||||||
repo = "borg@192.168.1.11:nyarlathotep"; # TODO for https://gitea.mathebau.de/Fachschaft/nixConfig/issues/33
|
repo = "borg@bragi:nyarlathotep"; # TODO for https://gitea.mathebau.de/Fachschaft/nixConfig/issues/33
|
||||||
startAt = "daily";
|
startAt = "daily";
|
||||||
user = "root";
|
user = "root";
|
||||||
group = "root";
|
group = "root";
|
||||||
|
|
|
@ -117,7 +117,7 @@ in {
|
||||||
# We don't want this in order to not need to persist borg cache and simplify new deployments.
|
# We don't want this in order to not need to persist borg cache and simplify new deployments.
|
||||||
BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK = "yes";
|
BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK = "yes";
|
||||||
};
|
};
|
||||||
repo = "borg@192.168.1.11:lobon"; # TODO for https://gitea.mathebau.de/Fachschaft/nixConfig/issues/33
|
repo = "borg@bragi:lobon"; # TODO for https://gitea.mathebau.de/Fachschaft/nixConfig/issues/33
|
||||||
startAt = "daily";
|
startAt = "daily";
|
||||||
user = "root";
|
user = "root";
|
||||||
group = "root";
|
group = "root";
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
{
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
...
|
|
||||||
}: let
|
|
||||||
inherit
|
|
||||||
(lib)
|
|
||||||
mkOption
|
|
||||||
types
|
|
||||||
last
|
|
||||||
init
|
|
||||||
;
|
|
||||||
inherit
|
|
||||||
(lib.strings)
|
|
||||||
splitString
|
|
||||||
concatStringsSep
|
|
||||||
toInt
|
|
||||||
;
|
|
||||||
cfg = config.vmNetwork;
|
|
||||||
in {
|
|
||||||
imports = [];
|
|
||||||
|
|
||||||
options.vmNetwork = {
|
|
||||||
ipv4 = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
description = "the ipv4 adress of this machine";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
networking = {
|
|
||||||
interfaces.enX0.ipv4.addresses = [
|
|
||||||
{
|
|
||||||
address = cfg.ipv4;
|
|
||||||
prefixLength = 16;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
defaultGateway = let
|
|
||||||
addr = splitString "." cfg.ipv4;
|
|
||||||
addrInit = init addr;
|
|
||||||
addrLastInt = builtins.toString (toInt (last addr) + 127);
|
|
||||||
in
|
|
||||||
concatStringsSep "." (addrInit ++ [addrLastInt]);
|
|
||||||
# https://www.hrz.tu-darmstadt.de/services/it_services/nameserver_dns/index.de.jsp
|
|
||||||
nameservers = ["130.83.22.63" "130.83.22.60" "130.83.56.60"];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
57
nixos/roles/hostmap.nix
Normal file
57
nixos/roles/hostmap.nix
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
# This data is taken from /etc/hosts from azatoth
|
||||||
|
{
|
||||||
|
bragi = {
|
||||||
|
ipv4 = "192.168.1.11";
|
||||||
|
};
|
||||||
|
tsathoggua = {
|
||||||
|
ipv4 = "192.168.0.13";
|
||||||
|
};
|
||||||
|
nyogtha = {
|
||||||
|
ipv4 = "192.168.0.14";
|
||||||
|
};
|
||||||
|
hastur = {
|
||||||
|
ipv4 = "192.168.0.15";
|
||||||
|
};
|
||||||
|
cthulhu = {
|
||||||
|
ipv4 = "192.168.0.16";
|
||||||
|
};
|
||||||
|
nyarlathotep = {
|
||||||
|
ipv4 = "192.168.0.17";
|
||||||
|
};
|
||||||
|
nodens = {
|
||||||
|
ipv4 = "192.168.0.18";
|
||||||
|
};
|
||||||
|
uvhash = {
|
||||||
|
ipv4 = "192.168.0.19";
|
||||||
|
};
|
||||||
|
aphoom-zhah = {
|
||||||
|
ipv4 = "192.168.0.20";
|
||||||
|
};
|
||||||
|
dagon = {
|
||||||
|
ipv4 = "192.168.0.21";
|
||||||
|
};
|
||||||
|
lobon = {
|
||||||
|
ipv4 = "192.168.0.22";
|
||||||
|
};
|
||||||
|
yibb-tstll = {
|
||||||
|
ipv4 = "192.168.0.23";
|
||||||
|
};
|
||||||
|
eihort = {
|
||||||
|
ipv4 = "192.168.0.24";
|
||||||
|
};
|
||||||
|
ghatanothoa = {
|
||||||
|
ipv4 = "192.168.0.25";
|
||||||
|
};
|
||||||
|
toth = {
|
||||||
|
ipv4 = "192.168.0.26";
|
||||||
|
};
|
||||||
|
ithaqua = {
|
||||||
|
ipv4 = "192.168.0.27";
|
||||||
|
};
|
||||||
|
cthugha = {
|
||||||
|
ipv4 = "192.168.0.30";
|
||||||
|
};
|
||||||
|
sanctamariamaterdei = {
|
||||||
|
ipv4 = "192.168.0.92";
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
{modulesPath, ...}: {
|
{modulesPath, ...}: {
|
||||||
imports = [
|
imports = [
|
||||||
(modulesPath + "/virtualisation/xen-domU.nix")
|
(modulesPath + "/virtualisation/xen-domU.nix")
|
||||||
|
./vmNetwork.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
46
nixos/roles/vmNetwork.nix
Normal file
46
nixos/roles/vmNetwork.nix
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit (lib) mapAttrsToList;
|
||||||
|
inherit (lib.attrsets) foldAttrs concatMapAttrs;
|
||||||
|
inherit (lib.asserts) assertMsg;
|
||||||
|
inherit (lib.lists) filter last init;
|
||||||
|
inherit (lib.strings) splitString toInt concatStringsSep;
|
||||||
|
inherit (builtins) elem toString;
|
||||||
|
hostmap = import ./hostmap.nix;
|
||||||
|
myhostName = config.networking.hostName;
|
||||||
|
# To turn the hostmap around suitable for networking.hosts the following simple code almost works
|
||||||
|
# concatMapAttrs (hostname: ipData: { ${ipData.ipv4} = [hostname]; }) hostmap
|
||||||
|
# but breaks as soon as we want to map two different names to the same ip.
|
||||||
|
# So the code looks uglier than one would expect.
|
||||||
|
globalhosts = foldAttrs (a: b: a ++ b) [] (mapAttrsToList (hostname: ipData: {${ipData.ipv4} = [hostname];}) hostmap);
|
||||||
|
# We replace our own ip with 127.0.0.1 in /etc/hosts
|
||||||
|
myhosts = concatMapAttrs (ip: hosts:
|
||||||
|
if (elem myhostName hosts)
|
||||||
|
# nixos maps the hostname to the loopback 127.0.0.2 by default, so we exclude it here.
|
||||||
|
# there is also a default localhost to 127.0.0.1 in place
|
||||||
|
then {"127.0.0.1" = filter (x: x != myhostName) hosts;}
|
||||||
|
else {${ip} = hosts;})
|
||||||
|
globalhosts;
|
||||||
|
myIp = assert (assertMsg (hostmap ? ${myhostName}.ipv4) "${myhostName} has no ip configured in nixos/roles/hostmap.nix"); hostmap.${myhostName}.ipv4;
|
||||||
|
in {
|
||||||
|
networking = {
|
||||||
|
hosts = myhosts;
|
||||||
|
interfaces.enX0.ipv4.addresses = [
|
||||||
|
{
|
||||||
|
address = myIp;
|
||||||
|
prefixLength = 16;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
defaultGateway = let
|
||||||
|
addr = splitString "." myIp;
|
||||||
|
addrInit = init addr;
|
||||||
|
addrLastInt = toString (toInt (last addr) + 127);
|
||||||
|
in
|
||||||
|
concatStringsSep "." (addrInit ++ [addrLastInt]);
|
||||||
|
# https://www.hrz.tu-darmstadt.de/services/it_services/nameserver_dns/index.de.jsp
|
||||||
|
nameservers = ["130.83.22.63" "130.83.22.60" "130.83.56.60"];
|
||||||
|
};
|
||||||
|
}
|
|
@ -12,29 +12,59 @@ pub struct AliasFile {
|
||||||
pub default_domain: FQDN,
|
pub default_domain: FQDN,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
pub struct OrdEmailAddress(EmailAddress);
|
pub struct AliasEmailAddress(EmailAddress);
|
||||||
|
|
||||||
impl PartialOrd for OrdEmailAddress {
|
impl AliasEmailAddress {
|
||||||
|
/// Create an `AliasEmailAddress` from some alias entry.
|
||||||
|
/// Return parameter for complete mail addresses and append the default domain for local parts.
|
||||||
|
pub fn new(
|
||||||
|
alias_entry: &str,
|
||||||
|
default_domain: &FQDN,
|
||||||
|
) -> Result<AliasEmailAddress, Box<dyn Error>> {
|
||||||
|
let mut addr = alias_entry.trim().to_string();
|
||||||
|
addr = addr.replace(',', "");
|
||||||
|
|
||||||
|
// The domain already fails on instantiation of the FQDN type if it contains an apostrophe.
|
||||||
|
if addr.contains('\'') {
|
||||||
|
return Err(format!(
|
||||||
|
"Mailaddress {addr} contains an apostrophe which breaks the script generation."
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if addr.contains('@') {
|
||||||
|
return Ok(AliasEmailAddress(
|
||||||
|
EmailAddress::parse(&addr, None).ok_or::<Box<dyn Error>>(
|
||||||
|
String::from("Mailaddress {addr} not parsable.").into(),
|
||||||
|
)?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let unsortable_mail = EmailAddress::new(&addr, &default_domain.to_string(), None)?;
|
||||||
|
Ok(AliasEmailAddress(unsortable_mail))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for AliasEmailAddress {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
Some(self.0.to_string().cmp(&other.0.to_string()))
|
Some(self.0.to_string().cmp(&other.0.to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ord for OrdEmailAddress {
|
impl Ord for AliasEmailAddress {
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
self.0.to_string().cmp(&other.0.to_string())
|
self.0.to_string().cmp(&other.0.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type AliasMap = BTreeMap<OrdEmailAddress, Vec<OrdEmailAddress>>;
|
pub type AliasMap = BTreeMap<AliasEmailAddress, Vec<AliasEmailAddress>>;
|
||||||
|
|
||||||
/// Read a virtual alias file <https://www.postfix.org/virtual.5.html>
|
/// Read a virtual alias file <https://www.postfix.org/virtual.5.html>
|
||||||
/// and convert it to a map of destination addresses to a list of their final forwarding addresses.
|
/// and convert it to a map of destination addresses to a list of their final forwarding addresses.
|
||||||
pub fn parse_alias_to_map(alias_files: Vec<AliasFile>) -> Result<AliasMap, Box<dyn Error>> {
|
pub fn parse_alias_to_map(alias_files: Vec<AliasFile>) -> Result<AliasMap, Box<dyn Error>> {
|
||||||
// File must exist in the current path
|
// File must exist in the current path
|
||||||
let mut redirect_map: AliasMap = AliasMap::new();
|
let mut redirect_map: AliasMap = AliasMap::new();
|
||||||
let mut destinations: Vec<OrdEmailAddress> = Vec::new();
|
let mut destinations: Vec<AliasEmailAddress> = Vec::new();
|
||||||
|
|
||||||
// Extract all pairs (destination to redirect addresses) from the alias files
|
// Extract all pairs (destination to redirect addresses) from the alias files
|
||||||
for alias_file in alias_files {
|
for alias_file in alias_files {
|
||||||
|
@ -48,20 +78,23 @@ pub fn parse_alias_to_map(alias_files: Vec<AliasFile>) -> Result<AliasMap, Box<d
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let redirects: Vec<OrdEmailAddress> = line
|
let redirects: Vec<AliasEmailAddress> = line
|
||||||
.split_at(line.find(char::is_whitespace).unwrap_or(0))
|
.split_at(line.find(char::is_whitespace).unwrap_or(0))
|
||||||
.1
|
.1
|
||||||
.split(' ')
|
.split(' ')
|
||||||
.filter(|address| !address.trim().to_string().replace(',', "").is_empty())
|
.filter(|address| !address.trim().to_string().replace(',', "").is_empty())
|
||||||
.map(|addr| to_mailaddress(addr, &alias_file.default_domain))
|
.map(|addr| AliasEmailAddress::new(addr, &alias_file.default_domain))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
if redirects.is_empty() {
|
if redirects.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
destinations.push(to_mailaddress(destination, &alias_file.default_domain)?);
|
destinations.push(AliasEmailAddress::new(
|
||||||
|
destination,
|
||||||
|
&alias_file.default_domain,
|
||||||
|
)?);
|
||||||
redirect_map.insert(
|
redirect_map.insert(
|
||||||
to_mailaddress(destination, &alias_file.default_domain)?,
|
AliasEmailAddress::new(destination, &alias_file.default_domain)?,
|
||||||
redirects,
|
redirects,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -95,29 +128,11 @@ pub fn parse_alias_to_map(alias_files: Vec<AliasFile>) -> Result<AliasMap, Box<d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if iterations == max_iterations {
|
if iterations == max_iterations {
|
||||||
return Err(String::from("Possibly infinite recursion detected in parse_alias_map. Did not terminate after {max_iterations} rounds.").into());
|
return Err(format!("Possibly infinite recursion detected in parse_alias_map. Did not terminate after {max_iterations} rounds.").into());
|
||||||
}
|
}
|
||||||
Ok(redirect_map)
|
Ok(redirect_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an `OrdEmailAddress` from some alias entry.
|
|
||||||
/// Return parameter for complete mail addresses and append the default domain for local parts.
|
|
||||||
fn to_mailaddress(
|
|
||||||
alias_entry: &str,
|
|
||||||
default_domain: &FQDN,
|
|
||||||
) -> Result<OrdEmailAddress, Box<dyn Error>> {
|
|
||||||
let mut addr = alias_entry.trim().to_string();
|
|
||||||
addr = addr.replace(',', "");
|
|
||||||
if addr.contains('@') {
|
|
||||||
return Ok(OrdEmailAddress(
|
|
||||||
EmailAddress::parse(&addr, None)
|
|
||||||
.ok_or::<Box<dyn Error>>(String::from("Mailaddress {addr} not parsable.").into())?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let unsortable_mail = EmailAddress::new(&addr, &default_domain.to_string(), None)?;
|
|
||||||
Ok(OrdEmailAddress(unsortable_mail))
|
|
||||||
}
|
|
||||||
|
|
||||||
// The output is wrapped in a Result to allow matching on errors.
|
// The output is wrapped in a Result to allow matching on errors.
|
||||||
// Returns an Iterator to the Reader of the lines of the file.
|
// Returns an Iterator to the Reader of the lines of the file.
|
||||||
pub fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
|
pub fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
|
||||||
|
@ -190,6 +205,23 @@ mod tests {
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn apostrophe_destination_detection() {
|
||||||
|
let result = parse_alias_to_map(vec![AliasFile {
|
||||||
|
content: read_lines("testdata/apostrophe_destination.aliases").unwrap(),
|
||||||
|
default_domain: FQDN::from_str("example.com").unwrap(),
|
||||||
|
}]);
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn apostrophe_redirect_detection() {
|
||||||
|
let result = parse_alias_to_map(vec![AliasFile {
|
||||||
|
content: read_lines("testdata/apostrophe_redirect.aliases").unwrap(),
|
||||||
|
default_domain: FQDN::from_str("example.com").unwrap(),
|
||||||
|
}]);
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic_parsing() {
|
fn basic_parsing() {
|
||||||
let result = parse_alias_to_map(vec![AliasFile {
|
let result = parse_alias_to_map(vec![AliasFile {
|
||||||
|
|
2
packages/alias-to-sieve/testdata/apostrophe_destination.aliases
vendored
Normal file
2
packages/alias-to-sieve/testdata/apostrophe_destination.aliases
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Apostrophes are not allowed
|
||||||
|
'orga me@example.com
|
2
packages/alias-to-sieve/testdata/apostrophe_redirect.aliases
vendored
Normal file
2
packages/alias-to-sieve/testdata/apostrophe_redirect.aliases
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Apostrophes are not allowed
|
||||||
|
orga me@e'xample.com
|
Loading…
Add table
Add a link
Reference in a new issue