import from nix-scripts

pull/1/head
Sebastien Bourdeauducq 2020-06-20 17:54:21 +08:00
commit 5a0afc48d4
40 changed files with 2978 additions and 0 deletions

View File

@ -0,0 +1,54 @@
{ config, pkgs, lib, ... }:
with lib;
let
makeBackup = pkgs.writeScript "make-backup" ''
#!${pkgs.bash}/bin/bash
set -e
umask 0077
DBDUMPDIR=`mktemp -d`
pushd $DBDUMPDIR
${config.services.mysql.package}/bin/mysqldump --single-transaction flarum > flarum.sql
${pkgs.sudo}/bin/sudo -u mattermost ${config.services.postgresql.package}/bin/pg_dump mattermost > mattermost.sql
${pkgs.gnutar}/bin/tar cf - --exclude "/var/lib/gitea/repositories/*/*.git/archives" /etc/nixos /var/lib/gitea flarum.sql mattermost.sql | \
${pkgs.bzip2}/bin/bzip2 | \
${pkgs.gnupg}/bin/gpg --symmetric --batch --passphrase-file /etc/nixos/secret/backup-passphrase | \
${pkgs.rclone}/bin/rclone rcat --config /etc/nixos/secret/rclone.conf dropbox:backup-`date +%F`.tar.bz2.gpg
popd
rm -rf $DBDUMPDIR
echo Backup done
'';
cfg = config.services.mlabs-backup;
in
{
options.services.mlabs-backup = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable backups";
};
};
config = mkIf cfg.enable {
systemd.services.mlabs-backup = {
description = "M-Labs backup";
serviceConfig = {
Type = "oneshot";
User = "root";
Group = "root";
ExecStart = "${makeBackup}";
};
};
systemd.timers.mlabs-backup = {
description = "M-Labs backup";
wantedBy = [ "timers.target" ];
timerConfig.OnCalendar = "weekly";
};
};
}

View File

