Compare commits
No commits in common. "7f2aa36d27cee296072304853bd7923e0c1450f9" and "b8a570ae0d0125a1ae4ab7a13ed11bca8bb00a54" have entirely different histories.
7f2aa36d27
...
b8a570ae0d
24 changed files with 200 additions and 666 deletions
105
.github/workflows/build.yaml
vendored
105
.github/workflows/build.yaml
vendored
|
@ -1,105 +0,0 @@
|
||||||
# name: build
|
|
||||||
# on: push
|
|
||||||
# jobs:
|
|
||||||
# checks:
|
|
||||||
# uses: nixbuild/nixbuild-action/.github/workflows/ci-workflow.yml@v19
|
|
||||||
# with:
|
|
||||||
# nix_conf: |
|
|
||||||
# allow-import-from-derivation = true
|
|
||||||
# secrets:
|
|
||||||
# nixbuild_token: ${{ secrets.nixbuild_token }}
|
|
||||||
|
|
||||||
|
|
||||||
name: "build"
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
push:
|
|
||||||
branches: [ "main" ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-matrix:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Install Nix
|
|
||||||
uses: DeterminateSystems/nix-installer-action@main
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
extra-conf: "system-features = nixos-test benchmark big-parallel kvm"
|
|
||||||
- name: Setup Caching
|
|
||||||
uses: cachix/cachix-action@v14
|
|
||||||
with:
|
|
||||||
name: selfhostblocks
|
|
||||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
|
||||||
- name: Generate Matrix
|
|
||||||
id: generate-matrix
|
|
||||||
run: |
|
|
||||||
nix flake show --allow-import-from-derivation --json \
|
|
||||||
| jq -c '.["checks"]["x86_64-linux"] | keys' > .output
|
|
||||||
|
|
||||||
cat .output
|
|
||||||
|
|
||||||
echo dynamic_list="$(cat .output)" >> "$GITHUB_OUTPUT"
|
|
||||||
outputs:
|
|
||||||
check: ${{ steps.generate-matrix.outputs.dynamic_list }}
|
|
||||||
|
|
||||||
manual:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Install Nix
|
|
||||||
uses: DeterminateSystems/nix-installer-action@main
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
extra-conf: "system-features = nixos-test benchmark big-parallel kvm"
|
|
||||||
- name: Setup Caching
|
|
||||||
uses: cachix/cachix-action@v14
|
|
||||||
with:
|
|
||||||
name: selfhostblocks
|
|
||||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
nix \
|
|
||||||
--print-build-logs \
|
|
||||||
--option keep-going true \
|
|
||||||
--show-trace \
|
|
||||||
build .#manualHtml
|
|
||||||
|
|
||||||
tests:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [ "build-matrix" ]
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
check: ${{ fromJson(needs.build-matrix.outputs.check) }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Install Nix
|
|
||||||
uses: DeterminateSystems/nix-installer-action@main
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
extra-conf: "system-features = nixos-test benchmark big-parallel kvm"
|
|
||||||
- name: Setup Caching
|
|
||||||
uses: cachix/cachix-action@v14
|
|
||||||
with:
|
|
||||||
name: selfhostblocks
|
|
||||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
nix build --print-build-logs --show-trace .#checks.x86_64-linux.${{ matrix.check }}
|
|
||||||
|
|
||||||
results:
|
|
||||||
if: ${{ always() }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Final Results
|
|
||||||
needs: [ manual, tests ]
|
|
||||||
steps:
|
|
||||||
- run: |
|
|
||||||
result="${{ needs.tests.result }}"
|
|
||||||
if [[ $result == "success" || $result == "skipped" ]]; then
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
exit 1
|
|
||||||
fi
|
|
37
.github/workflows/demo.yml
vendored
37
.github/workflows/demo.yml
vendored
|
@ -3,31 +3,15 @@ name: Demo
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
paths: &paths
|
||||||
|
- 'demo/**'
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
paths: *paths
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
path-filter:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
changed: ${{ steps.filter.outputs.changed }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: dorny/paths-filter@v3
|
|
||||||
id: filter
|
|
||||||
with:
|
|
||||||
filters: |
|
|
||||||
changed:
|
|
||||||
- '.github/workflows/demo.yml'
|
|
||||||
- 'demo/**'
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
needs: [ "path-filter" ]
|
|
||||||
if: needs.path-filter.outputs.changed == 'true'
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
demo:
|
demo:
|
||||||
|
@ -54,7 +38,7 @@ jobs:
|
||||||
|
|
||||||
- uses: cachix/cachix-action@v14
|
- uses: cachix/cachix-action@v14
|
||||||
with:
|
with:
|
||||||
name: selfhostblocks
|
name: mycache
|
||||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||||
|
|
||||||
- name: Build ${{ matrix.demo.name }} .#${{ matrix.demo.flake }}
|
- name: Build ${{ matrix.demo.name }} .#${{ matrix.demo.flake }}
|
||||||
|
@ -66,15 +50,4 @@ jobs:
|
||||||
--show-trace \
|
--show-trace \
|
||||||
build .#nixosConfigurations.${{ matrix.demo.flake }}.config.system.build.vm
|
build .#nixosConfigurations.${{ matrix.demo.flake }}.config.system.build.vm
|
||||||
|
|
||||||
result:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [ "build" ]
|
|
||||||
if: '!cancelled()'
|
|
||||||
steps:
|
|
||||||
- run: |
|
|
||||||
result="${{ needs.build.result }}"
|
|
||||||
if [[ $result == "success" || $result == "skipped" ]]; then
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
22
.github/workflows/garnix.yaml
vendored
Normal file
22
.github/workflows/garnix.yaml
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# Leaving commented because it does not work.
|
||||||
|
#
|
||||||
|
# name: "Final Results"
|
||||||
|
#
|
||||||
|
# on:
|
||||||
|
# check_suite:
|
||||||
|
# types: [completed]
|
||||||
|
#
|
||||||
|
# jobs:
|
||||||
|
# results:
|
||||||
|
# name: Final Results
|
||||||
|
# runs-on: ubuntu-latest
|
||||||
|
# steps:
|
||||||
|
# - run: echo
|
||||||
|
# - run: exit 1
|
||||||
|
# # see https://stackoverflow.com/a/67532120/4907315
|
||||||
|
# if: >-
|
||||||
|
# ${{
|
||||||
|
# contains(needs.*.result, 'failure')
|
||||||
|
# || contains(needs.*.result, 'cancelled')
|
||||||
|
# || contains(needs.*.result, 'skipped')
|
||||||
|
# }}
|
11
.github/workflows/pages.yml
vendored
11
.github/workflows/pages.yml
vendored
|
@ -2,8 +2,14 @@
|
||||||
name: Deploy docs
|
name: Deploy docs
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
# Runs on pushes targeting the default branch
|
||||||
push:
|
push:
|
||||||
branches: ["main"]
|
branches: ["main"]
|
||||||
|
# TODO: needed ?
|
||||||
|
# schedule:
|
||||||
|
# - cron: 0 0 * * 1
|
||||||
|
|
||||||
|
# Allows you to run this workflow manually from the Actions tab
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||||
|
@ -34,10 +40,9 @@ jobs:
|
||||||
- name: Install nix
|
- name: Install nix
|
||||||
uses: cachix/install-nix-action@v20
|
uses: cachix/install-nix-action@v20
|
||||||
|
|
||||||
- name: Setup Caching
|
- uses: cachix/cachix-action@v14
|
||||||
uses: cachix/cachix-action@v14
|
|
||||||
with:
|
with:
|
||||||
name: selfhostblocks
|
name: mycache
|
||||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||||
|
|
||||||
- name: Build docs
|
- name: Build docs
|
||||||
|
|
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -14,26 +14,9 @@
|
||||||
- `shb.authelia.oidcClients.description` -> `shb.authelia.oidcClients.client_name`
|
- `shb.authelia.oidcClients.description` -> `shb.authelia.oidcClients.client_name`
|
||||||
- `shb.authelia.oidcClients.secret` -> `shb.authelia.oidcClients.client_secret`
|
- `shb.authelia.oidcClients.secret` -> `shb.authelia.oidcClients.client_secret`
|
||||||
- `shb.authelia.ldapEndpoint` -> `shb.authelia.ldapHostname` and `shb.authelia.ldapPort`
|
- `shb.authelia.ldapEndpoint` -> `shb.authelia.ldapHostname` and `shb.authelia.ldapPort`
|
||||||
- `shb.authelia.jwtSecretFile` -> `shb.authelia.jwtSecret.result.path`
|
|
||||||
- `shb.authelia.ldapAdminPasswordFile` -> `shb.authelia.ldapAdminPassword.result.path`
|
|
||||||
- `shb.authelia.sessionSecretFile` -> `shb.authelia.sessionSecret.result.path`
|
|
||||||
- `shb.authelia.storageEncryptionKeyFile` -> `shb.authelia.storageEncryptionKey.result.path`
|
|
||||||
- `shb.authelia.identityProvidersOIDCIssuerPrivateKeyFile` -> `shb.authelia.identityProvidersOIDCIssuerPrivateKey.result.path`
|
|
||||||
- `shb.authelia.smtp.passwordFile` -> `shb.authelia.smtp.password.result.path`
|
|
||||||
- Make Nextcloud automatically disable maintenance mode upon service restart.
|
- Make Nextcloud automatically disable maintenance mode upon service restart.
|
||||||
- `shb.ldap.ldapUserPasswordFile` -> `shb.ldap.ldapUserPassword.result.path`
|
- `shb.ldap.ldapUserPasswordFile` -> `shb.ldap.ldapUserPassword.result.path`
|
||||||
- `shb.ldap.jwtSecretFile` -> `shb.ldap.jwtSecret.result.path`
|
- `shb.ldap.jwtSecretFile` -> `shb.ldap.jwtSecret.result.path`
|
||||||
- Jellyfin changes:
|
|
||||||
- `shb.jellyfin.ldap.passwordFile` -> `shb.jellyfin.ldap.adminPassword.result.path`.
|
|
||||||
- `shb.jellyfin.sso.secretFile` -> `shb.jellyfin.ldap.sharedSecret.result.path`.
|
|
||||||
- + `shb.jellyfin.ldap.sharedSecretForAuthelia`.
|
|
||||||
- Forgejo changes:
|
|
||||||
- `shb.forgejo.ldap.adminPasswordFile` -> `shb.forgejo.ldap.adminPassword.result.path`.
|
|
||||||
- `shb.forgejo.sso.secretFile` -> `shb.forgejo.ldap.sharedSecret.result.path`.
|
|
||||||
- `shb.forgejo.sso.secretFileForAuthelia` -> `shb.forgejo.ldap.sharedSecretForAuthelia.result.path`.
|
|
||||||
- `shb.forgejo.adminPasswordFile` -> `shb.forgejo.adminPassword.result.path`.
|
|
||||||
- `shb.forgejo.databasePasswordFile` -> `shb.forgejo.databasePassword.result.path`.
|
|
||||||
|
|
||||||
|
|
||||||
## User Facing Backwards Compatible Changes
|
## User Facing Backwards Compatible Changes
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*Modular server management based on NixOS modules and focused on best practices.*
|
*Modular server management based on NixOS modules and focused on best practices.*
|
||||||
|
|
||||||
[![Documentation](https://github.com/ibizaman/selfhostblocks/actions/workflows/pages.yml/badge.svg)](https://github.com/ibizaman/selfhostblocks/actions/workflows/pages.yml)
|
[![Documentation](https://github.com/ibizaman/selfhostblocks/actions/workflows/pages.yml/badge.svg)](https://github.com/ibizaman/selfhostblocks/actions/workflows/pages.yml)
|
||||||
[![Tests](https://github.com/ibizaman/selfhostblocks/actions/workflows/build.yaml/badge.svg)](https://github.com/ibizaman/selfhostblocks/actions/workflows/build.yaml)
|
[![Tests](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgarnix.io%2Fapi%2Fbadges%2Fibizaman%2Fselfhostblocks%3Fbranch%3Dmain)](https://garnix.io) (using Garnix)
|
||||||
[![Demo](https://github.com/ibizaman/selfhostblocks/actions/workflows/demo.yml/badge.svg)](https://github.com/ibizaman/selfhostblocks/actions/workflows/demo.yml)
|
[![Demo](https://github.com/ibizaman/selfhostblocks/actions/workflows/demo.yml/badge.svg)](https://github.com/ibizaman/selfhostblocks/actions/workflows/demo.yml)
|
||||||
|
|
||||||
SHB (Self Host Blocks) is yet another server management tool
|
SHB (Self Host Blocks) is yet another server management tool
|
||||||
|
|
|
@ -35,11 +35,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1731139594,
|
"lastModified": 1728492678,
|
||||||
"narHash": "sha256-IigrKK3vYRpUu+HEjPL/phrfh7Ox881er1UEsZvw9Q4=",
|
"narHash": "sha256-9UTxR8eukdg+XZeHgxW5hQA9fIKHsKCdOIUycTryeVw=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "76612b17c0ce71689921ca12d9ffdc9c23ce40b2",
|
"rev": "5633bcff0c6162b9e4b5f1264264611e950c8ec7",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
22
flake.nix
22
flake.nix
|
@ -26,6 +26,7 @@
|
||||||
src = nixpkgs;
|
src = nixpkgs;
|
||||||
inherit patches;
|
inherit patches;
|
||||||
};
|
};
|
||||||
|
|
||||||
pkgs = import patchedNixpkgs {
|
pkgs = import patchedNixpkgs {
|
||||||
inherit system;
|
inherit system;
|
||||||
};
|
};
|
||||||
|
@ -33,7 +34,6 @@
|
||||||
allModules = [
|
allModules = [
|
||||||
modules/blocks/authelia.nix
|
modules/blocks/authelia.nix
|
||||||
modules/blocks/davfs.nix
|
modules/blocks/davfs.nix
|
||||||
modules/blocks/hardcodedsecret.nix
|
|
||||||
modules/blocks/ldap.nix
|
modules/blocks/ldap.nix
|
||||||
modules/blocks/monitoring.nix
|
modules/blocks/monitoring.nix
|
||||||
modules/blocks/nginx.nix
|
modules/blocks/nginx.nix
|
||||||
|
@ -79,14 +79,12 @@
|
||||||
|
|
||||||
checks =
|
checks =
|
||||||
let
|
let
|
||||||
inherit (pkgs.lib) foldl foldlAttrs mergeAttrs optionalAttrs;
|
|
||||||
|
|
||||||
importFiles = files:
|
importFiles = files:
|
||||||
map (m: pkgs.callPackage m {}) files;
|
map (m: pkgs.callPackage m {}) files;
|
||||||
|
|
||||||
mergeTests = foldl mergeAttrs {};
|
mergeTests = pkgs.lib.lists.foldl pkgs.lib.trivial.mergeAttrs {};
|
||||||
|
|
||||||
flattenAttrs = root: attrset: foldlAttrs (acc: name: value: acc // {
|
flattenAttrs = root: attrset: pkgs.lib.attrsets.foldlAttrs (acc: name: value: acc // {
|
||||||
"${root}_${name}" = value;
|
"${root}_${name}" = value;
|
||||||
}) {} attrset;
|
}) {} attrset;
|
||||||
|
|
||||||
|
@ -98,21 +96,19 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
shblib = pkgs.callPackage ./lib {};
|
shblib = pkgs.callPackage ./lib {};
|
||||||
in (optionalAttrs (system == "x86_64-linux") ({
|
in (rec {
|
||||||
modules = shblib.check {
|
modules = shblib.check {
|
||||||
inherit pkgs;
|
inherit pkgs;
|
||||||
tests =
|
tests =
|
||||||
mergeTests (importFiles [
|
mergeTests (importFiles [
|
||||||
./test/modules/arr.nix
|
./test/modules/arr.nix
|
||||||
./test/modules/davfs.nix
|
./test/modules/davfs.nix
|
||||||
# TODO: Make this not use IFD
|
|
||||||
./test/modules/lib.nix
|
./test/modules/lib.nix
|
||||||
./test/modules/nginx.nix
|
./test/modules/nginx.nix
|
||||||
./test/modules/postgresql.nix
|
./test/modules/postgresql.nix
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
# TODO: Make this not use IFD
|
|
||||||
lib = nix-flake-tests.lib.check {
|
lib = nix-flake-tests.lib.check {
|
||||||
inherit pkgs;
|
inherit pkgs;
|
||||||
tests = pkgs.callPackage ./test/modules/lib.nix {};
|
tests = pkgs.callPackage ./test/modules/lib.nix {};
|
||||||
|
@ -123,7 +119,7 @@
|
||||||
// (vm_test "deluge" ./test/services/deluge.nix)
|
// (vm_test "deluge" ./test/services/deluge.nix)
|
||||||
// (vm_test "forgejo" ./test/services/forgejo.nix)
|
// (vm_test "forgejo" ./test/services/forgejo.nix)
|
||||||
// (vm_test "grocy" ./test/services/grocy.nix)
|
// (vm_test "grocy" ./test/services/grocy.nix)
|
||||||
// (vm_test "homeassistant" ./test/services/home-assistant.nix)
|
// (vm_test "home-assistant" ./test/services/home-assistant.nix)
|
||||||
// (vm_test "jellyfin" ./test/services/jellyfin.nix)
|
// (vm_test "jellyfin" ./test/services/jellyfin.nix)
|
||||||
// (vm_test "monitoring" ./test/services/monitoring.nix)
|
// (vm_test "monitoring" ./test/services/monitoring.nix)
|
||||||
// (vm_test "nextcloud" ./test/services/nextcloud.nix)
|
// (vm_test "nextcloud" ./test/services/nextcloud.nix)
|
||||||
|
@ -135,11 +131,7 @@
|
||||||
// (vm_test "postgresql" ./test/blocks/postgresql.nix)
|
// (vm_test "postgresql" ./test/blocks/postgresql.nix)
|
||||||
// (vm_test "restic" ./test/blocks/restic.nix)
|
// (vm_test "restic" ./test/blocks/restic.nix)
|
||||||
// (vm_test "ssl" ./test/blocks/ssl.nix)
|
// (vm_test "ssl" ./test/blocks/ssl.nix)
|
||||||
|
);
|
||||||
// (vm_test "contracts-secret" ./test/contracts/secret.nix)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
) // {
|
);
|
||||||
herculesCI.ciSystems = [ "x86_64-linux" ];
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{ pkgs, lib }:
|
{ pkgs, lib }:
|
||||||
let
|
let
|
||||||
inherit (builtins) isAttrs hasAttr;
|
inherit (builtins) isAttrs hasAttr;
|
||||||
inherit (lib) concatMapStringsSep concatStringsSep mapAttrsToList;
|
inherit (lib) concatStringsSep;
|
||||||
in
|
in
|
||||||
rec {
|
rec {
|
||||||
# Replace secrets in a file.
|
# Replace secrets in a file.
|
||||||
|
@ -31,34 +31,17 @@ rec {
|
||||||
resultPath = newPath;
|
resultPath = newPath;
|
||||||
};
|
};
|
||||||
|
|
||||||
genReplacement = secret:
|
|
||||||
let
|
|
||||||
t = { transform ? null, ... }: if isNull transform then x: x else transform;
|
|
||||||
in
|
|
||||||
lib.attrsets.nameValuePair (secretName secret.name) ((t secret) "$(cat ${toString secret.source})");
|
|
||||||
|
|
||||||
replaceSecretsScript = { file, resultPath, replacements, user ? null, permissions ? "u=r,g=r,o=" }:
|
replaceSecretsScript = { file, resultPath, replacements, user ? null, permissions ? "u=r,g=r,o=" }:
|
||||||
let
|
let
|
||||||
templatePath = resultPath + ".template";
|
templatePath = resultPath + ".template";
|
||||||
|
sedPatterns = lib.strings.concatStringsSep " " (lib.attrsets.mapAttrsToList (from: to: "-e \"s|${from}|${to}|\"") replacements);
|
||||||
# We check that the files containing the secrets have the
|
sedCmd = if replacements == {}
|
||||||
# correct permissions for us to read them in this separate
|
|
||||||
# step. Otherwise, the $(cat ...) commands inside the sed
|
|
||||||
# replacements could fail but not fail individually but
|
|
||||||
# not fail the whole script.
|
|
||||||
checkPermissions = concatMapStringsSep "\n" (pattern: "cat ${pattern.source} > /dev/null") replacements;
|
|
||||||
|
|
||||||
sedPatterns = concatMapStringsSep " " (pattern: "-e \"s|${pattern.name}|${pattern.value}|\"") (map genReplacement replacements);
|
|
||||||
|
|
||||||
sedCmd = if replacements == []
|
|
||||||
then "cat"
|
then "cat"
|
||||||
else "${pkgs.gnused}/bin/sed ${sedPatterns}";
|
else "${pkgs.gnused}/bin/sed ${sedPatterns}";
|
||||||
in
|
in
|
||||||
''
|
''
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
${checkPermissions}
|
|
||||||
|
|
||||||
mkdir -p $(dirname ${templatePath})
|
mkdir -p $(dirname ${templatePath})
|
||||||
ln -fs ${file} ${templatePath}
|
ln -fs ${file} ${templatePath}
|
||||||
rm -f ${resultPath}
|
rm -f ${resultPath}
|
||||||
|
@ -88,8 +71,8 @@ rec {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
secretName = names:
|
secretName = name:
|
||||||
"%SECRET${lib.strings.toUpper (lib.strings.concatMapStrings (s: "_" + s) names)}%";
|
"%SECRET${lib.strings.toUpper (lib.strings.concatMapStrings (s: "_" + s) name)}%";
|
||||||
|
|
||||||
withReplacements = attrs:
|
withReplacements = attrs:
|
||||||
let
|
let
|
||||||
|
@ -108,8 +91,15 @@ rec {
|
||||||
else value // { name = name; };
|
else value // { name = name; };
|
||||||
|
|
||||||
secretsWithName = mapAttrsRecursiveCond (v: ! v ? "source") addNameField attrs;
|
secretsWithName = mapAttrsRecursiveCond (v: ! v ? "source") addNameField attrs;
|
||||||
|
|
||||||
|
allSecrets = collect (v: builtins.isAttrs v && v ? "source") secretsWithName;
|
||||||
|
|
||||||
|
t = { transform ? null, ... }: if isNull transform then x: x else transform;
|
||||||
|
|
||||||
|
genReplacement = secret:
|
||||||
|
lib.attrsets.nameValuePair (secretName secret.name) ((t secret) "$(cat ${toString secret.source})");
|
||||||
in
|
in
|
||||||
collect (v: builtins.isAttrs v && v ? "source") secretsWithName;
|
lib.attrsets.listToAttrs (map genReplacement allSecrets);
|
||||||
|
|
||||||
# Inspired lib.attrsets.mapAttrsRecursiveCond but also recurses on lists.
|
# Inspired lib.attrsets.mapAttrsRecursiveCond but also recurses on lists.
|
||||||
mapAttrsRecursiveCond =
|
mapAttrsRecursiveCond =
|
||||||
|
@ -248,7 +238,7 @@ rec {
|
||||||
results = pkgs.lib.runTests tests;
|
results = pkgs.lib.runTests tests;
|
||||||
in
|
in
|
||||||
if results != [ ] then
|
if results != [ ] then
|
||||||
builtins.throw (concatStringsSep "\n" (map resultToString (lib.traceValSeqN 3 results)))
|
builtins.throw (builtins.concatStringsSep "\n" (map resultToString (lib.traceValSeqN 3 results)))
|
||||||
else
|
else
|
||||||
pkgs.runCommand "nix-flake-tests-success" { } "echo > $out";
|
pkgs.runCommand "nix-flake-tests-success" { } "echo > $out";
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
{ config, options, pkgs, lib, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.shb.authelia;
|
cfg = config.shb.authelia;
|
||||||
opt = options.shb.authelia;
|
|
||||||
|
|
||||||
contracts = pkgs.callPackage ../contracts {};
|
contracts = pkgs.callPackage ../contracts {};
|
||||||
shblib = pkgs.callPackage ../../lib {};
|
shblib = pkgs.callPackage ../../lib {};
|
||||||
|
@ -68,45 +67,33 @@ in
|
||||||
description = "Secrets needed by Authelia";
|
description = "Secrets needed by Authelia";
|
||||||
type = lib.types.submodule {
|
type = lib.types.submodule {
|
||||||
options = {
|
options = {
|
||||||
jwtSecret = contracts.secret.mkOption {
|
jwtSecretFile = lib.mkOption {
|
||||||
description = "JWT secret.";
|
type = lib.types.path;
|
||||||
mode = "0400";
|
description = "File containing the JWT secret.";
|
||||||
owner = cfg.autheliaUser;
|
|
||||||
restartUnits = [ "authelia-${opt.subdomain}.${opt.domain}" ];
|
|
||||||
};
|
};
|
||||||
ldapAdminPassword = contracts.secret.mkOption {
|
ldapAdminPasswordFile = lib.mkOption {
|
||||||
description = "LDAP admin user password.";
|
type = lib.types.path;
|
||||||
mode = "0400";
|
description = "File containing the LDAP admin user password.";
|
||||||
owner = cfg.autheliaUser;
|
|
||||||
restartUnits = [ "authelia-${opt.subdomain}.${opt.domain}" ];
|
|
||||||
};
|
};
|
||||||
sessionSecret = contracts.secret.mkOption {
|
sessionSecretFile = lib.mkOption {
|
||||||
description = "Session secret.";
|
type = lib.types.path;
|
||||||
mode = "0400";
|
description = "File containing the session secret.";
|
||||||
owner = cfg.autheliaUser;
|
|
||||||
restartUnits = [ "authelia-${opt.subdomain}.${opt.domain}" ];
|
|
||||||
};
|
};
|
||||||
storageEncryptionKey = contracts.secret.mkOption {
|
storageEncryptionKeyFile = lib.mkOption {
|
||||||
description = "Storage encryption key.";
|
type = lib.types.path;
|
||||||
mode = "0400";
|
description = "File containing the storage encryption key.";
|
||||||
owner = cfg.autheliaUser;
|
|
||||||
restartUnits = [ "authelia-${opt.subdomain}.${opt.domain}" ];
|
|
||||||
};
|
};
|
||||||
identityProvidersOIDCHMACSecret = contracts.secret.mkOption {
|
identityProvidersOIDCHMACSecretFile = lib.mkOption {
|
||||||
description = "Identity provider OIDC HMAC secret.";
|
type = lib.types.path;
|
||||||
mode = "0400";
|
description = "File containing the identity provider OIDC HMAC secret.";
|
||||||
owner = cfg.autheliaUser;
|
|
||||||
restartUnits = [ "authelia-${opt.subdomain}.${opt.domain}" ];
|
|
||||||
};
|
};
|
||||||
identityProvidersOIDCIssuerPrivateKey = contracts.secret.mkOption {
|
identityProvidersOIDCIssuerPrivateKeyFile = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
description = ''
|
description = ''
|
||||||
Identity provider OIDC issuer private key.
|
File containing the identity provider OIDC issuer private key.
|
||||||
|
|
||||||
Generate one with `nix run nixpkgs#openssl -- genrsa -out keypair.pem 2048`
|
Generate one with `nix run nixpkgs#openssl -- genrsa -out keypair.pem 2048`
|
||||||
'';
|
'';
|
||||||
mode = "0400";
|
|
||||||
owner = cfg.autheliaUser;
|
|
||||||
restartUnits = [ "authelia-${opt.subdomain}.${opt.domain}" ];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -220,11 +207,9 @@ in
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
description = "Username to connect to the SMTP host.";
|
description = "Username to connect to the SMTP host.";
|
||||||
};
|
};
|
||||||
password = contracts.secret.mkOption {
|
passwordFile = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
description = "File containing the password to connect to the SMTP host.";
|
description = "File containing the password to connect to the SMTP host.";
|
||||||
mode = "0400";
|
|
||||||
owner = cfg.autheliaUser;
|
|
||||||
restartUnits = [ "authelia-${fqdn}" ];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}))
|
}))
|
||||||
|
@ -297,20 +282,19 @@ in
|
||||||
user = cfg.autheliaUser;
|
user = cfg.autheliaUser;
|
||||||
|
|
||||||
secrets = {
|
secrets = {
|
||||||
jwtSecretFile = cfg.secrets.jwtSecret.result.path;
|
inherit (cfg.secrets) jwtSecretFile storageEncryptionKeyFile;
|
||||||
storageEncryptionKeyFile = cfg.secrets.storageEncryptionKey.result.path;
|
|
||||||
};
|
};
|
||||||
# See https://www.authelia.com/configuration/methods/secrets/
|
# See https://www.authelia.com/configuration/methods/secrets/
|
||||||
environmentVariables = {
|
environmentVariables = {
|
||||||
AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE = toString cfg.secrets.ldapAdminPassword.result.path;
|
AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE = toString cfg.secrets.ldapAdminPasswordFile;
|
||||||
AUTHELIA_SESSION_SECRET_FILE = toString cfg.secrets.sessionSecret.result.path;
|
AUTHELIA_SESSION_SECRET_FILE = toString cfg.secrets.sessionSecretFile;
|
||||||
# Not needed since we use peer auth.
|
# Not needed since we use peer auth.
|
||||||
# AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE = "/run/secrets/authelia/postgres_password";
|
# AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE = "/run/secrets/authelia/postgres_password";
|
||||||
AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE = toString cfg.secrets.storageEncryptionKey.result.path;
|
AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE = toString cfg.secrets.storageEncryptionKeyFile;
|
||||||
AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE = toString cfg.secrets.identityProvidersOIDCHMACSecret.result.path;
|
AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE = toString cfg.secrets.identityProvidersOIDCHMACSecretFile;
|
||||||
AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_PRIVATE_KEY_FILE = toString cfg.secrets.identityProvidersOIDCIssuerPrivateKey.result.path;
|
AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_PRIVATE_KEY_FILE = toString cfg.secrets.identityProvidersOIDCIssuerPrivateKeyFile;
|
||||||
|
|
||||||
AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE = lib.mkIf (!(builtins.isString cfg.smtp)) (toString cfg.smtp.password.result.path);
|
AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE = lib.mkIf (!(builtins.isString cfg.smtp)) (toString cfg.smtp.passwordFile);
|
||||||
};
|
};
|
||||||
settings = {
|
settings = {
|
||||||
server.address = "tcp://127.0.0.1:9091";
|
server.address = "tcp://127.0.0.1:9091";
|
||||||
|
|
|
@ -1,108 +0,0 @@
|
||||||
{ config, options, lib, pkgs, ... }:
|
|
||||||
let
|
|
||||||
cfg = config.shb.hardcodedsecret;
|
|
||||||
opt = options.shb.hardcodedsecret;
|
|
||||||
|
|
||||||
inherit (lib) mapAttrs' mkOption nameValuePair;
|
|
||||||
inherit (lib.types) attrsOf listOf path nullOr str submodule;
|
|
||||||
inherit (pkgs) writeText;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.shb.hardcodedsecret = mkOption {
|
|
||||||
default = {};
|
|
||||||
description = ''
|
|
||||||
Hardcoded secrets. These should only be used in tests.
|
|
||||||
'';
|
|
||||||
example = lib.literalExpression ''
|
|
||||||
{
|
|
||||||
mySecret = {
|
|
||||||
user = "me";
|
|
||||||
mode = "0400";
|
|
||||||
restartUnits = [ "myservice.service" ];
|
|
||||||
content = "My Secrets";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
type = attrsOf (submodule ({ name, ... }: {
|
|
||||||
options = {
|
|
||||||
mode = mkOption {
|
|
||||||
description = ''
|
|
||||||
Mode of the secret file.
|
|
||||||
'';
|
|
||||||
type = str;
|
|
||||||
default = "0400";
|
|
||||||
};
|
|
||||||
|
|
||||||
owner = mkOption {
|
|
||||||
description = ''
|
|
||||||
Linux user owning the secret file.
|
|
||||||
'';
|
|
||||||
type = str;
|
|
||||||
default = "root";
|
|
||||||
};
|
|
||||||
|
|
||||||
group = mkOption {
|
|
||||||
description = ''
|
|
||||||
Linux group owning the secret file.
|
|
||||||
'';
|
|
||||||
type = str;
|
|
||||||
default = "root";
|
|
||||||
};
|
|
||||||
|
|
||||||
restartUnits = mkOption {
|
|
||||||
description = ''
|
|
||||||
Systemd units to restart after the secret is updated.
|
|
||||||
'';
|
|
||||||
type = listOf str;
|
|
||||||
default = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
path = mkOption {
|
|
||||||
type = path;
|
|
||||||
description = ''
|
|
||||||
Path to the file containing the secret generated out of band.
|
|
||||||
|
|
||||||
This path will exist after deploying to a target host,
|
|
||||||
it is not available through the nix store.
|
|
||||||
'';
|
|
||||||
default = "/run/hardcodedsecrets/hardcodedsecret_${name}";
|
|
||||||
};
|
|
||||||
|
|
||||||
content = mkOption {
|
|
||||||
type = nullOr str;
|
|
||||||
description = ''
|
|
||||||
Content of the secret.
|
|
||||||
|
|
||||||
This will be stored in the nix store and should only be used for testing or maybe in dev.
|
|
||||||
'';
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
source = mkOption {
|
|
||||||
type = nullOr str;
|
|
||||||
description = ''
|
|
||||||
Source of the content of the secret.
|
|
||||||
'';
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
system.activationScripts = mapAttrs' (n: cfg':
|
|
||||||
let
|
|
||||||
source = if cfg'.source != null
|
|
||||||
then cfg'.source
|
|
||||||
else writeText "hardcodedsecret_${n}_content" cfg'.content;
|
|
||||||
in
|
|
||||||
nameValuePair "hardcodedsecret_${n}" ''
|
|
||||||
mkdir -p "$(dirname "${cfg'.path}")"
|
|
||||||
touch "${cfg'.path}"
|
|
||||||
chmod ${cfg'.mode} "${cfg'.path}"
|
|
||||||
chown ${cfg'.owner}:${cfg'.group} "${cfg'.path}"
|
|
||||||
cp ${source} "${cfg'.path}"
|
|
||||||
''
|
|
||||||
) cfg;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -112,12 +112,9 @@ in
|
||||||
'';
|
'';
|
||||||
readOnly = true;
|
readOnly = true;
|
||||||
default = {
|
default = {
|
||||||
# TODO: is there a workaround that avoid needing to use root?
|
user = "lldap";
|
||||||
# root because otherwise we cannot access the private StateDiretory
|
|
||||||
user = "root";
|
|
||||||
# /private because the systemd service uses DynamicUser=true
|
|
||||||
sourceDirectories = [
|
sourceDirectories = [
|
||||||
"/var/lib/private/lldap"
|
"/var/lib/lldap"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
{ pkgs, lib }:
|
{ lib }:
|
||||||
{
|
{
|
||||||
backup = import ./backup.nix { inherit lib; };
|
backup = import ./backup.nix { inherit lib; };
|
||||||
mount = import ./mount.nix { inherit lib; };
|
mount = import ./mount.nix { inherit lib; };
|
||||||
secret = import ./secret.nix { inherit lib; };
|
secret = import ./secret.nix { inherit lib; };
|
||||||
ssl = import ./ssl.nix { inherit lib; };
|
ssl = import ./ssl.nix { inherit lib; };
|
||||||
test = {
|
|
||||||
secret = import ./secret/test.nix { inherit pkgs lib; };
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
{ pkgs, lib, ... }:
|
|
||||||
let
|
|
||||||
pkgs' = pkgs;
|
|
||||||
|
|
||||||
testLib = pkgs.callPackage ../../../test/common.nix {};
|
|
||||||
|
|
||||||
inherit (lib) getAttrFromPath setAttrByPath;
|
|
||||||
inherit (lib) mkIf;
|
|
||||||
in
|
|
||||||
{ name,
|
|
||||||
configRoot,
|
|
||||||
createContent, # config to create a secret with value "secretA".
|
|
||||||
modules ? [],
|
|
||||||
owner ? "root",
|
|
||||||
group ? "root",
|
|
||||||
mode ? "0400",
|
|
||||||
restartUnits ? [ "myunit.service" ],
|
|
||||||
}: pkgs.testers.runNixOSTest {
|
|
||||||
name = "secret_${name}_${owner}_${group}_${mode}";
|
|
||||||
|
|
||||||
nodes.machine = { config, ... }: {
|
|
||||||
imports = ( testLib.baseImports pkgs' ) ++ modules;
|
|
||||||
config = lib.mkMerge [
|
|
||||||
(setAttrByPath configRoot {
|
|
||||||
A = {
|
|
||||||
inherit owner group mode restartUnits;
|
|
||||||
} // createContent;
|
|
||||||
})
|
|
||||||
(mkIf (owner != "root") {
|
|
||||||
users.users.${owner}.isNormalUser = true;
|
|
||||||
})
|
|
||||||
(mkIf (group != "root") {
|
|
||||||
users.groups.${group} = {};
|
|
||||||
})
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
testScript = { nodes, ... }:
|
|
||||||
let
|
|
||||||
cfg = (getAttrFromPath configRoot nodes.machine)."A";
|
|
||||||
in
|
|
||||||
''
|
|
||||||
owner = machine.succeed("stat -c '%U' ${cfg.path}").strip()
|
|
||||||
print(f"Got owner {owner}")
|
|
||||||
if owner != "${owner}":
|
|
||||||
raise Exception(f"Owner should be '${owner}' but got '{owner}'")
|
|
||||||
|
|
||||||
group = machine.succeed("stat -c '%G' ${cfg.path}").strip()
|
|
||||||
print(f"Got group {group}")
|
|
||||||
if group != "${group}":
|
|
||||||
raise Exception(f"Group should be '${group}' but got '{group}'")
|
|
||||||
|
|
||||||
mode = str(int(machine.succeed("stat -c '%a' ${cfg.path}").strip()))
|
|
||||||
print(f"Got mode {mode}")
|
|
||||||
wantedMode = str(int("${mode}"))
|
|
||||||
if mode != wantedMode:
|
|
||||||
raise Exception(f"Mode should be '{wantedMode}' but got '{mode}'")
|
|
||||||
|
|
||||||
content = machine.succeed("cat ${cfg.path}").strip()
|
|
||||||
print(f"Got content {content}")
|
|
||||||
if content != "secretA":
|
|
||||||
raise Exception(f"Content should be 'secretA' but got '{content}'")
|
|
||||||
'';
|
|
||||||
}
|
|
|
@ -82,12 +82,14 @@ in
|
||||||
default = "admin";
|
default = "admin";
|
||||||
};
|
};
|
||||||
|
|
||||||
adminPassword = contracts.secret.mkOption {
|
adminPasswordFile = lib.mkOption {
|
||||||
description = "LDAP admin password.";
|
type = lib.types.path;
|
||||||
mode = "0440";
|
description = ''
|
||||||
owner = "forgejo";
|
File containing the admin password of the LDAP server.
|
||||||
group = "forgejo";
|
|
||||||
restartUnits = [ "forgejo.service" ];
|
Must be readable by the forgejo system user.
|
||||||
|
'';
|
||||||
|
default = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
userGroup = lib.mkOption {
|
userGroup = lib.mkOption {
|
||||||
|
@ -138,37 +140,37 @@ in
|
||||||
default = "one_factor";
|
default = "one_factor";
|
||||||
};
|
};
|
||||||
|
|
||||||
sharedSecret = contracts.secret.mkOption {
|
secretFile = lib.mkOption {
|
||||||
description = "OIDC shared secret for Forgejo.";
|
type = lib.types.path;
|
||||||
mode = "0440";
|
description = ''
|
||||||
owner = "forgejo";
|
File containing the secret for the OIDC endpoint.
|
||||||
group = "forgejo";
|
|
||||||
restartUnits = [ "forgejo.service" ];
|
Must be readable by the forgejo system user.
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
sharedSecretForAuthelia = contracts.secret.mkOption {
|
secretFileForAuthelia = lib.mkOption {
|
||||||
description = "OIDC shared secret for Authelia.";
|
type = lib.types.path;
|
||||||
mode = "0400";
|
description = ''
|
||||||
owner = "authelia";
|
File containing the secret for the OIDC endpoint, must be readable by the Authelia user.
|
||||||
|
|
||||||
|
Must be readable by the authelia system user.
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
adminPassword = contracts.secret.mkOption {
|
adminPasswordFile = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
description = "File containing the Forgejo admin user password.";
|
description = "File containing the Forgejo admin user password.";
|
||||||
mode = "0440";
|
example = "/run/secrets/forgejo/adminPassword";
|
||||||
owner = "forgejo";
|
|
||||||
group = "forgejo";
|
|
||||||
restartUnits = [ "forgejo.service" ];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
databasePassword = contracts.secret.mkOption {
|
databasePasswordFile = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
description = "File containing the Forgejo database password.";
|
description = "File containing the Forgejo database password.";
|
||||||
mode = "0440";
|
example = "/run/secrets/forgejo/databasePassword";
|
||||||
owner = "forgejo";
|
|
||||||
group = "forgejo";
|
|
||||||
restartUnits = [ "forgejo.service" ];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
repositoryRoot = lib.mkOption {
|
repositoryRoot = lib.mkOption {
|
||||||
|
@ -342,7 +344,7 @@ in
|
||||||
services.forgejo.database = {
|
services.forgejo.database = {
|
||||||
type = "postgres";
|
type = "postgres";
|
||||||
|
|
||||||
passwordFile = cfg.databasePassword.result.path;
|
passwordFile = cfg.databasePasswordFile;
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -378,7 +380,7 @@ in
|
||||||
--host ${cfg.ldap.host} \
|
--host ${cfg.ldap.host} \
|
||||||
--port ${toString cfg.ldap.port} \
|
--port ${toString cfg.ldap.port} \
|
||||||
--bind-dn uid=${cfg.ldap.adminName},ou=people,${cfg.ldap.dcdomain} \
|
--bind-dn uid=${cfg.ldap.adminName},ou=people,${cfg.ldap.dcdomain} \
|
||||||
--bind-password $(tr -d '\n' < ${cfg.ldap.adminPassword.result.path}) \
|
--bind-password $(tr -d '\n' < ${cfg.ldap.adminPasswordFile}) \
|
||||||
--security-protocol Unencrypted \
|
--security-protocol Unencrypted \
|
||||||
--user-search-base ou=people,${cfg.ldap.dcdomain} \
|
--user-search-base ou=people,${cfg.ldap.dcdomain} \
|
||||||
--user-filter '(&(memberof=cn=${cfg.ldap.userGroup},ou=groups,${cfg.ldap.dcdomain})(|(uid=%[1]s)(mail=%[1]s)))' \
|
--user-filter '(&(memberof=cn=${cfg.ldap.userGroup},ou=groups,${cfg.ldap.dcdomain})(|(uid=%[1]s)(mail=%[1]s)))' \
|
||||||
|
@ -397,7 +399,7 @@ in
|
||||||
--host ${cfg.ldap.host} \
|
--host ${cfg.ldap.host} \
|
||||||
--port ${toString cfg.ldap.port} \
|
--port ${toString cfg.ldap.port} \
|
||||||
--bind-dn uid=${cfg.ldap.adminName},ou=people,${cfg.ldap.dcdomain} \
|
--bind-dn uid=${cfg.ldap.adminName},ou=people,${cfg.ldap.dcdomain} \
|
||||||
--bind-password $(tr -d '\n' < ${cfg.ldap.adminPassword.result.path}) \
|
--bind-password $(tr -d '\n' < ${cfg.ldap.adminPasswordFile}) \
|
||||||
--security-protocol Unencrypted \
|
--security-protocol Unencrypted \
|
||||||
--user-search-base ou=people,${cfg.ldap.dcdomain} \
|
--user-search-base ou=people,${cfg.ldap.dcdomain} \
|
||||||
--user-filter '(&(memberof=cn=${cfg.ldap.userGroup},ou=groups,${cfg.ldap.dcdomain})(|(uid=%[1]s)(mail=%[1]s)))' \
|
--user-filter '(&(memberof=cn=${cfg.ldap.userGroup},ou=groups,${cfg.ldap.dcdomain})(|(uid=%[1]s)(mail=%[1]s)))' \
|
||||||
|
@ -454,7 +456,7 @@ in
|
||||||
--name ${provider} \
|
--name ${provider} \
|
||||||
--provider openidConnect \
|
--provider openidConnect \
|
||||||
--key forgejo \
|
--key forgejo \
|
||||||
--secret $(tr -d '\n' < ${cfg.sso.sharedSecret.result.path}) \
|
--secret $(tr -d '\n' < ${cfg.sso.secretFile}) \
|
||||||
--auto-discover-url ${cfg.sso.endpoint}/.well-known/openid-configuration
|
--auto-discover-url ${cfg.sso.endpoint}/.well-known/openid-configuration
|
||||||
else
|
else
|
||||||
echo Did not find any sso configuration, creating one with name ${provider}.
|
echo Did not find any sso configuration, creating one with name ${provider}.
|
||||||
|
@ -462,7 +464,7 @@ in
|
||||||
--name ${provider} \
|
--name ${provider} \
|
||||||
--provider openidConnect \
|
--provider openidConnect \
|
||||||
--key forgejo \
|
--key forgejo \
|
||||||
--secret $(tr -d '\n' < ${cfg.sso.sharedSecret.result.path}) \
|
--secret $(tr -d '\n' < ${cfg.sso.secretFile}) \
|
||||||
--auto-discover-url ${cfg.sso.endpoint}/.well-known/openid-configuration
|
--auto-discover-url ${cfg.sso.endpoint}/.well-known/openid-configuration
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
@ -473,7 +475,7 @@ in
|
||||||
in {
|
in {
|
||||||
client_id = cfg.sso.clientID;
|
client_id = cfg.sso.clientID;
|
||||||
client_name = "Forgejo";
|
client_name = "Forgejo";
|
||||||
client_secret.source = cfg.sso.sharedSecretForAuthelia.result.path;
|
client_secret.source = cfg.sso.secretFileForAuthelia;
|
||||||
public = false;
|
public = false;
|
||||||
authorization_policy = cfg.sso.authorization_policy;
|
authorization_policy = cfg.sso.authorization_policy;
|
||||||
redirect_uris = [ "https://${cfg.subdomain}.${cfg.domain}/user/oauth2/${provider}/callback" ];
|
redirect_uris = [ "https://${cfg.subdomain}.${cfg.domain}/user/oauth2/${provider}/callback" ];
|
||||||
|
@ -484,8 +486,8 @@ in
|
||||||
(lib.mkIf cfg.enable {
|
(lib.mkIf cfg.enable {
|
||||||
systemd.services.forgejo.preStart = ''
|
systemd.services.forgejo.preStart = ''
|
||||||
admin="${lib.getExe config.services.forgejo.package} admin user"
|
admin="${lib.getExe config.services.forgejo.package} admin user"
|
||||||
$admin create --admin --email "root@localhost" --username meadmin --password "$(tr -d '\n' < ${cfg.adminPassword.result.path})" || true
|
$admin create --admin --email "root@localhost" --username meadmin --password "$(tr -d '\n' < ${cfg.adminPasswordFile})" || true
|
||||||
$admin change-password --username meadmin --password "$(tr -d '\n' < ${cfg.adminPassword.result.path})" || true
|
$admin change-password --username meadmin --password "$(tr -d '\n' < ${cfg.adminPasswordFile})" || true
|
||||||
'';
|
'';
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -67,12 +67,9 @@ in
|
||||||
default = "jellyfin_admin";
|
default = "jellyfin_admin";
|
||||||
};
|
};
|
||||||
|
|
||||||
adminPassword = contracts.secret.mkOption {
|
passwordFile = lib.mkOption {
|
||||||
description = "LDAP admin password.";
|
type = lib.types.path;
|
||||||
mode = "0440";
|
description = "File containing the LDAP admin password.";
|
||||||
owner = "jellyfin";
|
|
||||||
group = "jellyfin";
|
|
||||||
restartUnits = [ "jellyfin.service" ];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -121,18 +118,9 @@ in
|
||||||
default = "one_factor";
|
default = "one_factor";
|
||||||
};
|
};
|
||||||
|
|
||||||
sharedSecret = contracts.secret.mkOption {
|
secretFile = lib.mkOption {
|
||||||
description = "OIDC shared secret for Jellyfin.";
|
type = lib.types.path;
|
||||||
mode = "0440";
|
description = "File containing the OIDC shared secret.";
|
||||||
owner = "jellyfin";
|
|
||||||
group = "jellyfin";
|
|
||||||
restartUnits = [ "jellyfin.service" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
sharedSecretForAuthelia = contracts.secret.mkOption {
|
|
||||||
description = "OIDC shared secret for Authelia.";
|
|
||||||
mode = "0400";
|
|
||||||
owner = config.shb.authelia.autheliaUser;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -412,35 +400,30 @@ in
|
||||||
lib.strings.optionalString cfg.ldap.enable (shblib.replaceSecretsScript {
|
lib.strings.optionalString cfg.ldap.enable (shblib.replaceSecretsScript {
|
||||||
file = ldapConfig;
|
file = ldapConfig;
|
||||||
resultPath = "/var/lib/jellyfin/plugins/configurations/LDAP-Auth.xml";
|
resultPath = "/var/lib/jellyfin/plugins/configurations/LDAP-Auth.xml";
|
||||||
replacements = [
|
replacements = {
|
||||||
{
|
"%LDAP_PASSWORD%" = "$(cat ${cfg.ldap.passwordFile})";
|
||||||
name = [ "%LDAP_PASSWORD%" ];
|
};
|
||||||
source = cfg.ldap.adminPassword.result.path;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
})
|
})
|
||||||
+ lib.strings.optionalString cfg.sso.enable (shblib.replaceSecretsScript {
|
+ lib.strings.optionalString cfg.sso.enable (shblib.replaceSecretsScript {
|
||||||
file = ssoConfig;
|
file = ssoConfig;
|
||||||
resultPath = "/var/lib/jellyfin/plugins/configurations/SSO-Auth.xml";
|
resultPath = "/var/lib/jellyfin/plugins/configurations/SSO-Auth.xml";
|
||||||
replacements = [
|
replacements = {
|
||||||
{
|
"%SSO_SECRET%" = "$(cat ${cfg.sso.secretFile})";
|
||||||
name = [ "%SSO_SECRET%" ];
|
};
|
||||||
source = cfg.sso.sharedSecret.result.path;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
})
|
})
|
||||||
+ lib.strings.optionalString cfg.sso.enable (shblib.replaceSecretsScript {
|
+ lib.strings.optionalString cfg.sso.enable (shblib.replaceSecretsScript {
|
||||||
file = brandingConfig;
|
file = brandingConfig;
|
||||||
resultPath = "/var/lib/jellyfin/config/branding.xml";
|
resultPath = "/var/lib/jellyfin/config/branding.xml";
|
||||||
replacements = [
|
replacements = {
|
||||||
];
|
"%a%" = "%a%";
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
shb.authelia.oidcClients = lib.lists.optionals (!(isNull cfg.sso)) [
|
shb.authelia.oidcClients = lib.lists.optionals (!(isNull cfg.sso)) [
|
||||||
{
|
{
|
||||||
client_id = cfg.sso.clientID;
|
client_id = cfg.sso.clientID;
|
||||||
client_name = "Jellyfin";
|
client_name = "Jellyfin";
|
||||||
client_secret.source = cfg.sso.sharedSecretForAuthelia.result.path;
|
client_secret.source = cfg.sso.secretFile;
|
||||||
public = false;
|
public = false;
|
||||||
authorization_policy = cfg.sso.authorization_policy;
|
authorization_policy = cfg.sso.authorization_policy;
|
||||||
redirect_uris = [ "https://${cfg.subdomain}.${cfg.domain}/sso/OID/r/${cfg.sso.provider}" ];
|
redirect_uris = [ "https://${cfg.subdomain}.${cfg.domain}/sso/OID/r/${cfg.sso.provider}" ];
|
||||||
|
|
|
@ -13,7 +13,6 @@ in
|
||||||
(pkgs'.path + "/nixos/modules/profiles/headless.nix")
|
(pkgs'.path + "/nixos/modules/profiles/headless.nix")
|
||||||
(pkgs'.path + "/nixos/modules/profiles/qemu-guest.nix")
|
(pkgs'.path + "/nixos/modules/profiles/qemu-guest.nix")
|
||||||
../../modules/blocks/authelia.nix
|
../../modules/blocks/authelia.nix
|
||||||
../../modules/blocks/hardcodedsecret.nix
|
|
||||||
../../modules/blocks/ldap.nix
|
../../modules/blocks/ldap.nix
|
||||||
../../modules/blocks/postgresql.nix
|
../../modules/blocks/postgresql.nix
|
||||||
];
|
];
|
||||||
|
@ -45,12 +44,14 @@ in
|
||||||
ldapPort = config.shb.ldap.ldapPort;
|
ldapPort = config.shb.ldap.ldapPort;
|
||||||
dcdomain = config.shb.ldap.dcdomain;
|
dcdomain = config.shb.ldap.dcdomain;
|
||||||
secrets = {
|
secrets = {
|
||||||
jwtSecret.result.path = config.shb.hardcodedsecret.autheliaJwtSecret.path;
|
jwtSecretFile = pkgs.writeText "jwtSecretFile" "jwtSecretFile";
|
||||||
ldapAdminPassword.result.path = config.shb.hardcodedsecret.ldapAdminPassword.path;
|
ldapAdminPasswordFile = pkgs.writeText "ldapAdminPasswordFile" ldapAdminPassword;
|
||||||
sessionSecret.result.path = config.shb.hardcodedsecret.sessionSecret.path;
|
sessionSecretFile = pkgs.writeText "sessionSecretFile" "sessionSecretFile";
|
||||||
storageEncryptionKey.result.path = config.shb.hardcodedsecret.storageEncryptionKey.path;
|
storageEncryptionKeyFile = pkgs.writeText "storageEncryptionKeyFile" "storageEncryptionKeyFile";
|
||||||
identityProvidersOIDCHMACSecret.result.path = config.shb.hardcodedsecret.identityProvidersOIDCHMACSecret.path;
|
identityProvidersOIDCHMACSecretFile = pkgs.writeText "identityProvidersOIDCHMACSecretFile" "identityProvidersOIDCHMACSecretFile";
|
||||||
identityProvidersOIDCIssuerPrivateKey.result.path = config.shb.hardcodedsecret.identityProvidersOIDCIssuerPrivateKey.path;
|
# This needs to be of the correct shape and at least 2048 bits. Generated with:
|
||||||
|
# nix run nixpkgs#openssl -- genrsa -out keypair.pem 2048
|
||||||
|
identityProvidersOIDCIssuerPrivateKeyFile = pkgs.writeText "identityProvidersOIDCIssuerPrivateKeyFile" (builtins.readFile ./keypair.pem);
|
||||||
};
|
};
|
||||||
|
|
||||||
oidcClients = [
|
oidcClients = [
|
||||||
|
@ -72,28 +73,6 @@ in
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
shb.hardcodedsecret.autheliaJwtSecret = config.shb.authelia.secrets.jwtSecret.request // {
|
|
||||||
content = "jwtSecret";
|
|
||||||
};
|
|
||||||
shb.hardcodedsecret.ldapAdminPassword = config.shb.authelia.secrets.ldapAdminPassword.request // {
|
|
||||||
content = ldapAdminPassword;
|
|
||||||
};
|
|
||||||
shb.hardcodedsecret.sessionSecret = config.shb.authelia.secrets.sessionSecret.request // {
|
|
||||||
content = "sessionSecret";
|
|
||||||
};
|
|
||||||
shb.hardcodedsecret.storageEncryptionKey = config.shb.authelia.secrets.storageEncryptionKey.request // {
|
|
||||||
content = "storageEncryptionKey";
|
|
||||||
};
|
|
||||||
shb.hardcodedsecret.identityProvidersOIDCHMACSecret = config.shb.authelia.secrets.identityProvidersOIDCHMACSecret.request // {
|
|
||||||
content = "identityProvidersOIDCHMACSecret";
|
|
||||||
};
|
|
||||||
shb.hardcodedsecret.identityProvidersOIDCIssuerPrivateKey = config.shb.authelia.secrets.identityProvidersOIDCIssuerPrivateKey.request // {
|
|
||||||
source = (pkgs.runCommand "gen-private-key" {} ''
|
|
||||||
mkdir $out
|
|
||||||
${pkgs.openssl}/bin/openssl genrsa -out $out/private.pem 4096
|
|
||||||
'') + "/private.pem";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
testScript = { nodes, ... }: ''
|
testScript = { nodes, ... }: ''
|
||||||
|
|
|
@ -12,25 +12,11 @@ let
|
||||||
commonTest = user: pkgs.testers.runNixOSTest {
|
commonTest = user: pkgs.testers.runNixOSTest {
|
||||||
name = "restic_backupAndRestore_${user}";
|
name = "restic_backupAndRestore_${user}";
|
||||||
|
|
||||||
nodes.machine = { config, ... }: {
|
nodes.machine = {
|
||||||
imports = ( testLib.baseImports pkgs' ) ++ [
|
imports = ( testLib.baseImports pkgs' ) ++ [
|
||||||
../../modules/blocks/hardcodedsecret.nix
|
|
||||||
../../modules/blocks/restic.nix
|
../../modules/blocks/restic.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
shb.hardcodedsecret.A = {
|
|
||||||
owner = "root";
|
|
||||||
group = "keys";
|
|
||||||
mode = "0440";
|
|
||||||
content = "secretA";
|
|
||||||
};
|
|
||||||
shb.hardcodedsecret.B = {
|
|
||||||
owner = "root";
|
|
||||||
group = "keys";
|
|
||||||
mode = "0440";
|
|
||||||
content = "secretB";
|
|
||||||
};
|
|
||||||
|
|
||||||
shb.restic.instances."testinstance" = {
|
shb.restic.instances."testinstance" = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
|
@ -53,8 +39,8 @@ let
|
||||||
# Those are not needed by the repository but are still included
|
# Those are not needed by the repository but are still included
|
||||||
# so we can test them in the hooks section.
|
# so we can test them in the hooks section.
|
||||||
secrets = {
|
secrets = {
|
||||||
A.source = config.shb.hardcodedsecret.A.path;
|
A.source = "/run/secrets/A";
|
||||||
B.source = config.shb.hardcodedsecret.B.path;
|
B.source = "/run/secrets/B";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -111,6 +97,19 @@ let
|
||||||
if len(result) > 0:
|
if len(result) > 0:
|
||||||
raise Exception("Unexpected files:", result)
|
raise Exception("Unexpected files:", result)
|
||||||
|
|
||||||
|
with subtest("Create secrets"):
|
||||||
|
print(machine.succeed("""
|
||||||
|
mkdir -p /run/secrets/
|
||||||
|
|
||||||
|
echo secretA > /run/secrets/A
|
||||||
|
echo secretB > /run/secrets/B
|
||||||
|
|
||||||
|
chown root:keys -R /run/secrets
|
||||||
|
find /run/secrets -type d -exec chmod u=rwx,g=rx,o=x '{}' ';'
|
||||||
|
find /run/secrets -type f -exec chmod u=r,g=r,o= '{}' ';'
|
||||||
|
ls -l /run/secrets
|
||||||
|
"""))
|
||||||
|
|
||||||
with subtest("Create initial content"):
|
with subtest("Create initial content"):
|
||||||
machine.succeed("""
|
machine.succeed("""
|
||||||
mkdir -p /opt/files/A
|
mkdir -p /opt/files/A
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
{ lib }:
|
{
|
||||||
|
lib,
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
baseImports = pkgs: [
|
baseImports = pkgs: [
|
||||||
(pkgs.path + "/nixos/modules/profiles/headless.nix")
|
(pkgs.path + "/nixos/modules/profiles/headless.nix")
|
||||||
|
@ -107,7 +109,6 @@ in
|
||||||
../modules/blocks/postgresql.nix
|
../modules/blocks/postgresql.nix
|
||||||
../modules/blocks/authelia.nix
|
../modules/blocks/authelia.nix
|
||||||
../modules/blocks/nginx.nix
|
../modules/blocks/nginx.nix
|
||||||
../modules/blocks/hardcodedsecret.nix
|
|
||||||
]
|
]
|
||||||
++ additionalModules;
|
++ additionalModules;
|
||||||
|
|
||||||
|
@ -137,7 +138,7 @@ in
|
||||||
systemd.services.nginx.requires = [ config.shb.certs.certs.selfsigned.n.systemdService ];
|
systemd.services.nginx.requires = [ config.shb.certs.certs.selfsigned.n.systemdService ];
|
||||||
};
|
};
|
||||||
|
|
||||||
ldap = domain: pkgs: { config, ... }: {
|
ldap = domain: pkgs: {
|
||||||
imports = [
|
imports = [
|
||||||
../modules/blocks/ldap.nix
|
../modules/blocks/ldap.nix
|
||||||
];
|
];
|
||||||
|
@ -146,13 +147,6 @@ in
|
||||||
"127.0.0.1" = [ "ldap.${domain}" ];
|
"127.0.0.1" = [ "ldap.${domain}" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
shb.hardcodedsecret.ldapUserPassword = config.shb.ldap.ldapUserPassword.request // {
|
|
||||||
content = "ldapUserPassword";
|
|
||||||
};
|
|
||||||
shb.hardcodedsecret.jwtSecret = config.shb.ldap.ldapUserPassword.request // {
|
|
||||||
content = "jwtSecrets";
|
|
||||||
};
|
|
||||||
|
|
||||||
shb.ldap = {
|
shb.ldap = {
|
||||||
enable = true;
|
enable = true;
|
||||||
inherit domain;
|
inherit domain;
|
||||||
|
@ -160,8 +154,8 @@ in
|
||||||
ldapPort = 3890;
|
ldapPort = 3890;
|
||||||
webUIListenPort = 17170;
|
webUIListenPort = 17170;
|
||||||
dcdomain = "dc=example,dc=com";
|
dcdomain = "dc=example,dc=com";
|
||||||
ldapUserPassword.result.path = config.shb.hardcodedsecret.ldapUserPassword.path;
|
ldapUserPassword.result.path = pkgs.writeText "ldapUserPassword" "ldapUserPassword";
|
||||||
jwtSecret.result.path = config.shb.hardcodedsecret.jwtSecret.path;
|
jwtSecret.result.path = pkgs.writeText "jwtSecret" "jwtSecret";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -185,36 +179,17 @@ in
|
||||||
dcdomain = config.shb.ldap.dcdomain;
|
dcdomain = config.shb.ldap.dcdomain;
|
||||||
|
|
||||||
secrets = {
|
secrets = {
|
||||||
jwtSecret.result.path = config.shb.hardcodedsecret.autheliaJwtSecret.path;
|
jwtSecretFile = pkgs.writeText "jwtSecret" "jwtSecret";
|
||||||
ldapAdminPassword.result.path = config.shb.hardcodedsecret.ldapAdminPassword.path;
|
ldapAdminPasswordFile = pkgs.writeText "ldapUserPassword" "ldapUserPassword";
|
||||||
sessionSecret.result.path = config.shb.hardcodedsecret.sessionSecret.path;
|
sessionSecretFile = pkgs.writeText "sessionSecret" "sessionSecret";
|
||||||
storageEncryptionKey.result.path = config.shb.hardcodedsecret.storageEncryptionKey.path;
|
storageEncryptionKeyFile = pkgs.writeText "storageEncryptionKey" "storageEncryptionKey";
|
||||||
identityProvidersOIDCHMACSecret.result.path = config.shb.hardcodedsecret.identityProvidersOIDCHMACSecret.path;
|
identityProvidersOIDCHMACSecretFile = pkgs.writeText "identityProvidersOIDCHMACSecret" "identityProvidersOIDCHMACSecret";
|
||||||
identityProvidersOIDCIssuerPrivateKey.result.path = config.shb.hardcodedsecret.identityProvidersOIDCIssuerPrivateKey.path;
|
identityProvidersOIDCIssuerPrivateKeyFile = (pkgs.runCommand "gen-private-key" {} ''
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
shb.hardcodedsecret.autheliaJwtSecret = config.shb.authelia.secrets.jwtSecret.request // {
|
|
||||||
content = "jwtSecret";
|
|
||||||
};
|
|
||||||
shb.hardcodedsecret.ldapAdminPassword = config.shb.authelia.secrets.ldapAdminPassword.request // {
|
|
||||||
content = "ldapUserPassword";
|
|
||||||
};
|
|
||||||
shb.hardcodedsecret.sessionSecret = config.shb.authelia.secrets.sessionSecret.request // {
|
|
||||||
content = "sessionSecret";
|
|
||||||
};
|
|
||||||
shb.hardcodedsecret.storageEncryptionKey = config.shb.authelia.secrets.storageEncryptionKey.request // {
|
|
||||||
content = "storageEncryptionKey";
|
|
||||||
};
|
|
||||||
shb.hardcodedsecret.identityProvidersOIDCHMACSecret = config.shb.authelia.secrets.identityProvidersOIDCHMACSecret.request // {
|
|
||||||
content = "identityProvidersOIDCHMACSecret";
|
|
||||||
};
|
|
||||||
shb.hardcodedsecret.identityProvidersOIDCIssuerPrivateKey = config.shb.authelia.secrets.identityProvidersOIDCIssuerPrivateKey.request // {
|
|
||||||
source = (pkgs.runCommand "gen-private-key" {} ''
|
|
||||||
mkdir $out
|
mkdir $out
|
||||||
${pkgs.openssl}/bin/openssl genrsa -out $out/private.pem 4096
|
${pkgs.openssl}/bin/openssl genrsa -out $out/private.pem 4096
|
||||||
'') + "/private.pem";
|
'') + "/private.pem";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
{ pkgs, ... }:
|
|
||||||
let
|
|
||||||
contracts = pkgs.callPackage ../../modules/contracts {};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
hardcoded_root_root = contracts.test.secret {
|
|
||||||
name = "hardcoded";
|
|
||||||
modules = [ ../../modules/blocks/hardcodedsecret.nix ];
|
|
||||||
configRoot = [ "shb" "hardcodedsecret" ];
|
|
||||||
createContent = {
|
|
||||||
content = "secretA";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
hardcoded_user_group = contracts.test.secret {
|
|
||||||
name = "hardcoded";
|
|
||||||
modules = [ ../../modules/blocks/hardcodedsecret.nix ];
|
|
||||||
configRoot = [ "shb" "hardcodedsecret" ];
|
|
||||||
createContent = {
|
|
||||||
content = "secretA";
|
|
||||||
};
|
|
||||||
owner = "user";
|
|
||||||
group = "group";
|
|
||||||
mode = "640";
|
|
||||||
};
|
|
||||||
|
|
||||||
# TODO: how to do this?
|
|
||||||
# sops = contracts.test.secret {
|
|
||||||
# name = "sops";
|
|
||||||
# configRoot = cfg: name: cfg.sops.secrets.${name};
|
|
||||||
# createContent = content: {
|
|
||||||
# sopsFile = ./secret/sops.yaml;
|
|
||||||
# };
|
|
||||||
# };
|
|
||||||
}
|
|
|
@ -1,8 +1,6 @@
|
||||||
{ pkgs, lib, ... }:
|
{ pkgs, lib, ... }:
|
||||||
let
|
let
|
||||||
shblib = pkgs.callPackage ../../lib {};
|
shblib = pkgs.callPackage ../../lib {};
|
||||||
|
|
||||||
inherit (lib) nameValuePair;
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
# Tests that withReplacements can:
|
# Tests that withReplacements can:
|
||||||
|
@ -81,15 +79,15 @@ in
|
||||||
testLibGetReplacements = {
|
testLibGetReplacements = {
|
||||||
expected =
|
expected =
|
||||||
let
|
let
|
||||||
secrets = root: [
|
secrets = root: {
|
||||||
(nameValuePair "%SECRET_${root}B%" "$(cat /path/B)")
|
"%SECRET_${root}B%" = "$(cat /path/B)";
|
||||||
(nameValuePair "%SECRET_${root}C%" "prefix-$(cat /path/C)-suffix")
|
"%SECRET_${root}C%" = "prefix-$(cat /path/C)-suffix";
|
||||||
];
|
};
|
||||||
in
|
in
|
||||||
(secrets "") ++
|
(secrets "") //
|
||||||
(secrets "DOUBLENESTEDLIST_0_N_") ++
|
(secrets "NESTEDATTR_") //
|
||||||
(secrets "NESTEDATTR_") ++
|
(secrets "NESTEDLIST_0_") //
|
||||||
(secrets "NESTEDLIST_0_");
|
(secrets "DOUBLENESTEDLIST_0_N_");
|
||||||
expr =
|
expr =
|
||||||
let
|
let
|
||||||
item = {
|
item = {
|
||||||
|
@ -101,13 +99,13 @@ in
|
||||||
c.other = "other";
|
c.other = "other";
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
map shblib.genReplacement (shblib.getReplacements (
|
shblib.getReplacements (
|
||||||
item // {
|
item // {
|
||||||
nestedAttr = item;
|
nestedAttr = item;
|
||||||
nestedList = [ item ];
|
nestedList = [ item ];
|
||||||
doubleNestedList = [ { n = item; } ];
|
doubleNestedList = [ { n = item; } ];
|
||||||
}
|
}
|
||||||
));
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
testParseXML = {
|
testParseXML = {
|
||||||
|
|
|
@ -29,27 +29,19 @@ let
|
||||||
../../modules/services/forgejo.nix
|
../../modules/services/forgejo.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
basic = { config, ... }: {
|
basic = {
|
||||||
shb.forgejo = {
|
shb.forgejo = {
|
||||||
enable = true;
|
enable = true;
|
||||||
inherit domain subdomain;
|
inherit domain subdomain;
|
||||||
|
|
||||||
adminPassword.result.path = config.shb.hardcodedsecret.forgejoAdminPassword.path;
|
adminPasswordFile = pkgs.writeText "adminPasswordFile" adminPassword;
|
||||||
databasePassword.result.path = config.shb.hardcodedsecret.forgejoDatabasePassword.path;
|
databasePasswordFile = pkgs.writeText "databasePassword" "databasePassword";
|
||||||
};
|
};
|
||||||
|
|
||||||
# Needed for gitea-runner-local to be able to ping forgejo.
|
# Needed for gitea-runner-local to be able to ping forgejo.
|
||||||
networking.hosts = {
|
networking.hosts = {
|
||||||
"127.0.0.1" = [ "${subdomain}.${domain}" ];
|
"127.0.0.1" = [ "${subdomain}.${domain}" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
shb.hardcodedsecret.forgejoAdminPassword = config.shb.forgejo.adminPassword.request // {
|
|
||||||
content = adminPassword;
|
|
||||||
};
|
|
||||||
|
|
||||||
shb.hardcodedsecret.forgejoDatabasePassword = config.shb.forgejo.databasePassword.request // {
|
|
||||||
content = "databasePassword";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
https = { config, ... }: {
|
https = { config, ... }: {
|
||||||
|
@ -65,13 +57,9 @@ let
|
||||||
host = "127.0.0.1";
|
host = "127.0.0.1";
|
||||||
port = config.shb.ldap.ldapPort;
|
port = config.shb.ldap.ldapPort;
|
||||||
dcdomain = config.shb.ldap.dcdomain;
|
dcdomain = config.shb.ldap.dcdomain;
|
||||||
adminPassword.result.path = config.shb.hardcodedsecret.forgejoLdapUserPassword.path;
|
adminPasswordFile = config.shb.ldap.ldapUserPassword.result.path;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
shb.hardcodedsecret.forgejoLdapUserPassword = config.shb.forgejo.ldap.adminPassword.request // {
|
|
||||||
content = "ldapUserPassword";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
sso = { config, ... }: {
|
sso = { config, ... }: {
|
||||||
|
@ -79,18 +67,10 @@ let
|
||||||
sso = {
|
sso = {
|
||||||
enable = true;
|
enable = true;
|
||||||
endpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}";
|
endpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}";
|
||||||
sharedSecret.result.path = config.shb.hardcodedsecret.forgejoSSOPassword.path;
|
secretFile = pkgs.writeText "ssoSecretFile" "ssoSecretFile";
|
||||||
sharedSecretForAuthelia.result.path = config.shb.hardcodedsecret.forgejoSSOPasswordAuthelia.path;
|
secretFileForAuthelia = pkgs.writeText "ssoSecretFile" "ssoSecretFile";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
shb.hardcodedsecret.forgejoSSOPassword = config.shb.forgejo.sso.sharedSecret.request // {
|
|
||||||
content = "ssoPassword";
|
|
||||||
};
|
|
||||||
|
|
||||||
shb.hardcodedsecret.forgejoSSOPasswordAuthelia = config.shb.forgejo.sso.sharedSecretForAuthelia.request // {
|
|
||||||
content = "ssoPassword";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
|
|
@ -43,13 +43,9 @@ let
|
||||||
host = "127.0.0.1";
|
host = "127.0.0.1";
|
||||||
port = config.shb.ldap.ldapPort;
|
port = config.shb.ldap.ldapPort;
|
||||||
dcdomain = config.shb.ldap.dcdomain;
|
dcdomain = config.shb.ldap.dcdomain;
|
||||||
adminPassword.result.path = config.shb.hardcodedsecret.jellyfinLdapUserPassword.path;
|
passwordFile = config.shb.ldap.ldapUserPassword.result.path;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
shb.hardcodedsecret.jellyfinLdapUserPassword = config.shb.jellyfin.ldap.adminPassword.request // {
|
|
||||||
content = "ldapUserPassword";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
sso = { config, ... }: {
|
sso = { config, ... }: {
|
||||||
|
@ -57,18 +53,9 @@ let
|
||||||
sso = {
|
sso = {
|
||||||
enable = true;
|
enable = true;
|
||||||
endpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}";
|
endpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}";
|
||||||
sharedSecret.result.path = config.shb.hardcodedsecret.jellyfinSSOPassword.path;
|
secretFile = pkgs.writeText "ssoSecretFile" "ssoSecretFile";
|
||||||
sharedSecretForAuthelia.result.path = config.shb.hardcodedsecret.jellyfinSSOPasswordAuthelia.path;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
shb.hardcodedsecret.jellyfinSSOPassword = config.shb.jellyfin.sso.sharedSecret.request // {
|
|
||||||
content = "ssoPassword";
|
|
||||||
};
|
|
||||||
|
|
||||||
shb.hardcodedsecret.jellyfinSSOPasswordAuthelia = config.shb.jellyfin.sso.sharedSecretForAuthelia.request // {
|
|
||||||
content = "ssoPassword";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue