2
0
mirror of https://github.com/xcat2/confluent.git synced 2026-09-28 16:20:54 +00:00

Compare commits

...

26 Commits

Author SHA1 Message Date
Jarrod Johnson 45c13a3d46 Merge branch 'master' into nodesearch 2019-10-17 13:46:23 -04:00
Jarrod Johnson a04eea6927 Merge branch 'master' into nodesearch 2019-10-17 09:31:01 -04:00
Jarrod Johnson f46939b7ec Add missing comma to api attributes 2019-10-16 13:50:23 -04:00
Jarrod Johnson 17a8ab3211 Use python3 explicitly for building source
The source distrobution on CentOS7 does not work well
with python2.  Using python3 to build the dist for python2
and python3 fixes issues with extra data being missed in
packaging.
2019-10-16 13:25:21 -04:00
Jarrod Johnson 0fd4c3b2f7 Merge branch 'master' into nodesearch 2019-10-16 13:24:11 -04:00
Jarrod Johnson 829a5b08c1 Merge branch 'master' into nodesearch 2019-09-06 10:09:48 -04:00
Jarrod Johnson aa059c6a4d Amend some formatting 2019-08-12 16:19:35 -04:00
Jarrod Johnson 2e03b662ea Add a credential server implelmentation
This implements the api.armed logic and storing key when
using the clortho credential agent.
2019-08-12 15:31:24 -04:00
Jarrod Johnson 55a0aab548 Add node api key and arming
This is the groundwork for having node authentication.
The intent is for calling code to modify api.armed if
the administrator wants to opt into a one-time set of
credential.

This design as is currently does not fit a stateless
deploy model.  That may suggest an additional manual step
for a fully stateless model.  Alternatively adding support
for credential persistence through sealing to a node's TPM,
which would allow more freely retrievable node credentials.
2019-08-12 15:28:00 -04:00
Jarrod Johnson aaf5aebff7 Fix for tokens with null bytes
Since the server may employ the full range of byte values
in the echo token, use that length and the buffer to avoid
nulls truncating the token.
2019-08-12 15:24:51 -04:00
Jarrod Johnson c1abeaff04 Convert concept to IP based
This leaves the door open for routing if supported.

The server shall restrict IP_TTL to denote
acceptable distance from the manager to accept.
2019-08-09 16:43:48 -04:00
Jarrod Johnson 37d4543d24 Merge branch 'master' into nodesearch 2019-08-08 16:18:21 -04:00
Jarrod Johnson d13e286609 Merge branch 'master' into nodesearch 2019-07-30 14:46:43 -04:00
Jarrod Johnson a7c93627e2 Merge branch 'master' into nodesearch 2019-07-23 08:18:44 -04:00
Jarrod Johnson 6e5cfe69a8 Merge branch 'master' into nodesearch 2019-07-22 09:41:45 -04:00
Jarrod Johnson 626f1c16e2 Merge branch 'nodesearch' of github.com:jjohnson42/confluent into nodesearch 2019-07-19 14:10:13 -04:00
Jarrod Johnson 43480c2e3b Fix nodesensors -n with csv 2019-07-19 14:09:51 -04:00
Jarrod Johnson b91693a973 Merge branch 'master' into nodesearch 2019-07-17 13:05:20 -04:00
Jarrod Johnson da8b4d00d8 Merge branch 'master' into nodesearch 2019-07-16 18:46:52 -04:00
Jarrod Johnson b7b7fd82eb ECHO a packet back to manager
In a later phase, we will want assurances
that the neighbor table was populated.
Since here we have the sockaddr handy,
it makes a lot of sense to take the
opportunity to blind fire a packet back.
No reply is expected, just enough to
trigger arp/neighbor solicitation.
2019-07-16 07:51:24 -04:00
Jarrod Johnson 3789e43f35 Merge branch 'master' into nodesearch 2019-07-16 07:32:00 -04:00
Jarrod Johnson a325dcb423 Merge branch 'nodesearch' of github.com:jjohnson42/confluent into nodesearch 2019-07-15 11:09:29 -04:00
Jarrod Johnson c8d0009dac Avoid copernicus printout of more duplicate data
Separate v4 and v6 results for better chance of success in
dropping duplicate packets.
2019-07-15 11:09:20 -04:00
Jarrod Johnson 79f5dce6dc Implement node search for confluent
This is a viable client to find and get ones node identity.

