Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
158423e
TRITON-2513 - Update Access Keys to better support Manta S3
travispaul Oct 24, 2025
bfd7eca
Make more AccessKey fields required.
travispaul Oct 27, 2025
4496901
Update node-ufds
travispaul Oct 28, 2025
34e8c09
Update UFDS
travispaul Oct 28, 2025
bfef404
Adding STS support
cneira Nov 5, 2025
1cf292c
clean expired creds
cneira Nov 5, 2025
f3904e8
Update node-ufds version
travispaul Nov 18, 2025
180a6a4
Remove Istanbul completely, disabled by CAPI-461.
travispaul Nov 20, 2025
7b1dae8
Add tests for accesskey schema validation
travispaul Nov 20, 2025
a04cba0
add Make dependency for npm
travispaul Nov 20, 2025
ec9d8cd
Add cleanup for temporary keys
cneira Nov 26, 2025
a501de6
jslint
cneira Nov 26, 2025
73fd2e6
Bump bunyan, ldapjs, restify, and ufds
travispaul Nov 26, 2025
5b8f86e
jslint
cneira Nov 26, 2025
aeaed55
Add unit tests for temporary access keys
cneira Nov 26, 2025
611b91b
jslint
cneira Nov 26, 2025
23e56b3
Revert ldapjs update (breaks tests)
travispaul Nov 26, 2025
57dd833
Add crontab to remove temporary keys very 12 hours.
cneira Dec 1, 2025
e9c22ba
Fix copyright
cneira Dec 1, 2025
0bcea7d
Merge branch 'TRITON-2513' into MANTA-5485
cneira Dec 4, 2025
3b99100
Use sane defaults for cleanup script
cneira Dec 4, 2025
ad62ba5
Skip date validation on del operation for temporary access keys
cneira Dec 4, 2025
0f598a5
Use setTimeout in cleanupExpiredCredentials
cneira Dec 4, 2025
63b7ca7
Always should use pkgsrc bin path
cneira Dec 9, 2025
8b139ee
Use platform node
cneira Dec 9, 2025
f3ece97
change acceskeyid to accesskeyid
cneira Dec 9, 2025
bcbf6e9
Merge accesskey test files into one
cneira Dec 9, 2025
3517928
jsstyle
cneira Dec 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 5 additions & 13 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#
# Copyright 2021 Joyent, Inc.
# Copyright 2023 MNX Cloud, Inc.
# Copyright 2025 Edgecast Cloud LLC.
#

#
Expand All @@ -24,11 +25,6 @@
#
NAME := ufds

#
# Tools
#
ISTANBUL := ./node_modules/.bin/istanbul

#
# Files
#
Expand Down Expand Up @@ -100,16 +96,11 @@ PATH := $(NODE_INSTALL)/bin:/opt/local/bin:${PATH}
# Repo-specific targets
#
.PHONY: all
all: $(SMF_MANIFESTS) | $(ISTANBUL) $(REPO_DEPS) sdc-scripts
$(NPM) install

$(ISTANBUL): | $(NPM_EXEC)
$(NPM) install
all: $(SMF_MANIFESTS) | $(NPM_EXEC) $(REPO_DEPS) sdc-scripts
$(NPM) ci --include=dev

.PHONY: test
test: $(ISTANBUL)
# CAPI-461: disable istanbul
#$(ISTANBUL) cover --print none test/test.js
test:
node test/test.js

.PHONY: pkg
Expand All @@ -134,6 +125,7 @@ release: all docs
$(ROOT)/main.js \
$(ROOT)/node_modules \
$(ROOT)/package.json \
$(ROOT)/package-lock.json \
$(ROOT)/schema \
$(ROOT)/sapi_manifests \
$(ROOT)/smf \
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<!--
Copyright (c) 2014, Joyent, Inc.
Copyright 2022 MNX Cloud, Inc.
Copyright 2025 Edgecast Cloud LLC.
-->

# SDC-UFDS
Expand Down Expand Up @@ -35,7 +36,7 @@ This assumes several things:

- You've got a moray instance running exactly how it's specified on the
config file `etc/config.coal.json`.
- Your node version is greater than 0.8.
- Your node version is greater than v6.7.1
- You want to see debug output. If you don't, remove the `-d 2`, but it's
strongly recommended while hacking.
- You do have bunyan module installed globally. Given the `make all` command
Expand Down
161 changes: 161 additions & 0 deletions bin/cleanup-expired-creds
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
#!/opt/smartdc/ufds/build/node/bin/node
// -*- mode: js -*-
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

