Compare commits

..

1 Commits

Author SHA1 Message Date
3c6f40a62d temp! 2022-11-13 16:09:48 +00:00
7 changed files with 417 additions and 120 deletions

View File

@@ -7,29 +7,39 @@ Bootstrap an OS instance with bare essentials.
To bootstrap a macOS instance: To bootstrap a macOS instance:
```console ```console
$ curl -O https://git.infektor.net/config/bootstrap/raw/main/bootstrap-macOS.sh && chmod +x bootstrap-macOS.sh && ./bootstrap-macOS.sh $ curl -O https://code.infektor.net/config/bootstrap/raw/master/bootstrap-macOS.sh && chmod +x bootstrap-macOS.sh && ./bootstrap-macOS.sh
``` ```
To bootstrap a Debian based Linux instance: To bootstrap a Debian based Linux instance:
```console ```console
$ wget https://git.infektor.net/config/bootstrap/raw/main/bootstrap-Debian.sh && chmod +x bootstrap-Debian.sh && ./bootstrap-Debian.sh $ wget https://code.infektor.net/config/bootstrap/raw/master/bootstrap-Debian.sh && chmod +x bootstrap-Debian.sh && ./bootstrap-Debian.sh
``` ```
To bootstrap a Arch Linux based instance: To bootstrap a Arch Linux based instance:
```console ```console
$ curl -O https://git.infektor.net/config/bootstrap/raw/main/bootstrap-Arch.sh && chmod +x bootstrap-Arch.sh && ./bootstrap-Arch.sh $ curl -O https://code.infektor.net/config/bootstrap/raw/master/bootstrap-Arch.sh && chmod +x bootstrap-Arch.sh && ./bootstrap-Arch.sh
``` ```
To bootstrap a Fedora Linux instance: To bootstrap a Fedora Linux instance:
```console ```console
$ curl -O https://git.infektor.net/config/bootstrap/raw/main/bootstrap-Fedora.sh && chmod +x bootstrap-Fedora.sh && ./bootstrap-Fedora.sh $ curl -O https://code.infektor.net/config/bootstrap/raw/master/bootstrap-Fedora.sh && chmod +x bootstrap-Fedora.sh && ./bootstrap-Fedora.sh
``` ```
To bootstrap a Windows instance: To bootstrap a Windows instance:
```console ```console
$ Set-ExecutionPolicy Bypass -Scope Process -Force; Invoke-WebRequest -Uri "https://git.infektor.net/config/bootstrap/raw/main/bootstrap-Windows.ps1" -OutFile "./bootstrap-Windows.ps1"; ./bootstrap-Windows.ps1 $ bitsadmin /transfer bootstrap-Windows.cmd https://code.infektor.net/config/bootstrap/raw/master/bootstrap-Windows.cmd %cd%\bootstrap-Windows.cmd & %cd%\bootstrap-Windows.cmd
```
## Package
Install as a pip package to set SSH keys on any of GitHub, GitLab, BitBucket
Cloud, or Gogs servers:
```console
$ pip install git+https://code.infektor.net/config/bootstrap.git
$ python -c 'import bootstrap; bootstrap.set_ssh_keys()'
``` ```

View File

