Merge pull request #207115 from s1341/init_freeipa

freeipa: init at 4.10.1
This commit is contained in:
Benjamin Staffin 2023-03-30 13:15:18 -04:00 committed by GitHub
commit ff296a777e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 538 additions and 2 deletions

View File

@ -276,6 +276,7 @@
./security/doas.nix
./security/duosec.nix
./security/google_oslogin.nix
./security/ipa.nix
./security/lock-kernel-modules.nix
./security/misc.nix
./security/oath.nix

View File

@ -0,0 +1,258 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.security.ipa;
pyBool = x:
if x
then "True"
else "False";
ldapConf = pkgs.writeText "ldap.conf" ''
# Turning this off breaks GSSAPI used with krb5 when rdns = false
SASL_NOCANON on
URI ldaps://${cfg.server}
BASE ${cfg.basedn}
TLS_CACERT /etc/ipa/ca.crt
'';
nssDb =
pkgs.runCommand "ipa-nssdb"
{
nativeBuildInputs = [pkgs.nss.tools];
} ''
mkdir -p $out
certutil -d $out -N --empty-password
certutil -d $out -A --empty-password -n "${cfg.realm} IPA CA" -t CT,C,C -i ${cfg.certificate}
'';
in {
options = {
security.ipa = {
enable = mkEnableOption (lib.mdDoc "FreeIPA domain integration");
certificate = mkOption {
type = types.package;
description = lib.mdDoc ''
IPA server CA certificate.
Use `nix-prefetch-url http://$server/ipa/config/ca.crt` to
obtain the file and the hash.
'';
example = literalExpression ''
pkgs.fetchurl {
url = http://ipa.example.com/ipa/config/ca.crt;
sha256 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
};
'';
};
domain = mkOption {
type = types.str;
example = "example.com";
description = lib.mdDoc "Domain of the IPA server.";
};
realm = mkOption {
type = types.str;
example = "EXAMPLE.COM";
description = lib.mdDoc "Kerberos realm.";
};
server = mkOption {
type = types.str;
example = "ipa.example.com";
description = lib.mdDoc "IPA Server hostname.";
};
basedn = mkOption {
type = types.str;
example = "dc=example,dc=com";
description = lib.mdDoc "Base DN to use when performing LDAP operations.";
};
offlinePasswords = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc "Whether to store offline passwords when the server is down.";
};
cacheCredentials = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc "Whether to cache credentials.";
};
ifpAllowedUids = mkOption {
type = types.listOf types.string;
default = ["root"];
description = lib.mdDoc "A list of users allowed to access the ifp dbus interface.";
};
dyndns = {
enable = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc "Whether to enable FreeIPA automatic hostname updates.";
};
interface = mkOption {
type = types.str;
example = "eth0";
default = "*";
description = lib.mdDoc "Network interface to perform hostname updates through.";
};
};
chromiumSupport = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc "Whether to whitelist the FreeIPA domain in Chromium.";
};
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion = !config.krb5.enable;
message = "krb5 must be disabled through `krb5.enable` for FreeIPA integration to work.";
}
{
assertion = !config.users.ldap.enable;
message = "ldap must be disabled through `users.ldap.enable` for FreeIPA integration to work.";
}
];
environment.systemPackages = with pkgs; [krb5Full freeipa];
environment.etc = {
"ipa/default.conf".text = ''
[global]
basedn = ${cfg.basedn}
realm = ${cfg.realm}
domain = ${cfg.domain}
server = ${cfg.server}
host = ${config.networking.hostName}
xmlrpc_uri = https://${cfg.server}/ipa/xml
enable_ra = True
'';
"ipa/nssdb".source = nssDb;
"krb5.conf".text = ''
[libdefaults]
default_realm = ${cfg.realm}
dns_lookup_realm = false
dns_lookup_kdc = true
rdns = false
ticket_lifetime = 24h
forwardable = true
udp_preference_limit = 0
[realms]
${cfg.realm} = {
kdc = ${cfg.server}:88
master_kdc = ${cfg.server}:88
admin_server = ${cfg.server}:749
default_domain = ${cfg.domain}
pkinit_anchors = FILE:/etc/ipa/ca.crt
}
[domain_realm]
.${cfg.domain} = ${cfg.realm}
${cfg.domain} = ${cfg.realm}
${cfg.server} = ${cfg.realm}
[dbmodules]
${cfg.realm} = {
db_library = ${pkgs.freeipa}/lib/krb5/plugins/kdb/ipadb.so
}
'';
"openldap/ldap.conf".source = ldapConf;
};
environment.etc."chromium/policies/managed/freeipa.json" = mkIf cfg.chromiumSupport {
text = ''
{ "AuthServerWhitelist": "*.${cfg.domain}" }
'';
};
system.activationScripts.ipa = stringAfter ["etc"] ''
# libcurl requires a hard copy of the certificate
if ! ${pkgs.diffutils}/bin/diff ${cfg.certificate} /etc/ipa/ca.crt > /dev/null 2>&1; then
rm -f /etc/ipa/ca.crt
cp ${cfg.certificate} /etc/ipa/ca.crt
fi
if [ ! -f /etc/krb5.keytab ]; then
cat <<EOF
In order to complete FreeIPA integration, please join the domain by completing the following steps:
1. Authenticate as an IPA user authorized to join new hosts, e.g. kinit admin@${cfg.realm}
2. Join the domain and obtain the keytab file: ipa-join
3. Install the keytab file: sudo install -m 600 krb5.keytab /etc/
4. Restart sssd systemd service: sudo systemctl restart sssd
EOF
fi
'';
services.sssd.config = ''
[domain/${cfg.domain}]
id_provider = ipa
auth_provider = ipa
access_provider = ipa
chpass_provider = ipa
ipa_domain = ${cfg.domain}
ipa_server = _srv_, ${cfg.server}
ipa_hostname = ${config.networking.hostName}.${cfg.domain}
cache_credentials = ${pyBool cfg.cacheCredentials}
krb5_store_password_if_offline = ${pyBool cfg.offlinePasswords}
${optionalString ((toLower cfg.domain) != (toLower cfg.realm))
"krb5_realm = ${cfg.realm}"}
dyndns_update = ${pyBool cfg.dyndns.enable}
dyndns_iface = ${cfg.dyndns.interface}
ldap_tls_cacert = /etc/ipa/ca.crt
ldap_user_extra_attrs = mail:mail, sn:sn, givenname:givenname, telephoneNumber:telephoneNumber, lock:nsaccountlock
[sssd]
debug_level = 65510
services = nss, sudo, pam, ssh, ifp
domains = ${cfg.domain}
[nss]
homedir_substring = /home
[pam]
pam_pwd_expiration_warning = 3
pam_verbosity = 3
[sudo]
debug_level = 65510
[autofs]
[ssh]
[pac]
[ifp]
user_attributes = +mail, +telephoneNumber, +givenname, +sn, +lock
allowed_uids = ${concatStringsSep ", " cfg.ifpAllowedUids}
'';
services.ntp.servers = singleton cfg.server;
services.sssd.enable = true;
services.ntp.enable = true;
security.pki.certificateFiles = singleton cfg.certificate;
};
}

