Доверяем но проверяем

220cca06e02524cf3ef57dbeedff1555.png

Преамбула

Config Sprawl — конфигов становится все больше, каждый IaC инструмент «изобретает» свой диалект Yaml или Json. K8s, Helm, CloudFormation, Ansible, Istio, Spring Boot properties — современные проекты зачастую содержат больше конфигов чем кода и если для Java или Kotlin есть отличные IDE, линтеры и статические анализаторы кода вроде Sonarqube, то для IaC какого-то одного инструмента нет.

Существующие сканеры для IaC могут поддерживать например Ansible, Terraform и k8s, но не Nginx. Или они могут поддерживать только Nignx, но не другие форматы.

Их сложно расширять и добавлять новые политики, на изучение DSL вроде rego можно потратить много времени, но если нужно будет решить какую-то не тривиальную задачу, то DSL никогда не сравнится с полноценным языком программирования, особенно если это Lisp.

Julia идеально подходит для решения такого рода задач. Давайте посмотрим как это можно реализовать.

k8s

На конфигах Kubernetes можно было бы остановится отдельно. Найти ошибку в манифестах K8s очень сложно, они могут занимать сотни килобайт на диске, а не правильная конфигурация может стоить очень дорого. Но это если пытаться контролировать качество в ручную, а для автоматизации YAML K8s подходит отлично.

В данном случае мы будем смотреть используется ли ca сертификат для controller manager на master ноде.

  • Пример с неправильной конфигурацией

  • Пример с правильное конфигурацией

Используем библиотеку YAML для работы с конфигами k8s:

function is_negative(manifest)
    spec = get(manifest, "spec", Dict())
    containers = get(spec, "containers", [])

    for container in containers
        name = get(container, "name", "unknown")
        command = get(container, "command", [])
        args = get(container, "args", [])

        # Check if the command is kube-controller-manager
        if "kube-controller-manager" in command
            # Evaluate whether --root-ca-file is present in args
            has_root_ca_file = any(arg -> startswith(arg, "--root-ca-file"), args)

            # If --root-ca-file is missing, it's a negative case
            if !has_root_ca_file
                return true, name  # Return true and the container name
            end
        end
    end

    return false, ""  # Return false if no negative case is found
end

Проверяем:

julia k8s.jl k8s/negative.yaml
The manifest is positive. All required arguments are present.
(base) anton@Antons-Mac-mini Julia-symbolics % julia k8s.jl k8s/positive.yaml
The manifest is negative. Container 'command-demo-container' is missing '--root-ca-file'.

OpenAPI

Проверяем использование OAuth2 для авторизации в OpenAPI.

  • Пример неправильной конфигурации

  • Пример правильной конфигурации

Используем библиотеку JSON для работы с конфигами OpenAPI:

# Check if the OpenAPI specification is negative
function is_negative(openapi)
    # Extract the security section
    security = get(openapi, "security", [])

    # Extract the securitySchemes section
    components = get(openapi, "components", Dict())
    security_schemes = get(components, "securitySchemes", Dict())

    # Check if any security scheme in the security section is OAuth2
    for sec in security
        for scheme in keys(sec)
            if haskey(security_schemes, scheme)
                scheme_type = get(get(security_schemes, scheme, Dict()), "type", "")
                if scheme_type == "oauth2"
                    return true  # Negative case: OAuth2 is used
                end
            end
        end
    end

    return false  # Positive case: No OAuth2 is used
end

Проверяем:

julia openapi.jl openapi/negative.json
The OpenAPI specification is negative. OAuth2 is used for security.
(base) anton@Antons-Mac-mini Julia-symbolics % julia openapi.jl openapi/positive.json
The OpenAPI specification is positive. API keys are used for security.

Nginx

Nginx использует своей собственной формат конфигов вместо JSON и YAML, но это не является препятствием для Julia. Как и другие Lisp-подобные языки, Julia отлично подходит для работы с regex.

  • Пример с неправильной конфигурацией

  • Пример с правильное конфигурацией

При неправильное конфигурации модуля Stub Status, любой может получить доступ к чувствительной информации. Проверяем наличие auth_basic и auth_basic_user_file в конфиге:

# https://www.f5.com/company/blog/nginx/avoiding-top-10-nginx-configuration-mistakes
# Check if the configuration is negative
function is_negative(config)
    # Regex to match the /basic_status location block
    location_block_regex = r"location\s*=\s*/basic_status\s*\{([^}]*)\}"

    # Find all matches for the /basic_status location block
    matches = collect(eachmatch(location_block_regex, config))

    for match in matches
        block_content = match.captures[1]  # Extract the content inside the block

        # Check if the block contains auth_basic or auth_basic_user_file
        if occursin(r"auth_basic", block_content) || occursin(r"auth_basic_user_file", block_content)
            return true  # Negative case: Authentication directives are present
        end
    end

    return false  # Positive case: No authentication directives are present
end

Проверяем:

The configuration is positive. No authentication directives are present in the /basic_status block.
(base) anton@Antons-Mac-mini Julia-symbolics % julia nginx.jl nginx/negative.conf
The configuration is negative. Authentication directives are present in the /basic_status block.

Таким образом можно написать свой собственный rules-based engine вроде SonarQube. Это лишь простые примеры, возможности Julia особенно если говорить об интеграции AI гораздо шире.

© Habrahabr.ru