@ -0,0 +1,667 @@
# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page
# and in the NixOS manual (accessible by running nixos-help).
{ config, pkgs, ... }:
let
netifWan = "enp0s31f6";
netifLan = "enp3s0";
netifWifi = "wlp1s0";
netifSit = "henet0";
hydraWwwOutputs = "/var/www/hydra-outputs";
in
{
imports =
[
./hardware-configuration.nix
./homu/nixos-module.nix
./backup-module.nix
(builtins.fetchTarball {
url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/v2.3.0/nixos-mailserver-v2.3.0.tar.gz";
sha256 = "0lpz08qviccvpfws2nm83n7m2r8add2wvfg9bljx9yxx8107r919";
})
];
# Use the systemd-boot EFI boot loader.
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.blacklistedKernelModules = ["iwlwifi"];
security.apparmor.enable = true;
networking = {
hostName = "nixbld";
firewall = {
allowedTCPPorts = [ 80 443 ];
allowedUDPPorts = [ 53 67 ];
trustedInterfaces = [ netifLan ];
};
interfaces."${netifLan}" = {
ipv4.addresses = [{
address = "192.168.1.1";
prefixLength = 24;
}];
ipv6.addresses = [{
address = "2001:470:f821:1::";
prefixLength = 64;
}];
};
interfaces."${netifWifi}" = {
ipv4.addresses = [{
address = "192.168.12.1";
prefixLength = 24;
}];
ipv6.addresses = [{
address = "2001:470:f821:2::";
prefixLength = 64;
}];
};
nat = {
enable = true;
externalInterface = netifWan;
internalInterfaces = [ netifLan netifWifi ];
forwardPorts = [
{ sourcePort = 2201; destination = "192.168.1.201:22"; proto = "tcp"; }
{ sourcePort = 2202; destination = "192.168.1.202:22"; proto = "tcp"; }
{ sourcePort = 2203; destination = "192.168.1.203:22"; proto = "tcp"; }
{ sourcePort = 2204; destination = "192.168.1.204:22"; proto = "tcp"; }
{ sourcePort = 2205; destination = "192.168.1.205:22"; proto = "tcp"; }
];
extraCommands = ''
iptables -w -N block-lan-from-wifi
iptables -w -A block-lan-from-wifi -i ${netifLan} -o ${netifWifi} -j DROP
iptables -w -A block-lan-from-wifi -i ${netifWifi} -o ${netifLan} -j DROP
iptables -w -A FORWARD -j block-lan-from-wifi
'';
extraStopCommands = ''
iptables -w -D FORWARD -j block-lan-from-wifi 2>/dev/null|| true
iptables -w -F block-lan-from-wifi 2>/dev/null|| true
iptables -w -X block-lan-from-wifi 2>/dev/null|| true
'';
};
sits."${netifSit}" = {
dev = netifWan;
remote = "216.218.221.6";
local = "42.200.147.171";
ttl = 255;
};
interfaces."${netifSit}".ipv6 = {
addresses = [{ address = "2001:470:18:629::2"; prefixLength = 64; }];
routes = [{ address = "::"; prefixLength = 0; }];
};
};
boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = "1";
boot.kernel.sysctl."net.ipv6.conf.default.forwarding" = "1";
services.unbound = {
enable = true;
extraConfig =
''
server:
port: 5353
'';
};
services.hostapd = {
enable = true;
interface = netifWifi;
hwMode = "g";
ssid = "M-Labs";
wpaPassphrase = (import /etc/nixos/secret/wifi_password.nix);
extraConfig = ''
ieee80211d=1
country_code=HK
ieee80211n=1
wmm_enabled=1
auth_algs=1
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
'';
};
services.dnsmasq = {
enable = true;
servers = ["::1#5353"];
extraConfig = ''
interface=${netifLan}
interface=${netifWifi}
bind-interfaces
dhcp-range=interface:${netifLan},192.168.1.81,192.168.1.254,24h
dhcp-range=interface:${netifWifi},192.168.12.10,192.168.12.254,24h
enable-ra
dhcp-range=interface:${netifLan},::,constructor:${netifLan},ra-names
dhcp-range=interface:${netifWifi},::,constructor:${netifWifi},ra-only
no-resolv
# Static IPv4s to make Red Pitayas less annoying
dhcp-host=rp-f05cc9,192.168.1.190
dhcp-host=rp-f0612e,192.168.1.191
# Static IPv4s to make port redirections work
dhcp-host=rpi-1,192.168.1.201
dhcp-host=rpi-2,192.168.1.202
dhcp-host=rpi-3,192.168.1.203
dhcp-host=rpi-4,192.168.1.204
dhcp-host=rpi-5,192.168.1.205
# Default IP addresses for ARTIQ boards
address=/thermostat/192.168.1.26
address=/kc705/192.168.1.50
address=/zc706/192.168.1.51
address=/zc706-2/192.168.1.52
address=/sayma/192.168.1.60
address=/metlino/192.168.1.65
address=/kasli/192.168.1.70
address=/kasli-customer/192.168.1.75
address=/stabilizer-customer/192.168.1.76
# uTCA MCH from NAT
address=/tschernobyl/192.168.1.80
'';
};
# Select internationalisation properties.
i18n.defaultLocale = "en_US.UTF-8";
console = {
font = "Lat2-Terminus16";
keyMap = "de";
};
# Set your time zone.
time.timeZone = "Asia/Hong_Kong";
# List packages installed in system profile. To search, run:
# $ nix search wget
environment.systemPackages = with pkgs; [
wget vim git file lm_sensors acpi pciutils psmisc telnet nixops
irssi tmux usbutils imagemagick jq zip unzip
];
# Some programs need SUID wrappers, can be configured further or are
# started in user sessions.
# programs.mtr.enable = true;
# programs.gnupg.agent = { enable = true; enableSSHSupport = true; };
# List services that you want to enable:
services.apcupsd.enable = true;
services.apcupsd.configText = ''
UPSTYPE usb
NISIP 127.0.0.1
BATTERYLEVEL 10
MINUTES 5
'';
# Enable the OpenSSH daemon.
services.openssh.enable = true;
services.openssh.forwardX11 = true;
services.openssh.passwordAuthentication = false;
programs.mosh.enable = true;
programs.fish.enable = true;
# Enable CUPS to print documents.
services.avahi.enable = true;
services.avahi.interfaces = [ netifLan ];
services.avahi.publish.enable = true;
services.avahi.publish.userServices = true;
nixpkgs.config.allowUnfree = true;
services.printing.enable = true;
services.printing.drivers = [ pkgs.hplipWithPlugin ];
services.printing.browsing = true;
services.printing.listenAddresses = [ "*:631" ];
services.printing.defaultShared = true;
hardware.sane.enable = true;
hardware.sane.extraBackends = [ pkgs.hplipWithPlugin ];
users.extraGroups.plugdev = { };
users.extraUsers.sb = {
isNormalUser = true;
extraGroups = ["wheel" "plugdev" "dialout" "lp" "scanner"];
shell = pkgs.fish;
openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCyPk5WyFoWSvF4ozehxcVBoZ+UHgrI7VW/OoQfFFwIQe0qvetUZBMZwR2FwkLPAMZV8zz1v4EfncudEkVghy4P+/YVLlDjqDq9zwZnh8Nd/ifu84wmcNWHT2UcqnhjniCdshL8a44memzABnxfLLv+sXhP2x32cJAamo5y6fukr2qLp2jbXzR+3sv3klE0ruUXis/BR1lLqNJEYP8jB6fLn2sLKinnZPfn6DwVOk10mGeQsdME/eGl3phpjhODH9JW5V2V5nJBbC0rBnq+78dyArKVqjPSmIcSy72DEIpTctnMEN1W34BGrnsDd5Xd/DKxKxHKTMCHtZRwLC2X0NWN"
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCMALVC8RDTHec+PC8y1s3tcpUAODgq6DEzQdHDf/cyvDMfmCaPiMxfIdmkns5lMa03hymIfSmLUF0jFFDc7biRp7uf9AAXNsrTmplHii0l0McuOOZGlSdZM4eL817P7UwJqFMxJyFXDjkubhQiX6kp25Kfuj/zLnupRCaiDvE7ho/xay6Jrv0XLz935TPDwkc7W1asLIvsZLheB+sRz9SMOb9gtrvk5WXZl5JTOFOLu+JaRwQLHL/xdcHJTOod7tqHYfpoC5JHrEwKzbhTOwxZBQBfTQjQktKENQtBxXHTe71rUEWfEZQGg60/BC4BrRmh4qJjlJu3v4VIhC7SSHn1"
];
};
users.extraUsers.rj = {
isNormalUser = true;
extraGroups = ["wheel" "plugdev" "dialout"];
};
users.extraUsers.astro = {
isNormalUser = true;
extraGroups = ["plugdev" "dialout"];
shell = pkgs.bashInteractive;
};
users.extraUsers.nix = {
isNormalUser = true;
};
security.sudo.wheelNeedsPassword = false;
security.hideProcessInformation = true;
boot.kernel.sysctl."kernel.dmesg_restrict" = true;
services.udev.packages = [ pkgs.sane-backends ];
nix.distributedBuilds = true;
nix.buildMachines = [
{
hostName = "localhost";
maxJobs = 4;
system = "x86_64-linux";
supportedFeatures = ["big-parallel"];
}
{
hostName = "rpi-3";
sshUser = "nix";
sshKey = "/etc/nixos/secret/nix_id_rsa";
maxJobs = 1;
system = "aarch64-linux";
}
];
services.hydra = {
enable = true;
package = pkgs.hydra-unstable;
useSubstitutes = true;
hydraURL = "https://nixbld.m-labs.hk";
notificationSender = "hydra@m-labs.hk";
minimumDiskFree = 15; # in GB
minimumDiskFreeEvaluator = 1;
extraConfig =
''
binary_cache_secret_key_file = /etc/nixos/secret/nixbld.m-labs.hk-1
max_output_size = 10000000000
<runcommand>
job = web:web:web
command = [ $(jq '.buildStatus' < $HYDRA_JSON) = 0 ] && ln -sfn $(jq -r '.outputs[0].path' < $HYDRA_JSON) ${hydraWwwOutputs}/web
</runcommand>
<runcommand>
job = artiq:full:sipyco-manual-html
command = [ $(jq '.buildStatus' < $HYDRA_JSON) = 0 ] && ln -sfn $(jq -r '.outputs[0].path' < $HYDRA_JSON) ${hydraWwwOutputs}/sipyco-manual-html
</runcommand>
<runcommand>
job = artiq:full:sipyco-manual-latexpdf
command = [ $(jq '.buildStatus' < $HYDRA_JSON) = 0 ] && ln -sfn $(jq -r '.outputs[0].path' < $HYDRA_JSON) ${hydraWwwOutputs}/sipyco-manual-latexpdf
</runcommand>
<runcommand>
job = artiq:full-beta:artiq-manual-html
command = [ $(jq '.buildStatus' < $HYDRA_JSON) = 0 ] && ln -sfn $(jq -r '.outputs[0].path' < $HYDRA_JSON) ${hydraWwwOutputs}/artiq-manual-html-beta
</runcommand>
<runcommand>
job = artiq:full-beta:artiq-manual-latexpdf
command = [ $(jq '.buildStatus' < $HYDRA_JSON) = 0 ] && ln -sfn $(jq -r '.outputs[0].path' < $HYDRA_JSON) ${hydraWwwOutputs}/artiq-manual-latexpdf-beta
</runcommand>
<runcommand>
job = artiq:full-beta:conda-channel
command = [ $(jq '.buildStatus' < $HYDRA_JSON) = 0 ] && ln -sfn $(jq -r '.outputs[0].path' < $HYDRA_JSON) ${hydraWwwOutputs}/artiq-conda-channel-beta
</runcommand>
<runcommand>
job = artiq:full:artiq-manual-html
command = [ $(jq '.buildStatus' < $HYDRA_JSON) = 0 ] && ln -sfn $(jq -r '.outputs[0].path' < $HYDRA_JSON) ${hydraWwwOutputs}/artiq-manual-html
</runcommand>
<runcommand>
job = artiq:full:artiq-manual-latexpdf
command = [ $(jq '.buildStatus' < $HYDRA_JSON) = 0 ] && ln -sfn $(jq -r '.outputs[0].path' < $HYDRA_JSON) ${hydraWwwOutputs}/artiq-manual-latexpdf
</runcommand>
<runcommand>
job = artiq:full:conda-channel
command = [ $(jq '.buildStatus' < $HYDRA_JSON) = 0 ] && ln -sfn $(jq -r '.outputs[0].path' < $HYDRA_JSON) ${hydraWwwOutputs}/artiq-conda-channel
</runcommand>
'';
};
systemd.services.hydra-www-outputs-init = {
description = "Set up a hydra-owned directory for build outputs";
wantedBy = [ "multi-user.target" ];
requiredBy = [ "hydra-queue-runner.service" ];
before = [ "hydra-queue-runner.service" ];
serviceConfig = {
Type = "oneshot";
ExecStart = [ "${pkgs.coreutils}/bin/mkdir -p ${hydraWwwOutputs}" "${pkgs.coreutils}/bin/chown hydra-queue-runner:hydra ${hydraWwwOutputs}" ];
};
};
nix.extraOptions = ''
secret-key-files = /etc/nixos/secret/nixbld.m-labs.hk-1
'';
nix.sandboxPaths = ["/opt"];
services.munin-node.enable = true;
services.munin-cron = {
enable = true;
hosts = ''
[${config.networking.hostName}]
address localhost
'';
};
services.mlabs-backup.enable = true;
services.gitea = {
enable = true;
httpPort = 3001;
rootUrl = "https://git.m-labs.hk/";
appName = "M-Labs Git";
cookieSecure = true;
disableRegistration = true;
mailerPasswordFile = "/etc/nixos/secret/mailerpassword";
extraConfig =
''
[mailer]
ENABLED = true
HOST = ssl.serverraum.org:587
FROM = sysop@m-labs.hk
USER = sysop@m-labs.hk
[service]
ENABLE_NOTIFY_MAIL = true
[attachment]
ALLOWED_TYPES = */*
'';
};
systemd.tmpfiles.rules = [
"L+ '${config.services.gitea.stateDir}/custom/templates/home.tmpl' - - - - ${./gitea-home.tmpl}"
];
services.mattermost = {
enable = true;
siteUrl = "https://chat.m-labs.hk/";
mutableConfig = true;
};
services.matterbridge = {
enable = true;
configPath = "/etc/nixos/secret/matterbridge.toml";
};
nixpkgs.config.packageOverrides = super: let self = super.pkgs; in {
hydra-unstable = super.hydra-unstable.overrideAttrs(oa: {
patches = oa.patches or [] ++ [ ./hydra-conda.patch ./hydra-retry.patch ./hydra-unbreak-sysbuild.patch ];
hydraPath = oa.hydraPath + ":" + super.lib.makeBinPath [ super.jq ];
});
matterbridge = super.matterbridge.overrideAttrs(oa: {
patches = oa.patches or [] ++ [ ./matterbridge-disable-github.patch ];
});
};
security.acme.acceptTerms = true;
security.acme.email = "sb" + "@m-labs.hk";
security.acme.certs = {
"nixbld.m-labs.hk" = {
group = "nginx";
user = "nginx";
webroot = "/var/lib/acme/acme-challenge";
extraDomains = {
"m-labs.hk" = null;
"www.m-labs.hk" = null;
"conda.m-labs.hk" = null;
"lab.m-labs.hk" = null;
"git.m-labs.hk" = null;
"chat.m-labs.hk" = null;
"hooks.m-labs.hk" = null;
"forum.m-labs.hk" = null;
"perso.m-labs.hk" = null;
"nmigen.org" = null;
"www.nmigen.org" = null;
"openhardware.hk" = null;
"git.openhardware.hk" = null;
};
};
};
services.nginx = {
enable = true;
recommendedProxySettings = true;
recommendedGzipSettings = true;
virtualHosts = let
mainWebsite = {
addSSL = true;
useACMEHost = "nixbld.m-labs.hk";
root = "${hydraWwwOutputs}/web";
extraConfig = ''
error_page 404 /404.html;
'';
locations."^~ /fonts/".extraConfig = ''
expires 60d;
'';
locations."^~ /js/".extraConfig = ''
expires 60d;
'';
locations."/MathJax/" = {
alias = "/var/www/MathJax/";
extraConfig = ''
expires 60d;
'';
};
# legacy URLs, redirect to avoid breaking people's bookmarks
locations."/gateware.html".extraConfig = ''
return 301 /gateware/migen/;
'';
locations."/migen".extraConfig = ''
return 301 /gateware/migen/;
'';
locations."/artiq".extraConfig = ''
return 301 /experiment-control/artiq/;
'';
locations."/artiq/resources.html".extraConfig = ''
return 301 /experiment-control/resources/;
'';
# autogenerated manuals
locations."/artiq/sipyco-manual/" = {
alias = "${hydraWwwOutputs}/sipyco-manual-html/share/doc/sipyco-manual/html/";
};
locations."=/artiq/sipyco-manual.pdf" = {
alias = "${hydraWwwOutputs}/sipyco-manual-latexpdf/share/doc/sipyco-manual/SiPyCo.pdf";
};
locations."/artiq/manual-beta/" = {
alias = "${hydraWwwOutputs}/artiq-manual-html-beta/share/doc/artiq-manual/html/";
};
locations."=/artiq/manual-beta.pdf" = {
alias = "${hydraWwwOutputs}/artiq-manual-latexpdf-beta/share/doc/artiq-manual/ARTIQ.pdf";
};
locations."/artiq/manual/" = {
alias = "${hydraWwwOutputs}/artiq-manual-html/share/doc/artiq-manual/html/";
};
locations."=/artiq/manual.pdf" = {
alias = "${hydraWwwOutputs}/artiq-manual-latexpdf/share/doc/artiq-manual/ARTIQ.pdf";
};
# legacy content
locations."/migen/manual/" = {
alias = "/var/www/m-labs.hk.old/migen/manual/";
};
locations."/artiq/manual-release-4/" = {
alias = "/var/www/m-labs.hk.old/artiq/manual-release-4/";
};
locations."/artiq/manual-release-3/" = {
alias = "/var/www/m-labs.hk.old/artiq/manual-release-3/";
};
locations."/artiq/manual-release-2/" = {
alias = "/var/www/m-labs.hk.old/artiq/manual-release-2/";
};
};
in {
"m-labs.hk" = mainWebsite;
"www.m-labs.hk" = mainWebsite;
"lab.m-labs.hk" = {
addSSL = true;
useACMEHost = "nixbld.m-labs.hk";
locations."/munin/".alias = "/var/www/munin/";
locations."/munin".extraConfig = ''
auth_basic "Munin";
auth_basic_user_file /etc/nixos/secret/muninpasswd;
'';
locations."/homu/".proxyPass = "http://127.0.0.1:54856/";
};
"nixbld.m-labs.hk" = {
forceSSL = true;
useACMEHost = "nixbld.m-labs.hk";
locations."/".proxyPass = "http://127.0.0.1:3000";
};
"conda.m-labs.hk" = {
forceSSL = true;
useACMEHost = "nixbld.m-labs.hk";
locations."/artiq-beta/" = {
alias = "${hydraWwwOutputs}/artiq-conda-channel-beta/";
extraConfig = ''
autoindex on;
index bogus_index_file;
'';
};
locations."/artiq/" = {
alias = "${hydraWwwOutputs}/artiq-conda-channel/";
extraConfig = ''
autoindex on;
index bogus_index_file;
'';
};
};
"git.m-labs.hk" = {
forceSSL = true;
useACMEHost = "nixbld.m-labs.hk";
locations."/".proxyPass = "http://127.0.0.1:3001";
extraConfig = ''
client_max_body_size 300M;
'';
};
"chat.m-labs.hk" = {
forceSSL = true;
useACMEHost = "nixbld.m-labs.hk";
locations."/".proxyPass = "http://127.0.0.1:8065";
locations."~ /api/v[0-9]+/(users/)?websocket$".proxyPass = "http://127.0.0.1:8065";
locations."~ /api/v[0-9]+/(users/)?websocket$".proxyWebsockets = true;
};
"hooks.m-labs.hk" = {
forceSSL = true;
useACMEHost = "nixbld.m-labs.hk";
locations."/mattermost-github".extraConfig = ''
include ${pkgs.nginx}/conf/uwsgi_params;
uwsgi_pass unix:${config.services.uwsgi.runDir}/uwsgi-mgi.sock;
'';
locations."/rfq".extraConfig = ''
include ${pkgs.nginx}/conf/uwsgi_params;
uwsgi_pass unix:${config.services.uwsgi.runDir}/uwsgi-rfq.sock;
'';
};
"forum.m-labs.hk" = {
forceSSL = true;
useACMEHost = "nixbld.m-labs.hk";
root = "/var/www/flarum/public";
locations."~ \.php$".extraConfig = ''
fastcgi_pass unix:${config.services.phpfpm.pools.flarum.socket};
fastcgi_index index.php;
'';
extraConfig = ''
index index.php;
include /var/www/flarum/.nginx.conf;
'';
};
"perso.m-labs.hk" = {
addSSL = true;
useACMEHost = "nixbld.m-labs.hk";
root = "/var/www/perso";
};
"nmigen.org" = {
addSSL = true;
useACMEHost = "nixbld.m-labs.hk";
locations."/".extraConfig = ''
return 307 https://m-labs.hk/gateware/nmigen/;
'';
};
"www.nmigen.org" = {
addSSL = true;
useACMEHost = "nixbld.m-labs.hk";
locations."/".extraConfig = ''
return 307 https://m-labs.hk/gateware/nmigen/;
'';
};
"git.openhardware.hk" = {
forceSSL = true;
useACMEHost = "nixbld.m-labs.hk";
locations."/".proxyPass = "http://127.0.0.1:3002";
extraConfig = ''
client_max_body_size 300M;
'';
};
};
};
services.uwsgi = {
enable = true;
plugins = [ "python3" ];
instance = {
type = "emperor";
vassals = {
mattermostgithub = import ./mattermost-github-integration/uwsgi-config.nix { inherit config pkgs; };
rfq = import ./rfq/uwsgi-config.nix { inherit config pkgs; };
};
};
};
services.mysql = {
enable = true;
package = pkgs.mariadb;
};
services.phpfpm.pools.flarum = {
user = "nobody";
settings = {
"listen.owner" = "nginx";
"listen.group" = "nginx";
"listen.mode" = "0600";
"pm" = "dynamic";
"pm.max_children" = 5;
"pm.start_servers" = 2;
"pm.min_spare_servers" = 1;
"pm.max_spare_servers" = 3;
"pm.max_requests" = 500;
};
};
services.homu = {
enable = true;
config = "/etc/nixos/secret/homu.toml";
};
mailserver = {
enable = true;
localDnsResolver = false; # conflicts with dnsmasq
# Some mail servers do reverse DNS lookups to filter spam.
# Getting a proper reverse DNS record from ISP is difficult, so use whatever already exists.
fqdn = "42-200-147-171.static.imsbiz.com";
domains = [ "nmigen.org" ];
loginAccounts = (import /etc/nixos/secret/email_accounts.nix);
certificateScheme = 3;
};
security.acme.certs."${config.mailserver.fqdn}".extraDomains = {
"mail.nmigen.org" = null;
};
containers.openhardwarehk = {
autoStart = true;
config =
{ config, pkgs, ... }:
{
services.gitea = {
enable = true;
httpPort = 3002;
rootUrl = "https://git.openhardware.hk/";
appName = "Open Hardware HK";
cookieSecure = true;
disableRegistration = true;
extraConfig =
''
[attachment]
ALLOWED_TYPES = */*
'';
};
};
};
# This value determines the NixOS release with which your system is to be
# compatible, in order to avoid breaking some software such as database
# servers. You should change this only after NixOS release notes say you
# should.
system.stateVersion = "18.09"; # Did you read the comment?
}

