네트워크 보안/네트워크

strongswan #2 starter -> charon launch, config update

uzguns 2024. 11. 9. 12:30

StrongSwan starter는 Charon 프로세스를 실행하고 구성 파일을 읽어 IKE/IPsec 연결 메세지를 charon 데몬으로 전달하는 역할을 한다.

 

  • starter: strongswan/src/starter/
      • 바이너리 위치: /usr/libexec/ipsec/starter
  • charon: strongswan/src/charon/

 

 

Starter 실행 후 초기화 과정

Usage: starter [--nofork] [--auto-update <sec>]
               [--debug|--debug-more|--debug-all|--nolog]
               [--attach-gdb] [--daemon <name>]
               [--conf <path to ipsec.conf>]

 

1. library_init()

  • starter가 실행되면 가장 먼저 library_init()을 호출하여 기본 라이브러리를 초기화한다.

2. private_library_t 구조체 생성

  • private_settings와 printf_hook_t를 초기화

3. 설정 파일 로드

  • this->public.settings->load_files() 함수를 호출하여 설정 파일들을 로드한다.
  • 설정 파일 경로로 this->public.conf를 사용하며, 일반적으로 이 경로는 /etc/strongswan.conf를 가리킨다.
#file strongswan.conf

charon {
    load_modular = yes
    plugins {
        include strongswan.d/charon/*.conf
    }
}
  • load_modular = yes: charon이 모듈 방식으로 플러그인을 로드하도록 설정
  • plugins: strongSwan에서 사용하는 플러그인들을 설정
    • include strongswan.d/charon/*.conf: strongswan.d/charon/ 디렉토리에 있는 모든 .conf 파일을 포함

4. load_files 함수는 /etc/strongswan.conf 파일을 읽어들이고, strongswan.d 디렉토리 하위의 .conf 파일들을 로드한다.

  • 여기에는 strongswan.conf, charon.conf, charon-logging.conf, pki.conf, pool.conf, scepclient.conf, starter.conf, swanctl.conf 등이 포함
  • start.conf와 같은 추가 설정 파일도 필요에 따라 이 위치에서 로드

2. streams 생성

private_library_t *this;
this->public.streams = stream_manager_create();
  • private_library_t 구조체의 streams 멤버를 초기화한다.
  • stream_manager_create() 함수를 호출하여 스트림 관리자를 생성하고, 이를 this->public.streams에 할당한다.

 

Update configuration

1. command line argument parsing

--conf 옵션

  • ipsec.conf 설정 파일 경로를 지정. 기본적으로 /etc/strongswan.d/starter.conf에서 config_file 항목을 참조하며, starter.conf 파일에 명시된 설정을 사용

2. ipsec.conf 파일 읽기 및 signal 처리 함수 설정

cfg = confread_load(config_file);

 

Start Charon daemon

 

1. starter_start_charon 호출하여 charon process 시작

 

2. starter_stroke_configure 호출하여 ipsec.conf 설정 넘겨줌

 

 

 

3. stroke_add_conn() 호출하여 connection 추가

  • STR_ADD_CONN 메시지를 처리
  • 메시지를 해석하고 this->config->add()를 호출하여 설정을 추가
void stroke_add_conn(stroke_socket_t *this, stroke_msg_t *msg)
{
    // 메시지를 해석하고 IKE, peer 및 child 설정을 생성
    ike_cfg_t *ike_cfg = build_ike_cfg(this, msg);
    peer_cfg_t *peer_cfg = build_peer_cfg(this, ike_cfg);
    child_cfg_t *child_cfg = build_child_cfg(this, msg);

    // 설정 정보를 private_stroke_config_t 구조체의 리스트에 추가
    this->config->add(this->config, ike_cfg, peer_cfg, child_cfg);
    
    return send_stroke_msg(&msg);
}
  • conf 데이터를 읽고 stroke를 통해 전송한다.
    • charon 데몬에 전달할 구조체로 stroke_msg_t 구조체를 msg 변수로 선언하고 초기화
      • msg.type을 STR_ADD_CONN으로 설정(새로운 연결을 추가하는 메시지)
      • IKE 및 ESP 암호화 알고리즘, DPD 설정, 재키 설정, 터널 모드, MobIKE 지원 여부 등 추가
      • left와 right의 endpoint 설정 (starter_stroke_add_end)
      • 인증 방법(authby: leftauth or rightauth, rsa, psk, xauthrsasig..) 설정

4. add()

  • this->config->add()는 stroke_config.c의 add() 함수
  • 이 함수는 각종 설정 정보를 빌드하여 private_stroke_config_t의 리스트에 연결
void add(private_stroke_config_t *this, ike_cfg_t *ike_cfg, peer_cfg_t *peer_cfg, child_cfg_t *child_cfg)
{
    // 각 설정 정보를 빌드하고 리스트에 추가
    this->ike_configs->insert_last(this->ike_configs, ike_cfg);
    this->peer_configs->insert_last(this->peer_configs, peer_cfg);
    this->child_configs->insert_last(this->child_configs, child_cfg);
}

5. send_stroke_msg(&msg) 함수에서 charon.ctl 소켓을 통해 charon 데몬으로 전송

#define CHARON_CTL_FILE IPSEC_PIDDIR "/charon.ctl"

ctl_addr.sun_family = AF_UNIX;
strcpy(ctl_addr.sun_path, CHARON_CTL_FILE);

int sock = socket(AF_UNIX, SOCK_STREAM, 0);

if (connect(sock, (struct sockaddr *)&ctl_addr, offsetof(struct sockaddr_un, sun_path) + strlen(ctl_addr.sun_path)) < 0)
{
    close(sock);
    return -1;
}

/* send message */
if (write(sock, msg, msg->length) != msg->length)
{
    close(sock);
    return -1;
}
while ((byte_count = read(sock, buffer, sizeof(buffer)-1)) > 0)
{
    buffer[byte_count] = '\0';
    DBG1(DBG_APP, "%s", buffer);
}
  • charon은 이 메시지를 받아 새로운 연결을 설정하고 IKE 세션을 관리한다.

6. STR_INITIATE 메시지 처리

  • STR_INITIATE 메시지가 수신되면 stroke_initiate()가 호출되고, 이는 stroke_control.c의 initiate() 함수를 호출
  • 이 함수는 설정 정보를 불러와 charon_initiate()를 호출하게된다.
void stroke_initiate(stroke_socket_t *this, stroke_msg_t *msg)
{
    this->control->initiate(this->control, msg);
}

7. initiate()

  • stroke_control.c의 initiate() 함수는 get_peer_cfg_by_name()을 사용해 peer_cfg를 가져오고, 해당 설정에서 child_cfg를 얻는다.
  • 그 후, charon_initiate()가 호출된다.
void initiate(private_stroke_control_t *this, stroke_msg_t *msg)
{
    peer_cfg_t *peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, msg->initiate.name);
    child_cfg_t *child_cfg = get_child_from_peer(peer_cfg, msg->initiate.name);
    
    // 이후 charon_initiate() 호출
    charon_initiate(charon, peer_cfg, child_cfg);
}
  • 이후 charon_initiate()가 controller->initiate()를 호출하여 설정된 연결이 실제로 시작된다.

 

starter_stroke_add_conn <- connection 추가

starter_stroke_initiate_conn <- connection init

starter_stroke_route_conn <- connection route

 

References