Documentationcurrent version
Help us improve the docs by fixing typos and proposing enhancements.

Nikita

Action "krb5.ktutil.add"

Create and manage a keytab for an existing principal. It's different than ktadd in the way it can manage several principal on one keytab.

Example

const {$status} = await nikita.krb5.ktutil.add({
  principal: 'myservice/my.fqdn@MY.REALM',
  keytab: '/etc/security/keytabs/my.service.keytab',
  password: 'password'
})
console.info(`Keytab was created or modified: ${$status}`)

Schema definitions

definitions =
  config:
    type: 'object'
    properties:
      'admin':
        $ref: 'module://@nikitajs/krb5/src/execute#/definitions/config/properties/admin'
      'enctypes':
        type: 'array', items: type: 'string'
        default: ['aes256-cts-hmac-sha1-96', 'aes128-cts-hmac-sha1-96', 'des3-cbc-sha1','arcfour-hmac']
        description: '''
        The enctypes used by krb5_server.
        '''
      'gid':
        $ref: 'module://@nikitajs/file/lib/index#/definitions/config/properties/gid'
      'keytab':
        type: 'string'
        description: '''
        Path to the file storing key entries.
        '''
      'mode':
        $ref: 'module://@nikitajs/file/lib/index#/definitions/config/properties/mode'
      'password':
        type: 'string'
        description: '''
        Password associated to this principal; required if no randkey is
        provided.
        '''
      'principal':
        type: 'string'
        description: '''
        Principal to be created.
        '''
      'realm':
        type: 'string'
        description: '''
        The realm the principal belongs to.
        '''
      'uid':
        $ref: 'module://@nikitajs/file/lib/index#/definitions/config/properties/uid'
    required: ['keytab', 'password', 'principal']

Handler

handler = ({config, tools: {log}}) ->
  if /^\S+@\S+$/.test config.principal
    config.realm ?= config.principal.split('@')[1]
  else
    throw Error 'Property "realm" is required in principal' unless config.realm
    config.principal = "#{config.principal}@#{config.realm}"
  entries = []
  princ_entries = []
  princ = {}
  command = null
  # Get keytab entries
  {$status, stdout, code} = await @execute
    $shy: true
    command: "echo -e 'rkt #{config.keytab}\nlist -e -t \n' | ktutil"
    code: [0, 1]
  if $status
    log message: "Principal exist in Keytab, check kvno validity", level: 'DEBUG'
    for line in utils.string.lines stdout
      continue unless match = /^\s*(\d+)\s*(\d+)\s+([\d\/:]+\s+[\d\/:]+)\s+(.*)\s*\(([\w|-]*)\)\s*$/.exec line
      [_, slot, kvno, timestamp, principal, enctype] = match
      kvno = parseInt kvno, 10
      entries.push
        slot: slot
        kvno: kvno
        timestamps: timestamp
        principal: principal.trim()
        enctype: enctype
    princ_entries = entries.filter((e) -> "#{e.principal}" is "#{config.principal}").reverse()
  # Get principal information and compare to keytab entries kvnos
  {$status, stdout} = await @krb5.execute
    $shy: true
    admin: config.admin
    command: "getprinc -terse #{config.principal}"
  if $status
    values = utils.string.lines(stdout)[1]
    # Check if a ticket exists for this
    throw Error "Principal does not exist: '#{config.principal}'" unless values
    values = values.split '\t'
    mdate = parseInt(values[2], 10) * 1000
    kvno = parseInt values[8], 10
    princ = mdate: mdate, kvno: kvno
  # read keytab and check kvno validities
  command = null
  tmp_keytab = "#{config.keytab}.tmp_nikita_#{Date.now()}"
  for enctype in config.enctypes
    entry = if princ_entries.filter( (entry) -> entry.enctype is enctype).length is 1 then entries.filter( (entry) -> entry.enctype is enctype)[0] else null
    #entries.filter( (entry) -> entry.enctype is enctype).length is 1
    # add_entry_command = "add_entry -password -p #{config.principal} -k #{princ.kvno} -e #{enctype}\n#{config.password}\n"
    if entry? and (entry?.kvno isnt princ.kvno)
      command ?= "echo -e 'rkt #{config.keytab}\n"
      # remove entry if kvno not identical
      log message: "Remove from Keytab kvno '#{entry.kvno}', principal kvno '#{princ.kvno}'", level: 'INFO'
      command += "delete_entry #{entry?.slot}\n"
  if entries.length > princ_entries.length
    if command?
      await @execute
        command: command + "wkt #{tmp_keytab}\nquit\n' | ktutil"
    if command?
      await @fs.move
        source: tmp_keytab
        target: config.keytab
  if (entries.length is princ_entries.length) and command?
    await @fs.remove
      target: config.keytab
  # write entries in keytab
  command = null
  for enctype in config.enctypes
    entry = if princ_entries.filter( (entry) -> entry.enctype is enctype).length is 1 then entries.filter( (entry) -> entry.enctype is enctype)[0] else null
    if (entry?.kvno isnt princ.kvno) or !entry?
      command ?= "echo -e '"
      command += "add_entry -password -p #{config.principal} -k #{princ.kvno} -e #{enctype}\n#{config.password}\n"
  if command?
    await @execute
      command: command + "wkt #{config.keytab}\n' | ktutil"
  # Keytab ownership and permissions
  if config.uid? or config.gid?
    await @fs.chown
      target: config.keytab
      uid: config.uid
      gid: config.gid
  return unless config.mode
  await @fs.chmod
    target: config.keytab
    mode: config.mode

Exports

module.exports =
  handler: handler
  metadata:
    global: 'krb5'
    definitions: definitions

Fields in 'getprinc -terse' output

princ-canonical-name princ-exp-time last-pw-change pw-exp-time princ-max-life modifying-princ-canonical-name princ-mod-date princ-attributes <=== This is the field you want princ-kvno princ-mkvno princ-policy (or 'None') princ-max-renewable-life princ-last-success princ-last-failed princ-fail-auth-count princ-n-key-data ver kvno data-type[0] data-type[1]

Dependencies

utils = require '@nikitajs/core/lib/utils'
{mutate} = require 'mixme'
Edit on GitHub
Navigate
About

Nikita is an open source project hosted on GitHub and developed by Adaltas.