@@ -59,46 +59,37 @@ agree() {
} }
apt_install() { apt_install() {
sudo apt install --yes --install-recommends $1 > /dev/null sudo apt-get install --yes --install-recommends $1 > /dev/null
} }
pip_install() { pip_install() {
pip install --user $1 > /dev/null pip3 install --user $1 > /dev/null
} }
export PATH=~/.local/bin:$PATH export PATH=~/.local/bin:$PATH
agree "Update apt cache" && sudo apt update > /dev/null
agree "Upgrade apt packages" "N" && sudo apt upgrade > /dev/null
missing git && agree "Install git" && apt_install git missing git && agree "Install git" && apt_install git
if missing pip; then if missing python; then
agree "Install python-pip" && apt_install python-pip agree "Intsall python-is-python3" && apt_install python-is-python3
agree "Upgrade pip with pip" && \
sudo -H pip_install --upgrade pip > /dev/null
fi fi
missing virtualenv && agree "Install virtualenv" && pip_install virtualenv if missing pip3; then
agree "Install python-pip" && apt_install python3-pip
fi
if missing virtualenv; then
agree "Install python3-virtualenv" && apt_install python3-virtualenv
fi
if [ ! -f ~/.ssh/id_rsa ] && agree "Generate SSH key"; then if [ ! -f ~/.ssh/id_rsa ] && agree "Generate SSH key"; then
read -rp "SSH email: " email read -rp "SSH email: " email
[ ! -d ~/.ssh ] && mkdir -p ~/.ssh [ ! -d ~/.ssh ] && mkdir -p ~/.ssh
ssh-keygen -t rsa -b 4096 -C "$email" -N "" -f ~/.ssh/id_rsa ssh-keygen -t ed25519 -C "$email" -N "" -f ~/.ssh/id_rsa
fi fi
if ! missing virtualenv && agree "Set SSH keys on remote Git servers"; then missing conduit && agree "Install ansible" && \
env=$(mktemp -d) pip_install ansible
virtualenv $env > /dev/null
source $env/bin/activate
pip install git+https://code.infektor.net/config/bootstrap.git > /dev/null
python -c 'import bootstrap; bootstrap.set_ssh_keys()'
deactivate
rm -r $env
fi
missing conduit && agree "Install conduit" && \
pip_install git+ssh://git@github.com/kbenzie/conduit.git
echo "To use installed pip packages update your PATH:" echo "To use installed pip packages update your PATH:"
echo 'export PATH=~/.local/bin:$PATH' echo 'export PATH=~/.local/bin:$PATH'

193
bootstrap-Windows.cmd Normal file
View File

