Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
FROM alpine:3.22.1

# set version label
ARG BUILD_DATE
ARG VERSION
ARG WEBAPP_VERSION
ARG BUILD_DATE="10/9/2025"
ARG VERSION="0.7.4"
ARG WEBAPP_VERSION="0.7.4"

LABEL build_version="netboot.xyz version: ${VERSION} Build-date: ${BUILD_DATE}"
LABEL maintainer="antonym"

LABEL org.opencontainers.image.authors="antony@mes.ser.li"
LABEL org.opencontainers.image.url="https://github.com/netbootxyz/webapp"
LABEL org.opencontainers.image.title="NetBoot.xyz WebApp"
LABEL org.opencontainers.image.description="netboot.xyz official docker container - Your favorite operating systems in one place. A network-based bootable operating system installer based on iPXE."
LABEL org.opencontainers.image.documentation="https://netboot.xyz/docs/docker"
LABEL org.opencontainers.image.version="${WEBAPP_VERSION}"
LABEL org.opencontainers.image.vendor="https://NetBoot.xyz"
LABEL org.opencontainers.image.licenses="Apache-2.0 license"

RUN apk add --no-cache bash
SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]

RUN \
apk update && \
Expand All @@ -27,6 +38,7 @@ RUN \
supervisor \
syslog-ng \
tar \
tftp-hpa \
dnsmasq && \
apk add --no-cache --virtual=build-dependencies \
npm && \
Expand All @@ -35,7 +47,7 @@ RUN \
usermod -G users nbxyz && \
mkdir /app \
/config \
/defaults
/defaults

COPY . /app

Expand All @@ -48,12 +60,15 @@ ENV TFTPD_OPTS=''
ENV NGINX_PORT='80'
ENV WEB_APP_PORT='3000'

EXPOSE 69/udp
EXPOSE 80
EXPOSE 3000
EXPOSE 69/UDP 80/TCP 443/TCP
EXPOSE 3000/TCP 8080/TCP
VOLUME ["/assets", "/config"]

COPY root/ /

COPY docker-netbootxyz/root/ /
CMD ["/start.sh"]
SHELL ["/bin/bash", "-c"]

# default command
CMD ["sh","/start.sh"]
ENTRYPOINT ["/start.sh"]

60 changes: 47 additions & 13 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@
var baseurl = process.env.SUBFOLDER || '/';
var app = require('express')();
var { DownloaderHelper } = require('node-downloader-helper');
const { HttpProxyAgent } = require('http-proxy-agent');
const { HttpsProxyAgent } = require('https-proxy-agent');
const { URL } = require('url');
const getProxyForUrl = require('proxy-from-env').getProxyForUrl;
var exec = require('child_process').exec;
var express = require('express');
var fs = require('fs');
var http = require('http').Server(app);
var io = require('socket.io')(http, {path: baseurl + 'socket.io'});
var isBinaryFile = require("isbinaryfile").isBinaryFile;
var path = require('path');
var readdirp = require('readdirp');
let {readdirp} = require('readdirp');
var fetch = require('node-fetch');
var urlLib = require('url');

Expand Down Expand Up @@ -39,6 +43,26 @@ function disablesigs(){
}
}

function getProxyAgentFromUrl(request_url, request_options=false) {
const proxy_url = getProxyForUrl(request_url);
if(!proxy_url) {
return false;
}

const protocol = new URL(request_url).protocol;
let agent;

if (protocol === 'https:') {
agent = new HttpsProxyAgent(proxy_url);
return request_options ? { httpsRequestOptions: { agent } } : agent;
} else if (protocol === 'http:') {
agent = new HttpProxyAgent(proxy_url);
return request_options ? { httpRequestOptions: { agent } } : agent;
}

return false;
}

