From 5394796d1a95df63ef704e566f55c3b8d9262cce Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 10 Nov 2021 11:22:31 +0000 Subject: [PATCH] feature: add gpg (GnuPG) --- _webi/template.sh | 4 ++ gnupg/install.sh | 10 +++ gpg/README.md | 152 ++++++++++++++++++++++++++++++++++++++++++++++ gpg/install.ps1 | 6 ++ gpg/install.sh | 133 ++++++++++++++++++++++++++++++++++++++++ gpg/releases.js | 91 +++++++++++++++++++++++++++ 6 files changed, 396 insertions(+) create mode 100644 gnupg/install.sh create mode 100644 gpg/README.md create mode 100644 gpg/install.ps1 create mode 100644 gpg/install.sh create mode 100644 gpg/releases.js diff --git a/_webi/template.sh b/_webi/template.sh index adbc3e9..0ca652b 100644 --- a/_webi/template.sh +++ b/_webi/template.sh @@ -35,6 +35,7 @@ function __bootstrap_webi() { #PKG_ARCHES= #PKG_FORMATS= WEBI_UA="$(uname -a)" + WEBI_PKG_DOWNLOAD="" export WEBI_HOST ## @@ -152,6 +153,9 @@ function __bootstrap_webi() { my_dl="$HOME/Downloads/$WEBI_PKG_FILE" fi + WEBI_PKG_DOWNLOAD="${my_dl}" + export WEBI_PKG_DOWNLOAD + if [ -e "$my_dl" ]; then echo "Found $my_dl" return 0 diff --git a/gnupg/install.sh b/gnupg/install.sh new file mode 100644 index 0000000..8f4595b --- /dev/null +++ b/gnupg/install.sh @@ -0,0 +1,10 @@ +# title: GnuPG (gpg alias) +# homepage: https://webinstall.dev/gpg +# tagline: Alias for https://webinstall.dev/gpg +# alias: gpg +# description: | +# See https://webinstall.dev/gpg + +echo "'gnupg@${WEBI_TAG:-stable}' is an alias for 'gpg@${WEBI_VERSION:-}'" +WEBI_HOST=${WEBI_HOST:-"https://webinstall.dev"} +curl -fsSL "$WEBI_HOST/gpg@${WEBI_VERSION:-}" | bash diff --git a/gpg/README.md b/gpg/README.md new file mode 100644 index 0000000..89f5a02 --- /dev/null +++ b/gpg/README.md @@ -0,0 +1,152 @@ +--- +title: Gnu Privacy Guard +homepage: https://gnupg.org/ +tagline: | + GnuPG: a complete implementation of OpenPGP (RFC4880), also known as **P**retty **G**ood **P**rivacy. +--- + +### Before you start + +If `~/.gitconfig` exists and has both `name` and `email` fields, then a new gpg +key will be created after the install. Otherwise, you'll have to create one +yourself. + +## Cheat Sheet + +> Among other things, gpg is particularly useful for signing and verifying git +> commits (and emails too). + +Here we'll cover: + +- Important GPG Files & Directories +- Creating New Keys +- Listing Keys +- Signing Git Commits +- Exporting GPG Keys for GitHub +- Publishing GPG Keys to "the Blockchain" +- Running GPG Agent with launchd + +### Files + +These are the files / directories that are created and/or modified with this +install: + +```txt +~/.config/envman/PATH.env +~/.local/opt/gnupg/bin/gpg +~/.local/opt/gnupg/bin/gpg-agent +~/.local/opt/gnupg/bin/pinentry-mac.app/Contents/MacOS/pinentry-mac +~/.gnupg/gpg-agent.conf +~/Library/LaunchAgent/gpg-agent.plist +``` + +### How to create a new GPG key + +See the [Cheat Sheet](./gpg-pubkey) at [gpg-pubkey](./gpg-pubkey). + +### How to List GPG Key(s) + +```bash +gpg --list-secret-keys --keyid-format LONG +``` + +### How to configure git to sign commits + +See the [Cheat Sheet](./git-gpg-init) at [gpg-pubkey](./git-gpg-init). + +### How to Export GPG Key for GitHub + +See the [Cheat Sheet](./gpg-pubkey) at [gpg-pubkey](./gpg-pubkey). + +### How to Publish GPG Keys + +GPG is the OG "blockchain", as it were. + +If you'd like to publish your (public) key(s) to the public Key Servers for time +and all eternity, you can: + +```bash +gpg --send-keys "${MY_KEY_ID}" +``` + +(no IPFS needed 😉) + +### How to start gpg-agent with launchd + +(**Note**: this is **done for you** on install, but provided here for reference) + +It's a trick question: You can't. + +You need to use `gpg-connect-agent` instead. + +`~/Library/LaunchAgents/gpg-agent.plist`: + +```xml + + + + + Label + gpg-agent + ProgramArguments + + MY_HOME/.local/opt/gpg/bin/gpg-connect-agent + --agent-program + MY_HOME/.local/opt/gnupg/bin/gpg-agent + --homedir + MY_HOME/.gnupg/ + /bye + + + RunAtLoad + + + WorkingDirectory + MY_HOME + + StandardErrorPath + MY_HOME/.local/share/gpg-agent/var/log/gpg-agent.log + StandardOutPath + MY_HOME/.local/share/gpg-agent/var/log/gpg-agent.log + + +``` + +And then start it with launchctl: + +```bash +launchctl load -w ~/Library/LaunchAgents/gpg-agent.plist +``` + +### Troubleshooting 'gpg failed to sign the data' + +`gpg` is generally expected to be used with a Desktop client. On Linux servers +you may get this error: + +```txt +error: gpg failed to sign the data +fatal: failed to write commit object +``` + +Try to load the `gpg-agent`, set `GPG_TTY`, and then run a clearsign test. + +```bash +gpg-connect-agent /bye +export GPG_TTY=$(tty) +echo "test" | gpg --clearsign +``` + +If that works, update your `~/.bashrc`, `~/.zshrc`, and/or +`~/.config/fish/config.fish` to include the following: + +```bash +gpg-connect-agent /bye +export GPG_TTY=$(tty) +``` + +If this is failing on Mac or Windows, then `gpg-agent` is not starting as +expected on login (for Mac the above may work), and/or the `pinentry` command is +not in the PATH. + +If you just installed `gpg`, try closing and reopening your Terminal, or +possibly rebooting. diff --git a/gpg/install.ps1 b/gpg/install.ps1 new file mode 100644 index 0000000..d4dbf97 --- /dev/null +++ b/gpg/install.ps1 @@ -0,0 +1,6 @@ +#!/usr/bin/env pwsh + +echo "We don't yet have a way to automate the GnuPG Tools installer at the user level. In the meantime, try this:" +echo "" +echo " https://gnupg.org/download/#binary" +echo "" diff --git a/gpg/install.sh b/gpg/install.sh new file mode 100644 index 0000000..0a19fa4 --- /dev/null +++ b/gpg/install.sh @@ -0,0 +1,133 @@ +#!/bin/bash + +set -e +set -u + +function _install_gpg() { + if ! (uname -a | grep -i "darwin" > /dev/null); then + echo "No gpg installer for Linux yet. Try this instead:" + echo " sudo apt install -y gpg gnupg" + exit 1 + fi + + # Download the latest LTS + #curl -fsSL -o ~/Downloads/GnuPG-2.2.32.dmg 'https://sourceforge.net/projects/gpgosx/files/GnuPG-2.2.32.dmg/download' + webi_download + chmod a-w "${WEBI_PKG_DOWNLOAD}" + + # Mount the DMG in /Volumes + hdiutil detach -quiet /Volumes/GnuPG* 2> /dev/null || true + hdiutil attach -quiet -readonly "${WEBI_PKG_DOWNLOAD}" + + # Extract (completely) to ~/Downloads/GnuGP-VERSION.d + # (and detach the DMG) + rm -rf ~/Downloads/GnuPG-"${WEBI_VERSION}".d + pkgutil --expand-full /Volumes/GnuPG*/*.pkg ~/Downloads/GnuPG-"${WEBI_VERSION}".d + hdiutil detach -quiet /Volumes/GnuPG* + + # Move to ~/.local/opt/gnugp (where it belongs!) + if [[ ! -e ~/.local/opt/gnupg-"${WEBI_VERSION}" ]]; then + mv ~/Downloads/GnuPG-"${WEBI_VERSION}".d/GnuPG.pkg/Payload/ ~/.local/opt/gnupg-"${WEBI_VERSION}" + fi + + # Update symlink to latest + rm -rf ~/.local/opt/gnupg + ln -s gnupg-"${WEBI_VERSION}" ~/.local/opt/gnupg + + pathman add ~/.local/opt/gnupg/bin + export PATH="$HOME/.local/opt/gnupg/bin/:$PATH" + + # Prep for first use + mkdir -p ~/.gnupg/ + chmod 0700 ~/.gnupg/ + if [[ ! -e ~/.gnupg/gpg-agent.conf ]] || ! grep 'pinentry-program' ~/.gnupg/gpg-agent.conf; then + echo "pinentry-program $HOME/.local/opt/gnupg/bin/pinentry-mac.app/Contents/MacOS/pinentry-mac" >> ~/.gnupg/gpg-agent.conf + fi + + # Start with launchd + mkdir -p ~/Library/LaunchAgents/ + launchctl unload -w ~/Library/LaunchAgents/gpg-agent.plist 2> /dev/null || true + # TODO download and use sed to replace + echo ' + + + + Label + gpg-agent + ProgramArguments + + '"${HOME}"'/.local/opt/gpg/bin/gpg-connect-agent + --agent-program + '"${HOME}"'/.local/opt/gnupg/bin/gpg-agent + --homedir + '"${HOME}"'/.gnupg/ + /bye + + + RunAtLoad + + + WorkingDirectory + '"${HOME}"' + + StandardErrorPath + '"${HOME}"'/.local/share/gpg-agent/var/log/gpg-agent.log + StandardOutPath + '"${HOME}"'/.local/share/gpg-agent/var/log/gpg-agent.log + +' > ~/Library/LaunchAgents/gpg-agent.plist + launchctl load -w ~/Library/LaunchAgents/gpg-agent.plist + sleep 3 + + # (maybe) Create first key + if ! gpg --list-secret-keys | grep -q sec; then + _create_gpg_key + fi +} + +function _create_gpg_key() { + if [[ ! -e ~/.gitconfig ]]; then + return 0 + fi + + MY_NAME="$( + grep 'name\s*=' ~/.gitconfig | + head -n 1 | + cut -d'=' -f2 | + sed -e 's/^[\t ]*//' + )" + if [[ -z ${MY_NAME} ]]; then + return 0 + fi + + MY_EMAIL="$( + grep 'email\s*=.*@' ~/.gitconfig | + tr -d '\t ' | head -n 1 | + cut -d'=' -f2 + )" + if [[ -z ${MY_EMAIL} ]]; then + return 0 + fi + + MY_HOST="$(hostname)" + + # Without passphrase: + #gpg --batch --generate-key --pinentry=loopback --passphrase='' + + # With passphrase via macOS Keychain + gpg --batch --yes --generate-key << EOF + %echo Generating RSA 3072 key + Key-Type: RSA + Key-Length: 3072 + Subkey-Type: RSA + Subkey-Length: 3072 + Name-Real: ${MY_NAME} + Name-Comment: ${MY_HOST} + Name-Email: ${MY_EMAIL} + Expire-Date: 0 + %commit +EOF + +} + +_install_gpg diff --git a/gpg/releases.js b/gpg/releases.js new file mode 100644 index 0000000..7ab8f5f --- /dev/null +++ b/gpg/releases.js @@ -0,0 +1,91 @@ +'use strict'; + +let ltsRe = /GnuPG-(2\.2\.[\d\.]+)/; + +function createRssMatcher() { + return new RegExp( + '(https://sourceforge\\.net/projects/gpgosx/files/GnuPG-([\\d\\.]+)\\.dmg/download)', + 'g' + ); +} + +function createUrlMatcher() { + return new RegExp( + 'https://sourceforge\\.net/projects/gpgosx/files/(GnuPG-([\\d\\.]+)\\.dmg)/download', + '' + ); +} + +async function getRawReleases(request) { + let matcher = createRssMatcher(); + + let resp = await request({ + url: 'https://sourceforge.net/projects/gpgosx/rss?path=/' + }); + let links = []; + for (;;) { + let m = matcher.exec(resp.body); + if (!m) { + break; + } + links.push(m[1]); + } + return links; +} + +function transformReleases(links) { + //console.log(JSON.stringify(links, null, 2)); + //console.log(links.length); + + let matcher = createUrlMatcher(); + + let releases = links + .map(function (link) { + // strip 'go' prefix, standardize version + let isLts = ltsRe.test(link); + let parts = link.match(matcher); + if (!parts || !parts[2]) { + return null; + } + let segs = parts[2].split('.'); + let version = segs.slice(0, 3).join('.'); + if (segs.length > 3) { + version += '+' + segs.slice(3); + } + + return { + name: parts[1], + version: version, + // all go versions >= 1.0.0 are effectively LTS + lts: isLts, + channel: 'stable', + // TODO Sat, 19 Nov 2016 16:17:33 UT + date: '1970-01-01', // the world may never know + os: 'macos', + arch: 'amd64', + ext: 'dmg', + download: link + }; + }) + .filter(Boolean); + + return { + releases: releases + }; +} + +async function getAllReleases(request) { + let releases = await getRawReleases(request); + let all = transformReleases(releases); + return all; +} + +module.exports = getAllReleases; + +if (module === require.main) { + getAllReleases(require('@root/request')).then(function (all) { + all = require('../_webi/normalize.js')(all); + all.releases = all.releases.slice(0, 10000); + console.info(JSON.stringify(all, null, 2)); + }); +} -- 2.25.1