@@ -0,0 +1,193 @@
@echo off
setlocal
if [%1]==[/?] goto :help
echo %* | find "/?" > nul
if errorlevel 1 goto :main
:help
echo usage: %0 [/?]
echo
echo Bootstrap a macOS instance with:
echo
echo * Windows SDK - from MSDN
echo * Chocolatey - package manager
echo * Cmder, including Git - from Chocolatey
echo * Python - from Chocolatey
echo * virtualenv - from pip
echo * SSH key - from ssh-keygen
echo * GitHub public key - with SSH key
echo * GitLab public key - with SSH key
echo * BitBucket Cloud public key - with SSH key
echo * Gogs Cloud public key - with SSH key
echo * conduit - configuration manager
echo
echo optional arguments:
echo /? show this help message and exit
goto end
:main
:: Check for admin permissions
>nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system"
if errorlevel 1 (
echo Administrator privilages required!
exit /B 1
)
:: Install Windows SDK
:windows_sdk
set choice=
set /p choice="Install Windows SDK [Y/n]? "
if "%choice%"=="" goto windows_sdk_yes
if "%choice%"=="Y" goto windows_sdk_yes
if "%choice%"=="y" goto windows_sdk_yes
if "%choice%"=="N" goto windows_sdk_no
if "%choice%"=="n" goto windows_sdk_no
echo invalid value: %choice%
goto windows_sdk
:windows_sdk_yes
bitsadmin.exe /transfer "Download Windows SDK" https://go.microsoft.com/fwlink/p/?linkid=845298 %~dp0\winsdksetup.exe
if errorlevel 1 exit /B 1
echo Installing Windows SDK...
%~dp0\winsdksetup.exe /features + /q
if errorlevel 1 exit /B 1
del %~dp0\winsdksetup.exe
:windows_sdk_no
:: Install Chocolatey
:choco
set choice=
set /p choice="Install Chocolatey [Y/n]? "
if "%choice%"=="" goto choco_yes
if "%choice%"=="Y" goto choco_yes
if "%choice%"=="y" goto choco_yes
if "%choice%"=="N" goto choco_no
if "%choice%"=="n" goto choco_no
echo invalid value: %choice%
goto choco
:choco_yes
@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
if errorlevel 1 exit /B 1
:choco_no
:: Install Cmder, including Git
:git
set choice=
set /p choice="Install Cmder, including Git [Y/n]? "
if "%choice%"=="" goto git_yes
if "%choice%"=="Y" goto git_yes
if "%choice%"=="y" goto git_yes
if "%choice%"=="N" goto git_no
if "%choice%"=="n" goto git_no
echo invalid value: %choice%
goto git
:git_yes
choco install cmder --yes
if errorlevel 1 exit /B 1
:git_no
:: Install Python
:python
set choice=
set /p choice="Install Python [Y/n]? "
if "%choice%"=="" goto python_yes
if "%choice%"=="Y" goto python_yes
if "%choice%"=="y" goto python_yes
if "%choice%"=="N" goto python_no
if "%choice%"=="n" goto python_no
echo invalid value: %choice%
goto python
:python_yes
choco install python2 --yes
if errorlevel 1 exit /B 1
:python_no
:: Install virtualenv
:virtualenv
set choice=
set /p choice="Install virtualenv [Y/n]? "
if "%choice%"=="" goto virtualenv_yes
if "%choice%"=="Y" goto virtualenv_yes
if "%choice%"=="y" goto virtualenv_yes
if "%choice%"=="N" goto virtualenv_no
if "%choice%"=="n" goto virtualenv_no
echo invalid value: %choice%
goto virtualenv
:virtualenv_yes
"C:\Python27\Scripts\pip.exe" install virtualenv
if errorlevel 1 exit /B 1
:virtualenv_no
:: Generate SSH key
:ssh_key
set choice=
set /p choice="Generate SSH key [Y/n]? "
if "%choice%"=="" goto ssh_key_yes
if "%choice%"=="Y" goto ssh_key_yes
if "%choice%"=="y" goto ssh_key_yes
if "%choice%"=="N" goto ssh_key_no
if "%choice%"=="n" goto ssh_key_no
echo invalid value: %choice%
goto ssh_key
:ssh_key_yes
mkdir %USERPROFILE%\.ssh
set /P email="SSH email: "
"C:\tools\cmder\vendor\git-for-windows\usr\bin\ssh-keygen.exe" -t rsa -b 4096 -C "%email%" -N "" -f %USERPROFILE%\.ssh\id_rsa
if errorlevel 1 exit /B 1
:ssh_key_no
:: Set SSH keys on remote Git servers
:remote_ssh_keys
:: Add git to the PATH to pip can find it
set PATH=C:\tools\cmder\vendor\git-for-windows\bin;%PATH%
set choice=
set /p choice="Set SSH keys on remote Git servers [Y/n]? "
if "%choice%"=="" goto remote_ssh_keys_yes
if "%choice%"=="Y" goto remote_ssh_keys_yes
if "%choice%"=="y" goto remote_ssh_keys_yes
if "%choice%"=="N" goto remote_ssh_keys_no
if "%choice%"=="n" goto remote_ssh_keys_no
echo invalid value: %choice%
goto remote_ssh_keys
:remote_ssh_keys_yes
"C:\Python27\Scripts\virtualenv.exe" %~dp0\bootstrap_env
if errorlevel 1 exit /B 1
"%~dp0\bootstrap_env\Scripts\pip.exe" install git+https://code.infektor.net/config/bootstrap.git
if errorlevel 1 exit /B 1
"%~dp0\bootstrap_env\Scripts\python.exe" -c "import bootstrap; bootstrap.set_ssh_keys()"
if errorlevel 1 exit /B 1
:remote_ssh_keys_no
:: Install conduit
:conduit
set choice=
set /p choice="Install conduit [Y/n]? "
if "%choice%"=="" goto conduit_yes
if "%choice%"=="Y" goto conduit_yes
if "%choice%"=="y" goto conduit_yes
if "%choice%"=="N" goto conduit_no
if "%choice%"=="n" goto conduit_no
echo invalid value: %choice%
goto conduit
:conduit_yes
"C:\Python27\Scripts\pip.exe" install git+ssh://git@github.com/kbenzie/conduit.git
rmdir /Q /S "%~dp0\bootstrap_env"
:conduit_no
:remove_bootstrap
set choice=
set /p choice="Remove bootstrap-Windows.cmd [y/N]? "
if "%choice%"=="" goto remove_bootstrap_no
if "%choice%"=="Y" goto remove_bootstrap_yes
if "%choice%"=="y" goto remove_bootstrap_yes
if "%choice%"=="N" goto remove_bootstrap_no
if "%choice%"=="n" goto remove_bootstrap_no
echo invalid value: %choice%
goto remove_bootstrap
:remove_bootstrap_yes
start /b "" cmd /c del "%~f0"&exit /b
:remove_bootstrap_no
:end
endlocal

View File

