Skip to content

bug(K8s): sandbox create does not validate name against Kubernetes naming constraints #2115

Closed

Description

@varshaprasad96

Agent Diagnostic

  • Loaded openshell-cli skill and explored the sandbox creation flow across CLI, gateway, and Kubernetes driver.
  • Traced the name through the code: CLI passes --name to the gateway's CreateSandbox gRPC call. The gateway's validate_sandbox_spec (crates/openshell-server/src/grpc/validation.rs:93) checks name length and -- substring but does not validate character set.
  • The gateway calls validate_sandbox_create on the Kubernetes driver (crates/openshell-driver-kubernetes/src/driver.rs:344), which validates GPU requirements and driver config but not the name.
  • The name reaches create_sandbox at driver.rs:474 where it is used directly as metadata.name on the Sandbox CRD object. Kubernetes rejects it with an RFC 1123 validation error.
  • The Docker driver already handles this — sanitize_docker_name (crates/openshell-driver-docker/src/lib.rs:2958) lowercases and replaces invalid characters before creating the container, so names like my_sandbox and MySandbox work fine on Docker. The Kubernetes driver has no equivalent sanitization or validation.
  • Auto-generated names (petname, e.g. crack-krait) are always valid. Only user-provided --name values trigger this.

Description

openshell sandbox create --name "my_sandbox" on the Kubernetes driver passes the name through gateway validation (which only checks length and --), through the Kubernetes driver's validate_sandbox_create (which only checks GPU and driver config), and fails at the Kubernetes API because underscores are not valid in Kubernetes resource names.

The same names work fine on the Docker driver because sanitize_docker_name (crates/openshell-driver-docker/src/lib.rs:2958) lowercases the name and replaces invalid characters with - before creating the container. The Kubernetes driver should either validate and reject early, or sanitize the name to be Kubernetes-compatible, matching the level of input handling the Docker driver already provides.

The Kubernetes driver's validate_sandbox_create (crates/openshell-driver-kubernetes/src/driver.rs:344) already validates GPU requirements and driver config — adding a name check against Kubernetes naming constraints here would catch invalid names before they reach the API.

Reproduction Steps

  1. Deploy OpenShell on a Kubernetes cluster (kind, minikube, etc.) using the Helm chart
  2. Run openshell sandbox create --from base --name "my_sandbox" -- echo test
  3. Observe the Kubernetes API rejection
  4. Also try openshell sandbox create --from base --name "MySandbox" -- echo test
  5. Compare: the same names work on a Docker-backed gateway

Environment

  • OS: macOS (Darwin 25.5.0, Apple Silicon)
  • OpenShell CLI: 0.0.70
  • Gateway: 0.0.75-dev.2 (Helm chart 0.0.0-dev)
  • Kubernetes: v1.31.0 (kind with Podman)
  • Compute driver: kubernetes

  • I pointed my agent at the repo and had it investigate this issue
  • I loaded relevant skills (e.g., debug-openshell-cluster, debug-inference, openshell-cli)
  • My agent could not resolve this — the diagnostic above explains why - (my agent could just fix it by providing a new name, but it is a good practice to perform validation on the client side to the extent possible before send req to the server)

