...
This commit is contained in:
@@ -2,6 +2,80 @@
|
||||
|
||||
This module provides functionality for managing DNS records in Redis for use with CoreDNS. It supports various DNS record types and provides a simple interface for adding and managing DNS records.
|
||||
|
||||
|
||||
## Heroscript Examples
|
||||
|
||||
The following examples demonstrate how to define DNS records using heroscript actions:
|
||||
|
||||
### A Record
|
||||
```
|
||||
!!dns.a_record
|
||||
sub_domain: 'host1'
|
||||
ip: '1.2.3.4'
|
||||
ttl: 300
|
||||
```
|
||||
|
||||
### AAAA Record
|
||||
```
|
||||
!!dns.aaaa_record
|
||||
sub_domain: 'host1'
|
||||
ip: '2001:db8::1'
|
||||
ttl: 300
|
||||
```
|
||||
|
||||
### MX Record
|
||||
```
|
||||
!!dns.mx_record
|
||||
sub_domain: '*'
|
||||
host: 'mail.example.com'
|
||||
preference: 10
|
||||
ttl: 300
|
||||
```
|
||||
|
||||
### TXT Record
|
||||
```
|
||||
!!dns.txt_record
|
||||
sub_domain: '*'
|
||||
text: 'v=spf1 mx ~all'
|
||||
ttl: 300
|
||||
```
|
||||
|
||||
### SRV Record
|
||||
```
|
||||
!!dns.srv_record
|
||||
service: 'ssh'
|
||||
protocol: 'tcp'
|
||||
host: 'host1'
|
||||
target: 'sip.example.com'
|
||||
port: 5060
|
||||
priority: 10
|
||||
weight: 100
|
||||
ttl: 300
|
||||
```
|
||||
|
||||
### NS Record
|
||||
```
|
||||
!!dns.ns_record
|
||||
sub_domain: '@'
|
||||
host: 'ns1.example.com'
|
||||
ttl: 300
|
||||
```
|
||||
|
||||
### SOA Record
|
||||
```
|
||||
!!dns.soa_record
|
||||
mbox: 'hostmaster.example.com'
|
||||
ns: 'ns1.example.com'
|
||||
refresh: 44
|
||||
retry: 55
|
||||
expire: 66
|
||||
minttl: 100
|
||||
ttl: 300
|
||||
```
|
||||
|
||||
|
||||
## v
|
||||
|
||||
```v
|
||||
import freeflowuniverse.herolib.osal.core.coredns
|
||||
|
||||
@@ -93,3 +167,5 @@ SOARecord {
|
||||
ttl int // Default: 300
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -64,56 +64,6 @@ cat > "$PROFILE_SCRIPT" <<'EOF'
|
||||
SSH_AGENT_PID_FILE="$HOME/.ssh/agent.pid"
|
||||
SSH_AUTH_SOCK_FILE="$HOME/.ssh/agent.sock"
|
||||
|
||||
# Function to start ssh-agent
|
||||
start_ssh_agent() {
|
||||
mkdir -p "$HOME/.ssh"
|
||||
chmod 700 "$HOME/.ssh"
|
||||
|
||||
# Start ssh-agent and save connection info
|
||||
ssh-agent -s > "$SSH_AGENT_PID_FILE"
|
||||
source "$SSH_AGENT_PID_FILE"
|
||||
|
||||
# Save socket path for future sessions
|
||||
echo "$SSH_AUTH_SOCK" > "$SSH_AUTH_SOCK_FILE"
|
||||
|
||||
# Load all private keys found in ~/.ssh
|
||||
if [ -d "$HOME/.ssh" ]; then
|
||||
for KEY in "$HOME"/.ssh/*; do
|
||||
if [ -f "$KEY" ] && [ ! "${KEY##*.}" = "pub" ] && grep -q "PRIVATE KEY" "$KEY" 2>/dev/null; then
|
||||
ssh-add "$KEY" >/dev/null 2>&1 && echo "🔑 Loaded key: $(basename $KEY)"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if ssh-agent is running
|
||||
if [ -f "$SSH_AGENT_PID_FILE" ]; then
|
||||
source "$SSH_AGENT_PID_FILE" >/dev/null 2>&1
|
||||
# Test if agent is responsive
|
||||
if ! ssh-add -l >/dev/null 2>&1; then
|
||||
start_ssh_agent
|
||||
else
|
||||
# Agent is running, restore socket path
|
||||
if [ -f "$SSH_AUTH_SOCK_FILE" ]; then
|
||||
export SSH_AUTH_SOCK=$(cat "$SSH_AUTH_SOCK_FILE")
|
||||
fi
|
||||
fi
|
||||
else
|
||||
start_ssh_agent
|
||||
fi
|
||||
|
||||
# For interactive shells
|
||||
if [[ $- == *i* ]]; then
|
||||
echo "🔑 SSH Agent ready at $SSH_AUTH_SOCK"
|
||||
# Show loaded keys
|
||||
KEY_COUNT=$(ssh-add -l 2>/dev/null | wc -l)
|
||||
if [ "$KEY_COUNT" -gt 0 ]; then
|
||||
echo "🔑 $KEY_COUNT SSH key(s) loaded"
|
||||
fi
|
||||
fi
|
||||
|
||||
EOF
|
||||
|
||||
chown "$NEWUSER":"$NEWUSER" "$PROFILE_SCRIPT"
|
||||
chmod 644 "$PROFILE_SCRIPT"
|
||||
|
||||
|
||||
211
lib/osal/sshagent/agent.v
Normal file
211
lib/osal/sshagent/agent.v
Normal file
@@ -0,0 +1,211 @@
|
||||
module sshagent
|
||||
|
||||
// Check if SSH agent is properly configured and all is good
|
||||
fn agent_check(mut agent SSHAgent) ! {
|
||||
console.print_header('SSH Agent Check')
|
||||
|
||||
// Ensure single agent is running
|
||||
agent.ensure_single_agent()!
|
||||
|
||||
// Get diagnostics
|
||||
diag := agent.diagnostics()
|
||||
|
||||
for key, value in diag {
|
||||
console.print_item('${key}: ${value}')
|
||||
}
|
||||
|
||||
// Verify agent is responsive
|
||||
if !agent.is_agent_responsive() {
|
||||
return error('SSH agent is not responsive')
|
||||
}
|
||||
|
||||
// Load all existing keys from ~/.ssh that aren't loaded yet
|
||||
agent.init()!
|
||||
|
||||
console.print_green('✓ SSH Agent is properly configured and running')
|
||||
|
||||
// Show loaded keys
|
||||
loaded_keys := agent.keys_loaded()!
|
||||
console.print_item('Loaded keys: ${loaded_keys.len}')
|
||||
for key in loaded_keys {
|
||||
console.print_item(' - ${key.name} (${key.cat})')
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new SSH key
|
||||
fn sshkey_create(mut agent SSHAgent, name string, passphrase string) ! {
|
||||
console.print_header('Creating SSH key: ${name}')
|
||||
|
||||
// Check if key already exists
|
||||
if agent.exists(name: name) {
|
||||
console.print_debug('SSH key "${name}" already exists')
|
||||
return
|
||||
}
|
||||
|
||||
// Generate new key
|
||||
mut key := agent.generate(name, passphrase)!
|
||||
console.print_green('✓ SSH key "${name}" created successfully')
|
||||
|
||||
// Automatically load the key
|
||||
key.load()!
|
||||
console.print_green('✓ SSH key "${name}" loaded into agent')
|
||||
}
|
||||
|
||||
// Delete an SSH key
|
||||
fn sshkey_delete(mut agent SSHAgent, name string) ! {
|
||||
console.print_header('Deleting SSH key: ${name}')
|
||||
|
||||
// Check if key exists
|
||||
mut key := agent.get(name: name) or {
|
||||
console.print_debug('SSH key "${name}" does not exist')
|
||||
return
|
||||
}
|
||||
|
||||
// Get key paths before deletion
|
||||
key_path := key.keypath() or {
|
||||
console.print_debug('Private key path not available for "${name}"')
|
||||
key.keypath_pub() or { return } // Just to trigger the path lookup
|
||||
}
|
||||
key_pub_path := key.keypath_pub() or {
|
||||
console.print_debug('Public key path not available for "${name}"')
|
||||
return
|
||||
}
|
||||
|
||||
// Remove from agent if loaded (temporarily disabled due to reset_ssh panic)
|
||||
// if key.loaded {
|
||||
// key.forget()!
|
||||
// }
|
||||
|
||||
// Delete key files
|
||||
if key_path.exists() {
|
||||
key_path.delete()!
|
||||
console.print_debug('Deleted private key: ${key_path.path}')
|
||||
}
|
||||
if key_pub_path.exists() {
|
||||
key_pub_path.delete()!
|
||||
console.print_debug('Deleted public key: ${key_pub_path.path}')
|
||||
}
|
||||
|
||||
// Reinitialize agent to update key list
|
||||
agent.init()!
|
||||
|
||||
console.print_green('✓ SSH key "${name}" deleted successfully')
|
||||
}
|
||||
|
||||
// Load SSH key into agent
|
||||
fn sshkey_load(mut agent SSHAgent, name string) ! {
|
||||
console.print_header('Loading SSH key: ${name}')
|
||||
|
||||
mut key := agent.get(name: name) or {
|
||||
return error('SSH key "${name}" not found')
|
||||
}
|
||||
|
||||
if key.loaded {
|
||||
console.print_debug('SSH key "${name}" is already loaded')
|
||||
return
|
||||
}
|
||||
|
||||
key.load()!
|
||||
console.print_green('✓ SSH key "${name}" loaded into agent')
|
||||
}
|
||||
|
||||
// Check if SSH key is valid
|
||||
fn sshkey_check(mut agent SSHAgent, name string) ! {
|
||||
console.print_header('Checking SSH key: ${name}')
|
||||
|
||||
mut key := agent.get(name: name) or {
|
||||
return error('SSH key "${name}" not found')
|
||||
}
|
||||
|
||||
// Check if key files exist
|
||||
key_path := key.keypath() or {
|
||||
return error('Private key file not found for "${name}"')
|
||||
}
|
||||
|
||||
key_pub_path := key.keypath_pub() or {
|
||||
return error('Public key file not found for "${name}"')
|
||||
}
|
||||
|
||||
if !key_path.exists() {
|
||||
return error('Private key file does not exist: ${key_path.path}')
|
||||
}
|
||||
|
||||
if !key_pub_path.exists() {
|
||||
return error('Public key file does not exist: ${key_pub_path.path}')
|
||||
}
|
||||
|
||||
// Verify key can be loaded (if not already loaded)
|
||||
if !key.loaded {
|
||||
// Test load without actually loading (since forget is disabled)
|
||||
key_content := key_path.read()!
|
||||
if !key_content.contains('PRIVATE KEY') {
|
||||
return error('Invalid private key format in "${name}"')
|
||||
}
|
||||
}
|
||||
|
||||
console.print_item('Key type: ${key.cat}')
|
||||
console.print_item('Loaded: ${key.loaded}')
|
||||
console.print_item('Email: ${key.email}')
|
||||
console.print_item('Private key: ${key_path.path}')
|
||||
console.print_item('Public key: ${key_pub_path.path}')
|
||||
|
||||
console.print_green('✓ SSH key "${name}" is valid')
|
||||
}
|
||||
|
||||
// Copy private key to remote node
|
||||
fn remote_copy(mut agent SSHAgent, node_addr string, key_name string) ! {
|
||||
console.print_header('Copying SSH key "${key_name}" to ${node_addr}')
|
||||
|
||||
// Get the key
|
||||
mut key := agent.get(name: key_name) or {
|
||||
return error('SSH key "${key_name}" not found')
|
||||
}
|
||||
|
||||
// Create builder node
|
||||
mut b := builder.new()!
|
||||
mut node := b.node_new(ipaddr: node_addr)!
|
||||
|
||||
// Get private key content
|
||||
key_path := key.keypath()!
|
||||
if !key_path.exists() {
|
||||
return error('Private key file not found: ${key_path.path}')
|
||||
}
|
||||
|
||||
private_key_content := key_path.read()!
|
||||
|
||||
// Get home directory on remote
|
||||
home_dir := node.environ_get()!['HOME'] or {
|
||||
return error('Could not determine HOME directory on remote node')
|
||||
}
|
||||
|
||||
remote_ssh_dir := '${home_dir}/.ssh'
|
||||
remote_key_path := '${remote_ssh_dir}/${key_name}'
|
||||
|
||||
// Ensure .ssh directory exists with correct permissions
|
||||
node.exec_silent('mkdir -p ${remote_ssh_dir}')!
|
||||
node.exec_silent('chmod 700 ${remote_ssh_dir}')!
|
||||
|
||||
// Copy private key to remote
|
||||
node.file_write(remote_key_path, private_key_content)!
|
||||
node.exec_silent('chmod 600 ${remote_key_path}')!
|
||||
|
||||
// Generate public key on remote
|
||||
node.exec_silent('ssh-keygen -y -f ${remote_key_path} > ${remote_key_path}.pub')!
|
||||
node.exec_silent('chmod 644 ${remote_key_path}.pub')!
|
||||
|
||||
console.print_green('✓ SSH key "${key_name}" copied to ${node_addr}')
|
||||
}
|
||||
|
||||
// Add public key to authorized_keys on remote node
|
||||
fn remote_auth(mut agent SSHAgent, node_addr string, key_name string) ! {
|
||||
console.print_header('Adding SSH key "${key_name}" to authorized_keys on ${node_addr}')
|
||||
|
||||
// Create builder node
|
||||
mut b := builder.new()!
|
||||
mut node := b.node_new(ipaddr: node_addr)!
|
||||
|
||||
// Use existing builder integration
|
||||
agent.push_key_to_node(mut node, key_name)!
|
||||
|
||||
console.print_green('✓ SSH key "${key_name}" added to authorized_keys on ${node_addr}')
|
||||
}
|
||||
84
lib/osal/sshagent/play.v
Normal file
84
lib/osal/sshagent/play.v
Normal file
@@ -0,0 +1,84 @@
|
||||
module sshagent
|
||||
|
||||
import freeflowuniverse.herolib.core.playbook { PlayBook }
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.builder
|
||||
|
||||
pub fn play(mut plbook PlayBook) ! {
|
||||
if !plbook.exists(filter: 'sshagent.') {
|
||||
return
|
||||
}
|
||||
|
||||
// Get or create a single SSH agent instance
|
||||
mut agent := new_single()!
|
||||
|
||||
// Process sshagent.check actions
|
||||
mut check_actions := plbook.find(filter: 'sshagent.check')!
|
||||
for mut action in check_actions {
|
||||
agent_check(mut agent)!
|
||||
action.done = true
|
||||
}
|
||||
|
||||
// Process sshagent.sshkey_create actions
|
||||
mut create_actions := plbook.find(filter: 'sshagent.sshkey_create')!
|
||||
for mut action in create_actions {
|
||||
mut p := action.params
|
||||
name := p.get('name')!
|
||||
passphrase := p.get_default('passphrase', '')!
|
||||
|
||||
sshkey_create(mut agent, name, passphrase)!
|
||||
action.done = true
|
||||
}
|
||||
|
||||
// Process sshagent.sshkey_delete actions
|
||||
mut delete_actions := plbook.find(filter: 'sshagent.sshkey_delete')!
|
||||
for mut action in delete_actions {
|
||||
mut p := action.params
|
||||
name := p.get('name')!
|
||||
|
||||
sshkey_delete(mut agent, name)!
|
||||
action.done = true
|
||||
}
|
||||
|
||||
// Process sshagent.sshkey_load actions
|
||||
mut load_actions := plbook.find(filter: 'sshagent.sshkey_load')!
|
||||
for mut action in load_actions {
|
||||
mut p := action.params
|
||||
name := p.get('name')!
|
||||
|
||||
sshkey_load(mut agent, name)!
|
||||
action.done = true
|
||||
}
|
||||
|
||||
// Process sshagent.sshkey_check actions
|
||||
mut check_key_actions := plbook.find(filter: 'sshagent.sshkey_check')!
|
||||
for mut action in check_key_actions {
|
||||
mut p := action.params
|
||||
name := p.get('name')!
|
||||
|
||||
sshkey_check(mut agent, name)!
|
||||
action.done = true
|
||||
}
|
||||
|
||||
// Process sshagent.remote_copy actions
|
||||
mut remote_copy_actions := plbook.find(filter: 'sshagent.remote_copy')!
|
||||
for mut action in remote_copy_actions {
|
||||
mut p := action.params
|
||||
node_addr := p.get('node')!
|
||||
key_name := p.get('name')!
|
||||
|
||||
remote_copy(mut agent, node_addr, key_name)!
|
||||
action.done = true
|
||||
}
|
||||
|
||||
// Process sshagent.remote_auth actions
|
||||
mut remote_auth_actions := plbook.find(filter: 'sshagent.remote_auth')!
|
||||
for mut action in remote_auth_actions {
|
||||
mut p := action.params
|
||||
node_addr := p.get('node')!
|
||||
key_name := p.get('name')!
|
||||
|
||||
remote_auth(mut agent, node_addr, key_name)!
|
||||
action.done = true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user