Skip to content

Commit

Permalink
add release workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
ewilken committed Jan 3, 2024
1 parent 85bd413 commit 165b457
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 3 deletions.
83 changes: 83 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: Release

on:
release:
types: [published]

jobs:
build:
name: Build app bundle and upload it to the release
runs-on: macos-latest

steps:
- name: Checkout
uses: actions/checkout@v4
- name: Select latest stable Xcode version
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
# https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificate-on-macos-runners-for-xcode-development#creating-secrets-for-your-certificate-and-provisioning-profile
# https://defn.io/2023/09/22/distributing-mac-apps-with-github-actions
- name: Install the Apple certificate and provisioning profile
env:
# exported from Xcode
# base64 -i ID_CERTIFICATE.p12 > ID_CERTIFICATE_BASE64
ID_CERTIFICATE_BASE64: ${{ secrets.ID_CERTIFICATE_BASE64 }}
# openssl rand -hex 32 > ID_CERTIFICATE_PASSWORD
ID_CERTIFICATE_PASSWORD: ${{ secrets.ID_CERTIFICATE_PASSWORD }}
# exported from Xcode
# base64 -i BUILD_CERTIFICATE.p12 > BUILD_CERTIFICATE_BASE64
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
# openssl rand -hex 32 > BUILD_CERTIFICATE_PASSWORD
BUILD_CERTIFICATE_PASSWORD: ${{ secrets.BUILD_CERTIFICATE_PASSWORD }}
# openssl rand -hex 32 > KEYCHAIN_PASSWORD
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
# create variables
ID_CERTIFICATE_PATH=$RUNNER_TEMP/id_certificate.p12
BUILD_CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
# import certificates from secrets
echo -n "$ID_CERTIFICATE_BASE64" | base64 --decode -o $ID_CERTIFICATE_PATH
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $BUILD_CERTIFICATE_PATH
# create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# import certificate to keychain
security import $ID_CERTIFICATE_PATH -P "$ID_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security import $BUILD_CERTIFICATE_PATH -P "$BUILD_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
- name: Build app
run: |
mkdir -p dist
xcodebuild \
archive \
-project 'Nautik Helper.xcodeproj'/ \
-scheme 'Nautik Helper' \
-configuration Release \
-destination 'generic/platform=macOS' \
-archivePath 'dist/Nautik Helper.xcarchive'
-allowProvisioningUpdates
xcodebuild \
-exportArchive \
-archivePath 'dist/Nautik Helper.xcarchive' \
-exportOptionsPlist 'Nautik Helper/ExportOptions.plist' \
-exportPath dist/ \
-allowProvisioningUpdates
cd dist
zip -r helper-${{ github.ref }}.zip 'Nautik Helper.app'
cd ..
- name: Upload app bundle to release
uses: svenstaro/upload-release-action@v2
with:
file: dist/helper-${{ github.ref }}.zip
asset_name: helper-${{ github.ref }}.zip
tag: ${{ github.ref }}
overwrite: true
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
<img src="/Nautik Helper/Assets.xcassets/AppIcon.appiconset/app_icon_helper_1024.png" align="right" width="128" height="128" />
<img src="/Nautik Helper/Assets.xcassets/AppIcon.appiconset/app_icon_helper_256.png" align="right" width="128" height="128" />

# Nautik Helper

[![CI](https://github.com/nautik-io/helper/actions/workflows/ci.yml/badge.svg)](https://github.com/nautik-io/helper/actions/workflows/ci.yml)
[![Release](https://github.com/nautik-io/helper/actions/workflows/release.yml/badge.svg)](https://github.com/nautik-io/helper/actions/workflows/release.yml)

Helper app to bridge Nautik's compatibility with the full kubeconfig spec, including exec plugins, without having the main app exit the sandbox.

This exists because kubeconfig auth scenarios are diverse and a lot of setups (including the standard kubeconfig files exported out of of GCP, AWS, Azure and DigitalOcean) rely on calling executables in a user's PATH for ephemeral Kubernetes credential issuing. Calling arbitrary executables isn't possible as a sandboxed Mac app, but exiting the sandbox just to call local executables would prevent us from distributing the app via the Mac App Store. So all things exec are happening in this helper app that can optionally be used by people whose auth scenarios aren't implemented in the main app and also can easily be audited, since the whole exec thing might be suspicious to some people. Credential data is shared between the helper app and the main app using the system keychain.
This exists because kubeconfig auth scenarios are diverse and a lot of setups (including the standard kubeconfig files exported out of of GCP, AWS, Azure and DigitalOcean) rely on calling executables in a user's PATH for ephemeral Kubernetes credential issuing. Calling arbitrary executables isn't possible as a sandboxed Mac app, but exiting the sandbox just to call local executables would prevent us from distributing the app via the Mac App Store. So all things exec are happening in this helper app that can optionally be used by people whose auth scenarios aren't implemented in the main app and can also easily be audited, since the whole exec thing might be suspicious to some people. Additionally, there's support for token and certificate files. Credential data is shared between the helper app and the main app using the system keychain.

If you can prevent using this, don't use it and try using auth scenarios implemented in the main app instead, as they will work better across platforms without having to have a Mac running just to refresh credentials.

## How the helper app works

The helper app allows you to add kubeconfig files to keep track of via a file picker UI. The file paths of tracked kubeconfig files are stored on the [`UserDefaults`](https://developer.apple.com/documentation/foundation/userdefaults) in a serialized array of paths under the key `kubeconfigs`. Every valid cluster on the tracked kubeconfig files can be added to (or removed from) the keychain with a checkbox UI on the helper app's settings.

<img width="344" alt="The Nautik Helper app's main window, showing two Kubernetes clusters under management." src="https://github.com/nautik-io/helper/assets/19625431/33637cf9-c285-418e-9164-dd15f41ccf2f">

Clusters on the keychain are continuously evaluated by the helper app. If a cluster's corresponding kubeconfig entry includes `client-certificate`, `client-key` or `token-file` keys, the file contents of the corresponding files are copied into the `client-certificate-data`, `client-key-data` and `token` fields of the stored cluster to have them be consumed by the main app on macOS, iOS or iPadOS. If a cluster's corresponding kubeconfig entry includes an `exec` value, the helper app spawns a process as the user running the helper app, executing the corresponding exec-based authentication plugin and copying its output into the `client-certificate-data`, `client-key-data` and `token` fields of the stored cluster to have them be consumed by the main app.

<img width="472" alt="The Nautik Helper app's cluster settings window, showing two kubeconfig files with one Kubernetes cluster inside of each." src="https://github.com/nautik-io/helper/assets/19625431/698b8691-5eb1-4b4c-b86b-8bc36da28e43">

Support for the `auth-provider` field on the kubeconfig is currently unimplemented. But support for the `oidc` auth provider is planned to be included on the main app at a later point. Contributions to the helper app extending the range of supported auth methods are very welcome.

The helper app evaluates the refresh timestamps on stored clusters every 10 seconds, refreshes `client-certificate`, `client-key` and `token-file` values every 15 minutes and re-evaluates `exec` plugin outputs 5 minutes before they expire.

If you can prevent using this, don't use it and try to use the auth scenarios implemented in the main app instead, as they will work better across platforms without having to have a Mac running just to refresh credentials.
To allow to be run on multiple Macs and user accounts in parallel without interference, the helper app stores the device UUID and user of the system it was added on with the cluster.

## Installation

Expand Down

0 comments on commit 165b457

Please sign in to comment.