Skip to content
Merged
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
22 changes: 22 additions & 0 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,25 @@ jobs:

- name: Provision and verify two hosts and Jump
run: tests/integration/lxd.sh images:debian/12/cloud 2

installer:
name: Bash installer on ${{ matrix.image }}
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
image: ['ubuntu:22.04', 'ubuntu:24.04', 'images:debian/12/cloud']
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Prepare LXD and terminal driver
run: |
sudo apt-get update
sudo apt-get install -y expect
sudo snap install lxd --channel=5.21/stable || sudo snap refresh lxd --channel=5.21/stable
sudo lxd waitready
sudo lxd init --auto
- name: Install from scratch through bash and verify proxy
run: tests/integration/installer.sh '${{ matrix.image }}'
2 changes: 1 addition & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,4 @@ main() {
./mega-proxy setup-local
}

main "$@"
main "$@" 3<&0 <&3
3 changes: 2 additions & 1 deletion roles/admin/tasks/account.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@
name: "{{ megaproxy_admin.user }}"
state: present
create_home: true
password: x
password: "{{ lookup('ansible.builtin.password', '/dev/null', length=64) | password_hash('sha512') }}"
update_password: on_create
groups: sudo
append: true
register: megaproxy_admin_account


- name: Create administrative SSH directory
ansible.builtin.file:
path: "{{ megaproxy_admin_account.home }}/.ssh"
Expand Down
39 changes: 39 additions & 0 deletions tests/integration/installer.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/expect -f
set timeout 1200
set guest [lindex $argv 0]
set address [lindex $argv 1]
set key [lindex $argv 2]
proc answer {prompt value} {
expect -ex $prompt
after 150
send -- $value
}
spawn sudo lxc exec $guest --mode interactive -- env TERM=xterm-256color MEGAPROXY_REF=installer-ci GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=url.file:///tmp/source.git.insteadOf GIT_CONFIG_VALUE_0=https://github.com/andre487/MegaProxyServer.git bash -c {cat /tmp/install.sh | bash}
answer {Continue?} "y\r"
answer {Inventory host name} "installer\r"
answer {Public IP address or DNS name} "$address\r"
answer {Permanent administrative user} "\025megaproxy-admin\r"
answer {Administrative SSH port} "\r"
answer {Administrative SSH key} "\r"
answer {Services on this host} " \r"
answer {HTTPS endpoint} "\r"
answer {Certificate type} "\033\[B\r"
answer {Enable active-probe resistance?} "n"
answer {Enable HTTP/3} "n"
answer {HTTPS user login} "installer-user\r"
answer {Generate a strong password?} "n"
answer {Password} "installer-test-password-only\r"
answer {Add another HTTPS user?} "n"
answer {HTTPS profile title} "\r"
answer {Ansible Vault password:} "installer-vault-test-only\r"
expect -ex {Key saved outside this server}
exec sudo lxc file pull $guest/opt/megaproxy-server/.secrets/keys/installer/admin-megaproxy-admin $key
send -- "y"
expect {
-ex {Setup verified. Client configurations} {}
eof { puts stderr "Installer exited before verification"; exit 1 }
timeout { puts stderr "Installer timed out"; exit 1 }
}
expect eof
set result [wait]
exit [lindex $result 3]
53 changes: 53 additions & 0 deletions tests/integration/installer.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
set -euo pipefail
image=${1:-ubuntu:24.04}
guest=megaproxy-ci-installer
work_dir=$(mktemp -d)
cleanup() {
sudo lxc delete --force "$guest" >/dev/null 2>&1 || true
rm -rf "$work_dir"
}
trap cleanup EXIT
sudo iptables -P FORWARD ACCEPT
sudo ip6tables -P FORWARD ACCEPT
sudo lxc launch "$image" "$guest" -c security.nesting=true -c security.privileged=true
for _ in $(seq 1 60); do
state=$(sudo lxc exec "$guest" -- systemctl is-system-running || true)
[[ "$state" == running || "$state" == degraded ]] && break
sleep 2
done
sudo lxc exec "$guest" -- sh -c "sed -i 's|http://|https://|g' /etc/apt/sources.list 2>/dev/null || true; sed -i 's|http://|https://|g' /etc/apt/sources.list.d/*.sources 2>/dev/null || true"
# Clone the exact tested revision, including PR merge commits not available as a remote branch.
git clone --bare . "$work_dir/source.git"
git --git-dir="$work_dir/source.git" update-ref refs/heads/installer-ci HEAD
sudo lxc file push -r "$work_dir/source.git" "$guest/tmp/"
sudo lxc exec "$guest" -- chown -R root:root /tmp/source.git
sudo lxc file push install.sh "$guest/tmp/install.sh"
address=$(sudo lxc list "$guest" -c 4 --format csv | awk '{print $1}')
if ! expect tests/integration/installer.exp "$guest" "$address" "$work_dir/admin-key"; then
sudo lxc exec "$guest" -- journalctl -u ssh -b --no-pager -n 40 || true
sudo lxc exec "$guest" -- namei -l /home/megaproxy-admin/.ssh/authorized_keys || true
sudo lxc exec "$guest" -- passwd -S megaproxy-admin || true
sudo lxc exec "$guest" -- find /root /home -name authorized_keys -printf "%p %u %m\n" || true
exit 1
fi
sudo chown "$(id -u):$(id -g)" "$work_dir/admin-key"
chmod 600 "$work_dir/admin-key"
sudo lxc exec "$guest" -- cat /etc/ssh/ssh_host_ed25519_key.pub | awk -v host="$address" '{print host " " $0}' > "$work_dir/known_hosts"
ssh -i "$work_dir/admin-key" -o BatchMode=yes -o IdentitiesOnly=yes -o UserKnownHostsFile="$work_dir/known_hosts" "megaproxy-admin@$address" 'sudo -n true; sudo sshd -T | grep -x "permitrootlogin no"'
sudo lxc exec "$guest" -- test -s /opt/megaproxy-server/.generated/configs/MegaProxy.json
sudo lxc exec "$guest" -- systemctl is-active gost-hashed-auth megaproxy-gost haproxy
sudo lxc exec "$guest" -- sh -c 'mkdir -p /tmp/installer-target; echo installer-ok > /tmp/installer-target/index.html'
sudo lxc exec "$guest" -- systemd-run --unit=installer-target python3 -m http.server 18082 --bind 127.0.0.1 --directory /tmp/installer-target
for _ in $(seq 1 20); do
if response=$(curl --silent --show-error --fail --max-time 10 --noproxy '' --proxy-insecure --proxy "https://$address:443" --proxy-user installer-user:installer-test-password-only http://127.0.0.1:18082/); then
[[ "$response" == installer-ok ]]
break
fi
sleep 1
done
[[ "${response:-}" == installer-ok ]]
if curl --silent --fail --max-time 10 --noproxy '' --proxy-insecure --proxy "https://$address:443" --proxy-user installer-user:wrong-password http://127.0.0.1:18082/; then
echo 'Incorrect proxy password accepted' >&2
exit 1
fi
8 changes: 8 additions & 0 deletions tests/test_local_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,11 @@ def test_installer_can_be_piped_and_is_valid_bash():
result = sp.run(['bash', '-s', '--', '--help'], input=script, text=True, capture_output=True)
assert result.returncode == 0
assert 'Interactive Debian/Ubuntu' in result.stdout


def test_installer_restores_piped_stdin_after_terminal_prompts():
invocation = Path('install.sh').read_text().splitlines()[-1]
script = 'main() { exec </dev/null; }; ' + invocation + '\nprintf "returned\\n"\n'
result = sp.run(['bash'], input=script, text=True, capture_output=True, timeout=5)
assert result.returncode == 0
assert result.stdout == 'returned\n'
Loading