/*
* Copyright 2025 Edgecast Cloud LLC.
*/

/*
* Tool to manually run cleanup of expired temporary credentials.
*
* Usage:
* ./bin/cleanup-expired-creds [-c config] [-d] [-h]
*
* Options:
* -c, --config Path to config file (default: etc/config.coal.json)
* -d, --debug Enable debug logging
* -h, --help Show this help
*/

var fs = require('fs');
var path = require('path');
var util = require('util');

var bunyan = require('bunyan');
var ldapjs = require('ldapjs');
var nopt = require('nopt');

var cleanup = require('../lib/cleanup-expired-credentials');

///--- Globals

var opts = {
'config': String,
'debug': Boolean,
'help': Boolean
};

var shortOpts = {
'c': ['--config'],
'd': ['--debug'],
'h': ['--help']
};

///--- Internal Functions

function usage(code, message) {
var msg = (message ? message + '\n' : '') +
'usage: ' + path.basename(process.argv[1]) +
' [-c config_file] [-d] [-h]\n\n' +
'Options:\n' +
' -c, --config Path to config file (default: etc/config.coal.json)\n' +
' -d, --debug Enable debug logging\n' +
' -h, --help Show this help message';

if (code === 0) {
console.log(msg);
} else {
console.error(msg);
}

process.exit(code);
}

function loadConfig(configPath) {
try {
var content = fs.readFileSync(configPath, 'utf8');
return JSON.parse(content);
} catch (e) {
console.error('Unable to load config file %s: %s', configPath, e.message);
process.exit(1);
}
}

function createClient(config, log, callback) {
var proto = (config.port === 636) ? 'ldaps' : 'ldap';
var host = config.host || '127.0.0.1';
var port = config.port || 1389;
var url = util.format('%s://%s:%s', proto, host, port);

log.debug({url: url}, 'Connecting to UFDS');

var client = ldapjs.createClient({
connectTimeout: 5000,
log: log,
url: url,
tlsOptions: {
rejectUnauthorized: false
}
});

client.once('error', function (err) {
return callback(err);
});

client.once('connect', function () {
var dn = config.rootDN || 'cn=root';
var pw = config.rootPassword || 'secret';

log.debug({dn: dn}, 'Binding to UFDS');

client.bind(dn, pw, function (err) {
if (err) {
return callback(err);
}
return callback(null, client);
});
});
}

///--- Mainline

var parsed = nopt(opts, shortOpts, process.argv, 2);

if (parsed.help) {
usage(0);
}

var configPath = parsed.config ||
path.normalize(__dirname + '/../etc/config.json');

if (!fs.existsSync(configPath)) {
console.error('Config file not found: %s', configPath);
process.exit(1);
}

var config = loadConfig(configPath);

var log = bunyan.createLogger({
name: 'cleanup-expired-creds',
level: parsed.debug ? 'debug' : 'info',
stream: process.stderr,
serializers: bunyan.stdSerializers
});

log.info({config: configPath}, 'Starting expired credentials cleanup');

createClient(config, log, function (err, client) {
if (err) {
log.fatal(err, 'Failed to connect to UFDS');
process.exit(1);
}

log.info('Connected to UFDS, running cleanup...');

cleanup.cleanupExpiredCredentials(client, log, function (cleanupErr) {
if (cleanupErr) {
log.error(cleanupErr, 'Cleanup failed');
client.unbind();
process.exit(1);
}

log.info('Cleanup completed successfully');
client.unbind();
process.exit(0);
});
});
8 changes: 8 additions & 0 deletions boot/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#
# Copyright (c) 2014, Joyent, Inc.
# Copyright 2024 MNX Cloud, Inc.
# Copyright 2025 Edgecast Cloud LLC.
#

export PS4='[\D{%FT%TZ}] ${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
Expand Down Expand Up @@ -208,6 +209,13 @@ sdc_log_rotation_add ufds-capi /var/svc/log/*ufds-capi*.log 1g
sdc_log_rotation_add ufds-replicator /var/svc/log/*ufds-replicator*.log 1g
sdc_log_rotation_setup_end

# Setup crontab for expired credentials cleanup
echo "Setting up crontab for expired credentials cleanup"
crontab -l 2>/dev/null | grep -v cleanup-expired-creds > /tmp/crontab.$$ || true
echo "0 */12 * * * /opt/smartdc/$role/bin/cleanup-expired-creds -c /opt/smartdc/$role/etc/config.json >> /var/log/ufds-cleanup.log 2>&1" >> /tmp/crontab.$$
crontab /tmp/crontab.$$
rm -f /tmp/crontab.$$

