From 49507535481a2224e5112e52e6debb4b59b4d99c Mon Sep 17 00:00:00 2001 From: Dennis Schwerdel Date: Sun, 14 Feb 2021 13:27:26 +0100 Subject: [PATCH] Static builds that can install themselves --- .cargo/config | 15 +++++++++++ .github/workflows/package.yml | 4 +-- Cargo.toml | 1 + builder/Dockerfile-deb | 15 ++++++++--- builder/Dockerfile-rpm | 2 +- builder/build.sh | 48 ++++++++++++++++++++++++---------- src/config.rs | 8 ++++++ src/installer.rs | 49 +++++++++++++++++++++++++++++++++++ src/main.rs | 9 +++++++ 9 files changed, 130 insertions(+), 21 deletions(-) create mode 100644 src/installer.rs diff --git a/.cargo/config b/.cargo/config index f55a0b7..de148a5 100644 --- a/.cargo/config +++ b/.cargo/config @@ -3,12 +3,27 @@ linker = "arm-linux-gnueabihf-gcc" objcopy = { path = "arm-linux-gnueabihf-objcopy" } strip = { path = "arm-linux-gnueabihf-strip" } +[target.armv7-unknown-linux-musleabihf] +linker = "arm-linux-gnueabihf-gcc" +objcopy = { path = "arm-linux-gnueabihf-objcopy" } +strip = { path = "arm-linux-gnueabihf-strip" } + [target.arm-unknown-linux-gnueabihf] linker = "arm-linux-gnueabihf-gcc" objcopy = { path = "arm-linux-gnueabihf-objcopy" } strip = { path = "arm-linux-gnueabihf-strip" } +[target.arm-unknown-linux-musleabihf] +linker = "arm-linux-gnueabihf-gcc" +objcopy = { path = "arm-linux-gnueabihf-objcopy" } +strip = { path = "arm-linux-gnueabihf-strip" } + [target.aarch64-unknown-linux-gnu] linker = "aarch64-linux-gnu-gcc" objcopy = { path = "aarch64-linux-gnu-objcopy" } strip = { path = "aarch64-linux-gnu-strip" } + +[target.aarch64-unknown-linux-musl] +linker = "aarch64-linux-gnu-gcc" +objcopy = { path = "aarch64-linux-gnu-objcopy" } +strip = { path = "aarch64-linux-gnu-strip" } \ No newline at end of file diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index f9be6a7..84a459f 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -11,7 +11,7 @@ jobs: - name: Run builder uses: ./.github/actions/build-deb with: - rust: '1.49.0' + rust: '1.50.0' - name: Archive artifacts uses: actions/upload-artifact@v1 with: @@ -31,7 +31,7 @@ jobs: - name: Run builder uses: ./.github/actions/build-rpm with: - rust: '1.49.0' + rust: '1.50.0' - name: Archive artifacts uses: actions/upload-artifact@v1 with: diff --git a/Cargo.toml b/Cargo.toml index 25267e2..4aae4da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ default = ["nat", "websocket", "wizard"] nat = ["igd"] websocket = ["tungstenite", "url"] wizard = ["dialoguer"] +installer = [] [[bench]] name = "criterion" diff --git a/builder/Dockerfile-deb b/builder/Dockerfile-deb index 38b22ef..7eb73d0 100644 --- a/builder/Dockerfile-deb +++ b/builder/Dockerfile-deb @@ -11,6 +11,7 @@ RUN apt-get update \ libc6-dev-i386 \ gcc-5-multilib \ asciidoctor \ + musl musl-dev musl-tools \ && rm -rf /var/cache/dpkg RUN ln -s asm-generic/ /usr/include/asm @@ -19,7 +20,7 @@ RUN useradd -ms /bin/bash user USER user WORKDIR /home/user -ENV RUST=1.49.0 +ENV RUST=1.50.0 RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain ${RUST} @@ -27,12 +28,18 @@ ENV PATH=/home/user/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin RUN rustup target add i686-unknown-linux-gnu \ && rustup target add armv7-unknown-linux-gnueabihf \ - && rustup target add aarch64-unknown-linux-gnu + && rustup target add aarch64-unknown-linux-gnu \ + && rustup target add x86_64-unknown-linux-musl \ + && rustup target add i686-unknown-linux-musl \ + && rustup target add armv7-unknown-linux-musleabihf \ + && rustup target add aarch64-unknown-linux-musl RUN cargo install cargo-deb \ && rm -rf /home/user/.cargo/{git,tmp,registry} +ENV UPX_VER=3.96 +RUN curl https://github.com/upx/upx/releases/download/v${UPX_VER}/upx-${UPX_VER}-amd64_linux.tar.xz -Lf | tar -xJ --strip-components=1 -C /home/user/.cargo/bin + VOLUME /home/user/.cargo/tmp VOLUME /home/user/.cargo/git -VOLUME /home/user/.cargo/registry - +VOLUME /home/user/.cargo/registry \ No newline at end of file diff --git a/builder/Dockerfile-rpm b/builder/Dockerfile-rpm index 9a90a28..5a0d255 100644 --- a/builder/Dockerfile-rpm +++ b/builder/Dockerfile-rpm @@ -7,7 +7,7 @@ RUN useradd -ms /bin/bash user USER user WORKDIR /home/user -ENV RUST=1.49.0 +ENV RUST=1.50.0 RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain ${RUST} diff --git a/builder/build.sh b/builder/build.sh index 43d7425..548c832 100755 --- a/builder/build.sh +++ b/builder/build.sh @@ -32,24 +32,44 @@ mkdir -p ../dist docker build --rm -f=Dockerfile-deb -t vpncloud-builder-deb . # x86_64 deb -docker_cmd deb 'cd code && cargo deb' -cp $CACHE/deb/target/debian/vpncloud_${DEB_VERSION}_amd64.deb ../dist/vpncloud_${DEB_VERSION}_amd64.deb +if ! [ -f ../dist/vpncloud_${DEB_VERSION}_amd64.deb ]; then + docker_cmd deb 'cd code && cargo deb' + cp $CACHE/deb/target/debian/vpncloud_${DEB_VERSION}_amd64.deb ../dist/vpncloud_${DEB_VERSION}_amd64.deb +fi -# i386 deb -docker_cmd deb 'cd code && cargo deb --target i686-unknown-linux-gnu' -cp $CACHE/deb/target/i686-unknown-linux-gnu/debian/vpncloud_${DEB_VERSION}_i386.deb ../dist/vpncloud_${DEB_VERSION}_i386.deb +build_deb() { + ARCH=$1 + TARGET=$2 + if ! [ -f ../dist/vpncloud_${DEB_VERSION}_${ARCH}.deb ]; then + docker_cmd deb "cd code && cargo deb --target ${TARGET}" + cp $CACHE/deb/target/${TARGET}/debian/vpncloud_${DEB_VERSION}_${ARCH}.deb ../dist/vpncloud_${DEB_VERSION}_${ARCH}.deb + fi +} -# arm7hf deb -docker_cmd deb 'cd code && cargo deb --target armv7-unknown-linux-gnueabihf' -cp $CACHE/deb/target/armv7-unknown-linux-gnueabihf/debian/vpncloud_${DEB_VERSION}_armhf.deb ../dist/vpncloud_${DEB_VERSION}_armhf.deb +build_deb i386 i686-unknown-linux-gnu +build_deb armhf armv7-unknown-linux-gnueabihf +build_deb arm64 aarch64-unknown-linux-gnu -# aarch64 deb -docker_cmd deb 'cd code && cargo deb --target aarch64-unknown-linux-gnu' -cp $CACHE/deb/target/aarch64-unknown-linux-gnu/debian/vpncloud_${DEB_VERSION}_arm64.deb ../dist/vpncloud_${DEB_VERSION}_arm64.deb + +build_static() { + ARCH=$1 + TARGET=$2 + if ! [ -f ../dist/vpncloud_${VERSION}_static_${ARCH} ]; then + docker_cmd deb "cd code && cargo build --release --features installer --target ${TARGET} && upx --lzma target/${TARGET}/release/vpncloud" + cp $CACHE/deb/target/${TARGET}/release/vpncloud ../dist/vpncloud_${VERSION}_static_${ARCH} + fi +} + +build_static amd64 x86_64-unknown-linux-musl +build_static i386 i686-unknown-linux-gnu +build_static armhf armv7-unknown-linux-musleabihf +#build_static arm64 aarch64-unknown-linux-musl # fails for unknown reason docker build --rm -f=Dockerfile-rpm -t vpncloud-builder-rpm . -# x86_64 rpm -docker_cmd rpm 'cd code && cargo rpm build' -cp $CACHE/rpm/target/release/rpmbuild/RPMS/x86_64/vpncloud-${RPM_VERSION}.x86_64.rpm ../dist/vpncloud_${RPM_VERSION}.x86_64.rpm +if ! [ -f ../dist/vpncloud_${RPM_VERSION}.x86_64.rpm ]; then + # x86_64 rpm + docker_cmd rpm 'cd code && cargo rpm build' + cp $CACHE/rpm/target/release/rpmbuild/RPMS/x86_64/vpncloud-${RPM_VERSION}.x86_64.rpm ../dist/vpncloud_${RPM_VERSION}.x86_64.rpm +fi \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index 63f3b03..5a9c983 100644 --- a/src/config.rs +++ b/src/config.rs @@ -573,6 +573,14 @@ pub enum Command { /// Name of the network #[structopt(short, long)] name: Option + }, + + /// Install required utility files + #[cfg(feature = "installer")] + Install { + /// Remove installed files again + #[structopt(long)] + uninstall: bool } } diff --git a/src/installer.rs b/src/installer.rs new file mode 100644 index 0000000..2882a3c --- /dev/null +++ b/src/installer.rs @@ -0,0 +1,49 @@ +use crate::error::Error; +use std::{ + env, + fs::{self, File}, + io::Write, + os::unix::fs::PermissionsExt +}; + +const MANPAGE: &[u8] = include_bytes!("../target/vpncloud.1.gz"); +const SERVICE_FILE: &[u8] = include_bytes!("../assets/vpncloud@.service"); +const WS_PROXY_SERVICE_FILE: &[u8] = include_bytes!("../assets/vpncloud-wsproxy.service"); +const EXAMPLE_CONFIG: &[u8] = include_bytes!("../assets/example.net.disabled"); + +pub fn install() -> Result<(), Error> { + env::current_exe() + .and_then(|p| fs::copy(p, "/usr/bin/vpncloud")) + .map_err(|e| Error::FileIo("Failed to copy binary", e))?; + fs::set_permissions("/usr/bin/vpncloud", fs::Permissions::from_mode(755)) + .map_err(|e| Error::FileIo("Failed to set permissions for binary", e))?; + fs::create_dir_all("/etc/vpncloud").map_err(|e| Error::FileIo("Failed to create config folder", e))?; + fs::set_permissions("/etc/vpncloud", fs::Permissions::from_mode(600)) + .map_err(|e| Error::FileIo("Failed to set permissions for config folder", e))?; + File::create("/etc/vpncloud/example.net.disabled") + .and_then(|mut f| f.write_all(EXAMPLE_CONFIG)) + .map_err(|e| Error::FileIo("Failed to create example config", e))?; + File::create("/usr/share/man/man1/vpncloud.1.gz") + .and_then(|mut f| f.write_all(MANPAGE)) + .map_err(|e| Error::FileIo("Failed to create manpage", e))?; + File::create("/lib/systemd/system/vpncloud@.service") + .and_then(|mut f| f.write_all(SERVICE_FILE)) + .map_err(|e| Error::FileIo("Failed to create service file", e))?; + File::create("/lib/systemd/system/vpncloud-wsproxy.service") + .and_then(|mut f| f.write_all(WS_PROXY_SERVICE_FILE)) + .map_err(|e| Error::FileIo("Failed to create wsporxy service file", e))?; + info!("Install successful"); + Ok(()) +} + +pub fn uninstall() -> Result<(), Error> { + fs::remove_file("/etc/vpncloud/example.net.disabled").map_err(|e| Error::FileIo("Failed to remove binary", e))?; + fs::remove_file("/usr/share/man/man1/vpncloud.1.gz").map_err(|e| Error::FileIo("Failed to remove manpage", e))?; + fs::remove_file("/lib/systemd/system/vpncloud@.service") + .map_err(|e| Error::FileIo("Failed to remove service file", e))?; + fs::remove_file("/lib/systemd/system/vpncloud-wsproxy.service") + .map_err(|e| Error::FileIo("Failed to remove wsproxy service file", e))?; + fs::remove_file("/usr/bin/vpncloud").map_err(|e| Error::FileIo("Failed to remove binary", e))?; + info!("Uninstall successful"); + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index f0c93b6..d252bd3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,6 +29,7 @@ pub mod traffic; pub mod types; #[cfg(feature = "wizard")] pub mod wizard; #[cfg(feature = "websocket")] pub mod wsproxy; +#[cfg(feature = "installer")] pub mod installer; use structopt::StructOpt; @@ -282,6 +283,14 @@ fn main() { Command::Config { name } => { try_fail!(wizard::configure(name), "Wizard failed: {}"); } + #[cfg(feature = "installer")] + Command::Install { uninstall } => { + if uninstall { + try_fail!(installer::uninstall(), "Uninstall failed: {}"); + } else { + try_fail!(installer::install(), "Install failed: {}"); + } + } } return }