Skip to content

Commit

Permalink
Merge 42c52ff into b17a73b
Browse files Browse the repository at this point in the history
  • Loading branch information
rst0git committed Dec 6, 2023
2 parents b17a73b + 42c52ff commit a58f851
Show file tree
Hide file tree
Showing 48 changed files with 1,945 additions and 170 deletions.
5 changes: 3 additions & 2 deletions .cirrus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ task:
ln -sf /usr/include/google/protobuf/descriptor.proto images/google/protobuf/descriptor.proto
dnf config-manager --set-enabled crb # Same as CentOS 8 powertools
dnf -y install epel-release epel-next-release
dnf -y install --allowerasing asciidoc gcc git gnutls-devel libaio-devel libasan libcap-devel libnet-devel libnl3-devel libbsd-devel libselinux-devel make protobuf-c-devel protobuf-devel python-devel python-PyYAML python-protobuf python-junit_xml python3-importlib-metadata python-flake8 xmlto libdrm-devel
dnf -y install --allowerasing asciidoc gcc git gnutls-devel libaio-devel libasan libcap-devel libnet-devel libnl3-devel libbsd-devel libselinux-devel make protobuf-c-devel protobuf-devel python-devel python-PyYAML python-protobuf python-junit_xml python3-importlib-metadata python-flake8 python3-cryptography python3-wheel xmlto libdrm-devel
systemctl stop sssd
# Even with selinux in permissive mode the selinux tests will be executed.
# The Cirrus CI user runs as a service from selinux point of view and is
Expand Down Expand Up @@ -108,8 +108,9 @@ task:
yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm || :
yum install -y dnf-plugins-core
yum config-manager --set-enabled powertools
yum install -y --allowerasing asciidoc gcc git gnutls-devel libaio-devel libasan libcap-devel libnet-devel libnl3-devel libbsd-devel libselinux-devel make protobuf-c-devel protobuf-devel python3-devel python3-flake8 python3-PyYAML python3-protobuf python3-importlib-metadata python3-junit_xml xmlto libdrm-devel
yum install -y --allowerasing asciidoc gcc git gnutls-devel libaio-devel libasan libcap-devel libnet-devel libnl3-devel libbsd-devel libselinux-devel make protobuf-c-devel protobuf-devel python3-devel python3-flake8 python3-PyYAML python3-protobuf python3-importlib-metadata python3-junit_xml python3-cryptography python3-wheel xmlto libdrm-devel
alternatives --set python /usr/bin/python3
python3 -m pip install --upgrade pip
systemctl stop sssd
# Even with selinux in permissive mode the selinux tests will be executed
# The Cirrus CI user runs as a service from selinux point of view and is
Expand Down
17 changes: 17 additions & 0 deletions .github/workflows/encrypted-images.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Encrypted Images Test

on: [push, pull_request]

# Cancel any preceding run on the pull request.
concurrency:
group: encrypted-images-test-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/criu-dev' }}

jobs:
build:
runs-on: ubuntu-22.04

steps:
- uses: actions/checkout@v2
- name: Run CRIU Encrypted Images Test
run: sudo -E make -C scripts/ci local ENCRYPTED_IMAGES_TEST=1
8 changes: 8 additions & 0 deletions Documentation/criu.txt
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,14 @@ By default the option is set to *fpu* and *ins*.
option is intended for post-copy (lazy) migration and should be
used in conjunction with *restore* with appropriate options.

*-e*, *--encrypt*::
Encrypt the contents of the image files. The encryption key is
loaded from an X.509 certificate, which can be specified using
the *--tls-cert* option. The *restore* operation automatically
detects if the image files are encrypted and loads a private key
from a corresponding PEM file, which can be specified using the
*--tls-key* option.

*--file-validation* ['mode']::
Set the method to be used to validate open files. Validation is done
to ensure that the version of the file being restored is the same
Expand Down
109 changes: 91 additions & 18 deletions crit/crit/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@
import sys
import json
import os
import base64

from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend

import pycriu
from . import __version__


CRIU_KEY = '/etc/pki/criu/private/key.pem'


def inf(opts):
if opts['in']:
return open(opts['in'], 'rb')
Expand Down Expand Up @@ -36,11 +44,56 @@ def dinf(opts, name):
return open(os.path.join(opts['dir'], name), mode='rb')


