Last active
November 5, 2024 02:20
-
-
Save djoreilly/e31ade0f9684160d9c81cec07791859c to your computer and use it in GitHub Desktop.
Metadata server to allow booting cloud-init images on Libvirt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Server to answer requests from Libvirt VMs to http://169.254.169.254/ | |
Cloud images usually don't have a preset user/password, and this is needed to add a ssh pub key to .ssh/authorized_hosts. | |
Change SSH_PUB_KEY path below. | |
pip install bottle | |
sudo ip address add 169.254.169.254 dev virbr0 | |
open firewall | |
-A ufw-user-input -s 192.168.122.0/24 -d 169.254.169.254/32 -i virbr0 -p tcp -m tcp --dport 80 -j ACCEPT | |
sudo python md_svr.py | |
--- OR --- | |
start server without sudo on 192.168.122.1:8080 and no need for 169 ip on virbr0. | |
iptables -t nat -A PREROUTING -i virbr0 -p tcp -m tcp -d 169.254.169.254 --dport 80 -j DNAT --to-destination 192.168.122.1:8080 | |
iptables -t filter -A ufw-user-input -i virbr0 -p tcp -d 192.168.122.1 --dport 8080 -s 192.168.122.0/24 -j ACCEPT | |
change two lines at bottom of script | |
python md_svr.py | |
""" | |
import os | |
import json | |
from bottle import route, run, request | |
SSH_PUB_KEY = '/home/doreilly/.ssh/id_rsa.pub' | |
DNS_PATH = '/var/lib/libvirt/dnsmasq/' | |
@route('/') | |
def root(): | |
# print dict(request.headers) | |
return "2009-04-04/" | |
@route('/2009-04-04/meta-data/') | |
def metadata(): | |
return 'instance-id\nhostname\npublic-keys/' | |
@route('/2009-04-04/meta-data/hostname') | |
def hostname(): | |
"""Return the libvirt domain name from dnsmasq files""" | |
client_ip = request.get('REMOTE_ADDR') | |
with open(os.path.join(DNS_PATH, "virbr0.status")) as f: | |
for entry in json.load(f): | |
if entry.get('ip-address') == client_ip: | |
mac_addr = entry.get('mac-address') | |
break | |
else: | |
return | |
with open(os.path.join(DNS_PATH, "virbr0.macs")) as f: | |
for entry in json.load(f): | |
if mac_addr in entry.get('macs'): | |
return entry.get('domain') | |
@route('/2009-04-04/meta-data/instance-id') | |
def instance_id(): | |
return "i-%s" % request.get('REMOTE_ADDR') | |
@route('/2009-04-04/meta-data/public-keys') | |
@route('/2009-04-04/meta-data/public-keys/') | |
def pub_keys(): | |
return '0=default' | |
@route('/2009-04-04/meta-data/public-keys/0/') | |
def pub_key0(): | |
return 'openssh-key' | |
@route('/2009-04-04/meta-data/public-keys/0/openssh-key') | |
def openssh_key(): | |
with open(SSH_PUB_KEY) as f: | |
return f.read() | |
run(host='169.254.169.254', port=80, debug=True) | |
# comment previous line and uncomment next if you are natting the address and port | |
# run(host='192.168.122.1', port=8080, debug=True) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The VM needs cloud-init enabled in the image. When it boots, it calls the service to get the public ssh key, and puts it into .ssh/authorized_keys - so now you can ssh to the VM from the host.
$ wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
$ mount-image-callback focal-server-cloudimg-amd64.img -- sh -c 'echo "datasource: Ec2" > $MOUNTPOINT/etc/cloud/ds-identify.cfg'
$ virsh .... # boot new VM from image, or use image as backing volume
$ ssh ubuntu@vm-ip