Node credentials are a separate concern, to be handled later.
2019-07-15 11:09:20 -04:00
Jarrod Johnson 54f36e259f Avoid copernicus printout of more duplicate data
Separate v4 and v6 results for better chance of success in
dropping duplicate packets.
2019-07-15 10:30:36 -04:00
Jarrod Johnson acf67a6c81 Implement node search for confluent
This is a viable client to find and get ones node identity.

Node credentials are a separate concern, to be handled later.
2019-07-12 17:04:14 -04:00
6 changed files with 299 additions and 95 deletions
@@ -97,6 +97,15 @@ node = {
'description': ('Classification of node as server or switch'),
'validvalues': ('switch', 'server'),
},
'api.key': {
'description': ('Crypt of api key for self api requests by node'),
},
'api.armed': {
'description': ('Indicates whether an insecure api key request is allowed. '
'The format is an expiration time in ISO8601 format. When '
'the indicated time passes or the first time a node claims '
'the key, key grants will not be allowed.'),
},
#'id': {
# 'description': ('Numeric identifier for node')
#},
+87
View File
@@ -0,0 +1,87 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2019 Lenovo
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import confluent.config.configmanager as cfm
import datetime
import eventlet
import eventlet.green.socket as socket
import eventlet.greenpool
import os
class CredServer(object):
def __init__(self, bindhost='::', bindport=301, ttl=1):
self.srv = socket.socket(socket.AF_INET6)
self.srv.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl)
self.srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.srv.bind((bindhost, bindport))
self.srv.listen(32)
self.gpool = eventlet.greenpool.GreenPool(256)
self.cfm = cfm.ConfigManager(None)
self.runtime = eventlet.spawn(self.listen)
def listen(self):
while True:
client, info = self.srv.accept()
if info[1] > 1023:
client.close()
continue
self.gpool.spawn_n(self.handle_client, client)
def handle_client(self, client):
client.send('\xc2\xd1-\xa8\x80\xd8j\xba')
tlv = bytearray(client.recv(2))
if tlv[0] != 1:
client.close()
return
nodename = client.recv(tlv[1])
tlv = bytearray(client.recv(2))
apiarmed = self.cfm.get_node_attributes(nodename, 'api.armed')
apiarmed = apiarmed.get(nodename, {}).get('api.armed', {}).get('value', None)
if not apiarmed:
client.close()
return
now = datetime.datetime.utcnow()
expiry = datetime.datetime.strptime(apiarmed, "%Y-%m-%dT%H:%M:%SZ")
if now > expiry:
self.cfm.set_node_attributes({nodename: {'api.armed': ''}})
client.close()
return
client.send(b'\x02\x20')
rttoken = os.urandom(32)
client.send(rttoken)
client.send('\x00\x00')
tlv = bytearray(client.recv(2))
if tlv[0] != 3:
client.close()
return
echotoken = client.recv(tlv[1])
if echotoken != rttoken:
client.close()
return
tlv = bytearray(client.recv(2))
if tlv[0] != 4:
client.close()
return
echotoken = client.recv(tlv[1])
self.cfm.set_node_attributes({nodename: {'api.key': echotoken, 'api.armed': ''}})
client.recv(2) # drain end of message
client.send('\x05\x00') # report success
client.close()
if __name__ == '__main__':
a = CredServer()
while True:
eventlet.sleep(86400)
+4 -3
View File
@@ -65,7 +65,7 @@ import base64
import confluent.config.configmanager as cfm
import confluent.collective.manager as collective
import confluent.discovery.protocols.pxe as pxe
#import confluent.discovery.protocols.ssdp as ssdp
import confluent.discovery.protocols.ssdp as ssdp
import confluent.discovery.protocols.slp as slp
import confluent.discovery.handlers.imm as imm
import confluent.discovery.handlers.cpstorage as cpstorage
@@ -871,6 +871,8 @@ def get_nodename_from_chained_smms(cfg, handler, info):
nodename = newnodename
return nodename
def get_node_by_uuid(uuid):
return nodes_by_uuid.get(uuid, None)
def get_nodename_from_enclosures(cfg, info):
nodename = None
@@ -1228,8 +1230,7 @@ def start_detection():
if rechecker is None:
rechecktime = util.monotonic_time() + 900
rechecker = eventlet.spawn_after(900, _periodic_recheck, cfg)
# eventlet.spawn_n(ssdp.snoop, safe_detected)
eventlet.spawn_n(ssdp.snoop, None, None, ssdp, get_node_by_uuid)
def stop_autosense():
for watcher in list(autosensors):
@@ -30,9 +30,11 @@
import confluent.neighutil as neighutil
import confluent.util as util
import confluent.log as log
import eventlet.green.select as select
import eventlet.green.socket as socket
import struct
import traceback
mcastv4addr = '239.255.255.250'
mcastv6addr = 'ff02::c'
@@ -51,7 +53,7 @@ def scan(services, target=None):
yield rply
def snoop(handler, byehandler=None):
def snoop(handler, byehandler=None, protocol=None, uuidlookup=None):
"""Watch for SSDP notify messages
The handler shall be called on any service coming online.
@@ -67,6 +69,7 @@ def snoop(handler, byehandler=None):
# Normally, I like using v6/v4 agnostic socket. However, since we are
# dabbling in multicast wizardry here, such sockets can cause big problems,
# so we will have two distinct sockets
tracelog = log.Logger('trace')
known_peers = set([])
net6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
net6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
@@ -90,50 +93,73 @@ def snoop(handler, byehandler=None):
net6.bind(('', 1900))
peerbymacaddress = {}
while True:
newmacs = set([])
machandlers = {}
r, _, _ = select.select((net4, net6), (), (), 60)
neighutil.update_neigh()
while r:
for s in r:
(rsp, peer) = s.recvfrom(9000)
rsp = rsp.split('\r\n')
method, _, _ = rsp[0].split(' ', 2)
if method == 'NOTIFY':
ip = peer[0].partition('%')[0]
if ip not in neighutil.neightable:
continue
if peer in known_peers:
continue
mac = neighutil.neightable[ip]
known_peers.add(peer)
newmacs.add(mac)
if mac in peerbymacaddress:
peerbymacaddress[mac]['peers'].append(peer)
else:
peerbymacaddress[mac] = {
'hwaddr': mac,
'peers': [peer],
}
peerdata = peerbymacaddress[mac]
try:
newmacs = set([])
machandlers = {}
r, _, _ = select.select((net4, net6), (), (), 60)
neighutil.update_neigh()
while r:
for s in r:
(rsp, peer) = s.recvfrom(9000)
rsp = rsp.split('\r\n')
method, _, _ = rsp[0].split(' ', 2)
if method == 'NOTIFY':
ip = peer[0].partition('%')[0]
if ip not in neighutil.neightable:
continue
if peer in known_peers:
continue
mac = neighutil.neightable[ip]
known_peers.add(peer)
newmacs.add(mac)
if mac in peerbymacaddress:
peerbymacaddress[mac]['peers'].append(peer)
else:
peerbymacaddress[mac] = {
'hwaddr': mac,
'peers': [peer],
}
peerdata = peerbymacaddress[mac]
for headline in rsp[1:]:
if not headline:
continue
header, _, value = headline.partition(':')
header = header.strip()
value = value.strip()
if header == 'NT':
peerdata['service'] = value
elif header == 'NTS':
if value == 'ssdp:byebye':
machandlers[mac] = byehandler
elif value == 'ssdp:alive':
machandlers[mac] = None # handler
elif method == 'M-SEARCH':
if not uuidlookup:
continue
#ip = peer[0].partition('%')[0]
for headline in rsp[1:]:
if not headline:
continue
header, _, value = headline.partition(':')
header = header.strip()
value = value.strip()
if header == 'NT':
peerdata['service'] = value
elif header == 'NTS':
if value == 'ssdp:byebye':
machandlers[mac] = byehandler
elif value == 'ssdp:alive':
machandlers[mac] = handler
r, _, _ = select.select((net4, net6), (), (), 0.1)
for mac in newmacs:
thehandler = machandlers.get(mac, None)
if thehandler:
thehandler(peerbymacaddress[mac])
headline = headline.partition(':')
if len(headline) < 3:
continue
if headline[0] == 'ST' and headline[-1].startswith(' urn:xcat.org:service:confluent:'):
for query in headline[-1].split('/'):
if query.startswith('uuid='):
curruuid = query.split('=', 1)[1].lower()
node = uuidlookup(curruuid)
if not node:
break
reply = 'HTTP/1.1 200 OK\r\nNODENAME: {0}'.format(node)
s.sendto(reply, peer)
r, _, _ = select.select((net4, net6), (), (), 0.2)
for mac in newmacs:
thehandler = machandlers.get(mac, None)
if thehandler:
thehandler(peerbymacaddress[mac])
except Exception:
tracelog.log(traceback.format_exc(), ltype=log.DataTypes.event,
event=log.Events.stacktrace)
def _find_service(service, target):
+85 -44
View File
@@ -2,84 +2,125 @@
#include <arpa/inet.h>
#include <crypt.h>
#include <net/if.h>
#include <netdb.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define OUI_ETHERTYPE 0x88b7
#define MAXPACKET 1024
#define CHDR "\xa4\x8c\xdb\x30\x01"
int get_interface_index(int sock, char *interface) {
struct ifreq req;
memset(&req, 0, sizeof(req));
strncpy(req.ifr_name, interface, IFNAMSIZ);
if (ioctl(sock, SIOCGIFINDEX, &req) < 0) {
return -1;
}
return req.ifr_ifindex;
}
static const char cryptalpha[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./";
unsigned char* genpasswd() {
unsigned char* genpasswd(int len) {
unsigned char * passwd;
int urandom;
passwd = calloc(33, sizeof(char));
passwd = calloc(len + 1, sizeof(char));
urandom = open("/dev/urandom", O_RDONLY);
read(urandom, passwd, 32);
read(urandom, passwd, len);
close(urandom);
for (urandom = 0; urandom < 32; urandom++) {
passwd[urandom] = 0x30 + (passwd[urandom] >> 2);
for (urandom = 0; urandom < len; urandom++) {
passwd[urandom] = cryptalpha[passwd[urandom] >> 2];
}
return passwd;
}
int parse_macaddr(char* macaddr) {
unsigned char *curr;
unsigned char idx;
curr = strtok(macaddr, ":-");
idx = 0;
while (curr != NULL) {
macaddr[idx++] = strtoul(curr, NULL, 16);
curr = strtok(NULL, ":-");
}
}
int main(int argc, char* argv[]) {
int sock;
int iface;
unsigned char currlen, currtype;
unsigned char* passwd;
unsigned char* cryptedpass;
unsigned char* macaddr;
struct timeval timeout;
struct addrinfo hints;
struct addrinfo *addrs;
struct addrinfo *curr;
struct sockaddr_in net4bind;
struct sockaddr_in net6bind;
unsigned char buffer[MAXPACKET];
memset(&hints, 0, sizeof(struct addrinfo));
memset(&net4bind, 0, sizeof(struct sockaddr_in));
memset(&net6bind, 0, sizeof(struct sockaddr_in));
memset(&buffer, 0, MAXPACKET);
memset(&timeout, 0, sizeof(struct timeval));
timeout.tv_sec = 10;
net4bind.sin_port = htons(302);
net6bind.sin_port = htons(302);
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
passwd = genpasswd();
passwd = genpasswd(32);
memset(buffer, 0, MAXPACKET);
strncpy(buffer, "$5$", 3);
cryptedpass = genpasswd(8);
strncpy(buffer + 3, cryptedpass, 8);
free(cryptedpass);
cryptedpass = crypt(passwd, buffer);
if (argc < 3) {
fprintf(stderr, "Missing interface name and target MAC\n");
fprintf(stderr, "Missing node name and manager\n");
exit(1);
}
printf("%s\n", argv[2]);
parse_macaddr(argv[2]);
printf("%s\n", argv[2]);
sock = socket(AF_PACKET, SOCK_DGRAM, htons(OUI_ETHERTYPE));
if (sock < 0) {
fprintf(stderr, "Unable to open socket (run as root?)\n");
sock = getaddrinfo(argv[2], "301", &hints, &addrs);
if (sock != 0) {
fprintf(stderr, "Error trying to resolve %s\n", argv[2]);
exit(1);
}
iface = get_interface_index(sock, argv[1]);
if (iface < 0) {
fprintf(stderr, "Unable to find specified interface '%s'\n", argv[1]);
for (curr = addrs; curr != NULL; curr = curr->ai_next) {
sock = socket(curr->ai_family, curr->ai_socktype, curr->ai_protocol);
if (sock < 0) continue;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
if (curr->ai_family == AF_INET) {
bind(sock, (struct sockaddr*)&net4bind, sizeof(struct sockaddr_in));
} else if (curr->ai_family == AF_INET6) {
bind(sock, (struct sockaddr*)&net6bind, sizeof(struct sockaddr_in6));
} else {
continue;
}
if (connect(sock, curr->ai_addr, curr->ai_addrlen) == 0) break;
}
if (curr == NULL) {
fprintf(stderr, "Unable to reach %s\n", argv[2]);
exit(1);
}
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
freeaddrinfo(addrs);
read(sock, buffer, 8);
if (memcmp(buffer, "\xc2\xd1-\xa8\x80\xd8j\xba", 8) != 0) {
fprintf(stderr, "Unrecognized server\n");
exit(1);
}
dprintf(sock, "\x01%c%s", strlen(argv[1]), argv[1]);
write(sock, "\x00\x00", 2);
memset(buffer, 0, MAXPACKET);
read(sock, buffer, 2);
while (buffer[0] != 255) {
currtype = buffer[0];
currlen = buffer[1];
memset(buffer, 0, MAXPACKET);
if (currlen) {
read(sock, buffer, currlen); // Max is 255, well under MAX_PACKET
}
if (currtype == 2) {
dprintf(sock, "\x03%c", currlen);
write(sock, buffer, currlen);
dprintf(sock, "\x04%c%s", strlen(cryptedpass), cryptedpass);
write(sock, "\x00\x00", 2);
} else if (currtype == 5) {
printf(passwd);
printf("\n");
exit(0);
}
buffer[0] = 255;
read(sock, buffer, 2);
}
fprintf(stderr, "Password was not accepted\n");
exit(1);
}
+46 -6
View File
@@ -68,7 +68,11 @@ int main(int argc, char* argv[]) {
struct sockaddr_in6 addr, dst;
struct sockaddr_in addr4, dst4;
char msg[1024];
char *nodenameidx;
char nodename[1024];
char lastnodename[1024];
char lastmsg[1024];
char last6msg[1024];
int ifidx, offset;
fd_set rfds;
struct timeval tv;
@@ -80,6 +84,10 @@ int main(int argc, char* argv[]) {
memset(&addr, 0, sizeof(addr));
memset(&dst, 0, sizeof(dst));
memset(&dst4, 0, sizeof(dst4));
memset(nodename, 0, 1024);
memset(lastnodename, 0, 1024);
memset(lastmsg, 0, 1024);
memset(last6msg, 0, 1024);
addr.sin6_family = AF_INET6;
addr.sin6_addr = in6addr_any;
addr.sin6_port = htons(190);
@@ -141,20 +149,52 @@ int main(int argc, char* argv[]) {
if (ifidx == -1) perror("Unable to select");
if (ifidx) {
if (FD_ISSET(n4, &rfds)) {
recvfrom(n4, msg, 1024, 0, (struct sockaddr *)&dst4, &dst4size);
memset(msg, 0, 1024);
/* Deny packet access to the last 24 bytes to assure null */
recvfrom(n4, msg, 1000, 0, (struct sockaddr *)&dst4, &dst4size);
if (nodenameidx = strstr(msg, "NODENAME: ")) {
nodenameidx += 10;
strncpy(nodename, nodenameidx, 1024);
nodenameidx = strstr(nodenameidx, "\r");
if (nodenameidx) { nodenameidx[0] = 0; }
if (strncmp(lastnodename, nodename, 1024) != 0) {
printf("NODENAME: %s\n", nodename);
strncpy(lastnodename, nodename, 1024);
}
}
memset(msg, 0, 1024);
inet_ntop(dst4.sin_family, &dst4.sin_addr, msg, dst4size);
/* Take measure from printing out the same ip twice in a row */
if (strncmp(lastmsg, msg, 1024) != 0) {
printf("%s\n", msg);
sendto(n4, "PING", 4, 0, (const struct sockaddr *)&dst4, dst4size);
printf("MANAGER: %s\n", msg);
strncpy(lastmsg, msg, 1024);
}
}
if (FD_ISSET(ns, &rfds)) {
recvfrom(ns, msg, 1024, 0, (struct sockaddr *)&dst, &dstsize);
memset(msg, 0, 1024);
/* Deny packet access to the last 24 bytes to assure null */
recvfrom(ns, msg, 1000, 0, (struct sockaddr *)&dst, &dstsize);
if (nodenameidx = strstr(msg, "NODENAME: ")) {
nodenameidx += 10;
strncpy(nodename, nodenameidx, 1024);
nodenameidx = strstr(nodenameidx, "\r");
if (nodenameidx) { nodenameidx[0] = 0; }
if (strncmp(lastnodename, nodename, 1024) != 0) {
printf("NODENAME: %s\n", nodename);
strncpy(lastnodename, nodename, 1024);
}
}
memset(msg, 0, 1024);
inet_ntop(dst.sin6_family, &dst.sin6_addr, msg, dstsize);
if (strncmp(lastmsg, msg, 1024) != 0) {
printf("%s\n", msg);
strncpy(lastmsg, msg, 1024);
if (strncmp(last6msg, msg, 1024) != 0) {
sendto(ns, "PING", 4, 0, (const struct sockaddr *)&dst, dstsize);
printf("MANAGER: %s", msg);
if (strncmp(msg, "fe80::", 6) == 0) {
printf("%%%u", dst.sin6_scope_id);
}
printf("\n");
strncpy(last6msg, msg, 1024);
}
}
}