Coder Social home page Coder Social logo

node-spiffe's Introduction

spiffe Workload API client for Node.js

CI npm Powered by TypeScript

A SPIFFE Workload API client for Node.js.

Installation

You can install the module with your favorite package manager:

# with pnpm
pnpm add spiffe

# with yarn
yarn add spiffe

# with npm
npm install spiffe

Usage

Create a client:

import {createClient} from 'spiffe'

// Connect to the endpoint set from the SPIFFE_ENDPOINT_SOCKET environment variable
const client = createClient()

// Connect to a specific endpoint
const client = createClient('unix:///path/to/endpoint.sock')

// Read x509 credentials from Workload API
const rpc = client.fetchX509SVID()
for await (const message of rpc.responses) {
  message.svids.forEach((svid) => {
    const certificateBase64 = Buffer.from(svid.x509Svid).toString('base64')
    console.log('Certificate in Base64 Format:', certificateBase64)
  })

  if (message.svids.length > 0) {
    break
  }
}

License

MIT License, see LICENSE.

node-spiffe's People

Contributors

dependabot[bot] avatar jacobwgillespie avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar

node-spiffe's Issues

Improvement on Usage section of README.md

For developers who are not familiar with gRPC, it might not be that straightforward to understand the usage of the library.

I propose to improve the Usage section of the README.md with something like below.

import { createClient } from 'spiffe'

// Connect to the endpoint set from the SPIFFE_ENDPOINT_SOCKET environment variable
const client = createClient()

// Connect to a specific endpoint
const client = createClient('unix:///path/to/endpoint.sock')

// Read x509 credentials from Workload API
let rpc = client.fetchX509SVID();
for await (let message of rpc.responses) {
    message.svids.forEach((svid) => {
        let certificateBase64 = Buffer.from(svid.x509Svid).toString("base64")
        console.log("Certificate in Base64 Format:", certificateBase64)
    })
    if (messages.svids.length > 0) {
        break;
    }
}

Convertion of DER Encoded Certificates to PEM Encoded Certificates

Problem Statement

According to SPIFFE implementation, Workload API returns DER encoded intermediate CA's and leaf certificate also known as end entity certificate. And this certificates formed via concatenating certificates starting with leaf certificate.

Parsing these certificates and converting them is quite straightforward in Go with the help of standard crypto module.

image

According to SPIFFE documentation, it is possible to have multiple intermediate CA before a leaf certificate in the certificate chain.
But I was not able to find a library in Node.js which converts DER encoding to PEM encoding correctly, when this is the case.

Following libraries are investigated:

  1. Node.js Standard Crypto Module
    Problem with standard Crypto module in Node.js is, it does not parse all the buffer provided, only parses the leaf certificate and first intermediate CA. If there is more than one intermediate CA, it is lost.

  2. Node-Forge
    Has the same problem with Node.js standard crypto module, and in addition to that, node-forge does not support ECC based x509 certificates.

Why is this critical?
When Node.js applications want to make use of SPIFFE certificates, they need the certificates in PEM format because standard TLS module in Node.js supports only PEM encoded certificates. (createSecureContext function of standard module). Therefore the applications will not be able to use it

In short the problem is, SPIFFE allows having multiple Intermediate CA and leaf certificate concatenated with each other with DER encoding. But when existance of multple intermediate CA's, there is no library doing this properly. (Please

Solution Proposal

We can have a function in the client which converts DER to PEM format without parsing all certificate. Since DER encoding is a type length encoding, I was able to implement the following.

function parseDER(buffer) {
    let currentIndex = 0;
    let pemCerts = [];

    while (currentIndex < buffer.length) {
        let beginOfSequence = currentIndex;
        // Get the first byte for tag 
        const tag = buffer[currentIndex++];
        // Get the second byte for length value representing bytes
        let length = buffer[currentIndex++];
        if (length === 0x80) {
            // Indefinite length encoding is not supported
            throw new Error('Indefinite length encoding not supported');
        }
        if (length & 0x80) {
            const lengthBytes = length & 0x7F;
            length = 0;
            for (let i = 0; i < lengthBytes; i++) {
                length = (length << 8) | buffer[currentIndex++];
            }
        }
        // Extract the value based on the tag and length
        pemCerts.push(`-----BEGIN CERTIFICATE-----\n${Buffer.from(buffer.slice(beginOfSequence, currentIndex + length)).toString("base64")}\n-----END CERTIFICATE-----`);

        // Move the current index to the next tag
        currentIndex += length;
    }
    return pemCerts.join("\n")
}

I am open to any other solutions, or creating a PR for this. But first I wanted to share the issue with you to check if you have experienced similar problem.

Await of RPC call is not being resolved

When we make a call to the workload agent API over gRPC using the client with the following approach:

let call = service.myMethod(foo);
for await (let message of call.responses) {
    console.log("got a message", message)
}
await call;
console.log("After Response"). // never gets executed

"After response" console.log statement is never reached.
In protobuf-ts manual it is recommended to use it this way for error handling.
Please see here.

I think spire workload agent server does not end the call with something recognized by protobuf-ts, therefore if we want to await the call for error handling, await is blocking the execution.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.