@@ -1,65 +0,0 @@
$IsElevated = [bool]([Security.Principal.WindowsPrincipal] `
[Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
$Choices = '&Yes', '&No'
if (-Not $IsElevated) {
Write-Host "Not running as Administrator. Performing unprivileged actions."
# Install Scoop
Write-Host "`nInstall Scoop"
$ScoopInstaller = "$env:USERPROFILE/Downloads/ScoopInstaller.ps1"
Invoke-WebRequest -Uri https://get.scoop.sh -OutFile $ScoopInstaller
&$ScoopInstaller -ScoopDir "$env:LocalAppData/Scoop" -ScoopGlobalDir "$env:ProgramData/Scoop"
$Decision = $Host.UI.PromptForChoice('Relaunch as Administrator', 'Proceed?', $Choices, 0)
if ($Decision -eq 0) {
$Bootstrap = $MyInvocation.MyCommand.Path
Start-Process powershell.exe "-ExecutionPolicy ByPass -NoProfile -File $Bootstrap" -Verb RunAs
}
} else {
Write-Host "Running as Administrator. Performing privileged actions."
# Install 1Password
$Decision = $Host.UI.PromptForChoice('Install 1Password', 'Proceed?', $Choices, 0)
if ($Decision -eq 0) {
$1passwordInstaller = "$env:USERPROFILE/Downloads/1PasswordSetup-latest.exe"
Invoke-WebRequest -Uri "https://downloads.1password.com/win/1PasswordSetup-latest.exe" -OutFile "$1passwordInstaller"
&$1passwordInstaller
Remove-Item $1passwordInstaller
} else {
echo 'Skipping 1Password'
}
# Enable Hyper-V
$Decision = $Host.UI.PromptForChoice('Enable Hyper-V', 'Proceed?', $Choices, 0)
if ($Decision -eq 0) {
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All
} else {
echo 'Skipping Hyper-V'
}
# Enable Containters
$Decision = $Host.UI.PromptForChoice('Enable Containers', 'Proceed?', $Choices, 0)
if ($Decision -eq 0) {
Enable-WindowsOptionalFeature -Online -FeatureName Containers -All
} else {
echo 'Skipping Containers'
}
# Install Chocolatey
Write-Host "`nInstall Chocolatey"
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
# Install SSH Server
$Decision = $Host.UI.PromptForChoice('Install SSH Server', 'Proceed?', $Choices, 0)
if ($Decision -eq 0) {
choco install --yes "--package-parameters=/SSHServerFeature" openssh
Start-Service sshd
Set-Service -Name sshd -StartupType 'Automatic'
} else {
echo 'Skipping SSH Server'
}
Write-Host 'Press any key to continue...'
[System.Console]::ReadKey($true)
}

View File

