ceph中提供了cephx认证机制,客户端到服务端的访问,服务段之间的互访等都提供了认证过程,当然这些都是可配的。本文主要介绍ceph中key的常见过程。
ceph中有两个要弄清的概念:
key和keyring: key标识一个密钥,一串字符;keyring用来存放密钥,也可以理解为一个文件。
创建keyring的过程:
下面这部分代码,从ceph_osd.cc的main方法中提取的。
if (mkkey) {
common_init_finish(g_ceph_context);
//创建KeyRing实例
KeyRing *keyring = KeyRing::create_empty();
if (!keyring) {
derr << "Unable to get a Ceph keyring." << dendl;
return 1;
}
EntityName ename(g_conf->name);
EntityAuth eauth;
//从keyring文件中加载KeyRing信息
int ret = keyring->load(g_ceph_context, g_conf->keyring);
if (ret == 0 &&
keyring->get_auth(ename, eauth)) {
derr << "already have key in keyring " << g_conf->keyring << dendl;
} else {
//创建Key
eauth.key.create(g_ceph_context, CEPH_CRYPTO_AES);
keyring->add(ename, eauth);
bufferlist bl;
//将KeyRing信息序列化到bl中(以普通文本的形式)
keyring->encode_plaintext(bl);
//保存到keyring文件中
int r = bl.write_file(g_conf->keyring.c_str(), 0600);
if (r)
derr << TEXT_RED << " ** ERROR: writing new keyring to " << g_conf->keyring
<< ": " << cpp_strerror(r) << TEXT_NORMAL << dendl;
else
derr << "created new key in keyring " << g_conf->keyring << dendl;
}
}
这段代码主要实现创建key的过程,首先它试图从给定的keyring文件中去加载,如果没有再去创建,创建成功之后,又以普通文本的形式保持到keyring文件中。
其中涉及的一些抽象结构:
1.KeyRing继承与KeyStore,KeyRing类中包含一个keys(map < EntityName, EntityAuth >)属性。
2.EntityAuth中包含auid(uint64_t)key(CryptoKey) caps(map< string, bufferlist >)
3.CryptoKey中有type(__u16) created(utime_t) secret(bufferptr) ckh(mutable ceph::shared_ptr < CryptoKeyHandler >)
4.CryptoAES 继承于CryptoHandler
创建Key的过程实现
加载keyring
从keyring文件中读取信息,然后反序列化到KeyRing实例中。
int KeyRing::load(CephContext *cct, const std::string &filename)
{
if (filename.empty())
¦ return -EINVAL;
bufferlist bl;
std::string err;
//从keyring文件中读取key
int ret = bl.read_file(filename.c_str(), &err);
if (ret < 0) {
¦ lderr(cct) << "error reading file: " << filename << ": " << err << dendl;
¦ return ret;
}
//将读取到的信息,反序列化到keys中
try {
¦ bufferlist::iterator iter = bl.begin();
¦ decode(iter);
}
catch (const buffer::error& err) {
¦ lderr(cct) << "error parsing file " << filename << dendl;
¦ return -EIO;
}
ldout(cct, 2) << "KeyRing::load: loaded key file " << filename << dendl;
return 0;
}
void KeyRing::decode(bufferlist::iterator& bl) {
__u8 struct_v;
bufferlist::iterator start_pos = bl;
try {
¦ ::decode(struct_v, bl);
¦ ::decode(keys, bl);
} catch (buffer::error& err) {
¦ keys.clear();
¦ decode_plaintext(start_pos);
}
}
上面如果发现keyring中没有,或者加载失败,则进入创建新key的过程。
创建key
int CryptoKey::create(CephContext *cct, int t)
{
CryptoHandler *ch = CryptoHandler::create(t);
if (!ch) {
¦ if (cct)
¦ ¦ lderr(cct) << "ERROR: cct->get_crypto_handler(type=" << t << ") returned NULL" << dendl;
¦ return -EOPNOTSUPP;
}
bufferptr s;
int r = ch->create(s);
delete ch;
if (r < 0)
¦ return r;
r = _set_secret(t, s);
if (r < 0)
¦ return r;
created = ceph_clock_now();
return r;
}
该法方法中创建了CryptoHandler来完成key的创建,深入到create方法中会发现该方法返回的是CryptoHandler的子类,由它来创建key。
创建CryptoAES实例。
CryptoHandler *CryptoHandler::create(int type)
{
switch (type) {
case CEPH_CRYPTO_NONE:
¦ return new CryptoNone;
case CEPH_CRYPTO_AES:
¦ return new CryptoAES;
default:
¦ return NULL;
}
}
创建一串字符来作为key
int CryptoAES::create(bufferptr& secret)
{
bufferlist bl;
int r = get_random_bytes(AES_KEY_LEN, bl);
if (r < 0)
¦ return r;
secret = buffer::ptr(bl.c_str(), bl.length());
return 0;
}
int get_random_bytes(char *buf, int len)
{
int fd = TEMP_FAILURE_RETRY(::open("/dev/urandom", O_RDONLY));
if (fd < 0)
return -errno;
int ret = safe_read_exact(fd, buf, len);
VOID_TEMP_FAILURE_RETRY(::close(fd));
return ret;
}
由以上代码可以知道key是由linux系统的/dev/urandom,随机生成。
设置key
int CryptoKey::_set_secret(int t, const bufferptr& s)
{
if (s.length() == 0) {
¦ secret = s;
¦ ckh.reset();
¦ return 0;
}
CryptoHandler *ch = CryptoHandler::create(t);
if (ch) {
¦ int ret = ch->validate_secret(s);
¦ if (ret < 0) {
¦ ¦ delete ch;
¦ ¦ return ret;
¦ }
¦ string error;
¦ ckh.reset(ch->get_key_handler(s, error));
¦ delete ch;
¦ if (error.length()) {
¦ ¦ return -EIO;
¦ }
} else {
¦ ¦ return -EOPNOTSUPP;
}
type = t;
secret = s;
return 0;
}
验证key的长度,创建CryptoAESKeyHandler,并生成对secret加密后的字符串。
int CryptoAES::validate_secret(const bufferptr& secret)
{
if (secret.length() < (size_t)AES_KEY_LEN) {
¦ return -EINVAL;
}
return 0;
}
CryptoKeyHandler *CryptoAES::get_key_handler(const bufferptr& secret,
string& error)
{
CryptoAESKeyHandler *ckh = new CryptoAESKeyHandler;
ostringstream oss;
if (ckh->init(secret, oss) < 0) {
¦ error = oss.str();
¦ delete ckh;
¦ return NULL;
}
return ckh;
}
创建secret的加密后的key。
int CryptoAESKeyHandler::init(const bufferptr& s, ostringstream& err) {
¦ secret = s;
¦ enc_key = new CryptoPP::AES::Encryption(
¦ ¦ (byte*)secret.c_str(), CryptoPP::AES::DEFAULT_KEYLENGTH);
¦ dec_key = new CryptoPP::AES::Decryption(
¦ ¦ (byte*)secret.c_str(), CryptoPP::AES::DEFAULT_KEYLENGTH);
¦ return 0;
}
到这里key就创建完成了,下面是将内存中的key导入的文件中(普通文本形式)。
将内存中的key序列化之后导出到keyring中
//将KeyRing实例中的信息,以普通文本的形式写入到bl中。
void KeyRing::encode_plaintext(bufferlist& bl)
{
std::ostringstream os;
print(os);
string str = os.str();
bl.append(str);
}
//将KeyRing实例中的信息,输出到out流中。
void KeyRing::print(ostream& out)
{
for (map<EntityName, EntityAuth>::iterator p = keys.begin();
¦ ¦ ¦p != keys.end();
¦ ¦ ¦++p) {
¦ out << "[" << p->first << "]" << std::endl;
¦ out << "\tkey = " << p->second.key << std::endl;
¦ if (p->second.auid != CEPH_AUTH_UID_DEFAULT)
¦ ¦ out << "\tauid = " << p->second.auid << std::endl;
¦ for (map<string, bufferlist>::iterator q = p->second.caps.begin();
¦q != p->second.caps.end();
¦++q) {
¦ ¦ bufferlist::iterator dataiter = q->second.begin();
¦ ¦ string caps;
¦ ¦ ::decode(caps, dataiter);
¦ ¦ out << "\tcaps " << q->first << " = \"" << caps << '"' << std::endl;
¦ }
}
}
这个方法就导出了平常在keyring文件中看到的key的形式。