You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
336 lines
13 KiB
336 lines
13 KiB
pkgs: { config, options, name, nodes, ... }: |
|
with pkgs; |
|
|
|
let |
|
maintenanceMode = false; |
|
cfg = config.services.cardano-db-sync; |
|
nodeCfg = config.services.cardano-node; |
|
nodeId = config.node.nodeId; |
|
hostAddr = getListenIp nodes.${name}; |
|
inherit (cardanoDbSyncHaskellPackages.cardano-db-sync.components.exes) cardano-db-sync; |
|
inherit (cardanoDbSyncHaskellPackages.cardano-db-sync-extended.components.exes) cardano-db-sync-extended; |
|
inherit (cardanoDbSyncHaskellPackages.cardano-node.components.exes) cardano-node; |
|
inherit (cardanoDbSyncHaskellPackages.cardano-db.components.exes) cardano-db-tool; |
|
in { |
|
imports = [ |
|
(sourcePaths.cardano-node + "/nix/nixos") |
|
(sourcePaths.cardano-graphql + "/nix/nixos") |
|
(sourcePaths.cardano-rest + "/nix/nixos") |
|
(sourcePaths.cardano-db-sync + "/nix/nixos") |
|
(sourcePaths.cardano-rosetta + "/nix/nixos") |
|
cardano-ops.modules.base-service |
|
cardano-ops.modules.cardano-postgres |
|
]; |
|
|
|
environment.systemPackages = with pkgs; [ |
|
bat fd lsof netcat ncdu ripgrep tree vim dnsutils cardano-cli |
|
cardano-db-tool |
|
]; |
|
services.cardano-postgres.enable = true; |
|
services.postgresql = { |
|
ensureDatabases = [ "cexplorer" ]; |
|
ensureUsers = [ |
|
{ |
|
name = "cexplorer"; |
|
ensurePermissions = { |
|
"DATABASE cexplorer" = "ALL PRIVILEGES"; |
|
}; |
|
} |
|
]; |
|
identMap = '' |
|
explorer-users root cexplorer |
|
explorer-users cexplorer cexplorer |
|
explorer-users postgres postgres |
|
''; |
|
authentication = '' |
|
local all all ident map=explorer-users |
|
''; |
|
}; |
|
|
|
services.graphql-engine.enable = true; |
|
services.cardano-graphql = { |
|
enable = true; |
|
genesisByron = nodeCfg.nodeConfig.ByronGenesisFile; |
|
genesisShelley = nodeCfg.nodeConfig.ShelleyGenesisFile; |
|
allowListPath = cardano-explorer-app-pkgs.allowList; |
|
cardanoNodeSocketPath = nodeCfg.socketPath; |
|
}; |
|
|
|
services.cardano-rosetta-server = { |
|
enable = true; |
|
topologyFilePath = nodeCfg.topology; |
|
cardanoCliPath = cardano-cli + /bin/cardano-cli; |
|
genesisPath = nodeCfg.nodeConfig.ShelleyGenesisFile; |
|
cardanoNodePath = cardano-node + /bin/cardano-node; |
|
cardanoNodeSocketPath = nodeCfg.socketPath; |
|
bindAddress = "127.0.0.1"; |
|
port = 8082; |
|
dbConnectionString = "socket://${cfg.postgres.user}:*@${cfg.postgres.socketdir}?db=${cfg.postgres.database}"; |
|
}; |
|
|
|
# Temporarily required until the following cardano-graphql issue is fixed: |
|
# https://github.com/input-output-hk/cardano-graphql/issues/268 |
|
systemd.services.cardano-graphql.startLimitIntervalSec = 0; |
|
systemd.services.cardano-graphql.serviceConfig.Restart = "always"; |
|
systemd.services.cardano-graphql.serviceConfig.RestartSec = "10s"; |
|
|
|
services.cardano-node = { |
|
rtsArgs = lib.mkForce |
|
(if globals.withHighCapacityExplorer then |
|
[ "-N2" "-A10m" "-qg" "-qb" "-M10G" ] |
|
else |
|
[ "-N2" "-A10m" "-qg" "-qb" "-M3G" ]); |
|
package = cardano-node; |
|
}; |
|
|
|
systemd.services.cardano-node.serviceConfig.MemoryMax = lib.mkForce |
|
(if globals.withHighCapacityExplorer then "14G" else "3.5G"); |
|
|
|
services.cardano-db-sync = { |
|
enable = true; |
|
cluster = globals.environmentName; |
|
environment = globals.environmentConfig; |
|
socketPath = nodeCfg.socketPath; |
|
logConfig = iohkNix.cardanoLib.defaultExplorerLogConfig // { hasPrometheus = [ hostAddr 12698 ]; }; |
|
user = "cexplorer"; |
|
extended = globals.withCardanoDBExtended; |
|
package = if globals.withCardanoDBExtended |
|
then cardano-db-sync-extended |
|
else cardano-db-sync; |
|
postgres = { |
|
database = "cexplorer"; |
|
}; |
|
|
|
}; |
|
|
|
systemd.services.cardano-db-sync.serviceConfig = { |
|
# Put cardano-db-sync in "cardano-node" group so that it can write socket file: |
|
SupplementaryGroups = "cardano-node"; |
|
# FIXME: https://github.com/input-output-hk/cardano-db-sync/issues/102 |
|
Restart = "always"; |
|
RestartSec = "30s"; |
|
}; |
|
|
|
systemd.services.cardano-rosetta-server.serviceConfig = { |
|
User = "cexplorer"; |
|
SupplementaryGroups = "cardano-node"; |
|
}; |
|
|
|
systemd.services.cardano-submit-api.serviceConfig = lib.mkIf globals.withSubmitApi { |
|
# Put cardano-db-sync in "cardano-node" group so that it can write socket file: |
|
SupplementaryGroups = "cardano-node"; |
|
}; |
|
|
|
services.cardano-explorer-api = { |
|
enable = true; |
|
port = 8100; |
|
package = cardano-rest-pkgs.cardanoRestHaskellPackages.cardano-explorer-api.components.exes.cardano-explorer-api; |
|
}; |
|
systemd.services.cardano-explorer-api.startLimitIntervalSec = 0; |
|
systemd.services.cardano-explorer-api.serviceConfig.Restart = "always"; |
|
systemd.services.cardano-explorer-api.serviceConfig.RestartSec = "10s"; |
|
systemd.services.cardano-explorer-api.serviceConfig.LimitNOFILE = 4096; |
|
|
|
services.cardano-submit-api = lib.mkIf globals.withSubmitApi { |
|
enable = true; |
|
port = 8101; |
|
environment = pkgs.globals.environmentConfig; |
|
socketPath = config.services.cardano-node.socketPath; |
|
package = cardano-rest-pkgs.cardanoRestHaskellPackages.cardano-submit-api.components.exes.cardano-submit-api; |
|
}; |
|
|
|
networking.firewall.allowedTCPPorts = [ 80 443 ]; |
|
|
|
security.acme = lib.mkIf (config.deployment.targetEnv != "libvirtd") { |
|
email = "devops@iohk.io"; |
|
acceptTerms = true; # https://letsencrypt.org/repository/ |
|
}; |
|
|
|
systemd.services.dump-registered-relays-topology = let |
|
extract_relays_sql = writeText "extract_relays.sql" '' |
|
select array_to_json(array_agg(row_to_json(t))) from ( |
|
select COALESCE(ipv4, dns_name) as addr, port from ( |
|
select min(update_id) as update_id, ipv4, dns_name, port from pool_relay where |
|
ipv4 is null or ipv4 !~ '(^0\.)|(^10\.)|(^100\.6[4-9]\.)|(^100\.[7-9]\d\.)|(^100\.1[0-1]\d\.)|(^100\.12[0-7]\.)|(^127\.)|(^169\.254\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.0\.0\.)|(^192\.0\.2\.)|(^192\.88\.99\.)|(^192\.168\.)|(^198\.1[8-9]\.)|(^198\.51\.100\.)|(^203.0\.113\.)|(^22[4-9]\.)|(^23[0-9]\.)|(^24[0-9]\.)|(^25[0-5]\.)' |
|
group by ipv4, dns_name, port order by update_id |
|
) t |
|
) t; |
|
''; |
|
relays_exclude_file = builtins.toFile "relays-exclude.txt" (lib.concatStringsSep "\n" globals.static.relaysExcludeList); |
|
networkMagic = (builtins.fromJSON (builtins.readFile globals.environmentConfig.nodeConfig.ShelleyGenesisFile)).networkMagic; |
|
in { |
|
path = [ config.services.postgresql.package jq netcat curl dnsutils ]; |
|
script = '' |
|
set -uo pipefail |
|
excludeList="$(sort ${relays_exclude_file})" |
|
cd $STATE_DIRECTORY |
|
rm -f relays.json |
|
for r in $(psql -t < ${extract_relays_sql} | jq -c '.[]'); do |
|
addr=$(echo "$r" | jq -r '.addr') |
|
port=$(echo "$r" | jq -r '.port') |
|
allAddresses="$addr\n$(dig +short $addr)" |
|
excludedAddresses=$(comm -12 <(echo "$allAddresses" | sort) <(echo "$excludeList")) |
|
nbExcludedAddresses=$(echo $excludedAddresses | wc -w) |
|
if [[ $nbExcludedAddresses == 0 ]]; then |
|
set +e |
|
PING="$(timeout 2s ${cardano-ping}/bin/cardano-ping -h $addr -p $port -m ${toString networkMagic} -c 1 -q --json)" |
|
res=$? |
|
if [ $res -eq 0 ]; then |
|
echo $PING | ${jq}/bin/jq -c > /dev/null 2>&1 |
|
res=$? |
|
fi |
|
set -e |
|
if [ $res -eq 0 ]; then |
|
>&2 echo "Successfully pinged $addr:$port" |
|
geoinfo=$(curl -s https://json.geoiplookup.io/$addr) |
|
continent=$(echo "$geoinfo" | jq -r '.continent_name') |
|
country_code=$(echo "$geoinfo" | jq -r '.country_code') |
|
if [ "$country_code" == "US" ]; then |
|
state=$(echo $geoinfo | jq -r '.region') |
|
else |
|
state=$country_code |
|
fi |
|
echo $r | jq -c --arg continent "$continent" \ |
|
--arg state "$state" '. + {continent: $continent, state: $state}' >> relays.json |
|
else |
|
>&2 echo "failed to cardano-ping $addr:$port" |
|
fi |
|
else |
|
>&2 echo "$addr excluded due to dns name or IPs being in exclude list:\n$excludedAddresses" |
|
fi |
|
done |
|
if [ -f relays.json ]; then |
|
cat relays.json | jq -n '. + [inputs]' | jq '{ Producers : . }' > topology.json |
|
mkdir -p relays |
|
mv topology.json relays/topology.json |
|
fi |
|
''; |
|
serviceConfig = { |
|
User = cfg.user; |
|
StateDirectory = "registered-relays-dump"; |
|
}; |
|
}; |
|
systemd.timers.dump-registered-relays-topology = { |
|
timerConfig.OnCalendar = "hourly"; |
|
wantedBy = [ "timers.target" ]; |
|
}; |
|
|
|
services.nginx = { |
|
enable = true; |
|
package = nginxExplorer; |
|
eventsConfig = '' |
|
worker_connections 4096; |
|
''; |
|
appendConfig = '' |
|
worker_rlimit_nofile 16384; |
|
''; |
|
recommendedGzipSettings = true; |
|
recommendedOptimisation = true; |
|
recommendedProxySettings = true; |
|
commonHttpConfig = '' |
|
log_format x-fwd '$remote_addr - $remote_user [$time_local] ' |
|
'"$request" "$http_accept_language" $status $body_bytes_sent ' |
|
'"$http_referer" "$http_user_agent" "$http_x_forwarded_for"'; |
|
|
|
access_log syslog:server=unix:/dev/log x-fwd; |
|
limit_req_zone $binary_remote_addr zone=apiPerIP:100m rate=1r/s; |
|
limit_req_status 429; |
|
map $http_accept_language $lang { |
|
default en; |
|
~de de; |
|
~ja ja; |
|
} |
|
''; |
|
virtualHosts = { |
|
"${globals.explorerHostName}" = { |
|
serverAliases = globals.explorerAliases; |
|
enableACME = true; |
|
forceSSL = globals.explorerForceSSL; |
|
locations = (if maintenanceMode then { |
|
"/" = let |
|
maintenanceFile = __toFile "maintenance.html" '' |
|
<!doctype html> |
|
<title>Site Maintenance</title> |
|
<style> |
|
body { text-align: center; padding: 150px; } |
|
h1 { font-size: 50px; } |
|
body { font: 20px Helvetica, sans-serif; color: #333; } |
|
article { display: block; text-align: left; width: 650px; margin: 0 auto; } |
|
a { color: #dc8100; text-decoration: none; } |
|
a:hover { color: #333; text-decoration: none; } |
|
</style> |
|
|
|
<article> |
|
<h1>We’ll be back soon!</h1> |
|
<div> |
|
<p>Sorry for the inconvenience, but we’re performing some maintenance at the moment. We’ll be back online shortly!</p> |
|
<p>— IOHK DevOps</p> |
|
</div> |
|
</article> |
|
''; |
|
rootDir = pkgs.runCommand "nginx-root-dir" {} '' |
|
mkdir $out |
|
cd $out |
|
cp ${maintenanceFile} index.html; |
|
''; |
|
in { |
|
extraConfig = '' |
|
etag off; |
|
add_header etag "\"${builtins.substring 11 32 rootDir}\""; |
|
root ${rootDir}; |
|
''; |
|
tryFiles = "$uri /index.html"; |
|
}; |
|
} else { |
|
"/" = { |
|
root = (cardano-explorer-app-pkgs.overrideScope'(self: super: { |
|
static = super.static.override { |
|
graphqlApiHost = globals.explorerHostName; |
|
cardanoNetwork = globals.environmentName; |
|
gaTrackingId = globals.static.gaTrackingId or null; |
|
}; |
|
})).static; |
|
tryFiles = "$uri $uri/index.html /index.html"; |
|
extraConfig = '' |
|
rewrite /tx/([0-9a-f]+) /$lang/transaction.html?id=$1 redirect; |
|
rewrite /address/([0-9a-zA-Z]+) /$lang/address.html?address=$1 redirect; |
|
rewrite /block/([0-9a-zA-Z]+) /$lang/block.html?id=$1 redirect; |
|
rewrite /epoch/([0-9]+) /$lang/epoch.html?number=$1 redirect; |
|
rewrite ^([^.]*[^/])$ $1.html redirect; |
|
''; |
|
}; |
|
# To avoid 502 alerts when withSubmitApi is false |
|
"/api/submit/tx" = lib.mkIf globals.withSubmitApi { |
|
proxyPass = "http://127.0.0.1:8101/api/submit/tx"; |
|
}; |
|
"/api" = { |
|
proxyPass = "http://127.0.0.1:8100/api"; |
|
extraConfig = '' |
|
limit_req zone=apiPerIP; |
|
''; |
|
}; |
|
"/graphql" = { |
|
proxyPass = "http://127.0.0.1:3100/"; |
|
}; |
|
"/rosetta/" = { |
|
proxyPass = "http://127.0.0.1:8082/"; |
|
}; |
|
}) // { |
|
"/relays" = { |
|
root = "/var/lib/registered-relays-dump"; |
|
}; |
|
}; |
|
}; |
|
"explorer-ip" = { |
|
locations = { |
|
"/metrics2/exporter" = { |
|
proxyPass = "http://127.0.0.1:8080/"; |
|
}; |
|
"/metrics2/cardano-graphql" = { |
|
proxyPass = "http://127.0.0.1:3100/metrics"; |
|
}; |
|
}; |
|
}; |
|
}; |
|
}; |
|
}
|
|
|