micronaut-kubernetes icon indicating copy to clipboard operation
micronaut-kubernetes copied to clipboard

Secrets with non-printable characters are corrupted on the app side

Open dima-starosud opened this issue 3 years ago • 0 comments

Expected Behavior

All k8s secrets are properly base64-decoded on application side.

Actual Behaviour

Some of the secrets (in particular ZuD1WklPYQCr) are corrupted when received by @Value inside the service.

Steps To Reproduce

  1. Add following k8s secret, note it contains non-printable characters, and on service side it's consumed as byte[]:
apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: my-secret
  namespace: default
data:
  MY_SECRET: "ZuD1WklPYQCr"
  1. Use it in the service on application side:
@Singleton
@Slf4j
public class MyService {
    public MyService(
            @Value("${MY_SECRET}") byte[] corrupted
    ) {
        // use one provided by Micronaut
        log.warn("Corrupted: {}", corrupted);
        log.warn("Encoded corrupted: {}", Base64.getEncoder().encodeToString(corrupted));

        // manual decoding
        var expected = Base64.getDecoder().decode("ZuD1WklPYQCr".getBytes());
        log.warn("Expected: {}", expected);
        log.warn("Encoded expected: {}", Base64.getEncoder().encodeToString(expected));
    }
}
  1. Output will be following:
Corrupted: [102, -17, -65, -67, -17, -65, -67, 90, 73, 79, 97, 0, -17, -65, -67]
Encoded corrupted: Zu+/ve+/vVpJT2EA77+9
Expected: [102, -32, -11, 90, 73, 79, 97, 0, -85]
Encoded expected: ZuD1WklPYQCr
  1. So, as one can see bytes were changed during the traveling from k8s to the service.

Details

Tried to investigate a bit, and it looks like Micronaut uses new String(v.getValue()) here in KubernetesUtils.java, instead of passing byte[] as is. And this conversion to String actually changes byte-representation.

Removing this conversion actually fixes the issue, but requires more changes in core/*/AbstractInitializableBeanDefinition.java, where it does applicationContext.resolvePlaceholders(stringValue), which resolves to String, I think here would be better to resolve to Object and do any conversion later on.

In general I think it's better to pass configurations just as is (byte[]/Object) and do required conversion right before resolving for the service.

Will be happy to provide more details if needed.

I think this may also require enhancement request to micronaut-core.

Environment Information

  • gcr.io/distroless/java17
  • JDK17

Example Application

No response

Version

3.3.0

dima-starosud avatar Feb 11 '22 11:02 dima-starosud