Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor MinioAdminClient using HTTP client #1291

Merged
merged 27 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8c71fd2
Update deprecated mc admin commands
pbrw Apr 25, 2023
6f65a71
Merge branch 'master' into master
harshavardhana May 19, 2023
5dd8726
Merge branch 'master' into master
balamurugana May 19, 2023
5cd16c1
Merge branch 'master' into master
balamurugana May 21, 2023
26f9aff
Merge branch 'minio:master' into master
pbrw Jul 2, 2023
74ea6f2
Merge branch 'minio:master' into master
pbrw Aug 21, 2023
aca0955
Cryptography to read/write encrypted MinIO Admin payload
pbrw Jul 24, 2023
26b1258
Basic MinIO user management API
pbrw Aug 21, 2023
e2b3c90
User: Add, Info, Remove, List
pbrw Aug 21, 2023
b2e92e9
simplify api execution
balamurugana Aug 23, 2023
e502e18
fix retrieve credentials
pbrw Aug 24, 2023
a0a5563
Policy management commands
pbrw Aug 24, 2023
0d3e28e
Group management commands
pbrw Aug 25, 2023
be865a9
User enable/disable commands
pbrw Aug 25, 2023
fa13450
Service commands
pbrw Aug 25, 2023
e7a64bc
Service, update, info, top locks commands
pbrw Aug 25, 2023
e59936d
Config commands
pbrw Aug 28, 2023
68f610a
Remove unnecessary 'else'
pbrw Aug 29, 2023
255da0e
Quota, Profile commands
pbrw Aug 29, 2023
3660126
KMS key status commands
pbrw Aug 29, 2023
cd02b42
Typo fix
pbrw Sep 19, 2023
cfad703
Merge branch 'master' into minio-admin-http
harshavardhana Sep 25, 2023
a67ffbf
Prometheus generate command
pbrw Sep 26, 2023
c60cd4a
Revert "Prometheus generate command"
balamurugana Sep 26, 2023
518f659
add site replication apis
balamurugana Sep 26, 2023
d1b3cab
Merge branch 'master' into minio-admin-http
pbrw Sep 26, 2023
5bd31ac
Typo fix
pbrw Sep 27, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip setuptools
pip install certifi urllib3 mock pytest
pip install certifi urllib3 mock pytest argon2-cffi pycryptodome
- name: Run check if Ubuntu
if: matrix.os == 'ubuntu-latest'
run: |
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ default: tests

getdeps:
@echo "Installing required dependencies"
@pip install --user --upgrade autopep8 certifi pytest pylint urllib3
@pip install --user --upgrade autopep8 certifi pytest pylint urllib3 argon2-cffi pycryptodome

check: getdeps
@echo "Running checks"
Expand Down
2 changes: 1 addition & 1 deletion minio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

__title__ = "minio-py"
__author__ = "MinIO, Inc."
__version__ = "7.1.18"
__version__ = "7.2.0"
__license__ = "Apache 2.0"
__copyright__ = "Copyright 2015, 2016, 2017, 2018, 2019, 2020 MinIO, Inc."

Expand Down
17 changes: 6 additions & 11 deletions minio/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

import itertools
import os
import platform
import tarfile
from datetime import timedelta
from io import BytesIO
Expand All @@ -50,12 +49,13 @@
parse_copy_object, parse_list_objects)
from .deleteobjects import DeleteError, DeleteRequest, DeleteResult
from .error import InvalidResponseError, S3Error, ServerError
from .helpers import (MAX_MULTIPART_COUNT, MAX_MULTIPART_OBJECT_SIZE,
MAX_PART_SIZE, MIN_PART_SIZE, BaseURL, ObjectWriteResult,
ThreadPool, check_bucket_name, check_non_empty_string,
check_sse, check_ssec, genheaders, get_part_info,
from .helpers import (_DEFAULT_USER_AGENT, MAX_MULTIPART_COUNT,
MAX_MULTIPART_OBJECT_SIZE, MAX_PART_SIZE, MIN_PART_SIZE,
BaseURL, ObjectWriteResult, ThreadPool,
check_bucket_name, check_non_empty_string, check_sse,
check_ssec, genheaders, get_part_info,
headers_to_strings, is_valid_policy_type, makedirs,
md5sum_hash, read_part_data, sha256_hash, queryencode)
md5sum_hash, queryencode, read_part_data, sha256_hash)
from .legalhold import LegalHold
from .lifecycleconfig import LifecycleConfig
from .notificationconfig import NotificationConfig
Expand All @@ -70,11 +70,6 @@
from .versioningconfig import VersioningConfig
from .xml import Element, SubElement, findtext, getbytes, marshal, unmarshal

