Python websocket on interactive exec


I trying execute a command in a instance by LXD API /1.0/instances/{name}/exec in interactive mode, and getting two sockets - bi-direction and control as described in the documentation:

In interactive mode, a single bi-directional websocket is used for stdin and stdout/stderr.

An additional "control" socket is always added on top which can be used for out of band communication with LXD.
This allows sending signals and window sizing information through.

Below it my Python code. Copy source code to new a file and save it. Then replace value of instance_name to your an instance name and run the code:

import os
from requests import Session, Response
import requests_unixsocket
from urllib import parse
from shlex import shlex

unix_socket = '/var/lib/lxd/unix.socket'
instance_name = 'pgsql'

# ------------------------------------ Certs default paths ---------------
lxc_conf_path = os.path.expanduser('~/.config/lxc/')
cert = os.path.join(lxc_conf_path, 'client.crt')
key = os.path.join(lxc_conf_path, 'client.key')
certs = (cert, key,)
print('Certs:', certs)
# -------------------------------------------------------------------------

endpoint = requests_unixsocket.DEFAULT_SCHEME + parse.quote(unix_socket, safe='')
session = requests_unixsocket.Session()

    "command": tuple(shlex('pwd')),
    "environment": None,
    "wait-for-websocket": True,
    "interactive": True,
    "user": None,
    "group": None,
    "cwd": None,

response ='{endpoint}/1.0/instances/{instance_name}/exec', json=json)

print('URI:', response.request.method, response.request.url)
if isinstance(response.request.body, bytes):
    print('BODY:', response.request.body.decode('utf-8'))

metadata = response.json()['metadata']
operation_id = metadata['id']
fds = metadata['metadata']['fds']['0']
fds_ctrl = metadata['metadata']['fds']['control']


uri = f'wss://{operation_id}/websocket?secret={fds}'
uri_ctrl = f'wss://{operation_id}/websocket?secret={fds_ctrl}'


Certs: ('/home/dv/.config/lxc/client.crt', '/home/dv/.config/lxc/client.key')
URI: POST http+unix://%2Fvar%2Flib%2Flxd%2Funix.socket/1.0/instances/pgsql/exec
BODY: {"command": ["pwd"], "environment": null, "wait-for-websocket": true, "interactive": true, "user": null, "group": null, "cwd": null}

I got two URL (URI) which are shown in the console output above to connect websokets by documentation /1.0/operations/{id}/websocket or need to use untrusted API link? /1.0/operations/{id}/websocket?public

What to do next? How to access a socket and get stdout, stderr, stdin from running a command?

I tried to get to access with websockets but server reject all connection by timeout and I don’t get anything.

Next example for websokets, for addition to the python code above:

import asyncio
# from websockets.sync.client import ClientConnection, connect, unix_connect
import websockets
import ssl

ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ssl_context.load_cert_chain(certfile=certs[0], keyfile=certs[1])
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE

async def message():
    async with websockets.connect(uri=uri, ssl=ssl_context) as socket:
        # msg = input("Write your message to the server: ")
        await socket.send('msg')
        print(await socket.recv())


Before running a code above, I switched LXD to mode listening on

lxc config set core.https_address

And I get an error after running the code:

websockets.exceptions.ConnectionClosedError: no close frame received or sent

I do not know what to do next…

It turned out that I had an issue with access via certificates (Trouble auntentification in LXD and SSL/TLS keys) to LXD, so I’m closing the question