View File

@ -0,0 +1,23 @@
{{template "base/head" .}}
<div class="home">
<div class="ui stackable middle very relaxed page grid">
<div class="sixteen wide center aligned centered column">
<div>
<img class="logo" src="{{AppSubUrl}}/img/gitea-lg.png" />
</div>
<div class="hero">
<h1 class="ui icon header title">
{{AppName}}
</h1>
</div>
</div>
</div>
<div class="ui stackable middle very relaxed page grid">
<div class="sixteen wide center column">
<p class="large">
Welcome! This Gitea instance is here to support projects related to <a href="https://m-labs.hk">M-Labs</a>. You may want to browse the <a href="https://git.m-labs.hk/M-Labs/">M-Labs organization</a> where many projects are located. If you would like an account (we give them to anyone who wants to contribute on projects related to Sinara, ARTIQ, nMigen, etc.), simply write a short email to sb@m-***.hk stating the username you would like to have.
</p>
</div>
</div>
</div>
{{template "base/footer" .}}

View File

@ -0,0 +1,13 @@
diff --git a/homu/git_helper.py b/homu/git_helper.py
index 0f70c69..f53fb57 100755
--- a/homu/git_helper.py
+++ b/homu/git_helper.py
@@ -7,7 +7,7 @@ SSH_KEY_FILE = os.path.join(os.path.dirname(__file__), '../cache/key')
def main():
- args = ['ssh', '-i', SSH_KEY_FILE, '-S', 'none'] + sys.argv[1:]
+ args = ['ssh', '-o', 'StrictHostKeyChecking=no', '-i', SSH_KEY_FILE, '-S', 'none'] + sys.argv[1:]
os.execvp('ssh', args)

View File

@ -0,0 +1,52 @@
{ config, pkgs, lib, ... }:
with lib;
let
homu = pkgs.callPackage ./pkg.nix {};
cfg = config.services.homu;
in
{
options.services.homu = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable the bot";
};
dbDir = mkOption {
type = types.str;
default = "/var/db/homu";
description = "Path to the database file (use the same path in config.toml)";
};
config = mkOption {
description = "Location of config.toml";
type = types.str;
};
};
config = mkIf cfg.enable {
users.users.homu = {
group = "homu";
home = cfg.dbDir;
createHome = true;
};
users.groups.homu = {};
systemd.services.homu = {
description = "Homu bot";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
Type = "simple";
ExecStart = "${homu}/bin/homu -c ${cfg.config}";
Restart = "always";
RestartSec = "5sec";
User = "homu";
Group = "homu";
};
};
};
}

View File

@ -0,0 +1,26 @@
diff --git a/homu/git_helper.py b/homu/git_helper.py
index 0f70c69..732230c 100755
--- a/homu/git_helper.py
+++ b/homu/git_helper.py
@@ -3,7 +3,7 @@
import sys
import os
-SSH_KEY_FILE = os.path.join(os.path.dirname(__file__), '../cache/key')
+SSH_KEY_FILE = os.path.expanduser("~/cache/key")
def main():
diff --git a/homu/main.py b/homu/main.py
index 16b60a2..a2e109a 100644
--- a/homu/main.py
+++ b/homu/main.py
@@ -649,7 +649,7 @@ def git_push(git_cmd, branch, state):
def init_local_git_cmds(repo_cfg, git_cfg):
- fpath = 'cache/{}/{}'.format(repo_cfg['owner'], repo_cfg['name'])
+ fpath = '{}/cache/{}/{}'.format(os.path.expanduser("~"), repo_cfg['owner'], repo_cfg['name'])
url = 'git@github.com:{}/{}.git'.format(repo_cfg['owner'], repo_cfg['name']) # noqa
if not os.path.exists(SSH_KEY_FILE):

View File

@ -0,0 +1,34 @@
{ python3Packages, python3, fetchFromGitHub, git, openssh }:
let
uritemplate_0_2_0 = python3Packages.github3_py.overrideAttrs(oa: rec {
version = "0.2.0";
src = python3Packages.fetchPypi {
pname = "uritemplate.py";
inherit version;
sha256 = "1pfk04pmnysz0383lwzgig8zqlwiv2n4pmq51f0mc60zz1jimq4g";
};
});
github3_py_0_9_6 = python3Packages.github3_py.overrideAttrs(oa: rec {
version = "0.9.6";
src = python3Packages.fetchPypi {
pname = "github3.py";
inherit version;
sha256 = "1i8xnh586z4kka7pjl7cy08fmzjs14c8jdp8ykb9jjpzsy2xncdq";
};
propagatedBuildInputs = [ python3Packages.requests uritemplate_0_2_0 ];
});
in
python3Packages.buildPythonApplication {
name = "homu";
src = fetchFromGitHub {
owner = "servo";
repo = "homu";
rev = "2ea53e76ebac3e5fa11bc39054b3cd4c42eff607";
sha256 = "1ih7s8zfbpq0qb9vqbxzr0r4s9ff52l4ipr916kwbck3ygliq3r9";
};
patches = [ ./patch-cache-directory.patch ./disable-ssh-host-keycheck.patch ];
postInstall = "chmod 755 $out/${python3.sitePackages}/homu/git_helper.py";
propagatedBuildInputs = [ github3_py_0_9_6 git openssh ] ++ (with python3Packages; [ toml jinja2 requests bottle waitress retrying ]);
checkPhase = "python -m unittest discover tests -v";
}

View File

@ -0,0 +1,47 @@
commit 5aa5f8d5742883d41d7278a2c8bc2c9a2ddfef45
Author: Sebastien Bourdeauducq <sb@m-labs.hk>
Date: Sun Apr 14 18:25:27 2019 +0800
add SVG icon for conda package
diff --git a/src/root/product-list.tt b/src/root/product-list.tt
index 298d0a66..85914bbd 100644
--- a/src/root/product-list.tt
+++ b/src/root/product-list.tt
@@ -157,6 +157,11 @@
<img src="[% c.uri_for("/static/images/debian.png") %]" alt="DEB" />
</td>
<td>Debian package</td>
+ [% CASE "conda" %]
+ <td>
+ <img src="[% c.uri_for("/static/images/conda.svg") %]" width="32" height="32" alt="Conda" />
+ </td>
+ <td>Conda package</td>
[% CASE "iso" %]
<td>
<img src="[% c.uri_for("/static/images/iso.png") %]" alt="ISO" />
diff --git a/src/root/static/images/conda.svg b/src/root/static/images/conda.svg
new file mode 100644
index 00000000..67859731
--- /dev/null
+++ b/src/root/static/images/conda.svg
@@ -0,0 +1,18 @@
+<svg width="128" height="128" style="enable-background:new 0 0 128 128;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <g id="g2">
+ <g>
+ <path d="M118.89,75.13c-1.31-2.72-3.46-5.53-6.97-7.33c-2.37-1.48-4.57-2.24-6.01-2.63 c1.53-5.6-0.64-10.06-3.69-13.39c-4.53-4.88-9.27-5.59-9.27-5.59l-0.01,0c1.56-3.03,2.15-6.54,1.36-9.99 c-1-4.26-3.29-6.94-6.31-8.73c-3.09-1.83-6.91-2.73-10.83-3.43c-1.88-0.34-9.81-1.45-13.1-6c-2.65-3.69-2.73-10.33-3.45-12.32 c-0.77-2.05-3.38-1.15-6.23,0.76c-3.33,2.22-10.23,9.35-12.89,16.49c-2.03,5.47-2.08,10.21-1.28,13.89 c-3.29,0.55-5.76,1.66-6.23,1.88c-0.16,0.05-0.32,0.1-0.49,0.17c-3.01,1.24-9.43,7.02-10.01,15.85c-0.2,3.14,0.21,6.31,1.2,9.26 c-3.94,1.1-6.22,2.54-6.26,2.57c-2,0.75-5.18,2.95-6.15,4.13c-1.97,2.38-3.34,5.21-4.15,8.18C6.35,85.36,7,92.71,10.14,98.67 c1.74,3.31,4.12,6.83,6.74,9.52c8.55,8.79,23.31,12.11,34.96,14.03c14.19,2.34,29.05,1.52,42.33-3.97 c19.92-8.22,25.22-21.44,26-25.17C121.92,84.77,119.8,77,118.89,75.13z" style="fill:#865D53;"/>
+ <g>
+ <g>
+ <ellipse cx="85.95" cy="66.39" rx="16.61" ry="15.5" style="fill:#FFFFFF;" transform="matrix(0.1106 -0.9939 0.9939 0.1106 10.453 144.4706)"/>
+ <path d="M92.63,66.36c-0.23,3.3-3.14,5.82-6.49,5.62c-3.36-0.19-5.9-3.04-5.67-6.34 c0.22-3.31,3.12-5.82,6.48-5.62C90.31,60.21,92.86,63.06,92.63,66.36" style="fill:#2F2F2F;"/>
+ </g>
+ <g>
+ <ellipse cx="42.46" cy="66.4" rx="15.5" ry="16.61" style="fill:#FFFFFF;" transform="matrix(0.9972 -0.0752 0.0752 0.9972 -4.8714 3.3796)"/>
+ <path d="M49.02,65.13c0.38,3.29-2.01,6.3-5.34,6.72c-3.34,0.43-6.36-1.9-6.74-5.18 c-0.4-3.29,1.99-6.3,5.33-6.73C45.6,59.52,48.63,61.85,49.02,65.13" style="fill:#2F2F2F;"/>
+ </g>
+ </g>
+ <path d="M87.35,89.46c-2.22-1.5-5.02-0.51-7.49,0c-6.9,1.42-12.95,1.48-15.86,1.48 c-2.91,0-8.96-0.06-15.86-1.48c-2.47-0.51-5.27-1.5-7.49,0c-2.82,1.9-0.74,8.74,3.7,13.36c2.68,2.79,9.07,8.21,19.66,8.21 c10.58,0,16.97-5.42,19.66-8.21C88.09,98.2,90.17,91.37,87.35,89.46z" style="fill:#ED6D31;"/>
+ </g>
+ </g>
+</svg>
\ No newline at end of file

View File