@@ -1,27 +0,0 @@
#!/usr/bin/env bash
# Copy SSH keys from Windows host.
cp -r /mnt/c/Users/Benie/.ssh ~/.ssh
chown benie:benie ~/.ssh
for file in ~/.ssh/*; do
chmod 0600 $file
done
# Upgrade Debian to unstable
sudo apt-get update
sudo apt-get upgrade --yes
sudo sed 's/stretch/buster/g' -i /etc/apt/sources.list
sudo apt-get update
sudo apt-get full-upgrade --yes
sudo sed 's/buster/bullseye/g' -i /etc/apt/sources.list
sudo apt-get update
sudo apt-get full-upgrade --yes
sudo sed 's/bullseye/unstable/g' -i /etc/apt/sources.list
sudo apt-get update
sudo apt-get full-upgrade --yes
# Install ansible
sudo apt-get install ansible git python3-apt python3-pip

184
bootstrap/__init__.py Normal file
View File

@@ -0,0 +1,184 @@
"""Interactively set SSH keys on remote Git servers."""
from __future__ import print_function
from getpass import getpass
from os import environ
from os.path import join
from platform import node, system
from requests import ConnectionError, get, post
from requests.auth import HTTPBasicAuth
from requests.compat import urlparse
try:
input = raw_input
except NameError:
pass
class BootstrapError(Exception):
"""Bootstrap Exception."""
pass
def bootstrap_error(response):
"""Create a BootstrapError from a Response."""
return BootstrapError('%s %s' % (response.status_code, response.reason))
def agree(question, default='Y'):
"""Prompt user to answer a yes/no question."""
valid = {
'y': True,
'Y': True,
'yes': True,
'n': False,
'N': False,
'no': False,
'': {'Y': True,
'N': False}[default]
}
answer = input('%s [%s]? ' % (question, {'Y': 'Y/n', 'N': 'y/N'}[default]))
try:
return valid[answer]
except KeyError:
print('invalid input: %s' % answer)
return agree(question)
def get_url(service, default):
"""Get URL."""
url = input('%s URL%s: ' % (service, ' (%s)' % default if default else ''))
if url == '':
if default:
return default
else:
print('invalid input: %s' % url)
return get_url(service, default)
return url
def get_username_password(service):
"""Get username/password."""
username = input('%s username: ' % service)
password = getpass('%s password: ' % service)
return (username, password)
def get_local_key():
"""Get local SSH key."""
if system() == 'Windows':
home = environ['userprofile']
else:
home = environ['HOME']
with open(join(home, '.ssh', 'id_rsa.pub'), 'r') as key_file:
return key_file.read().rstrip()
def key_exists(keys, local_key):
"""Check if local SSH key is already set."""
for key in keys:
if local_key.startswith(key['key']):
return True
return False
def set_github_ssh_key():
"""Set GitHub SSH key."""
url = urlparse(get_url('GitHub', 'https://github.com'))
keys_url = '%s://api.%s/user/keys' % (url.scheme, url.netloc)
username, password = get_username_password('GitHub')
auth = HTTPBasicAuth(username, password)
response = get(keys_url, auth=auth)
if response.status_code != 200:
raise bootstrap_error(response)
keys = response.json()
local_key = get_local_key()
if not key_exists(keys, local_key):
response = post(
keys_url, auth=auth, json={'title': node(),
'key': local_key})
if response.status_code != 201:
raise bootstrap_error(response)
def set_gitlab_ssh_key():
"""Set GitLab SSH key."""
api_url = '%s/api/v4' % get_url('GitLab', 'https://gitlab.com')
keys_url = '%s/user/keys' % api_url
username, password = get_username_password('GitLab')
credentials = {'login': username, 'password': password}
response = get(keys_url, credentials)
if response.status_code != 200:
raise bootstrap_error(response)
keys = response.json()
local_key = get_local_key()
if not key_exists(keys, local_key):
response = post(keys_url,
credentials,
json={
'title': node(),
'key': local_key
})
if response.status_code != 201:
raise bootstrap_error(response)
def set_bitbucket_cloud_ssh_key():
"""Set BitBucket Cloud SSH key."""
username, password = get_username_password('BitBucket Cloud')
keys_url = 'https://api.bitbucket.org/1.0/users/%s/ssh-keys' % username
auth = HTTPBasicAuth(username, password)
response = get(keys_url, auth=auth)
if response.status_code != 200:
raise bootstrap_error(response)
keys = response.json()
local_key = get_local_key()
if not key_exists(keys, local_key):
response = post(
keys_url, auth=auth, data={'label': node(),
'key': local_key})
if response.status_code != 200:
raise bootstrap_error(response)
def set_gogs_ssh_key():
"""Set Gogs SSH key."""
keys_url = '%s/api/v1/user/keys' % get_url('Gogs', None)
username, password = get_username_password('Gogs')
auth = HTTPBasicAuth(username, password)
response = get(keys_url, auth=auth)
if response.status_code != 200:
raise bootstrap_error(response)
keys = response.json()
local_key = get_local_key()
if not key_exists(keys, local_key):
response = post(
keys_url, auth=auth, json={'title': node(),
'key': local_key})
if response.status_code != 201:
raise bootstrap_error(response)
def set_ssh_keys():
"""Interactively set SSH keys on remote Git servers."""
try:
for service in ['GitHub', 'GitLab', 'BitBucket Cloud', 'Gogs']:
question = 'Set %s SSH key' % service
default = 'Y'
while agree(question, default):
try:
{
'GitHub': set_github_ssh_key,
'GitLab': set_gitlab_ssh_key,
'BitBucket Cloud': set_bitbucket_cloud_ssh_key,
'Gogs': set_gogs_ssh_key,
}[service]()
if service == 'BitBucket Cloud':
break
question = 'Set another %s SSH key' % service
default = 'N'
except (BootstrapError, ConnectionError) as error:
print('error: %s' % error.message)
except KeyboardInterrupt:
exit(130)

11
setup.py Normal file
View File

@@ -0,0 +1,11 @@
"""Setup bootstrap package."""
from setuptools import find_packages, setup
setup(
name='bootstrap',
version='0.1.0',
packages=find_packages(),
install_requires=[
'requests',
])