mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-09-23 00:30:43 -07:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02d2deb5bd | ||
|
|
69582b2867 | ||
|
|
4e227d6efb | ||
|
|
2ad96f2aee | ||
|
|
22b28ff189 |
No files matched your search
+12
-2
@@ -14,14 +14,24 @@ server {
|
|||||||
|
|
||||||
location / {
|
location / {
|
||||||
try_files $uri $uri.html $uri/ =404;
|
try_files $uri $uri.html $uri/ =404;
|
||||||
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
|
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||||
|
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: ws: wss: https:;" always;
|
||||||
|
add_header Last-Modified "";
|
||||||
expires off;
|
expires off;
|
||||||
}
|
}
|
||||||
|
|
||||||
error_page 404 /404.html;
|
error_page 404 /404.html;
|
||||||
location = /404.html {
|
location = /404.html {
|
||||||
internal;
|
internal;
|
||||||
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
|
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||||
|
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: ws: wss: https:;" always;
|
||||||
|
add_header Last-Modified "";
|
||||||
expires off;
|
expires off;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,9 +62,93 @@ export NETBIRD_GOOGLE_TAG_MANAGER_ID=${NETBIRD_GOOGLE_TAG_MANAGER_ID}
|
|||||||
export NETBIRD_TOKEN_SOURCE=${NETBIRD_TOKEN_SOURCE:-accessToken}
|
export NETBIRD_TOKEN_SOURCE=${NETBIRD_TOKEN_SOURCE:-accessToken}
|
||||||
export NETBIRD_DRAG_QUERY_PARAMS=${NETBIRD_DRAG_QUERY_PARAMS:-false}
|
export NETBIRD_DRAG_QUERY_PARAMS=${NETBIRD_DRAG_QUERY_PARAMS:-false}
|
||||||
export NETBIRD_WASM_PATH=${NETBIRD_WASM_PATH}
|
export NETBIRD_WASM_PATH=${NETBIRD_WASM_PATH}
|
||||||
|
export NETBIRD_CSP=${NETBIRD_CSP}
|
||||||
|
export NETBIRD_CSP_CONNECT_SRC=${NETBIRD_CSP_CONNECT_SRC}
|
||||||
|
export NETBIRD_DISABLE_CSP=${NETBIRD_DISABLE_CSP}
|
||||||
|
|
||||||
echo "NetBird latest version: ${NETBIRD_LATEST_VERSION}"
|
echo "NetBird latest version: ${NETBIRD_LATEST_VERSION}"
|
||||||
|
|
||||||
|
# Build CSP
|
||||||
|
if [[ "${NETBIRD_DISABLE_CSP}" == "true" ]]; then
|
||||||
|
echo "CSP disabled via NETBIRD_DISABLE_CSP"
|
||||||
|
sed -i '/add_header Content-Security-Policy/d' /etc/nginx/http.d/default.conf
|
||||||
|
else
|
||||||
|
FIRST_PARTY_CSP="https://pkgs.netbird.io"
|
||||||
|
FIRST_PARTY_CSP_CONNECT_SRC="$NETBIRD_CSP_CONNECT_SRC"
|
||||||
|
THIRD_PARTY_CSP=""
|
||||||
|
THIRD_PARTY_CSP_CONNECT_SRC="https://api.github.com/repos/netbirdio/netbird/releases/latest https://raw.githubusercontent.com/netbirdio/dashboard/"
|
||||||
|
THIRD_PARTY_CSP_SCRIPT_SRC=""
|
||||||
|
|
||||||
|
CSP_DOMAINS=""
|
||||||
|
CSP_DOMAINS_CONNECT_SRC=""
|
||||||
|
|
||||||
|
if [[ -n "${NETBIRD_CSP}" ]]; then
|
||||||
|
CSP_DOMAINS="$CSP_DOMAINS $NETBIRD_CSP"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add AUTH_AUTHORITY to CSP
|
||||||
|
if [[ -n "${AUTH_AUTHORITY}" ]]; then
|
||||||
|
CSP_DOMAINS="$CSP_DOMAINS $AUTH_AUTHORITY"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add AUTH_AUDIENCE to CSP
|
||||||
|
if [[ -n "${AUTH_AUDIENCE}" && "${AUTH_AUDIENCE}" != "none" && "${AUTH_AUDIENCE}" == *.* ]]; then
|
||||||
|
if [[ "${AUTH_AUDIENCE}" == *"http://"* || "${AUTH_AUDIENCE}" == *"https://"* ]]; then
|
||||||
|
CSP_DOMAINS="$CSP_DOMAINS $AUTH_AUDIENCE"
|
||||||
|
else
|
||||||
|
CSP_DOMAINS="$CSP_DOMAINS https://$AUTH_AUDIENCE"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add NETBIRD_MGMT_API_ENDPOINT to CSP
|
||||||
|
if [[ -n "${NETBIRD_MGMT_API_ENDPOINT}" ]]; then
|
||||||
|
MGMT_HOST=$(echo "$NETBIRD_MGMT_API_ENDPOINT" | sed -E 's|https?://||' | cut -d'/' -f1)
|
||||||
|
if [[ -n "$MGMT_HOST" ]]; then
|
||||||
|
if [[ "$NETBIRD_MGMT_API_ENDPOINT" == https://* ]]; then
|
||||||
|
CSP_DOMAINS="$CSP_DOMAINS $NETBIRD_MGMT_API_ENDPOINT"
|
||||||
|
CSP_DOMAINS_CONNECT_SRC="$CSP_DOMAINS_CONNECT_SRC wss://$MGMT_HOST"
|
||||||
|
elif [[ "$NETBIRD_MGMT_API_ENDPOINT" == http://* ]]; then
|
||||||
|
CSP_DOMAINS="$CSP_DOMAINS $NETBIRD_MGMT_API_ENDPOINT"
|
||||||
|
CSP_DOMAINS_CONNECT_SRC="$CSP_DOMAINS_CONNECT_SRC ws://$MGMT_HOST"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add LETSENCRYPT_DOMAIN to CSP
|
||||||
|
if [[ -n "${LETSENCRYPT_DOMAIN}" && "${LETSENCRYPT_DOMAIN}" != "none" ]]; then
|
||||||
|
if [[ "$LETSENCRYPT_DOMAIN" == *"localhost"* ]]; then
|
||||||
|
CSP_DOMAINS="$CSP_DOMAINS http://$LETSENCRYPT_DOMAIN"
|
||||||
|
CSP_DOMAINS_CONNECT_SRC="$CSP_DOMAINS_CONNECT_SRC ws://$LETSENCRYPT_DOMAIN ws://*.$LETSENCRYPT_DOMAIN"
|
||||||
|
else
|
||||||
|
CSP_DOMAINS="$CSP_DOMAINS https://$LETSENCRYPT_DOMAIN"
|
||||||
|
CSP_DOMAINS_CONNECT_SRC="$CSP_DOMAINS_CONNECT_SRC wss://$LETSENCRYPT_DOMAIN wss://*.$LETSENCRYPT_DOMAIN"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
CSP_CONNECT_SRC="$CSP_DOMAINS $CSP_DOMAINS_CONNECT_SRC $FIRST_PARTY_CSP $FIRST_PARTY_CSP_CONNECT_SRC $THIRD_PARTY_CSP $THIRD_PARTY_CSP_CONNECT_SRC"
|
||||||
|
CSP_FRAME_SRC="$CSP_DOMAINS $FIRST_PARTY_CSP $THIRD_PARTY_CSP"
|
||||||
|
CSP_SCRIPT_SRC="$CSP_DOMAINS $FIRST_PARTY_CSP $THIRD_PARTY_CSP $THIRD_PARTY_CSP_SCRIPT_SRC"
|
||||||
|
|
||||||
|
# Remove duplicates
|
||||||
|
CSP_CONNECT_SRC=$(echo $CSP_CONNECT_SRC | tr ' ' '\n' | grep -v '^$' | sort -u | tr '\n' ' ' | sed 's/ $//')
|
||||||
|
CSP_FRAME_SRC=$(echo $CSP_FRAME_SRC | tr ' ' '\n' | grep -v '^$' | sort -u | tr '\n' ' ' | sed 's/ $//')
|
||||||
|
CSP_SCRIPT_SRC=$(echo $CSP_SCRIPT_SRC | tr ' ' '\n' | grep -v '^$' | sort -u | tr '\n' ' ' | sed 's/ $//')
|
||||||
|
|
||||||
|
# Update CSP in nginx config
|
||||||
|
CSP_POLICY="default-src 'none'; connect-src 'self' $CSP_CONNECT_SRC; frame-src 'self' $CSP_FRAME_SRC; script-src 'self' 'wasm-unsafe-eval' $CSP_SCRIPT_SRC; font-src 'self'; img-src * data:; manifest-src 'self'; style-src 'self' 'unsafe-inline'; frame-ancestors 'self'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests;"
|
||||||
|
CSP_HEADER="add_header Content-Security-Policy \"$CSP_POLICY\" always;"
|
||||||
|
|
||||||
|
# Escape sed special characters in replacement string (& | \)
|
||||||
|
CSP_HEADER=$(printf '%s' "$CSP_HEADER" | sed -e 's/[\\&|]/\\&/g')
|
||||||
|
|
||||||
|
echo "CSP header: $CSP_HEADER"
|
||||||
|
|
||||||
|
# Replace CSP header in nginx config
|
||||||
|
sed -i "s|add_header Content-Security-Policy \"[^\"]*\" always;|$CSP_HEADER|g" /etc/nginx/http.d/default.conf || {
|
||||||
|
echo "Failed to replace CSP header"
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
# replace ENVs in the config
|
# replace ENVs in the config
|
||||||
ENV_STR="\$\$USE_AUTH0 \$\$AUTH_AUDIENCE \$\$AUTH_AUTHORITY \$\$AUTH_CLIENT_ID \$\$AUTH_CLIENT_SECRET \$\$AUTH_SUPPORTED_SCOPES \$\$NETBIRD_MGMT_API_ENDPOINT \$\$NETBIRD_MGMT_GRPC_API_ENDPOINT \$\$NETBIRD_HOTJAR_TRACK_ID \$\$NETBIRD_GOOGLE_ANALYTICS_ID \$\$NETBIRD_GOOGLE_TAG_MANAGER_ID \$\$AUTH_REDIRECT_URI \$\$AUTH_SILENT_REDIRECT_URI \$\$NETBIRD_TOKEN_SOURCE \$\$NETBIRD_DRAG_QUERY_PARAMS \$\$NETBIRD_WASM_PATH"
|
ENV_STR="\$\$USE_AUTH0 \$\$AUTH_AUDIENCE \$\$AUTH_AUTHORITY \$\$AUTH_CLIENT_ID \$\$AUTH_CLIENT_SECRET \$\$AUTH_SUPPORTED_SCOPES \$\$NETBIRD_MGMT_API_ENDPOINT \$\$NETBIRD_MGMT_GRPC_API_ENDPOINT \$\$NETBIRD_HOTJAR_TRACK_ID \$\$NETBIRD_GOOGLE_ANALYTICS_ID \$\$NETBIRD_GOOGLE_TAG_MANAGER_ID \$\$AUTH_REDIRECT_URI \$\$AUTH_SILENT_REDIRECT_URI \$\$NETBIRD_TOKEN_SOURCE \$\$NETBIRD_DRAG_QUERY_PARAMS \$\$NETBIRD_WASM_PATH"
|
||||||
|
|
||||||
@@ -74,4 +158,7 @@ for f in $(grep -R -l AUTH_SUPPORTED_SCOPES /usr/share/nginx/html); do
|
|||||||
cp "$f" "$f".copy
|
cp "$f" "$f".copy
|
||||||
envsubst "$ENV_STR" < "$f".copy > "$f"
|
envsubst "$ENV_STR" < "$f".copy > "$f"
|
||||||
rm "$f".copy
|
rm "$f".copy
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Reload nginx to pick up the updated CSP and config
|
||||||
|
nginx -s reload
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
"dev": "next dev -p 3000",
|
"dev": "next dev -p 3000",
|
||||||
"turbo": "next dev -p 3000 --turbo",
|
"turbo": "next dev -p 3000 --turbo",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
|
"postbuild": "node postbuild.js",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"cypress:open": "cypress open"
|
"cypress:open": "cypress open"
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
const { resolve, join } = require("path");
|
||||||
|
const { createHash } = require("crypto");
|
||||||
|
const {
|
||||||
|
readFileSync,
|
||||||
|
writeFileSync,
|
||||||
|
mkdirSync,
|
||||||
|
readdirSync,
|
||||||
|
statSync,
|
||||||
|
} = require("fs");
|
||||||
|
|
||||||
|
process.env.NODE_ENV = "production";
|
||||||
|
const PLACEHOLDER = "NB_INLINE_SCRIPT_PLACEHOLDER";
|
||||||
|
console.log("Starting post-build script to extract inline scripts...");
|
||||||
|
|
||||||
|
// Function to find HTML files recursively
|
||||||
|
function findHtmlFiles(dir) {
|
||||||
|
const files = [];
|
||||||
|
const entries = readdirSync(dir);
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
const fullPath = join(dir, entry);
|
||||||
|
const stat = statSync(fullPath);
|
||||||
|
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
files.push(...findHtmlFiles(fullPath));
|
||||||
|
} else if (entry.endsWith(".html")) {
|
||||||
|
files.push(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Next.js export output, the files are in the 'out' directory
|
||||||
|
const baseDir = resolve("out");
|
||||||
|
const htmlFiles = findHtmlFiles(baseDir);
|
||||||
|
|
||||||
|
console.log(`Found ${htmlFiles.length} .html files to process`);
|
||||||
|
|
||||||
|
// Ensure assets directory exists
|
||||||
|
const assetsDir = `${baseDir}/assets`;
|
||||||
|
mkdirSync(assetsDir, { recursive: true });
|
||||||
|
|
||||||
|
htmlFiles.forEach((file) => {
|
||||||
|
// Read file contents
|
||||||
|
const contents = readFileSync(file, "utf8");
|
||||||
|
const scripts = [];
|
||||||
|
|
||||||
|
// Extract inline scripts
|
||||||
|
const newFile = contents.replace(
|
||||||
|
/<script(?![^>]*src)([^>]*)>(.+?)<\/script>/gs,
|
||||||
|
(match, attributes, scriptContent) => {
|
||||||
|
// Skip if script has src attribute (external script)
|
||||||
|
if (attributes.includes("src=")) {
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
const addPlaceholderString = scripts.length === 0;
|
||||||
|
const cleanedScript = scriptContent.trim();
|
||||||
|
|
||||||
|
if (cleanedScript) {
|
||||||
|
scripts.push(
|
||||||
|
`${cleanedScript}${cleanedScript.endsWith(";") ? "" : ";"}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return addPlaceholderString ? PLACEHOLDER : "";
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Early exit if no inline scripts found
|
||||||
|
if (!scripts.length) {
|
||||||
|
console.log(`No inline scripts found`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine scripts and create hash
|
||||||
|
const chunk = scripts.join("\n");
|
||||||
|
const hash = createHash("md5").update(chunk).digest("hex").slice(0, 8);
|
||||||
|
const chunkFileName = `chunk.${hash}.js`;
|
||||||
|
const chunkPath = `${assetsDir}/${chunkFileName}`;
|
||||||
|
|
||||||
|
// Write the chunk file
|
||||||
|
writeFileSync(chunkPath, chunk, "utf8");
|
||||||
|
|
||||||
|
// Replace placeholder string with script tag
|
||||||
|
const updatedFile = newFile.replace(
|
||||||
|
PLACEHOLDER,
|
||||||
|
`<script src="/assets/${chunkFileName}" crossorigin=""></script>`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Write updated HTML file
|
||||||
|
writeFileSync(file, updatedFile, "utf8");
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("Post-build script completed successfully!");
|
||||||
Reference in new issue
Block a user