@ -0,0 +1,19 @@
commit 86bf81c0b8a51bffa4b4b566e1caaac6f0e041d3
Author: Sebastien Bourdeauducq <sb@m-labs.hk>
Date: Thu Mar 14 17:45:32 2019 +0800
add option to disable retries on transient failures
diff --git a/src/hydra-queue-runner/build-remote.cc b/src/hydra-queue-runner/build-remote.cc
index 69c430eb..bdbc808d 100644
--- a/src/hydra-queue-runner/build-remote.cc
+++ b/src/hydra-queue-runner/build-remote.cc
@@ -344,7 +344,7 @@ void State::buildRemote(ref<Store> destStore,
break;
case BuildResult::TransientFailure:
result.stepStatus = bsFailed;
- result.canRetry = true;
+ result.canRetry = get(step->drv->env, "__hydraRetry").value_or("1") == "1";
result.errorMsg = "";
break;
case BuildResult::TimedOut:

View File

@ -0,0 +1,25 @@
diff --git a/src/lib/Hydra/Schema/Builds.pm b/src/lib/Hydra/Schema/Builds.pm
index d4334300..014d07ce 100644
--- a/src/lib/Hydra/Schema/Builds.pm
+++ b/src/lib/Hydra/Schema/Builds.pm
@@ -608,6 +608,7 @@ makeQueries('', "");
makeQueries('ForProject', "and project = ?");
makeQueries('ForJobset', "and jobset_id = ?");
makeQueries('ForJob', "and jobset_id = ? and job = ?");
+makeQueries('ForJobName', "and jobset_id = (select id from jobsets j where j.name = ?) and job = ?");
my %hint = (
diff --git a/src/script/hydra-eval-jobset b/src/script/hydra-eval-jobset
index ea336bfc..2f208418 100755
--- a/src/script/hydra-eval-jobset
+++ b/src/script/hydra-eval-jobset
@@ -142,7 +142,7 @@ sub fetchInputSystemBuild {
$projectName ||= $project->name;
$jobsetName ||= $jobset->name;
- my @latestBuilds = $db->resultset('LatestSucceededForJob')
+ my @latestBuilds = $db->resultset('LatestSucceededForJobName')
->search({}, {bind => [$jobsetName, $jobName]});
my @validBuilds = ();

View File

@ -0,0 +1,15 @@
diff --git a/bridge/mattermost/helpers.go b/bridge/mattermost/helpers.go
index 14b7469d..d9b77bdf 100644
--- a/bridge/mattermost/helpers.go
+++ b/bridge/mattermost/helpers.go
@@ -206,6 +206,10 @@ func (b *Bmattermost) skipMessage(message *matterclient.Message) bool {
return true
}
+ if message.Username == "github" {
+ return true
+ }
+
// if the message has reactions don't repost it (for now, until we can correlate reaction with message)
if message.Post.HasReactions {
return true

View File

@ -0,0 +1,32 @@
{ fetchFromGitHub, python3Packages }:
with python3Packages;
buildPythonPackage rec {
pname = "mattermost-github-integration";
version = "0.0.0-unstable";
src = fetchFromGitHub {
owner = "softdevteam";
repo = "mattermost-github-integration";
rev = "1124a0ff233b50ed6070cb84cfffd128ad219831";
sha256 = "1hfvjaxjhliy8sv9j3616fkdwd2jqhfsj9ai7ggx88zhxknrfx85";
};
propagatedBuildInputs = [
appdirs
click
flask
itsdangerous
jinja2
markupsafe
olefile
packaging
pillow
pyparsing
requests
six
werkzeug
];
checkInputs = [
pytest
];
doCheck = true;
}

View File

@ -0,0 +1,15 @@
{ config, pkgs }:
let
pkg = pkgs.callPackage ./pkg.nix {};
in {
type = "normal";
pythonPackages = self: [ pkg ];
module = "mattermostgithub:app";
env = [
"MGI_CONFIG_FILE=${./../secret/mattermost-github-integration.py}"
];
socket = "${config.services.uwsgi.runDir}/uwsgi-mgi.sock";
# allow access from nginx
chmod-socket = 666;
}

View File

@ -0,0 +1,93 @@
{ config, pkgs, lib, ... }:
with lib;
let
notifico = (pkgs.callPackage ./pkg.nix {})
.overrideAttrs (attrs: {
buildInputs = attrs.buildInputs ++ [ pkgs.makeWrapper ];
# Extend the module path so that local_config.py can be found
postInstall = ''
${attrs.postInstall}
wrapProgram $out/bin/notifico \
--set PYTHONPATH "$${PYTHONPATH}:${cfg.dbDir}"
'';
});
cfg = config.services.notifico;
in
{
options.services.notifico = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable the commit notification service";
};
enableLocalRedis = mkOption {
type = types.bool;
default = true;
description = "Enable a local Redis server";
};
dbDir = mkOption {
type = types.str;
default = "/var/db/notifico";
description = "Home directory and location of the database file";
};
config = mkOption {
description = "Path to local_config.py, https://github.com/notifico/notifico/raw/master/notifico/config.py";
type = types.str;
};
};
config = mkIf cfg.enable {
users.users.notifico = {
group = "notifico";
home = cfg.dbDir;
createHome = true;
};
users.groups.notifico = {};
services.redis = mkIf cfg.enableLocalRedis {
enable = true;
bind = "127.0.0.1";
};
systemd.services =
let
User = "notifico";
Group = "notifico";
WorkingDirectory = "${cfg.dbDir}";
ExecStartPre = [
"${pkgs.coreutils}/bin/rm -f local_config.pyc"
"${pkgs.coreutils}/bin/ln -sf ${cfg.config} local_config.py"
];
notifico-init = {
description = "Notifico initialization";
serviceConfig = {
inherit User Group WorkingDirectory ExecStartPre;
Type = "oneshot";
ExecStart = "${notifico}/bin/notifico init";
};
};
notificoService = component: {
description = "Notifico ${component}";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" "notifico-init.service" ];
requires = [ "notifico-init.service" ];
serviceConfig = {
inherit User Group WorkingDirectory ExecStartPre;
Type = "simple";
ExecStart = "${notifico}/bin/notifico ${component}";
Restart = "always";
RestartSec = "5sec";
};
};
in {
inherit notifico-init;
notifico-www = notificoService "www";
notifico-worker = notificoService "worker";
notifico-bots = notificoService "bots";
};
};
}

View File

@ -0,0 +1,107 @@
{ python2Packages, python2, fetchFromGitHub, fetchurl }:
let
Flask-Gravatar = python2Packages.buildPythonPackage {
name = "Flask-Gravatar";
src = python2Packages.fetchPypi {
pname = "Flask-Gravatar";
version = "0.5.0";
sha256 = "1qb2ylirjajdqsmldhwfdhf8i86k7vlh3y4gnqfqj4n6q8qmyrk0";
};
propagatedBuildInputs = with python2Packages; [
pytestrunner
flask
];
checkInputs = with python2Packages; [
check-manifest
coverage
isort
pydocstyle
pytestcache
pytestcov
pytestpep8
pytest
pygments
];
};
utopia = python2Packages.buildPythonPackage {
name = "utopia";
src = fetchFromGitHub {
owner = "notifico";
repo = "utopia";
rev = "70293ed5e1ca55232e0fae71061e7e9b9b29be6f";
sha256 = "11cnh9l4d9jlhafnfis9si6kgk9zsdd5439qnhxh6dca3x4a986q";
};
propagatedBuildInputs = with python2Packages; [
gevent
blinker
];
doCheck = false;
};
Flask-WTF = python2Packages.flask_wtf.overrideAttrs(oa: rec {
version = "0.8.4";
src = python2Packages.fetchPypi {
pname = "Flask-WTF";
inherit version;
sha256 = "1khbwmlrcnk9f46f7kf531n06pkyfs6nc8fk273js9mj2igngg2y";
};
});
Flask-XML-RPC = python2Packages.flask_wtf.overrideAttrs(oa: rec {
version = "0.1.2";
src = python2Packages.fetchPypi {
pname = "Flask-XML-RPC";
inherit version;
sha256 = "1dwalj7pc5iid9l1k50q5mllirnn9f5s7jq54a66x48a4j179p2a";
};
});
in
python2Packages.buildPythonApplication {
name = "notifico";
src = fetchFromGitHub {
owner = "notifico";
repo = "notifico";
rev = "6af849e4c75dff4d740051676f5a2093a44efcee";
sha256 = "18jifqdvjy4x5s1bh7vx501pin52g4n3hhw1z4m2c0h512z4spdr";
};
patches = [
(fetchurl {
url = https://github.com/whitequark/notifico/commit/22b582fad6cb97af6f7437e8462d720ddacc42ef.patch;
sha256 = "0w8i8hf1r8b0p1y1zn9vyvnyi20qp120aiyalqymhsxsh17mma52";
})
];
propagatedBuildInputs = with python2Packages; [
flask
Flask-WTF
Flask-Gravatar
flask_sqlalchemy
Flask-XML-RPC
flask_mail
flask-caching
Fabric
sqlalchemy
utopia
gevent
oauth2
redis
gunicorn
requests
PyGithub
xmltodict
unidecode
raven
blinker
docopt
celery
];
postInstall = ''
mkdir $out/bin
cat << EOF > $out/bin/notifico
#!${python2}/bin/python
import sys
from notifico.__main__ import main
sys.exit(main(sys.argv))
EOF
chmod +x $out/bin/notifico
'';
}

View File

@ -0,0 +1,9 @@
{ python3Packages, runCommand }:
# Note: we do not use fetchgit but a local copy instead to avoid
# chicken-and-egg problem if reinstalling nixbld.m-labs.hk from scratch.
with python3Packages; buildPythonPackage rec {
name = "rfq";
src = ./src;
propagatedBuildInputs = [ flask flask_mail python-dotenv ];
}

View File

@ -0,0 +1,10 @@
blinker
click
Flask
Flask-Mail
itsdangerous
Jinja2
MarkupSafe
python-dotenv
six
Werkzeug

View File

@ -0,0 +1,75 @@
from os import getenv
from dotenv import load_dotenv
from flask import Flask
from flask import current_app
from flask import json
from flask import jsonify
from flask import make_response
from flask import request
from flask_mail import Mail
from flask_mail import Message
from werkzeug.middleware.proxy_fix import ProxyFix
load_dotenv()
app = Flask(__name__)
app.config.update(
DEBUG=getenv("FLASK_DEBUG") == "True",
MAIL_SERVER=getenv("FLASK_MAIL_SERVER"),
MAIL_PORT=getenv("FLASK_MAIL_PORT"),
MAIL_USE_SSL=getenv("FLASK_MAIL_USE_SSL"),
MAIL_DEBUG=False,
MAIL_USERNAME=getenv("FLASK_MAIL_USERNAME"),
MAIL_PASSWORD=getenv("FLASK_MAIL_PASSWORD"),
MAIL_RECIPIENT=getenv("FLASK_MAIL_RECIPIENT"),
MAIL_SENDER=getenv("FLASK_MAIL_SENDER")
)
app.wsgi_app = ProxyFix(app.wsgi_app)
mail = Mail(app)
@app.after_request
def after(response):
response.headers["Access-Control-Allow-Origin"] = "*"
response.headers["Access-Control-Allow-Headers"] = "*"
return response
@app.route("/rfq", methods=["POST"])
def send_rfq():
payload = request.json
payload = json.loads(json.htmlsafe_dumps(payload))
if payload is None:
resp = jsonify(error="invalid data")
return make_response(resp, 400)
if "email" not in payload:
resp = jsonify(error="missing email")
return make_response(resp, 400)
if "note" not in payload:
resp = jsonify(error="missing note")
return make_response(resp, 400)
if "configuration" not in payload:
resp = jsonify(error="missing configuration")
return make_response(resp, 400)
sender = current_app.config["MAIL_SENDER"]
recipient = current_app.config["MAIL_RECIPIENT"]
msg = Message(
"RFQ for Sinara hardware from {}".format(payload["email"]),
sender=sender,
recipients=[recipient, payload["email"]])
msg.body = ("From: {}\nConfiguration: {}\nNote: {}"
.format(payload["email"], payload["configuration"], payload["note"]))
with mail.connect() as conn:
conn.send(msg)
return jsonify("ok")

View File

@ -0,0 +1,14 @@
from setuptools import setup
from setuptools import find_packages
setup(
name='rfq',
version='1.0.0',
packages=find_packages(),
zip_safe=False,
install_requires=[
'Flask',
'Flask-Mail',
'python-dotenv'
]
)

View File

@ -0,0 +1,21 @@
{ config, pkgs }:
let
pkg = pkgs.callPackage ./pkg.nix {};
in {
type = "normal";
pythonPackages = self: [ pkg ];
module = "rfq:app";
env = [
"FLASK_MAIL_SERVER=ssl.serverraum.org"
"FLASK_MAIL_PORT=465"
"FLASK_MAIL_USE_SSL=True"
"FLASK_MAIL_USERNAME=sales@m-labs.hk"
"FLASK_MAIL_PASSWORD=${import /etc/nixos/secret/sales_password.nix}"
"FLASK_MAIL_RECIPIENT=sales@m-labs.hk"
"FLASK_MAIL_SENDER=sales@m-labs.hk"
];
socket = "${config.services.uwsgi.runDir}/uwsgi-rfq.sock";
# allow access from nginx
chmod-socket = 666;
}

View File

@ -0,0 +1,11 @@
-rw------- 1 root root backup-passphrase
-rw------- 1 root root email_accounts.nix
-rw------- 1 homu homu homu.toml
-rw-rw---- 1 gitea gitea mailerpassword
-rw------- 1 matterbridge matterbridge matterbridge.toml
-rw------- 1 uwsgi uwsgi mattermost-github-integration.py
-rw------- 1 nginx nginx muninpasswd
-rw-rw---- 1 hydra hydra nixbld.m-labs.hk-1
-rw-rw---- 1 hydra hydra nix_id_rsa
-rw------- 1 root root rclone.conf
-rw------- 1 root root wifi_password.nix

View File

@ -0,0 +1,33 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, ... }:
{
imports =
[ <nixpkgs/nixos/modules/installer/scan/not-detected.nix>
];
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-uuid/89db7e45-7b7e-44ec-bebc-a02935cc5ba0";
fsType = "ext4";
};
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/060C-8772";
fsType = "vfat";
};
swapDevices = [ ];
nix.maxJobs = lib.mkDefault 16;
powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
}

View File

@ -0,0 +1,29 @@
{ config, lib, pkgs, ... }:
{
imports =
[ <nixpkgs/nixos/modules/installer/scan/not-detected.nix>
];
boot.initrd.availableKernelModules = [ "ata_generic" "uhci_hcd" "ehci_pci" "ahci" "usb_storage" "usbhid" "floppy" "sd_mod" "sr_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-uuid/35d9c50c-e479-43a9-8324-b8ded5b71844";
fsType = "ext4";
};
swapDevices =
[ { device = "/dev/disk/by-uuid/d8480389-c558-4c46-a58f-00207315dbdd"; }
];
nix.maxJobs = lib.mkDefault 2;
boot.loader.grub.enable = true;
boot.loader.grub.version = 2;
boot.loader.grub.device = "/dev/sda";
services.xserver.videoDrivers = ["intel"];
}

