Compare commits
1 Commits
step35/595
...
step35/429
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
666ff65ac6 |
@@ -46,6 +46,10 @@
|
|||||||
- role: cron_manager
|
- role: cron_manager
|
||||||
tags: [cron, schedule]
|
tags: [cron, schedule]
|
||||||
|
|
||||||
|
- role: emacs
|
||||||
|
when: wizard_name == "Bezalel"
|
||||||
|
tags: [emacs, daemon]
|
||||||
|
|
||||||
post_tasks:
|
post_tasks:
|
||||||
- name: "Final validation — scan for banned providers"
|
- name: "Final validation — scan for banned providers"
|
||||||
shell: |
|
shell: |
|
||||||
|
|||||||
26
ansible/roles/emacs/README.md
Normal file
26
ansible/roles/emacs/README.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# emacs Ansible Role
|
||||||
|
|
||||||
|
Installs and configures a shared Emacs daemon for the Bezalel VPS.
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
- Ensures `fleet` group exists
|
||||||
|
- Installs Emacs (if needed)
|
||||||
|
- Creates `/srv/fleet/emacs/sockets` (mode 2775, group fleet)
|
||||||
|
- Creates `/srv/fleet/logs` (mode 2775)
|
||||||
|
- Touches `/srv/fleet/logs/emacs-audit.log` (mode 0664)
|
||||||
|
- Deploys `/root/.emacs.d/init.el` with:
|
||||||
|
- Socket dir set to shared location
|
||||||
|
- Group-accessible socket (#o660)
|
||||||
|
- Audit hooks for file saves and client connections
|
||||||
|
- Deploys `/usr/local/bin/emacsclient-bezalel` wrapper that logs caller identity
|
||||||
|
- Deploys systemd unit `emacs-bezalel.service` and starts it
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The role is automatically included site-wide when `wizard_name == 'bezalel'`.
|
||||||
|
|
||||||
|
## Acceptance
|
||||||
|
|
||||||
|
- `emacsclient -s bezalel -e "(+ 1 1)"` should print `2` and return exit 0
|
||||||
|
- `/srv/fleet/logs/emacs-audit.log` should contain user=... entries for each client and file write
|
||||||
8
ansible/roles/emacs/handlers/main.yml
Normal file
8
ansible/roles/emacs/handlers/main.yml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
# Handlers for the emacs role
|
||||||
|
|
||||||
|
- name: Restart emacs-bezalel
|
||||||
|
systemd:
|
||||||
|
name: emacs-bezalel
|
||||||
|
state: restarted
|
||||||
|
daemon_reload: yes
|
||||||
92
ansible/roles/emacs/tasks/main.yml
Normal file
92
ansible/roles/emacs/tasks/main.yml
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
---
|
||||||
|
# =============================================================================
|
||||||
|
# emacs — Shared Emacs daemon with multi-user socket and audit trail
|
||||||
|
# =============================================================================
|
||||||
|
# Deploys and configures Emacs as a daemon on the VPS with:
|
||||||
|
# - Named socket "bezalel" in /srv/fleet/emacs/sockets
|
||||||
|
# - Group-writable socket for fleet access
|
||||||
|
# - Audit logging for connections and file writes
|
||||||
|
# - Per-user identity via EMACS_USER env
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- name: "Ensure fleet group exists (idempotent)"
|
||||||
|
group:
|
||||||
|
name: fleet
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: "Ensure Emacs package installed"
|
||||||
|
package:
|
||||||
|
name: emacs
|
||||||
|
state: present
|
||||||
|
when: machine_type == 'vps'
|
||||||
|
ignore_errors: true # not critical if emacs already present
|
||||||
|
|
||||||
|
- name: "Create Emacs socket directory"
|
||||||
|
file:
|
||||||
|
path: /srv/fleet/emacs/sockets
|
||||||
|
state: directory
|
||||||
|
mode: '2775'
|
||||||
|
group: fleet
|
||||||
|
owner: root
|
||||||
|
recurse: true
|
||||||
|
|
||||||
|
- name: "Create Emacs logs directory"
|
||||||
|
file:
|
||||||
|
path: /srv/fleet/logs
|
||||||
|
state: directory
|
||||||
|
mode: '2775'
|
||||||
|
group: fleet
|
||||||
|
owner: root
|
||||||
|
recurse: true
|
||||||
|
|
||||||
|
- name: "Ensure audit log file exists with group write"
|
||||||
|
file:
|
||||||
|
path: /srv/fleet/logs/emacs-audit.log
|
||||||
|
state: touch
|
||||||
|
mode: '0664'
|
||||||
|
group: fleet
|
||||||
|
owner: root
|
||||||
|
|
||||||
|
- name: "Create root .emacs.d directory if missing"
|
||||||
|
file:
|
||||||
|
path: /root/.emacs.d
|
||||||
|
state: directory
|
||||||
|
mode: '0755'
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
|
||||||
|
- name: "Deploy Emacs init.el configuration"
|
||||||
|
template:
|
||||||
|
src: init.el.j2
|
||||||
|
dest: /root/.emacs.d/init.el
|
||||||
|
mode: '0644'
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
notify: Restart emacs-bezalel
|
||||||
|
|
||||||
|
- name: "Deploy emacsclient wrapper script"
|
||||||
|
template:
|
||||||
|
src: client-wrapper.sh.j2
|
||||||
|
dest: /usr/local/bin/emacsclient-bezalel
|
||||||
|
mode: '0755'
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
|
||||||
|
- name: "Deploy systemd unit for Emacs daemon"
|
||||||
|
template:
|
||||||
|
src: emacs-bezalel.service.j2
|
||||||
|
dest: /etc/systemd/system/emacs-bezalel.service
|
||||||
|
mode: '0644'
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
notify: Restart emacs-bezalel
|
||||||
|
|
||||||
|
- name: "Reload systemd to pick up new unit"
|
||||||
|
systemd:
|
||||||
|
daemon_reload: yes
|
||||||
|
|
||||||
|
- name: "Ensure Emacs daemon is enabled and started"
|
||||||
|
systemd:
|
||||||
|
name: emacs-bezalel
|
||||||
|
enabled: true
|
||||||
|
state: started
|
||||||
19
ansible/roles/emacs/templates/client-wrapper.sh.j2
Normal file
19
ansible/roles/emacs/templates/client-wrapper.sh.j2
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Emacs client wrapper for Bezalel — logs identity and delegates to emacsclient
|
||||||
|
# This script must be installed setuid root? No — just group-executable.
|
||||||
|
# AUDIT log: /srv/fleet/logs/emacs-audit.log
|
||||||
|
|
||||||
|
AUDIT_LOG="/srv/fleet/logs/emacs-audit.log"
|
||||||
|
USERNAME="$(whoami)"
|
||||||
|
TIMESTAMP="$(date -Iseconds)"
|
||||||
|
|
||||||
|
# Log the client connection attempt (pre-connect)
|
||||||
|
echo "${TIMESTAMP} user=${USERNAME} action=emacsclient-connect args=$*" >> "${AUDIT_LOG}"
|
||||||
|
|
||||||
|
# Pass the username into the Emacs server environment so it can
|
||||||
|
# appear in server-side audit entries as well.
|
||||||
|
export EMACS_USER="${USERNAME}"
|
||||||
|
|
||||||
|
# Delegate to the real emacsclient, preserving all args.
|
||||||
|
# The socket name 'bezalel' is configured by server-name in init.el.
|
||||||
|
exec /usr/bin/emacsclient -s bezalel "$@"
|
||||||
16
ansible/roles/emacs/templates/emacs-bezalel.service.j2
Normal file
16
ansible/roles/emacs/templates/emacs-bezalel.service.j2
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Emacs daemon for Bezalel (shared fleet service)
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=forking
|
||||||
|
ExecStart=/usr/bin/emacs --daemon=bezalel
|
||||||
|
ExecStop=/usr/bin/emacsclient -s bezalel -e "(progn (setq kill-emacs-hook nil) (kill-emacs))"
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10
|
||||||
|
# Ensure HOME is set correctly
|
||||||
|
Environment=HOME=/root
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
53
ansible/roles/emacs/templates/init.el.j2
Normal file
53
ansible/roles/emacs/templates/init.el.j2
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
;; Emacs init for Bezalel daemon — shared fleet service
|
||||||
|
;; Managed by Ansible. DO NOT EDIT MANUALLY.
|
||||||
|
;; Last updated: {{ ansible_date_time.iso8601 }}
|
||||||
|
|
||||||
|
;; -------------------- Socket configuration --------------------
|
||||||
|
;; Use a shared, group-accessible socket directory
|
||||||
|
(setq server-socket-dir "/srv/fleet/emacs/sockets/")
|
||||||
|
|
||||||
|
;; Ensure the socket directory exists with setgid so new files inherit group 'fleet'
|
||||||
|
(let ((dir server-socket-dir))
|
||||||
|
(unless (file-directory-p dir)
|
||||||
|
(make-directory dir t))
|
||||||
|
(set-file-modes dir #o2775))
|
||||||
|
|
||||||
|
;; Socket file permissions: group read/write so all fleet members can connect
|
||||||
|
(setq server-socket-permissions #o660)
|
||||||
|
|
||||||
|
;; Name this daemon instance "bezalel" (matches systemd --daemon=bezalel)
|
||||||
|
(setq server-name "bezalel")
|
||||||
|
|
||||||
|
;; -------------------- Audit trail --------------------
|
||||||
|
(defvar emacs-audit-log "/srv/fleet/logs/emacs-audit.log"
|
||||||
|
"Path to the fleet audit log for Emacs operations.")
|
||||||
|
|
||||||
|
(defun emacs-audit-log-entry (operation file &optional user)
|
||||||
|
"Append an audit entry for OPERATION on FILE by USER.
|
||||||
|
USER defaults to EMACS_USER env var (set by client wrapper) or
|
||||||
|
the daemon's user-login-name."
|
||||||
|
(let ((user (or user
|
||||||
|
(getenv "EMACS_USER")
|
||||||
|
(user-login-name))))
|
||||||
|
(with-temp-buffer
|
||||||
|
(insert (format "%s user=%s operation=%s file=%s\n"
|
||||||
|
(format-time-string "%Y-%m-%dT%H:%M:%S%z")
|
||||||
|
user
|
||||||
|
operation
|
||||||
|
(expand-file-name file)))
|
||||||
|
(write-region (point-min) (point-max) emacs-audit-log 'append))))
|
||||||
|
|
||||||
|
;; Log every file buffer save
|
||||||
|
(add-hook 'after-save-hook
|
||||||
|
(lambda ()
|
||||||
|
(when buffer-file-name
|
||||||
|
(emacs-audit-log-entry "write" buffer-file-name))))
|
||||||
|
|
||||||
|
;; Also log client connections (best-effort via EMACS_USER)
|
||||||
|
(add-hook 'server-after-make-frame-hook
|
||||||
|
(lambda ()
|
||||||
|
(let ((client (frame-parameter nil 'client)))
|
||||||
|
(when client
|
||||||
|
(emacs-audit-log-entry "connect" "emacsclient" (getenv "EMACS_USER"))))))
|
||||||
|
|
||||||
|
(provide 'init)
|
||||||
Reference in New Issue
Block a user