184 lines
5.5 KiB
Markdown
184 lines
5.5 KiB
Markdown
<!--
|
|
# SPDX-License-Identifier: BSD-2-Clause
|
|
# SPDX-FileCopyrightText: 2023 KDE e.V.
|
|
# SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
|
|
-->
|
|
|
|
# apksigner.py - An APK Signing Service
|
|
|
|
Provides code signing for APKs.
|
|
|
|
## Prerequisites
|
|
|
|
The service needs an SFTP server for exchanging data with the clients.
|
|
|
|
Additionally, for signing APKs you need
|
|
|
|
- a key store file containing the certificate to sign the APKs with,
|
|
- a file containing the password to unlock the key store.
|
|
|
|
## Installation
|
|
|
|
### Install an Android SDK
|
|
|
|
`apksigner.py` needs the tools `apksigner`, `zipalign`, and `apkanalyzer` from
|
|
an Android SDK. The following instructions are based on the installation of
|
|
the Android SDK in KDE's Android Docker image. See below for an alternative
|
|
setup using KDE's Android Docker image.
|
|
|
|
```sh
|
|
export NDK_VERSION=22.1.7171670
|
|
export SDK_PLATFORM=android-33
|
|
export SDK_BUILD_TOOLS=30.0.3
|
|
export SDK_PACKAGES="tools platform-tools"
|
|
|
|
export ANDROID_HOME=~/android-sdk
|
|
export ANDROID_SDK_ROOT=${ANDROID_HOME}
|
|
export PATH=${ANDROID_HOME}/cmdline-tools/tools/bin:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools:${PATH}
|
|
|
|
curl -Lo /tmp/sdk-tools.zip 'https://dl.google.com/android/repository/commandlinetools-linux-6609375_latest.zip'
|
|
mkdir -p ${ANDROID_SDK_ROOT}/cmdline-tools
|
|
unzip -q /tmp/sdk-tools.zip -d ${ANDROID_SDK_ROOT}/cmdline-tools
|
|
rm -f /tmp/sdk-tools.zip
|
|
|
|
yes | sdkmanager --licenses
|
|
sdkmanager --verbose "platforms;${SDK_PLATFORM}" "build-tools;${SDK_BUILD_TOOLS}" "ndk;${NDK_VERSION}" ${SDK_PACKAGES}
|
|
sdkmanager --uninstall --verbose emulator
|
|
```
|
|
|
|
### Install the Service
|
|
|
|
Clone this repository on a server that shall provide the service,
|
|
create a virtual environment, and install the requirements.
|
|
|
|
```sh
|
|
git clone https://invent.kde.org/sysadmin/ci-notary-service.git apksigner
|
|
cd apksigner
|
|
python3 -m venv .venv
|
|
. .venv/bin/activate
|
|
pip3 install -r requirements.txt
|
|
```
|
|
|
|
### Configure the Service
|
|
|
|
Copy `apksigner.sample.ini` to `apksigner.ini` and replace the example values
|
|
with the appropriate values for your installation.
|
|
|
|
Then copy `apksigner-projects.sample.yaml` to `apksigner-projects.yaml` and
|
|
specify the settings for the projects you want to sign APKs for.
|
|
|
|
## Run the Service
|
|
|
|
Activate the virtual environment and start `apksigner.py`.
|
|
|
|
```sh
|
|
cd ~/apksigner
|
|
. .venv/bin/activate
|
|
python3 apksigner.py --id workerA --config apksigner.ini
|
|
```
|
|
|
|
where you replace `workerA` with a unique alpha-numeric ID for each instance
|
|
of `apksigner.py` you start.
|
|
|
|
## Use the Service to Sign an APK
|
|
|
|
Clone this repository on the client, create a virtual environment, and install
|
|
the requirements.
|
|
|
|
```sh
|
|
git clone https://invent.kde.org/sysadmin/ci-notary-service.git apksigner
|
|
cd apksigner
|
|
python3 -m venv .venv
|
|
. .venv/bin/activate
|
|
pip3 install -r requirements.txt
|
|
```
|
|
|
|
Copy `signapk.sample.ini` to `signapk.ini` and replace the example values
|
|
with the appropriate values for your installation. In particular, the value
|
|
for `BasePath` must match the corresponding value in the settings of the
|
|
service.
|
|
|
|
Then run `signapk.py` to sign the APK
|
|
|
|
```sh
|
|
python3 signapk.py --config signapk.ini <apk>
|
|
```
|
|
|
|
## Managing the Service with systemd
|
|
|
|
We set up the service under the user's control with a per-user systemd instance.
|
|
|
|
Copy `apksigner-user.sample.service` to `~/.config/systemd/user/apksigner-user.service`
|
|
and modify it as needed. The example uses the username as ID of the service
|
|
worker. Make sure that the ID is unique if you run multiple service workers.
|
|
|
|
Start the service with
|
|
|
|
```sh
|
|
systemctl --user start apksigner-user.service
|
|
```
|
|
|
|
If you want the service to be started automatically, then enable autostart with
|
|
|
|
```sh
|
|
systemctl --user enable apksigner-user.service
|
|
```
|
|
|
|
Note: By default, the systemd user instance and thus the user-managed services are
|
|
started after the first login of a user and killed after the last session of the
|
|
user is closed. To start the user instance right after boot, and keep the systemd
|
|
user instance running after the last session closes you have to enable lingering
|
|
for the user:
|
|
|
|
```sh
|
|
loginctl enable-linger USERNAME
|
|
```
|
|
|
|
For more details on user-managed services see https://wiki.archlinux.org/title/Systemd/User.
|
|
|
|
## Running the Service with KDE's Android Docker Image
|
|
|
|
We assume that you have already created a suitable `apksigner.ini` and
|
|
`apksigner-projects.yaml` and put them in the working copy of this repository.
|
|
|
|
The correct values for `apksigner.ini` for KDE's Android Docker image are
|
|
|
|
```ini
|
|
[ApkSigning]
|
|
KeyStore = /keystore.jks
|
|
KeyStorePass = file:/keystorepass
|
|
SDKToolsPath = /opt/android-sdk/build-tools/30.0.2
|
|
APKAnalyzerPath = /opt/android-sdk/cmdline-tools/tools/bin/apkanalyzer
|
|
|
|
[General]
|
|
ProjectSettings = apksigner-projects.yaml
|
|
```
|
|
|
|
Start a Docker container from inside the working copy of this repository
|
|
passing in the working copy, the `known_hosts` file, the files with the KeyStore
|
|
and the password to unlock the KeyStore, and the socket of the SSH agent:
|
|
|
|
```sh
|
|
docker run -it --rm \
|
|
-v $(pwd):/apksigner \
|
|
-v <known_hosts_file>:/home/user/.ssh/known_hosts \
|
|
-v <keystorefile>:/keystore.jks -v <keystorepassfile>:/keystorepass \
|
|
-v $SSH_AUTH_SOCK:/ssh-agent --env SSH_AUTH_SOCK=/ssh-agent \
|
|
invent-registry.kde.org/sysadmin/ci-images/android-qt515
|
|
```
|
|
|
|
Then inside the container run:
|
|
|
|
```sh
|
|
python3 -m venv ~/.venv
|
|
. ~/.venv/bin/activate
|
|
cd /apksigner
|
|
pip3 install -r requirements.txt
|
|
python3 apksigner.py --id workerA --config apksigner.ini
|
|
```
|
|
|
|
### Details
|
|
|
|
To protect the private SSH key we forward the SSH agent into the Docker container.
|
|
Alternatively, you can mount a file holding a private OpenSSH RSA key into the
|
|
container.
|