When you use the AWS API to run a command on a remote docker container (ECS), the AWS API gives you back a websocket to read the output of your command from. When using the aws
command line utility (which also uses the AWS API), reading the websocket stream is handled by session-manager-plugin.
session-manager-plugin is written in GoLang, and I've been trying to rewrite parts of it in Python. I don't speak GoLang, but I fumbled my way though adding some code to session-manager-plugin to output the raw binary data it is sending, and receiving when the binary is being used.
Essentially, the output of the command you ran is split up into messages, each one with headers, and a payload. One of the headers for each message is a messageID, which is a UUID. Each message needs to be acknowledged by telling the server that you're received the message with that UUID.
The issue I'm having is that when analyzing the raw binary data, I can see that a message that was received with UUID b'\x85\xc3\x12P\n\x08\xaf)\xfd\xba\x1b8\x1asMd'
is being acknowledged by session-manager-plugin with a packet that says this:
b'{"AcknowledgedMessageType":"output_stream_data","AcknowledgedMessageId":"fdba1b38-1a73-4d64-85c3-12500a08af29","AcknowledgedMessageSequenceNumber":4,"IsSequentialMessage":true}'
To figure out what UUID b'\x85\xc3\x12P\n\x08\xaf)\xfd\xba\x1b8\x1asMd'
is in Python, I do this:
import uuid
print(str(uuid.UUID(bytes=b'\x85\xc3\x12P\n\x08\xaf)\xfd\xba\x1b8\x1asMd')))
# 85c31250-0a08-af29-fdba-1b381a734d64
At first glance, the UUID of the message that was received, and the UUID of the message being acknowledged do not match, but if you look closely, you'll see that the UUID of the original message that was received is reversed from the UUID being acknowledged. Sort of. In the 16 byte UUID, the first 8 bytes come after the last 8 bytes.
85c31250-0a08-af29-fdba-1b381a734d64
fdba1b38-1a73-4d64-85c3-12500a08af29
Is there any reason this would be happening? Am I decoding b'\x85\xc3\x12P\n\x08\xaf)\xfd\xba\x1b8\x1asMd'
wrong?
Note: As you can see from above, the UUID in the Acknowledgement packet is inside of JSON. If I was decoding it wrong, the whole thing would be gibberish.
Also note that this is just an analysis of a perfectly working session-manager-plugin communication stream. One way or another, this actually works. I'm just trying to figure out how so I can re-create it.
CodePudding user response:
Looking at the source code for session-manager-plugin, it would appear it reads the first eight bytes as the least significant bytes, then reads the next eight bytes as the most significant bytes, then appends it in the order MSB, LSB. Seems to me like that would produce the behavior you're seeing.
// getUuid gets the 128bit uuid from an array of bytes starting from the offset.
func getUuid(log log.T, byteArray []byte, offset int) (result uuid.UUID, err error) {
byteArrayLength := len(byteArray)
if offset > byteArrayLength-1 || offset 16-1 > byteArrayLength-1 || offset < 0 {
log.Error("getUuid failed: Offset is invalid.")
return nil, errors.New("Offset is outside the byte array.")
}
leastSignificantLong, err := getLong(log, byteArray, offset)
if err != nil {
log.Error("getUuid failed: failed to get uuid LSBs Long value.")
return nil, errors.New("Failed to get uuid LSBs long value.")
}
leastSignificantBytes, err := longToBytes(log, leastSignificantLong)
if err != nil {
log.Error("getUuid failed: failed to get uuid LSBs bytes value.")
return nil, errors.New("Failed to get uuid LSBs bytes value.")
}
mostSignificantLong, err := getLong(log, byteArray, offset 8)
if err != nil {
log.Error("getUuid failed: failed to get uuid MSBs Long value.")
return nil, errors.New("Failed to get uuid MSBs long value.")
}
mostSignificantBytes, err := longToBytes(log, mostSignificantLong)
if err != nil {
log.Error("getUuid failed: failed to get uuid MSBs bytes value.")
return nil, errors.New("Failed to get uuid MSBs bytes value.")
}
uuidBytes := append(mostSignificantBytes, leastSignificantBytes...)
return uuid.New(uuidBytes), nil
}