Compare commits
2 commits
5ba1a70ff2
...
369bdc1838
Author | SHA1 | Date | |
---|---|---|---|
369bdc1838 | |||
bca1119fd4 |
6 changed files with 122 additions and 55 deletions
96
manage.py
96
manage.py
|
@ -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'], 660)
|
||||||
|
setOwner(env['socket'], env['podman_uid'], getUid('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")
|
||||||
|
|
|
@ -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']}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
40
pyenv.yml
40
pyenv.yml
|
@ -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
|
||||||
|
|
30
setup.sh
30
setup.sh
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue