Compare commits

..

2 Commits

Author SHA1 Message Date
ab7a7c2ef3 Add blog
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-14 19:16:34 +01:00
289c649bc3 Add gitea runner image
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-14 17:25:42 +01:00
12 changed files with 160 additions and 27 deletions

8
flake.lock generated
View File

@@ -511,11 +511,11 @@
"secrets": { "secrets": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1757583391, "lastModified": 1757873556,
"narHash": "sha256-q5ZXkTv0SJw7OMbu2K3b03Fbb+1Hz6ZafqdqGneyX9A=", "narHash": "sha256-WYrV46if1XsiQKOQEMNtHdAPeFDeu7YBdcoNSXc3sf8=",
"ref": "refs/heads/main", "ref": "refs/heads/main",
"rev": "42df461dac05dccd22df0c36007174dd73aa0aea", "rev": "21ab0b0a59264b1da501f90725bf2c03e07ae941",
"revCount": 40, "revCount": 43,
"type": "git", "type": "git",
"url": "ssh://git@karaolidis.com/karaolidis/nix-secrets.git" "url": "ssh://git@karaolidis.com/karaolidis/nix-secrets.git"
}, },

View File

@@ -0,0 +1,60 @@
{ user, home }:
{
config,
inputs,
lib,
pkgs,
...
}:
let
hmConfig = config.home-manager.users.${user};
inherit (hmConfig.virtualisation.quadlet) volumes networks;
in
{
home-manager.users.${user} = {
sops = {
secrets."blog/apiKey".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
templates.blog-receiver-env.content = ''
AUTH_KEY=${hmConfig.sops.placeholder."blog/apiKey"}
'';
};
virtualisation.quadlet = {
volumes.blog = { };
containers = {
blog.containerConfig = {
image = "docker-archive:${pkgs.dockerImages.nginx}";
networks = [ networks.traefik.ref ];
volumes = [
"${volumes.blog.ref}:/var/www/nginx:ro"
];
labels = [
"traefik.enable=true"
"traefik.http.routers.blog.rule=Host(`blog.karaolidis.com`)"
];
};
blog-receiver = {
containerConfig = {
image = "docker-archive:${pkgs.dockerImages.nginx-receiver}";
networks = [ networks.traefik.ref ];
volumes = [ "${volumes.blog.ref}:/var/www/nginx" ];
environments = {
TARGET_DIR = "/var/www/nginx";
SUBPATH = "/upload";
};
environmentFiles = [ hmConfig.sops.templates.blog-receiver-env.path ];
labels = [
"traefik.enable=true"
"traefik.http.routers.blog-receiver.rule=Host(`blog.karaolidis.com`) && PathPrefix(`/upload`)"
];
};
unitConfig.After = [ "sops-nix.service" ];
};
};
};
};
}

View File