View File

@ -0,0 +1,24 @@
{ stdenv, lib, python3Packages }:
python3Packages.buildPythonApplication rec {
pname = "lesscpy";
version = "0.13.0";
src = python3Packages.fetchPypi {
inherit pname version;
sha256 = "1bbjag13kawnjdn7q4flfrkd0a21rgn9ycfqsgfdmg658jsx1ipk";
};
checkInputs = with python3Packages; [ pytestCheckHook ];
pythonImportsCheck = [ "lesscpy" ];
propagatedBuildInputs = with python3Packages; [ ply six ];
doCheck = false; # Really weird test failures (`nix-build-python2.css not found`)
meta = with lib; {
description = "Python LESS Compiler";
homepage = "https://github.com/lesscpy/lesscpy";
license = licenses.mit;
maintainers = with maintainers; [ s1341 ];
};
}

View File

@ -1,5 +1,5 @@
{ lib, stdenv, fetchurl, pkg-config, perl, bison, bootstrap_cmds
, openssl, openldap, libedit, keyutils
, openssl, openldap, libedit, keyutils, libverto
# for passthru.tests
, bind
@ -14,6 +14,7 @@
# This is called "staticOnly" because krb5 does not support
# builting both static and shared, see below.
, staticOnly ? false
, withVerto ? false
}:
# Note: this package is used for bootstrapping fetchurl, and thus
@ -39,6 +40,7 @@ stdenv.mkDerivation rec {
# krb5's ./configure does not allow passing --enable-shared and --enable-static at the same time.
# See https://bbs.archlinux.org/viewtopic.php?pid=1576737#p1576737
++ lib.optionals staticOnly [ "--enable-static" "--disable-shared" ]
++ lib.optional withVerto "--with-system-verto"
++ lib.optional stdenv.isFreeBSD ''WARN_CFLAGS=""''
++ lib.optionals (stdenv.buildPlatform != stdenv.hostPlatform)
[ "krb5_cv_attr_constructor_destructor=yes,yes"
@ -53,7 +55,8 @@ stdenv.mkDerivation rec {
buildInputs = [ openssl ]
++ lib.optionals (stdenv.hostPlatform.isLinux && stdenv.hostPlatform.libc != "bionic" && !(stdenv.hostPlatform.useLLVM or false)) [ keyutils ]
++ lib.optionals (!libOnly) [ openldap libedit ];
++ lib.optionals (!libOnly) [ openldap libedit ]
++ lib.optionals withVerto [ libverto ];
sourceRoot = "krb5-${version}/src";

View File

@ -0,0 +1,23 @@
{ stdenv, lib, fetchPypi, buildPythonPackage, cryptography,
ldap, requests, six }:
buildPythonPackage rec {
pname = "dogtag-pki";
version = "11.2.1";
src = fetchPypi {
inherit pname version;
sha256 = "sha256-rQSnQPNYr5SyeNbKoFAbnGb2X/8utrfWLa8gu93hy2w=";
};
buildInputs = [ cryptography ldap ];
pythonImportsCheck = [ "pki" ];
propagatedBuildInputs = [ requests six ];
meta = with lib; {
description = "An enterprise-class Certificate Authority";
homepage = "https://github.com/dogtagpki/pki";
license = licenses.gpl2;
maintainers = with maintainers; [ s1341 ];
};
}

View File

@ -0,0 +1,27 @@
{ stdenv, lib, buildPythonPackage, fetchPypi, pytestCheckHook, pyusb }:
buildPythonPackage rec {
pname = "python-yubico";
version = "1.3.2";
src = fetchPypi {
inherit pname version;
sha256 = "1gd3an1cdcq328nr1c9ijrsf32v0crv6dgq7knld8m9cadj517c7";
};
propagatedBuildInputs = [ pyusb ];
checkInputs = [ pytestCheckHook ];
pythonImportsCheck = [ "yubico" ];
disabledTests = [
"usb" # requires a physical yubikey to test
];
meta = with lib; {
description = "Python code to talk to YubiKeys";
homepage = "https://github.com/Yubico/python-yubico";
license = licenses.bsd2;
maintainers = with maintainers; [ s1341 ];
};
}

View File

@ -0,0 +1,171 @@
{ stdenv
, lib
, fetchurl
, pkgconfig
, autoconf
, automake
, kerberos
, openldap
, popt
, sasl
, curl
, xmlrpc_c
, ding-libs
, p11-kit
, gettext
, nspr
, nss
, _389-ds-base
, svrcore
, libuuid
, talloc
, tevent
, samba
, libunistring
, libverto
, libpwquality
, systemd
, python3
, bind
, sssd
, jre
, rhino
, lesscpy
, jansson
, runtimeShell
}:
let
pathsPy = ./paths.py;
pythonInputs = with python3.pkgs; [
six
ldap
dns
netaddr
netifaces
gssapi
dogtag-pki
pyasn1
sssd
cffi
lxml
dbus-python
cryptography
memcached
qrcode
pyusb
yubico
setuptools
jinja2
augeas
samba
];
in
stdenv.mkDerivation rec {
pname = "freeipa";
version = "4.10.1";
src = fetchurl {
url = "https://releases.pagure.org/freeipa/freeipa-${version}.tar.gz";
sha256 = "sha256-q2rQzcBl1tI4/7+hxEwOY9ND86hObe7O7Y9EEH7cUoA=";
};
nativeBuildInputs = [
python3.pkgs.wrapPython
jre
rhino
lesscpy
automake
autoconf
gettext
];
buildInputs = [
kerberos
openldap
popt
sasl
curl
xmlrpc_c
pkgconfig
ding-libs
p11-kit
python3
nspr
nss
_389-ds-base
svrcore
libuuid
talloc
tevent
samba
libunistring
libverto
systemd
bind
libpwquality
jansson
] ++ pythonInputs;
postPatch = ''
patchShebangs makeapi makeaci install/ui/util
substituteInPlace ipaplatform/setup.py \
--replace 'ipaplatform.debian' 'ipaplatform.nixos'
substituteInPlace ipasetup.py.in \
--replace 'int(v)' 'int(v.replace("post", ""))'
substituteInPlace client/ipa-join.c \
--replace /usr/sbin/ipa-getkeytab $out/bin/ipa-getkeytab
cp -r ipaplatform/{fedora,nixos}
substitute ${pathsPy} ipaplatform/nixos/paths.py \
--subst-var out \
--subst-var-by bind ${bind.dnsutils} \
--subst-var-by curl ${curl} \
--subst-var-by kerberos ${kerberos}
'';
NIX_CFLAGS_COMPILE = "-I${_389-ds-base}/include/dirsrv";
pythonPath = pythonInputs;
# Building and installing the server fails with silent Rhino errors, skipping
# for now. Need a newer Rhino version.
#buildFlags = [ "client" "server" ]
configureFlags = [
"--with-systemdsystemunitdir=$out/lib/systemd/system"
"--with-ipaplatform=nixos"
"--disable-server"
];
postInstall = ''
echo "
#!${runtimeShell}
echo 'ipa-client-install is not available on NixOS. Please see security.ipa, instead.'
exit 1
" > $out/sbin/ipa-client-install
'';
postFixup = ''
wrapPythonPrograms
rm -rf $out/etc/ipa $out/var/lib/ipa-client/sysrestore
'';
meta = with lib; {
description = "Identity, Policy and Audit system";
longDescription = ''
IPA is an integrated solution to provide centrally managed Identity (users,
hosts, services), Authentication (SSO, 2FA), and Authorization
(host access control, SELinux user roles, services). The solution provides
features for further integration with Linux based clients (SUDO, automount)
and integration with Active Directory based infrastructures (Trusts).
'';
homepage = "https://www.freeipa.org/";
license = licenses.gpl3Plus;
maintainers = [ maintainers.s1341 ];
platforms = platforms.linux;
};
}

View File

@ -0,0 +1,13 @@
from ipaplatform.fedora.paths import FedoraPathNamespace
class NixOSPathNamespace(FedoraPathNamespace):
SBIN_IPA_JOIN = "@out@/bin/ipa-join"
IPA_GETCERT = "@out@/bin/ipa-getcert"
IPA_RMKEYTAB = "@out@/bin/ipa-rmkeytab"
IPA_GETKEYTAB = "@out@/bin/ipa-getkeytab"
NSUPDATE = "@bind@/bin/nsupdate"
BIN_CURL = "@curl@/bin/curl"
KINIT = "@kerberos@/bin/kinit"
KDESTROY = "@kerberos@/bin/kdestroy"
paths = NixOSPathNamespace()

View File

@ -2432,6 +2432,8 @@ with pkgs;
krusader = libsForQt5.callPackage ../applications/file-managers/krusader { };
lesscpy = callPackage ../development/compilers/lesscpy { };
lf = callPackage ../applications/file-managers/lf { };
ctpv = callPackage ../applications/file-managers/lf/ctpv.nix { };
@ -19989,6 +19991,16 @@ with pkgs;
inherit (darwin) autoSignDarwinBinariesHook;
};
freeipa = callPackage ../os-specific/linux/freeipa {
kerberos = krb5.override {
withVerto = true;
};
sasl = cyrus_sasl;
samba = samba4.override {
enableLDAP = true;
};
};
freetts = callPackage ../development/libraries/freetts {
jdk = jdk8; # TODO: remove override https://github.com/NixOS/nixpkgs/pull/89731
};

View File

@ -2841,6 +2841,8 @@ self: super: with self; {
dogpile-cache = callPackage ../development/python-modules/dogpile-cache { };
dogtag-pki = callPackage ../development/python-modules/dogtag-pki { };
dogtail = callPackage ../development/python-modules/dogtail { };
doit = callPackage ../development/python-modules/doit { };
@ -12830,6 +12832,8 @@ self: super: with self; {
ytmusicapi = callPackage ../development/python-modules/ytmusicapi { };
yubico = callPackage ../development/python-modules/yubico { };
yubico-client = callPackage ../development/python-modules/yubico-client { };
z3c-checkversions = callPackage ../development/python-modules/z3c-checkversions { };