diff --git a/.dappnode_profile b/.dappnode_profile index 7c6e7f36..baefcefe 100755 --- a/.dappnode_profile +++ b/.dappnode_profile @@ -10,10 +10,10 @@ export BIND_VERSION="${BIND_VERSION:-0.2.11}" export IPFS_VERSION="${IPFS_VERSION:-0.2.23}" export VPN_VERSION="${VPN_VERSION:-0.2.10}" -export DAPPMANAGER_VERSION="${DAPPMANAGER_VERSION:-0.2.88}" +export DAPPMANAGER_VERSION="${DAPPMANAGER_VERSION:-0.2.99}" export WIFI_VERSION="${WIFI_VERSION:-0.2.9}" export WIREGUARD_VERSION="${WIREGUARD_VERSION:-0.1.3}" -export HTTPS_VERSION="${HTTPS:-0.2.1}" +export HTTPS_VERSION="${HTTPS:-0.2.2}" export DAPPNODE_DIR="/usr/src/dappnode" export DAPPNODE_CORE_DIR="${DAPPNODE_DIR}/DNCORE" @@ -30,6 +30,8 @@ alias dappnode_status='docker compose $DNCORE_YMLS ps' alias dappnode_stop='docker compose $DNCORE_YMLS stop && docker stop $(docker container ls -a -q -f name=DAppNode*)' # Start docker core containers alias dappnode_start='docker compose $DNCORE_YMLS up -d && docker start $(docker container ls -a -q -f name=DAppNode*)' +# Remove docker core containers. This does not remove named volumes +alias dappnode_down='docker compose $DNCORE_YMLS down' # Return open-vpn credentials from a specific user. e.g: dappnode_get dappnode_admin alias dappnode_openvpn_get='docker exec -i DAppNodeCore-vpn.dnp.dappnode.eth vpncli get' # Return open-vpn admin credentials diff --git a/.github/workflows/artifacts.yml b/.github/workflows/artifacts.yml index 015f2205..e48a1e8f 100644 --- a/.github/workflows/artifacts.yml +++ b/.github/workflows/artifacts.yml @@ -68,39 +68,78 @@ jobs: sed -i -e "/HTTPS_VERSION/s/[0-9]*\.[0-9]*\.[0-9]*/"${HTTPS_VERSION}"/" .dappnode_profile cat .dappnode_profile - # ISO ATTENDED - - name: Build attended + # Debian ISO ATTENDED + - name: Build Debian attended run: | - sed -i -e "/UNATTENDED/s/true/false/" docker-compose.yml - docker-compose build - docker-compose up + export BASE_OS=debian + export UNATTENDED=false + docker compose up --build - # Verify ISO attended created - - name: Check iso attended + - name: Check Debian ISO attended run: | - ls -lrt images/DAppNode-debian-bookworm-amd64.iso + ls -lrt images/Dappnode-debian-*.iso - # Set new name for the release asset - - name: Set DAppNode attended ISO name + - name: Set Debian Dappnode attended ISO name run: | - cp ./images/DAppNode-debian-bookworm-amd64.iso DAppNode-${CORE_VERSION}-debian-bookworm-amd64.iso + file=$(ls images/Dappnode-debian-*.iso) + attended_filename="${file/images\/Dappnode-/Dappnode-${CORE_VERSION}-}" + mv "$file" "$attended_filename" - # ISO UNATTENDED - - name: Build unattended + # Debian ISO UNATTENDED + - name: Build Debian unattended run: | - sed -i -e "/UNATTENDED/s/false/true/" docker-compose.yml - docker-compose build - docker-compose up + export BASE_OS=debian + export UNATTENDED=true + docker compose up --build - # Verify ISO unattended was created - - name: Check iso unattended + - name: Check Debian ISO unattended run: | - ls -lrt images/DAppNode-debian-bookworm-amd64.iso + ls -lrt images/Dappnode-debian-*.iso # Set new name for the release asset - - name: Set DAppNode unttended ISO name + - name: Set Dappnode unttended ISO name + run: | + file=$(ls images/Dappnode-debian-*.iso) + + core_filename="${file/images\/Dappnode-/Dappnode-${CORE_VERSION}-}" + unattended_filename="${core_filename/%.iso/-unattended.iso}" + + mv "$file" "$unattended_filename" + + # Ubuntu ISO ATTENDED + - name: Build Ubuntu attended + run: | + export BASE_OS=ubuntu + export UNATTENDED=false + docker compose up --build + + - name: Check Ubuntu ISO attended + run: | + ls -lrt images/Dappnode-ubuntu-*.iso + + - name: Set Ubuntu Dappnode attended ISO name + run: | + file=$(ls images/Dappnode-ubuntu-*.iso) + attended_filename="${file/images\/Dappnode-/Dappnode-${CORE_VERSION}-}" + mv "$file" "$attended_filename" + + # Ubuntu ISO UNATTENDED + - name: Build Ubuntu unattended + run: | + export BASE_OS=ubuntu + export UNATTENDED=true + docker compose up --build + + - name: Check Ubuntu ISO unattended + run: | + ls -lrt images/Dappnode-ubuntu-*.iso + + - name: Set Ubuntu Dappnode unattended ISO name run: | - cp ./images/DAppNode-debian-bookworm-amd64.iso DAppNode-${CORE_VERSION}-debian-bookworm-amd64-unattended.iso + file=$(ls images/Dappnode-ubuntu-*.iso) + core_filename="${file/images\/Dappnode-/Dappnode-${CORE_VERSION}-}" + unattended_filename="${core_filename/%.iso/-unattended.iso}" + mv "$file" "$unattended_filename" - name: Create dappnode_profile.sh run: | @@ -112,8 +151,8 @@ jobs: with: name: test-artifact path: | - ./DAppNode-*-amd64.iso - ./DAppNode-*-amd64-unattended.iso + ./Dappnode-debian-*.iso + ./Dappnode-ubuntu-*.iso ./scripts/dappnode_install.sh ./scripts/dappnode_install_pre.sh ./scripts/dappnode_uninstall.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 48b32d87..94223b52 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,138 +27,225 @@ on: description: "Version of the OpenVPN Package. Only numbers" required: true -env: - BIND_VERSION: ${{ github.event.inputs.bind }} - IPFS_VERSION: ${{ github.event.inputs.ipfs }} - DAPPMANAGER_VERSION: ${{ github.event.inputs.dappmanager }} - WIFI_VERSION: ${{ github.event.inputs.wifi }} - WIREGUARD_VERSION: ${{ github.event.inputs.wireguard }} - HTTPS_VERSION: ${{ github.event.inputs.https }} - VPN_VERSION: ${{ github.event.inputs.vpn }} - CORE_VERSION: ${{ github.event.inputs.core }} - jobs: - pre-release: - name: create pre release + set-versions: + name: Set versions and check regex runs-on: ubuntu-latest - defaults: - run: - shell: bash - + outputs: + bind: ${{ steps.set_outputs.outputs.bind }} + ipfs: ${{ steps.set_outputs.outputs.ipfs }} + dappmanager: ${{ steps.set_outputs.outputs.dappmanager }} + wifi: ${{ steps.set_outputs.outputs.wifi }} + wireguard: ${{ steps.set_outputs.outputs.wireguard }} + https: ${{ steps.set_outputs.outputs.https }} + vpn: ${{ steps.set_outputs.outputs.vpn }} + core: ${{ steps.set_outputs.outputs.core }} steps: - # Regex for versions introduced - name: Check versions regex run: | - [[ $BIND_VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && [[ $IPFS_VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && [[ $DAPPMANAGER_VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && \ - [[ $WIFI_VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && [[ $WIREGUARD_VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && [[ $HTTPS_VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && \ - [[ $VPN_VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && [[ $CORE_VERSION =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "versions introduced in wrong format"; exit 1; } - + [[ "${{ github.event.inputs.bind }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && [[ "${{ github.event.inputs.ipfs }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && [[ "${{ github.event.inputs.dappmanager }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && \ + [[ "${{ github.event.inputs.wifi }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && [[ "${{ github.event.inputs.wireguard }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && [[ "${{ github.event.inputs.https }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && \ + [[ "${{ github.event.inputs.vpn }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && [[ "${{ github.event.inputs.core }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "versions introduced in wrong format"; exit 1; } - name: Checkout uses: actions/checkout@v4 - - # Edit the profile with the new versions introduced - name: Set new versions run: | - sed -i -e "/BIND_VERSION/s/[0-9]*\.[0-9]*\.[0-9]*/"${BIND_VERSION}"/" .dappnode_profile - sed -i -e "/IPFS_VERSION/s/[0-9]*\.[0-9]*\.[0-9]*/"${IPFS_VERSION}"/" .dappnode_profile - sed -i -e "/VPN_VERSION/s/[0-9]*\.[0-9]*\.[0-9]*/"${VPN_VERSION}"/" .dappnode_profile - sed -i -e "/DAPPMANAGER_VERSION/s/[0-9]*\.[0-9]*\.[0-9]*/"${DAPPMANAGER_VERSION}"/" .dappnode_profile - sed -i -e "/WIFI_VERSION/s/[0-9]*\.[0-9]*\.[0-9]*/"${WIFI_VERSION}"/" .dappnode_profile - sed -i -e "/WIREGUARD_VERSION/s/[0-9]*\.[0-9]*\.[0-9]*/"${WIREGUARD_VERSION}"/" .dappnode_profile - sed -i -e "/HTTPS_VERSION/s/[0-9]*\.[0-9]*\.[0-9]*/"${HTTPS_VERSION}"/" .dappnode_profile + sed -i -e "/BIND_VERSION/s/[0-9]*\.[0-9]*\.[0-9]*/"${{ github.event.inputs.bind }}"/" .dappnode_profile + sed -i -e "/IPFS_VERSION/s/[0-9]*\.[0-9]*\.[0-9]*/"${{ github.event.inputs.ipfs }}"/" .dappnode_profile + sed -i -e "/VPN_VERSION/s/[0-9]*\.[0-9]*\.[0-9]*/"${{ github.event.inputs.vpn }}"/" .dappnode_profile + sed -i -e "/DAPPMANAGER_VERSION/s/[0-9]*\.[0-9]*\.[0-9]*/"${{ github.event.inputs.dappmanager }}"/" .dappnode_profile + sed -i -e "/WIFI_VERSION/s/[0-9]*\.[0-9]*\.[0-9]*/"${{ github.event.inputs.wifi }}"/" .dappnode_profile + sed -i -e "/WIREGUARD_VERSION/s/[0-9]*\.[0-9]*\.[0-9]*/"${{ github.event.inputs.wireguard }}"/" .dappnode_profile + sed -i -e "/HTTPS_VERSION/s/[0-9]*\.[0-9]*\.[0-9]*/"${{ github.event.inputs.https }}"/" .dappnode_profile cat .dappnode_profile - - # ISO ATTENDED - - name: Build attended + - name: Create dappnode_profile.sh + run: cp .dappnode_profile dappnode_profile.sh + - name: Upload dappnode_profile.sh + uses: actions/upload-artifact@v4 + with: + name: dappnode_profile + path: dappnode_profile.sh + - name: Set outputs + id: set_outputs run: | - sed -i -e "/UNATTENDED/s/true/false/" docker-compose.yml - docker-compose build - docker-compose up - - # Verify ISO attended created - - name: Check iso attended + echo "core=${{ github.event.inputs.core }}" >> $GITHUB_OUTPUT + echo "bind=${{ github.event.inputs.bind }}" >> $GITHUB_OUTPUT + echo "ipfs=${{ github.event.inputs.ipfs }}" >> $GITHUB_OUTPUT + echo "dappmanager=${{ github.event.inputs.dappmanager }}" >> $GITHUB_OUTPUT + echo "wifi=${{ github.event.inputs.wifi }}" >> $GITHUB_OUTPUT + echo "wireguard=${{ github.event.inputs.wireguard }}" >> $GITHUB_OUTPUT + echo "https=${{ github.event.inputs.https }}" >> $GITHUB_OUTPUT + echo "vpn=${{ github.event.inputs.vpn }}" >> $GITHUB_OUTPUT + + build-debian-attended: + name: Build Debian attended ISO + runs-on: ubuntu-latest + needs: set-versions + steps: + - uses: actions/checkout@v4 + - name: Download dappnode_profile + uses: actions/download-artifact@v4 + with: + name: dappnode_profile + - name: Rename dappnode_profile.sh to .dappnode_profile + run: mv dappnode_profile.sh .dappnode_profile + - name: Build Debian attended run: | - ls -lrt images/DAppNode-debian-bookworm-amd64.iso - - # Set new name for the release asset - - name: Set DAppNode attended ISO name + export BASE_OS=debian + export UNATTENDED=false + docker compose up --build + - name: Set Debian Dappnode attended ISO name run: | - cp ./images/DAppNode-debian-bookworm-amd64.iso DAppNode-${CORE_VERSION}-debian-bookworm-amd64.iso - - # ISO UNATTENDED - - name: Build unattended + file=$(ls images/Dappnode-debian-*.iso) + filename=$(basename "$file") + core_filename="Dappnode-${{ needs.set-versions.outputs.core }}-debian-${filename#Dappnode-debian-}" + attended_filename="${core_filename/%.iso/-attended.iso}" + sudo cp "$file" "images/$attended_filename" + - name: Upload Debian attended ISO + uses: actions/upload-artifact@v4 + with: + name: debian-attended-iso + path: images/*-attended.iso + - name: Get SHA-256 Debian attended run: | - sed -i -e "/UNATTENDED/s/false/true/" docker-compose.yml - docker-compose build - docker-compose up + file=$(find images/ -type f -name '*-attended.iso') + shasum -a 256 $file > SHASUM_DEBIAN_ATTENDED.txt + - name: Upload Debian attended SHA256 + uses: actions/upload-artifact@v4 + with: + name: debian-attended-sha + path: SHASUM_DEBIAN_ATTENDED.txt - # Verify ISO unattended was created - - name: Check iso unattended + build-debian-unattended: + name: Build Debian unattended ISO + runs-on: ubuntu-latest + needs: set-versions + steps: + - uses: actions/checkout@v4 + - name: Download dappnode_profile + uses: actions/download-artifact@v4 + with: + name: dappnode_profile + - name: Rename dappnode_profile.sh to .dappnode_profile + run: mv dappnode_profile.sh .dappnode_profile + - name: Build Debian unattended run: | - ls -lrt images/DAppNode-debian-bookworm-amd64.iso - - # Set new name for the release asset - - name: Set DAppNode unttended ISO name + export BASE_OS=debian + export UNATTENDED=true + docker compose build + docker compose up + - name: Set Debian Dappnode unattended ISO name run: | - cp ./images/DAppNode-debian-bookworm-amd64.iso DAppNode-${CORE_VERSION}-debian-bookworm-amd64-unattended.iso - - # Create profile.sh script (not able to set dot (.) before the name in the gh release asset) - - name: Create dappnode_profile.sh + file=$(ls images/Dappnode-debian-*.iso) + filename=$(basename "$file") + core_filename="Dappnode-${{ needs.set-versions.outputs.core }}-debian-${filename#Dappnode-debian-}" + unattended_filename="${core_filename/%.iso/-unattended.iso}" + sudo cp "$file" "images/$unattended_filename" + - name: Upload Debian unattended ISO + uses: actions/upload-artifact@v4 + with: + name: debian-unattended-iso + path: images/*-unattended.iso + - name: Get SHA-256 Debian unattended run: | - cp .dappnode_profile dappnode_profile.sh + file=$(find images/ -type f -name '*-unattended.iso') + shasum -a 256 $file > SHASUM_DEBIAN_UNATTENDED.txt + - name: Upload Debian unattended SHA256 + uses: actions/upload-artifact@v4 + with: + name: debian-unattended-sha + path: SHASUM_DEBIAN_UNATTENDED.txt - # SHASUMs - - name: Get SHA-256 attended - id: shasum-attended + build-ubuntu-unattended: + name: Build Ubuntu unattended ISO + runs-on: ubuntu-latest + needs: set-versions + steps: + - uses: actions/checkout@v4 + - name: Download dappnode_profile + uses: actions/download-artifact@v4 + with: + name: dappnode_profile + - name: Rename dappnode_profile.sh to .dappnode_profile + run: mv dappnode_profile.sh .dappnode_profile + - name: Build Ubuntu unattended run: | - SHASUM_ATTENDED=$(shasum -a 256 DAppNode-${CORE_VERSION}-debian-bookworm-amd64.iso) - echo "::set-output name=SHASUM_ATTENDED::$SHASUM_ATTENDED" - - - name: Get SHA-256 unattended - id: shasum-unattended + export BASE_OS=ubuntu + export UNATTENDED=true + docker compose up --build + - name: Set Ubuntu Dappnode unattended ISO name run: | - SHASUM_UNATTENDED=$(shasum -a 256 DAppNode-${CORE_VERSION}-debian-bookworm-amd64-unattended.iso) - echo "::set-output name=SHASUM_UNATTENDED::$SHASUM_UNATTENDED" + file=$(ls images/Dappnode-ubuntu-*.iso) + filename=$(basename "$file") + core_filename="Dappnode-${{ needs.set-versions.outputs.core }}-ubuntu-${filename#Dappnode-ubuntu-}" + unattended_filename="${core_filename/%.iso/-unattended.iso}" + sudo cp "$file" "images/$unattended_filename" + - name: Get SHA-256 Ubuntu unattended + run: | + file=$(find images/ -type f -name '*-ubuntu-*-unattended.iso') + shasum -a 256 $file > SHASUM_UBUNTU_UNATTENDED.txt + - name: Upload Ubuntu unattended ISO and SHA256 to SSH + uses: appleboy/scp-action@v0.1.7 + with: + host: ${{ secrets.ISO_SSH_HOST }} + username: ${{ secrets.ISO_SSH_USER }} + key: ${{ secrets.ISO_SSH_PRIVATE_KEY }} + port: ${{ secrets.SSH_PORT }} + source: SHASUM_UBUNTU_UNATTENDED.txt,images/*-ubuntu-*-unattended.iso + target: ${{ secrets.ISO_SSH_PATH }} + overwrite: true + - name: Upload Ubuntu unattended SHA256 as artifact + uses: actions/upload-artifact@v4 + with: + name: ubuntu-unattended-sha + path: SHASUM_UBUNTU_UNATTENDED.txt - # Release body + release: + name: Combine, release, and upload + runs-on: ubuntu-latest + needs: + - set-versions + - build-debian-attended + - build-debian-unattended + - build-ubuntu-unattended + steps: + - uses: actions/checkout@v4 + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: ./artifacts + - name: Move ISOs and SHAs + run: | + mkdir -p images + mv artifacts/debian-attended-iso/* images/ + mv artifacts/debian-unattended-iso/* images/ + mv artifacts/debian-attended-sha/* . + mv artifacts/debian-unattended-sha/* . + mv artifacts/ubuntu-unattended-sha/* . + mv artifacts/dappnode_profile/* . - name: Write release content run: | - echo -en "# Versions\n| Package | Version |\n|---|---|\nbind.dnp.dappnode.eth|${BIND_VERSION}|\n|ipfs.dnp.dappnode.eth|${IPFS_VERSION}|\n|vpn.dnp.dappnode.eth |${VPN_VERSION}|\n|dappmanager.dnp.dappnode.eth|${DAPPMANAGER_VERSION}|\n|wifi.dnp.dappnode.eth|${WIFI_VERSION}|\n|https.dnp.dappnode.eth|${HTTPS_VERSION}|\n|wireguard.dnp.dappnode.eth|${WIREGUARD_VERSION}|\n# Changes\nChanges implemented in release ${CORE_VERSION}\n# Attended version\nInstall and customize DAppNode using the attended ISO: **DAppNode-${CORE_VERSION}-debian-bookworm-amd64.iso**\n\n## ISO SHA-256 Checksum\n\`\`\`\nshasum -a 256 DAppNode-${CORE_VERSION}-debian-bookworm-amd64.iso\n${SHASUM_ATTENDED}\n\`\`\`\n# Unattended version\nInstall DAppNode easily using the unattended ISO: **DAppNode-${CORE_VERSION}-debian-bookworm-amd64-unattended.iso**\nDo a reboot right after the installation\n:warning: **Warning**: This ISO will install Dappnode automatically, deleting all existing partitions on the disk\n\ndefault login data:\n - **__user__**: dappnode\n - **__password__**: dappnode.s0\n## ISO SHA-256 Checksum\n\`\`\`\nshasum -a 256 DAppNode-${CORE_VERSION}-debian-bookworm-amd64-unattended.iso\n${SHASUM_UNATTENDED}\n\`\`\`\n# DAppNode for Raspberry Pi 4 64bit\n[Instructions](https://github.com/dappnode/DAppNode/wiki/DAppNodeARM-Installation-Guide)\n\ndefault login data:\n - **__user__**: dappnode\n - **__password__**: dappnodepi" > CHANGELOG.md + SHASUM_DEBIAN_ATTENDED=$(cat SHASUM_DEBIAN_ATTENDED.txt) + SHASUM_DEBIAN_UNATTENDED=$(cat SHASUM_DEBIAN_UNATTENDED.txt) + SHASUM_UBUNTU_UNATTENDED=$(cat SHASUM_UBUNTU_UNATTENDED.txt) + echo -en "# Versions\n| Package | Version |\n|---|---|\nbind.dnp.dappnode.eth|${{ needs.set-versions.outputs.bind }}|\n|ipfs.dnp.dappnode.eth|${{ needs.set-versions.outputs.ipfs }}|\n|vpn.dnp.dappnode.eth |${{ needs.set-versions.outputs.vpn }}|\n|dappmanager.dnp.dappnode.eth|${{ needs.set-versions.outputs.dappmanager }}|\n|wifi.dnp.dappnode.eth|${{ needs.set-versions.outputs.wifi }}|\n|https.dnp.dappnode.eth|${{ needs.set-versions.outputs.https }}|\n|wireguard.dnp.dappnode.eth|${{ needs.set-versions.outputs.wireguard }}|\n# Changes\nChanges implemented in release ${{ needs.set-versions.outputs.core }}\n# Debian Attended version\nInstall and customize DAppNode using the attended ISO: **DAppNode-${{ needs.set-versions.outputs.core }}-debian-bookworm-amd64.iso**\n\n## ISO SHA-256 Checksum\n```\nshasum -a 256 DAppNode-${{ needs.set-versions.outputs.core }}-debian-bookworm-amd64.iso\n$SHASUM_DEBIAN_ATTENDED\n```\n# Debian Unattended version\nInstall DAppNode easily using the unattended ISO: **DAppNode-${{ needs.set-versions.outputs.core }}-debian-bookworm-amd64-unattended.iso**\nDo a reboot right after the installation\n:warning: **Warning**: This ISO will install Dappnode automatically, deleting all existing partitions on the disk\n\n## ISO SHA-256 Checksum\n```\nshasum -a 256 DAppNode-${{ needs.set-versions.outputs.core }}-debian-bookworm-amd64-unattended.iso\n$SHASUM_DEBIAN_UNATTENDED\n```\n# Ubuntu Unattended version\nInstall DAppNode easily using the unattended ISO: **DAppNode-${{ needs.set-versions.outputs.core }}-ubuntu-bookworm-amd64-unattended.iso**\n\n## ISO SHA-256 Checksum\n```\nshasum -a 256 DAppNode-${{ needs.set-versions.outputs.core }}-ubuntu-bookworm-amd64-unattended.iso\n$SHASUM_UBUNTU_UNATTENDED\n```\nUploaded at https://ubuntu.iso.dappnode.io\n# DAppNode for Raspberry Pi 4 64bit\n[Instructions](https://github.com/dappnode/DAppNode/wiki/DAppNodeARM-Installation-Guide)\n\ndefault login data:\n - **__user__**: dappnode\n - **__password__**: dappnodepi" > CHANGELOG.md cat CHANGELOG.md - env: - SHASUM_ATTENDED: ${{ steps.shasum-attended.outputs.SHASUM_ATTENDED }} - SHASUM_UNATTENDED: ${{ steps.shasum-unattended.outputs.SHASUM_UNATTENDED }} - - # PRE-RELEASE ASSETS - - name: Pre release - uses: softprops/action-gh-release@v1 + - name: Print images directory + run: | + echo "Images directory content:" + ls -lrt images/ + - name: Create pre-release + uses: softprops/action-gh-release@v2 with: - tag_name: ${{ github.event.inputs.core }} + tag_name: ${{ needs.set-versions.outputs.core }} prerelease: true files: | - ./DAppNode-*-amd64.iso - ./DAppNode-*-amd64-unattended.iso - ./scripts/dappnode_install.sh - ./scripts/dappnode_install_pre.sh - ./scripts/dappnode_uninstall.sh - ./scripts/dappnode_access_credentials.sh + ./images/Dappnode-*-debian-*-attended.iso + ./images/Dappnode-*-debian-*-unattended.iso + ./scripts/dappnode_install*.sh + ./scripts/dappnode_uninstall*.sh + ./scripts/dappnode_access_credentials*.sh dappnode_profile.sh body_path: CHANGELOG.md - env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Check git diff - id: git_diff - run: | - git diff --no-index --word-diff-regex=[^[:space:]] --no-patch .dappnode_profile && echo '::set-output name=HAS_CHANGED::true' || echo '::set-output name=HAS_CHANGED::false' - - - name: Create Pull Request - if: steps.git_diff.outputs.HAS_CHANGED == 'true' - uses: peter-evans/create-pull-request@v5 - with: - add-paths: ".dappnode_profile" - commit-message: "update core packages versions" - branch: "update-profile" - title: "update core packages versions" - delete-branch: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9d9a2322..d7c85e1b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,17 +32,35 @@ jobs: sudo /bin/bash ./scripts/dappnode_uninstall.sh y iso: - name: test ISO + name: test Debian and Ubuntu ISO runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - - name: create iso + + # Create Debian ISO + - name: create Debian ISO run: | - docker-compose build - docker-compose up + export BASE_OS=debian + export UNATTENDED=true + docker compose up --build ls images/ - - name: verify image + + # Verify Debian ISO creation + - name: verify Debian ISO + run: | + ls -lrt images/Dappnode-debian-*.iso + + # Create Ubuntu ISO + - name: create Ubuntu ISO + run: | + export BASE_OS=ubuntu + export UNATTENDED=true + docker compose up --build + ls images/ + + # Verify Ubuntu ISO creation + - name: verify Ubuntu ISO run: | - ls -lrt images/DAppNode-debian-bookworm-amd64.iso + ls -lrt images/Dappnode-ubuntu-*.iso diff --git a/.github/workflows/validate_autoinstall.yml b/.github/workflows/validate_autoinstall.yml new file mode 100644 index 00000000..45b71ac0 --- /dev/null +++ b/.github/workflows/validate_autoinstall.yml @@ -0,0 +1,42 @@ +name: Validate Ubuntu autoinstall YAML Files + +on: + push: + pull_request: + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install check-jsonschema yamllint yq + + - name: Lint attended autoinstall.yaml + run: yamllint -c .yamllint iso/preseeds/ubuntu/autoinstall.yaml + + - name: Lint unattended autoinstall.yaml + run: yamllint -c .yamllint iso/preseeds/ubuntu/autoinstall_unattended.yaml + + - name: Download JSON schema + run: curl -o schema.json https://raw.githubusercontent.com/canonical/subiquity/main/autoinstall-schema.json + + - name: Extract autoinstall properties from YAML files + run: | + yq '.autoinstall' iso/preseeds/ubuntu/autoinstall.yaml > attended.yaml + yq '.autoinstall' iso/preseeds/ubuntu/autoinstall_unattended.yaml > unattended.yaml + + - name: Validate attended autoinstall.yaml + run: | + check-jsonschema --schemafile schema.json attended.yaml + + - name: Validate unattended autoinstall.yaml + run: | + check-jsonschema --schemafile schema.json unattended.yaml diff --git a/.yamllint b/.yamllint new file mode 100644 index 00000000..c9b624e1 --- /dev/null +++ b/.yamllint @@ -0,0 +1,6 @@ +# .yamllint +extends: default + +rules: + line-length: + max: 150 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index a7784e29..a09836ef 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,10 @@ FROM docker:dind -# hadolint ignore=DL3018 -RUN apk update && \ - apk add --no-cache xorriso git xz curl ca-certificates iptables cpio bash perl-utils \ - docker-compose && \ - rm -rf /var/cache/apk/* - -#RUN apk add -U --repository http://dl-cdn.alpinelinux.org/alpine/edge/testing aufs-util -# RUN addgroup -g 2999 docker +# Install required packages +RUN apk update && \ + apk add --no-cache xorriso git xz curl ca-certificates iptables cpio bash perl-utils -# Create app directory WORKDIR /usr/src/app COPY . . -CMD ["/usr/src/app/iso/scripts/generate_ISO.sh"] +CMD ["/usr/src/app/iso/scripts/generate_ISO.sh"] diff --git a/README.md b/README.md index 12c5e324..89817f1c 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@


