E (add rootful podman)

This commit is contained in:
Viyurz 2024-10-07 11:37:52 +02:00
parent 5ba1a70ff2
commit bca1119fd4
Signed by: Viyurz
SSH key fingerprint: SHA256:IskOHTmhHSJIvAt04N6aaxd5SZCVWW1Guf9tEcxIMj8
6 changed files with 122 additions and 55 deletions

View file

@ -14,15 +14,14 @@ def backupProj(project):
def getImageId (image): def getImageId (image):
return subprocess.run(["podman", "image", "inspect", "--format", "{{.Id}}", image], capture_output=True, text=True).stdout.strip() return runPodman("image", ["inspect", "--format", "{{.Id}}", image]).stdout.strip()
def getUid(service): def getUid(service):
if service in env['users'].keys(): if service in env['users'].keys():
user = env['users'][service] + env['uid_shift'] return env['users'][service] + env['uid_shift']
else: else:
user = 1000 return env['podman_uid']
return user
def pullProj(project): def pullProj(project):
@ -34,7 +33,7 @@ def pullProj(project):
pulledImages = [] pulledImages = []
for image in images: for image in images:
currentId = getImageId(image) currentId = getImageId(image)
subprocess.run(["podman", "pull", image]) runPodman("pull", image)
pulledId = getImageId(image) pulledId = getImageId(image)
if currentId != pulledId: if currentId != pulledId:
pulledImages += image pulledImages += image
@ -57,17 +56,50 @@ def renderFile(templateFile):
outputFile.close() outputFile.close()
def runPodman(cmd, args):
if isinstance(args, str):
args = args.split(' ')
if cmd == 'compose':
runArgs = ["podman-compose"] + args
else:
runArgs = ["podman", cmd] + args
if env['rootless']:
return subprocess.run(runArgs, capture_output=True, text=True)
else:
return runSudo(runArgs)
def runSudo(args):
if isinstance(args, str):
args = args.split(' ')
child = subprocess.Popen(["sudo"] + args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if re.search('^\\[sudo\\] password for .+', child.stdout.read().strip()):
child.communicate(input=input().encode())[0]
child.wait()
stderr = child.stderr.read().strip()
if stderr != '':
print(stderr, file=sys.stderr)
return child
def setCertPerms(service): def setCertPerms(service):
for path in ["/etc/letsencrypt", "/etc/letsencrypt/live", "/etc/letsencrypt/archive"]: dirs = ["/etc/letsencrypt", "/etc/letsencrypt/live", "/etc/letsencrypt/archive"]
setOwner(path, 0, 0)
setPerms(path, 751)
pkeyFile = env['certs'][service]['pkey'] pkeyFile = env['certs'][service]['pkey']
domain_dir = re.search('.+(?=\\/.+$)', pkeyFile).group(0) domain_dir = re.search('.+(?=\\/.+$)', pkeyFile).group(0)
for path in [domain_dir, re.sub('live', 'archive', domain_dir)]: dirs += [domain_dir, re.sub('live', 'archive', domain_dir)]
setOwner(path, env['host_uid'], getUid(service))
setPerms(path, 550) for path in dirs:
setOwner(path, 0, env['podman_uid'])
setPerms(path, 711)
setOwner(pkeyFile, 0, getUid(service)) setOwner(pkeyFile, 0, getUid(service))
setPerms(pkeyFile, 640) setPerms(pkeyFile, 640)
@ -77,8 +109,8 @@ def setNftables():
renderFile("nftables.conf.mako") renderFile("nftables.conf.mako")
if not filecmp.cmp("nftables.conf.rendered", "/etc/nftables.conf"): if not filecmp.cmp("nftables.conf.rendered", "/etc/nftables.conf"):
print("nftables.conf changed, copying new version.") print("nftables.conf changed, copying new version.")
sudoRun(["cp", "nftables.conf.rendered", "/etc/nftables.conf"]) runSudo(["cp", "nftables.conf.rendered", "/etc/nftables.conf"])
sudoRun(["systemctl", "restart", "nftables"]) runSudo(["systemctl", "restart", "nftables"])
else: else:
print("nftables.conf unchanged.") print("nftables.conf unchanged.")
@ -92,7 +124,7 @@ def setOwner(path, uid=None, gid=None):
if stat.st_uid != uid or stat.st_gid != gid: if stat.st_uid != uid or stat.st_gid != gid:
print(f"Changing ownership of {path} to {uid}:{gid} from {stat.st_uid}:{stat.st_gid}.") print(f"Changing ownership of {path} to {uid}:{gid} from {stat.st_uid}:{stat.st_gid}.")
sudoRun(["chown", f"{uid}:{gid}", path]) runSudo(["chown", f"{uid}:{gid}", path])
else: else:
print(f"Ownership of {path} already set to {uid}:{gid}.") print(f"Ownership of {path} already set to {uid}:{gid}.")
@ -103,10 +135,10 @@ def setPerms(path, mode):
curMode = oct(stat.st_mode)[-3:] curMode = oct(stat.st_mode)[-3:]
if mode != curMode: if mode != curMode:
print(f"Changing permissions of {path} to {mode} from {curMode}.") print(f"Changing permissions of {path} to {mode} from {curMode}.")
if stat.st_uid == env['host_uid']: if stat.st_uid == os.getuid():
subprocess.run(["chmod", mode, path]) subprocess.run(["chmod", mode, path])
else: else:
subprocess.run(["sudo", "chmod", mode, path]) runSudo(["chmod", mode, path])
else: else:
print(f"Permissions of {path} already set to {mode}.") print(f"Permissions of {path} already set to {mode}.")
@ -116,6 +148,15 @@ def setupProj(project):
backupProj(project) backupProj(project)
if project == 'diun':
if not os.path.isfile(env['socket']):
if env['rootless']:
subprocess.run(["systemctl", "--user", "restart", "podman.socket"])
else:
runSudo("systemctl restart podman.socket")
setPerms(env['socket'], 640)
setOwner(env['socket'], env['podman_uid'], env['users']['diun'])
if project in env['certs'].keys(): if project in env['certs'].keys():
setCertPerms(project) setCertPerms(project)
@ -123,22 +164,18 @@ def setupProj(project):
renderFile(templateFile) renderFile(templateFile)
renderedFilename = re.sub('\\.mako$', '.rendered', templateFile) renderedFilename = re.sub('\\.mako$', '.rendered', templateFile)
setPerms(renderedFilename, 640) setPerms(renderedFilename, 640)
setOwner(renderedFilename, env['host_uid'], getUid(project)) setOwner(renderedFilename, os.getuid(), getUid(project))
upProj(project) upProj(project)
def sudoRun(args):
child = subprocess.Popen(["sudo"] + args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if re.search('^\\[sudo\\] password for .+', child.stdout.read().strip()):
child.communicate(input=input().encode())[0]
def upProj(project): def upProj(project):
if runPodman("container", ["exists", project]).returncode == 0:
print(f"Tearing down stack for project {project}.")
runPodman("compose", ["-f", f"projects/{project}/compose.yaml.rendered", "down"])
print(f"Creating & starting stack for project {project}.") print(f"Creating & starting stack for project {project}.")
if subprocess.run(["podman", "container", "exists", project]).returncode == 0: runPodman("compose", ["-f", f"projects/{project}/compose.yaml.rendered", "up", "-d"])
subprocess.run(["podman-compose", "-f", f"projects/{project}/compose.yaml.rendered", "down"])
subprocess.run(["podman-compose", "-f", f"projects/{project}/compose.yaml.rendered", "up", "-d"])
def updateProj(project): def updateProj(project):
@ -160,13 +197,14 @@ def main():
with open(envFile, 'r') as envfile, open(secretsFile, 'r') as secretsfile: with open(envFile, 'r') as envfile, open(secretsFile, 'r') as secretsfile:
global env, secrets global env, secrets
env = yaml.safe_load(envfile) env = yaml.safe_load(Template(filename=envFile).render())
env = yaml.safe_load(Template(filename=envFile).render(env=env))
secrets = yaml.safe_load(secretsfile) secrets = yaml.safe_load(secretsfile)
setOwner(secretsFile, env['host_uid'], env['host_uid']) setOwner(secretsFile, os.getuid(), os.getgid())
setPerms(secretsFile, 600) setPerms(secretsFile, 600)
print("\nUsing socket " + env['socket'] + ".")
print("\nChoose action:") print("\nChoose action:")
print("[1/S] Setup project") print("[1/S] Setup project")
print("[2/U] Update project") print("[2/U] Update project")

View file

@ -2,7 +2,6 @@ services:
coturn: coturn:
container_name: coturn container_name: coturn
image: docker.io/coturn/coturn:4-alpine image: docker.io/coturn/coturn:4-alpine
network_mode: pasta
restart: always restart: always
user: ${env['users']['coturn']}:${env['users']['coturn']} user: ${env['users']['coturn']}:${env['users']['coturn']}
ports: ports:
@ -18,3 +17,4 @@ services:
- ${env['certs']['coturn']['cert']}:/etc/coturn/cert.pem:ro - ${env['certs']['coturn']['cert']}:/etc/coturn/cert.pem:ro
- ${env['certs']['coturn']['pkey']}:/etc/coturn/pkey.pem:ro - ${env['certs']['coturn']['pkey']}:/etc/coturn/pkey.pem:ro
${env['networks_attr']}

View file

@ -2,8 +2,8 @@ services:
diun: diun:
container_name: diun container_name: diun
image: docker.io/crazymax/diun:4 image: docker.io/crazymax/diun:4
network_mode: pasta
restart: always restart: always
user: ${env['users']['diun']}:${env['users']['diun']}
command: serve command: serve
env_file: .env.rendered env_file: .env.rendered
volumes: volumes:
@ -11,5 +11,7 @@ services:
- ./images.yml:/etc/diun/images.yml:ro - ./images.yml:/etc/diun/images.yml:ro
- data:/data - data:/data
${env['networks_attr']}
volumes: volumes:
data: data:

View file

@ -2,7 +2,6 @@ services:
homepage: homepage:
container_name: homepage container_name: homepage
image: git.ahur.ac/viyurz/homepage:latest image: git.ahur.ac/viyurz/homepage:latest
network_mode: pasta
restart: always restart: always
user: ${env['users']['homepage']}:${env['users']['homepage']} user: ${env['users']['homepage']}:${env['users']['homepage']}
ports: ports:

View file

@ -1,10 +1,23 @@
domain: viyurz.fr domain: viyurz.fr
timezone: "Europe/Paris" timezone: "Europe/Paris"
host_uid: 1000
socket: "/run/user/${env['host_uid']}/podman/podman.sock"
# UID shift for mapping between host & containers <%!
uid_shift: 99999 import os, subprocess
uid = os.getuid()
rootless = os.path.exists(f"/run/user/{uid}/podman/podman.sock")
%>
% if rootless:
rootless: true
podman_uid: ${uid}
uid_shift: ${int(subprocess.run(['sh', '-c', "grep " + os.getlogin() + " /etc/subuid | cut -d ':' -f 2"], capture_output=True, text=True).stdout.strip()) - 1}
socket: "/run/user/${uid}/podman/podman.sock"
% else:
rootless: false
podman_uid: 0
uid_shift: 0
socket: "/run/podman/podman.sock"
% endif
# cifs_credentials is undefined when we run the backup playbook # cifs_credentials is undefined when we run the backup playbook
@ -43,17 +56,23 @@ cifs_mounts:
dir_mode: 750 dir_mode: 750
borg_repodir: "${env['cifs_mounts']['backups']['path']}/borg" borg_repodir: "{env['cifs_mounts']['backups']['path']}/borg"
borg_passphrase_file: /etc/borg-passphrase.txt borg_passphrase_file: /etc/borg-passphrase.txt
certs: certs:
coturn: coturn:
cert: "/etc/letsencrypt/live/turn.${env['domain']}/fullchain.pem" cert: "/etc/letsencrypt/live/turn.viyurz.fr/fullchain.pem"
pkey: "/etc/letsencrypt/live/turn.${env['domain']}/privkey.pem" pkey: "/etc/letsencrypt/live/turn.viyurz.fr/privkey.pem"
mailserver: mailserver:
cert: "/etc/letsencrypt/live/mail.${env['domain']}/fullchain.pem" cert: "/etc/letsencrypt/live/mail.viyurz.fr/fullchain.pem"
pkey: "/etc/letsencrypt/live/mail.${env['domain']}/privkey.pem" pkey: "/etc/letsencrypt/live/mail.viyurz.fr/privkey.pem"
networks_attr: |
networks:
default:
enable_ipv6: true
# Ports exposed to host # Ports exposed to host
@ -89,6 +108,7 @@ ports:
# UID in containers # UID in containers
users: users:
coturn: 666 coturn: 666
diun: 1011
etebase: 373 etebase: 373
fireshare: 1007 fireshare: 1007
hedgedoc: 1004 hedgedoc: 1004
@ -118,6 +138,6 @@ volumes:
stump_configdir: /mnt/stump/config stump_configdir: /mnt/stump/config
stump_datadir: /mnt/stump/data stump_datadir: /mnt/stump/data
synapse_datadir: /mnt/synapsedata synapse_datadir: /mnt/synapsedata
syncthing_datadir: "${env['cifs_mounts']['syncthing']['path']}" syncthing_datadir: "{env['cifs_mounts']['syncthing']['path']}"
uptime_kuma_datadir: /mnt/uptimekumadata uptime_kuma_datadir: /mnt/uptimekumadata
vaultwarden_datadir: /mnt/vwdata vaultwarden_datadir: /mnt/vwdata

View file

@ -6,15 +6,27 @@ if [[ $(whoami) == "root" ]]; then
fi fi
sudo apt install -y aardvark-dns dbus-user-session nftables passt podman podman-compose python3-mako uidmap while ! [[ "$podman_mode" =~ ^(rootful|rootless)$ ]]; do
read -rp "Rootful or rootless Podman? " podman_mode
for unit in podman.service podman.socket podman-auto-update.service podman-auto-update.timer podman-clean-transient.service podman-restart.service; do
sudo systemctl disable --now "$unit"
done done
sudo loginctl enable-linger $USER declare -a podman_units=(podman.service podman.socket podman-auto-update.service podman-auto-update.timer podman-clean-transient.service podman-restart.service)
if [[ "$podman_mode" == "rootless" ]]; then
sudo apt install -y aardvark-dns dbus-user-session nftables passt podman podman-compose python3-mako uidmap
sudo loginctl enable-linger "$USER"
sudo systemctl disable --now "${podman_units[@]}"
systemctl --user enable --now "${podman_units[@]}"
else
sudo apt install -y aardvark-dns nftables podman podman-compose python3-mako
systemctl --user disable --now "${podman_units[@]}"
sudo systemctl enable --now "${podman_units[@]}"
fi
declare -A sysctl_vars=( declare -A sysctl_vars=(
@ -27,12 +39,8 @@ echo -n "" | sudo tee /etc/sysctl.d/podman.conf
for key in "${!sysctl_vars[@]}"; do for key in "${!sysctl_vars[@]}"; do
value="${sysctl_vars[$key]}" value="${sysctl_vars[$key]}"
echo "$key = $value" | sudo tee -a /etc/sysctl.d/podman.conf echo "$key = $value" | sudo tee -a /etc/sysctl.d/podman.conf
sudo sysctl -p
done done
sudo sysctl -p
for unit in podman.service podman.socket podman-restart.service; do
systemctl --user enable --now "$unit"
done
sudo systemctl enable --now nftables sudo systemctl enable --now nftables