feat: add SSH login information and dotfiles status check to hello.py; include OpenSSH server tasks in Ansible configuration
Some checks failed
Nix Format Check / check-format (push) Failing after 39s
Some checks failed
Nix Format Check / check-format (push) Failing after 39s
This commit is contained in:
3
.bashrc
3
.bashrc
@ -82,6 +82,9 @@ export NIXPKGS_ALLOW_UNFREE=1
|
|||||||
# Allow insecure nixpkgs
|
# Allow insecure nixpkgs
|
||||||
export NIXPKGS_ALLOW_INSECURE=1
|
export NIXPKGS_ALLOW_INSECURE=1
|
||||||
|
|
||||||
|
# 1Password SSH Agent
|
||||||
|
export SSH_AUTH_SOCK=$HOME/.1password/agent.sock
|
||||||
|
|
||||||
# Set DOTF_HOSTNAME to the hostname from .hostname file
|
# Set DOTF_HOSTNAME to the hostname from .hostname file
|
||||||
# If this file doesn't exist, use mennos-unknown-hostname
|
# If this file doesn't exist, use mennos-unknown-hostname
|
||||||
export DOTF_HOSTNAME="mennos-unknown-hostname"
|
export DOTF_HOSTNAME="mennos-unknown-hostname"
|
||||||
|
@ -2,11 +2,129 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import subprocess
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
# Import helper functions
|
# Import helper functions
|
||||||
sys.path.append(os.path.join(os.path.expanduser("~/.dotfiles"), "bin"))
|
sys.path.append(os.path.join(os.path.expanduser("~/.dotfiles"), "bin"))
|
||||||
from helpers.functions import printfe, logo, _rainbow_color
|
from helpers.functions import printfe, logo, _rainbow_color
|
||||||
|
|
||||||
|
def get_last_ssh_login():
|
||||||
|
"""Get information about the last SSH login"""
|
||||||
|
try:
|
||||||
|
# Using lastlog to get the last login information
|
||||||
|
result = subprocess.run(['lastlog2', '-u', os.environ.get("USER", "")],
|
||||||
|
capture_output=True, text=True)
|
||||||
|
if result.returncode == 0:
|
||||||
|
lines = result.stdout.strip().split('\n')
|
||||||
|
if len(lines) >= 2: # Header line + data line
|
||||||
|
# Parse the last login line for SSH connections
|
||||||
|
parts = lines[1].split()
|
||||||
|
if len(parts) >= 7 and 'ssh' in ' '.join(parts).lower():
|
||||||
|
time_str = ' '.join(parts[3:6])
|
||||||
|
ip = parts[-1]
|
||||||
|
return f"Last SSH login: {time_str} from {ip}"
|
||||||
|
return None
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def check_dotfiles_status():
|
||||||
|
"""Check if the dotfiles repository is dirty"""
|
||||||
|
dotfiles_path = os.environ.get("DOTFILES_PATH", os.path.expanduser("~/.dotfiles"))
|
||||||
|
try:
|
||||||
|
if not os.path.isdir(os.path.join(dotfiles_path, ".git")):
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Check for git status details
|
||||||
|
status = {
|
||||||
|
'is_dirty': False,
|
||||||
|
'untracked': 0,
|
||||||
|
'modified': 0,
|
||||||
|
'staged': 0,
|
||||||
|
'commit_hash': '',
|
||||||
|
'unpushed': 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get status of files
|
||||||
|
result = subprocess.run(['git', 'status', '--porcelain'],
|
||||||
|
cwd=dotfiles_path,
|
||||||
|
capture_output=True, text=True)
|
||||||
|
|
||||||
|
if result.stdout.strip():
|
||||||
|
status['is_dirty'] = True
|
||||||
|
for line in result.stdout.splitlines():
|
||||||
|
if line.startswith('??'):
|
||||||
|
status['untracked'] += 1
|
||||||
|
if line.startswith(' M') or line.startswith('MM'):
|
||||||
|
status['modified'] += 1
|
||||||
|
if line.startswith('M ') or line.startswith('A '):
|
||||||
|
status['staged'] += 1
|
||||||
|
|
||||||
|
# Get current commit hash
|
||||||
|
result = subprocess.run(['git', 'rev-parse', '--short', 'HEAD'],
|
||||||
|
cwd=dotfiles_path,
|
||||||
|
capture_output=True, text=True)
|
||||||
|
if result.returncode == 0:
|
||||||
|
status['commit_hash'] = result.stdout.strip()
|
||||||
|
|
||||||
|
# Count unpushed commits
|
||||||
|
# Fix: Remove capture_output and set stdout explicitly
|
||||||
|
result = subprocess.run(['git', 'log', '--oneline', '@{u}..'],
|
||||||
|
cwd=dotfiles_path,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
text=True)
|
||||||
|
if result.returncode == 0:
|
||||||
|
status['unpushed'] = len(result.stdout.splitlines())
|
||||||
|
|
||||||
|
return status
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error checking dotfiles status: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_condensed_status():
|
||||||
|
"""Generate a condensed status line for trash and git status"""
|
||||||
|
status_parts = []
|
||||||
|
|
||||||
|
# Define ANSI color codes
|
||||||
|
RED = "\033[31m"
|
||||||
|
YELLOW = "\033[33m"
|
||||||
|
GREEN = "\033[32m"
|
||||||
|
BLUE = "\033[34m"
|
||||||
|
RESET = "\033[0m"
|
||||||
|
|
||||||
|
# Check trash status
|
||||||
|
trash_path = os.path.expanduser("~/.local/share/Trash/files")
|
||||||
|
try:
|
||||||
|
if os.path.exists(trash_path):
|
||||||
|
items = os.listdir(trash_path)
|
||||||
|
count = len(items)
|
||||||
|
if count > 0:
|
||||||
|
status_parts.append(f"[!] {count} file(s) in trash")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Check dotfiles status
|
||||||
|
dotfiles_status = check_dotfiles_status()
|
||||||
|
if dotfiles_status is not None:
|
||||||
|
if dotfiles_status['is_dirty']:
|
||||||
|
status_parts.append(f"{YELLOW}dotfiles is dirty{RESET}")
|
||||||
|
status_parts.append(f"{RED}[{dotfiles_status['untracked']}] untracked{RESET}")
|
||||||
|
status_parts.append(f"{YELLOW}[{dotfiles_status['modified']}] modified{RESET}")
|
||||||
|
status_parts.append(f"{GREEN}[{dotfiles_status['staged']}] staged{RESET}")
|
||||||
|
|
||||||
|
if dotfiles_status['commit_hash']:
|
||||||
|
status_parts.append(f"[{BLUE}{dotfiles_status['commit_hash']}{RESET}]")
|
||||||
|
|
||||||
|
if dotfiles_status['unpushed'] > 0:
|
||||||
|
status_parts.append(f"[!] You have {dotfiles_status['unpushed']} commit(s) to push")
|
||||||
|
else:
|
||||||
|
status_parts.append("Unable to check dotfiles status")
|
||||||
|
|
||||||
|
if status_parts:
|
||||||
|
return " - ".join(status_parts)
|
||||||
|
return None
|
||||||
|
|
||||||
def welcome():
|
def welcome():
|
||||||
"""Display welcome message with hostname and username"""
|
"""Display welcome message with hostname and username"""
|
||||||
print()
|
print()
|
||||||
@ -21,6 +139,16 @@ def welcome():
|
|||||||
print(_rainbow_color(username), end="")
|
print(_rainbow_color(username), end="")
|
||||||
print("\033[36m]\033[0m")
|
print("\033[36m]\033[0m")
|
||||||
|
|
||||||
|
# Display last SSH login info if available
|
||||||
|
ssh_login = get_last_ssh_login()
|
||||||
|
if ssh_login:
|
||||||
|
print(f"\033[33m{ssh_login}\033[0m")
|
||||||
|
|
||||||
|
# Display condensed status line
|
||||||
|
condensed_status = get_condensed_status()
|
||||||
|
if condensed_status:
|
||||||
|
print(f"\033[33m{condensed_status}\033[0m")
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
logo(continue_after=True)
|
logo(continue_after=True)
|
||||||
welcome()
|
welcome()
|
||||||
|
@ -22,6 +22,10 @@
|
|||||||
ansible.builtin.import_tasks: tasks/global/ollama.yml
|
ansible.builtin.import_tasks: tasks/global/ollama.yml
|
||||||
become: true
|
become: true
|
||||||
|
|
||||||
|
- name: Include OpenSSH Server tasks
|
||||||
|
ansible.builtin.import_tasks: tasks/global/openssh-server.yml
|
||||||
|
become: true
|
||||||
|
|
||||||
- name: Ensure common packages are installed
|
- name: Ensure common packages are installed
|
||||||
ansible.builtin.package:
|
ansible.builtin.package:
|
||||||
name:
|
name:
|
||||||
@ -31,9 +35,11 @@
|
|||||||
- trash-cli
|
- trash-cli
|
||||||
- curl
|
- curl
|
||||||
- wget
|
- wget
|
||||||
|
# Python is used for the dotfiles CLI tools
|
||||||
- python3
|
- python3
|
||||||
- python3-pip
|
- python3-pip
|
||||||
- python3-venv
|
- python3-venv
|
||||||
|
- lastlog2 # Used for displaying last login information
|
||||||
state: present
|
state: present
|
||||||
become: true
|
become: true
|
||||||
|
|
||||||
|
22
config/ansible/tasks/global/openssh-server.yml
Normal file
22
config/ansible/tasks/global/openssh-server.yml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
- name: Ensure openssh-server is installed
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: openssh-server
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Ensure SSH server configuration is proper
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: templates/sshd_config.j2
|
||||||
|
dest: /etc/ssh/sshd_config
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: '0644'
|
||||||
|
validate: '/usr/sbin/sshd -t -f %s'
|
||||||
|
notify: Restart SSH service
|
||||||
|
register: ssh_config
|
||||||
|
|
||||||
|
- name: Ensure SSH service is enabled and running
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: ssh
|
||||||
|
state: started
|
||||||
|
enabled: true
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
# This is the sshd server system-wide configuration file. See
|
# This is the sshd server system-wide configuration file. See
|
||||||
# sshd_config(5) for more information.
|
# sshd_config(5) for more information.
|
||||||
|
|
||||||
@ -11,6 +10,15 @@
|
|||||||
|
|
||||||
Include /etc/ssh/sshd_config.d/*.conf
|
Include /etc/ssh/sshd_config.d/*.conf
|
||||||
|
|
||||||
|
# When systemd socket activation is used (the default), the socket
|
||||||
|
# configuration must be re-generated after changing Port, AddressFamily, or
|
||||||
|
# ListenAddress.
|
||||||
|
#
|
||||||
|
# For changes to take effect, run:
|
||||||
|
#
|
||||||
|
# systemctl daemon-reload
|
||||||
|
# systemctl restart ssh.socket
|
||||||
|
#
|
||||||
Port 400
|
Port 400
|
||||||
#AddressFamily any
|
#AddressFamily any
|
||||||
#ListenAddress 0.0.0.0
|
#ListenAddress 0.0.0.0
|
||||||
@ -25,12 +33,12 @@ Port 400
|
|||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
#SyslogFacility AUTH
|
#SyslogFacility AUTH
|
||||||
#LogLevel INFO
|
LogLevel INFO
|
||||||
|
|
||||||
# Authentication:
|
# Authentication:
|
||||||
|
|
||||||
#LoginGraceTime 2m
|
#LoginGraceTime 2m
|
||||||
PermitRootLogin prohibit-password
|
PermitRootLogin no
|
||||||
#StrictModes yes
|
#StrictModes yes
|
||||||
#MaxAuthTries 6
|
#MaxAuthTries 6
|
||||||
#MaxSessions 10
|
#MaxSessions 10
|
||||||
@ -54,8 +62,8 @@ PubkeyAuthentication yes
|
|||||||
#IgnoreRhosts yes
|
#IgnoreRhosts yes
|
||||||
|
|
||||||
# To disable tunneled clear text passwords, change to no here!
|
# To disable tunneled clear text passwords, change to no here!
|
||||||
PasswordAuthentication yes
|
PasswordAuthentication no
|
||||||
PermitEmptyPasswords no
|
#PermitEmptyPasswords no
|
||||||
|
|
||||||
# Change to yes to enable challenge-response passwords (beware issues with
|
# Change to yes to enable challenge-response passwords (beware issues with
|
||||||
# some PAM modules and threads)
|
# some PAM modules and threads)
|
||||||
@ -84,8 +92,8 @@ KbdInteractiveAuthentication no
|
|||||||
# and KbdInteractiveAuthentication to 'no'.
|
# and KbdInteractiveAuthentication to 'no'.
|
||||||
UsePAM yes
|
UsePAM yes
|
||||||
|
|
||||||
AllowAgentForwarding yes
|
#AllowAgentForwarding yes
|
||||||
AllowTcpForwarding yes
|
#AllowTcpForwarding yes
|
||||||
#GatewayPorts no
|
#GatewayPorts no
|
||||||
X11Forwarding yes
|
X11Forwarding yes
|
||||||
#X11DisplayOffset 10
|
#X11DisplayOffset 10
|
||||||
@ -96,8 +104,8 @@ PrintMotd no
|
|||||||
#TCPKeepAlive yes
|
#TCPKeepAlive yes
|
||||||
#PermitUserEnvironment no
|
#PermitUserEnvironment no
|
||||||
#Compression delayed
|
#Compression delayed
|
||||||
#ClientAliveInterval 0
|
ClientAliveInterval 300
|
||||||
#ClientAliveCountMax 3
|
ClientAliveCountMax 2
|
||||||
#UseDNS no
|
#UseDNS no
|
||||||
#PidFile /run/sshd.pid
|
#PidFile /run/sshd.pid
|
||||||
#MaxStartups 10:30:100
|
#MaxStartups 10:30:100
|
||||||
@ -116,7 +124,7 @@ Subsystem sftp /usr/lib/openssh/sftp-server
|
|||||||
|
|
||||||
# Example of overriding settings on a per-user basis
|
# Example of overriding settings on a per-user basis
|
||||||
#Match User anoncvs
|
#Match User anoncvs
|
||||||
# X11Forwarding no
|
X11Forwarding yes
|
||||||
# AllowTcpForwarding no
|
AllowTcpForwarding yes
|
||||||
# PermitTTY no
|
# PermitTTY no
|
||||||
# ForceCommand cvs server
|
# ForceCommand cvs server
|
6
config/home-manager/flake.lock
generated
6
config/home-manager/flake.lock
generated
@ -39,11 +39,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-unstable": {
|
"nixpkgs-unstable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1741379970,
|
"lastModified": 1741513245,
|
||||||
"narHash": "sha256-Wh7esNh7G24qYleLvgOSY/7HlDUzWaL/n4qzlBePpiw=",
|
"narHash": "sha256-7rTAMNTY1xoBwz0h7ZMtEcd8LELk9R5TzBPoHuhNSCk=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "36fd87baa9083f34f7f5027900b62ee6d09b1f2f",
|
"rev": "e3e32b642a31e6714ec1b712de8c91a3352ce7e1",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
Reference in New Issue
Block a user