58
nixops/common-users.nix Normal file
View File

@ -0,0 +1,58 @@
{ pkgs, ... }:
{
sb = {
isNormalUser = true;
extraGroups = ["wheel" "plugdev" "dialout"];
openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCyPk5WyFoWSvF4ozehxcVBoZ+UHgrI7VW/OoQfFFwIQe0qvetUZBMZwR2FwkLPAMZV8zz1v4EfncudEkVghy4P+/YVLlDjqDq9zwZnh8Nd/ifu84wmcNWHT2UcqnhjniCdshL8a44memzABnxfLLv+sXhP2x32cJAamo5y6fukr2qLp2jbXzR+3sv3klE0ruUXis/BR1lLqNJEYP8jB6fLn2sLKinnZPfn6DwVOk10mGeQsdME/eGl3phpjhODH9JW5V2V5nJBbC0rBnq+78dyArKVqjPSmIcSy72DEIpTctnMEN1W34BGrnsDd5Xd/DKxKxHKTMCHtZRwLC2X0NWN"
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCMALVC8RDTHec+PC8y1s3tcpUAODgq6DEzQdHDf/cyvDMfmCaPiMxfIdmkns5lMa03hymIfSmLUF0jFFDc7biRp7uf9AAXNsrTmplHii0l0McuOOZGlSdZM4eL817P7UwJqFMxJyFXDjkubhQiX6kp25Kfuj/zLnupRCaiDvE7ho/xay6Jrv0XLz935TPDwkc7W1asLIvsZLheB+sRz9SMOb9gtrvk5WXZl5JTOFOLu+JaRwQLHL/xdcHJTOod7tqHYfpoC5JHrEwKzbhTOwxZBQBfTQjQktKENQtBxXHTe71rUEWfEZQGg60/BC4BrRmh4qJjlJu3v4VIhC7SSHn1"
];
};
rj = {
isNormalUser = true;
extraGroups = ["wheel" "plugdev" "dialout"];
openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDZOitjBp9uB+Hldt5M040Jq/3rVFBbw40Xemkau3BLvMn8TzkJs5NLrlNa4vcwFecA/nh7aPzdGHc1b/E2EYCfM29oo/oVBJsp/L66YUbnYrneFNVp8Ccw3tZPPAiADjLZWta0JQLVVY6Dqtt0SH/oU5jC1F1qCa+krWqkKAVE8rfxYVspBGagxlpZuE83UC0j2yXrbHq6ZrAW917wXUEpcIR+mKalDM2Aa1FAZZH9upty2yysyOHh6/ljurz6tMRqjzjdJtVJ2YXf4GZpIuYcxCU1kvLKPLN0MZA+aXtraCGmEdjdx38sfRqHBnffXhCkJIo+W4aw4Xae2xplmGeInWqnUwsWxuVJENdPfbBOBdMRuFemuPZdmBcohczDygOC3h+oljBvQF6Ffyvk38pVLbd91p1+qgvtW7OcXTUjm17K1Oa54RGUcm1W2w3yJKCc8RQZXlwVtneTX0VoK39LC1yWfyMBg8sWeT66oE+v2CCEzsB0A1xZx/dK0r5bdfv8uNAH5d8RGL++zNEVrsA4iZF6FEeXgaoje3tKMqKTgOx4EDh93ie2rv7oE8xrPL5g0vb8wBQ1Kf4rukd7FPVu+E4+W5oSnQ42BJ9Z4sFCLQ9Dnhbg4VvREzAe9rVzfAG368iCVKkFcSq+AaLquqrBpwLbz10V3GLDARlF2IZZGQ== rj@lab.m-labs.hk"
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC27krR8G8Pb59YuYm7+X2mmNnVdk/t9myYgO8LH0zfb2MeeXX5+90nW9kMjKflJss/oLl8dkD85jbJ0fRbRkfJd20pGCqCUuYAbYKkowigFVEkbrbWSLkmf+clRjzJOuBuUA0uq0XKS17uMC3qhu+dDdBOAIKb3L83NfVE8p8Pjb4BPktQrdxefM43/x4jTMuc7tgxVmTOEge3+rmVPK2GnLkUBgBn8b6S+9ElPd63HXI5J5f61v21l5N9V0mhTu1pv6PiDRdFIlFDK9dLVZcZ2qlzpKmCnFrOoreBEgre44SpfFe5/MMItxvWiVsj/rij/rHZZiol1k7JiQCnEHeCCbjjvcBBka5HxZgcb3vBZVceTOawrmjbdbA2dq35sUptz/bEgdZ1UVCmVpWsdROAlEDBmSSbcVwxzcvhoKnkpbuP4Q0V3tVKSLW053ADFNB4frtwY5nAZfsVErFLLphjwb8nlyJoDRNapQrn5syEiW0ligX2AAskZTYIl2A5AYyWPrmX6HJOPqZGatMU3qQiRMxs+hFqhyyCmBgl0kcsgW09MBKtJWk1Fbii98MHqgRUN9R7AUiYy5p78Pnv9DC8DT8Ubl9zoP0g5d40P9NGK2LAhMxLXvtckJ4ERqbSEcNZJw+q4jBrOHnMTz+NLdAUiEtru+6T2OdhaHv+eiNlFQ== robert-jordens-rsa4096"
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCUdbne3NtIG+iy/jer76/OY+IksuS3BDLSXPnWrGejWnig9h+L6sUV0lEVI6dqp+W/b8jWqPB8nh5S0NZsCd3Ta3Go82k/SPPkh9lB2PpfquhCjLnmC/RNc3TgC4FuiS+NZHqXaTggYHubNwEK+8gynMqkMQXjOGU02U0CtUfsYdAm75AW60DySZCRNwOcU0Ndpn1UCpha7fL1k179Dd/OtArkYsIL24ohlfxFeOB3jGYQK6ATmzbvCRjwIKXcyECuajWwfnDg9FtDWrqHNzu5dJlvmxoWm8zCDgMj53uiA7TjujQN81MYrIJNeEwSr5jXQMqzA3mzlk4k3Z0qs3TP robert-jordens-64FEFBAF-4D0749B2-rsa2048"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMUaB2G1jexxfkdlly3fdWslH54/s/bOuvk9AxqpjtAY robert-jordens-ed25519"
];
};
harry = {
isNormalUser = true;
extraGroups = ["plugdev" "dialout" "wireshark"];
openssh.authorizedKeys.keys = [
"ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBDcPNCgtdz8erFPRrAwCr4JrkeYXJUUvoRBgP0X2HlzJgDe1Inuo6sC6CGcO3IXbf4MwVA9XEp8BYPHARVeEHhufg/0wnIABLx2GcK99yxOLDUe4h/3YwtqvOcqHEsDx7w=="
];
};
astro = {
isNormalUser = true;
extraGroups = ["plugdev" "dialout"];
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGJJTSJdpDh82486uPiMhhyhnci4tScp5uUe7156MBC8 a"
];
shell = pkgs.bashInteractive;
};
pca006132 = {
isNormalUser = true;
extraGroups = ["plugdev" "dialout"];
openssh.authorizedKeys.keys = [
"ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBE/sPOOiw3843+rrcYV2pOVkffNc1xsOgnuCUmy1Fa2VF8x9kqmgQv61sxsuKRkKKoinvqrASxLkWVd6nkiiDuEISibEXs8r1BwuT05cS7RkEhCakSMZ6y/iqOtjt2bx+A=="
];
};
occheung = {
isNormalUser = true;
extraGroups = ["plugdev" "dialout"];
openssh.authorizedKeys.keys = [
"ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPEvmWmxpFpMgp5fpjKud8ev0cyf/+X5fEpQt/YD/+u4mbvZYPE300DLqQ0h/qjgvaGMz1ndf4idYnRdy+plJEC/+hmlRW5NlcpAr3S/LYAisacgKToFVl+MlBo+emS9Ig=="
];
};
dsleung = {
isNormalUser = true;
extraGroups = ["plugdev" "dialout"];
openssh.authorizedKeys.keys = [
"ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBDbE7HzZKSwGbgRnzwrzCzb3gZKLSritwnEpHS4sa9oXJ5oLFkuFZOpPYDeiMlbUJ9jCk5FRmkLYIkrbz06SUr7P/eUjxu79ENi3RhfVu+ZrrPvgkhKvM/CiXvw3xCOu0w=="
];
};
}

149
nixops/desktop.nix Normal file
View File

@ -0,0 +1,149 @@
{ host }:
{ config, pkgs, ... }:
let
m-labs = import (fetchTarball https://nixbld.m-labs.hk/channel/custom/artiq/full/artiq-full/nixexprs.tar.xz) { inherit pkgs; };
pkgs-unstable = import (fetchTarball https://github.com/NixOS/nixpkgs/archive/master.tar.gz) {};
in
{
deployment.targetHost = host;
disabledModules = [ "security/pam.nix" ];
imports =
[
(./. + "/${host}-hardware-configuration.nix")
./pam_p11
];
networking.hostName = host;
time.timeZone = "Asia/Hong_Kong";
# List packages installed in system profile. To search, run:
# $ nix search wget
nixpkgs.config.allowUnfree = true;
environment.systemPackages = with pkgs; [
opensc yubikey-manager yubikey-manager-qt
wget vim gitAndTools.gitFull firefox chromium thunderbird hexchat
usbutils pciutils file lm_sensors audacious acpi
gimp imagemagick
(python3.withPackages(ps: with ps; [ numpy scipy matplotlib qtconsole regex ]))
mosh psmisc libreoffice-fresh
gtkwave telnet unzip zip gnupg
gnome3.gnome-tweaks
jq sublime3 rink qemu_kvm
tmux xc3sprog m-labs.openocd screen gdb minicom picocom tigervnc
emacs bat ripgrep
pkgs-unstable.rust-analyzer
(pkgs-unstable.vscode-with-extensions.override {
vscodeExtensions = [
pkgs-unstable.vscode-extensions.matklad.rust-analyzer
];
})
(import ./fish-nix-shell)
];
programs.wireshark.enable = true;
services.openssh.enable = true;
services.openssh.forwardX11 = true;
services.openssh.passwordAuthentication = false;
hardware.u2f.enable = true;
services.pcscd.enable = true;
programs.ssh.extraConfig =
''
PKCS11Provider "${pkgs.opensc}/lib/opensc-pkcs11.so"
'';
programs.ssh.startAgent = true;
services.gnome3.gnome-keyring.enable = pkgs.lib.mkForce false;
programs.ssh.agentPKCS11Whitelist = "${pkgs.opensc}/lib/opensc-pkcs11.so";
security.pam.p11.enable = true;
# Enable CUPS to print documents.
services.printing = {
enable = true;
extraConf =
''
Browsing Off
BrowseLocalProtocols none
'';
browsedConf =
''
BrowseRemoteProtocols none
BrowseProtocols none
'';
};
services.avahi = {
enable = true;
nssmdns = true;
};
# Enable sound.
sound.enable = true;
hardware.pulseaudio = {
enable = true;
extraModules = [ pkgs.pulseaudio-modules-bt ];
package = pkgs.pulseaudioFull;
};
i18n.inputMethod = {
enabled = "fcitx";
fcitx.engines = with pkgs.fcitx-engines; [ table-extra m17n ];
};
fonts.fonts = [ pkgs.noto-fonts pkgs.noto-fonts-cjk pkgs.noto-fonts-emoji pkgs.noto-fonts-extra pkgs.emacs-all-the-icons-fonts ];
# Enable the X11 windowing system.
services.xserver.enable = true;
services.xserver.layout = "us";
services.xserver.xkbOptions = "eurosign:e";
# Enable touchpad support.
services.xserver.libinput.enable = true;
services.xserver.displayManager.gdm.enable = true;
services.xserver.displayManager.gdm.autoSuspend = false;
powerManagement.enable = false;
services.xserver.desktopManager.gnome3.enable = true;
environment.gnome3.excludePackages = [ pkgs.epiphany pkgs.gnome3.geary ];
hardware.bluetooth.enable = true;
programs.fish.enable = true;
programs.fish.promptInit = ''
fish-nix-shell --info-right | source
'';
users.mutableUsers = false;
users.defaultUserShell = pkgs.fish;
users.extraGroups.plugdev = { };
users.extraUsers = import ./common-users.nix { inherit pkgs; };
security.sudo.wheelNeedsPassword = false;
services.udev.packages = [ m-labs.openocd ];
services.udev.extraRules = ''
# leaf maple
SUBSYSTEM=="usb", ATTRS{idVendor}=="1eaf", ATTRS{idProduct}=="0003", MODE="0660", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="1eaf", ATTRS{idProduct}=="0004", MODE="0660", GROUP="plugdev"
# glasgow
SUBSYSTEM=="usb", ATTRS{idVendor}=="20b7", ATTRS{idProduct}=="9db1", MODE="0660", GROUP="plugdev"
# hackrf
SUBSYSTEM=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="6089", MODE="0660", GROUP="plugdev"
# bladerf
SUBSYSTEM=="usb", ATTRS{idVendor}=="2cf0", ATTRS{idProduct}=="5250", MODE="0660", GROUP="plugdev"
# personal measurement device
SUBSYSTEM=="usb", ATTRS{idVendor}=="09db", ATTRS{idProduct}=="007a", MODE="0660", GROUP="plugdev"
# saleae
SUBSYSTEM=="usb", ATTRS{idVendor}=="0925", ATTRS{idProduct}=="3881", MODE="0660", GROUP="plugdev"
# ocean optics
SUBSYSTEM=="usb", ATTRS{idVendor}=="2457", ATTRS{idProduct}=="1002", MODE="0660", GROUP="plugdev"
# yubikey
SUBSYSTEM=="usb", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0116", MODE="0660", GROUP="plugdev"
'';
nix.binaryCachePublicKeys = ["nixbld.m-labs.hk-1:5aSRVA5b320xbNvu30tqxVPXpld73bhtOeH6uAjRyHc="];
nix.binaryCaches = ["https://nixbld.m-labs.hk" "https://cache.nixos.org"];
nix.sandboxPaths = ["/opt"];
# This value determines the NixOS release with which your system is to be
# compatible, in order to avoid breaking some software such as database
# servers. You should change this only after NixOS release notes say you
# should.
system.stateVersion = "19.03"; # Did you read the comment?
}

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 haslersn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,50 @@
# fish-nix-shell
fish support for the *nix-shell* environment of the Nix package manager.
## Installation
### Installation in the user environment
Execute
```
nix-env -if https://github.com/haslersn/fish-nix-shell/archive/master.tar.gz
```
and add the following to your *~/.config/fish/config.fish*. Create it if it doesn't exist.
```
fish-nix-shell --info-right | source
```
### System-wide installation
Add the package to your */etc/nixos/configuration.nix*:
```
environment.systemPackages = with pkgs; [
#
# Other packages here ...
#
(import (fetchGit "https://github.com/haslersn/fish-nix-shell"))
];
```
and then execute: `sudo nixos-rebuild switch`
If you want to configure it system-wide, also add:
```
programs.fish.enable = true;
programs.fish.promptInit = ''
fish-nix-shell --info-right | source
'';
```
## Flags
The `fish-nix-shell` command **optionally** takes the following flags:
| Flag | Meaning |
| - | - |
| `--info-right` | While in a *fish-nix-shell*, display information about the loaded packages at the right.

View File

@ -0,0 +1,34 @@
#!/bin/sh
function init_fish () {
cat <<EOF
# Overwrite the nix-shell command
function nix-shell
fish-nix-shell-wrapper \$argv
set -gx FISH_NIX_SHELL_EXIT_STATUS \$status
end
EOF
for arg in "$@"; do
case "$arg" in
--info-right)
cat <<EOF
# Print additional information inside a nix-shell environment
function fish_right_prompt
nix-shell-info
set -e FISH_NIX_SHELL_EXIT_STATUS
end
EOF
;;
*) exit 1;;
esac
done
}
cat <<EOF
# If you see this output, you probably forgot to pipe this output into 'source':
# fish-nix-shell $@ | source
EOF
init_fish "$@"

