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:
+61
-31
@@ -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_CODE — continuing with original password"
|
echo "⚠️ Password change returned HTTP $HTTP_CODE — using 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 ""
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user