- window에 redis 설치
- dev 용 환경변수 설정
- prod용 환경변수 설정
- 이러면 npm run start:dev -> dev.yaml 참조
- npm run start:prod 에서는 prod.yaml을 참조한다.
- 그런데, prod 용은 일관적으로 .env로 관리하고
- dev용만 따로 dev.yaml에 관리하면 될듯하다.
- redis config 설정
import { createClient } from 'redis';
import * as config from 'config';
export const RedisClient = createClient({
url: `redis://${config.get('redis-db').host}:${config.get('redis-db').port}`,
socket: {
reconnectStrategy: (retries) => {
if (retries > 10) {
console.error('Redis 연결 최대 재시도 횟수 초과');
return new Error('Redis 연결 실패');
}
return Math.min(retries * 100, 3000);
}
}
});
RedisClient.on('error', (err) => console.error('Redis 클라이언트 에러:', err));
RedisClient.on('connect', () => console.log('Redis에 연결되었습니다.'));
RedisClient.on('reconnecting', () => console.log('Redis에 재연결 중...'));
(async () => {
await RedisClient.connect();
})();
* 개발환경과 배포환경의 환경변수 구분 문제
- 방안1
- package.json에 환경을명시한다.
- redisconf를 동적으로 생성한다.
- dev환경에서는 config를 참조
- prod 환경에서는 .env를 참조
"start:dev": "cross-env NODE_ENV=development nest start --watch",
const getRedisConfig = () => {
const isDevelopment = process.env.NODE_ENV === 'development';
const redisHost = isDevelopment ? config.get('redis-db').host : process.env.REDIS_HOST;
const redisPort = isDevelopment ? config.get('redis-db').port : process.env.REDIS_PORT;
return {
host: redisHost,
port: redisPort
};
};
- 방안2
- .env는 개발자마다 가지고있음을 이용
- 깃헙의 secret env => prod 전용 env
- 개발자의 env 는 localhost로 작성하면 될것 같다.
* ssh 터널링 접속문제
- ssh 접근시 was에 열쇠 2개를 가지고 들어간다. (was용, db용)
- 그런데, ts 코드에서는 열쇠 2개를 어떻게 가지고 갈지에 대한 고민이 있었다.
- 논의결과, ssh접속시 key를 요청받을 것으로 예상.
- 내부 redis port인 6379 port를 열어두면 was에서 오는 요청에 대해 key 요청을 하지 않을것으로 예상!
* refactor : 반복되는 유효성 검증 분리
`validate-roomdecorator.ts` 파일의 코드는 데코레이터를 사용하여 WebSocket 핸들러 메서드가 호출되기 전에 게임 방이 유효한지 확인하는 기능을 추가합니다.
- `ValidateRoom` 함수는 데코레이터를 반환합니다.
- 데코레이터는 원래 메서드를 감싸는 새로운 메서드를 정의합니다.
- 새로운 메서드는 첫 번째 인수로 게임 방 ID를, 두 번째 인수로 클라이언트 소켓을 받습니다.
- 게임 방 ID를 사용하여 방이 존재하는지 확인합니다.
- 방이 존재하지 않으면 클라이언트에게 오류 메시지를 전송하고 메서드 실행을 중단합니다.
- 방이 존재하면 원래 메서드를 호출합니다.
이 데코레이터는 게임 방이 유효한지 확인하는 로직을 여러 메서드에 반복해서 작성하지 않도록 도와줍니다.
export function ValidateRoom() {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const client: Socket = args[1];
const room = this.rooms.get(args[0]);
if (!room) {
client.emit('error', '[ERROR] 존재하지 않는 게임 방입니다.');
return;
}
return originalMethod.apply(this, args);
};
return descriptor;
};
}
- 사용예 : room 의 유효성체크는 데코레이터가 해준다.
@SubscribeMessage(socketEvents.START_QUIZ_TIME)
@ValidateRoom()
handleStartQuizTime(
@MessageBody() startGameDto: StartGameDto,
@ConnectedSocket() client: Socket
): void {
const { gameId } = startGameDto;
const room = this.rooms.get(gameId);
* 서버 해킹 issued
- 추정원인 : ubuntu의 비밀번호를 123456으로 설정 => bot의 network scan에 걸려서 코인채굴기로 활용된것같다.
- 해결 : root로그인 막기 && user 도 비밀번호 로그인 막기 && pem 으로만 접속가능 && (nginx, node설치등 다시...)
- nginx의 user가 "nginx"로 되어있어서 index.html을 불러오는 과정에서 permission denied 오류발생
- 해결 : ubuntu로 바꾸니 일단 해결
* nginx.conf에 user는 왜 있을까?
nginx 설정 파일인 nginx.conf에는 user 라는 값이 있다. user는 nginx 프로세스를 실행할 os 계정을 명시한 값인데 보안상 root 계정으로 nginx 프로세스를 실행하는 걸 막기 위해 사용된다.
user [사용자명] [사용자그룹]
예를 들어
user user1
이렇게 설정하고 root 계정으로 nginx를 실행하면 nginx 프로세스는 root가 아닌 user1 계정으로 실행된다.
user 값은 주석처리 할 수도 있다.
#user user1
이 경우 nginx를 실행한 os 계정으로 프로세스가 실행된다.
이렇게 하는 이유는 결국 보안 때문인데 root 계정으로 nginx를 실행하는 걸 방지하기 위해서다.
nginx 프로세스가 root로 실행되면 위협이 될 수 있다.
예를 들어 nginx로 구동한 웹 사이트에 악의적인 스크립트를 업로드하는데 성공한다면 그 스크립트는 root 권한으로 실행할 수 있다. 이 스크립트는 root 권한을 가지고 있기 때문에 실행에 제한이 없어서 피해 범위가 넓다.
반면 일부 범위에만 권한을 가지고 있는 사용자라면 악성 스크립트가 영향을 줄 수 있는 범위는 줄어든다.
사실 이건 nginx 뿐만이 아니라 다른 프로세스에도 해당되는 이야기다. 때문에 왠만하면 프로세스는 root가 아닌 계정으로 실행하는 게 올바른 방향이다.
* 레퍼런스
https://binwrite.com/nginx-conf-user/
nginx.conf에 user는 왜 있을까? – 엔지니어의 기록
nginx 설정 파일인 nginx.conf에는 user 라는 값이 있다. user는 nginx 프로세스를 실행할 os 계정을 명시한 값인데 보안상 root 계정으로 nginx 프로세스를 실행하는 걸 막기 위해 사용된다. user [사용자명] [
binwrite.com