@@ -12,6 +12,7 @@ in
imports = [ imports = [
(import ./attic { inherit user home; }) (import ./attic { inherit user home; })
(import ./authelia { inherit user home; }) (import ./authelia { inherit user home; })
(import ./blog { inherit user home; })
(import ./comentario { inherit user home; }) (import ./comentario { inherit user home; })
(import ./gitea { inherit user home; }) (import ./gitea { inherit user home; })
(import ./grafana { inherit user home; }) (import ./grafana { inherit user home; })

View File

@@ -61,7 +61,12 @@ in
home-manager.users.${user} = home-manager.users.${user} =
let let
autheliaClientId = "I2ZYDFGWP1bzfiauXe94IaiReZF6SqoEskSp6phoL2L8l16Cq7YX3Vr4pkQOSYfNDOwuFjTRIpqQ8eAqK0M93NeEgpr8YoPhKHyR"; autheliaClientId = "I2ZYDFGWP1bzfiauXe94IaiReZF6SqoEskSp6phoL2L8l16Cq7YX3Vr4pkQOSYfNDOwuFjTRIpqQ8eAqK0M93NeEgpr8YoPhKHyR";
inherit (hmConfig.virtualisation.quadlet) containers volumes networks; inherit (hmConfig.virtualisation.quadlet)
containers
volumes
networks
images
;
in in
{ {
sops = { sops = {
@@ -214,6 +219,16 @@ in
gitea-act-runner-cache = { }; gitea-act-runner-cache = { };
}; };
images.gitea-act-runner-worker.imageConfig = {
image = "docker-archive:${pkgs.dockerImages.gitea-act-runner-worker}";
tag =
let
name = pkgs.dockerImages.gitea-act-runner-worker.passthru.buildArgs.name;
tag = pkgs.dockerImages.gitea-act-runner-worker.passthru.imageTag;
in
"localhost/${name}:${tag}";
};
containers = { containers = {
gitea = { gitea = {
containerConfig = { containerConfig = {
@@ -278,8 +293,22 @@ in
volumes = volumes =
let let
uid = builtins.toString config.users.users.${user}.uid; uid = builtins.toString config.users.users.${user}.uid;
runnerConfig = (pkgs.formats.yaml { }).generate "config.yaml" {
runner = {
file = "/var/lib/gitea-act-runner/registration";
capacity = 4;
labels = [ "nix:docker://${images.gitea-act-runner-worker.imageConfig.tag}" ];
};
cache.dir = "/tmp/gitea-act-runner/";
container = {
privileged = true;
docker_host = "-";
};
};
in in
[ [
"${runnerConfig}:/etc/gitea-act-runner/config.yaml:ro"
"/run/user/${uid}/podman/podman.sock:/var/run/docker.sock" "/run/user/${uid}/podman/podman.sock:/var/run/docker.sock"
"${volumes.gitea-act-runner-data.ref}:/var/lib/gitea-act-runner" "${volumes.gitea-act-runner-data.ref}:/var/lib/gitea-act-runner"
"${volumes.gitea-act-runner-cache.ref}:/tmp/gitea-act-runner" "${volumes.gitea-act-runner-cache.ref}:/tmp/gitea-act-runner"

View File

@@ -170,14 +170,14 @@ in
]; ];
volumes = volumes =
let let
post-setup = pkgs.writeTextFile { postSetup = pkgs.writeTextFile {
name = "post-setup.sh"; name = "post-setup.sh";
executable = true; executable = true;
text = builtins.readFile ./post-setup.sh; text = builtins.readFile ./post-setup.sh;
}; };
in in
[ [
"${post-setup}:/etc/nextcloud/post-setup.sh:ro" "${postSetup}:/etc/nextcloud/post-setup.sh:ro"
"/mnt/storage/private/storm/containers/storage/volumes/nextcloud-data/_data:/var/lib/nextcloud" "/mnt/storage/private/storm/containers/storage/volumes/nextcloud-data/_data:/var/lib/nextcloud"
"${volumes.nextcloud-log.ref}:/var/log/nextcloud" "${volumes.nextcloud-log.ref}:/var/log/nextcloud"
"${volumes.nextcloud-config.ref}:/var/www/nextcloud/config" "${volumes.nextcloud-config.ref}:/var/www/nextcloud/config"

View File

@@ -20,6 +20,7 @@ final: prev:
flaresolverr = final.docker-image-flaresolverr; flaresolverr = final.docker-image-flaresolverr;
gitea = final.docker-image-gitea; gitea = final.docker-image-gitea;
gitea-act-runner = final.docker-image-gitea-act-runner; gitea-act-runner = final.docker-image-gitea-act-runner;
gitea-act-runner-worker = final.docker-image-gitea-act-runner-worker;
grafana = final.docker-image-grafana; grafana = final.docker-image-grafana;
grafana-image-renderer = final.docker-image-grafana-image-renderer; grafana-image-renderer = final.docker-image-grafana-image-renderer;
jellyfin = final.docker-image-jellyfin; jellyfin = final.docker-image-jellyfin;

View File

@@ -13,6 +13,7 @@
docker-image-flaresolverr = import ./docker/flaresolverr { inherit pkgs; }; docker-image-flaresolverr = import ./docker/flaresolverr { inherit pkgs; };
docker-image-gitea = import ./docker/gitea { inherit pkgs; }; docker-image-gitea = import ./docker/gitea { inherit pkgs; };
docker-image-gitea-act-runner = import ./docker/gitea-act-runner { inherit pkgs; }; docker-image-gitea-act-runner = import ./docker/gitea-act-runner { inherit pkgs; };
docker-image-gitea-act-runner-worker = import ./docker/gitea-act-runner-worker { inherit pkgs; };
docker-image-grafana = import ./docker/grafana { inherit pkgs; }; docker-image-grafana = import ./docker/grafana { inherit pkgs; };
docker-image-grafana-image-renderer = import ./docker/grafana-image-renderer { inherit pkgs; }; docker-image-grafana-image-renderer = import ./docker/grafana-image-renderer { inherit pkgs; };
docker-image-jellyfin = import ./docker/jellyfin { inherit pkgs; }; docker-image-jellyfin = import ./docker/jellyfin { inherit pkgs; };

View File

@@ -0,0 +1,38 @@
{ pkgs, ... }:
let
containerPolicy = pkgs.writeTextDir "/etc/containers/policy.json" (
builtins.readFile (
(pkgs.formats.json { }).generate "policy.json" {
default = [ { type = "insecureAcceptAnything"; } ];
transports.docker-daemon."" = [ { type = "insecureAcceptAnything"; } ];
}
)
);
in
pkgs.dockerTools.buildImage {
name = "gitea-act-runner-worker";
fromImage = pkgs.docker-image-base;
copyToRoot = pkgs.buildEnv {
name = "root";
paths = with pkgs; [
git
git-lfs
curl
jq
nix
nodejs
buildah
skopeo
containerPolicy
];
pathsToLink = [
"/bin"
"/etc"
];
};
runAsRoot = ''
mkdir -p /var/tmp
'';
}

View File

@@ -10,16 +10,7 @@ let
runnerConfig = pkgs.writeTextDir "/etc/gitea-act-runner/config.yaml" ( runnerConfig = pkgs.writeTextDir "/etc/gitea-act-runner/config.yaml" (
builtins.readFile ( builtins.readFile (
(pkgs.formats.yaml { }).generate "config.yaml" { (pkgs.formats.yaml { }).generate "config.yaml" {
runner = { runner.file = "/var/lib/gitea-act-runner/registration";
file = "/var/lib/gitea-act-runner/registration";
capacity = 4;
labels = [
"ubuntu-latest:docker://catthehacker/ubuntu:act-latest"
"ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04"
"ubuntu-20.04:docker://catthehacker/ubuntu:act-20.04"
"ubuntu-18.04:docker://catthehacker/ubuntu:act-18.04"
];
};
cache.dir = "/tmp/gitea-act-runner/"; cache.dir = "/tmp/gitea-act-runner/";
# https://gitea.com/gitea/act_runner/issues/223#issuecomment-743748 # https://gitea.com/gitea/act_runner/issues/223#issuecomment-743748
container.docker_host = "-"; container.docker_host = "-";

View File

@@ -27,11 +27,14 @@ var (
maxUploadSize int64 = 1 << 30 // 1GB maxUploadSize int64 = 1 << 30 // 1GB
deployLock sync.Mutex deployLock sync.Mutex
infoLog = log.New(os.Stdout, "", log.LstdFlags)
errorLog = log.New(os.Stderr, "", log.LstdFlags)
) )
func main() { func main() {
if authenticationKey == "" || targetDirectory == "" { if authenticationKey == "" || targetDirectory == "" {
log.Fatal("AUTH_KEY and TARGET_DIR must be set") errorLog.Fatal("AUTH_KEY and TARGET_DIR must be set")
} }
if port == "" { if port == "" {
@@ -43,15 +46,15 @@ func main() {
basePath = "/" + subPath basePath = "/" + subPath
} }
log.Printf("starting server on :%s, endpoint %q, target directory %q", port, basePath, targetDirectory) infoLog.Printf("starting server on :%s, endpoint %q, target directory %q", port, basePath, targetDirectory)
http.HandleFunc(basePath, withRecovery(handle)) http.HandleFunc(basePath, withRecovery(handle))
log.Fatal(http.ListenAndServe(":"+port, nil)) errorLog.Fatal(http.ListenAndServe(":"+port, nil))
} }
func handle(w http.ResponseWriter, r *http.Request) { func handle(w http.ResponseWriter, r *http.Request) {
remoteIP := realIP(r) remoteIP := realIP(r)
log.Printf("incoming %q request on %q from %s", r.Method, r.URL.Path, remoteIP) infoLog.Printf("incoming %q request on %q from %s", r.Method, r.URL.Path, remoteIP)
if r.Method != http.MethodPost { if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed) http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
@@ -60,7 +63,7 @@ func handle(w http.ResponseWriter, r *http.Request) {
auth := r.Header.Get("Authorization") auth := r.Header.Get("Authorization")
if subtle.ConstantTimeCompare([]byte(auth), []byte(authenticationKey)) != 1 { if subtle.ConstantTimeCompare([]byte(auth), []byte(authenticationKey)) != 1 {
log.Printf("unauthorized request from %s", remoteIP) errorLog.Printf("unauthorized request from %s", remoteIP)
http.Error(w, "unauthorized", http.StatusUnauthorized) http.Error(w, "unauthorized", http.StatusUnauthorized)
return return
} }
@@ -112,7 +115,7 @@ func handle(w http.ResponseWriter, r *http.Request) {
defer os.RemoveAll(extractDir) defer os.RemoveAll(extractDir)
if err := extractor.Extract(ctx, archiveStream, extract(extractDir)); err != nil { if err := extractor.Extract(ctx, archiveStream, extract(extractDir)); err != nil {
log.Printf("failed to extract archive: %v", err) errorLog.Printf("failed to extract archive: %v", err)
http.Error(w, "bad archive", http.StatusBadRequest) http.Error(w, "bad archive", http.StatusBadRequest)
return return
} }
@@ -131,7 +134,7 @@ func handle(w http.ResponseWriter, r *http.Request) {
} }
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
log.Printf("upload successful from %s", remoteIP) infoLog.Printf("upload successful from %s", remoteIP)
} }
func realIP(r *http.Request) string { func realIP(r *http.Request) string {
@@ -235,7 +238,7 @@ func withRecovery(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
defer func() { defer func() {
if v := recover(); v != nil { if v := recover(); v != nil {
log.Printf("panic: %v", v) errorLog.Printf("panic: %v", v)
http.Error(w, "internal error", http.StatusInternalServerError) http.Error(w, "internal error", http.StatusInternalServerError)
} }
}() }()

View File

@@ -1 +1,10 @@
{ patcher, ... }: { } { patcher, ... }:
{
quadlet-nix.patches = [
(patcher.fetchpatch {
name = "feat: supports images";
url = "https://github.com/SEIAROTg/quadlet-nix/compare/main...karaolidis:quadlet-nix:image.diff";
hash = "sha256-XLdOrSJ/gyLARGI0psBejtpX9Z2NSRTaUbFtBi8BxPw=";
})
];
}