mylamour / blog

Your internal mediocrity is the moment when you lost the faith of being excellent. Just do it.
https://fz.cool
61 stars 14 forks source link

let's write a ftp services with Data At Rest Encryption #82

Open mylamour opened 3 years ago

mylamour commented 3 years ago

0x01 Intro

In this tutorial, we will build a ftp Service with data at rest encryption. that's mean all the file was encrypted at FTP server.

This is what you need:

image

As you can see in this picture, we are going to use linux system authentication to verify user and use file system as the storage backend. that's mean you can integrated the auth part with linux system but not to modified the services code.

0x02 Event Handler

Here is the sequence diagram. as you can see in the below picture, it was mainly with 3 parts. and here's a brief introduction.

pki_connection (1)

KEK file should be created on the user's first login, and updated KEK file content with generate random AES key and encrypted it by RSA Key

0x03 Encrypt/Decrypt

Before we talking about encrypt & decrypt, you should know some basic crypto algorithms. RSA and AES is a common crypto algorithm. AES was a symmetric algorithms , that's mean you can use one aes key to encrypt/decrypt file. RSA was a asymmetric algorithms , and you can use public key to encrypt some message, but only able to use private key to decrypt that. in this case, we use RSA to protected the AES key which is really used to encrypt and decrypt files.

image

here is a encrypted key

➜  keks git:(main) cat .8b1c1c1eae6c650485e77efbc336c5bfb84ffe0b0bea65610b721762.secret
62a65113d8a5fab27f56491addf69fc5d03ca4441b1b6fb558e5c0a20b613d38170c341385698b078bc8fdd157cb78cd0d3c0f95bfee53e9f05e100cda01583c3a8f99250c5be8565403436b4b35356138b5397fd72a824e1d3785002347568ab92f36b511063d25a8a3915766bc3a85caf4f57d503b6aa99d9fa4e683aa8b3821a68b4ffba116350e88e2a08f8d3385764bdfe157764c85c94039f6c3a2ba37395e6ceb46f7c4be88220352bb35091b249e7b6d7fbad297def32bf86d87a1c0f4dff1a9d081eab80907934914112e18e613fd7ec27caca82e1b48a518e7b0fa5987ea8508ebc051ad94fa509c0e510636930a3b83b6f3624d416f460e3abf2e
def key_decrypt(ciphertext):
    data = {"secret_path": SECRET_PATH, "secret_version": SECRET_VERSION, "ciphertext": ciphertext}

    response = requests.post('{}/key/decrypt/rsa'.format(EAAS_URL), json=data)

    return json.loads(response.text)['plaintext']

def key_encrypt(plaintext):
    data = {"secret_path": "{}".format(
        SECRET_PATH), "secret_version": SECRET_VERSION, "plaintext":plaintext}

    response = requests.post('{}/key/encrypt/rsa'.format(EAAS_URL), json=data)

    return json.loads(response.text)['ciphertext']
class Dropzone(TLS_FTPHandler):

    def encrypt(self, message):
        message = self.pad(message)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return iv + cipher.encrypt(message)

    def decrypt(self, ciphertext):
        iv = ciphertext[:AES.block_size]
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        plaintext = cipher.decrypt(ciphertext[AES.block_size:])
        return plaintext.rstrip(b"\0")

    def encrypt_file(self, file_name):
        with open(file_name, 'rb') as fo:
            plaintext = fo.read()
        enc = self.encrypt(plaintext)
        with open(file_name + ".enc", 'wb') as fo:
            fo.write(enc)

    def decrypt_file(self, file_name):
        with open(file_name, 'rb') as fo:
            ciphertext = fo.read()
        dec = self.decrypt(ciphertext)
        with open(file_name[:-4], 'wb') as fo:
            fo.write(dec)

also, as you can see, this class was Inherited from TLS_FTPHandler, that's mean it was able to enable FTP over TLS feature.

0x04 with Container

we can build a service easily with docker, So I've been using docker to build services recently.

here is the docker-compose file

  dropzone:
    depends_on: 
      - eaas
    build: 

      context: dropzone/
      args:
        DROPZONE_PORT: ${DROPZONE_PORT}
        DROPZONE_SECRET_PATH: ${DROPZONE_SECRET_PATH}
        DROPZONE_SECRET_VERSION: ${DROPZONE_SECRET_VERSION}

    image: mo-vault:dropzone
    container_name: mo.vault.dropzone
    ports: 
      - "${DROPZONE_PORT}:${DROPZONE_PORT}"
    links:
      - "eaas:mo.vault.eaas"

    healthcheck:
      test: ["CMD", "curl", "-f", "http://mo.vault.eaas:8443"]
      interval: 30s
      timeout: 10s
      retries: 5

    volumes:
      - ${PWD}/dropzone:/dropzone
      - ${PWD}/local/dropzone/keks:/dropzone/keks
      - $PWD/local/dropzone/remote:/home/

if you want access some container services within another container, you should specialized the network link.

Now, we can run it with docker-compose directly.

rm -rf local/tokens/user02 local/dropzone && docker-compose  --env-file ./config/.env.dev build
rm -rf local/tokens/user02 local/dropzone && docker-compose  --env-file ./config/.env.dev up eass dropzone

for the whole demo, you can see this recorder:

tricks: it was recored with asciinema, and you can modified the cast file to delete some personal info.

asciicast

0x05 Conclusion

In this blog, we use pyftpdliib and eaas to build a FTP services with DARE (you can find the whole project code with this project code). Maybe FTP is a little out of date, but it's still a good example to explain how we build a service to support data at rest encryption. and you can use sftp to do another demo, just handle the login/logout put/download event. Also, you can change the backend with s3 fs, and integrated Auth with LDAP, and so on.

0x06 Resources