_DEFAULT_USER_AGENT = (
f"MinIO ({platform.system()}; {platform.machine()}) "
f"{__title__}/{__version__}"
)


class Minio: # pylint: disable=too-many-public-methods
"""
Expand Down
146 changes: 146 additions & 0 deletions minio/crypto.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# -*- coding: utf-8 -*-
# MinIO Python Library for Amazon S3 Compatible Cloud Storage, (C)
# 2015, 2016, 2017 MinIO, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# pylint: disable=too-many-lines,disable=too-many-branches,too-many-statements
# pylint: disable=too-many-arguments

"""Cryptography to read and write encrypted MinIO Admin payload"""

import os
pbrw marked this conversation as resolved.
Show resolved Hide resolved

from argon2.low_level import Type, hash_secret_raw
from Crypto.Cipher import AES, ChaCha20_Poly1305

_NONCE_LEN = 8
_SALT_LEN = 32


class AesGcmCipherProvider:
"""AES-GCM cipher provider"""
@staticmethod
def get_cipher(key: bytes, nonce: bytes):
"""Get cipher"""
return AES.new(key, AES.MODE_GCM, nonce)


class ChaCha20Poly1305CipherProvider:
"""ChaCha20Poly1305 cipher provider"""
@staticmethod
def get_cipher(key: bytes, nonce: bytes):
"""Get cipher"""
return ChaCha20_Poly1305.new(key=key, nonce=nonce)


def encrypt(payload: bytes, password: str) -> bytes:
"""
Encrypts data using AES-GCM using a 256-bit Argon2ID key.
To see the original implementation in Go, check out the madmin-go library
(https://github.com/minio/madmin-go/blob/main/encrypt.go#L38)
"""
cipher_provider = AesGcmCipherProvider()
nonce = os.urandom(_NONCE_LEN)
salt = os.urandom(_SALT_LEN)

padded_nonce = [0] * (_NONCE_LEN + 4)
padded_nonce[:_NONCE_LEN] = nonce

key = _generate_key(password.encode(), salt)
additional_data = _generate_additional_data(
cipher_provider, key, bytes(padded_nonce))

padded_nonce[8] = 0x01
padded_nonce = bytes(padded_nonce)

cipher = cipher_provider.get_cipher(key, padded_nonce)
cipher.update(additional_data)
encrypted_data, mac = cipher.encrypt_and_digest(payload)

payload = salt
payload += bytes([0x00])
payload += nonce
payload += encrypted_data
payload += mac

return bytes(payload)


def decrypt(payload: bytes, password: str) -> bytes:
"""
Decrypts data using AES-GCM or ChaCha20Poly1305 using a
256-bit Argon2ID key. To see the original implementation in Go,
check out the madmin-go library
(https://github.com/minio/madmin-go/blob/main/encrypt.go#L38)
"""
pos = 0
salt = payload[pos:pos+_SALT_LEN]
pos += _SALT_LEN

cipher_id = payload[pos]
if cipher_id == 0:
cipher_provider = AesGcmCipherProvider()
elif cipher_id == 1:
cipher_provider = ChaCha20Poly1305CipherProvider()
else:
return None

pos += 1

nonce = payload[pos:pos+_NONCE_LEN]
pos += _NONCE_LEN

encrypted_data = payload[pos:-16]
hmac_tag = payload[-16:]

key = _generate_key(password.encode(), salt)

padded_nonce = [0] * 12
padded_nonce[:_NONCE_LEN] = nonce

additional_data = _generate_additional_data(
cipher_provider, key, bytes(padded_nonce))
padded_nonce[8] = 1

cipher = cipher_provider.get_cipher(key, bytes(padded_nonce))

cipher.update(additional_data)
decrypted_data = cipher.decrypt_and_verify(encrypted_data, hmac_tag)

return decrypted_data


def _generate_additional_data(cipher_provider, key: bytes,
padded_nonce: bytes) -> bytes:
"""Generate additional data"""
cipher = cipher_provider.get_cipher(key, padded_nonce)
tag = cipher.digest()
new_tag = [0] * 17
new_tag[1:] = tag
new_tag[0] = 0x80
return bytes(new_tag)


def _generate_key(password: bytes, salt: bytes) -> bytes:
"""Generate 256-bit Argon2ID key"""
return hash_secret_raw(
secret=password,
salt=salt,
time_cost=1,
memory_cost=65536,
parallelism=4,
hash_len=32,
type=Type.ID,
version=19
)
Loading