def get_cipher_token(opts):
"""
get_cipher_token returns the decrypted cipher token.
"""
plaintext_token = None
if 'in' in opts:
dir_path = os.path.dirname(os.path.realpath(opts['in']))
elif 'dir' in opts:
dir_path = os.path.realpath(opts['dir'])
else:
raise TypeError("Invalid input")
cipher_img_path = os.path.join(dir_path, 'cipher.img')

# We assume that when this image is not present,
# the checkpoint images are not encrypted. Thus,
# here we continue with normal decode if 'cipher.img'
# doesn't exist.
if not os.path.exists(cipher_img_path):
return

with open(cipher_img_path, mode='rb') as cipher_img_file:
cipher_img = pycriu.images.load(cipher_img_file)
# Validate the content of the cipher.img
if ('entries' not in cipher_img or
len(cipher_img['entries']) != 1 or
'token' not in cipher_img['entries'][0]):
raise TypeError("Invalid cipher image")

encrypted_token = base64.b64decode(cipher_img['entries'][0]['token'])

priv_key_file = opts['tls_key']
with open(priv_key_file, "rb") as f:
priv_key = serialization.load_pem_private_key(f.read(), None, default_backend())
if not isinstance(priv_key, rsa.RSAPrivateKey):
raise TypeError("Only RSA private keys are supported.")

# GnuTLS uses the PKCS#1 v1.5 padding scheme by default.
plaintext_token = priv_key.decrypt(encrypted_token, padding.PKCS1v15())
return plaintext_token


def decode(opts):
indent = None
token = None

if opts['in'] and os.path.basename(opts['in']) not in ['cipher.img', 'stats-dump', 'stats-restore']:
token = get_cipher_token(opts)

try:
img = pycriu.images.load(inf(opts), opts['pretty'], opts['nopl'])
img = pycriu.images.load(inf(opts), opts['pretty'], opts['nopl'], token=token)
except pycriu.images.MagicException as exc:
print("Unknown magic %#x.\n"
"Maybe you are feeding me an image with "
Expand Down Expand Up @@ -101,10 +154,11 @@ def show_ps(p, opts, depth=0):

def explore_ps(opts):
pss = {}
ps_img = pycriu.images.load(dinf(opts, 'pstree.img'))
token = get_cipher_token(opts)
ps_img = pycriu.images.load(dinf(opts, 'pstree.img'), token=token)
for p in ps_img['entries']:
core = pycriu.images.load(
dinf(opts, 'core-%d.img' % get_task_id(p, 'pid')))
dinf(opts, 'core-%d.img' % get_task_id(p, 'pid')), token=token)
ps = ps_item(p, core['entries'][0])
pss[ps.pid] = ps

Expand All @@ -128,10 +182,11 @@ def explore_ps(opts):

def ftype_find_in_files(opts, ft, fid):
global files_img
token = get_cipher_token(opts)

if files_img is None:
try:
files_img = pycriu.images.load(dinf(opts, "files.img"))['entries']
files_img = pycriu.images.load(dinf(opts, "files.img"), token=token)['entries']
except Exception:
files_img = []

Expand All @@ -146,6 +201,7 @@ def ftype_find_in_files(opts, ft, fid):


def ftype_find_in_image(opts, ft, fid, img):
token = get_cipher_token(opts)
f = ftype_find_in_files(opts, ft, fid)
if f:
if ft['field'] in f:
Expand All @@ -154,7 +210,7 @@ def ftype_find_in_image(opts, ft, fid, img):
return None

if ft['img'] is None:
ft['img'] = pycriu.images.load(dinf(opts, img))['entries']
ft['img'] = pycriu.images.load(dinf(opts, img), token=token)['entries']
for f in ft['img']:
if f['id'] == fid:
return f
Expand Down Expand Up @@ -218,18 +274,19 @@ def get_file_str(opts, fd):


def explore_fds(opts):
ps_img = pycriu.images.load(dinf(opts, 'pstree.img'))
token = get_cipher_token(opts)
ps_img = pycriu.images.load(dinf(opts, 'pstree.img'), token=token)
for p in ps_img['entries']:
pid = get_task_id(p, 'pid')
idi = pycriu.images.load(dinf(opts, 'ids-%s.img' % pid))
idi = pycriu.images.load(dinf(opts, 'ids-%s.img' % pid), token=token)
fdt = idi['entries'][0]['files_id']
fdi = pycriu.images.load(dinf(opts, 'fdinfo-%d.img' % fdt))
fdi = pycriu.images.load(dinf(opts, 'fdinfo-%d.img' % fdt), token=token)