Activity

  1. changed the title [-]bug(kubernetes): sandbox create does not validate name against Kubernetes naming constraints[/-] [+]bug(K8s): sandbox create does not validate name against Kubernetes naming constraints[/+] on Jul 2, 2026
  2. added
    area:gatewayGateway server and control-plane work
    and removed
    state:triage-neededOpened without agent diagnostics and needs triage
    on Jul 9, 2026
  3. added theissue type on Jul 9, 2026
  4. 2000krysztof commented on Jul 13, 2026

    @2000krysztof
    Contributor

    I would like to work on this issue.

  5. TaylorMutch commented on Jul 13, 2026

    @TaylorMutch
    Collaborator

    @2000krysztof I approved your vouch, thanks for volunteering!

  6. Hokonoken commented on Jul 14, 2026

    @Hokonoken

    Welcome @2000krysztof, and good luck with this one! I looked into it earlier, so a few edge cases that might be worth covering, if they help:

    • Validate the name as a single DNS-1123 label (≤63 chars, ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$) rather than a full subdomain. The sandbox name is the leading hostname label in service routing (the routing host is {sandbox}.{base_domain}, and service endpoints are keyed {sandbox}--{service}), so a dotted-but-otherwise-valid subdomain name would still break routing.
    • Call the validation first in validate_sandbox_create, so the friendly error precedes the raw RFC-1123 API error the issue is about.
    • I'd leave the -- service separator to the routing layer rather than banning it in name validation (it's cross-driver), unless you find a concrete reason to reject it here.

    Happy to share the two unit tests I drafted (a valid-name case and an invalid dotted/too-long case) if useful — otherwise, all yours.

  7. 2000krysztof commented on Jul 14, 2026

    @2000krysztof
    Contributor

    @Hokonoken I think that 63 characters is fine for this, because it gives enough of a margine. I would be happy to take a look at your tests that you made.

  8. Hokonoken commented on Jul 15, 2026

    @Hokonoken

    Here they are — two unit tests plus the small validator they exercise, so the block is self-contained. Take them as-is or adapt freely, no attribution needed.

    I re-ran both today on current main (e6f319c7): the only adaptation needed was the call site, since validate_sandbox_create now starts with self.validate_driver_config_for_sandbox(sandbox) — I put validate_sandbox_name(&sandbox.name)?; as the first line before it. Both tests pass there (cargo test -p openshell-driver-kubernetes sandbox_name_validation).

    // crates/openshell-driver-kubernetes/src/driver.rs
    
    const MAX_SANDBOX_NAME_LEN: usize = 63;
    
    /// Reject sandbox names Kubernetes cannot accept before they reach the API.
    fn validate_sandbox_name(name: &str) -> Result<(), tonic::Status> {
        let valid = !name.is_empty()
            && name.len() <= MAX_SANDBOX_NAME_LEN
            && !name.starts_with('-')
            && !name.ends_with('-')
            && name
                .bytes()
                .all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit() || byte == b'-');
        if valid {
            return Ok(());
        }
        Err(tonic::Status::invalid_argument(format!(
            "sandbox name {name:?} is not a valid Kubernetes object name: use at most \
             {MAX_SANDBOX_NAME_LEN} lowercase letters, digits, or '-', starting and ending \
             with a letter or digit"
        )))
    }
    
    #[test]
    fn sandbox_name_validation_accepts_kubernetes_safe_names() {
        let max_len = "a".repeat(MAX_SANDBOX_NAME_LEN);
        for name in ["crack-krait", "a", "abc123", "123abc", max_len.as_str()] {
            assert!(
                validate_sandbox_name(name).is_ok(),
                "expected {name:?} to be accepted"
            );
        }
    }
    
    #[test]
    fn sandbox_name_validation_rejects_names_kubernetes_would_refuse() {
        let over_len = "a".repeat(MAX_SANDBOX_NAME_LEN + 1);
        for name in [
            "my_sandbox",
            "MySandbox",
            "",
            "-leading",
            "trailing-",
            "dotted.name",
            "has space",
            over_len.as_str(),
        ] {
            let status = validate_sandbox_name(name)
                .expect_err(&format!("expected {name:?} to be rejected"));
            assert_eq!(status.code(), tonic::Code::InvalidArgument, "{name:?}");
        }
    }

    Two deliberate choices encoded in the reject list, in case you want to change them:

    • dotted.name is rejected even though the Kubernetes API would accept it for metadata.name — that's the label-vs-subdomain point from my earlier comment.
    • There is no -- rejection (a--b passes): Kubernetes accepts it, and the {sandbox}--{service} separator collision felt like the routing layer's call rather than the driver's, but that one is arguable either way.

    Good luck with the PR!

  9. 2000krysztof commented on Jul 15, 2026

    @2000krysztof
    Contributor

    @Hokonoken Thank you for the code snippet I will add some of these things into my solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:gatewayGateway server and control-plane work

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions