Compare commits
No commits in common. "1a761fd17e02adab1bff8ce8989effde7a02f9cc" and "48434138834b51f0e77f4e3995a29f7618793d39" have entirely different histories.
1a761fd17e
...
4843413883
23 changed files with 31 additions and 346 deletions
|
@ -1,6 +1,4 @@
|
||||||
FROM debian
|
FROM debian
|
||||||
|
|
||||||
RUN apt-get update; apt-get install -y python3
|
|
||||||
|
|
||||||
COPY src/machine_admin /machine_admin
|
COPY src/machine_admin /machine_admin
|
||||||
COPY src/machine_setup.py /
|
COPY src/machine_setup.py /
|
||||||
|
|
17
README.md
17
README.md
|
@ -1,13 +1,14 @@
|
||||||
This repo has some scripts and small notes for setting up development and deployment environments on different platforms.
|
This repo is a collection of ad-hoc scripts and snippets for setting up and administering a small cloud server. They are just a stepping stone toward for formal automation.
|
||||||
|
|
||||||
* [Mac Development](./doc/mac.md)
|
# Machine setup
|
||||||
* [Emacs](./doc/emacs_notes.md)
|
|
||||||
* [VM Notes](./doc/vm_notes.md)
|
|
||||||
|
|
||||||
There is another repo just for dotfiles that may be of interest also: https://codeberg.org/jmsgrogan/dotfiles
|
```sh
|
||||||
|
./setup_machine.sh
|
||||||
|
```
|
||||||
|
|
||||||
# Copyright
|
# Set up basic nginx server
|
||||||
|
|
||||||
The contents of this repo are Copyright James Grogan 2024. Software is licensed under the AGPL. See the included LICENSE file for details.
|
```sh
|
||||||
|
./setup_basic_nginx.sh
|
||||||
|
```
|
||||||
|
|
||||||
Documentation and images can be re-used under a Creative Commons CC BY-SA license: https://creativecommons.org/licenses/by-sa/4.0/
|
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
|
|
||||||
# Emacs
|
|
||||||
|
|
||||||
## Base
|
|
||||||
|
|
||||||
* `C-x C-s`: Save current file.
|
|
||||||
* `C-x C-f`: Find or open file.
|
|
||||||
|
|
||||||
## Markdown
|
|
||||||
|
|
||||||
[Module Reference](http://www.mplayerhq.hu/design7/news.html)
|
|
||||||
|
|
||||||
* `C-c C-s 1`: Insert level one heading with hash delimiter.
|
|
||||||
* `C-c C-c p`: Show preview in browser.
|
|
||||||
* `C-c C-x l`: Toggle url hiding
|
|
||||||
* `C-c C-x i`: Toggle inline images
|
|
||||||
* `C-c C-x m`: Toggle markdown visibility
|
|
||||||
* `S-Tab`: Toggle global visibility
|
|
||||||
* `TAB`: Toggle local header visibility
|
|
||||||
|
|
||||||
|
|
||||||
## Evil
|
|
||||||
|
|
||||||
[Reference card](https://michaelgoerz.net/refcards/vimqrc.pdf)
|
|
||||||
|
|
||||||
* `CTRL- {D, U, F, B}`: Scroll down/up by half or full page
|
|
||||||
* `zt zz zb`: Scroll current line to top, centre, bottom
|
|
||||||
* `0 $`: Move to beginning or end of line
|
|
||||||
* `{ }`: Move to beginning or end of paragraph
|
|
||||||
* `h l j k`: Character left, right, up, down
|
|
||||||
* `y p`: Yank (copy) or paste
|
|
||||||
|
|
||||||
## Magit
|
|
||||||
|
|
||||||
[Reference card](https://magit.vc/manual/magit-refcard.pdf)
|
|
||||||
|
|
||||||
* `CTRL-x g`: Status buffer
|
|
||||||
* `S`: Stage all changes
|
|
||||||
* `c`: Start a commit
|
|
||||||
* `C-c C-c`: Finish a commit
|
|
||||||
* `P`: Start a push
|
|
||||||
* `u`: Push to origin
|
|
||||||
|
|
||||||
## Auctex
|
|
||||||
|
|
||||||
## Org Mode ##
|
|
||||||
|
|
||||||
https://orgmode.org/worg/org-tutorials/org4beginners.html
|
|
||||||
|
|
||||||
* `TAB/S-TAB`: Fold/Unfold
|
|
||||||
* `M-up/down`: move headline up/down
|
|
||||||
* `M-left/right`: promote or demote headline
|
|
||||||
* `M-RET`: add new headline
|
|
||||||
|
|
||||||
## Treemacs ##
|
|
||||||
|
|
||||||
https://github.com/Alexander-Miller/treemacs
|
|
||||||
|
|
||||||
* `C-c C-w s`: Switch workspace
|
|
||||||
|
|
||||||
## Lsp Mode ##
|
|
||||||
|
|
||||||
### Python ###
|
|
||||||
|
|
||||||
Install language LSP server:
|
|
||||||
|
|
||||||
```
|
|
||||||
brew install python-lsp-server
|
|
||||||
```
|
|
47
doc/mac.md
47
doc/mac.md
|
@ -1,47 +0,0 @@
|
||||||
# Applications
|
|
||||||
|
|
||||||
## Multimedia
|
|
||||||
|
|
||||||
* [mplayer](http://www.mplayerhq.hu/design7/news.html)
|
|
||||||
|
|
||||||
``` shell
|
|
||||||
brew install mplayer
|
|
||||||
```
|
|
||||||
|
|
||||||
## [Gnu Stow](https://www.gnu.org/software/stow/)
|
|
||||||
|
|
||||||
Gnu Stow is a symlink manager that can be used to manager program configuration files or 'dotfiles'.
|
|
||||||
|
|
||||||
``` shell
|
|
||||||
brew install stow
|
|
||||||
```
|
|
||||||
|
|
||||||
## [Amethyst](https://ianyh.com/amethyst/)
|
|
||||||
|
|
||||||
Tiling window manager that doesn't need too heavy permissions.
|
|
||||||
|
|
||||||
# Keyboard Shortcuts
|
|
||||||
|
|
||||||
## Desktop
|
|
||||||
|
|
||||||
* `CMD-m`: Minimize focused app.
|
|
||||||
* `CMD-q`: Kill the focused app.
|
|
||||||
* `CMD-TAB`: Iterate apps in dock with arrows. Hold `OPT` when releasing to open.
|
|
||||||
* `CTRL-CMD-q`: Lock Screen
|
|
||||||
|
|
||||||
## Amethyst
|
|
||||||
|
|
||||||
* `OPT-SHIFT-k`: Move focus ccw (j for cw)
|
|
||||||
* `OPT-SHIFT-p`: Move focus to ccw screen (n for cw)
|
|
||||||
* `CTRL-OPT-SHIFT-h`: Swap screens ccw (l for c)
|
|
||||||
|
|
||||||
## Safari
|
|
||||||
|
|
||||||
* Arrow keys: Scroll
|
|
||||||
* `OPT-Arrow`: Fast scroll
|
|
||||||
* `SHIFT-CMD-]`: Next or previous tab
|
|
||||||
* `CMD-t`: New tab
|
|
||||||
* `CMD-w`: Close active tab
|
|
||||||
* `CMD-[`: Previous or next page
|
|
||||||
* `CTRL-CMD-1`: Toggle bookmark view
|
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
# Prepare Installation Images
|
|
||||||
|
|
||||||
Want fully automated installation.
|
|
||||||
|
|
||||||
## Deb
|
|
||||||
|
|
||||||
Get `netinst` ISO: https://cdimage.debian.org/debian-cd/12.5.0/amd64/iso-cd/
|
|
||||||
|
|
||||||
Deb Preseeding: https://wiki.debian.org/DebianInstaller/Preseed
|
|
||||||
|
|
||||||
ISO modification for preseeding: https://wiki.debian.org/DebianInstaller/Preseed/EditIso
|
|
||||||
|
|
||||||
|
|
||||||
# Set up KVM
|
|
||||||
|
|
||||||
```sh
|
|
||||||
```
|
|
||||||
|
|
||||||
# Install Qemu
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo apt install qemu-utils qemu-system-x86 qemu-system-gui libvirt-daemon-system virtinst
|
|
||||||
```
|
|
||||||
|
|
||||||
Add user to libvirt group
|
|
||||||
|
|
||||||
```sh
|
|
||||||
su -l
|
|
||||||
adduser $MY_USER libvirt
|
|
||||||
```
|
|
||||||
|
|
||||||
# References
|
|
||||||
|
|
||||||
KVM and Debian: https://wiki.debian.org/KVM
|
|
||||||
Qemu and Debian: https://wiki.debian.org/QEMU
|
|
||||||
Qemu and libvrt: https://rabexc.org/posts/how-to-get-started-with-libvirt-on
|
|
|
@ -1,74 +0,0 @@
|
||||||
from pathlib import Path
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from machine_admin.package_manager import SystemPackage
|
|
||||||
from machine_admin.util import run_op
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
class DebPreseeder:
|
|
||||||
|
|
||||||
def __init__(self, iso_path: Path, preseed_path: Path):
|
|
||||||
self.iso_path = iso_path
|
|
||||||
self.preseed_path = preseed_path
|
|
||||||
|
|
||||||
self.udevil = SystemPackage("udevil")
|
|
||||||
self.gzip = SystemPackage("gzip")
|
|
||||||
self.mount_point = None
|
|
||||||
|
|
||||||
def mount_iso(self):
|
|
||||||
self.udevil.check_available()
|
|
||||||
op = f"udevil mount {self.iso_path}"
|
|
||||||
logger.info(f"Mounting iso | {op}")
|
|
||||||
output = run_op(op, capture_output=True)
|
|
||||||
self.mount_point = Path(output.split(" ")[3])
|
|
||||||
|
|
||||||
def add_preseed_file(self):
|
|
||||||
|
|
||||||
# Copy ISO content to writeable location
|
|
||||||
op = f"cp -rT {self.mount_point} {self.workdir}/isofiles/"
|
|
||||||
run_op(op)
|
|
||||||
|
|
||||||
# Make path writeable
|
|
||||||
install_arch_path = f"{self.workdir}/isofiles/install.amd"
|
|
||||||
run_op(f"chmod +w -R {install_arch_path}")
|
|
||||||
|
|
||||||
# Extract initrd
|
|
||||||
self.gzip.check_available()
|
|
||||||
initrd_path = f"{install_arch_path}/initrd"
|
|
||||||
run_op(f"gunzip {initrd_path}.gz")
|
|
||||||
|
|
||||||
# Add the preseed file
|
|
||||||
op = f"echo {self.preseed_path} | cpio -H newc -o -A -F {initrd_path}"
|
|
||||||
run_op(op)
|
|
||||||
|
|
||||||
# Recompress the initrd
|
|
||||||
run_op(f"gzip {initrd_path}")
|
|
||||||
|
|
||||||
# Restore path permissions
|
|
||||||
run_op(f"chmod -w -R {install_arch_path}")
|
|
||||||
|
|
||||||
def regenerate_checksum(self):
|
|
||||||
checksum_path = f"{self.workdir}/isofiles/md5sum.txt"
|
|
||||||
run_op(f"chmod +w {checksum_path}")
|
|
||||||
|
|
||||||
def regenerate_iso(self, workdir: Path):
|
|
||||||
|
|
||||||
self.mount_iso()
|
|
||||||
|
|
||||||
self.add_preseed_file()
|
|
||||||
|
|
||||||
self.regenerate_checksum()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,6 @@ import logging
|
||||||
|
|
||||||
from .util import run_op
|
from .util import run_op
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
class UfwInterface:
|
class UfwInterface:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -11,12 +9,12 @@ class UfwInterface:
|
||||||
|
|
||||||
def enable(self):
|
def enable(self):
|
||||||
op = "ufw enable"
|
op = "ufw enable"
|
||||||
logger.info(f"Enabling ufw: {op}")
|
logging.info(f"Enabling ufw: {op}")
|
||||||
run_op(op)
|
run_op(op)
|
||||||
|
|
||||||
def allow_app(self, app_name: str):
|
def allow_app(self, app_name: str):
|
||||||
op = f"ufw allow {app_name}"
|
op = f"ufw allow {app_name}"
|
||||||
logger.info(f"Allowing ufw app: {op}")
|
logging.info(f"Allowing ufw app: {op}")
|
||||||
run_op(op)
|
run_op(op)
|
||||||
|
|
||||||
class Firewall:
|
class Firewall:
|
||||||
|
|
|
@ -21,7 +21,7 @@ class Machine:
|
||||||
self.ssh_config.restart_service()
|
self.ssh_config.restart_service()
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
self.package_manager.upgrade()
|
self.package_manager.update()
|
||||||
self.user_manager.setup_user(self.user)
|
self.user_manager.setup_user(self.user)
|
||||||
self.enable_firewall()
|
self.enable_firewall()
|
||||||
self.secure_ssh_config()
|
self.secure_ssh_config()
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .platform.distro import Distro
|
from .util import run_op
|
||||||
from .util import run_op, has_program
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
class AptInterface:
|
class AptInterface:
|
||||||
|
|
||||||
|
@ -12,35 +9,20 @@ class AptInterface:
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
op = "apt-get update"
|
op = "apt-get update"
|
||||||
logger.info(f"Updating apt: {op}")
|
logging.info(f"Updating apt: {op}")
|
||||||
run_op(op)
|
run_op(op)
|
||||||
|
|
||||||
def upgrade(self):
|
def upgrade(self):
|
||||||
op = "apt-get -y upgrade"
|
op = "apt-get -y upgrade"
|
||||||
logger.info(f"Upgrading via apt: {op}")
|
logging.info(f"Upgrading via apt: {op}")
|
||||||
run_op(op)
|
run_op(op)
|
||||||
|
|
||||||
def install_packages(self, packages: list):
|
def install_packages(self, packages: list):
|
||||||
packages_str = " ".join(packages)
|
packages_str = "".join(packages)
|
||||||
op = f"apt-get install -y {packages_str}"
|
op = f"apt-get install -y {packages_str}"
|
||||||
logger.info(f"Installing packages: {op}")
|
logging.info(f"Installing packages: {op}")
|
||||||
run_op(op)
|
run_op(op)
|
||||||
|
|
||||||
class SystemPackage:
|
|
||||||
|
|
||||||
def __init__(self, name):
|
|
||||||
self.name = name
|
|
||||||
self.package_alt_names = {}
|
|
||||||
|
|
||||||
def check_available(self):
|
|
||||||
if has_program(self.name):
|
|
||||||
return
|
|
||||||
|
|
||||||
msg = f"Program {self.name} not found"
|
|
||||||
if Distro.has_apt():
|
|
||||||
msg = f"Program not found. Install with: sudo apt-get install {self.name}"
|
|
||||||
raise RuntimeError(msg)
|
|
||||||
|
|
||||||
|
|
||||||
class PackageManager:
|
class PackageManager:
|
||||||
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
from machine_admin.util import has_program
|
|
||||||
|
|
||||||
class Distro:
|
|
||||||
|
|
||||||
def __init__(self, name):
|
|
||||||
self.name = name
|
|
||||||
self.version = ""
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def has_apt(self):
|
|
||||||
return has_program("apt-get")
|
|
|
@ -1,6 +0,0 @@
|
||||||
import sys
|
|
||||||
|
|
||||||
from .distro import Distro
|
|
||||||
|
|
||||||
def is_linux():
|
|
||||||
return sys.platform == "linux" or sys.platform == "linux2"
|
|
|
@ -3,8 +3,6 @@ import logging
|
||||||
|
|
||||||
from .util import run_op
|
from .util import run_op
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
class SshConfig:
|
class SshConfig:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -15,16 +13,14 @@ class SshConfig:
|
||||||
"UsePAM": "No"}
|
"UsePAM": "No"}
|
||||||
|
|
||||||
def sync_target_values(self):
|
def sync_target_values(self):
|
||||||
logger.info(f"Updating ssh config in: {self.config_path}")
|
logging.info(f"Updating ssh config in: {self.config_path}")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def restart_service(self):
|
def restart_service(self):
|
||||||
op = "systemctl restart ssh"
|
op = "systemctl restart ssh"
|
||||||
logger.info(f"Restarting ssh service: {op}")
|
logging.info(f"Restarting ssh service: {op}")
|
||||||
run_op(op)
|
run_op(op)
|
||||||
|
|
||||||
def copy_ssh_dir_to_user(self, username:str):
|
def copy_ssh_dir_to_user(self, username:str):
|
||||||
op = f"rsync --archive --chown={username}:{username} ~/.ssh /home/{username}"
|
op = f"rsync --archive --chown={username}:{username} ~/.ssh /home/{username}"
|
||||||
logger.info(f"Copying ssh dir to user: {op}")
|
|
||||||
run_op(op)
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
from .util import run_op
|
from .util import run_op
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
class User:
|
class User:
|
||||||
def __init__(self, name, has_sudo=False):
|
def __init__(self, name, has_sudo=False):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -10,20 +8,20 @@ class User:
|
||||||
|
|
||||||
class UserManager:
|
class UserManager:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def setup_user(self, user: User):
|
def setup_user(self, user: User):
|
||||||
self.add_user(user)
|
add_user(user)
|
||||||
if user.has_sudo:
|
if user.has_sudo:
|
||||||
self.add_user_to_sudo(user)
|
add_user_to_sudo(user)
|
||||||
|
|
||||||
def add_user(self, user: User):
|
def add_user(self, user: User):
|
||||||
op = f'adduser {user.name} --disabled-password --comment ""'
|
op = f"adduser {user.name}"
|
||||||
logger.info(f"Adding user: {op}")
|
logging.info(f"Adding user: {op}")
|
||||||
run_op(op)
|
run_op(op)
|
||||||
|
|
||||||
def add_user_to_sudo(self, user: User):
|
def add_user_to_sudo(self, user: User):
|
||||||
op = f"usermod -aG sudo {user.name}"
|
op = f"usermod -aG sudo {user.name}"
|
||||||
logger.info(f"Adding user to sudo: {op}")
|
logging.info(f"Adding user to sudo: {op}")
|
||||||
run_op(op)
|
run_op(op)
|
|
@ -1,34 +1,13 @@
|
||||||
import subprocess
|
import subprocess
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
_DRY_RUN = False
|
_DRY_RUN = False
|
||||||
|
|
||||||
def set_is_dry_run(is_dry_run: bool):
|
def set_is_dry_run(is_dry_run: bool):
|
||||||
_DRY_RUN = is_dry_run
|
_DRY_RUN = is_dry_run
|
||||||
|
|
||||||
def run_op(op: str, capture_output:bool = False,
|
def run_op(op: str):
|
||||||
cwd:str = None):
|
if _DRY_RUN:
|
||||||
if not _DRY_RUN:
|
return subprocess.run(op, shell=True)
|
||||||
if capture_output:
|
|
||||||
ret = subprocess.run(op,
|
|
||||||
shell=True,
|
|
||||||
capture_output = True,
|
|
||||||
text = True,
|
|
||||||
cwd=cwd)
|
|
||||||
ret.check_returncode()
|
|
||||||
return ret.stdout
|
|
||||||
else:
|
|
||||||
ret = subprocess.run(op, shell=True, cwd=cwd)
|
|
||||||
ret.check_returncode()
|
|
||||||
else:
|
else:
|
||||||
logger.info(f"Dry Run | {op}")
|
logging.info(f"Dry Run | {op}")
|
||||||
|
|
||||||
def has_program(program_name: str):
|
|
||||||
op = f"which {program_name}"
|
|
||||||
ret = subprocess.run(op,
|
|
||||||
shell=True,
|
|
||||||
capture_output = True,
|
|
||||||
text = True)
|
|
||||||
return len(ret.stdout)
|
|
|
@ -3,9 +3,6 @@ import logging
|
||||||
|
|
||||||
from machine_admin.user import User
|
from machine_admin.user import User
|
||||||
from machine_admin.machine import Machine
|
from machine_admin.machine import Machine
|
||||||
from machine_admin.util import set_is_dry_run
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
@ -13,17 +10,13 @@ if __name__ == "__main__":
|
||||||
prog='MachineSetup',
|
prog='MachineSetup',
|
||||||
description='Scripts for machine provisioning')
|
description='Scripts for machine provisioning')
|
||||||
|
|
||||||
parser.add_argument('username',
|
parser.add_argument('--username',
|
||||||
help="Name of the default non-root user")
|
help="Name of the default non-root user")
|
||||||
parser.add_argument('--dry_run',
|
parser.add_argument('--dry_run',
|
||||||
help="If set then don't change the system state - used for testing.",
|
help="If set then don't change the system state - used for testing.",
|
||||||
default = False)
|
default = False)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
set_is_dry_run(args.dry_run)
|
|
||||||
|
|
||||||
logging.basicConfig()
|
|
||||||
logging.getLogger().setLevel(logging.INFO)
|
|
||||||
|
|
||||||
user = User(args.username, has_sudo=True)
|
user = User(args.username, has_sudo=True)
|
||||||
machine = Machine(user)
|
machine = Machine(user)
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
for f in $(find . -name '*.HEIC');
|
|
||||||
do echo "Converting $f";
|
|
||||||
/home/jgrogan/code/ImageMagick-7.1.1-35/utilities/magick mogrify -format png -quality 100% "$f";
|
|
||||||
done;
|
|
|
@ -1,2 +0,0 @@
|
||||||
pytest
|
|
||||||
.pytest_cache
|
|
|
@ -1,11 +0,0 @@
|
||||||
from machine_admin.util import set_is_dry_run
|
|
||||||
from machine_admin.user import User
|
|
||||||
from machine_admin.machine import Machine
|
|
||||||
|
|
||||||
set_is_dry_run(True)
|
|
||||||
|
|
||||||
def test_machine_setup():
|
|
||||||
|
|
||||||
user = User("test_user", has_sudo=True)
|
|
||||||
machine = Machine(user)
|
|
||||||
machine.setup()
|
|
Loading…
Add table
Add a link
Reference in a new issue