////// PATHS //////
//// Main ////
baserouter.get("/", function (req, res) {
Expand All @@ -65,9 +89,11 @@ io.on('connection', function(socket){
var tftpcmd = '/usr/sbin/dnsmasq --version | head -n1';
var nginxcmd = '/usr/sbin/nginx -v';
var dashinfo = {};
const fetch_uri = 'https://api.github.com/repos/netbootxyz/netboot.xyz/releases/latest';
var fetch_opts = {headers:{'user-agent':'node.js'},agent:getProxyAgentFromUrl(fetch_uri,false)};
dashinfo['webversion'] = version;
dashinfo['menuversion'] = fs.readFileSync('/config/menuversion.txt', 'utf8');
fetch('https://api.github.com/repos/netbootxyz/netboot.xyz/releases/latest', {headers: {'user-agent': 'node.js'}})
fetch(fetch_uri, fetch_opts)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
Expand All @@ -88,10 +114,10 @@ io.on('connection', function(socket){
dashinfo['nginxversion'] = stderr;
io.sockets.in(socket.id).emit('renderdash',dashinfo);
});
});
});
});
});
});
});
})
.catch(error => {
console.log('There was a problem with the fetch operation: ' + error.message);
Expand Down Expand Up @@ -163,7 +189,7 @@ io.on('connection', function(socket){
io.sockets.in(socket.id).emit('renderconfig', remote_files, local_files);
});
});
// When a create file is
// When a create file is
socket.on('createipxe', function(filename){
var rootDir = '/config/menus/local/';
var filePath = path.resolve(rootDir, filename);
Expand All @@ -183,7 +209,14 @@ io.on('connection', function(socket){
var remotemenuversion = fs.readFileSync('/config/menuversion.txt', 'utf8');
var endpointsfile = fs.readFileSync('/config/endpoints.yml');
var endpoints = yaml.load(endpointsfile);
var localfiles = await readdirp.promise('/assets/.');
// Wrap readdirp in a promise
var localfiles = await new Promise((resolve, reject) => {
const entries = [];
readdirp('/assets/.')
.on('data', (entry) => entries.push(entry))
.on('end', () => resolve(entries))
.on('error', (error) => reject(error));
});
var assets = [];
if (localfiles.length != 0){
for (var i in localfiles){
Expand Down Expand Up @@ -213,8 +246,8 @@ io.on('connection', function(socket){
});
// When Dev Browser is requested reach out to github for versions
socket.on('devgetbrowser', async function(){
var api_url = 'https://api.github.com/repos/netbootxyz/netboot.xyz/';
var options = {headers: {'user-agent': 'node.js'}};
const api_url = 'https://api.github.com/repos/netbootxyz/netboot.xyz/';
var options = {headers:{'user-agent':'node.js'},agent:getProxyAgentFromUrl(api_url,false)};
var releasesResponse = await fetch(api_url + 'releases', options);
if (!releasesResponse.ok) {
throw new Error(`HTTP error! status: ${releasesResponse.status}`);
Expand Down Expand Up @@ -279,7 +312,7 @@ async function upgrademenu(version, callback){
}
for (var i in rom_files){
var file = rom_files[i];
var url = download_endpoint + file;
var url = download_endpoint + file;
downloads.push({'url':url,'path':remote_folder});
}
// static config for endpoints
Expand Down Expand Up @@ -323,10 +356,11 @@ async function downloader(downloads){
var value = downloads[i];
var url = value.url;
var path = value.path;
var dloptions = {override:true,retry:{maxRetries:2,delay:5000}};
var agent = getProxyAgentFromUrl(url, true);
var dloptions = Object.assign({}, {override:true,retry:{maxRetries:2,delay:5000}}, agent);
var dl = new DownloaderHelper(url, path, dloptions);

dl.on('end', function(){
dl.on('end', function(){
console.log('Downloaded ' + url + ' to ' + path);
});

Expand All @@ -350,11 +384,11 @@ async function downloader(downloads){
const parsedUrl = urlLib.parse(url);
if (!allowedHosts.includes(parsedUrl.host)){
// Part 2 if exists repeat
var response = await fetch(url + '.part2', {method: 'HEAD'});
var response = await fetch(url + '.part2',{method:'HEAD',agent:getProxyAgentFromUrl(url,false)});
var urltest = response.headers.get('server');
if (urltest == 'AmazonS3' || urltest == 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0') {
var dl2 = new DownloaderHelper(url + '.part2', path, dloptions);
dl2.on('end', function(){
dl2.on('end', function(){
console.log('Downloaded ' + url + '.part2' + ' to ' + path);
});
dl2.on('progress', function(stats){
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@
"ejs": "3.1.10",
"express": "4.21.2",
"http": "0.0.0",
"http-proxy-agent": "7.0.2",
"https-proxy-agent": "7.0.5",
"isbinaryfile": "5.0.6",
"js-yaml": "4.1.0",
"node-downloader-helper": "2.1.9",
"readdirp": "3.6.0",
"proxy-from-env": "1.1.0",
"readdirp": "4.0.1",
"node-fetch": "2.7.0",
"socket.io": "4.8.1",
"systeminformation": "5.27.10"
Expand Down
2 changes: 1 addition & 1 deletion root/defaults/default
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
server {
listen 80;
listen ${NGINX_PORT};
location / {
root /assets;
autoindex on;
Expand Down
6 changes: 3 additions & 3 deletions root/etc/supervisor.conf
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ daemon=off
priority = 2

[program:webapp]
environment=NODE_ENV="production",PORT=3000
environment=NODE_ENV="production",PORT="%(ENV_WEB_APP_PORT)s",HTTPS_PROXY="%(ENV_HTTPS_PROXY)s",HTTP_PROXY="%(ENV_HTTP_PROXY)s",NO_PROXY="%(ENV_NO_PROXY)s"
command=/usr/bin/node app.js
user=nbxyz
directory=/app
priority = 3

[program:in.tftpd]
command=/usr/sbin/in.tftpd -Lvvv --user nbxyz --secure %(ENV_TFTPD_OPTS)s /config/menus
[program:dnsmasq]
command=/usr/sbin/dnsmasq --port=0 --keep-in-foreground --enable-tftp --user=nbxyz --tftp-secure --tftp-root=/config/menus %(ENV_TFTPD_OPTS)s
stdout_logfile=/config/tftpd.log
redirect_stderr=true
priority = 4
Expand Down
65 changes: 65 additions & 0 deletions root/init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/bin/bash

set -exo pipefail
SCRIPT_DIR="$(readlink -f "$(dirname -- "${BASH_SOURCE[0]}")")"

# Leave empty for standard flow or any other value for config recreate.
RECREATE_CONFIGURATION="${RECREATE_CONFIGURATION:-''}"

# make config, logs, nginx etc. dirs
mkdir -p /var/lib/nginx/tmp/client_body /var/tmp/nginx \
/config/menus/remote /config/menus/local \
/config/nginx/site-confs /config/log/nginx /assets /run

# Check for file exisitance, and depending on environment replace/create/nothing
[[ -n "${RECREATE_CONFIGURATION}" ]] && \
rm -f /config/nginx/nginx.conf /config/nginx/site-confs/*

[[ ! -f /config/nginx/nginx.conf ]] && \
cp /defaults/nginx.conf /config/nginx/nginx.conf

[[ ! -f /config/nginx/site-confs/default ]] && \
envsubst < /defaults/default > /config/nginx/site-confs/default

# Ownership
chown -R nbxyz:nbxyz /assets /var/lib/nginx /var/log/nginx

# download menus if not found
if [[ ! -f /config/menus/remote/menu.ipxe ]]; then
echo "[netbootxyz-init] Downloading netboot.xyz at ${MENU_VERSION}"
echo "[netbootxyz-init] Downloading Menus at ${MENU_VERSION}"
curl -o /config/endpoints.yml -sL \
"https://raw.githubusercontent.com/netbootxyz/netboot.xyz/${MENU_VERSION}/endpoints.yml"
curl -o /tmp/menus.tar.gz -sL \
"https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/menus.tar.gz"
tar xf /tmp/menus.tar.gz -C /config/menus/remote

# boot files
echo "[netbootxyz-init] Downloading boot files at ${MENU_VERSION}"
curl -o /config/menus/remote/netboot.xyz.kpxe \
-sL "https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz.kpxe"
curl -o /config/menus/remote/netboot.xyz-undionly.kpxe \
-sL "https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz-undionly.kpxe"
curl -o /config/menus/remote/netboot.xyz.efi \
-sL "https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz.efi"
curl -o /config/menus/remote/netboot.xyz-snp.efi \
-sL "https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz-snp.efi"
curl -o /config/menus/remote/netboot.xyz-snponly.efi \
-sL "https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz-snponly.efi"
curl -o /config/menus/remote/netboot.xyz-arm64.efi \
-sL "https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz-arm64.efi"
curl -o /config/menus/remote/netboot.xyz-arm64-snp.efi \
-sL "https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz-arm64-snp.efi"
curl -o /config/menus/remote/netboot.xyz-arm64-snponly.efi \
-sL "https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz-arm64-snponly.efi"

# layer and cleanup
echo "[netbootxyz-init] layer and cleanup "

echo -n "${MENU_VERSION}" > /config/menuversion.txt
cp -r /config/menus/remote/* /config/menus
rm -f /tmp/menus.tar.gz
fi

# Ownership
chown -R nbxyz:nbxyz /config
76 changes: 2 additions & 74 deletions root/start.sh
Original file line number Diff line number Diff line change
@@ -1,79 +1,7 @@
#!/bin/bash

# make our folders
mkdir -p \
/assets \
/config/nginx/site-confs \
/config/log/nginx \
/run \
/var/lib/nginx/tmp/client_body \
/var/tmp/nginx

# copy config files
[[ ! -f /config/nginx/nginx.conf ]] && \
cp /defaults/nginx.conf /config/nginx/nginx.conf
[[ ! -f /config/nginx/site-confs/default ]] && \
cp /defaults/default /config/nginx/site-confs/default

# Ownership
chown -R nbxyz:nbxyz /assets
chown -R nbxyz:nbxyz /var/lib/nginx
chown -R nbxyz:nbxyz /var/log/nginx

# create local logs dir
mkdir -p \
/config/menus/remote \
/config/menus/local

# download menus if not found
if [[ ! -f /config/menus/remote/menu.ipxe ]]; then
if [[ -z ${MENU_VERSION+x} ]]; then \
MENU_VERSION=$(curl -sL "https://api.github.com/repos/netbootxyz/netboot.xyz/releases/latest" | jq -r '.tag_name')
fi
echo "[netbootxyz-init] Downloading netboot.xyz at ${MENU_VERSION}"
# menu files
curl -o \
/config/endpoints.yml -sL \
"https://raw.githubusercontent.com/netbootxyz/netboot.xyz/${MENU_VERSION}/endpoints.yml"
curl -o \
/tmp/menus.tar.gz -sL \
"https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/menus.tar.gz"
tar xf \
/tmp/menus.tar.gz -C \
/config/menus/remote
# boot files
curl -o \
/config/menus/remote/netboot.xyz.kpxe -sL \
"https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz.kpxe"
curl -o \
/config/menus/remote/netboot.xyz-undionly.kpxe -sL \
"https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz-undionly.kpxe"
curl -o \
/config/menus/remote/netboot.xyz.efi -sL \
"https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz.efi"
curl -o \
/config/menus/remote/netboot.xyz-snp.efi -sL \
"https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz-snp.efi"
curl -o \
/config/menus/remote/netboot.xyz-snponly.efi -sL \
"https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz-snponly.efi"
curl -o \
/config/menus/remote/netboot.xyz-arm64.efi -sL \
"https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz-arm64.efi"
curl -o \
/config/menus/remote/netboot.xyz-arm64-snp.efi -sL \
"https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz-arm64-snp.efi"
curl -o \
/config/menus/remote/netboot.xyz-arm64-snponly.efi -sL \
"https://github.com/netbootxyz/netboot.xyz/releases/download/${MENU_VERSION}/netboot.xyz-arm64-snponly.efi"
# layer and cleanup
echo -n ${MENU_VERSION} > /config/menuversion.txt
cp -r /config/menus/remote/* /config/menus
rm -f /tmp/menus.tar.gz
fi

# Ownership
chown -R nbxyz:nbxyz /config
# Perform the initial configuration
/init.sh

echo " _ _ _ "
echo " _ __ ___| |_| |__ ___ ___ | |_ __ ___ _ ____ "
Expand Down