- +

@@ -38,7 +38,7 @@ DAppNode is empowering people by creating a simple, transparent system for hosti DAppNode lowers the barrier of entry for non tech-savvy participants. It allows you to deploy, update, and manage P2P clients and nodes without leaving your browser. No terminal or command line interface.

- +

@@ -119,7 +119,7 @@ Champions are community members that are in charge of maintaining packages: keep Get your DAppNode and start contributing to decentralization by running your own nodes. -[Install DAppNode on your host machine](https://docs.dappnode.io/user/quick-start/Core/installation) or buy your DAppNode with all the stuff configured and prepared to be used in [DAppNode shop](https://dappnode.com/en-us/collections/frontpage) +[Install DAppNode on your host machine](https://docs.dappnode.io/docs/user/install/overview) or buy your DAppNode with all the stuff configured and prepared to be used in [DAppNode shop](https://dappnode.com/en-us/collections/frontpage) ### Install DAppNode with ISO diff --git a/docker-compose.yml b/docker-compose.yml index 7eddc984..b358db40 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,8 @@ services: environment: - BUILD=false # In case you want to re-generate a all the images, not recommended - CLEAN=true # it remove the images directory - - UNATTENDED=false # UNATTENDED version + - UNATTENDED=${UNATTENDED} # UNATTENDED version + - BASE_OS=${BASE_OS} # Base OS version (debian or ubuntu) volumes: - ./images:/images - "/var/run/docker.sock:/var/run/docker.sock" diff --git a/funding.json b/funding.json new file mode 100644 index 00000000..f01a2642 --- /dev/null +++ b/funding.json @@ -0,0 +1,5 @@ +{ + "opRetro": { + "projectId": "0xf839a585342327848d4541a6fcc315404e879537a60a1370f2cd45a94283a1ba" + } +} diff --git a/iso/boot/splash.png b/iso/boot/splash.png index 835dc493..a6f4196e 100644 Binary files a/iso/boot/splash.png and b/iso/boot/splash.png differ diff --git a/iso/boot/ubuntu/grub.cfg b/iso/boot/ubuntu/grub.cfg new file mode 100644 index 00000000..88c8305b --- /dev/null +++ b/iso/boot/ubuntu/grub.cfg @@ -0,0 +1,21 @@ +if loadfont unicode ; then + set gfxmode=800x600 + insmod efi_gop + insmod efi_uga + insmod video_bochs + insmod video_cirrus + insmod gfxterm + insmod png + terminal_output gfxterm +fi + +set timeout=6 + +set theme=/boot/grub/themes/dappnode/theme.txt + +menuentry "Install Dappnode (over Ubuntu Server)" { + set background_color=black + set gfxpayload=keep # Maintain the graphical resolution through the booting + linux /casper/vmlinuz autoinstall vga=788 FRONTEND_BACKGROUND=dark --- # Added autoinstall to make it unattended + initrd /casper/initrd +} \ No newline at end of file diff --git a/iso/boot/ubuntu/themes/dappnode/splash.png b/iso/boot/ubuntu/themes/dappnode/splash.png new file mode 100644 index 00000000..236fa034 Binary files /dev/null and b/iso/boot/ubuntu/themes/dappnode/splash.png differ diff --git a/iso/boot/ubuntu/themes/dappnode/theme.txt b/iso/boot/ubuntu/themes/dappnode/theme.txt new file mode 100644 index 00000000..f096526d --- /dev/null +++ b/iso/boot/ubuntu/themes/dappnode/theme.txt @@ -0,0 +1,41 @@ +title-color: "white" +title-text: "Dappnode Installer" +title-font: "Sans Regular 16" +desktop-color: "black" +desktop-image: "/boot/grub/themes/dappnode/splash.png" +message-color: "white" +message-bg-color: "black" +terminal-font: "Sans Regular 12" + ++ boot_menu { + left = 18% + width = 50% + top = 200 + height = 200 + item_font = "Sans Regular 12" + item_color = #d3d3d3 + selected_item_color = "white" + item_height = 20 + item_padding = 15 + item_spacing = 5 +} + ++ vbox { + top = 100%-60 + left = 10% + + hbox { + top = 0 + left = 20% + + label {text = "Enter: " font = "Sans 10" color = "white" align = "left"} + + label {text = "Select " font = "Sans 10" color = "#d3d3d3" align = "left"} + } + + hbox { + top = 0 + left = 20% + + label {text = "E: " font = "Sans 10" color = "white" align = "left"} + + label {text = "Edit Selection " font = "Sans 10" color = "#d3d3d3" align = "left"} + + label {text = " " font = "Sans 10" color = "white" align = "left"} + + label {text = "C: " font = "Sans 10" color = "white" align = "left"} + + label {text = "GRUB Command line" font = "Sans 10" color = "#d3d3d3" align = "left"} + } +} diff --git a/iso/preseeds/ubuntu/autoinstall.yaml b/iso/preseeds/ubuntu/autoinstall.yaml new file mode 100644 index 00000000..4fb3eadc --- /dev/null +++ b/iso/preseeds/ubuntu/autoinstall.yaml @@ -0,0 +1,30 @@ +# cloud-config +--- +autoinstall: + version: 1 + interactive-sections: + - identity + - keyboard + - locale + - network + - storage + - timezone + - ssh + + packages: + - linux-generic + - wpasupplicant + - intel-microcode + - iucode-tool + - iptables + + late-commands: + - "curtin in-target --target=/target -- apt-get update" + - "curtin in-target --target=/target -- apt-get install -y chrony build-essential iw iwd avahi-utils" + - "mkdir -p /target/usr/src/dappnode" + - "cp -ar /cdrom/dappnode/* /target/usr/src/dappnode/" + - "cp -a /cdrom/dappnode/scripts/rc.local /target/etc/rc.local" + - "chmod +x /target/usr/src/dappnode/scripts/dappnode_install_pre.sh" + - "touch /target/usr/src/dappnode/.firstboot" + - "cp -ar /etc/netplan/* /target/etc/netplan/" # Necessary for prerequisites + - "curtin in-target --target=/target -- /usr/src/dappnode/scripts/dappnode_install_pre.sh UPDATE" diff --git a/iso/preseeds/ubuntu/autoinstall_unattended.yaml b/iso/preseeds/ubuntu/autoinstall_unattended.yaml new file mode 100644 index 00000000..0e4fd24e --- /dev/null +++ b/iso/preseeds/ubuntu/autoinstall_unattended.yaml @@ -0,0 +1,45 @@ +# cloud-config +--- +autoinstall: + version: 1 + + locale: en_US.UTF-8 + + keyboard: + layout: us + + # network left as default (DHCP in interfaces named en* or eth*) + storage: + layout: + name: lvm + sizing-policy: all + + identity: + hostname: dappnode + username: dappnode + password: "$6$insecur3$rnEv9Amdjn3ctXxPYOlzj/cwvLT43GjWzkPECIHNqd8Vvza5bMG8QqMwEIBKYqnj609D.4ngi4qlmt29dLE.71" + + ssh: + install-server: true + # By default, the password is allowed if no authorized keys are provided + + packages: + - linux-generic + - wpasupplicant + - intel-microcode + - iucode-tool + - iptables + + timezone: UTC + + late-commands: + - "curtin in-target --target=/target -- apt-get update" + - "curtin in-target --target=/target -- apt-get install -y chrony build-essential iw iwd avahi-utils" + - "mkdir -p /target/usr/src/dappnode" + - "cp -ar /cdrom/dappnode/* /target/usr/src/dappnode/" + - "cp -a /cdrom/dappnode/scripts/rc.local /target/etc/rc.local" + - "chmod +x /target/usr/src/dappnode/scripts/dappnode_install_pre.sh" + - "touch /target/usr/src/dappnode/.firstboot" + - "cp -ar /etc/netplan/* /target/etc/netplan/" # Necessary for prerequisites + - "curtin in-target --target=/target -- /usr/src/dappnode/scripts/dappnode_install_pre.sh UPDATE" + # TODO: Handle /etc/network/interfaces and /etc/network/devhotplug diff --git a/iso/scripts/common_iso_generation.sh b/iso/scripts/common_iso_generation.sh new file mode 100644 index 00000000..1cd65a0c --- /dev/null +++ b/iso/scripts/common_iso_generation.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +WORKDIR="/usr/src/app" +ISO_BUILD_PATH="${WORKDIR}/dappnode-iso" +DAPPNODE_ISO_PREFIX="Dappnode-" + +download_iso() { + local iso_path=$1 + local iso_name=$2 + local iso_url=$3 + + echo "[INFO] Downloading base ISO image: ${iso_name}..." + if [ ! -f "${iso_path}" ]; then + wget "${iso_url}" -O "${iso_path}" + fi + echo "[INFO] Download complete!" +} + +verify_download() { + local iso_path=$1 + local expected_shasum=$2 + + echo "[INFO] Verifying download..." + [[ "$(shasum -a 256 ${iso_path})" != "$expected_shasum" ]] && { + echo "[ERROR] Wrong shasum for ${iso_path}" + exit 1 + } + echo "[INFO] Verification complete!" +} + +clean_old_files() { + local iso_extraction_dir=$1 + local base_iso_prefix=$2 + + echo "[INFO] Cleaning old files..." + rm -rf "${iso_extraction_dir}º" + rm -rf "${base_iso_prefix}*" +} + +extract_iso() { + local iso_path=$1 + local extraction_target_dir=$2 + + echo "[INFO] Extracting the ISO..." + osirrox -indev "${iso_path}" -extract / "${extraction_target_dir}" +} + +# Using a 512-byte block size to ensure the entire Master Boot Record (MBR) is captured. +# The MBR contains boot code, the partition table, and a boot signature, all essential for creating bootable media. +# This ensures that the new ISO being created is bootable under different system setups +prepare_boot_process() { + local iso_path=$1 + local mbr_output_path=$2 + local block_size=512 + + echo "[INFO] Obtaining the MBR for hybrid ISO..." + dd if="${iso_path}" bs=${block_size} count=1 of="${mbr_output_path}" +} + +add_dappnode_files_to_iso_build() { + local iso_build_path=$1 + local workdir=$2 + + echo "[INFO] Creating necessary directories and copying files..." + mkdir -p ${iso_build_path}/dappnode + cp -r ${workdir}/scripts ${iso_build_path}/dappnode + cp -r ${workdir}/dappnode/* ${iso_build_path}/dappnode +} + +# TODO: Is this ok for Ubuntu? Check what this is for +handle_checksums() { + echo "Fix md5 sum..." + # shellcheck disable=SC2046 + md5sum $(find ! -name "md5sum.txt" ! -path "./isolinux/*" -type f) >md5sum.txt +} diff --git a/iso/scripts/generate_ISO.sh b/iso/scripts/generate_ISO.sh index 024e5397..bf241775 100755 --- a/iso/scripts/generate_ISO.sh +++ b/iso/scripts/generate_ISO.sh @@ -22,4 +22,8 @@ fi mkdir -p /usr/src/app/dappnode touch /usr/src/app/dappnode/iso_install.log -/usr/src/app/iso/scripts/generate_dappnode_iso_debian.sh +if [ "$BASE_OS" = "ubuntu" ]; then + /usr/src/app/iso/scripts/generate_dappnode_iso_ubuntu.sh +else + /usr/src/app/iso/scripts/generate_dappnode_iso_debian.sh +fi diff --git a/iso/scripts/generate_dappnode_iso_debian.sh b/iso/scripts/generate_dappnode_iso_debian.sh index 3f02fb73..201e0082 100755 --- a/iso/scripts/generate_dappnode_iso_debian.sh +++ b/iso/scripts/generate_dappnode_iso_debian.sh @@ -1,85 +1,98 @@ #!/bin/bash set -e -# Source = https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.5.0-amd64-netinst.iso -ISO_NAME=debian-12.5.0-amd64-netinst.iso -ISO_PATH="/images/${ISO_NAME}" -ISO_URL=https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/ -SHASUM="013f5b44670d81280b5b1bc02455842b250df2f0c6763398feb69af1a805a14f ${ISO_PATH}" - -echo "Downloading debian ISO image: ${ISO_NAME}..." -if [ ! -f ${ISO_PATH} ]; then - wget ${ISO_URL}/${ISO_NAME} \ - -O ${ISO_PATH} -fi -echo "Done!" - -echo "Verifying download..." -[[ "$(shasum -a 256 ${ISO_PATH})" != "$SHASUM" ]] && { - echo "ERROR: wrong shasum" - exit 1 +SCRIPTS_DIR=$(dirname "${BASH_SOURCE[0]}") + +source ${SCRIPTS_DIR}/common_iso_generation.sh + +# Source = https://cdimage.debian.org/mirror/cdimage/archive/12.11.0/amd64/iso-cd/debian-12.11.0-amd64-netinst.iso +BASE_ISO_NAME="debian-12.11.0-amd64-netinst.iso" +BASE_ISO_PATH="/images/${BASE_ISO_NAME}" +BASE_ISO_URL="https://cdimage.debian.org/mirror/cdimage/archive/12.11.0/amd64/iso-cd/${BASE_ISO_NAME}" +BASE_ISO_SHASUM="30ca12a15cae6a1033e03ad59eb7f66a6d5a258dcf27acd115c2bd42d22640e8 ${BASE_ISO_PATH}" + +DAPPNODE_ISO_NAME="${DAPPNODE_ISO_PREFIX}${BASE_ISO_NAME}" +DAPPNODE_ISO_PATH="/images/${DAPPNODE_ISO_NAME}" + +customize_debian_preseed() { + local iso_build_path=$1 + local workdir=$2 + + echo "[INFO] Customizing preseed..." + + local tmp_initrd="/tmp/makeinitrd" + local install_dir="${iso_build_path}/install.amd" + local preseeds_dir="${workdir}/iso/preseeds" + + rm -rf "${tmp_initrd}" + mkdir -p "${tmp_initrd}" + + local preseed_name="preseed.cfg" + [[ $UNATTENDED == *"true"* ]] && preseed_name="preseed_unattended.cfg" + + local preseed_file="${preseeds_dir}/${preseed_name}" + + if [ ! -f "${preseed_file}" ]; then + echo "[ERROR] Preseed file not found: ${preseed_file}" + exit 1 + fi + + # Extract the initrd into a temporary directory + gunzip -c "${install_dir}/initrd.gz" | cpio -idum -D "${tmp_initrd}" || { + echo "[ERROR] Could not decompress and extract initrd" + exit 1 + } + + # Add the preseed file to the initrd + cp "${preseed_file}" "${tmp_initrd}/preseed.cfg" || { + echo "[ERROR] Could not copy preseed file" + exit 1 + } + + # Recreate (and recompress) the initrd + (cd "${tmp_initrd}" && find . -print0 | cpio -0 -ov -H newc | gzip >"${install_dir}/initrd.gz") || { + echo "[ERROR] Could not create new initrd" + exit 1 + } + + echo "[INFO] Preseed customization complete." +} + +configure_boot_menu() { + local iso_build_path=$1 + local workdir=$2 + + local boot_dir="${workdir}/iso/boot" + + echo "[INFO] Configuring the boot menu for Dappnode..." + cp ${boot_dir}/grub.cfg ${iso_build_path}/boot/grub/grub.cfg + cp ${boot_dir}/theme_1 ${iso_build_path}/boot/grub/theme/1 + cp ${boot_dir}/isolinux.cfg ${iso_build_path}/isolinux/isolinux.cfg + cp ${boot_dir}/menu.cfg ${iso_build_path}/isolinux/menu.cfg + cp ${boot_dir}/txt.cfg ${iso_build_path}/isolinux/txt.cfg + cp ${boot_dir}/splash.png ${iso_build_path}/isolinux/splash.png +} + +generate_debian_iso() { + local mbr_path=$1 + local iso_output_path=$2 + local iso_build_path=$3 + + echo "[INFO] Generating new ISO..." + + xorriso -as mkisofs -isohybrid-mbr ${mbr_path} \ + -c /isolinux/boot.cat -b /isolinux/isolinux.bin -no-emul-boot -boot-load-size 4 \ + -boot-info-table -eltorito-alt-boot -e /boot/grub/efi.img -no-emul-boot \ + -isohybrid-gpt-basdat -o "${iso_output_path}" ${iso_build_path} } -echo "Clean old files..." -rm -rf dappnode-isoº -rm -rf DappNode-debian-* - -echo "Extracting the iso..." -xorriso -osirrox on -indev /images/${ISO_NAME} \ - -extract / dappnode-iso - -echo "Obtaining the isohdpfx.bin for hybrid ISO..." -dd if=/images/${ISO_NAME} bs=432 count=1 \ - of=dappnode-iso/isolinux/isohdpfx.bin - -cd /usr/src/app/dappnode-iso # /usr/src/app/dappnode-iso - -echo "Downloading third-party packages..." -sed '1,/^\#\!ISOBUILD/!d' /usr/src/app/scripts/dappnode_install_pre.sh >/tmp/vars.sh -# shellcheck disable=SC1091 -source /tmp/vars.sh - -echo "Creating necessary directories and copying files..." -mkdir -p /usr/src/app/dappnode-iso/dappnode -cp -r /usr/src/app/scripts /usr/src/app/dappnode-iso/dappnode -cp -r /usr/src/app/dappnode/* /usr/src/app/dappnode-iso/dappnode - -echo "Customizing preseed..." -mkdir -p /tmp/makeinitrd -cd install.amd -cp initrd.gz /tmp/makeinitrd/ -if [[ $UNATTENDED == *"true"* ]]; then - cp /usr/src/app/iso/preseeds/preseed_unattended.cfg /tmp/makeinitrd/preseed.cfg -else - cp /usr/src/app/iso/preseeds/preseed.cfg /tmp/makeinitrd/preseed.cfg -fi -cd /tmp/makeinitrd -gunzip initrd.gz -cpio -id -H newc /tmp/list -echo "preseed.cfg" >>/tmp/list -rm initrd -cpio -o -H newc initrd -gzip initrd -cd - -mv /tmp/makeinitrd/initrd.gz ./initrd.gz -cd .. - -echo "Configuring the boot menu for DappNode..." -cp /usr/src/app/iso/boot/grub.cfg boot/grub/grub.cfg -cp /usr/src/app/iso/boot/theme_1 boot/grub/theme/1 -cp /usr/src/app/iso/boot/isolinux.cfg isolinux/isolinux.cfg -cp /usr/src/app/iso/boot/menu.cfg isolinux/menu.cfg -cp /usr/src/app/iso/boot/txt.cfg isolinux/txt.cfg -cp /usr/src/app/iso/boot/splash.png isolinux/splash.png - -echo "Fix md5 sum..." -# shellcheck disable=SC2046 -md5sum $(find ! -name "md5sum.txt" ! -path "./isolinux/*" -type f) >md5sum.txt - -echo "Generating new iso..." -xorriso -as mkisofs -isohybrid-mbr isolinux/isohdpfx.bin \ - -c isolinux/boot.cat -b isolinux/isolinux.bin -no-emul-boot -boot-load-size 4 \ - -boot-info-table -eltorito-alt-boot -e boot/grub/efi.img -no-emul-boot \ - -isohybrid-gpt-basdat -o /images/DAppNode-debian-bookworm-amd64.iso . +download_iso "${BASE_ISO_PATH}" "${BASE_ISO_NAME}" "${BASE_ISO_URL}" +verify_download "${BASE_ISO_PATH}" "${BASE_ISO_SHASUM}" +clean_old_files "${ISO_BUILD_PATH}" "${DAPPNODE_ISO_PREFIX}" +extract_iso "${BASE_ISO_PATH}" "${ISO_BUILD_PATH}" +prepare_boot_process "${BASE_ISO_PATH}" "${ISO_BUILD_PATH}/isolinux/isohdpfx.bin" +add_dappnode_files_to_iso_build "${ISO_BUILD_PATH}" "${WORKDIR}" +customize_debian_preseed "${ISO_BUILD_PATH}" "${WORKDIR}" +configure_boot_menu "${ISO_BUILD_PATH}" "${WORKDIR}" +handle_checksums # TODO: Check if it fits both ubuntu and debian +generate_debian_iso "${ISO_BUILD_PATH}/isolinux/isohdpfx.bin" "${DAPPNODE_ISO_PATH}" "${ISO_BUILD_PATH}" diff --git a/iso/scripts/generate_dappnode_iso_ubuntu.sh b/iso/scripts/generate_dappnode_iso_ubuntu.sh new file mode 100755 index 00000000..c1c1a270 --- /dev/null +++ b/iso/scripts/generate_dappnode_iso_ubuntu.sh @@ -0,0 +1,74 @@ +#!/bin/bash +set -e + +SCRIPTS_DIR=$(dirname "${BASH_SOURCE[0]}") + +source ${SCRIPTS_DIR}/common_iso_generation.sh + +BASE_ISO_NAME=ubuntu-24.04.3-live-server-amd64.iso +BASE_ISO_PATH="/images/${BASE_ISO_NAME}" +BASE_ISO_URL="https://releases.ubuntu.com/24.04/${BASE_ISO_NAME}" +BASE_ISO_SHASUM="c3514bf0056180d09376462a7a1b4f213c1d6e8ea67fae5c25099c6fd3d8274b ${BASE_ISO_PATH}" + +DAPPNODE_ISO_NAME="${DAPPNODE_ISO_PREFIX}${BASE_ISO_NAME}" +DAPPNODE_ISO_PATH="/images/${DAPPNODE_ISO_NAME}" + +get_efi_partition() { + local base_iso_path=$1 + local dest_efi_path=$2 + local block_size=512 + + local efi_start=$(fdisk -l ${base_iso_path} | grep 'Appended2' | awk '{print $2}') + local efi_end=$(fdisk -l ${base_iso_path} | grep 'Appended2' | awk '{print $3}') + local efi_size=$(expr ${efi_end} - ${efi_start} + 1) + + echo "[INFO] Obtaining the EFI partition image from ${efi_start} with size ${efi_size}..." + dd if=${base_iso_path} bs=${block_size} skip="$efi_start" count="$efi_size" of=${dest_efi_path} +} + +add_ubuntu_autoinstall() { + local preseeds_dir=$1 + local iso_build_path=$2 + + echo "[INFO] Adding preseed..." + if [[ $UNATTENDED == *"true"* ]]; then + cp ${preseeds_dir}/autoinstall_unattended.yaml ${iso_build_path}/autoinstall.yaml + else + cp ${preseeds_dir}/autoinstall.yaml ${iso_build_path}/autoinstall.yaml + fi +} + +configure_boot_menu() { + echo "[INFO] Configuring the boot menu for Dappnode..." + cp -r /usr/src/app/iso/boot/ubuntu/* ${ISO_BUILD_PATH}/boot/grub/ +} + +generate_ubuntu_iso() { + local mbr_path=$1 + local efi_path=$2 + local iso_output_path=$3 + local iso_build_path=$4 + + echo "[INFO] Creating the new Ubuntu ISO..." + mkisofs \ + -rational-rock -joliet -joliet-long -full-iso9660-filenames \ + -iso-level 3 -partition_offset 16 --grub2-mbr ${mbr_path} \ + --mbr-force-bootable -append_partition 2 0xEF ${efi_path} \ + -appended_part_as_gpt \ + -eltorito-catalog /boot.catalog \ + -eltorito-boot /boot/grub/i386-pc/eltorito.img -no-emul-boot -boot-load-size 4 \ + -boot-info-table --grub2-boot-info -eltorito-alt-boot --efi-boot '--interval:appended_partition_2:all::' -no-emul-boot \ + -o ${iso_output_path} ${iso_build_path} +} + +download_iso "${BASE_ISO_PATH}" "${BASE_ISO_NAME}" "${BASE_ISO_URL}" +verify_download "${BASE_ISO_PATH}" "${BASE_ISO_SHASUM}" +clean_old_files "${ISO_BUILD_PATH}" "${DAPPNODE_ISO_PREFIX}" +extract_iso "${BASE_ISO_PATH}" "${ISO_BUILD_PATH}" +prepare_boot_process "${BASE_ISO_PATH}" "${ISO_BUILD_PATH}/mbr" +get_efi_partition "${BASE_ISO_PATH}" "${ISO_BUILD_PATH}/efi" +add_dappnode_files_to_iso_build "${ISO_BUILD_PATH}" "${WORKDIR}" +add_ubuntu_autoinstall "/usr/src/app/iso/preseeds/ubuntu" "${ISO_BUILD_PATH}" +configure_boot_menu +handle_checksums +generate_ubuntu_iso "${ISO_BUILD_PATH}/mbr" "${ISO_BUILD_PATH}/efi" "${DAPPNODE_ISO_PATH}" "${ISO_BUILD_PATH}" diff --git a/scripts/dappnode_install.sh b/scripts/dappnode_install.sh index 331a252f..fc58ff17 100755 --- a/scripts/dappnode_install.sh +++ b/scripts/dappnode_install.sh @@ -11,6 +11,7 @@ LOGS_DIR="$DAPPNODE_DIR/logs" CONTENT_HASH_FILE="${DAPPNODE_CORE_DIR}/packages-content-hash.csv" LOGFILE="${LOGS_DIR}/dappnode_install.log" MOTD_FILE="/etc/motd" +UPDATE_MOTD_DIR="/etc/update-motd.d" DAPPNODE_PROFILE="${DAPPNODE_CORE_DIR}/.dappnode_profile" # Get URLs PROFILE_BRANCH=${PROFILE_BRANCH:-"master"} @@ -56,9 +57,27 @@ is_iso_install() { fi } -# Check is port 80 in used (necessary for HTTPS) +# Check if port 80 is in use (necessary for HTTPS) +# Returns IS_PORT_USED=true only if port 80 or 443 is used by something OTHER than our HTTPS container is_port_used() { - lsof -i -P -n | grep ":80 (LISTEN)" &>/dev/null && IS_PORT_USED=true || IS_PORT_USED=false + # Check if port 80 or 443 is in use at all + local port80_used port443_used + lsof -i -P -n | grep ":80 (LISTEN)" &>/dev/null && port80_used=true || port80_used=false + lsof -i -P -n | grep ":443 (LISTEN)" &>/dev/null && port443_used=true || port443_used=false + + if [ "$port80_used" = false ] && [ "$port443_used" = false ]; then + IS_PORT_USED=false + return + fi + + # If either port is in use, check if it's our HTTPS container + if docker ps --format '{{.Names}}' 2>/dev/null | grep -q "^DAppNodeCore-https.dnp.dappnode.eth$"; then + # Port 80 or 443 is used by our HTTPS container, so we consider it "not used" for package determination + IS_PORT_USED=false + else + # Port 80 or 443 is used by something else + IS_PORT_USED=true + fi } # Determine packages to be installed @@ -186,18 +205,50 @@ dappnode_core_load() { } customMotd() { - if [ -f ${MOTD_FILE} ]; then - cat <${MOTD_FILE} - ___ _ _ _ _ -| \ /_\ _ __ _ __| \| |___ __| |___ -| |) / _ \| '_ \ '_ \ . / _ \/ _ / -_) -|___/_/ \_\ .__/ .__/_|\_\___/\__,_\___| - |_| |_| -EOF - echo -e "$WELCOME_MESSAGE" >>"$MOTD_FILE" + + generateMotdText + + if [ -d "${UPDATE_MOTD_DIR}" ]; then + # Ubuntu configuration + modifyMotdGeneration fi } +# Debian distros use /etc/motd plain text file +generateMotdText() { + # Check and create the MOTD file if it does not exist + if [ ! -f "${MOTD_FILE}" ]; then + touch "${MOTD_FILE}" + fi + + # Write the ASCII art and welcome message as plain text + cat <<'EOF' >"${MOTD_FILE}" + ___ _ + | \ __ _ _ __ _ __ _ _ ___ __| |___ + | |) / _` | '_ \ '_ \ ' \/ _ \/ _` / -_) + |___/\__,_| .__/ .__/_||_\___/\__,_\___| + |_| |_| +EOF + echo -e "$WELCOME_MESSAGE" >>"${MOTD_FILE}" +} + +# Ubuntu distros use /etc/update-motd.d/ to generate the motd +modifyMotdGeneration() { + disabled_motd_dir="${UPDATE_MOTD_DIR}/disabled" + + mkdir -p "${disabled_motd_dir}" + + # Move all the files in /etc/update-motd.d/ to /etc/update-motd.d/disabled/ + # Except for the files listed in "files_to_keep" + files_to_keep="00-header 50-landscape-sysinfo 98-reboot-required" + for file in ${UPDATE_MOTD_DIR}/*; do + base_file=$(basename "${file}") + if [ -f "${file}" ] && ! echo "${files_to_keep}" | grep -qw "${base_file}"; then + mv "${file}" "${disabled_motd_dir}/" + fi + done +} + addSwap() { # Is swap enabled? IS_SWAP=$(swapon --show | wc -l) @@ -233,12 +284,21 @@ dappnode_start() { # Show credentials to the user on login USER=$(grep 1000 /etc/passwd | cut -f 1 -d:) - [ -n "$USER" ] && PROFILE=/home/$USER/.profile || PROFILE=/root/.profile - - if ! grep -q "${DAPPNODE_PROFILE}" "$PROFILE"; then - echo "######## DAPPNODE PROFILE ########" >>$PROFILE - echo -e "source ${DAPPNODE_PROFILE}\n" >>$PROFILE - fi + [ -n "$USER" ] && USER_HOME=/home/$USER || USER_HOME=/root + + # Add profile sourcing to both .profile and .bashrc for maximum compatibility + for config_file in .profile .bashrc; do + CONFIG_PATH="$USER_HOME/$config_file" + + # Create config file if it doesn't exist + [ ! -f "$CONFIG_PATH" ] && touch "$CONFIG_PATH" + + # Add profile sourcing if not already present + if ! grep -q "${DAPPNODE_PROFILE}" "$CONFIG_PATH"; then + echo "######## DAPPNODE PROFILE ########" >>"$CONFIG_PATH" + echo -e "source ${DAPPNODE_PROFILE}\n" >>"$CONFIG_PATH" + fi + done # Remove return from profile sed -i '/return/d' $DAPPNODE_PROFILE | tee -a $LOGFILE @@ -290,6 +350,30 @@ installExtraDpkg() { fi } +# The main user needs to be added to the docker group to be able to run docker commands without sudo +# Explained in: https://docs.docker.com/engine/install/linux-postinstall/ +addUserToDockerGroup() { + # UID is provided to the first regular user created in the system + USER=$(grep 1000 "/etc/passwd" | cut -f 1 -d:) + + # If USER is not found, warn the user and return + if [ -z "$USER" ]; then + echo -e "\e[33mWARN: Default user not found. Could not add it to the docker group.\e[0m" 2>&1 | tee -a $LOGFILE + return + fi + + if groups "$USER" | grep &>/dev/null '\bdocker\b'; then + echo -e "\e[32mUser $USER is already in the docker group\e[0m" 2>&1 | tee -a $LOGFILE + return + fi + + # This step is already done in the dappnode_install_pre.sh script, + # but it's not working in the Ubuntu ISO because the late-commands in the autoinstall.yaml + # file are executed before the user is created. + usermod -aG docker "$USER" + echo -e "\e[32mUser $USER added to the docker group\e[0m" 2>&1 | tee -a $LOGFILE +} + ############################################## #### SCRIPT START #### ############################################## @@ -315,9 +399,12 @@ if [ "$ARCH" == "amd64" ]; then installSgx echo -e "\e[32mInstalling extra packages...\e[0m" 2>&1 | tee -a $LOGFILE - installExtraDpkg + installExtraDpkg # TODO: Why is this being called twice? fi +echo -e "\e[32mAdding user to docker group...\e[0m" 2>&1 | tee -a $LOGFILE +addUserToDockerGroup + echo -e "\e[32mCreating dncore_network if needed...\e[0m" 2>&1 | tee -a $LOGFILE docker network create --driver bridge --subnet 172.33.0.0/16 dncore_network 2>&1 | tee -a $LOGFILE diff --git a/scripts/dappnode_install_pre.sh b/scripts/dappnode_install_pre.sh index 2966bb1b..d6cf4739 100755 --- a/scripts/dappnode_install_pre.sh +++ b/scripts/dappnode_install_pre.sh @@ -6,9 +6,6 @@ DAPPNODE_DIR="/usr/src/dappnode" LOGS_DIR="$DAPPNODE_DIR/logs" lsb_dist="$(. /etc/os-release && echo "$ID")" - -#!ISOBUILD Do not modify, variables above imported for ISO build - detect_installation_type() { if [ -f "${DAPPNODE_DIR}/iso_install.log" ]; then LOG_FILE="${LOGS_DIR}/iso_install.log" @@ -20,25 +17,33 @@ detect_installation_type() { fi } - add_docker_repo() { apt-get update -y apt-get remove -y docker docker-engine docker.io containerd runc | tee -a $LOG_FILE - apt-get install -y ca-certificates curl gnupg lsb-release | tee -a $LOG_FILE - mkdir -p /etc/apt/keyrings && chmod -R 0755 /etc/apt/keyrings - curl -fsSL "https://download.docker.com/linux/${lsb_dist}/gpg" | gpg --dearmor --yes -o /etc/apt/keyrings/docker.gpg + + # Add Docker GPG key + apt-get install -y ca-certificates curl lsb-release | tee -a $LOG_FILE + install -m 0755 -d /etc/apt/keyrings + curl -fsSL "https://download.docker.com/linux/${lsb_dist}/gpg" -o /etc/apt/keyrings/docker.asc chmod a+r /etc/apt/keyrings/docker.gpg - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$lsb_dist $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null + + # Add the repository to APT sources + echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/$lsb_dist $(lsb_release -cs) stable" | + tee /etc/apt/sources.list.d/docker.list >/dev/null + + apt-get update -y } # DOCKER INSTALLATION install_docker() { apt-get update -y - apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin | tee -a $LOG_FILE + apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin | tee -a $LOG_FILE # Ensure xz is installed - [ -f "/usr/bin/xz" ] || ( apt-get install -y xz-utils) + [ -f "/usr/bin/xz" ] || (apt-get install -y xz-utils) + # Not working in Ubuntu ISO because the user is not created before executing late-commands USER=$(grep 1000 "/etc/passwd" | cut -f 1 -d:) [ -z "$USER" ] || usermod -aG docker "$USER" @@ -56,31 +61,31 @@ install_docker() { # DOCKER-COMPOSE FOR LEGACY SCRIPTS, SHOULD BE REMOVED EVENTUALLY alias_docker_compose() { - cat > /usr/local/bin/docker-compose</usr/local/bin/docker-compose </dev/null 2>&1 ; then + if modprobe wireguard >/dev/null 2>&1; then echo -e "\e[32m \n\n Verified wiregurd-dkms installation \n\n \e[0m" 2>&1 | tee -a $LOG_FILE else echo -e "\e[31m \n\n WARNING: wireguard kernel module is not installed, Wireguard DAppNode package might not work! \n\n \e[0m" 2>&1 | tee -a $LOG_FILE fi } -# LSOF INSTALLATION: used to scan host port 80 in use, https package installation will deppend on it +# LSOF INSTALLATION: used to scan host port 80 in use, https package installation will deppend on it install_lsof() { apt-get update -y apt-get install lsof -y | tee -a $LOG_FILE - if lsof -v >/dev/null 2>&1 ; then + if lsof -v >/dev/null 2>&1; then echo -e "\e[32m \n\n Verified lsof installation \n\n \e[0m" 2>&1 | tee -a $LOG_FILE else echo -e "\e[31m \n\n WARNING: lsof not installed, HTTPS DAppNode package might not be installed! \n\n \e[0m" 2>&1 | tee -a $LOG_FILE @@ -88,10 +93,10 @@ install_lsof() { } # IPTABLES INSTALLATION: mandatory for docker, on bullseye is not installed by default -install_iptables () { +install_iptables() { apt-get update -y apt-get install iptables -y | tee -a $LOG_FILE - if iptables -v >/dev/null 2>&1 ; then + if iptables -v >/dev/null 2>&1; then echo -e "\e[32m \n\n Verified iptables installation \n\n \e[0m" 2>&1 | tee -a $LOG_FILE else echo -e "\e[31m \n\n WARNING: iptables not installed, Docker may not work! \n\n \e[0m" 2>&1 | tee -a $LOG_FILE @@ -99,11 +104,39 @@ install_iptables () { } # HOST UPDATE -host_update () { +host_update() { apt-get update 2>&1 | tee -a $LOG_FILE apt-get -y upgrade 2>&1 | tee -a $LOG_FILE } +check_ubuntu_connectivity() { + { netplan get | grep "dhcp4: true" &>/dev/null; } || { + echo "Interfaces not found" + exit 1 + } +} + +check_debian_connectivity() { + { [ -f /etc/network/interfaces ] && grep "iface en.* inet dhcp" /etc/network/interfaces &>/dev/null; } || { + echo "Interfaces not found" + exit 1 + } +} + +add_debian_missing_interfaces() { + # shellcheck disable=SC2013 + for IFACE in $(grep "en.*" /usr/src/dappnode/hotplug); do + # shellcheck disable=SC2143 + if [[ $(grep -L "$IFACE" /etc/network/interfaces) ]]; then + { + echo "# $IFACE" + echo "allow-hotplug $IFACE" + echo "iface $IFACE inet dhcp" + } >>/etc/network/interfaces + fi + done +} + ############################################## #### SCRIPT START #### ############################################## @@ -122,8 +155,7 @@ if [ "$1" == "UPDATE" ]; then host_update 2>&1 | tee -a $LOG_FILE fi - -if find /etc/apt/ -name "*.list" -print0 | xargs --null cat | grep -q "https://download.docker.com/linux/$lsb_dist" ; then +if find /etc/apt/ -name "*.list" -print0 | xargs --null cat | grep -q "https://download.docker.com/linux/$lsb_dist"; then echo -e "\e[32m \n\n docker repo is already added \n\n \e[0m" 2>&1 | tee -a $LOG_FILE else add_docker_repo | tee -a $LOG_FILE @@ -136,15 +168,8 @@ else install_docker 2>&1 | tee -a $LOG_FILE fi -# Only install docker-compose if needed -if docker-compose -v >/dev/null 2>&1; then - echo -e "\e[32m \n\n docker-compose is already aliased \n\n \e[0m" 2>&1 | tee -a $LOG_FILE -else - alias_docker_compose 2>&1 | tee -a $LOG_FILE -fi - # Only install wireguard-dkms if needed -if modprobe wireguard >/dev/null 2>&1 ; then +if modprobe wireguard >/dev/null 2>&1; then echo -e "\e[32m \n\n wireguard-dkms is already installed \n\n \e[0m" 2>&1 | tee -a $LOG_FILE else install_wireguard_dkms 2>&1 | tee -a $LOG_FILE @@ -157,16 +182,10 @@ else install_lsof 2>&1 | tee -a $LOG_FILE fi -#Check connectivity -{ [ -f /etc/network/interfaces ] && grep "iface en.* inet dhcp" /etc/network/interfaces &>/dev/null; } || { echo "Interfaces not found"; exit 1; } - -## Add missing interfaces -if [ -f /usr/src/dappnode/hotplug ]; then - # shellcheck disable=SC2013 - for IFACE in $(grep "en.*" /usr/src/dappnode/hotplug); do - # shellcheck disable=SC2143 - if [[ $(grep -L "$IFACE" /etc/network/interfaces) ]]; then - { echo "# $IFACE"; echo "allow-hotplug $IFACE"; echo "iface $IFACE inet dhcp"; } >> /etc/network/interfaces - fi - done +## Add or Update Network Configuration Based on OS +if [ "$lsb_dist" = "ubuntu" ]; then + check_ubuntu_connectivity +else + check_debian_connectivity + add_debian_missing_interfaces fi diff --git a/scripts/dappnode_uninstall.sh b/scripts/dappnode_uninstall.sh index 9f9c68b7..66bc9f35 100755 --- a/scripts/dappnode_uninstall.sh +++ b/scripts/dappnode_uninstall.sh @@ -45,11 +45,17 @@ uninstall() { echo -e "\e[32mRemoving DAppNode directory\e[0m" rm -rf /usr/src/dappnode - # Remove profile file + # Remove profile file references from shell config files USER=$(grep 1000 /etc/passwd | cut -f 1 -d:) - [ -n "$USER" ] && PROFILE=/home/$USER/.profile || PROFILE=/root/.profile - sed -i '/######## DAPPNODE PROFILE ########/g' $PROFILE - sed -i '/.*dappnode_profile/g' $PROFILE + [ -n "$USER" ] && USER_HOME=/home/$USER || USER_HOME=/root + + for config_file in .profile .bashrc; do + CONFIG_PATH="$USER_HOME/$config_file" + if [ -f "$CONFIG_PATH" ]; then + sed -i '/######## DAPPNODE PROFILE ########/d' "$CONFIG_PATH" + sed -i '/.*dappnode_profile/d' "$CONFIG_PATH" + fi + done echo -e "\e[32mDAppNode uninstalled!\e[0m" }