View File

@ -0,0 +1,22 @@
#!/bin/sh
fns () {
pkgs=$FISH_NIX_SHELL_PKGS
for arg in "$@"; do
if [[ $arg == -* ]]; then
pkg=
if [[ $arg == --pure ]] || [[ $arg == --command ]] || [[ $arg == --run ]]; then
command nix-shell $@
return
elif [[ $arg == -p ]] || [[ $arg == --packages ]]; then
pkg=1
fi
elif [[ $pkg == 1 ]]; then
pkgs+=" "$arg
fi
done
if [[ -n $name ]] && [[ $name != shell ]]; then
pkgs+=" "$name
fi
env FISH_NIX_SHELL_PKGS="$pkgs" nix-shell "$@" --command fish
}
fns "$@"

View File

@ -0,0 +1,21 @@
#!/bin/sh
if [[ $IN_NIX_SHELL != "" ]]; then
printf "\033[1;32m"
output=$(echo $FISH_NIX_SHELL_PKGS | xargs)
if [[ -n $name ]] && [[ $name != shell ]]; then
output+=" "$name
fi
if [[ -n $output ]]; then
output=$(echo $output $additional_pkgs | tr ' ' '\n' | sort -u | tr '\n' ' ' | xargs)
printf "$output "
else
printf "[unknown nix-shell] "
fi
printf "\033[0m"
elif [[ $FISH_NIX_SHELL_EXIT_STATUS ]]; then
if [[ $FISH_NIX_SHELL_EXIT_STATUS == 0 ]]; then
printf "\033[1;36mexited nix-shell \033[0m"
else
printf "\033[1;31mERROR \033[0m"
fi
fi

View File

@ -0,0 +1,16 @@
with import <nixpkgs> {}; stdenv.mkDerivation rec {
name = "fish-nix-shell";
src = fetchGit "https://github.com/haslersn/fish-nix-shell";
nativeBuildInputs = [ makeWrapper ];
installPhase = ''
mkdir -p $out
cp LICENSE $out
cp -r bin $out
wrapProgram $out/bin/fish-nix-shell
wrapProgram $out/bin/fish-nix-shell-wrapper --prefix PATH ":" ${fish}/bin
wrapProgram $out/bin/nix-shell-info
'';
meta.description = "fish support for the nix-shell environment of the Nix package manager.";
meta.license = "MIT";
meta.homepage = https://github.com/haslersn/fish-nix-shell;
}

View File

@ -0,0 +1,33 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, ... }:
{
imports =
[ <nixpkgs/nixos/modules/installer/scan/not-detected.nix>
];
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-uuid/a86f2eff-c873-4af1-bb80-a903383b9c8c";
fsType = "ext4";
};
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/8C30-F6DC";
fsType = "vfat";
};
swapDevices = [ ];
nix.maxJobs = lib.mkDefault 16;
powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
}

View File

@ -0,0 +1,33 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, ... }:
{
imports =
[ <nixpkgs/nixos/modules/installer/scan/not-detected.nix>
];
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-uuid/2fe28058-4186-4e65-9d3d-f7ec5dffc171";
fsType = "ext4";
};
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/E085-5F21";
fsType = "vfat";
};
swapDevices = [ ];
nix.maxJobs = lib.mkDefault 16;
powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
}

View File

@ -0,0 +1,30 @@
{ config, lib, pkgs, ... }:
{
imports =
[ <nixpkgs/nixos/modules/installer/scan/not-detected.nix>
];
boot.initrd.availableKernelModules = [ "xhci_pci" "ehci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" "sr_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-uuid/62a38d9c-452c-4648-be12-6131e95b8276";
fsType = "ext4";
};
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/88F6-46F2";
fsType = "vfat";
};
swapDevices = [ ];
nix.maxJobs = lib.mkDefault 8;
powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
}

112
nixops/light.nix Normal file
View File

@ -0,0 +1,112 @@
{ host }:
{ config, pkgs, ... }:
{
deployment.targetHost = host;
disabledModules = [ "security/pam.nix" ];
imports =
[
(./. + "/${host}-hardware-configuration.nix")
./pam_p11
];
networking.hostName = host;
time.timeZone = "Asia/Hong_Kong";
# List packages installed in system profile. To search, run:
# $ nix search wget
documentation.enable = false;
nixpkgs.config.allowUnfree = true;
environment.systemPackages = with pkgs; [
opensc
wget vim git firefox usbutils pciutils file lm_sensors acpi
gimp imagemagick
(python3.withPackages(ps: with ps; [ numpy scipy ]))
psmisc
telnet unzip zip gnupg
sublime3 rink
tmux screen tigervnc
(import ./fish-nix-shell)
];
programs.wireshark.enable = true;
services.openssh.enable = true;
services.openssh.forwardX11 = true;
services.openssh.passwordAuthentication = false;
hardware.u2f.enable = true;
services.pcscd.enable = true;
programs.ssh.extraConfig =
''
PKCS11Provider "${pkgs.opensc}/lib/opensc-pkcs11.so"
'';
programs.ssh.startAgent = true;
programs.ssh.agentPKCS11Whitelist = "${pkgs.opensc}/lib/opensc-pkcs11.so";
security.pam.p11.enable = true;
# Enable CUPS to print documents.
services.printing = {
enable = true;
extraConf =
''
Browsing Off
BrowseLocalProtocols none
'';
browsedConf =
''
BrowseRemoteProtocols none
BrowseProtocols none
'';
};
services.avahi = {
enable = true;
nssmdns = true;
};
# Enable sound.
sound.enable = true;
hardware.pulseaudio = {
enable = true;
extraModules = [ pkgs.pulseaudio-modules-bt ];
package = pkgs.pulseaudioFull;
};
i18n.inputMethod = {
enabled = "fcitx";
fcitx.engines = with pkgs.fcitx-engines; [ table-extra m17n ];
};
fonts.fonts = [ pkgs.noto-fonts pkgs.noto-fonts-cjk pkgs.noto-fonts-emoji pkgs.noto-fonts-extra ];
# Enable the X11 windowing system.
services.xserver.enable = true;
services.xserver.layout = "us";
services.xserver.xkbOptions = "eurosign:e";
# Enable touchpad support.
services.xserver.libinput.enable = true;
services.xserver.displayManager.lightdm.enable = true;
services.xserver.desktopManager.xfce.enable = true;
programs.fish.enable = true;
programs.fish.promptInit = ''
fish-nix-shell --info-right | source
'';
users.mutableUsers = false;
users.defaultUserShell = pkgs.fish;
users.extraGroups.plugdev = { };
users.extraUsers = import ./common-users.nix { inherit pkgs; };
security.sudo.wheelNeedsPassword = false;
nix.binaryCachePublicKeys = ["nixbld.m-labs.hk-1:5aSRVA5b320xbNvu30tqxVPXpld73bhtOeH6uAjRyHc="];
nix.binaryCaches = ["https://nixbld.m-labs.hk" "https://cache.nixos.org"];
nix.sandboxPaths = ["/opt"];
# This value determines the NixOS release with which your system is to be
# compatible, in order to avoid breaking some software such as database
# servers. You should change this only after NixOS release notes say you
# should.
system.stateVersion = "19.03"; # Did you read the comment?
}

13
nixops/nixops.nix Normal file
View File

@ -0,0 +1,13 @@
{
rpi-1 = import ./rpi.nix { host = "rpi-1"; rpi4 = false; };
rpi-2 = import ./rpi.nix { host = "rpi-2"; rpi4 = false; };
rpi-3 = import ./rpi.nix { host = "rpi-3"; rpi4 = true; };
rpi-4 = import ./rpi.nix { host = "rpi-4"; rpi4 = true; };
rpi-5 = import ./rpi.nix { host = "rpi-5"; rpi4 = true; };
juno = import ./desktop.nix { host = "juno"; };
zeus = import ./desktop.nix { host = "zeus"; };
hera = import ./desktop.nix { host = "hera"; };
hestia = import ./desktop.nix { host = "hestia"; };
chiron = import ./desktop.nix { host = "chiron"; };
cnc = import ./light.nix { host = "cnc"; };
}

843
nixops/pam_p11/default.nix Normal file
View File

