mirror of
https://github.com/kiberpipa/nix-rehash.git
synced 2024-09-11 11:55:33 +03:00
refractor the code for another purpose
This commit is contained in:
parent
3f234958bc
commit
99b6f91e19
74
README.md
74
README.md
@ -1,74 +0,0 @@
|
||||
nix-docker
|
||||
==========
|
||||
|
||||
Use [NixOS](http://nixos.org/nixos) configurations to provision [Docker](http://docker.io)
|
||||
containers.
|
||||
|
||||
[Read about the what and why in this blog post](http://zef.me/6049/nix-docker)
|
||||
|
||||
Installation with Vagrant
|
||||
-------------------------
|
||||
The easy way to do this is to use [Vagrant](http://vagrantup.com).
|
||||
|
||||
When you have Vagrant installed:
|
||||
|
||||
git clone https://github.com/zefhemel/nix-docker.git
|
||||
cd nix-docker
|
||||
vagrant up
|
||||
vagrant ssh
|
||||
|
||||
If all went well, you're now in a VM that has both Docker and Nix installed
|
||||
and `nix-docker` in its path. You can now cd into the nix-docker/samples
|
||||
directory to try to build some of the examples. Note that the `~/nix-docker`
|
||||
directory is mounted from your host machine, so you can edit your files with
|
||||
your favorite editor and have them available within the VM.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
To use nix-docker you need [Nix](http://nixos.org/nix) installed as well as
|
||||
[Docker](http://www.docker.io). Realistically, your best way to do this on
|
||||
an Ubuntu (12.04 or 13.04) box. Once these are installed, installing
|
||||
`nix-docker` is as simple as:
|
||||
|
||||
git clone https://github.com/zefhemel/nix-docker.git
|
||||
nix-env -f nix-docker/default.nix -i nix-docker
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
To build a stand-alone Docker image:
|
||||
|
||||
nix-docker -b -t my-image configuration.nix
|
||||
|
||||
This will build the configuration specified in `configuration.nix`, have a look
|
||||
in the `samples/` directory for examples. It will produce a docker image named
|
||||
`my-image` which you can then run anywhere. Use `username/my-image` to be able
|
||||
to push them to the Docker index.
|
||||
|
||||
To build a host-mounted package:
|
||||
|
||||
nix-docker -t my-image configuration.nix
|
||||
|
||||
This will produce a Nix package (symlinked in the current directory in `result`)
|
||||
containing a script you can use to spawn the container using Docker, e.g.:
|
||||
|
||||
sudo -E ./result/sbin/docker-run
|
||||
|
||||
to run the container in the foreground, or:
|
||||
|
||||
sudo -E ./result/sbin/docker-run -d
|
||||
|
||||
to daemonize it. What the `docker-run` script will do is check if there's
|
||||
already a docker image available with the current image name and tag based on
|
||||
the Nix build hash. If not, it will quickly build it first (these images take up
|
||||
barely any space on disk). Then, it will boot up the container.
|
||||
|
||||
Distributing host-mounted packages is done by first copying the Nix closure
|
||||
resulting from the build to the target machine (when you do the build it
|
||||
will give you example commands to run):
|
||||
|
||||
nix-copy-closure root@targetmachine /nix/store/....
|
||||
|
||||
Then, you can spawn the container remotely with the script path provided
|
||||
in the output of the build command.
|
35
Vagrantfile
vendored
35
Vagrantfile
vendored
@ -1,35 +0,0 @@
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "raring64"
|
||||
config.vm.box_url = "http://goo.gl/ceHWg"
|
||||
|
||||
config.vm.provider :virtualbox do |vb|
|
||||
vb.customize ["modifyvm", :id, "--memory", "1024"]
|
||||
|
||||
# We'll attach an extra 50GB disk for all nix and docker data
|
||||
file_to_disk = "disk.vmdk"
|
||||
vb.customize ['createhd', '--filename', file_to_disk, '--size', 50 * 1024]
|
||||
vb.customize ['storageattach', :id, '--storagectl', 'SATA Controller', '--port', 1, '--device', 0, '--type', 'hdd', '--medium', file_to_disk]
|
||||
end
|
||||
|
||||
config.vm.provision :shell, inline: <<eos
|
||||
echo "PATH=/home/vagrant/nix-docker/nix-docker/bin:\\$PATH" > /etc/profile.d/path.sh
|
||||
|
||||
if [ ! -d /data ]; then
|
||||
mkfs.ext4 -F /dev/sdb
|
||||
mkdir -p /nix
|
||||
echo "/dev/sdb /nix ext4 defaults 0 2" >> /etc/fstab
|
||||
mount /nix
|
||||
mkdir -p /nix/docker-lib
|
||||
ln -s /nix/docker-lib /var/lib/docker
|
||||
fi
|
||||
eos
|
||||
|
||||
config.vm.provision :shell, :path => "scripts/install-docker.sh"
|
||||
config.vm.provision :shell, inline: "chmod 777 /var/run/docker.sock"
|
||||
config.vm.provision :shell, :path => "scripts/install-nix.sh"
|
||||
|
||||
config.vm.network "private_network", ip: "192.168.22.22"
|
||||
|
||||
config.vm.synced_folder ".", "/home/vagrant/nix-docker"
|
||||
|
||||
end
|
@ -1,4 +0,0 @@
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
services.mysql.enable = true;
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
#!/bin/sh
|
||||
sudo -E nix-docker -b -t zefhemel/base-nix --from busybox base-configuration.nix
|
52
default.nix
52
default.nix
@ -1,12 +1,42 @@
|
||||
{ pkgs ? import <nixpkgs> { system = "x86_64-linux"; }
|
||||
, name
|
||||
, configuration ? <configuration>
|
||||
, baseImage ? "busybox"
|
||||
}:
|
||||
with pkgs.lib;
|
||||
let
|
||||
pkgs = import <nixpkgs> {};
|
||||
in
|
||||
pkgs.stdenv.mkDerivation {
|
||||
name = "nix-docker-0.1";
|
||||
src = ./nix-docker;
|
||||
buildInputs = [ pkgs.python27 ];
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
cp -R * $out/
|
||||
'';
|
||||
}
|
||||
moduleList = [
|
||||
./user.nix ./supervisord.nix ./systemd.nix ./environment.nix
|
||||
|
||||
<nixpkgs/nixos/modules/config/users-groups.nix>
|
||||
<nixpkgs/nixos/modules/misc/ids.nix>
|
||||
<nixpkgs/nixos/modules/misc/assertions.nix>
|
||||
<nixpkgs/nixos/modules/services/databases/redis.nix>
|
||||
<nixpkgs/nixos/modules/services/databases/mysql.nix>
|
||||
<nixpkgs/nixos/modules/services/databases/postgresql.nix>
|
||||
<nixpkgs/nixos/modules/testing/service-runner.nix>
|
||||
];
|
||||
|
||||
config = (evalModules {
|
||||
modules = [configuration] ++ moduleList;
|
||||
args = { inherit pkgs; };
|
||||
}).config;
|
||||
|
||||
systemd = import ./systemd.nix { inherit pkgs config; };
|
||||
|
||||
startScript = pkgs.writeScript "build" ''
|
||||
#!/bin/sh
|
||||
${config.userNix.startScript}
|
||||
'';
|
||||
|
||||
in pkgs.stdenv.mkDerivation {
|
||||
inherit name;
|
||||
src = ./.;
|
||||
|
||||
phases = [ "installPhase" ];
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/etc/start
|
||||
ln -s ${startScript} $out/etc/start
|
||||
'';
|
||||
}
|
||||
|
23
environment.nix
Normal file
23
environment.nix
Normal file
@ -0,0 +1,23 @@
|
||||
{ config, pkgs, ... }:
|
||||
with pkgs.lib;
|
||||
{
|
||||
options = {
|
||||
environment.systemPackages = mkOption {
|
||||
default = [];
|
||||
description = "Packages to be put in the system profile.";
|
||||
};
|
||||
|
||||
environment.umask = mkOption {
|
||||
default = "002";
|
||||
type = with types; string;
|
||||
};
|
||||
|
||||
# HACK HACK
|
||||
system.activationScripts.etc = mkOption {}; # Ignore
|
||||
system.build.etc = mkOption {}; # Ignore
|
||||
};
|
||||
|
||||
config = {
|
||||
environment.systemPackages = with pkgs; [ coreutils ];
|
||||
};
|
||||
}
|
8
foo.nix
Normal file
8
foo.nix
Normal file
@ -0,0 +1,8 @@
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
config = {
|
||||
services.postgresql.enable = true;
|
||||
services.postgresql.package = pkgs.postgresql92;
|
||||
services.postgresql.dataDir = "/tmp/postgres";
|
||||
};
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
[
|
||||
./modules/config/docker.nix
|
||||
./modules/config/user-groups.nix
|
||||
./modules/config/environment.nix
|
||||
./modules/servers/supervisord.nix
|
||||
|
||||
./modules/shim/systemd.nix
|
||||
<nixpkgs/nixos/modules/system/etc/etc.nix>
|
||||
<nixpkgs/nixos/modules/misc/assertions.nix>
|
||||
<nixpkgs/nixos/modules/misc/ids.nix>
|
||||
<nixpkgs/nixos/modules/services/databases/redis.nix>
|
||||
<nixpkgs/nixos/modules/services/databases/mysql.nix>
|
||||
<nixpkgs/nixos/modules/programs/ssh.nix>
|
||||
<nixpkgs/nixos/modules/services/search/elasticsearch.nix>
|
||||
|
||||
# These modules needed some patching to work well
|
||||
./modules/servers/http/apache/default.nix
|
||||
./modules/servers/openssh.nix
|
||||
]
|
@ -1,87 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os.path
|
||||
import subprocess
|
||||
import argparse
|
||||
import sys
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
parser = argparse.ArgumentParser(description='Build Docker images with Nix', prog='nix-docker')
|
||||
parser.add_argument('configuration', nargs='?', help='configuration.nix file to build', default="configuration.nix")
|
||||
parser.add_argument('-b', action='store_true', help="Build a docker image")
|
||||
parser.add_argument('-t', help='Image name', default="nix-docker-build")
|
||||
parser.add_argument('--from', help="Image to use as a base image", default="busybox")
|
||||
parser.add_argument('-f', action='store_true', help="Force nix-docker into something it does not what to do.")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
base_image = getattr(args, "from")
|
||||
configuration_file = args.configuration
|
||||
image_name = args.t
|
||||
full_build = args.b
|
||||
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
|
||||
nix_closure_temp = "%s/nix-closure" % temp_dir
|
||||
force = args.f
|
||||
|
||||
root_path = os.path.normpath(os.path.realpath(sys.argv[0]) + "/../..")
|
||||
|
||||
def build():
|
||||
print "Building Nix closure for the image..."
|
||||
if args.b:
|
||||
mountBuild = "false"
|
||||
else:
|
||||
mountBuild = "true"
|
||||
cmd = ["nix-build", "%s/docker.nix" % root_path, '-I', 'configuration=' + configuration_file, "--arg", "mountBuild", mountBuild, "--argstr", "name", image_name, "--argstr", "baseImage", base_image]
|
||||
if full_build:
|
||||
cmd.append("--no-out-link")
|
||||
nix_path = subprocess.check_output(cmd)
|
||||
return nix_path.strip()
|
||||
|
||||
def get_available_nix_paths():
|
||||
try:
|
||||
paths_string = subprocess.check_output(["docker", "run", base_image, "/bin/ls", "/nix/store"])
|
||||
return paths_string.strip().split("\n")
|
||||
except:
|
||||
return []
|
||||
|
||||
def cleanup():
|
||||
subprocess.check_call(["rm", "-rf", temp_dir])
|
||||
|
||||
def copy_closure_and_build(package_nix_path, available_paths):
|
||||
paths = subprocess.check_output(["nix-store", "-qR", package_nix_path]).strip().split("\n")
|
||||
print "Available", available_paths
|
||||
paths_to_copy = filter(lambda path: not path[len("/nix/store/"):] in available_paths, paths)
|
||||
print "New Nix store paths to copy to image:"
|
||||
for path in paths_to_copy:
|
||||
print " ", path
|
||||
os.mkdir(nix_closure_temp)
|
||||
cmd = ["cp", "-r"]
|
||||
cmd.extend(paths_to_copy)
|
||||
cmd.append(nix_closure_temp)
|
||||
shutil.copyfile("%s/Dockerfile" % package_nix_path, "%s/Dockerfile" % temp_dir)
|
||||
subprocess.check_call(cmd)
|
||||
subprocess.check_call(["chown", "-R", "root:root", nix_closure_temp])
|
||||
subprocess.check_call(["docker", "build", "-rm=true", "-t", image_name, temp_dir])
|
||||
cleanup()
|
||||
|
||||
if __name__ == '__main__':
|
||||
if full_build and os.getenv("USER") != "root" and not force:
|
||||
print "When doing a full build you need to run nix-docker as root. Rerun this command with 'sudo -E nix-docker ...'"
|
||||
sys.exit(1)
|
||||
|
||||
if not os.path.exists(configuration_file):
|
||||
print "Could not find configuration file: %s" % configuration_file
|
||||
sys.exit(1)
|
||||
|
||||
package_nix_path = build()
|
||||
if full_build:
|
||||
available_paths = get_available_nix_paths()
|
||||
copy_closure_and_build(package_nix_path, available_paths)
|
||||
print "To run: sudo docker run -t -i", image_name
|
||||
else:
|
||||
print "Result in", package_nix_path, "test with sudo ./result/sbin/docker-run"
|
||||
print "To deploy: nix-copy-closure -s root@<machine>", package_nix_path
|
||||
print "To run: ssh root@<machine>", package_nix_path + "/sbin/docker-run -d"
|
@ -1,82 +0,0 @@
|
||||
{ pkgs ? import <nixpkgs> { system = "x86_64-linux"; }
|
||||
, name
|
||||
, configuration ? <configuration>
|
||||
, mountBuild ? true
|
||||
, baseImage ? "busybox"
|
||||
}:
|
||||
with pkgs.lib;
|
||||
let
|
||||
config = evalModules {
|
||||
modules = concatLists [ [configuration] (import ./all-modules.nix) ];
|
||||
args = { inherit pkgs; };
|
||||
};
|
||||
|
||||
localNixPath = pkg: "nix_store/${substring 11 (stringLength pkg.outPath) pkg.outPath}";
|
||||
|
||||
systemd = import ./systemd.nix { inherit pkgs config; };
|
||||
environment = import ./environment.nix { inherit pkgs config; };
|
||||
|
||||
verboseFlag = if config.config.docker.verbose then "v" else "";
|
||||
|
||||
|
||||
buildScript = pkgs.writeScript "build" ''
|
||||
#!/bin/sh -e${verboseFlag}
|
||||
${config.config.docker.buildScript}
|
||||
'';
|
||||
|
||||
bootScript = pkgs.writeScript "boot" ''
|
||||
#!/bin/sh -e${verboseFlag}
|
||||
umask ${config.config.environment.umask}
|
||||
${if mountBuild then config.config.docker.buildScript else ""}
|
||||
${config.config.docker.bootScript}
|
||||
'';
|
||||
|
||||
dockerFile = pkgs.writeText "Dockerfile" ''
|
||||
FROM ${if mountBuild then "busybox" else baseImage}
|
||||
${if !mountBuild then
|
||||
''
|
||||
ADD nix-closure /nix/store
|
||||
RUN ${buildScript}
|
||||
''
|
||||
else ""}
|
||||
CMD ${bootScript}
|
||||
${
|
||||
concatMapStrings (port: "EXPOSE ${toString port}\n") config.config.docker.ports
|
||||
}
|
||||
${
|
||||
concatMapStrings (port: "VOLUME ${toString port}\n") config.config.docker.volumes
|
||||
}
|
||||
'';
|
||||
|
||||
imageHash = substring 11 8 dockerFile.outPath;
|
||||
|
||||
runContainerScript = pkgs.writeScript "docker-run" ''
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [ "" == "$(docker images | grep -E "${name}\s*${imageHash}")" ]; then
|
||||
docker build -t ${name}:${imageHash} $(dirname $0)/..
|
||||
fi
|
||||
|
||||
OPTIONS="-t -i $*"
|
||||
if [ "$1" == "-d" ]; then
|
||||
OPTIONS="$*"
|
||||
fi
|
||||
|
||||
docker run $OPTIONS ${if mountBuild then "-v /nix/store:/nix/store" else ""} ${name}:${imageHash}
|
||||
'';
|
||||
|
||||
in pkgs.stdenv.mkDerivation {
|
||||
name = replaceChars ["/"] ["-"] name;
|
||||
src = ./.;
|
||||
|
||||
phases = [ "installPhase" ];
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
${if mountBuild then ''
|
||||
mkdir -p $out/sbin
|
||||
cp ${runContainerScript} $out/sbin/docker-run
|
||||
'' else ""}
|
||||
cp ${dockerFile} $out/Dockerfile
|
||||
'';
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
{ config, pkgs, ... }:
|
||||
with pkgs.lib;
|
||||
{
|
||||
options = {
|
||||
docker.ports = mkOption {
|
||||
default = [];
|
||||
description = "Ports to expose to the outside world.";
|
||||
example = [ 80 22 ];
|
||||
};
|
||||
|
||||
docker.volumes = mkOption {
|
||||
default = [];
|
||||
description = "Volumes to create for container.";
|
||||
example = [ "/var/lib" "/var/log" ];
|
||||
};
|
||||
|
||||
docker.buildScripts = mkOption {
|
||||
default = {};
|
||||
example = {
|
||||
setupUsers = "cp passwd /etc/passwd";
|
||||
};
|
||||
description = "Scripts (as text) to be run during build, executed alphabetically";
|
||||
};
|
||||
|
||||
docker.bootScript = mkOption {
|
||||
default = "";
|
||||
description = "Script (text) to run when container booted.";
|
||||
};
|
||||
|
||||
docker.buildScript = mkOption {};
|
||||
|
||||
docker.verbose = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
};
|
||||
|
||||
# HACK: Let's ignore these for now
|
||||
networking = mkOption {};
|
||||
security = mkOption {};
|
||||
};
|
||||
|
||||
config = {
|
||||
docker.buildScript = concatStrings (attrValues config.docker.buildScripts);
|
||||
networking.enableIPv6 = false;
|
||||
};
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
{ config, pkgs, ... }:
|
||||
with pkgs.lib;
|
||||
let
|
||||
etc2 = filter (f: f.enable) (attrValues config.environment.etc);
|
||||
|
||||
etc = pkgs.stdenv.mkDerivation {
|
||||
name = "etc";
|
||||
|
||||
builder = <nixpkgs/nixos/modules/system/etc/make-etc.sh>;
|
||||
|
||||
preferLocalBuild = true;
|
||||
|
||||
/* !!! Use toXML. */
|
||||
sources = map (x: x.source) etc2;
|
||||
targets = map (x: x.target) etc2;
|
||||
modes = map (x: x.mode) etc2;
|
||||
};
|
||||
in {
|
||||
options = {
|
||||
environment.systemPackages = mkOption {
|
||||
default = [];
|
||||
description = "Packages to be put in the system profile.";
|
||||
};
|
||||
|
||||
environment.umask = mkOption {
|
||||
default = "002";
|
||||
type = with types; string;
|
||||
};
|
||||
|
||||
# HACK HACK
|
||||
system.activationScripts.etc = mkOption {}; # Ignore
|
||||
system.build.etc = mkOption {}; # Ignore
|
||||
};
|
||||
|
||||
config = {
|
||||
docker.buildScripts."0-systemEnv" = let
|
||||
systemPackages = config.environment.systemPackages;
|
||||
systemEnv = pkgs.buildEnv { name = "system-env"; paths = systemPackages; };
|
||||
in ''
|
||||
rm -rf /usr
|
||||
chmod 777 /tmp
|
||||
ln -s ${systemEnv} /usr
|
||||
ln -sf /usr/bin/bash /bin/bash
|
||||
'';
|
||||
|
||||
environment.systemPackages = with pkgs; [ coreutils bash ];
|
||||
|
||||
docker.buildScripts."0-etc" = ''
|
||||
echo "setting up /etc..."
|
||||
${pkgs.perl}/bin/perl ${<nixpkgs/nixos/modules/system/etc/setup-etc.pl>} ${etc}/etc
|
||||
'';
|
||||
};
|
||||
}
|
@ -1,272 +0,0 @@
|
||||
{pkgs, config, ...}:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
let
|
||||
|
||||
userOpts = { name, config, ... }: {
|
||||
|
||||
options = {
|
||||
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
description = "The name of the user account. If undefined, the name of the attribute set will be used.";
|
||||
};
|
||||
|
||||
description = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
example = "Alice Q. User";
|
||||
description = ''
|
||||
A short description of the user account, typically the
|
||||
user's full name. This is actually the “GECOS” or “comment”
|
||||
field in <filename>/etc/passwd</filename>.
|
||||
'';
|
||||
};
|
||||
|
||||
uid = mkOption {
|
||||
type = with types; uniq (nullOr int);
|
||||
default = null;
|
||||
description = "The account UID. If undefined, NixOS will select a free UID.";
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = "nogroup";
|
||||
description = "The user's primary group.";
|
||||
};
|
||||
|
||||
extraGroups = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = "The user's auxiliary groups.";
|
||||
};
|
||||
|
||||
home = mkOption {
|
||||
type = types.str;
|
||||
default = "/var/empty";
|
||||
description = "The user's home directory.";
|
||||
};
|
||||
|
||||
shell = mkOption {
|
||||
type = types.str;
|
||||
default = "/run/current-system/sw/sbin/nologin";
|
||||
description = "The path to the user's shell.";
|
||||
};
|
||||
|
||||
createHome = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "If true, the home directory will be created automatically.";
|
||||
};
|
||||
|
||||
useDefaultShell = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "If true, the user's shell will be set to <literal>users.defaultUserShell</literal>.";
|
||||
};
|
||||
|
||||
password = mkOption {
|
||||
type = with types; uniq (nullOr str);
|
||||
default = null;
|
||||
description = ''
|
||||
The user's password. If undefined, no password is set for
|
||||
the user. Warning: do not set confidential information here
|
||||
because it is world-readable in the Nix store. This option
|
||||
should only be used for public accounts such as
|
||||
<literal>guest</literal>.
|
||||
'';
|
||||
};
|
||||
|
||||
isSystemUser = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Indicates if the user is a system user or not.";
|
||||
};
|
||||
|
||||
createUser = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Indicates if the user should be created automatically as a local user.
|
||||
Set this to false if the user for instance is an LDAP user. NixOS will
|
||||
then not modify any of the basic properties for the user account.
|
||||
'';
|
||||
};
|
||||
|
||||
isAlias = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "If true, the UID of this user is not required to be unique and can thus alias another user.";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = {
|
||||
name = mkDefault name;
|
||||
uid = mkDefault null;
|
||||
shell = mkDefault "/bin/sh";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
groupOpts = { name, config, ... }: {
|
||||
|
||||
options = {
|
||||
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
description = "The name of the group. If undefined, the name of the attribute set will be used.";
|
||||
};
|
||||
|
||||
gid = mkOption {
|
||||
type = with types; uniq (nullOr int);
|
||||
default = null;
|
||||
description = "The GID of the group. If undefined, NixOS will select a free GID.";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = {
|
||||
name = mkDefault name;
|
||||
gid = mkDefault null;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
extraUsers = config.users.extraUsers;
|
||||
extraGroups = config.users.extraGroups;
|
||||
|
||||
uidUsers = listToAttrs
|
||||
(imap (i: name:
|
||||
let
|
||||
user = getAttr name extraUsers;
|
||||
in {
|
||||
name=name;
|
||||
value = if user.uid == null then
|
||||
setAttr user "uid" (builtins.add 1000 i)
|
||||
else user;
|
||||
})
|
||||
(attrNames extraUsers));
|
||||
|
||||
gidGroups = listToAttrs
|
||||
(imap (i: name:
|
||||
let
|
||||
group = getAttr name extraGroups;
|
||||
in {
|
||||
name=name;
|
||||
value = if group.gid == null then
|
||||
setAttr group "gid" (builtins.add 1000 i)
|
||||
else group;
|
||||
})
|
||||
(attrNames extraGroups));
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
|
||||
users.extraUsers = mkOption {
|
||||
default = {};
|
||||
type = types.loaOf types.optionSet;
|
||||
example = {
|
||||
alice = {
|
||||
uid = 1234;
|
||||
description = "Alice Q. User";
|
||||
home = "/home/alice";
|
||||
createHome = true;
|
||||
group = "users";
|
||||
extraGroups = ["wheel"];
|
||||
shell = "/bin/sh";
|
||||
};
|
||||
};
|
||||
description = ''
|
||||
Additional user accounts to be created automatically by the system.
|
||||
This can also be used to set options for root.
|
||||
'';
|
||||
options = [ userOpts ];
|
||||
};
|
||||
|
||||
users.extraGroups = mkOption {
|
||||
default = {};
|
||||
example =
|
||||
{ students.gid = 1001;
|
||||
hackers = { };
|
||||
};
|
||||
type = types.loaOf types.optionSet;
|
||||
description = ''
|
||||
Additional groups to be created automatically by the system.
|
||||
'';
|
||||
options = [ groupOpts ];
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
|
||||
users.extraUsers = {
|
||||
root = {
|
||||
uid = 0;
|
||||
description = "System administrator";
|
||||
home = "/root";
|
||||
group = "root";
|
||||
};
|
||||
nobody = {
|
||||
uid = 1;
|
||||
description = "Unprivileged account (don't use!)";
|
||||
};
|
||||
ldap = {};
|
||||
};
|
||||
|
||||
users.extraGroups = {
|
||||
root = { gid = 0; };
|
||||
wheel = { };
|
||||
disk = { };
|
||||
kmem = { };
|
||||
tty = { };
|
||||
floppy = { };
|
||||
uucp = { };
|
||||
lp = { };
|
||||
cdrom = { };
|
||||
tape = { };
|
||||
audio = { };
|
||||
video = { };
|
||||
dialout = { };
|
||||
nogroup = { };
|
||||
users = { };
|
||||
utmp = { };
|
||||
adm = { };
|
||||
};
|
||||
|
||||
environment.etc.passwd.text =
|
||||
concatMapStrings (name:
|
||||
let
|
||||
user = getAttr name uidUsers;
|
||||
in
|
||||
if user.createUser then
|
||||
"${user.name}:x:${toString user.uid}:${toString (getAttr user.group gidGroups).gid}:${user.description}:${user.home}:${user.shell}\n"
|
||||
else
|
||||
""
|
||||
) (attrNames uidUsers);
|
||||
|
||||
environment.etc.group.text =
|
||||
concatMapStrings (name:
|
||||
let
|
||||
group = getAttr name gidGroups;
|
||||
in
|
||||
"${group.name}:x:${toString group.gid}:\n"
|
||||
) (attrNames gidGroups);
|
||||
|
||||
docker.buildScripts."1-create-homes" = concatMapStrings (name:
|
||||
let
|
||||
user = getAttr name uidUsers;
|
||||
in
|
||||
if user.createHome then
|
||||
"mkdir -p ${user.home}; chown ${user.name}:${user.group} ${user.home}\n"
|
||||
else
|
||||
""
|
||||
) (attrNames uidUsers);
|
||||
};
|
||||
|
||||
}
|
@ -1,641 +0,0 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
let
|
||||
|
||||
mainCfg = config.services.httpd;
|
||||
|
||||
httpd = mainCfg.package;
|
||||
|
||||
version24 = !versionOlder httpd.version "2.4";
|
||||
|
||||
httpdConf = mainCfg.configFile;
|
||||
|
||||
php = pkgs.php.override { apacheHttpd = httpd; };
|
||||
|
||||
getPort = cfg: if cfg.port != 0 then cfg.port else if cfg.enableSSL then 443 else 80;
|
||||
|
||||
extraModules = attrByPath ["extraModules"] [] mainCfg;
|
||||
extraForeignModules = filter builtins.isAttrs extraModules;
|
||||
extraApacheModules = filter (x: !(builtins.isAttrs x)) extraModules; # I'd prefer using builtins.isString here, but doesn't exist yet
|
||||
|
||||
|
||||
makeServerInfo = cfg: {
|
||||
# Canonical name must not include a trailing slash.
|
||||
canonicalName =
|
||||
(if cfg.enableSSL then "https" else "http") + "://" +
|
||||
cfg.hostName +
|
||||
(if getPort cfg != (if cfg.enableSSL then 443 else 80) then ":${toString (getPort cfg)}" else "");
|
||||
|
||||
# Admin address: inherit from the main server if not specified for
|
||||
# a virtual host.
|
||||
adminAddr = if cfg.adminAddr != null then cfg.adminAddr else mainCfg.adminAddr;
|
||||
|
||||
vhostConfig = cfg;
|
||||
serverConfig = mainCfg;
|
||||
fullConfig = config; # machine config
|
||||
};
|
||||
|
||||
|
||||
allHosts = [mainCfg] ++ mainCfg.virtualHosts;
|
||||
|
||||
|
||||
callSubservices = serverInfo: defs:
|
||||
let f = svc:
|
||||
let
|
||||
svcFunction =
|
||||
if svc ? function then svc.function
|
||||
else import (toString "${toString ./.}/${if svc ? serviceType then svc.serviceType else svc.serviceName}.nix");
|
||||
config = (evalModules
|
||||
{ modules = [ { options = res.options; config = svc.config or svc; } ];
|
||||
check = false;
|
||||
}).config;
|
||||
defaults = {
|
||||
extraConfig = "";
|
||||
extraModules = [];
|
||||
extraModulesPre = [];
|
||||
extraPath = [];
|
||||
extraServerPath = [];
|
||||
globalEnvVars = [];
|
||||
robotsEntries = "";
|
||||
startupScript = "";
|
||||
enablePHP = false;
|
||||
phpOptions = "";
|
||||
options = {};
|
||||
};
|
||||
res = defaults // svcFunction { inherit config pkgs serverInfo php; };
|
||||
in res;
|
||||
in map f defs;
|
||||
|
||||
|
||||
# !!! callSubservices is expensive
|
||||
subservicesFor = cfg: callSubservices (makeServerInfo cfg) cfg.extraSubservices;
|
||||
|
||||
mainSubservices = subservicesFor mainCfg;
|
||||
|
||||
allSubservices = mainSubservices ++ concatMap subservicesFor mainCfg.virtualHosts;
|
||||
|
||||
|
||||
# !!! should be in lib
|
||||
writeTextInDir = name: text:
|
||||
pkgs.runCommand name {inherit text;} "ensureDir $out; echo -n \"$text\" > $out/$name";
|
||||
|
||||
|
||||
enableSSL = any (vhost: vhost.enableSSL) allHosts;
|
||||
|
||||
|
||||
# Names of modules from ${httpd}/modules that we want to load.
|
||||
apacheModules =
|
||||
[ # HTTP authentication mechanisms: basic and digest.
|
||||
"auth_basic" "auth_digest"
|
||||
|
||||
# Authentication: is the user who he claims to be?
|
||||
"authn_file" "authn_dbm" "authn_anon"
|
||||
(if version24 then "authn_core" else "authn_alias")
|
||||
|
||||
# Authorization: is the user allowed access?
|
||||
"authz_user" "authz_groupfile" "authz_host"
|
||||
|
||||
# Other modules.
|
||||
"ext_filter" "include" "log_config" "env" "mime_magic"
|
||||
"cern_meta" "expires" "headers" "usertrack" /* "unique_id" */ "setenvif"
|
||||
"mime" "dav" "status" "autoindex" "asis" "info" "dav_fs"
|
||||
"vhost_alias" "negotiation" "dir" "imagemap" "actions" "speling"
|
||||
"userdir" "alias" "rewrite" "proxy" "proxy_http"
|
||||
]
|
||||
++ optionals version24 [
|
||||
"mpm_${mainCfg.multiProcessingModule}"
|
||||
"authz_core"
|
||||
"unixd"
|
||||
]
|
||||
++ (if mainCfg.multiProcessingModule == "prefork" then [ "cgi" ] else [ "cgid" ])
|
||||
++ optional enableSSL "ssl"
|
||||
++ extraApacheModules;
|
||||
|
||||
|
||||
allDenied = if version24 then ''
|
||||
Require all denied
|
||||
'' else ''
|
||||
Order deny,allow
|
||||
Deny from all
|
||||
'';
|
||||
|
||||
allGranted = if version24 then ''
|
||||
Require all granted
|
||||
'' else ''
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
'';
|
||||
|
||||
|
||||
loggingConf = ''
|
||||
ErrorLog ${mainCfg.logDir}/error_log
|
||||
|
||||
LogLevel notice
|
||||
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b" common
|
||||
LogFormat "%{Referer}i -> %U" referer
|
||||
LogFormat "%{User-agent}i" agent
|
||||
|
||||
CustomLog ${mainCfg.logDir}/access_log ${mainCfg.logFormat}
|
||||
'';
|
||||
|
||||
|
||||
browserHacks = ''
|
||||
BrowserMatch "Mozilla/2" nokeepalive
|
||||
BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0
|
||||
BrowserMatch "RealPlayer 4\.0" force-response-1.0
|
||||
BrowserMatch "Java/1\.0" force-response-1.0
|
||||
BrowserMatch "JDK/1\.0" force-response-1.0
|
||||
BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully
|
||||
BrowserMatch "^WebDrive" redirect-carefully
|
||||
BrowserMatch "^WebDAVFS/1.[012]" redirect-carefully
|
||||
BrowserMatch "^gnome-vfs" redirect-carefully
|
||||
'';
|
||||
|
||||
|
||||
sslConf = ''
|
||||
SSLSessionCache shm:${mainCfg.stateDir}/ssl_scache(512000)
|
||||
|
||||
SSLMutex posixsem
|
||||
|
||||
SSLRandomSeed startup builtin
|
||||
SSLRandomSeed connect builtin
|
||||
'';
|
||||
|
||||
|
||||
mimeConf = ''
|
||||
TypesConfig ${httpd}/conf/mime.types
|
||||
|
||||
AddType application/x-x509-ca-cert .crt
|
||||
AddType application/x-pkcs7-crl .crl
|
||||
AddType application/x-httpd-php .php .phtml
|
||||
|
||||
<IfModule mod_mime_magic.c>
|
||||
MIMEMagicFile ${httpd}/conf/magic
|
||||
</IfModule>
|
||||
|
||||
AddEncoding x-compress Z
|
||||
AddEncoding x-gzip gz tgz
|
||||
'';
|
||||
|
||||
|
||||
perServerConf = isMainServer: cfg: let
|
||||
|
||||
serverInfo = makeServerInfo cfg;
|
||||
|
||||
subservices = callSubservices serverInfo cfg.extraSubservices;
|
||||
|
||||
documentRoot = if cfg.documentRoot != null then cfg.documentRoot else
|
||||
pkgs.runCommand "empty" {} "ensureDir $out";
|
||||
|
||||
documentRootConf = ''
|
||||
DocumentRoot "${documentRoot}"
|
||||
|
||||
<Directory "${documentRoot}">
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride None
|
||||
${allGranted}
|
||||
</Directory>
|
||||
'';
|
||||
|
||||
robotsTxt = pkgs.writeText "robots.txt" ''
|
||||
${# If this is a vhost, the include the entries for the main server as well.
|
||||
if isMainServer then ""
|
||||
else concatMapStrings (svc: svc.robotsEntries) mainSubservices}
|
||||
${concatMapStrings (svc: svc.robotsEntries) subservices}
|
||||
'';
|
||||
|
||||
robotsConf = ''
|
||||
Alias /robots.txt ${robotsTxt}
|
||||
'';
|
||||
|
||||
in ''
|
||||
ServerName ${serverInfo.canonicalName}
|
||||
|
||||
${concatMapStrings (alias: "ServerAlias ${alias}\n") cfg.serverAliases}
|
||||
|
||||
${if cfg.sslServerCert != null then ''
|
||||
SSLCertificateFile ${cfg.sslServerCert}
|
||||
SSLCertificateKeyFile ${cfg.sslServerKey}
|
||||
'' else ""}
|
||||
|
||||
${if cfg.enableSSL then ''
|
||||
SSLEngine on
|
||||
'' else if enableSSL then /* i.e., SSL is enabled for some host, but not this one */
|
||||
''
|
||||
SSLEngine off
|
||||
'' else ""}
|
||||
|
||||
${if isMainServer || cfg.adminAddr != null then ''
|
||||
ServerAdmin ${cfg.adminAddr}
|
||||
'' else ""}
|
||||
|
||||
${if !isMainServer && mainCfg.logPerVirtualHost then ''
|
||||
ErrorLog ${mainCfg.logDir}/error_log-${cfg.hostName}
|
||||
CustomLog ${mainCfg.logDir}/access_log-${cfg.hostName} ${cfg.logFormat}
|
||||
'' else ""}
|
||||
|
||||
${robotsConf}
|
||||
|
||||
${if isMainServer || cfg.documentRoot != null then documentRootConf else ""}
|
||||
|
||||
${if cfg.enableUserDir then ''
|
||||
|
||||
UserDir public_html
|
||||
UserDir disabled root
|
||||
|
||||
<Directory "/home/*/public_html">
|
||||
AllowOverride FileInfo AuthConfig Limit Indexes
|
||||
Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
|
||||
<Limit GET POST OPTIONS>
|
||||
${allGranted}
|
||||
</Limit>
|
||||
<LimitExcept GET POST OPTIONS>
|
||||
${allDenied}
|
||||
</LimitExcept>
|
||||
</Directory>
|
||||
|
||||
'' else ""}
|
||||
|
||||
${if cfg.globalRedirect != null then ''
|
||||
RedirectPermanent / ${cfg.globalRedirect}
|
||||
'' else ""}
|
||||
|
||||
${
|
||||
let makeFileConf = elem: ''
|
||||
Alias ${elem.urlPath} ${elem.file}
|
||||
'';
|
||||
in concatMapStrings makeFileConf cfg.servedFiles
|
||||
}
|
||||
|
||||
${
|
||||
let makeDirConf = elem: ''
|
||||
Alias ${elem.urlPath} ${elem.dir}/
|
||||
<Directory ${elem.dir}>
|
||||
Options +Indexes
|
||||
${allGranted}
|
||||
AllowOverride All
|
||||
</Directory>
|
||||
'';
|
||||
in concatMapStrings makeDirConf cfg.servedDirs
|
||||
}
|
||||
|
||||
${concatMapStrings (svc: svc.extraConfig) subservices}
|
||||
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
|
||||
|
||||
confFile = pkgs.writeText "httpd.conf" ''
|
||||
|
||||
ServerRoot ${httpd}
|
||||
|
||||
${optionalString version24 ''
|
||||
DefaultRuntimeDir ${mainCfg.stateDir}/runtime
|
||||
''}
|
||||
|
||||
PidFile ${mainCfg.stateDir}/httpd.pid
|
||||
|
||||
${optionalString (mainCfg.multiProcessingModule != "prefork") ''
|
||||
# mod_cgid requires this.
|
||||
ScriptSock ${mainCfg.stateDir}/cgisock
|
||||
''}
|
||||
|
||||
<IfModule prefork.c>
|
||||
MaxClients ${toString mainCfg.maxClients}
|
||||
MaxRequestsPerChild ${toString mainCfg.maxRequestsPerChild}
|
||||
</IfModule>
|
||||
|
||||
${let
|
||||
ports = map getPort allHosts;
|
||||
uniquePorts = uniqList {inputList = ports;};
|
||||
in concatMapStrings (port: "Listen ${toString port}\n") uniquePorts
|
||||
}
|
||||
|
||||
User ${mainCfg.user}
|
||||
Group ${mainCfg.group}
|
||||
|
||||
${let
|
||||
load = {name, path}: "LoadModule ${name}_module ${path}\n";
|
||||
allModules =
|
||||
concatMap (svc: svc.extraModulesPre) allSubservices
|
||||
++ map (name: {inherit name; path = "${httpd}/modules/mod_${name}.so";}) apacheModules
|
||||
++ optional enablePHP { name = "php5"; path = "${php}/modules/libphp5.so"; }
|
||||
++ concatMap (svc: svc.extraModules) allSubservices
|
||||
++ extraForeignModules;
|
||||
in concatMapStrings load allModules
|
||||
}
|
||||
|
||||
AddHandler type-map var
|
||||
|
||||
<Files ~ "^\.ht">
|
||||
${allDenied}
|
||||
</Files>
|
||||
|
||||
${mimeConf}
|
||||
${loggingConf}
|
||||
${browserHacks}
|
||||
|
||||
Include ${httpd}/conf/extra/httpd-default.conf
|
||||
Include ${httpd}/conf/extra/httpd-autoindex.conf
|
||||
Include ${httpd}/conf/extra/httpd-multilang-errordoc.conf
|
||||
Include ${httpd}/conf/extra/httpd-languages.conf
|
||||
|
||||
${if enableSSL then sslConf else ""}
|
||||
|
||||
# Fascist default - deny access to everything.
|
||||
<Directory />
|
||||
Options FollowSymLinks
|
||||
AllowOverride None
|
||||
${allDenied}
|
||||
</Directory>
|
||||
|
||||
# But do allow access to files in the store so that we don't have
|
||||
# to generate <Directory> clauses for every generated file that we
|
||||
# want to serve.
|
||||
<Directory /nix/store>
|
||||
${allGranted}
|
||||
</Directory>
|
||||
|
||||
# Generate directives for the main server.
|
||||
${perServerConf true mainCfg}
|
||||
|
||||
# Always enable virtual hosts; it doesn't seem to hurt.
|
||||
${let
|
||||
ports = map getPort allHosts;
|
||||
uniquePorts = uniqList {inputList = ports;};
|
||||
directives = concatMapStrings (port: "NameVirtualHost *:${toString port}\n") uniquePorts;
|
||||
in optionalString (!version24) directives
|
||||
}
|
||||
|
||||
${let
|
||||
makeVirtualHost = vhost: ''
|
||||
<VirtualHost *:${toString (getPort vhost)}>
|
||||
${perServerConf false vhost}
|
||||
</VirtualHost>
|
||||
'';
|
||||
in concatMapStrings makeVirtualHost mainCfg.virtualHosts
|
||||
}
|
||||
'';
|
||||
|
||||
|
||||
enablePHP = any (svc: svc.enablePHP) allSubservices;
|
||||
|
||||
|
||||
# Generate the PHP configuration file. Should probably be factored
|
||||
# out into a separate module.
|
||||
phpIni = pkgs.runCommand "php.ini"
|
||||
{ options = concatStringsSep "\n"
|
||||
([ mainCfg.phpOptions ] ++ (map (svc: svc.phpOptions) allSubservices));
|
||||
}
|
||||
''
|
||||
cat ${php}/etc/php-recommended.ini > $out
|
||||
echo "$options" >> $out
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
|
||||
{
|
||||
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
|
||||
services.httpd = {
|
||||
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Whether to enable the Apache HTTP Server.";
|
||||
};
|
||||
|
||||
package = mkOption {
|
||||
type = types.path;
|
||||
default = pkgs.apacheHttpd.override { mpm = mainCfg.multiProcessingModule; };
|
||||
example = "pkgs.apacheHttpd_2_4";
|
||||
description = ''
|
||||
Overridable attribute of the Apache HTTP Server package to use.
|
||||
'';
|
||||
};
|
||||
|
||||
configFile = mkOption {
|
||||
type = types.path;
|
||||
default = confFile;
|
||||
example = literalExample ''pkgs.writeText "httpd.conf" "# my custom config file ...";'';
|
||||
description = ''
|
||||
Override the configuration file used by Apache. By default,
|
||||
NixOS generates one automatically.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Cnfiguration lines appended to the generated Apache
|
||||
configuration file. Note that this mechanism may not work
|
||||
when <option>configFile</option> is overridden.
|
||||
'';
|
||||
};
|
||||
|
||||
extraModules = mkOption {
|
||||
type = types.listOf types.unspecified;
|
||||
default = [];
|
||||
example = literalExample ''[ "proxy_connect" { name = "php5"; path = "''${php}/modules/libphp5.so"; } ]'';
|
||||
description = ''
|
||||
Additional Apache modules to be used. These can be
|
||||
specified as a string in the case of modules distributed
|
||||
with Apache, or as an attribute set specifying the
|
||||
<varname>name</varname> and <varname>path</varname> of the
|
||||
module.
|
||||
'';
|
||||
};
|
||||
|
||||
logPerVirtualHost = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
If enabled, each virtual host gets its own
|
||||
<filename>access_log</filename> and
|
||||
<filename>error_log</filename>, namely suffixed by the
|
||||
<option>hostName</option> of the virtual host.
|
||||
'';
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "wwwrun";
|
||||
description = ''
|
||||
User account under which httpd runs. The account is created
|
||||
automatically if it doesn't exist.
|
||||
'';
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = "wwwrun";
|
||||
description = ''
|
||||
Group under which httpd runs. The account is created
|
||||
automatically if it doesn't exist.
|
||||
'';
|
||||
};
|
||||
|
||||
logDir = mkOption {
|
||||
type = types.path;
|
||||
default = "/var/log/httpd";
|
||||
description = ''
|
||||
Directory for Apache's log files. It is created automatically.
|
||||
'';
|
||||
};
|
||||
|
||||
stateDir = mkOption {
|
||||
type = types.path;
|
||||
default = "/run/httpd";
|
||||
description = ''
|
||||
Directory for Apache's transient runtime state (such as PID
|
||||
files). It is created automatically. Note that the default,
|
||||
<filename>/run/httpd</filename>, is deleted at boot time.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualHosts = mkOption {
|
||||
type = types.listOf (types.submodule (
|
||||
{ options = import ./per-server-options.nix {
|
||||
inherit pkgs;
|
||||
forMainServer = false;
|
||||
};
|
||||
}));
|
||||
default = [];
|
||||
example = [
|
||||
{ hostName = "foo";
|
||||
documentRoot = "/data/webroot-foo";
|
||||
}
|
||||
{ hostName = "bar";
|
||||
documentRoot = "/data/webroot-bar";
|
||||
}
|
||||
];
|
||||
description = ''
|
||||
Specification of the virtual hosts served by Apache. Each
|
||||
element should be an attribute set specifying the
|
||||
configuration of the virtual host. The available options
|
||||
are the non-global options permissible for the main host.
|
||||
'';
|
||||
};
|
||||
|
||||
phpOptions = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
example =
|
||||
''
|
||||
date.timezone = "CET"
|
||||
'';
|
||||
description =
|
||||
"Options appended to the PHP configuration file <filename>php.ini</filename>.";
|
||||
};
|
||||
|
||||
multiProcessingModule = mkOption {
|
||||
type = types.str;
|
||||
default = "prefork";
|
||||
example = "worker";
|
||||
description =
|
||||
''
|
||||
Multi-processing module to be used by Apache. Available
|
||||
modules are <literal>prefork</literal> (the default;
|
||||
handles each request in a separate child process),
|
||||
<literal>worker</literal> (hybrid approach that starts a
|
||||
number of child processes each running a number of
|
||||
threads) and <literal>event</literal> (a recent variant of
|
||||
<literal>worker</literal> that handles persistent
|
||||
connections more efficiently).
|
||||
'';
|
||||
};
|
||||
|
||||
maxClients = mkOption {
|
||||
type = types.int;
|
||||
default = 150;
|
||||
example = 8;
|
||||
description = "Maximum number of httpd processes (prefork)";
|
||||
};
|
||||
|
||||
maxRequestsPerChild = mkOption {
|
||||
type = types.int;
|
||||
default = 0;
|
||||
example = 500;
|
||||
description =
|
||||
"Maximum number of httpd requests answered per httpd child (prefork), 0 means unlimited";
|
||||
};
|
||||
}
|
||||
|
||||
# Include the options shared between the main server and virtual hosts.
|
||||
// (import ./per-server-options.nix {
|
||||
inherit pkgs;
|
||||
forMainServer = true;
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = mkIf config.services.httpd.enable {
|
||||
|
||||
users.extraUsers.wwwrun =
|
||||
{ name = "wwwrun";
|
||||
group = "wwwrun";
|
||||
description = "Apache httpd user";
|
||||
};
|
||||
|
||||
users.extraGroups.wwwrun = {};
|
||||
|
||||
environment.systemPackages = [httpd] ++ concatMap (svc: svc.extraPath) allSubservices;
|
||||
|
||||
services.httpd.phpOptions =
|
||||
''
|
||||
; Needed for PHP's mail() function.
|
||||
sendmail_path = sendmail -t -i
|
||||
|
||||
; Apparently PHP doesn't use $TZ.
|
||||
date.timezone = "${config.time.timeZone}"
|
||||
'';
|
||||
|
||||
docker.buildScripts.apache =
|
||||
''
|
||||
mkdir -m 0750 -p ${mainCfg.stateDir}
|
||||
chown root.${mainCfg.group} ${mainCfg.stateDir}
|
||||
${optionalString version24 ''
|
||||
mkdir -m 0750 -p "${mainCfg.stateDir}/runtime"
|
||||
chown root.${mainCfg.group} "${mainCfg.stateDir}/runtime"
|
||||
''}
|
||||
mkdir -m 0700 -p ${mainCfg.logDir}
|
||||
|
||||
${optionalString (mainCfg.documentRoot != null)
|
||||
''
|
||||
# Create the document root directory if does not exists yet
|
||||
mkdir -p ${mainCfg.documentRoot}
|
||||
''
|
||||
}
|
||||
|
||||
# Get rid of old semaphores. These tend to accumulate across
|
||||
# server restarts, eventually preventing it from restarting
|
||||
# successfully.
|
||||
for i in $(${pkgs.utillinux}/bin/ipcs -s | grep ' ${mainCfg.user} ' | cut -f2 -d ' '); do
|
||||
${pkgs.utillinux}/bin/ipcrm -s $i
|
||||
done
|
||||
|
||||
# Run the startup hooks for the subservices.
|
||||
for i in ${toString (map (svn: svn.startupScript) allSubservices)}; do
|
||||
echo Running Apache startup hook $i...
|
||||
$i
|
||||
done
|
||||
'';
|
||||
|
||||
supervisord.services.httpd =
|
||||
{
|
||||
command = "${httpd}/bin/httpd -f ${httpdConf} -DFOREGROUND";
|
||||
};
|
||||
};
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
# This file defines the options that can be used both for the Apache
|
||||
# main server configuration, and for the virtual hosts. (The latter
|
||||
# has additional options that affect the web server as a whole, like
|
||||
# the user/group to run under.)
|
||||
|
||||
{ forMainServer, pkgs }:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
{
|
||||
|
||||
hostName = mkOption {
|
||||
type = types.str;
|
||||
default = "localhost";
|
||||
description = "Canonical hostname for the server.";
|
||||
};
|
||||
|
||||
serverAliases = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
example = ["www.example.org" "www.example.org:8080" "example.org"];
|
||||
description = ''
|
||||
Additional names of virtual hosts served by this virtual host configuration.
|
||||
'';
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.int;
|
||||
default = 0;
|
||||
description = ''
|
||||
Port for the server. 0 means use the default port: 80 for http
|
||||
and 443 for https (i.e. when enableSSL is set).
|
||||
'';
|
||||
};
|
||||
|
||||
enableSSL = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Whether to enable SSL (https) support.";
|
||||
};
|
||||
|
||||
# Note: sslServerCert and sslServerKey can be left empty, but this
|
||||
# only makes sense for virtual hosts (they will inherit from the
|
||||
# main server).
|
||||
|
||||
sslServerCert = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
example = "/var/host.cert";
|
||||
description = "Path to server SSL certificate.";
|
||||
};
|
||||
|
||||
sslServerKey = mkOption {
|
||||
type = types.path;
|
||||
example = "/var/host.key";
|
||||
description = "Path to server SSL certificate key.";
|
||||
};
|
||||
|
||||
adminAddr = mkOption ({
|
||||
type = types.nullOr types.str;
|
||||
example = "admin@example.org";
|
||||
description = "E-mail address of the server administrator.";
|
||||
} // (if forMainServer then {} else {default = null;}));
|
||||
|
||||
documentRoot = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
example = "/data/webserver/docs";
|
||||
description = ''
|
||||
The path of Apache's document root directory. If left undefined,
|
||||
an empty directory in the Nix store will be used as root.
|
||||
'';
|
||||
};
|
||||
|
||||
servedDirs = mkOption {
|
||||
type = types.listOf types.attrs;
|
||||
default = [];
|
||||
example = [
|
||||
{ urlPath = "/nix";
|
||||
dir = "/home/eelco/Dev/nix-homepage";
|
||||
}
|
||||
];
|
||||
description = ''
|
||||
This option provides a simple way to serve static directories.
|
||||
'';
|
||||
};
|
||||
|
||||
servedFiles = mkOption {
|
||||
type = types.listOf types.attrs;
|
||||
default = [];
|
||||
example = [
|
||||
{ urlPath = "/foo/bar.png";
|
||||
dir = "/home/eelco/some-file.png";
|
||||
}
|
||||
];
|
||||
description = ''
|
||||
This option provides a simple way to serve individual, static files.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
example = ''
|
||||
<Directory /home>
|
||||
Options FollowSymlinks
|
||||
AllowOverride All
|
||||
</Directory>
|
||||
'';
|
||||
description = ''
|
||||
These lines go to httpd.conf verbatim. They will go after
|
||||
directories and directory aliases defined by default.
|
||||
'';
|
||||
};
|
||||
|
||||
extraSubservices = mkOption {
|
||||
type = types.listOf types.unspecified;
|
||||
default = [];
|
||||
description = "Extra subservices to enable in the webserver.";
|
||||
};
|
||||
|
||||
enableUserDir = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to enable serving <filename>~/public_html</filename> as
|
||||
<literal>/~<replaceable>username</replaceable></literal>.
|
||||
'';
|
||||
};
|
||||
|
||||
globalRedirect = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = http://newserver.example.org/;
|
||||
description = ''
|
||||
If set, all requests for this host are redirected permanently to
|
||||
the given URL.
|
||||
'';
|
||||
};
|
||||
|
||||
logFormat = mkOption {
|
||||
type = types.str;
|
||||
default = "common";
|
||||
example = "combined";
|
||||
description = "
|
||||
Log format for Apache's log files. Possible values are: combined, common, referer, agent.
|
||||
";
|
||||
};
|
||||
|
||||
}
|
@ -1,333 +0,0 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
let
|
||||
|
||||
cfg = config.services.openssh;
|
||||
cfgc = config.programs.ssh;
|
||||
|
||||
nssModulesPath = config.system.nssModules.path;
|
||||
|
||||
permitRootLoginCheck = v:
|
||||
v == "yes" ||
|
||||
v == "without-password" ||
|
||||
v == "forced-commands-only" ||
|
||||
v == "no";
|
||||
|
||||
knownHosts = map (h: getAttr h cfg.knownHosts) (attrNames cfg.knownHosts);
|
||||
|
||||
knownHostsFile = pkgs.writeText "ssh_known_hosts" (
|
||||
flip concatMapStrings knownHosts (h:
|
||||
"${concatStringsSep "," h.hostNames} ${builtins.readFile h.publicKeyFile}"
|
||||
)
|
||||
);
|
||||
|
||||
userOptions = {
|
||||
|
||||
openssh.authorizedKeys = {
|
||||
keys = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = ''
|
||||
A list of verbatim OpenSSH public keys that should be added to the
|
||||
user's authorized keys. The keys are added to a file that the SSH
|
||||
daemon reads in addition to the the user's authorized_keys file.
|
||||
You can combine the <literal>keys</literal> and
|
||||
<literal>keyFiles</literal> options.
|
||||
'';
|
||||
};
|
||||
|
||||
keyFiles = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = ''
|
||||
A list of files each containing one OpenSSH public key that should be
|
||||
added to the user's authorized keys. The contents of the files are
|
||||
read at build time and added to a file that the SSH daemon reads in
|
||||
addition to the the user's authorized_keys file. You can combine the
|
||||
<literal>keyFiles</literal> and <literal>keys</literal> options.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
authKeysFiles = let
|
||||
mkAuthKeyFile = u: {
|
||||
target = "ssh/authorized_keys.d/${u.name}";
|
||||
mode = "0444";
|
||||
source = pkgs.writeText "${u.name}-authorized_keys" ''
|
||||
${concatStringsSep "\n" u.openssh.authorizedKeys.keys}
|
||||
${concatMapStrings (f: builtins.readFile f + "\n") u.openssh.authorizedKeys.keyFiles}
|
||||
'';
|
||||
};
|
||||
usersWithKeys = attrValues (flip filterAttrs config.users.extraUsers (n: u:
|
||||
length u.openssh.authorizedKeys.keys != 0 || length u.openssh.authorizedKeys.keyFiles != 0
|
||||
));
|
||||
in map mkAuthKeyFile usersWithKeys;
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
|
||||
services.openssh = {
|
||||
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to enable the OpenSSH secure shell daemon, which
|
||||
allows secure remote logins.
|
||||
'';
|
||||
};
|
||||
|
||||
forwardX11 = mkOption {
|
||||
type = types.bool;
|
||||
default = cfgc.setXAuthLocation;
|
||||
description = ''
|
||||
Whether to allow X11 connections to be forwarded.
|
||||
'';
|
||||
};
|
||||
|
||||
allowSFTP = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to enable the SFTP subsystem in the SSH daemon. This
|
||||
enables the use of commands such as <command>sftp</command> and
|
||||
<command>sshfs</command>.
|
||||
'';
|
||||
};
|
||||
|
||||
permitRootLogin = mkOption {
|
||||
default = "without-password";
|
||||
type = types.addCheck types.str permitRootLoginCheck;
|
||||
description = ''
|
||||
Whether the root user can login using ssh. Valid values are
|
||||
<literal>yes</literal>, <literal>without-password</literal>,
|
||||
<literal>forced-commands-only</literal> or
|
||||
<literal>no</literal>.
|
||||
'';
|
||||
};
|
||||
|
||||
gatewayPorts = mkOption {
|
||||
type = types.str;
|
||||
default = "no";
|
||||
description = ''
|
||||
Specifies whether remote hosts are allowed to connect to
|
||||
ports forwarded for the client. See
|
||||
<citerefentry><refentrytitle>sshd_config</refentrytitle>
|
||||
<manvolnum>5</manvolnum></citerefentry>.
|
||||
'';
|
||||
};
|
||||
|
||||
ports = mkOption {
|
||||
type = types.listOf types.int;
|
||||
default = [22];
|
||||
description = ''
|
||||
Specifies on which ports the SSH daemon listens.
|
||||
'';
|
||||
};
|
||||
|
||||
passwordAuthentication = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Specifies whether password authentication is allowed.
|
||||
'';
|
||||
};
|
||||
|
||||
challengeResponseAuthentication = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Specifies whether challenge/response authentication is allowed.
|
||||
'';
|
||||
};
|
||||
|
||||
hostKeys = mkOption {
|
||||
type = types.listOf types.attrs;
|
||||
default =
|
||||
[ { path = "/etc/ssh/ssh_host_dsa_key";
|
||||
type = "dsa";
|
||||
bits = 1024;
|
||||
}
|
||||
{ path = "/etc/ssh/ssh_host_ecdsa_key";
|
||||
type = "ecdsa";
|
||||
bits = 521;
|
||||
}
|
||||
];
|
||||
description = ''
|
||||
NixOS can automatically generate SSH host keys. This option
|
||||
specifies the path, type and size of each key. See
|
||||
<citerefentry><refentrytitle>ssh-keygen</refentrytitle>
|
||||
<manvolnum>1</manvolnum></citerefentry> for supported types
|
||||
and sizes.
|
||||
'';
|
||||
};
|
||||
|
||||
authorizedKeysFiles = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = "Files from with authorized keys are read.";
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = "Verbatim contents of <filename>sshd_config</filename>.";
|
||||
};
|
||||
|
||||
knownHosts = mkOption {
|
||||
default = {};
|
||||
type = types.loaOf types.optionSet;
|
||||
description = ''
|
||||
The set of system-wide known SSH hosts.
|
||||
'';
|
||||
example = [
|
||||
{
|
||||
hostNames = [ "myhost" "myhost.mydomain.com" "10.10.1.4" ];
|
||||
publicKeyFile = literalExample "./pubkeys/myhost_ssh_host_dsa_key.pub";
|
||||
}
|
||||
{
|
||||
hostNames = [ "myhost2" ];
|
||||
publicKeyFile = literalExample "./pubkeys/myhost2_ssh_host_dsa_key.pub";
|
||||
}
|
||||
];
|
||||
options = {
|
||||
hostNames = mkOption {
|
||||
type = types.listOf types.string;
|
||||
default = [];
|
||||
description = ''
|
||||
A list of host names and/or IP numbers used for accessing
|
||||
the host's ssh service.
|
||||
'';
|
||||
};
|
||||
publicKeyFile = mkOption {
|
||||
description = ''
|
||||
The path to the public key file for the host. The public
|
||||
key file is read at build time and saved in the Nix store.
|
||||
You can fetch a public key file from a running SSH server
|
||||
with the <command>ssh-keyscan</command> command.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
users.extraUsers = mkOption {
|
||||
options = [ userOptions ];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
users.extraUsers = singleton
|
||||
{ name = "sshd";
|
||||
uid = config.ids.uids.sshd;
|
||||
description = "SSH privilege separation user";
|
||||
home = "/var/empty";
|
||||
};
|
||||
|
||||
environment.etc = authKeysFiles ++ [
|
||||
{ source = "${pkgs.openssh}/etc/ssh/moduli";
|
||||
target = "ssh/moduli";
|
||||
}
|
||||
{ source = knownHostsFile;
|
||||
target = "ssh/ssh_known_hosts";
|
||||
}
|
||||
];
|
||||
|
||||
systemd.services.sshd =
|
||||
{ description = "SSH Daemon";
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
stopIfChanged = false;
|
||||
|
||||
path = [ pkgs.openssh pkgs.gawk ];
|
||||
|
||||
environment.LD_LIBRARY_PATH = "";
|
||||
environment.LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
|
||||
|
||||
preStart =
|
||||
''
|
||||
mkdir -m 0755 -p /etc/ssh
|
||||
|
||||
${flip concatMapStrings cfg.hostKeys (k: ''
|
||||
if ! [ -f "${k.path}" ]; then
|
||||
ssh-keygen -t "${k.type}" -b "${toString k.bits}" -f "${k.path}" -N ""
|
||||
fi
|
||||
'')}
|
||||
'';
|
||||
|
||||
serviceConfig =
|
||||
{ ExecStart =
|
||||
"${pkgs.openssh}/sbin/sshd " +
|
||||
"-f ${pkgs.writeText "sshd_config" cfg.extraConfig} -D";
|
||||
Restart = "always";
|
||||
KillMode = "process";
|
||||
PIDFile = "/run/sshd.pid";
|
||||
};
|
||||
};
|
||||
|
||||
services.openssh.authorizedKeysFiles =
|
||||
[ ".ssh/authorized_keys" ".ssh/authorized_keys2" "/etc/ssh/authorized_keys.d/%u" ];
|
||||
|
||||
services.openssh.extraConfig =
|
||||
''
|
||||
PidFile /run/sshd.pid
|
||||
|
||||
Protocol 2
|
||||
|
||||
UsePAM no
|
||||
|
||||
AddressFamily ${if config.networking.enableIPv6 then "any" else "inet"}
|
||||
${concatMapStrings (port: ''
|
||||
Port ${toString port}
|
||||
'') cfg.ports}
|
||||
|
||||
${optionalString cfgc.setXAuthLocation ''
|
||||
XAuthLocation ${pkgs.xorg.xauth}/bin/xauth
|
||||
''}
|
||||
|
||||
${if cfg.forwardX11 then ''
|
||||
X11Forwarding yes
|
||||
'' else ''
|
||||
X11Forwarding no
|
||||
''}
|
||||
|
||||
${optionalString cfg.allowSFTP ''
|
||||
Subsystem sftp ${pkgs.openssh}/libexec/sftp-server
|
||||
''}
|
||||
|
||||
PermitRootLogin ${cfg.permitRootLogin}
|
||||
GatewayPorts ${cfg.gatewayPorts}
|
||||
PasswordAuthentication ${if cfg.passwordAuthentication then "yes" else "no"}
|
||||
ChallengeResponseAuthentication ${if cfg.challengeResponseAuthentication then "yes" else "no"}
|
||||
|
||||
PrintMotd no # handled by pam_motd
|
||||
|
||||
AuthorizedKeysFile ${toString cfg.authorizedKeysFiles}
|
||||
|
||||
${flip concatMapStrings cfg.hostKeys (k: ''
|
||||
HostKey ${k.path}
|
||||
'')}
|
||||
'';
|
||||
|
||||
assertions = [{ assertion = if cfg.forwardX11 then cfgc.setXAuthLocation else true;
|
||||
message = "cannot enable X11 forwarding without setting xauth location";}];
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"name" : "nix-docker",
|
||||
"version" : "0.1.0",
|
||||
"dependencies" : {
|
||||
"optimist" : "0.6.0"
|
||||
},
|
||||
"directories": {
|
||||
"bin": "./bin"
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
# Expose the apache port (80)
|
||||
docker.ports = [
|
||||
config.services.httpd.port
|
||||
];
|
||||
|
||||
services.httpd = {
|
||||
enable = true;
|
||||
port = 80;
|
||||
documentRoot = ./www;
|
||||
adminAddr = "zef.hemel@logicblox.com";
|
||||
};
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
# Boots up all kinds of services: redis, apache, ssh, mysql
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
docker.ports = [ 1234 80 22 ];
|
||||
|
||||
services.redis = {
|
||||
enable = true;
|
||||
port = 1234;
|
||||
logLevel = "debug";
|
||||
};
|
||||
|
||||
services.httpd.enable = true;
|
||||
services.httpd.port = 80;
|
||||
services.httpd.documentRoot = ./www;
|
||||
services.httpd.adminAddr = "zef.hemel@logicblox.com";
|
||||
|
||||
services.mysql.enable = true;
|
||||
services.openssh.enable = true;
|
||||
|
||||
supervisord.tailLogs = true;
|
||||
|
||||
users.extraUsers.zef = {
|
||||
group = "users";
|
||||
home = "/";
|
||||
shell = "/bin/bash";
|
||||
createHome = true;
|
||||
openssh.authorizedKeys.keys = [ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCjWpdyDIsS09lWlOsMG9OMTHB/N/afVU12BwKcyjjhbezPdFEgHK4cZBN7m1bvoFKl832BdB+ZjeRH4UGBcUpvrFu1vE7Lf/0vZDU7qzzWQE9V+tfSPwDiXPf9QnCYeZmYPDHUHDUEse9LKBZbt6UKF1tuTD8ussV5jvEFBaesDhCqD1TJ4b4O877cdx9+VTOuDSEDm32jQ2az27d1b/5DoEKBe5cJSC3PhObAQ7OAYrVVBFX9ffKpaSvV6yqo+rhCmXP9DjNgBwMtElreoXL3h5Xbw2AiER5oHNUAEA2XGpnOVOr7ZZUAbMC0/0dq387jQZCqe7gIDZCqjDpGhUa9 zefhemel@gmail.com" ];
|
||||
};
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
# Runs an SSH server in a Docker container
|
||||
# Creates a user "you" that you can login with
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
docker.ports = [ 22 ];
|
||||
|
||||
services.openssh.enable = true;
|
||||
|
||||
users.extraUsers.you = {
|
||||
group = "users";
|
||||
home = "/";
|
||||
shell = "/bin/bash";
|
||||
createHome = true;
|
||||
openssh.authorizedKeys.keys = [
|
||||
# Replace with your own SSH key (e.g. from ~/.ssh/id_rsa.pub)
|
||||
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCjWpdyDIsS09lWlOsMG9OMTHB/N/afVU12BwKcyjjhbezPdFEgHK4cZBN7m1bvoFKl832BdB+ZjeRH4UGBcUpvrFu1vE7Lf/0vZDU7qzzWQE9V+tfSPwDiXPf9QnCYeZmYPDHUHDUEse9LKBZbt6UKF1tuTD8ussV5jvEFBaesDhCqD1TJ4b4O877cdx9+VTOuDSEDm32jQ2az27d1b/5DoEKBe5cJSC3PhObAQ7OAYrVVBFX9ffKpaSvV6yqo+rhCmXP9DjNgBwMtElreoXL3h5Xbw2AiER5oHNUAEA2XGpnOVOr7ZZUAbMC0/0dq387jQZCqe7gIDZCqjDpGhUa9"
|
||||
];
|
||||
};
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
{ config, pkgs, ... }:
|
||||
# We'll start with defining some local variables
|
||||
let
|
||||
# Settings used to configure the wordpress MySQL database
|
||||
mysqlHost = "localhost";
|
||||
mysqlDb = "wordpress";
|
||||
mysqlUser = "wordpress";
|
||||
mysqlPassword = "wordpress";
|
||||
|
||||
# Data paths
|
||||
mysqlDataPath = "/data/mysql";
|
||||
wordpressUploads = "/data/uploads";
|
||||
apacheLogs = "/data/log";
|
||||
|
||||
# Our bare-bones wp-config.php file using the above settings
|
||||
wordpressConfig = pkgs.writeText "wp-config.php" ''
|
||||
<?php
|
||||
define('DB_NAME', '${mysqlDb}');
|
||||
define('DB_USER', '${mysqlUser}');
|
||||
define('DB_PASSWORD', '${mysqlPassword}');
|
||||
define('DB_HOST', '${mysqlHost}');
|
||||
define('DB_CHARSET', 'utf8');
|
||||
$table_prefix = 'wp_';
|
||||
if ( !defined('ABSPATH') )
|
||||
define('ABSPATH', dirname(__FILE__) . '/');
|
||||
require_once(ABSPATH . 'wp-settings.php');
|
||||
'';
|
||||
|
||||
# .htaccess to support pretty URLs
|
||||
htaccess = pkgs.writeText "htaccess" ''
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule . /index.php [L]
|
||||
</IfModule>
|
||||
'';
|
||||
|
||||
# For shits and giggles, let's package the responsive theme
|
||||
responsiveTheme = pkgs.stdenv.mkDerivation {
|
||||
name = "responsive-theme";
|
||||
# Download the theme from the wordpress site
|
||||
src = pkgs.fetchurl {
|
||||
url = http://wordpress.org/themes/download/responsive.1.9.3.9.zip;
|
||||
sha256 = "0397a8nd8q384z2i4lw4a1ij835walp3b5bfdmr76mdl27vbkvmd";
|
||||
};
|
||||
# We need unzip to build this package
|
||||
buildInputs = [ pkgs.unzip ];
|
||||
# Installing simply means copying all files to the output directory
|
||||
installPhase = "mkdir -p $out; cp -R * $out/";
|
||||
};
|
||||
|
||||
# The wordpress package itself
|
||||
wordpress = pkgs.stdenv.mkDerivation {
|
||||
name = "wordpress";
|
||||
# Fetch directly from the wordpress site, want to upgrade?
|
||||
# Just change the version URL and update the hash
|
||||
src = pkgs.fetchurl {
|
||||
url = http://wordpress.org/wordpress-3.7.1.tar.gz;
|
||||
sha256 = "1m2dlr54fqf5m4kgqc5hrrisrsia0r5j1r2xv1f12gmzb63swsvg";
|
||||
};
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
# Copy all the wordpress files we downloaded
|
||||
cp -R * $out/
|
||||
# We'll symlink the wordpress config
|
||||
ln -s ${wordpressConfig} $out/wp-config.php
|
||||
# As well as our custom .htaccess
|
||||
ln -s ${htaccess} $out/.htaccess
|
||||
# And the uploads directory
|
||||
ln -s ${wordpressUploads} $out/wp-content/uploads
|
||||
# And the responsive theme
|
||||
ln -s ${responsiveTheme} $out/wp-content/themes/responsive
|
||||
# You can add plugins the same way
|
||||
'';
|
||||
};
|
||||
# This is where the body of our configuration starts
|
||||
in {
|
||||
# Expose just port 80
|
||||
docker.ports = [ 80 ];
|
||||
|
||||
# And let's store valuable data in a volume
|
||||
docker.volumes = [ "/data" ];
|
||||
|
||||
# Apache configuration
|
||||
services.httpd = {
|
||||
enable = true;
|
||||
adminAddr = "zef@zef.me";
|
||||
|
||||
# We'll set the wordpress package as our document root
|
||||
documentRoot = wordpress;
|
||||
|
||||
# Let's store our logs in the volume as well
|
||||
logDir = apacheLogs;
|
||||
|
||||
# And enable the PHP5 apache module
|
||||
extraModules = [ { name = "php5"; path = "${pkgs.php}/modules/libphp5.so"; } ];
|
||||
|
||||
# And some extra config to make things work nicely
|
||||
extraConfig = ''
|
||||
<Directory ${wordpress}>
|
||||
DirectoryIndex index.php
|
||||
Allow from *
|
||||
Options FollowSymLinks
|
||||
AllowOverride All
|
||||
</Directory>
|
||||
'';
|
||||
};
|
||||
|
||||
# Disable these when not using "localhost" as database name
|
||||
services.mysql.enable = true;
|
||||
# Let's store our data in the volume, so it'll survive restarts
|
||||
services.mysql.dataDir = mysqlDataPath;
|
||||
|
||||
# This service runs evey time you start and ensures two things:
|
||||
# 1. That our uploads directory exists and is owned by Apache
|
||||
# 2. that the MySQL database exists
|
||||
supervisord.services.initApp = {
|
||||
command = pkgs.writeScript "init-wordpress.sh" ''
|
||||
#!/bin/sh
|
||||
|
||||
mkdir -p ${wordpressUploads}
|
||||
chown ${config.services.httpd.user} ${wordpressUploads}
|
||||
|
||||
if [ ! -d /data/mysql/${mysqlDb} ]; then
|
||||
# Wait until MySQL is up
|
||||
while [ ! -e /var/run/mysql/mysqld.pid ]; do
|
||||
sleep 1
|
||||
done
|
||||
mysql -e 'CREATE DATABASE ${mysqlDb};'
|
||||
mysql -e 'GRANT ALL ON ${mysqlDb}.* TO ${mysqlUser}@localhost IDENTIFIED BY "${mysqlPassword}";'
|
||||
fi
|
||||
'';
|
||||
# This script can exit immediately, no worries
|
||||
startsecs = 0;
|
||||
};
|
||||
}
|
@ -1 +0,0 @@
|
||||
Hello world 2
|
@ -1 +0,0 @@
|
||||
These are scripts to automatically install Docker and Nix and has been tested to work on Ubuntu 13.04.
|
@ -1,21 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ "$(which docker)" != "" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
apt-get update
|
||||
|
||||
apt-get install -y linux-image-extra-`uname -r`
|
||||
|
||||
sh -c "wget -qO- https://get.docker.io/gpg | apt-key add -"
|
||||
|
||||
# Add the Docker repository to your apt sources list.
|
||||
sh -c "echo deb http://get.docker.io/ubuntu docker main\
|
||||
> /etc/apt/sources.list.d/docker.list"
|
||||
|
||||
# update
|
||||
apt-get update
|
||||
|
||||
# install
|
||||
apt-get install -y lxc-docker
|
@ -1,105 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
# Check if Nix is already installed
|
||||
|
||||
if [ -d "/nix/store" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Install the binary tarball...
|
||||
apt-get install -y curl
|
||||
cd /
|
||||
curl -L http://hydra.nixos.org/job/nix/trunk/binaryTarball.x86_64-linux/latest/download | tar xj
|
||||
/usr/bin/nix-finish-install
|
||||
rm /usr/bin/nix-finish-install
|
||||
|
||||
# Hack
|
||||
chmod 777 /nix/var/nix/profiles
|
||||
|
||||
# Setup multiuserbu
|
||||
|
||||
# Allow all users to create profiles
|
||||
mkdir -p /nix/var/nix/profiles/per-user
|
||||
chmod 1777 /nix/var/nix/profiles/per-user
|
||||
|
||||
# Add build users
|
||||
# 9 is the exit code when the group already exists
|
||||
groupadd -r nixbld || [ "$?" -eq 9 ]
|
||||
for n in 1 2 3 4 5 6 7 8 9 10; do
|
||||
useradd -c "Nix build user $n" -d /var/empty -g nixbld -G nixbld \
|
||||
-M -N -r -s `which nologin` nixbld$n || [ "$?" -eq 9 ]
|
||||
done
|
||||
chown root:nixbld /nix/store
|
||||
chmod 1775 /nix/store
|
||||
mkdir -p /etc/nix
|
||||
grep -w build-users-group /etc/nix/nix.conf 2>/dev/null || echo "build-users-group = nixbld" >> /etc/nix/nix.conf
|
||||
grep -w binary-caches /etc/nix/nix.conf 2>/dev/null || echo "binary-caches = http://cache.nixos.org" >> /etc/nix/nix.conf
|
||||
grep -w trusted-binary-caches /etc/nix/nix.conf 2>/dev/null || echo "trusted-binary-caches = http://hydra.nixos.org http://cache.nixos.org" >> /etc/nix/nix.conf
|
||||
|
||||
# Use a multiuser-compatible profile script
|
||||
unlink /etc/profile.d/nix.sh
|
||||
cat > /etc/profile.d/nix.sh <<EOF
|
||||
if test -n "\$HOME"; then
|
||||
NIX_LINK="\$HOME/.nix-profile"
|
||||
|
||||
if [ -w /nix/var/nix/db ]; then
|
||||
OWNS_STORE=1
|
||||
fi
|
||||
|
||||
# Set the default profile.
|
||||
if ! [ -L "\$NIX_LINK" ]; then
|
||||
echo "creating \$NIX_LINK" >&2
|
||||
mkdir -p "/nix/var/nix/profiles/per-user/\$LOGNAME"
|
||||
_NIX_PROFILE_LINK="/nix/var/nix/profiles/per-user/\$LOGNAME/profile"
|
||||
ln -s /nix/var/nix/profiles/default \$_NIX_PROFILE_LINK
|
||||
ln -s "\$_NIX_PROFILE_LINK" "\$NIX_LINK"
|
||||
fi
|
||||
|
||||
# Subscribe the root user to the Nixpkgs channel by default.
|
||||
if [ ! -e "\$HOME/.nix-channels" ]; then
|
||||
echo "http://nixos.org/channels/nixpkgs-unstable nixpkgs" > "\$HOME/.nix-channels"
|
||||
fi
|
||||
|
||||
# Set up nix-defexpr
|
||||
NIX_DEFEXPR="\$HOME/.nix-defexpr"
|
||||
if ! [ -e "\$NIX_DEFEXPR" ]; then
|
||||
echo "creating \$NIX_DEFEXPR" >&2
|
||||
mkdir -p "\$NIX_DEFEXPR"
|
||||
_NIX_CHANNEL_LINK=/nix/var/nix/profiles/per-user/root/channels
|
||||
ln -s "\$_NIX_CHANNEL_LINK" "\$NIX_DEFEXPR/channels"
|
||||
#/nix/var/nix/profiles/default/bin/nix-channel --update
|
||||
fi
|
||||
|
||||
if [ -z "\$OWNS_STORE" ]; then
|
||||
export NIX_REMOTE=daemon
|
||||
export PATH="/nix/var/nix/profiles/default/bin:\$PATH"
|
||||
fi
|
||||
export PATH="\$NIX_LINK/bin:\$PATH"
|
||||
|
||||
# Set up NIX_PATH
|
||||
export NIX_PATH="\$NIX_DEFEXPR/channels"
|
||||
unset OWNS_STORE
|
||||
fi
|
||||
EOF
|
||||
|
||||
# Add default nix profile to global path and enable it during sudo
|
||||
sed -i 's/"$/:\/nix\/var\/nix\/profiles\/default\/bin"/' /etc/environment
|
||||
sed -i 's/secure_path="/secure_path="\/nix\/var\/nix\/profiles\/default\/bin:/' /etc/sudoers
|
||||
|
||||
# Install upstart job
|
||||
cat > /etc/init/nix-daemon.conf <<EOF
|
||||
description "Nix Daemon"
|
||||
start on filesystem
|
||||
stop on shutdown
|
||||
respawn
|
||||
env NIX_CONF_DIR="/etc/nix"
|
||||
exec $(readlink -f /nix/var/nix/profiles/default/bin/nix-daemon) --daemon
|
||||
EOF
|
||||
|
||||
# Start nix daemon
|
||||
initctl start nix-daemon
|
||||
|
||||
# Update the nix channel
|
||||
/nix/var/nix/profiles/default/bin/nix-channel --update
|
@ -10,10 +10,6 @@ let
|
||||
default = "/";
|
||||
description = "Current directory when running the command";
|
||||
};
|
||||
user = mkOption {
|
||||
default = "root";
|
||||
description = "The user to run the command as";
|
||||
};
|
||||
environment = mkOption {
|
||||
default = {};
|
||||
example = {
|
||||
@ -59,7 +55,7 @@ in {
|
||||
config = mkIf config.supervisord.enable {
|
||||
supervisord.configFile = pkgs.writeText "supervisord.conf" ''
|
||||
[supervisord]
|
||||
logfile=/var/log/supervisord/supervisord.log
|
||||
logfile=/tmp/supervisor/var/log/supervisord/supervisord.log
|
||||
|
||||
${concatMapStrings (name:
|
||||
let
|
||||
@ -71,22 +67,25 @@ in {
|
||||
environment=${concatMapStrings (name: "${name}=\"${toString (getAttr name cfg.environment)}\",") (attrNames cfg.environment)}
|
||||
directory=${cfg.directory}
|
||||
redirect_stderr=true
|
||||
stdout_logfile=/var/log/supervisord/${name}.log
|
||||
user=${cfg.user}
|
||||
stdout_logfile=/tmp/supervisor/var/log/supervisord/${name}.log
|
||||
startsecs=${toString cfg.startsecs}
|
||||
''
|
||||
) (attrNames services)
|
||||
}
|
||||
'';
|
||||
|
||||
docker.bootScript = ''
|
||||
mkdir -p /var/log/supervisord
|
||||
userNix.startScript = let
|
||||
systemPackages = config.environment.systemPackages;
|
||||
systemEnv = pkgs.buildEnv { name = "system-env"; paths = systemPackages; };
|
||||
in ''
|
||||
mkdir -p /tmp/supervisor/var/log/supervisord
|
||||
export PATH="${systemEnv}/bin:${systemEnv}/sbin"
|
||||
${pkgs.pythonPackages.supervisor}/bin/supervisord -c ${config.supervisord.configFile} ${if config.supervisord.tailLogs then ''
|
||||
|
||||
sleep 2
|
||||
touch /var/log/supervisord/test.log
|
||||
touch $(pwd)/var/log/supervisord/test.log
|
||||
tail -n 100 -f /var/log/supervisord/*.log
|
||||
'' else "-n"}
|
||||
'';
|
||||
};
|
||||
}
|
||||
}
|
@ -9,11 +9,19 @@ let
|
||||
|
||||
oneShotServices = filterAttrs (name: cfg: isOneShot cfg) services;
|
||||
|
||||
filterCommand = cmd:
|
||||
let
|
||||
filtered = substring 1 (stringLength cmd -2) cmd;
|
||||
splitted = pkgs.lib.splitString " " filtered;
|
||||
in if eqStrings (substring 0 1 cmd) "@" then
|
||||
traceVal (head splitted) + concatStringsSep " " (drop 2 splitted)
|
||||
else cmd;
|
||||
|
||||
configToCommand = name: cfg: ''
|
||||
#!/bin/sh -e
|
||||
${if hasAttr "preStart" cfg then cfg.preStart else ""}
|
||||
${if hasAttr "ExecStart" cfg.serviceConfig then
|
||||
cfg.serviceConfig.ExecStart
|
||||
filterCommand cfg.serviceConfig.ExecStart
|
||||
else if hasAttr "script" cfg then
|
||||
cfg.script
|
||||
else
|
||||
@ -31,23 +39,23 @@ in {
|
||||
};
|
||||
|
||||
config = {
|
||||
docker.buildScripts."1-systemd-oneshot" = concatMapStrings (name: "${configToCommand name (getAttr name oneShotServices)}\n") (attrNames oneShotServices);
|
||||
userNix.startScripts."1-systemd-oneshot" = concatMapStrings (name: "${configToCommand name (getAttr name oneShotServices)}\n") (attrNames oneShotServices);
|
||||
|
||||
supervisord.services = listToAttrs (map (name:
|
||||
let
|
||||
cfg = getAttr name runServices;
|
||||
cfg = getAttr name services;
|
||||
in
|
||||
{
|
||||
name = name;
|
||||
value = {
|
||||
command = pkgs.writeScript "${name}-run" (configToCommand name cfg);
|
||||
user = if hasAttr "User" cfg.serviceConfig then cfg.serviceConfig.User else "root";
|
||||
environment = (if hasAttr "environment" cfg then cfg.environment else {}) //
|
||||
(if hasAttr "path" cfg then
|
||||
{ PATH = concatStringsSep ":" (map (prg: "${prg}/bin") cfg.path); }
|
||||
else {});
|
||||
{ PATH = "%(ENV_PATH)s:" + concatStringsSep ":" (map (prg: "${prg}/bin") cfg.path); }
|
||||
else {
|
||||
PATH="%(ENV_PATH)s"; });
|
||||
};
|
||||
}
|
||||
) (attrNames runServices));
|
||||
};
|
||||
}
|
||||
}
|
17
user.nix
Normal file
17
user.nix
Normal file
@ -0,0 +1,17 @@
|
||||
{ config, pkgs, ... }:
|
||||
with pkgs.lib;
|
||||
{
|
||||
options = {
|
||||
userNix.startScripts = mkOption {
|
||||
default = {};
|
||||
description = "Scripts (as text) to be run during build, executed alphabetically";
|
||||
};
|
||||
|
||||
userNix.startScript = mkOption {};
|
||||
|
||||
};
|
||||
|
||||
config = {
|
||||
userNix.startScript = concatStrings (attrValues config.userNix.startScripts);
|
||||
};
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"modes": {
|
||||
"python": {
|
||||
"filenames": ["nix-docker"]
|
||||
},
|
||||
"ruby": {
|
||||
"filenames": ["Vagrantfile"]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user