Compare commits

..
Author SHA1 Message Date
telackey 42a4035a64 Add --fqdn-policy option to deploy-webapp-from-registry. 2024-04-12 16:57:36 -05:00
telackey 7cce68959f relay 2024-04-04 22:55:45 -05:00
telackey 0cd90ad3dc Target reverse_proxy by env. 2024-04-04 14:32:12 -05:00
telackey 90d92c3e15 Fix Docker build 2024-04-02 22:20:02 -05:00
telackey 202c68d996 unused file 2024-04-02 21:25:06 -05:00
telackey 236dea1b44 typo 2024-04-02 18:43:05 -05:00
telackey 9aa02fc26a missing env 2024-04-02 18:39:48 -05:00
telackey 80e039d70e Remove abandoned file. 2024-04-02 18:03:59 -05:00
telackey d7f06ad51e Combine stacks. 2024-04-02 18:02:54 -05:00
telackey 343d31c324 Add README 2024-04-01 16:07:47 -05:00
telackey fd00143b76 Add README 2024-04-01 16:06:52 -05:00
telackey 36e89b5a78 Linea Sepolia Testnet-based Nitro Auth 2024-04-01 15:53:01 -05:00
telackey 80b299c1e5 Swap caddy for nginx 2024-03-22 17:10:23 -05:00
telackey f20dcb8457 revert 2024-03-21 16:19:42 -05:00
telackey 7929a137b6 revert 2024-03-21 16:19:14 -05:00
telackey 247025de28 Shorten sleep 2024-03-21 12:12:26 -05:00
telackey 76821f00a0 stack updates 2024-03-21 02:10:37 -05:00
telackey 78219c0e04 Ask nitor 2024-03-19 21:11:52 -05:00
telackey 9e87088328 WIP: nitro auth 2024-03-19 16:49:51 -05:00
telackey 523b5779be Auto-detect which certificate to use (including wildcards). (#779)
Rather than always requesting a certificate, attempt to re-use an existing certificate if it already exists in the k8s cluster.  This includes matching to a wildcard certificate.

Reviewed-on: cerc-io/stack-orchestrator#779
Co-authored-by: Thomas E Lackey <telackey@bozemanpass.com>
Co-committed-by: Thomas E Lackey <telackey@bozemanpass.com>
2024-03-07 17:38:36 +00:00
telackey 62f7ce649d Exit non-0 if docker build fails. (#778)
Make sure to check the exit code of the docker build and bubble it back up to laconic-so.

Reviewed-on: cerc-io/stack-orchestrator#778
Co-authored-by: Thomas E Lackey <telackey@bozemanpass.com>
Co-committed-by: Thomas E Lackey <telackey@bozemanpass.com>
2024-03-06 18:38:30 +00:00
18 changed files with 637 additions and 126 deletions
@@ -0,0 +1,113 @@
version: '3.7'
services:
go-nitro-alice:
image: cerc/go-nitro:local
hostname: go-nitro-alice
restart: on-failure
depends_on:
- go-nitro-bootnode
environment:
CERC_NITRO_CHAIN_PK: ${CERC_NITRO_CHAIN_PK_ALICE:-888814df89c4358d7ddb3fa4b0213e7331239a80e1f013eaa7b2deca2a41a218}
CERC_NITRO_CHAIN_URL: ${CERC_NITRO_CHAIN_URL:-ws://fixturenet-eth-geth-1:8546}
CERC_NA_ADDRESS: ${CERC_NA_ADDRESS}
CERC_VPA_ADDRESS: ${CERC_VPA_ADDRESS}
CERC_CA_ADDRESS: ${CERC_CA_ADDRESS}
env_file:
- ../config/go-nitro-auth/alice.env
entrypoint: ["bash", "-c", "/app/run-nitro-node.sh"]
volumes:
- nitro_deployment:/app/deployment
- go_nitro_data_alice:/app/data
- ../config/go-nitro/run-nitro-node.sh:/app/run-nitro-node.sh
healthcheck:
test: ["CMD", "nc", "-vz", "localhost", "4006"]
interval: 5s
timeout: 5s
retries: 100
start_period: 100s
ports:
- "3006"
- "4006"
- "5006"
- "4106"
- "4206"
- "4216"
go-nitro-bob:
image: cerc/go-nitro:local
hostname: go-nitro-bob
restart: on-failure
depends_on:
- go-nitro-bootnode
environment:
CERC_NITRO_CHAIN_PK: ${CERC_NITRO_CHAIN_PK_BOB:-570b909da9669b2f35a0b1ac70b8358516d55ae1b5b3710e95e9a94395090597}
CERC_NITRO_CHAIN_URL: ${CERC_NITRO_CHAIN_URL:-ws://fixturenet-eth-geth-1:8546}
CERC_NA_ADDRESS: ${CERC_NA_ADDRESS}
CERC_VPA_ADDRESS: ${CERC_VPA_ADDRESS}
CERC_CA_ADDRESS: ${CERC_CA_ADDRESS}
env_file:
- ../config/go-nitro-auth/bob.env
entrypoint: ["bash", "-c", "/app/run-nitro-node.sh"]
volumes:
- nitro_deployment:/app/deployment
- go_nitro_data_bob:/app/data
- ../config/go-nitro/run-nitro-node.sh:/app/run-nitro-node.sh
healthcheck:
test: ["CMD", "nc", "-vz", "localhost", "4007"]
interval: 5s
timeout: 5s
retries: 100
start_period: 100s
ports:
- "3007"
- "4007"
- "5007"
- "4107"
- "4207"
- "4217"
go-nitro-bootnode:
image: cerc/go-nitro:local
hostname: go-nitro-bootnode
restart: on-failure
environment:
CERC_NITRO_CHAIN_URL: ${CERC_NITRO_CHAIN_URL:-ws://fixturenet-eth-geth-1:8546}
CERC_NA_ADDRESS: ${CERC_NA_ADDRESS}
CERC_VPA_ADDRESS: ${CERC_VPA_ADDRESS}
CERC_CA_ADDRESS: ${CERC_CA_ADDRESS}
CERC_NITRO_RELAY_ON: true
env_file:
- ../config/go-nitro-auth/bootnode.env
entrypoint: [ "bash", "-c", "/app/run-nitro-node.sh" ]
volumes:
- nitro_deployment:/app/deployment
- go_nitro_data_bootnode:/app/data
- ../config/go-nitro/run-nitro-node.sh:/app/run-nitro-node.sh
healthcheck:
test: [ "CMD", "nc", "-vz", "localhost", "4008" ]
interval: 5s
timeout: 5s
retries: 100
start_period: 100s
ports:
- "9090"
nitro-caddy:
image: caddy:2-alpine
restart: always
environment:
CERC_ETH_RPC_ENDPOINT: ${CERC_ETH_RPC_ENDPOINT:-http://fixturenet-eth-geth-1:8545}
volumes:
- ../config/go-nitro-auth/caddy/Caddyfile:/etc/caddy/Caddyfile
ports:
- "5678"
depends_on:
- go-nitro-alice
- go-nitro-bob
volumes:
go_nitro_data_alice:
go_nitro_data_bob:
go_nitro_data_bootnode:
nitro_deployment:
@@ -0,0 +1,19 @@
# SET ME! Your on-chain private key.
#CERC_NITRO_CHAIN_PK=<MY_PK_GOES_HERE>
# Default is CERC_NITRO_CHAIN_PK.
#CERC_NITRO_PK=<MY_PK_GOES_HERE>
# SET ME! The WebSocket to connect to.
#CERC_NITRO_CHAIN_URL=wss://linea-sepolia.infura.io/ws/v3/<MY_API_KEY_GOES_HERE>
CERC_NITRO_AUTH_ON=false
CERC_NITRO_AUTH_RPC_PORT=4006
CERC_NITRO_AUTH_UI_PORT=4206
CERC_NITRO_BOOT_PEERS="/dns4/go-nitro-bootnode/tcp/3008/p2p/16Uiu2HAmFYz5YPf3GtkfQsAwYJfDsKSm34U9qAiTwxW7RJUVGMRK"
CERC_NITRO_DURABLE_STORE_FOLDER=/app/data/nitro-store
CERC_NITRO_MSG_PORT=3006
CERC_NITRO_RPC_PORT=4006
CERC_NITRO_UI_PORT=4106
CERC_NITRO_USE_DURABLE_STORE=true
CERC_NITRO_WS_MSG_PORT=5006
@@ -0,0 +1,19 @@
# SET ME! Your on-chain private key.
#CERC_NITRO_CHAIN_PK=<MY_PK_GOES_HERE>
# Default is CERC_NITRO_CHAIN_PK.
#CERC_NITRO_PK=<MY_PK_GOES_HERE>
# SET ME! The WebSocket to connect to.
#CERC_NITRO_CHAIN_URL=wss://linea-sepolia.infura.io/ws/v3/<MY_API_KEY_GOES_HERE>
CERC_NITRO_AUTH_ON=true
CERC_NITRO_AUTH_RPC_PORT=4007
CERC_NITRO_AUTH_UI_PORT=4207
CERC_NITRO_BOOT_PEERS="/dns4/go-nitro-bootnode/tcp/3008/p2p/16Uiu2HAmFYz5YPf3GtkfQsAwYJfDsKSm34U9qAiTwxW7RJUVGMRK"
CERC_NITRO_DURABLE_STORE_FOLDER=/app/data/nitro-store
CERC_NITRO_MSG_PORT=3007
CERC_NITRO_RPC_PORT=4007
CERC_NITRO_UI_PORT=4107
CERC_NITRO_USE_DURABLE_STORE=true
CERC_NITRO_WS_MSG_PORT=5007
@@ -0,0 +1,17 @@
CERC_NITRO_CHAIN_PK=596db2ac27479cfdf60f708bf64ae44a1c5090e9446cca011d72bc9a59b47d3d
CERC_NITRO_PK=f41086394674cf00a66448c6688295d7330af39f6f38fed89ea023e39382d6a0
# The WebSocket to connect to.
#CERC_NITRO_CHAIN_URL=wss://linea-sepolia.infura.io/ws/v3/<MY_API_KEY_GOES_HERE>
CERC_NITRO_AUTH_ON=false
CERC_NITRO_AUTH_RPC_PORT=4008
CERC_NITRO_AUTH_UI_PORT=4208
CERC_NITRO_DURABLE_STORE_FOLDER=/app/data/nitro-store
CERC_NITRO_MSG_PORT=3008
CERC_NITRO_RPC_PORT=4008
CERC_NITRO_UI_PORT=4108
CERC_NITRO_USE_DURABLE_STORE=true
CERC_NITRO_WS_MSG_PORT=5008
CERC_SCRIPT_DEBUG=true
@@ -0,0 +1,42 @@
:5678 {
handle /pay* {
reverse_proxy http://go-nitro-bob:8547
}
@eth-key-in-header {
path_regexp /eth/?$
header X-Api-Key *
}
handle @eth-key-in-header {
forward_auth go-nitro-bob:8547 {
uri /auth/{header.x-api-key}
}
rewrite * /
reverse_proxy {$CERC_ETH_RPC_ENDPOINT} {
header_up Host {upstream_hostport}
}
}
@eth-key-in-path {
path_regexp apikey eth/(.+)$
}
handle @eth-key-in-path {
forward_auth go-nitro-bob:8547 {
uri /auth/{re.apikey.1}
}
rewrite * /
reverse_proxy {$CERC_ETH_RPC_ENDPOINT} {
header_up Host {upstream_hostport}
}
}
handle /eth* {
respond "401 Unauthorized" 401
}
handle {
reverse_proxy http://go-nitro-alice:4206
}
}
@@ -4,6 +4,11 @@ if [ -n "$CERC_SCRIPT_DEBUG" ]; then
set -x
fi
if [ -z "$CERC_NITRO_CHAIN_PK" ] || [ -z "$CERC_NITRO_CHAIN_URL" ]; then
echo "You most set both CERC_NITRO_CHAIN_PK and CERC_NITRO_CHAIN_URL." 1>&2
exit 1
fi
nitro_addresses_file="/app/deployment/nitro-addresses.json"
# Check if CERC_NA_ADDRESS environment variable is set
@@ -29,22 +34,86 @@ fi
echo "Running Nitro node"
# Assuming CERC_NITRO_CHAIN_URL is of format <ws|http>://host:port
ws_host=$(echo "$CERC_NITRO_CHAIN_URL" | awk -F '://' '{print $2}' | cut -d ':' -f 1)
ws_port=$(echo "$CERC_NITRO_CHAIN_URL" | awk -F '://' '{print $2}' | cut -d ':' -f 2)
# Wait till chain endpoint is available
retry_interval=5
while true; do
nc -z -w 1 "$ws_host" "$ws_port"
if [ $? -eq 0 ]; then
echo "Chain endpoint is available"
break
if [[ "${CERC_GO_NITRO_WAIT_FOR_CHAIN:-true}" == "true" ]]; then
# Assuming CERC_NITRO_CHAIN_URL is of format <ws|http>://host[:port][/foo]
ws_host=$(echo "$CERC_NITRO_CHAIN_URL" | awk -F '://' '{print $2}' | cut -d'/' -f 1 | cut -d ':' -f 1)
ws_port=$(echo "$CERC_NITRO_CHAIN_URL" | awk -F '://' '{print $2}' | cut -d'/' -f 1 | cut -d ':' -f 2)
if [[ "$ws_port" == "$ws_host" ]]; then
ws_port=""
fi
echo "Chain endpoint not yet available, retrying in $retry_interval seconds..."
sleep $retry_interval
done
# Wait till chain endpoint is available
retry_interval=5
while true; do
nc -z -w 1 "$ws_host" "${ws_port:-443}"
./nitro -chainurl ${CERC_NITRO_CHAIN_URL} -msgport ${CERC_NITRO_MSG_PORT} -rpcport ${CERC_NITRO_RPC_PORT} -wsmsgport ${CERC_NITRO_WS_MSG_PORT} -publicip "0.0.0.0" -pk ${CERC_NITRO_PK} -chainpk ${CERC_NITRO_CHAIN_PK} -naaddress ${NA_ADDRESS} -vpaaddress ${VPA_ADDRESS} -caaddress ${CA_ADDRESS} -usedurablestore ${CERC_NITRO_USE_DURABLE_STORE} -durablestorefolder ${CERC_NITRO_DURABLE_STORE_FOLDER} -tlscertfilepath "" -tlsKeyFilepath ""
if [ $? -eq 0 ]; then
echo "Chain endpoint is available"
break
fi
echo "Chain endpoint not yet available, retrying in $retry_interval seconds..."
sleep $retry_interval
done
fi
if [[ -n "$CERC_NITRO_UI_PORT" ]] && [[ -d "/app-node/packages/nitro-gui/dist" ]]; then
for f in `ls /app-node/packages/nitro-gui/dist/assets/*.js`; do
sed -i "s#\"CERC_RUNTIME_ENV_RPC_HOST\"#\"localhost:${CERC_NITRO_RPC_PORT}\"#g" "$f"
sed -i "s#\"CERC_RUNTIME_ENV_TARGET_URL\"#\"http://localhost:5678\"#g" "$f"
done
http-server -p $CERC_NITRO_UI_PORT /app-node/packages/nitro-gui/dist &
fi
if [[ -n "$CERC_NITRO_AUTH_UI_PORT" ]] && [[ -d "/app-node/packages/nitro-auth-gui/dist" ]]; then
for f in `ls /app-node/packages/nitro-auth-gui/dist/assets/*.js`; do
sed -i "s#\"CERC_RUNTIME_ENV_RPC_URL\"#\"http://localhost:${CERC_NITRO_RPC_PORT}\"#g" "$f"
sed -i "s#\"CERC_RUNTIME_ENV_TARGET_URL\"#\"http://localhost:5678\"#g" "$f"
done
http-server -p $CERC_NITRO_AUTH_UI_PORT /app-node/packages/nitro-auth-gui/dist &
fi
if [[ "$CERC_NITRO_AUTH_ON" == "true" ]] && [[ -d "/app-node/packages/nitro-auth/dist" ]]; then
bash -c "sleep 6 && cd /app-node/packages/nitro-auth && yarn start" &
fi
if [[ "$CERC_NITRO_RELAY_ON" == "true" ]]; then
if [[ ! -f "/app/deployment/relay-node.json" ]]; then
node /usr/local/lib/node_modules/@cerc-io/peer/dist/cli/create-peer.js \
-f /app/deployment/relay-node.json
fi
DEBUG='laconic:*' node /usr/local/lib/node_modules/@cerc-io/peer/dist/cli/relay.js \
--host 0.0.0.0 \
-f /app/deployment/relay-node.json &
fi
if [[ -z "$CERC_CHAIN_START_BLOCK" ]]; then
if [[ ! -f "/app/deployment/chainstartblock.json" ]]; then
curl --location "$(echo $CERC_NITRO_CHAIN_URL | sed 's/^ws/http/' | sed 's#/ws/#/#')" \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 124,
"method": "eth_blockNumber",
"params": []
}' > /app/deployment/chainstartblock.json
fi
CERC_CHAIN_START_BLOCK=$(printf "%d" `cat /app/deployment/chainstartblock.json | jq -r '.result'`)
fi
cd /app
./nitro \
-chainurl ${CERC_NITRO_CHAIN_URL} \
-msgport ${CERC_NITRO_MSG_PORT} \
-rpcport ${CERC_NITRO_RPC_PORT} \
-wsmsgport ${CERC_NITRO_WS_MSG_PORT} \
-publicip "0.0.0.0" \
-pk ${CERC_NITRO_PK:-$CERC_NITRO_CHAIN_PK} \
-chainpk ${CERC_NITRO_CHAIN_PK} \
-naaddress ${NA_ADDRESS} \
-vpaaddress ${VPA_ADDRESS} \
-caaddress ${CA_ADDRESS} \
-usedurablestore=${CERC_NITRO_USE_DURABLE_STORE} \
-durablestorefolder ${CERC_NITRO_DURABLE_STORE_FOLDER} \
-bootpeers "${CERC_NITRO_BOOT_PEERS}" \
-chainstartblock $CERC_CHAIN_START_BLOCK
+99
View File
@@ -0,0 +1,99 @@
#!/bin/bash
CERC_NITRO_RPC_FUND_AMOUNT=${CERC_NITRO_RPC_FUND_AMOUNT:-0}
CERC_NITRO_RPC_HOST_ALICE=${CERC_NITRO_RPC_HOST_ALICE:-go-nitro-alice}
CERC_NITRO_RPC_PORT_ALICE=${CERC_NITRO_RPC_PORT_ALICE:-4006}
CERC_NITRO_USE_TLS=${CERC_NITRO_USE_TLS:-false}
CERC_NITRO_ADDRESS_BOB=${CERC_NITRO_ADDRESS_BOB:-0xe07e314501cc73b24cf45a6577486017300e153c}
# Wait till chain endpoint is available
retry_interval=5
while true; do
nc -z -w 1 "$CERC_NITRO_RPC_HOST_ALICE" "$CERC_NITRO_RPC_PORT_ALICE"
if [ $? -eq 0 ]; then
echo "Nitro endpoint is available"
break
fi
echo "Nitro endpoint not yet available, retrying in $retry_interval seconds..."
sleep $retry_interval
done
if [[ "$CERC_NITRO_RPC_FUND_AMOUNT" -gt 0 ]]; then
nitro-rpc-client -h $CERC_NITRO_RPC_HOST_ALICE \
-p $CERC_NITRO_RPC_PORT_ALICE \
-s=$CERC_NITRO_USE_TLS \
get-all-ledger-channels | \
jq "[.[] | select(.Status == \"Open\") | select(.Balance.Them == \"$CERC_NITRO_ADDRESS_BOB\")] | first" > \
/app/deployment/nitro-ledger-channel-alice-to-bob.json
ledger_channel=$(jq -r '.ID' /app/deployment/nitro-ledger-channel-alice-to-bob.json 2>/dev/null | sed 's/^null$//')
if [[ -z "${ledger_channel}" ]]; then
echo "Creating new ledger channel ..."
nitro-rpc-client -h $CERC_NITRO_RPC_HOST_ALICE \
-p $CERC_NITRO_RPC_PORT_ALICE \
-s=$CERC_NITRO_USE_TLS \
-n \
direct-fund --amount $CERC_NITRO_RPC_FUND_AMOUNT $CERC_NITRO_ADDRESS_BOB
nitro-rpc-client -h $CERC_NITRO_RPC_HOST_ALICE \
-p $CERC_NITRO_RPC_PORT_ALICE \
-s=$CERC_NITRO_USE_TLS \
get-all-ledger-channels | \
jq "[.[] | select(.Status == \"Open\") | select(.Balance.Them == \"$CERC_NITRO_ADDRESS_BOB\")] | first" > \
/app/deployment/nitro-ledger-channel-alice-to-bob.json
ledger_channel=$(jq -r '.ID' /app/deployment/nitro-ledger-channel-alice-to-bob.json)
fi
nitro-rpc-client -h $CERC_NITRO_RPC_HOST_ALICE \
-p $CERC_NITRO_RPC_PORT_ALICE \
-s=$CERC_NITRO_USE_TLS \
get-payment-channels-by-ledger $ledger_channel > \
/app/deployment/nitro-payment-channels-alice-to-bob.json
first_open_channel=$(jq '[.[] | select(.Status == "Open")] | first' /app/deployment/nitro-payment-channels-alice-to-bob.json | sed 's/^null$//')
if [[ -z "$first_open_channel" ]]; then
echo "Creating new payment channel ..."
nitro-rpc-client -h $CERC_NITRO_RPC_HOST_ALICE \
-p $CERC_NITRO_RPC_PORT_ALICE \
-s=$CERC_NITRO_USE_TLS \
-n \
virtual-fund --amount $((CERC_NITRO_RPC_FUND_AMOUNT/2)) $CERC_NITRO_ADDRESS_BOB
nitro-rpc-client -h $CERC_NITRO_RPC_HOST_ALICE \
-p $CERC_NITRO_RPC_PORT_ALICE \
-s=$CERC_NITRO_USE_TLS \
get-payment-channels-by-ledger $ledger_channel > \
/app/deployment/nitro-payment-channels-alice-to-bob.json
first_open_channel=$(jq '[.[] | select(.Status == "Open")] | first' /app/deployment/nitro-payment-channels-alice-to-bob.json | sed 's/^null$//')
fi
echo ""
echo "################################################################"
echo ""
echo "LEDGER:"
cat /app/deployment/nitro-ledger-channel-alice-to-bob.json | jq
echo ""
echo ""
echo "PAYMENT:"
cat /app/deployment/nitro-payment-channels-alice-to-bob.json | jq
echo ""
echo ""
fi
if [ -n "$1" ]; then
exec "$@"
exit $?
fi
while [ 1 -eq 1 ]; do
sleep 100
done
@@ -8,9 +8,30 @@ COPY . .
RUN go build -v -o nitro .
# Reduce image size
FROM debian:bullseye-slim
FROM node:18-bullseye-slim as builder-node
RUN apt-get update
RUN apt-get install -y ca-certificates jq netcat
RUN apt-get install -y make
WORKDIR /app
COPY . .
RUN find . -name 'node_modules' | xargs -n1 rm -rf
RUN find . -name 'dist' | xargs -n1 rm -rf
RUN yarn
WORKDIR /app/packages/nitro-gui
RUN VITE_RPC_HOST=CERC_RUNTIME_ENV_RPC_HOST yarn build
RUN if [ -d /app/packages/nitro-auth-gui ]; then \
cd /app/packages/nitro-auth-gui && \
VITE_RPC_URL=CERC_RUNTIME_ENV_RPC_URL VITE_TARGET_URL=CERC_RUNTIME_ENV_TARGET_URL yarn build; \
fi
WORKDIR /app/packages/nitro-auth
RUN if [ -d /app/packages/nitro-auth ]; then cd /app/packages/nitro-auth && yarn build; fi
FROM node:18-bullseye-slim
RUN apt-get update
RUN apt-get install -y ca-certificates jq netcat make curl wget
RUN rm -rf /var/lib/apt/lists/*
RUN npm install -g http-server
RUN npm config set @cerc-io:registry https://git.vdb.to/api/packages/cerc-io/npm/
RUN npm install -g @cerc-io/peer
WORKDIR /app
COPY --from=builder /app/nitro .
COPY --from=builder-node /app /app-node
@@ -11,8 +11,14 @@ CERC_CONTAINER_BUILD_DOCKERFILE=${CERC_CONTAINER_BUILD_DOCKERFILE:-$SCRIPT_DIR/D
CERC_CONTAINER_BUILD_TAG=${CERC_CONTAINER_BUILD_TAG:-cerc/nextjs-base:local}
docker build -t $CERC_CONTAINER_BUILD_TAG ${build_command_args} -f $CERC_CONTAINER_BUILD_DOCKERFILE $CERC_CONTAINER_BUILD_WORK_DIR
rc=$?
if [ $? -eq 0 ] && [ "$CERC_CONTAINER_BUILD_TAG" != "cerc/nextjs-base:local" ]; then
if [ $rc -ne 0 ]; then
echo "BUILD FAILED" 1>&2
exit $rc
fi
if [ "$CERC_CONTAINER_BUILD_TAG" != "cerc/nextjs-base:local" ]; then
cat <<EOF
#################################################################
@@ -4,9 +4,10 @@ RUN apk --update --no-cache add python3 alpine-sdk bash curl jq
WORKDIR /app
COPY . .
COPY packages/nitro-rpc-client .
RUN echo "Installing dependencies" && \
yarn
RUN cd packages/nitro-rpc-client
RUN yarn build
RUN ln -s /app/bin/nitro-rpc-client.js /bin/nitro-rpc-client
@@ -11,8 +11,14 @@ CERC_CONTAINER_BUILD_DOCKERFILE=${CERC_CONTAINER_BUILD_DOCKERFILE:-$SCRIPT_DIR/D
CERC_CONTAINER_BUILD_TAG=${CERC_CONTAINER_BUILD_TAG:-cerc/webapp-base:local}
docker build -t $CERC_CONTAINER_BUILD_TAG ${build_command_args} -f $CERC_CONTAINER_BUILD_DOCKERFILE $CERC_CONTAINER_BUILD_WORK_DIR
rc=$?
if [ $? -eq 0 ] && [ "$CERC_CONTAINER_BUILD_TAG" != "cerc/webapp-base:local" ]; then
if [ $rc -ne 0 ]; then
echo "BUILD FAILED" 1>&2
exit $rc
fi
if [ "$CERC_CONTAINER_BUILD_TAG" != "cerc/webapp-base:local" ]; then
cat <<EOF
#################################################################
@@ -0,0 +1,104 @@
# go-nitro-auth
Deploy a stack for demoing Nitro-based auth, using either a local fixturenet (fully self-contained) or remote testnet.
## Local Fixturenet (Self-Contained)
### Clone required repositories
```
$ laconic-so --stack fixturenet-eth setup-repositories
$ laconic-so --stack go-nitro-auth setup-repositories
```
### Build containers
```
$ laconic-so --stack fixturenet-eth build-containers
$ laconic-so --stack go-nitro-auth build-containers
```
### Create a deployment
```
$ laconic-so --stack fixturenet-eth deploy init --output nitro-net.yml
$ laconic-so --stack fixturenet-eth deploy create --spec-file nitro-net.yml --deployment-dir /srv/nitro-net
$ laconic-so --stack go-nitro-auth deploy init --map-ports-to-host any-same --output nitro-auth.yml
$ laconic-so --stack go-nitro-auth deploy create --spec-file nitro-auth.yml --deployment-dir /srv/nitro-auth
# Place them both in the same namespace (TODO: support setting the deployment name via --cluster).
$ cp /srv/nitro-net/deployment.yml /srv/nitro-auth/deployment.yml
```
### Start the containers
```
$ laconic-so deployment --dir /srv/nitro-net up
$ laconic-so deployment --dir /srv/nitro-auth up
```
### Open the webapp
Visit http://localhost:5678
## Remote Testnet
This example will use the Linea Sepolia testnet.
### Clone required repositories
```
$ laconic-so --stack go-nitro-auth setup-repositories
```
### Build containers
```
$ laconic-so --stack go-nitro-auth build-containers
```
### Create a deployment
```
$ laconic-so --stack go-nitro-auth deploy init --map-ports-to-host any-same --output nitro-auth.yml
$ laconic-so --stack go-nitro-auth deploy create --spec-file nitro-auth.yml --deployment-dir /srv/nitro-auth
```
### Set your keys, contract addresses, etc.
You must set the private keys for two accounts with funds on the target network, as well as the contract addresses
(if they already exist) or else an account to create them. You must also set the URL to use for WebSocket connections,
eg, `wss://linea-sepolia.infura.io/ws/v3/<MY_API_KEY>`
#### Config
```
$ vim /srv/nitro-auth/config.env
# Addresses of existing contracts.
CERC_CA_ADDRESS="0x1Ae815c3e7556e16ceaB6B6d46306C1870EB6d24"
CERC_NA_ADDRESS="0xc453C5E3f304bb545A3Df7bBa02fe6274A056636"
CERC_VPA_ADDRESS="0xA11af80D75b1150631FA78178c94fa451c7172a8"
# Else the private key of an account and RPC URL to use create them.
CERC_PRIVATE_KEY_DEPLOYER=<PRIV_KEY_HERE>
CERC_ETH_RPC_ENDPOINT=https://rpc.sepolia.linea.build
# The WebSocket chain URL.
CERC_NITRO_CHAIN_URL=wss://linea-sepolia.infura.io/ws/v3/<MY_API_KEY_HERE>
# Private key for "Alice" account (payer)
CERC_NITRO_CHAIN_PK_ALICE=<ALICE_PRIVATE_KEY_HERE>
# Private key for "Bob" account (payee)
CERC_NITRO_CHAIN_PK_BOB=<BOB_PRIVATE_KEY_HERE>
```
### Start the stack
```
$ laconic-so deployment --dir /srv/nitro-auth up
```
### Open the webapp
Visit http://localhost:5678
@@ -0,0 +1,16 @@
version: "1.0"
name: go-nitro-auth
description: "Stack to demonstrate payments between various services"
repos:
# for nitro-contracts
- github.com/cerc-io/ts-nitro@v0.1.15
# for go-nitro and nitro-rpc-client
- github.com/cerc-io/go-nitro@telackey/update
containers:
# nitro
- cerc/nitro-contracts
- cerc/go-nitro
- cerc/nitro-rpc-client
pods:
- nitro-contracts
- go-nitro-auth
+12 -29
View File
@@ -101,7 +101,7 @@ class ClusterInfo:
)
return service
def get_ingress(self, use_tls=False):
def get_ingress(self, use_tls=False, certificate=None, cluster_issuer="letsencrypt-prod"):
# No ingress for a deployment that has no http-proxy defined, for now
http_proxy_info_list = self.spec.get_http_proxy()
ingress = None
@@ -110,32 +110,13 @@ class ClusterInfo:
http_proxy_info = http_proxy_info_list[0]
if opts.o.debug:
print(f"http-proxy: {http_proxy_info}")
host_name = http_proxy_info["host-name"]
tls = None
tls_issuer = None
if use_tls:
tls_info = http_proxy_info.get("tls", {})
tls_hosts = tls_info.get("hosts", [host_name])
tls_issuer = tls_info.get("issuer", "letsencrypt-prod")
tls_secret_name = f"{self.app_name}-tls"
if "secret" in tls_info:
# If an existing secret is specified, unset the issuer so that we don't try to re-request it.
tls_secret_name = tls_info["secret"]
tls_issuer = None
if opts.o.debug:
print(f"TLS hosts/secret: {tls_hosts}/{tls_secret_name}")
tls = [client.V1IngressTLS(
hosts=tls_hosts,
secret_name=tls_secret_name
)]
# TODO: good enough parsing for webapp deployment for now
host_name = http_proxy_info["host-name"]
rules = []
tls = [client.V1IngressTLS(
hosts=certificate["spec"]["dnsNames"] if certificate else [host_name],
secret_name=certificate["spec"]["secretName"] if certificate else f"{self.app_name}-tls"
)] if use_tls else None
paths = []
for route in http_proxy_info["routes"]:
path = route["path"]
@@ -166,15 +147,17 @@ class ClusterInfo:
tls=tls,
rules=rules
)
annotations = {
ingress_annotations = {
"kubernetes.io/ingress.class": "nginx",
}
if tls_issuer:
annotations["cert-manager.io/cluster-issuer"] = tls_issuer
if not certificate:
ingress_annotations["cert-manager.io/cluster-issuer"] = cluster_issuer
ingress = client.V1Ingress(
metadata=client.V1ObjectMeta(
name=f"{self.app_name}-ingress",
annotations=annotations
annotations=ingress_annotations
),
spec=spec
)
+43 -2
View File
@@ -169,6 +169,39 @@ class K8sDeployer(Deployer):
print("Service created:")
print(f"{service_resp}")
def _find_certificate_for_host_name(self, host_name):
all_certificates = self.custom_obj_api.list_namespaced_custom_object(
group="cert-manager.io",
version="v1",
namespace=self.k8s_namespace,
plural="certificates"
)
host_parts = host_name.split(".", 1)
host_as_wild = None
if len(host_parts) == 2:
host_as_wild = f"*.{host_parts[1]}"
now = datetime.utcnow().replace(tzinfo=timezone.utc)
fmt = "%Y-%m-%dT%H:%M:%S%z"
# Walk over all the configured certificates.
for cert in all_certificates["items"]:
dns = cert["spec"]["dnsNames"]
# Check for an exact hostname match or a wildcard match.
if host_name in dns or host_as_wild in dns:
status = cert.get("status", {})
# Check the certificate date.
if "notAfter" in status and "notBefore" in status:
before = datetime.strptime(status["notBefore"], fmt)
after = datetime.strptime(status["notAfter"], fmt)
if before < now < after:
# Check the status is Ready
for condition in status.get("conditions", []):
if "True" == condition.get("status") and "Ready" == condition.get("type"):
return cert
return None
def up(self, detach, services):
if not opts.o.dry_run:
if self.is_kind():
@@ -189,8 +222,15 @@ class K8sDeployer(Deployer):
self._create_volume_data()
self._create_deployment()
http_proxy_info = self.cluster_info.spec.get_http_proxy()
# Note: at present we don't support tls for kind (and enabling tls causes errors)
ingress: client.V1Ingress = self.cluster_info.get_ingress(use_tls=not self.is_kind())
use_tls = http_proxy_info and not self.is_kind()
certificate = self._find_certificate_for_host_name(http_proxy_info[0]["host-name"]) if use_tls else None
if opts.o.debug:
if certificate:
print(f"Using existing certificate: {certificate}")
ingress: client.V1Ingress = self.cluster_info.get_ingress(use_tls=use_tls, certificate=certificate)
if ingress:
if opts.o.debug:
print(f"Sending this ingress: {ingress}")
@@ -353,7 +393,8 @@ class K8sDeployer(Deployer):
hostname = ingress.spec.rules[0].host
ip = ingress.status.load_balancer.ingress[0].ip
tls = "notBefore: %s; notAfter: %s; names: %s" % (
cert["status"]["notBefore"], cert["status"]["notAfter"], ", ".join(ingress.spec.tls[0].hosts))
cert["status"]["notBefore"], cert["status"]["notAfter"], ingress.spec.tls[0].hosts
)
except: # noqa: E722
pass
@@ -15,7 +15,6 @@
import click
import os
import sys
from pathlib import Path
from urllib.parse import urlparse
from tempfile import NamedTemporaryFile
@@ -24,7 +23,6 @@ from stack_orchestrator.util import error_exit, global_options2
from stack_orchestrator.deploy.deployment_create import init_operation, create_operation
from stack_orchestrator.deploy.deploy import create_deploy_context
from stack_orchestrator.deploy.deploy_types import DeployCommandContext
from stack_orchestrator.deploy.webapp.util import TlsDetails
def _fixup_container_tag(deployment_dir: str, image: str):
@@ -38,16 +36,16 @@ def _fixup_container_tag(deployment_dir: str, image: str):
wfile.write(contents)
def _fixup_url_spec(spec_file_name: str, url: str, tls_details: TlsDetails = TlsDetails()):
def _fixup_url_spec(spec_file_name: str, url: str):
# url is like: https://example.com/path
parsed_url = urlparse(url)
http_proxy_spec = f''' http-proxy:
http_proxy_spec = f'''
http-proxy:
- host-name: {parsed_url.hostname}
routes:
- path: '{parsed_url.path if parsed_url.path else "/"}'
proxy-to: webapp:80
{tls_details.to_yaml(indent=6)}
'''
'''
spec_file_path = Path(spec_file_name)
with open(spec_file_path) as rfile:
contents = rfile.read()
@@ -56,8 +54,7 @@ def _fixup_url_spec(spec_file_name: str, url: str, tls_details: TlsDetails = Tls
wfile.write(contents)
def create_deployment(ctx, deployment_dir, image, url, kube_config, image_registry, env_file,
tls_details: TlsDetails = None):
def create_deployment(ctx, deployment_dir, image, url, kube_config, image_registry, env_file):
# Do the equivalent of:
# 1. laconic-so --stack webapp-template deploy --deploy-to k8s init --output webapp-spec.yml
# --config (eqivalent of the contents of my-config.env)
@@ -89,7 +86,7 @@ def create_deployment(ctx, deployment_dir, image, url, kube_config, image_regist
None
)
# Add the TLS and DNS spec
_fixup_url_spec(spec_file_name, url, tls_details)
_fixup_url_spec(spec_file_name, url)
create_operation(
deploy_command_context,
spec_file_name,
@@ -119,16 +116,8 @@ def command(ctx):
@click.option("--image", help="image to deploy", required=True)
@click.option("--url", help="url to serve", required=True)
@click.option("--env-file", help="environment file for webapp")
@click.option("--tls-host", help="Override TLS hostname (eg, '*.mydomain.com')")
@click.option("--tls-secret", help="Override TLS secret name")
@click.option("--tls-issuer", help="TLS issuer to use (default: letsencrypt-prod)")
@click.pass_context
def create(ctx, deployment_dir, image, url, kube_config, image_registry, env_file, tls_host, tls_secret, tls_issuer):
def create(ctx, deployment_dir, image, url, kube_config, image_registry, env_file):
'''create a deployment for the specified webapp container'''
if (tls_secret and not tls_host) or (tls_host and not tls_secret):
print("Cannot specify --tls-host without --tls-secret", file=sys.stderr)
sys.exit(2)
return create_deployment(ctx, deployment_dir, image, url, kube_config, image_registry, env_file,
TlsDetails(tls_host, tls_secret, tls_issuer))
return create_deployment(ctx, deployment_dir, image, url, kube_config, image_registry, env_file)
@@ -26,7 +26,7 @@ import click
from stack_orchestrator.deploy.images import remote_image_exists, add_tags_to_image
from stack_orchestrator.deploy.webapp import deploy_webapp
from stack_orchestrator.deploy.webapp.util import (LaconicRegistryClient, TimedLogger, TlsDetails,
from stack_orchestrator.deploy.webapp.util import (LaconicRegistryClient, TimedLogger,
build_container_image, push_container_image,
file_hash, deploy_to_k8s, publish_deployment,
hostname_for_deployment_request, generate_hostname_for_app,
@@ -39,13 +39,13 @@ def process_app_deployment_request(
app_deployment_request,
deployment_record_namespace,
dns_record_namespace,
dns_suffix,
default_dns_suffix,
deployment_parent_dir,
kube_config,
image_registry,
force_rebuild,
tls_details,
logger,
fqdn_policy,
logger
):
logger.log("BEGIN - process_app_deployment_request")
@@ -57,14 +57,15 @@ def process_app_deployment_request(
requested_name = hostname_for_deployment_request(app_deployment_request, laconic)
logger.log(f"Determined requested name: {requested_name}")
# HACK
if "." in requested_name:
raise Exception("Only unqualified hostnames allowed at this time.")
fqdn = f"{requested_name}.{dns_suffix}"
if "allow" == fqdn_policy or "preexisting" == fqdn_policy:
fqdn = requested_name
else:
raise Exception(f"{requested_name} is invalid: only unqualified hostnames are allowed.")
else:
fqdn = f"{requested_name}.{default_dns_suffix}"
# 3. check ownership of existing dnsrecord vs this request
# TODO: Support foreign DNS
dns_crn = f"{dns_record_namespace}/{fqdn}"
dns_record = laconic.get_record(dns_crn)
if dns_record:
@@ -76,7 +77,9 @@ def process_app_deployment_request(
logger.log(f"Matched DnsRecord ownership: {matched_owner}")
else:
raise Exception("Unable to confirm ownership of DnsRecord %s for request %s" %
(dns_record.id, app_deployment_request.id))
(dns_crn, app_deployment_request.id))
elif "preexisting" == fqdn_policy:
raise Exception(f"No pre-existing DnsRecord {dns_crn} could be found for request {app_deployment_request.id}.")
# 4. get build and runtime config from request
env_filename = None
@@ -107,7 +110,7 @@ def process_app_deployment_request(
(app_deployment_crn, deployment_dir))
print("deploy_webapp", deployment_dir)
deploy_webapp.create_deployment(ctx, deployment_dir, deployment_container_tag,
f"https://{fqdn}", kube_config, image_registry, env_filename, tls_details)
f"https://{fqdn}", kube_config, image_registry, env_filename)
elif env_filename:
shutil.copyfile(env_filename, deployment_config_file)
@@ -192,6 +195,7 @@ def dump_known_requests(filename, requests, status="SEEN"):
@click.option("--state-file", help="File to store state about previously seen requests.")
@click.option("--only-update-state", help="Only update the state file, don't process any requests anything.", is_flag=True)
@click.option("--dns-suffix", help="DNS domain to use eg, laconic.servesthe.world")
@click.option("--fqdn-policy", help="How to handle requests with an FQDN: prohibit, allow, preexisting", default="prohibit")
@click.option("--record-namespace-dns", help="eg, crn://laconic/dns")
@click.option("--record-namespace-deployments", help="eg, crn://laconic/deployments")
@click.option("--dry-run", help="Don't do anything, just report what would be done.", is_flag=True)
@@ -199,14 +203,11 @@ def dump_known_requests(filename, requests, status="SEEN"):
@click.option("--exclude-tags", help="Exclude requests with matching tags (comma-separated).", default="")
@click.option("--force-rebuild", help="Rebuild even if the image already exists.", is_flag=True)
@click.option("--log-dir", help="Output build/deployment logs to directory.", default=None)
@click.option("--tls-host", help="Override TLS hostname (eg, '*.mydomain.com')")
@click.option("--tls-secret", help="Override TLS secret name")
@click.option("--tls-issuer", help="TLS issuer to use (default: letsencrypt-prod)")
@click.pass_context
def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_dir, # noqa: C901
request_id, discover, state_file, only_update_state,
dns_suffix, record_namespace_dns, record_namespace_deployments, dry_run,
include_tags, exclude_tags, force_rebuild, log_dir, tls_host, tls_secret, tls_issuer):
dns_suffix, fqdn_policy, record_namespace_dns, record_namespace_deployments, dry_run,
include_tags, exclude_tags, force_rebuild, log_dir):
if request_id and discover:
print("Cannot specify both --request-id and --discover", file=sys.stderr)
sys.exit(2)
@@ -224,8 +225,8 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_
print("--dns-suffix, --record-namespace-dns, and --record-namespace-deployments are all required", file=sys.stderr)
sys.exit(2)
if (tls_secret and not tls_host) or (tls_host and not tls_secret):
print("Cannot specify --tls-host without --tls-secret", file=sys.stderr)
if fqdn_policy not in ["prohibit", "allow", "preexisting"]:
print("--fqdn-policy must be one of 'prohibit', 'allow', or 'preexisting'", file=sys.stderr)
sys.exit(2)
# Split CSV and clean up values.
@@ -313,7 +314,6 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_
print("Found %d unsatisfied request(s) to process." % len(requests_to_execute))
if not dry_run:
tls_details = TlsDetails(tls_host, tls_secret, tls_issuer)
for r in requests_to_execute:
dump_known_requests(state_file, [r], "DEPLOYING")
status = "ERROR"
@@ -343,7 +343,7 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_
kube_config,
image_registry,
force_rebuild,
tls_details,
fqdn_policy,
logger
)
status = "DEPLOYED"
-34
View File
@@ -26,40 +26,6 @@ import uuid
import yaml
class TlsDetails:
def __init__(self, host_or_hosts=None, secret_name: str = None, issuer_name: str = None):
if host_or_hosts:
if isinstance(host_or_hosts, list):
self.hosts = host_or_hosts
else:
self.hosts = [host_or_hosts]
else:
self.hosts = None
self.secret_name = secret_name
self.issuer_name = issuer_name
def to_yaml(self, indent=6):
if not self.hosts and not self.secret_name and not self.issuer_name:
return ""
ret = " " * indent + "tls:\n"
indent += 2
if self.issuer_name:
ret += " " * indent + "issuer: '%s'\n" % self.issuer_name
if self.secret_name:
ret += " " * indent + "secret: '%s'\n" % self.secret_name
if self.hosts:
ret += " " * indent + "hosts:"
indent += 2
for h in self.hosts:
ret += "\n" + " " * indent + "- '%s'" % h
return ret
class AttrDict(dict):
def __init__(self, *args, **kwargs):
super(AttrDict, self).__init__(*args, **kwargs)