# Add build/node/bin and node_modules/.bin to PATH
echo "" >>/root/.profile
echo "export PATH=/opt/smartdc/$role/build/node/bin:/opt/smartdc/$role/node_modules/.bin:\$PATH" >>/root/.profile
Expand Down
132 changes: 132 additions & 0 deletions docs/temporary-credentials.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Temporary Credentials (STS)

UFDS supports temporary security credentials for AWS STS (Security Token Service)
compatibility. These credentials have a limited lifespan and automatically expire.

## Overview

Temporary credentials are stored as `accesskey` entries with `credentialtype=temporary`.
They include:

- **accesskeyid**: The access key identifier
- **accesskeysecret**: The secret access key
- **sessiontoken**: Token required for API authentication
- **expiration**: ISO timestamp when the credential expires
- **principaluuid**: UUID of the user who owns the credential
- **credentialtype**: Set to `temporary` (vs `permanent` for regular keys)

## Automatic Cleanup

Expired temporary credentials are automatically cleaned up by UFDS. The cleanup job
runs periodically and removes credentials where `expiration <= now`.

### How It Works

1. Searches for entries matching:
```
(&(objectclass=accesskey)(credentialtype=temporary)(expiration<=<current_time>))
```

2. Deletes each expired entry with rate limiting (5 concurrent deletes) to avoid
overloading Moray.

3. Logs results showing count of deleted credentials.

## Manual Cleanup Tool

A CLI tool is provided for manually triggering cleanup of expired credentials.

### Location

```
/opt/smartdc/ufds/bin/cleanup-expired-creds
```

### Usage

```bash
./bin/cleanup-expired-creds [-c config_file] [-d] [-h]
```

### Options

| Option | Description |
|--------|-------------|
| `-c, --config` | Path to config file (default: `etc/config.coal.json`) |
| `-d, --debug` | Enable debug logging (shows each credential deleted) |
| `-h, --help` | Show help message |

### Examples

**Basic cleanup (production config):**
```bash
cd /opt/smartdc/ufds
./bin/cleanup-expired-creds -c etc/config.json
```

**With debug output to see each deletion:**
```bash
./bin/cleanup-expired-creds -c etc/config.json -d
```

**Using COAL development config:**
```bash
./bin/cleanup-expired-creds -c etc/config.coal.json
```

### Sample Output

Normal output:
```json
{"level":30,"msg":"Starting expired credentials cleanup"...}
{"level":30,"msg":"Connected to UFDS, running cleanup..."...}
{"level":30,"count":994,"msg":"Found expired temporary credentials to delete"...}
{"level":30,"deleted":994,"msg":"Successfully deleted expired credentials"...}
{"level":30,"msg":"Cleanup completed successfully"...}
```

Debug output (with `-d` flag) includes each deletion:
```json
{"level":20,"dn":"accesskeyid=abc123..., uuid=user-uuid, ou=users, o=smartdc",
"accesskeyid":"abc123...","expiration":"2025-11-26T17:00:00.000Z",
"msg":"Deleting expired temporary credential"...}
```

### Exit Codes

| Code | Meaning |
|------|---------|
| 0 | Success - all expired credentials deleted |
| 1 | Failure - connection error or some deletions failed |

## Configuration

The cleanup interval can be configured when starting the cleanup service
programmatically:

```javascript
var cleanup = require('./lib/cleanup-expired-credentials');

// Start with custom interval (default is 5 minutes)
var intervalMs = 10 * 60 * 1000; // 10 minutes
cleanup.startCleanupInterval(ufdsClient, log, intervalMs);
```

## Troubleshooting

### "Moray failure: unable to acquire backend connection"

This occurs when too many parallel operations overwhelm Moray. The cleanup tool
uses rate limiting (5 concurrent deletes) to prevent this. If you still see this
error, Moray may be under heavy load from other operations.

### No credentials found

If no expired credentials are found, either:
- All temporary credentials are still valid (not yet expired)
- No temporary credentials exist in the system
- Previous cleanup already removed them

### Permission denied

Ensure the config file has correct `rootDN` and `rootPassword` for UFDS bind.
Loading