mirror of
https://github.com/archtechx/nix.git
synced 2025-12-14 04:14:04 +00:00
Compare commits
No commits in common. "ffa4ebb11a5eb6c3321e1d6496b0ba786554c74f" and "4bc7ebaf4c92ce746d1258ca53054aa8fd55f9c6" have entirely different histories.
ffa4ebb11a
...
4bc7ebaf4c
5 changed files with 31 additions and 266 deletions
86
README.md
86
README.md
|
|
@ -107,15 +107,14 @@ The default php-fpm opcache configuration is to cache everything *forever* witho
|
||||||
revalidation. Therefore, make sure to include `sudo systemctl reload phpfpm-${name}` in
|
revalidation. Therefore, make sure to include `sudo systemctl reload phpfpm-${name}` in
|
||||||
your deployment script.
|
your deployment script.
|
||||||
|
|
||||||
To deploy your app, you can use
|
To deploy your app, you can use ssh deployments, rather than webhooks triggering pull hooks
|
||||||
[ssh deployments](https://stancl.substack.com/i/170830424/setting-up-deployments),
|
or other techniques. Since this module creates a new user for each site, this deployment
|
||||||
rather than webhooks triggering pull hooks or other techniques. Since this module
|
technique becomes non-problematic and it's one of the simplest things you can do. Just
|
||||||
creates a new user for each site, this deployment technique becomes non-problematic
|
ssh-keygen a private key, make a GitHub Actions job use that on push, and include the
|
||||||
and it's one of the simplest things you can do. Just ssh-keygen a private key, make a
|
public key in the site's `sshKeys` array. Then, to be able to `git pull` the site on the
|
||||||
GitHub Actions job use that on push, and include the public key in the site's `sshKeys` array.
|
server, add the user's `~/.ssh/id_ed25519.pub` to the repository's deployment keys. The
|
||||||
Then, to be able to `git pull` the site on the server, add the user's `~/.ssh/id_ed25519.pub`
|
ssh key for the user is generated automatically (can be disabled by setting `generateSshKey`
|
||||||
to the repository's deployment keys. The ssh key for the user is generated automatically
|
to false).
|
||||||
(can be disabled by setting `generateSshKey` to false).
|
|
||||||
|
|
||||||
Also, if you're using `ssl` you should put this line into your system config:
|
Also, if you're using `ssl` you should put this line into your system config:
|
||||||
```nix
|
```nix
|
||||||
|
|
@ -171,33 +170,15 @@ Simply `scp laravel.nix root@<your server ip>:/etc/nixos/` and start writing con
|
||||||
|
|
||||||
### www redirects
|
### www redirects
|
||||||
|
|
||||||
To redirect `www.acme.com` to `acme.com`, you can use the `wwwRedirect` attribute. It should be
|
The module doesn't handle www redirects automatically. This may be added in the future.
|
||||||
null for no redirect, or an integer status code for an enabled redirect.
|
|
||||||
|
|
||||||
```nix
|
At this time, I'd recommend handling basic redirects like that on Cloudflare.
|
||||||
(laravelSite {
|
|
||||||
name = "foo";
|
|
||||||
domains = [ "foo.com" ]
|
|
||||||
wwwRedirect = 301; # permanent redirect
|
|
||||||
# ...
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
With the config above, `www.foo.com/bar` will return a redirect to `foo.com/bar`, with the schema
|
|
||||||
matching the site's `ssl` config.
|
|
||||||
|
|
||||||
### Default nginx server
|
### Default nginx server
|
||||||
|
|
||||||
Out of the box, if nginx cannot match an incoming request's host to a specific virtual host it will
|
Out of the box, if nginx cannot match an incoming request's host to a specific virtual host it will
|
||||||
just use _some_ vhost. You can prevent behavior that by adding a module like this:
|
just use _some_ vhost. You can prevent behavior that by adding a module like this:
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> You can also use the `catchall.nix` module here (which includes the code below):
|
|
||||||
>
|
|
||||||
> `scp catchall.nix root@<server ip>:/etc/nixos/`
|
|
||||||
>
|
|
||||||
> Then just add `./catchall.nix` to your modules array.
|
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
{
|
{
|
||||||
services.nginx.virtualHosts."catchall" = {
|
services.nginx.virtualHosts."catchall" = {
|
||||||
|
|
@ -277,13 +258,6 @@ However a more proper solution is to use the `real_ip` module in common nginx co
|
||||||
we can follow the [guide from the NixOS
|
we can follow the [guide from the NixOS
|
||||||
wiki](https://nixos.wiki/wiki/Nginx#Using_realIP_when_behind_CloudFlare_or_other_CDN).
|
wiki](https://nixos.wiki/wiki/Nginx#Using_realIP_when_behind_CloudFlare_or_other_CDN).
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> You can also use the `realip.nix` module here (which wraps the code below):
|
|
||||||
>
|
|
||||||
> `scp realip.nix root@<server ip>:/etc/nixos/`
|
|
||||||
>
|
|
||||||
> Then just add `./realip.nix` to your modules array.
|
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
# New module in your modules array
|
# New module in your modules array
|
||||||
{
|
{
|
||||||
|
|
@ -325,38 +299,6 @@ curl -s https://www.cloudflare.com/ips-v4 | sha256 | xargs nix hash convert --ha
|
||||||
curl -s https://www.cloudflare.com/ips-v6 | sha256 | xargs nix hash convert --hash-algo sha256 --to nix32
|
curl -s https://www.cloudflare.com/ips-v6 | sha256 | xargs nix hash convert --hash-algo sha256 --to nix32
|
||||||
```
|
```
|
||||||
|
|
||||||
## Static sites
|
|
||||||
|
|
||||||
For hosting static sites, you can use `static.nix` very similarly to `laravel.nix`. Notable differences:
|
|
||||||
1. `root` is required, e.g. `name="foo"; root="build";` means `/srv/foo/build` will be served. In other
|
|
||||||
words, even though this is for static sites, we do not serve the entire `/srv/{name}` dir to allow
|
|
||||||
for version control and build steps.
|
|
||||||
2. By default, the `static-generic` user is used. Static sites do not always need strict user separation
|
|
||||||
since there's no request runtime. That said, the user is *very* limited and only has `pkgs.git` and
|
|
||||||
`pkgs.unzip`. Therefore it's only suited for static sites that are at most pulled from somewhere,
|
|
||||||
rather than built using Node.js. Also note that GitHub generally doesn't allow using a single SSH key
|
|
||||||
as the deploy key on multiple repos. For these reasons, it's still recommended to enable user creation
|
|
||||||
via `user = true;`.
|
|
||||||
|
|
||||||
Full usage:
|
|
||||||
```nix
|
|
||||||
(staticSite {
|
|
||||||
name = "foo"; # name of the site
|
|
||||||
root = "build"; # directory within /srv/foo to be served by nginx
|
|
||||||
|
|
||||||
user = true; # if false, static-generic is used. Default: false
|
|
||||||
domains = [ "foo.com" "bar.com" ]; # domains to serve the site on
|
|
||||||
ssl = true; # enableACME + forceSSL. Default: false
|
|
||||||
# Status code for www-to-non-www redirects. No redirect if null. Applies to all sites
|
|
||||||
wwwRedirect = 301; # Default: null
|
|
||||||
cloudflareOnly = true; # use Authenticated Origin Pulls. See the dedicated section. Default: false
|
|
||||||
extraPackages = [ pkgs.nodejs_24 ]; # only applies if user=true
|
|
||||||
generateSshKey = true; # defaults to true, used even with user=false
|
|
||||||
sshKeys = [ "array" "of" "public" "ssh" "keys" ]; # optional
|
|
||||||
extraNginxConfig = "nginx configuration string"; # optional
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Maintenance
|
## Maintenance
|
||||||
|
|
||||||
It's a good idea to have `/etc/nixos` tracked in version control so you can easily revert the config
|
It's a good idea to have `/etc/nixos` tracked in version control so you can easily revert the config
|
||||||
|
|
@ -381,11 +323,3 @@ Then clean garbage:
|
||||||
```sh
|
```sh
|
||||||
sudo nix-collect-garbage -d
|
sudo nix-collect-garbage -d
|
||||||
```
|
```
|
||||||
|
|
||||||
## Rebuilding
|
|
||||||
|
|
||||||
From personal testing, running `nixos-rebuild switch` doesn't necessarily cause any downtime for users
|
|
||||||
if your website is behind Cloudflare. NixOS first builds everything it needs and only then, usually pretty
|
|
||||||
quickly, restarts (and adds, removes, etc) services as needed. This means your nginx **might** be down for
|
|
||||||
a very brief period, but if Cloudflare cannot connect to your server it will retry a couple of times. So at
|
|
||||||
most some requests will be very slightly delayed, but users should not see any errors on most rebuilds.
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
{
|
|
||||||
services.nginx.virtualHosts."catchall" = {
|
|
||||||
default = true;
|
|
||||||
locations."/".return = "444";
|
|
||||||
rejectSSL = true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
52
laravel.nix
52
laravel.nix
|
|
@ -3,7 +3,6 @@
|
||||||
phpPackage, # e.g. pkgs.php84
|
phpPackage, # e.g. pkgs.php84
|
||||||
domains ? [], # e.g. [ "example.com" "acme.com" ]
|
domains ? [], # e.g. [ "example.com" "acme.com" ]
|
||||||
ssl ? false, # Should SSL be used
|
ssl ? false, # Should SSL be used
|
||||||
wwwRedirect ? null, # The status code used for www-to-non-www redirects. Null means no redirect
|
|
||||||
cloudflareOnly ? false, # Should CF Authenticated Origin Pulls be used
|
cloudflareOnly ? false, # Should CF Authenticated Origin Pulls be used
|
||||||
extraNginxConfig ? null, # Extra nginx config string
|
extraNginxConfig ? null, # Extra nginx config string
|
||||||
sshKeys ? null, # SSH public keys used to log into the site's user for deployments
|
sshKeys ? null, # SSH public keys used to log into the site's user for deployments
|
||||||
|
|
@ -32,7 +31,7 @@
|
||||||
|
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
username = "laravel-${name}";
|
mkUsername = siteName: "laravel-${siteName}";
|
||||||
in {
|
in {
|
||||||
services.nginx.enable = true;
|
services.nginx.enable = true;
|
||||||
security.acme.acceptTerms = lib.mkIf ssl true;
|
security.acme.acceptTerms = lib.mkIf ssl true;
|
||||||
|
|
@ -42,14 +41,13 @@ in {
|
||||||
|
|
||||||
# Create welcome message for user
|
# Create welcome message for user
|
||||||
# todo: the created /etc file should ideally be 0750
|
# todo: the created /etc file should ideally be 0750
|
||||||
# Note: keep in sync with static.nix
|
|
||||||
environment.etc."laravel-${name}-bashrc".text = ''
|
environment.etc."laravel-${name}-bashrc".text = ''
|
||||||
export PATH="$HOME/.config/composer/vendor/bin/:$PATH"
|
export PATH="$HOME/.config/composer/vendor/bin/:$PATH"
|
||||||
|
|
||||||
# Laravel site welcome message
|
# Laravel site welcome message
|
||||||
echo "Welcome to ${name} Laravel site!"
|
echo "Welcome to ${name} Laravel site!"
|
||||||
echo "Domains: ${lib.concatStringsSep ", " domains}"
|
echo "Domains: ${lib.concatStringsSep ", " domains}"
|
||||||
echo "User home: /home/${username}"
|
echo "User home: /home/${mkUsername name}"
|
||||||
echo "Site: /srv/${name}"
|
echo "Site: /srv/${name}"
|
||||||
echo "Restart php-fpm: sudo systemctl reload phpfpm-${name}"
|
echo "Restart php-fpm: sudo systemctl reload phpfpm-${name}"
|
||||||
${lib.optionalString queue ''echo "Restart queue: php artisan queue:restart"''}
|
${lib.optionalString queue ''echo "Restart queue: php artisan queue:restart"''}
|
||||||
|
|
@ -62,12 +60,12 @@ in {
|
||||||
systemd.tmpfiles.rules = [
|
systemd.tmpfiles.rules = [
|
||||||
"d /srv 0751 root root - -"
|
"d /srv 0751 root root - -"
|
||||||
"d /home 0751 root root - -"
|
"d /home 0751 root root - -"
|
||||||
"d /srv/${name} 0750 ${username} ${username} - -"
|
"d /srv/${name} 0750 ${mkUsername name} ${mkUsername name} - -"
|
||||||
"C /home/${username}/.bashrc 0640 ${username} ${username} - /etc/laravel-${name}-bashrc"
|
"C /home/${mkUsername name}/.bashrc 0640 ${mkUsername name} ${mkUsername name} - /etc/laravel-${name}-bashrc"
|
||||||
];
|
];
|
||||||
|
|
||||||
services.cron.systemCronJobs = [
|
services.cron.systemCronJobs = [
|
||||||
"* * * * * ${username} cd /srv/${name} && ${phpPackage}/bin/php artisan schedule:run > /dev/null 2>&1"
|
"* * * * * ${mkUsername name} cd /srv/${name} && ${phpPackage}/bin/php artisan schedule:run > /dev/null 2>&1"
|
||||||
];
|
];
|
||||||
|
|
||||||
# Laravel queue worker service
|
# Laravel queue worker service
|
||||||
|
|
@ -77,8 +75,8 @@ in {
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "simple";
|
Type = "simple";
|
||||||
User = username;
|
User = mkUsername name;
|
||||||
Group = username;
|
Group = mkUsername name;
|
||||||
WorkingDirectory = "/srv/${name}";
|
WorkingDirectory = "/srv/${name}";
|
||||||
ExecStart = "${phpPackage}/bin/php artisan queue:work ${queueArgs}";
|
ExecStart = "${phpPackage}/bin/php artisan queue:work ${queueArgs}";
|
||||||
Restart = "always";
|
Restart = "always";
|
||||||
|
|
@ -90,9 +88,8 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
# SSH key generation for git deployments
|
# SSH key generation for git deployments
|
||||||
# Note: keep in sync with static.nix
|
|
||||||
systemd.services."generate-ssh-key-${name}" = lib.mkIf generateSshKey {
|
systemd.services."generate-ssh-key-${name}" = lib.mkIf generateSshKey {
|
||||||
description = "Generate SSH key for ${username}";
|
description = "Generate SSH key for ${mkUsername name}";
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
after = [ "users.target" ];
|
after = [ "users.target" ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
|
|
@ -101,15 +98,15 @@ in {
|
||||||
User = "root";
|
User = "root";
|
||||||
};
|
};
|
||||||
script = ''
|
script = ''
|
||||||
USER_HOME="/home/${username}"
|
USER_HOME="/home/${mkUsername name}"
|
||||||
SSH_DIR="$USER_HOME/.ssh"
|
SSH_DIR="$USER_HOME/.ssh"
|
||||||
KEY_FILE="$SSH_DIR/id_ed25519"
|
KEY_FILE="$SSH_DIR/id_ed25519"
|
||||||
|
|
||||||
if [[ ! -f "$KEY_FILE" ]]; then
|
if [[ ! -f "$KEY_FILE" ]]; then
|
||||||
echo "Generating SSH key for ${username}"
|
echo "Generating SSH key for ${mkUsername name}"
|
||||||
mkdir -p "$SSH_DIR"
|
mkdir -p "$SSH_DIR"
|
||||||
${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f "$KEY_FILE" -N "" -C "${username}"
|
${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f "$KEY_FILE" -N "" -C "${mkUsername name}"
|
||||||
chown -R ${username}:${username} "$SSH_DIR"
|
chown -R ${mkUsername name}:${mkUsername name} "$SSH_DIR"
|
||||||
chmod 700 "$SSH_DIR"
|
chmod 700 "$SSH_DIR"
|
||||||
chmod 600 "$KEY_FILE"
|
chmod 600 "$KEY_FILE"
|
||||||
chmod 640 "$KEY_FILE.pub"
|
chmod 640 "$KEY_FILE.pub"
|
||||||
|
|
@ -117,7 +114,7 @@ in {
|
||||||
echo "Public key for deploy key:"
|
echo "Public key for deploy key:"
|
||||||
cat "$KEY_FILE.pub"
|
cat "$KEY_FILE.pub"
|
||||||
else
|
else
|
||||||
echo "SSH key already exists for ${username}"
|
echo "SSH key already exists for ${mkUsername name}"
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
@ -172,18 +169,11 @@ in {
|
||||||
deny all;
|
deny all;
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
}) // lib.optionalAttrs (wwwRedirect != null) (lib.genAttrs (map (domain: "www.${domain}") domains) (wwwDomain: {
|
});
|
||||||
enableACME = ssl;
|
|
||||||
forceSSL = ssl;
|
|
||||||
|
|
||||||
locations."/" = {
|
|
||||||
return = "${toString wwwRedirect} ${if ssl then "https" else "http"}://${lib.removePrefix "www." wwwDomain}$request_uri";
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
|
|
||||||
# PHP-FPM pool configuration
|
# PHP-FPM pool configuration
|
||||||
services.phpfpm.pools.${name} = {
|
services.phpfpm.pools.${name} = {
|
||||||
user = username;
|
user = mkUsername name;
|
||||||
phpPackage = phpPackage;
|
phpPackage = phpPackage;
|
||||||
settings = poolSettings // extraPoolSettings // {
|
settings = poolSettings // extraPoolSettings // {
|
||||||
"listen.owner" = config.services.nginx.user;
|
"listen.owner" = config.services.nginx.user;
|
||||||
|
|
@ -191,11 +181,11 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
# User and group settings
|
# User and group settings
|
||||||
users.users.${username} = {
|
users.users.${mkUsername name} = {
|
||||||
group = username;
|
group = mkUsername name;
|
||||||
isSystemUser = true;
|
isSystemUser = true;
|
||||||
createHome = true;
|
createHome = true;
|
||||||
home = "/home/${username}";
|
home = "/home/${mkUsername name}";
|
||||||
homeMode = "750";
|
homeMode = "750";
|
||||||
shell = pkgs.bashInteractive;
|
shell = pkgs.bashInteractive;
|
||||||
packages = [ phpPackage pkgs.git pkgs.unzip phpPackage.packages.composer ] ++ extraPackages;
|
packages = [ phpPackage pkgs.git pkgs.unzip phpPackage.packages.composer ] ++ extraPackages;
|
||||||
|
|
@ -203,14 +193,14 @@ in {
|
||||||
openssh.authorizedKeys.keys = sshKeys;
|
openssh.authorizedKeys.keys = sshKeys;
|
||||||
};
|
};
|
||||||
|
|
||||||
users.groups.${username} = {};
|
users.groups.${mkUsername name} = {};
|
||||||
|
|
||||||
# Add site group to nginx service
|
# Add site group to nginx service
|
||||||
systemd.services.nginx.serviceConfig.SupplementaryGroups = [ username ];
|
systemd.services.nginx.serviceConfig.SupplementaryGroups = [ (mkUsername name) ];
|
||||||
|
|
||||||
# Sudo rules for service management
|
# Sudo rules for service management
|
||||||
security.sudo.extraRules = [{
|
security.sudo.extraRules = [{
|
||||||
users = [ username ];
|
users = [ (mkUsername name) ];
|
||||||
commands = [
|
commands = [
|
||||||
{
|
{
|
||||||
command = "/run/current-system/sw/bin/systemctl reload phpfpm-${name}";
|
command = "/run/current-system/sw/bin/systemctl reload phpfpm-${name}";
|
||||||
|
|
|
||||||
20
realip.nix
20
realip.nix
|
|
@ -1,20 +0,0 @@
|
||||||
{ pkgs, lib, ... }: {
|
|
||||||
services.nginx.commonHttpConfig =
|
|
||||||
let
|
|
||||||
realIpsFromList = lib.strings.concatMapStringsSep "\n" (x: "set_real_ip_from ${x};");
|
|
||||||
fileToList = x: lib.strings.splitString "\n" (builtins.readFile x);
|
|
||||||
cfipv4 = fileToList (pkgs.fetchurl {
|
|
||||||
url = "https://www.cloudflare.com/ips-v4";
|
|
||||||
sha256 = "0ywy9sg7spafi3gm9q5wb59lbiq0swvf0q3iazl0maq1pj1nsb7h";
|
|
||||||
});
|
|
||||||
cfipv6 = fileToList (pkgs.fetchurl {
|
|
||||||
url = "https://www.cloudflare.com/ips-v6";
|
|
||||||
sha256 = "1ad09hijignj6zlqvdjxv7rjj8567z357zfavv201b9vx3ikk7cy";
|
|
||||||
});
|
|
||||||
in
|
|
||||||
''
|
|
||||||
${realIpsFromList cfipv4}
|
|
||||||
${realIpsFromList cfipv6}
|
|
||||||
real_ip_header CF-Connecting-IP;
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
132
static.nix
132
static.nix
|
|
@ -1,132 +0,0 @@
|
||||||
{
|
|
||||||
name, # Name of the site, /srv/{name} will be based on this as well as the username if user=true
|
|
||||||
root, # The directory within /srv/{name} that should be served by nginx
|
|
||||||
user ? false, # Should a user be created. If false, static-generic is used
|
|
||||||
domains ? [], # e.g. [ "example.com" "acme.com" ]
|
|
||||||
ssl ? false, # Should SSL be used
|
|
||||||
wwwRedirect ? null, # The status code used for www-to-non-www redirects. Null means no redirect
|
|
||||||
cloudflareOnly ? false, # Should CF Authenticated Origin Pulls be used
|
|
||||||
extraNginxConfig ? null, # Extra nginx config string
|
|
||||||
sshKeys ? null, # SSH public keys used to log into the site's user for deployments
|
|
||||||
extraPackages ? [], # Any extra packages the user should have in $PATH (only used with user=true)
|
|
||||||
generateSshKey ? true, # Generate an SSH key for the user (used for GH deploy keys)
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
|
|
||||||
{ lib, pkgs, ... }:
|
|
||||||
let
|
|
||||||
username = if user then "static-${name}" else "static-generic";
|
|
||||||
in {
|
|
||||||
services.nginx.enable = true;
|
|
||||||
security.acme.acceptTerms = true;
|
|
||||||
networking.firewall.allowedTCPPorts = [ 80 ] ++ lib.optionals ssl [ 443 ];
|
|
||||||
|
|
||||||
services.nginx.virtualHosts = lib.genAttrs domains (domain: {
|
|
||||||
enableACME = ssl;
|
|
||||||
forceSSL = ssl;
|
|
||||||
root = "/srv/${name}/${root}";
|
|
||||||
|
|
||||||
extraConfig = ''
|
|
||||||
${lib.optionalString cloudflareOnly ''
|
|
||||||
ssl_verify_client on;
|
|
||||||
ssl_client_certificate ${pkgs.fetchurl {
|
|
||||||
url = "https://developers.cloudflare.com/ssl/static/authenticated_origin_pull_ca.pem";
|
|
||||||
sha256 = "0hxqszqfzsbmgksfm6k0gp0hsx9k1gqx24gakxqv0391wl6fsky1";
|
|
||||||
}};
|
|
||||||
''}
|
|
||||||
${lib.optionalString (extraNginxConfig != null) extraNginxConfig}
|
|
||||||
'';
|
|
||||||
|
|
||||||
locations = {
|
|
||||||
"= /favicon.ico".extraConfig = ''
|
|
||||||
access_log off;
|
|
||||||
log_not_found off;
|
|
||||||
'';
|
|
||||||
|
|
||||||
"= /robots.txt".extraConfig = ''
|
|
||||||
access_log off;
|
|
||||||
log_not_found off;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}) // lib.optionalAttrs (wwwRedirect != null) (lib.genAttrs (map (domain: "www.${domain}") domains) (wwwDomain: {
|
|
||||||
enableACME = ssl;
|
|
||||||
forceSSL = ssl;
|
|
||||||
|
|
||||||
locations."/" = {
|
|
||||||
return = "${toString wwwRedirect} ${if ssl then "https" else "http"}://${lib.removePrefix "www." wwwDomain}$request_uri";
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
|
|
||||||
systemd.tmpfiles.rules = [
|
|
||||||
"d /srv 0751 root root - -"
|
|
||||||
"d /home 0751 root root - -"
|
|
||||||
"d /srv/${name} 0750 ${username} ${username} - -"
|
|
||||||
] ++ lib.optional user
|
|
||||||
"C /home/${username}/.bashrc 0640 ${username} ${username} - /etc/static-${name}-bashrc";
|
|
||||||
|
|
||||||
# User and group settings
|
|
||||||
users.users.${username} = {
|
|
||||||
group = username;
|
|
||||||
isSystemUser = true;
|
|
||||||
createHome = true;
|
|
||||||
home = "/home/${username}";
|
|
||||||
homeMode = "750";
|
|
||||||
shell = pkgs.bashInteractive;
|
|
||||||
packages = [ pkgs.git pkgs.unzip ] ++ lib.optionals user extraPackages;
|
|
||||||
} // lib.optionalAttrs (sshKeys != null && user) {
|
|
||||||
openssh.authorizedKeys.keys = sshKeys;
|
|
||||||
};
|
|
||||||
|
|
||||||
users.groups.${username} = {};
|
|
||||||
|
|
||||||
# Add site group to nginx service
|
|
||||||
systemd.services.nginx.serviceConfig.SupplementaryGroups = [ username ];
|
|
||||||
|
|
||||||
# SSH key generation for git deployments
|
|
||||||
# Note: keep in sync with laravel.nix
|
|
||||||
# Unlike laravel.nix, the key here includes username, not the site name since static-generic
|
|
||||||
# can be used for multiple sites
|
|
||||||
systemd.services."generate-ssh-key-${username}" = lib.mkIf generateSshKey {
|
|
||||||
description = "Generate SSH key for ${username}";
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
after = [ "users.target" ];
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
RemainAfterExit = true;
|
|
||||||
User = "root";
|
|
||||||
};
|
|
||||||
script = ''
|
|
||||||
USER_HOME="/home/${username}"
|
|
||||||
SSH_DIR="$USER_HOME/.ssh"
|
|
||||||
KEY_FILE="$SSH_DIR/id_ed25519"
|
|
||||||
|
|
||||||
if [[ ! -f "$KEY_FILE" ]]; then
|
|
||||||
echo "Generating SSH key for ${username}"
|
|
||||||
mkdir -p "$SSH_DIR"
|
|
||||||
${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f "$KEY_FILE" -N "" -C "${username}"
|
|
||||||
chown -R ${username}:${username} "$SSH_DIR"
|
|
||||||
chmod 700 "$SSH_DIR"
|
|
||||||
chmod 600 "$KEY_FILE"
|
|
||||||
chmod 640 "$KEY_FILE.pub"
|
|
||||||
echo "SSH key generated: $KEY_FILE.pub"
|
|
||||||
echo "Public key for deploy key:"
|
|
||||||
cat "$KEY_FILE.pub"
|
|
||||||
else
|
|
||||||
echo "SSH key already exists for ${username}"
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
# Create welcome message for user
|
|
||||||
# Note: keep in sync with laravel.nix (same block, minor changes here)
|
|
||||||
environment.etc."static-${name}-bashrc" = lib.mkIf user {
|
|
||||||
text = ''
|
|
||||||
echo "Welcome to ${name} static site!"
|
|
||||||
echo "Domains: ${lib.concatStringsSep ", " domains}"
|
|
||||||
echo "User home: /home/${username}"
|
|
||||||
echo "Site: /srv/${name}"
|
|
||||||
${lib.optionalString generateSshKey ''echo "SSH public key: cat ~/.ssh/id_ed25519.pub"''}
|
|
||||||
echo "---"
|
|
||||||
'';
|
|
||||||
} ;
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue