OpenMiInk/third_party/ci-notary-service/doc/apksigner.md
Hellings b8c8ff98e2 Hi
2026-10-16 09:05:56 -04:00

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.