fix(mirror): Nexus Docker proxy with optional Liara upstream + credential support

provision.sh: Docker proxy defaults to registry-1.docker.io (works directly
  from VPS). Set DOCKER_MIRROR_URL/USER/PASS env vars to route through
  docker-mirror.liara.ir once Liara credentials are obtained.

update-docker-upstream.sh: swap Docker proxy upstream at any time without
  re-running the full provision (useful after getting Liara credentials).

indexType auto-selects: HUB for docker.io direct, REGISTRY for Liara/Harbor.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-05-28 15:56:22 +03:30
parent 61e44c63ab
commit 7321c59e43
2 changed files with 134 additions and 31 deletions
+61 -31
View File
@@ -3,22 +3,32 @@
# Nexus first-time provisioning # Nexus first-time provisioning
# Run once on the server after: docker compose -f docker-compose.mirror.yml up -d # Run once on the server after: docker compose -f docker-compose.mirror.yml up -d
# #
# What this does: # Docker upstream:
# 1. Waits for Nexus to finish starting up # By default → registry-1.docker.io (Docker Hub directly — works from VPS)
# 2. Reads the one-time admin password, changes it to NEXUS_ADMIN_PASS # With Liara → docker-mirror.liara.ir (Iranian relay, faster + more reliable)
# 3. Enables anonymous (unauthenticated) read access — needed for CI #
# 4. Creates proxy repos: nuget-proxy, npm-proxy, docker-hub-proxy # To use Liara mirror, get credentials from https://app.liara.ir
# (Account → Docker Mirror section), then run:
# DOCKER_MIRROR_URL=https://docker-mirror.liara.ir \
# DOCKER_MIRROR_USER=your-liara-email \
# DOCKER_MIRROR_PASS=your-liara-token \
# ./mirrors/nexus/provision.sh
# #
# Usage: # Usage:
# ./mirrors/nexus/provision.sh # ./mirrors/nexus/provision.sh # Docker Hub direct
# NEXUS_ADMIN_PASS=MySecret ./mirrors/nexus/provision.sh # NEXUS_ADMIN_PASS=MySecret ./mirrors/nexus/provision.sh
# ───────────────────────────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail set -euo pipefail
NEXUS_URL="http://localhost:8081" NEXUS_URL="http://localhost:8081"
NEW_PASS="${NEXUS_ADMIN_PASS:-Mirror@2024!}" # change this or set env var NEW_PASS="${NEXUS_ADMIN_PASS:-Mirror@2024!}"
CONTAINER="meezi-mirror-nexus" CONTAINER="meezi-mirror-nexus"
# Docker upstream — override with Liara mirror once you have credentials
DOCKER_UPSTREAM="${DOCKER_MIRROR_URL:-https://registry-1.docker.io}"
DOCKER_USER="${DOCKER_MIRROR_USER:-}"
DOCKER_PASS="${DOCKER_MIRROR_PASS:-}"
# ── 1. Wait for Nexus ──────────────────────────────────────────────────────── # ── 1. Wait for Nexus ────────────────────────────────────────────────────────
echo "⏳ Waiting for Nexus (can take 2-3 min on first boot)..." echo "⏳ Waiting for Nexus (can take 2-3 min on first boot)..."
until curl -sf "$NEXUS_URL/service/rest/v1/status" | grep -q '"edition"'; do until curl -sf "$NEXUS_URL/service/rest/v1/status" | grep -q '"edition"'; do
@@ -41,12 +51,12 @@ if [ -n "$INIT_PASS_FILE" ]; then
if [ "$HTTP_CODE" = "204" ]; then if [ "$HTTP_CODE" = "204" ]; then
echo "✅ Password updated" echo "✅ Password updated"
else else
echo "⚠️ Password change returned HTTP $HTTP_CODEcontinuing with original password" echo "⚠️ Password change returned HTTP $HTTP_CODEusing original password"
NEW_PASS="$INIT_PASS_FILE" NEW_PASS="$INIT_PASS_FILE"
fi fi
ADMIN_PASS="$NEW_PASS" ADMIN_PASS="$NEW_PASS"
else else
echo "️ admin.password not present — using NEXUS_ADMIN_PASS (already provisioned?)" echo "️ admin.password not found — using NEXUS_ADMIN_PASS (already provisioned?)"
ADMIN_PASS="$NEW_PASS" ADMIN_PASS="$NEW_PASS"
fi fi
@@ -65,7 +75,7 @@ curl -sf $AUTH -X PUT "$NEXUS_URL/service/rest/v1/security/realms/active" \
-d '["NexusAuthenticatingRealm","NexusAuthorizingRealm","NuGetApiKey","NpmToken"]' \ -d '["NexusAuthenticatingRealm","NexusAuthorizingRealm","NuGetApiKey","NpmToken"]' \
&& echo "✅ Realms configured (NuGet, npm)" && echo "✅ Realms configured (NuGet, npm)"
# ── Helper: create repo (skip if already exists) ──────────────────────────── # ── Helper: create or update repo ───────────────────────────────────────────
create_repo() { create_repo() {
local TYPE="$1" local TYPE="$1"
local JSON="$2" local JSON="$2"
@@ -77,7 +87,7 @@ create_repo() {
-d "$JSON") -d "$JSON")
case "$HTTP" in case "$HTTP" in
201) echo "$NAME created" ;; 201) echo "$NAME created" ;;
400) echo "⚠️ $NAME already exists (skipped)" ;; 400) echo "⚠️ $NAME already exists (skipped — update via UI if needed)" ;;
*) echo "$NAME failed — HTTP $HTTP" ;; *) echo "$NAME failed — HTTP $HTTP" ;;
esac esac
} }
@@ -119,8 +129,17 @@ create_repo "npm/proxy" '{
"httpClient": { "blocked": false, "autoBlock": true } "httpClient": { "blocked": false, "autoBlock": true }
}' }'
# ── 6. Docker Hub proxy (port 8083) ───────────────────────────────────────── # ── 6. Docker proxy ──────────────────────────────────────────────────────────
echo "🐳 Creating Docker Hub proxy → registry-1.docker.io (port 8083) ..." if [ -n "$DOCKER_USER" ] && [ -n "$DOCKER_PASS" ]; then
echo "🐳 Creating Docker proxy → $DOCKER_UPSTREAM (with auth) ..."
DOCKER_AUTH_JSON='"authentication":{"type":"username","username":"'"$DOCKER_USER"'","password":"'"$DOCKER_PASS"'"},'
DOCKER_INDEX_TYPE="REGISTRY" # Liara is a plain registry, not hub.docker.com
else
echo "🐳 Creating Docker proxy → $DOCKER_UPSTREAM (no auth) ..."
DOCKER_AUTH_JSON=""
DOCKER_INDEX_TYPE="HUB" # direct Docker Hub — use hub index
fi
create_repo "docker/proxy" '{ create_repo "docker/proxy" '{
"name": "docker-hub-proxy", "name": "docker-hub-proxy",
"online": true, "online": true,
@@ -129,42 +148,53 @@ create_repo "docker/proxy" '{
"strictContentTypeValidation": true "strictContentTypeValidation": true
}, },
"proxy": { "proxy": {
"remoteUrl": "https://registry-1.docker.io", "remoteUrl": "'"$DOCKER_UPSTREAM"'",
"contentMaxAge": 1440, "contentMaxAge": 1440,
"metadataMaxAge": 1440 "metadataMaxAge": 1440
}, },
"negativeCache": { "enabled": true, "timeToLive": 1440 }, "negativeCache": { "enabled": true, "timeToLive": 1440 },
"httpClient": { "blocked": false, "autoBlock": true }, "httpClient": {
"blocked": false,
"autoBlock": true,
'"$DOCKER_AUTH_JSON"'
"connection": { "useTrustStore": false }
},
"docker": { "docker": {
"v1Enabled": false, "v1Enabled": false,
"forceBasicAuth": false, "forceBasicAuth": false,
"httpPort": 8083 "httpPort": 8083
}, },
"dockerProxy": { "dockerProxy": {
"indexType": "HUB", "indexType": "'"$DOCKER_INDEX_TYPE"'",
"cacheForeignLayers": false "cacheForeignLayers": false
} }
}' }'
# ── Done ───────────────────────────────────────────────────────────────────── # ── Done ─────────────────────────────────────────────────────────────────────
echo "" echo ""
echo "═══════════════════════════════════════════════════════" echo "═══════════════════════════════════════════════════════════════"
echo "🎉 Nexus provisioned successfully!" echo "🎉 Nexus provisioned!"
echo "═══════════════════════════════════════════════════════" echo "═══════════════════════════════════════════════════════════════"
echo "" echo ""
echo " UI → http://SERVER_IP:8081" echo " UI → http://$(hostname -I | awk '{print $1}'):8081"
echo " admin / $ADMIN_PASS" echo " admin / $ADMIN_PASS"
echo "" echo ""
echo " NuGet → http://SERVER_IP:8081/repository/nuget-proxy/index.json" echo " NuGet → http://$(hostname -I | awk '{print $1}'):8081/repository/nuget-proxy/index.json"
echo " npm → http://SERVER_IP:8081/repository/npm-proxy/" echo " npm → http://$(hostname -I | awk '{print $1}'):8081/repository/npm-proxy/"
echo " Docker → http://SERVER_IP:8083" echo " Docker → http://$(hostname -I | awk '{print $1}'):8083 ← upstream: $DOCKER_UPSTREAM"
echo "" echo ""
echo "To activate Docker Hub mirror — edit /etc/docker/daemon.json:" if [ -z "$DOCKER_USER" ]; then
cat << 'EOF' echo " 💡 To switch Docker upstream to Liara mirror (faster in Iran):"
{ echo " Get credentials from https://app.liara.ir → Docker Mirror, then:"
"insecure-registries": ["SERVER_IP:8083"], echo ""
"registry-mirrors": ["http://SERVER_IP:8083"] echo " DOCKER_MIRROR_URL=https://docker-mirror.liara.ir \\"
} echo " DOCKER_MIRROR_USER=your-email \\"
EOF echo " DOCKER_MIRROR_PASS=your-token \\"
echo " then: systemctl restart docker" echo " ./mirrors/nexus/update-docker-upstream.sh"
echo ""
fi
echo "To activate Docker Hub mirror on this server:"
echo " Edit /etc/docker/daemon.json:"
echo ' { "insecure-registries": ["'"$(hostname -I | awk '{print $1}'):8083"'"], "registry-mirrors": ["http://'"$(hostname -I | awk '{print $1}'):8083"'"] }'
echo " systemctl restart docker"
echo "" echo ""
+73
View File
@@ -0,0 +1,73 @@
#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────────────────────
# Switch the Nexus Docker proxy upstream to Liara mirror (or back to Docker Hub)
#
# Usage (with Liara):
# DOCKER_MIRROR_URL=https://docker-mirror.liara.ir \
# DOCKER_MIRROR_USER=your-liara-email \
# DOCKER_MIRROR_PASS=your-liara-token \
# NEXUS_ADMIN_PASS=Mirror@2024! \
# ./mirrors/nexus/update-docker-upstream.sh
#
# Usage (back to Docker Hub direct):
# NEXUS_ADMIN_PASS=Mirror@2024! ./mirrors/nexus/update-docker-upstream.sh
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail
NEXUS_URL="http://localhost:8081"
ADMIN_PASS="${NEXUS_ADMIN_PASS:-Mirror@2024!}"
AUTH="-u admin:$ADMIN_PASS"
DOCKER_UPSTREAM="${DOCKER_MIRROR_URL:-https://registry-1.docker.io}"
DOCKER_USER="${DOCKER_MIRROR_USER:-}"
DOCKER_PASS="${DOCKER_MIRROR_PASS:-}"
if [ -n "$DOCKER_USER" ] && [ -n "$DOCKER_PASS" ]; then
AUTH_BLOCK='"authentication":{"type":"username","username":"'"$DOCKER_USER"'","password":"'"$DOCKER_PASS"'"},'
INDEX_TYPE="REGISTRY"
echo "🐳 Switching Docker proxy → $DOCKER_UPSTREAM (with auth)"
else
AUTH_BLOCK=""
INDEX_TYPE="HUB"
echo "🐳 Switching Docker proxy → $DOCKER_UPSTREAM (no auth)"
fi
HTTP=$(curl -s -o /dev/null -w "%{http_code}" $AUTH \
-X PUT "$NEXUS_URL/service/rest/v1/repositories/docker/proxy/docker-hub-proxy" \
-H "Content-Type: application/json" \
-d '{
"name": "docker-hub-proxy",
"online": true,
"storage": {
"blobStoreName": "default",
"strictContentTypeValidation": true
},
"proxy": {
"remoteUrl": "'"$DOCKER_UPSTREAM"'",
"contentMaxAge": 1440,
"metadataMaxAge": 1440
},
"negativeCache": { "enabled": true, "timeToLive": 1440 },
"httpClient": {
"blocked": false,
"autoBlock": true,
'"$AUTH_BLOCK"'
"connection": { "useTrustStore": false }
},
"docker": {
"v1Enabled": false,
"forceBasicAuth": false,
"httpPort": 8083
},
"dockerProxy": {
"indexType": "'"$INDEX_TYPE"'",
"cacheForeignLayers": false
}
}')
if [ "$HTTP" = "204" ]; then
echo "✅ docker-hub-proxy updated → $DOCKER_UPSTREAM"
else
echo "❌ Update failed — HTTP $HTTP"
exit 1
fi