# 외부 서비스 연동 모듈 — Arimail(메일)·Gitea(소스 보관) — v1.5.7 AI Webmaker v1.5.7 · 2026-10-14 · 대상: 운영자·개발자 ## 1. 연동 모듈 (요구 5·6) 웹메이커가 다른 제품으로 바꿔 쓸 수 있는 외부 서비스는 종류(kind)마다 **연동 모듈** 하나를 고른다. 모듈은 `lib/connectors/` 에 있고 등록소 `lib/connectors/index.js` 가 설정 파일(`source.provider`, `mail.provider`)에 따라 고른다. | 종류 | 모듈 | 하는 일 | 쓰는 쪽 | |---|---|---|---| | `source` 소스 보관 | `gitea.js` | 푸시 주소·웹 주소·브랜치 접두어, git/ssh 오류 해석, 연결 확인(`git ls-remote` + API), API 토큰이 있으면 저장소 만들기·공개키 등록 | `lib/gitstore.js` (git 명령은 그대로, 접속 정보만 모듈에서) | | `mail` 메일 서버 | `arimail.js` | 관리자 API 로그인, 계정 목록·생성·비밀번호 재설정·삭제, 상태(/api/health), 웹메일 주소·메일 프로그램 설정값 | `lib/mailsys.js` (받는 메일 백엔드: 사이트 관리자 계정의 메일 사용 켜기/끄기, 웹메일 연결) | 모듈의 공통 모양: `id, label, kind, settings(), configured(), status()`. 다른 제품(GitLab, 다른 메일 서버)으로 바꾸려면 같은 모양의 모듈을 만들고 `index.js` 의 `MODULES` 와 `lib/config.js` 의 provider 선택지에 넣는다. 접속 정보(주소·계정)는 설정 파일, 비밀값은 보관소(`lib/vault.js` 종류 `gitea`, `arimail`)에 둔다. ## 2. Arimail 이란 OZ-Group 의 메일 게이트웨이 **mail-gateway** 가 0.9.x 에서 **AriMail(Arimail)** 로 이름이 바뀐다. Resend 로 받은 메일을 보관하고 웹메일(`/`), 관리자 페이지(`/admin`), IMAP(993/143)·POP3(995/110)·SMTP 제출(465/587) 서버를 연다. 한 설치는 한 메일 도메인(환경 파일 `MAILBOX=admin@도메인`)의 계정 `로그인ID@도메인` 을 다룬다. 설치·운영 문서는 그 저장소의 `docs/SETUP.md`. 웹메이커와의 관계 - **보내기**는 지금처럼 웹메이커의 Resend 키(관리자 콘솔 › 설정 › 메일 발송)로 한다. Arimail 도 같은 Resend 로 보내므로 발신 인증(DKIM) 레코드는 공통이다. - **받기·웹메일·메일 계정**은 Arimail 이 맡는다. 사이트 관리자 계정의 "메일 사용" 을 켜면 웹메이커가 Arimail 관리자 API 로 같은 로그인 ID 의 계정을 만들고, 끄면 지운다. 웹메이커는 사이트 관리자 비밀번호의 원문을 갖고 있지 않으므로(scrypt 해시만) **켤 때 메일 비밀번호를 정한다** — 웹메일·IMAP·POP·SMTP 가 그 비밀번호를 쓴다. - 사이트의 `/webmail` 진입 파일 → 웹메이커 `/webmail/<사이트ID>/` → 메일 서버가 연결된 사이트면 Arimail 웹메일로 302. 사이트 관리자 페이지 › 메일의 "웹메일 열기" 도 그 주소. - **도메인 일치 조건**: 사이트의 메일 도메인(관리자 콘솔 › 메일 시스템에서 켠 도메인)이 `mail.domain` 과 같을 때만 받는 메일이 연결된다. 다른 도메인 사이트는 "메일 서버는 X 도메인만 다룬다" 안내와 함께 보내기만 가능하다. 도메인마다 Arimail 을 하나씩 설치한다(여러 설치를 한 설정에 담는 것은 다음 단계). ## 3. 설치·등록 (`deploy/install-arimail.sh`, 요구 4) 서비스 서버에서 웹메이커 실행 계정으로(비밀번호 없는 sudo 필요): ```bash # 새로 설치 + 등록 (소스: ~/work/mail-gateway 체크아웃을 번들로 묶거나 --repo 로 복제) deploy/install-arimail.sh --host mail.example.com --mailbox admin@example.com [--source ~/work/mail-gateway | --bundle <번들> | --repo ] [--user arimail] [--app-name arimail] [--port 8790] [--node ] [--from-name "이름"] [--limit-accounts 50 --limit-mailbox 500M …] # 이미 설치된(또는 다른 서버의) Arimail 등록만 deploy/install-arimail.sh --register-only --url https://mail.example.com --domain example.com --admin-login webmaker-admin --admin-password '…' [--api-url http://127.0.0.1:8790] # 이 서버에 설치된 Arimail 을 찾아 등록 (install-services.sh --check / remote-update.sh 가 부른다) deploy/install-arimail.sh --check ``` 하는 일: ① 소스 확보(git bundle — 서버에 저장소 자격 증명을 두지 않음) ② 실행 계정(시스템 계정)과 Node 22.13+(시스템 node 가 맞으면 그것, 아니면 `/opt/-node`) ③ Arimail 표준 절차 `deploy/new-site.sh`(standalone) → `setup.sh`(root: 디렉터리·`/etc//gateway.env`·systemd·sudoers·nginx vhost·certbot 훅) → `deploy.sh`(실행 계정: 테스트·릴리스·DB 마이그레이션·재시작). nginx 가 없는 서버(Caddy 등)는 nginx 를 뺀 같은 준비를 하고 프록시 설정 예시를 알려 준다 ④ `gateway.env` 의 `RESEND_API_KEY` 가 비어 있으면 웹메이커 보관소의 메일 발송 키를 넣는다 ⑤ 웹메이커 전용 Arimail 관리자 계정 `webmaker-admin`(무작위 비밀번호)을 만들고 설정 파일에 등록: `mail.provider=arimail`, `mail.url=https://`, `mail.apiUrl=http://127.0.0.1:`, `mail.domain`, `mail.adminLogin`, `mail.adminPassword`(보관소) ⑥ 남은 수동 작업 안내. 남는 수동 작업 1. DNS: `mail.<도메인>` A 레코드 → 서버 공인 IP(프록시 없이). 바뀌면 Arimail 의 `-tls.timer` 가 certbot 으로 인증서를 받아 443·메일 포트 TLS 를 켠다. 2. Resend: 도메인을 수신·발신 도메인으로 등록하고 Webhooks → `https://mail.<도메인>/api/webhooks/resend`(email.received) 의 Signing Secret 을 `/etc//gateway.env` 의 `RESEND_WEBHOOK_SECRET` 에 넣고 서비스 재시작. 받는 메일 MX 는 Resend 안내값(관리자 콘솔 › 메일 시스템 › 기본값의 "메일 서버 주소" 에 그 값을 넣으면 DNS 안내 표에 나온다). 3. 초기 관리자 `admin / adminpasswd` 로 `/admin` 에 들어가 비밀번호를 바꾼다(웹메이커는 별도 계정 `webmaker-admin` 을 쓴다). `deploy/install-services.sh --gitea | --arimail … | --check` 가 Gitea 와 Arimail 을 한 번에 다루며, `deploy/install.sh`·`remote-install.sh`(끝에 Gitea 설치, `ARIMAIL_HOST`·`ARIMAIL_MAILBOX` 환경 변수가 있으면 Arimail 도)·`remote-update.sh`(`--check`)가 부른다. ## 4. 동작 확인 - 초기 설정 페이지 / 관리자 콘솔 › 시스템 설정 › "메일 서버 — Arimail" 카드 → 연결 확인: `연결 성공 — Arimail 0.9.9 · 도메인 example.com · 계정 N개`. - 관리자 콘솔 › 메일 시스템 상단: "받는 메일·웹메일: Arimail … 연결됨 — 메일 도메인이 example.com 인 사이트에 적용". - 사이트 관리자 페이지 › 메일 › 메일 계정 스위치 켜기 → 메일 비밀번호 입력 → `메일 서버에 계정을 만들었습니다: id@example.com …` 안내. Arimail 관리자 페이지 계정 목록에 같은 계정이 보인다. 끄면 계정이 지워진다(메일은 Arimail 저장소에 남아 관리자 페이지에서 정리). - 보관소 항목 `arimail`·`gitea` 의 "연결 확인" 도 같은 모듈 status() 를 쓴다. ## 5. 자주 나는 메시지 | 메시지 | 원인 | 해결 | |---|---|---| | `메일 서버(Arimail) 연동이 꺼져 있어 받을 수 없습니다` | `mail.provider=none` | 설정 페이지 › 메일 연동에서 Arimail 로 바꾸고 주소·도메인·관리자 계정 입력, 또는 `install-arimail.sh` | | `Arimail 주소는 있지만 도메인 또는 관리자 비밀번호가 없습니다` | 설정 미완 | `mail.domain`, `mail.adminPassword` 입력 | | `Arimail 응답 없음 — 연결 실패: ECONNREFUSED` | 서비스가 꺼짐·내부 API 주소 틀림 | `systemctl status `, `mail.apiUrl` 확인 | | `Arimail 관리자 로그인에 실패했습니다` | 비밀번호 틀림·계정이 관리자 역할 아님 | Arimail `/admin` 에서 계정 확인, `install-arimail.sh --check` 로 webmaker-admin 비밀번호 재설정·재등록 | | `메일 비밀번호를 입력하세요` (메일 사용 켜기) | Arimail 이 연결된 사이트는 비밀번호가 필요 | 사이트 관리자 페이지의 입력 창에서 정함 | | `메일 비밀번호가 Arimail 의 비밀번호 정책에 맞지 않습니다` | Arimail 관리자 페이지 › 비밀번호 정책 | 정책에 맞는 비밀번호 | | `Arimail 의 계정 수 제한(LIMIT_ACCOUNTS)에 이르렀습니다` | 설치 때 정한 시스템 제한 | `/etc//gateway.env` 의 `LIMIT_ACCOUNTS` 조정 후 재시작 | | `메일 서버(Arimail)는 X 도메인만 다루는데 이 사이트의 메일 도메인은 Y 입니다` | 도메인 불일치 | 같은 도메인으로 메일 시스템을 켜거나 그 도메인용 Arimail 설치 | | `Arimail 로그인 시도가 너무 많아 잠시 막혔습니다 (15분 뒤 다시)` / 표의 "Arimail 로그인 잠금 · HH:MM 까지" | 틀린 비밀번호로 로그인을 반복 (Arimail 15분 10회 제한) | 1.6.39: 해제 시각까지 "연결 확인" 이 비활성. 그 사이 "수정"/"이전 비밀번호로" 로 맞는 값을 저장해 두고 해제 뒤 한 번만 확인 | | `Arimail 관리자 계정 "…" 은(는) 웹메이커에서 지울 수 없습니다 — … 보호됩니다` | 연동 관리자·관리자 역할 계정 삭제·역할 하향 시도 | 1.6.39: 설계상 막음. 필요하면 Arimail /admin 에서 직접 | | `사이트 관리자 비밀번호와 다릅니다 — 메일 비밀번호는 이 계정의 비밀번호와 같게 유지되므로…` (메일 사용 켜기) | 본인 계정에 다른 메일 비밀번호를 넣음 | 1.6.39: 지금 로그인한 비밀번호를 입력 | | 사이트 관리자 페이지 메일 계정 표 "비밀번호 동기화: 실패 — … 비밀번호 정책" | Arimail 비밀번호 정책이 웹메이커 규칙보다 엄격 | 관리자 비밀번호를 정책에 맞게 바꾸면 다음 로그인 때 다시 맞춰진다 | | `Arimail 로그인 실패 — … (저장된 비밀번호가 틀리면 "다른 관리자 계정으로 재설정" 을 쓰세요)` (비밀번호 초기화) | 보관소의 연동 계정 비밀번호가 틀림 | 열리는 폼에 Arimail 의 다른 관리자(초기 admin 등) 아이디·비밀번호 입력 → 연동 계정 비밀번호 재생성 | ## 6. 여러 메일 서버 — 다른 서버에 설치된 Arimail (v1.6.38, 요구 2) 웹메이커와 Arimail 을 다른 서버에 나눠 설치할 수 있고(예: webmaker.ibit.kr 121.152.149.45 ↔ webmail.ibit.kr 115.68.194.192), 메일 도메인마다 다른 Arimail 을 붙일 수 있다. - **등록부** `lib/mailservers.js` → `data/mail-servers.json`(id·닉네임·IP·공개 주소·API 주소·메일 도메인·관리자 ID·연결 확인). 관리자 비밀번호는 보관소(`lib/vault.js`) 종류 `arimail`, 식별자 `arimail-`. - **기본 서버(설정 파일)**: `mail.url / mail.apiUrl / mail.domain / mail.adminLogin / mail.adminPassword` 의 서버는 id `primary` 로 등록부에 함께 보인다. `install-arimail.sh --register-only` 는 여기에 쓴다. 닉네임·IP 는 `mail.nickname`·`mail.ip`. - **관리자 콘솔 › 메일 시스템 › 메일 서버 (Arimail) 연동**: 추가(닉네임·서버 IP·Arimail 주소·API 주소·메일 도메인·관리자 ID·비밀번호) · 연결 확인(health → 관리자 로그인 → 계정 수) · 수정 · 기본으로 · 삭제. 비밀번호는 화면·작업 기록에 나오지 않는다. - **사이트가 쓰는 서버**: 사이트 메일 설정의 "메일 서버" 에서 고른 서버 → 없으면 사이트 메일 도메인과 같은 도메인을 다루는 서버 → 없으면 기본 서버. 도메인이 맞지 않으면 사이트 메일 상태가 그 이유를 말한다. - **MX 기본값**: 사이트·전역 MX 호스트가 비어 있으면 고른 메일 서버의 호스트(예: webmail.ibit.kr)를 MX 로 안내한다. - **API**: `GET/POST /api/admin/mail-servers`, `PUT/DELETE /api/admin/mail-servers/`, `POST /api/admin/mail-servers//test`, `POST /api/admin/mail-servers//default`. - 연동 모듈: `require('./lib/connectors/arimail').forServer(server)` 가 서버에 묶인 동작(로그인·계정·상태)을 돌려준다. 최상위 함수는 기본 서버에 묶여 있다. ## 7. 비밀번호 — 보관·되돌리기·잠금·보기·초기화·동기화 (v1.6.39, 요구 2·3·4) 2026-10-21 webmaker.ibit.kr 에서 기본 메일 서버를 수정하다 비밀번호 칸에 값을 넣어 동작하던 Arimail 관리자 비밀번호를 덮어쓰고, "연결 확인" 을 반복해 15분 잠금까지 걸린 사고에서 나온 설계다. - **연동 관리자 비밀번호의 자리**: 기본 서버는 설정 파일 `mail.adminPassword`(보관소 `arimail`), 추가 서버는 보관소 `arimail-`. **이전 값**은 `prev-arimail` / `prev-arimail-`(비밀번호를 바꾸기 직전 자동 보관). 모두 `data/credentials.json`(0600, AES-256-GCM) 이고 1.6.39 부터 `.bak` 이 함께 남는다(`config.json`·`mail-servers.json` 도). - **관리자 콘솔 › 메일 시스템 › 메일 서버 연동 표** - 관리자 ID 칸 **[보기]/[숨기기]·[복사]** — `GET /api/admin/mail-servers//password`(관리자 전용). 60초 뒤 자동 숨김. 작업 기록 `mailserver.reveal`(비밀번호 없음). 초기 설정 뒤 Arimail `/admin` 로그인에 쓴다. - **수정** 때 비밀번호 칸에 값이 있으면 확인 창 — 그대로 두려면 칸을 비운다. - **이전 비밀번호로** — `POST …/revert-password`. 이전 값 ↔ 현재 값을 맞바꾼다(다시 되돌릴 수 있음). - **비밀번호 초기화** — `POST …/reset-password`. 16자 무작위(대·소문자·숫자·기호) 비밀번호를 Arimail 계정에 설정하고 보관. 저장된 비밀번호로 로그인이 안 되면 "다른 관리자 계정으로 재설정" 폼(`{via:{login,password}}`, 저장하지 않음; 연동 계정이 없으면 관리자 역할로 생성). - **잠금** — Arimail 이 `rate_limited` 를 돌려주면 등록부에 `lockedUntil`(15분). 그동안 "연결 확인" 비활성 + 남은 시간 표시, 서버의 `mailservers.test()` 도 Arimail 을 부르지 않는다. 해제 뒤 성공하면 기록이 지워진다. - **관리자 계정 보호**(`protectAdmin`): 연동 관리자 계정과 Arimail 관리자 역할 계정은 웹메이커의 DELETE·역할 하향·비활성화가 403. 비밀번호·이름 변경과 admin 으로 올리기는 허용. - **사이트 관리자 비밀번호 ↔ 메일 비밀번호 동기화**(`mailsys.syncPassword`): 웹메이커는 관리자 비밀번호를 해시로만 가지므로, 원문을 아는 순간(사이트 관리자 페이지·웹메일 로그인 성공)마다 등록부 `data/mail-accounts.json` 의 `pwSyncHash` 와 지금 해시를 비교해 다르면 Arimail 계정 비밀번호를 PATCH 로 맞춘다. 결과는 `pwSyncAt / pwSyncError` 와 작업 기록 `mail.account.sync`(원문 없음). "메일 사용 켜기" 는 본인 계정이면 관리자 비밀번호 그대로(다르면 400), 소유자·운영자가 대신 켤 때는 임시 비밀번호 → 다음 로그인 때 자동으로 맞춰진다. 사이트 관리자 페이지 메일 계정 표의 "비밀번호 동기화" 열에서 상태를 본다. - API 정리: `GET …//password` · `POST …//revert-password` · `POST …//reset-password` · 연결 확인 응답 `locked / lockedUntil / code` · 서버 객체 `hasPrevPassword / prevPasswordAt / lockedUntil / adminUrl`.