Skip to content

About

No description, website, or topics provided.

Resources

Code of conduct

Security policy

Stars

1 star

Watchers

38 watching

Forks

Latest commit

 

History

36 Commits

Folders and files

Repository files navigation

Secrets Client C

The secret-agent-client-c library is a C client for the Aerospike Secret Agent. It is used to request secrets from the secret agent.

Building

Dependencies

This client is built using make. Clone this repo, cd into it, and run make

Shared and static libraries will be output in target//lib

Usage

Make use of the secret client through the APIs exposed in sa_client.h.

Start by creating and configuring a secret agent client, sa_client using sa_client_init() or sa_client_new().

Request secrets using sa_secret_get_bytes().

NOTE: Returned secrets always have an extra byte added to the end in case they are strings and the caller needs to null terminate them. Secrets are not automatically null terminated.

Logging is disabled by default but can be enabled by passing a pointer to a function of type sa_log_func to the sa_set_log_function function. Log lines do not include request or response bytes, except the error message the agent returns in its Error field.

Timeouts

sa_cfg.timeout is in milliseconds. The TCP connect, across every address addr resolves to, must finish within timeout or fails with SA_FAILED_TIMEOUT. Addresses are tried in order, so one that does not answer can use the whole timeout and leave the rest untried. After that, each wait during the TLS handshake, the request and the response gets its own timeout. DNS resolution is not covered. 0 fails at once and a negative value means no limit.

Examples

Request a secret over TCP with logging. Log function.

void mylog(const char* format, ...)
{
    va_list args;

    printf("LOGGED DURING TEST: ");
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
    printf("\n");
}

Main.

    const char* addr = "127.0.0.1";
    const char* port = "3005";

    sa_cfg cfg;
    sa_cfg_init(&cfg);
    cfg.addr = addr;
    cfg.port = port;
    cfg.timeout = 2000;

    sa_client c;
    sa_client_init(&c, &cfg);

    sa_set_log_function(&mylog);

    const char* path = "secrets:<resource_key>:<secret_key>";
    size_t result_size = 0;

    uint8_t* secret;
    sa_err err = sa_secret_get_bytes(&c, path, &secret, &result_size);
    
    assert(err.code == SA_OK);

    // null terminate the secret for use as a string
    secret[result_size] = 0;
    printf("secret: %s\n", (char*)secret);
    free(secret);

Request a secret over TCP with TLS and logging. The agent's certificate must cover addr, here with an IP:127.0.0.1 subject alternative name. See TLS certificate verification.

    const char* addr = "127.0.0.1";
    const char* port = "3005";

    const char* capath = "./path/to/cacert.pem";
    char* cacert = NULL;
    // read_cert_file reads out the entire cert file
    cacert = read_cert_file(capath);

    sa_cfg cfg;
    sa_cfg_init(&cfg);
    cfg.addr = addr;
    cfg.port = port;
    cfg.timeout = 3000;
    cfg.tls.ca_string = cacert;
    cfg.tls.enabled = true;

    sa_client c;
    sa_client_init(&c, &cfg);

    sa_set_log_function(&mylog);

    const char* path = "secrets:<resource_key>:<secret_key>";
    size_t result_size = 0;

    uint8_t* secret;
    sa_err err = sa_secret_get_bytes(&c, path, &secret, &result_size);
    
    assert(err.code == SA_OK);

    // null terminate the secret for use as a string
    secret[result_size] = 0;
    printf("secret: %s\n", (char*)secret);
    free(secret);

TLS certificate verification

When tls.enabled is set, the agent's certificate must chain to a CA in tls.ca_string (the system trust store is not used) and must cover addr. tls.ca_string must include the root CA, an intermediate alone is not enough.

  • An IPv4 or IPv6 literal must match an IP address subject alternative name. An IPv4-mapped address such as ::ffff:127.0.0.1 is checked as that IPv6 address, so it does not match IP:127.0.0.1.
  • Anything else is a host name, matched against the certificate's DNS names, or its subject common name when it has none, and sent as SNI. A wildcard matches a whole leftmost label (*.example.com) but not part of one (a*.example.com).
  • Bracketed ([::1]), scoped (fe80::1%lo0) and trailing-dot (localhost.) forms are not supported, nor are shorthand or zero-padded IPv4 forms such as 127.1, 0x7f.0.0.1 or 010.0.0.1.

The reason for a failure, such as hostname mismatch, is logged through the log function.

Breaking change: earlier versions accepted any certificate. Setups that relied on that, for example connecting to 0.0.0.0 with a certificate issued for localhost, or passing a CA that did not issue the agent's certificate, now fail with SA_FAILED_INTERNAL. Verification cannot be turned off.

Testing

make test builds the library and src/test/tests, generates throwaway certificates into target/<platform>/test-certs with src/test/gen-certs.sh, and runs the tests.

The tests start an in-process fake Secret Agent (plain TCP and TLS) on the loopback interface, so no real agent or network access is needed. The IPv6 cases are skipped when ::1 is unavailable, and the end-to-end scoped IPv6 case when the loopback interface has no fe80::1 (macOS has it, Linux usually does not). The test binary wraps socket() to slow down connecting, which works because the library is linked into it statically.

Requirements: a C compiler, make, jansson, and OpenSSL including the openssl command line tool. On macOS the Makefile uses Homebrew under /opt/homebrew. Homebrew's openssl@3 is keg-only, so point the compiler at it:

brew install jansson openssl@3
export CPATH="$(brew --prefix openssl@3)/include" LIBRARY_PATH="$(brew --prefix openssl@3)/lib"
make test

To run the tests on Linux in Docker from the repository root:

docker run --rm -v "$PWD":/src:ro ubuntu:24.04 sh -c '
apt-get update && apt-get install -y build-essential libssl-dev libjansson-dev openssl &&
cp -r /src /build && cd /build && rm -rf target src/test/tests && make test'

docker run --rm -v "$PWD":/src:ro rockylinux/rockylinux:8 sh -c '
dnf install -y dnf-plugins-core && dnf config-manager --set-enabled powertools &&
dnf install -y gcc make openssl openssl-devel jansson-devel &&
cp -r /src /build && cd /build && rm -rf target src/test/tests && make test'

About

No description, website, or topics provided.

Resources

Code of conduct

Security policy

Stars

1 star

Watchers

38 watching

Forks

Releases

Packages

Used by

Contributors

Languages