안녕하세요, 저는 3년째 AI API 게이트웨이 인프라를 구축하고 운영하는 백엔드 엔지니어입니다. 이번 글에서는 HolySheep AI의 RBAC + ABAC 하이브리드 권한 모델을 활용한 실전 접근 제어 구현 방법을 다룹니다. HolySheep AI의 RESTful API를 기반으로 한 역할 기반 + 속성 기반 접근 제어의 설계와 구현을 단계별로 설명드리겠습니다.
왜 RBAC + ABAC 하이브리드 모델인가?
단순한 RBAC만으로는 세밀한 권한 관리가 어렵습니다. 예를 들어, "개발자" 역할이더라도 특정 모델(GPT-4.1)에만 접근 가능하게 하거나, 부서별 사용량 한도를 다르게 설정해야 하는 경우가 있습니다. ABAC를 결합하면 속성(사용자 특성, 리소스 메타데이터, 환경 컨텍스트)을 기반으로 동적으로 권한을 판단할 수 있습니다.
핵심 개념 정리
- RBAC (Role-Based Access Control): 역할 기반 접근 제어 — 사용자에게 역할을 부여하고, 역할에 권한을 매핑
- ABAC (Attribute-Based Access Control): 속성 기반 접근 제어 — 사용자 속성, 리소스 속성, 환경 속성을 조합하여 접근 결정
- 하이브리드 모델: RBAC의 간결한 역할 관리 + ABAC의 세밀한 속성 제어
HolySheep AI 권한 모델 아키텍처
HolySheep AI는 API 키 단위의 권한 관리를 지원하며, 각 API 키에 대한 RBAC 역할 할당과 ABAC 속성 필터를 적용할 수 있습니다. 이를 통해 기업 환경에서 요구하는 세분화된 접근 제어를 구현할 수 있습니다.
프로젝트 설정 및 환경 구성
# 프로젝트 디렉토리 생성 및 초기화
mkdir ai-access-control && cd ai-access-control
npm init -y
필수 의존성 설치
npm install express helmet cors express-rate-limit
npm install jsonwebtoken bcryptjs uuid
npm install dotenv
HolySheep AI SDK 설치 (선택사항)
npm install @holysheep/ai-sdk
프로젝트 구조 생성
mkdir -p src/{middleware,routes,services,models,config}
touch src/index.js
HolySheep AI API 키 생성 및 권한 설정
먼저 HolySheep AI 콘솔에서 API 키를 생성하고 역할별 권한을 설정해야 합니다. HolySheep AI는 로컬 결제(해외 신용카드 불필요)을 지원하므로 빠르게 시작할 수 있습니다.
# .env 파일 설정
HolySheep AI API 설정
HOLYSHEEP_API_KEY=YOUR_HOLYSHEEP_API_KEY
HOLYSHEEP_BASE_URL=https://api.holysheep.ai/v1
HOLYSHEEP_ORG_ID=your_org_id
JWT 시크릿 키
JWT_SECRET=your_super_secret_jwt_key_change_in_production
서버 설정
PORT=3000
NODE_ENV=development
RBAC + ABAC 권한 시스템 구현
1. 역할 정의 및 권한 매핑
// src/config/roles.js
// HolySheep AI의 RBAC 역할 정의
const ROLES = {
ADMIN: 'admin',
DEVELOPER: 'developer',
ANALYST: 'analyst',
READONLY: 'readonly'
};
const PERMISSIONS = {
// 모델 접근 권한
MODEL_ACCESS: {
GPT4: 'gpt4',
CLAUDE: 'claude',
GEMINI: 'gemini',
DEEPSEEK: 'deepseek'
},
// 작업 유형 권한
TASK_TYPES: {
CHAT: 'chat',
COMPLETION: 'completion',
EMBEDDING: 'embedding',
IMAGE: 'image'
},
// 관리 권한
ADMIN_ACTIONS: {
CREATE_KEY: 'create_api_key',
REVOKE_KEY: 'revoke_api_key',
VIEW_USAGE: 'view_usage',
MANAGE_ROLES: 'manage_roles'
}
};
// 역할별 권한 매핑 (RBAC)
const ROLE_PERMISSIONS = {
[ROLES.ADMIN]: {
models: ['gpt4', 'claude', 'gemini', 'deepseek'],
tasks: ['chat', 'completion', 'embedding', 'image'],
admin: ['create_api_key', 'revoke_api_key', 'view_usage', 'manage_roles'],
budgetLimit: null, // 무제한
rateLimit: 10000
},
[ROLES.DEVELOPER]: {
models: ['gpt4', 'claude', 'gemini', 'deepseek'],
tasks: ['chat', 'completion', 'embedding'],
admin: [],
budgetLimit: 100000, // $100 USD
rateLimit: 1000
},
[ROLES.ANALYST]: {
models: ['gpt4', 'gemini'],
tasks: ['chat', 'embedding'],
admin: [],
budgetLimit: 50000, // $50 USD
rateLimit: 500
},
[ROLES.READONLY]: {
models: ['gpt4'],
tasks: ['chat'],
admin: [],
budgetLimit: 10000, // $10 USD
rateLimit: 100
}
};
module.exports = { ROLES, PERMISSIONS, ROLE_PERMISSIONS };
2. ABAC 속성 시스템 구현
// src/services/abacService.js
// ABAC (Attribute-Based Access Control) 속성 평가 엔진
const ENVIRONMENT_ATTRIBUTES = {
// 시간 기반 속성
getTimeWindow: () => {
const hour = new Date().getHours();
if (hour >= 9 && hour < 18) return 'business_hours';
if (hour >= 18 && hour < 22) return 'evening';
return 'night';
},
// IP 기반 속성 (실제 구현 시 IP 라이브러리 사용)
getIpCategory: (ip) => {
const internalRanges = ['192.168.', '10.', '172.16.', '172.31.'];
const isInternal = internalRanges.some(range => ip.startsWith(range));
return isInternal ? 'internal' : 'external';
}
};
class ABACEngine {
constructor() {
this.policies = [];
}
// ABAC 정책 등록
registerPolicy(policy) {
this.policies.push({
id: policy.id,
effect: policy.effect, // 'permit' or 'deny'
target: policy.target,
conditions: policy.conditions || []
});
}
// 속성 추출
extractAttributes(user, resource, environment) {
return {
// 사용자 속성 (Subject Attributes)
subject: {
userId: user.id,
role: user.role,
department: user.department,
clearanceLevel: user.clearanceLevel || 1,
apiKeyId: user.activeApiKeyId,
createdAt: user.createdAt
},
// 리소스 속성 (Resource Attributes)
resource: {
model: resource.model,
taskType: resource.taskType,
costPerToken: resource.costPerToken,
dataClassification: resource.dataClassification || 'public'
},
// 환경 속성 (Environment Attributes)
environment: {
timeWindow: ENVIRONMENT_ATTRIBUTES.getTimeWindow(),
ipCategory: ENVIRONMENT_ATTRIBUTES.getIpCategory(environment.ip),
requestCount: environment.requestCount || 1,
totalSpent: environment.totalSpent || 0
}
};
}
// ABAC 정책 평가
evaluatePolicies(attributes) {
let decision = 'permit'; // 기본값: 허용
for (const policy of this.policies) {
if (this.matchesTarget(policy.target, attributes)) {
if (this.evaluateConditions(policy.conditions, attributes)) {
if (policy.effect === 'deny') {
return { decision: 'deny', policyId: policy.id };
}
decision = 'permit';
}
}
}
return { decision };
}
// 타겟 매칭
matchesTarget(target, attributes) {
if (!target) return true;
if (target.roles && !target.roles.includes(attributes.subject.role)) {
return false;
}
if (target.models && !target.models.includes(attributes.resource.model)) {
return false;
}
if (target.maxCost && attributes.resource.costPerToken > target.maxCost) {
return false;
}
return true;
}
// 조건 평가
evaluateConditions(conditions, attributes) {
if (!conditions || conditions.length === 0) return true;
return conditions.every(condition => {
const attrValue = this.getAttributeValue(condition.attribute, attributes);
switch (condition.operator) {
case 'eq':
return attrValue === condition.value;
case 'neq':
return attrValue !== condition.value;
case 'gt':
return attrValue > condition.value;
case 'lt':
return attrValue < condition.value;
case 'in':
return condition.value.includes(attrValue);
case 'notIn':
return !condition.value.includes(attrValue);
case 'contains':
return String(attrValue).includes(condition.value);
default:
return false;
}
});
}
// 중첩 속성 값 조회
getAttributeValue(path, attributes) {
const parts = path.split('.');
let current = attributes;
for (const part of parts) {
if (current && typeof current === 'object' && part in current) {
current = current[part];
} else {
return undefined;
}
}
return current;
}
}
module.exports = { ABACEngine, ENVIRONMENT_ATTRIBUTES };
3. 하이브리드 권한 미들웨어
// src/middleware/hybridAuth.js
// RBAC + ABAC 하이브리드 권한 검증 미들웨어
const { ROLE_PERMISSIONS, ROLES } = require('../config/roles');
const { ABACEngine } = require('../services/abacService');
class HybridAccessControl {
constructor() {
this.abacEngine = new ABACEngine();
this.initializeDefaultPolicies();
}
// 기본 ABAC 정책 설정
initializeDefaultPolicies() {
// 높은 비용 모델은 높은 클리어런스 레벨 필요
this.abacEngine.registerPolicy({
id: 'high-cost-model-restriction',
effect: 'deny',
target: {
models: ['gpt4', 'claude']
},
conditions: [
{ attribute: 'subject.clearanceLevel', operator: 'lt', value: 3 }
]
});
// 야간 시간에는 읽기 전용 작업만 허용
this.abacEngine.registerPolicy({
id: 'night-time-restriction',
effect: 'deny',
target: {},
conditions: [
{ attribute: 'environment.timeWindow', operator: 'eq', value: 'night' },
{ attribute: 'resource.taskType', operator: 'in', value: ['chat', 'completion'] }
]
});
// 외부 IP에서 민감 데이터 분류 리소스 접근 차단
this.abacEngine.registerPolicy({
id: 'external-sensitive-data',
effect: 'deny',
target: {},
conditions: [
{ attribute: 'environment.ipCategory', operator: 'eq', value: 'external' },
{ attribute: 'resource.dataClassification', operator: 'eq', value: 'confidential' }
]
});
// 예산 초과 시 모든 요청 차단
this.abacEngine.registerPolicy({
id: 'budget-exceeded',
effect: 'deny',
target: {},
conditions: [
{ attribute: 'environment.totalSpent', operator: 'gte', value: 100000 }
]
});
}
// RBAC 권한 확인
checkRBAC(userRole, requiredPermission) {
const roleConfig = ROLE_PERMISSIONS[userRole];
if (!roleConfig) {
return { allowed: false, reason: 'Unknown role' };
}
// admin 역할은 모든 권한 보유
if (userRole === ROLES.ADMIN) {
return { allowed: true };
}
// 모델 접근 권한 확인
if (requiredPermission.model && !roleConfig.models.includes(requiredPermission.model)) {
return { allowed: false, reason: Model ${requiredPermission.model} not permitted for role ${userRole} };
}
// 작업 유형 권한 확인
if (requiredPermission.task && !roleConfig.tasks.includes(requiredPermission.task)) {
return { allowed: false, reason: Task ${requiredPermission.task} not permitted for role ${userRole} };
}
// 관리 권한 확인
if (requiredPermission.admin && !roleConfig.admin.includes(requiredPermission.admin)) {
return { allowed: false, reason: Admin action ${requiredPermission.admin} not permitted };
}
return { allowed: true };
}
// 하이브리드 접근 제어 미들웨어 생성
createMiddleware(options = {}) {
return async (req, res, next) => {
try {
// 1. 요청에서 사용자 및 리소스 정보 추출
const user = req.user; // JWT에서 디코딩된 사용자 정보
const resource = {
model: req.body.model || req.query.model,
taskType: this.inferTaskType(req),
costPerToken: this.getModelCost(req.body.model),
dataClassification: req.headers['x-data-classification'] || 'public'
};
const environment = {
ip: req.ip || req.connection.remoteAddress,
requestCount: parseInt(req.headers['x-request-count'] || '1'),
totalSpent: parseInt(req.headers['x-total-spent'] || '0')
};
// 2. RBAC 체크 (첫 번째 필터)
const requiredPermission = {
model: resource.model,
task: resource.taskType,
admin: req.headers['x-admin-action']
};
const rbacResult = this.checkRBAC(user.role, requiredPermission);
if (!rbacResult.allowed) {
return res.status(403).json({
error: 'Access Denied',
reason: rbacResult.reason,
layer: 'RBAC'
});
}
// 3. ABAC 체크 (두 번째 필터)
const attributes = this.abacEngine.extractAttributes(user, resource, environment);
const abacResult = this.abacEngine.evaluatePolicies(attributes);
if (abacResult.decision === 'deny') {
return res.status(403).json({
error: 'Access Denied',
reason: 'ABAC policy violation',
policyId: abacResult.policyId,
layer: 'ABAC'
});
}
// 4. Rate Limit 체크
const roleConfig = ROLE_PERMISSIONS[user.role];
if (roleConfig.rateLimit) {
const currentRate = await this.getCurrentRateLimit(user.id);
if (currentRate >= roleConfig.rateLimit) {
return res.status(429).json({
error: 'Rate limit exceeded',
limit: roleConfig.rateLimit,
current: currentRate
});
}
}
// 5. Budget 체크
if (roleConfig.budgetLimit && environment.totalSpent >= roleConfig.budgetLimit) {
return res.status(402).json({
error: 'Budget exceeded',
limit: roleConfig.budgetLimit,
spent: environment.totalSpent
});
}
// 6. 모든 검증을 통과한 경우 요청 속성 주입
req.accessContext = {
userRole: user.role,
permissions: roleConfig,
attributes
};
next();
} catch (error) {
console.error('Hybrid Access Control Error:', error);
return res.status(500).json({ error: 'Access control evaluation failed' });
}
};
}
// 작업 유형 추론
inferTaskType(req) {
if (req.body.messages) return 'chat';
if (req.body.prompt) return 'completion';
if (req.body.input) return 'embedding';
if (req.body.image) return 'image';
return 'unknown';
}
// 모델 비용 조회 (HolySheep AI 가격 기준)
getModelCost(model) {
const costs = {
'gpt-4': 30, // $30/MTok
'gpt-4-turbo': 15, // $15/MTok
'gpt-4.1': 8, // $8/MTok
'claude-3-opus': 75,
'claude-3-sonnet': 15, // $15/MTok
'claude-3.5-sonnet': 15,
'gemini-pro': 5,
'gemini-2.5-flash': 2.5, // $2.50/MTok
'deepseek-chat': 0.42 // $0.42/MTok
};
return costs[model] || 10;
}
// 현재 rate limit 조회 (실제 구현 시 Redis 등 사용)
async getCurrentRateLimit(userId) {
// 임시 구현
return 0;
}
}
module.exports = new HybridAccessControl();
4. HolySheep AI API 호출 통합
// src/services/aiGatewayService.js
// HolySheep AI API 통합 서비스
const axios = require('axios');
class AIGatewayService {
constructor() {
this.baseURL = process.env.HOLYSHEEP_BASE_URL || 'https://api.holysheep.ai/v1';
}
// HolySheep AI API 클라이언트 생성
createClient(apiKey) {
return axios.create({
baseURL: this.baseURL,
headers: {
'Authorization': Bearer ${apiKey},
'Content-Type': 'application/json'
},
timeout: 30000
});
}
// 채팅 완료 요청 (HolySheep AI 경유)
async chatCompletion(apiKey, params, accessContext) {
const client = this.createClient(apiKey);
try {
const startTime = Date.now();
const response = await client.post('/chat/completions', {
model: params.model,
messages: params.messages,
temperature: params.temperature || 0.7,
max_tokens: params.max_tokens || 2048,
stream: params.stream || false
});
const latency = Date.now() - startTime;
return {
success: true,
data: response.data,
metadata: {
latency,
model: params.model,
role: accessContext.userRole,
costEstimate: this.estimateCost(params.model, response.data.usage)
}
};
} catch (error) {
throw this.handleError(error);
}
}
// 임베딩 생성
async createEmbedding(apiKey, params) {
const client = this.createClient(apiKey);
try {
const response = await client.post('/embeddings', {
model: params.model || 'text-embedding-3-small',
input: params.input
});
return {
success: true,
data: response.data
};
} catch (error) {
throw this.handleError(error);
}
}
// 비용 추정
estimateCost(model, usage) {
const inputCost = (usage.prompt_tokens / 1000000) * this.getModelCost(model);
const outputCost = (usage.completion_tokens / 1000000) * this.getModelCost(model);
return inputCost + outputCost;
}
// 모델 비용 조회
getModelCost(model) {
const costs = {
'gpt-4': 30,
'gpt-4-turbo': 15,
'gpt-4.1': 8,
'claude-3-opus': 75,
'claude-3-sonnet': 15,
'claude-3.5-sonnet': 15,
'gemini-pro': 5,
'gemini-2.5-flash': 2.5,
'deepseek-chat': 0.42,
'deepseek-v3': 0.42
};
return costs[model] || 10;
}
// 에러 처리
handleError(error) {
if (error.response) {
const { status, data } = error.response;
switch (status) {
case 401:
return new Error('Invalid API key or unauthorized');
case 403:
return new Error('Access forbidden: ' + (data.message || 'Permission denied'));
case 429:
return new Error('Rate limit exceeded');
case 500:
return new Error('HolySheep AI service error');
default:
return new Error(API Error: ${status} - ${JSON.stringify(data)});
}
}
return new Error('Network error: ' + error.message);
}
}
module.exports = new AIGatewayService();
5. API 라우트 구현
// src/routes/aiRoutes.js
// AI API 라우트
const express = require('express');
const router = express.Router();
const aiGateway = require('../services/aiGatewayService');
const hybridAuth = require('../middleware/hybridAuth');
// HolySheep AI 채팅 완료 엔드포인트
router.post('/chat',
hybridAuth.createMiddleware(),
async (req, res) => {
try {
const { model, messages, temperature, max_tokens, stream } = req.body;
if (!model || !messages) {
return res.status(400).json({
error: 'Missing required fields: