Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions modules/cardano.nix
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ in
# assert cfg.networkNumbers ? cfg.network;
{
options.cardano = {
enable = lib.mkEnableOption "all Cardano services and HTTP proxy.";
network = lib.mkOption {
description = "Cardano network to operate on.";
type = types.enum (lib.attrNames cfg.networkNumbers);
Expand Down
6 changes: 6 additions & 0 deletions modules/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
config.flake.overlays.ogmios
];
};
http = {
imports = [
./services/http-proxy.nix
./http.nix
];
};
# the default module imports all modules
default = {
imports = with builtins; attrValues (removeAttrs config.flake.nixosModules ["default"]);
Expand Down
32 changes: 32 additions & 0 deletions modules/http.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
config,
lib,
...
}: let
cfg = config.cardano.http;
inherit (lib) mkIf mkEnableOption optional;
in {
options.cardano.http = {
enable =
mkEnableOption "HTTP SSL proxy and load balancer for cardano services"
// {default = config.cardano.enable or false;};
};
config = mkIf cfg.enable {
networking.firewall.allowedTCPPorts = [80] ++ optional config.services.http-proxy.https.enable 443;

services.http-proxy = {
enable = true;
servers = mkIf config.cardano.enable ["127.0.0.1"];
services = {
cardano-node = {
inherit (config.services.cardano-node) port;
inherit (config.services.cardano-node.package.passthru.identifier) version;
};
ogmios = {
inherit (config.services.ogmios) port;
inherit (config.services.ogmios.package) version;
};
};
};
};
}
8 changes: 3 additions & 5 deletions modules/node.nix
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@
cfg = config.cardano.node;
in {
options.cardano.node = {
enable = lib.mkOption {
description = "Whether to enable the cardano-node service.";
type = lib.types.bool;
default = config.cardano.enable or false;
};
enable =
lib.mkEnableOption "cardano-node service"
// {default = config.cardano.enable or false;};

socketPath = lib.mkOption {
description = "Path to cardano-node socket.";
Expand Down
8 changes: 3 additions & 5 deletions modules/ogmios.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@
cfg = config.cardano.ogmios;
in {
options.cardano.ogmios = {
enable = lib.mkOption {
description = "Whether to enable the Ogmios bridge interface for cardano-node.";
type = lib.types.bool;
default = config.cardano.enable or false;
};
enable =
lib.mkEnableOption "Ogmios bridge interface for cardano-node"
// {default = config.cardano.enable or false;};
};

config = lib.mkIf cfg.enable {
Expand Down
112 changes: 112 additions & 0 deletions modules/services/http-proxy.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
{
config,
lib,
...
}: let
cfg = config.services.http-proxy;
inherit (lib) types listToAttrs mkOption mkEnableOption mapAttrs mkIf optionalString;
in {
options.services.http-proxy = {
enable = mkEnableOption "HTTP proxy, TLS endpoint and load balancer";
domainName = mkOption {
description = "Domain name. For each service a virtualHost is configured as a subdomain.";
type = types.str;
default = "";
};
https.enable = mkOption {
description = "Enable TLS and redirect all connections to HTTPS. Requires certificates. Supports ACME.";
type = types.bool;
default = false;
};
https.acme.enable = mkOption {
description = "Enable Let's Encrypt ACME TLS certificates. Requires public DNS records pointing to server and `security.acme` configured.";
type = types.bool;
default = cfg.https.enable;
};
servers = mkOption {
description = "List of upstream server host names used for all services.";
type = types.listOf types.str;
default = [];
};
services = mkOption {
description = "Configuraiton for each upstream service.";
type = types.attrsOf (types.submodule ({name, ...}: {
options = {
name = mkOption {
description = "Name of the service.";
type = types.str;
default = name;
};
servers = mkOption {
description = "List of upstream server host names.";
type = types.listOf types.str;
default = cfg.servers;
};
port = mkOption {
description = "Upstream server port.";
type = types.port;
};
version = mkOption {
description = "This string will be served at path '/version'.";
type = types.nullOr types.str;
default = null;
};
};
}));
};
_mkUpstream = mkOption {
type = types.functionTo types.attrs;
internal = true;
default = service: {
servers = listToAttrs (map
(server: {
name = "${server}:${toString service.port}";
value = {};
})
service.servers);
};
};
_mkVirtualHost = mkOption {
type = types.functionTo types.attrs;
internal = true;
default = service: {
serverName = "${service.name}${optionalString (cfg.domainName != "") ".${cfg.domainName}"}";
forceSSL = cfg.https.enable;
enableACME = cfg.https.acme.enable;
locations = {
"=/version" = mkIf (service.version != null) {
return = "200 ${service.version}";
extraConfig = "add_header Content-Type text/plain;";
};
"/" = {
proxyWebsockets = true;
proxyPass = "http://${service.name}";
};
};
extraConfig = "
proxy_hide_header Access-Control-Allow-Origin;
add_header Access-Control-Allow-Origin * always;
proxy_hide_header Access-Control-Allow-Headers;
add_header Access-Control-Allow-Headers * always;
proxy_hide_header Access-Control-Allow-Methods;
add_header Access-Control-Allow-Methods * always;
";
};
};
};
config = mkIf cfg.enable {
services.nginx = {
enable = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
serverNamesHashBucketSize = 128;

statusPage = true;

upstreams = mapAttrs (_: cfg._mkUpstream) cfg.services;
virtualHosts = mapAttrs (_: cfg._mkVirtualHost) cfg.services;
};
};
}
2 changes: 1 addition & 1 deletion modules/services/ogmios.nix
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ in {
host = mkOption {
description = "Host address or name to listen on.";
type = str;
default = "localhost";
default = "127.0.0.1";
};

port = mkOption {
Expand Down
1 change: 1 addition & 0 deletions tests/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
./cardano-cli.nix
./cardano-node.nix
./ogmios.nix
./http.nix
];
}
36 changes: 36 additions & 0 deletions tests/http.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
perSystem.vmTests.tests.http = {
impure = true;
module = {
nodes.node = {config, ...}: {
cardano = {
network = "preview";
node.enable = true;
ogmios.enable = true;
};
services.ogmios.host = "0.0.0.0";
networking.firewall.allowedTCPPorts = [config.services.ogmios.port];
};

nodes.proxy = {
cardano = {
http.enable = true;
};
services.http-proxy.servers = ["node"];
};

nodes.client = {pkgs, ...}: {
environment.systemPackages = [pkgs.curl];
};

testScript = {nodes, ...}: ''
start_all()
node.wait_for_unit("ogmios")
node.wait_until_succeeds('curl --fail http://127.0.0.1:1337/health')
proxy.wait_for_unit("nginx")
client.wait_until_succeeds('curl --fail -H "Host: ogmios" http://proxy/health')
client.succeed('[ "${nodes.node.services.ogmios.package.version}" == "$(curl --silent --fail -H "Host: ogmios" http://proxy/version)" ]')
'';
};
};
}