@ -0,0 +1,843 @@
# This module provides configuration for the PAM (Pluggable
# Authentication Modules) system.
{ config, lib, pkgs, ... }:
with lib;
let
pam_p11 = pkgs.callPackage ./pam_p11.nix {};
parentConfig = config;
pamOpts = { config, name, ... }: let cfg = config; in let config = parentConfig; in {
options = {
name = mkOption {
example = "sshd";
type = types.str;
description = "Name of the PAM service.";
};
unixAuth = mkOption {
default = true;
type = types.bool;
description = ''
Whether users can log in with passwords defined in
<filename>/etc/shadow</filename>.
'';
};
rootOK = mkOption {
default = false;
type = types.bool;
description = ''
If set, root doesn't need to authenticate (e.g. for the
<command>useradd</command> service).
'';
};
p11Auth = mkOption {
default = config.security.pam.p11.enable;
type = types.bool;
description = ''
If set, keys listed in
<filename>~/.ssh/authorized_keys</filename> and
<filename>~/.eid/authorized_certificates</filename>
can be used to log in with the associated PKCS#11 tokens.
'';
};
u2fAuth = mkOption {
default = config.security.pam.u2f.enable;
type = types.bool;
description = ''
If set, users listed in
<filename>$XDG_CONFIG_HOME/Yubico/u2f_keys</filename> (or
<filename>$HOME/.config/Yubico/u2f_keys</filename> if XDG variable is
not set) are able to log in with the associated U2F key. Path can be
changed using <option>security.pam.u2f.authFile</option> option.
'';
};
yubicoAuth = mkOption {
default = config.security.pam.yubico.enable;
type = types.bool;
description = ''
If set, users listed in
<filename>~/.yubico/authorized_yubikeys</filename>
are able to log in with the associated Yubikey tokens.
'';
};
googleAuthenticator = {
enable = mkOption {
default = false;
type = types.bool;
description = ''
If set, users with enabled Google Authenticator (created
<filename>~/.google_authenticator</filename>) will be required
to provide Google Authenticator token to log in.
'';
};
};
usbAuth = mkOption {
default = config.security.pam.usb.enable;
type = types.bool;
description = ''
If set, users listed in
<filename>/etc/pamusb.conf</filename> are able to log in
with the associated USB key.
'';
};
otpwAuth = mkOption {
default = config.security.pam.enableOTPW;
type = types.bool;
description = ''
If set, the OTPW system will be used (if
<filename>~/.otpw</filename> exists).
'';
};
googleOsLoginAccountVerification = mkOption {
default = false;
type = types.bool;
description = ''
If set, will use the Google OS Login PAM modules
(<literal>pam_oslogin_login</literal>,
<literal>pam_oslogin_admin</literal>) to verify possible OS Login
users and set sudoers configuration accordingly.
This only makes sense to enable for the <literal>sshd</literal> PAM
service.
'';
};
googleOsLoginAuthentication = mkOption {
default = false;
type = types.bool;
description = ''
If set, will use the <literal>pam_oslogin_login</literal>'s user
authentication methods to authenticate users using 2FA.
This only makes sense to enable for the <literal>sshd</literal> PAM
service.
'';
};
fprintAuth = mkOption {
default = config.services.fprintd.enable;
type = types.bool;
description = ''
If set, fingerprint reader will be used (if exists and
your fingerprints are enrolled).
'';
};
oathAuth = mkOption {
default = config.security.pam.oath.enable;
type = types.bool;
description = ''
If set, the OATH Toolkit will be used.
'';
};
sshAgentAuth = mkOption {
default = false;
type = types.bool;
description = ''
If set, the calling user's SSH agent is used to authenticate
against the keys in the calling user's
<filename>~/.ssh/authorized_keys</filename>. This is useful
for <command>sudo</command> on password-less remote systems.
'';
};
duoSecurity = {
enable = mkOption {
default = false;
type = types.bool;
description = ''
If set, use the Duo Security pam module
<literal>pam_duo</literal> for authentication. Requires
configuration of <option>security.duosec</option> options.
'';
};
};
startSession = mkOption {
default = false;
type = types.bool;
description = ''
If set, the service will register a new session with
systemd's login manager. For local sessions, this will give
the user access to audio devices, CD-ROM drives. In the
default PolicyKit configuration, it also allows the user to
reboot the system.
'';
};
setEnvironment = mkOption {
type = types.bool;
default = true;
description = ''
Whether the service should set the environment variables
listed in <option>environment.sessionVariables</option>
using <literal>pam_env.so</literal>.
'';
};
setLoginUid = mkOption {
type = types.bool;
description = ''
Set the login uid of the process
(<filename>/proc/self/loginuid</filename>) for auditing
purposes. The login uid is only set by entry points like
<command>login</command> and <command>sshd</command>, not by
commands like <command>sudo</command>.
'';
};
forwardXAuth = mkOption {
default = false;
type = types.bool;
description = ''
Whether X authentication keys should be passed from the
calling user to the target user (e.g. for
<command>su</command>)
'';
};
pamMount = mkOption {
default = config.security.pam.mount.enable;
type = types.bool;
description = ''
Enable PAM mount (pam_mount) system to mount fileystems on user login.
'';
};
allowNullPassword = mkOption {
default = false;
type = types.bool;
description = ''
Whether to allow logging into accounts that have no password
set (i.e., have an empty password field in
<filename>/etc/passwd</filename> or
<filename>/etc/group</filename>). This does not enable
logging into disabled accounts (i.e., that have the password
field set to <literal>!</literal>). Note that regardless of
what the pam_unix documentation says, accounts with hashed
empty passwords are always allowed to log in.
'';
};
nodelay = mkOption {
default = false;
type = types.bool;
description = ''
Wheather the delay after typing a wrong password should be disabled.
'';
};
requireWheel = mkOption {
default = false;
type = types.bool;
description = ''
Whether to permit root access only to members of group wheel.
'';
};
limits = mkOption {
description = ''
Attribute set describing resource limits. Defaults to the
value of <option>security.pam.loginLimits</option>.
'';
};
showMotd = mkOption {
default = false;
type = types.bool;
description = "Whether to show the message of the day.";
};
makeHomeDir = mkOption {
default = false;
type = types.bool;
description = ''
Whether to try to create home directories for users
with <literal>$HOME</literal>s pointing to nonexistent
locations on session login.
'';
};
updateWtmp = mkOption {
default = false;
type = types.bool;
description = "Whether to update <filename>/var/log/wtmp</filename>.";
};
logFailures = mkOption {
default = false;
type = types.bool;
description = "Whether to log authentication failures in <filename>/var/log/faillog</filename>.";
};
enableAppArmor = mkOption {
default = false;
type = types.bool;
description = ''
Enable support for attaching AppArmor profiles at the
user/group level, e.g., as part of a role based access
control scheme.
'';
};
enableKwallet = mkOption {
default = false;
type = types.bool;
description = ''
If enabled, pam_wallet will attempt to automatically unlock the
user's default KDE wallet upon login. If the user has no wallet named
"kdewallet", or the login password does not match their wallet
password, KDE will prompt separately after login.
'';
};
sssdStrictAccess = mkOption {
default = false;
type = types.bool;
description = "enforce sssd access control";
};
enableGnomeKeyring = mkOption {
default = false;
type = types.bool;
description = ''
If enabled, pam_gnome_keyring will attempt to automatically unlock the
user's default Gnome keyring upon login. If the user login password does
not match their keyring password, Gnome Keyring will prompt separately
after login.
'';
};
text = mkOption {
type = types.nullOr types.lines;
description = "Contents of the PAM service file.";
};
};
config = {
name = mkDefault name;
setLoginUid = mkDefault cfg.startSession;
limits = mkDefault config.security.pam.loginLimits;
# !!! TODO: move the LDAP stuff to the LDAP module, and the
# Samba stuff to the Samba module. This requires that the PAM
# module provides the right hooks.
text = mkDefault
(''
# Account management.
account required pam_unix.so
${optionalString use_ldap
"account sufficient ${pam_ldap}/lib/security/pam_ldap.so"}
${optionalString (config.services.sssd.enable && cfg.sssdStrictAccess==false)
"account sufficient ${pkgs.sssd}/lib/security/pam_sss.so"}
${optionalString (config.services.sssd.enable && cfg.sssdStrictAccess)
"account [default=bad success=ok user_unknown=ignore] ${pkgs.sssd}/lib/security/pam_sss.so"}
${optionalString config.krb5.enable
"account sufficient ${pam_krb5}/lib/security/pam_krb5.so"}
${optionalString cfg.googleOsLoginAccountVerification ''
account [success=ok ignore=ignore default=die] ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so
account [success=ok default=ignore] ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_admin.so
''}
# Authentication management.
${optionalString cfg.googleOsLoginAuthentication
"auth [success=done perm_denied=bad default=ignore] ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so"}
${optionalString cfg.rootOK
"auth sufficient pam_rootok.so"}
${optionalString cfg.requireWheel
"auth required pam_wheel.so use_uid"}
${optionalString cfg.logFailures
"auth required pam_tally.so"}
${optionalString (config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth)
"auth sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=~/.ssh/authorized_keys:~/.ssh/authorized_keys2:/etc/ssh/authorized_keys.d/%u"}
${optionalString cfg.fprintAuth
"auth sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so"}
${let p11 = config.security.pam.p11; in optionalString cfg.p11Auth
"auth ${p11.control} ${pam_p11}/lib/security/pam_p11.so ${pkgs.opensc}/lib/opensc-pkcs11.so"}
${let u2f = config.security.pam.u2f; in optionalString cfg.u2fAuth
"auth ${u2f.control} ${pkgs.pam_u2f}/lib/security/pam_u2f.so ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} ${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"}"}
${optionalString cfg.usbAuth
"auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so"}
${let oath = config.security.pam.oath; in optionalString cfg.oathAuth
"auth requisite ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}"}
${let yubi = config.security.pam.yubico; in optionalString cfg.yubicoAuth
"auth ${yubi.control} ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"}"}
'' +
# Modules in this block require having the password set in PAM_AUTHTOK.
# pam_unix is marked as 'sufficient' on NixOS which means nothing will run
# after it succeeds. Certain modules need to run after pam_unix
# prompts the user for password so we run it once with 'required' at an
# earlier point and it will run again with 'sufficient' further down.
# We use try_first_pass the second time to avoid prompting password twice
(optionalString (cfg.unixAuth &&
(config.security.pam.enableEcryptfs
|| cfg.pamMount
|| cfg.enableKwallet
|| cfg.enableGnomeKeyring
|| cfg.googleAuthenticator.enable
|| cfg.duoSecurity.enable)) ''
auth required pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth
${optionalString config.security.pam.enableEcryptfs
"auth optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap"}
${optionalString cfg.pamMount
"auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so"}
${optionalString cfg.enableKwallet
("auth optional ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so" +
" kwalletd=${pkgs.libsForQt5.kwallet.bin}/bin/kwalletd5")}
${optionalString cfg.enableGnomeKeyring
"auth optional ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so"}
${optionalString cfg.googleAuthenticator.enable
"auth required ${pkgs.googleAuthenticator}/lib/security/pam_google_authenticator.so no_increment_hotp"}
${optionalString cfg.duoSecurity.enable
"auth required ${pkgs.duo-unix}/lib/security/pam_duo.so"}
'') + ''
${optionalString cfg.unixAuth
"auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth try_first_pass"}
${optionalString cfg.otpwAuth
"auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so"}
${optionalString use_ldap
"auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"}
${optionalString config.services.sssd.enable
"auth sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass"}
${optionalString config.krb5.enable ''
auth [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass
auth [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass
auth sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass
''}
auth required pam_deny.so
# Password management.
password sufficient pam_unix.so nullok sha512
${optionalString config.security.pam.enableEcryptfs
"password optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"}
${optionalString cfg.pamMount
"password optional ${pkgs.pam_mount}/lib/security/pam_mount.so"}
${optionalString use_ldap
"password sufficient ${pam_ldap}/lib/security/pam_ldap.so"}
${optionalString config.services.sssd.enable
"password sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_authtok"}
${optionalString config.krb5.enable
"password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass"}
${optionalString config.services.samba.syncPasswordsByPam
"password optional ${pkgs.samba}/lib/security/pam_smbpass.so nullok use_authtok try_first_pass"}
${optionalString cfg.enableGnomeKeyring
"password optional ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so use_authtok"}
# Session management.
${optionalString cfg.setEnvironment ''
session required pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0
''}
session required pam_unix.so
${optionalString cfg.setLoginUid
"session ${
if config.boot.isContainer then "optional" else "required"
} pam_loginuid.so"}
${optionalString cfg.makeHomeDir
"session required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=0022"}
${optionalString cfg.updateWtmp
"session required ${pkgs.pam}/lib/security/pam_lastlog.so silent"}
${optionalString config.security.pam.enableEcryptfs
"session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"}
${optionalString use_ldap
"session optional ${pam_ldap}/lib/security/pam_ldap.so"}
${optionalString config.services.sssd.enable
"session optional ${pkgs.sssd}/lib/security/pam_sss.so"}
${optionalString config.krb5.enable
"session optional ${pam_krb5}/lib/security/pam_krb5.so"}
${optionalString cfg.otpwAuth
"session optional ${pkgs.otpw}/lib/security/pam_otpw.so"}
${optionalString cfg.startSession
"session optional ${pkgs.systemd}/lib/security/pam_systemd.so"}
${optionalString cfg.forwardXAuth
"session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99"}
${optionalString (cfg.limits != [])
"session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits}"}
${optionalString (cfg.showMotd && config.users.motd != null)
"session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd}"}
${optionalString cfg.pamMount
"session optional ${pkgs.pam_mount}/lib/security/pam_mount.so"}
${optionalString (cfg.enableAppArmor && config.security.apparmor.enable)
"session optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug"}
${optionalString (cfg.enableKwallet)
("session optional ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so" +
" kwalletd=${pkgs.libsForQt5.kwallet.bin}/bin/kwalletd5")}
${optionalString (cfg.enableGnomeKeyring)
"session optional ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start"}
${optionalString (config.virtualisation.lxc.lxcfs.enable)
"session optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all"}
'');
};
};
inherit (pkgs) pam_krb5 pam_ccreds;
use_ldap = (config.users.ldap.enable && config.users.ldap.loginPam);
pam_ldap = if config.users.ldap.daemon.enable then pkgs.nss_pam_ldapd else pkgs.pam_ldap;
# Create a limits.conf(5) file.
makeLimitsConf = limits:
pkgs.writeText "limits.conf"
(concatMapStrings ({ domain, type, item, value }:
"${domain} ${type} ${item} ${toString value}\n")
limits);
motd = pkgs.writeText "motd" config.users.motd;
makePAMService = name: service:
{ name = "pam.d/${name}";
value.source = pkgs.writeText "${name}.pam" service.text;
};
in
{
imports = [
(mkRenamedOptionModule [ "security" "pam" "enableU2F" ] [ "security" "pam" "u2f" "enable" ])
];
###### interface
options = {
security.pam.loginLimits = mkOption {
default = [];
example =
[ { domain = "ftp";
type = "hard";
item = "nproc";
value = "0";
}
{ domain = "@student";
type = "-";
item = "maxlogins";
value = "4";
}
];
description =
'' Define resource limits that should apply to users or groups.
Each item in the list should be an attribute set with a
<varname>domain</varname>, <varname>type</varname>,
<varname>item</varname>, and <varname>value</varname>
attribute. The syntax and semantics of these attributes
must be that described in the limits.conf(5) man page.
Note that these limits do not apply to systemd services,
whose limits can be changed via <option>systemd.extraConfig</option>
instead.
'';
};
security.pam.services = mkOption {
default = [];
type = with types; loaOf (submodule pamOpts);
description =
''
This option defines the PAM services. A service typically
corresponds to a program that uses PAM,
e.g. <command>login</command> or <command>passwd</command>.
Each attribute of this set defines a PAM service, with the attribute name
defining the name of the service.
'';
};
security.pam.makeHomeDir.skelDirectory = mkOption {
type = types.str;
default = "/var/empty";
example = "/etc/skel";
description = ''
Path to skeleton directory whose contents are copied to home
directories newly created by <literal>pam_mkhomedir</literal>.
'';
};
security.pam.enableSSHAgentAuth = mkOption {
type = types.bool;
default = false;
description =
''
Enable sudo logins if the user's SSH agent provides a key
present in <filename>~/.ssh/authorized_keys</filename>.
This allows machines to exclusively use SSH keys instead of
passwords.
'';
};
security.pam.enableOTPW = mkEnableOption "the OTPW (one-time password) PAM module";
security.pam.p11 = {
enable = mkOption {
default = false;
type = types.bool;
description = ''
Enables P11 PAM (<literal>pam_p11</literal>) module.
If set, users can log in with SSH keys and PKCS#11 tokens.
More information can be found <link
xlink:href="https://github.com/OpenSC/pam_p11">here</link>.
'';
};
control = mkOption {
default = "sufficient";
type = types.enum [ "required" "requisite" "sufficient" "optional" ];
description = ''
This option sets pam "control".
If you want to have multi factor authentication, use "required".
If you want to use the PKCS#11 device instead of the regular password,
use "sufficient".
Read
<citerefentry>
<refentrytitle>pam.conf</refentrytitle>
<manvolnum>5</manvolnum>
</citerefentry>
for better understanding of this option.
'';
};
};
security.pam.u2f = {
enable = mkOption {
default = false;
type = types.bool;
description = ''
Enables U2F PAM (<literal>pam-u2f</literal>) module.
If set, users listed in
<filename>$XDG_CONFIG_HOME/Yubico/u2f_keys</filename> (or
<filename>$HOME/.config/Yubico/u2f_keys</filename> if XDG variable is
not set) are able to log in with the associated U2F key. The path can
be changed using <option>security.pam.u2f.authFile</option> option.
File format is:
<literal>username:first_keyHandle,first_public_key: second_keyHandle,second_public_key</literal>
This file can be generated using <command>pamu2fcfg</command> command.
More information can be found <link
xlink:href="https://developers.yubico.com/pam-u2f/">here</link>.
'';
};
authFile = mkOption {
default = null;
type = with types; nullOr path;
description = ''
By default <literal>pam-u2f</literal> module reads the keys from
<filename>$XDG_CONFIG_HOME/Yubico/u2f_keys</filename> (or
<filename>$HOME/.config/Yubico/u2f_keys</filename> if XDG variable is
not set).
If you want to change auth file locations or centralize database (for
example use <filename>/etc/u2f-mappings</filename>) you can set this
option.
File format is:
<literal>username:first_keyHandle,first_public_key: second_keyHandle,second_public_key</literal>
This file can be generated using <command>pamu2fcfg</command> command.
More information can be found <link
xlink:href="https://developers.yubico.com/pam-u2f/">here</link>.
'';
};
control = mkOption {
default = "sufficient";
type = types.enum [ "required" "requisite" "sufficient" "optional" ];
description = ''
This option sets pam "control".
If you want to have multi factor authentication, use "required".
If you want to use U2F device instead of regular password, use "sufficient".
Read
<citerefentry>
<refentrytitle>pam.conf</refentrytitle>
<manvolnum>5</manvolnum>
</citerefentry>
for better understanding of this option.
'';
};
debug = mkOption {
default = false;
type = types.bool;
description = ''
Debug output to stderr.
'';
};
interactive = mkOption {
default = false;
type = types.bool;
description = ''
Set to prompt a message and wait before testing the presence of a U2F device.
Recommended if your device doesnt have a tactile trigger.
'';
};
cue = mkOption {
default = false;
type = types.bool;
description = ''
By default <literal>pam-u2f</literal> module does not inform user
that he needs to use the u2f device, it just waits without a prompt.
If you set this option to <literal>true</literal>,
<literal>cue</literal> option is added to <literal>pam-u2f</literal>
module and reminder message will be displayed.
'';
};
};
security.pam.yubico = {
enable = mkOption {
default = false;
type = types.bool;
description = ''
Enables Yubico PAM (<literal>yubico-pam</literal>) module.
If set, users listed in
<filename>~/.yubico/authorized_yubikeys</filename>
are able to log in with the associated Yubikey tokens.
The file must have only one line:
<literal>username:yubikey_token_id1:yubikey_token_id2</literal>
More information can be found <link
xlink:href="https://developers.yubico.com/yubico-pam/">here</link>.
'';
};
control = mkOption {
default = "sufficient";
type = types.enum [ "required" "requisite" "sufficient" "optional" ];
description = ''
This option sets pam "control".
If you want to have multi factor authentication, use "required".
If you want to use Yubikey instead of regular password, use "sufficient".
Read
<citerefentry>
<refentrytitle>pam.conf</refentrytitle>
<manvolnum>5</manvolnum>
</citerefentry>
for better understanding of this option.
'';
};
id = mkOption {
example = "42";
type = types.str;
description = "client id";
};
debug = mkOption {
default = false;
type = types.bool;
description = ''
Debug output to stderr.
'';
};
mode = mkOption {
default = "client";
type = types.enum [ "client" "challenge-response" ];
description = ''
Mode of operation.
Use "client" for online validation with a YubiKey validation service such as
the YubiCloud.
Use "challenge-response" for offline validation using YubiKeys with HMAC-SHA-1
Challenge-Response configurations. See the man-page ykpamcfg(1) for further
details on how to configure offline Challenge-Response validation.
More information can be found <link
xlink:href="https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html">here</link>.
'';
};
};
security.pam.enableEcryptfs = mkEnableOption "eCryptfs PAM module (mounting ecryptfs home directory on login)";
users.motd = mkOption {
default = null;
example = "Today is Sweetmorn, the 4th day of The Aftermath in the YOLD 3178.";
type = types.nullOr types.lines;
description = "Message of the day shown to users when they log in.";
};
};
###### implementation
config = {
environment.systemPackages =
# Include the PAM modules in the system path mostly for the manpages.
[ pkgs.pam ]
++ optional config.users.ldap.enable pam_ldap
++ optional config.services.sssd.enable pkgs.sssd
++ optionals config.krb5.enable [pam_krb5 pam_ccreds]
++ optionals config.security.pam.enableOTPW [ pkgs.otpw ]
++ optionals config.security.pam.oath.enable [ pkgs.oathToolkit ]
++ optionals config.security.pam.p11.enable [ pam_p11 ]
++ optionals config.security.pam.u2f.enable [ pkgs.pam_u2f ];
boot.supportedFilesystems = optionals config.security.pam.enableEcryptfs [ "ecryptfs" ];
security.wrappers = {
unix_chkpwd = {
source = "${pkgs.pam}/sbin/unix_chkpwd.orig";
owner = "root";
setuid = true;
};
};
environment.etc = mapAttrs' makePAMService config.security.pam.services;
security.pam.services =
{ other.text =
''
auth required pam_warn.so
auth required pam_deny.so
account required pam_warn.so
account required pam_deny.so
password required pam_warn.so
password required pam_deny.so
session required pam_warn.so
session required pam_deny.so
'';
# Most of these should be moved to specific modules.
i3lock = {};
i3lock-color = {};
vlock = {};
xlock = {};
xscreensaver = {};
runuser = { rootOK = true; unixAuth = false; setEnvironment = false; };
/* FIXME: should runuser -l start a systemd session? Currently
it complains "Cannot create session: Already running in a
session". */
runuser-l = { rootOK = true; unixAuth = false; };
};
};
}

View File

@ -0,0 +1,23 @@
{ stdenv, fetchFromGitHub, autoreconfHook, pkg-config, openssl, libp11, pam }:
stdenv.mkDerivation rec {
pname = "pam_p11";
version = "0.3.1";
src = fetchFromGitHub {
owner = "OpenSC";
repo = "pam_p11";
rev = "pam_p11-${version}";
sha256 = "1caidy18rq5zk82d51x8vwidmkhwmanf3qm25x1yrdlbhxv6m7lk";
};
patchPhase =
''
substituteInPlace src/match_openssh.c --replace \
'"%s/.ssh/authorized_keys", pw->pw_dir)' \
'"/etc/ssh/authorized_keys.d/%s", pw->pw_name)'
'';
nativeBuildInputs = [ autoreconfHook pkg-config ];
buildInputs = [ pam openssl libp11 ];
}

63
nixops/rpi.nix Normal file
View File

@ -0,0 +1,63 @@
{ host, rpi4 }:
{ config, pkgs, ... }:
let
m-labs = import (fetchTarball https://nixbld.m-labs.hk/channel/custom/artiq/full/artiq-full/nixexprs.tar.xz) { inherit pkgs; };
in
{
deployment.targetHost = host;
nixpkgs.system = "aarch64-linux";
boot.loader.grub.enable = false;
boot.loader.generic-extlinux-compatible.enable = !rpi4;
boot.loader.raspberryPi = pkgs.lib.mkIf rpi4 {
enable = true;
version = 4;
};
boot.kernelPackages = pkgs.lib.mkIf rpi4 pkgs.linuxPackages_latest;
fileSystems = if rpi4 then {
"/boot" = {
device = "/dev/disk/by-label/FIRMWARE";
fsType = "vfat";
};
"/" = {
device = "/dev/disk/by-label/NIXOS_SD";
fsType = "ext4";
};
} else {
"/" = {
device = "/dev/disk/by-label/NIXOS_SD";
fsType = "ext4";
};
};
services.openssh.enable = true;
services.openssh.passwordAuthentication = false;
networking.hostName = host;
time.timeZone = "Asia/Hong_Kong";
users.extraGroups.plugdev = { };
users.mutableUsers = false;
users.defaultUserShell = pkgs.fish;
users.extraUsers = (import ./common-users.nix { inherit pkgs; }) // {
nix = {
isNormalUser = true;
};
};
security.sudo.wheelNeedsPassword = false;
services.udev.packages = [ m-labs.openocd ];
documentation.enable = false;
environment.systemPackages = with pkgs; [
psmisc wget vim git usbutils lm_sensors file telnet mosh tmux xc3sprog m-labs.openocd screen gdb minicom picocom
];
programs.fish.enable = true;
programs.wireshark.enable = true;
nix.binaryCachePublicKeys = ["nixbld.m-labs.hk-1:5aSRVA5b320xbNvu30tqxVPXpld73bhtOeH6uAjRyHc="];
nix.binaryCaches = ["https://cache.nixos.org" "https://nixbld.m-labs.hk"];
nix.trustedUsers = ["root" "nix"];
}

View File

@ -0,0 +1,33 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, ... }:
{
imports =
[ <nixpkgs/nixos/modules/installer/scan/not-detected.nix>
];
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-uuid/890b20a2-08d5-4635-b3a7-003ad4a11a19";
fsType = "ext4";
};
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/91B4-E546";
fsType = "vfat";
};
swapDevices = [ ];
nix.maxJobs = lib.mkDefault 16;
powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
}