print("%d" % pid)
for fd in fdi['entries']:
print("\t%7d: %s" % (fd['fd'], get_file_str(opts, fd)))

fdi = pycriu.images.load(dinf(opts, 'fs-%d.img' % pid))['entries'][0]
fdi = pycriu.images.load(dinf(opts, 'fs-%d.img' % pid), token=token)['entries'][0]
print("\t%7s: %s" %
('cwd', get_file_str(opts, {
'type': 'REG',
Expand Down Expand Up @@ -258,11 +315,12 @@ def get(self, iid):


def explore_mems(opts):
ps_img = pycriu.images.load(dinf(opts, 'pstree.img'))
token = get_cipher_token(opts)
ps_img = pycriu.images.load(dinf(opts, 'pstree.img'), token=token)
vids = vma_id()
for p in ps_img['entries']:
pid = get_task_id(p, 'pid')
mmi = pycriu.images.load(dinf(opts, 'mm-%d.img' % pid))['entries'][0]
mmi = pycriu.images.load(dinf(opts, 'mm-%d.img' % pid), token=token)['entries'][0]

print("%d" % pid)
print("\t%-36s %s" % ('exe',
Expand Down Expand Up @@ -311,12 +369,13 @@ def explore_mems(opts):


def explore_rss(opts):
ps_img = pycriu.images.load(dinf(opts, 'pstree.img'))
token = get_cipher_token(opts)
ps_img = pycriu.images.load(dinf(opts, 'pstree.img'), token=token)
for p in ps_img['entries']:
pid = get_task_id(p, 'pid')
vmas = pycriu.images.load(dinf(opts, 'mm-%d.img' %
pid))['entries'][0]['vmas']
pms = pycriu.images.load(dinf(opts, 'pagemap-%d.img' % pid))['entries']
vmas = pycriu.images.load(
dinf(opts, 'mm-%d.img' % pid), token=token)['entries'][0]['vmas']
pms = pycriu.images.load(dinf(opts, 'pagemap-%d.img' % pid), token=token)['entries']

print("%d" % pid)
vmi = 0
Expand Down Expand Up @@ -385,6 +444,10 @@ def main():
'-o',
'--out',
help='where to put criu image in json format (stdout by default)')
decode_parser.add_argument(
'--tls-key', default=CRIU_KEY,
help=f'path to private key in PEM format used to decrypt images (default: {CRIU_KEY})'
)
decode_parser.set_defaults(func=decode, nopl=False)

# Encode
Expand All @@ -409,15 +472,25 @@ def main():
x_parser = subparsers.add_parser('x', help='explore image dir')
x_parser.add_argument('dir')
x_parser.add_argument('what', choices=['ps', 'fds', 'mems', 'rss'])
x_parser.add_argument(
'--tls-key', default=CRIU_KEY,
help=f'path to private key in PEM format used to decrypt images (default: {CRIU_KEY})'
)
x_parser.set_defaults(func=explore)

# Show
show_parser = subparsers.add_parser(
'show', help="convert criu image from binary to human-readable json")
show_parser.add_argument("in")
show_parser.add_argument('--nopl',
help='do not show entry payload (if exists)',
action='store_true')
show_parser.add_argument(
'--nopl',
help='do not show entry payload (if exists)',
action='store_true'
)
show_parser.add_argument(
'--tls-key', default=CRIU_KEY,
help=f'path to private key in PEM format used to decrypt images (default: {CRIU_KEY})'
)
show_parser.set_defaults(func=decode, pretty=True, out=None)

opts = vars(parser.parse_args())
Expand Down
2 changes: 1 addition & 1 deletion criu/action-scripts.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ static int run_shell_scripts(const char *action)
list_for_each_entry(script, &scripts, node) {
int err;
pr_debug("\t[%s]\n", script->path);
err = cr_system(-1, -1, -1, script->path, (char *[]){ script->path, NULL }, 0);
err = cr_system(-1, -1, -1, script->path, (char *[]){ script->path, NULL }, 0, TLS_MODE_NONE);
if (err)
pr_err("Script %s exited with %d\n", script->path, err);
retval |= err;
Expand Down
Loading

0 comments on commit a58f851

Please sign in to comment.